From 4a41c7bd27d423d51478a3e6d74e49cda874d299 Mon Sep 17 00:00:00 2001 From: CD-Z Date: Wed, 12 Jun 2024 21:35:00 +0200 Subject: [PATCH 01/49] started --- src/screens/library/constants/constants.ts | 32 +++ src/screens/settings/Settings.ts | 131 +++++++++ .../SettingsGeneralScreenV2.tsx | 270 ++++++++++++++++++ 3 files changed, 433 insertions(+) create mode 100644 src/screens/settings/Settings.ts create mode 100644 src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx diff --git a/src/screens/library/constants/constants.ts b/src/screens/library/constants/constants.ts index 493b97793..94a129091 100644 --- a/src/screens/library/constants/constants.ts +++ b/src/screens/library/constants/constants.ts @@ -101,3 +101,35 @@ export const displayModesList = [ value: DisplayModes.List, }, ]; + +export enum GridSizes { + ZERO, + XL, + L, + M, + S, + XS, +} + +export const gridSizeList = [ + { + label: 'XL', + value: GridSizes.XL, + }, + { + label: 'L', + value: GridSizes.L, + }, + { + label: 'M', + value: GridSizes.M, + }, + { + label: 'S', + value: GridSizes.S, + }, + { + label: 'XS', + value: GridSizes.XS, + }, +]; diff --git a/src/screens/settings/Settings.ts b/src/screens/settings/Settings.ts new file mode 100644 index 000000000..a20c1853e --- /dev/null +++ b/src/screens/settings/Settings.ts @@ -0,0 +1,131 @@ +import { AppSettings, LibrarySettings } from '@hooks/persisted/useSettings'; +import { MoreStackParamList, SettingsStackParamList } from '@navigators/types'; +import { + CompositeNavigationProp, + NavigatorScreenParams, +} from '@react-navigation/native'; +import { StackScreenProps } from '@react-navigation/stack'; +import { + displayModesList, + gridSizeList, +} from '@screens/library/constants/constants'; +import { getString } from '@strings/translations'; +import React from 'react'; + +type settingsGroupTypes = + | 'GeneralSettings' + | 'ReaderSettings' + | 'TrackerSettings' + | 'BackupSettings' + | 'AppearanceSettings' + | 'AdvancedSettings' + | 'LibrarySettings' + | 'RespositorySettings' + | undefined; + +type ModalOptions = { + label: string; + value: string | number; +}; + +type SettingsTypeModes = 'single' | 'multiple' | 'order'; + +interface SettingsType< + T extends keyof AppSettings | keyof LibrarySettings, + V extends SettingsTypeModes, +> { + type: 'Modal'; + mode: SettingsTypeModes; + value: V extends 'multiple' ? T[] : T; + options: ModalOptions[]; + // value: T extends keyof AppSettings ? AppSettings[T] : T extends keyof LibrarySettings ? LibrarySettings[T] : never; +} + +interface Setting { + title: string; + description?: string; + + settingsType: SettingsType< + keyof AppSettings | keyof LibrarySettings, + SettingsTypeModes + >; +} + +interface SettingSubGroup { + subGroupTitle: string; + settings: Setting[]; +} + +interface SettingsGroup { + groupTitle: string; + navigateParam: settingsGroupTypes; + subGroup: SettingSubGroup[]; +} + +interface Settings { + general: SettingsGroup; +} + +const settings: Settings = { + general: { + groupTitle: getString('generalSettings'), + navigateParam: 'GeneralSettings', + subGroup: [ + { + subGroupTitle: getString('common.display'), + settings: [ + { + title: getString('generalSettingsScreen.displayMode'), + // description: () displayModesList[displayMode].label + settingsType: { + type: 'Modal', + mode: 'single', + value: 'displayMode', + options: displayModesList, + // value: + }, + }, + { + title: getString('generalSettingsScreen.itemsPerRowLibrary'), + settingsType: { + type: 'Modal', + mode: 'single', + value: 'novelsPerRow', + options: gridSizeList, + // value: + }, + }, + { + title: getString('generalSettingsScreen.novelBadges'), + settingsType: { + type: 'Modal', + mode: 'multiple', + value: [ + 'showDownloadBadges', + 'showNumberOfNovels', + 'showUnreadBadges', + ], + options: gridSizeList, + // value: + }, + }, + { + title: getString('generalSettingsScreen.novelSort'), + settingsType: { + type: 'Modal', + mode: 'order', + value: 'novelsPerRow', + options: gridSizeList, + // value: + }, + }, + ], + }, + { + subGroupTitle: getString('common.display'), + settings: [], + }, + ], + }, +} as const; +export default settings; diff --git a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx new file mode 100644 index 000000000..bdf2dbc51 --- /dev/null +++ b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx @@ -0,0 +1,270 @@ +import React from 'react'; +import { ScrollView } from 'react-native'; + +import DisplayModeModal from './modals/DisplayModeModal'; +import GridSizeModal from './modals/GridSizeModal'; + +import { + useAppSettings, + useLastUpdate, + useLibrarySettings, + useTheme, +} from '@hooks/persisted'; +import DefaultChapterSortModal from '../components/DefaultChapterSortModal'; +import { + DisplayModes, + displayModesList, + LibrarySortOrder, +} from '@screens/library/constants/constants'; +import { useBoolean } from '@hooks'; +import { Appbar, List } from '@components'; +import NovelSortModal from './modals/NovelSortModal'; +import NovelBadgesModal from './modals/NovelBadgesModal'; +import { NavigationState } from '@react-navigation/native'; +import { getString } from '@strings/translations'; +import SettingSwitch from '../components/SettingSwitch'; + +interface GenralSettingsProps { + navigation: NavigationState; +} + +const GenralSettings: React.FC = ({ navigation }) => { + const theme = useTheme(); + + const { + displayMode = DisplayModes.Comfortable, + novelsPerRow = 3, + showDownloadBadges = true, + showNumberOfNovels = false, + showUnreadBadges = true, + sortOrder = LibrarySortOrder.DateAdded_DESC, + } = useLibrarySettings(); + + const sortOrderDisplay: string[] = sortOrder.split(' '); + const sortOrderNameMap = new Map([ + ['name', 'libraryScreen.bottomSheet.sortOrders.alphabetically'], + ['chaptersDownloaded', 'libraryScreen.bottomSheet.sortOrders.download'], + ['chaptersUnread', 'libraryScreen.bottomSheet.sortOrders.totalChapters'], + ['id', 'libraryScreen.bottomSheet.sortOrders.dateAdded'], + ['lastReadAt', 'libraryScreen.bottomSheet.sortOrders.lastRead'], + ['lastUpdatedAt', 'libraryScreen.bottomSheet.sortOrders.lastUpdated'], + ]); + const { + updateLibraryOnLaunch, + downloadNewChapters, + onlyUpdateOngoingNovels, + defaultChapterSort, + refreshNovelMetadata, + disableHapticFeedback, + useLibraryFAB, + setAppSettings, + } = useAppSettings(); + + const { showLastUpdateTime, setShowLastUpdateTime } = useLastUpdate(); + + const generateNovelBadgesDescription = () => { + let res = []; + if (showDownloadBadges) { + res.push(getString('libraryScreen.bottomSheet.display.download')); + } + if (showUnreadBadges) { + res.push(getString('libraryScreen.bottomSheet.display.unread')); + } + if (showNumberOfNovels) { + res.push(getString('libraryScreen.bottomSheet.display.numberOfItems')); + } + return res.join(', '); + }; + + /** + * Display Mode Modal + */ + const displayModalRef = useBoolean(); + + /** + * Grid Size Modal + */ + const gridSizeModalRef = useBoolean(); + + /** + * Novel Badges Modal + */ + const novelBadgesModalRef = useBoolean(); + const novelBadgesDescription = generateNovelBadgesDescription(); + /** + * Novel Sort Modal + */ + const novelSortModalRef = useBoolean(); + /** + * Chapter Sort Modal + */ + const defaultChapterSortModal = useBoolean(); + return ( + <> + + + + + {getString('common.display')} + + + + + + + {getString('library')} + + setAppSettings({ updateLibraryOnLaunch: !updateLibraryOnLaunch }) + } + theme={theme} + /> + setAppSettings({ useLibraryFAB: !useLibraryFAB })} + theme={theme} + /> + + + {getString('generalSettingsScreen.novel')} + + + + + {getString('generalSettingsScreen.globalUpdate')} + + + setAppSettings({ + onlyUpdateOngoingNovels: !onlyUpdateOngoingNovels, + }) + } + theme={theme} + /> + + setAppSettings({ refreshNovelMetadata: !refreshNovelMetadata }) + } + theme={theme} + /> + setShowLastUpdateTime(!showLastUpdateTime)} + theme={theme} + /> + + + {getString('generalSettingsScreen.autoDownload')} + + + setAppSettings({ downloadNewChapters: !downloadNewChapters }) + } + theme={theme} + /> + + + {getString('generalSettings')} + + + setAppSettings({ disableHapticFeedback: !disableHapticFeedback }) + } + theme={theme} + /> + + + + + + + + + ); +}; + +export default GenralSettings; From 763820561b04ce72b80119d2e63797754d9d52cb Mon Sep 17 00:00:00 2001 From: CD-Z Date: Fri, 14 Jun 2024 22:50:13 +0200 Subject: [PATCH 02/49] Working Display Mode and Items per row --- App.tsx | 7 + src/components/NovelCover.tsx | 4 +- src/components/NovelList.tsx | 4 +- src/navigators/MoreStack.tsx | 2 +- .../SourceScreenSkeletonLoading.tsx | 4 +- src/screens/library/constants/constants.ts | 1 - src/screens/settings/Settings.ts | 119 +++++---- ...Screen.tsx => SettingsGeneralScreenV1.tsx} | 3 +- .../SettingsGeneralScreenV2.tsx | 236 ++++-------------- .../modals/DefaultSettingModal.tsx | 120 +++++++++ src/screens/settings/SettingsScreen.tsx | 19 ++ 11 files changed, 268 insertions(+), 251 deletions(-) rename src/screens/settings/SettingsGeneralScreen/{SettingsGeneralScreen.tsx => SettingsGeneralScreenV1.tsx} (99%) create mode 100644 src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx diff --git a/App.tsx b/App.tsx index 888eb1551..a9e8bba58 100644 --- a/App.tsx +++ b/App.tsx @@ -17,6 +17,13 @@ import AppErrorBoundary from '@components/AppErrorBoundary/AppErrorBoundary'; import Main from './src/navigators/Main'; import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; +declare global { + interface ObjectConstructor { + typedKeys(obj: T): Array; + } +} +Object.typedKeys = Object.keys as any; + Notifications.setNotificationHandler({ handleNotification: async () => { return { diff --git a/src/components/NovelCover.tsx b/src/components/NovelCover.tsx index 27debc47c..f3f092242 100644 --- a/src/components/NovelCover.tsx +++ b/src/components/NovelCover.tsx @@ -72,7 +72,7 @@ function NovelCover({ displayMode = DisplayModes.Comfortable, showDownloadBadges = true, showUnreadBadges = true, - novelsPerRow = 3, + novelsPerRow = 2, } = useLibrarySettings(); const window = useWindowDimensions(); @@ -80,7 +80,7 @@ function NovelCover({ const orientation = useDeviceOrientation(); const numColumns = useMemo( - () => (orientation === 'landscape' ? 6 : novelsPerRow), + () => (orientation === 'landscape' ? 6 : novelsPerRow + 1), [orientation, novelsPerRow], ); diff --git a/src/components/NovelList.tsx b/src/components/NovelList.tsx index 7d884d620..e676c2085 100644 --- a/src/components/NovelList.tsx +++ b/src/components/NovelList.tsx @@ -24,7 +24,7 @@ interface NovelListProps extends FlatListProps { } const NovelList: React.FC = props => { - const { displayMode = DisplayModes.Comfortable, novelsPerRow = 3 } = + const { displayMode = DisplayModes.Comfortable, novelsPerRow = 2 } = useLibrarySettings(); const orientation = useDeviceOrientation(); @@ -38,7 +38,7 @@ const NovelList: React.FC = props => { if (orientation === 'landscape') { return 6; } else { - return novelsPerRow; + return novelsPerRow + 1; } }, [isListView, orientation, novelsPerRow]); diff --git a/src/navigators/MoreStack.tsx b/src/navigators/MoreStack.tsx index 709118fc9..cf959d52c 100644 --- a/src/navigators/MoreStack.tsx +++ b/src/navigators/MoreStack.tsx @@ -9,7 +9,7 @@ import TrackerSettings from '../screens/settings/SettingsTrackerScreen'; import ReaderSettings from '../screens/settings/SettingsReaderScreen/SettingsReaderScreen'; import BackupSettings from '../screens/settings/SettingsBackupScreen'; import AdvancedSettings from '../screens/settings/SettingsAdvancedScreen'; -import GeneralSettings from '../screens/settings/SettingsGeneralScreen/SettingsGeneralScreen'; +import GeneralSettings from '../screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2'; import TaskQueue from '../screens/more/TaskQueueScreen'; import Downloads from '../screens/more/DownloadsScreen'; import AppearanceSettings from '../screens/settings/SettingsAppearanceScreen'; diff --git a/src/screens/browse/loadingAnimation/SourceScreenSkeletonLoading.tsx b/src/screens/browse/loadingAnimation/SourceScreenSkeletonLoading.tsx index 6bba1ae91..dba7ab50a 100644 --- a/src/screens/browse/loadingAnimation/SourceScreenSkeletonLoading.tsx +++ b/src/screens/browse/loadingAnimation/SourceScreenSkeletonLoading.tsx @@ -18,7 +18,7 @@ const SourceScreenSkeletonLoading: React.FC = ({ }) => { const [highlightColor, backgroundColor] = getLoadingColors(theme); - const { displayMode = DisplayModes.Comfortable, novelsPerRow = 3 } = + const { displayMode = DisplayModes.Comfortable, novelsPerRow = 2 } = useLibrarySettings(); const window = useWindowDimensions(); @@ -27,7 +27,7 @@ const SourceScreenSkeletonLoading: React.FC = ({ const orientation = useDeviceOrientation(); const numColumns = useMemo( - () => (orientation === 'landscape' ? 6 : novelsPerRow), + () => (orientation === 'landscape' ? 6 : novelsPerRow + 1), [orientation, novelsPerRow], ); diff --git a/src/screens/library/constants/constants.ts b/src/screens/library/constants/constants.ts index 94a129091..ab24cf3ba 100644 --- a/src/screens/library/constants/constants.ts +++ b/src/screens/library/constants/constants.ts @@ -103,7 +103,6 @@ export const displayModesList = [ ]; export enum GridSizes { - ZERO, XL, L, M, diff --git a/src/screens/settings/Settings.ts b/src/screens/settings/Settings.ts index a20c1853e..a6282ec70 100644 --- a/src/screens/settings/Settings.ts +++ b/src/screens/settings/Settings.ts @@ -6,6 +6,7 @@ import { } from '@react-navigation/native'; import { StackScreenProps } from '@react-navigation/stack'; import { + DisplayModes, displayModesList, gridSizeList, } from '@screens/library/constants/constants'; @@ -25,39 +26,48 @@ type settingsGroupTypes = type ModalOptions = { label: string; - value: string | number; + value: number; }; -type SettingsTypeModes = 'single' | 'multiple' | 'order'; +export type SettingsTypeModes = 'single' | 'multiple' | 'order'; -interface SettingsType< - T extends keyof AppSettings | keyof LibrarySettings, - V extends SettingsTypeModes, -> { - type: 'Modal'; - mode: SettingsTypeModes; - value: V extends 'multiple' ? T[] : T; - options: ModalOptions[]; - // value: T extends keyof AppSettings ? AppSettings[T] : T extends keyof LibrarySettings ? LibrarySettings[T] : never; -} +export type SettingsType = + | { + mode: 'single' | 'order'; + valueKey: T; + defaultValue: number | boolean; + options: ModalOptions[]; + // value: T extends keyof AppSettings ? AppSettings[T] : T extends keyof LibrarySettings ? LibrarySettings[T] : never; + } + | { + mode: 'multiple'; + valueKey: T[]; + defaultValue: Array; + options: ModalOptions[]; + // value: T extends keyof AppSettings ? AppSettings[T] : T extends keyof LibrarySettings ? LibrarySettings[T] : never; + }; -interface Setting { +type BaseModalSetting = { title: string; description?: string; - - settingsType: SettingsType< - keyof AppSettings | keyof LibrarySettings, - SettingsTypeModes - >; -} + type: 'Modal'; +}; +export type ModalSetting = + | (BaseModalSetting & { settingOrigin: 'App' } & SettingsType< + keyof AppSettings + >) + | (BaseModalSetting & { settingOrigin: 'Library' } & SettingsType< + keyof LibrarySettings + >); interface SettingSubGroup { subGroupTitle: string; - settings: Setting[]; + settings: ModalSetting[]; } interface SettingsGroup { groupTitle: string; + icon: string; navigateParam: settingsGroupTypes; subGroup: SettingSubGroup[]; } @@ -69,6 +79,7 @@ interface Settings { const settings: Settings = { general: { groupTitle: getString('generalSettings'), + icon: 'tune', navigateParam: 'GeneralSettings', subGroup: [ { @@ -77,47 +88,51 @@ const settings: Settings = { { title: getString('generalSettingsScreen.displayMode'), // description: () displayModesList[displayMode].label - settingsType: { - type: 'Modal', - mode: 'single', - value: 'displayMode', - options: displayModesList, - // value: - }, + type: 'Modal', + settingOrigin: 'Library', + + mode: 'single', + valueKey: 'displayMode', + defaultValue: DisplayModes.Comfortable, + options: displayModesList, + // valueKey: }, { title: getString('generalSettingsScreen.itemsPerRowLibrary'), - settingsType: { - type: 'Modal', - mode: 'single', - value: 'novelsPerRow', - options: gridSizeList, - // value: - }, + type: 'Modal', + settingOrigin: 'Library', + + mode: 'single', + valueKey: 'novelsPerRow', + defaultValue: 3, + options: gridSizeList, + // valueKey: }, { title: getString('generalSettingsScreen.novelBadges'), - settingsType: { - type: 'Modal', - mode: 'multiple', - value: [ - 'showDownloadBadges', - 'showNumberOfNovels', - 'showUnreadBadges', - ], - options: gridSizeList, - // value: - }, + type: 'Modal', + settingOrigin: 'Library', + + mode: 'multiple', + valueKey: [ + 'showDownloadBadges', + 'showNumberOfNovels', + 'showUnreadBadges', + ], + defaultValue: [true, false, true], + options: gridSizeList, + // valueKey: }, { title: getString('generalSettingsScreen.novelSort'), - settingsType: { - type: 'Modal', - mode: 'order', - value: 'novelsPerRow', - options: gridSizeList, - // value: - }, + type: 'Modal', + settingOrigin: 'Library', + + mode: 'order', + valueKey: 'novelsPerRow', + defaultValue: 3, + options: gridSizeList, + // value: }, ], }, diff --git a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreen.tsx b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV1.tsx similarity index 99% rename from src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreen.tsx rename to src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV1.tsx index aad44019b..cba7aae01 100644 --- a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreen.tsx +++ b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV1.tsx @@ -33,7 +33,7 @@ const GenralSettings: React.FC = ({ navigation }) => { const { displayMode = DisplayModes.Comfortable, - novelsPerRow = 3, + novelsPerRow = 2, showDownloadBadges = true, showNumberOfNovels = false, showUnreadBadges = true, @@ -122,6 +122,7 @@ const GenralSettings: React.FC = ({ navigation }) => { title={getString('generalSettingsScreen.itemsPerRowLibrary')} description={ novelsPerRow + + 1 + ' ' + getString('generalSettingsScreen.itemsPerRow') } diff --git a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx index bdf2dbc51..f01db8d17 100644 --- a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx +++ b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx @@ -23,6 +23,8 @@ import NovelBadgesModal from './modals/NovelBadgesModal'; import { NavigationState } from '@react-navigation/native'; import { getString } from '@strings/translations'; import SettingSwitch from '../components/SettingSwitch'; +import S from '../Settings'; +import DefaultSettingModal from './modals/DefaultSettingModal'; interface GenralSettingsProps { navigation: NavigationState; @@ -30,17 +32,11 @@ interface GenralSettingsProps { const GenralSettings: React.FC = ({ navigation }) => { const theme = useTheme(); + const Settings = S.general; - const { - displayMode = DisplayModes.Comfortable, - novelsPerRow = 3, - showDownloadBadges = true, - showNumberOfNovels = false, - showUnreadBadges = true, - sortOrder = LibrarySortOrder.DateAdded_DESC, - } = useLibrarySettings(); + const { setLibrarySettings, ...librarySettings } = useLibrarySettings(); - const sortOrderDisplay: string[] = sortOrder.split(' '); + // const sortOrderDisplay: string[] = sortOrder.split(' '); const sortOrderNameMap = new Map([ ['name', 'libraryScreen.bottomSheet.sortOrders.alphabetically'], ['chaptersDownloaded', 'libraryScreen.bottomSheet.sortOrders.download'], @@ -49,32 +45,23 @@ const GenralSettings: React.FC = ({ navigation }) => { ['lastReadAt', 'libraryScreen.bottomSheet.sortOrders.lastRead'], ['lastUpdatedAt', 'libraryScreen.bottomSheet.sortOrders.lastUpdated'], ]); - const { - updateLibraryOnLaunch, - downloadNewChapters, - onlyUpdateOngoingNovels, - defaultChapterSort, - refreshNovelMetadata, - disableHapticFeedback, - useLibraryFAB, - setAppSettings, - } = useAppSettings(); + const appSettings = useAppSettings(); const { showLastUpdateTime, setShowLastUpdateTime } = useLastUpdate(); - const generateNovelBadgesDescription = () => { - let res = []; - if (showDownloadBadges) { - res.push(getString('libraryScreen.bottomSheet.display.download')); - } - if (showUnreadBadges) { - res.push(getString('libraryScreen.bottomSheet.display.unread')); - } - if (showNumberOfNovels) { - res.push(getString('libraryScreen.bottomSheet.display.numberOfItems')); - } - return res.join(', '); - }; + // const generateNovelBadgesDescription = () => { + // let res = []; + // if (showDownloadBadges) { + // res.push(getString('libraryScreen.bottomSheet.display.download')); + // } + // if (showUnreadBadges) { + // res.push(getString('libraryScreen.bottomSheet.display.unread')); + // } + // if (showNumberOfNovels) { + // res.push(getString('libraryScreen.bottomSheet.display.numberOfItems')); + // } + // return res.join(', '); + // }; /** * Display Mode Modal @@ -90,7 +77,7 @@ const GenralSettings: React.FC = ({ navigation }) => { * Novel Badges Modal */ const novelBadgesModalRef = useBoolean(); - const novelBadgesDescription = generateNovelBadgesDescription(); + // const novelBadgesDescription = generateNovelBadgesDescription(); /** * Novel Sort Modal */ @@ -102,167 +89,36 @@ const GenralSettings: React.FC = ({ navigation }) => { return ( <> - - - - {getString('common.display')} - - - - - - - {getString('library')} - - setAppSettings({ updateLibraryOnLaunch: !updateLibraryOnLaunch }) - } - theme={theme} - /> - setAppSettings({ useLibraryFAB: !useLibraryFAB })} - theme={theme} - /> - - - {getString('generalSettingsScreen.novel')} - - - - - {getString('generalSettingsScreen.globalUpdate')} - - - setAppSettings({ - onlyUpdateOngoingNovels: !onlyUpdateOngoingNovels, - }) - } - theme={theme} - /> - - setAppSettings({ refreshNovelMetadata: !refreshNovelMetadata }) - } - theme={theme} - /> - setShowLastUpdateTime(!showLastUpdateTime)} - theme={theme} - /> - - - {getString('generalSettingsScreen.autoDownload')} - - - setAppSettings({ downloadNewChapters: !downloadNewChapters }) - } - theme={theme} - /> - - - {getString('generalSettings')} - - - setAppSettings({ disableHapticFeedback: !disableHapticFeedback }) - } - theme={theme} - /> - - - - - - - + + {Settings.subGroup.map(setting => { + return ( + <> + + {setting.subGroupTitle} + + {setting.settings.map(settingOption => { + return ( + + ); + })} + + ); + })} + + {getString('common.display')} + + + ); }; diff --git a/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx new file mode 100644 index 000000000..5302873f3 --- /dev/null +++ b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx @@ -0,0 +1,120 @@ +import React from 'react'; +import { Text, StyleSheet } from 'react-native'; + +import { Portal, Modal, overlay } from 'react-native-paper'; + +import { RadioButton } from '@components/RadioButton/RadioButton'; +import { ThemeColors } from '@theme/types'; +import { useLibrarySettings } from '@hooks/persisted'; +import { List } from '@components'; +import { useBoolean } from '@hooks/index'; +import settings, { + ModalSetting, + SettingsTypeModes, +} from '@screens/settings/Settings'; +import { + AppSettings, + LibrarySettings, + useAppSettings, +} from '@hooks/persisted/useSettings'; +import { options } from 'sanitize-html'; + +interface DisplayModeModalProps< + T extends keyof AppSettings | keyof LibrarySettings, + V extends SettingsTypeModes, +> { + // setting: SettingsType< + // keyof AppSettings | keyof LibrarySettings, + // SettingsTypeModes + // >; + setting: ModalSetting; + // value: any; + theme: ThemeColors; +} + +const DefaultSettingModal: React.FC< + DisplayModeModalProps< + keyof AppSettings | keyof LibrarySettings, + SettingsTypeModes + > +> = ({ theme, setting }) => { + const modalRef = useBoolean(); + + const { setLibrarySettings, ...librarySettings } = useLibrarySettings(); + const { setAppSettings, ...appSettings } = useAppSettings(); + console.log(useLibrarySettings()); + + const update = (value: unknown) => { + if (setting.mode === 'single') { + if (setting.settingOrigin === 'Library') { + setLibrarySettings({ + [setting.valueKey]: value, + }); + } else { + setAppSettings({ + [setting.valueKey]: value, + }); + } + } + }; + const currentValue = () => { + if (setting.mode === 'single') { + if (setting.settingOrigin === 'Library') { + return librarySettings[setting.valueKey] ?? setting.defaultValue; + } else { + return appSettings[setting.valueKey] ?? setting.defaultValue; + } + } + }; + + return ( + <> + + + + + {setting.title} + + {setting.mode === 'single' + ? setting.options.map(mode => ( + update(mode.value)} + label={mode.label} + theme={theme} + /> + )) + : null} + + + + ); +}; + +export default DefaultSettingModal; + +const styles = StyleSheet.create({ + containerStyle: { + paddingVertical: 20, + margin: 20, + borderRadius: 28, + }, + modalHeader: { + paddingHorizontal: 24, + fontSize: 24, + marginBottom: 10, + }, +}); diff --git a/src/screens/settings/SettingsScreen.tsx b/src/screens/settings/SettingsScreen.tsx index d040c4446..6bd0813ba 100644 --- a/src/screens/settings/SettingsScreen.tsx +++ b/src/screens/settings/SettingsScreen.tsx @@ -6,6 +6,7 @@ import { useTheme } from '@hooks/persisted'; import { getString } from '@strings/translations'; import { SettingsScreenProps } from '@navigators/types'; +import Settings from './Settings'; const SettingsScreen = ({ navigation }: SettingsScreenProps) => { const theme = useTheme(); @@ -18,6 +19,24 @@ const SettingsScreen = ({ navigation }: SettingsScreenProps) => { theme={theme} /> + {Object.keys(Settings).map(k => { + const key = k as any as keyof typeof Settings; + const setting = Settings[key]; + + return ( + + //@ts-ignore + navigation.navigate('SettingsStack', { + screen: setting.navigateParam, + }) + } + theme={theme} + /> + ); + })} Date: Sat, 15 Jun 2024 15:01:44 +0200 Subject: [PATCH 03/49] finished general setting screen (except show last update) --- src/screens/library/constants/constants.ts | 25 +++ src/screens/settings/Settings.ts | 151 ++++++----------- .../SettingsGeneralScreenV2.tsx | 61 ++++--- .../modals/DefaultSettingModal.tsx | 98 ++++++++--- .../utils/useUpdateSettingsFn.ts | 25 +++ src/screens/settings/SettingsScreen.tsx | 11 +- .../settings/components/SettingSwitchV2.tsx | 40 +++++ .../settingsGroups/generalSettingsGroup.ts | 156 ++++++++++++++++++ 8 files changed, 413 insertions(+), 154 deletions(-) create mode 100644 src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts create mode 100644 src/screens/settings/components/SettingSwitchV2.tsx create mode 100644 src/screens/settings/settingsGroups/generalSettingsGroup.ts diff --git a/src/screens/library/constants/constants.ts b/src/screens/library/constants/constants.ts index ab24cf3ba..95289011d 100644 --- a/src/screens/library/constants/constants.ts +++ b/src/screens/library/constants/constants.ts @@ -76,6 +76,19 @@ export const librarySortOrderList = [ }, ]; +export enum ChapterSortOrder { + BySource_ASC = 'ORDER BY position ASC', + BySource_DESC = 'ORDER BY position DESC', +} + +export const chapterSortOrderList = [ + { + label: getString('generalSettingsScreen.bySource'), + ASC: ChapterSortOrder.BySource_ASC, + DESC: ChapterSortOrder.BySource_DESC, + }, +]; + export enum DisplayModes { Compact, Comfortable, @@ -132,3 +145,15 @@ export const gridSizeList = [ value: GridSizes.XS, }, ]; + +export const badgesList = [ + { + label: getString('libraryScreen.bottomSheet.display.downloadBadges'), + }, + { + label: getString('libraryScreen.bottomSheet.display.unreadBadges'), + }, + { + label: getString('libraryScreen.bottomSheet.display.showNoOfItems'), + }, +]; diff --git a/src/screens/settings/Settings.ts b/src/screens/settings/Settings.ts index a6282ec70..0d0780aba 100644 --- a/src/screens/settings/Settings.ts +++ b/src/screens/settings/Settings.ts @@ -1,17 +1,5 @@ import { AppSettings, LibrarySettings } from '@hooks/persisted/useSettings'; -import { MoreStackParamList, SettingsStackParamList } from '@navigators/types'; -import { - CompositeNavigationProp, - NavigatorScreenParams, -} from '@react-navigation/native'; -import { StackScreenProps } from '@react-navigation/stack'; -import { - DisplayModes, - displayModesList, - gridSizeList, -} from '@screens/library/constants/constants'; -import { getString } from '@strings/translations'; -import React from 'react'; +import GeneralSettings from './settingsGroups/generalSettingsGroup'; type settingsGroupTypes = | 'GeneralSettings' @@ -31,21 +19,43 @@ type ModalOptions = { export type SettingsTypeModes = 'single' | 'multiple' | 'order'; -export type SettingsType = +type ValueKey = T extends 'App' + ? keyof AppSettings + : keyof LibrarySettings; + +type SettingOrigin = 'App' | 'Library'; + +export type ModalSettingsType = { + settingOrigin: T; +} & ( | { - mode: 'single' | 'order'; - valueKey: T; - defaultValue: number | boolean; - options: ModalOptions[]; - // value: T extends keyof AppSettings ? AppSettings[T] : T extends keyof LibrarySettings ? LibrarySettings[T] : never; + mode: 'single'; + valueKey: ValueKey; + defaultValue: number; + options: Array<{ + label: string; + value: number; + }>; } | { mode: 'multiple'; - valueKey: T[]; - defaultValue: Array; - options: ModalOptions[]; - // value: T extends keyof AppSettings ? AppSettings[T] : T extends keyof LibrarySettings ? LibrarySettings[T] : never; - }; + valueKey: Array>; + defaultValue: Array; + options: Array<{ + label: string; + }>; + } + | { + mode: 'order'; + valueKey: ValueKey; + defaultValue: string; + options: Array<{ + label: string; + ASC: string; + DESC: string; + }>; + } +); type BaseModalSetting = { title: string; @@ -53,19 +63,30 @@ type BaseModalSetting = { type: 'Modal'; }; export type ModalSetting = - | (BaseModalSetting & { settingOrigin: 'App' } & SettingsType< - keyof AppSettings - >) - | (BaseModalSetting & { settingOrigin: 'Library' } & SettingsType< - keyof LibrarySettings - >); + | (BaseModalSetting & ModalSettingsType<'App'>) + | (BaseModalSetting & ModalSettingsType<'Library'>); + +export type SwitchSettingsType = { + settingOrigin: T; + valueKey: ValueKey; + defaultValue: boolean; +}; + +type BaseSwitchSetting = { + title: string; + description?: string; + type: 'Switch'; +}; +export type SwitchSetting = + | (BaseSwitchSetting & SwitchSettingsType<'App'>) + | (BaseSwitchSetting & SwitchSettingsType<'Library'>); interface SettingSubGroup { subGroupTitle: string; - settings: ModalSetting[]; + settings: Array; } -interface SettingsGroup { +export interface SettingsGroup { groupTitle: string; icon: string; navigateParam: settingsGroupTypes; @@ -77,70 +98,6 @@ interface Settings { } const settings: Settings = { - general: { - groupTitle: getString('generalSettings'), - icon: 'tune', - navigateParam: 'GeneralSettings', - subGroup: [ - { - subGroupTitle: getString('common.display'), - settings: [ - { - title: getString('generalSettingsScreen.displayMode'), - // description: () displayModesList[displayMode].label - type: 'Modal', - settingOrigin: 'Library', - - mode: 'single', - valueKey: 'displayMode', - defaultValue: DisplayModes.Comfortable, - options: displayModesList, - // valueKey: - }, - { - title: getString('generalSettingsScreen.itemsPerRowLibrary'), - type: 'Modal', - settingOrigin: 'Library', - - mode: 'single', - valueKey: 'novelsPerRow', - defaultValue: 3, - options: gridSizeList, - // valueKey: - }, - { - title: getString('generalSettingsScreen.novelBadges'), - type: 'Modal', - settingOrigin: 'Library', - - mode: 'multiple', - valueKey: [ - 'showDownloadBadges', - 'showNumberOfNovels', - 'showUnreadBadges', - ], - defaultValue: [true, false, true], - options: gridSizeList, - // valueKey: - }, - { - title: getString('generalSettingsScreen.novelSort'), - type: 'Modal', - settingOrigin: 'Library', - - mode: 'order', - valueKey: 'novelsPerRow', - defaultValue: 3, - options: gridSizeList, - // value: - }, - ], - }, - { - subGroupTitle: getString('common.display'), - settings: [], - }, - ], - }, + general: GeneralSettings, } as const; export default settings; diff --git a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx index f01db8d17..cf32910ea 100644 --- a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx +++ b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx @@ -25,6 +25,8 @@ import { getString } from '@strings/translations'; import SettingSwitch from '../components/SettingSwitch'; import S from '../Settings'; import DefaultSettingModal from './modals/DefaultSettingModal'; +import useUpdateSettingsFn from './utils/useUpdateSettingsFn'; +import SettingSwitchV2 from '../components/SettingSwitchV2'; interface GenralSettingsProps { navigation: NavigationState; @@ -86,6 +88,7 @@ const GenralSettings: React.FC = ({ navigation }) => { * Chapter Sort Modal */ const defaultChapterSortModal = useBoolean(); + return ( <> = ({ navigation }) => { handleGoBack={navigation.goBack} theme={theme} /> - - {Settings.subGroup.map(setting => { - return ( - <> - - {setting.subGroupTitle} - - {setting.settings.map(settingOption => { - return ( - - ); - })} - - ); - })} - - {getString('common.display')} - - - + + + {Settings.subGroup.map((setting, index) => { + return ( + <> + {index === 0 ? null : } + + {setting.subGroupTitle} + + {setting.settings.map((settingOption, i) => { + if (settingOption.type === 'Modal') { + return ( + + ); + } else if (settingOption.type === 'Switch') { + return ( + + ); + } + })} + + ); + })} + + ); }; diff --git a/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx index 5302873f3..2b05b8936 100644 --- a/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx +++ b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { Text, StyleSheet } from 'react-native'; import { Portal, Modal, overlay } from 'react-native-paper'; @@ -6,7 +6,7 @@ import { Portal, Modal, overlay } from 'react-native-paper'; import { RadioButton } from '@components/RadioButton/RadioButton'; import { ThemeColors } from '@theme/types'; import { useLibrarySettings } from '@hooks/persisted'; -import { List } from '@components'; +import { Checkbox, List } from '@components'; import { useBoolean } from '@hooks/index'; import settings, { ModalSetting, @@ -18,6 +18,8 @@ import { useAppSettings, } from '@hooks/persisted/useSettings'; import { options } from 'sanitize-html'; +import { SortItem } from '@components/Checkbox/Checkbox'; +import useUpdateSettingsFn from '../utils/useUpdateSettingsFn'; interface DisplayModeModalProps< T extends keyof AppSettings | keyof LibrarySettings, @@ -42,36 +44,51 @@ const DefaultSettingModal: React.FC< const { setLibrarySettings, ...librarySettings } = useLibrarySettings(); const { setAppSettings, ...appSettings } = useAppSettings(); - console.log(useLibrarySettings()); - const update = (value: unknown) => { - if (setting.mode === 'single') { + // const update = (value: unknown, key?: string) => { + // if (setting.settingOrigin === 'Library') { + // setLibrarySettings({ + // //@ts-expect-error + // [key ?? setting.valueKey]: value, + // }); + // } else { + // setAppSettings({ + // //@ts-expect-error + // [key ?? setting.valueKey]: value, + // }); + // } + // }; + const update = useUpdateSettingsFn(setting.settingOrigin); + const currentValue = useMemo(() => { + if (setting.mode === 'single' || setting.mode === 'order') { if (setting.settingOrigin === 'Library') { - setLibrarySettings({ - [setting.valueKey]: value, - }); + return librarySettings[setting.valueKey] ?? setting.defaultValue; } else { - setAppSettings({ - [setting.valueKey]: value, - }); + return appSettings[setting.valueKey] ?? setting.defaultValue; } - } - }; - const currentValue = () => { - if (setting.mode === 'single') { + } else if (setting.mode === 'multiple') { if (setting.settingOrigin === 'Library') { - return librarySettings[setting.valueKey] ?? setting.defaultValue; + return setting.valueKey.map((k, i) => { + return librarySettings[k] ?? setting.defaultValue[i]; + }); } else { - return appSettings[setting.valueKey] ?? setting.defaultValue; + return setting.valueKey.map((k, i) => { + return appSettings[k] ?? setting.defaultValue[i]; + }); } } - }; + return 0; + }, [librarySettings, appSettings]); return ( <> @@ -91,12 +108,49 @@ const DefaultSettingModal: React.FC< ? setting.options.map(mode => ( update(mode.value)} + status={currentValue === mode.value} + onPress={() => update(mode.value, setting.valueKey)} label={mode.label} theme={theme} /> )) + : setting.mode === 'multiple' + ? setting.options.map((mode, i) => { + return ( + + //@ts-expect-error + update(!currentValue[i], setting.valueKey[i]) + } + theme={theme} + /> + ); + }) + : setting.mode === 'order' + ? setting.options.map((mode, i) => { + return ( + + update( + currentValue === mode.ASC ? mode.DESC : mode.ASC, + setting.valueKey, + ) + } + /> + ); + }) : null} @@ -104,7 +158,7 @@ const DefaultSettingModal: React.FC< ); }; -export default DefaultSettingModal; +export default React.memo(DefaultSettingModal); const styles = StyleSheet.create({ containerStyle: { diff --git a/src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts b/src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts new file mode 100644 index 000000000..50c7ed91a --- /dev/null +++ b/src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts @@ -0,0 +1,25 @@ +import { useAppSettings, useLibrarySettings } from '@hooks/persisted'; +import { AppSettings, LibrarySettings } from '@hooks/persisted/useSettings'; + +type UpdateFunction = ( + value: unknown, + key: keyof AppSettings | keyof LibrarySettings, +) => asserts key is T; + +export default function useUpdateSettingsFn(settingOrigin: 'Library' | 'App') { + const { setLibrarySettings } = useLibrarySettings(); + const { setAppSettings } = useAppSettings(); + if (settingOrigin === 'Library') { + const update: UpdateFunction = (value, key) => + setLibrarySettings({ + [key]: value, + }); + return update; + } else { + const update: UpdateFunction = (value, key) => + setAppSettings({ + [key]: value, + }); + return update; + } +} diff --git a/src/screens/settings/SettingsScreen.tsx b/src/screens/settings/SettingsScreen.tsx index 6bd0813ba..6c9d67320 100644 --- a/src/screens/settings/SettingsScreen.tsx +++ b/src/screens/settings/SettingsScreen.tsx @@ -37,16 +37,7 @@ const SettingsScreen = ({ navigation }: SettingsScreenProps) => { /> ); })} - - navigation.navigate('SettingsStack', { - screen: 'GeneralSettings', - }) - } - theme={theme} - /> + { + let res; + if (setting.settingOrigin === 'Library') { + res = librarySettings[setting.valueKey] ?? setting.defaultValue; + } else { + res = appSettings[setting.valueKey] ?? setting.defaultValue; + } + return res as boolean; + }, [librarySettings, appSettings]); + + return ( + update(!currentValue, setting.valueKey)} + theme={theme} + style={{ paddingHorizontal: 16 }} + /> + ); +} diff --git a/src/screens/settings/settingsGroups/generalSettingsGroup.ts b/src/screens/settings/settingsGroups/generalSettingsGroup.ts new file mode 100644 index 000000000..089d1c9da --- /dev/null +++ b/src/screens/settings/settingsGroups/generalSettingsGroup.ts @@ -0,0 +1,156 @@ +import { + ChapterSortOrder, + DisplayModes, + LibrarySortOrder, + badgesList, + chapterSortOrderList, + displayModesList, + gridSizeList, + librarySortOrderList, +} from '@screens/library/constants/constants'; +import { getString } from '@strings/translations'; +import { SettingsGroup } from '../Settings'; + +const GeneralSettings: SettingsGroup = { + groupTitle: getString('generalSettings'), + icon: 'tune', + navigateParam: 'GeneralSettings', + subGroup: [ + { + subGroupTitle: getString('common.display'), + settings: [ + { + title: getString('generalSettingsScreen.displayMode'), + // description: () displayModesList[displayMode].label + type: 'Modal', + settingOrigin: 'Library', + + mode: 'single', + valueKey: 'displayMode', + defaultValue: DisplayModes.Comfortable, + options: displayModesList, + }, + { + title: getString('generalSettingsScreen.itemsPerRowLibrary'), + type: 'Modal', + settingOrigin: 'Library', + + mode: 'single', + valueKey: 'novelsPerRow', + defaultValue: 3, + options: gridSizeList, + }, + { + title: getString('generalSettingsScreen.novelBadges'), + type: 'Modal', + settingOrigin: 'Library', + + mode: 'multiple', + valueKey: [ + 'showDownloadBadges', + 'showNumberOfNovels', + 'showUnreadBadges', + ], + defaultValue: [true, false, true], + options: badgesList, + }, + { + title: getString('generalSettingsScreen.novelSort'), + type: 'Modal', + settingOrigin: 'Library', + + mode: 'order', + valueKey: 'sortOrder', + defaultValue: LibrarySortOrder.DateAdded_DESC, + options: librarySortOrderList, + }, + ], + }, + { + subGroupTitle: getString('library'), + settings: [ + { + title: getString('generalSettingsScreen.updateLibrary'), + description: getString('generalSettingsScreen.updateLibraryDesc'), + type: 'Switch', + settingOrigin: 'App', + valueKey: 'updateLibraryOnLaunch', + defaultValue: false, + }, + { + title: getString('generalSettingsScreen.useFAB'), + type: 'Switch', + settingOrigin: 'App', + valueKey: 'useLibraryFAB', + defaultValue: false, + }, + ], + }, + { + subGroupTitle: getString('generalSettingsScreen.novel'), + settings: [ + { + title: getString('generalSettingsScreen.chapterSort'), + type: 'Modal', + settingOrigin: 'App', + + mode: 'order', + valueKey: 'defaultChapterSort', + defaultValue: ChapterSortOrder.BySource_DESC, + options: chapterSortOrderList, + }, + ], + }, + { + subGroupTitle: getString('generalSettingsScreen.globalUpdate'), + settings: [ + { + title: getString('generalSettingsScreen.updateOngoing'), + type: 'Switch', + settingOrigin: 'App', + + valueKey: 'onlyUpdateOngoingNovels', + defaultValue: true, + }, + { + title: getString('generalSettingsScreen.refreshMetadata'), + description: getString( + 'generalSettingsScreen.refreshMetadataDescription', + ), + type: 'Switch', + settingOrigin: 'App', + valueKey: 'refreshNovelMetadata', + defaultValue: false, + }, + //TODO Add Show last update time + ], + }, + { + subGroupTitle: getString('generalSettingsScreen.autoDownload'), + settings: [ + { + title: getString('generalSettingsScreen.downloadNewChapters'), + type: 'Switch', + settingOrigin: 'App', + + valueKey: 'downloadNewChapters', + defaultValue: false, + }, + ], + }, + { + subGroupTitle: getString('generalSettings'), + settings: [ + { + title: getString('generalSettingsScreen.disableHapticFeedback'), + type: 'Switch', + settingOrigin: 'App', + + valueKey: 'disableHapticFeedback', + defaultValue: false, + }, + ], + }, + ], +} as const; +export default GeneralSettings; From 28e5f66f38f1dfd9ee8f44023dc55e548518ef4d Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sat, 15 Jun 2024 17:08:37 +0200 Subject: [PATCH 04/49] added all desciptions to generalsettigs --- src/screens/settings/Settings.ts | 21 +-- .../SettingsGeneralScreenV2.tsx | 130 ++---------------- .../modals/DefaultSettingModal.tsx | 15 +- .../utils/useUpdateSettingsFn.ts | 24 +++- .../settings/components/RenderSettings.tsx | 37 +++++ .../settings/components/SettingSwitchV2.tsx | 20 ++- .../settingsGroups/generalSettingsGroup.ts | 43 +++++- 7 files changed, 144 insertions(+), 146 deletions(-) create mode 100644 src/screens/settings/components/RenderSettings.tsx diff --git a/src/screens/settings/Settings.ts b/src/screens/settings/Settings.ts index 0d0780aba..850497215 100644 --- a/src/screens/settings/Settings.ts +++ b/src/screens/settings/Settings.ts @@ -12,18 +12,15 @@ type settingsGroupTypes = | 'RespositorySettings' | undefined; -type ModalOptions = { - label: string; - value: number; -}; - export type SettingsTypeModes = 'single' | 'multiple' | 'order'; -type ValueKey = T extends 'App' +export type ValueKey = T extends 'App' ? keyof AppSettings - : keyof LibrarySettings; + : T extends 'Library' + ? keyof LibrarySettings + : 'showLastUpdateTime'; -type SettingOrigin = 'App' | 'Library'; +export type SettingOrigin = 'App' | 'Library' | 'lastUpdateTime'; export type ModalSettingsType = { settingOrigin: T; @@ -32,6 +29,7 @@ export type ModalSettingsType = { mode: 'single'; valueKey: ValueKey; defaultValue: number; + description?: (value: number) => string; options: Array<{ label: string; value: number; @@ -41,6 +39,7 @@ export type ModalSettingsType = { mode: 'multiple'; valueKey: Array>; defaultValue: Array; + description?: (value: Array) => string; options: Array<{ label: string; }>; @@ -49,6 +48,8 @@ export type ModalSettingsType = { mode: 'order'; valueKey: ValueKey; defaultValue: string; + description?: (value: string) => string; + options: Array<{ label: string; ASC: string; @@ -59,7 +60,6 @@ export type ModalSettingsType = { type BaseModalSetting = { title: string; - description?: string; type: 'Modal'; }; export type ModalSetting = @@ -79,9 +79,10 @@ type BaseSwitchSetting = { }; export type SwitchSetting = | (BaseSwitchSetting & SwitchSettingsType<'App'>) + | (BaseSwitchSetting & SwitchSettingsType<'lastUpdateTime'>) | (BaseSwitchSetting & SwitchSettingsType<'Library'>); -interface SettingSubGroup { +export interface SettingSubGroup { subGroupTitle: string; settings: Array; } diff --git a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx index cf32910ea..9089d92c6 100644 --- a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx +++ b/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx @@ -1,32 +1,12 @@ import React from 'react'; import { ScrollView } from 'react-native'; -import DisplayModeModal from './modals/DisplayModeModal'; -import GridSizeModal from './modals/GridSizeModal'; - -import { - useAppSettings, - useLastUpdate, - useLibrarySettings, - useTheme, -} from '@hooks/persisted'; -import DefaultChapterSortModal from '../components/DefaultChapterSortModal'; -import { - DisplayModes, - displayModesList, - LibrarySortOrder, -} from '@screens/library/constants/constants'; -import { useBoolean } from '@hooks'; +import { useTheme } from '@hooks/persisted'; import { Appbar, List } from '@components'; -import NovelSortModal from './modals/NovelSortModal'; -import NovelBadgesModal from './modals/NovelBadgesModal'; import { NavigationState } from '@react-navigation/native'; -import { getString } from '@strings/translations'; -import SettingSwitch from '../components/SettingSwitch'; import S from '../Settings'; -import DefaultSettingModal from './modals/DefaultSettingModal'; -import useUpdateSettingsFn from './utils/useUpdateSettingsFn'; -import SettingSwitchV2 from '../components/SettingSwitchV2'; +import RenderSettings from '../components/RenderSettings'; +import { SafeAreaView } from 'react-native-safe-area-context'; interface GenralSettingsProps { navigation: NavigationState; @@ -36,101 +16,19 @@ const GenralSettings: React.FC = ({ navigation }) => { const theme = useTheme(); const Settings = S.general; - const { setLibrarySettings, ...librarySettings } = useLibrarySettings(); - - // const sortOrderDisplay: string[] = sortOrder.split(' '); - const sortOrderNameMap = new Map([ - ['name', 'libraryScreen.bottomSheet.sortOrders.alphabetically'], - ['chaptersDownloaded', 'libraryScreen.bottomSheet.sortOrders.download'], - ['chaptersUnread', 'libraryScreen.bottomSheet.sortOrders.totalChapters'], - ['id', 'libraryScreen.bottomSheet.sortOrders.dateAdded'], - ['lastReadAt', 'libraryScreen.bottomSheet.sortOrders.lastRead'], - ['lastUpdatedAt', 'libraryScreen.bottomSheet.sortOrders.lastUpdated'], - ]); - const appSettings = useAppSettings(); - - const { showLastUpdateTime, setShowLastUpdateTime } = useLastUpdate(); - - // const generateNovelBadgesDescription = () => { - // let res = []; - // if (showDownloadBadges) { - // res.push(getString('libraryScreen.bottomSheet.display.download')); - // } - // if (showUnreadBadges) { - // res.push(getString('libraryScreen.bottomSheet.display.unread')); - // } - // if (showNumberOfNovels) { - // res.push(getString('libraryScreen.bottomSheet.display.numberOfItems')); - // } - // return res.join(', '); - // }; - - /** - * Display Mode Modal - */ - const displayModalRef = useBoolean(); - - /** - * Grid Size Modal - */ - const gridSizeModalRef = useBoolean(); - - /** - * Novel Badges Modal - */ - const novelBadgesModalRef = useBoolean(); - // const novelBadgesDescription = generateNovelBadgesDescription(); - /** - * Novel Sort Modal - */ - const novelSortModalRef = useBoolean(); - /** - * Chapter Sort Modal - */ - const defaultChapterSortModal = useBoolean(); - return ( - <> - - - - {Settings.subGroup.map((setting, index) => { - return ( - <> - {index === 0 ? null : } - - {setting.subGroupTitle} - - {setting.settings.map((settingOption, i) => { - if (settingOption.type === 'Modal') { - return ( - - ); - } else if (settingOption.type === 'Switch') { - return ( - - ); - } - })} - - ); - })} - + + + + + {Settings.subGroup.map(RenderSettings)} - + ); }; diff --git a/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx index 2b05b8936..bd2165c53 100644 --- a/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx +++ b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx @@ -20,6 +20,7 @@ import { import { options } from 'sanitize-html'; import { SortItem } from '@components/Checkbox/Checkbox'; import useUpdateSettingsFn from '../utils/useUpdateSettingsFn'; +import { badgesList } from '@screens/library/constants/constants'; interface DisplayModeModalProps< T extends keyof AppSettings | keyof LibrarySettings, @@ -80,15 +81,19 @@ const DefaultSettingModal: React.FC< return 0; }, [librarySettings, appSettings]); + function generateDescription() { + if (!setting.description) { + return; + } + + return setting.description(currentValue); + } + return ( <> diff --git a/src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts b/src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts index 50c7ed91a..6d440e310 100644 --- a/src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts +++ b/src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts @@ -1,25 +1,39 @@ -import { useAppSettings, useLibrarySettings } from '@hooks/persisted'; +import { + useAppSettings, + useLastUpdate, + useLibrarySettings, +} from '@hooks/persisted'; import { AppSettings, LibrarySettings } from '@hooks/persisted/useSettings'; +import { SettingOrigin, ValueKey } from '@screens/settings/Settings'; -type UpdateFunction = ( +type UpdateFunction> = ( value: unknown, - key: keyof AppSettings | keyof LibrarySettings, + key: ValueKey, ) => asserts key is T; -export default function useUpdateSettingsFn(settingOrigin: 'Library' | 'App') { +export default function useUpdateSettingsFn(settingOrigin: SettingOrigin) { const { setLibrarySettings } = useLibrarySettings(); const { setAppSettings } = useAppSettings(); + const { setShowLastUpdateTime } = useLastUpdate(); if (settingOrigin === 'Library') { const update: UpdateFunction = (value, key) => setLibrarySettings({ [key]: value, }); return update; - } else { + } else if (settingOrigin === 'App') { const update: UpdateFunction = (value, key) => setAppSettings({ [key]: value, }); return update; + } else if (settingOrigin === 'lastUpdateTime') { + const update: UpdateFunction<'showLastUpdateTime'> = (value, key) => + setShowLastUpdateTime(value as boolean); + return update; + } else { + throw new Error( + 'settingOrigin with the type of ' + settingOrigin + ' is not implemented', + ); } } diff --git a/src/screens/settings/components/RenderSettings.tsx b/src/screens/settings/components/RenderSettings.tsx new file mode 100644 index 000000000..be13651c4 --- /dev/null +++ b/src/screens/settings/components/RenderSettings.tsx @@ -0,0 +1,37 @@ +import { List } from '@components'; +import { SettingSubGroup } from '../Settings'; +import DefaultSettingModal from '../SettingsGeneralScreen/modals/DefaultSettingModal'; +import SettingSwitchV2 from './SettingSwitchV2'; +import { useTheme } from '@hooks/persisted'; + +export default function (setting: SettingSubGroup, index: number) { + const theme = useTheme(); + + return ( + <> + {index === 0 ? null : } + + {setting.subGroupTitle} + + {setting.settings.map((settingOption, i) => { + if (settingOption.type === 'Modal') { + return ( + + ); + } else if (settingOption.type === 'Switch') { + return ( + + ); + } + })} + + ); +} diff --git a/src/screens/settings/components/SettingSwitchV2.tsx b/src/screens/settings/components/SettingSwitchV2.tsx index e4f4d6fe4..e6861c8ab 100644 --- a/src/screens/settings/components/SettingSwitchV2.tsx +++ b/src/screens/settings/components/SettingSwitchV2.tsx @@ -2,7 +2,11 @@ import { SwitchItem } from '@components'; import { ThemeColors } from '@theme/types'; import useUpdateSettingsFn from '../SettingsGeneralScreen/utils/useUpdateSettingsFn'; import { SwitchSetting } from '../Settings'; -import { useAppSettings, useLibrarySettings } from '@hooks/persisted'; +import { + useAppSettings, + useLastUpdate, + useLibrarySettings, +} from '@hooks/persisted'; import { useMemo } from 'react'; interface SettingSwitchProps { @@ -14,17 +18,21 @@ export default function SettingSwitchV2({ setting, theme, }: SettingSwitchProps) { - const update = useUpdateSettingsFn(setting.settingOrigin); + const update = useUpdateSettingsFn(setting.settingOrigin)!; const { setLibrarySettings, ...librarySettings } = useLibrarySettings(); const { setAppSettings, ...appSettings } = useAppSettings(); + const { showLastUpdateTime } = useLastUpdate(); + const currentValue = useMemo(() => { let res; if (setting.settingOrigin === 'Library') { - res = librarySettings[setting.valueKey] ?? setting.defaultValue; - } else { - res = appSettings[setting.valueKey] ?? setting.defaultValue; + res = librarySettings[setting.valueKey]; + } else if (setting.settingOrigin === 'App') { + res = appSettings[setting.valueKey]; + } else if (setting.settingOrigin === 'lastUpdateTime') { + res = showLastUpdateTime; } - return res as boolean; + return (res ?? setting.defaultValue) as boolean; }, [librarySettings, appSettings]); return ( diff --git a/src/screens/settings/settingsGroups/generalSettingsGroup.ts b/src/screens/settings/settingsGroups/generalSettingsGroup.ts index 089d1c9da..aa11b4468 100644 --- a/src/screens/settings/settingsGroups/generalSettingsGroup.ts +++ b/src/screens/settings/settingsGroups/generalSettingsGroup.ts @@ -11,6 +11,15 @@ import { import { getString } from '@strings/translations'; import { SettingsGroup } from '../Settings'; +const sortOrderNameMap = new Map([ + ['name', 'libraryScreen.bottomSheet.sortOrders.alphabetically'], + ['chaptersDownloaded', 'libraryScreen.bottomSheet.sortOrders.download'], + ['chaptersUnread', 'libraryScreen.bottomSheet.sortOrders.totalChapters'], + ['id', 'libraryScreen.bottomSheet.sortOrders.dateAdded'], + ['lastReadAt', 'libraryScreen.bottomSheet.sortOrders.lastRead'], + ['lastUpdatedAt', 'libraryScreen.bottomSheet.sortOrders.lastUpdated'], +]); + const GeneralSettings: SettingsGroup = { groupTitle: getString('generalSettings'), icon: 'tune', @@ -21,7 +30,7 @@ const GeneralSettings: SettingsGroup = { settings: [ { title: getString('generalSettingsScreen.displayMode'), - // description: () displayModesList[displayMode].label + description: val => displayModesList[val].label, type: 'Modal', settingOrigin: 'Library', @@ -34,6 +43,10 @@ const GeneralSettings: SettingsGroup = { title: getString('generalSettingsScreen.itemsPerRowLibrary'), type: 'Modal', settingOrigin: 'Library', + description: val => + ''.concat( + val + 1 + ' ' + getString('generalSettingsScreen.itemsPerRow'), + ), mode: 'single', valueKey: 'novelsPerRow', @@ -44,6 +57,13 @@ const GeneralSettings: SettingsGroup = { title: getString('generalSettingsScreen.novelBadges'), type: 'Modal', settingOrigin: 'Library', + description: val => + badgesList + .filter((v, i) => { + return val[i]; + }) + .map(v => v.label) + .join(', '), mode: 'multiple', valueKey: [ @@ -58,7 +78,11 @@ const GeneralSettings: SettingsGroup = { title: getString('generalSettingsScreen.novelSort'), type: 'Modal', settingOrigin: 'Library', - + description: val => { + const v = val.split(' '); + //@ts-expect-error + return getString(sortOrderNameMap.get(v[0])) + ' ' + v[1]; + }, mode: 'order', valueKey: 'sortOrder', defaultValue: LibrarySortOrder.DateAdded_DESC, @@ -93,7 +117,12 @@ const GeneralSettings: SettingsGroup = { title: getString('generalSettingsScreen.chapterSort'), type: 'Modal', settingOrigin: 'App', - + description: val => + `${getString('generalSettingsScreen.bySource')} ${ + val === 'ORDER BY position ASC' + ? getString('generalSettingsScreen.asc') + : getString('generalSettingsScreen.desc') + }`, mode: 'order', valueKey: 'defaultChapterSort', defaultValue: ChapterSortOrder.BySource_DESC, @@ -122,7 +151,13 @@ const GeneralSettings: SettingsGroup = { valueKey: 'refreshNovelMetadata', defaultValue: false, }, - //TODO Add Show last update time + { + title: getString('generalSettingsScreen.updateTime'), + type: 'Switch', + settingOrigin: 'lastUpdateTime', + valueKey: 'showLastUpdateTime', + defaultValue: true, + }, ], }, { From d7cdec9469f238a12ab4443e027c413236ab70fa Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sat, 15 Jun 2024 19:37:18 +0200 Subject: [PATCH 05/49] added Appearance settings themePicker --- src/navigators/MoreStack.tsx | 4 +- src/screens/settings/Settings.ts | 24 +++++++-- .../settings/components/RenderSettings.tsx | 9 ++++ .../components/SettingsThemePicker.tsx | 52 +++++++++++++++++++ .../settingsGroups/appearanceSettingsGroup.ts | 27 ++++++++++ .../SettingsAppearanceScreenV2.tsx | 35 +++++++++++++ .../SettingsGeneralScreenV2.tsx | 0 7 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 src/screens/settings/components/SettingsThemePicker.tsx create mode 100644 src/screens/settings/settingsGroups/appearanceSettingsGroup.ts create mode 100644 src/screens/settings/settingsScreens/SettingsAppearanceScreenV2.tsx rename src/screens/settings/{SettingsGeneralScreen => settingsScreens}/SettingsGeneralScreenV2.tsx (100%) diff --git a/src/navigators/MoreStack.tsx b/src/navigators/MoreStack.tsx index cf959d52c..42bec7292 100644 --- a/src/navigators/MoreStack.tsx +++ b/src/navigators/MoreStack.tsx @@ -9,10 +9,10 @@ import TrackerSettings from '../screens/settings/SettingsTrackerScreen'; import ReaderSettings from '../screens/settings/SettingsReaderScreen/SettingsReaderScreen'; import BackupSettings from '../screens/settings/SettingsBackupScreen'; import AdvancedSettings from '../screens/settings/SettingsAdvancedScreen'; -import GeneralSettings from '../screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2'; +import GeneralSettings from '../screens/settings/settingsScreens/SettingsGeneralScreenV2'; import TaskQueue from '../screens/more/TaskQueueScreen'; import Downloads from '../screens/more/DownloadsScreen'; -import AppearanceSettings from '../screens/settings/SettingsAppearanceScreen'; +import AppearanceSettings from '../screens/settings/settingsScreens/SettingsAppearanceScreenV2'; import CategoriesScreen from '@screens/Categories/CategoriesScreen'; import RespositorySettings from '@screens/settings/SettingsRepositoryScreen/SettingsRepositoryScreen'; // import LibrarySettings from '@screens/settings/SettingsLibraryScreen/SettingsLibraryScreen'; diff --git a/src/screens/settings/Settings.ts b/src/screens/settings/Settings.ts index 850497215..9a08d7686 100644 --- a/src/screens/settings/Settings.ts +++ b/src/screens/settings/Settings.ts @@ -1,5 +1,7 @@ import { AppSettings, LibrarySettings } from '@hooks/persisted/useSettings'; import GeneralSettings from './settingsGroups/generalSettingsGroup'; +import AppearanceSettings from './settingsGroups/appearanceSettingsGroup'; +import { ThemeColors } from '@theme/types'; type settingsGroupTypes = | 'GeneralSettings' @@ -18,9 +20,17 @@ export type ValueKey = T extends 'App' ? keyof AppSettings : T extends 'Library' ? keyof LibrarySettings - : 'showLastUpdateTime'; + : T extends 'lastUpdateTime' + ? 'showLastUpdateTime' + : T extends 'ColorPicker' + ? never + : never; -export type SettingOrigin = 'App' | 'Library' | 'lastUpdateTime'; +export type SettingOrigin = + | 'App' + | 'Library' + | 'lastUpdateTime' + | 'ColorPicker'; export type ModalSettingsType = { settingOrigin: T; @@ -82,9 +92,15 @@ export type SwitchSetting = | (BaseSwitchSetting & SwitchSettingsType<'lastUpdateTime'>) | (BaseSwitchSetting & SwitchSettingsType<'Library'>); +export type ThemePickerSetting = { + title: string; + type: 'ThemePicker'; + options: Array; +}; + export interface SettingSubGroup { subGroupTitle: string; - settings: Array; + settings: Array; } export interface SettingsGroup { @@ -96,9 +112,11 @@ export interface SettingsGroup { interface Settings { general: SettingsGroup; + appearance: SettingsGroup; } const settings: Settings = { general: GeneralSettings, + appearance: AppearanceSettings, } as const; export default settings; diff --git a/src/screens/settings/components/RenderSettings.tsx b/src/screens/settings/components/RenderSettings.tsx index be13651c4..e742bba45 100644 --- a/src/screens/settings/components/RenderSettings.tsx +++ b/src/screens/settings/components/RenderSettings.tsx @@ -3,6 +3,7 @@ import { SettingSubGroup } from '../Settings'; import DefaultSettingModal from '../SettingsGeneralScreen/modals/DefaultSettingModal'; import SettingSwitchV2 from './SettingSwitchV2'; import { useTheme } from '@hooks/persisted'; +import SettingsThemePicker from './SettingsThemePicker'; export default function (setting: SettingSubGroup, index: number) { const theme = useTheme(); @@ -30,6 +31,14 @@ export default function (setting: SettingSubGroup, index: number) { theme={theme} /> ); + } else if (settingOption.type === 'ThemePicker') { + return ( + + ); } })} diff --git a/src/screens/settings/components/SettingsThemePicker.tsx b/src/screens/settings/components/SettingsThemePicker.tsx new file mode 100644 index 000000000..1d376b956 --- /dev/null +++ b/src/screens/settings/components/SettingsThemePicker.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { ScrollView, Text } from 'react-native'; + +import { ThemePicker as TP } from '@components/ThemePicker/ThemePicker'; + +import { ThemeColors } from '@theme/types'; +import { ThemePickerSetting } from '../Settings'; +import { useMMKVObject, useMMKVString } from 'react-native-mmkv'; + +interface ThemePicker { + theme: ThemeColors; + settings: ThemePickerSetting; +} + +export default function SettingsThemePicker({ theme, settings }: ThemePicker) { + const [, setTheme] = useMMKVObject('APP_THEME'); + const [, setCustomAccentColor] = useMMKVString('CUSTOM_ACCENT_COLOR'); + return ( + <> + + {settings.title} + + + {settings.options.map(item => ( + { + setTheme(item); + setCustomAccentColor(undefined); + }} + /> + ))} + + + ); +} diff --git a/src/screens/settings/settingsGroups/appearanceSettingsGroup.ts b/src/screens/settings/settingsGroups/appearanceSettingsGroup.ts new file mode 100644 index 000000000..39e9b083e --- /dev/null +++ b/src/screens/settings/settingsGroups/appearanceSettingsGroup.ts @@ -0,0 +1,27 @@ +import { getString } from '@strings/translations'; +import { SettingsGroup } from '../Settings'; +import { darkThemes, lightThemes } from '@theme/md3'; + +const AppearanceSettings: SettingsGroup = { + groupTitle: getString('appearance'), + icon: 'palette-outline', + navigateParam: 'AppearanceSettings', + subGroup: [ + { + subGroupTitle: getString('appearanceScreen.appTheme'), + settings: [ + { + title: getString('appearanceScreen.lightTheme'), + type: 'ThemePicker', + options: lightThemes, + }, + { + title: getString('appearanceScreen.darkTheme'), + type: 'ThemePicker', + options: darkThemes, + }, + ], + }, + ], +}; +export default AppearanceSettings; diff --git a/src/screens/settings/settingsScreens/SettingsAppearanceScreenV2.tsx b/src/screens/settings/settingsScreens/SettingsAppearanceScreenV2.tsx new file mode 100644 index 000000000..854e43b29 --- /dev/null +++ b/src/screens/settings/settingsScreens/SettingsAppearanceScreenV2.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { ScrollView } from 'react-native'; + +import { useTheme } from '@hooks/persisted'; +import { Appbar, List } from '@components'; +import { NavigationState } from '@react-navigation/native'; +import S from '../Settings'; +import RenderSettings from '../components/RenderSettings'; +import { SafeAreaView } from 'react-native-safe-area-context'; + +interface GenralSettingsProps { + navigation: NavigationState; +} + +const GenralSettings: React.FC = ({ navigation }) => { + const theme = useTheme(); + const Settings = S.appearance; + + return ( + + + + + {Settings.subGroup.map(RenderSettings)} + + + ); +}; + +export default GenralSettings; diff --git a/src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx b/src/screens/settings/settingsScreens/SettingsGeneralScreenV2.tsx similarity index 100% rename from src/screens/settings/SettingsGeneralScreen/SettingsGeneralScreenV2.tsx rename to src/screens/settings/settingsScreens/SettingsGeneralScreenV2.tsx From 44914be09bd7a3205f7d0805a1e39e7c3a11339f Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sat, 15 Jun 2024 19:46:05 +0200 Subject: [PATCH 06/49] added Pure black dark mode --- .../modals/DefaultSettingModal.tsx | 2 +- .../components/SettingsThemePicker.tsx | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx index bd2165c53..1e254117a 100644 --- a/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx +++ b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx @@ -85,7 +85,7 @@ const DefaultSettingModal: React.FC< if (!setting.description) { return; } - + //@ts-expect-error return setting.description(currentValue); } diff --git a/src/screens/settings/components/SettingsThemePicker.tsx b/src/screens/settings/components/SettingsThemePicker.tsx index 1d376b956..d4cf2ad97 100644 --- a/src/screens/settings/components/SettingsThemePicker.tsx +++ b/src/screens/settings/components/SettingsThemePicker.tsx @@ -5,7 +5,13 @@ import { ThemePicker as TP } from '@components/ThemePicker/ThemePicker'; import { ThemeColors } from '@theme/types'; import { ThemePickerSetting } from '../Settings'; -import { useMMKVObject, useMMKVString } from 'react-native-mmkv'; +import { + useMMKVBoolean, + useMMKVObject, + useMMKVString, +} from 'react-native-mmkv'; +import SettingSwitch from './SettingSwitch'; +import { getString } from '@strings/translations'; interface ThemePicker { theme: ThemeColors; @@ -15,6 +21,8 @@ interface ThemePicker { export default function SettingsThemePicker({ theme, settings }: ThemePicker) { const [, setTheme] = useMMKVObject('APP_THEME'); const [, setCustomAccentColor] = useMMKVString('CUSTOM_ACCENT_COLOR'); + const [isAmoledBlack = false, setAmoledBlack] = + useMMKVBoolean('AMOLED_BLACK'); return ( <> ))} + {theme.isDark && settings.options[0].isDark ? ( + setAmoledBlack(prevVal => !prevVal)} + theme={theme} + /> + ) : null} ); } From 891a9f0d6509d1bb441a4bb5239043154c4286c2 Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sat, 15 Jun 2024 21:02:05 +0200 Subject: [PATCH 07/49] added useKeyboardHeight hook --- src/hooks/common/useKeyboardHeight.ts | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/hooks/common/useKeyboardHeight.ts diff --git a/src/hooks/common/useKeyboardHeight.ts b/src/hooks/common/useKeyboardHeight.ts new file mode 100644 index 000000000..3af2afc9c --- /dev/null +++ b/src/hooks/common/useKeyboardHeight.ts @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react'; +import { Keyboard, KeyboardEvent } from 'react-native'; + +export const useKeyboardHeight = () => { + const [keyboardHeight, setKeyboardHeight] = useState(0); + + useEffect(() => { + function onKeyboardDidShow(e: KeyboardEvent) { + setKeyboardHeight(e.endCoordinates.height); + } + + function onKeyboardDidHide() { + setKeyboardHeight(0); + } + + const showSubscription = Keyboard.addListener( + 'keyboardDidShow', + onKeyboardDidShow, + ); + const hideSubscription = Keyboard.addListener( + 'keyboardDidHide', + onKeyboardDidHide, + ); + return () => { + showSubscription.remove(); + hideSubscription.remove(); + }; + }, []); + + return keyboardHeight; +}; From 309c627760f8c764ad82635eaa512b61837fb45d Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sat, 15 Jun 2024 21:15:31 +0200 Subject: [PATCH 08/49] Working accentColorModal --- src/screens/settings/Settings.ts | 28 ++- .../modals/ColorPickerModal.tsx | 219 ++++++++++++++++++ .../modals/DefaultSettingModal.tsx | 25 +- .../settings/components/RenderSettings.tsx | 59 +++-- .../components/SettingsThemePicker.tsx | 1 + .../settingsGroups/appearanceSettingsGroup.ts | 6 + 6 files changed, 282 insertions(+), 56 deletions(-) create mode 100644 src/screens/settings/SettingsGeneralScreen/modals/ColorPickerModal.tsx diff --git a/src/screens/settings/Settings.ts b/src/screens/settings/Settings.ts index 9a08d7686..31ecbd335 100644 --- a/src/screens/settings/Settings.ts +++ b/src/screens/settings/Settings.ts @@ -22,15 +22,11 @@ export type ValueKey = T extends 'App' ? keyof LibrarySettings : T extends 'lastUpdateTime' ? 'showLastUpdateTime' - : T extends 'ColorPicker' - ? never + : T extends 'MMKV' + ? 'isDark' : never; -export type SettingOrigin = - | 'App' - | 'Library' - | 'lastUpdateTime' - | 'ColorPicker'; +export type SettingOrigin = 'App' | 'Library' | 'lastUpdateTime' | 'MMKV'; export type ModalSettingsType = { settingOrigin: T; @@ -98,9 +94,25 @@ export type ThemePickerSetting = { options: Array; }; +export type ColorPickerSettingsType = { + settingOrigin: T; + // valueKey: ValueKey; + // defaultValue: boolean; +}; + +type BaseColorPickerSetting = { + title: string; + description?: (val: string) => string; + type: 'ColorPicker'; +}; +export type ColorPickerSetting = + | BaseColorPickerSetting & ColorPickerSettingsType<'MMKV'>; + export interface SettingSubGroup { subGroupTitle: string; - settings: Array; + settings: Array< + ModalSetting | SwitchSetting | ThemePickerSetting | ColorPickerSetting + >; } export interface SettingsGroup { diff --git a/src/screens/settings/SettingsGeneralScreen/modals/ColorPickerModal.tsx b/src/screens/settings/SettingsGeneralScreen/modals/ColorPickerModal.tsx new file mode 100644 index 000000000..f0adfe559 --- /dev/null +++ b/src/screens/settings/SettingsGeneralScreen/modals/ColorPickerModal.tsx @@ -0,0 +1,219 @@ +import React, { useState } from 'react'; +import { FlatList, Pressable, StyleSheet, Text, View } from 'react-native'; + +import { Modal, overlay, Portal, TextInput } from 'react-native-paper'; +import { List } from '@components'; +import { ThemeColors } from '@theme/types'; +import { useBoolean } from '@hooks/index'; +import { ColorPickerSetting } from '@screens/settings/Settings'; +import { useMMKVString } from 'react-native-mmkv'; +import { useKeyboardHeight } from '@hooks/common/useKeyboardHeight'; + +interface ColorPickerModalProps { + settings: ColorPickerSetting; + theme: ThemeColors; + showAccentColors?: boolean; +} + +const ColorPickerModal: React.FC = ({ + theme, + settings, + showAccentColors, +}) => { + const currentValue = + settings.settingOrigin === 'MMKV' ? rgbToHex(theme.primary) : ''; + console.log(currentValue); + + const [text, setText] = useState(currentValue); + const [error, setError] = useState(); + const modalRef = useBoolean(); + const keyboardHeight = useKeyboardHeight(); + + const [, setCustomAccentColor] = useMMKVString('CUSTOM_ACCENT_COLOR'); + + const update = (val: string) => { + setText(val); + if (settings.settingOrigin === 'MMKV') { + setCustomAccentColor(val); + } + }; + + const onDismiss = () => { + modalRef.setFalse(); + if (error) { + setText(currentValue); + } + setError(null); + }; + + const onOpen = () => { + modalRef.setTrue(); + setText(currentValue); + }; + + const onSubmitEditing = () => { + const re = /^#([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3})$/i; + setError(null); + const t = rgbToHex(text); + if (error) { + return; + } + if (t.match(re)) { + update(t); + onDismiss(); + } else { + setError('Enter a valid hex color code'); + } + }; + + const accentColors = [ + '#EF5350', + '#EC407A', + '#AB47BC', + '#7E57C2', + '#5C6BC0', + '#42A5F5', + '#29B6FC', + '#26C6DA', + '#26A69A', + '#66BB6A', + '#9CCC65', + '#D4E157', + '#FFEE58', + '#FFCA28', + '#FFA726', + '#FF7043', + '#8D6E63', + '#BDBDBD', + '#78909C', + '#000000', + ]; + + return ( + <> + + + + + {settings.title} + + {showAccentColors ? ( + item} + renderItem={({ item }) => ( + + { + update(item); + onDismiss(); + }} + /> + + )} + /> + ) : null} + + {error} + + + + ); + function componentToHex(c: number) { + if (c > 256 || c < 0) { + setError('No valid rgb value'); + } + var hex = c.toString(16); + return hex.length == 1 ? '0' + hex : hex; + } + + function rgbToHex(rgb: string) { + if (!rgb.match(/rgb\(/i)) { + return rgb; + } + const match = rgb.match(/\d+/g); + if (match?.length !== 3) { + setError('No valid rgb value'); + return rgb; + } + let r, g, b; + try { + r = parseInt(match[0]); + g = parseInt(match[1]); + b = parseInt(match[2]); + } catch (error) { + setError('No valid rgb value'); + return rgb; + } + return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b); + } +}; + +export default ColorPickerModal; + +const styles = StyleSheet.create({ + modalContainer: { + margin: 30, + padding: 24, + borderRadius: 28, + }, + modalTitle: { + fontSize: 24, + marginBottom: 16, + }, + errorText: { + color: '#FF0033', + paddingTop: 8, + }, +}); diff --git a/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx index 1e254117a..7dc3b3446 100644 --- a/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx +++ b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx @@ -8,30 +8,20 @@ import { ThemeColors } from '@theme/types'; import { useLibrarySettings } from '@hooks/persisted'; import { Checkbox, List } from '@components'; import { useBoolean } from '@hooks/index'; -import settings, { - ModalSetting, - SettingsTypeModes, -} from '@screens/settings/Settings'; +import { ModalSetting, SettingsTypeModes } from '@screens/settings/Settings'; import { AppSettings, LibrarySettings, useAppSettings, } from '@hooks/persisted/useSettings'; -import { options } from 'sanitize-html'; import { SortItem } from '@components/Checkbox/Checkbox'; import useUpdateSettingsFn from '../utils/useUpdateSettingsFn'; -import { badgesList } from '@screens/library/constants/constants'; interface DisplayModeModalProps< T extends keyof AppSettings | keyof LibrarySettings, V extends SettingsTypeModes, > { - // setting: SettingsType< - // keyof AppSettings | keyof LibrarySettings, - // SettingsTypeModes - // >; setting: ModalSetting; - // value: any; theme: ThemeColors; } @@ -46,19 +36,6 @@ const DefaultSettingModal: React.FC< const { setLibrarySettings, ...librarySettings } = useLibrarySettings(); const { setAppSettings, ...appSettings } = useAppSettings(); - // const update = (value: unknown, key?: string) => { - // if (setting.settingOrigin === 'Library') { - // setLibrarySettings({ - // //@ts-expect-error - // [key ?? setting.valueKey]: value, - // }); - // } else { - // setAppSettings({ - // //@ts-expect-error - // [key ?? setting.valueKey]: value, - // }); - // } - // }; const update = useUpdateSettingsFn(setting.settingOrigin); const currentValue = useMemo(() => { if (setting.mode === 'single' || setting.mode === 'order') { diff --git a/src/screens/settings/components/RenderSettings.tsx b/src/screens/settings/components/RenderSettings.tsx index e742bba45..acfec4677 100644 --- a/src/screens/settings/components/RenderSettings.tsx +++ b/src/screens/settings/components/RenderSettings.tsx @@ -4,6 +4,7 @@ import DefaultSettingModal from '../SettingsGeneralScreen/modals/DefaultSettingM import SettingSwitchV2 from './SettingSwitchV2'; import { useTheme } from '@hooks/persisted'; import SettingsThemePicker from './SettingsThemePicker'; +import ColorPickerModal from '../SettingsGeneralScreen/modals/ColorPickerModal'; export default function (setting: SettingSubGroup, index: number) { const theme = useTheme(); @@ -15,30 +16,40 @@ export default function (setting: SettingSubGroup, index: number) { {setting.subGroupTitle} {setting.settings.map((settingOption, i) => { - if (settingOption.type === 'Modal') { - return ( - - ); - } else if (settingOption.type === 'Switch') { - return ( - - ); - } else if (settingOption.type === 'ThemePicker') { - return ( - - ); + switch (settingOption.type) { + case 'Modal': + return ( + + ); + case 'Switch': + return ( + + ); + case 'ThemePicker': + return ( + + ); + case 'ColorPicker': + return ( + + ); } })} diff --git a/src/screens/settings/components/SettingsThemePicker.tsx b/src/screens/settings/components/SettingsThemePicker.tsx index d4cf2ad97..74bc7b2bc 100644 --- a/src/screens/settings/components/SettingsThemePicker.tsx +++ b/src/screens/settings/components/SettingsThemePicker.tsx @@ -23,6 +23,7 @@ export default function SettingsThemePicker({ theme, settings }: ThemePicker) { const [, setCustomAccentColor] = useMMKVString('CUSTOM_ACCENT_COLOR'); const [isAmoledBlack = false, setAmoledBlack] = useMMKVBoolean('AMOLED_BLACK'); + return ( <> c.toUpperCase() ?? '', + settingOrigin: 'MMKV', + }, ], }, ], From 0c5a67be681323f4385d4c8ad3a32d0e6d84350a Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sat, 15 Jun 2024 21:21:26 +0200 Subject: [PATCH 09/49] finished appearance screen --- .../settingsGroups/appearanceSettingsGroup.ts | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/screens/settings/settingsGroups/appearanceSettingsGroup.ts b/src/screens/settings/settingsGroups/appearanceSettingsGroup.ts index a5b338156..35816cd22 100644 --- a/src/screens/settings/settingsGroups/appearanceSettingsGroup.ts +++ b/src/screens/settings/settingsGroups/appearanceSettingsGroup.ts @@ -28,6 +28,51 @@ const AppearanceSettings: SettingsGroup = { }, ], }, + { + subGroupTitle: getString('appearanceScreen.novelInfo'), + settings: [ + { + title: getString('appearanceScreen.hideBackdrop'), + type: 'Switch', + settingOrigin: 'App', + valueKey: 'hideBackdrop', + defaultValue: false, + }, + { + title: getString('advancedSettingsScreen.useFAB'), + type: 'Switch', + settingOrigin: 'App', + valueKey: 'useFabForContinueReading', + defaultValue: false, + }, + ], + }, + { + subGroupTitle: getString('appearanceScreen.navbar'), + settings: [ + { + title: getString('appearanceScreen.showUpdatesInTheNav'), + type: 'Switch', + settingOrigin: 'App', + valueKey: 'showUpdatesTab', + defaultValue: true, + }, + { + title: getString('appearanceScreen.showHistoryInTheNav'), + type: 'Switch', + settingOrigin: 'App', + valueKey: 'showHistoryTab', + defaultValue: true, + }, + { + title: getString('appearanceScreen.alwaysShowNavLabels'), + type: 'Switch', + settingOrigin: 'App', + valueKey: 'showLabelsInNav', + defaultValue: true, + }, + ], + }, ], }; export default AppearanceSettings; From 8471c887f9dd4b3461eb5cb89222a7204fc95a6a Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sat, 15 Jun 2024 21:23:54 +0200 Subject: [PATCH 10/49] removed console.log --- .../settings/SettingsGeneralScreen/modals/ColorPickerModal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/screens/settings/SettingsGeneralScreen/modals/ColorPickerModal.tsx b/src/screens/settings/SettingsGeneralScreen/modals/ColorPickerModal.tsx index f0adfe559..26bd390ac 100644 --- a/src/screens/settings/SettingsGeneralScreen/modals/ColorPickerModal.tsx +++ b/src/screens/settings/SettingsGeneralScreen/modals/ColorPickerModal.tsx @@ -22,7 +22,6 @@ const ColorPickerModal: React.FC = ({ }) => { const currentValue = settings.settingOrigin === 'MMKV' ? rgbToHex(theme.primary) : ''; - console.log(currentValue); const [text, setText] = useState(currentValue); const [error, setError] = useState(); From 9118e83afd004769f0b2e5cd486bfaf9c4a3f4d8 Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sat, 15 Jun 2024 23:13:13 +0200 Subject: [PATCH 11/49] added dragable readersettings webview --- .../modals/DefaultSettingModal.tsx | 1 + .../SettingsReaderScreen.tsx | 31 +++++++++++++++++-- src/screens/settings/SettingsScreen.tsx | 10 ------ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx index 7dc3b3446..2f709f6f4 100644 --- a/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx +++ b/src/screens/settings/SettingsGeneralScreen/modals/DefaultSettingModal.tsx @@ -115,6 +115,7 @@ const DefaultSettingModal: React.FC< ? setting.options.map((mode, i) => { return ( { 'unread': 1, 'updatedTime': null, }; + const [webViewHeight, setWebViewHeight] = useState(280); const [hidden, setHidden] = useState(true); const batteryLevel = useBatteryLevel(); const readerSettings = useChapterReaderSettings(); @@ -138,7 +139,7 @@ const SettingsReaderScreen = () => { theme={theme} /> - + { }} /> - + + { + setWebViewHeight(e.nativeEvent.pageY - 84); + }} + > + ••• + + diff --git a/src/screens/settings/SettingsScreen.tsx b/src/screens/settings/SettingsScreen.tsx index 6c9d67320..58d672059 100644 --- a/src/screens/settings/SettingsScreen.tsx +++ b/src/screens/settings/SettingsScreen.tsx @@ -38,16 +38,6 @@ const SettingsScreen = ({ navigation }: SettingsScreenProps) => { ); })} - - navigation.navigate('SettingsStack', { - screen: 'AppearanceSettings', - }) - } - theme={theme} - /> Date: Sun, 16 Jun 2024 17:02:12 +0200 Subject: [PATCH 12/49] added dynamic SettingsStack --- src/navigators/MoreStack.tsx | 9 ++++----- src/navigators/types/index.ts | 20 +++++++++++-------- ...eralScreenV2.tsx => SettingsSubScreen.tsx} | 17 ++++++++-------- 3 files changed, 25 insertions(+), 21 deletions(-) rename src/screens/settings/settingsScreens/{SettingsGeneralScreenV2.tsx => SettingsSubScreen.tsx} (62%) diff --git a/src/navigators/MoreStack.tsx b/src/navigators/MoreStack.tsx index 42bec7292..eef5f60f8 100644 --- a/src/navigators/MoreStack.tsx +++ b/src/navigators/MoreStack.tsx @@ -9,10 +9,10 @@ import TrackerSettings from '../screens/settings/SettingsTrackerScreen'; import ReaderSettings from '../screens/settings/SettingsReaderScreen/SettingsReaderScreen'; import BackupSettings from '../screens/settings/SettingsBackupScreen'; import AdvancedSettings from '../screens/settings/SettingsAdvancedScreen'; -import GeneralSettings from '../screens/settings/settingsScreens/SettingsGeneralScreenV2'; +import SettingsSubScreen from '../screens/settings/settingsScreens/SettingsSubScreen'; import TaskQueue from '../screens/more/TaskQueueScreen'; import Downloads from '../screens/more/DownloadsScreen'; -import AppearanceSettings from '../screens/settings/settingsScreens/SettingsAppearanceScreenV2'; + import CategoriesScreen from '@screens/Categories/CategoriesScreen'; import RespositorySettings from '@screens/settings/SettingsRepositoryScreen/SettingsRepositoryScreen'; // import LibrarySettings from '@screens/settings/SettingsLibraryScreen/SettingsLibraryScreen'; @@ -20,7 +20,7 @@ import StatsScreen from '@screens/StatsScreen/StatsScreen'; import { MoreStackParamList, SettingsStackParamList } from './types'; const Stack = createStackNavigator< - MoreStackParamList & SettingsStackParamList + SettingsStackParamList & MoreStackParamList >(); const stackNavigatorConfig = { headerShown: false }; @@ -28,11 +28,10 @@ const stackNavigatorConfig = { headerShown: false }; const SettingsStack = () => ( - + - {/* */} diff --git a/src/navigators/types/index.ts b/src/navigators/types/index.ts index 82f7f7c10..3e47207ea 100644 --- a/src/navigators/types/index.ts +++ b/src/navigators/types/index.ts @@ -4,6 +4,7 @@ import { NavigatorScreenParams, } from '@react-navigation/native'; import { StackScreenProps } from '@react-navigation/stack'; +import { Settings } from '@screens/settings/Settings'; import { MaterialBottomTabScreenProps } from 'react-native-paper'; export type RootStackParamList = { @@ -71,16 +72,19 @@ export type MoreStackParamList = { Statistics: undefined; }; +type SettingsProps = { + settingsSource: keyof Settings; +}; + export type SettingsStackParamList = { Settings: undefined; - GeneralSettings: undefined; - ReaderSettings: undefined; - TrackerSettings: undefined; - BackupSettings: undefined; - AppearanceSettings: undefined; - AdvancedSettings: undefined; - LibrarySettings: undefined; - RespositorySettings: undefined; + SubScreen: SettingsProps; + ReaderSettings: SettingsProps; + TrackerSettings: SettingsProps; + BackupSettings: SettingsProps; + AdvancedSettings: SettingsProps; + LibrarySettings: SettingsProps; + RespositorySettings: SettingsProps; }; export type NovelScreenProps = StackScreenProps; diff --git a/src/screens/settings/settingsScreens/SettingsGeneralScreenV2.tsx b/src/screens/settings/settingsScreens/SettingsSubScreen.tsx similarity index 62% rename from src/screens/settings/settingsScreens/SettingsGeneralScreenV2.tsx rename to src/screens/settings/settingsScreens/SettingsSubScreen.tsx index 9089d92c6..a8a627204 100644 --- a/src/screens/settings/settingsScreens/SettingsGeneralScreenV2.tsx +++ b/src/screens/settings/settingsScreens/SettingsSubScreen.tsx @@ -3,25 +3,26 @@ import { ScrollView } from 'react-native'; import { useTheme } from '@hooks/persisted'; import { Appbar, List } from '@components'; -import { NavigationState } from '@react-navigation/native'; import S from '../Settings'; import RenderSettings from '../components/RenderSettings'; import { SafeAreaView } from 'react-native-safe-area-context'; +import { StackScreenProps } from '@react-navigation/stack'; +import { SettingsStackParamList } from '@navigators/types'; -interface GenralSettingsProps { - navigation: NavigationState; -} +type Props = StackScreenProps< + SettingsStackParamList, + keyof Omit +>; -const GenralSettings: React.FC = ({ navigation }) => { +const SettingsSubScreen: React.FC = ({ navigation, route }) => { const theme = useTheme(); - const Settings = S.general; + const Settings = S[route.params.settingsSource]; return ( @@ -32,4 +33,4 @@ const GenralSettings: React.FC = ({ navigation }) => { ); }; -export default GenralSettings; +export default SettingsSubScreen; From c9bf5e07547a5bf1bb89833f4f8168960cb7b679 Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sun, 23 Jun 2024 20:11:10 +0200 Subject: [PATCH 13/49] progress on reader settings --- src/navigators/MoreStack.tsx | 3 +- src/navigators/types/index.ts | 4 - src/screens/settings/Settings.ts | 67 +++- .../utils/useUpdateSettingsFn.ts | 24 +- .../SettingsReaderScreen.tsx | 4 +- src/screens/settings/SettingsScreen.tsx | 13 +- .../settings/components/RenderSettings.tsx | 65 +--- .../components/RenderSettingsGroup.tsx | 22 ++ .../settings/components/SettingSwitchV2.tsx | 68 +++- .../settings/components/SettingTextInput.tsx | 86 +++++ .../settingsGroups/readerSettingsGroup.ts | 96 ++++++ .../ReaderSettingsSubScreen.tsx | 300 ++++++++++++++++++ .../SettingsAppearanceScreenV2.tsx | 35 -- .../settingsScreens/SettingsSubScreen.tsx | 32 +- 14 files changed, 692 insertions(+), 127 deletions(-) create mode 100644 src/screens/settings/components/RenderSettingsGroup.tsx create mode 100644 src/screens/settings/components/SettingTextInput.tsx create mode 100644 src/screens/settings/settingsGroups/readerSettingsGroup.ts create mode 100644 src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx delete mode 100644 src/screens/settings/settingsScreens/SettingsAppearanceScreenV2.tsx diff --git a/src/navigators/MoreStack.tsx b/src/navigators/MoreStack.tsx index eef5f60f8..48ec4c15e 100644 --- a/src/navigators/MoreStack.tsx +++ b/src/navigators/MoreStack.tsx @@ -18,6 +18,7 @@ import RespositorySettings from '@screens/settings/SettingsRepositoryScreen/Sett // import LibrarySettings from '@screens/settings/SettingsLibraryScreen/SettingsLibraryScreen'; import StatsScreen from '@screens/StatsScreen/StatsScreen'; import { MoreStackParamList, SettingsStackParamList } from './types'; +import ReaderSettingsSubScreen from '@screens/settings/settingsScreens/ReaderSettingsSubScreen'; const Stack = createStackNavigator< SettingsStackParamList & MoreStackParamList @@ -29,7 +30,7 @@ const SettingsStack = () => ( - + diff --git a/src/navigators/types/index.ts b/src/navigators/types/index.ts index 3e47207ea..e11b468ec 100644 --- a/src/navigators/types/index.ts +++ b/src/navigators/types/index.ts @@ -142,10 +142,6 @@ export type SettingsScreenProps = CompositeScreenProps< StackScreenProps, StackScreenProps >; -export type AppearanceSettingsScreenProps = StackScreenProps< - SettingsStackParamList, - 'AppearanceSettings' ->; export type TrackerSettingsScreenProps = StackScreenProps< SettingsStackParamList, 'TrackerSettings' diff --git a/src/screens/settings/Settings.ts b/src/screens/settings/Settings.ts index 31ecbd335..76df316e9 100644 --- a/src/screens/settings/Settings.ts +++ b/src/screens/settings/Settings.ts @@ -1,7 +1,13 @@ -import { AppSettings, LibrarySettings } from '@hooks/persisted/useSettings'; +import { + AppSettings, + ChapterGeneralSettings, + ChapterReaderSettings, + LibrarySettings, +} from '@hooks/persisted/useSettings'; import GeneralSettings from './settingsGroups/generalSettingsGroup'; import AppearanceSettings from './settingsGroups/appearanceSettingsGroup'; import { ThemeColors } from '@theme/types'; +import ReaderSettings from './settingsGroups/readerSettingsGroup'; type settingsGroupTypes = | 'GeneralSettings' @@ -24,9 +30,19 @@ export type ValueKey = T extends 'App' ? 'showLastUpdateTime' : T extends 'MMKV' ? 'isDark' + : T extends 'GeneralChapter' + ? keyof ChapterGeneralSettings + : T extends 'ReaderChapter' + ? keyof ChapterReaderSettings : never; -export type SettingOrigin = 'App' | 'Library' | 'lastUpdateTime' | 'MMKV'; +export type SettingOrigin = + | 'App' + | 'Library' + | 'lastUpdateTime' + | 'MMKV' + | 'GeneralChapter' + | 'ReaderChapter'; export type ModalSettingsType = { settingOrigin: T; @@ -76,6 +92,7 @@ export type SwitchSettingsType = { settingOrigin: T; valueKey: ValueKey; defaultValue: boolean; + dependents?: Array; }; type BaseSwitchSetting = { @@ -83,10 +100,33 @@ type BaseSwitchSetting = { description?: string; type: 'Switch'; }; -export type SwitchSetting = - | (BaseSwitchSetting & SwitchSettingsType<'App'>) - | (BaseSwitchSetting & SwitchSettingsType<'lastUpdateTime'>) - | (BaseSwitchSetting & SwitchSettingsType<'Library'>); +export type SwitchSetting = BaseSwitchSetting & + ( + | SwitchSettingsType<'App'> + | SwitchSettingsType<'lastUpdateTime'> + | SwitchSettingsType<'Library'> + | SwitchSettingsType<'GeneralChapter'> + | SwitchSettingsType<'ReaderChapter'> + ); + +export type NumberInputSettingsType = { + settingOrigin: T; + valueKey: ValueKey; + defaultValue: string; +}; + +type BaseNumberInputSetting = { + title: string; + description?: string; + type: 'NumberInput'; +}; +export type NumberInputSetting = BaseNumberInputSetting & + ( + | NumberInputSettingsType<'App'> + | NumberInputSettingsType<'lastUpdateTime'> + | NumberInputSettingsType<'Library'> + | NumberInputSettingsType<'GeneralChapter'> + ); export type ThemePickerSetting = { title: string; @@ -108,11 +148,16 @@ type BaseColorPickerSetting = { export type ColorPickerSetting = | BaseColorPickerSetting & ColorPickerSettingsType<'MMKV'>; +export type SettingsSubGroupSettings = + | ModalSetting + | SwitchSetting + | ThemePickerSetting + | ColorPickerSetting + | NumberInputSetting; + export interface SettingSubGroup { subGroupTitle: string; - settings: Array< - ModalSetting | SwitchSetting | ThemePickerSetting | ColorPickerSetting - >; + settings: Array; } export interface SettingsGroup { @@ -122,13 +167,15 @@ export interface SettingsGroup { subGroup: SettingSubGroup[]; } -interface Settings { +export interface Settings { general: SettingsGroup; appearance: SettingsGroup; + reader: SettingsGroup; } const settings: Settings = { general: GeneralSettings, appearance: AppearanceSettings, + reader: ReaderSettings, } as const; export default settings; diff --git a/src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts b/src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts index 6d440e310..45f2715e7 100644 --- a/src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts +++ b/src/screens/settings/SettingsGeneralScreen/utils/useUpdateSettingsFn.ts @@ -3,7 +3,14 @@ import { useLastUpdate, useLibrarySettings, } from '@hooks/persisted'; -import { AppSettings, LibrarySettings } from '@hooks/persisted/useSettings'; +import { + AppSettings, + ChapterGeneralSettings, + ChapterReaderSettings, + LibrarySettings, + useChapterGeneralSettings, + useChapterReaderSettings, +} from '@hooks/persisted/useSettings'; import { SettingOrigin, ValueKey } from '@screens/settings/Settings'; type UpdateFunction> = ( @@ -15,6 +22,9 @@ export default function useUpdateSettingsFn(settingOrigin: SettingOrigin) { const { setLibrarySettings } = useLibrarySettings(); const { setAppSettings } = useAppSettings(); const { setShowLastUpdateTime } = useLastUpdate(); + const { setChapterGeneralSettings } = useChapterGeneralSettings(); + const { setChapterReaderSettings } = useChapterReaderSettings(); + if (settingOrigin === 'Library') { const update: UpdateFunction = (value, key) => setLibrarySettings({ @@ -31,6 +41,18 @@ export default function useUpdateSettingsFn(settingOrigin: SettingOrigin) { const update: UpdateFunction<'showLastUpdateTime'> = (value, key) => setShowLastUpdateTime(value as boolean); return update; + } else if (settingOrigin === 'GeneralChapter') { + const update: UpdateFunction = (value, key) => + setChapterGeneralSettings({ + [key]: value, + }); + return update; + } else if (settingOrigin === 'ReaderChapter') { + const update: UpdateFunction = (value, key) => + setChapterReaderSettings({ + [key]: value, + }); + return update; } else { throw new Error( 'settingOrigin with the type of ' + settingOrigin + ' is not implemented', diff --git a/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx b/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx index 4c8cb2a1c..cf1b9657f 100644 --- a/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx +++ b/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx @@ -38,7 +38,9 @@ type WebViewPostEvent = { data?: { [key: string]: string | number }; }; -const SettingsReaderScreen = () => { +const SettingsReaderScreen = r => { + console.log(JSON.stringify(r, null, 2)); + const theme = useTheme(); const navigation = useNavigation(); const webViewRef = useRef(null); diff --git a/src/screens/settings/SettingsScreen.tsx b/src/screens/settings/SettingsScreen.tsx index 58d672059..2bf7f55dd 100644 --- a/src/screens/settings/SettingsScreen.tsx +++ b/src/screens/settings/SettingsScreen.tsx @@ -19,18 +19,18 @@ const SettingsScreen = ({ navigation }: SettingsScreenProps) => { theme={theme} /> - {Object.keys(Settings).map(k => { - const key = k as any as keyof typeof Settings; + {Object.typedKeys(Settings).map(key => { const setting = Settings[key]; return ( - //@ts-ignore navigation.navigate('SettingsStack', { - screen: setting.navigateParam, + screen: key === 'reader' ? 'ReaderSettings' : 'SubScreen', + params: { settingsSource: key }, }) } theme={theme} @@ -44,6 +44,7 @@ const SettingsScreen = ({ navigation }: SettingsScreenProps) => { onPress={() => navigation.navigate('SettingsStack', { screen: 'ReaderSettings', + params: { settingsSource: 'general' }, }) } theme={theme} @@ -54,6 +55,7 @@ const SettingsScreen = ({ navigation }: SettingsScreenProps) => { onPress={() => navigation.navigate('SettingsStack', { screen: 'RespositorySettings', + params: { settingsSource: 'general' }, }) } theme={theme} @@ -64,6 +66,7 @@ const SettingsScreen = ({ navigation }: SettingsScreenProps) => { onPress={() => navigation.navigate('SettingsStack', { screen: 'TrackerSettings', + params: { settingsSource: 'general' }, }) } theme={theme} @@ -74,6 +77,7 @@ const SettingsScreen = ({ navigation }: SettingsScreenProps) => { onPress={() => navigation.navigate('SettingsStack', { screen: 'BackupSettings', + params: { settingsSource: 'general' }, }) } theme={theme} @@ -84,6 +88,7 @@ const SettingsScreen = ({ navigation }: SettingsScreenProps) => { onPress={() => navigation.navigate('SettingsStack', { screen: 'AdvancedSettings', + params: { settingsSource: 'general' }, }) } theme={theme} diff --git a/src/screens/settings/components/RenderSettings.tsx b/src/screens/settings/components/RenderSettings.tsx index acfec4677..4c8abd793 100644 --- a/src/screens/settings/components/RenderSettings.tsx +++ b/src/screens/settings/components/RenderSettings.tsx @@ -1,57 +1,26 @@ -import { List } from '@components'; -import { SettingSubGroup } from '../Settings'; +import { SettingsSubGroupSettings } from '../Settings'; import DefaultSettingModal from '../SettingsGeneralScreen/modals/DefaultSettingModal'; import SettingSwitchV2 from './SettingSwitchV2'; import { useTheme } from '@hooks/persisted'; import SettingsThemePicker from './SettingsThemePicker'; import ColorPickerModal from '../SettingsGeneralScreen/modals/ColorPickerModal'; +import SettingTextInput from './SettingTextInput'; -export default function (setting: SettingSubGroup, index: number) { +export default function ({ setting }: { setting: SettingsSubGroupSettings }) { const theme = useTheme(); - return ( - <> - {index === 0 ? null : } - - {setting.subGroupTitle} - - {setting.settings.map((settingOption, i) => { - switch (settingOption.type) { - case 'Modal': - return ( - - ); - case 'Switch': - return ( - - ); - case 'ThemePicker': - return ( - - ); - case 'ColorPicker': - return ( - - ); - } - })} - - ); + switch (setting.type) { + case 'Modal': + return ; + case 'Switch': + return ; + case 'ThemePicker': + return ; + case 'NumberInput': + return ; + case 'ColorPicker': + return ( + + ); + } } diff --git a/src/screens/settings/components/RenderSettingsGroup.tsx b/src/screens/settings/components/RenderSettingsGroup.tsx new file mode 100644 index 000000000..1001e3b71 --- /dev/null +++ b/src/screens/settings/components/RenderSettingsGroup.tsx @@ -0,0 +1,22 @@ +import { List } from '@components'; +import { SettingSubGroup } from '../Settings'; +import { useTheme } from '@hooks/persisted'; +import RenderSettings from './RenderSettings'; + +export default function (setting: SettingSubGroup, index: number) { + const theme = useTheme(); + + return ( + <> + {index === 0 ? null : } + + {setting.subGroupTitle} + + {setting.settings.map((settingOption, i) => { + return ( + + ); + })} + + ); +} diff --git a/src/screens/settings/components/SettingSwitchV2.tsx b/src/screens/settings/components/SettingSwitchV2.tsx index e6861c8ab..fc6695946 100644 --- a/src/screens/settings/components/SettingSwitchV2.tsx +++ b/src/screens/settings/components/SettingSwitchV2.tsx @@ -1,13 +1,23 @@ import { SwitchItem } from '@components'; import { ThemeColors } from '@theme/types'; import useUpdateSettingsFn from '../SettingsGeneralScreen/utils/useUpdateSettingsFn'; -import { SwitchSetting } from '../Settings'; +import { SettingOrigin, SwitchSetting, ValueKey } from '../Settings'; import { useAppSettings, + useChapterGeneralSettings, + useChapterReaderSettings, useLastUpdate, useLibrarySettings, } from '@hooks/persisted'; import { useMemo } from 'react'; +import { View } from 'react-native'; +import RenderSettings from './RenderSettings'; +import Animated, { + Easing, + ReduceMotion, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; interface SettingSwitchProps { setting: SwitchSetting; @@ -18,10 +28,13 @@ export default function SettingSwitchV2({ setting, theme, }: SettingSwitchProps) { - const update = useUpdateSettingsFn(setting.settingOrigin)!; - const { setLibrarySettings, ...librarySettings } = useLibrarySettings(); - const { setAppSettings, ...appSettings } = useAppSettings(); + const up = useUpdateSettingsFn(setting.settingOrigin)!; + + const librarySettings = useLibrarySettings(); + const appSettings = useAppSettings(); const { showLastUpdateTime } = useLastUpdate(); + const chapterSettings = useChapterGeneralSettings(); + const chapterReaderSettings = useChapterReaderSettings(); const currentValue = useMemo(() => { let res; @@ -31,18 +44,47 @@ export default function SettingSwitchV2({ res = appSettings[setting.valueKey]; } else if (setting.settingOrigin === 'lastUpdateTime') { res = showLastUpdateTime; + } else if (setting.settingOrigin === 'GeneralChapter') { + res = chapterSettings[setting.valueKey]; + } else if (setting.settingOrigin === 'ReaderChapter') { + res = chapterReaderSettings[setting.valueKey]; } return (res ?? setting.defaultValue) as boolean; - }, [librarySettings, appSettings]); + }, [librarySettings, appSettings, showLastUpdateTime, chapterSettings]); + + const maxHeight = useSharedValue(100); + const opacity = useSharedValue(1); + const update = (value: unknown, key: ValueKey) => { + maxHeight.value = withTiming( + value ? 100 * (setting.dependents?.length ?? 0) : 0, + ); + opacity.value = withTiming(value ? 1 : 0, { + easing: Easing.out(Easing.exp), + reduceMotion: ReduceMotion.System, + }); + //@ts-ignore + up(value, key); + }; return ( - update(!currentValue, setting.valueKey)} - theme={theme} - style={{ paddingHorizontal: 16 }} - /> + <> + update(!currentValue, setting.valueKey)} + theme={theme} + style={{ paddingHorizontal: 16 }} + /> + {!setting.dependents ? null : ( + + {setting.dependents.map((dep, i) => { + return ( + + ); + })} + + )} + ); } diff --git a/src/screens/settings/components/SettingTextInput.tsx b/src/screens/settings/components/SettingTextInput.tsx new file mode 100644 index 000000000..31a7e22ae --- /dev/null +++ b/src/screens/settings/components/SettingTextInput.tsx @@ -0,0 +1,86 @@ +import { StyleSheet, TextInput, View, Text } from 'react-native'; +import { ThemeColors } from '@theme/types'; +import useUpdateSettingsFn from '../SettingsGeneralScreen/utils/useUpdateSettingsFn'; +import { SettingOrigin, SwitchSetting } from '../Settings'; +import { + useAppSettings, + useChapterGeneralSettings, + useChapterReaderSettings, + useLastUpdate, + useLibrarySettings, +} from '@hooks/persisted'; +import { useMemo } from 'react'; +import { defaultTo } from 'lodash-es'; + +interface SettingSwitchProps { + setting: SwitchSetting; + theme: ThemeColors; +} + +export default function SettingTextInput({ + setting, + theme, +}: SettingSwitchProps) { + const update = useUpdateSettingsFn(setting.settingOrigin)!; + const librarySettings = useLibrarySettings(); + const appSettings = useAppSettings(); + const { showLastUpdateTime } = useLastUpdate(); + const chapterSettings = useChapterGeneralSettings(); + const chapterReaderSettings = useChapterReaderSettings(); + + const currentValue = useMemo(() => { + let res; + if (setting.settingOrigin === 'Library') { + res = librarySettings[setting.valueKey]; + } else if (setting.settingOrigin === 'App') { + res = appSettings[setting.valueKey]; + } else if (setting.settingOrigin === 'lastUpdateTime') { + res = showLastUpdateTime; + } else if (setting.settingOrigin === 'GeneralChapter') { + res = chapterSettings[setting.valueKey]; + } else if (setting.settingOrigin === 'ReaderChapter') { + res = chapterReaderSettings[setting.valueKey]; + } + return (res ?? setting.defaultValue) as boolean; + }, [librarySettings, appSettings, showLastUpdateTime, chapterSettings]); + const labelStyle = [styles.fontSizeL, { color: theme.onSurface }]; + + return ( + + + {setting.title} + + { + if (text) { + //@ts-ignore + update(text, setting.valueKey); + } + }} + /> + + ); +} +const styles = StyleSheet.create({ + fontSizeL: { + fontSize: 16, + width: 50, + height: 46, + textAlignVertical: 'center', + }, + autoScrollInterval: { + paddingHorizontal: 16, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + //? overflow scroll because the animation looks better + overflow: 'scroll', + }, + paddingRightM: { + paddingVertical: 12, + flex: 1, + }, +}); diff --git a/src/screens/settings/settingsGroups/readerSettingsGroup.ts b/src/screens/settings/settingsGroups/readerSettingsGroup.ts new file mode 100644 index 000000000..ad4228f19 --- /dev/null +++ b/src/screens/settings/settingsGroups/readerSettingsGroup.ts @@ -0,0 +1,96 @@ +import { getString } from '@strings/translations'; +import { SettingsGroup } from '../Settings'; + +const ReaderSettings: SettingsGroup = { + groupTitle: getString('readerSettings.title'), + icon: 'book-open-outline', + navigateParam: 'ReaderSettings', + subGroup: [ + { + subGroupTitle: getString('generalSettings'), + settings: [ + { + title: getString('readerScreen.bottomSheet.verticalSeekbar'), + description: getString('readerSettings.verticalSeekbarDesc'), + type: 'Switch', + settingOrigin: 'GeneralChapter', + valueKey: 'verticalSeekbar', + defaultValue: true, + }, + { + title: getString('readerScreen.bottomSheet.volumeButtonsScroll'), + type: 'Switch', + settingOrigin: 'GeneralChapter', + valueKey: 'useVolumeButtons', + defaultValue: false, + }, + { + title: getString('readerScreen.bottomSheet.swipeGestures'), + type: 'Switch', + settingOrigin: 'GeneralChapter', + valueKey: 'swipeGestures', + defaultValue: false, + }, + { + title: getString('readerScreen.bottomSheet.bionicReading'), + type: 'Switch', + settingOrigin: 'GeneralChapter', + valueKey: 'bionicReading', + defaultValue: false, + }, + { + title: getString('readerScreen.bottomSheet.autoscroll'), + type: 'Switch', + settingOrigin: 'GeneralChapter', + valueKey: 'autoScroll', + defaultValue: false, + }, + ], + }, + { + subGroupTitle: getString('readerScreen.bottomSheet.autoscroll'), + settings: [ + { + title: getString('readerScreen.bottomSheet.autoscroll'), + type: 'Switch', + settingOrigin: 'GeneralChapter', + valueKey: 'autoScroll', + defaultValue: false, + dependents: [ + { + title: getString('readerSettings.autoScrollInterval'), + type: 'NumberInput', + settingOrigin: 'GeneralChapter', + valueKey: 'autoScrollInterval', + defaultValue: '10', + }, + { + title: getString('readerSettings.autoScrollOffset'), + type: 'NumberInput', + settingOrigin: 'GeneralChapter', + valueKey: 'autoScrollOffset', + defaultValue: '', + // defaultValue: defaultTo( + // autoScrollOffset, + // Math.round(screenHeight), + // ).toString(), + }, + ], + }, + ], + }, + { + subGroupTitle: getString('readerScreen.bottomSheet.autoscroll'), + settings: [ + { + title: getString('readerScreen.bottomSheet.autoscroll'), + type: 'Switch', + settingOrigin: 'GeneralChapter', + valueKey: 'autoScroll', + defaultValue: false, + }, + ], + }, + ], +}; +export default ReaderSettings; diff --git a/src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx b/src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx new file mode 100644 index 000000000..acc1b4576 --- /dev/null +++ b/src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx @@ -0,0 +1,300 @@ +import { View, StatusBar, Dimensions, Text } from 'react-native'; +import React, { useMemo, useRef, useState } from 'react'; + +import WebView from 'react-native-webview'; +import { dummyHTML } from '../SettingsReaderScreen/utils'; + +import { Appbar } from '@components/index'; + +import { + useChapterGeneralSettings, + useChapterReaderSettings, + useTheme, +} from '@hooks/persisted'; +import { getString } from '@strings/translations'; + +import color from 'color'; +import { useBatteryLevel } from 'react-native-device-info'; +import * as Speech from 'expo-speech'; +import * as Clipboard from 'expo-clipboard'; +import { showToast } from '@utils/showToast'; +import SettingsSubScreen from './SettingsSubScreen'; +import { StackScreenProps } from '@react-navigation/stack'; +import { SettingsStackParamList } from '@navigators/types'; + +export type TextAlignments = + | 'left' + | 'center' + | 'auto' + | 'right' + | 'justify' + | undefined; + +type WebViewPostEvent = { + type: string; + data?: { [key: string]: string | number }; +}; +type Props = StackScreenProps< + SettingsStackParamList, + keyof Omit +>; + +const ReaderSettingsSubScreen: React.FC = ({ navigation, route }) => { + const theme = useTheme(); + const webViewRef = useRef(null); + const novel = { + 'artist': null, + 'author': 'Kinugasa Shougo', + 'cover': + 'file:///storage/emulated/0/Android/data/com.rajarsheechatterjee.LNReader/files/Novels/lightnovelcave/16/cover.png?1717862123181', + 'genres': 'Drama,Slice of Life,Psychological,School Life,Shounen', + 'id': 16, + 'inLibrary': 1, + 'isLocal': 0, + 'name': 'Classroom of the Elite (LN)', + 'path': 'novel/classroom-of-the-elite-16091321', + 'pluginId': 'lightnovelcave', + 'status': 'Ongoing', + 'summary': + 'Kōdo Ikusei Senior High School, a leading prestigious school with state-of-the-art facilities where nearly 100% of students go on to university or find employment. The students there have the freedom to wear any hairstyle and bring any personal effects they desire. Kōdo Ikusei is a paradise-like school, but the truth is that only the most superior of students receive favorable treatment.The protagonist Kiyotaka Ayanokōji is a student of D-class, which is where the school dumps its “inferior” students in order to ridicule them. For a certain reason, Kiyotaka was careless on his entrance examination, and was put in D-class. After meeting Suzune Horikita and Kikyō Kushida, two other students in his class, Kiyotaka’s situation begins to change.Show More', + 'totalPages': 8, + }; + const chapter = { + 'bookmark': 0, + 'chapterNumber': 2.1, + 'id': 3722, + 'isDownloaded': 1, + 'name': 'Chapter V4C2.1 - A Vast Array of Thoughts Part 1', + 'novelId': 16, + 'page': '2', + 'path': 'novel/classroom-of-the-elite-547/vol-4-chapter-2-1', + 'position': 0, + 'progress': 3, + 'readTime': '2024-06-08 22:56:09', + 'releaseTime': '14 tháng 9 năm 2021', + 'unread': 1, + 'updatedTime': null, + }; + const [webViewHeight, setWebViewHeight] = useState(280); + const [hidden, setHidden] = useState(true); + const layoutHeight = Dimensions.get('window').height; + const batteryLevel = useBatteryLevel(); + const readerSettings = useChapterReaderSettings(); + const { + showScrollPercentage, + showBatteryAndTime, + verticalSeekbar, + bionicReading, + } = useChapterGeneralSettings(); + const assetsUriPrefix = useMemo( + () => (__DEV__ ? 'http://localhost:8081/assets' : 'file:///android_asset'), + [], + ); + const dummyChapterInfo = { + sourceId: 11, + chapterId: 99, + novelId: 20, + novelName: 'novel name', + chapterName: "chapter' name", + }; + const webViewCSS = ` + + + + `; + + const readerBackgroundColor = readerSettings.theme; + + return ( + <> + + + + { + const event: WebViewPostEvent = JSON.parse(ev.nativeEvent.data); + switch (event.type) { + case 'hide': + if (hidden) { + webViewRef.current?.injectJavaScript('toolWrapper.show()'); + } else { + webViewRef.current?.injectJavaScript('toolWrapper.hide()'); + } + setHidden(!hidden); + break; + case 'speak': + if (event.data && typeof event.data === 'string') { + Speech.speak(event.data, { + onDone() { + webViewRef.current?.injectJavaScript('tts.next?.()'); + }, + voice: readerSettings.tts?.voice?.identifier, + pitch: readerSettings.tts?.pitch || 1, + rate: readerSettings.tts?.rate || 1, + }); + } else { + webViewRef.current?.injectJavaScript('tts.next?.()'); + } + break; + case 'stop-speak': + Speech.stop(); + break; + case 'copy': + if (event.data && typeof event.data === 'string') { + Clipboard.setStringAsync(event.data).then(() => { + showToast( + getString('common.copiedToClipboard', { name: '' }), + ); + }); + } + break; + } + }} + source={{ + html: ` + + + + ${webViewCSS} + + + + + ${dummyHTML} + + +
+ +
+ + + + + + + `, + }} + /> +
+ + { + setWebViewHeight(e.nativeEvent.pageY - 10 - 84); + }} + > + ••• + + + + + + + ); +}; +export default ReaderSettingsSubScreen; diff --git a/src/screens/settings/settingsScreens/SettingsAppearanceScreenV2.tsx b/src/screens/settings/settingsScreens/SettingsAppearanceScreenV2.tsx deleted file mode 100644 index 854e43b29..000000000 --- a/src/screens/settings/settingsScreens/SettingsAppearanceScreenV2.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import { ScrollView } from 'react-native'; - -import { useTheme } from '@hooks/persisted'; -import { Appbar, List } from '@components'; -import { NavigationState } from '@react-navigation/native'; -import S from '../Settings'; -import RenderSettings from '../components/RenderSettings'; -import { SafeAreaView } from 'react-native-safe-area-context'; - -interface GenralSettingsProps { - navigation: NavigationState; -} - -const GenralSettings: React.FC = ({ navigation }) => { - const theme = useTheme(); - const Settings = S.appearance; - - return ( - - - - - {Settings.subGroup.map(RenderSettings)} - - - ); -}; - -export default GenralSettings; diff --git a/src/screens/settings/settingsScreens/SettingsSubScreen.tsx b/src/screens/settings/settingsScreens/SettingsSubScreen.tsx index a8a627204..ff8d307e8 100644 --- a/src/screens/settings/settingsScreens/SettingsSubScreen.tsx +++ b/src/screens/settings/settingsScreens/SettingsSubScreen.tsx @@ -4,32 +4,44 @@ import { ScrollView } from 'react-native'; import { useTheme } from '@hooks/persisted'; import { Appbar, List } from '@components'; import S from '../Settings'; -import RenderSettings from '../components/RenderSettings'; -import { SafeAreaView } from 'react-native-safe-area-context'; +import RenderSettings from '../components/RenderSettingsGroup'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { StackScreenProps } from '@react-navigation/stack'; import { SettingsStackParamList } from '@navigators/types'; type Props = StackScreenProps< SettingsStackParamList, keyof Omit ->; +> & { disableAppbar?: boolean }; -const SettingsSubScreen: React.FC = ({ navigation, route }) => { +const SettingsSubScreen: React.FC = ({ + navigation, + route, + disableAppbar, +}) => { const theme = useTheme(); const Settings = S[route.params.settingsSource]; + const insets = useSafeAreaInsets(); return ( - - + + {disableAppbar ? null : ( - - {Settings.subGroup.map(RenderSettings)} - - + )} + {Settings.subGroup.map(RenderSettings)} +
); }; From 08f46f68537a57e84c3bfe7074d84a1d6d6fe4a0 Mon Sep 17 00:00:00 2001 From: CD-Z Date: Tue, 25 Jun 2024 21:53:31 +0200 Subject: [PATCH 14/49] added TextArea settings --- src/screens/settings/Settings.ts | 27 +++- .../SettingsReaderScreen.tsx | 4 +- .../RenderSettings.tsx | 7 +- .../RenderSettingsGroup.tsx | 8 +- .../SettingSwitchV2.tsx | 1 - .../SettingTextInput.tsx | 6 +- .../modals/CustomFileModal.tsx | 145 ++++++++++++++++++ .../modals/DefaultSettingModal.tsx | 2 +- .../modals/TextAreaModal.tsx | 139 +++++++++++++++++ .../settingsGroups/readerSettingsGroup.ts | 39 +++-- .../ReaderSettingsSubScreen.tsx | 2 +- .../settingsScreens/SettingsSubScreen.tsx | 17 +- 12 files changed, 367 insertions(+), 30 deletions(-) rename src/screens/settings/{components => dynamicComponents}/RenderSettings.tsx (77%) rename src/screens/settings/{components => dynamicComponents}/RenderSettingsGroup.tsx (85%) rename src/screens/settings/{components => dynamicComponents}/SettingSwitchV2.tsx (98%) rename src/screens/settings/{components => dynamicComponents}/SettingTextInput.tsx (95%) create mode 100644 src/screens/settings/dynamicComponents/modals/CustomFileModal.tsx rename src/screens/settings/{SettingsGeneralScreen => dynamicComponents}/modals/DefaultSettingModal.tsx (97%) create mode 100644 src/screens/settings/dynamicComponents/modals/TextAreaModal.tsx diff --git a/src/screens/settings/Settings.ts b/src/screens/settings/Settings.ts index 76df316e9..aefbc29a5 100644 --- a/src/screens/settings/Settings.ts +++ b/src/screens/settings/Settings.ts @@ -126,6 +126,30 @@ export type NumberInputSetting = BaseNumberInputSetting & | NumberInputSettingsType<'lastUpdateTime'> | NumberInputSettingsType<'Library'> | NumberInputSettingsType<'GeneralChapter'> + | NumberInputSettingsType<'ReaderChapter'> + ); + +export type TextAreaSettingsType = { + settingOrigin: T; + valueKey: ValueKey; + defaultValue: string; +}; + +type BaseTextAreaSetting = { + title: string; + placeholder?: string; + description?: string; + openFileLabel: string; + clearDialog: string; + type: 'TextArea'; +}; +export type TextAreaSetting = BaseTextAreaSetting & + ( + | TextAreaSettingsType<'App'> + | TextAreaSettingsType<'lastUpdateTime'> + | TextAreaSettingsType<'Library'> + | TextAreaSettingsType<'GeneralChapter'> + | TextAreaSettingsType<'ReaderChapter'> ); export type ThemePickerSetting = { @@ -153,7 +177,8 @@ export type SettingsSubGroupSettings = | SwitchSetting | ThemePickerSetting | ColorPickerSetting - | NumberInputSetting; + | NumberInputSetting + | TextAreaSetting; export interface SettingSubGroup { subGroupTitle: string; diff --git a/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx b/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx index cf1b9657f..4c8cb2a1c 100644 --- a/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx +++ b/src/screens/settings/SettingsReaderScreen/SettingsReaderScreen.tsx @@ -38,9 +38,7 @@ type WebViewPostEvent = { data?: { [key: string]: string | number }; }; -const SettingsReaderScreen = r => { - console.log(JSON.stringify(r, null, 2)); - +const SettingsReaderScreen = () => { const theme = useTheme(); const navigation = useNavigation(); const webViewRef = useRef(null); diff --git a/src/screens/settings/components/RenderSettings.tsx b/src/screens/settings/dynamicComponents/RenderSettings.tsx similarity index 77% rename from src/screens/settings/components/RenderSettings.tsx rename to src/screens/settings/dynamicComponents/RenderSettings.tsx index 4c8abd793..88f32ab37 100644 --- a/src/screens/settings/components/RenderSettings.tsx +++ b/src/screens/settings/dynamicComponents/RenderSettings.tsx @@ -1,10 +1,11 @@ import { SettingsSubGroupSettings } from '../Settings'; -import DefaultSettingModal from '../SettingsGeneralScreen/modals/DefaultSettingModal'; import SettingSwitchV2 from './SettingSwitchV2'; import { useTheme } from '@hooks/persisted'; -import SettingsThemePicker from './SettingsThemePicker'; +import SettingsThemePicker from '../components/SettingsThemePicker'; import ColorPickerModal from '../SettingsGeneralScreen/modals/ColorPickerModal'; import SettingTextInput from './SettingTextInput'; +import DefaultSettingModal from './modals/DefaultSettingModal'; +import TextAreaModal from './modals/TextAreaModal'; export default function ({ setting }: { setting: SettingsSubGroupSettings }) { const theme = useTheme(); @@ -22,5 +23,7 @@ export default function ({ setting }: { setting: SettingsSubGroupSettings }) { return ( ); + case 'TextArea': + return ; } } diff --git a/src/screens/settings/components/RenderSettingsGroup.tsx b/src/screens/settings/dynamicComponents/RenderSettingsGroup.tsx similarity index 85% rename from src/screens/settings/components/RenderSettingsGroup.tsx rename to src/screens/settings/dynamicComponents/RenderSettingsGroup.tsx index 1001e3b71..ac48dd9e8 100644 --- a/src/screens/settings/components/RenderSettingsGroup.tsx +++ b/src/screens/settings/dynamicComponents/RenderSettingsGroup.tsx @@ -3,7 +3,13 @@ import { SettingSubGroup } from '../Settings'; import { useTheme } from '@hooks/persisted'; import RenderSettings from './RenderSettings'; -export default function (setting: SettingSubGroup, index: number) { +export default function ({ + setting, + index, +}: { + setting: SettingSubGroup; + index: number; +}) { const theme = useTheme(); return ( diff --git a/src/screens/settings/components/SettingSwitchV2.tsx b/src/screens/settings/dynamicComponents/SettingSwitchV2.tsx similarity index 98% rename from src/screens/settings/components/SettingSwitchV2.tsx rename to src/screens/settings/dynamicComponents/SettingSwitchV2.tsx index fc6695946..7bcd13df7 100644 --- a/src/screens/settings/components/SettingSwitchV2.tsx +++ b/src/screens/settings/dynamicComponents/SettingSwitchV2.tsx @@ -10,7 +10,6 @@ import { useLibrarySettings, } from '@hooks/persisted'; import { useMemo } from 'react'; -import { View } from 'react-native'; import RenderSettings from './RenderSettings'; import Animated, { Easing, diff --git a/src/screens/settings/components/SettingTextInput.tsx b/src/screens/settings/dynamicComponents/SettingTextInput.tsx similarity index 95% rename from src/screens/settings/components/SettingTextInput.tsx rename to src/screens/settings/dynamicComponents/SettingTextInput.tsx index 31a7e22ae..a33b9bfeb 100644 --- a/src/screens/settings/components/SettingTextInput.tsx +++ b/src/screens/settings/dynamicComponents/SettingTextInput.tsx @@ -1,7 +1,7 @@ import { StyleSheet, TextInput, View, Text } from 'react-native'; import { ThemeColors } from '@theme/types'; import useUpdateSettingsFn from '../SettingsGeneralScreen/utils/useUpdateSettingsFn'; -import { SettingOrigin, SwitchSetting } from '../Settings'; +import { NumberInputSetting, SettingOrigin, SwitchSetting } from '../Settings'; import { useAppSettings, useChapterGeneralSettings, @@ -13,7 +13,7 @@ import { useMemo } from 'react'; import { defaultTo } from 'lodash-es'; interface SettingSwitchProps { - setting: SwitchSetting; + setting: NumberInputSetting; theme: ThemeColors; } @@ -21,12 +21,12 @@ export default function SettingTextInput({ setting, theme, }: SettingSwitchProps) { - const update = useUpdateSettingsFn(setting.settingOrigin)!; const librarySettings = useLibrarySettings(); const appSettings = useAppSettings(); const { showLastUpdateTime } = useLastUpdate(); const chapterSettings = useChapterGeneralSettings(); const chapterReaderSettings = useChapterReaderSettings(); + const update = useUpdateSettingsFn(setting.settingOrigin)!; const currentValue = useMemo(() => { let res; diff --git a/src/screens/settings/dynamicComponents/modals/CustomFileModal.tsx b/src/screens/settings/dynamicComponents/modals/CustomFileModal.tsx new file mode 100644 index 000000000..307340cae --- /dev/null +++ b/src/screens/settings/dynamicComponents/modals/CustomFileModal.tsx @@ -0,0 +1,145 @@ +import React, { useState } from 'react'; +import { StyleSheet, Text, View } from 'react-native'; +import { Modal, overlay, TextInput } from 'react-native-paper'; +import { StorageAccessFramework } from 'expo-file-system'; +import * as DocumentPicker from 'expo-document-picker'; + +import { Button } from '@components/index'; + +import { showToast } from '@utils/showToast'; +import { useTheme } from '@hooks/persisted'; +import { getString } from '@strings/translations'; +import { useKeyboardHeight } from '@hooks/common/useKeyboardHeight'; + +interface CustomFileModal { + visible: boolean; + onDismiss: () => void; + defaultValue: string; + onSave: (val: string) => void; + title: string; + mimeType: 'text/css' | 'application/javascript'; + openFileLabel: string; + placeholder?: string; + description?: string; +} + +const CustomFileModal: React.FC = ({ + onDismiss, + visible, + defaultValue, + onSave, + title, + mimeType, + openFileLabel, + placeholder, + description, +}) => { + const theme = useTheme(); + const [text, setText] = useState(''); + const keyboardHeight = useKeyboardHeight(); + + const openDocumentPicker = async () => { + try { + const file = await DocumentPicker.getDocumentAsync({ + copyToCacheDirectory: false, + type: mimeType, + }); + + if (file.assets) { + const content = await StorageAccessFramework.readAsStringAsync( + file.assets[0].uri, + ); + + onSave(content.trim()); + onDismiss(); + } + } catch (error: any) { + showToast(error.message); + } + }; + + return ( + + + {title} + + {description} + + + - - - - -
- - ); -}; - -export default TrackerScreen; From 8b277fa8b54d922fbe41bdf8faa1f37c00965416 Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sun, 27 Oct 2024 15:32:52 +0100 Subject: [PATCH 39/49] fix custom js --- src/screens/reader/components/WebViewReader.tsx | 11 ++++++++++- .../settingsScreens/ReaderSettingsSubScreen.tsx | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/screens/reader/components/WebViewReader.tsx b/src/screens/reader/components/WebViewReader.tsx index 7928b02ec..ec22eee1d 100644 --- a/src/screens/reader/components/WebViewReader.tsx +++ b/src/screens/reader/components/WebViewReader.tsx @@ -248,7 +248,16 @@ const WebViewReader: React.FC = ({ `, diff --git a/src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx b/src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx index d7fbd0252..2e8692eb7 100644 --- a/src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx +++ b/src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx @@ -249,7 +249,7 @@ const ReaderSettingsSubScreen: React.FC = ({ navigation, route }) => { let sourceId =${dummyChapterInfo.sourceId}; let chapterId =${dummyChapterInfo.chapterId}; let novelId =${dummyChapterInfo.novelId}; - let html = document.getElementsByTagName("chapter")[0].innerHTML; + let html = document.getElementById("LNReader-chapter").innerHTML; ${readerSettings.customJS} } document.addEventListener("DOMContentLoaded", fn); From 987d10d2c1b34fb4e4f4a55831509a8ce3850302 Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sun, 27 Oct 2024 15:45:43 +0100 Subject: [PATCH 40/49] minimum height for reader settings --- .../settings/settingsScreens/ReaderSettingsSubScreen.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx b/src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx index 2e8692eb7..1a5c40e0d 100644 --- a/src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx +++ b/src/screens/settings/settingsScreens/ReaderSettingsSubScreen.tsx @@ -21,6 +21,7 @@ import { showToast } from '@utils/showToast'; import SettingsSubScreen from './SettingsSubScreen'; import { StackScreenProps } from '@react-navigation/stack'; import { SettingsStackParamList } from '@navigators/types'; +import { WINDOW_HEIGHT } from '@gorhom/bottom-sheet'; export type TextAlignments = | 'left' @@ -278,7 +279,10 @@ const ReaderSettingsSubScreen: React.FC = ({ navigation, route }) => { borderRadius: 10, }} onTouchMove={e => { - setWebViewHeight(e.nativeEvent.pageY - 10 - 84); + const newHeight = e.nativeEvent.pageY - 10 - 84; + if (newHeight < WINDOW_HEIGHT * 0.7) { + setWebViewHeight(newHeight); + } }} > ••• From b9a4a8ab74d7306955db97c5d895dbbf10aa9e37 Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sun, 27 Oct 2024 15:53:04 +0100 Subject: [PATCH 41/49] fix lint --- src/screens/settings/dynamic/modals/CustomFileModal.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/screens/settings/dynamic/modals/CustomFileModal.tsx b/src/screens/settings/dynamic/modals/CustomFileModal.tsx index d8fdf612b..e4750427b 100644 --- a/src/screens/settings/dynamic/modals/CustomFileModal.tsx +++ b/src/screens/settings/dynamic/modals/CustomFileModal.tsx @@ -1,12 +1,5 @@ import React, { useEffect, useRef, useState } from 'react'; -import { - Animated, - Pressable, - StyleSheet, - Text, - Touchable, - View, -} from 'react-native'; +import { Animated, Pressable, StyleSheet, Text, View } from 'react-native'; import { Modal, overlay, TextInput } from 'react-native-paper'; import { StorageAccessFramework } from 'expo-file-system'; import * as DocumentPicker from 'expo-document-picker'; From 6c98f4d9d1c29c1fd71d8d5282667b323dc8d212 Mon Sep 17 00:00:00 2001 From: CD-Z Date: Sun, 27 Oct 2024 16:38:51 +0100 Subject: [PATCH 42/49] make bigger --- src/screens/settings/dynamic/modals/CustomFileModal.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/screens/settings/dynamic/modals/CustomFileModal.tsx b/src/screens/settings/dynamic/modals/CustomFileModal.tsx index e4750427b..64d1ceaf7 100644 --- a/src/screens/settings/dynamic/modals/CustomFileModal.tsx +++ b/src/screens/settings/dynamic/modals/CustomFileModal.tsx @@ -40,8 +40,8 @@ const CustomFileModal: React.FC = ({ const keyboardHeight = useKeyboardHeight(); const modalAnim = useRef(new Animated.Value(30)).current; - const modalMB = useRef(new Animated.Value(WINDOW_HEIGHT * 0.25)).current; - const modalH = useRef(new Animated.Value(WINDOW_HEIGHT * 0.55)).current; + const modalMB = useRef(new Animated.Value(WINDOW_HEIGHT * 0.15 - 24)).current; + const modalH = useRef(new Animated.Value(WINDOW_HEIGHT * 0.7)).current; const modalBR = useRef(new Animated.Value(14)).current; const animate = (anim: Animated.Value, num: number) => { @@ -61,8 +61,8 @@ const CustomFileModal: React.FC = ({ } else { animate(modalAnim, 30); animate(modalBR, 14); - animate(modalMB, WINDOW_HEIGHT * 0.225); - animate(modalH, WINDOW_HEIGHT * 0.55); + animate(modalMB, WINDOW_HEIGHT * 0.15 - 24); + animate(modalH, WINDOW_HEIGHT * 0.7); } }, [keyboardHeight]); From fd370a4903a2b6339a06c11b91c4166afc951728 Mon Sep 17 00:00:00 2001 From: CD-Z Date: Tue, 29 Oct 2024 13:09:09 +0100 Subject: [PATCH 43/49] switched to react native reanimated animation --- .../dynamic/modals/CustomFileModal.tsx | 122 +++++++++++------- 1 file changed, 72 insertions(+), 50 deletions(-) diff --git a/src/screens/settings/dynamic/modals/CustomFileModal.tsx b/src/screens/settings/dynamic/modals/CustomFileModal.tsx index 64d1ceaf7..27e4841d3 100644 --- a/src/screens/settings/dynamic/modals/CustomFileModal.tsx +++ b/src/screens/settings/dynamic/modals/CustomFileModal.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { Animated, Pressable, StyleSheet, Text, View } from 'react-native'; +import React, { useEffect, useState } from 'react'; +import { Pressable, StyleSheet, Text } from 'react-native'; import { Modal, overlay, TextInput } from 'react-native-paper'; import { StorageAccessFramework } from 'expo-file-system'; import * as DocumentPicker from 'expo-document-picker'; @@ -11,6 +11,12 @@ import { useTheme } from '@hooks/persisted'; import { getString } from '@strings/translations'; import { useKeyboardHeight } from '@hooks/common/useKeyboardHeight'; import { WINDOW_HEIGHT } from '@gorhom/bottom-sheet'; +import Animated, { + useAnimatedStyle, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; interface CustomFileModal { visible: boolean; @@ -38,33 +44,7 @@ const CustomFileModal: React.FC = ({ const theme = useTheme(); const [text, setText] = useState(''); const keyboardHeight = useKeyboardHeight(); - - const modalAnim = useRef(new Animated.Value(30)).current; - const modalMB = useRef(new Animated.Value(WINDOW_HEIGHT * 0.15 - 24)).current; - const modalH = useRef(new Animated.Value(WINDOW_HEIGHT * 0.7)).current; - const modalBR = useRef(new Animated.Value(14)).current; - - const animate = (anim: Animated.Value, num: number) => { - Animated.timing(anim, { - toValue: num, - duration: 100, - useNativeDriver: false, - }).start(); - }; - - useEffect(() => { - if (keyboardHeight) { - animate(modalAnim, 0); - animate(modalBR, 0); - animate(modalMB, keyboardHeight + -12); - animate(modalH, WINDOW_HEIGHT - keyboardHeight - 72); - } else { - animate(modalAnim, 30); - animate(modalBR, 14); - animate(modalMB, WINDOW_HEIGHT * 0.15 - 24); - animate(modalH, WINDOW_HEIGHT * 0.7); - } - }, [keyboardHeight]); + const { top } = useSafeAreaInsets(); const openDocumentPicker = async () => { try { @@ -85,6 +65,50 @@ const CustomFileModal: React.FC = ({ showToast(error.message); } }; + const marginHorizontal = useSharedValue(30); + const marginBottom = useSharedValue(WINDOW_HEIGHT * 0.2 - 24); + const height = useSharedValue(WINDOW_HEIGHT * 0.6); + const borderRadius = useSharedValue(14); + const padding = useSharedValue(24); + + const maxHeight = useSharedValue(100); + + const buttonMargin = useSharedValue(16); + + useEffect(() => { + if (keyboardHeight) { + marginHorizontal.value = 0; + borderRadius.value = 0; + marginBottom.value = keyboardHeight - 4; + height.value = WINDOW_HEIGHT - keyboardHeight - top; + maxHeight.value = 0; + buttonMargin.value = 0; + padding.value = 12; + } else { + marginHorizontal.value = 30; + borderRadius.value = 14; + marginBottom.value = WINDOW_HEIGHT * 0.2 - 24; + height.value = WINDOW_HEIGHT * 0.6; + maxHeight.value = 100; + buttonMargin.value = 16; + padding.value = 24; + } + }, [keyboardHeight]); + + const duration = 150; + const animatedStyles = useAnimatedStyle(() => ({ + marginHorizontal: withTiming(marginHorizontal.value, { duration }), + marginBottom: withTiming(marginBottom.value, { duration: 75 }), + height: withTiming(height.value, { duration }), + borderRadius: withTiming(borderRadius.value, { duration }), + padding: withTiming(padding.value, { duration }), + })); + const hideView = useAnimatedStyle(() => ({ + maxHeight: withTiming(maxHeight.value, { duration }), + })); + const hideMargin = useAnimatedStyle(() => ({ + marginTop: withTiming(buttonMargin.value, { duration }), + })); return ( = ({ styles.modalContainer, { backgroundColor: overlay(2, theme.surface), - marginHorizontal: modalAnim, - marginBottom: modalMB, - height: modalH, - borderRadius: modalBR, }, + animatedStyles, ]} > - - {title} - - - {description} - + + + {title} + + + {description} + + = ({ style={[{ color: theme.onSurface }, styles.textInput]} theme={{ colors: { ...theme } }} /> - +