From db478130e6e182b4e47dd7a88d0aba877be0422d Mon Sep 17 00:00:00 2001 From: nyagami Date: Fri, 8 Mar 2024 18:51:39 +0700 Subject: [PATCH] Fix Filter Sheet color & Reader Context menu (#991) * fix: initialize once * fix: filter sheet color * fix: jump to chapter modal * move volume button to context menu * rework TTS * fix: cleanup * minor fix --- App.tsx | 19 +- android/app/src/main/assets/css/index.css | 155 ++++++++-- android/app/src/main/assets/js/index.js | 281 +++++++++++++++--- android/app/src/main/assets/js/type.d.ts | 62 ++++ src/database/db.ts | 12 +- src/navigators/Main.tsx | 4 +- .../components/FilterBottomSheet.tsx | 11 +- .../novel/components/JumpToChapterModal.tsx | 17 +- .../reader/components/WebViewReader.tsx | 30 +- 9 files changed, 471 insertions(+), 120 deletions(-) create mode 100644 android/app/src/main/assets/js/type.d.ts diff --git a/App.tsx b/App.tsx index f5b7ce4e2..8b066de26 100644 --- a/App.tsx +++ b/App.tsx @@ -3,7 +3,7 @@ import { enableFreeze } from 'react-native-screens'; enableFreeze(true); -import React, { useEffect } from 'react'; +import React from 'react'; import { StatusBar } from 'react-native'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import LottieSplashScreen from 'react-native-lottie-splash-screen'; @@ -18,7 +18,6 @@ import { deserializePlugins } from '@plugins/pluginManager'; import Main from './src/navigators/Main'; import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; -import { usePlugins } from '@hooks/persisted'; import { MMKVStorage } from '@utils/mmkv/mmkv'; import { BACKGROUND_ACTION } from '@services/constants'; @@ -32,17 +31,13 @@ Notifications.setNotificationHandler({ }, }); -const App = () => { - const { refreshPlugins } = usePlugins(); - useEffect(() => { - createTables(); - deserializePlugins().then(() => LottieSplashScreen.hide()); - refreshPlugins(); - if (!BackgroundService.isRunning()) { - MMKVStorage.delete(BACKGROUND_ACTION); - } - }, []); +createTables(); +deserializePlugins().then(() => LottieSplashScreen.hide()); +if (!BackgroundService.isRunning()) { + MMKVStorage.delete(BACKGROUND_ACTION); +} +const App = () => { return ( diff --git a/android/app/src/main/assets/css/index.css b/android/app/src/main/assets/css/index.css index f24c21279..a028dec92 100644 --- a/android/app/src/main/assets/css/index.css +++ b/android/app/src/main/assets/css/index.css @@ -9,6 +9,7 @@ html { color: var(--theme-onSecondary); background-color: var(--theme-secondary); } + ::-moz-selection { color: var(--theme-onSecondary); background-color: var(--theme-secondary); @@ -57,6 +58,7 @@ img { font-family: var(--readerSettings-fontFamily); font-size: 16px; border-width: 0; + user-select: none; } .nextButton { @@ -86,24 +88,40 @@ img { display: none; } -.hidden { - visibility: hidden; - width: 0; -} - #ToolWrapper { position: fixed; top: 25vh; right: 5vw; + transition: 150ms; +} + +#ToolWrapper.hidden { + opacity: 0; + right: 0; +} + +#TTS-Controller { + display: block; +} + +#TTS-Controller button{ + background: var(--theme-surface-0-9); + border: 0; +} + +#TTS-Controller svg { + fill: var(--theme-onSurface); } #ScrollBar { + margin-top: 8px; width: 2.4rem; height: 45vh; border-radius: 1.2rem; background-color: var(--theme-surface-0-9); touch-action: none; font-size: 16px; + user-select: none; } .scrollbar-item { @@ -132,7 +150,6 @@ img { width: 0.1rem; height: 0; background-color: var(--theme-primary); - transform: translateX(-0.1rem); } #scrollbar-thumb-wrapper { @@ -171,45 +188,119 @@ img { color: var(--readerSettings-textColor); font-size: 1rem; text-align: center; + user-select: none; } -.reader-footer-item{ +.reader-footer-item { display: flex; } -.tts { - position: relative; - bottom: 1rem; +tts.highlight { + color: var(--theme-onSecondary); + background-color: var(--theme-secondary); } -.tts > svg { - fill: var(--theme-secondary); - -webkit-transition: 0.3s; - transition: 0.3s; +.contextMenu { + position: absolute; + height: 0; + overflow: hidden; + background: var(--theme-surface-0-9); + -webkit-backdrop-filter: blur(1px); + backdrop-filter: blur(1px); + position: fixed; + top: var(--top); + left: var(--left); + -webkit-animation: menuAnimation 0.4s 0s both; + animation: menuAnimation 0.4s 0s both; + transform-origin: left; + list-style: none; + margin: 4px; + padding: 0; + display: flex; + flex-direction: column; + z-index: 999999999; } -.tts::before{ - position: absolute; - content: ''; - height: 40px; - width: 2px; - background-color: var(--theme-secondary);; - transform-origin: top right; - transform: translate(4px, 4px) rotate(-45deg); - -webkit-transition: 0.3s; - transition: 0.3s; +.contextMenu-item { + padding: 4px, } -.tts.speak::before{ - height: 0; - background-color: var(--theme-primary); +.contextMenu-button { + color: var(--theme-onSurface); + background: 0; + border: 0; + white-space: nowrap; + width: 100%; + border-radius: 4px; + padding: 6px 24px 6px 7px; + text-align: left; + display: flex; + align-items: center; + font-size: 14px; + width: 100%; + -webkit-animation: menuItemAnimation 0.2s 0s both; + animation: menuItemAnimation 0.2s 0s both; + font-family: var(--readerSettings-fontFamily); + cursor: pointer; + user-select: none; + -webkit-tap-highlight-color: var(--theme-rippleColor); } -.tts.speak > svg{ - fill: var(--theme-primary); +.contextMenu-button svg { + fill: var(--theme-onSurface); +} +.contextMenu-button span { + margin-left: 4px; } -tts.highlight { - color: var(--theme-onSecondary); - background-color: var(--theme-secondary); +@-webkit-keyframes menuAnimation { + 0% { + opacity: 0; + transform: scale(0.5); + } + + 100% { + height: var(--height); + opacity: 1; + border-radius: 8px; + transform: scale(1); + } +} + +@keyframes menuAnimation { + 0% { + opacity: 0; + transform: scale(0.5); + } + + 100% { + height: var(--height); + opacity: 1; + border-radius: 8px; + transform: scale(1); + } +} + +@-webkit-keyframes menuItemAnimation { + 0% { + opacity: 0; + transform: translateX(-10px); + } + + 100% { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes menuItemAnimation { + 0% { + opacity: 0; + transform: translateX(-10px); + } + + 100% { + opacity: 1; + transform: translateX(0); + } } \ No newline at end of file diff --git a/android/app/src/main/assets/js/index.js b/android/app/src/main/assets/js/index.js index a19eb7bb1..7b21b3294 100644 --- a/android/app/src/main/assets/js/index.js +++ b/android/app/src/main/assets/js/index.js @@ -1,5 +1,30 @@ +/** + * https://fonts.google.com/icons + * Filled + * other settings are default + */ +const copyIcon = + ''; +const volumeIcon = + ''; +const selectAllIcon = + ''; +const translateIcon = + ''; +const resumeIcon = + ''; +const pauseIcon = + ''; +const stopIcon = + ''; +const selectVolumeIcon = + ''; +/** + * @type {import("./type").Reader} + */ class Reader { constructor() { + this.selection = window.getSelection(); this.footerWrapper = document.getElementById('reader-footer-wrapper'); this.percentage = document.getElementById('reader-percentage'); this.battery = document.getElementById('reader-battery'); @@ -121,11 +146,11 @@ class ToolWrapper { this.tools = []; } hide = () => { - this.$.classList.add('d-none'); + this.$.classList.add('hidden'); this.visible = false; }; show = () => { - this.$.classList.remove('d-none'); + this.$.classList.remove('hidden'); this.visible = true; for (const tool of this.tools) { tool.onShow?.(); @@ -144,12 +169,13 @@ class ScrollHandler {
-
-
-
-
+
+
+
+
+
100 @@ -236,36 +262,54 @@ class SwipeHandler { }; } +/** + * @type {import('./type').TextToSpeech} + */ class TextToSpeech { constructor(reader) { this.reader = reader; - this.$ = document.getElementById('TextToSpeech'); - this.chapter = document.querySelector('chapter'); - (this.leaf = this.chapter), (this.TTSWrapper = null); // swap these 2 elements + (this.leaf = this.reader.chapter), (this.TTSWrapper = null); // swap these 2 elements this.TTSEle = null; - this.speaking = false; - this.icon = this.$.querySelector('.tts'); + this.reading = false; + this.$ = document.getElementById('TTS-Controller'); + this.$.classList.add('d-none'); this.$.onclick = () => { - if (this.speaking) { - this.icon.classList.remove('speak'); - this.stop(); + if (this.reading) { + this.pause(); } else { - this.icon.classList.add('speak'); - const selection = window.getSelection(); - if (selection.type === 'Range') { - if (this.leaf && this.TTSWrapper) { - this.TTSWrapper.replaceWith(this.leaf); - } - this.leaf = selection.anchorNode; - this.makeLeafSpeakable(); - this.speak(); - } else { - this.next(); - } + this.resume(); } }; } + start = () => { + this.$.innerHTML = ``; + this.$.classList.remove('d-none'); + this.stop(); + this.next(); + }; + + startHere = () => { + this.$.innerHTML = ``; + this.$.classList.remove('d-none'); + this.stop(); + if (this.reader.selection.type === 'Range') { + if (this.leaf && this.TTSWrapper) { + this.TTSWrapper.replaceWith(this.leaf); + } + this.leaf = this.reader.selection.anchorNode; + this.makeLeafSpeakable(); + this.speak(); + } else { + this.next(); + } + }; + + resume = () => { + this.$.innerHTML = ``; + this.next(); + }; + findLeaf() { while (this.leaf.firstChild) { this.leaf = this.leaf.firstChild; @@ -273,14 +317,14 @@ class TextToSpeech { } findNextLeaf() { - if (this.chapter.isSameNode(this.leaf)) { + if (this.reader.chapter.isSameNode(this.leaf)) { this.findLeaf(); } else if (this.leaf.nextSibling) { this.leaf = this.leaf.nextSibling; this.findLeaf(); } else { this.leaf = this.leaf.parentNode; - if (this.chapter.isSameNode(this.leaf)) { + if (this.reader.chapter.isSameNode(this.leaf)) { return; } this.findNextLeaf(); @@ -300,8 +344,8 @@ class TextToSpeech { } do { this.findNextLeaf(); - } while (!this.readable() && !this.chapter.isSameNode(this.leaf)); - if (this.chapter.isSameNode(this.leaf)) { + } while (!this.readable() && !this.reader.chapter.isSameNode(this.leaf)); + if (this.reader.chapter.isSameNode(this.leaf)) { return; } this.makeLeafSpeakable(); @@ -323,37 +367,176 @@ class TextToSpeech { } next = () => { - if (this.TTSEle) { - this.TTSEle.classList.remove('highlight'); - } - if (this.speaking) { - if (this.TTSEle && this.TTSEle.nextElementSibling) { - this.TTSEle = this.TTSEle.nextElementSibling; - } else { - this.findNextTextNode(); + try { + if (this.TTSEle) { + this.TTSEle.classList.remove('highlight'); } - } else { - if (!this.TTSEle) { - this.findNextTextNode(); + if (this.reading) { + if (this.TTSEle && this.TTSEle.nextElementSibling) { + this.TTSEle = this.TTSEle.nextElementSibling; + } else { + this.findNextTextNode(); + } + } else { + if (!this.TTSEle) { + this.findNextTextNode(); + } } + this.speak(); + } catch (e) { + alert(e); } - this.speak(); }; + stop = () => { + this.$.classList.add('d-none'); + this.reading = false; + if (this.leaf && this.TTSWrapper) { + this.TTSWrapper.replaceWith(this.leaf); + this.TTSWrapper = null; + this.TTSEle = null; + this.leaf = this.reader.chapter; + } + this.reader.post({ type: 'stop-speak' }); + }; + + pause = () => { + this.$.innerHTML = ``; + this.reading = false; + this.reader.post({ type: 'stop-speak' }); + }; + + started = () => this.TTSWrapper !== null; + speak = () => { - this.speaking = true; - if (this.chapter.isSameNode(this.leaf)) { + this.reading = true; + if (this.reader.chapter.isSameNode(this.leaf)) { return; } this.TTSEle.classList.add('highlight'); this.reader.post({ type: 'speak', data: this.TTSEle?.innerText.trim() }); }; +} - stop = () => { - this.speaking = false; - this.reader.post({ type: 'stop-speak' }); - }; +/** + * @type {import("./type").ContextMenu} + */ +class ContextMenu { + /** + * @param {import("./type").Reader} reader + */ + constructor(reader) { + this.reader = reader; + this.items = { + START_READING: this.renderItem({ + name: 'Start Reading', + icon: volumeIcon, + action: () => { + tts.start(); + this.closeMenu(); + }, + }), + START_HERE: this.renderItem({ + name: 'Start Here', + icon: selectVolumeIcon, + action: () => { + tts.startHere(); + this.closeMenu(); + }, + }), + COPY: this.renderItem({ + name: 'Copy', + icon: copyIcon, + action: () => { + this.reader.post({ + type: 'copy', + data: this.reader.selection.toString(), + }); + }, + }), + SELECT_ALL: this.renderItem({ + name: 'Select All', + icon: selectAllIcon, + action: () => { + const range = document.createRange(); + range.selectNodeContents(this.reader.chapter); + this.reader.selection.removeAllRanges(); + this.reader.selection.addRange(range); + }, + }), + }; + this.commonItems = [this.items.COPY, this.items.SELECT_ALL]; + this.contextMenu = document.createElement('ul'); + this.contextMenu.classList.add('contextMenu'); + this.isOpened = false; + } + + /** + * @param {{name: string, icon: string, action: () => void}} data + * @returns + */ + renderItem(data) { + const item = document.createElement('li'); + const button = document.createElement('button'); + button.innerHTML = `${data.icon} ${data.name}`; + button.classList.add('contextMenu-button'); + item.classList.add('contextMenu-item'); + item.appendChild(button); + button.addEventListener('click', event => { + event.stopPropagation(); + data.action(); + }); + return item; + } + + renderMenu(items) { + this.contextMenu.innerHTML = ''; + items.concat(this.commonItems).forEach((item, index) => { + item.firstChild.setAttribute('style', `animation-delay: ${index * 0.1}s`); + this.contextMenu.appendChild(item); + }); + } + + closeMenu() { + if (this.isOpened) { + this.isOpened = false; + this.contextMenu.remove(); + } + } + + init() { + this.reader.chapter.addEventListener('click', () => { + this.closeMenu(this.contextMenu); + }); + document.addEventListener('contextmenu', e => { + e.preventDefault(); + this.isOpened = true; + if (this.reader.selection.type === 'Range') { + this.renderMenu([this.items.START_HERE, this.items.START_READING]); + } else { + this.renderMenu([this.items.START_READING]); + } + const { clientX, clientY } = e; + document.body.appendChild(this.contextMenu); + const positionY = + clientY + this.contextMenu.scrollHeight >= window.innerHeight + ? window.innerHeight - this.contextMenu.scrollHeight - 20 + : clientY; + const positionX = + clientX + this.contextMenu.scrollWidth >= window.innerWidth + ? window.innerWidth - this.contextMenu.scrollWidth - 20 + : clientX; + this.contextMenu.setAttribute( + 'style', + `--width: ${this.contextMenu.scrollWidth}px; + --height: ${this.contextMenu.scrollHeight}px; + --top: ${positionY}px; + --left: ${positionX}px;`, + ); + }); + } } + try { var swipeHandler = new SwipeHandler(); var toolWrapper = new ToolWrapper(); @@ -361,6 +544,8 @@ try { var scrollHandler = new ScrollHandler(reader, toolWrapper); var tts = new TextToSpeech(reader); toolWrapper.tools = [scrollHandler, tts]; + const contextMenu = new ContextMenu(reader); + contextMenu.init(); } catch (e) { alert(e); } diff --git a/android/app/src/main/assets/js/type.d.ts b/android/app/src/main/assets/js/type.d.ts new file mode 100644 index 000000000..436b15b9e --- /dev/null +++ b/android/app/src/main/assets/js/type.d.ts @@ -0,0 +1,62 @@ +import { + ChapterGeneralSettings, + ChapterReaderSettings, +} from '@hooks/persisted/useSettings'; +import 'typescript'; +import 'typescript/lib/lib.dom'; + +interface Reader { + selection: Selection; + footerWrapper: HTMLDivElement; + percentage: HTMLDivElement; + battery: HTMLDivElement; + time: HTMLDivElement; + paddingTop: number; + chapter: HTMLElement; + chapterHeight: number; + layoutHeight: number; + pluginId: string; + novelId: string; + chapterId: string; + saveProgressInterval: NodeJS.Timeout; + timeInterval: NodeJS.Timeout; + refresh: () => void; + post: (message: { type: string; data?: any }) => void; + updateReaderSettings: (settings: ChapterReaderSettings) => void; + updateGeneralSettings: (settings: ChapterGeneralSettings) => void; + updateBatteryLevel: (level: number) => void; +} + +interface TextToSpeech { + reader: Reader; + leaf: HTMLElement; + TTSWrapper: HTMLElement; + TTSEle: HTMLElement; + reading: boolean; + start: () => void; + startHere: () => void; + resume: () => void; + pause: () => void; + stop: () => void; + started: () => boolean; +} + +export enum ContextMenuItem { + START_READING = 'START_READING', + START_HERE = 'START_HERE', + COPY = 'COPY', + SELECT_ALL = 'SELECT_ALL', +} +interface ContextMenu { + reader: Reader; + contextMenu: HTMLUListElement; + items: Record; + renderItem: (data: { + name: string; + icon: string; + action: () => void; + }) => HTMLLIElement; + renderMenu: (items: HTMLLIElement[]) => void; + closeMenu: (menu: HTMLUListElement) => void; + init: () => void; +} diff --git a/src/database/db.ts b/src/database/db.ts index f4a722eda..d90b41655 100644 --- a/src/database/db.ts +++ b/src/database/db.ts @@ -21,14 +21,12 @@ export const createTables = () => { db.exec([{ sql: 'PRAGMA foreign_keys = ON', args: [] }], false, () => {}); db.transaction(tx => { tx.executeSql(createNovelTableQuery); - tx.executeSql(createCategoriesTableQuery, [], () => { - tx.executeSql(createCategoryDefaultQuery); - tx.executeSql(createCategoryTriggerQuery); - }); + tx.executeSql(createCategoriesTableQuery); + tx.executeSql(createCategoryDefaultQuery); + tx.executeSql(createCategoryTriggerQuery); tx.executeSql(createNovelCategoryTableQuery); - tx.executeSql(createChapterTableQuery, [], () => { - tx.executeSql(createChapterNovelIdIndexQuery); - }); + tx.executeSql(createChapterTableQuery); + tx.executeSql(createChapterNovelIdIndexQuery); }); }; diff --git a/src/navigators/Main.tsx b/src/navigators/Main.tsx index 192392685..0668696a8 100644 --- a/src/navigators/Main.tsx +++ b/src/navigators/Main.tsx @@ -4,7 +4,7 @@ import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; import { setBarColor } from '@theme/utils/setBarColor'; -import { useAppSettings, useTheme } from '@hooks/persisted'; +import { useAppSettings, usePlugins, useTheme } from '@hooks/persisted'; import { useGithubUpdateChecker } from '@hooks/common/githubUpdateChecker'; /** @@ -37,6 +37,7 @@ const Stack = createStackNavigator(); const MainNavigator = () => { const theme = useTheme(); const { updateLibraryOnLaunch } = useAppSettings(); + const { refreshPlugins } = usePlugins(); useEffect(() => { const timer = setTimeout(async () => { @@ -52,6 +53,7 @@ const MainNavigator = () => { if (updateLibraryOnLaunch) { updateLibrary(); } + refreshPlugins(); }, []); const { isNewVersion, latestRelease } = useGithubUpdateChecker(); diff --git a/src/screens/BrowseSourceScreen/components/FilterBottomSheet.tsx b/src/screens/BrowseSourceScreen/components/FilterBottomSheet.tsx index 511ead2fb..a3c8a2505 100644 --- a/src/screens/BrowseSourceScreen/components/FilterBottomSheet.tsx +++ b/src/screens/BrowseSourceScreen/components/FilterBottomSheet.tsx @@ -62,6 +62,7 @@ const FilterItem: React.FC = ({ = ({ }, ]} > - {filter.label} + {` ${filter.label} `} } value={value || 'whatever'} editable={false} - outlineColor={isVisible ? theme.primary : theme.outline} - textColor={isVisible ? theme.primary : theme.outline} + theme={{ colors: { background: 'transparent' } }} + outlineColor={isVisible ? theme.primary : theme.onSurface} + textColor={isVisible ? theme.primary : theme.onSurface} right={ } /> @@ -102,6 +104,7 @@ const FilterItem: React.FC = ({ { closeCard(); setSelectedFilters(prevFilters => ({ diff --git a/src/screens/novel/components/JumpToChapterModal.tsx b/src/screens/novel/components/JumpToChapterModal.tsx index a9b954238..6197fb447 100644 --- a/src/screens/novel/components/JumpToChapterModal.tsx +++ b/src/screens/novel/components/JumpToChapterModal.tsx @@ -27,6 +27,8 @@ const JumpToChapterModal = ({ novel, chapterListRef, }: JumpToChapterModalProps) => { + const minNumber = Math.min(...chapters.map(c => c.chapterNumber || -1)); + const maxNumber = Math.max(...chapters.map(c => c.chapterNumber || -1)); const theme = useTheme(); const [mode, setMode] = useState(false); const [openChapter, setOpenChapter] = useState(false); @@ -100,15 +102,20 @@ const JumpToChapterModal = ({ const onSubmit = () => { if (!mode) { const num = Number(text); - if (num && num > 0 && num <= chapters.length) { + if (num && num >= minNumber && num <= maxNumber) { if (openChapter) { - return navigateToChapter(chapters[num - 1]); + const chapter = chapters.find(c => c.chapterNumber === num); + if (chapter) { + return navigateToChapter(chapter); + } + } else { + const index = chapters.findIndex(c => c.chapterNumber === num); + return scrollToIndex(index); } - return scrollToIndex(num - 1); } return setError( getString('novelScreen.jumpToChapterModal.error.validChapterNumber') + - ` (${num <= 0 ? '≤ 0' : '≤ ' + chapters.length})`, + ` (${num < minNumber ? '≥ ' + minNumber : '≤ ' + maxNumber})`, ); } else { const searchedChapters = chapters.filter(chap => @@ -160,7 +167,7 @@ const JumpToChapterModal = ({ mode ? getString('novelScreen.jumpToChapterModal.chapterName') : getString('novelScreen.jumpToChapterModal.chapterNumber') + - ` (≤ ${chapters.length})` + ` (≥ ${minNumber}, ≤ ${maxNumber})` } onChangeText={onChangeText} onSubmitEditing={onSubmit} diff --git a/src/screens/reader/components/WebViewReader.tsx b/src/screens/reader/components/WebViewReader.tsx index d5a85dcbf..53bc8a280 100644 --- a/src/screens/reader/components/WebViewReader.tsx +++ b/src/screens/reader/components/WebViewReader.tsx @@ -24,6 +24,8 @@ import { } from '@hooks/persisted/useSettings'; import { getBatteryLevelSync } from 'react-native-device-info'; import * as Speech from 'expo-speech'; +import * as Clipboard from 'expo-clipboard'; +import { showToast } from '@utils/showToast'; type WebViewPostEvent = { type: string; @@ -166,6 +168,13 @@ const WebViewReader: FC = props => { 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={{ @@ -187,20 +196,24 @@ const WebViewReader: FC = props => { --theme-onPrimary: ${theme.onPrimary}; --theme-secondary: ${theme.secondary}; --theme-tertiary: ${theme.tertiary}; + --theme-onTertiary: ${theme.onTertiary}; --theme-onSecondary: ${theme.onSecondary}; --theme-surface: ${theme.surface}; --theme-surface-0-9: ${color(theme.surface) .alpha(0.9) .toString()}; - --theme-onSurface:${theme.onSurface}; + --theme-onSurface: ${theme.onSurface}; + --theme-surfaceVariant: ${theme.surfaceVariant}; + --theme-onSurfaceVariant: ${theme.onSurfaceVariant}; --theme-outline: ${theme.outline}; + --theme-rippleColor: ${theme.rippleColor}; --chapterCtn-height: ${layoutHeight - 140}; } @font-face { font-family: ${readerSettings.fontFamily}; - src: url("${assetsUriPrefix}/fonts/${ - readerSettings.fontFamily - }.ttf"); + src: url("file:///android_asset/fonts/${ + readerSettings.fontFamily + }.ttf"); } @@ -224,15 +237,10 @@ const WebViewReader: FC = props => { > ${html} -
-
- - - -
+ -