From 93876a73cc6f1bd328ba1f3cbfc6b9925d603c13 Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Fri, 16 May 2025 00:19:32 +0800 Subject: [PATCH] layout: responsive translator popup, also closes #1157 (#1160) --- .../reader/components/annotator/Annotator.tsx | 26 +-- .../components/annotator/DeepLPopup.tsx | 90 +++++---- .../src/app/reader/hooks/useTextSelector.ts | 32 ++-- apps/readest-app/src/components/Popup.tsx | 172 +++++++++++------- 4 files changed, 198 insertions(+), 122 deletions(-) diff --git a/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx b/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx index 87dd9ef1..0e0a4636 100644 --- a/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx +++ b/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx @@ -73,7 +73,7 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { const dictPopupWidth = Math.min(480, maxWidth); const dictPopupHeight = Math.min(300, maxHeight); const transPopupWidth = Math.min(480, maxWidth); - const transPopupHeight = Math.min(360, maxHeight); + const transPopupHeight = Math.min(265, maxHeight); const annotPopupWidth = Math.min(useResponsiveSize(300), maxWidth); const annotPopupHeight = useResponsiveSize(44); const androidSelectionHandlerHeight = 0; @@ -91,13 +91,8 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { view?.deselect(); }; - const { - handleScroll, - handlePointerup, - handleSelectionchange, - handleAnnotPopup, - handleShowAnnotation, - } = useTextSelector(bookKey, setSelection, handleDismissPopup); + const { handleScroll, handlePointerup, handleSelectionchange, handleShowPopup, handleUpToPopup } = + useTextSelector(bookKey, setSelection, handleDismissPopup); const onLoad = (event: Event) => { const detail = (event as CustomEvent).detail; @@ -163,15 +158,15 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { setSelectedStyle(annotation.style!); setSelectedColor(annotation.color!); setSelection(selection); - handleShowAnnotation(); + handleUpToPopup(); }; useFoliateEvents(view, { onLoad, onDrawAnnotation, onShowAnnotation }); useEffect(() => { - handleAnnotPopup(showAnnotPopup); + handleShowPopup(showAnnotPopup || showWiktionaryPopup || showWikipediaPopup || showDeepLPopup); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [showAnnotPopup]); + }, [showAnnotPopup, showWiktionaryPopup, showWikipediaPopup, showDeepLPopup]); useEffect(() => { eventDispatcher.on('export-annotations', handleExportMarkdown); @@ -214,11 +209,11 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { popupPadding, ); if (triangPos.point.x == 0 || triangPos.point.y == 0) return; - setShowAnnotPopup(true); setAnnotPopupPosition(annotPopupPos); setDictPopupPosition(dictPopupPos); setTranslatorPopupPosition(transPopupPos); setTrianglePosition(triangPos); + handleShowAnnotPopup(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [selection, bookKey]); @@ -245,6 +240,13 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [progress]); + const handleShowAnnotPopup = () => { + setShowAnnotPopup(true); + setShowDeepLPopup(false); + setShowWiktionaryPopup(false); + setShowWikipediaPopup(false); + }; + const handleCopy = () => { if (!selection || !selection.text) return; eventDispatcher.dispatch('toast', { diff --git a/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx b/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx index d8ed4aa7..6537457b 100644 --- a/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx +++ b/apps/readest-app/src/app/reader/components/annotator/DeepLPopup.tsx @@ -1,33 +1,33 @@ import React, { useEffect, useState } from 'react'; import Popup from '@/components/Popup'; import { Position } from '@/utils/sel'; -import { getAPIBaseUrl } from '@/services/environment'; +import { useAuth } from '@/context/AuthContext'; import { useSettingsStore } from '@/store/settingsStore'; import { useTranslation } from '@/hooks/useTranslation'; -import { useAuth } from '@/context/AuthContext'; +import { useResponsiveSize } from '@/hooks/useResponsiveSize'; +import { getAPIBaseUrl } from '@/services/environment'; +import { TRANSLATED_LANGS } from '@/services/constants'; -const LANGUAGES = { - AUTO: 'Auto Detect', - EN: 'English', - DE: 'German', - FR: 'French', - ES: 'Spanish', - IT: 'Italian', - EL: 'Greek', - PT: 'Portuguese', - NL: 'Dutch', - PL: 'Polish', - UK: 'Ukrainian', - RU: 'Russian', - AR: 'Arabic', - TR: 'Turkish', - ID: 'Indonesian', - KO: 'Korean', - JA: 'Japanese', - 'ZH-HANS': 'Chinese (Simplified)', - 'ZH-HANT': 'Chinese (Traditional)', +const notSupportedLangs = ['hi', 'vi']; + +const generateTranslatorLangs = () => { + const langs = { ...TRANSLATED_LANGS }; + const result: Record = {}; + for (const [code, name] of Object.entries(langs)) { + if (notSupportedLangs.includes(code)) continue; + let newCode = code.toUpperCase(); + if (newCode === 'ZH-CN') { + newCode = 'ZH-HANS'; + } else if (newCode === 'ZH-TW') { + newCode = 'ZH-HANT'; + } + result[newCode] = name; + } + return result; }; +const TRANSLATOR_LANGS = generateTranslatorLangs(); + const DEEPL_API_ENDPOINT = getAPIBaseUrl() + '/deepl/translate'; interface DeepLPopupProps { @@ -51,7 +51,9 @@ const DeepLPopup: React.FC = ({ const [sourceLang, setSourceLang] = useState('AUTO'); const [targetLang, setTargetLang] = useState(settings.globalReadSettings.translateTargetLang); const [translation, setTranslation] = useState(null); - const [detectedSourceLang, setDetectedSourceLang] = useState(null); + const [detectedSourceLang, setDetectedSourceLang] = useState< + keyof typeof TRANSLATOR_LANGS | null + >(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -117,47 +119,59 @@ const DeepLPopup: React.FC = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [text, token, sourceLang, targetLang]); + const popupPadding = useResponsiveSize(10); + const maxHeight = window.innerHeight - 2 * popupPadding; + const popupMaxHeight = Math.min(360, maxHeight); + return (
-
+
-

{_('Original Text')}

+

{_('Original Text')}

-

{text}

+

{text}

-
+
+ +
-

{_('Translated Text')}

+

{_('Translated Text')}