mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-12 03:13:50 +02:00
improve user dialog ui
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div @click="confirm" class="avatar-info">
|
||||
<span style="margin-right: 5px">{{ avatarName }}</span>
|
||||
<span v-if="avatarType" :class="color" style="margin-right: 5px">{{ avatarType }}</span>
|
||||
<span v-if="avatarType" :class="color" style="margin-right: 5px"><i :class="avatarTypeIcons" /></span>
|
||||
<span v-if="avatarTags" style="color: #909399; font-family: monospace; font-size: 12px">{{ avatarTags }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { useAvatarStore } from '../stores';
|
||||
|
||||
@@ -27,6 +27,14 @@
|
||||
const color = ref('');
|
||||
let ownerId = '';
|
||||
|
||||
const avatarTypeIcons = computed(() => {
|
||||
return avatarType.value === '(own)'
|
||||
? 'ri-lock-line'
|
||||
: avatarType.value === '(public)'
|
||||
? 'ri-lock-unlock-line'
|
||||
: '';
|
||||
});
|
||||
|
||||
const parse = async () => {
|
||||
ownerId = '';
|
||||
avatarName.value = '';
|
||||
@@ -35,7 +43,7 @@
|
||||
avatarTags.value = '';
|
||||
|
||||
if (!props.imageurl) {
|
||||
avatarName.value = '-';
|
||||
avatarName.value = '';
|
||||
} else if (props.hintownerid) {
|
||||
avatarName.value = props.hintavatarname;
|
||||
ownerId = props.hintownerid;
|
||||
|
||||
141
src/components/dialogs/UserDialog/EditNoteAndMemoDialog.vue
Normal file
141
src/components/dialogs/UserDialog/EditNoteAndMemoDialog.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
class="x-dialog"
|
||||
:model-value="props.visible"
|
||||
title="Edit Note And Memo"
|
||||
:show-close="false"
|
||||
top="30vh"
|
||||
width="500px"
|
||||
append-to-body
|
||||
@close="cancel">
|
||||
<span class="name">{{ t('dialog.user.info.note') }}</span>
|
||||
<br />
|
||||
<el-input
|
||||
v-model="note"
|
||||
class="extra"
|
||||
type="textarea"
|
||||
maxlength="256"
|
||||
show-word-limit
|
||||
:rows="6"
|
||||
:autosize="{ minRows: 2, maxRows: 20 }"
|
||||
:placeholder="t('dialog.user.info.note_placeholder')"
|
||||
size="small"
|
||||
resize="none"></el-input>
|
||||
<span class="name">{{ t('dialog.user.info.memo') }}</span>
|
||||
<br />
|
||||
<el-input
|
||||
v-model="memo"
|
||||
class="extra"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
:autosize="{ minRows: 2, maxRows: 20 }"
|
||||
:placeholder="t('dialog.user.info.memo_placeholder')"
|
||||
size="small"
|
||||
resize="none"></el-input>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel">Cancel</el-button>
|
||||
<el-button type="primary" @click="saveChanges"> Confirm </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { miscRequest, userRequest } from '../../../api';
|
||||
import { replaceBioSymbols, saveUserMemo } from '../../../shared/utils';
|
||||
import { useUserStore } from '../../../stores';
|
||||
|
||||
const { userDialog } = storeToRefs(useUserStore());
|
||||
const { cachedUsers } = useUserStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:visible']);
|
||||
|
||||
const note = ref('');
|
||||
const memo = ref('');
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
if (!val) return;
|
||||
note.value = userDialog.value.note;
|
||||
memo.value = userDialog.value.memo;
|
||||
}
|
||||
);
|
||||
|
||||
function saveChanges() {
|
||||
cleanNote(note.value);
|
||||
checkNote(userDialog.value.ref, note.value);
|
||||
onUserMemoChange();
|
||||
emit('update:visible', false);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
emit('update:visible', false);
|
||||
}
|
||||
|
||||
function checkNote(ref, note) {
|
||||
if (ref.note !== note) {
|
||||
addNote(ref.id, note);
|
||||
}
|
||||
}
|
||||
|
||||
async function addNote(userId, note) {
|
||||
if (userDialog.value.id === userId) {
|
||||
userDialog.value.noteSaving = true;
|
||||
}
|
||||
const args = await miscRequest.saveNote({
|
||||
targetUserId: userId,
|
||||
note
|
||||
});
|
||||
handleNoteChange(args);
|
||||
}
|
||||
|
||||
function handleNoteChange(args) {
|
||||
let _note = '';
|
||||
let targetUserId = '';
|
||||
if (typeof args.json !== 'undefined') {
|
||||
_note = replaceBioSymbols(args.json.note);
|
||||
}
|
||||
if (typeof args.params !== 'undefined') {
|
||||
targetUserId = args.params.targetUserId;
|
||||
}
|
||||
if (targetUserId === userDialog.value.id) {
|
||||
if (_note === args.params.note) {
|
||||
userDialog.value.noteSaving = false;
|
||||
userDialog.value.note = _note;
|
||||
} else {
|
||||
// response is cached sadge :<
|
||||
userRequest.getUser({ userId: targetUserId });
|
||||
}
|
||||
}
|
||||
const ref = cachedUsers.get(targetUserId);
|
||||
if (typeof ref !== 'undefined') {
|
||||
ref.note = _note;
|
||||
}
|
||||
}
|
||||
|
||||
function onUserMemoChange() {
|
||||
const D = userDialog.value;
|
||||
saveUserMemo(D.id, memo.value);
|
||||
}
|
||||
|
||||
function cleanNote(note) {
|
||||
if (!note.value) return;
|
||||
// remove newlines because they aren't supported
|
||||
note.value = note.value?.replace(/[\r\n]/g, '');
|
||||
}
|
||||
</script>
|
||||
@@ -120,6 +120,7 @@
|
||||
<el-dropdown-item :icon="Operation" command="Group Moderation">{{
|
||||
t('dialog.user.actions.group_moderation')
|
||||
}}</el-dropdown-item>
|
||||
<el-dropdown-item :icon="Edit" command="Edit Note Memo"> Edit Note and Memo </el-dropdown-item>
|
||||
<el-dropdown-item :icon="UserFilled" command="Show Avatar Author" divided>{{
|
||||
t('dialog.user.actions.show_avatar_author')
|
||||
}}</el-dropdown-item>
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
class="x-dialog x-user-dialog"
|
||||
v-model="userDialog.visible"
|
||||
:show-close="false"
|
||||
width="770px">
|
||||
top="10vh"
|
||||
width="940px">
|
||||
<div v-loading="userDialog.loading">
|
||||
<UserSummaryHeader
|
||||
:get-user-state-text="getUserStateText"
|
||||
@@ -99,53 +100,19 @@
|
||||
</template>
|
||||
|
||||
<div class="x-friend-list" style="max-height: none">
|
||||
<div v-if="!hideUserNotes" class="x-friend-item" style="width: 100%; cursor: default">
|
||||
<div class="detail">
|
||||
<div v-if="!hideUserNotes" class="x-friend-item" style="width: 100%; cursor: pointer">
|
||||
<div class="detail" v-if="userDialog.note" @click="isEditNoteAndMemoDialogVisible = true">
|
||||
<span class="name">{{ t('dialog.user.info.note') }}</span>
|
||||
<el-input
|
||||
v-model="userDialog.note"
|
||||
class="extra"
|
||||
type="textarea"
|
||||
maxlength="256"
|
||||
:rows="2"
|
||||
:autosize="{ minRows: 1, maxRows: 20 }"
|
||||
:placeholder="t('dialog.user.info.note_placeholder')"
|
||||
size="small"
|
||||
resize="none"
|
||||
@change="checkNote(userDialog.ref, userDialog.note)"
|
||||
@input="cleanNote(userDialog.note)"></el-input>
|
||||
<div style="float: right">
|
||||
<el-icon v-if="userDialog.noteSaving" class="is-loading" style="margin-left: 5px"
|
||||
><Loading
|
||||
/></el-icon>
|
||||
<el-icon
|
||||
v-else-if="userDialog.note !== userDialog.ref.note"
|
||||
style="margin-left: 5px"
|
||||
><More
|
||||
/></el-icon>
|
||||
<el-button
|
||||
v-if="userDialog.note"
|
||||
type="text"
|
||||
:icon="Delete"
|
||||
size="small"
|
||||
style="margin-left: 5px; padding: 0"
|
||||
@click="deleteNote(userDialog.id)"></el-button>
|
||||
</div>
|
||||
<div>{{ userDialog.note }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!hideUserMemos" class="x-friend-item" style="width: 100%; cursor: default">
|
||||
<div class="detail">
|
||||
<div
|
||||
v-if="userDialog.memo && !hideUserMemos"
|
||||
class="x-friend-item"
|
||||
style="width: 100%; cursor: pointer">
|
||||
<div class="detail" @click="isEditNoteAndMemoDialogVisible = true">
|
||||
<span class="name">{{ t('dialog.user.info.memo') }}</span>
|
||||
<el-input
|
||||
v-model="userDialog.memo"
|
||||
class="extra"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
:autosize="{ minRows: 1, maxRows: 20 }"
|
||||
:placeholder="t('dialog.user.info.memo_placeholder')"
|
||||
size="small"
|
||||
resize="none"
|
||||
@change="onUserMemoChange"></el-input>
|
||||
<div>{{ userDialog.memo }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="x-friend-item" style="width: 100%; cursor: default">
|
||||
@@ -236,7 +203,7 @@
|
||||
font-size: 12px;
|
||||
white-space: pre-wrap;
|
||||
margin: 0 0.5em 0 0;
|
||||
max-height: 40vh;
|
||||
max-height: 210px;
|
||||
overflow-y: auto;
|
||||
"
|
||||
>{{ userDialog.ref.bio || '-' }}</pre
|
||||
@@ -1216,15 +1183,14 @@
|
||||
v-model:sendInviteDialog="sendInviteDialog"
|
||||
@closeInviteDialog="closeInviteDialog" />
|
||||
<PreviousInstancesUserDialog v-model:previous-instances-user-dialog="previousInstancesUserDialog" />
|
||||
<template v-if="userDialog.visible">
|
||||
<SocialStatusDialog
|
||||
:social-status-dialog="socialStatusDialog"
|
||||
:social-status-history-table="socialStatusHistoryTable" />
|
||||
<LanguageDialog />
|
||||
<BioDialog :bio-dialog="bioDialog" />
|
||||
<PronounsDialog :pronouns-dialog="pronounsDialog" />
|
||||
<ModerateGroupDialog
|
||||
/></template>
|
||||
<SocialStatusDialog
|
||||
:social-status-dialog="socialStatusDialog"
|
||||
:social-status-history-table="socialStatusHistoryTable" />
|
||||
<LanguageDialog />
|
||||
<BioDialog :bio-dialog="bioDialog" />
|
||||
<PronounsDialog :pronouns-dialog="pronounsDialog" />
|
||||
<ModerateGroupDialog />
|
||||
<EditNoteAndMemoDialog v-model:visible="isEditNoteAndMemoDialogVisible" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
@@ -1240,7 +1206,6 @@
|
||||
Download,
|
||||
Edit,
|
||||
Loading,
|
||||
More,
|
||||
MoreFilled,
|
||||
Refresh,
|
||||
Setting,
|
||||
@@ -1265,8 +1230,6 @@
|
||||
openExternalLink,
|
||||
parseLocation,
|
||||
refreshInstancePlayerCount,
|
||||
replaceBioSymbols,
|
||||
saveUserMemo,
|
||||
timeToText,
|
||||
userImage,
|
||||
userOnlineFor,
|
||||
@@ -1315,6 +1278,7 @@
|
||||
const SendInviteRequestDialog = defineAsyncComponent(() => import('./SendInviteRequestDialog.vue'));
|
||||
const SocialStatusDialog = defineAsyncComponent(() => import('./SocialStatusDialog.vue'));
|
||||
const ModerateGroupDialog = defineAsyncComponent(() => import('../ModerateGroupDialog.vue'));
|
||||
const EditNoteAndMemoDialog = defineAsyncComponent(() => import('./EditNoteAndMemoDialog.vue'));
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -1343,7 +1307,6 @@
|
||||
leaveGroup,
|
||||
leaveGroupPrompt,
|
||||
setGroupVisibility,
|
||||
setGroupSubscription,
|
||||
handleGroupList,
|
||||
showModerateGroupDialog
|
||||
} = useGroupStore();
|
||||
@@ -1442,6 +1405,8 @@
|
||||
showingTranslated: false
|
||||
});
|
||||
|
||||
const isEditNoteAndMemoDialogVisible = ref(false);
|
||||
|
||||
const userDialogAvatars = computed(() => {
|
||||
const { avatars, avatarReleaseStatus } = userDialog.value;
|
||||
if (avatarReleaseStatus === 'public' || avatarReleaseStatus === 'private') {
|
||||
@@ -1493,11 +1458,6 @@
|
||||
return t('dialog.user.status.offline');
|
||||
}
|
||||
|
||||
function cleanNote(note) {
|
||||
// remove newlines because they aren't supported
|
||||
userDialog.value.note = note.replace(/[\r\n]/g, '');
|
||||
}
|
||||
|
||||
function handleUserDialogTab(tabName) {
|
||||
const userId = userDialog.value.id;
|
||||
if (tabName === 'Groups') {
|
||||
@@ -1799,6 +1759,8 @@
|
||||
} else {
|
||||
setPlayerModeration(D.id, 5);
|
||||
}
|
||||
} else if (command === 'Edit Note Memo') {
|
||||
isEditNoteAndMemoDialogVisible.value = true;
|
||||
} else {
|
||||
const i18nPreFix = 'dialog.user.actions.';
|
||||
const formattedCommand = command.toLowerCase().replace(/ /g, '_');
|
||||
@@ -2228,63 +2190,6 @@
|
||||
userDialog.value.isFavoriteWorldsLoading = false;
|
||||
}
|
||||
|
||||
function checkNote(ref, note) {
|
||||
if (ref.note !== note) {
|
||||
addNote(ref.id, note);
|
||||
}
|
||||
}
|
||||
|
||||
async function addNote(userId, note) {
|
||||
if (userDialog.value.id === userId) {
|
||||
userDialog.value.noteSaving = true;
|
||||
}
|
||||
const args = await miscRequest.saveNote({
|
||||
targetUserId: userId,
|
||||
note
|
||||
});
|
||||
handleNoteChange(args);
|
||||
}
|
||||
|
||||
function handleNoteChange(args) {
|
||||
let _note = '';
|
||||
let targetUserId = '';
|
||||
if (typeof args.json !== 'undefined') {
|
||||
_note = replaceBioSymbols(args.json.note);
|
||||
}
|
||||
if (typeof args.params !== 'undefined') {
|
||||
targetUserId = args.params.targetUserId;
|
||||
}
|
||||
if (targetUserId === userDialog.value.id) {
|
||||
if (_note === args.params.note) {
|
||||
userDialog.value.noteSaving = false;
|
||||
userDialog.value.note = _note;
|
||||
} else {
|
||||
// response is cached sadge :<
|
||||
userRequest.getUser({ userId: targetUserId });
|
||||
}
|
||||
}
|
||||
const ref = cachedUsers.get(targetUserId);
|
||||
if (typeof ref !== 'undefined') {
|
||||
ref.note = _note;
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteNote(userId) {
|
||||
if (userDialog.value.id === userId) {
|
||||
userDialog.value.noteSaving = true;
|
||||
}
|
||||
const args = await miscRequest.saveNote({
|
||||
targetUserId: userId,
|
||||
note: ''
|
||||
});
|
||||
handleNoteChange(args);
|
||||
}
|
||||
|
||||
function onUserMemoChange() {
|
||||
const D = userDialog.value;
|
||||
saveUserMemo(D.id, D.memo);
|
||||
}
|
||||
|
||||
function showBioDialog() {
|
||||
const D = bioDialog.value;
|
||||
D.bio = currentUser.value.bio;
|
||||
|
||||
@@ -82,6 +82,20 @@
|
||||
style="margin-right: 5px; margin-top: 5px">
|
||||
{{ userDialog.ref.$trustLevel }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="userDialog.ref.ageVerified && userDialog.ref.ageVerificationStatus"
|
||||
type="info"
|
||||
effect="plain"
|
||||
size="small"
|
||||
class="x-tag-age-verification"
|
||||
style="margin-right: 5px; margin-top: 5px">
|
||||
<template v-if="userDialog.ref.ageVerificationStatus === '18+'">
|
||||
<i class="ri-info-card-line"></i> 18+
|
||||
</template>
|
||||
<template v-else>
|
||||
<i class="ri-info-card-line"></i>
|
||||
</template>
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="userDialog.isFriend && userDialog.friend"
|
||||
type="info"
|
||||
@@ -89,11 +103,8 @@
|
||||
size="small"
|
||||
class="x-tag-friend"
|
||||
style="margin-right: 5px; margin-top: 5px">
|
||||
{{
|
||||
t('dialog.user.tags.friend_no', {
|
||||
number: userDialog.ref.$friendNumber ? userDialog.ref.$friendNumber : ''
|
||||
})
|
||||
}}
|
||||
<i class="ri-user-add-line"></i>
|
||||
{{ userDialog.ref.$friendNumber ? userDialog.ref.$friendNumber : '' }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="userDialog.ref.$isTroll"
|
||||
@@ -122,6 +133,7 @@
|
||||
style="margin-right: 5px; margin-top: 5px">
|
||||
{{ t('dialog.user.tags.vrchat_team') }}
|
||||
</el-tag>
|
||||
|
||||
<el-tag
|
||||
v-if="userDialog.ref.$platform === 'standalonewindows'"
|
||||
type="info"
|
||||
@@ -129,7 +141,7 @@
|
||||
size="small"
|
||||
class="x-tag-platform-pc"
|
||||
style="margin-right: 5px; margin-top: 5px">
|
||||
PC
|
||||
<i class="ri-computer-line"></i>
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-else-if="userDialog.ref.$platform === 'android'"
|
||||
@@ -138,7 +150,7 @@
|
||||
size="small"
|
||||
class="x-tag-platform-quest"
|
||||
style="margin-right: 5px; margin-top: 5px">
|
||||
Android
|
||||
<i class="ri-android-line"></i>
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-else-if="userDialog.ref.$platform === 'ios'"
|
||||
@@ -147,8 +159,8 @@
|
||||
size="small"
|
||||
class="x-tag-platform-ios"
|
||||
style="margin-right: 5px; margin-top: 5px"
|
||||
>iOS</el-tag
|
||||
>
|
||||
><i class="ri-apple-line"></i
|
||||
></el-tag>
|
||||
<el-tag
|
||||
v-else-if="userDialog.ref.$platform"
|
||||
type="info"
|
||||
@@ -158,20 +170,7 @@
|
||||
style="margin-right: 5px; margin-top: 5px">
|
||||
{{ userDialog.ref.$platform }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="userDialog.ref.ageVerified && userDialog.ref.ageVerificationStatus"
|
||||
type="info"
|
||||
effect="plain"
|
||||
size="small"
|
||||
class="x-tag-age-verification"
|
||||
style="margin-right: 5px; margin-top: 5px">
|
||||
<template v-if="userDialog.ref.ageVerificationStatus === '18+'">
|
||||
{{ t('dialog.user.tags.18_plus_verified') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ t('dialog.user.tags.age_verified') }}
|
||||
</template>
|
||||
</el-tag>
|
||||
|
||||
<el-tag
|
||||
v-if="userDialog.ref.$customTag"
|
||||
type="info"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useFriendStore, useUserStore } from '../../stores';
|
||||
import { database } from '../../service/database.js';
|
||||
import { useFriendStore } from '../../stores';
|
||||
|
||||
/**
|
||||
* @returns {Promise<void>}
|
||||
@@ -43,6 +43,7 @@ async function getUserMemo(userId) {
|
||||
*/
|
||||
async function saveUserMemo(id, memo) {
|
||||
const friendStore = useFriendStore();
|
||||
const userStore = useUserStore();
|
||||
if (memo) {
|
||||
await database.setUserMemo({
|
||||
userId: id,
|
||||
@@ -61,6 +62,7 @@ async function saveUserMemo(id, memo) {
|
||||
} else {
|
||||
ref.$nickName = '';
|
||||
}
|
||||
userStore.userDialog.memo = memo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user