mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-22 00:03:51 +02:00
470 lines
15 KiB
Vue
470 lines
15 KiB
Vue
<template>
|
|
<div class="w-223">
|
|
<DialogHeader class="sr-only">
|
|
<DialogTitle>{{
|
|
userDialog.ref?.displayName || userDialog.id || t('dialog.user.info.header')
|
|
}}</DialogTitle>
|
|
<DialogDescription>{{ getUserStateText(userDialog.ref || {}) }}</DialogDescription>
|
|
</DialogHeader>
|
|
<UserSummaryHeader
|
|
:get-user-state-text="getUserStateText"
|
|
:copy-user-display-name="copyUserDisplayName"
|
|
:toggle-badge-visibility="toggleBadgeVisibility"
|
|
:toggle-badge-showcased="toggleBadgeShowcased"
|
|
:user-dialog-command="userDialogCommand" />
|
|
|
|
<TabsUnderline
|
|
v-model="userDialog.activeTab"
|
|
:items="userDialogTabs"
|
|
:unmount-on-hide="false"
|
|
@update:modelValue="userDialogTabClick">
|
|
<template #Info>
|
|
<UserDialogInfoTab ref="infoTabRef" @show-bio-dialog="showBioDialog" />
|
|
</template>
|
|
|
|
<template v-if="userDialog.id !== currentUser.id && !currentUser.hasSharedConnectionsOptOut" #mutual>
|
|
<UserDialogMutualFriendsTab ref="mutualFriendsTabRef" />
|
|
</template>
|
|
|
|
<template #Groups>
|
|
<UserDialogGroupsTab ref="groupsTabRef" />
|
|
</template>
|
|
|
|
<template #Worlds>
|
|
<UserDialogWorldsTab ref="worldsTabRef" />
|
|
</template>
|
|
|
|
<template #favorite-worlds>
|
|
<UserDialogFavoriteWorldsTab ref="favoriteWorldsTabRef" />
|
|
</template>
|
|
|
|
<template #Avatars>
|
|
<UserDialogAvatarsTab ref="avatarsTabRef" />
|
|
</template>
|
|
|
|
<template #JSON>
|
|
<DialogJsonTab
|
|
:tree-data="treeData"
|
|
:tree-data-key="treeData?.id"
|
|
:dialog-id="userDialog.id"
|
|
:dialog-ref="userDialog.ref"
|
|
@refresh="refreshUserDialogTreeData()" />
|
|
</template>
|
|
</TabsUnderline>
|
|
<SendInviteDialog
|
|
v-model:sendInviteDialogVisible="sendInviteDialogVisible"
|
|
v-model:sendInviteDialog="sendInviteDialog"
|
|
@closeInviteDialog="closeInviteDialog" />
|
|
<SendInviteRequestDialog
|
|
v-model:sendInviteRequestDialogVisible="sendInviteRequestDialogVisible"
|
|
v-model:sendInviteDialog="sendInviteDialog"
|
|
@closeInviteDialog="closeInviteDialog" />
|
|
<SocialStatusDialog
|
|
:social-status-dialog="socialStatusDialog"
|
|
:social-status-history-table="socialStatusHistoryTable" />
|
|
<LanguageDialog />
|
|
<BioDialog :bio-dialog="bioDialog" />
|
|
<PronounsDialog :pronouns-dialog="pronounsDialog" />
|
|
<ModerateGroupDialog />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, defineAsyncComponent, ref, watch } from 'vue';
|
|
import { DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
|
import { TabsUnderline } from '@/components/ui/tabs';
|
|
import { storeToRefs } from 'pinia';
|
|
import { toast } from 'vue-sonner';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
import {
|
|
useAvatarStore,
|
|
useFavoriteStore,
|
|
useFriendStore,
|
|
useGalleryStore,
|
|
useGroupStore,
|
|
useInstanceStore,
|
|
useInviteStore,
|
|
useLocationStore,
|
|
useModalStore,
|
|
useModerationStore,
|
|
useNotificationStore,
|
|
useUserStore
|
|
} from '../../../stores';
|
|
import { copyToClipboard } from '../../../shared/utils';
|
|
import { formatJsonVars } from '../../../shared/utils/base/ui';
|
|
import { miscRequest } from '../../../api';
|
|
import { useUserDialogCommands } from './useUserDialogCommands';
|
|
|
|
import DialogJsonTab from '../DialogJsonTab.vue';
|
|
import SendInviteDialog from '../InviteDialog/SendInviteDialog.vue';
|
|
import UserDialogAvatarsTab from './UserDialogAvatarsTab.vue';
|
|
import UserDialogFavoriteWorldsTab from './UserDialogFavoriteWorldsTab.vue';
|
|
import UserDialogGroupsTab from './UserDialogGroupsTab.vue';
|
|
import UserDialogInfoTab from './UserDialogInfoTab.vue';
|
|
import UserDialogMutualFriendsTab from './UserDialogMutualFriendsTab.vue';
|
|
import UserDialogWorldsTab from './UserDialogWorldsTab.vue';
|
|
import UserSummaryHeader from './UserSummaryHeader.vue';
|
|
|
|
const BioDialog = defineAsyncComponent(() => import('./BioDialog.vue'));
|
|
const LanguageDialog = defineAsyncComponent(() => import('./LanguageDialog.vue'));
|
|
const PronounsDialog = defineAsyncComponent(() => import('./PronounsDialog.vue'));
|
|
const SendInviteRequestDialog = defineAsyncComponent(() => import('./SendInviteRequestDialog.vue'));
|
|
const SocialStatusDialog = defineAsyncComponent(() => import('./SocialStatusDialog.vue'));
|
|
const ModerateGroupDialog = defineAsyncComponent(() => import('../ModerateGroupDialog.vue'));
|
|
|
|
const { t } = useI18n();
|
|
const userDialogTabs = computed(() => {
|
|
const tabs = [
|
|
{ value: 'Info', label: t('dialog.user.info.header') },
|
|
{ value: 'Groups', label: t('dialog.user.groups.header') },
|
|
{ value: 'Worlds', label: t('dialog.user.worlds.header') },
|
|
{ value: 'favorite-worlds', label: t('dialog.user.favorite_worlds.header') },
|
|
{ value: 'Avatars', label: t('dialog.user.avatars.header') },
|
|
{ value: 'JSON', label: t('dialog.user.json.header') }
|
|
];
|
|
if (userDialog.value.id !== currentUser.value.id && !currentUser.value.hasSharedConnectionsOptOut) {
|
|
tabs.splice(1, 0, { value: 'mutual', label: t('dialog.user.mutual_friends.header') });
|
|
}
|
|
return tabs;
|
|
});
|
|
const infoTabRef = ref(null);
|
|
const favoriteWorldsTabRef = ref(null);
|
|
const mutualFriendsTabRef = ref(null);
|
|
const worldsTabRef = ref(null);
|
|
const avatarsTabRef = ref(null);
|
|
const groupsTabRef = ref(null);
|
|
|
|
const modalStore = useModalStore();
|
|
const instanceStore = useInstanceStore();
|
|
|
|
const { userDialog, languageDialog, currentUser, isLocalUserVrcPlusSupporter } = storeToRefs(useUserStore());
|
|
const { cachedUsers, showUserDialog, refreshUserDialogAvatars, showSendBoopDialog } = useUserStore();
|
|
const { showFavoriteDialog } = useFavoriteStore();
|
|
const { showAvatarDialog, showAvatarAuthorDialog } = useAvatarStore();
|
|
|
|
const { showGroupDialog, showModerateGroupDialog } = useGroupStore();
|
|
const { inviteGroupDialog } = storeToRefs(useGroupStore());
|
|
const { lastLocation, lastLocationDestination } = storeToRefs(useLocationStore());
|
|
const { refreshInviteMessageTableData } = useInviteStore();
|
|
const { friendLogTable } = storeToRefs(useFriendStore());
|
|
const { getFriendRequest, handleFriendDelete } = useFriendStore();
|
|
const { clearInviteImageUpload, showGalleryPage } = useGalleryStore();
|
|
|
|
const { applyPlayerModeration, handlePlayerModerationDelete } = useModerationStore();
|
|
|
|
const {
|
|
sendInviteDialogVisible,
|
|
sendInviteDialog,
|
|
sendInviteRequestDialogVisible,
|
|
userDialogCommand,
|
|
registerCallbacks
|
|
} = useUserDialogCommands(userDialog, {
|
|
t,
|
|
toast,
|
|
modalStore,
|
|
currentUser,
|
|
cachedUsers,
|
|
friendLogTable,
|
|
lastLocation,
|
|
lastLocationDestination,
|
|
inviteGroupDialog,
|
|
showUserDialog,
|
|
showFavoriteDialog,
|
|
showAvatarDialog,
|
|
showAvatarAuthorDialog,
|
|
showModerateGroupDialog,
|
|
showSendBoopDialog,
|
|
showGalleryPage,
|
|
getFriendRequest,
|
|
handleFriendDelete,
|
|
applyPlayerModeration,
|
|
handlePlayerModerationDelete,
|
|
refreshInviteMessageTableData,
|
|
clearInviteImageUpload,
|
|
instanceStore,
|
|
useNotificationStore
|
|
});
|
|
|
|
watch(
|
|
() => userDialog.value.loading,
|
|
() => {
|
|
if (userDialog.value.visible) {
|
|
!userDialog.value.loading && loadLastActiveTab();
|
|
}
|
|
}
|
|
);
|
|
|
|
const userDialogLastMutualFriends = ref('');
|
|
const userDialogLastGroup = ref('');
|
|
const userDialogLastAvatar = ref('');
|
|
const userDialogLastWorld = ref('');
|
|
const userDialogLastFavoriteWorld = ref('');
|
|
|
|
const socialStatusDialog = ref({
|
|
visible: false,
|
|
loading: false,
|
|
status: '',
|
|
statusDescription: ''
|
|
});
|
|
const socialStatusHistoryTable = ref({
|
|
data: [],
|
|
|
|
layout: 'table'
|
|
});
|
|
|
|
const bioDialog = ref({
|
|
visible: false,
|
|
loading: false,
|
|
bio: '',
|
|
bioLinks: []
|
|
});
|
|
|
|
const pronounsDialog = ref({
|
|
visible: false,
|
|
loading: false,
|
|
pronouns: ''
|
|
});
|
|
const treeData = ref({});
|
|
|
|
/**
|
|
*
|
|
* @param user
|
|
*/
|
|
function getUserStateText(user) {
|
|
let state = '';
|
|
if (user.state === 'active') {
|
|
state = t('dialog.user.status.active');
|
|
} else if (user.state === 'offline') {
|
|
state = t('dialog.user.status.offline');
|
|
} else {
|
|
return getUserStatusText(user.status);
|
|
}
|
|
if (user.status && user.status !== 'active') {
|
|
state += ` (${getUserStatusText(user.status)})`;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param status
|
|
*/
|
|
function getUserStatusText(status) {
|
|
if (status === 'active') {
|
|
return t('dialog.user.status.active');
|
|
}
|
|
if (status === 'join me') {
|
|
return t('dialog.user.status.join_me');
|
|
}
|
|
if (status === 'ask me') {
|
|
return t('dialog.user.status.ask_me');
|
|
}
|
|
if (status === 'busy') {
|
|
return t('dialog.user.status.busy');
|
|
}
|
|
return t('dialog.user.status.offline');
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function refreshUserDialogTreeData() {
|
|
const D = userDialog.value;
|
|
if (D.id === currentUser.value.id) {
|
|
treeData.value = formatJsonVars({
|
|
...currentUser.value,
|
|
...D.ref
|
|
});
|
|
return;
|
|
}
|
|
treeData.value = formatJsonVars(D.ref);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param tabName
|
|
*/
|
|
function handleUserDialogTab(tabName) {
|
|
userDialog.value.lastActiveTab = tabName;
|
|
const userId = userDialog.value.id;
|
|
if (tabName === 'Info') {
|
|
infoTabRef.value?.onTabActivated();
|
|
} else if (tabName === 'mutual') {
|
|
if (userId === currentUser.value.id) {
|
|
userDialog.value.activeTab = 'Info';
|
|
userDialog.value.lastActiveTab = 'Info';
|
|
return;
|
|
}
|
|
if (userDialogLastMutualFriends.value !== userId) {
|
|
userDialogLastMutualFriends.value = userId;
|
|
mutualFriendsTabRef.value?.getUserMutualFriends(userId);
|
|
}
|
|
} else if (tabName === 'Groups') {
|
|
if (userDialogLastGroup.value !== userId) {
|
|
userDialogLastGroup.value = userId;
|
|
groupsTabRef.value?.getUserGroups(userId);
|
|
}
|
|
} else if (tabName === 'Avatars') {
|
|
avatarsTabRef.value?.setUserDialogAvatars(userId);
|
|
if (userDialogLastAvatar.value !== userId) {
|
|
userDialogLastAvatar.value = userId;
|
|
if (userId === currentUser.value.id) {
|
|
refreshUserDialogAvatars();
|
|
} else {
|
|
avatarsTabRef.value?.setUserDialogAvatarsRemote(userId);
|
|
}
|
|
}
|
|
} else if (tabName === 'Worlds') {
|
|
worldsTabRef.value?.setUserDialogWorlds(userId);
|
|
if (userDialogLastWorld.value !== userId) {
|
|
userDialogLastWorld.value = userId;
|
|
worldsTabRef.value?.refreshUserDialogWorlds();
|
|
}
|
|
} else if (tabName === 'favorite-worlds') {
|
|
if (userDialogLastFavoriteWorld.value !== userId) {
|
|
userDialogLastFavoriteWorld.value = userId;
|
|
favoriteWorldsTabRef.value?.getUserFavoriteWorlds(userId);
|
|
}
|
|
} else if (tabName === 'JSON') {
|
|
refreshUserDialogTreeData();
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function loadLastActiveTab() {
|
|
handleUserDialogTab(userDialog.value.lastActiveTab);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param tabName
|
|
*/
|
|
function userDialogTabClick(tabName) {
|
|
if (tabName === userDialog.value.lastActiveTab) {
|
|
if (tabName === 'JSON') {
|
|
refreshUserDialogTreeData();
|
|
}
|
|
return;
|
|
}
|
|
handleUserDialogTab(tabName);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function showPronounsDialog() {
|
|
const D = pronounsDialog.value;
|
|
D.pronouns = currentUser.value.pronouns;
|
|
D.visible = true;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function showLanguageDialog() {
|
|
const D = languageDialog.value;
|
|
D.visible = true;
|
|
}
|
|
|
|
// Register simple dialog openers as callbacks for the command composable
|
|
registerCallbacks({
|
|
showSocialStatusDialog,
|
|
showLanguageDialog,
|
|
showBioDialog,
|
|
showPronounsDialog,
|
|
showEditNoteAndMemoDialog: () => {
|
|
infoTabRef.value?.showEditNoteAndMemoDialog();
|
|
}
|
|
});
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function showSocialStatusDialog() {
|
|
const D = socialStatusDialog.value;
|
|
const { statusHistory } = currentUser.value;
|
|
const statusHistoryArray = [];
|
|
for (let i = 0; i < statusHistory.length; ++i) {
|
|
const addStatus = {
|
|
no: i + 1,
|
|
status: statusHistory[i]
|
|
};
|
|
statusHistoryArray.push(addStatus);
|
|
}
|
|
socialStatusHistoryTable.value.data = statusHistoryArray;
|
|
D.status = currentUser.value.status;
|
|
D.statusDescription = currentUser.value.statusDescription;
|
|
D.visible = true;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param badge
|
|
*/
|
|
async function toggleBadgeVisibility(badge) {
|
|
if (badge.hidden) {
|
|
badge.showcased = false;
|
|
}
|
|
const args = await miscRequest.updateBadge({
|
|
badgeId: badge.badgeId,
|
|
hidden: badge.hidden,
|
|
showcased: badge.showcased
|
|
});
|
|
handleBadgeUpdate(args);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param badge
|
|
*/
|
|
async function toggleBadgeShowcased(badge) {
|
|
if (badge.showcased) {
|
|
badge.hidden = false;
|
|
}
|
|
const args = await miscRequest.updateBadge({
|
|
badgeId: badge.badgeId,
|
|
hidden: badge.hidden,
|
|
showcased: badge.showcased
|
|
});
|
|
handleBadgeUpdate(args);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param args
|
|
*/
|
|
function handleBadgeUpdate(args) {
|
|
if (args.json) {
|
|
toast.success(t('message.badge.updated'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function showBioDialog() {
|
|
const D = bioDialog.value;
|
|
D.bio = currentUser.value.bio;
|
|
D.bioLinks = currentUser.value.bioLinks.slice();
|
|
D.visible = true;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param displayName
|
|
*/
|
|
function copyUserDisplayName(displayName) {
|
|
copyToClipboard(displayName, 'User DisplayName copied to clipboard');
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function closeInviteDialog() {
|
|
clearInviteImageUpload();
|
|
}
|
|
</script>
|