From 519ff43b6789e7624f96e99271b0b6d11fc79db8 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Fri, 20 Mar 2026 03:29:45 +0100 Subject: [PATCH] Use stable array/object constants II --- .../app-updates/components/ServerUpdateChecker.tsx | 3 ++- src/features/app-updates/components/WebUIUpdateChecker.tsx | 3 ++- src/features/browse/extensions/Extensions.tsx | 7 ++++--- src/features/browse/sources/Sources.tsx | 7 ++++--- src/features/category/components/CategorySelect.tsx | 5 +++-- .../chapter/components/actions/ChapterActionMenuItems.tsx | 6 +++--- src/features/extension/info/screens/ExtensionInfo.tsx | 3 ++- src/features/global-search/screens/SearchAll.tsx | 2 +- src/features/library/components/LibraryOptionsPanel.tsx | 3 ++- src/features/manga/components/TrackMangaButton.tsx | 3 ++- src/features/manga/screens/Manga.tsx | 3 ++- src/features/navigation-bar/NavigationBar.constants.ts | 3 ++- src/features/reader/hooks/useReaderSetChaptersState.ts | 3 ++- src/features/settings/screens/ImageProcessingSetting.tsx | 3 ++- src/features/settings/screens/More.tsx | 3 ++- src/features/source/browse/screens/SourceMangas.tsx | 2 +- src/features/tracker/components/TrackerSearch.tsx | 3 ++- 17 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/features/app-updates/components/ServerUpdateChecker.tsx b/src/features/app-updates/components/ServerUpdateChecker.tsx index 75ff4cc8..5283d109 100644 --- a/src/features/app-updates/components/ServerUpdateChecker.tsx +++ b/src/features/app-updates/components/ServerUpdateChecker.tsx @@ -20,6 +20,7 @@ import { VersionUpdateInfoDialog } from '@/features/app-updates/components/Versi import { useMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts'; import { useLocalStorage } from '@/base/hooks/useStorage.tsx'; import { AppRoutes } from '@/base/AppRoute.constants.ts'; +import { STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts'; const disabledUpdateCheck = () => Promise.resolve(); @@ -45,7 +46,7 @@ export const ServerUpdateChecker = () => { }); const { data } = requestManager.useGetAbout(); - const { aboutServer } = data ?? {}; + const { aboutServer } = data ?? STABLE_EMPTY_OBJECT; const selectedServerChannelInfo = serverUpdateCheckData?.checkForServerUpdates?.find( (channel) => channel.channel === aboutServer?.buildType, diff --git a/src/features/app-updates/components/WebUIUpdateChecker.tsx b/src/features/app-updates/components/WebUIUpdateChecker.tsx index 83dd0f4d..97f1a55d 100644 --- a/src/features/app-updates/components/WebUIUpdateChecker.tsx +++ b/src/features/app-updates/components/WebUIUpdateChecker.tsx @@ -27,6 +27,7 @@ import { useMetadataServerSettings } from '@/features/settings/services/ServerSe import { getErrorMessage, noOp } from '@/lib/HelperFunctions.ts'; import { AppStorage } from '@/lib/storage/AppStorage.ts'; import { BrowserUtil } from '@/lib/BrowserUtil.ts'; +import { STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts'; const disabledUpdateCheck = () => Promise.resolve(); @@ -53,7 +54,7 @@ export const WebUIUpdateChecker = () => { const shouldCheckForUpdate = !isAutoUpdateEnabled && webUIInformAvailableUpdate; const { data: aboutData } = requestManager.useGetAbout(); - const { aboutWebUI } = aboutData ?? {}; + const { aboutWebUI } = aboutData ?? STABLE_EMPTY_OBJECT; const { data: webUIUpdateData, refetch: checkForUpdate } = requestManager.useCheckForWebUIUpdate({ notifyOnNetworkStatusChange: true, diff --git a/src/features/browse/extensions/Extensions.tsx b/src/features/browse/extensions/Extensions.tsx index c457a27a..402d8b4d 100644 --- a/src/features/browse/extensions/Extensions.tsx +++ b/src/features/browse/extensions/Extensions.tsx @@ -41,6 +41,7 @@ import { ExtensionAction, ExtensionGroupState, ExtensionState } from '@/features import { EXTENSION_ACTION_TO_FAILURE_TRANSLATION_MAP } from '@/features/extension/Extensions.constants.ts'; import { AppRoutes } from '@/base/AppRoute.constants.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; +import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; import { createUpdateMetadataServerSettings, useMetadataServerSettings, @@ -142,11 +143,11 @@ export function Extensions({ tabsMenuHeight }: { tabsMenuHeight: number }) { const areReposDefined = !!serverSettingsData?.settings.extensionRepos.length; const areMultipleReposInUse = (serverSettingsData?.settings.extensionRepos.length ?? 0) > 1; - const allExtensions = data?.fetchExtensions?.extensions; - const allLangs = useMemo(() => getLanguagesFromExtensions(allExtensions ?? []), [allExtensions]); + const allExtensions = data?.fetchExtensions?.extensions ?? STABLE_EMPTY_ARRAY; + const allLangs = useMemo(() => getLanguagesFromExtensions(allExtensions), [allExtensions]); const filteredExtensions = useMemo( - () => filterExtensions(allExtensions ?? [], { selectedLanguages: shownLangs, showNsfw, query }), + () => filterExtensions(allExtensions, { selectedLanguages: shownLangs, showNsfw, query }), [allExtensions, shownLangs, showNsfw, query], ); const groupedExtensions = useMemo(() => groupExtensionsByLanguage(filteredExtensions), [filteredExtensions]); diff --git a/src/features/browse/sources/Sources.tsx b/src/features/browse/sources/Sources.tsx index 982c7a56..166f4d57 100644 --- a/src/features/browse/sources/Sources.tsx +++ b/src/features/browse/sources/Sources.tsx @@ -30,6 +30,7 @@ import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx'; import { StyledGroupHeader } from '@/base/components/virtuoso/StyledGroupHeader.tsx'; import { StyledGroupItemWrapper } from '@/base/components/virtuoso/StyledGroupItemWrapper.tsx'; import { SourceLanguageSelect } from '@/features/source/components/SourceLanguageSelect.tsx'; +import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; export function Sources({ tabsMenuHeight }: { tabsMenuHeight: number }) { const { t } = useLingui(); @@ -45,10 +46,10 @@ export function Sources({ tabsMenuHeight }: { tabsMenuHeight: number }) { error, refetch, } = requestManager.useGetSourceList({ notifyOnNetworkStatusChange: true }); - const sources = data?.sources.nodes; + const sources = data?.sources.nodes ?? STABLE_EMPTY_ARRAY; const filteredSources = useMemo( () => - SourceService.filter(sources ?? [], { + SourceService.filter(sources, { isNsfw: showNsfw ? undefined : true, languages: shownLangs, keepLocalSource: true, @@ -58,7 +59,7 @@ export function Sources({ tabsMenuHeight }: { tabsMenuHeight: number }) { ); const sourcesForLanguageFilter = useMemo( () => - SourceService.filter(sources ?? [], { + SourceService.filter(sources, { isNsfw: showNsfw ? undefined : true, removeLocalSource: true, }), diff --git a/src/features/category/components/CategorySelect.tsx b/src/features/category/components/CategorySelect.tsx index 69414d45..5268594e 100644 --- a/src/features/category/components/CategorySelect.tsx +++ b/src/features/category/components/CategorySelect.tsx @@ -26,6 +26,7 @@ import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx'; import { makeToast } from '@/base/utils/Toast.ts'; import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'; import { updateMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts'; +import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; import type { GetCategoriesBaseQuery, GetCategoriesBaseQueryVariables, @@ -112,9 +113,9 @@ export function CategorySelect(props: CategorySelectProps) { const { data } = requestManager.useGetCategories( GET_CATEGORIES_BASE, ); - const categoriesData = data?.categories.nodes; + const categoriesData = data?.categories.nodes ?? STABLE_EMPTY_ARRAY; - const allCategories = useMemo(() => Categories.getUserCreated(categoriesData ?? []), [categoriesData]); + const allCategories = useMemo(() => Categories.getUserCreated(categoriesData), [categoriesData]); const defaultCategoryIds = useMemo( () => (addToLibrary ? Categories.getIds(Categories.getDefaults(allCategories)) : []), diff --git a/src/features/chapter/components/actions/ChapterActionMenuItems.tsx b/src/features/chapter/components/actions/ChapterActionMenuItems.tsx index df106f28..026d1f8b 100644 --- a/src/features/chapter/components/actions/ChapterActionMenuItems.tsx +++ b/src/features/chapter/components/actions/ChapterActionMenuItems.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; +import { STABLE_EMPTY_ARRAY, STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts'; import CheckBoxOutlineBlank from '@mui/icons-material/CheckBoxOutlineBlank'; import Delete from '@mui/icons-material/Delete'; import Download from '@mui/icons-material/Download'; @@ -79,7 +79,7 @@ export const ChapterActionMenuItems = ({ const { t } = useLingui(); const isSingleMode = !!chapter; - const { isDownloaded, isRead, isBookmarked } = chapter ?? {}; + const { isDownloaded, isRead, isBookmarked } = chapter ?? STABLE_EMPTY_OBJECT; const mangaChaptersResponse = requestManager.useGetMangaChapters( GET_CHAPTERS_MANGA, @@ -89,7 +89,7 @@ export const ChapterActionMenuItems = ({ fetchPolicy: 'cache-only', }, ); - const allChapters = mangaChaptersResponse.data?.chapters.nodes ?? []; + const allChapters = mangaChaptersResponse.data?.chapters.nodes ?? STABLE_EMPTY_ARRAY; const { settings: { deleteChaptersWithBookmark }, diff --git a/src/features/extension/info/screens/ExtensionInfo.tsx b/src/features/extension/info/screens/ExtensionInfo.tsx index 6b563aec..5dbb22ba 100644 --- a/src/features/extension/info/screens/ExtensionInfo.tsx +++ b/src/features/extension/info/screens/ExtensionInfo.tsx @@ -24,6 +24,7 @@ import { Header } from '@/features/extension/info/components/Header.tsx'; import { Meta } from '@/features/extension/info/components/Meta.tsx'; import { ActionButton } from '@/features/extension/info/components/ActionButton.tsx'; import { SourceCard } from '@/features/extension/info/components/SourceCard.tsx'; +import { STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts'; export const ExtensionInfo = () => { const { t } = useLingui(); @@ -34,7 +35,7 @@ export const ExtensionInfo = () => { const extensionResponse = requestManager.useGetExtension(pkgName); const sourcesResponse = requestManager.useGetSourceList(); - const { extension } = extensionResponse.data ?? {}; + const { extension } = extensionResponse.data ?? STABLE_EMPTY_OBJECT; const sources = useMemo(() => { if (!sourcesResponse.data?.sources) { return []; diff --git a/src/features/global-search/screens/SearchAll.tsx b/src/features/global-search/screens/SearchAll.tsx index 091acfe6..96b274bf 100644 --- a/src/features/global-search/screens/SearchAll.tsx +++ b/src/features/global-search/screens/SearchAll.tsx @@ -150,7 +150,7 @@ const SourceSearchPreview = React.memo( const { data: searchResult, isLoading, error, abortRequest } = results[0]!; currentAbortRequest.current = abortRequest; - const mangas = searchResult?.fetchSourceManga?.mangas ?? []; + const mangas = searchResult?.fetchSourceManga?.mangas ?? STABLE_EMPTY_ARRAY; const noMangasFound = !error && !isLoading && !mangas.length; useEffect(() => { diff --git a/src/features/library/components/LibraryOptionsPanel.tsx b/src/features/library/components/LibraryOptionsPanel.tsx index a2237189..eeb66df4 100644 --- a/src/features/library/components/LibraryOptionsPanel.tsx +++ b/src/features/library/components/LibraryOptionsPanel.tsx @@ -21,6 +21,7 @@ import { Trackers } from '@/features/tracker/services/Trackers.ts'; import type { GetTrackersSettingsQuery } from '@/lib/graphql/generated/graphql.ts'; import { MangaStatus } from '@/lib/graphql/generated/graphql.ts'; import { GET_TRACKERS_SETTINGS } from '@/lib/graphql/tracker/TrackerQuery.ts'; +import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; import { createUpdateCategoryMetadata, useGetCategoryMetadata } from '@/features/category/services/CategoryMetadata.ts'; import { makeToast } from '@/base/utils/Toast.ts'; import { @@ -62,7 +63,7 @@ export const LibraryOptionsPanel = ({ const { t } = useLingui(); const trackerList = requestManager.useGetTrackerList(GET_TRACKERS_SETTINGS); - const loggedInTrackers = Trackers.getLoggedIn(trackerList.data?.trackers.nodes ?? []); + const loggedInTrackers = Trackers.getLoggedIn(trackerList.data?.trackers.nodes ?? STABLE_EMPTY_ARRAY); const categoryLibraryOptions = useGetCategoryMetadata(category); const updateCategoryLibraryOptions = createUpdateCategoryMetadata(category, (e) => diff --git a/src/features/manga/components/TrackMangaButton.tsx b/src/features/manga/components/TrackMangaButton.tsx index 57960215..2265e1cf 100644 --- a/src/features/manga/components/TrackMangaButton.tsx +++ b/src/features/manga/components/TrackMangaButton.tsx @@ -23,6 +23,7 @@ import { GET_TRACKERS_SETTINGS } from '@/lib/graphql/tracker/TrackerQuery.ts'; import type { MangaTrackRecordInfo } from '@/features/manga/Manga.types.ts'; import { AppRoutes } from '@/base/AppRoute.constants.ts'; import { MediaQuery } from '@/base/utils/MediaQuery.tsx'; +import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; export const TrackMangaButton = ({ manga }: { manga: MangaTrackRecordInfo & Pick }) => { const { t } = useLingui(); @@ -30,7 +31,7 @@ export const TrackMangaButton = ({ manga }: { manga: MangaTrackRecordInfo & Pick const isMobileWidth = MediaQuery.useIsMobileWidth(); const trackerList = requestManager.useGetTrackerList(GET_TRACKERS_SETTINGS); - const trackers = trackerList.data?.trackers.nodes ?? []; + const trackers = trackerList.data?.trackers.nodes ?? STABLE_EMPTY_ARRAY; const mangaTrackers = manga.trackRecords.nodes; const loggedInTrackers = Trackers.getLoggedIn(trackers); diff --git a/src/features/manga/screens/Manga.tsx b/src/features/manga/screens/Manga.tsx index 9f03b2ae..1ccb72cd 100644 --- a/src/features/manga/screens/Manga.tsx +++ b/src/features/manga/screens/Manga.tsx @@ -26,13 +26,14 @@ import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholde import type { GetMangaScreenQuery } from '@/lib/graphql/generated/graphql.ts'; import { GET_MANGA_SCREEN } from '@/lib/graphql/manga/MangaQuery.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; +import { STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts'; import { useAppTitleAndAction } from '@/features/navigation-bar/hooks/useAppTitleAndAction.ts'; import type { MangaLocationState } from '@/features/manga/Manga.types.ts'; export const Manga: React.FC = () => { const { t } = useLingui(); const { id } = useParams<{ id: string }>(); - const { mode } = useLocation().state ?? {}; + const { mode } = useLocation().state ?? STABLE_EMPTY_OBJECT; const autofetchedRef = useRef(false); diff --git a/src/features/navigation-bar/NavigationBar.constants.ts b/src/features/navigation-bar/NavigationBar.constants.ts index 9323c2ba..ada138ee 100644 --- a/src/features/navigation-bar/NavigationBar.constants.ts +++ b/src/features/navigation-bar/NavigationBar.constants.ts @@ -25,6 +25,7 @@ import type { NavbarItem } from '@/features/navigation-bar/NavigationBar.types.t import { NavBarItemMoreGroup } from '@/features/navigation-bar/NavigationBar.types.ts'; import { AppRoutes } from '@/base/AppRoute.constants.ts'; import { requestManager } from '@/lib/requests/RequestManager.ts'; +import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; import { DownloaderState } from '@/lib/graphql/generated/graphql.ts'; type RestrictedNavBarItem = Omit & { show: Show }; @@ -64,7 +65,7 @@ const NAVIGATION_BAR_BASE_ITEMS = [ useBadge: () => { const { data } = requestManager.useGetExtensionList({ fetchPolicy: 'cache-only' }); - const extensions = data?.extensions.nodes ?? []; + const extensions = data?.extensions.nodes ?? STABLE_EMPTY_ARRAY; const availableUpdates = extensions.filter((extension) => extension.hasUpdate).length; if (!availableUpdates) { diff --git a/src/features/reader/hooks/useReaderSetChaptersState.ts b/src/features/reader/hooks/useReaderSetChaptersState.ts index 72147127..6dd8fd65 100644 --- a/src/features/reader/hooks/useReaderSetChaptersState.ts +++ b/src/features/reader/hooks/useReaderSetChaptersState.ts @@ -21,6 +21,7 @@ import type { ChapterListOptions } from '@/features/chapter/Chapter.types.ts'; import { getReaderChapterFromCache } from '@/features/reader/Reader.utils.ts'; import { DirectionOffset } from '@/base/Base.types.ts'; import { getReaderChaptersStore } from '@/features/reader/stores/ReaderStore.ts'; +import { STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts'; import { READER_DEFAULT_CHAPTERS_STATE } from '@/features/reader/stores/ReaderChaptersStore.ts'; @@ -37,7 +38,7 @@ export const useReaderSetChaptersState = ( const navigate = useNavigate(); const locationState = useLocation().state; - const { updateInitialChapter } = locationState ?? {}; + const { updateInitialChapter } = locationState ?? STABLE_EMPTY_OBJECT; const finalInitialChapter = updateInitialChapter ? undefined : initialChapter; useEffect(() => { diff --git a/src/features/settings/screens/ImageProcessingSetting.tsx b/src/features/settings/screens/ImageProcessingSetting.tsx index fdb5e3f8..d25a9568 100644 --- a/src/features/settings/screens/ImageProcessingSetting.tsx +++ b/src/features/settings/screens/ImageProcessingSetting.tsx @@ -33,6 +33,7 @@ import { toValidServerConversions, } from '@/features/settings/ImageProcessing.utils.ts'; import { Processing } from '@/features/settings/components/images/Processing.tsx'; +import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; export const ImageProcessingSetting = ({ type }: { type: ImageProcessingType }) => { const { t } = useLingui(); @@ -46,7 +47,7 @@ export const ImageProcessingSetting = ({ type }: { type: ImageProcessingType }) const settingKey = IMAGE_PROCESSING_TYPE_TO_SETTING[type]; - const conversions = data?.settings?.[settingKey] ?? []; + const conversions = data?.settings?.[settingKey] ?? STABLE_EMPTY_ARRAY; const [tmpConversions, setTmpConversions] = useState( normalizeConversions(maybeAddDefault(addStableIdToConversions(conversions))), diff --git a/src/features/settings/screens/More.tsx b/src/features/settings/screens/More.tsx index 4e818a3d..d756a491 100644 --- a/src/features/settings/screens/More.tsx +++ b/src/features/settings/screens/More.tsx @@ -23,6 +23,7 @@ import { useMetadataServerSettings } from '@/features/settings/services/ServerSe import type { NavbarItem } from '@/features/navigation-bar/NavigationBar.types.ts'; import { NavBarItemMoreGroup } from '@/features/navigation-bar/NavigationBar.types.ts'; import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts'; +import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; export const More = () => { const { t } = useLingui(); @@ -45,7 +46,7 @@ export const More = () => { const hiddenNavBarItemsByMoreGroup = Object.groupBy(hiddenNavBarItems, (item) => item.moreGroup); const hiddenItemsMoreGroup = [ - ...(hiddenNavBarItemsByMoreGroup[NavBarItemMoreGroup.HIDDEN_ITEM] ?? []), + ...(hiddenNavBarItemsByMoreGroup[NavBarItemMoreGroup.HIDDEN_ITEM] ?? STABLE_EMPTY_ARRAY), { path: AppRoutes.settings.childRoutes.categories.path, title: msg`Categories`, diff --git a/src/features/source/browse/screens/SourceMangas.tsx b/src/features/source/browse/screens/SourceMangas.tsx index e24edab1..4e4c4bb4 100644 --- a/src/features/source/browse/screens/SourceMangas.tsx +++ b/src/features/source/browse/screens/SourceMangas.tsx @@ -234,7 +234,7 @@ export function SourceMangas() { ); const [filtersToApply, setLocationFiltersToApply] = useSessionStorage( `source-mangas-location-${locationKey}-${sourceId}-filters`, - currentFiltersToApply ?? [], + currentFiltersToApply ?? STABLE_EMPTY_ARRAY, ); const [dialogFiltersToApply, setDialogFiltersToApply] = useState(filtersToApply); const [currentContentType, setCurrentContentType] = useSessionStorage( diff --git a/src/features/tracker/components/TrackerSearch.tsx b/src/features/tracker/components/TrackerSearch.tsx index 6d80ac71..495ffa45 100644 --- a/src/features/tracker/components/TrackerSearch.tsx +++ b/src/features/tracker/components/TrackerSearch.tsx @@ -40,6 +40,7 @@ import type { TrackerIdInfo, TTrackerBind } from '@/features/tracker/Tracker.typ import { Tracker } from '@/features/tracker/Tracker.types.ts'; import { CustomButtonIcon } from '@/base/components/buttons/CustomButtonIcon.tsx'; import { CustomTooltip } from '@/base/components/CustomTooltip.tsx'; +import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts'; const TrackButton = ({ mangaId, @@ -133,7 +134,7 @@ export const TrackerSearch = ({ const trackerSearch = requestManager.useTrackerSearch(tracker.id, searchString, { notifyOnNetworkStatusChange: true, }); - const searchResults = trackerSearch.data?.searchTracker.trackSearches ?? []; + const searchResults = trackerSearch.data?.searchTracker.trackSearches ?? STABLE_EMPTY_ARRAY; const hasResults = !!searchResults.length; const hasNoResults = !trackerSearch.loading && !trackerSearch.error && !hasResults;