diff --git a/schema.graphql b/schema.graphql index 4c561e3b..d9eb5beb 100644 --- a/schema.graphql +++ b/schema.graphql @@ -456,6 +456,11 @@ type PartialSettingsType implements Settings { excludeNotStarted: Boolean excludeUnreadChapters: Boolean extensionRepos: [String!] + flareSolverrEnabled: Boolean + flareSolverrSessionName: String + flareSolverrSessionTtl: Int + flareSolverrTimeout: Int + flareSolverrUrl: String globalUpdateInterval: Float gqlDebugLogsEnabled: Boolean initialOpenInBrowserEnabled: Boolean @@ -566,6 +571,11 @@ type SettingsType implements Settings { excludeNotStarted: Boolean! excludeUnreadChapters: Boolean! extensionRepos: [String!]! + flareSolverrEnabled: Boolean! + flareSolverrSessionName: String! + flareSolverrSessionTtl: Int! + flareSolverrTimeout: Int! + flareSolverrUrl: String! globalUpdateInterval: Float! gqlDebugLogsEnabled: Boolean! initialOpenInBrowserEnabled: Boolean! @@ -683,6 +693,10 @@ type TrackSearchType { trackingUrl: String! tracker: TrackerType! } +type TrackStatusType { + name: String! + value: Int! +} type TrackerEdge implements Edge { cursor: Cursor! node: TrackerType! @@ -699,6 +713,8 @@ type TrackerType { id: Int! isLoggedIn: Boolean! name: String! + scores: [String!]! + statuses: [TrackStatusType!]! trackRecords: TrackRecordNodeList! } type TriStateFilter { @@ -849,6 +865,11 @@ interface Settings { excludeNotStarted: Boolean excludeUnreadChapters: Boolean extensionRepos: [String!] + flareSolverrEnabled: Boolean + flareSolverrSessionName: String + flareSolverrSessionTtl: Int + flareSolverrTimeout: Int + flareSolverrUrl: String globalUpdateInterval: Float gqlDebugLogsEnabled: Boolean initialOpenInBrowserEnabled: Boolean @@ -1377,6 +1398,11 @@ input PartialSettingsTypeInput { excludeNotStarted: Boolean excludeUnreadChapters: Boolean extensionRepos: [String!] + flareSolverrEnabled: Boolean + flareSolverrSessionName: String + flareSolverrSessionTtl: Int + flareSolverrTimeout: Int + flareSolverrUrl: String globalUpdateInterval: Float gqlDebugLogsEnabled: Boolean initialOpenInBrowserEnabled: Boolean diff --git a/src/gql/Fragments.gql b/src/gql/Fragments.gql index dc2e924a..66d2f060 100644 --- a/src/gql/Fragments.gql +++ b/src/gql/Fragments.gql @@ -44,6 +44,15 @@ fragment MangaTypeFragment on MangaType { thumbnailUrl unreadCount downloadCount + latestFetchedChapter { + fetchedAt + } + latestUploadedChapter { + uploadDate + } + latestReadChapter { + lastReadAt + } lastReadChapter { lastReadAt } diff --git a/src/gql/Mutations.gql b/src/gql/Mutations.gql index 398a058c..c5b3e851 100644 --- a/src/gql/Mutations.gql +++ b/src/gql/Mutations.gql @@ -503,3 +503,49 @@ mutation updateTrack($input: UpdateTrackInput!) { } } } + +mutation setServerSettings($settings: PartialSettingsTypeInput = {}) { + setSettings(input: { settings: $settings }) { + settings { + autoDownloadAheadLimit + autoDownloadNewChapters + backupInterval + backupPath + backupTTL + backupTime + basicAuthEnabled + basicAuthPassword + basicAuthUsername + debugLogsEnabled + downloadAsCbz + downloadsPath + electronPath + excludeCompleted + excludeNotStarted + excludeEntryWithUnreadChapters + excludeUnreadChapters + flareSolverrEnabled + extensionRepos + flareSolverrSessionName + flareSolverrSessionTtl + flareSolverrTimeout + flareSolverrUrl + globalUpdateInterval + gqlDebugLogsEnabled + initialOpenInBrowserEnabled + ip + localSourcePath + maxSourcesInParallel + port + socksProxyEnabled + socksProxyHost + socksProxyPort + systemTrayEnabled + updateMangas + webUIChannel + webUIFlavor + webUIInterface + webUIUpdateCheckInterval + } + } +} diff --git a/src/gql/Queries.gql b/src/gql/Queries.gql index e2dd2507..9958adbd 100644 --- a/src/gql/Queries.gql +++ b/src/gql/Queries.gql @@ -25,7 +25,13 @@ query category($id: Int!) { thumbnailUrl unreadCount downloadCount - lastReadChapter { + latestFetchedChapter { + fetchedAt + } + latestUploadedChapter { + uploadDate + } + latestReadChapter { lastReadAt } chapters { @@ -412,3 +418,47 @@ query trackRecords { } } } + +query serverSettings { + settings { + autoDownloadAheadLimit + autoDownloadNewChapters + backupInterval + backupPath + backupTTL + backupTime + basicAuthEnabled + basicAuthPassword + basicAuthUsername + debugLogsEnabled + downloadAsCbz + downloadsPath + electronPath + excludeCompleted + excludeEntryWithUnreadChapters + excludeNotStarted + extensionRepos + excludeUnreadChapters + flareSolverrEnabled + flareSolverrSessionName + flareSolverrSessionTtl + flareSolverrTimeout + flareSolverrUrl + globalUpdateInterval + gqlDebugLogsEnabled + initialOpenInBrowserEnabled + ip + localSourcePath + maxSourcesInParallel + port + socksProxyEnabled + socksProxyHost + socksProxyPort + systemTrayEnabled + updateMangas + webUIChannel + webUIFlavor + webUIInterface + webUIUpdateCheckInterval + } +} diff --git a/src/lib/generated.ts b/src/lib/generated.ts index 4d18618d..1adeb7c9 100644 --- a/src/lib/generated.ts +++ b/src/lib/generated.ts @@ -1380,6 +1380,11 @@ export type PartialSettingsType = Settings & { excludeNotStarted?: Maybe; excludeUnreadChapters?: Maybe; extensionRepos?: Maybe>; + flareSolverrEnabled?: Maybe; + flareSolverrSessionName?: Maybe; + flareSolverrSessionTtl?: Maybe; + flareSolverrTimeout?: Maybe; + flareSolverrUrl?: Maybe; globalUpdateInterval?: Maybe; gqlDebugLogsEnabled?: Maybe; initialOpenInBrowserEnabled?: Maybe; @@ -1417,6 +1422,11 @@ export type PartialSettingsTypeInput = { excludeNotStarted?: InputMaybe; excludeUnreadChapters?: InputMaybe; extensionRepos?: InputMaybe>; + flareSolverrEnabled?: InputMaybe; + flareSolverrSessionName?: InputMaybe; + flareSolverrSessionTtl?: InputMaybe; + flareSolverrTimeout?: InputMaybe; + flareSolverrUrl?: InputMaybe; globalUpdateInterval?: InputMaybe; gqlDebugLogsEnabled?: InputMaybe; initialOpenInBrowserEnabled?: InputMaybe; @@ -1757,6 +1767,11 @@ export type Settings = { excludeNotStarted?: Maybe; excludeUnreadChapters?: Maybe; extensionRepos?: Maybe>; + flareSolverrEnabled?: Maybe; + flareSolverrSessionName?: Maybe; + flareSolverrSessionTtl?: Maybe; + flareSolverrTimeout?: Maybe; + flareSolverrUrl?: Maybe; globalUpdateInterval?: Maybe; gqlDebugLogsEnabled?: Maybe; initialOpenInBrowserEnabled?: Maybe; @@ -1795,6 +1810,11 @@ export type SettingsType = Settings & { excludeNotStarted: Scalars['Boolean']['output']; excludeUnreadChapters: Scalars['Boolean']['output']; extensionRepos: Array; + flareSolverrEnabled: Scalars['Boolean']['output']; + flareSolverrSessionName: Scalars['String']['output']; + flareSolverrSessionTtl: Scalars['Int']['output']; + flareSolverrTimeout: Scalars['Int']['output']; + flareSolverrUrl: Scalars['String']['output']; globalUpdateInterval: Scalars['Float']['output']; gqlDebugLogsEnabled: Scalars['Boolean']['output']; initialOpenInBrowserEnabled: Scalars['Boolean']['output']; @@ -2081,6 +2101,12 @@ export type TrackSearchType = { trackingUrl: Scalars['String']['output']; }; +export type TrackStatusType = { + __typename?: 'TrackStatusType'; + name: Scalars['String']['output']; + value: Scalars['Int']['output']; +}; + export type TrackerConditionInput = { icon?: InputMaybe; id?: InputMaybe; @@ -2115,6 +2141,8 @@ export type TrackerType = { id: Scalars['Int']['output']; isLoggedIn: Scalars['Boolean']['output']; name: Scalars['String']['output']; + scores: Array; + statuses: Array; trackRecords: TrackRecordNodeList; }; @@ -2450,7 +2478,7 @@ export type TrackRecordTypeFragmentFragment = { __typename?: 'TrackRecordType', export type ChapterTypeFragmentFragment = { __typename?: 'ChapterType', isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, id: number, chapterNumber: number, fetchedAt: any, lastPageRead: number, name: string, sourceOrder: number, uploadDate: any, pageCount: number, scanlator?: string | null }; -export type MangaTypeFragmentFragment = { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, genre: Array, id: number, inLibrary: boolean, lastFetchedAt?: any | null, realUrl?: string | null, status: MangaStatus, title: string, thumbnailUrl?: string | null, unreadCount: number, downloadCount: number, lastReadChapter?: { __typename?: 'ChapterType', lastReadAt: any } | null, meta: Array<{ __typename?: 'MangaMetaType', value: string, key: string }>, source?: { __typename?: 'SourceType', displayName: string } | null, categories: { __typename?: 'CategoryNodeList', nodes: Array<{ __typename?: 'CategoryType', id: number }> }, trackRecords: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, mangaId: number, remoteId: any, remoteUrl: string, title: string, trackerId: number }> } }; +export type MangaTypeFragmentFragment = { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, genre: Array, id: number, inLibrary: boolean, lastFetchedAt?: any | null, realUrl?: string | null, status: MangaStatus, title: string, thumbnailUrl?: string | null, unreadCount: number, downloadCount: number, latestFetchedChapter?: { __typename?: 'ChapterType', fetchedAt: any } | null, latestUploadedChapter?: { __typename?: 'ChapterType', uploadDate: any } | null, latestReadChapter?: { __typename?: 'ChapterType', lastReadAt: any } | null, lastReadChapter?: { __typename?: 'ChapterType', lastReadAt: any } | null, meta: Array<{ __typename?: 'MangaMetaType', value: string, key: string }>, source?: { __typename?: 'SourceType', displayName: string } | null, categories: { __typename?: 'CategoryNodeList', nodes: Array<{ __typename?: 'CategoryType', id: number }> }, trackRecords: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, mangaId: number, remoteId: any, remoteUrl: string, title: string, trackerId: number }> } }; export type SourceTypeFragmentFragment = { __typename?: 'SourceType', id: any, displayName: string, iconUrl: string, lang: string }; @@ -2529,7 +2557,7 @@ export type FetchMangaInfoMutationVariables = Exact<{ }>; -export type FetchMangaInfoMutation = { __typename?: 'Mutation', fetchManga: { __typename?: 'FetchMangaPayload', manga: { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, genre: Array, id: number, inLibrary: boolean, lastFetchedAt?: any | null, realUrl?: string | null, status: MangaStatus, title: string, thumbnailUrl?: string | null, unreadCount: number, downloadCount: number, lastReadChapter?: { __typename?: 'ChapterType', lastReadAt: any } | null, meta: Array<{ __typename?: 'MangaMetaType', value: string, key: string }>, source?: { __typename?: 'SourceType', displayName: string } | null, categories: { __typename?: 'CategoryNodeList', nodes: Array<{ __typename?: 'CategoryType', id: number }> }, trackRecords: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, mangaId: number, remoteId: any, remoteUrl: string, title: string, trackerId: number }> } } } }; +export type FetchMangaInfoMutation = { __typename?: 'Mutation', fetchManga: { __typename?: 'FetchMangaPayload', manga: { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, genre: Array, id: number, inLibrary: boolean, lastFetchedAt?: any | null, realUrl?: string | null, status: MangaStatus, title: string, thumbnailUrl?: string | null, unreadCount: number, downloadCount: number, latestFetchedChapter?: { __typename?: 'ChapterType', fetchedAt: any } | null, latestUploadedChapter?: { __typename?: 'ChapterType', uploadDate: any } | null, latestReadChapter?: { __typename?: 'ChapterType', lastReadAt: any } | null, lastReadChapter?: { __typename?: 'ChapterType', lastReadAt: any } | null, meta: Array<{ __typename?: 'MangaMetaType', value: string, key: string }>, source?: { __typename?: 'SourceType', displayName: string } | null, categories: { __typename?: 'CategoryNodeList', nodes: Array<{ __typename?: 'CategoryType', id: number }> }, trackRecords: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, mangaId: number, remoteId: any, remoteUrl: string, title: string, trackerId: number }> } } } }; export type FetchMangaChaptersMutationVariables = Exact<{ id: Scalars['Int']['input']; @@ -2765,6 +2793,13 @@ export type UpdateTrackMutationVariables = Exact<{ export type UpdateTrackMutation = { __typename?: 'Mutation', updateTrack: { __typename?: 'UpdateTrackPayload', trackRecord?: { __typename?: 'TrackRecordType', id: number, mangaId: number, remoteId: any, remoteUrl: string, title: string, trackerId: number } | null } }; +export type SetServerSettingsMutationVariables = Exact<{ + settings?: InputMaybe; +}>; + + +export type SetServerSettingsMutation = { __typename?: 'Mutation', setSettings: { __typename?: 'SetSettingsPayload', settings: { __typename?: 'SettingsType', autoDownloadAheadLimit: number, autoDownloadNewChapters: boolean, backupInterval: number, backupPath: string, backupTTL: number, backupTime: string, basicAuthEnabled: boolean, basicAuthPassword: string, basicAuthUsername: string, debugLogsEnabled: boolean, downloadAsCbz: boolean, downloadsPath: string, electronPath: string, excludeCompleted: boolean, excludeNotStarted: boolean, excludeEntryWithUnreadChapters: boolean, excludeUnreadChapters: boolean, flareSolverrEnabled: boolean, extensionRepos: Array, flareSolverrSessionName: string, flareSolverrSessionTtl: number, flareSolverrTimeout: number, flareSolverrUrl: string, globalUpdateInterval: number, gqlDebugLogsEnabled: boolean, initialOpenInBrowserEnabled: boolean, ip: string, localSourcePath: string, maxSourcesInParallel: number, port: number, socksProxyEnabled: boolean, socksProxyHost: string, socksProxyPort: string, systemTrayEnabled: boolean, updateMangas: boolean, webUIChannel: WebUiChannel, webUIFlavor: WebUiFlavor, webUIInterface: WebUiInterface, webUIUpdateCheckInterval: number } } }; + export type CategoriesQueryVariables = Exact<{ notEqualTo?: InputMaybe; }>; @@ -2777,14 +2812,14 @@ export type CategoryQueryVariables = Exact<{ }>; -export type CategoryQuery = { __typename?: 'Query', category: { __typename?: 'CategoryType', mangas: { __typename?: 'MangaNodeList', nodes: Array<{ __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, thumbnailUrl?: string | null, unreadCount: number, downloadCount: number, lastReadChapter?: { __typename?: 'ChapterType', lastReadAt: any } | null, chapters: { __typename?: 'ChapterNodeList', totalCount: number } }> } } }; +export type CategoryQuery = { __typename?: 'Query', category: { __typename?: 'CategoryType', mangas: { __typename?: 'MangaNodeList', nodes: Array<{ __typename?: 'MangaType', id: number, title: string, inLibrary: boolean, thumbnailUrl?: string | null, unreadCount: number, downloadCount: number, latestFetchedChapter?: { __typename?: 'ChapterType', fetchedAt: any } | null, latestUploadedChapter?: { __typename?: 'ChapterType', uploadDate: any } | null, latestReadChapter?: { __typename?: 'ChapterType', lastReadAt: any } | null, chapters: { __typename?: 'ChapterNodeList', totalCount: number } }> } } }; export type GetMangaQueryVariables = Exact<{ id: Scalars['Int']['input']; }>; -export type GetMangaQuery = { __typename?: 'Query', manga: { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, genre: Array, id: number, inLibrary: boolean, lastFetchedAt?: any | null, realUrl?: string | null, status: MangaStatus, title: string, thumbnailUrl?: string | null, unreadCount: number, downloadCount: number, chapters: { __typename?: 'ChapterNodeList', totalCount: number, nodes: Array<{ __typename?: 'ChapterType', isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, id: number, chapterNumber: number, fetchedAt: any, lastPageRead: number, name: string, sourceOrder: number, uploadDate: any, pageCount: number, scanlator?: string | null }> }, lastReadChapter?: { __typename?: 'ChapterType', lastReadAt: any } | null, meta: Array<{ __typename?: 'MangaMetaType', value: string, key: string }>, source?: { __typename?: 'SourceType', displayName: string } | null, categories: { __typename?: 'CategoryNodeList', nodes: Array<{ __typename?: 'CategoryType', id: number }> }, trackRecords: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, mangaId: number, remoteId: any, remoteUrl: string, title: string, trackerId: number }> } } }; +export type GetMangaQuery = { __typename?: 'Query', manga: { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, genre: Array, id: number, inLibrary: boolean, lastFetchedAt?: any | null, realUrl?: string | null, status: MangaStatus, title: string, thumbnailUrl?: string | null, unreadCount: number, downloadCount: number, chapters: { __typename?: 'ChapterNodeList', totalCount: number, nodes: Array<{ __typename?: 'ChapterType', isBookmarked: boolean, isDownloaded: boolean, isRead: boolean, id: number, chapterNumber: number, fetchedAt: any, lastPageRead: number, name: string, sourceOrder: number, uploadDate: any, pageCount: number, scanlator?: string | null }> }, latestFetchedChapter?: { __typename?: 'ChapterType', fetchedAt: any } | null, latestUploadedChapter?: { __typename?: 'ChapterType', uploadDate: any } | null, latestReadChapter?: { __typename?: 'ChapterType', lastReadAt: any } | null, lastReadChapter?: { __typename?: 'ChapterType', lastReadAt: any } | null, meta: Array<{ __typename?: 'MangaMetaType', value: string, key: string }>, source?: { __typename?: 'SourceType', displayName: string } | null, categories: { __typename?: 'CategoryNodeList', nodes: Array<{ __typename?: 'CategoryType', id: number }> }, trackRecords: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, mangaId: number, remoteId: any, remoteUrl: string, title: string, trackerId: number }> } } }; export type GetSingleChapterQueryVariables = Exact<{ id: Scalars['Int']['input']; @@ -2915,6 +2950,11 @@ export type TrackRecordsQueryVariables = Exact<{ [key: string]: never; }>; export type TrackRecordsQuery = { __typename?: 'Query', trackRecords: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, mangaId: number, remoteId: any, remoteUrl: string, title: string, trackerId: number }> } }; +export type ServerSettingsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type ServerSettingsQuery = { __typename?: 'Query', settings: { __typename?: 'SettingsType', autoDownloadAheadLimit: number, autoDownloadNewChapters: boolean, backupInterval: number, backupPath: string, backupTTL: number, backupTime: string, basicAuthEnabled: boolean, basicAuthPassword: string, basicAuthUsername: string, debugLogsEnabled: boolean, downloadAsCbz: boolean, downloadsPath: string, electronPath: string, excludeCompleted: boolean, excludeEntryWithUnreadChapters: boolean, excludeNotStarted: boolean, extensionRepos: Array, excludeUnreadChapters: boolean, flareSolverrEnabled: boolean, flareSolverrSessionName: string, flareSolverrSessionTtl: number, flareSolverrTimeout: number, flareSolverrUrl: string, globalUpdateInterval: number, gqlDebugLogsEnabled: boolean, initialOpenInBrowserEnabled: boolean, ip: string, localSourcePath: string, maxSourcesInParallel: number, port: number, socksProxyEnabled: boolean, socksProxyHost: string, socksProxyPort: string, systemTrayEnabled: boolean, updateMangas: boolean, webUIChannel: WebUiChannel, webUIFlavor: WebUiFlavor, webUIInterface: WebUiInterface, webUIUpdateCheckInterval: number } }; + export type DownloadChangedSubscriptionVariables = Exact<{ [key: string]: never; }>; @@ -2966,6 +3006,15 @@ export const MangaTypeFragmentFragmentDoc = gql` thumbnailUrl unreadCount downloadCount + latestFetchedChapter { + fetchedAt + } + latestUploadedChapter { + uploadDate + } + latestReadChapter { + lastReadAt + } lastReadChapter { lastReadAt } @@ -3505,6 +3554,53 @@ export const UpdateTrackDoc = gql` } } ${TrackRecordTypeFragmentFragmentDoc}`; +export const SetServerSettingsDoc = gql` + mutation setServerSettings($settings: PartialSettingsTypeInput = {}) { + setSettings(input: {settings: $settings}) { + settings { + autoDownloadAheadLimit + autoDownloadNewChapters + backupInterval + backupPath + backupTTL + backupTime + basicAuthEnabled + basicAuthPassword + basicAuthUsername + debugLogsEnabled + downloadAsCbz + downloadsPath + electronPath + excludeCompleted + excludeNotStarted + excludeEntryWithUnreadChapters + excludeUnreadChapters + flareSolverrEnabled + extensionRepos + flareSolverrSessionName + flareSolverrSessionTtl + flareSolverrTimeout + flareSolverrUrl + globalUpdateInterval + gqlDebugLogsEnabled + initialOpenInBrowserEnabled + ip + localSourcePath + maxSourcesInParallel + port + socksProxyEnabled + socksProxyHost + socksProxyPort + systemTrayEnabled + updateMangas + webUIChannel + webUIFlavor + webUIInterface + webUIUpdateCheckInterval + } + } +} + `; export const CategoriesDoc = gql` query categories($notEqualTo: Int = null) { categories(filter: {id: {notEqualTo: $notEqualTo}}) { @@ -3525,7 +3621,13 @@ export const CategoryDoc = gql` thumbnailUrl unreadCount downloadCount - lastReadChapter { + latestFetchedChapter { + fetchedAt + } + latestUploadedChapter { + uploadDate + } + latestReadChapter { lastReadAt } chapters { @@ -3920,6 +4022,51 @@ export const TrackRecordsDoc = gql` } } ${TrackRecordTypeFragmentFragmentDoc}`; +export const ServerSettingsDoc = gql` + query serverSettings { + settings { + autoDownloadAheadLimit + autoDownloadNewChapters + backupInterval + backupPath + backupTTL + backupTime + basicAuthEnabled + basicAuthPassword + basicAuthUsername + debugLogsEnabled + downloadAsCbz + downloadsPath + electronPath + excludeCompleted + excludeEntryWithUnreadChapters + excludeNotStarted + extensionRepos + excludeUnreadChapters + flareSolverrEnabled + flareSolverrSessionName + flareSolverrSessionTtl + flareSolverrTimeout + flareSolverrUrl + globalUpdateInterval + gqlDebugLogsEnabled + initialOpenInBrowserEnabled + ip + localSourcePath + maxSourcesInParallel + port + socksProxyEnabled + socksProxyHost + socksProxyPort + systemTrayEnabled + updateMangas + webUIChannel + webUIFlavor + webUIInterface + webUIUpdateCheckInterval + } +} + `; export const DownloadChangedDoc = gql` subscription downloadChanged { downloadChanged { @@ -4426,6 +4573,18 @@ export const updateTrack = ( }); return m; } +export const setServerSettings = ( + options: Omit< + MutationOptions, + "mutation" + > + ) => { + const m = client.mutate({ + mutation: SetServerSettingsDoc, + ...options, + }); + return m; + } export const categories = ( options: Omit< WatchQueryOptions, @@ -5394,6 +5553,50 @@ export const trackRecords = ( return client.query({query: TrackRecordsDoc, ...options}) } +export const serverSettings = ( + options: Omit< + WatchQueryOptions, + "query" + > + ): Readable< + ApolloQueryResult & { + query: ObservableQuery< + ServerSettingsQuery, + ServerSettingsQueryVariables + >; + } + > => { + const q = client.watchQuery({ + query: ServerSettingsDoc, + ...options, + }); + var result = readable< + ApolloQueryResult & { + query: ObservableQuery< + ServerSettingsQuery, + ServerSettingsQueryVariables + >; + } + >( + { data: {} as any, loading: true, error: undefined, networkStatus: 1, query: q }, + (set) => { + q.subscribe((v: any) => { + set({ ...v, query: q }); + }); + } + ); + return result; + } + + export const AsyncserverSettings = ( + options: Omit< + QueryOptions, + "query" + > + ) => { + return client.query({query: ServerSettingsDoc, ...options}) + } + export const downloadChanged = ( options: Omit, "query"> ) => { diff --git a/src/lib/simpleStores.ts b/src/lib/simpleStores.ts index a408c8e4..ab571301 100644 --- a/src/lib/simpleStores.ts +++ b/src/lib/simpleStores.ts @@ -77,8 +77,10 @@ type mangaMeta = typeof mangaMetaDefaults; export enum sort { Unread = 'Unread', Alphabetical = 'Alphabetical', - 'Last Read' = 'Last Read', - ID = 'ID' + ID = 'ID', + 'Latest Read' = 'Latest Read', + 'Latest Fetched' = 'Latest Fetched', + 'Latest Uploaded' = 'Latest Uploaded' } export enum display { diff --git a/src/lib/util.ts b/src/lib/util.ts index 509849f4..9291840d 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -12,7 +12,11 @@ import { type BindTrackMutation, type UpdateTrackMutation, type GetMangaQuery, - GetMangaDoc + GetMangaDoc, + type SetServerSettingsMutation, + ServerSettingsDoc, + setServerSettings, + type PartialSettingsTypeInput } from './generated'; import type { FetchResult } from '@apollo/client/link/core'; @@ -217,6 +221,12 @@ export type PartialBy = Omit & Partial>; export function enumKeys(e: E): (keyof E)[] { return Object.keys(e) as (keyof E)[]; } +export function enumValues(e: E): E[keyof E][] { + return Object.values(e) as E[keyof E][]; +} +export function enumEntries(e: E): [keyof E, E[keyof E]][] { + return Object.entries(e) as [keyof E, E[keyof E]][]; +} export function groupBy( list: T[], @@ -290,3 +300,22 @@ export function bindTrackUpdater( data: { manga: mga } }); } + +export function setServerSettingsUpdater( + cache: ApolloCache, + { data }: FetchResult +) { + if (!data) return; + const settings = data.setSettings.settings; + cache.writeQuery({ + query: ServerSettingsDoc, + data: { settings } + }); +} + +export function setSettings(settings: PartialSettingsTypeInput) { + ErrorHelp( + 'failed to set server settings', + setServerSettings({ variables: { settings }, update: setServerSettingsUpdater }) + ); +} diff --git a/src/routes/(app)/(library)/+page.svelte b/src/routes/(app)/(library)/+page.svelte index 8d8c7b04..a12bb515 100644 --- a/src/routes/(app)/(library)/+page.svelte +++ b/src/routes/(app)/(library)/+page.svelte @@ -118,9 +118,20 @@ case sort.Alphabetical: tru = a.title > b.title; break; - case sort['Last Read']: - tru = a.lastReadChapter?.lastReadAt > b.lastReadChapter?.lastReadAt; + case sort['Latest Read']: + tru = + parseInt(a.latestReadChapter?.lastReadAt ?? 0) > + parseInt(b.latestReadChapter?.lastReadAt ?? 0); break; + case sort['Latest Fetched']: + tru = + parseInt(a.latestFetchedChapter?.fetchedAt ?? 0) > + parseInt(b.latestFetchedChapter?.fetchedAt ?? 0); + break; + case sort['Latest Uploaded']: + tru = + parseInt(a.latestUploadedChapter?.uploadDate ?? 0) > + parseInt(b.latestUploadedChapter?.uploadDate ?? 0); } if ($Meta.Asc) tru = !tru; diff --git a/src/routes/(app)/browse/migrate/manga/[MangaID]/migrateModal.svelte b/src/routes/(app)/browse/migrate/manga/[MangaID]/migrateModal.svelte index 46282bf5..8a6ee591 100644 --- a/src/routes/(app)/browse/migrate/manga/[MangaID]/migrateModal.svelte +++ b/src/routes/(app)/browse/migrate/manga/[MangaID]/migrateModal.svelte @@ -142,7 +142,9 @@ thumbnailUrl: dat.manga.thumbnailUrl, unreadCount: dat.manga.unreadCount ?? 0, downloadCount: dat.manga.downloadCount ?? 0, - lastReadChapter: dat.manga.lastReadChapter ?? undefined, + latestFetchedChapter: dat.manga.latestFetchedChapter, + latestReadChapter: dat.manga.latestReadChapter, + latestUploadedChapter: dat.manga.latestUploadedChapter, chapters: dat.manga.chapters ?? { totalCount: 0 } diff --git a/src/routes/(app)/manga/[MangaID]/(manga)/MangaCatagoryModal.svelte b/src/routes/(app)/manga/[MangaID]/(manga)/MangaCatagoryModal.svelte index e7d8527d..9b0a1f63 100644 --- a/src/routes/(app)/manga/[MangaID]/(manga)/MangaCatagoryModal.svelte +++ b/src/routes/(app)/manga/[MangaID]/(manga)/MangaCatagoryModal.svelte @@ -78,7 +78,9 @@ thumbnailUrl: manga.thumbnailUrl, unreadCount: manga.unreadCount, downloadCount: manga.downloadCount, - lastReadChapter: manga.lastReadChapter, + latestFetchedChapter: manga.latestFetchedChapter, + latestReadChapter: manga.latestReadChapter, + latestUploadedChapter: manga.latestUploadedChapter, chapters: manga.chapters }; // add to categories that now have it diff --git a/src/routes/(app)/settings/+page.svelte b/src/routes/(app)/settings/+page.svelte index fbc6cbb1..4391a99c 100644 --- a/src/routes/(app)/settings/+page.svelte +++ b/src/routes/(app)/settings/+page.svelte @@ -141,3 +141,10 @@
Cache
+ + +
Server Settings
+
diff --git a/src/routes/(app)/settings/server/+page.svelte b/src/routes/(app)/settings/server/+page.svelte new file mode 100644 index 00000000..34bb40ec --- /dev/null +++ b/src/routes/(app)/settings/server/+page.svelte @@ -0,0 +1,404 @@ + + + +{#if $settingsData.error} + {JSON.stringify($settingsData.error)} +{:else if $settingsData.errors} + {JSON.stringify($settingsData.errors)} +{:else if $settingsData.loading} + Loading... +{:else if $settingsData.data} + {@const settings = $settingsData.data.settings} +
+ + setSettings({ autoDownloadAheadLimit })} + /> + + setSettings({ autoDownloadNewChapters })} + /> + + setSettings({ backupInterval })} + /> + + setSettings({ backupPath })} + /> + + setSettings({ backupTTL })} + /> + + { + if (/^([01][0-9]|2[0-3]):([0-5][0-9])$/.test(backupTime)) { + setSettings({ backupTime }); + return; + } + errortoast('Time validation failed', 'Backup Time must be in format HH:MM'); + backupTime = settings.backupTime; + }} + /> + + setSettings({ basicAuthEnabled })} + /> + + setSettings({ basicAuthPassword })} + type="password" + /> + + setSettings({ basicAuthUsername })} + /> + + setSettings({ debugLogsEnabled })} + /> + + setSettings({ downloadAsCbz })} + /> + + setSettings({ downloadsPath })} + /> + + setSettings({ electronPath })} + /> + + setSettings({ excludeCompleted })} + /> + + setSettings({ excludeEntryWithUnreadChapters })} + /> + + setSettings({ excludeNotStarted })} + /> + + + + setSettings({ excludeUnreadChapters })} + /> + + setSettings({ flareSolverrEnabled })} + /> + + setSettings({ flareSolverrSessionName })} + /> + + setSettings({ flareSolverrSessionTtl })} + /> + + setSettings({ flareSolverrTimeout })} + /> + + setSettings({ flareSolverrUrl })} + /> + + setSettings({ globalUpdateInterval })} + /> + + setSettings({ gqlDebugLogsEnabled })} + /> + + setSettings({ initialOpenInBrowserEnabled })} + /> + + { + if (/^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/.test(ip)) { + setSettings({ ip }); + return; + } + errortoast('Time validation failed', 'Backup Time must be in format HH:MM'); + ip = settings.ip; + }} + /> + + setSettings({ localSourcePath })} + /> + + setSettings({ maxSourcesInParallel })} + /> + + { + if (port < 65535 && port > 0) { + setSettings({ port }); + return; + } + errortoast('Port validation failed', 'Port must be between 0 and 65535'); + port = settings.port; + }} + /> + + setSettings({ socksProxyEnabled })} + /> + + setSettings({ socksProxyHost })} + /> + + { + const int = parseInt(socksProxyPort); + if (!isNaN(int) && int < 65535 && int > 0) { + setSettings({ socksProxyPort }); + return; + } + errortoast( + 'socksProxyPort validation failed', + 'socksProxyPort must be a number between 0 and 65535' + ); + socksProxyPort = settings.socksProxyPort; + }} + /> + + setSettings({ systemTrayEnabled })} + /> + + setSettings({ updateMangas })} + /> + + setSettings({ webUIFlavor })} + /> + + + {#each options as option} + + {/each} + +
+ diff --git a/src/routes/(app)/settings/server/components/extensionReposModal.svelte b/src/routes/(app)/settings/server/components/extensionReposModal.svelte new file mode 100644 index 00000000..14c5ae9c --- /dev/null +++ b/src/routes/(app)/settings/server/components/extensionReposModal.svelte @@ -0,0 +1,59 @@ + + + +{#if $modalStore[0]} +
+

Extension Repos

+
+
+
+ + +
+
+ {#each repos as value} +
+
+ {value} +
+ +
+ {/each} +
+
+
+
+
+{/if} diff --git a/src/routes/(app)/settings/server/components/number.svelte b/src/routes/(app)/settings/server/components/number.svelte new file mode 100644 index 00000000..39962732 --- /dev/null +++ b/src/routes/(app)/settings/server/components/number.svelte @@ -0,0 +1,26 @@ + + + + diff --git a/src/routes/(app)/settings/server/components/text.svelte b/src/routes/(app)/settings/server/components/text.svelte new file mode 100644 index 00000000..4a95ceee --- /dev/null +++ b/src/routes/(app)/settings/server/components/text.svelte @@ -0,0 +1,35 @@ + + + + diff --git a/src/routes/(app)/settings/server/components/toggle.svelte b/src/routes/(app)/settings/server/components/toggle.svelte new file mode 100644 index 00000000..9effe037 --- /dev/null +++ b/src/routes/(app)/settings/server/components/toggle.svelte @@ -0,0 +1,32 @@ + + + +