From f3c479fe3283e3b18e2592ceac45b796e17355f1 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Mon, 23 Jun 2025 01:51:14 +0200 Subject: [PATCH] Add option to disable sources of an extension --- .../extension/screens/ExtensionInfo.tsx | 79 +++++++++++-------- .../global-search/screens/SearchAll.tsx | 1 + src/modules/metadata/Metadata.constants.ts | 1 + src/modules/source/Source.types.ts | 1 + src/modules/source/screens/Sources.tsx | 8 +- src/modules/source/services/SourceMetadata.ts | 1 + src/modules/source/services/Sources.ts | 16 +++- 7 files changed, 72 insertions(+), 35 deletions(-) diff --git a/src/modules/extension/screens/ExtensionInfo.tsx b/src/modules/extension/screens/ExtensionInfo.tsx index 2a6fa61b..83f03b91 100644 --- a/src/modules/extension/screens/ExtensionInfo.tsx +++ b/src/modules/extension/screens/ExtensionInfo.tsx @@ -17,6 +17,8 @@ import Card from '@mui/material/Card'; import IconButton from '@mui/material/IconButton'; import SettingsIcon from '@mui/icons-material/Settings'; import Divider from '@mui/material/Divider'; +import Switch from '@mui/material/Switch'; +import CardActionArea from '@mui/material/CardActionArea'; import { SpinnerImage } from '@/modules/core/components/SpinnerImage.tsx'; import { Metadata } from '@/modules/core/components/texts/Metadata.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; @@ -41,7 +43,8 @@ import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { SourceConfigurableInfo, SourceIdInfo, SourceLanguageInfo } from '@/modules/source/Source.types.ts'; import { MUIUtil } from '@/lib/mui/MUI.util.ts'; import { useBackButton } from '@/modules/core/hooks/useBackButton.ts'; -import { OptionalCardActionAreaLink } from '@/modules/core/components/lists/cards/OptionalCardActionAreaLink.tsx'; +import { createUpdateSourceMetadata, useGetSourceMetadata } from '@/modules/source/services/SourceMetadata.ts'; +import { makeToast } from '@/modules/core/utils/Toast.ts'; const Header = ({ name, pkgName, iconUrl, repo }: TExtension) => ( @@ -140,43 +143,53 @@ const ActionButton = ({ pkgName, isInstalled, isObsolete, hasUpdate }: TExtensio ); }; -const SourceList = ({ sources }: { sources: (SourceIdInfo & SourceLanguageInfo & SourceConfigurableInfo)[] }) => { +const SourceCard = (source: SourceIdInfo & SourceLanguageInfo & SourceConfigurableInfo) => { + const { id, isConfigurable } = source; + const { t } = useTranslation(); + const { isEnabled } = useGetSourceMetadata(source); + + const updateSetting = createUpdateSourceMetadata(source, (e) => + makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)), + ); return ( - - {sources?.map((source) => ( - - - - - - {translateExtensionLanguage(Sources.getLanguage(source))} - - {source.isConfigurable && ( - - - - - - )} - - - - - ))} - + + + updateSetting('isEnabled', !isEnabled)}> + + + {translateExtensionLanguage(Sources.getLanguage(source))} + + {isConfigurable && ( + + e.stopPropagation()} + {...MUIUtil.preventRippleProp()} + > + + + + )} + updateSetting('isEnabled', checked)} /> + + + + ); }; +const SourceList = ({ sources }: { sources: (SourceIdInfo & SourceLanguageInfo & SourceConfigurableInfo)[] }) => ( + + {sources.map((source) => ( + + ))} + +); + export const ExtensionInfo = () => { const { t } = useTranslation(); const { pkgName } = useParams<{ pkgName: string }>(); @@ -195,7 +208,7 @@ export const ExtensionInfo = () => { return sourcesResponse.data.sources.nodes .filter((source) => source.extension.pkgName === extension?.pkgName) .sort((a, b) => languageSortComparator(Sources.getLanguage(a), Sources.getLanguage(b))); - }, [extension?.pkgName]); + }, [extension?.pkgName, sourcesResponse.data]); const isLoading = extensionResponse.loading || sourcesResponse.loading; const error = extensionResponse.error || sourcesResponse.error; diff --git a/src/modules/global-search/screens/SearchAll.tsx b/src/modules/global-search/screens/SearchAll.tsx index a1785e01..1f190e0d 100644 --- a/src/modules/global-search/screens/SearchAll.tsx +++ b/src/modules/global-search/screens/SearchAll.tsx @@ -263,6 +263,7 @@ export const SearchAll: React.FC = () => { languages: shownLangs, keepLocalSource: true, pinned: shouldShowOnlyPinnedSources, + enabled: true, }), [sources, shownLangs, shouldShowOnlyPinnedSources], ); diff --git a/src/modules/metadata/Metadata.constants.ts b/src/modules/metadata/Metadata.constants.ts index b705773e..23791eaf 100644 --- a/src/modules/metadata/Metadata.constants.ts +++ b/src/modules/metadata/Metadata.constants.ts @@ -96,6 +96,7 @@ export const APP_METADATA_KEY_TO_TYPE = { shouldUsePureBlackMode: 'boolean', mangaGridItemWidth: 'number', isPinned: 'boolean', + isEnabled: 'boolean', lastUsedSourceId: 'string', shouldShowOnlySourcesWithResults: 'boolean', excludedScanlators: 'string', // string[] diff --git a/src/modules/source/Source.types.ts b/src/modules/source/Source.types.ts index b821605d..14e0abbe 100644 --- a/src/modules/source/Source.types.ts +++ b/src/modules/source/Source.types.ts @@ -27,6 +27,7 @@ export type SavedSourceSearch = { query?: string; filters?: IPos[] }; export interface ISourceMetadata { savedSearches?: Record; isPinned: boolean; + isEnabled: boolean; } export type SourceFilters = GetSourceBrowseQuery['source']['filters'][number]; diff --git a/src/modules/source/screens/Sources.tsx b/src/modules/source/screens/Sources.tsx index efa8b337..0310346e 100644 --- a/src/modules/source/screens/Sources.tsx +++ b/src/modules/source/screens/Sources.tsx @@ -48,7 +48,13 @@ export function Sources({ tabsMenuHeight }: { tabsMenuHeight: number }) { } = requestManager.useGetSourceList({ notifyOnNetworkStatusChange: true }); const sources = data?.sources.nodes; const filteredSources = useMemo( - () => SourceService.filter(sources ?? [], { showNsfw, languages: shownLangs, keepLocalSource: true }), + () => + SourceService.filter(sources ?? [], { + showNsfw, + languages: shownLangs, + keepLocalSource: true, + enabled: true, + }), [sources, shownLangs], ); const sourcesByLanguage = useMemo(() => { diff --git a/src/modules/source/services/SourceMetadata.ts b/src/modules/source/services/SourceMetadata.ts index e38a701d..ec72839a 100644 --- a/src/modules/source/services/SourceMetadata.ts +++ b/src/modules/source/services/SourceMetadata.ts @@ -23,6 +23,7 @@ import { const DEFAULT_SOURCE_METADATA: ISourceMetadata = { savedSearches: undefined, isPinned: false, + isEnabled: true, }; const convertAppMetadataToGqlMetadata = ( diff --git a/src/modules/source/services/Sources.ts b/src/modules/source/services/Sources.ts index 832b2e40..ebc55f69 100644 --- a/src/modules/source/services/Sources.ts +++ b/src/modules/source/services/Sources.ts @@ -81,7 +81,14 @@ export class Sources { languages, keepLocalSource, pinned, - }: { showNsfw?: boolean; languages?: string[]; keepLocalSource?: boolean; pinned?: boolean } = {}, + enabled, + }: { + showNsfw?: boolean; + languages?: string[]; + keepLocalSource?: boolean; + pinned?: boolean; + enabled?: boolean; + } = {}, ): Source[] { const normalizedLanguages = toComparableLanguages(toUniqueLanguageCodes(languages ?? [])); @@ -105,6 +112,13 @@ export class Sources { !pinned || getSourceMetadata(source).isPinned || (keepLocalSource && Sources.isLocalSource(source)), + ) + .filter( + (source) => + enabled === undefined || + !enabled || + getSourceMetadata(source).isEnabled || + (keepLocalSource && Sources.isLocalSource(source)), ); }