From 0bf88d3fd10151c0bfd8403cc7e0665267e97320 Mon Sep 17 00:00:00 2001 From: Chance Zibolski Date: Mon, 18 Mar 2024 10:35:35 -0700 Subject: [PATCH] Update reader width live when updating in reader view (#581) Basically, instead of updating the width of the image after clicking `ok`, the width is adjusted live, which makes it easier to figure out the right value for the manga being viewed. While implementing this, I realized we can't update the backend every time we re-render when `liveUpdate` is true, so I've updated the logic for persisting settings to support skipping the API call to save the settings in the backend, and only perform the API it when the user clicks ok on the number setting. Signed-off-by: Chance Zibolski --- src/components/navbar/ReaderNavBar.tsx | 6 +- .../reader/ReaderSettingsOptions.tsx | 3 +- src/components/settings/NumberSetting.tsx | 95 +++++++++---------- src/screens/Reader.tsx | 10 +- .../settings/DefaultReaderSettings.tsx | 10 +- 5 files changed, 64 insertions(+), 60 deletions(-) diff --git a/src/components/navbar/ReaderNavBar.tsx b/src/components/navbar/ReaderNavBar.tsx index 096bd9bb..990e4d26 100644 --- a/src/components/navbar/ReaderNavBar.tsx +++ b/src/components/navbar/ReaderNavBar.tsx @@ -118,7 +118,7 @@ const OpenDrawerButton = styled(IconButton)(({ theme }) => ({ interface IProps { settings: IReaderSettings; - setSettingValue: (key: keyof IReaderSettings, value: AllowedMetadataValueTypes) => void; + setSettingValue: (key: keyof IReaderSettings, value: AllowedMetadataValueTypes, persist?: boolean) => void; manga: TManga; chapter: TChapter; curPage: number; @@ -150,10 +150,10 @@ export function ReaderNavBar(props: IProps) { const disableChapterNavButtons = retrievingNextChapter; - const updateSettingValue = (key: keyof IReaderSettings, value: AllowedMetadataValueTypes) => { + const updateSettingValue = (key: keyof IReaderSettings, value: AllowedMetadataValueTypes, persist?: boolean) => { // prevent closing the navBar when updating the "staticNav" setting setUpdateDrawerOnRender(key !== 'staticNav'); - setSettingValue(key, value); + setSettingValue(key, value, persist); }; const updateDrawer = (open: boolean) => { diff --git a/src/components/reader/ReaderSettingsOptions.tsx b/src/components/reader/ReaderSettingsOptions.tsx index 38e82fc1..d0303f62 100644 --- a/src/components/reader/ReaderSettingsOptions.tsx +++ b/src/components/reader/ReaderSettingsOptions.tsx @@ -15,7 +15,7 @@ import { NumberSetting } from '@/components/settings/NumberSetting.tsx'; import { isHorizontalReaderType } from '@/components/reader/Page.tsx'; interface IProps extends IReaderSettings { - setSettingValue: (key: keyof IReaderSettings, value: AllowedMetadataValueTypes) => void; + setSettingValue: (key: keyof IReaderSettings, value: AllowedMetadataValueTypes, persist?: boolean) => void; } export function ReaderSettingsOptions({ @@ -107,6 +107,7 @@ export function ReaderSettingsOptions({ valueUnit="%" showSlider handleUpdate={(width: number) => setSettingValue('readerWidth', width)} + handleLiveUpdate={(width: number) => setSettingValue('readerWidth', width, false)} listItemTextSx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} /> )} diff --git a/src/components/settings/NumberSetting.tsx b/src/components/settings/NumberSetting.tsx index 48192976..1729f9cb 100644 --- a/src/components/settings/NumberSetting.tsx +++ b/src/components/settings/NumberSetting.tsx @@ -13,7 +13,7 @@ import TextField from '@mui/material/TextField'; import { InputAdornment, ListItemText, Stack, SxProps, Typography, Theme } from '@mui/material'; import DialogActions from '@mui/material/DialogActions'; import Button from '@mui/material/Button'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import ListItemButton from '@mui/material/ListItemButton'; import * as React from 'react'; @@ -39,6 +39,7 @@ type BaseProps = { showSlider?: never; disabled?: boolean; listItemTextSx?: SxProps; + handleLiveUpdate?: (value: number) => void; }; type PropsWithSlider = Omit & @@ -62,45 +63,49 @@ export const NumberSetting = ({ handleUpdate, showSlider, disabled = false, + handleLiveUpdate, listItemTextSx: sx, }: Props) => { const { t } = useTranslation(); const [isDialogOpen, setIsDialogOpen] = useState(false); const [dialogValue, setDialogValue] = useState(value); + const [originalValue, setOriginalValue] = useState(value); - const closeDialog = useCallback( - (resetValue: boolean) => { + const updateValue = useCallback( + (newValue: number, persist: boolean) => { + setDialogValue(newValue); + const didValueChange = newValue !== originalValue; + // Call handleUpdate if the value changed and 'persist' is true, + // otherwise call handleLiveUpdate if it's defined. + if (persist && didValueChange) { + handleUpdate(newValue); + } else if (handleLiveUpdate) { + handleLiveUpdate(newValue); + } + }, + [originalValue, setDialogValue, handleLiveUpdate, handleUpdate], + ); + + const cancel = useCallback(() => { + updateValue(originalValue, true); + setOriginalValue(originalValue); + setIsDialogOpen(false); + }, [originalValue, handleUpdate]); + + const resetToDefault = useCallback(() => { + if (defaultValue !== undefined) { + updateValue(defaultValue, true); + setOriginalValue(defaultValue); setIsDialogOpen(false); + } + }, [defaultValue, handleUpdate]); - if (resetValue) { - setDialogValue(value); - } - }, - [value], - ); - - const closeDialogWithReset = useCallback(() => closeDialog(true), [closeDialog]); - - const updateSetting = useCallback( - (newValue: number, shouldCloseDialog: boolean = true) => { - if (shouldCloseDialog) { - closeDialog(false); - } - - const didValueChange = value !== newValue; - if (!didValueChange) { - return; - } - - handleUpdate(newValue); - }, - [value, handleUpdate, closeDialog], - ); - - useEffect(() => { - setDialogValue(value); - }, [value]); + const submit = () => { + updateValue(dialogValue, true); + setOriginalValue(dialogValue); + setIsDialogOpen(false); + }; return ( <> @@ -114,7 +119,7 @@ export const NumberSetting = ({ /> - + {dialogTitle} {(!!dialogDescription || !!dialogDisclaimer) && ( @@ -158,7 +163,10 @@ export const NumberSetting = ({ autoFocus value={dialogValue} type="number" - onChange={(e) => setDialogValue(Number(e.target.value))} + onChange={(e) => { + const newValue = Number(e.target.value); + updateValue(newValue, false); + }} /> {showSlider ? ( setDialogValue(newValue as number)} + onChange={(_, newValue) => { + updateValue(newValue as number, false); + }} /> ) : null} {defaultValue !== undefined ? ( - ) : null} - - diff --git a/src/screens/Reader.tsx b/src/screens/Reader.tsx index 42588e63..7b5f1a3b 100644 --- a/src/screens/Reader.tsx +++ b/src/screens/Reader.tsx @@ -257,11 +257,13 @@ export function Reader() { .response.catch(); }; - const setSettingValue = (key: keyof IReaderSettings, value: AllowedMetadataValueTypes) => { + const setSettingValue = (key: keyof IReaderSettings, value: AllowedMetadataValueTypes, persist: boolean = true) => { setSettings({ ...settings, [key]: value }); - requestUpdateMangaMetadata(manga, [[key, value]]).catch(() => - makeToast(t('reader.settings.error.label.failed_to_save_settings'), 'warning'), - ); + if (persist) { + requestUpdateMangaMetadata(manga, [[key, value]]).catch(() => + makeToast(t('reader.settings.error.label.failed_to_save_settings'), 'warning'), + ); + } }; const openNextChapter = useCallback( diff --git a/src/screens/settings/DefaultReaderSettings.tsx b/src/screens/settings/DefaultReaderSettings.tsx index de972cc7..e6667926 100644 --- a/src/screens/settings/DefaultReaderSettings.tsx +++ b/src/screens/settings/DefaultReaderSettings.tsx @@ -39,10 +39,12 @@ export function DefaultReaderSettings() { useSetDefaultBackTo('settings'); - const setSettingValue = (key: keyof IReaderSettings, value: AllowedMetadataValueTypes) => { - requestUpdateServerMetadata([[key, value]]).catch(() => - makeToast(t('reader.settings.error.label.failed_to_save_settings'), 'warning'), - ); + const setSettingValue = (key: keyof IReaderSettings, value: AllowedMetadataValueTypes, persist: boolean = true) => { + if (persist) { + requestUpdateServerMetadata([[key, value]]).catch(() => + makeToast(t('reader.settings.error.label.failed_to_save_settings'), 'warning'), + ); + } }; if (loading) {