mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-28 03:03:47 +02:00
refactor queryRequest
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
_entityCacheInternals,
|
||||
fetchWithEntityPolicy,
|
||||
patchAndRefetchActiveQuery,
|
||||
patchUserFromEvent,
|
||||
patchQueryDataWithRecency
|
||||
} from '../entityCache';
|
||||
import { queryClient } from '../client';
|
||||
@@ -130,4 +131,54 @@ describe('entity query cache helpers', () => {
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test('internal completeness guard requires params + entity identifier', () => {
|
||||
expect(_entityCacheInternals.hasCompleteEntityData(undefined)).toBe(
|
||||
false
|
||||
);
|
||||
expect(_entityCacheInternals.hasCompleteEntityData({})).toBe(false);
|
||||
expect(
|
||||
_entityCacheInternals.hasCompleteEntityData({
|
||||
params: {}
|
||||
})
|
||||
).toBe(false);
|
||||
expect(
|
||||
_entityCacheInternals.hasCompleteEntityData({
|
||||
params: { userId: 'usr_1' }
|
||||
})
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('patchUserFromEvent skips placeholder cache entries', () => {
|
||||
const queryKey = ['user', 'usr_1'];
|
||||
queryClient.setQueryData(queryKey, {
|
||||
params: {}
|
||||
});
|
||||
|
||||
patchUserFromEvent({
|
||||
id: 'usr_1',
|
||||
displayName: 'Alice'
|
||||
});
|
||||
|
||||
expect(queryClient.getQueryData(queryKey)).toEqual({
|
||||
params: {}
|
||||
});
|
||||
});
|
||||
|
||||
test('patchUserFromEvent patches when query has complete data', () => {
|
||||
const queryKey = ['user', 'usr_1'];
|
||||
queryClient.setQueryData(queryKey, {
|
||||
params: { userId: 'usr_1' },
|
||||
ref: { id: 'usr_1', displayName: 'Old' },
|
||||
json: { id: 'usr_1', displayName: 'Old' }
|
||||
});
|
||||
|
||||
patchUserFromEvent({
|
||||
id: 'usr_1',
|
||||
displayName: 'New'
|
||||
});
|
||||
|
||||
const data = queryClient.getQueryData(queryKey);
|
||||
expect(data.ref.displayName).toBe('New');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,13 +50,6 @@ describe('query policy configuration', () => {
|
||||
refetchOnWindowFocus: false
|
||||
});
|
||||
|
||||
expect(entityQueryPolicies.instance).toMatchObject({
|
||||
staleTime: 0,
|
||||
gcTime: 10000,
|
||||
retry: 0,
|
||||
refetchOnWindowFocus: false
|
||||
});
|
||||
|
||||
expect(entityQueryPolicies.friendList).toMatchObject({
|
||||
staleTime: 20000,
|
||||
gcTime: 90000,
|
||||
@@ -104,9 +97,6 @@ describe('query policy configuration', () => {
|
||||
expect(getEntityQueryPolicy('worldCollection')).toBe(
|
||||
entityQueryPolicies.worldCollection
|
||||
);
|
||||
expect(getEntityQueryPolicy('instance')).toBe(
|
||||
entityQueryPolicies.instance
|
||||
);
|
||||
expect(getEntityQueryPolicy('friendList')).toBe(
|
||||
entityQueryPolicies.friendList
|
||||
);
|
||||
|
||||
@@ -15,6 +15,10 @@ const RECENCY_FIELDS = [
|
||||
'createdAt'
|
||||
];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
function getComparableEntity(data) {
|
||||
if (!data || typeof data !== 'object') {
|
||||
return null;
|
||||
@@ -32,6 +36,10 @@ function getComparableEntity(data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
function parseTimestamp(value) {
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return value;
|
||||
@@ -45,6 +53,10 @@ function parseTimestamp(value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
function getRecencyTimestamp(data) {
|
||||
const comparable = getComparableEntity(data);
|
||||
if (!comparable) {
|
||||
@@ -61,6 +73,11 @@ function getRecencyTimestamp(data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param currentData
|
||||
* @param nextData
|
||||
*/
|
||||
function shouldReplaceCurrent(currentData, nextData) {
|
||||
if (typeof currentData === 'undefined') {
|
||||
return true;
|
||||
@@ -80,6 +97,35 @@ function shouldReplaceCurrent(currentData, nextData) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
function hasCompleteEntityData(data) {
|
||||
if (!data || typeof data !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const params = data.params;
|
||||
if (!params || typeof params !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasEntityId = Boolean(
|
||||
data?.ref?.id ||
|
||||
data?.json?.id ||
|
||||
params.userId ||
|
||||
params.avatarId ||
|
||||
params.worldId ||
|
||||
params.groupId ||
|
||||
params.fileId ||
|
||||
params.printId ||
|
||||
params.inventoryId
|
||||
);
|
||||
|
||||
return hasEntityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{queryKey: unknown[], nextData: any}} options
|
||||
*/
|
||||
@@ -142,7 +188,8 @@ export async function patchAndRefetchActiveQuery({ queryKey, nextData }) {
|
||||
export function patchUserFromEvent(ref) {
|
||||
if (!ref?.id) return;
|
||||
const queryKey = queryKeys.user(ref.id);
|
||||
if (!queryClient.getQueryData(queryKey)) return;
|
||||
const existing = queryClient.getQueryData(queryKey);
|
||||
if (!hasCompleteEntityData(existing)) return;
|
||||
patchQueryDataWithRecency({
|
||||
queryKey,
|
||||
nextData: {
|
||||
@@ -160,7 +207,8 @@ export function patchUserFromEvent(ref) {
|
||||
export function patchAvatarFromEvent(ref) {
|
||||
if (!ref?.id) return;
|
||||
const queryKey = queryKeys.avatar(ref.id);
|
||||
if (!queryClient.getQueryData(queryKey)) return;
|
||||
const existing = queryClient.getQueryData(queryKey);
|
||||
if (!hasCompleteEntityData(existing)) return;
|
||||
patchQueryDataWithRecency({
|
||||
queryKey,
|
||||
nextData: {
|
||||
@@ -178,7 +226,8 @@ export function patchAvatarFromEvent(ref) {
|
||||
export function patchWorldFromEvent(ref) {
|
||||
if (!ref?.id) return;
|
||||
const queryKey = queryKeys.world(ref.id);
|
||||
if (!queryClient.getQueryData(queryKey)) return;
|
||||
const existing = queryClient.getQueryData(queryKey);
|
||||
if (!hasCompleteEntityData(existing)) return;
|
||||
patchQueryDataWithRecency({
|
||||
queryKey,
|
||||
nextData: {
|
||||
@@ -204,7 +253,8 @@ export function patchGroupFromEvent(ref) {
|
||||
};
|
||||
|
||||
const keyFalse = queryKeys.group(ref.id, false);
|
||||
if (queryClient.getQueryData(keyFalse)) {
|
||||
const existingFalse = queryClient.getQueryData(keyFalse);
|
||||
if (hasCompleteEntityData(existingFalse)) {
|
||||
patchQueryDataWithRecency({
|
||||
queryKey: keyFalse,
|
||||
nextData
|
||||
@@ -212,7 +262,8 @@ export function patchGroupFromEvent(ref) {
|
||||
}
|
||||
|
||||
const keyTrue = queryKeys.group(ref.id, true);
|
||||
if (queryClient.getQueryData(keyTrue)) {
|
||||
const existingTrue = queryClient.getQueryData(keyTrue);
|
||||
if (hasCompleteEntityData(existingTrue)) {
|
||||
patchQueryDataWithRecency({
|
||||
queryKey: keyTrue,
|
||||
nextData
|
||||
@@ -220,30 +271,8 @@ export function patchGroupFromEvent(ref) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} ref
|
||||
*/
|
||||
export function patchInstanceFromEvent(ref) {
|
||||
if (!ref?.id) return;
|
||||
|
||||
const [worldId, instanceId] = String(ref.id).split(':');
|
||||
if (!worldId || !instanceId) return;
|
||||
|
||||
const queryKey = queryKeys.instance(worldId, instanceId);
|
||||
if (!queryClient.getQueryData(queryKey)) return;
|
||||
|
||||
patchQueryDataWithRecency({
|
||||
queryKey,
|
||||
nextData: {
|
||||
cache: false,
|
||||
json: ref,
|
||||
params: { worldId, instanceId },
|
||||
ref
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const _entityCacheInternals = {
|
||||
hasCompleteEntityData,
|
||||
getRecencyTimestamp,
|
||||
shouldReplaceCurrent
|
||||
};
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
export { queryClient } from './client';
|
||||
export { queryKeys } from './keys';
|
||||
export { entityQueryPolicies, getEntityQueryPolicy, toQueryOptions } from './policies';
|
||||
export {
|
||||
entityQueryPolicies,
|
||||
getEntityQueryPolicy,
|
||||
toQueryOptions
|
||||
} from './policies';
|
||||
export {
|
||||
fetchWithEntityPolicy,
|
||||
patchAndRefetchActiveQuery,
|
||||
@@ -9,6 +13,5 @@ export {
|
||||
patchAvatarFromEvent,
|
||||
patchWorldFromEvent,
|
||||
patchGroupFromEvent,
|
||||
patchInstanceFromEvent,
|
||||
refetchActiveEntityQuery
|
||||
} from './entityCache';
|
||||
|
||||
@@ -2,7 +2,11 @@ export const queryKeys = Object.freeze({
|
||||
user: (userId) => ['user', userId],
|
||||
avatar: (avatarId) => ['avatar', avatarId],
|
||||
world: (worldId) => ['world', worldId],
|
||||
group: (groupId, includeRoles = false) => ['group', groupId, Boolean(includeRoles)],
|
||||
group: (groupId, includeRoles = false) => [
|
||||
'group',
|
||||
groupId,
|
||||
Boolean(includeRoles)
|
||||
],
|
||||
groupPosts: ({ groupId, n = 100, offset = 0 } = {}) => [
|
||||
'group',
|
||||
groupId,
|
||||
@@ -12,8 +16,19 @@ export const queryKeys = Object.freeze({
|
||||
offset: Number(offset)
|
||||
}
|
||||
],
|
||||
groupMember: ({ groupId, userId } = {}) => ['group', groupId, 'member', userId],
|
||||
groupMembers: ({ groupId, n = 100, offset = 0, sort = '', roleId = '' } = {}) => [
|
||||
groupMember: ({ groupId, userId } = {}) => [
|
||||
'group',
|
||||
groupId,
|
||||
'member',
|
||||
userId
|
||||
],
|
||||
groupMembers: ({
|
||||
groupId,
|
||||
n = 100,
|
||||
offset = 0,
|
||||
sort = '',
|
||||
roleId = ''
|
||||
} = {}) => [
|
||||
'group',
|
||||
groupId,
|
||||
'members',
|
||||
@@ -41,7 +56,6 @@ export const queryKeys = Object.freeze({
|
||||
'calendarEvent',
|
||||
eventId
|
||||
],
|
||||
instance: (worldId, instanceId) => ['instance', worldId, instanceId],
|
||||
worldsByUser: ({
|
||||
userId,
|
||||
n = 50,
|
||||
@@ -91,7 +105,13 @@ export const queryKeys = Object.freeze({
|
||||
type: String(type || '')
|
||||
}
|
||||
],
|
||||
favoriteWorlds: ({ n = 300, offset = 0, ownerId = '', userId = '', tag = '' } = {}) => [
|
||||
favoriteWorlds: ({
|
||||
n = 300,
|
||||
offset = 0,
|
||||
ownerId = '',
|
||||
userId = '',
|
||||
tag = ''
|
||||
} = {}) => [
|
||||
'favorite',
|
||||
'worlds',
|
||||
{
|
||||
@@ -102,7 +122,13 @@ export const queryKeys = Object.freeze({
|
||||
tag: String(tag || '')
|
||||
}
|
||||
],
|
||||
favoriteAvatars: ({ n = 300, offset = 0, tag = '', ownerId = '', userId = '' } = {}) => [
|
||||
favoriteAvatars: ({
|
||||
n = 300,
|
||||
offset = 0,
|
||||
tag = '',
|
||||
ownerId = '',
|
||||
userId = ''
|
||||
} = {}) => [
|
||||
'favorite',
|
||||
'avatars',
|
||||
{
|
||||
@@ -129,7 +155,12 @@ export const queryKeys = Object.freeze({
|
||||
}
|
||||
],
|
||||
print: (printId) => ['gallery', 'print', printId],
|
||||
inventoryItems: ({ n = 100, offset = 0, order = 'newest', types = '' } = {}) => [
|
||||
inventoryItems: ({
|
||||
n = 100,
|
||||
offset = 0,
|
||||
order = 'newest',
|
||||
types = ''
|
||||
} = {}) => [
|
||||
'inventory',
|
||||
'items',
|
||||
{
|
||||
|
||||
@@ -37,12 +37,6 @@ export const entityQueryPolicies = Object.freeze({
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: false
|
||||
}),
|
||||
instance: Object.freeze({
|
||||
staleTime: 0,
|
||||
gcTime: 10 * SECOND,
|
||||
retry: 0,
|
||||
refetchOnWindowFocus: false
|
||||
}),
|
||||
friendList: Object.freeze({
|
||||
staleTime: 20 * SECOND,
|
||||
gcTime: 90 * SECOND,
|
||||
@@ -76,7 +70,7 @@ export const entityQueryPolicies = Object.freeze({
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {'user'|'avatar'|'world'|'group'|'groupCollection'|'worldCollection'|'instance'|'friendList'|'favoriteCollection'|'galleryCollection'|'inventoryCollection'|'fileObject'} entity
|
||||
* @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) {
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { useQuery } from '@tanstack/vue-query';
|
||||
|
||||
import { avatarRequest, groupRequest, instanceRequest, userRequest, worldRequest } from '../api';
|
||||
import { queryKeys } from './keys';
|
||||
import { avatarRequest, groupRequest, userRequest, worldRequest } from '../api';
|
||||
import { entityQueryPolicies, toQueryOptions } from './policies';
|
||||
import { queryKeys } from './keys';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param userId
|
||||
* @param options
|
||||
*/
|
||||
export function useUserQuery(userId, options = {}) {
|
||||
return useQuery({
|
||||
...options,
|
||||
@@ -14,6 +19,11 @@ export function useUserQuery(userId, options = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param avatarId
|
||||
* @param options
|
||||
*/
|
||||
export function useAvatarQuery(avatarId, options = {}) {
|
||||
return useQuery({
|
||||
...options,
|
||||
@@ -24,6 +34,11 @@ export function useAvatarQuery(avatarId, options = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param worldId
|
||||
* @param options
|
||||
*/
|
||||
export function useWorldQuery(worldId, options = {}) {
|
||||
return useQuery({
|
||||
...options,
|
||||
@@ -34,6 +49,12 @@ export function useWorldQuery(worldId, options = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param groupId
|
||||
* @param includeRoles
|
||||
* @param options
|
||||
*/
|
||||
export function useGroupQuery(groupId, includeRoles = false, options = {}) {
|
||||
return useQuery({
|
||||
...options,
|
||||
@@ -43,13 +64,3 @@ export function useGroupQuery(groupId, includeRoles = false, options = {}) {
|
||||
...toQueryOptions(entityQueryPolicies.group)
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstanceQuery(worldId, instanceId, options = {}) {
|
||||
return useQuery({
|
||||
...options,
|
||||
queryKey: queryKeys.instance(worldId, instanceId),
|
||||
queryFn: () => instanceRequest.getInstance({ worldId, instanceId }),
|
||||
enabled: Boolean(worldId && instanceId),
|
||||
...toQueryOptions(entityQueryPolicies.instance)
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user