refactor dialog commands func

This commit is contained in:
pa
2026-03-09 23:09:43 +09:00
parent ca57cd6590
commit 163b5b0127
9 changed files with 762 additions and 408 deletions

View File

@@ -413,6 +413,7 @@
import DialogJsonTab from '../DialogJsonTab.vue';
import GroupDialogInfoTab from './GroupDialogInfoTab.vue';
import { useGroupDialogCommands } from './useGroupDialogCommands';
import GroupDialogMembersTab from './GroupDialogMembersTab.vue';
import GroupDialogPhotosTab from './GroupDialogPhotosTab.vue';
import GroupDialogPostsTab from './GroupDialogPostsTab.vue';
@@ -444,6 +445,28 @@
const { showFullscreenImageDialog } = useGalleryStore();
const { groupDialogCommand } = useGroupDialogCommands(groupDialog, {
t,
modalStore,
currentUser,
showGroupDialog,
leaveGroupPrompt,
setGroupVisibility,
setGroupSubscription,
showGroupMemberModerationDialog,
showInviteGroupDialog: (groupId, userId) => {
if (groupId) {
inviteGroupDialog.value.groupId = groupId;
}
if (userId) {
inviteGroupDialog.value.userId = userId;
}
inviteGroupDialog.value.visible = true;
},
showGroupPostEditDialog,
groupRequest
});
const groupDialogTabCurrentName = ref('0');
const treeData = ref({});
const membersTabRef = ref(null);
@@ -474,20 +497,7 @@
}
);
/**
*
* @param groupId
* @param userId
*/
function showInviteGroupDialog(groupId, userId) {
if (groupId) {
inviteGroupDialog.value.groupId = groupId;
}
if (userId) {
inviteGroupDialog.value.userId = userId;
}
inviteGroupDialog.value.visible = true;
}
/**
*
@@ -594,109 +604,7 @@
* @param gallery
*/
/**
*
* @param command
*/
function groupDialogCommand(command) {
const D = groupDialog.value;
if (D.visible === false) {
return;
}
switch (command) {
case 'Share':
copyToClipboard(groupDialog.value.ref.$url);
break;
case 'Create Post':
showGroupPostEditDialog(groupDialog.value.id, null);
break;
case 'Moderation Tools':
showGroupMemberModerationDialog(groupDialog.value.id);
break;
case 'Invite To Group':
showInviteGroupDialog(D.id, '');
break;
case 'Refresh':
const groupId = D.id;
showGroupDialog(groupId, { forceRefresh: true });
break;
case 'Leave Group':
leaveGroupPrompt(D.id);
break;
case 'Block Group':
blockGroup(D.id);
break;
case 'Unblock Group':
unblockGroup(D.id);
break;
case 'Visibility Everyone':
setGroupVisibility(D.id, 'visible');
break;
case 'Visibility Friends':
setGroupVisibility(D.id, 'friends');
break;
case 'Visibility Hidden':
setGroupVisibility(D.id, 'hidden');
break;
case 'Subscribe To Announcements':
setGroupSubscription(D.id, true);
break;
case 'Unsubscribe To Announcements':
setGroupSubscription(D.id, false);
break;
}
}
/**
*
* @param groupId
*/
function blockGroup(groupId) {
modalStore
.confirm({
description: t('confirm.block_group'),
title: t('confirm.title')
})
.then(({ ok }) => {
if (!ok) return;
groupRequest
.blockGroup({
groupId
})
.then((args) => {
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
showGroupDialog(args.params.groupId);
}
});
})
.catch(() => {});
}
/**
*
* @param groupId
*/
function unblockGroup(groupId) {
modalStore
.confirm({
description: t('confirm.unblock_group'),
title: t('confirm.title')
})
.then(({ ok }) => {
if (!ok) return;
groupRequest
.unblockGroup({
groupId,
userId: currentUser.value.id
})
.then((args) => {
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
showGroupDialog(args.params.groupId);
}
});
})
.catch(() => {});
}
/**
*

View File

@@ -0,0 +1,140 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ref } from 'vue';
import { useGroupDialogCommands } from '../useGroupDialogCommands';
vi.mock('../../../../shared/utils', () => ({
copyToClipboard: vi.fn()
}));
const { copyToClipboard } = await import('../../../../shared/utils');
function createGroupDialog(overrides = {}) {
return ref({
visible: true,
id: 'grp_123',
ref: {
$url: 'https://vrchat.com/home/group/grp_123'
},
...overrides
});
}
function createDeps(overrides = {}) {
return {
t: vi.fn((key) => key),
modalStore: {
confirm: vi.fn().mockResolvedValue({ ok: true })
},
currentUser: ref({ id: 'usr_current' }),
showGroupDialog: vi.fn(),
leaveGroupPrompt: vi.fn(),
setGroupVisibility: vi.fn(),
setGroupSubscription: vi.fn(),
showGroupMemberModerationDialog: vi.fn(),
showInviteGroupDialog: vi.fn(),
showGroupPostEditDialog: vi.fn(),
groupRequest: {
blockGroup: vi.fn().mockResolvedValue({
params: { groupId: 'grp_123' }
}),
unblockGroup: vi.fn().mockResolvedValue({
params: { groupId: 'grp_123' }
})
},
...overrides
};
}
describe('useGroupDialogCommands', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('returns early when dialog is not visible', () => {
const groupDialog = createGroupDialog({ visible: false });
const deps = createDeps();
const { groupDialogCommand } = useGroupDialogCommands(groupDialog, deps);
groupDialogCommand('Refresh');
expect(deps.showGroupDialog).not.toHaveBeenCalled();
});
it('Share copies group URL', () => {
const groupDialog = createGroupDialog();
const deps = createDeps();
const { groupDialogCommand } = useGroupDialogCommands(groupDialog, deps);
groupDialogCommand('Share');
expect(copyToClipboard).toHaveBeenCalledWith(
'https://vrchat.com/home/group/grp_123'
);
});
it('Invite To Group dispatches invite callback', () => {
const groupDialog = createGroupDialog();
const deps = createDeps();
const { groupDialogCommand } = useGroupDialogCommands(groupDialog, deps);
groupDialogCommand('Invite To Group');
expect(deps.showInviteGroupDialog).toHaveBeenCalledWith('grp_123', '');
});
it('Refresh calls showGroupDialog with forceRefresh', () => {
const groupDialog = createGroupDialog();
const deps = createDeps();
const { groupDialogCommand } = useGroupDialogCommands(groupDialog, deps);
groupDialogCommand('Refresh');
expect(deps.showGroupDialog).toHaveBeenCalledWith('grp_123', {
forceRefresh: true
});
});
it('Block Group confirms and calls blockGroup', async () => {
const groupDialog = createGroupDialog();
const deps = createDeps();
const { groupDialogCommand } = useGroupDialogCommands(groupDialog, deps);
groupDialogCommand('Block Group');
await vi.waitFor(() => {
expect(deps.modalStore.confirm).toHaveBeenCalled();
expect(deps.groupRequest.blockGroup).toHaveBeenCalledWith({
groupId: 'grp_123'
});
expect(deps.showGroupDialog).toHaveBeenCalledWith('grp_123');
});
});
it('Unblock Group confirms and calls unblockGroup', async () => {
const groupDialog = createGroupDialog();
const deps = createDeps();
const { groupDialogCommand } = useGroupDialogCommands(groupDialog, deps);
groupDialogCommand('Unblock Group');
await vi.waitFor(() => {
expect(deps.modalStore.confirm).toHaveBeenCalled();
expect(deps.groupRequest.unblockGroup).toHaveBeenCalledWith({
groupId: 'grp_123',
userId: 'usr_current'
});
expect(deps.showGroupDialog).toHaveBeenCalledWith('grp_123');
});
});
it('does not run confirmed action when confirmation is cancelled', async () => {
const groupDialog = createGroupDialog();
const deps = createDeps({
modalStore: {
confirm: vi.fn().mockResolvedValue({ ok: false })
}
});
const { groupDialogCommand } = useGroupDialogCommands(groupDialog, deps);
groupDialogCommand('Block Group');
await vi.waitFor(() => {
expect(deps.modalStore.confirm).toHaveBeenCalled();
});
expect(deps.groupRequest.blockGroup).not.toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,164 @@
import { copyToClipboard } from '../../../shared/utils';
/**
* Composable for GroupDialog command dispatch.
* Uses a command map pattern consistent with Avatar/World/User dialogs.
* @param {import('vue').Ref} groupDialog - reactive ref to the group dialog state
* @param {object} deps - external dependencies
* @param deps.t
* @param deps.modalStore
* @param deps.currentUser
* @param deps.showGroupDialog
* @param deps.leaveGroupPrompt
* @param deps.setGroupVisibility
* @param deps.setGroupSubscription
* @param deps.showGroupMemberModerationDialog
* @param deps.showInviteGroupDialog
* @param deps.showGroupPostEditDialog
* @param deps.groupRequest
* @returns {object} command composable API
*/
export function useGroupDialogCommands(
groupDialog,
{
t,
modalStore,
currentUser,
showGroupDialog,
leaveGroupPrompt,
setGroupVisibility,
setGroupSubscription,
showGroupMemberModerationDialog,
showInviteGroupDialog,
showGroupPostEditDialog,
groupRequest
}
) {
// --- Command map ---
// Direct commands: function
// Confirmed commands: { confirm: () => ({title, description, ...}), handler: fn }
/**
*
*/
function buildCommandMap() {
const D = () => groupDialog.value;
return {
// --- Direct commands ---
Share: () => {
copyToClipboard(D().ref.$url);
},
'Create Post': () => {
showGroupPostEditDialog(D().id, null);
},
'Moderation Tools': () => {
showGroupMemberModerationDialog(D().id);
},
'Invite To Group': () => {
showInviteGroupDialog(D().id, '');
},
Refresh: () => {
showGroupDialog(D().id, { forceRefresh: true });
},
'Leave Group': () => {
leaveGroupPrompt(D().id);
},
'Visibility Everyone': () => {
setGroupVisibility(D().id, 'visible');
},
'Visibility Friends': () => {
setGroupVisibility(D().id, 'friends');
},
'Visibility Hidden': () => {
setGroupVisibility(D().id, 'hidden');
},
'Subscribe To Announcements': () => {
setGroupSubscription(D().id, true);
},
'Unsubscribe To Announcements': () => {
setGroupSubscription(D().id, false);
},
// --- Confirmed commands ---
'Block Group': {
confirm: () => ({
title: t('confirm.title'),
description: t('confirm.block_group')
}),
handler: (id) => {
groupRequest.blockGroup({ groupId: id }).then((args) => {
if (
groupDialog.value.visible &&
groupDialog.value.id === args.params.groupId
) {
showGroupDialog(args.params.groupId);
}
});
}
},
'Unblock Group': {
confirm: () => ({
title: t('confirm.title'),
description: t('confirm.unblock_group')
}),
handler: (id) => {
groupRequest
.unblockGroup({
groupId: id,
userId: currentUser.value.id
})
.then((args) => {
if (
groupDialog.value.visible &&
groupDialog.value.id === args.params.groupId
) {
showGroupDialog(args.params.groupId);
}
});
}
}
};
}
const commandMap = buildCommandMap();
/**
* Dispatch a group dialog command.
* @param {string} command
*/
function groupDialogCommand(command) {
const D = groupDialog.value;
if (D.visible === false) {
return;
}
const entry = commandMap[command];
if (!entry) {
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 {
groupDialogCommand
};
}