Use stable array/object constants
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
import type { Ref } from 'react';
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Box from '@mui/material/Box';
|
||||
import Stack from '@mui/material/Stack';
|
||||
@@ -61,7 +62,7 @@ export const SpinnerImage = ({ ref, ...props }: SpinnerImageProps) => {
|
||||
alt,
|
||||
onLoad,
|
||||
onError,
|
||||
spinnerStyle: { small, ...spinnerStyle } = {},
|
||||
spinnerStyle: { small, ...spinnerStyle } = STABLE_EMPTY_OBJECT,
|
||||
imgStyle,
|
||||
hideImgStyle,
|
||||
priority,
|
||||
|
||||
@@ -10,11 +10,12 @@ import Box from '@mui/material/Box';
|
||||
import type { CircularProgressProps } from '@mui/material/CircularProgress';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts';
|
||||
|
||||
export const Progress = ({
|
||||
progress,
|
||||
showText = true,
|
||||
progressProps = {},
|
||||
progressProps = STABLE_EMPTY_OBJECT,
|
||||
}: {
|
||||
progress: number;
|
||||
showText?: boolean;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
* with a few changes to fix a bug on mobile devices where opening the sub menu immediately triggered the on click of the underlying menu item
|
||||
*/
|
||||
|
||||
import { STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts';
|
||||
import type { MenuProps as MuiMenuProps } from '@mui/material/Menu';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import type { MenuItemProps as MuiMenuItemProps } from '@mui/material/MenuItem';
|
||||
@@ -64,7 +65,7 @@ export const NestedMenuItem = ({ ref, ...props }: NestedMenuItemProps) => {
|
||||
children,
|
||||
className,
|
||||
tabIndex: tabIndexProp,
|
||||
ContainerProps: ContainerPropsProp = {},
|
||||
ContainerProps: ContainerPropsProp = STABLE_EMPTY_OBJECT,
|
||||
MenuProps,
|
||||
...MenuItemProps
|
||||
} = props;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import type { RefObject } from 'react';
|
||||
import { useLayoutEffect, useState } from 'react';
|
||||
import { STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts';
|
||||
|
||||
export const useIntersectionObserver = (
|
||||
ref: RefObject<HTMLElement | null> | HTMLElement | undefined | null,
|
||||
@@ -17,7 +18,7 @@ export const useIntersectionObserver = (
|
||||
root,
|
||||
rootMargin,
|
||||
threshold,
|
||||
}: IntersectionObserverInit & { ignoreInitialObserve?: boolean } = {},
|
||||
}: IntersectionObserverInit & { ignoreInitialObserve?: boolean } = STABLE_EMPTY_OBJECT,
|
||||
): (() => void) => {
|
||||
const [disconnect, setDisconnect] = useState<() => void>(() => {});
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
import { makeToast } from '@/base/utils/Toast.ts';
|
||||
import { ChapterListCard } from '@/features/chapter/components/cards/ChapterListCard.tsx';
|
||||
import { VirtuosoPersisted } from '@/lib/virtuoso/Component/VirtuosoPersisted.tsx';
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
|
||||
type ChapterListHeaderProps = {
|
||||
scrollbarWidth: number;
|
||||
@@ -140,7 +141,7 @@ export const ChapterList = ({
|
||||
manga.id,
|
||||
{ notifyOnNetworkStatusChange: true },
|
||||
);
|
||||
const chapters = useMemo(() => chaptersData?.chapters.nodes ?? [], [chaptersData?.chapters.nodes]);
|
||||
const chapters = chaptersData?.chapters.nodes ?? STABLE_EMPTY_ARRAY;
|
||||
|
||||
const visibleChapters = useMemo(() => filterAndSortChapters(chapters, options), [chapters, options]);
|
||||
const visibleChapterIds = useMemo(() => Chapters.getIds(visibleChapters), [visibleChapters]);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { STABLE_EMPTY_ARRAY } 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';
|
||||
@@ -71,7 +72,7 @@ export const ChapterActionMenuItems = ({
|
||||
chapter,
|
||||
handleSelection,
|
||||
canBeDownloaded = false,
|
||||
selectedChapters = [],
|
||||
selectedChapters = STABLE_EMPTY_ARRAY,
|
||||
onClose,
|
||||
selectable = true,
|
||||
}: Props) => {
|
||||
|
||||
@@ -33,6 +33,7 @@ import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts';
|
||||
import { useAppAction } from '@/features/navigation-bar/hooks/useAppAction.ts';
|
||||
import type { ChapterDownloadStatus } from '@/features/chapter/Chapter.types.ts';
|
||||
import { VirtuosoPersisted } from '@/lib/virtuoso/Component/VirtuosoPersisted.tsx';
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
|
||||
export const DownloadQueue: React.FC = () => {
|
||||
const { t } = useLingui();
|
||||
@@ -49,7 +50,7 @@ export const DownloadQueue: React.FC = () => {
|
||||
} = requestManager.useGetDownloadStatus({ notifyOnNetworkStatusChange: true });
|
||||
const downloaderData = downloadStatusData?.downloadStatus;
|
||||
|
||||
const queue = downloaderData?.queue ?? [];
|
||||
const queue = downloaderData?.queue ?? STABLE_EMPTY_ARRAY;
|
||||
const status = downloaderData?.state ?? DownloaderState.Started;
|
||||
const isQueueEmpty = !queue.length;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import { AppbarSearch } from '@/base/components/AppbarSearch.tsx';
|
||||
import { useDebounce } from '@/base/hooks/useDebounce.ts';
|
||||
import type { MangaCardProps } from '@/features/manga/Manga.types.ts';
|
||||
import { EmptyView } from '@/base/components/feedback/EmptyView.tsx';
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
||||
import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx';
|
||||
import { BaseMangaGrid } from '@/features/manga/components/BaseMangaGrid.tsx';
|
||||
@@ -254,7 +255,7 @@ export const SearchAll: React.FC = () => {
|
||||
} = useMetadataServerSettings();
|
||||
|
||||
const { data, loading, error, refetch } = requestManager.useGetSourceList({ notifyOnNetworkStatusChange: true });
|
||||
const sources = useMemo(() => data?.sources.nodes ?? [], [data?.sources.nodes]);
|
||||
const sources = data?.sources.nodes ?? STABLE_EMPTY_ARRAY;
|
||||
|
||||
const [sourceToLoadingStateMap, setSourceToLoadingStateMap] = useState<SourceToLoadingStateMap>(new Map());
|
||||
const debouncedSourceToLoadingStateMap = useDebounce(sourceToLoadingStateMap, 500);
|
||||
@@ -304,7 +305,7 @@ export const SearchAll: React.FC = () => {
|
||||
selectedLanguages={shownLangs}
|
||||
setSelectedLanguages={setShownLangs}
|
||||
languages={sourceLanguages}
|
||||
sources={sources ?? []}
|
||||
sources={sources}
|
||||
/>
|
||||
</>,
|
||||
[shownLangs, setShownLangs, sourceLanguages, sources],
|
||||
|
||||
@@ -21,6 +21,7 @@ import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
import { ChapterHistoryCard } from '@/features/history/components/ChapterHistoryCard.tsx';
|
||||
import { Chapters } from '@/features/chapter/services/Chapters.ts';
|
||||
import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts';
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
|
||||
export const History: React.FC = () => {
|
||||
const { t } = useLingui();
|
||||
@@ -39,7 +40,7 @@ export const History: React.FC = () => {
|
||||
});
|
||||
const hasNextPage = !!chapterHistoryData?.chapters.pageInfo.hasNextPage;
|
||||
const endCursor = chapterHistoryData?.chapters.pageInfo.endCursor;
|
||||
const readEntries = chapterHistoryData?.chapters.nodes ?? [];
|
||||
const readEntries = chapterHistoryData?.chapters.nodes ?? STABLE_EMPTY_ARRAY;
|
||||
const groupedHistory = useMemo(
|
||||
() => Object.entries(Chapters.groupByDate(readEntries, 'lastReadAt')),
|
||||
[readEntries],
|
||||
|
||||
@@ -50,6 +50,7 @@ import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts';
|
||||
import { useAppAction } from '@/features/navigation-bar/hooks/useAppAction.ts';
|
||||
import { AppRoutes } from '@/base/AppRoute.constants.ts';
|
||||
import { SearchParam } from '@/base/Base.types.ts';
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
|
||||
const TitleWithSizeTag = styled('span')({
|
||||
display: 'flex',
|
||||
@@ -82,7 +83,7 @@ export function Library() {
|
||||
const tabsData = categoriesResponse?.categories.nodes.filter(
|
||||
(category) => category.id !== 0 || (category.id === 0 && category.mangas.totalCount),
|
||||
);
|
||||
const tabs = tabsData ?? [];
|
||||
const tabs = tabsData ?? STABLE_EMPTY_ARRAY;
|
||||
|
||||
const librarySizeResponse = requestManager.useGetMangas<
|
||||
GetLibraryMangaCountQuery,
|
||||
@@ -102,7 +103,7 @@ export function Library() {
|
||||
loading: mangaLoading,
|
||||
refetch: refetchCategoryMangas,
|
||||
} = requestManager.useGetCategoryMangas(activeTab?.id, { skip: !activeTab, notifyOnNetworkStatusChange: true });
|
||||
const categoryMangas = categoryMangaResponse?.mangas.nodes ?? [];
|
||||
const categoryMangas = categoryMangaResponse?.mangas.nodes ?? STABLE_EMPTY_ARRAY;
|
||||
const {
|
||||
visibleMangas: mangas,
|
||||
showFilteredOutMessage,
|
||||
@@ -127,6 +128,7 @@ export function Library() {
|
||||
} = useSelectableCollection<MangaType['id'], string>(mangas.length, {
|
||||
itemIds: mangaIds,
|
||||
currentKey: activeTab?.id.toString(),
|
||||
initialState: undefined,
|
||||
});
|
||||
|
||||
const handleSelect: typeof handleSelection = useCallback(
|
||||
|
||||
@@ -37,6 +37,7 @@ import type { MangaAction, MangaDownloadInfo, MangaIdInfo, MangaUnreadInfo } fro
|
||||
import { MANGA_ACTION_TO_TRANSLATION } from '@/features/manga/Manga.constants.ts';
|
||||
import { AppRoutes } from '@/base/AppRoute.constants.ts';
|
||||
import { CategorySelect } from '@/features/category/components/CategorySelect.tsx';
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
|
||||
type BaseProps = { onClose: () => void; setHideMenu: (hide: boolean) => void };
|
||||
|
||||
@@ -65,7 +66,7 @@ export const MangaActionMenuItems = ({
|
||||
const [isTrackDialogOpen, setIsTrackDialogOpen] = useState(false);
|
||||
|
||||
const isSingleMode = !!manga;
|
||||
const selectedMangas = passedSelectedMangas ?? [];
|
||||
const selectedMangas = passedSelectedMangas ?? STABLE_EMPTY_ARRAY;
|
||||
|
||||
const getMenuItemTitle = createGetMenuItemTitle(isSingleMode, MANGA_ACTION_TO_TRANSLATION);
|
||||
const shouldShowMenuItem = createShouldShowMenuItem(isSingleMode);
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx';
|
||||
import type { NavbarContextType } from '@/features/navigation-bar/NavigationBar.types.ts';
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
|
||||
export const useAppAction = (action: NavbarContextType['action'], dependencies: any[] = []) => {
|
||||
export const useAppAction = (action: NavbarContextType['action'], dependencies: any[] = STABLE_EMPTY_ARRAY) => {
|
||||
const { setAction } = useNavBarContext();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Box from '@mui/material/Box';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
@@ -32,7 +33,7 @@ const BaseReaderNavBarDesktopChapterNavigation = ({
|
||||
currentChapterNumber,
|
||||
previousChapter,
|
||||
nextChapter,
|
||||
chapters = [],
|
||||
chapters = STABLE_EMPTY_ARRAY,
|
||||
readerThemeDirection,
|
||||
}: {
|
||||
currentChapterId: ChapterIdInfo['id'] | undefined;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts';
|
||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||
import Button from '@mui/material/Button';
|
||||
import Stack from '@mui/material/Stack';
|
||||
@@ -159,7 +160,7 @@ export function Options({ sourceFilter, group, updateFilterValue, update }: IFil
|
||||
}
|
||||
|
||||
export function SourceOptions({
|
||||
savedSearches = {},
|
||||
savedSearches = STABLE_EMPTY_OBJECT,
|
||||
selectSavedSearch,
|
||||
updateSavedSearches,
|
||||
sourceFilter,
|
||||
|
||||
@@ -52,6 +52,7 @@ import { GridLayout, SearchParam } from '@/base/Base.types';
|
||||
import { AppRoutes } from '@/base/AppRoute.constants.ts';
|
||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
import { Sources } from '@/features/source/services/Sources.ts';
|
||||
import { STABLE_EMPTY_ARRAY, STABLE_EMPTY_OBJECT } from '@/base/Base.constants.ts';
|
||||
import { useAppTitleAndAction } from '@/features/navigation-bar/hooks/useAppTitleAndAction.ts';
|
||||
import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx';
|
||||
import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx';
|
||||
@@ -219,7 +220,7 @@ export function SourceMangas() {
|
||||
useLocation<{
|
||||
contentType: SourceContentType;
|
||||
clearCache: boolean;
|
||||
}>().state ?? {};
|
||||
}>().state ?? STABLE_EMPTY_OBJECT;
|
||||
|
||||
const {
|
||||
settings: { hideLibraryEntries },
|
||||
@@ -288,7 +289,7 @@ export function SourceMangas() {
|
||||
{ data, error, isLoading: loading, size: lastPageNum, abortRequest, filteredOutAllItemsOfFetchedPage },
|
||||
] = useSourceManga(sourceId, contentType, query, filtersToApply, 1, hideLibraryEntries);
|
||||
currentAbortRequest.current = abortRequest;
|
||||
const mangas = data?.fetchSourceManga?.mangas ?? [];
|
||||
const mangas = data?.fetchSourceManga?.mangas ?? STABLE_EMPTY_ARRAY;
|
||||
const hasNextPage = !!data?.fetchSourceManga?.hasNextPage;
|
||||
const isLoading = loading || (filteredOutAllItemsOfFetchedPage && hasNextPage);
|
||||
const { data: sourceData } = requestManager.useGetSource<GetSourceBrowseQuery, GetSourceBrowseQueryVariables>(
|
||||
@@ -297,7 +298,7 @@ export function SourceMangas() {
|
||||
);
|
||||
const source = sourceData?.source;
|
||||
|
||||
const filters = source?.filters ?? [];
|
||||
const filters = source?.filters ?? STABLE_EMPTY_ARRAY;
|
||||
const { savedSearches = {} } = useGetSourceMetadata(source ?? DEFAULT_SOURCE);
|
||||
const updateSourceMetadata = createUpdateSourceMetadata<'savedSearches'>(source ?? { id: '-1' }, (e) =>
|
||||
makeToast(t`Failed to save changes`, 'error', getErrorMessage(e)),
|
||||
|
||||
@@ -28,6 +28,7 @@ import { makeToast } from '@/base/utils/Toast.ts';
|
||||
import type { PreferenceProps } from '@/features/source/Source.types.ts';
|
||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts';
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
|
||||
function getPrefComponent(type: string) {
|
||||
switch (type) {
|
||||
@@ -58,7 +59,7 @@ export function SourceConfigure() {
|
||||
>(GET_SOURCE_SETTINGS, sourceId, {
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
const sourcePreferences = data?.source.preferences ?? [];
|
||||
const sourcePreferences = data?.source.preferences ?? STABLE_EMPTY_ARRAY;
|
||||
|
||||
const updateValue =
|
||||
(position: number): PreferenceProps['updateValue'] =>
|
||||
|
||||
@@ -24,6 +24,7 @@ import type { MangaIdInfo } from '@/features/manga/Manga.types.ts';
|
||||
import { AppRoutes } from '@/base/AppRoute.constants.ts';
|
||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
import { EmptyView } from '@/base/components/feedback/EmptyView.tsx';
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
|
||||
const getTrackerMode = (id: number, trackersInUse: number[], searchModeForTracker?: number): TrackerMode => {
|
||||
if (id === searchModeForTracker) {
|
||||
@@ -46,13 +47,13 @@ export const TrackManga = ({ manga }: { manga: MangaIdInfo & Pick<MangaType, 'ti
|
||||
const trackerList = requestManager.useGetTrackerList<GetTrackersBindQuery>(GET_TRACKERS_BIND, {
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
const trackers = trackerList.data?.trackers.nodes ?? [];
|
||||
const trackers = trackerList.data?.trackers.nodes ?? STABLE_EMPTY_ARRAY;
|
||||
|
||||
const mangaTrackRecordsList = requestManager.useGetManga<GetMangaTrackRecordsQuery>(
|
||||
GET_MANGA_TRACK_RECORDS,
|
||||
manga.id,
|
||||
);
|
||||
const mangaTrackRecords = mangaTrackRecordsList.data?.manga.trackRecords.nodes ?? [];
|
||||
const mangaTrackRecords = mangaTrackRecordsList.data?.manga.trackRecords.nodes ?? STABLE_EMPTY_ARRAY;
|
||||
|
||||
const loggedInTrackers = Trackers.getLoggedIn(trackers);
|
||||
const trackersInUse = Trackers.getLoggedIn(Trackers.getTrackers(mangaTrackRecords, trackers));
|
||||
|
||||
@@ -27,6 +27,7 @@ import type { GetTrackersSettingsQuery } from '@/lib/graphql/generated/graphql.t
|
||||
import type { MetadataTrackingSettings } from '@/features/tracker/Tracker.types.ts';
|
||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts';
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
|
||||
export const TrackingSettings = () => {
|
||||
const { t } = useLingui();
|
||||
@@ -50,7 +51,7 @@ export const TrackingSettings = () => {
|
||||
} = requestManager.useGetTrackerList<GetTrackersSettingsQuery>(GET_TRACKERS_SETTINGS, {
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
const trackers = data?.trackers.nodes ?? [];
|
||||
const trackers = data?.trackers.nodes ?? STABLE_EMPTY_ARRAY;
|
||||
|
||||
const loading = areMetadataServerSettingsLoading || areTrackersLoading;
|
||||
const error = metadataServerSettingsError ?? trackersError;
|
||||
|
||||
@@ -25,6 +25,7 @@ import { useNavBarContext } from '@/features/navigation-bar/NavbarContext.tsx';
|
||||
import { Chapters } from '@/features/chapter/services/Chapters.ts';
|
||||
import { useAppTitleAndAction } from '@/features/navigation-bar/hooks/useAppTitleAndAction.ts';
|
||||
import { GROUPED_VIRTUOSO_Z_INDEX } from '@/lib/virtuoso/Virtuoso.constants.ts';
|
||||
import { STABLE_EMPTY_ARRAY } from '@/base/Base.constants.ts';
|
||||
|
||||
export const Updates: React.FC = () => {
|
||||
const { t } = useLingui();
|
||||
@@ -42,7 +43,7 @@ export const Updates: React.FC = () => {
|
||||
});
|
||||
const hasNextPage = !!chapterUpdateData?.chapters.pageInfo.hasNextPage;
|
||||
const endCursor = chapterUpdateData?.chapters.pageInfo.endCursor;
|
||||
const updateEntries = chapterUpdateData?.chapters.nodes ?? [];
|
||||
const updateEntries = chapterUpdateData?.chapters.nodes ?? STABLE_EMPTY_ARRAY;
|
||||
const groupedUpdates = useMemo(
|
||||
() => Object.entries(Chapters.groupByDate(updateEntries, 'fetchedAt')),
|
||||
[updateEntries],
|
||||
|
||||
Reference in New Issue
Block a user