Use stable array/object constants II

This commit is contained in:
schroda
2026-03-20 03:29:45 +01:00
parent 5a2e5f83bc
commit 519ff43b67
17 changed files with 38 additions and 24 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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]);

View File

@@ -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,
}),

View File

@@ -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<GetCategoriesBaseQuery, GetCategoriesBaseQueryVariables>(
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)) : []),

View File

@@ -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<GetChaptersMangaQuery>(
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 },

View File

@@ -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 [];

View File

@@ -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(() => {

View File

@@ -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<GetTrackersSettingsQuery>(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) =>

View File

@@ -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<MangaType, 'title'> }) => {
const { t } = useLingui();
@@ -30,7 +31,7 @@ export const TrackMangaButton = ({ manga }: { manga: MangaTrackRecordInfo & Pick
const isMobileWidth = MediaQuery.useIsMobileWidth();
const trackerList = requestManager.useGetTrackerList<GetTrackersSettingsQuery>(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);

View File

@@ -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<MangaLocationState>().state ?? {};
const { mode } = useLocation<MangaLocationState>().state ?? STABLE_EMPTY_OBJECT;
const autofetchedRef = useRef(false);

View File

@@ -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<Show extends NavbarItem['show']> = Omit<NavbarItem, 'show'> & { 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) {

View File

@@ -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<ReaderOpenChapterLocationState>().state;
const { updateInitialChapter } = locationState ?? {};
const { updateInitialChapter } = locationState ?? STABLE_EMPTY_OBJECT;
const finalInitialChapter = updateInitialChapter ? undefined : initialChapter;
useEffect(() => {

View File

@@ -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))),

View File

@@ -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`,

View File

@@ -234,7 +234,7 @@ export function SourceMangas() {
);
const [filtersToApply, setLocationFiltersToApply] = useSessionStorage<IPos[]>(
`source-mangas-location-${locationKey}-${sourceId}-filters`,
currentFiltersToApply ?? [],
currentFiltersToApply ?? STABLE_EMPTY_ARRAY,
);
const [dialogFiltersToApply, setDialogFiltersToApply] = useState<IPos[]>(filtersToApply);
const [currentContentType, setCurrentContentType] = useSessionStorage<SourceContentType | undefined>(

View File

@@ -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;