Skip to content

Commit

Permalink
Fix/chapter not getting deleted after being read (#532)
Browse files Browse the repository at this point in the history
* Create permanent subscriptions

The subscriptions have to be permanently open, otherwise, the cache gets outdated.

E.g. in case a view is open, which does not subscribe to the download updates, finished downloads are never received and thus, data of existing chapters/mangas in the cache get outdated

* [Codegen][Tool] Get latest data from cache for automatic chapter deletion after reading

The manga chapters query is on "standby" to prevent the reader from rerender in case the underlying query data updates, which would cause the reader to jump back to the beginning of the last read page.
Thus, when checking if the chapter should be automatically deleted, the data from the query can't be used, since it is outdated.
  • Loading branch information
schroda authored Jan 1, 2024
1 parent 7d3d825 commit 05fa3d0
Show file tree
Hide file tree
Showing 12 changed files with 93 additions and 32 deletions.
16 changes: 16 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { DownloadSettings } from '@/screens/settings/DownloadSettings.tsx';
import { ServerSettings } from '@/screens/settings/ServerSettings.tsx';
import { WebUISettings } from '@/screens/settings/WebUISettings.tsx';
import { ServerUpdateChecker } from '@/components/settings/ServerUpdateChecker.tsx';
import { requestManager } from '@/lib/requests/RequestManager.ts';

if (process.env.NODE_ENV !== 'production') {
// Adds messages only in a dev environment
Expand All @@ -52,10 +53,25 @@ const ScrollToTop = () => {
return null;
};

/**
* Creates permanent subscriptions to always have the latest data.
*
* E.g. in case a view is open, which does not subscribe to the download updates, finished downloads are never received
* and thus, data of existing chapters/mangas in the cache get outdated
*/
const BackgroundSubscriptions = () => {
requestManager.useDownloadSubscription();
requestManager.useUpdaterSubscription();
requestManager.useWebUIUpdateSubscription();

return null;
};

export const App: React.FC = () => (
<AppContext>
<ScrollToTop />
<ServerUpdateChecker />
<BackgroundSubscriptions />
<CssBaseline />
<DefaultNavBar />
<Container
Expand Down
4 changes: 2 additions & 2 deletions src/components/chapter/ChapterList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ interface IProps {
export const ChapterList: React.FC<IProps> = ({ manga, isRefreshing }) => {
const { t } = useTranslation();

const { data: downloaderData } = requestManager.useDownloadSubscription();
const queue = (downloaderData?.downloadChanged.queue as DownloadType[]) ?? [];
const { data: downloaderData } = requestManager.useGetDownloadStatus();
const queue = (downloaderData?.downloadStatus.queue as DownloadType[]) ?? [];

const [options, dispatch] = useChapterOptions(manga.id);
const { data: chaptersData, loading: isLoading } = requestManager.useGetMangaChapters(manga.id);
Expand Down
4 changes: 2 additions & 2 deletions src/components/library/UpdateChecker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export function UpdateChecker({ handleFinishedUpdate }: { handleFinishedUpdate?:
const { data: lastUpdateTimestampData, refetch: reFetchLastTimestamp } =
requestManager.useGetLastGlobalUpdateTimestamp();
const lastUpdateTimestamp = lastUpdateTimestampData?.lastUpdateTimestamp.timestamp;
const { data: updaterData } = requestManager.useUpdaterSubscription();
const status = updaterData?.updateStatusChanged;
const { data: updaterData } = requestManager.useGetGlobalUpdateSummary();
const status = updaterData?.updateStatus;

const loading = !!status?.isRunning;
const progress = useMemo(
Expand Down
10 changes: 5 additions & 5 deletions src/lib/graphql/generated/apollo-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {FieldPolicy, FieldReadFunction, Reference, TypePolicies, TypePolicy} from '@apollo/client/cache';
import {
GetCategoryQueryVariables, GetChapterQueryVariables,
GetChaptersQuery, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables,
GetMangaQueryVariables, GetSourceQueryVariables,
GetChaptersQuery, GetDownloadStatusQueryVariables, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables,
GetMangaQueryVariables, GetSourceQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables,
} from "@/lib/graphql/generated/graphql.ts";
import {FieldFunctionOptions} from "@apollo/client/cache/inmemory/policies";
export type AboutServerPayloadKeySpecifier = ('buildTime' | 'buildType' | 'discord' | 'github' | 'name' | 'revision' | 'version' | AboutServerPayloadKeySpecifier)[];
Expand Down Expand Up @@ -518,10 +518,10 @@ export type QueryFieldPolicy = {
chapters?: FieldPolicy<GetChaptersQuery['chapters']> | FieldReadFunction<GetChaptersQuery['chapters']>,
checkForServerUpdates?: FieldPolicy<any> | FieldReadFunction<any>,
checkForWebUIUpdate?: FieldPolicy<any> | FieldReadFunction<any>,
downloadStatus?: FieldPolicy<any> | FieldReadFunction<any>,
downloadStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>>,
extension?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetExtensionQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetExtensionQueryVariables>>,
extensions?: FieldPolicy<any> | FieldReadFunction<any>,
getWebUIUpdateStatus?: FieldPolicy<any> | FieldReadFunction<any>,
getWebUIUpdateStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetWebuiUpdateStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetWebuiUpdateStatusQueryVariables>>,
lastUpdateTimestamp?: FieldPolicy<any> | FieldReadFunction<any>,
manga?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetMangaQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetMangaQueryVariables>>,
mangas?: FieldPolicy<any> | FieldReadFunction<any>,
Expand All @@ -531,7 +531,7 @@ export type QueryFieldPolicy = {
settings?: FieldPolicy<any> | FieldReadFunction<any>,
source?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetSourceQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetSourceQueryVariables>>,
sources?: FieldPolicy<any> | FieldReadFunction<any>,
updateStatus?: FieldPolicy<any> | FieldReadFunction<any>,
updateStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetUpdateStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetUpdateStatusQueryVariables>>,
validateBackup?: FieldPolicy<any> | FieldReadFunction<any>
};
export type ReorderChapterDownloadPayloadKeySpecifier = ('clientMutationId' | 'downloadStatus' | ReorderChapterDownloadPayloadKeySpecifier)[];
Expand Down
4 changes: 2 additions & 2 deletions src/lib/graphql/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2369,14 +2369,14 @@ export type UpdateExtensionMutationVariables = Exact<{
}>;


export type UpdateExtensionMutation = { __typename?: 'Mutation', updateExtension: { __typename?: 'UpdateExtensionPayload', clientMutationId?: string | null, extension: { __typename?: 'ExtensionType', apkName: string, versionName: string, versionCode: number, isInstalled: boolean, isObsolete: boolean, hasUpdate: boolean } } };
export type UpdateExtensionMutation = { __typename?: 'Mutation', updateExtension: { __typename?: 'UpdateExtensionPayload', clientMutationId?: string | null, extension: { __typename?: 'ExtensionType', pkgName: string, apkName: string, versionName: string, versionCode: number, isInstalled: boolean, isObsolete: boolean, hasUpdate: boolean } } };

export type UpdateExtensionsMutationVariables = Exact<{
input: UpdateExtensionsInput;
}>;


export type UpdateExtensionsMutation = { __typename?: 'Mutation', updateExtensions: { __typename?: 'UpdateExtensionsPayload', clientMutationId?: string | null, extensions: Array<{ __typename?: 'ExtensionType', apkName: string, versionName: string, versionCode: number, isInstalled: boolean, isObsolete: boolean, hasUpdate: boolean }> } };
export type UpdateExtensionsMutation = { __typename?: 'Mutation', updateExtensions: { __typename?: 'UpdateExtensionsPayload', clientMutationId?: string | null, extensions: Array<{ __typename?: 'ExtensionType', pkgName: string, apkName: string, versionName: string, versionCode: number, isInstalled: boolean, isObsolete: boolean, hasUpdate: boolean }> } };

export type InstallExternalExtensionMutationVariables = Exact<{
file: Scalars['Upload']['input'];
Expand Down
19 changes: 16 additions & 3 deletions src/lib/requests/RequestManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,17 @@ import {
UpdateMangasCategoriesMutation,
UpdateMangasCategoriesMutationVariables,
UpdateMangaCategoriesPatchInput,
GetWebuiUpdateStatusQuery,
GetWebuiUpdateStatusQueryVariables,
} from '@/lib/graphql/generated/graphql.ts';
import { GET_GLOBAL_METADATAS } from '@/lib/graphql/queries/GlobalMetadataQuery.ts';
import { SET_GLOBAL_METADATA } from '@/lib/graphql/mutations/GlobalMetadataMutation.ts';
import { CHECK_FOR_SERVER_UPDATES, CHECK_FOR_WEBUI_UPDATE, GET_ABOUT } from '@/lib/graphql/queries/ServerInfoQuery.ts';
import {
CHECK_FOR_SERVER_UPDATES,
CHECK_FOR_WEBUI_UPDATE,
GET_ABOUT,
GET_WEBUI_UPDATE_STATUS,
} from '@/lib/graphql/queries/ServerInfoQuery.ts';
import { GET_EXTENSIONS } from '@/lib/graphql/queries/ExtensionQuery.ts';
import {
GET_EXTENSIONS_FETCH,
Expand Down Expand Up @@ -2104,8 +2111,8 @@ export class RequestManager {
}

public useGetDownloadStatus(
options?: SubscriptionHookOptions<GetDownloadStatusQuery, GetDownloadStatusQueryVariables>,
): SubscriptionResult<GetDownloadStatusQuery, GetDownloadStatusQueryVariables> {
options?: QueryHookOptions<GetDownloadStatusQuery, GetDownloadStatusQueryVariables>,
): AbortableApolloUseQueryResponse<GetDownloadStatusQuery, GetDownloadStatusQueryVariables> {
return this.doRequest(GQLMethod.USE_QUERY, GET_DOWNLOAD_STATUS, {}, options);
}

Expand Down Expand Up @@ -2163,6 +2170,12 @@ export class RequestManager {
): AbortableApolloMutationResponse<ResetWebuiUpdateStatusMutation> {
return this.doRequest(GQLMethod.MUTATION, RESET_WEBUI_UPDATE_STATUS, undefined, options);
}

public useGetWebUIUpdateStatus(
options?: QueryHookOptions<GetWebuiUpdateStatusQuery, GetWebuiUpdateStatusQueryVariables>,
): AbortableApolloUseQueryResponse<GetWebuiUpdateStatusQuery, GetWebuiUpdateStatusQueryVariables> {
return this.doRequest(GQLMethod.USE_QUERY, GET_WEBUI_UPDATE_STATUS, undefined, options);
}
}

export const requestManager = new RequestManager();
17 changes: 17 additions & 0 deletions src/lib/requests/client/GraphQLClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const typePolicies: StrictTypedTypePolicies = {
SettingsType: { keyFields: [] },
DownloadStatus: { keyFields: [] },
DownloadType: { keyFields: ['chapter'] },
WebUIUpdateStatus: { keyFields: [] },
UpdateStatus: { keyFields: [] },
Query: {
fields: {
manga(_, { args, toReference }) {
Expand Down Expand Up @@ -58,6 +60,21 @@ const typePolicies: StrictTypedTypePolicies = {
key: args?.key,
});
},
downloadStatus(_, { toReference }) {
return toReference({
__typename: 'DownloadStatus',
key: {},
});
},
getWebUIUpdateStatus(_, { toReference }) {
return toReference({
__typename: 'WebUIUpdateStatus',
key: {},
});
},
updateStatus(_, { toReference }) {
return toReference({ __typename: 'UpdateStatus', key: {} });
},
chapters: {
keyArgs: ['condition', 'filter', 'orderBy', 'orderByType'],
merge(existing, incoming) {
Expand Down
1 change: 0 additions & 1 deletion src/screens/DownloadQueue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ const DownloadChapterItem = ({
export const DownloadQueue: React.FC = () => {
const { t } = useTranslation();

requestManager.useDownloadSubscription();
const [reorderDownload, { reset: revertReorder }] = requestManager.useReorderChapterInDownloadQueue();

const { data: downloadStatusData, loading: isLoading } = requestManager.useGetDownloadStatus();
Expand Down
18 changes: 16 additions & 2 deletions src/screens/Reader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { useDebounce } from '@/util/useDebounce.ts';
import { UpdateChapterPatchInput } from '@/lib/graphql/generated/graphql.ts';
import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts';
import { defaultPromiseErrorHandler } from '@/util/defaultPromiseErrorHandler.ts';
import { FULL_CHAPTER_FIELDS } from '@/lib/graphql/Fragments.ts';

const isDupChapter = async (chapterIndex: number, currentChapter: TChapter) => {
const nextChapter = await requestManager.getChapter(currentChapter.manga.id, chapterIndex).response;
Expand Down Expand Up @@ -191,9 +192,22 @@ export function Reader() {
(mangaChapter) => mangaChapter.sourceOrder === chapterToDeleteSourceOrder,
);

if (!chapterToDelete) {
return -1;
}

const chapterToDeleteUpToDateData = requestManager.graphQLClient.client.cache.readFragment<TChapter>({
id: requestManager.graphQLClient.client.cache.identify({
__typename: 'ChapterType',
id: chapterToDelete.id,
}),
fragment: FULL_CHAPTER_FIELDS,
fragmentName: 'FULL_CHAPTER_FIELDS',
});

const shouldDeleteChapter =
chapterToDelete?.isDownloaded &&
(!chapterToDelete?.isBookmarked || metadataSettings.deleteChaptersWithBookmark);
chapterToDeleteUpToDateData?.isDownloaded &&
(!chapterToDeleteUpToDateData?.isBookmarked || metadataSettings.deleteChaptersWithBookmark);
if (!shouldDeleteChapter) {
return -1;
}
Expand Down
4 changes: 2 additions & 2 deletions src/screens/Updates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ export const Updates: React.FC = () => {
const updateEntries = chapterUpdateData?.chapters.nodes ?? [];
const groupedUpdates = useMemo(() => groupByDate(updateEntries), [updateEntries]);
const groupCounts: number[] = useMemo(() => groupedUpdates.map((group) => group[1]), [groupedUpdates]);
const { data: downloaderData } = requestManager.useDownloadSubscription();
const queue = (downloaderData?.downloadChanged.queue as DownloadType[]) ?? [];
const { data: downloaderData } = requestManager.useGetDownloadStatus();
const queue = (downloaderData?.downloadStatus.queue as DownloadType[]) ?? [];

const lastUpdateTimestampCompRef = useRef<HTMLElement>(null);
const [lastUpdateTimestampCompHeight, setLastUpdateTimestampCompHeight] = useState(0);
Expand Down
18 changes: 10 additions & 8 deletions src/screens/settings/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,11 @@ export function About() {
} = requestManager.useCheckForWebUIUpdate({ notifyOnNetworkStatusChange: true });
const webUIUpdateCheckError = orgWebUIUpdateCheckError || webUIUpdateData?.checkForWebUIUpdate.tag === '';

const { data: webUIUpdateStatusData } = requestManager.useWebUIUpdateSubscription();
const { state: webUIUpdateState, progress: webUIUpdateProgress } =
webUIUpdateStatusData?.webUIUpdateStatusChange ?? { state: UpdateState.Idle, progress: 0 };
const { data: webUIUpdateStatusData } = requestManager.useGetWebUIUpdateStatus();
const { state: webUIUpdateState, progress: webUIUpdateProgress } = webUIUpdateStatusData?.getWebUIUpdateStatus ?? {
state: UpdateState.Idle,
progress: 0,
};

useEffect(() => {
const isError = webUIUpdateState === UpdateState.Error;
Expand All @@ -236,7 +238,7 @@ export function About() {

makeToast(
t('settings.about.webui.label.update_success', {
version: webUIUpdateStatusData!.webUIUpdateStatusChange.info.tag,
version: webUIUpdateStatusData!.getWebUIUpdateStatus.info.tag,
}),
'success',
);
Expand All @@ -245,16 +247,16 @@ export function About() {
fragment: ABOUT_WEBUI,
data: {
__typename: 'AboutWebUI',
channel: webUIUpdateStatusData!.webUIUpdateStatusChange.info.channel,
tag: webUIUpdateStatusData!.webUIUpdateStatusChange.info.tag,
channel: webUIUpdateStatusData!.getWebUIUpdateStatus.info.channel,
tag: webUIUpdateStatusData!.getWebUIUpdateStatus.info.tag,
},
});
requestManager.graphQLClient.client.cache.writeFragment({
fragment: WEBUI_UPDATE_CHECK,
data: {
__typename: 'WebUIUpdateCheck',
channel: webUIUpdateStatusData!.webUIUpdateStatusChange.info.channel,
tag: webUIUpdateStatusData!.webUIUpdateStatusChange.info.tag,
channel: webUIUpdateStatusData!.getWebUIUpdateStatus.info.channel,
tag: webUIUpdateStatusData!.getWebUIUpdateStatus.info.tag,
updateAvailable: false,
},
});
Expand Down
10 changes: 5 additions & 5 deletions tools/scripts/codegenFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ const addImports = format(
`import {FieldPolicy, FieldReadFunction, Reference, TypePolicies, TypePolicy} from '@apollo/client/cache';
import {
\tGetCategoryQueryVariables, GetChapterQueryVariables,
\tGetChaptersQuery, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables,
\tGetMangaQueryVariables, GetSourceQueryVariables,
\tGetChaptersQuery, GetDownloadStatusQueryVariables, GetExtensionQueryVariables, GetGlobalMetadataQueryVariables,
\tGetMangaQueryVariables, GetSourceQueryVariables, GetUpdateStatusQueryVariables, GetWebuiUpdateStatusQueryVariables,
} from "@/lib/graphql/generated/graphql.ts";
import {FieldFunctionOptions} from "@apollo/client/cache/inmemory/policies";`,
);
Expand Down Expand Up @@ -95,10 +95,10 @@ const fixTypingOfQueryTypePolicies = format(
\tchapters?: FieldPolicy<GetChaptersQuery['chapters']> | FieldReadFunction<GetChaptersQuery['chapters']>,
\tcheckForServerUpdates?: FieldPolicy<any> | FieldReadFunction<any>,
\tcheckForWebUIUpdate?: FieldPolicy<any> | FieldReadFunction<any>,
\tdownloadStatus?: FieldPolicy<any> | FieldReadFunction<any>,
\tdownloadStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetDownloadStatusQueryVariables>>,
\textension?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetExtensionQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetExtensionQueryVariables>>,
\textensions?: FieldPolicy<any> | FieldReadFunction<any>,
\tgetWebUIUpdateStatus?: FieldPolicy<any> | FieldReadFunction<any>,
\tgetWebUIUpdateStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetWebuiUpdateStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetWebuiUpdateStatusQueryVariables>>,
\tlastUpdateTimestamp?: FieldPolicy<any> | FieldReadFunction<any>,
\tmanga?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetMangaQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetMangaQueryVariables>>,
\tmangas?: FieldPolicy<any> | FieldReadFunction<any>,
Expand All @@ -108,7 +108,7 @@ const fixTypingOfQueryTypePolicies = format(
\tsettings?: FieldPolicy<any> | FieldReadFunction<any>,
\tsource?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetSourceQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetSourceQueryVariables>>,
\tsources?: FieldPolicy<any> | FieldReadFunction<any>,
\tupdateStatus?: FieldPolicy<any> | FieldReadFunction<any>,
\tupdateStatus?: FieldPolicy<Reference, Reference, Reference, FieldFunctionOptions<GetUpdateStatusQueryVariables>> | FieldReadFunction<Reference, Reference, FieldFunctionOptions<GetUpdateStatusQueryVariables>>,
\tvalidateBackup?: FieldPolicy<any> | FieldReadFunction<any>
};`,
);
Expand Down

0 comments on commit 05fa3d0

Please sign in to comment.