diff --git a/apps/readest-app/src/app/library/components/LibraryHeader.tsx b/apps/readest-app/src/app/library/components/LibraryHeader.tsx index 2eaa2f97..44a15a1b 100644 --- a/apps/readest-app/src/app/library/components/LibraryHeader.tsx +++ b/apps/readest-app/src/app/library/components/LibraryHeader.tsx @@ -50,6 +50,7 @@ const LibraryHeader: React.FC = ({ ref={headerRef} className={clsx( 'titlebar z-10 h-11 w-full py-2 pr-6', + appService?.hasSafeAreaInset && 'mt-[env(safe-area-inset-top)]', isTrafficLightVisible ? 'pl-16' : 'pl-2', )} > diff --git a/apps/readest-app/src/app/library/page.tsx b/apps/readest-app/src/app/library/page.tsx index 34949f0f..23bcf297 100644 --- a/apps/readest-app/src/app/library/page.tsx +++ b/apps/readest-app/src/app/library/page.tsx @@ -353,7 +353,7 @@ const LibraryPage = () => {
@@ -370,7 +370,13 @@ const LibraryPage = () => { )} {libraryLoaded && (libraryBooks.length > 0 ? ( -
+
= ({ bookKeys, onCloseBook }) => { + const { appService } = useEnv(); const { getConfig, getBookData } = useBookDataStore(); const { getProgress, getViewState, getViewSettings } = useReaderStore(); const { sideBarBookKey } = useSidebarStore(); @@ -39,7 +41,10 @@ const BooksGrid: React.FC = ({ bookKeys, onCloseBook }) => { return (
= ({ bookKeys, onCloseBook }) => {
{isBookmarked && } = ({ bookKey, pageinfo, isHoveredAnim }) => { const _ = useTranslation(); + const { appService } = useEnv(); const { hoveredBookKey, setHoveredBookKey, getView, getProgress } = useReaderStore(); const { isSideBarVisible } = useSidebarStore(); const view = getView(bookKey); @@ -62,9 +63,9 @@ const FooterBar: React.FC = ({ bookKey, pageinfo, isHoveredAnim className={clsx( 'footer-bar absolute bottom-0 z-10 flex h-12 w-full items-center gap-x-4 px-4', 'shadow-xs bg-base-100 transition-opacity duration-300', - isPWA() ? 'pb-[env(safe-area-inset-bottom)]' : '', - isTauriAppPlatform() && 'rounded-window-bottom-right', - !isSideBarVisible && isTauriAppPlatform() && 'rounded-window-bottom-left', + appService?.hasSafeAreaInset && 'pb-[env(safe-area-inset-bottom)]', + appService?.hasRoundedWindow && 'rounded-window-bottom-right', + !isSideBarVisible && appService?.hasRoundedWindow && 'rounded-window-bottom-left', isHoveredAnim && 'hover-bar-anim', hoveredBookKey === bookKey ? `opacity-100` : `opacity-0`, )} diff --git a/apps/readest-app/src/app/reader/components/HeaderBar.tsx b/apps/readest-app/src/app/reader/components/HeaderBar.tsx index 7ecd1fad..a7c855ab 100644 --- a/apps/readest-app/src/app/reader/components/HeaderBar.tsx +++ b/apps/readest-app/src/app/reader/components/HeaderBar.tsx @@ -5,7 +5,6 @@ import { PiDotsThreeVerticalBold } from 'react-icons/pi'; import { useEnv } from '@/context/EnvContext'; import { useReaderStore } from '@/store/readerStore'; import { useSidebarStore } from '@/store/sidebarStore'; -import { isTauriAppPlatform } from '@/services/environment'; import { useResponsiveSize } from '@/hooks/useResponsiveSize'; import useTrafficLight from '@/hooks/useTrafficLight'; import WindowButtons from '@/components/WindowButtons'; @@ -53,8 +52,8 @@ const HeaderBar: React.FC = ({ `header-bar absolute top-0 z-10 flex h-11 w-full items-center pr-4`, isTrafficLightVisible && isTopLeft && !isSideBarVisible ? 'pl-16' : 'pl-4', `shadow-xs bg-base-100 transition-opacity duration-300`, - isTauriAppPlatform() && 'rounded-window-top-right', - !isSideBarVisible && isTauriAppPlatform() && 'rounded-window-top-left', + appService?.hasRoundedWindow && 'rounded-window-top-right', + !isSideBarVisible && appService?.hasRoundedWindow && 'rounded-window-top-left', isHoveredAnim && 'hover-bar-anim', hoveredBookKey === bookKey || isDropdownOpen ? `visible` : `opacity-0`, isDropdownOpen && 'header-bar-pinned', diff --git a/apps/readest-app/src/app/reader/components/Reader.tsx b/apps/readest-app/src/app/reader/components/Reader.tsx index e2ac88d4..2ee238eb 100644 --- a/apps/readest-app/src/app/reader/components/Reader.tsx +++ b/apps/readest-app/src/app/reader/components/Reader.tsx @@ -8,13 +8,12 @@ import { useEnv } from '@/context/EnvContext'; import { useTheme } from '@/hooks/useTheme'; import { useLibraryStore } from '@/store/libraryStore'; import { useSettingsStore } from '@/store/settingsStore'; -import { isTauriAppPlatform } from '@/services/environment'; import { AboutWindow } from '@/components/AboutWindow'; import { Toast } from '@/components/Toast'; import ReaderContent from './ReaderContent'; const Reader: React.FC<{ ids?: string }> = ({ ids }) => { - const { envConfig } = useEnv(); + const { envConfig, appService } = useEnv(); const { settings, setSettings } = useSettingsStore(); const { library, setLibrary } = useLibraryStore(); const isInitiating = useRef(false); @@ -42,7 +41,7 @@ const Reader: React.FC<{ ids?: string }> = ({ ids }) => {
diff --git a/apps/readest-app/src/app/reader/components/notebook/Notebook.tsx b/apps/readest-app/src/app/reader/components/notebook/Notebook.tsx index b333039e..c70d3cbc 100644 --- a/apps/readest-app/src/app/reader/components/notebook/Notebook.tsx +++ b/apps/readest-app/src/app/reader/components/notebook/Notebook.tsx @@ -13,7 +13,6 @@ import { TextSelection } from '@/utils/sel'; import { BookNote } from '@/types/book'; import { uniqueId } from '@/utils/misc'; import { eventDispatcher } from '@/utils/event'; -import { isTauriAppPlatform } from '@/services/environment'; import useDragBar from '../../hooks/useDragBar'; import BooknoteItem from '../sidebar/BooknoteItem'; import NotebookHeader from './Header'; @@ -25,7 +24,7 @@ const MAX_NOTEBOOK_WIDTH = 0.45; const Notebook: React.FC = ({}) => { const _ = useTranslation(); const { updateAppTheme } = useTheme(); - const { envConfig } = useEnv(); + const { envConfig, appService } = useEnv(); const { settings } = useSettingsStore(); const { sideBarBookKey } = useSidebarStore(); const { notebookWidth, isNotebookVisible, isNotebookPinned } = useNotebookStore(); @@ -152,7 +151,7 @@ const Notebook: React.FC = ({}) => {
= ({ bookDoc, sideBarBookKey }) => { + const { appService } = useEnv(); const scrollContainerRef = useRef(null); const { getConfig, setConfig } = useBookDataStore(); const config = getConfig(sideBarBookKey); @@ -92,7 +93,10 @@ const SidebarContent: React.FC<{
diff --git a/apps/readest-app/src/app/reader/components/sidebar/SideBar.tsx b/apps/readest-app/src/app/reader/components/sidebar/SideBar.tsx index cabe2360..be441474 100644 --- a/apps/readest-app/src/app/reader/components/sidebar/SideBar.tsx +++ b/apps/readest-app/src/app/reader/components/sidebar/SideBar.tsx @@ -7,7 +7,7 @@ import { useReaderStore } from '@/store/readerStore'; import { useSidebarStore } from '@/store/sidebarStore'; import { BookSearchResult } from '@/types/book'; import { eventDispatcher } from '@/utils/event'; -import { isTauriAppPlatform } from '@/services/environment'; +import { useEnv } from '@/context/EnvContext'; import { useTheme } from '@/hooks/useTheme'; import SidebarHeader from './Header'; import SidebarContent from './Content'; @@ -24,6 +24,7 @@ const MAX_SIDEBAR_WIDTH = 0.45; const SideBar: React.FC<{ onGoToLibrary: () => void; }> = ({ onGoToLibrary }) => { + const { appService } = useEnv(); const { updateAppTheme } = useTheme(); const { settings } = useSettingsStore(); const { sideBarBookKey } = useSidebarStore(); @@ -118,7 +119,8 @@ const SideBar: React.FC<{
{ const _ = useTranslation(); + const { appService } = useEnv(); const { getBookData } = useBookDataStore(); const { getView, getViewSettings } = useReaderStore(); const [bookKey, setBookKey] = useState(''); @@ -274,7 +275,9 @@ const TTSControl = () => { ref={iconRef} className={clsx( 'absolute right-6 h-12 w-12', - isPWA() ? 'bottom-[calc(env(safe-area-inset-bottom)+48px)]' : 'bottom-12', + appService?.hasSafeAreaInset + ? 'bottom-[calc(env(safe-area-inset-bottom)+48px)]' + : 'bottom-12', )} > diff --git a/apps/readest-app/src/services/appService.ts b/apps/readest-app/src/services/appService.ts index 08767924..27318fcb 100644 --- a/apps/readest-app/src/services/appService.ts +++ b/apps/readest-app/src/services/appService.ts @@ -37,14 +37,19 @@ import { downloadFile, uploadFile, deleteFile, createProgressHandler } from '@/l import { ProgressHandler } from '@/utils/transfer'; export abstract class BaseAppService implements AppService { + osPlatform: string = getOSPlatform(); isMobile: boolean = ['android', 'ios'].includes(getOSPlatform()); localBooksDir: string = ''; abstract fs: FileSystem; abstract appPlatform: AppPlatform; abstract isAppDataSandbox: boolean; + abstract isAndroidApp: boolean; + abstract isIOSApp: boolean; abstract hasTrafficLight: boolean; abstract hasWindowBar: boolean; abstract hasContextMenu: boolean; + abstract hasRoundedWindow: boolean; + abstract hasSafeAreaInset: boolean; abstract resolvePath(fp: string, base: BaseDir): { baseDir: number; base: BaseDir; fp: string }; abstract getCoverImageUrl(book: Book): string; diff --git a/apps/readest-app/src/services/nativeAppService.ts b/apps/readest-app/src/services/nativeAppService.ts index d1e804fe..b0c014f4 100644 --- a/apps/readest-app/src/services/nativeAppService.ts +++ b/apps/readest-app/src/services/nativeAppService.ts @@ -22,8 +22,9 @@ import { isValidURL } from '@/utils/misc'; import { BaseAppService } from './appService'; import { LOCAL_BOOKS_SUBDIR } from './constants'; +import { isPWA } from './environment'; -export const isMobile = ['android', 'ios'].includes(osType()); +const OS_TYPE = osType(); const resolvePath = (fp: string, base: BaseDir): { baseDir: number; base: BaseDir; fp: string } => { switch (base) { @@ -117,10 +118,14 @@ export const nativeFileSystem: FileSystem = { export class NativeAppService extends BaseAppService { fs = nativeFileSystem; appPlatform = 'tauri' as AppPlatform; - isAppDataSandbox = isMobile; - hasTrafficLight = osType() === 'macos'; - hasWindowBar = !(osType() === 'ios' || osType() === 'android'); - hasContextMenu = !(osType() === 'ios' || osType() === 'android'); + isAppDataSandbox = ['android', 'ios'].includes(OS_TYPE); + isAndroidApp = OS_TYPE === 'android'; + isIOSApp = OS_TYPE === 'ios'; + hasTrafficLight = OS_TYPE === 'macos'; + hasWindowBar = !(OS_TYPE === 'ios' || OS_TYPE === 'android'); + hasContextMenu = !(OS_TYPE === 'ios' || OS_TYPE === 'android'); + hasRoundedWindow = !(OS_TYPE === 'ios' || OS_TYPE === 'android'); + hasSafeAreaInset = OS_TYPE === 'ios' || OS_TYPE === 'android' || isPWA(); override resolvePath(fp: string, base: BaseDir): { baseDir: number; base: BaseDir; fp: string } { return resolvePath(fp, base); diff --git a/apps/readest-app/src/services/webAppService.ts b/apps/readest-app/src/services/webAppService.ts index d16568af..70788c1b 100644 --- a/apps/readest-app/src/services/webAppService.ts +++ b/apps/readest-app/src/services/webAppService.ts @@ -179,9 +179,13 @@ export class WebAppService extends BaseAppService { fs = indexedDBFileSystem; appPlatform = 'web' as AppPlatform; isAppDataSandbox = false; + isAndroidApp = false; + isIOSApp = false; hasTrafficLight = false; hasWindowBar = false; hasContextMenu = false; + hasRoundedWindow = false; + hasSafeAreaInset = false; override resolvePath(fp: string, base: BaseDir): { baseDir: number; base: BaseDir; fp: string } { return resolvePath(fp, base); diff --git a/apps/readest-app/src/types/system.ts b/apps/readest-app/src/types/system.ts index 45b9ba8f..f887e14f 100644 --- a/apps/readest-app/src/types/system.ts +++ b/apps/readest-app/src/types/system.ts @@ -22,12 +22,17 @@ export interface FileSystem { export interface AppService { fs: FileSystem; + osPlatform: string; appPlatform: AppPlatform; hasTrafficLight: boolean; hasWindowBar: boolean; hasContextMenu: boolean; + hasRoundedWindow: boolean; + hasSafeAreaInset: boolean; isMobile: boolean; isAppDataSandbox: boolean; + isAndroidApp: boolean; + isIOSApp: boolean; selectFiles(name: string, extensions: string[]): Promise; showMessage(msg: string, kind?: ToastType, title?: string, okLabel?: string): Promise;