From ca57cd659007841953dd39ab54883bb1a9c03fc3 Mon Sep 17 00:00:00 2001 From: pa Date: Mon, 9 Mar 2026 22:08:18 +0900 Subject: [PATCH] refactor query requests to use queryRequest module --- src/api/__tests__/queryRequest.test.js | 50 +++++++++- src/api/queryRequest.js | 52 ++++++++++- src/queries/__tests__/policies.test.js | 91 +++++++++++++------ src/queries/index.js | 1 - src/queries/keys.js | 45 +++++++++ src/queries/policies.js | 68 ++++++++++++-- src/shared/utils/common.js | 4 +- src/stores/avatar.js | 4 +- src/stores/user.js | 4 +- src/stores/world.js | 6 +- .../Settings/components/Tabs/AdvancedTab.vue | 11 +-- .../Tools/dialogs/GroupCalendarDialog.vue | 45 ++++++++- 12 files changed, 317 insertions(+), 64 deletions(-) diff --git a/src/api/__tests__/queryRequest.test.js b/src/api/__tests__/queryRequest.test.js index 498fb858..c26aa5f2 100644 --- a/src/api/__tests__/queryRequest.test.js +++ b/src/api/__tests__/queryRequest.test.js @@ -10,20 +10,35 @@ vi.mock('../../queries', () => ({ user: { staleTime: 20000, gcTime: 90000, retry: 1, refetchOnWindowFocus: false }, worldCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, groupCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, + groupCalendarCollection: { staleTime: 120000, gcTime: 600000, retry: 1, refetchOnWindowFocus: false }, + groupFollowingCalendarCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, + groupFeaturedCalendarCollection: { staleTime: 300000, gcTime: 900000, retry: 1, refetchOnWindowFocus: false }, + groupCalendarEvent: { staleTime: 120000, gcTime: 600000, retry: 1, refetchOnWindowFocus: false }, avatar: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, + avatarCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, + avatarGallery: { staleTime: 30000, gcTime: 120000, retry: 1, refetchOnWindowFocus: false }, world: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, group: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, friendList: { staleTime: 20000, gcTime: 90000, retry: 1, refetchOnWindowFocus: false }, favoriteCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, galleryCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, inventoryCollection: { staleTime: 20000, gcTime: 120000, retry: 1, refetchOnWindowFocus: false }, + inventoryObject: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, + fileAnalysis: { staleTime: 120000, gcTime: 600000, retry: 1, refetchOnWindowFocus: false }, + worldPersistData: { staleTime: 120000, gcTime: 600000, retry: 1, refetchOnWindowFocus: false }, + mutualCounts: { staleTime: 120000, gcTime: 600000, retry: 1, refetchOnWindowFocus: false }, + visits: { staleTime: 300000, gcTime: 900000, retry: 1, refetchOnWindowFocus: false }, fileObject: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false } }, fetchWithEntityPolicy: (...args) => mockFetchWithEntityPolicy(...args), queryKeys: { user: (userId) => ['user', userId], + avatars: (params) => ['avatar', 'list', params], worldsByUser: (params) => ['worlds', 'user', params.userId, params], groupCalendar: (groupId) => ['group', groupId, 'calendar'], + groupCalendars: (params) => ['group', 'calendar', params], + followingGroupCalendars: (params) => ['group', 'calendar', 'following', params], + featuredGroupCalendars: (params) => ['group', 'calendar', 'featured', params], avatar: (avatarId) => ['avatar', avatarId], world: (worldId) => ['world', worldId], group: (groupId, includeRoles) => ['group', groupId, Boolean(includeRoles)], @@ -32,6 +47,7 @@ vi.mock('../../queries', () => ({ groupMembers: (params) => ['group', params.groupId, 'members', params], groupGallery: (params) => ['group', params.groupId, 'gallery', params.galleryId, params], groupCalendarEvent: (params) => ['group', params.groupId, 'calendarEvent', params.eventId], + avatarGallery: (avatarId) => ['avatar', avatarId, 'gallery'], friends: (params) => ['friends', params], favoriteLimits: () => ['favorite', 'limits'], favorites: (params) => ['favorite', 'items', params], @@ -41,15 +57,22 @@ vi.mock('../../queries', () => ({ galleryFiles: (params) => ['gallery', 'files', params], prints: (params) => ['gallery', 'prints', params], print: (printId) => ['gallery', 'print', printId], + inventoryItem: (inventoryId) => ['inventory', 'item', inventoryId], userInventoryItem: (params) => ['inventory', 'item', params.userId, params.inventoryId], inventoryItems: (params) => ['inventory', 'items', params], + inventoryTemplate: (inventoryTemplateId) => ['inventory', 'template', inventoryTemplateId], + fileAnalysis: (params) => ['analysis', params.fileId, Number(params.version), String(params.variant || '')], + worldPersistData: (worldId) => ['world', worldId, 'persistData'], + mutualCounts: (userId) => ['user', userId, 'mutualCounts'], + visits: () => ['visits'], file: (fileId) => ['file', fileId] } })); vi.mock('../user', () => ({ default: { - getUser: (...args) => mockGetUser(...args) + getUser: (...args) => mockGetUser(...args), + getMutualCounts: vi.fn() } })); @@ -68,11 +91,16 @@ vi.mock('../group', () => ({ getGroupMember: vi.fn(), getGroupMembers: vi.fn(), getGroupGallery: vi.fn(), - getGroupCalendarEvent: vi.fn() + getGroupCalendarEvent: vi.fn(), + getGroupCalendars: vi.fn(), + getFollowingGroupCalendars: vi.fn(), + getFeaturedGroupCalendars: vi.fn() } })); -vi.mock('../avatar', () => ({ default: { getAvatar: vi.fn() } })); +vi.mock('../avatar', () => ({ + default: { getAvatar: vi.fn(), getAvatarGallery: vi.fn(), getAvatars: vi.fn() } +})); vi.mock('../friend', () => ({ default: { getFriends: vi.fn() } })); vi.mock('../favorite', () => ({ default: { @@ -88,9 +116,21 @@ vi.mock('../vrcPlusImage', () => ({ default: { getPrints: vi.fn(), getPrint: vi.fn() } })); vi.mock('../inventory', () => ({ - default: { getUserInventoryItem: vi.fn(), getInventoryItems: vi.fn() } + default: { + getUserInventoryItem: vi.fn(), + getInventoryItem: vi.fn(), + getInventoryItems: vi.fn(), + getInventoryTemplate: vi.fn() + } +})); +vi.mock('../misc', () => ({ + default: { + getFile: vi.fn(), + getFileAnalysis: vi.fn(), + getVisits: vi.fn(), + hasWorldPersistData: vi.fn() + } })); -vi.mock('../misc', () => ({ default: { getFile: vi.fn() } })); import queryRequest from '../queryRequest'; diff --git a/src/api/queryRequest.js b/src/api/queryRequest.js index d197945f..e1fefab5 100644 --- a/src/api/queryRequest.js +++ b/src/api/queryRequest.js @@ -67,11 +67,31 @@ const registry = Object.freeze({ policy: entityQueryPolicies.groupCollection, queryFn: (params) => groupRequest.getGroupCalendar(params.groupId) }, + groupCalendars: { + key: (params) => queryKeys.groupCalendars(params), + policy: entityQueryPolicies.groupCalendarCollection, + queryFn: (params) => groupRequest.getGroupCalendars(params) + }, + followingGroupCalendars: { + key: (params) => queryKeys.followingGroupCalendars(params), + policy: entityQueryPolicies.groupFollowingCalendarCollection, + queryFn: (params) => groupRequest.getFollowingGroupCalendars(params) + }, + featuredGroupCalendars: { + key: (params) => queryKeys.featuredGroupCalendars(params), + policy: entityQueryPolicies.groupFeaturedCalendarCollection, + queryFn: (params) => groupRequest.getFeaturedGroupCalendars(params) + }, groupCalendarEvent: { key: (params) => queryKeys.groupCalendarEvent(params), - policy: entityQueryPolicies.groupCollection, + policy: entityQueryPolicies.groupCalendarEvent, queryFn: (params) => groupRequest.getGroupCalendarEvent(params) }, + avatarGallery: { + key: (params) => queryKeys.avatarGallery(params.avatarId), + policy: entityQueryPolicies.avatarGallery, + queryFn: (params) => avatarRequest.getAvatarGallery(params.avatarId) + }, friends: { key: (params) => queryKeys.friends(params), policy: entityQueryPolicies.friendList, @@ -122,11 +142,41 @@ const registry = Object.freeze({ policy: entityQueryPolicies.inventoryCollection, queryFn: (params) => inventoryRequest.getUserInventoryItem(params) }, + inventoryItem: { + key: (params) => queryKeys.inventoryItem(params.inventoryId), + policy: entityQueryPolicies.inventoryObject, + queryFn: (params) => inventoryRequest.getInventoryItem(params) + }, inventoryItems: { key: (params) => queryKeys.inventoryItems(params), policy: entityQueryPolicies.inventoryCollection, queryFn: (params) => inventoryRequest.getInventoryItems(params) }, + inventoryTemplate: { + key: (params) => queryKeys.inventoryTemplate(params.inventoryTemplateId), + policy: entityQueryPolicies.inventoryObject, + queryFn: (params) => inventoryRequest.getInventoryTemplate(params) + }, + fileAnalysis: { + key: (params) => queryKeys.fileAnalysis(params), + policy: entityQueryPolicies.fileAnalysis, + queryFn: (params) => miscRequest.getFileAnalysis(params) + }, + worldPersistData: { + key: (params) => queryKeys.worldPersistData(params.worldId), + policy: entityQueryPolicies.worldPersistData, + queryFn: (params) => miscRequest.hasWorldPersistData(params) + }, + mutualCounts: { + key: (params) => queryKeys.mutualCounts(params.userId), + policy: entityQueryPolicies.mutualCounts, + queryFn: (params) => userRequest.getMutualCounts(params) + }, + visits: { + key: () => queryKeys.visits(), + policy: entityQueryPolicies.visits, + queryFn: () => miscRequest.getVisits() + }, file: { key: (params) => queryKeys.file(params.fileId), policy: entityQueryPolicies.fileObject, diff --git a/src/queries/__tests__/policies.test.js b/src/queries/__tests__/policies.test.js index bd5fc6c9..d5703783 100644 --- a/src/queries/__tests__/policies.test.js +++ b/src/queries/__tests__/policies.test.js @@ -2,7 +2,6 @@ import { describe, expect, test } from 'vitest'; import { entityQueryPolicies, - getEntityQueryPolicy, toQueryOptions } from '../policies'; @@ -42,6 +41,32 @@ describe('query policy configuration', () => { retry: 1, refetchOnWindowFocus: false }); + expect(entityQueryPolicies.groupCalendarCollection).toMatchObject({ + staleTime: 120000, + gcTime: 600000, + retry: 1, + refetchOnWindowFocus: false + }); + expect( + entityQueryPolicies.groupFollowingCalendarCollection + ).toMatchObject({ + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }); + expect(entityQueryPolicies.groupFeaturedCalendarCollection).toMatchObject({ + staleTime: 300000, + gcTime: 900000, + retry: 1, + refetchOnWindowFocus: false + }); + expect(entityQueryPolicies.groupCalendarEvent).toMatchObject({ + staleTime: 120000, + gcTime: 600000, + retry: 1, + refetchOnWindowFocus: false + }); expect(entityQueryPolicies.worldCollection).toMatchObject({ staleTime: 60000, @@ -77,6 +102,42 @@ describe('query policy configuration', () => { retry: 1, refetchOnWindowFocus: false }); + expect(entityQueryPolicies.inventoryObject).toMatchObject({ + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }); + expect(entityQueryPolicies.avatarGallery).toMatchObject({ + staleTime: 30000, + gcTime: 120000, + retry: 1, + refetchOnWindowFocus: false + }); + expect(entityQueryPolicies.fileAnalysis).toMatchObject({ + staleTime: 120000, + gcTime: 600000, + retry: 1, + refetchOnWindowFocus: false + }); + expect(entityQueryPolicies.worldPersistData).toMatchObject({ + staleTime: 120000, + gcTime: 600000, + retry: 1, + refetchOnWindowFocus: false + }); + expect(entityQueryPolicies.mutualCounts).toMatchObject({ + staleTime: 120000, + gcTime: 600000, + retry: 1, + refetchOnWindowFocus: false + }); + expect(entityQueryPolicies.visits).toMatchObject({ + staleTime: 300000, + gcTime: 900000, + retry: 1, + refetchOnWindowFocus: false + }); expect(entityQueryPolicies.fileObject).toMatchObject({ staleTime: 60000, @@ -86,34 +147,6 @@ describe('query policy configuration', () => { }); }); - test('exposes entity policy lookup', () => { - expect(getEntityQueryPolicy('user')).toBe(entityQueryPolicies.user); - expect(getEntityQueryPolicy('avatar')).toBe(entityQueryPolicies.avatar); - expect(getEntityQueryPolicy('world')).toBe(entityQueryPolicies.world); - expect(getEntityQueryPolicy('group')).toBe(entityQueryPolicies.group); - expect(getEntityQueryPolicy('groupCollection')).toBe( - entityQueryPolicies.groupCollection - ); - expect(getEntityQueryPolicy('worldCollection')).toBe( - entityQueryPolicies.worldCollection - ); - expect(getEntityQueryPolicy('friendList')).toBe( - entityQueryPolicies.friendList - ); - expect(getEntityQueryPolicy('favoriteCollection')).toBe( - entityQueryPolicies.favoriteCollection - ); - expect(getEntityQueryPolicy('galleryCollection')).toBe( - entityQueryPolicies.galleryCollection - ); - expect(getEntityQueryPolicy('inventoryCollection')).toBe( - entityQueryPolicies.inventoryCollection - ); - expect(getEntityQueryPolicy('fileObject')).toBe( - entityQueryPolicies.fileObject - ); - }); - test('normalizes policy values to query options', () => { const options = toQueryOptions(entityQueryPolicies.group); diff --git a/src/queries/index.js b/src/queries/index.js index a9cc1ea7..81999954 100644 --- a/src/queries/index.js +++ b/src/queries/index.js @@ -2,7 +2,6 @@ export { queryClient } from './client'; export { queryKeys } from './keys'; export { entityQueryPolicies, - getEntityQueryPolicy, toQueryOptions } from './policies'; export { diff --git a/src/queries/keys.js b/src/queries/keys.js index 61ad5999..c34e396c 100644 --- a/src/queries/keys.js +++ b/src/queries/keys.js @@ -50,12 +50,42 @@ export const queryKeys = Object.freeze({ } ], groupCalendar: (groupId) => ['group', groupId, 'calendar'], + groupCalendars: ({ n = 100, offset = 0, date = '' } = {}) => [ + 'group', + 'calendar', + { + n: Number(n), + offset: Number(offset), + date: String(date || '') + } + ], + followingGroupCalendars: ({ n = 100, offset = 0, date = '' } = {}) => [ + 'group', + 'calendar', + 'following', + { + n: Number(n), + offset: Number(offset), + date: String(date || '') + } + ], + featuredGroupCalendars: ({ n = 100, offset = 0, date = '' } = {}) => [ + 'group', + 'calendar', + 'featured', + { + n: Number(n), + offset: Number(offset), + date: String(date || '') + } + ], groupCalendarEvent: ({ groupId, eventId } = {}) => [ 'group', groupId, 'calendarEvent', eventId ], + avatarGallery: (avatarId) => ['avatar', avatarId, 'gallery'], worldsByUser: ({ userId, n = 50, @@ -176,5 +206,20 @@ export const queryKeys = Object.freeze({ userId, inventoryId ], + inventoryItem: (inventoryId) => ['inventory', 'item', inventoryId], + inventoryTemplate: (inventoryTemplateId) => [ + 'inventory', + 'template', + inventoryTemplateId + ], + fileAnalysis: ({ fileId, version, variant } = {}) => [ + 'analysis', + fileId, + Number(version), + String(variant || '') + ], + worldPersistData: (worldId) => ['world', worldId, 'persistData'], + mutualCounts: (userId) => ['user', userId, 'mutualCounts'], + visits: () => ['visits'], file: (fileId) => ['file', fileId] }); diff --git a/src/queries/policies.js b/src/queries/policies.js index 6cb65c7d..5f052ae6 100644 --- a/src/queries/policies.js +++ b/src/queries/policies.js @@ -31,6 +31,30 @@ export const entityQueryPolicies = Object.freeze({ retry: 1, refetchOnWindowFocus: false }), + groupCalendarCollection: Object.freeze({ + staleTime: 120 * SECOND, + gcTime: 600 * SECOND, + retry: 1, + refetchOnWindowFocus: false + }), + groupFollowingCalendarCollection: Object.freeze({ + staleTime: 60 * SECOND, + gcTime: 300 * SECOND, + retry: 1, + refetchOnWindowFocus: false + }), + groupFeaturedCalendarCollection: Object.freeze({ + staleTime: 300 * SECOND, + gcTime: 900 * SECOND, + retry: 1, + refetchOnWindowFocus: false + }), + groupCalendarEvent: Object.freeze({ + staleTime: 120 * SECOND, + gcTime: 600 * SECOND, + retry: 1, + refetchOnWindowFocus: false + }), worldCollection: Object.freeze({ staleTime: 60 * SECOND, gcTime: 300 * SECOND, @@ -61,6 +85,42 @@ export const entityQueryPolicies = Object.freeze({ retry: 1, refetchOnWindowFocus: false }), + inventoryObject: Object.freeze({ + staleTime: 60 * SECOND, + gcTime: 300 * SECOND, + retry: 1, + refetchOnWindowFocus: false + }), + avatarGallery: Object.freeze({ + staleTime: 30 * SECOND, + gcTime: 120 * SECOND, + retry: 1, + refetchOnWindowFocus: false + }), + fileAnalysis: Object.freeze({ + staleTime: 120 * SECOND, + gcTime: 600 * SECOND, + retry: 1, + refetchOnWindowFocus: false + }), + worldPersistData: Object.freeze({ + staleTime: 120 * SECOND, + gcTime: 600 * SECOND, + retry: 1, + refetchOnWindowFocus: false + }), + mutualCounts: Object.freeze({ + staleTime: 120 * SECOND, + gcTime: 600 * SECOND, + retry: 1, + refetchOnWindowFocus: false + }), + visits: Object.freeze({ + staleTime: 300 * SECOND, + gcTime: 900 * SECOND, + retry: 1, + refetchOnWindowFocus: false + }), fileObject: Object.freeze({ staleTime: 60 * SECOND, gcTime: 300 * SECOND, @@ -69,14 +129,6 @@ export const entityQueryPolicies = Object.freeze({ }) }); -/** - * @param {'user'|'avatar'|'world'|'group'|'groupCollection'|'worldCollection'|'friendList'|'favoriteCollection'|'galleryCollection'|'inventoryCollection'|'fileObject'} entity - * @returns {{staleTime: number, gcTime: number, retry: number, refetchOnWindowFocus: boolean}} - */ -export function getEntityQueryPolicy(entity) { - return entityQueryPolicies[entity]; -} - /** * @param {{staleTime: number, gcTime: number, retry: number, refetchOnWindowFocus: boolean}} policy * @returns {{staleTime: number, gcTime: number, retry: number, refetchOnWindowFocus: boolean}} diff --git a/src/shared/utils/common.js b/src/shared/utils/common.js index 74290ed1..a9fc3f09 100644 --- a/src/shared/utils/common.js +++ b/src/shared/utils/common.js @@ -19,7 +19,7 @@ import { AppDebug } from '../../service/appConfig.js'; import { compareUnityVersion } from './avatar'; import { getAvailablePlatforms } from './platformUtils'; import { i18n } from '../../plugin/i18n'; -import { miscRequest } from '../../api'; +import { queryRequest } from '../../api'; /** * @param {string} fileName @@ -263,7 +263,7 @@ async function getBundleDateSize(ref) { if (!fileId || !version) { continue; } - const args = await miscRequest.getFileAnalysis({ + const args = await queryRequest.fetch('fileAnalysis', { fileId, version, variant diff --git a/src/stores/avatar.js b/src/stores/avatar.js index 4540c2f8..8689777c 100644 --- a/src/stores/avatar.js +++ b/src/stores/avatar.js @@ -249,8 +249,8 @@ export const useAvatarStore = defineStore('Avatar', () => { */ async function getAvatarGallery(avatarId) { const D = avatarDialog.value; - const args = await avatarRequest - .getAvatarGallery(avatarId) + const args = await queryRequest + .fetch('avatarGallery', { avatarId }) .finally(() => { D.galleryLoading = false; }); diff --git a/src/stores/user.js b/src/stores/user.js index c09cc68f..adbda1c2 100644 --- a/src/stores/user.js +++ b/src/stores/user.js @@ -841,8 +841,8 @@ export const useUserStore = defineStore('User', () => { }); if (!currentUser.value.hasSharedConnectionsOptOut) { try { - userRequest - .getMutualCounts({ userId }) + queryRequest + .fetch('mutualCounts', { userId }) .then((args) => { if (args.params.userId === D.id) { D.mutualFriendCount = diff --git a/src/stores/world.js b/src/stores/world.js index bb2b2b35..325e7e83 100644 --- a/src/stores/world.js +++ b/src/stores/world.js @@ -14,7 +14,7 @@ import { parseLocation, sanitizeEntityJson } from '../shared/utils'; -import { instanceRequest, miscRequest, worldRequest } from '../api'; +import { instanceRequest, queryRequest, worldRequest } from '../api'; import { database } from '../service/database'; import { patchWorldFromEvent } from '../queries'; import { processBulk } from '../service/request'; @@ -190,8 +190,8 @@ export const useWorldStore = defineStore('World', () => { D.isQuest = isQuest; D.isIos = isIos; updateVRChatWorldCache(); - miscRequest - .hasWorldPersistData({ + queryRequest + .fetch('worldPersistData', { worldId: D.id }) .then((args) => { diff --git a/src/views/Settings/components/Tabs/AdvancedTab.vue b/src/views/Settings/components/Tabs/AdvancedTab.vue index 59bba203..4babbe6a 100644 --- a/src/views/Settings/components/Tabs/AdvancedTab.vue +++ b/src/views/Settings/components/Tabs/AdvancedTab.vue @@ -376,10 +376,9 @@