diff --git a/src/modules/backup/screens/Backup.tsx b/src/modules/backup/screens/Backup.tsx index 8fa6158b..42578b99 100644 --- a/src/modules/backup/screens/Backup.tsx +++ b/src/modules/backup/screens/Backup.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { useEffect, useLayoutEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import List from '@mui/material/List'; import ListItemText from '@mui/material/ListItemText'; import { fromEvent } from 'file-selector'; @@ -37,7 +37,6 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts' import { ServerSettings } from '@/modules/settings/Settings.types.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; type BackupSettingsType = Pick; @@ -61,15 +60,8 @@ let backupRestoreId: string | undefined; export function Backup() { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); - useAppTitle(t('settings.backup.title')); - useLayoutEffect(() => { - setAction(null); - return () => { - setAction(null); - }; - }, [t]); + useAppTitle(t('settings.backup.title')); const { data: settingsData, diff --git a/src/modules/browse/screens/BrowseSettings.tsx b/src/modules/browse/screens/BrowseSettings.tsx index 8ed1018d..5c906270 100644 --- a/src/modules/browse/screens/BrowseSettings.tsx +++ b/src/modules/browse/screens/BrowseSettings.tsx @@ -7,7 +7,6 @@ */ import { Trans, useTranslation } from 'react-i18next'; -import { useLayoutEffect } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; @@ -27,7 +26,6 @@ import { makeToast } from '@/modules/core/utils/Toast.ts'; import { MetadataBrowseSettings } from '@/modules/browse/Browse.types.ts'; import { ServerSettings as GqlServerSettings } from '@/modules/settings/Settings.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; type ExtensionsSettings = Pick; @@ -40,12 +38,8 @@ const extractBrowseSettings = (settings: GqlServerSettings): ExtensionsSettings export const BrowseSettings = () => { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); - useAppTitle(t('global.label.browse')); - useLayoutEffect(() => { - setAction(null); - }, [t]); + useAppTitle(t('global.label.browse')); const { data, loading, error, refetch } = requestManager.useGetServerSettings({ notifyOnNetworkStatusChange: true, diff --git a/src/modules/category/screens/CategorySettings.tsx b/src/modules/category/screens/CategorySettings.tsx index 31f5e414..953b3b06 100644 --- a/src/modules/category/screens/CategorySettings.tsx +++ b/src/modules/category/screens/CategorySettings.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { ComponentProps, useLayoutEffect, useMemo, useState } from 'react'; +import { ComponentProps, useMemo, useState } from 'react'; import { useTheme } from '@mui/material/styles'; import Fab from '@mui/material/Fab'; import AddIcon from '@mui/icons-material/Add'; @@ -32,7 +32,6 @@ import { GET_CATEGORIES_SETTINGS } from '@/lib/graphql/queries/CategoryQuery.ts' import { CategorySettingsCard } from '@/modules/category/components/CategorySettingsCard.tsx'; import { CategoryIdInfo } from '@/modules/category/Category.types.ts'; import { getErrorMessage, noOp } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { makeToast } from '@/modules/core/utils/Toast.ts'; import { DndSortableItem } from '@/lib/dnd-kit/DndSortableItem.tsx'; import { DndKitUtil } from '@/lib/dnd-kit/DndKitUtil.ts'; @@ -41,17 +40,9 @@ import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; export function CategorySettings() { const { t } = useTranslation(); + useAppTitle(t('category.dialog.title.edit_category_other')); - const { setAction } = useNavBarContext(); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); - const { data, loading, error, refetch } = requestManager.useGetCategories< GetCategoriesSettingsQuery, GetCategoriesSettingsQueryVariables diff --git a/src/modules/device/screens/DeviceSetting.tsx b/src/modules/device/screens/DeviceSetting.tsx index 61360df1..8d7163af 100644 --- a/src/modules/device/screens/DeviceSetting.tsx +++ b/src/modules/device/screens/DeviceSetting.tsx @@ -11,7 +11,6 @@ import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; import MenuItem from '@mui/material/MenuItem'; import { useTranslation } from 'react-i18next'; -import { useLayoutEffect } from 'react'; import { updateMetadataServerSettings, useMetadataServerSettings, @@ -26,23 +25,13 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts' import { DEFAULT_DEVICE } from '@/modules/device/services/Device.ts'; import { MetadataServerSettingKeys, MetadataServerSettings } from '@/modules/settings/Settings.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; export const DeviceSetting = () => { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); useAppTitle(t('settings.device.title.device')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); - const { metadata, settings: { devices }, diff --git a/src/modules/downloads/screens/DownloadQueue.tsx b/src/modules/downloads/screens/DownloadQueue.tsx index 73b777c8..33c0a7d4 100644 --- a/src/modules/downloads/screens/DownloadQueue.tsx +++ b/src/modules/downloads/screens/DownloadQueue.tsx @@ -10,7 +10,7 @@ import PauseIcon from '@mui/icons-material/Pause'; import PlayArrowIcon from '@mui/icons-material/PlayArrow'; import Box from '@mui/material/Box'; import IconButton from '@mui/material/IconButton'; -import React, { useLayoutEffect, useMemo, useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import DeleteSweepIcon from '@mui/icons-material/DeleteSweep'; import { Virtuoso } from 'react-virtuoso'; @@ -26,12 +26,12 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts' import { ChapterDownloadStatus } from '@/modules/chapter/services/Chapters.ts'; import { DownloaderState } from '@/lib/graphql/generated/graphql.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { DndSortableItem } from '@/lib/dnd-kit/DndSortableItem.tsx'; import { DndKitUtil } from '@/lib/dnd-kit/DndKitUtil.ts'; import { DndOverlayItem } from '@/lib/dnd-kit/DndOverlayItem.tsx'; import { DownloadQueueChapterCard } from '@/modules/downloads/components/DownloadQueueChapterCard.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; +import { useAppAction } from '@/modules/navigation-bar/hooks/useAppAction.ts'; export const DownloadQueue: React.FC = () => { const { t } = useTranslation(); @@ -52,8 +52,6 @@ export const DownloadQueue: React.FC = () => { const status = downloaderData?.state ?? DownloaderState.Started; const isQueueEmpty = !queue.length; - const { setAction } = useNavBarContext(); - const dndItems = useMemo(() => queue.map((download) => download.chapter), [queue]); const dndSensors = DndKitUtil.useSensorsForDevice(); const [dndActiveDownload, setDndActiveDownload] = useState(null); @@ -99,30 +97,24 @@ export const DownloadQueue: React.FC = () => { categoryReorder(queue, oldIndex, newIndex); }; - useLayoutEffect(() => { - setAction( - <> - - - - - + useAppAction( + <> + + + + + - - - {status === DownloaderState.Stopped ? : } - - - , - ); - - return () => { - setAction(null); - }; - }, [t, status, isQueueEmpty]); + + + {status === DownloaderState.Stopped ? : } + + + , + ); // Virtuoso's resize observer can throw this error, // which is caught by DnD and aborts dragging. diff --git a/src/modules/downloads/screens/DownloadSettings.tsx b/src/modules/downloads/screens/DownloadSettings.tsx index cab87d3c..973cfa4c 100644 --- a/src/modules/downloads/screens/DownloadSettings.tsx +++ b/src/modules/downloads/screens/DownloadSettings.tsx @@ -7,7 +7,6 @@ */ import { useTranslation } from 'react-i18next'; -import { useLayoutEffect } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; @@ -32,7 +31,6 @@ import { GET_CATEGORIES_SETTINGS } from '@/lib/graphql/queries/CategoryQuery.ts' import { MetadataDownloadSettings } from '@/modules/downloads/Downloads.types.ts'; import { ServerSettings } from '@/modules/settings/Settings.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; type DownloadSettingsType = Pick< @@ -56,18 +54,9 @@ const extractDownloadSettings = (settings: ServerSettings): DownloadSettingsType export const DownloadSettings = () => { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); useAppTitle(t('download.title.download')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); - const categories = requestManager.useGetCategories( GET_CATEGORIES_SETTINGS, ); diff --git a/src/modules/extension/screens/Extensions.tsx b/src/modules/extension/screens/Extensions.tsx index e284ce9a..9d733364 100644 --- a/src/modules/extension/screens/Extensions.tsx +++ b/src/modules/extension/screens/Extensions.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { fromEvent } from 'file-selector'; import IconButton from '@mui/material/IconButton'; import AddIcon from '@mui/icons-material/Add'; @@ -45,13 +45,13 @@ import { import { EXTENSION_ACTION_TO_FAILURE_TRANSLATION_KEY_MAP } from '@/modules/extension/Extensions.constants.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { ExtensionOptions } from '@/modules/extension/components/ExtensionOptions'; import { createUpdateMetadataServerSettings, useMetadataServerSettings, } from '@/modules/settings/services/ServerSettingsMetadata.ts'; import { MetadataBrowseSettings } from '@/modules/browse/Browse.types.ts'; +import { useAppAction } from '@/modules/navigation-bar/hooks/useAppAction.ts'; const LANGUAGE = 0; const EXTENSIONS = 1; @@ -115,7 +115,6 @@ const GroupHeader = ({ export function Extensions({ tabsMenuHeight }: { tabsMenuHeight: number }) { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); const navigate = useNavigate(); const { pathname, search, state } = useLocation<{ selectedExtensionPkg?: TExtension['pkgName'] }>(); const selectedExtensionPkg = state?.selectedExtensionPkg; @@ -201,47 +200,42 @@ export function Extensions({ tabsMenuHeight }: { tabsMenuHeight: number }) { fetchExtensions(); }, [refetchExtensions]); - useLayoutEffect(() => { - setAction( - <> - - - { - const input = document.createElement('input'); - input.style.display = 'none'; - input.type = 'file'; - input.onchange = () => { - const file = input.files?.[0]; - if (file) { - submitExternalExtension(file); - } - }; + useAppAction( + <> + + + { + const input = document.createElement('input'); + input.style.display = 'none'; + input.type = 'file'; + input.onchange = () => { + const file = input.files?.[0]; + if (file) { + submitExternalExtension(file); + } + }; - document.documentElement.appendChild(input); - input.click(); - document.documentElement.removeChild(input); - }} - color="inherit" - > - - - + document.documentElement.appendChild(input); + input.click(); + document.documentElement.removeChild(input); + }} + color="inherit" + > + + + - - updateMetadataServerSettings('extensionLanguages', languages) - } - languages={allLangs} - /> - , - ); - - return () => { - setAction(null); - }; - }, [t, shownLangs, allLangs]); + + updateMetadataServerSettings('extensionLanguages', languages) + } + languages={allLangs} + /> + , + [t, shownLangs, allLangs], + ); useWindowEvent('drop', async (e) => { e.preventDefault(); diff --git a/src/modules/global-search/screens/SearchAll.tsx b/src/modules/global-search/screens/SearchAll.tsx index a23b18fc..11293c4d 100644 --- a/src/modules/global-search/screens/SearchAll.tsx +++ b/src/modules/global-search/screens/SearchAll.tsx @@ -9,7 +9,7 @@ import Card from '@mui/material/Card'; import CardActionArea from '@mui/material/CardActionArea'; import Typography from '@mui/material/Typography'; -import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Link, useLocation } from 'react-router-dom'; import { StringParam, useQueryParam } from 'use-query-params'; import { useTranslation } from 'react-i18next'; @@ -30,11 +30,11 @@ import { EmptyViewAbsoluteCentered } from '@/modules/core/components/feedback/Em import { translateExtensionLanguage } from '@/modules/extension/Extensions.utils.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { Sources } from '@/modules/source/services/Sources.ts'; import { SourceDisplayNameInfo, SourceIdInfo } from '@/modules/source/Source.types.ts'; import { useMetadataServerSettings } from '@/modules/settings/services/ServerSettingsMetadata.ts'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; +import { useAppAction } from '@/modules/navigation-bar/hooks/useAppAction.ts'; type SourceLoadingState = { isLoading: boolean; hasResults: boolean; emptySearch: boolean; error: any }; type SourceToLoadingStateMap = Map; @@ -189,8 +189,6 @@ const SourceSearchPreview = React.memo( export const SearchAll: React.FC = () => { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); - const { pathname, state } = useLocation<{ mangaTitle?: string }>(); const isMigrateMode = pathname.startsWith('/migrate/source'); @@ -236,22 +234,21 @@ export const SearchAll: React.FC = () => { [setSourceToLoadingStateMap], ); - useLayoutEffect(() => { - setAction( - <> - - - , - ); - - return () => { - setAction(null); - }; - }, [t, shownLangs, setShownLangs, sources]); + useAppAction( + useMemo( + () => ( + <> + + + + ), + [shownLangs, setShownLangs, sourceLanguages], + ), + ); if (loading) { return ; diff --git a/src/modules/history/screens/HistorySettings.tsx b/src/modules/history/screens/HistorySettings.tsx index 6b02ba1c..11e5d6e7 100644 --- a/src/modules/history/screens/HistorySettings.tsx +++ b/src/modules/history/screens/HistorySettings.tsx @@ -7,7 +7,6 @@ */ import { useTranslation } from 'react-i18next'; -import { useLayoutEffect } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; @@ -21,20 +20,14 @@ import { EmptyViewAbsoluteCentered } from '@/modules/core/components/feedback/Em import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'; import { makeToast } from '@/modules/core/utils/Toast.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { MetadataHistorySettings } from '@/modules/history/History.types.ts'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; export const HistorySettings = () => { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); useAppTitle(t('history.title')); - useLayoutEffect(() => { - setAction(null); - }, [t]); - const { settings: { hideHistory }, request: { loading, error, refetch }, diff --git a/src/modules/library/screens/Library.tsx b/src/modules/library/screens/Library.tsx index d679cf5f..96e9ca44 100644 --- a/src/modules/library/screens/Library.tsx +++ b/src/modules/library/screens/Library.tsx @@ -43,8 +43,8 @@ import { useLibraryOptionsContext } from '@/modules/library/contexts/LibraryOpti import { useMetadataServerSettings } from '@/modules/settings/services/ServerSettingsMetadata.ts'; import { getCategoryMetadata } from '@/modules/category/services/CategoryMetadata.ts'; import { GET_LIBRARY_MANGA_COUNT } from '@/lib/graphql/queries/MangaQuery.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; +import { useAppAction } from '@/modules/navigation-bar/hooks/useAppAction.ts'; const TitleWithSizeTag = styled('span')({ display: 'flex', @@ -167,7 +167,6 @@ export function Library() { ); }, [isSelectModeActive, selectedMangas]); - const { setAction } = useNavBarContext(); useAppTitle( {t('library.title')} @@ -176,49 +175,33 @@ export function Library() { t('library.title'), [t, showTabSize, librarySize], ); - useLayoutEffect(() => { - setAction( - <> - {!isSelectModeActive && activeTab && ( - <> - - - - - )} - - handleSelectAll(selectAll, [...new Set(mangas.map((manga) => manga.id))]) - } - onModeChange={(checked) => { - setIsSelectModeActive(checked); + useAppAction( + <> + {!isSelectModeActive && activeTab && ( + <> + + + + + )} + handleSelectAll(selectAll, [...new Set(mangas.map((manga) => manga.id))])} + onModeChange={(checked) => { + setIsSelectModeActive(checked); - if (checked) { - handleSelectAll(true, [...new Set(mangas.map((manga) => manga.id))]); - } else { - tabs.forEach((tab) => handleSelectAll(false, [], tab.id.toString())); - } - }} - /> - , - ); - return () => { - setAction(null); - }; - }, [ - t, - librarySize, - areCategoriesLoading, - isSelectModeActive, - areNoItemsSelected, - areAllItemsSelected, - mangas.length, - activeTab, - showTabSize, - ]); + if (checked) { + handleSelectAll(true, [...new Set(mangas.map((manga) => manga.id))]); + } else { + tabs.forEach((tab) => handleSelectAll(false, [], tab.id.toString())); + } + }} + /> + , + [isSelectModeActive, areNoItemsSelected, areAllItemsSelected, activeTab], + ); const handleTabChange = (newTab: number) => { setTabSearchParam(newTab); diff --git a/src/modules/library/screens/LibraryDuplicates.tsx b/src/modules/library/screens/LibraryDuplicates.tsx index 89ef0ca0..c4ea8970 100644 --- a/src/modules/library/screens/LibraryDuplicates.tsx +++ b/src/modules/library/screens/LibraryDuplicates.tsx @@ -7,7 +7,7 @@ */ import { useTranslation } from 'react-i18next'; -import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import IconButton from '@mui/material/IconButton'; import SettingsIcon from '@mui/icons-material/Settings'; import PopupState, { bindMenu, bindTrigger } from 'material-ui-popup-state'; @@ -34,8 +34,8 @@ import { VirtuosoUtil } from '@/lib/virtuoso/Virtuoso.util.tsx'; import { LibraryDuplicatesWorkerInput, TMangaDuplicate, TMangaDuplicates } from '@/modules/library/Library.types.ts'; import { GridLayout } from '@/modules/core/Core.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; +import { useAppAction } from '@/modules/navigation-bar/hooks/useAppAction.ts'; export const LibraryDuplicates = () => { const { t } = useTranslation(); @@ -46,39 +46,30 @@ export const LibraryDuplicates = () => { false, ); - const { setAction } = useNavBarContext(); useAppTitle(t('library.settings.advanced.duplicates.label.title')); - useLayoutEffect(() => { - setAction( - <> - - - {(popupState) => ( - <> - - - - - - setCheckAlternativeTitles(checked)} - /> - - - - )} - - , - ); - - return () => { - setAction(null); - }; - }, [t, gridLayout, checkAlternativeTitles]); + useAppAction( + <> + + + {(popupState) => ( + <> + + + + + + setCheckAlternativeTitles(checked)} + /> + + + + )} + + , + ); const { data, loading, error, refetch } = requestManager.useGetMangas< GetMangasDuplicatesQuery, diff --git a/src/modules/library/screens/LibrarySettings.tsx b/src/modules/library/screens/LibrarySettings.tsx index 1b247a1a..e4adcc37 100644 --- a/src/modules/library/screens/LibrarySettings.tsx +++ b/src/modules/library/screens/LibrarySettings.tsx @@ -7,7 +7,6 @@ */ import { useTranslation } from 'react-i18next'; -import { useLayoutEffect } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemButton from '@mui/material/ListItemButton'; @@ -38,7 +37,6 @@ import { GET_MANGAS_BASE } from '@/lib/graphql/queries/MangaQuery.ts'; import { MetadataLibrarySettings } from '@/modules/library/Library.types.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; const removeNonLibraryMangasFromCategories = async (): Promise => { @@ -65,16 +63,8 @@ const removeNonLibraryMangasFromCategories = async (): Promise => { export function LibrarySettings() { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); useAppTitle(t('library.title')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); const categories = requestManager.useGetCategories( GET_CATEGORIES_SETTINGS, diff --git a/src/modules/manga/screens/Manga.tsx b/src/modules/manga/screens/Manga.tsx index 4d70cbf6..5765bbcf 100644 --- a/src/modules/manga/screens/Manga.tsx +++ b/src/modules/manga/screens/Manga.tsx @@ -11,7 +11,7 @@ import CircularProgress from '@mui/material/CircularProgress'; import IconButton from '@mui/material/IconButton'; import Stack from '@mui/material/Stack'; import Box from '@mui/material/Box'; -import React, { useEffect, useLayoutEffect, useRef } from 'react'; +import React, { useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router-dom'; import { isNetworkRequestInFlight } from '@apollo/client/core/networkStatus'; @@ -26,13 +26,12 @@ import { LoadingPlaceholder } from '@/modules/core/components/feedback/LoadingPl import { GetMangaScreenQuery } from '@/lib/graphql/generated/graphql.ts'; import { GET_MANGA_SCREEN } from '@/lib/graphql/queries/MangaQuery.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; +import { useAppAction } from '@/modules/navigation-bar/hooks/useAppAction.ts'; export const Manga: React.FC = () => { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); const { id } = useParams<{ id: string }>(); const autofetchedRef = useRef(false); @@ -61,50 +60,36 @@ export const Manga: React.FC = () => { }, [manga]); useAppTitle(manga?.title ?? t('manga.title_one')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t, manga?.title]); - - useLayoutEffect(() => { - setAction( - - {error && !isValidating && !refreshing && ( - - {t('manga.error.label.request_failure')} -
- {getErrorMessage(error)} - - } - > - refetch()}> - - -
- )} - {manga && (refreshing || isValidating) && ( - - + useAppAction( + + {error && !isValidating && !refreshing && ( + + {t('manga.error.label.request_failure')} +
+ {getErrorMessage(error)} + + } + > + refetch()}> + - )} - {manga && } -
, - ); - - return () => { - setAction(null); - }; - }, [t, error, isValidating, refreshing, manga, refresh]); + + )} + {manga && (refreshing || isValidating) && ( + + + + )} + {manga && } +
, + ); if (error && !manga) { return ( diff --git a/src/modules/migration/screens/Migrate.tsx b/src/modules/migration/screens/Migrate.tsx index 40d633e9..85eaba64 100644 --- a/src/modules/migration/screens/Migrate.tsx +++ b/src/modules/migration/screens/Migrate.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { useEffect, useLayoutEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { requestManager } from '@/lib/requests/RequestManager.ts'; @@ -22,12 +22,11 @@ import { SOURCE_BASE_FIELDS } from '@/lib/graphql/fragments/SourceFragments.ts'; import { BaseMangaGrid } from '@/modules/manga/components/BaseMangaGrid.tsx'; import { GridLayout } from '@/modules/core/Core.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; +import { useAppAction } from '@/modules/navigation-bar/hooks/useAppAction.ts'; export const Migrate = () => { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); const { sourceId: paramSourceId } = useParams<{ sourceId: string }>(); @@ -70,13 +69,7 @@ export const Migrate = () => { }); useAppTitle(name ?? sourceId ?? t('migrate.title')); - useLayoutEffect(() => { - setAction(); - - return () => { - setAction(null); - }; - }, [t, name, sourceId, gridLayout]); + useAppAction(); useEffect(() => { if (isSourceLoading || isKnownSource) { diff --git a/src/modules/navigation-bar/hooks/useAppAction.ts b/src/modules/navigation-bar/hooks/useAppAction.ts new file mode 100644 index 00000000..9af3f22f --- /dev/null +++ b/src/modules/navigation-bar/hooks/useAppAction.ts @@ -0,0 +1,20 @@ +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import { useEffect } from 'react'; +import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; +import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts'; + +export const useAppAction = (action: NavbarContextType['action'], dependencies: any[] = []) => { + const { setAction } = useNavBarContext(); + + useEffect(() => { + setAction(action); + return () => setAction(null); + }, dependencies); +}; diff --git a/src/modules/reader/screens/GlobalReaderSettings.tsx b/src/modules/reader/screens/GlobalReaderSettings.tsx index a5e547d9..13efe2fe 100644 --- a/src/modules/reader/screens/GlobalReaderSettings.tsx +++ b/src/modules/reader/screens/GlobalReaderSettings.tsx @@ -7,12 +7,11 @@ */ import { useTranslation } from 'react-i18next'; -import { useLayoutEffect, useState } from 'react'; +import { useState } from 'react'; import { useDefaultReaderSettingsWithDefaultFlag } from '@/modules/reader/services/ReaderSettingsMetadata.ts'; import { LoadingPlaceholder } from '@/modules/core/components/feedback/LoadingPlaceholder.tsx'; import { EmptyViewAbsoluteCentered } from '@/modules/core/components/feedback/EmptyViewAbsoluteCentered.tsx'; import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { IReaderSettings, ReadingMode } from '@/modules/reader/types/Reader.types.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderSettingsTabs } from '@/modules/reader/components/settings/ReaderSettingsTabs.tsx'; @@ -22,16 +21,8 @@ import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; export const GlobalReaderSettings = () => { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); useAppTitle(t('reader.settings.title.reader')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); const [activeTab, setActiveTab] = useState(0); diff --git a/src/modules/settings/screens/About.tsx b/src/modules/settings/screens/About.tsx index be459e0f..5bbcda09 100644 --- a/src/modules/settings/screens/About.tsx +++ b/src/modules/settings/screens/About.tsx @@ -6,7 +6,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { useLayoutEffect } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; @@ -21,22 +20,13 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts' import { EmptyViewAbsoluteCentered } from '@/modules/core/components/feedback/EmptyViewAbsoluteCentered.tsx'; import { VersionInfo } from '@/modules/app-updates/components/VersionInfo.tsx'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { epochToDate } from '@/util/DateHelper.ts'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; export function About() { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); useAppTitle(t('settings.about.title')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); const { data, loading, error, refetch } = requestManager.useGetAbout({ notifyOnNetworkStatusChange: true }); diff --git a/src/modules/settings/screens/Appearance.tsx b/src/modules/settings/screens/Appearance.tsx index a085f32f..62d46e89 100644 --- a/src/modules/settings/screens/Appearance.tsx +++ b/src/modules/settings/screens/Appearance.tsx @@ -7,7 +7,6 @@ */ import { useTranslation } from 'react-i18next'; -import { useLayoutEffect } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; @@ -35,7 +34,6 @@ import { makeToast } from '@/modules/core/utils/Toast.ts'; import { MetadataThemeSettings } from '@/modules/theme/AppTheme.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; import { AppStorage } from '@/lib/storage/AppStorage.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; export const Appearance = () => { @@ -44,15 +42,7 @@ export const Appearance = () => { const { mode, setMode } = useColorScheme(); const actualThemeMode = (mode ?? themeMode) as ThemeMode; - const { setAction } = useNavBarContext(); useAppTitle(t('settings.appearance.title')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); const { settings, diff --git a/src/modules/settings/screens/More.tsx b/src/modules/settings/screens/More.tsx index bb594d77..7195ae32 100644 --- a/src/modules/settings/screens/More.tsx +++ b/src/modules/settings/screens/More.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { Fragment, useLayoutEffect } from 'react'; +import { Fragment } from 'react'; import { useTranslation } from 'react-i18next'; import List from '@mui/material/List'; import ListItemIcon from '@mui/material/ListItemIcon'; @@ -15,7 +15,6 @@ import ListAltIcon from '@mui/icons-material/ListAlt'; import Divider from '@mui/material/Divider'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { ListItemLink } from '@/modules/core/components/lists/ListItemLink.tsx'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { NAVIGATION_BAR_ITEMS } from '@/modules/navigation-bar/NavigationBar.constants.ts'; import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx'; import { NavigationBarUtil } from '@/modules/navigation-bar/NavigationBar.util.ts'; @@ -25,9 +24,10 @@ import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; export const More = () => { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); const isMobileWidth = MediaQuery.useIsMobileWidth(); + useAppTitle(t('global.label.more')); + const { settings: { hideHistory }, } = useMetadataServerSettings(); @@ -59,15 +59,6 @@ export const More = () => { [NavBarItemMoreGroup.HIDDEN_ITEM]: hiddenItemsMoreGroup, }; - useAppTitle(t('global.label.more')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); - return ( {Object.entries(finalHiddenNavBarItemsByGroup).map(([group, items], index, list) => ( diff --git a/src/modules/settings/screens/ServerSettings.tsx b/src/modules/settings/screens/ServerSettings.tsx index cc597ec5..b7885c8e 100644 --- a/src/modules/settings/screens/ServerSettings.tsx +++ b/src/modules/settings/screens/ServerSettings.tsx @@ -7,7 +7,7 @@ */ import { useTranslation, Trans } from 'react-i18next'; -import { useLayoutEffect, useMemo } from 'react'; +import { useMemo } from 'react'; import Link from '@mui/material/Link'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; @@ -31,7 +31,6 @@ 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 { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; type ServerSettingsType = Pick< @@ -95,16 +94,8 @@ const getLogFilesCleanupDisplayValue = (ttl: number): string => { export const ServerSettings = () => { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); useAppTitle(t('settings.server.title.server')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); const { settings: { serverInformAvailableUpdate }, diff --git a/src/modules/settings/screens/Settings.tsx b/src/modules/settings/screens/Settings.tsx index 148d2c8f..8192cc07 100644 --- a/src/modules/settings/screens/Settings.tsx +++ b/src/modules/settings/screens/Settings.tsx @@ -6,7 +6,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { useLayoutEffect } from 'react'; import AutoStoriesIcon from '@mui/icons-material/AutoStories'; import List from '@mui/material/List'; import BackupIcon from '@mui/icons-material/Backup'; @@ -29,21 +28,12 @@ import { requestManager } from '@/lib/requests/RequestManager.ts'; import { makeToast } from '@/modules/core/utils/Toast.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; export function Settings() { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); useAppTitle(t('settings.title')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); const [triggerClearServerCache, { loading: isClearingServerCache }] = requestManager.useClearServerCache(); diff --git a/src/modules/settings/screens/WebUISettings.tsx b/src/modules/settings/screens/WebUISettings.tsx index 2cc95237..55f93ff4 100644 --- a/src/modules/settings/screens/WebUISettings.tsx +++ b/src/modules/settings/screens/WebUISettings.tsx @@ -7,12 +7,10 @@ */ import { useTranslation } from 'react-i18next'; -import { useLayoutEffect } from 'react'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; import Switch from '@mui/material/Switch'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { requestManager } from '@/lib/requests/RequestManager.ts'; import { WebUIUpdateIntervalSetting } from '@/modules/settings/components/webUI/WebUIUpdateIntervalSetting.tsx'; import { TextSetting } from '@/modules/core/components/settings/text/TextSetting.tsx'; @@ -121,16 +119,8 @@ const extractWebUISettings = (settings: ServerSettings): WebUISettingsType => ({ export const WebUISettings = () => { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); useAppTitle(t('settings.webui.title.webui')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); const { settings: { webUIInformAvailableUpdate }, diff --git a/src/modules/source/screens/SourceConfigure.tsx b/src/modules/source/screens/SourceConfigure.tsx index 60a225bb..3a717e0a 100644 --- a/src/modules/source/screens/SourceConfigure.tsx +++ b/src/modules/source/screens/SourceConfigure.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { createElement, useLayoutEffect } from 'react'; +import { createElement } from 'react'; import { useParams } from 'react-router-dom'; import List from '@mui/material/List'; import { useTranslation } from 'react-i18next'; @@ -27,7 +27,6 @@ import { GET_SOURCE_SETTINGS } from '@/lib/graphql/queries/SourceQuery.ts'; import { makeToast } from '@/modules/core/utils/Toast.ts'; import { PreferenceProps } from '@/modules/source/Source.types.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; function getPrefComponent(type: string) { @@ -49,16 +48,8 @@ function getPrefComponent(type: string) { export function SourceConfigure() { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); useAppTitle(t('source.configuration.title')); - useLayoutEffect(() => { - setAction(null); - - return () => { - setAction(null); - }; - }, [t]); const { sourceId } = useParams<{ sourceId: string }>(); const { data, loading, error, refetch } = requestManager.useGetSource< diff --git a/src/modules/source/screens/SourceMangas.tsx b/src/modules/source/screens/SourceMangas.tsx index ce802ce6..27886b72 100644 --- a/src/modules/source/screens/SourceMangas.tsx +++ b/src/modules/source/screens/SourceMangas.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useParams, useNavigate, useLocation, useSearchParams } from 'react-router-dom'; import IconButton from '@mui/material/IconButton'; import SettingsIcon from '@mui/icons-material/Settings'; @@ -51,9 +51,10 @@ import { MangaIdInfo } from '@/modules/manga/Manga.types.ts'; import { GridLayout } from '@/modules/core/Core.types.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { Sources } from '@/modules/source/services/Sources.ts'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; +import { useAppAction } from '@/modules/navigation-bar/hooks/useAppAction.ts'; +import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; const DEFAULT_SOURCE: SourceIdInfo = { id: '-1' }; @@ -205,7 +206,7 @@ const useSourceManga = ( export function SourceMangas() { const { t } = useTranslation(); - const { setAction, appBarHeight } = useNavBarContext(); + const { appBarHeight } = useNavBarContext(); const { sourceId } = useParams<{ sourceId: string }>(); @@ -400,30 +401,24 @@ export function SourceMangas() { }, [clearCache]); useAppTitle(source?.displayName ?? t('source.title_one')); - useLayoutEffect(() => { - setAction( - <> - - - {source?.isConfigurable && ( - - navigate(AppRoutes.sources.childRoutes.configure.path(sourceId))} - aria-label="display more actions" - edge="end" - color="inherit" - > - - - - )} - , - ); - - return () => { - setAction(null); - }; - }, [t, source]); + useAppAction( + <> + + + {source?.isConfigurable && ( + + navigate(AppRoutes.sources.childRoutes.configure.path(sourceId))} + aria-label="display more actions" + edge="end" + color="inherit" + > + + + + )} + , + ); const EmptyViewComponent = mangas.length ? EmptyView : EmptyViewAbsoluteCentered; diff --git a/src/modules/source/screens/Sources.tsx b/src/modules/source/screens/Sources.tsx index e7fba8e9..f98b3f06 100644 --- a/src/modules/source/screens/Sources.tsx +++ b/src/modules/source/screens/Sources.tsx @@ -6,7 +6,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { Fragment, useLayoutEffect, useMemo } from 'react'; +import { Fragment, useMemo } from 'react'; import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; import TravelExploreIcon from '@mui/icons-material/TravelExplore'; @@ -24,13 +24,12 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts' import { translateExtensionLanguage } from '@/modules/extension/Extensions.utils.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { getErrorMessage } from '@/lib/HelperFunctions.ts'; -import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { Sources as SourceService } from '@/modules/source/services/Sources.ts'; import { useMetadataServerSettings } from '@/modules/settings/services/ServerSettingsMetadata.ts'; +import { useAppAction } from '@/modules/navigation-bar/hooks/useAppAction.ts'; export function Sources() { const { t } = useTranslation(); - const { setAction } = useNavBarContext(); const [shownLangs, setShownLangs] = useLocalStorage('shownSourceLangs', sourceDefualtLangs()); const { @@ -61,26 +60,21 @@ export function Sources() { const navigate = useNavigate(); - useLayoutEffect(() => { - setAction( - <> - - navigate(AppRoutes.sources.childRoutes.searchAll.path)} color="inherit"> - - - - - , - ); - - return () => { - setAction(null); - }; - }, [t, shownLangs, sourceLanguages]); + useAppAction( + <> + + navigate(AppRoutes.sources.childRoutes.searchAll.path)} color="inherit"> + + + + + , + [t, shownLangs, sourceLanguages], + ); if (isLoading) return ; diff --git a/src/modules/updates/screens/Updates.tsx b/src/modules/updates/screens/Updates.tsx index fe188ec5..7bb18a79 100644 --- a/src/modules/updates/screens/Updates.tsx +++ b/src/modules/updates/screens/Updates.tsx @@ -25,12 +25,12 @@ import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContex import { Chapters } from '@/modules/chapter/services/Chapters.ts'; import { GROUPED_VIRTUOSO_Z_INDEX } from '@/modules/core/AppRoute.constants.ts'; import { useAppTitle } from '@/modules/navigation-bar/hooks/useAppTitle.ts'; +import { useAppAction } from '@/modules/navigation-bar/hooks/useAppAction.ts'; export const Updates: React.FC = () => { const { t } = useTranslation(); const { appBarHeight } = useNavBarContext(); - const { setAction } = useNavBarContext(); const { data: chapterUpdateData, loading: isLoading, @@ -74,13 +74,7 @@ export const Updates: React.FC = () => { const lastUpdateTimestamp = lastUpdateTimestampData?.lastUpdateTimestamp.timestamp; useAppTitle(t('updates.title')); - useLayoutEffect(() => { - setAction(); - - return () => { - setAction(null); - }; - }, [t, lastUpdateTimestamp]); + useAppAction(); const loadMore = useCallback(() => { if (!hasNextPage) {