forked from akai/readest
Various enhancements for mobile platforms (#188)
* Larger area to toggle header/footer bar * Don't show rounded window on web platform * Disable tooltips on mobile platforms as there is no mouse pointer * Dedupe voices from web speech API
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import * as React from 'react';
|
||||
import { useState, useRef, useEffect, Suspense } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
@@ -233,7 +234,12 @@ const LibraryPage = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='library-page rounded-window bg-base-200/50 text-base-content flex h-dvh select-none flex-col overflow-hidden'>
|
||||
<div
|
||||
className={clsx(
|
||||
'library-page bg-base-200/50 text-base-content flex h-dvh select-none flex-col overflow-hidden',
|
||||
isTauriAppPlatform() && 'rounded-window',
|
||||
)}
|
||||
>
|
||||
<div className='fixed top-0 z-40 w-full'>
|
||||
<LibraryHeader
|
||||
isSelectMode={isSelectMode}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useSidebarStore } from '@/store/sidebarStore';
|
||||
import { isTauriAppPlatform } from '@/services/environment';
|
||||
import FoliateViewer from './FoliateViewer';
|
||||
import getGridTemplate from '@/utils/grid';
|
||||
import SectionInfo from './SectionInfo';
|
||||
@@ -59,7 +60,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
<div
|
||||
id={`gridcell-${bookKey}`}
|
||||
key={bookKey}
|
||||
className='rounded-window relative h-full w-full overflow-hidden'
|
||||
className={`${isTauriAppPlatform() ? 'rounded-window' : ''} relative h-full w-full overflow-hidden`}
|
||||
>
|
||||
{isBookmarked && <Ribbon width={marginGap} />}
|
||||
<HeaderBar
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useReaderStore } from '@/store/readerStore';
|
||||
import { useSidebarStore } from '@/store/sidebarStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { isTauriAppPlatform } from '@/services/environment';
|
||||
import Button from '@/components/Button';
|
||||
|
||||
interface FooterBarProps {
|
||||
@@ -60,8 +61,9 @@ const FooterBar: React.FC<FooterBarProps> = ({ bookKey, pageinfo, isHoveredAnim
|
||||
<div
|
||||
className={clsx(
|
||||
'footer-bar absolute bottom-0 z-10 flex h-12 w-full items-center px-4',
|
||||
'shadow-xs bg-base-100 rounded-window-bottom-right transition-opacity duration-300',
|
||||
!isSideBarVisible && 'rounded-window-bottom-left',
|
||||
'shadow-xs bg-base-100 transition-opacity duration-300',
|
||||
isTauriAppPlatform() && 'rounded-window-bottom-right',
|
||||
!isSideBarVisible && isTauriAppPlatform() && 'rounded-window-bottom-left',
|
||||
isHoveredAnim && 'hover-bar-anim',
|
||||
hoveredBookKey === bookKey ? `opacity-100` : `opacity-0`,
|
||||
)}
|
||||
|
||||
@@ -5,6 +5,7 @@ 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 useTrafficLight from '@/hooks/useTrafficLight';
|
||||
import WindowButtons from '@/components/WindowButtons';
|
||||
import Dropdown from '@/components/Dropdown';
|
||||
@@ -49,8 +50,9 @@ const HeaderBar: React.FC<HeaderBarProps> = ({
|
||||
className={clsx(
|
||||
`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 rounded-window-top-right transition-opacity duration-300`,
|
||||
!isSideBarVisible && 'rounded-window-top-left',
|
||||
`shadow-xs bg-base-100 transition-opacity duration-300`,
|
||||
isTauriAppPlatform() && 'rounded-window-top-right',
|
||||
!isSideBarVisible && isTauriAppPlatform() && 'rounded-window-top-left',
|
||||
isHoveredAnim && 'hover-bar-anim',
|
||||
hoveredBookKey === bookKey || isDropdownOpen ? `visible` : `opacity-0`,
|
||||
isDropdownOpen && 'header-bar-pinned',
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import * as React from 'react';
|
||||
import { useEffect, Suspense, useRef } from 'react';
|
||||
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
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';
|
||||
@@ -33,7 +35,12 @@ const Reader: React.FC<{ ids?: string }> = ({ ids }) => {
|
||||
return (
|
||||
library.length > 0 &&
|
||||
settings.globalReadSettings && (
|
||||
<div className='reader-page rounded-window bg-base-100 text-base-content select-none'>
|
||||
<div
|
||||
className={clsx(
|
||||
`reader-page bg-base-100 text-base-content select-none`,
|
||||
isTauriAppPlatform() && 'rounded-window',
|
||||
)}
|
||||
>
|
||||
<Suspense>
|
||||
<ReaderContent ids={ids} settings={settings} />
|
||||
<AboutWindow />
|
||||
|
||||
@@ -15,7 +15,7 @@ const PopupButton: React.FC<PopupButtonProps> = ({ showTooltip, tooltipText, Ico
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className='tooltip tooltip-bottom'
|
||||
className='lg:tooltip lg:tooltip-bottom'
|
||||
data-tip={!buttonClicked && showTooltip ? tooltipText : null}
|
||||
>
|
||||
<button
|
||||
|
||||
@@ -12,6 +12,7 @@ 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';
|
||||
@@ -140,7 +141,7 @@ const Notebook: React.FC = ({}) => {
|
||||
<div
|
||||
className={clsx(
|
||||
'notebook-container bg-base-200 right-0 z-20 h-full min-w-60 select-none',
|
||||
'rounded-window-top-right rounded-window-bottom-right',
|
||||
isTauriAppPlatform() && 'rounded-window-top-right rounded-window-bottom-right',
|
||||
!isNotebookPinned && 'shadow-2xl',
|
||||
)}
|
||||
style={{
|
||||
|
||||
@@ -28,7 +28,7 @@ const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
<div className='flex items-center justify-between'>
|
||||
<h2 className='font-medium'>{_('Theme Mode')}</h2>
|
||||
<div className='flex gap-2'>
|
||||
<div className='tooltip tooltip-bottom' data-tip={_('Auto Mode')}>
|
||||
<div className='lg:tooltip lg:tooltip-bottom' data-tip={_('Auto Mode')}>
|
||||
<button
|
||||
className={`btn btn-ghost btn-circle ${themeMode === 'auto' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => updateThemeMode('auto')}
|
||||
@@ -37,7 +37,7 @@ const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='tooltip tooltip-bottom' data-tip={_('Light Mode')}>
|
||||
<div className='lg:tooltip lg:tooltip-bottom' data-tip={_('Light Mode')}>
|
||||
<button
|
||||
className={`btn btn-ghost btn-circle ${themeMode === 'light' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => updateThemeMode('light')}
|
||||
@@ -46,7 +46,7 @@ const ColorPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='tooltip tooltip-bottom' data-tip={_('Dark Mode')}>
|
||||
<div className='lg:tooltip lg:tooltip-bottom' data-tip={_('Dark Mode')}>
|
||||
<button
|
||||
className={`btn btn-ghost btn-circle ${themeMode === 'dark' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => updateThemeMode('dark')}
|
||||
|
||||
@@ -29,7 +29,7 @@ const DialogMenu: React.FC<DialogMenuProps> = ({ toggleDropdown }) => {
|
||||
<span style={{ minWidth: '20px' }}>
|
||||
{isFontLayoutSettingsGlobal && <MdCheck size={20} className='text-base-content' />}
|
||||
</span>
|
||||
<div className='tooltip' data-tip='Uncheck for current book settings'>
|
||||
<div className='lg:tooltip' data-tip={_('Uncheck for current book settings')}>
|
||||
<span className='ml-2'>{_('Global Settings')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -148,7 +148,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
<div className='flex items-center justify-between'>
|
||||
<h2 className='font-medium'>{_('Writing Mode')}</h2>
|
||||
<div className='flex gap-2'>
|
||||
<div className='tooltip tooltip-bottom' data-tip={_('Default')}>
|
||||
<div className='lg:tooltip lg:tooltip-bottom' data-tip={_('Default')}>
|
||||
<button
|
||||
className={`btn btn-ghost btn-circle ${writingMode === 'auto' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => setWritingMode('auto')}
|
||||
@@ -157,7 +157,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='tooltip tooltip-bottom' data-tip={_('Horizontal Direction')}>
|
||||
<div className='lg:tooltip lg:tooltip-bottom' data-tip={_('Horizontal Direction')}>
|
||||
<button
|
||||
className={`btn btn-ghost btn-circle ${writingMode === 'horizontal-tb' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => setWritingMode('horizontal-tb')}
|
||||
@@ -166,7 +166,7 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='tooltip tooltip-bottom' data-tip={_('Vertical Direction')}>
|
||||
<div className='lg:tooltip lg:tooltip-bottom' data-tip={_('Vertical Direction')}>
|
||||
<button
|
||||
className={`btn btn-ghost btn-circle ${writingMode === 'vertical-rl' ? 'btn-active bg-base-300' : ''}`}
|
||||
onClick={() => setWritingMode('vertical-rl')}
|
||||
|
||||
@@ -7,6 +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 SidebarHeader from './Header';
|
||||
import SidebarContent from './Content';
|
||||
import BookCard from './BookCard';
|
||||
@@ -105,8 +106,8 @@ const SideBar: React.FC<{
|
||||
<>
|
||||
<div
|
||||
className={clsx(
|
||||
'sidebar-container bg-base-200 z-20 flex h-full min-w-60 flex-col',
|
||||
'rounded-window-top-left rounded-window-bottom-left select-none',
|
||||
'sidebar-container bg-base-200 z-20 flex h-full min-w-60 select-none flex-col',
|
||||
isTauriAppPlatform() && 'rounded-window-top-left rounded-window-bottom-left',
|
||||
!isSideBarPinned && 'shadow-2xl',
|
||||
)}
|
||||
style={{
|
||||
|
||||
@@ -26,7 +26,7 @@ const TabNavigation: React.FC<{
|
||||
{tabs.map((tab) => (
|
||||
<div
|
||||
key={tab}
|
||||
className='tooltip tooltip-top z-50 m-1.5 flex-1 cursor-pointer rounded-md p-2'
|
||||
className='lg:tooltip lg:tooltip-top z-50 m-1.5 flex-1 cursor-pointer rounded-md p-2'
|
||||
data-tip={
|
||||
tab === 'toc'
|
||||
? _('Table of Contents')
|
||||
|
||||
@@ -29,14 +29,7 @@ export const useClickEvent = (
|
||||
if (!consumed) {
|
||||
const centerStartX = rect.left + rect.width * 0.375;
|
||||
const centerEndX = rect.left + rect.width * 0.625;
|
||||
const centerStartY = rect.top + rect.height * 0.375;
|
||||
const centerEndY = rect.top + rect.height * 0.625;
|
||||
if (
|
||||
screenX >= centerStartX &&
|
||||
screenX <= centerEndX &&
|
||||
screenY >= centerStartY &&
|
||||
screenY <= centerEndY
|
||||
) {
|
||||
if (screenX >= centerStartX && screenX <= centerEndX) {
|
||||
// toggle visibility of the header bar and the footer bar
|
||||
setHoveredBookKey(hoveredBookKey ? '' : bookKey);
|
||||
} else if (screenX >= rect.left + rect.width / 2) {
|
||||
|
||||
@@ -20,9 +20,13 @@ const Button: React.FC<ButtonProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={clsx('tooltip z-50 h-8 min-h-8 w-8', tooltip && `tooltip-${tooltipDirection}`, {
|
||||
'tooltip-hidden': !tooltip,
|
||||
})}
|
||||
className={clsx(
|
||||
'lg:tooltip z-50 h-8 min-h-8 w-8',
|
||||
tooltip && `lg:tooltip-${tooltipDirection}`,
|
||||
{
|
||||
'tooltip-hidden': !tooltip,
|
||||
},
|
||||
)}
|
||||
data-tip={tooltip}
|
||||
>
|
||||
<button
|
||||
|
||||
@@ -297,9 +297,23 @@ export class WebSpeechClient implements TTSClient {
|
||||
.filter((voice) => voice.lang.startsWith(locale))
|
||||
.filter((voice) => isValidVoice(voice.voiceURI || ''))
|
||||
.filter(isNotBlacklisted);
|
||||
const voices = filteredVoices.map((voice) => {
|
||||
return { id: voice.voiceURI, name: voice.name, lang: voice.lang } as TTSVoice;
|
||||
});
|
||||
const seenIds = new Set<string>();
|
||||
const voices = filteredVoices
|
||||
.map(
|
||||
(voice) =>
|
||||
({
|
||||
id: voice.voiceURI,
|
||||
name: voice.name,
|
||||
lang: voice.lang,
|
||||
}) as TTSVoice,
|
||||
)
|
||||
.filter((voice) => {
|
||||
if (seenIds.has(voice.id)) {
|
||||
return false;
|
||||
}
|
||||
seenIds.add(voice.id);
|
||||
return true;
|
||||
});
|
||||
voices.forEach((voice) => {
|
||||
voice.disabled = !this.available;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user