Update to server download subscription changes

This commit is contained in:
schroda
2024-08-21 21:29:01 +02:00
parent 61ae717e9a
commit a819d45489
9 changed files with 217 additions and 42 deletions

View File

@@ -8,27 +8,52 @@
import gql from 'graphql-tag'; import gql from 'graphql-tag';
const DOWNLOAD_TYPE_FIELDS = gql`
fragment DOWNLOAD_TYPE_FIELDS on DownloadType {
chapter {
id
name
sourceOrder
isDownloaded
}
manga {
id
title
downloadCount
}
progress
state
tries
}
`;
export const DOWNLOAD_STATUS_FIELDS = gql` export const DOWNLOAD_STATUS_FIELDS = gql`
${DOWNLOAD_TYPE_FIELDS}
fragment DOWNLOAD_STATUS_FIELDS on DownloadStatus { fragment DOWNLOAD_STATUS_FIELDS on DownloadStatus {
state state
queue { queue {
chapter { ...DOWNLOAD_TYPE_FIELDS
id }
name }
sourceOrder `;
isDownloaded
} export const DOWNLOAD_UPDATES_FIELDS = gql`
${DOWNLOAD_TYPE_FIELDS}
manga {
id fragment DOWNLOAD_UPDATES_FIELDS on DownloadUpdates {
title state
downloadCount omittedUpdates
}
updates {
progress type
state download {
tries ...DOWNLOAD_TYPE_FIELDS
position
}
} }
} }
`; `;

View File

@@ -214,14 +214,27 @@ export type DownloadStatusFieldPolicy = {
queue?: FieldPolicy<any> | FieldReadFunction<any>, queue?: FieldPolicy<any> | FieldReadFunction<any>,
state?: FieldPolicy<any> | FieldReadFunction<any> state?: FieldPolicy<any> | FieldReadFunction<any>
}; };
export type DownloadTypeKeySpecifier = ('chapter' | 'manga' | 'progress' | 'state' | 'tries' | DownloadTypeKeySpecifier)[]; export type DownloadTypeKeySpecifier = ('chapter' | 'manga' | 'position' | 'progress' | 'state' | 'tries' | DownloadTypeKeySpecifier)[];
export type DownloadTypeFieldPolicy = { export type DownloadTypeFieldPolicy = {
chapter?: FieldPolicy<any> | FieldReadFunction<any>, chapter?: FieldPolicy<any> | FieldReadFunction<any>,
manga?: FieldPolicy<any> | FieldReadFunction<any>, manga?: FieldPolicy<any> | FieldReadFunction<any>,
position?: FieldPolicy<any> | FieldReadFunction<any>,
progress?: FieldPolicy<any> | FieldReadFunction<any>, progress?: FieldPolicy<any> | FieldReadFunction<any>,
state?: FieldPolicy<any> | FieldReadFunction<any>, state?: FieldPolicy<any> | FieldReadFunction<any>,
tries?: FieldPolicy<any> | FieldReadFunction<any> tries?: FieldPolicy<any> | FieldReadFunction<any>
}; };
export type DownloadUpdateKeySpecifier = ('download' | 'type' | DownloadUpdateKeySpecifier)[];
export type DownloadUpdateFieldPolicy = {
download?: FieldPolicy<any> | FieldReadFunction<any>,
type?: FieldPolicy<any> | FieldReadFunction<any>
};
export type DownloadUpdatesKeySpecifier = ('initial' | 'omittedUpdates' | 'state' | 'updates' | DownloadUpdatesKeySpecifier)[];
export type DownloadUpdatesFieldPolicy = {
initial?: FieldPolicy<any> | FieldReadFunction<any>,
omittedUpdates?: FieldPolicy<any> | FieldReadFunction<any>,
state?: FieldPolicy<any> | FieldReadFunction<any>,
updates?: FieldPolicy<any> | FieldReadFunction<any>
};
export type EdgeKeySpecifier = ('cursor' | 'node' | EdgeKeySpecifier)[]; export type EdgeKeySpecifier = ('cursor' | 'node' | EdgeKeySpecifier)[];
export type EdgeFieldPolicy = { export type EdgeFieldPolicy = {
cursor?: FieldPolicy<any> | FieldReadFunction<any>, cursor?: FieldPolicy<any> | FieldReadFunction<any>,
@@ -822,9 +835,10 @@ export type StopDownloaderPayloadFieldPolicy = {
clientMutationId?: FieldPolicy<any> | FieldReadFunction<any>, clientMutationId?: FieldPolicy<any> | FieldReadFunction<any>,
downloadStatus?: FieldPolicy<any> | FieldReadFunction<any> downloadStatus?: FieldPolicy<any> | FieldReadFunction<any>
}; };
export type SubscriptionKeySpecifier = ('downloadChanged' | 'updateStatusChanged' | 'webUIUpdateStatusChange' | SubscriptionKeySpecifier)[]; export type SubscriptionKeySpecifier = ('downloadChanged' | 'downloadStatusChanged' | 'updateStatusChanged' | 'webUIUpdateStatusChange' | SubscriptionKeySpecifier)[];
export type SubscriptionFieldPolicy = { export type SubscriptionFieldPolicy = {
downloadChanged?: FieldPolicy<any> | FieldReadFunction<any>, downloadChanged?: FieldPolicy<any> | FieldReadFunction<any>,
downloadStatusChanged?: FieldPolicy<any> | FieldReadFunction<any>,
updateStatusChanged?: FieldPolicy<any> | FieldReadFunction<any>, updateStatusChanged?: FieldPolicy<any> | FieldReadFunction<any>,
webUIUpdateStatusChange?: FieldPolicy<any> | FieldReadFunction<any> webUIUpdateStatusChange?: FieldPolicy<any> | FieldReadFunction<any>
}; };
@@ -1201,6 +1215,14 @@ export type StrictTypedTypePolicies = {
keyFields?: false | DownloadTypeKeySpecifier | (() => undefined | DownloadTypeKeySpecifier), keyFields?: false | DownloadTypeKeySpecifier | (() => undefined | DownloadTypeKeySpecifier),
fields?: DownloadTypeFieldPolicy, fields?: DownloadTypeFieldPolicy,
}, },
DownloadUpdate?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | DownloadUpdateKeySpecifier | (() => undefined | DownloadUpdateKeySpecifier),
fields?: DownloadUpdateFieldPolicy,
},
DownloadUpdates?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | DownloadUpdatesKeySpecifier | (() => undefined | DownloadUpdatesKeySpecifier),
fields?: DownloadUpdatesFieldPolicy,
},
Edge?: Omit<TypePolicy, "fields" | "keyFields"> & { Edge?: Omit<TypePolicy, "fields" | "keyFields"> & {
keyFields?: false | EdgeKeySpecifier | (() => undefined | EdgeKeySpecifier), keyFields?: false | EdgeKeySpecifier | (() => undefined | EdgeKeySpecifier),
fields?: EdgeFieldPolicy, fields?: EdgeFieldPolicy,

View File

@@ -467,6 +467,11 @@ export type DoubleFilterInput = {
notIn?: InputMaybe<Array<Scalars['Float']['input']>>; notIn?: InputMaybe<Array<Scalars['Float']['input']>>;
}; };
export type DownloadChangedInput = {
/** Sets a max number of updates that can be contained in a download update message.Everything above this limit will be omitted and the "downloadStatus" should be re-fetched via the corresponding query. Due to the graphql subscription execution strategy not supporting batching for data loaders, the data loaders run into the n+1 problem, which can cause the server to get unresponsive until the status update has been handled. This is an issue e.g. when mass en- or dequeuing downloads. */
maxUpdates?: InputMaybe<Scalars['Int']['input']>;
};
export type DownloadEdge = Edge & { export type DownloadEdge = Edge & {
__typename?: 'DownloadEdge'; __typename?: 'DownloadEdge';
cursor: Scalars['Cursor']['output']; cursor: Scalars['Cursor']['output'];
@@ -498,11 +503,37 @@ export type DownloadType = {
__typename?: 'DownloadType'; __typename?: 'DownloadType';
chapter: ChapterType; chapter: ChapterType;
manga: MangaType; manga: MangaType;
position: Scalars['Int']['output'];
progress: Scalars['Float']['output']; progress: Scalars['Float']['output'];
state: DownloadState; state: DownloadState;
tries: Scalars['Int']['output']; tries: Scalars['Int']['output'];
}; };
export type DownloadUpdate = {
__typename?: 'DownloadUpdate';
download: DownloadType;
type: DownloadUpdateType;
};
export enum DownloadUpdateType {
Dequeued = 'DEQUEUED',
Error = 'ERROR',
Finished = 'FINISHED',
Position = 'POSITION',
Progress = 'PROGRESS',
Queued = 'QUEUED'
}
export type DownloadUpdates = {
__typename?: 'DownloadUpdates';
/** The current download queue at the time of sending initial message. Is null for all following messages */
initial?: Maybe<Array<DownloadType>>;
/** Indicates whether updates have been omitted based on the "maxUpdates" subscription variable. In case updates have been omitted, the "downloadStatus" query should be re-fetched. */
omittedUpdates: Scalars['Boolean']['output'];
state: DownloaderState;
updates: Array<DownloadUpdate>;
};
export enum DownloaderState { export enum DownloaderState {
Started = 'STARTED', Started = 'STARTED',
Stopped = 'STOPPED' Stopped = 'STOPPED'
@@ -1415,7 +1446,7 @@ export type MutationUpdateWebUiArgs = {
input: WebUiUpdateInput; input: WebUiUpdateInput;
}; };
export type Node = CategoryMetaType | CategoryType | ChapterMetaType | ChapterType | DownloadType | ExtensionType | GlobalMetaType | MangaMetaType | MangaType | PartialSettingsType | SettingsType | SourceMetaType | SourceType | TrackRecordType | TrackerType; export type Node = CategoryMetaType | CategoryType | ChapterMetaType | ChapterType | DownloadType | DownloadUpdate | ExtensionType | GlobalMetaType | MangaMetaType | MangaType | PartialSettingsType | SettingsType | SourceMetaType | SourceType | TrackRecordType | TrackerType;
export type NodeList = { export type NodeList = {
/** A list of edges which contains the [T] and cursor to aid in pagination. */ /** A list of edges which contains the [T] and cursor to aid in pagination. */
@@ -2177,11 +2208,18 @@ export type StringFilterInput = {
export type Subscription = { export type Subscription = {
__typename?: 'Subscription'; __typename?: 'Subscription';
/** @deprecated Replaced width downloadStatusChanged, replace with downloadStatusChanged(input) */
downloadChanged: DownloadStatus; downloadChanged: DownloadStatus;
downloadStatusChanged: DownloadUpdates;
updateStatusChanged: UpdateStatus; updateStatusChanged: UpdateStatus;
webUIUpdateStatusChange: WebUiUpdateStatus; webUIUpdateStatusChange: WebUiUpdateStatus;
}; };
export type SubscriptionDownloadStatusChangedArgs = {
input: DownloadChangedInput;
};
export type SwitchPreference = { export type SwitchPreference = {
__typename?: 'SwitchPreference'; __typename?: 'SwitchPreference';
currentValue?: Maybe<Scalars['Boolean']['output']>; currentValue?: Maybe<Scalars['Boolean']['output']>;
@@ -2731,8 +2769,12 @@ export type ChapterListFieldsFragment = { __typename?: 'ChapterType', fetchedAt:
export type ChapterUpdateListFieldsFragment = { __typename?: 'ChapterType', fetchedAt: string, uploadDate: string, id: number, name: string, mangaId: number, scanlator?: string | null, realUrl?: string | null, sourceOrder: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean, manga: { __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string } }; export type ChapterUpdateListFieldsFragment = { __typename?: 'ChapterType', fetchedAt: string, uploadDate: string, id: number, name: string, mangaId: number, scanlator?: string | null, realUrl?: string | null, sourceOrder: number, chapterNumber: number, isRead: boolean, isDownloaded: boolean, isBookmarked: boolean, manga: { __typename?: 'MangaType', id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string } };
export type DownloadTypeFieldsFragment = { __typename?: 'DownloadType', progress: number, state: DownloadState, tries: number, chapter: { __typename?: 'ChapterType', id: number, name: string, sourceOrder: number, isDownloaded: boolean }, manga: { __typename?: 'MangaType', id: number, title: string, downloadCount: number } };
export type DownloadStatusFieldsFragment = { __typename?: 'DownloadStatus', state: DownloaderState, queue: Array<{ __typename?: 'DownloadType', progress: number, state: DownloadState, tries: number, chapter: { __typename?: 'ChapterType', id: number, name: string, sourceOrder: number, isDownloaded: boolean }, manga: { __typename?: 'MangaType', id: number, title: string, downloadCount: number } }> }; export type DownloadStatusFieldsFragment = { __typename?: 'DownloadStatus', state: DownloaderState, queue: Array<{ __typename?: 'DownloadType', progress: number, state: DownloadState, tries: number, chapter: { __typename?: 'ChapterType', id: number, name: string, sourceOrder: number, isDownloaded: boolean }, manga: { __typename?: 'MangaType', id: number, title: string, downloadCount: number } }> };
export type DownloadUpdatesFieldsFragment = { __typename?: 'DownloadUpdates', state: DownloaderState, omittedUpdates: boolean, updates: Array<{ __typename?: 'DownloadUpdate', type: DownloadUpdateType, download: { __typename?: 'DownloadType', position: number, progress: number, state: DownloadState, tries: number, chapter: { __typename?: 'ChapterType', id: number, name: string, sourceOrder: number, isDownloaded: boolean }, manga: { __typename?: 'MangaType', id: number, title: string, downloadCount: number } } }> };
export type ExtensionListFieldsFragment = { __typename?: 'ExtensionType', pkgName: string, name: string, lang: string, versionCode: number, versionName: string, iconUrl: string, repo?: string | null, isNsfw: boolean, isInstalled: boolean, isObsolete: boolean, hasUpdate: boolean }; export type ExtensionListFieldsFragment = { __typename?: 'ExtensionType', pkgName: string, name: string, lang: string, versionCode: number, versionName: string, iconUrl: string, repo?: string | null, isNsfw: boolean, isInstalled: boolean, isObsolete: boolean, hasUpdate: boolean };
export type PageInfoFragment = { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null }; export type PageInfoFragment = { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null };
@@ -3554,10 +3596,12 @@ export type GetLastUpdateTimestampQueryVariables = Exact<{ [key: string]: never;
export type GetLastUpdateTimestampQuery = { __typename?: 'Query', lastUpdateTimestamp: { __typename?: 'LastUpdateTimestampPayload', timestamp: string } }; export type GetLastUpdateTimestampQuery = { __typename?: 'Query', lastUpdateTimestamp: { __typename?: 'LastUpdateTimestampPayload', timestamp: string } };
export type DownloadStatusSubscriptionVariables = Exact<{ [key: string]: never; }>; export type DownloadStatusSubscriptionVariables = Exact<{
input: DownloadChangedInput;
}>;
export type DownloadStatusSubscription = { __typename?: 'Subscription', downloadChanged: { __typename?: 'DownloadStatus', state: DownloaderState, queue: Array<{ __typename?: 'DownloadType', progress: number, state: DownloadState, tries: number, chapter: { __typename?: 'ChapterType', id: number, name: string, sourceOrder: number, isDownloaded: boolean }, manga: { __typename?: 'MangaType', id: number, title: string, downloadCount: number } }> } }; export type DownloadStatusSubscription = { __typename?: 'Subscription', downloadStatusChanged: { __typename?: 'DownloadUpdates', state: DownloaderState, omittedUpdates: boolean, updates: Array<{ __typename?: 'DownloadUpdate', type: DownloadUpdateType, download: { __typename?: 'DownloadType', position: number, progress: number, state: DownloadState, tries: number, chapter: { __typename?: 'ChapterType', id: number, name: string, sourceOrder: number, isDownloaded: boolean }, manga: { __typename?: 'MangaType', id: number, title: string, downloadCount: number } } }> } };
export type WebuiUpdateSubscriptionVariables = Exact<{ [key: string]: never; }>; export type WebuiUpdateSubscriptionVariables = Exact<{ [key: string]: never; }>;

View File

@@ -7,14 +7,14 @@
*/ */
import gql from 'graphql-tag'; import gql from 'graphql-tag';
import { DOWNLOAD_STATUS_FIELDS } from '@/lib/graphql/fragments/DownloadFragments.ts'; import { DOWNLOAD_UPDATES_FIELDS } from '@/lib/graphql/fragments/DownloadFragments.ts';
export const DOWNLOAD_STATUS_SUBSCRIPTION = gql` export const DOWNLOAD_STATUS_SUBSCRIPTION = gql`
${DOWNLOAD_STATUS_FIELDS} ${DOWNLOAD_UPDATES_FIELDS}
subscription DOWNLOAD_STATUS_SUBSCRIPTION { subscription DOWNLOAD_STATUS_SUBSCRIPTION($input: DownloadChangedInput!) {
downloadChanged { downloadStatusChanged(input: $input) {
...DOWNLOAD_STATUS_FIELDS ...DOWNLOAD_UPDATES_FIELDS
} }
} }
`; `;

View File

@@ -68,6 +68,7 @@ import {
DequeueChapterDownloadsMutationVariables, DequeueChapterDownloadsMutationVariables,
DownloadStatusSubscription, DownloadStatusSubscription,
DownloadStatusSubscriptionVariables, DownloadStatusSubscriptionVariables,
DownloadUpdateType,
EnqueueChapterDownloadMutation, EnqueueChapterDownloadMutation,
EnqueueChapterDownloadMutationVariables, EnqueueChapterDownloadMutationVariables,
EnqueueChapterDownloadsMutation, EnqueueChapterDownloadsMutation,
@@ -2777,10 +2778,11 @@ export class RequestManager {
const wrappedMutate = (mutationOptions: Parameters<typeof mutate>[0]) => { const wrappedMutate = (mutationOptions: Parameters<typeof mutate>[0]) => {
const variables = mutationOptions?.variables?.input; const variables = mutationOptions?.variables?.input;
const cachedDownloadStatus = this.graphQLClient.client.readFragment< const cachedDownloadStatus = this.graphQLClient.client.readFragment<
DownloadStatusSubscription['downloadChanged'] GetDownloadStatusQuery['downloadStatus']
>({ >({
id: 'DownloadStatus:{}', id: 'DownloadStatus:{}',
fragment: DOWNLOAD_STATUS_FIELDS, fragment: DOWNLOAD_STATUS_FIELDS,
fragmentName: 'DOWNLOAD_STATUS_FIELDS',
}); });
if (!variables) { if (!variables) {
@@ -2933,7 +2935,84 @@ export class RequestManager {
public useDownloadSubscription( public useDownloadSubscription(
options?: SubscriptionHookOptions<DownloadStatusSubscription, DownloadStatusSubscriptionVariables>, options?: SubscriptionHookOptions<DownloadStatusSubscription, DownloadStatusSubscriptionVariables>,
): SubscriptionResult<DownloadStatusSubscription, DownloadStatusSubscriptionVariables> { ): SubscriptionResult<DownloadStatusSubscription, DownloadStatusSubscriptionVariables> {
return this.doRequest(GQLMethod.USE_SUBSCRIPTION, DOWNLOAD_STATUS_SUBSCRIPTION, {}, options); return this.doRequest(
GQLMethod.USE_SUBSCRIPTION,
DOWNLOAD_STATUS_SUBSCRIPTION,
{ input: { maxUpdates: 30 } },
{
...options,
onData: (onDataOptions) => {
const downloadChanged = onDataOptions.data.data?.downloadStatusChanged;
const { cache } = this.graphQLClient.client;
if (downloadChanged?.omittedUpdates) {
cache.evict({ broadcast: true, fieldName: 'downloadStatus' });
cache.evict({ broadcast: true, id: 'DownloadStatus:{}' });
return;
}
const downloadsToRemove =
downloadChanged?.updates
.filter((update) =>
[
DownloadUpdateType.Dequeued,
DownloadUpdateType.Finished,
DownloadUpdateType.Position,
].includes(update.type),
)
.map((update) => update.download.chapter.id) ?? [];
const downloadsToAdd =
downloadChanged?.updates
.filter((update) => update.type === DownloadUpdateType.Queued)
.map((update) => update.download) ?? [];
const downloadsToReorder =
downloadChanged?.updates
.filter((update) => update.type === DownloadUpdateType.Position)
.map((update) => update.download) ?? [];
cache.updateQuery<GetDownloadStatusQuery, GetDownloadStatusQueryVariables>(
{
query: GET_DOWNLOAD_STATUS,
variables: {},
},
(data) => {
if (!data) {
return data;
}
const queueWithAddedDownloads = [...data.downloadStatus.queue, ...downloadsToAdd];
const queueWithoutRemovedDownloads = !downloadsToRemove.length
? queueWithAddedDownloads
: queueWithAddedDownloads.filter(
(download) => !downloadsToRemove.includes(download.chapter.id),
);
const queueWithReorderedDownloads = !downloadsToReorder.length
? queueWithoutRemovedDownloads
: (() => {
const tmpQueue = [...queueWithoutRemovedDownloads];
downloadsToReorder.forEach((download) => {
tmpQueue.splice(download.position, 0, download);
});
return tmpQueue;
})();
return {
...data,
downloadStatus: {
...data.downloadStatus,
state: downloadChanged?.state ?? data.downloadStatus.state,
queue: queueWithReorderedDownloads,
},
};
},
);
},
},
);
} }
public useUpdaterSubscription( public useUpdaterSubscription(

View File

@@ -87,11 +87,16 @@ const typePolicies: StrictTypedTypePolicies = {
key: args?.key, key: args?.key,
}); });
}, },
downloadStatus(_, { toReference }) { downloadStatus: {
return toReference({ read(_, { toReference }) {
__typename: 'DownloadStatus', return toReference({
key: {}, __typename: 'DownloadStatus',
}); key: {},
});
},
merge(_, incoming) {
return incoming;
},
}, },
getWebUIUpdateStatus(_, { toReference }) { getWebUIUpdateStatus(_, { toReference }) {
return toReference({ return toReference({

View File

@@ -57,7 +57,7 @@ export const DownloadStateIndicator = ({ download }: { download: ChapterDownload
}} }}
> >
<> <>
{isDownloading && `${Math.round(download.progress * 100)}%`} {(isDownloading || isPartiallyDownloaded) && `${Math.round(download.progress * 100)}%`}
{!isDownloading && {!isDownloading &&
!isPartiallyDownloaded && !isPartiallyDownloaded &&
t(DOWNLOAD_STATE_TO_TRANSLATION_KEY_MAP[download.state])} t(DOWNLOAD_STATE_TO_TRANSLATION_KEY_MAP[download.state])}

View File

@@ -34,7 +34,7 @@ import { NavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.t
import { LoadingPlaceholder } from '@/modules/core/components/placeholder/LoadingPlaceholder.tsx'; import { LoadingPlaceholder } from '@/modules/core/components/placeholder/LoadingPlaceholder.tsx';
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'; import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
import { ChapterDownloadStatus, ChapterIdInfo } from '@/modules/chapter/services/Chapters.ts'; import { ChapterDownloadStatus, ChapterIdInfo } from '@/modules/chapter/services/Chapters.ts';
import { DownloadState } from '@/lib/graphql/generated/graphql.ts'; import { DownloaderState, DownloadState } from '@/lib/graphql/generated/graphql.ts';
const HeightPreservingItem = ({ children, ...props }: BoxProps) => ( const HeightPreservingItem = ({ children, ...props }: BoxProps) => (
// the height is necessary to prevent the item container from collapsing, which confuses Virtuoso measurements // the height is necessary to prevent the item container from collapsing, which confuses Virtuoso measurements
@@ -145,7 +145,7 @@ export const DownloadQueue: React.FC = () => {
}; };
const toggleQueueStatus = () => { const toggleQueueStatus = () => {
if (status === 'STOPPED') { if (status === DownloaderState.Stopped) {
requestManager.startDownloads(); requestManager.startDownloads();
} else { } else {
requestManager.stopDownloads(); requestManager.stopDownloads();
@@ -162,9 +162,9 @@ export const DownloadQueue: React.FC = () => {
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Tooltip title={t(status === 'STOPPED' ? 'global.button.start' : 'global.button.stop')}> <Tooltip title={t(status === DownloaderState.Started ? 'global.button.start' : 'global.button.stop')}>
<IconButton onClick={toggleQueueStatus} size="large" disabled={isQueueEmpty} color="inherit"> <IconButton onClick={toggleQueueStatus} size="large" disabled={isQueueEmpty} color="inherit">
{status === 'STOPPED' ? <PlayArrowIcon /> : <PauseIcon />} {status === DownloaderState.Stopped ? <PlayArrowIcon /> : <PauseIcon />}
</IconButton> </IconButton>
</Tooltip> </Tooltip>
</>, </>,
@@ -220,7 +220,7 @@ export const DownloadQueue: React.FC = () => {
}; };
const handleDelete = async (chapter: ChapterIdInfo) => { const handleDelete = async (chapter: ChapterIdInfo) => {
const isRunning = status === 'STARTED'; const isRunning = status === DownloaderState.Started;
try { try {
if (isRunning) { if (isRunning) {
@@ -287,9 +287,9 @@ export const DownloadQueue: React.FC = () => {
components={{ components={{
Item: HeightPreservingItem, Item: HeightPreservingItem,
}} }}
computeItemKey={(_, item) => item.manga.id} computeItemKey={(_, item) => item.chapter.id}
itemContent={(index, item) => ( itemContent={(index, item) => (
<Draggable draggableId={`${item.manga.id}-${item.chapter.sourceOrder}`} index={index}> <Draggable draggableId={`${item.chapter.id}`} index={index}>
{(draggableProvided) => ( {(draggableProvided) => (
<DownloadChapterItem <DownloadChapterItem
provided={draggableProvided} provided={draggableProvided}

View File

@@ -1,7 +1,7 @@
[ [
{ {
"uiVersion": "PREVIEW", "uiVersion": "PREVIEW",
"serverVersion": "r1566" "serverVersion": "r1611"
}, },
{ {
"tag": "v1.1.0", "tag": "v1.1.0",