Integrate extension api v1.6 changes

This commit is contained in:
schroda
2026-07-01 15:10:36 +02:00
parent 3812a84514
commit dd1199f3be
32 changed files with 1190 additions and 176 deletions

View File

@@ -24,6 +24,8 @@ import { RestClient } from '@/lib/requests/client/RestClient.ts';
import { GraphQLClient } from '@/lib/requests/client/GraphQLClient.ts';
import { BaseClient } from '@/lib/requests/client/BaseClient.ts';
import type {
AddExtensionStoreMutation,
AddExtensionStoreMutationVariables,
CheckForServerUpdatesQuery,
CheckForServerUpdatesQueryVariables,
CheckForWebuiUpdateQuery,
@@ -74,6 +76,10 @@ import type {
GetExtensionsFetchMutationVariables,
GetExtensionsQuery,
GetExtensionsQueryVariables,
GetExtensionStoreQuery,
GetExtensionStoreQueryVariables,
GetExtensionStoresQuery,
GetExtensionStoresQueryVariables,
GetGlobalMetadatasQuery,
GetGlobalMetadatasQueryVariables,
GetKoSyncStatusQuery,
@@ -108,6 +114,8 @@ import type {
KoSyncLogoutMutationVariables,
RefreshMangaMutation,
RefreshMangaMutationVariables,
RemoveExtensionStoreMutation,
RemoveExtensionStoreMutationVariables,
ReorderChapterDownloadMutation,
ReorderChapterDownloadMutationVariables,
ResetWebuiUpdateStatusMutation,
@@ -333,6 +341,10 @@ import { GET_KO_SYNC_STATUS } from '@/lib/graphql/koreader/KoreaderSyncQuery.ts'
import { ImageCache } from '@/lib/service-worker/ImageCache.ts';
import { Sources } from '@/features/source/services/Sources.ts';
import uniqBy from 'lodash/fp/uniqBy';
import { EXTENSION_STORE_FIELDS } from '@/lib/graphql/extension/store/ExtensionStoreFragments.ts';
import { ADD_EXTENSION_STORE, REMOVE_EXTENSION_STORE } from '@/lib/graphql/extension/store/ExtensionStoreMutation.ts';
import { assertIsDefined } from '@/base/Asserts.ts';
import { GET_EXTENSION_STORE, GET_EXTENSION_STORES } from '@/lib/graphql/extension/store/ExtensionStoreQuery.ts';
enum GQLMethod {
QUERY = 'QUERY',
@@ -1420,6 +1432,81 @@ export class RequestManager {
return this.doRequest(GQLMethod.MUTATION, UPDATE_WEBUI, undefined, options);
}
public useGetExtensionStore(
indexUrl: string,
options?: QueryHookOptions<GetExtensionStoreQuery, GetExtensionStoreQueryVariables>,
): AbortableApolloUseQueryResponse<GetExtensionStoreQuery, GetExtensionStoreQueryVariables> {
return this.doRequest(GQLMethod.USE_QUERY, GET_EXTENSION_STORE, { indexUrl }, options);
}
public useGetExtensionStores(
options?: QueryHookOptions<GetExtensionStoresQuery, GetExtensionStoresQueryVariables>,
): AbortableApolloUseQueryResponse<GetExtensionStoresQuery, GetExtensionStoresQueryVariables> {
return this.doRequest(GQLMethod.USE_QUERY, GET_EXTENSION_STORES, undefined, options);
}
public useAddExtensionStore(
options?: MutationHookOptions<AddExtensionStoreMutation, AddExtensionStoreMutationVariables>,
): AbortableApolloUseMutationResponse<AddExtensionStoreMutation, AddExtensionStoreMutationVariables> {
return this.doRequest(GQLMethod.USE_MUTATION, ADD_EXTENSION_STORE, undefined, {
refetchQueries: [GET_EXTENSION_STORES],
...options,
});
}
public useRemoveExtensionStore(
options?: MutationHookOptions<RemoveExtensionStoreMutation, RemoveExtensionStoreMutationVariables>,
): AbortableApolloUseMutationResponse<RemoveExtensionStoreMutation, RemoveExtensionStoreMutationVariables> {
const [mutate, request] = this.doRequest(GQLMethod.USE_MUTATION, REMOVE_EXTENSION_STORE, undefined, {
refetchQueries: [GET_EXTENSION_STORES],
...options,
});
const wrappedMutate = (mutateOptions: Parameters<typeof mutate>[0]) => {
const variables = mutateOptions?.variables;
assertIsDefined(variables);
const { indexUrl } = variables.input;
return mutate({
update: (cache) => {
cache.updateQuery<GetExtensionStoresQuery, GetExtensionStoresQueryVariables>(
{ query: GET_EXTENSION_STORES },
(data) => ({
...data!,
extensionStores: {
...data!.extensionStores,
nodes: data!.extensionStores.nodes.filter((store) => store.indexUrl !== indexUrl),
},
}),
);
},
optimisticResponse: {
__typename: 'Mutation',
removeExtensionStore: {
__typename: 'RemoveExtensionStorePayload',
extensionStore: {
__typename: 'ExtensionStoreType',
indexUrl,
signingKey: '',
name: 'store',
isLegacy: false,
badgeLabel: '',
contactWebsite: '',
contactDiscord: null,
extensionListUrl: null,
extensions: { __typename: 'ExtensionNodeList', totalCount: 0 },
},
},
},
...mutateOptions,
});
};
return [wrappedMutate, request];
}
public useGetExtension(
pkgName: string,
options?: QueryHookOptions<GetExtensionQuery, GetExtensionQueryVariables>,
@@ -1442,17 +1529,19 @@ export class RequestManager {
{},
{
...options,
refetchQueries: [GET_EXTENSIONS],
update(cache, { data: mutationData }) {
if (!mutationData?.fetchExtensions?.extensions) {
return;
}
cache.writeQuery({
query: GET_EXTENSIONS,
data: {
cache.updateQuery<GetExtensionsQuery, GetExtensionsQueryVariables>(
{ query: GET_EXTENSIONS },
() => ({
__typename: 'Query',
extensions: {
__typename: 'ExtensionNodeList',
nodes: mutationData.fetchExtensions.extensions,
nodes: mutationData?.fetchExtensions?.extensions ?? [],
pageInfo: {
__typename: 'PageInfo',
hasNextPage: false,
@@ -1460,10 +1549,10 @@ export class RequestManager {
startCursor: null,
endCursor: null,
},
totalCount: mutationData.fetchExtensions.extensions.length,
totalCount: mutationData?.fetchExtensions?.extensions.length ?? 0,
},
},
});
}),
);
},
},
);
@@ -1482,11 +1571,14 @@ export class RequestManager {
setUpdatedCache({});
}, [result.loading]);
const cachedResult = this.cache.getResponseFor<typeof result>(
EXTENSION_LIST_CACHE_KEY,
undefined,
d(1).minutes.inWholeMilliseconds,
);
const getCachedResult = () =>
this.cache.getResponseFor<typeof result>(
EXTENSION_LIST_CACHE_KEY,
undefined,
d(1).minutes.inWholeMilliseconds,
);
const cachedResult = getCachedResult();
const normalizedCachedResult = useMemo(
() =>
!cachedResult
@@ -1510,6 +1602,17 @@ export class RequestManager {
fragment: EXTENSION_LIST_FIELDS,
}) ?? extension,
),
extensionStores: cachedResult.data.fetchExtensions.extensionStores.map(
(store) =>
this.graphQLClient.client.cache.readFragment<
NonNullable<
GetExtensionsFetchMutation['fetchExtensions']
>['extensionStores'][0]
>({
id: this.graphQLClient.client.cache.identify(store),
fragment: EXTENSION_STORE_FIELDS,
}),
),
},
},
},
@@ -1517,7 +1620,7 @@ export class RequestManager {
);
const wrappedMutate = async (mutateOptions: Parameters<typeof mutate>[0]) => {
if (cachedResult) {
if (getCachedResult()) {
return normalizedCachedResult;
}
@@ -1586,7 +1689,9 @@ export class RequestManager {
return {
...extension,
...installedExtension,
hasUpdate: installedExtension?.versionCode < extension.versionCode,
hasUpdate:
Number(installedExtension?.versionCodeLong ?? -1) <
Number(extension.versionCodeLong),
};
})
: [
@@ -1595,6 +1700,7 @@ export class RequestManager {
GetExtensionsFetchMutation['fetchExtensions']
>['extensions'][number],
],
extensionStores: cachedExtensions.data?.fetchExtensions!.extensionStores,
},
},
};
@@ -1660,6 +1766,7 @@ export class RequestManager {
...(response.data?.updateExtension?.extension ?? []),
};
}) ?? [],
extensionStores: cachedExtensions.data.fetchExtensions?.extensionStores ?? [],
},
},
};
@@ -1727,6 +1834,7 @@ export class RequestManager {
) ?? []),
};
}) ?? [],
extensionStores: cachedExtensions.data.fetchExtensions?.extensionStores ?? [],
},
},
};