diff --git a/package.json b/package.json index ec201a3f..08533b97 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "updateDeps": "tsx tools/scripts/updateDependencies.ts", "gql:codegen-base": "graphql-codegen --config gql_codegen.ts", "gql:codegen-formatter": "tsx tools/scripts/codegenFormatter.ts", - "gql:codegen": "yarn gql:codegen-base & yarn gql:codegen-formatter", + "gql:codegen": "yarn gql:codegen-base && yarn gql:codegen-formatter", "i18n:gen-resources": "tsx tools/scripts/weblate/generatei18nResources.ts", "dayjs:gen-locales-array": "tsx tools/scripts/dayjs/generateDayJsLocales.ts", "dayjs:gen-locales-import": "tsx tools/scripts/dayjs/generateDayJsLocalesImport.ts", diff --git a/public/locales/en.json b/public/locales/en.json index 0209e1cf..967bcb40 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -1284,11 +1284,33 @@ } }, "auth": { - "basic": { - "label": { - "enable": "Use basic authentication", - "password": "Basic authentication $t(global.label.password, lowercase)", - "username": "Basic authentication $t(global.label.username, lowercase)" + "label": { + "password": "$t(global.label.password)", + "title": "Authentication Mode", + "username": "$t(global.label.username)" + }, + "mode": { + "option": { + "basicAuth": { + "label": { + "description": "Your browser will prompt you to enter credentials with a dialog.", + "title": "Basic Authentication" + } + }, + "none": { + "label": { + "description": "Disable authentication", + "info": "Your library will be accessible. Use this only on private networks or when otherwise securing access.", + "title": "None" + } + }, + "simpleLogin": { + "label": { + "description": "A simple login page will be presented to login.", + "info": "When you enable this, you may need to refresh this tab for the login page to appear.", + "title": "Simple Login" + } + } } }, "title": "Authentication" diff --git a/src/lib/graphql/fragments/SettingsFragments.ts b/src/lib/graphql/fragments/SettingsFragments.ts index 96faefba..f30f2195 100644 --- a/src/lib/graphql/fragments/SettingsFragments.ts +++ b/src/lib/graphql/fragments/SettingsFragments.ts @@ -52,9 +52,9 @@ export const SERVER_SETTINGS = gql` updateMangas # Authentication - basicAuthEnabled - basicAuthUsername - basicAuthPassword + authMode + authPassword + authUsername # misc debugLogsEnabled diff --git a/src/lib/graphql/generated/apollo-helpers.ts b/src/lib/graphql/generated/apollo-helpers.ts index 7e65883b..d4f49a33 100644 --- a/src/lib/graphql/generated/apollo-helpers.ts +++ b/src/lib/graphql/generated/apollo-helpers.ts @@ -552,8 +552,11 @@ export type PageInfoFieldPolicy = { hasPreviousPage?: FieldPolicy | FieldReadFunction, startCursor?: FieldPolicy | FieldReadFunction }; -export type PartialSettingsTypeKeySpecifier = ('autoDownloadAheadLimit' | 'autoDownloadIgnoreReUploads' | 'autoDownloadNewChapters' | 'autoDownloadNewChaptersLimit' | 'backupInterval' | 'backupPath' | 'backupTTL' | 'backupTime' | 'basicAuthEnabled' | 'basicAuthPassword' | 'basicAuthUsername' | 'debugLogsEnabled' | 'downloadAsCbz' | 'downloadsPath' | 'electronPath' | 'excludeCompleted' | 'excludeEntryWithUnreadChapters' | 'excludeNotStarted' | 'excludeUnreadChapters' | 'extensionRepos' | 'flareSolverrAsResponseFallback' | 'flareSolverrEnabled' | 'flareSolverrSessionName' | 'flareSolverrSessionTtl' | 'flareSolverrTimeout' | 'flareSolverrUrl' | 'globalUpdateInterval' | 'gqlDebugLogsEnabled' | 'initialOpenInBrowserEnabled' | 'ip' | 'localSourcePath' | 'maxLogFileSize' | 'maxLogFiles' | 'maxLogFolderSize' | 'maxSourcesInParallel' | 'opdsChapterSortOrder' | 'opdsEnablePageReadProgress' | 'opdsItemsPerPage' | 'opdsMarkAsReadOnDownload' | 'opdsShowOnlyDownloadedChapters' | 'opdsShowOnlyUnreadChapters' | 'opdsUseBinaryFileSizes' | 'port' | 'socksProxyEnabled' | 'socksProxyHost' | 'socksProxyPassword' | 'socksProxyPort' | 'socksProxyUsername' | 'socksProxyVersion' | 'systemTrayEnabled' | 'updateMangas' | 'webUIChannel' | 'webUIFlavor' | 'webUIInterface' | 'webUIUpdateCheckInterval' | PartialSettingsTypeKeySpecifier)[]; +export type PartialSettingsTypeKeySpecifier = ('authMode' | 'authPassword' | 'authUsername' | 'autoDownloadAheadLimit' | 'autoDownloadIgnoreReUploads' | 'autoDownloadNewChapters' | 'autoDownloadNewChaptersLimit' | 'backupInterval' | 'backupPath' | 'backupTTL' | 'backupTime' | 'basicAuthEnabled' | 'basicAuthPassword' | 'basicAuthUsername' | 'debugLogsEnabled' | 'downloadAsCbz' | 'downloadsPath' | 'electronPath' | 'excludeCompleted' | 'excludeEntryWithUnreadChapters' | 'excludeNotStarted' | 'excludeUnreadChapters' | 'extensionRepos' | 'flareSolverrAsResponseFallback' | 'flareSolverrEnabled' | 'flareSolverrSessionName' | 'flareSolverrSessionTtl' | 'flareSolverrTimeout' | 'flareSolverrUrl' | 'globalUpdateInterval' | 'gqlDebugLogsEnabled' | 'initialOpenInBrowserEnabled' | 'ip' | 'localSourcePath' | 'maxLogFileSize' | 'maxLogFiles' | 'maxLogFolderSize' | 'maxSourcesInParallel' | 'opdsChapterSortOrder' | 'opdsEnablePageReadProgress' | 'opdsItemsPerPage' | 'opdsMarkAsReadOnDownload' | 'opdsShowOnlyDownloadedChapters' | 'opdsShowOnlyUnreadChapters' | 'opdsUseBinaryFileSizes' | 'port' | 'socksProxyEnabled' | 'socksProxyHost' | 'socksProxyPassword' | 'socksProxyPort' | 'socksProxyUsername' | 'socksProxyVersion' | 'systemTrayEnabled' | 'updateMangas' | 'webUIChannel' | 'webUIFlavor' | 'webUIInterface' | 'webUIUpdateCheckInterval' | PartialSettingsTypeKeySpecifier)[]; export type PartialSettingsTypeFieldPolicy = { + authMode?: FieldPolicy | FieldReadFunction, + authPassword?: FieldPolicy | FieldReadFunction, + authUsername?: FieldPolicy | FieldReadFunction, autoDownloadAheadLimit?: FieldPolicy | FieldReadFunction, autoDownloadIgnoreReUploads?: FieldPolicy | FieldReadFunction, autoDownloadNewChapters?: FieldPolicy | FieldReadFunction, @@ -702,8 +705,11 @@ export type SetSourceMetaPayloadFieldPolicy = { clientMutationId?: FieldPolicy | FieldReadFunction, meta?: FieldPolicy | FieldReadFunction }; -export type SettingsKeySpecifier = ('autoDownloadAheadLimit' | 'autoDownloadIgnoreReUploads' | 'autoDownloadNewChapters' | 'autoDownloadNewChaptersLimit' | 'backupInterval' | 'backupPath' | 'backupTTL' | 'backupTime' | 'basicAuthEnabled' | 'basicAuthPassword' | 'basicAuthUsername' | 'debugLogsEnabled' | 'downloadAsCbz' | 'downloadsPath' | 'electronPath' | 'excludeCompleted' | 'excludeEntryWithUnreadChapters' | 'excludeNotStarted' | 'excludeUnreadChapters' | 'extensionRepos' | 'flareSolverrAsResponseFallback' | 'flareSolverrEnabled' | 'flareSolverrSessionName' | 'flareSolverrSessionTtl' | 'flareSolverrTimeout' | 'flareSolverrUrl' | 'globalUpdateInterval' | 'gqlDebugLogsEnabled' | 'initialOpenInBrowserEnabled' | 'ip' | 'localSourcePath' | 'maxLogFileSize' | 'maxLogFiles' | 'maxLogFolderSize' | 'maxSourcesInParallel' | 'opdsChapterSortOrder' | 'opdsEnablePageReadProgress' | 'opdsItemsPerPage' | 'opdsMarkAsReadOnDownload' | 'opdsShowOnlyDownloadedChapters' | 'opdsShowOnlyUnreadChapters' | 'opdsUseBinaryFileSizes' | 'port' | 'socksProxyEnabled' | 'socksProxyHost' | 'socksProxyPassword' | 'socksProxyPort' | 'socksProxyUsername' | 'socksProxyVersion' | 'systemTrayEnabled' | 'updateMangas' | 'webUIChannel' | 'webUIFlavor' | 'webUIInterface' | 'webUIUpdateCheckInterval' | SettingsKeySpecifier)[]; +export type SettingsKeySpecifier = ('authMode' | 'authPassword' | 'authUsername' | 'autoDownloadAheadLimit' | 'autoDownloadIgnoreReUploads' | 'autoDownloadNewChapters' | 'autoDownloadNewChaptersLimit' | 'backupInterval' | 'backupPath' | 'backupTTL' | 'backupTime' | 'basicAuthEnabled' | 'basicAuthPassword' | 'basicAuthUsername' | 'debugLogsEnabled' | 'downloadAsCbz' | 'downloadsPath' | 'electronPath' | 'excludeCompleted' | 'excludeEntryWithUnreadChapters' | 'excludeNotStarted' | 'excludeUnreadChapters' | 'extensionRepos' | 'flareSolverrAsResponseFallback' | 'flareSolverrEnabled' | 'flareSolverrSessionName' | 'flareSolverrSessionTtl' | 'flareSolverrTimeout' | 'flareSolverrUrl' | 'globalUpdateInterval' | 'gqlDebugLogsEnabled' | 'initialOpenInBrowserEnabled' | 'ip' | 'localSourcePath' | 'maxLogFileSize' | 'maxLogFiles' | 'maxLogFolderSize' | 'maxSourcesInParallel' | 'opdsChapterSortOrder' | 'opdsEnablePageReadProgress' | 'opdsItemsPerPage' | 'opdsMarkAsReadOnDownload' | 'opdsShowOnlyDownloadedChapters' | 'opdsShowOnlyUnreadChapters' | 'opdsUseBinaryFileSizes' | 'port' | 'socksProxyEnabled' | 'socksProxyHost' | 'socksProxyPassword' | 'socksProxyPort' | 'socksProxyUsername' | 'socksProxyVersion' | 'systemTrayEnabled' | 'updateMangas' | 'webUIChannel' | 'webUIFlavor' | 'webUIInterface' | 'webUIUpdateCheckInterval' | SettingsKeySpecifier)[]; export type SettingsFieldPolicy = { + authMode?: FieldPolicy | FieldReadFunction, + authPassword?: FieldPolicy | FieldReadFunction, + authUsername?: FieldPolicy | FieldReadFunction, autoDownloadAheadLimit?: FieldPolicy | FieldReadFunction, autoDownloadIgnoreReUploads?: FieldPolicy | FieldReadFunction, autoDownloadNewChapters?: FieldPolicy | FieldReadFunction, @@ -760,8 +766,11 @@ export type SettingsFieldPolicy = { webUIInterface?: FieldPolicy | FieldReadFunction, webUIUpdateCheckInterval?: FieldPolicy | FieldReadFunction }; -export type SettingsTypeKeySpecifier = ('autoDownloadAheadLimit' | 'autoDownloadIgnoreReUploads' | 'autoDownloadNewChapters' | 'autoDownloadNewChaptersLimit' | 'backupInterval' | 'backupPath' | 'backupTTL' | 'backupTime' | 'basicAuthEnabled' | 'basicAuthPassword' | 'basicAuthUsername' | 'debugLogsEnabled' | 'downloadAsCbz' | 'downloadsPath' | 'electronPath' | 'excludeCompleted' | 'excludeEntryWithUnreadChapters' | 'excludeNotStarted' | 'excludeUnreadChapters' | 'extensionRepos' | 'flareSolverrAsResponseFallback' | 'flareSolverrEnabled' | 'flareSolverrSessionName' | 'flareSolverrSessionTtl' | 'flareSolverrTimeout' | 'flareSolverrUrl' | 'globalUpdateInterval' | 'gqlDebugLogsEnabled' | 'initialOpenInBrowserEnabled' | 'ip' | 'localSourcePath' | 'maxLogFileSize' | 'maxLogFiles' | 'maxLogFolderSize' | 'maxSourcesInParallel' | 'opdsChapterSortOrder' | 'opdsEnablePageReadProgress' | 'opdsItemsPerPage' | 'opdsMarkAsReadOnDownload' | 'opdsShowOnlyDownloadedChapters' | 'opdsShowOnlyUnreadChapters' | 'opdsUseBinaryFileSizes' | 'port' | 'socksProxyEnabled' | 'socksProxyHost' | 'socksProxyPassword' | 'socksProxyPort' | 'socksProxyUsername' | 'socksProxyVersion' | 'systemTrayEnabled' | 'updateMangas' | 'webUIChannel' | 'webUIFlavor' | 'webUIInterface' | 'webUIUpdateCheckInterval' | SettingsTypeKeySpecifier)[]; +export type SettingsTypeKeySpecifier = ('authMode' | 'authPassword' | 'authUsername' | 'autoDownloadAheadLimit' | 'autoDownloadIgnoreReUploads' | 'autoDownloadNewChapters' | 'autoDownloadNewChaptersLimit' | 'backupInterval' | 'backupPath' | 'backupTTL' | 'backupTime' | 'basicAuthEnabled' | 'basicAuthPassword' | 'basicAuthUsername' | 'debugLogsEnabled' | 'downloadAsCbz' | 'downloadsPath' | 'electronPath' | 'excludeCompleted' | 'excludeEntryWithUnreadChapters' | 'excludeNotStarted' | 'excludeUnreadChapters' | 'extensionRepos' | 'flareSolverrAsResponseFallback' | 'flareSolverrEnabled' | 'flareSolverrSessionName' | 'flareSolverrSessionTtl' | 'flareSolverrTimeout' | 'flareSolverrUrl' | 'globalUpdateInterval' | 'gqlDebugLogsEnabled' | 'initialOpenInBrowserEnabled' | 'ip' | 'localSourcePath' | 'maxLogFileSize' | 'maxLogFiles' | 'maxLogFolderSize' | 'maxSourcesInParallel' | 'opdsChapterSortOrder' | 'opdsEnablePageReadProgress' | 'opdsItemsPerPage' | 'opdsMarkAsReadOnDownload' | 'opdsShowOnlyDownloadedChapters' | 'opdsShowOnlyUnreadChapters' | 'opdsUseBinaryFileSizes' | 'port' | 'socksProxyEnabled' | 'socksProxyHost' | 'socksProxyPassword' | 'socksProxyPort' | 'socksProxyUsername' | 'socksProxyVersion' | 'systemTrayEnabled' | 'updateMangas' | 'webUIChannel' | 'webUIFlavor' | 'webUIInterface' | 'webUIUpdateCheckInterval' | SettingsTypeKeySpecifier)[]; export type SettingsTypeFieldPolicy = { + authMode?: FieldPolicy | FieldReadFunction, + authPassword?: FieldPolicy | FieldReadFunction, + authUsername?: FieldPolicy | FieldReadFunction, autoDownloadAheadLimit?: FieldPolicy | FieldReadFunction, autoDownloadIgnoreReUploads?: FieldPolicy | FieldReadFunction, autoDownloadNewChapters?: FieldPolicy | FieldReadFunction, diff --git a/src/lib/graphql/generated/graphql.ts b/src/lib/graphql/generated/graphql.ts index c31f6fe1..23616ca5 100644 --- a/src/lib/graphql/generated/graphql.ts +++ b/src/lib/graphql/generated/graphql.ts @@ -35,11 +35,19 @@ export type AboutWebUi = { tag: Scalars['String']['output']; }; +export enum AuthMode { + BasicAuth = 'BASIC_AUTH', + None = 'NONE', + SimpleLogin = 'SIMPLE_LOGIN' +} + export enum BackupRestoreState { Failure = 'FAILURE', Idle = 'IDLE', RestoringCategories = 'RESTORING_CATEGORIES', RestoringManga = 'RESTORING_MANGA', + RestoringMeta = 'RESTORING_META', + RestoringSettings = 'RESTORING_SETTINGS', Success = 'SUCCESS' } @@ -53,6 +61,8 @@ export type BackupRestoreStatus = { export type BindTrackInput = { clientMutationId?: InputMaybe; mangaId: Scalars['Int']['input']; + /** This will only work if the tracker of the track record supports private tracking */ + private?: InputMaybe; remoteId: Scalars['LongString']['input']; trackerId: Scalars['Int']['input']; }; @@ -1062,6 +1072,7 @@ export type MangaType = { firstUnreadChapter?: Maybe; genre: Array; hasDuplicateChapters: Scalars['Boolean']['output']; + highestNumberedChapter?: Maybe; id: Scalars['Int']['output']; inLibrary: Scalars['Boolean']['output']; inLibraryAt: Scalars['LongString']['output']; @@ -1519,6 +1530,9 @@ export type PageInfo = { export type PartialSettingsType = Settings & { __typename?: 'PartialSettingsType'; + authMode?: Maybe; + authPassword?: Maybe; + authUsername?: Maybe; /** @deprecated Replaced with autoDownloadNewChaptersLimit, replace with autoDownloadNewChaptersLimit */ autoDownloadAheadLimit?: Maybe; autoDownloadIgnoreReUploads?: Maybe; @@ -1528,8 +1542,11 @@ export type PartialSettingsType = Settings & { backupPath?: Maybe; backupTTL?: Maybe; backupTime?: Maybe; + /** @deprecated Removed - prefer authMode */ basicAuthEnabled?: Maybe; + /** @deprecated Removed - prefer authPassword */ basicAuthPassword?: Maybe; + /** @deprecated Removed - prefer authUsername */ basicAuthUsername?: Maybe; debugLogsEnabled?: Maybe; downloadAsCbz?: Maybe; @@ -1579,6 +1596,9 @@ export type PartialSettingsType = Settings & { }; export type PartialSettingsTypeInput = { + authMode?: InputMaybe; + authPassword?: InputMaybe; + authUsername?: InputMaybe; autoDownloadIgnoreReUploads?: InputMaybe; autoDownloadNewChapters?: InputMaybe; autoDownloadNewChaptersLimit?: InputMaybe; @@ -1586,9 +1606,6 @@ export type PartialSettingsTypeInput = { backupPath?: InputMaybe; backupTTL?: InputMaybe; backupTime?: InputMaybe; - basicAuthEnabled?: InputMaybe; - basicAuthPassword?: InputMaybe; - basicAuthUsername?: InputMaybe; debugLogsEnabled?: InputMaybe; downloadAsCbz?: InputMaybe; downloadsPath?: InputMaybe; @@ -1680,6 +1697,8 @@ export type QueryCategoriesArgs = { last?: InputMaybe; offset?: InputMaybe; order?: InputMaybe>; + orderBy?: InputMaybe; + orderByType?: InputMaybe; }; @@ -1702,6 +1721,8 @@ export type QueryChaptersArgs = { last?: InputMaybe; offset?: InputMaybe; order?: InputMaybe>; + orderBy?: InputMaybe; + orderByType?: InputMaybe; }; @@ -1719,6 +1740,8 @@ export type QueryExtensionsArgs = { last?: InputMaybe; offset?: InputMaybe; order?: InputMaybe>; + orderBy?: InputMaybe; + orderByType?: InputMaybe; }; @@ -1736,6 +1759,8 @@ export type QueryMangasArgs = { last?: InputMaybe; offset?: InputMaybe; order?: InputMaybe>; + orderBy?: InputMaybe; + orderByType?: InputMaybe; }; @@ -1753,6 +1778,8 @@ export type QueryMetasArgs = { last?: InputMaybe; offset?: InputMaybe; order?: InputMaybe>; + orderBy?: InputMaybe; + orderByType?: InputMaybe; }; @@ -1780,6 +1807,8 @@ export type QuerySourcesArgs = { last?: InputMaybe; offset?: InputMaybe; order?: InputMaybe>; + orderBy?: InputMaybe; + orderByType?: InputMaybe; }; @@ -1797,6 +1826,8 @@ export type QueryTrackRecordsArgs = { last?: InputMaybe; offset?: InputMaybe; order?: InputMaybe>; + orderBy?: InputMaybe; + orderByType?: InputMaybe; }; @@ -1813,6 +1844,8 @@ export type QueryTrackersArgs = { last?: InputMaybe; offset?: InputMaybe; order?: InputMaybe>; + orderBy?: InputMaybe; + orderByType?: InputMaybe; }; @@ -1943,6 +1976,9 @@ export type SetSourceMetaPayload = { }; export type Settings = { + authMode?: Maybe; + authPassword?: Maybe; + authUsername?: Maybe; /** @deprecated Replaced with autoDownloadNewChaptersLimit, replace with autoDownloadNewChaptersLimit */ autoDownloadAheadLimit?: Maybe; autoDownloadIgnoreReUploads?: Maybe; @@ -1952,8 +1988,11 @@ export type Settings = { backupPath?: Maybe; backupTTL?: Maybe; backupTime?: Maybe; + /** @deprecated Removed - prefer authMode */ basicAuthEnabled?: Maybe; + /** @deprecated Removed - prefer authPassword */ basicAuthPassword?: Maybe; + /** @deprecated Removed - prefer authUsername */ basicAuthUsername?: Maybe; debugLogsEnabled?: Maybe; downloadAsCbz?: Maybe; @@ -2004,6 +2043,9 @@ export type Settings = { export type SettingsType = Settings & { __typename?: 'SettingsType'; + authMode: AuthMode; + authPassword: Scalars['String']['output']; + authUsername: Scalars['String']['output']; /** @deprecated Replaced with autoDownloadNewChaptersLimit, replace with autoDownloadNewChaptersLimit */ autoDownloadAheadLimit: Scalars['Int']['output']; autoDownloadIgnoreReUploads?: Maybe; @@ -2013,8 +2055,11 @@ export type SettingsType = Settings & { backupPath: Scalars['String']['output']; backupTTL: Scalars['Int']['output']; backupTime: Scalars['String']['output']; + /** @deprecated Removed - prefer authMode */ basicAuthEnabled: Scalars['Boolean']['output']; + /** @deprecated Removed - prefer authPassword */ basicAuthPassword: Scalars['String']['output']; + /** @deprecated Removed - prefer authUsername */ basicAuthUsername: Scalars['String']['output']; debugLogsEnabled: Scalars['Boolean']['output']; downloadAsCbz: Scalars['Boolean']['output']; @@ -2322,6 +2367,7 @@ export type TrackRecordConditionInput = { lastChapterRead?: InputMaybe; libraryId?: InputMaybe; mangaId?: InputMaybe; + private?: InputMaybe; remoteId?: InputMaybe; remoteUrl?: InputMaybe; score?: InputMaybe; @@ -2347,6 +2393,7 @@ export type TrackRecordFilterInput = { mangaId?: InputMaybe; not?: InputMaybe; or?: InputMaybe>; + private?: InputMaybe; remoteId?: InputMaybe; remoteUrl?: InputMaybe; score?: InputMaybe; @@ -2370,6 +2417,7 @@ export enum TrackRecordOrderBy { Id = 'ID', LastChapterRead = 'LAST_CHAPTER_READ', MangaId = 'MANGA_ID', + Private = 'PRIVATE', RemoteId = 'REMOTE_ID', Score = 'SCORE', StartDate = 'START_DATE', @@ -2392,6 +2440,7 @@ export type TrackRecordType = { libraryId?: Maybe; manga: MangaType; mangaId: Scalars['Int']['output']; + private: Scalars['Boolean']['output']; remoteId: Scalars['LongString']['output']; remoteUrl: Scalars['String']['output']; score: Scalars['Float']['output']; @@ -2406,11 +2455,19 @@ export type TrackRecordType = { export type TrackSearchType = { __typename?: 'TrackSearchType'; coverUrl: Scalars['String']['output']; + displayScore: Scalars['String']['output']; + finishedReadingDate: Scalars['LongString']['output']; id: Scalars['Int']['output']; + lastChapterRead: Scalars['Float']['output']; + libraryId?: Maybe; + private: Scalars['Boolean']['output']; publishingStatus: Scalars['String']['output']; publishingType: Scalars['String']['output']; remoteId: Scalars['LongString']['output']; + score: Scalars['Float']['output']; startDate: Scalars['String']['output']; + startedReadingDate: Scalars['LongString']['output']; + status: Scalars['Int']['output']; summary: Scalars['String']['output']; title: Scalars['String']['output']; totalChapters: Scalars['Int']['output']; @@ -2467,7 +2524,9 @@ export type TrackerType = { name: Scalars['String']['output']; scores: Array; statuses: Array; - supportsTrackDeletion?: Maybe; + supportsPrivateTracking: Scalars['Boolean']['output']; + supportsReadingDates: Scalars['Boolean']['output']; + supportsTrackDeletion: Scalars['Boolean']['output']; trackRecords: TrackRecordNodeList; }; @@ -2747,10 +2806,14 @@ export enum UpdateStrategy { export type UpdateTrackInput = { clientMutationId?: InputMaybe; + /** This will only work if the tracker of the track record supports reading dates */ finishDate?: InputMaybe; lastChapterRead?: InputMaybe; + /** This will only work if the tracker of the track record supports private tracking */ + private?: InputMaybe; recordId: Scalars['Int']['input']; scoreString?: InputMaybe; + /** This will only work if the tracker of the track record supports reading dates */ startDate?: InputMaybe; status?: InputMaybe; }; @@ -2901,13 +2964,13 @@ export type MangaChapterNodeFieldsFragment = { __typename?: 'MangaType', firstUn export type MangaReaderFieldsFragment = { __typename?: 'MangaType', genre: Array, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, source?: { __typename?: 'SourceType', id: string, name: string, displayName: string, lang: string } | null, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number } }; -export type MangaLibraryFieldsFragment = { __typename?: 'MangaType', genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, status: MangaStatus, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }; +export type MangaLibraryFieldsFragment = { __typename?: 'MangaType', genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, status: MangaStatus, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }; export type MangaScreenFieldsFragment = { __typename?: 'MangaType', artist?: string | null, author?: string | null, description?: string | null, status: MangaStatus, realUrl?: string | null, sourceId: string, genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }; export type MangaLibraryDuplicateScreenFieldsFragment = { __typename?: 'MangaType', description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, chapters: { __typename?: 'ChapterNodeList', totalCount: number } }; -export type ServerSettingsFragment = { __typename?: 'SettingsType', ip: string, port: number, socksProxyEnabled: boolean, socksProxyVersion: number, socksProxyHost: string, socksProxyPort: string, socksProxyUsername: string, socksProxyPassword: string, webUIFlavor: WebUiFlavor, initialOpenInBrowserEnabled: boolean, webUIInterface: WebUiInterface, electronPath: string, webUIChannel: WebUiChannel, webUIUpdateCheckInterval: number, downloadAsCbz: boolean, downloadsPath: string, autoDownloadNewChapters: boolean, excludeEntryWithUnreadChapters: boolean, autoDownloadNewChaptersLimit: number, autoDownloadIgnoreReUploads?: boolean | null, extensionRepos: Array, maxSourcesInParallel: number, excludeUnreadChapters: boolean, excludeNotStarted: boolean, excludeCompleted: boolean, globalUpdateInterval: number, updateMangas: boolean, basicAuthEnabled: boolean, basicAuthUsername: string, basicAuthPassword: string, debugLogsEnabled: boolean, systemTrayEnabled: boolean, maxLogFileSize: string, maxLogFiles: number, maxLogFolderSize: string, backupPath: string, backupTime: string, backupInterval: number, backupTTL: number, localSourcePath: string, flareSolverrEnabled: boolean, flareSolverrUrl: string, flareSolverrTimeout: number, flareSolverrSessionName: string, flareSolverrSessionTtl: number, flareSolverrAsResponseFallback: boolean, opdsUseBinaryFileSizes: boolean, opdsItemsPerPage: number, opdsEnablePageReadProgress: boolean, opdsMarkAsReadOnDownload: boolean, opdsShowOnlyUnreadChapters: boolean, opdsShowOnlyDownloadedChapters: boolean, opdsChapterSortOrder: SortOrder }; +export type ServerSettingsFragment = { __typename?: 'SettingsType', ip: string, port: number, socksProxyEnabled: boolean, socksProxyVersion: number, socksProxyHost: string, socksProxyPort: string, socksProxyUsername: string, socksProxyPassword: string, webUIFlavor: WebUiFlavor, initialOpenInBrowserEnabled: boolean, webUIInterface: WebUiInterface, electronPath: string, webUIChannel: WebUiChannel, webUIUpdateCheckInterval: number, downloadAsCbz: boolean, downloadsPath: string, autoDownloadNewChapters: boolean, excludeEntryWithUnreadChapters: boolean, autoDownloadNewChaptersLimit: number, autoDownloadIgnoreReUploads?: boolean | null, extensionRepos: Array, maxSourcesInParallel: number, excludeUnreadChapters: boolean, excludeNotStarted: boolean, excludeCompleted: boolean, globalUpdateInterval: number, updateMangas: boolean, authMode: AuthMode, authPassword: string, authUsername: string, debugLogsEnabled: boolean, systemTrayEnabled: boolean, maxLogFileSize: string, maxLogFiles: number, maxLogFolderSize: string, backupPath: string, backupTime: string, backupInterval: number, backupTTL: number, localSourcePath: string, flareSolverrEnabled: boolean, flareSolverrUrl: string, flareSolverrTimeout: number, flareSolverrSessionName: string, flareSolverrSessionTtl: number, flareSolverrAsResponseFallback: boolean, opdsUseBinaryFileSizes: boolean, opdsItemsPerPage: number, opdsEnablePageReadProgress: boolean, opdsMarkAsReadOnDownload: boolean, opdsShowOnlyUnreadChapters: boolean, opdsShowOnlyDownloadedChapters: boolean, opdsChapterSortOrder: SortOrder }; export type SourceMetaFieldsFragment = { __typename?: 'SourceMetaType', sourceId: string, key: string, value: string }; @@ -2925,7 +2988,7 @@ export type TrackerBaseFieldsFragment = { __typename?: 'TrackerType', id: number export type TrackerSettingFieldsFragment = { __typename?: 'TrackerType', authUrl?: string | null, id: number, name: string, icon: string, isLoggedIn: boolean, isTokenExpired: boolean }; -export type TrackerBindFieldsFragment = { __typename?: 'TrackerType', icon: string, supportsTrackDeletion?: boolean | null, scores: Array, id: number, name: string, isLoggedIn: boolean, isTokenExpired: boolean, statuses: Array<{ __typename?: 'TrackStatusType', name: string, value: number }>, trackRecords: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, remoteId: string, trackerId: number, remoteUrl: string, title: string, status: number, lastChapterRead: number, totalChapters: number, score: number, displayScore: string, startDate: string, finishDate: string }> } }; +export type TrackerBindFieldsFragment = { __typename?: 'TrackerType', icon: string, supportsTrackDeletion: boolean, scores: Array, id: number, name: string, isLoggedIn: boolean, isTokenExpired: boolean, statuses: Array<{ __typename?: 'TrackStatusType', name: string, value: number }>, trackRecords: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, remoteId: string, trackerId: number, remoteUrl: string, title: string, status: number, lastChapterRead: number, totalChapters: number, score: number, displayScore: string, startDate: string, finishDate: string }> } }; export type TrackRecordSearchFieldsFragment = { __typename?: 'TrackSearchType', id: number, remoteId: string, title: string, trackingUrl: string, coverUrl: string, publishingType: string, startDate: string, publishingStatus: string, summary: string }; @@ -3269,14 +3332,14 @@ export type ResetServerSettingsMutationVariables = Exact<{ }>; -export type ResetServerSettingsMutation = { __typename?: 'Mutation', resetSettings: { __typename?: 'ResetSettingsPayload', settings: { __typename?: 'SettingsType', ip: string, port: number, socksProxyEnabled: boolean, socksProxyVersion: number, socksProxyHost: string, socksProxyPort: string, socksProxyUsername: string, socksProxyPassword: string, webUIFlavor: WebUiFlavor, initialOpenInBrowserEnabled: boolean, webUIInterface: WebUiInterface, electronPath: string, webUIChannel: WebUiChannel, webUIUpdateCheckInterval: number, downloadAsCbz: boolean, downloadsPath: string, autoDownloadNewChapters: boolean, excludeEntryWithUnreadChapters: boolean, autoDownloadNewChaptersLimit: number, autoDownloadIgnoreReUploads?: boolean | null, extensionRepos: Array, maxSourcesInParallel: number, excludeUnreadChapters: boolean, excludeNotStarted: boolean, excludeCompleted: boolean, globalUpdateInterval: number, updateMangas: boolean, basicAuthEnabled: boolean, basicAuthUsername: string, basicAuthPassword: string, debugLogsEnabled: boolean, systemTrayEnabled: boolean, maxLogFileSize: string, maxLogFiles: number, maxLogFolderSize: string, backupPath: string, backupTime: string, backupInterval: number, backupTTL: number, localSourcePath: string, flareSolverrEnabled: boolean, flareSolverrUrl: string, flareSolverrTimeout: number, flareSolverrSessionName: string, flareSolverrSessionTtl: number, flareSolverrAsResponseFallback: boolean, opdsUseBinaryFileSizes: boolean, opdsItemsPerPage: number, opdsEnablePageReadProgress: boolean, opdsMarkAsReadOnDownload: boolean, opdsShowOnlyUnreadChapters: boolean, opdsShowOnlyDownloadedChapters: boolean, opdsChapterSortOrder: SortOrder } } }; +export type ResetServerSettingsMutation = { __typename?: 'Mutation', resetSettings: { __typename?: 'ResetSettingsPayload', settings: { __typename?: 'SettingsType', ip: string, port: number, socksProxyEnabled: boolean, socksProxyVersion: number, socksProxyHost: string, socksProxyPort: string, socksProxyUsername: string, socksProxyPassword: string, webUIFlavor: WebUiFlavor, initialOpenInBrowserEnabled: boolean, webUIInterface: WebUiInterface, electronPath: string, webUIChannel: WebUiChannel, webUIUpdateCheckInterval: number, downloadAsCbz: boolean, downloadsPath: string, autoDownloadNewChapters: boolean, excludeEntryWithUnreadChapters: boolean, autoDownloadNewChaptersLimit: number, autoDownloadIgnoreReUploads?: boolean | null, extensionRepos: Array, maxSourcesInParallel: number, excludeUnreadChapters: boolean, excludeNotStarted: boolean, excludeCompleted: boolean, globalUpdateInterval: number, updateMangas: boolean, authMode: AuthMode, authPassword: string, authUsername: string, debugLogsEnabled: boolean, systemTrayEnabled: boolean, maxLogFileSize: string, maxLogFiles: number, maxLogFolderSize: string, backupPath: string, backupTime: string, backupInterval: number, backupTTL: number, localSourcePath: string, flareSolverrEnabled: boolean, flareSolverrUrl: string, flareSolverrTimeout: number, flareSolverrSessionName: string, flareSolverrSessionTtl: number, flareSolverrAsResponseFallback: boolean, opdsUseBinaryFileSizes: boolean, opdsItemsPerPage: number, opdsEnablePageReadProgress: boolean, opdsMarkAsReadOnDownload: boolean, opdsShowOnlyUnreadChapters: boolean, opdsShowOnlyDownloadedChapters: boolean, opdsChapterSortOrder: SortOrder } } }; export type UpdateServerSettingsMutationVariables = Exact<{ input: SetSettingsInput; }>; -export type UpdateServerSettingsMutation = { __typename?: 'Mutation', setSettings: { __typename?: 'SetSettingsPayload', settings: { __typename?: 'SettingsType', ip: string, port: number, socksProxyEnabled: boolean, socksProxyVersion: number, socksProxyHost: string, socksProxyPort: string, socksProxyUsername: string, socksProxyPassword: string, webUIFlavor: WebUiFlavor, initialOpenInBrowserEnabled: boolean, webUIInterface: WebUiInterface, electronPath: string, webUIChannel: WebUiChannel, webUIUpdateCheckInterval: number, downloadAsCbz: boolean, downloadsPath: string, autoDownloadNewChapters: boolean, excludeEntryWithUnreadChapters: boolean, autoDownloadNewChaptersLimit: number, autoDownloadIgnoreReUploads?: boolean | null, extensionRepos: Array, maxSourcesInParallel: number, excludeUnreadChapters: boolean, excludeNotStarted: boolean, excludeCompleted: boolean, globalUpdateInterval: number, updateMangas: boolean, basicAuthEnabled: boolean, basicAuthUsername: string, basicAuthPassword: string, debugLogsEnabled: boolean, systemTrayEnabled: boolean, maxLogFileSize: string, maxLogFiles: number, maxLogFolderSize: string, backupPath: string, backupTime: string, backupInterval: number, backupTTL: number, localSourcePath: string, flareSolverrEnabled: boolean, flareSolverrUrl: string, flareSolverrTimeout: number, flareSolverrSessionName: string, flareSolverrSessionTtl: number, flareSolverrAsResponseFallback: boolean, opdsUseBinaryFileSizes: boolean, opdsItemsPerPage: number, opdsEnablePageReadProgress: boolean, opdsMarkAsReadOnDownload: boolean, opdsShowOnlyUnreadChapters: boolean, opdsShowOnlyDownloadedChapters: boolean, opdsChapterSortOrder: SortOrder } } }; +export type UpdateServerSettingsMutation = { __typename?: 'Mutation', setSettings: { __typename?: 'SetSettingsPayload', settings: { __typename?: 'SettingsType', ip: string, port: number, socksProxyEnabled: boolean, socksProxyVersion: number, socksProxyHost: string, socksProxyPort: string, socksProxyUsername: string, socksProxyPassword: string, webUIFlavor: WebUiFlavor, initialOpenInBrowserEnabled: boolean, webUIInterface: WebUiInterface, electronPath: string, webUIChannel: WebUiChannel, webUIUpdateCheckInterval: number, downloadAsCbz: boolean, downloadsPath: string, autoDownloadNewChapters: boolean, excludeEntryWithUnreadChapters: boolean, autoDownloadNewChaptersLimit: number, autoDownloadIgnoreReUploads?: boolean | null, extensionRepos: Array, maxSourcesInParallel: number, excludeUnreadChapters: boolean, excludeNotStarted: boolean, excludeCompleted: boolean, globalUpdateInterval: number, updateMangas: boolean, authMode: AuthMode, authPassword: string, authUsername: string, debugLogsEnabled: boolean, systemTrayEnabled: boolean, maxLogFileSize: string, maxLogFiles: number, maxLogFolderSize: string, backupPath: string, backupTime: string, backupInterval: number, backupTTL: number, localSourcePath: string, flareSolverrEnabled: boolean, flareSolverrUrl: string, flareSolverrTimeout: number, flareSolverrSessionName: string, flareSolverrSessionTtl: number, flareSolverrAsResponseFallback: boolean, opdsUseBinaryFileSizes: boolean, opdsItemsPerPage: number, opdsEnablePageReadProgress: boolean, opdsMarkAsReadOnDownload: boolean, opdsShowOnlyUnreadChapters: boolean, opdsShowOnlyDownloadedChapters: boolean, opdsChapterSortOrder: SortOrder } } }; export type GetSourceMangasFetchMutationVariables = Exact<{ input: FetchSourceMangaInput; @@ -3432,7 +3495,7 @@ export type GetCategoryMangasQueryVariables = Exact<{ }>; -export type GetCategoryMangasQuery = { __typename?: 'Query', category: { __typename?: 'CategoryType', id: number, mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, status: MangaStatus, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } } }; +export type GetCategoryMangasQuery = { __typename?: 'Query', category: { __typename?: 'CategoryType', id: number, mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, status: MangaStatus, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } } }; export type GetChaptersReaderQueryVariables = Exact<{ after?: InputMaybe; @@ -3611,7 +3674,7 @@ export type GetMangasLibraryQueryVariables = Exact<{ }>; -export type GetMangasLibraryQuery = { __typename?: 'Query', mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, status: MangaStatus, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } }; +export type GetMangasLibraryQuery = { __typename?: 'Query', mangas: { __typename?: 'MangaNodeList', totalCount: number, nodes: Array<{ __typename?: 'MangaType', genre: Array, lastFetchedAt?: string | null, inLibraryAt: string, status: MangaStatus, artist?: string | null, author?: string | null, description?: string | null, id: number, title: string, thumbnailUrl?: string | null, thumbnailUrlLastFetched?: string | null, inLibrary: boolean, initialized: boolean, sourceId: string, unreadCount: number, downloadCount: number, bookmarkCount: number, hasDuplicateChapters: boolean, meta: Array<{ __typename?: 'MangaMetaType', mangaId: number, key: string, value: string }>, source?: { __typename?: 'SourceType', id: string, displayName: string } | null, trackRecords: { __typename?: 'TrackRecordNodeList', totalCount: number, nodes: Array<{ __typename?: 'TrackRecordType', id: number, trackerId: number }> }, chapters: { __typename?: 'ChapterNodeList', totalCount: number }, firstUnreadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, isRead: boolean, mangaId: number } | null, lastReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestReadChapter?: { __typename?: 'ChapterType', id: number, sourceOrder: number, lastReadAt: string } | null, latestFetchedChapter?: { __typename?: 'ChapterType', id: number, fetchedAt: string } | null, latestUploadedChapter?: { __typename?: 'ChapterType', id: number, uploadDate: string } | null }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } }; export type GetMangasDuplicatesQueryVariables = Exact<{ after?: InputMaybe; @@ -3662,7 +3725,7 @@ export type GetWebuiUpdateStatusQuery = { __typename?: 'Query', getWebUIUpdateSt export type GetServerSettingsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetServerSettingsQuery = { __typename?: 'Query', settings: { __typename?: 'SettingsType', ip: string, port: number, socksProxyEnabled: boolean, socksProxyVersion: number, socksProxyHost: string, socksProxyPort: string, socksProxyUsername: string, socksProxyPassword: string, webUIFlavor: WebUiFlavor, initialOpenInBrowserEnabled: boolean, webUIInterface: WebUiInterface, electronPath: string, webUIChannel: WebUiChannel, webUIUpdateCheckInterval: number, downloadAsCbz: boolean, downloadsPath: string, autoDownloadNewChapters: boolean, excludeEntryWithUnreadChapters: boolean, autoDownloadNewChaptersLimit: number, autoDownloadIgnoreReUploads?: boolean | null, extensionRepos: Array, maxSourcesInParallel: number, excludeUnreadChapters: boolean, excludeNotStarted: boolean, excludeCompleted: boolean, globalUpdateInterval: number, updateMangas: boolean, basicAuthEnabled: boolean, basicAuthUsername: string, basicAuthPassword: string, debugLogsEnabled: boolean, systemTrayEnabled: boolean, maxLogFileSize: string, maxLogFiles: number, maxLogFolderSize: string, backupPath: string, backupTime: string, backupInterval: number, backupTTL: number, localSourcePath: string, flareSolverrEnabled: boolean, flareSolverrUrl: string, flareSolverrTimeout: number, flareSolverrSessionName: string, flareSolverrSessionTtl: number, flareSolverrAsResponseFallback: boolean, opdsUseBinaryFileSizes: boolean, opdsItemsPerPage: number, opdsEnablePageReadProgress: boolean, opdsMarkAsReadOnDownload: boolean, opdsShowOnlyUnreadChapters: boolean, opdsShowOnlyDownloadedChapters: boolean, opdsChapterSortOrder: SortOrder } }; +export type GetServerSettingsQuery = { __typename?: 'Query', settings: { __typename?: 'SettingsType', ip: string, port: number, socksProxyEnabled: boolean, socksProxyVersion: number, socksProxyHost: string, socksProxyPort: string, socksProxyUsername: string, socksProxyPassword: string, webUIFlavor: WebUiFlavor, initialOpenInBrowserEnabled: boolean, webUIInterface: WebUiInterface, electronPath: string, webUIChannel: WebUiChannel, webUIUpdateCheckInterval: number, downloadAsCbz: boolean, downloadsPath: string, autoDownloadNewChapters: boolean, excludeEntryWithUnreadChapters: boolean, autoDownloadNewChaptersLimit: number, autoDownloadIgnoreReUploads?: boolean | null, extensionRepos: Array, maxSourcesInParallel: number, excludeUnreadChapters: boolean, excludeNotStarted: boolean, excludeCompleted: boolean, globalUpdateInterval: number, updateMangas: boolean, authMode: AuthMode, authPassword: string, authUsername: string, debugLogsEnabled: boolean, systemTrayEnabled: boolean, maxLogFileSize: string, maxLogFiles: number, maxLogFolderSize: string, backupPath: string, backupTime: string, backupInterval: number, backupTTL: number, localSourcePath: string, flareSolverrEnabled: boolean, flareSolverrUrl: string, flareSolverrTimeout: number, flareSolverrSessionName: string, flareSolverrSessionTtl: number, flareSolverrAsResponseFallback: boolean, opdsUseBinaryFileSizes: boolean, opdsItemsPerPage: number, opdsEnablePageReadProgress: boolean, opdsMarkAsReadOnDownload: boolean, opdsShowOnlyUnreadChapters: boolean, opdsShowOnlyDownloadedChapters: boolean, opdsChapterSortOrder: SortOrder } }; export type GetSourceBrowseQueryVariables = Exact<{ id: Scalars['LongString']['input']; @@ -3703,7 +3766,7 @@ export type GetTrackersSettingsQuery = { __typename?: 'Query', trackers: { __typ export type GetTrackersBindQueryVariables = Exact<{ [key: string]: never; }>; -export type GetTrackersBindQuery = { __typename?: 'Query', trackers: { __typename?: 'TrackerNodeList', totalCount: number, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null }, nodes: Array<{ __typename?: 'TrackerType', icon: string, supportsTrackDeletion?: boolean | null, scores: Array, id: number, name: string, isLoggedIn: boolean, isTokenExpired: boolean, statuses: Array<{ __typename?: 'TrackStatusType', name: string, value: number }>, trackRecords: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, remoteId: string, trackerId: number, remoteUrl: string, title: string, status: number, lastChapterRead: number, totalChapters: number, score: number, displayScore: string, startDate: string, finishDate: string }> } }> } }; +export type GetTrackersBindQuery = { __typename?: 'Query', trackers: { __typename?: 'TrackerNodeList', totalCount: number, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null }, nodes: Array<{ __typename?: 'TrackerType', icon: string, supportsTrackDeletion: boolean, scores: Array, id: number, name: string, isLoggedIn: boolean, isTokenExpired: boolean, statuses: Array<{ __typename?: 'TrackStatusType', name: string, value: number }>, trackRecords: { __typename?: 'TrackRecordNodeList', nodes: Array<{ __typename?: 'TrackRecordType', id: number, remoteId: string, trackerId: number, remoteUrl: string, title: string, status: number, lastChapterRead: number, totalChapters: number, score: number, displayScore: string, startDate: string, finishDate: string }> } }> } }; export type TrackerSearchQueryVariables = Exact<{ query: Scalars['String']['input']; diff --git a/src/modules/settings/Settings.constants.ts b/src/modules/settings/Settings.constants.ts index 8f4dcacb..bff21d99 100644 --- a/src/modules/settings/Settings.constants.ts +++ b/src/modules/settings/Settings.constants.ts @@ -8,10 +8,16 @@ import { DEFAULT_DEVICE } from '@/modules/device/services/Device.ts'; import { DEFAULT_SORT_SETTINGS } from '@/modules/migration/Migration.constants.ts'; -import { MetadataServerSettings } from '@/modules/settings/Settings.types.ts'; +import { GlobalUpdateSkipEntriesSettings, MetadataServerSettings } from '@/modules/settings/Settings.types.ts'; import { GridLayout } from '@/modules/core/Core.types.ts'; import { getDefaultLanguages } from '@/modules/core/utils/Languages.ts'; import { ThemeMode } from '@/modules/theme/contexts/AppThemeContext.tsx'; +import { + SelectSettingValue, + SelectSettingValueDisplayInfo, +} from '@/modules/core/components/settings/SelectSetting.tsx'; +import { AuthMode, WebUiChannel, WebUiFlavor, WebUiInterface } from '@/lib/graphql/generated/graphql.ts'; +import { TranslationKey } from '@/Base.types.ts'; export const MANGA_GRID_WIDTH = { min: 100, @@ -75,3 +81,109 @@ export const SERVER_SETTINGS_METADATA_DEFAULT: MetadataServerSettings = { mangaDynamicColorSchemes: true, mangaGridItemWidth: MANGA_GRID_WIDTH.default, }; + +const AUTH_MODES = [AuthMode.None].concat(Object.values(AuthMode).filter((mode) => mode !== AuthMode.None)); +const AUTH_MODES_TO_TRANSLATION_KEY: { [mode in AuthMode]: SelectSettingValueDisplayInfo } = { + [AuthMode.None]: { + text: 'settings.server.auth.mode.option.none.label.title', + description: 'settings.server.auth.mode.option.none.label.description', + disclaimer: 'settings.server.auth.mode.option.none.label.info', + }, + [AuthMode.BasicAuth]: { + text: 'settings.server.auth.mode.option.basicAuth.label.title', + description: 'settings.server.auth.mode.option.basicAuth.label.description', + }, + [AuthMode.SimpleLogin]: { + text: 'settings.server.auth.mode.option.simpleLogin.label.title', + description: 'settings.server.auth.mode.option.simpleLogin.label.description', + disclaimer: 'settings.server.auth.mode.option.simpleLogin.label.info', + }, +}; +export const AUTH_MODES_SELECT_VALUES: SelectSettingValue[] = AUTH_MODES.map((mode) => [ + mode, + AUTH_MODES_TO_TRANSLATION_KEY[mode], +]); + +const WEB_UI_FLAVORS = Object.values(WebUiFlavor); +const WEB_UI_FLAVOR_TO_TRANSLATION_KEY: { [flavor in WebUiFlavor]: SelectSettingValueDisplayInfo } = { + [WebUiFlavor.Webui]: { + text: 'settings.webui.title.webui', + description: 'settings.webui.flavor.option.webui.label.description', + disclaimer: 'settings.webui.flavor.label.info', + }, + [WebUiFlavor.Vui]: { + text: 'settings.webui.flavor.option.vui.label.title', + description: 'settings.webui.flavor.option.vui.label.description', + disclaimer: 'settings.webui.flavor.label.info', + }, + [WebUiFlavor.Custom]: { + text: 'settings.webui.flavor.option.custom.label.title', + description: 'settings.webui.flavor.option.custom.label.description', + }, +}; +export const WEB_UI_FLAVOR_SELECT_VALUES: SelectSettingValue[] = WEB_UI_FLAVORS.map((flavor) => [ + flavor, + WEB_UI_FLAVOR_TO_TRANSLATION_KEY[flavor], +]); + +const WEB_UI_CHANNELS = Object.values(WebUiChannel); +const WEB_UI_CHANNEL_TO_TRANSLATION_KEYS: { + [channel in WebUiChannel]: SelectSettingValueDisplayInfo; +} = { + [WebUiChannel.Bundled]: { + text: 'settings.webui.channel.option.bundled.label.title', + description: 'settings.webui.channel.option.bundled.label.description', + disclaimer: 'settings.webui.flavor.label.info', + }, + [WebUiChannel.Stable]: { + text: 'settings.webui.channel.option.stable.label.title', + description: 'settings.webui.channel.option.stable.label.description', + disclaimer: 'settings.webui.flavor.label.info', + }, + [WebUiChannel.Preview]: { + text: 'settings.webui.channel.option.preview.label.title', + description: 'settings.webui.channel.option.preview.label.description', + disclaimer: 'settings.webui.channel.option.preview.label.disclaimer', + }, +}; +export const WEB_UI_CHANNEL_SELECT_VALUES: SelectSettingValue[] = WEB_UI_CHANNELS.map((channel) => [ + channel, + WEB_UI_CHANNEL_TO_TRANSLATION_KEYS[channel], +]); + +const WEB_UI_INTERFACES = Object.values(WebUiInterface); +const WEB_UI_INTERFACE_TO_TRANSLATION_KEYS: { + [webUIInterface in WebUiInterface]: SelectSettingValueDisplayInfo; +} = { + [WebUiInterface.Browser]: { + text: 'settings.webui.interface.option.label.browser', + description: 'settings.webui.interface.label.description', + }, + [WebUiInterface.Electron]: { + text: 'settings.webui.interface.option.label.electron', + description: 'settings.webui.interface.label.description', + }, +}; +export const WEB_UI_INTERFACE_SELECT_VALUES: SelectSettingValue[] = WEB_UI_INTERFACES.map( + (webUIInterface) => [webUIInterface, WEB_UI_INTERFACE_TO_TRANSLATION_KEYS[webUIInterface]], +); + +export const GLOBAL_UPDATE_INTERVAL = { + default: 12, + min: 6, + max: 24 * 7 * 4, // 1 month +}; + +export const GLOBAL_UPDATE_SKIP_ENTRIES_TO_TRANSLATION: { + [setting in keyof GlobalUpdateSkipEntriesSettings]: TranslationKey; +} = { + excludeUnreadChapters: 'library.settings.global_update.entries.label.unread_chapters', + excludeNotStarted: 'library.settings.global_update.entries.label.not_started', + excludeCompleted: 'library.settings.global_update.entries.label.completed', +}; + +export const WEB_UI_UPDATE_INTERVAL = { + default: 23, + min: 1, + max: 23, +}; diff --git a/src/modules/settings/Settings.types.ts b/src/modules/settings/Settings.types.ts index 14a364c4..0ee1483e 100644 --- a/src/modules/settings/Settings.types.ts +++ b/src/modules/settings/Settings.types.ts @@ -16,6 +16,7 @@ import { MetadataUpdateSettings } from '@/modules/app-updates/AppUpdateChecker.t import { MetadataThemeSettings } from '@/modules/theme/AppTheme.types.ts'; import { GetServerSettingsQuery } from '@/lib/graphql/generated/graphql.ts'; import { MetadataHistorySettings } from '@/modules/history/History.types.ts'; +import { ServerSettings as GqlServerSettings } from '@/modules/settings/Settings.types.ts'; export type MetadataServerSettingKeys = keyof MetadataServerSettings; @@ -36,3 +37,52 @@ export interface ISearchSettings { } export type ServerSettings = Omit; +export type ServerSettingsType = Pick< + GqlServerSettings, + | 'ip' + | 'port' + | 'socksProxyEnabled' + | 'socksProxyVersion' + | 'socksProxyHost' + | 'socksProxyPort' + | 'socksProxyUsername' + | 'socksProxyPassword' + | 'debugLogsEnabled' + | 'systemTrayEnabled' + | 'maxLogFiles' + | 'maxLogFileSize' + | 'maxLogFolderSize' + | 'authMode' + | 'authUsername' + | 'authPassword' + | 'flareSolverrEnabled' + | 'flareSolverrTimeout' + | 'flareSolverrUrl' + | 'flareSolverrSessionName' + | 'flareSolverrSessionTtl' + | 'flareSolverrAsResponseFallback' + | 'opdsUseBinaryFileSizes' + | 'opdsItemsPerPage' + | 'opdsEnablePageReadProgress' + | 'opdsMarkAsReadOnDownload' + | 'opdsShowOnlyUnreadChapters' + | 'opdsShowOnlyDownloadedChapters' + | 'opdsChapterSortOrder' +>; + +export type WebUISettingsType = Pick< + ServerSettings, + | 'webUIFlavor' + | 'initialOpenInBrowserEnabled' + | 'webUIInterface' + | 'electronPath' + | 'webUIChannel' + | 'webUIUpdateCheckInterval' +>; + +export type GlobalUpdateSkipEntriesSettings = Pick< + ServerSettings, + 'excludeUnreadChapters' | 'excludeNotStarted' | 'excludeCompleted' +>; + +export type LibrarySettingsType = Pick; diff --git a/src/modules/settings/components/globalUpdate/GlobalUpdateSettings.tsx b/src/modules/settings/components/globalUpdate/GlobalUpdateSettings.tsx index 4dd93ee6..b02cc0f0 100644 --- a/src/modules/settings/components/globalUpdate/GlobalUpdateSettings.tsx +++ b/src/modules/settings/components/globalUpdate/GlobalUpdateSettings.tsx @@ -20,11 +20,9 @@ import { GlobalUpdateSettingsEntries } from '@/modules/settings/components/globa import { GlobalUpdateSettingsInterval } from '@/modules/settings/components/globalUpdate/GlobalUpdateSettingsInterval.tsx'; import { requestManager } from '@/lib/requests/RequestManager.ts'; import { makeToast } from '@/modules/core/utils/Toast.ts'; -import { ServerSettings } from '@/modules/settings/Settings.types.ts'; +import { LibrarySettingsType, ServerSettings } from '@/modules/settings/Settings.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -type LibrarySettingsType = Pick; - export const GlobalUpdateSettings = ({ serverSettings, categories, diff --git a/src/modules/settings/components/globalUpdate/GlobalUpdateSettingsEntries.tsx b/src/modules/settings/components/globalUpdate/GlobalUpdateSettingsEntries.tsx index d61b14d7..5f141295 100644 --- a/src/modules/settings/components/globalUpdate/GlobalUpdateSettingsEntries.tsx +++ b/src/modules/settings/components/globalUpdate/GlobalUpdateSettingsEntries.tsx @@ -20,34 +20,23 @@ import { requestManager } from '@/lib/requests/RequestManager.ts'; import { makeToast } from '@/modules/core/utils/Toast.ts'; import { CheckboxContainer } from '@/modules/core/components/inputs/CheckboxContainer.ts'; import { CheckboxInput } from '@/modules/core/components/inputs/CheckboxInput.tsx'; -import { TranslationKey } from '@/Base.types.ts'; -import { ServerSettings } from '@/modules/settings/Settings.types.ts'; +import { GlobalUpdateSkipEntriesSettings, ServerSettings } from '@/modules/settings/Settings.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; - -type GlobalUpdateSkipEntriesSettings = Pick< - ServerSettings, - 'excludeUnreadChapters' | 'excludeNotStarted' | 'excludeCompleted' ->; - -const settingToTextMap: { [setting in keyof GlobalUpdateSkipEntriesSettings]: TranslationKey } = { - excludeUnreadChapters: 'library.settings.global_update.entries.label.unread_chapters', - excludeNotStarted: 'library.settings.global_update.entries.label.not_started', - excludeCompleted: 'library.settings.global_update.entries.label.completed', -}; +import { GLOBAL_UPDATE_SKIP_ENTRIES_TO_TRANSLATION } from '@/modules/settings/Settings.constants.ts'; const getSkipMangasText = (settings: GlobalUpdateSkipEntriesSettings) => { const skipSettings: string[] = []; if (settings.excludeUnreadChapters) { - skipSettings.push(translate(settingToTextMap.excludeUnreadChapters) as string); + skipSettings.push(translate(GLOBAL_UPDATE_SKIP_ENTRIES_TO_TRANSLATION.excludeUnreadChapters) as string); } if (settings.excludeNotStarted) { - skipSettings.push(translate(settingToTextMap.excludeNotStarted) as string); + skipSettings.push(translate(GLOBAL_UPDATE_SKIP_ENTRIES_TO_TRANSLATION.excludeNotStarted) as string); } if (settings.excludeCompleted) { - skipSettings.push(translate(settingToTextMap.excludeCompleted) as string); + skipSettings.push(translate(GLOBAL_UPDATE_SKIP_ENTRIES_TO_TRANSLATION.excludeCompleted) as string); } const isNothingExcluded = !skipSettings.length; @@ -132,7 +121,11 @@ export const GlobalUpdateSettingsEntries = ({ serverSettings }: { serverSettings {Object.entries(dialogSettings).map(([setting, value]) => ( { setDialogSettings({ diff --git a/src/modules/settings/components/globalUpdate/GlobalUpdateSettingsInterval.tsx b/src/modules/settings/components/globalUpdate/GlobalUpdateSettingsInterval.tsx index c18b7650..cb5b6466 100644 --- a/src/modules/settings/components/globalUpdate/GlobalUpdateSettingsInterval.tsx +++ b/src/modules/settings/components/globalUpdate/GlobalUpdateSettingsInterval.tsx @@ -17,10 +17,7 @@ import { NumberSetting } from '@/modules/core/components/settings/NumberSetting. import { getPersistedServerSetting, usePersistedValue } from '@/modules/core/hooks/usePersistedValue.tsx'; import { ServerSettings } from '@/modules/settings/Settings.types.ts'; - -const DEFAULT_INTERVAL_HOURS = 12; -const MIN_INTERVAL_HOURS = 6; -const MAX_INTERVAL_HOURS = 24 * 7 * 4; // 1 month +import { GLOBAL_UPDATE_INTERVAL } from '@/modules/settings/Settings.constants.ts'; export const GlobalUpdateSettingsInterval = ({ globalUpdateInterval, @@ -34,7 +31,7 @@ export const GlobalUpdateSettingsInterval = ({ const [mutateSettings] = requestManager.useUpdateServerSettings(); const [currentAutoUpdateIntervalHours, persistAutoUpdateIntervalHours] = usePersistedValue( 'lastGlobalUpdateInterval', - DEFAULT_INTERVAL_HOURS, + GLOBAL_UPDATE_INTERVAL.default, autoUpdateIntervalHours, getPersistedServerSetting, ); @@ -66,9 +63,9 @@ export const GlobalUpdateSettingsInterval = ({ hours: currentAutoUpdateIntervalHours, })} value={currentAutoUpdateIntervalHours} - minValue={MIN_INTERVAL_HOURS} - maxValue={MAX_INTERVAL_HOURS} - defaultValue={DEFAULT_INTERVAL_HOURS} + minValue={GLOBAL_UPDATE_INTERVAL.min} + maxValue={GLOBAL_UPDATE_INTERVAL.max} + defaultValue={GLOBAL_UPDATE_INTERVAL.default} showSlider valueUnit={t('global.time.hour_short')} handleUpdate={updateSetting} diff --git a/src/modules/settings/components/webUI/WebUIUpdateIntervalSetting.tsx b/src/modules/settings/components/webUI/WebUIUpdateIntervalSetting.tsx index 3d880ceb..63e3bb20 100644 --- a/src/modules/settings/components/webUI/WebUIUpdateIntervalSetting.tsx +++ b/src/modules/settings/components/webUI/WebUIUpdateIntervalSetting.tsx @@ -18,10 +18,7 @@ import { getPersistedServerSetting, usePersistedValue } from '@/modules/core/hoo import { makeToast } from '@/modules/core/utils/Toast.ts'; import { ServerSettings } from '@/modules/settings/Settings.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; - -const DEFAULT_VALUE = 23; -const MIN_VALUE = 1; -const MAX_VALUE = 23; // 1 month +import { WEB_UI_UPDATE_INTERVAL } from '@/modules/settings/Settings.constants.ts'; export const WebUIUpdateIntervalSetting = ({ disabled = false, @@ -36,7 +33,7 @@ export const WebUIUpdateIntervalSetting = ({ const [mutateSettings] = requestManager.useUpdateServerSettings(); const [currentUpdateCheckInterval, persistUpdateCheckInterval] = usePersistedValue( 'lastUpdateCheckInterval', - DEFAULT_VALUE, + WEB_UI_UPDATE_INTERVAL.default, updateCheckInterval, getPersistedServerSetting, ); @@ -75,9 +72,9 @@ export const WebUIUpdateIntervalSetting = ({ hours: currentUpdateCheckInterval, })} value={currentUpdateCheckInterval} - minValue={MIN_VALUE} - maxValue={MAX_VALUE} - defaultValue={DEFAULT_VALUE} + minValue={WEB_UI_UPDATE_INTERVAL.min} + maxValue={WEB_UI_UPDATE_INTERVAL.max} + defaultValue={WEB_UI_UPDATE_INTERVAL.default} showSlider valueUnit={t('global.time.hour_short')} handleUpdate={updateSetting} diff --git a/src/modules/settings/screens/ServerSettings.tsx b/src/modules/settings/screens/ServerSettings.tsx index 118ca814..9e58023e 100644 --- a/src/modules/settings/screens/ServerSettings.tsx +++ b/src/modules/settings/screens/ServerSettings.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { useTranslation, Trans } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; import { useMemo } from 'react'; import Link from '@mui/material/Link'; import List from '@mui/material/List'; @@ -29,43 +29,11 @@ import { } from '@/modules/settings/services/ServerSettingsMetadata.ts'; import { makeToast } from '@/modules/core/utils/Toast.ts'; import { MetadataUpdateSettings } from '@/modules/app-updates/AppUpdateChecker.types.ts'; -import { ServerSettings as GqlServerSettings } from '@/modules/settings/Settings.types.ts'; +import { ServerSettings as GqlServerSettings, ServerSettingsType } from '@/modules/settings/Settings.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; -import { SortOrder } from '@/lib/graphql/generated/graphql'; - -type ServerSettingsType = Pick< - GqlServerSettings, - | 'ip' - | 'port' - | 'socksProxyEnabled' - | 'socksProxyVersion' - | 'socksProxyHost' - | 'socksProxyPort' - | 'socksProxyUsername' - | 'socksProxyPassword' - | 'debugLogsEnabled' - | 'systemTrayEnabled' - | 'maxLogFiles' - | 'maxLogFileSize' - | 'maxLogFolderSize' - | 'basicAuthEnabled' - | 'basicAuthUsername' - | 'basicAuthPassword' - | 'flareSolverrEnabled' - | 'flareSolverrTimeout' - | 'flareSolverrUrl' - | 'flareSolverrSessionName' - | 'flareSolverrSessionTtl' - | 'flareSolverrAsResponseFallback' - | 'opdsUseBinaryFileSizes' - | 'opdsItemsPerPage' - | 'opdsEnablePageReadProgress' - | 'opdsMarkAsReadOnDownload' - | 'opdsShowOnlyUnreadChapters' - | 'opdsShowOnlyDownloadedChapters' - | 'opdsChapterSortOrder' ->; +import { AuthMode, SortOrder } from '@/lib/graphql/generated/graphql'; +import { AUTH_MODES_SELECT_VALUES } from '@/modules/settings/Settings.constants.ts'; const extractServerSettings = (settings: GqlServerSettings): ServerSettingsType => ({ ip: settings.ip, @@ -81,9 +49,9 @@ const extractServerSettings = (settings: GqlServerSettings): ServerSettingsType maxLogFiles: settings.maxLogFiles, maxLogFileSize: settings.maxLogFileSize, maxLogFolderSize: settings.maxLogFolderSize, - basicAuthEnabled: settings.basicAuthEnabled, - basicAuthUsername: settings.basicAuthUsername, - basicAuthPassword: settings.basicAuthPassword, + authMode: settings.authMode, + authUsername: settings.authUsername, + authPassword: settings.authPassword, flareSolverrEnabled: settings.flareSolverrEnabled, flareSolverrTimeout: settings.flareSolverrTimeout, flareSolverrUrl: settings.flareSolverrUrl, @@ -216,6 +184,7 @@ export const ServerSettings = () => { } const serverSettings = extractServerSettings(data!.settings); + const authModeDisabled = !serverSettings.authUsername.trim() || !serverSettings.authPassword.trim(); return ( @@ -295,27 +264,25 @@ export const ServerSettings = () => { } > - - - updateSetting('basicAuthEnabled', e.target.checked)} - /> - - !serverSettings.basicAuthEnabled || !!value.trim()} - handleChange={(authUsername) => updateSetting('basicAuthUsername', authUsername)} + + settingName={t('settings.server.auth.label.title')} + value={serverSettings.authMode} + values={AUTH_MODES_SELECT_VALUES} + handleChange={(mode) => updateSetting('authMode', mode)} + disabled={authModeDisabled} /> serverSettings.authMode === AuthMode.None || !!value.trim()} + handleChange={(authUsername) => updateSetting('authUsername', authUsername)} + /> + !serverSettings.basicAuthEnabled || !!value.trim()} - handleChange={(authPassword) => updateSetting('basicAuthPassword', authPassword)} + validate={(value) => serverSettings.authMode === AuthMode.None || !!value.trim()} + handleChange={(authPassword) => updateSetting('authPassword', authPassword)} /> ; - -const FLAVORS = Object.values(WebUiFlavor); -const FLAVOR_TO_TRANSLATION_KEY: { [flavor in WebUiFlavor]: SelectSettingValueDisplayInfo } = { - [WebUiFlavor.Webui]: { - text: 'settings.webui.title.webui', - description: 'settings.webui.flavor.option.webui.label.description', - disclaimer: 'settings.webui.flavor.label.info', - }, - [WebUiFlavor.Vui]: { - text: 'settings.webui.flavor.option.vui.label.title', - description: 'settings.webui.flavor.option.vui.label.description', - disclaimer: 'settings.webui.flavor.label.info', - }, - [WebUiFlavor.Custom]: { - text: 'settings.webui.flavor.option.custom.label.title', - description: 'settings.webui.flavor.option.custom.label.description', - }, -}; -const FLAVOR_SELECT_VALUES: SelectSettingValue[] = FLAVORS.map((flavor) => [ - flavor, - FLAVOR_TO_TRANSLATION_KEY[flavor], -]); - -const CHANNELS = Object.values(WebUiChannel); -const CHANNEL_TO_TRANSLATION_KEYS: { - [channel in WebUiChannel]: SelectSettingValueDisplayInfo; -} = { - [WebUiChannel.Bundled]: { - text: 'settings.webui.channel.option.bundled.label.title', - description: 'settings.webui.channel.option.bundled.label.description', - disclaimer: 'settings.webui.flavor.label.info', - }, - [WebUiChannel.Stable]: { - text: 'settings.webui.channel.option.stable.label.title', - description: 'settings.webui.channel.option.stable.label.description', - disclaimer: 'settings.webui.flavor.label.info', - }, - [WebUiChannel.Preview]: { - text: 'settings.webui.channel.option.preview.label.title', - description: 'settings.webui.channel.option.preview.label.description', - disclaimer: 'settings.webui.channel.option.preview.label.disclaimer', - }, -}; -const CHANNEL_SELECT_VALUES: SelectSettingValue[] = CHANNELS.map((channel) => [ - channel, - CHANNEL_TO_TRANSLATION_KEYS[channel], -]); - -const INTERFACES = Object.values(WebUiInterface); -const INTERFACE_TO_TRANSLATION_KEYS: { - [webUIInterface in WebUiInterface]: SelectSettingValueDisplayInfo; -} = { - [WebUiInterface.Browser]: { - text: 'settings.webui.interface.option.label.browser', - description: 'settings.webui.interface.label.description', - }, - [WebUiInterface.Electron]: { - text: 'settings.webui.interface.option.label.electron', - description: 'settings.webui.interface.label.description', - }, -}; -const INTERFACE_SELECT_VALUES: SelectSettingValue[] = INTERFACES.map((webUIInterface) => [ - webUIInterface, - INTERFACE_TO_TRANSLATION_KEYS[webUIInterface], -]); +import { + WEB_UI_CHANNEL_SELECT_VALUES, + WEB_UI_FLAVOR_SELECT_VALUES, + WEB_UI_INTERFACE_SELECT_VALUES, +} from '@/modules/settings/Settings.constants.ts'; const extractWebUISettings = (settings: ServerSettings): WebUISettingsType => ({ webUIFlavor: settings.webUIFlavor, @@ -190,7 +116,7 @@ export const WebUISettings = () => { settingName={t('settings.webui.flavor.label.title')} value={webUISettings.webUIFlavor} - values={FLAVOR_SELECT_VALUES} + values={WEB_UI_FLAVOR_SELECT_VALUES} handleChange={(flavor) => updateSetting('webUIFlavor', flavor)} /> @@ -204,7 +130,7 @@ export const WebUISettings = () => { settingName={t('settings.webui.interface.label.title')} value={webUISettings.webUIInterface} - values={INTERFACE_SELECT_VALUES} + values={WEB_UI_INTERFACE_SELECT_VALUES} handleChange={(webUIInterface) => updateSetting('webUIInterface', webUIInterface)} /> { settingName={t('settings.webui.channel.label.title')} value={webUISettings.webUIChannel} - values={CHANNEL_SELECT_VALUES} + values={WEB_UI_CHANNEL_SELECT_VALUES} handleChange={(channel) => updateSetting('webUIChannel', channel)} disabled={isCustomWebUI} /> diff --git a/versionToServerVersionMapping.json b/versionToServerVersionMapping.json index 8c16faff..18eaf6e8 100644 --- a/versionToServerVersionMapping.json +++ b/versionToServerVersionMapping.json @@ -1,7 +1,7 @@ [ { "uiVersion": "PREVIEW", - "serverVersion": "r1754" + "serverVersion": "r1837" }, { "tag": "20250703.01",