diff --git a/src/config/i18n.js b/src/config/i18n.js index 3854818..a29fb80 100644 --- a/src/config/i18n.js +++ b/src/config/i18n.js @@ -643,4 +643,20 @@ export const I18N = { zh: `收藏词汇`, en: `Favorite Words`, }, + touch_setting: { + zh: `触屏设置`, + en: `Touch Setting`, + }, + touch_tap_0: { + zh: `禁用`, + en: `Disable`, + }, + touch_tap_2: { + zh: `双指轻触`, + en: `Two finger tap`, + }, + touch_tap_3: { + zh: `三指轻触`, + en: `Three finger tap`, + }, }; diff --git a/src/config/index.js b/src/config/index.js index 753983d..6d281ed 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -404,6 +404,14 @@ export const TRANS_MIN_LENGTH = 5; // 最短翻译长度 export const TRANS_MAX_LENGTH = 5000; // 最长翻译长度 export const TRANS_NEWLINE_LENGTH = 20; // 换行字符数 +// 触屏操作 +export const DEFAULT_TOUCH_OPERATION = { + [OPT_SHORTCUT_TRANSLATE]: [2, 1, 500], + [OPT_SHORTCUT_STYLE]: [3, 1, 500], + [OPT_SHORTCUT_POPUP]: [0, 1, 500], + [OPT_SHORTCUT_SETTING]: [3, 2, 500], +}; + export const DEFAULT_SETTING = { darkMode: false, // 深色模式 uiLang: "en", // 界面语言 @@ -423,6 +431,7 @@ export const DEFAULT_SETTING = { shortcuts: DEFAULT_SHORTCUTS, // 快捷键 inputRule: DEFAULT_INPUT_RULE, // 输入框设置 tranboxSetting: DEFAULT_TRANBOX_SETTING, // 划词翻译设置 + touchOperations: DEFAULT_TOUCH_OPERATION, // 触屏操作 }; export const DEFAULT_RULES = [GLOBLA_RULE]; diff --git a/src/hooks/Touch.js b/src/hooks/Touch.js new file mode 100644 index 0000000..ca462d2 --- /dev/null +++ b/src/hooks/Touch.js @@ -0,0 +1,19 @@ +import { useCallback } from "react"; +import { DEFAULT_TOUCH_OPERATION } from "../config"; +import { useSetting } from "./Setting"; + +export function useTouch(action) { + const { setting, updateSetting } = useSetting(); + const touchOperations = setting?.touchOperations || DEFAULT_TOUCH_OPERATION; + const touchOperation = touchOperations[action]; + + const setTouchOperation = useCallback( + async (val, idx) => { + touchOperations[action][idx] = val; + await updateSetting({ touchOperations: { ...touchOperations } }); + }, + [action, touchOperations, updateSetting] + ); + + return { touchOperation, setTouchOperation }; +} diff --git a/src/libs/touch.js b/src/libs/touch.js new file mode 100644 index 0000000..942e8e8 --- /dev/null +++ b/src/libs/touch.js @@ -0,0 +1,31 @@ +export function touchTapListener(fn, setting) { + const [touchLength, touchCount, touchTime] = setting; + + let lastTouch = 0; + let curCount = 0; + const handleTouchend = (e) => { + if (e.touches.length !== touchLength) { + return; + } + + const timer = setTimeout(() => { + clearTimeout(timer); + curCount = 0; + }, touchTime); + + curCount++; + const now = Date.now(); + if (curCount === touchCount && now - lastTouch <= touchTime) { + timer && clearTimeout(timer); + curCount = 0; + fn(); + } + + lastTouch = now; + }; + + document.addEventListener("touchend", handleTouchend); + return () => { + document.removeEventListener("touchend", handleTouchend); + }; +} diff --git a/src/views/Options/Navigator.js b/src/views/Options/Navigator.js index bd00c1e..c796d8b 100644 --- a/src/views/Options/Navigator.js +++ b/src/views/Options/Navigator.js @@ -13,8 +13,9 @@ import SyncIcon from "@mui/icons-material/Sync"; import ApiIcon from "@mui/icons-material/Api"; import SendTimeExtensionIcon from "@mui/icons-material/SendTimeExtension"; import InputIcon from "@mui/icons-material/Input"; -import SelectAllIcon from '@mui/icons-material/SelectAll'; -import EventNoteIcon from '@mui/icons-material/EventNote'; +import SelectAllIcon from "@mui/icons-material/SelectAll"; +import EventNoteIcon from "@mui/icons-material/EventNote"; +import TouchAppIcon from "@mui/icons-material/TouchApp"; function LinkItem({ label, url, icon }) { const match = useMatch(url); @@ -47,6 +48,12 @@ export default function Navigator(props) { url: "/input", icon: , }, + { + id: "touch_setting", + label: i18n("touch_setting"), + url: "/touch", + icon: , + }, { id: "selection_translate", label: i18n("selection_translate"), diff --git a/src/views/Options/index.js b/src/views/Options/index.js index 6170369..56f0bcb 100644 --- a/src/views/Options/index.js +++ b/src/views/Options/index.js @@ -22,6 +22,7 @@ import Webfix from "./Webfix"; import InputSetting from "./InputSetting"; import Tranbox from "./Tranbox"; import FavWords from "./FavWords"; +import TouchSetting from "./touchSetting"; export default function Options() { const [error, setError] = useState(""); @@ -122,6 +123,7 @@ export default function Options() { } /> } /> } /> + } /> } /> } /> } /> diff --git a/src/views/Options/touchSetting.js b/src/views/Options/touchSetting.js new file mode 100644 index 0000000..22adbb6 --- /dev/null +++ b/src/views/Options/touchSetting.js @@ -0,0 +1,103 @@ +import Box from "@mui/material/Box"; +import Stack from "@mui/material/Stack"; +import TextField from "@mui/material/TextField"; +import MenuItem from "@mui/material/MenuItem"; +import { useI18n } from "../../hooks/I18n"; +import { + OPT_SHORTCUT_TRANSLATE, + OPT_SHORTCUT_STYLE, + OPT_SHORTCUT_POPUP, + OPT_SHORTCUT_SETTING, +} from "../../config"; +import Grid from "@mui/material/Grid"; +import { limitNumber } from "../../libs/utils"; +import { useTouch } from "../../hooks/Touch"; + +function TouchItem({ action, name }) { + const i18n = useI18n(); + const { touchOperation, setTouchOperation } = useTouch(action); + const [triggerShortcut, triggerCount, triggerTime] = touchOperation; + + const handleChangeShortcut = (e) => { + const value = limitNumber(e.target.value, 0, 3); + setTouchOperation(value, 0); + }; + + const handleChangeCount = (e) => { + const value = limitNumber(e.target.value, 1, 3); + setTouchOperation(value, 2); + }; + + const handleChangeTime = (e) => { + const value = limitNumber(e.target.value, 100, 1000); + setTouchOperation(value, 3); + }; + + return ( + + + + + {[0, 2, 3].map((val) => ( + + {i18n(`touch_tap_${val}`)} + + ))} + + + + + {[1, 2, 3].map((val) => ( + + {val} + + ))} + + + + + + + + ); +} + +export default function TouchSetting() { + return ( + + + + + + + + + ); +}