mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-20 23:33:50 +02:00
706 lines
24 KiB
JavaScript
706 lines
24 KiB
JavaScript
import { ref } from 'vue';
|
|
|
|
import {
|
|
favoriteRequest,
|
|
friendRequest,
|
|
miscRequest,
|
|
notificationRequest,
|
|
playerModerationRequest,
|
|
queryRequest
|
|
} from '../../../api';
|
|
import { copyToClipboard, parseLocation } from '../../../shared/utils';
|
|
import { database } from '../../../service/database';
|
|
|
|
/**
|
|
* Composable for UserDialog command dispatch.
|
|
* Uses a command map pattern instead of if-else/switch-case chains.
|
|
* @param {import('vue').Ref} userDialog - reactive ref to the user dialog state
|
|
* @param {object} deps - external dependencies
|
|
* @param deps.t
|
|
* @param deps.toast
|
|
* @param deps.modalStore
|
|
* @param deps.currentUser
|
|
* @param deps.cachedUsers
|
|
* @param deps.friendLogTable
|
|
* @param deps.lastLocation
|
|
* @param deps.lastLocationDestination
|
|
* @param deps.inviteGroupDialog
|
|
* @param deps.showUserDialog
|
|
* @param deps.showFavoriteDialog
|
|
* @param deps.showAvatarDialog
|
|
* @param deps.showAvatarAuthorDialog
|
|
* @param deps.showModerateGroupDialog
|
|
* @param deps.showSendBoopDialog
|
|
* @param deps.showGalleryPage
|
|
* @param deps.getFriendRequest
|
|
* @param deps.handleFriendDelete
|
|
* @param deps.applyPlayerModeration
|
|
* @param deps.handlePlayerModerationDelete
|
|
* @param deps.refreshInviteMessageTableData
|
|
* @param deps.clearInviteImageUpload
|
|
* @param deps.instanceStore
|
|
* @param deps.useNotificationStore
|
|
* @returns {object} command composable API
|
|
*/
|
|
export function 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
|
|
}
|
|
) {
|
|
// --- Invite dialog state ---
|
|
const sendInviteDialogVisible = ref(false);
|
|
const sendInviteDialog = ref({
|
|
messageSlot: {},
|
|
userId: '',
|
|
params: {}
|
|
});
|
|
const sendInviteRequestDialogVisible = ref(false);
|
|
|
|
// --- Internal helpers ---
|
|
|
|
/**
|
|
* @param {object} args
|
|
*/
|
|
function handleSendFriendRequest(args) {
|
|
const ref = cachedUsers.get(args.params.userId);
|
|
if (typeof ref === 'undefined') {
|
|
return;
|
|
}
|
|
const friendLogHistory = {
|
|
created_at: new Date().toJSON(),
|
|
type: 'FriendRequest',
|
|
userId: ref.id,
|
|
displayName: ref.displayName
|
|
};
|
|
friendLogTable.value.data.push(friendLogHistory);
|
|
database.addFriendLogHistory(friendLogHistory);
|
|
|
|
const D = userDialog.value;
|
|
if (D.visible === false || D.id !== args.params.userId) {
|
|
return;
|
|
}
|
|
if (args.json.success) {
|
|
D.isFriend = true;
|
|
} else {
|
|
D.outgoingRequest = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {object} args
|
|
*/
|
|
function handleCancelFriendRequest(args) {
|
|
const ref = cachedUsers.get(args.params.userId);
|
|
if (typeof ref === 'undefined') {
|
|
return;
|
|
}
|
|
const friendLogHistory = {
|
|
created_at: new Date().toJSON(),
|
|
type: 'CancelFriendRequest',
|
|
userId: ref.id,
|
|
displayName: ref.displayName
|
|
};
|
|
friendLogTable.value.data.push(friendLogHistory);
|
|
database.addFriendLogHistory(friendLogHistory);
|
|
const D = userDialog.value;
|
|
if (D.visible === false || D.id !== args.params.userId) {
|
|
return;
|
|
}
|
|
D.outgoingRequest = false;
|
|
}
|
|
|
|
/**
|
|
* @param {object} args
|
|
*/
|
|
function handleSendPlayerModeration(args) {
|
|
const ref = applyPlayerModeration(args.json);
|
|
const D = userDialog.value;
|
|
if (
|
|
D.visible === false ||
|
|
(ref.targetUserId !== D.id &&
|
|
ref.sourceUserId !== currentUser.value.id)
|
|
) {
|
|
return;
|
|
}
|
|
if (ref.type === 'block') {
|
|
D.isBlock = true;
|
|
} else if (ref.type === 'mute') {
|
|
D.isMute = true;
|
|
} else if (ref.type === 'interactOff') {
|
|
D.isInteractOff = true;
|
|
} else if (ref.type === 'muteChat') {
|
|
D.isMuteChat = true;
|
|
}
|
|
toast.success(t('message.user.moderated'));
|
|
}
|
|
|
|
/**
|
|
* @param {string} userId
|
|
* @param {number} type
|
|
*/
|
|
function setPlayerModeration(userId, type) {
|
|
const D = userDialog.value;
|
|
AppApi.SetVRChatUserModeration(currentUser.value.id, userId, type).then(
|
|
(result) => {
|
|
if (result) {
|
|
if (type === 4) {
|
|
D.isShowAvatar = false;
|
|
D.isHideAvatar = true;
|
|
} else if (type === 5) {
|
|
D.isShowAvatar = true;
|
|
D.isHideAvatar = false;
|
|
} else {
|
|
D.isShowAvatar = false;
|
|
D.isHideAvatar = false;
|
|
}
|
|
} else {
|
|
toast.error(t('message.avatar.change_moderation_failed'));
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param {object} params
|
|
* @param {string} userId
|
|
*/
|
|
function showSendInviteDialogFn(params, userId) {
|
|
sendInviteDialog.value = {
|
|
params,
|
|
userId,
|
|
messageSlot: {}
|
|
};
|
|
refreshInviteMessageTableData('message');
|
|
clearInviteImageUpload();
|
|
sendInviteDialogVisible.value = true;
|
|
}
|
|
|
|
/**
|
|
* @param {object} params
|
|
* @param {string} userId
|
|
*/
|
|
function showSendInviteRequestDialogFn(params, userId) {
|
|
sendInviteDialog.value = {
|
|
params,
|
|
userId,
|
|
messageSlot: {}
|
|
};
|
|
refreshInviteMessageTableData('request');
|
|
clearInviteImageUpload();
|
|
sendInviteRequestDialogVisible.value = true;
|
|
}
|
|
|
|
// --- Command map ---
|
|
// Direct commands: function
|
|
// Confirmed commands: { confirm: () => ({title, description, ...}), handler: fn }
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function buildCommandMap() {
|
|
const D = () => userDialog.value;
|
|
|
|
return {
|
|
// --- Direct commands ---
|
|
Refresh: () => {
|
|
const userId = D().id;
|
|
D().id = '';
|
|
showUserDialog(userId);
|
|
},
|
|
Share: () => {
|
|
copyToClipboard(
|
|
`https://vrchat.com/home/user/${D().id}`,
|
|
'User URL copied to clipboard'
|
|
);
|
|
},
|
|
'Add Favorite': () => {
|
|
showFavoriteDialog('friend', D().id);
|
|
},
|
|
'Edit Social Status': 'showSocialStatusDialog',
|
|
'Edit Language': 'showLanguageDialog',
|
|
'Edit Bio': 'showBioDialog',
|
|
'Edit Pronouns': 'showPronounsDialog',
|
|
'Request Invite': () => {
|
|
notificationRequest
|
|
.sendRequestInvite(
|
|
{
|
|
platform: 'standalonewindows'
|
|
},
|
|
D().id
|
|
)
|
|
.then((args) => {
|
|
toast('Request invite sent');
|
|
return args;
|
|
});
|
|
},
|
|
'Invite Message': () => {
|
|
const L = parseLocation(lastLocation.value.location);
|
|
queryRequest
|
|
.fetch('world', {
|
|
worldId: L.worldId
|
|
})
|
|
.then((args) => {
|
|
showSendInviteDialogFn(
|
|
{
|
|
instanceId: lastLocation.value.location,
|
|
worldId: lastLocation.value.location,
|
|
worldName: args.ref.name
|
|
},
|
|
D().id
|
|
);
|
|
});
|
|
},
|
|
'Request Invite Message': () => {
|
|
showSendInviteRequestDialogFn(
|
|
{
|
|
platform: 'standalonewindows'
|
|
},
|
|
D().id
|
|
);
|
|
},
|
|
Invite: () => {
|
|
let currentLocation = lastLocation.value.location;
|
|
if (lastLocation.value.location === 'traveling') {
|
|
currentLocation = lastLocationDestination.value;
|
|
}
|
|
const L = parseLocation(currentLocation);
|
|
queryRequest
|
|
.fetch('world', {
|
|
worldId: L.worldId
|
|
})
|
|
.then((args) => {
|
|
notificationRequest
|
|
.sendInvite(
|
|
{
|
|
instanceId: L.tag,
|
|
worldId: L.tag,
|
|
worldName: args.ref.name
|
|
},
|
|
D().id
|
|
)
|
|
.then((_args) => {
|
|
toast(t('message.invite.sent'));
|
|
return _args;
|
|
});
|
|
});
|
|
},
|
|
'Show Avatar Author': () => {
|
|
const { currentAvatarImageUrl } = D().ref;
|
|
showAvatarAuthorDialog(
|
|
D().id,
|
|
D().$avatarInfo.ownerId,
|
|
currentAvatarImageUrl
|
|
);
|
|
},
|
|
'Show Fallback Avatar Details': () => {
|
|
const { fallbackAvatar } = D().ref;
|
|
if (fallbackAvatar) {
|
|
showAvatarDialog(fallbackAvatar);
|
|
} else {
|
|
toast.error('No fallback avatar set');
|
|
}
|
|
},
|
|
'Previous Instances': () => {
|
|
instanceStore.showPreviousInstancesListDialog('user', D().ref);
|
|
},
|
|
'Manage Gallery': () => {
|
|
userDialog.value.visible = false;
|
|
showGalleryPage();
|
|
},
|
|
'Invite To Group': () => {
|
|
inviteGroupDialog.value.groupId = '';
|
|
inviteGroupDialog.value.userId = D().id;
|
|
inviteGroupDialog.value.visible = true;
|
|
},
|
|
'Send Boop': () => {
|
|
showSendBoopDialog(D().id);
|
|
},
|
|
'Group Moderation': () => {
|
|
showModerateGroupDialog(D().id);
|
|
},
|
|
'Hide Avatar': () => {
|
|
if (D().isHideAvatar) {
|
|
setPlayerModeration(D().id, 0);
|
|
} else {
|
|
setPlayerModeration(D().id, 4);
|
|
}
|
|
},
|
|
'Show Avatar': () => {
|
|
if (D().isShowAvatar) {
|
|
setPlayerModeration(D().id, 0);
|
|
} else {
|
|
setPlayerModeration(D().id, 5);
|
|
}
|
|
},
|
|
'Edit Note Memo': 'showEditNoteAndMemoDialog',
|
|
|
|
// --- Confirmed commands ---
|
|
'Delete Favorite': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t('dialog.user.actions.delete_favorite')
|
|
})
|
|
}),
|
|
handler: (userId) => {
|
|
favoriteRequest.deleteFavorite({
|
|
objectId: userId
|
|
});
|
|
}
|
|
},
|
|
'Accept Friend Request': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t('dialog.user.actions.accept_friend_request')
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const key = getFriendRequest(userId);
|
|
if (key === '') {
|
|
const args = await friendRequest.sendFriendRequest({
|
|
userId
|
|
});
|
|
handleSendFriendRequest(args);
|
|
} else {
|
|
notificationRequest
|
|
.acceptFriendRequestNotification({
|
|
notificationId: key
|
|
})
|
|
.then((args) => {
|
|
useNotificationStore().handleNotificationAccept(
|
|
args
|
|
);
|
|
})
|
|
.catch((err) => {
|
|
if (
|
|
err &&
|
|
err.message &&
|
|
err.message.includes('404')
|
|
) {
|
|
useNotificationStore().handleNotificationHide(
|
|
key
|
|
);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
'Decline Friend Request': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t('dialog.user.actions.decline_friend_request')
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const key = getFriendRequest(userId);
|
|
if (key === '') {
|
|
const args = await friendRequest.cancelFriendRequest({
|
|
userId
|
|
});
|
|
handleCancelFriendRequest(args);
|
|
} else {
|
|
notificationRequest
|
|
.hideNotification({
|
|
notificationId: key
|
|
})
|
|
.then(() => {
|
|
useNotificationStore().handleNotificationHide(
|
|
key
|
|
);
|
|
});
|
|
}
|
|
}
|
|
},
|
|
'Cancel Friend Request': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t('dialog.user.actions.cancel_friend_request')
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const args = await friendRequest.cancelFriendRequest({
|
|
userId
|
|
});
|
|
handleCancelFriendRequest(args);
|
|
}
|
|
},
|
|
'Send Friend Request': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t('dialog.user.actions.send_friend_request')
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const args = await friendRequest.sendFriendRequest({
|
|
userId
|
|
});
|
|
handleSendFriendRequest(args);
|
|
}
|
|
},
|
|
'Moderation Unblock': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t('dialog.user.actions.moderation_unblock')
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const args =
|
|
await playerModerationRequest.deletePlayerModeration({
|
|
moderated: userId,
|
|
type: 'block'
|
|
});
|
|
handlePlayerModerationDelete(args);
|
|
}
|
|
},
|
|
'Moderation Block': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t('dialog.user.actions.moderation_block')
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const args =
|
|
await playerModerationRequest.sendPlayerModeration({
|
|
moderated: userId,
|
|
type: 'block'
|
|
});
|
|
handleSendPlayerModeration(args);
|
|
}
|
|
},
|
|
'Moderation Unmute': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t('dialog.user.actions.moderation_unmute')
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const args =
|
|
await playerModerationRequest.deletePlayerModeration({
|
|
moderated: userId,
|
|
type: 'mute'
|
|
});
|
|
handlePlayerModerationDelete(args);
|
|
}
|
|
},
|
|
'Moderation Mute': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t('dialog.user.actions.moderation_mute')
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const args =
|
|
await playerModerationRequest.sendPlayerModeration({
|
|
moderated: userId,
|
|
type: 'mute'
|
|
});
|
|
handleSendPlayerModeration(args);
|
|
}
|
|
},
|
|
'Moderation Enable Avatar Interaction': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t(
|
|
'dialog.user.actions.moderation_enable_avatar_interaction'
|
|
)
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const args =
|
|
await playerModerationRequest.deletePlayerModeration({
|
|
moderated: userId,
|
|
type: 'interactOff'
|
|
});
|
|
handlePlayerModerationDelete(args);
|
|
}
|
|
},
|
|
'Moderation Disable Avatar Interaction': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t(
|
|
'dialog.user.actions.moderation_disable_avatar_interaction'
|
|
)
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const args =
|
|
await playerModerationRequest.sendPlayerModeration({
|
|
moderated: userId,
|
|
type: 'interactOff'
|
|
});
|
|
handleSendPlayerModeration(args);
|
|
}
|
|
},
|
|
'Moderation Enable Chatbox': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t(
|
|
'dialog.user.actions.moderation_enable_chatbox'
|
|
)
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const args =
|
|
await playerModerationRequest.deletePlayerModeration({
|
|
moderated: userId,
|
|
type: 'muteChat'
|
|
});
|
|
handlePlayerModerationDelete(args);
|
|
}
|
|
},
|
|
'Moderation Disable Chatbox': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t(
|
|
'dialog.user.actions.moderation_disable_chatbox'
|
|
)
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const args =
|
|
await playerModerationRequest.sendPlayerModeration({
|
|
moderated: userId,
|
|
type: 'muteChat'
|
|
});
|
|
handleSendPlayerModeration(args);
|
|
}
|
|
},
|
|
'Report Hacking': {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t('dialog.user.actions.report_hacking')
|
|
})
|
|
}),
|
|
handler: (userId) => {
|
|
miscRequest.reportUser({
|
|
userId,
|
|
contentType: 'user',
|
|
reason: 'behavior-hacking',
|
|
type: 'report'
|
|
});
|
|
}
|
|
},
|
|
Unfriend: {
|
|
confirm: () => ({
|
|
title: t('confirm.title'),
|
|
description: t('confirm.command_question', {
|
|
command: t('dialog.user.actions.unfriend')
|
|
})
|
|
}),
|
|
handler: async (userId) => {
|
|
const args = await friendRequest.deleteFriend(
|
|
{
|
|
userId
|
|
},
|
|
t('dialog.user.actions.unfriend_success_msg')
|
|
);
|
|
handleFriendDelete(args);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
const commandMap = buildCommandMap();
|
|
|
|
// Callbacks for string-type commands (delegated to component)
|
|
let componentCallbacks = {};
|
|
|
|
/**
|
|
* Register component-level callbacks for string-type commands.
|
|
* These are simple dialog openers that stay in the component.
|
|
* @param {object} callbacks
|
|
*/
|
|
function registerCallbacks(callbacks) {
|
|
componentCallbacks = callbacks;
|
|
}
|
|
|
|
/**
|
|
* Dispatch a user dialog command.
|
|
* @param {string} command
|
|
*/
|
|
function userDialogCommand(command) {
|
|
const D = userDialog.value;
|
|
if (D.visible === false) {
|
|
return;
|
|
}
|
|
|
|
const entry = commandMap[command];
|
|
|
|
if (!entry) {
|
|
return;
|
|
}
|
|
|
|
// String entry => delegate to component callback
|
|
if (typeof entry === 'string') {
|
|
const cb = componentCallbacks[entry];
|
|
if (cb) {
|
|
cb();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Direct function
|
|
if (typeof entry === 'function') {
|
|
entry();
|
|
return;
|
|
}
|
|
|
|
// Confirmed command
|
|
if (entry.confirm) {
|
|
modalStore
|
|
.confirm(entry.confirm())
|
|
.then(({ ok }) => {
|
|
if (ok) {
|
|
entry.handler(D.id);
|
|
}
|
|
})
|
|
.catch(() => {});
|
|
}
|
|
}
|
|
|
|
return {
|
|
sendInviteDialogVisible,
|
|
sendInviteDialog,
|
|
sendInviteRequestDialogVisible,
|
|
userDialogCommand,
|
|
registerCallbacks
|
|
};
|
|
}
|