diff --git a/Changelog.md b/Changelog.md index 52d83aa52..4bca5f200 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ ### ✨ 新增功能 +- 订阅 QR code 分享 - 新增 macOS 托盘速率显示 - 快捷键操作通知操作结果 diff --git a/package.json b/package.json index fa7fc15e4..68126ddd3 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "monaco-editor": "^0.55.1", "monaco-yaml": "^5.4.1", "nanoid": "^5.1.7", + "qrcode.react": "^4.2.0", "react": "19.2.5", "react-dom": "19.2.5", "react-error-boundary": "6.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4907489a6..388d77a6c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,6 +107,9 @@ importers: nanoid: specifier: ^5.1.7 version: 5.1.7 + qrcode.react: + specifier: ^4.2.0 + version: 4.2.0(react@19.2.5) react: specifier: 19.2.5 version: 19.2.5 @@ -3101,6 +3104,11 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qrcode.react@4.2.0: + resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom@19.2.5: resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==} peerDependencies: @@ -6762,6 +6770,10 @@ snapshots: punycode@2.3.1: {} + qrcode.react@4.2.0(react@19.2.5): + dependencies: + react: 19.2.5 + react-dom@19.2.5(react@19.2.5): dependencies: react: 19.2.5 diff --git a/src/components/profile/profile-item.tsx b/src/components/profile/profile-item.tsx index 283768830..7ad2ac57c 100644 --- a/src/components/profile/profile-item.tsx +++ b/src/components/profile/profile-item.tsx @@ -1,20 +1,20 @@ import { useSortable } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { - RefreshRounded, - DragIndicatorRounded, - CheckBoxRounded, CheckBoxOutlineBlankRounded, + CheckBoxRounded, + DragIndicatorRounded, + RefreshRounded, } from '@mui/icons-material' import { Box, - Typography, - LinearProgress, + CircularProgress, IconButton, keyframes, - MenuItem, + LinearProgress, Menu, - CircularProgress, + MenuItem, + Typography, } from '@mui/material' import { open } from '@tauri-apps/plugin-shell' import { useLockFn } from 'ahooks' @@ -28,11 +28,11 @@ import { GroupsEditorViewer } from '@/components/profile/groups-editor-viewer' import { RulesEditorViewer } from '@/components/profile/rules-editor-viewer' import { useEditorDocument } from '@/hooks/use-editor-document' import { - viewProfile, - readProfileFile, - updateProfile, - saveProfileFile, getNextUpdateTime, + readProfileFile, + saveProfileFile, + updateProfile, + viewProfile, } from '@/services/cmds' import { showNotice } from '@/services/notice-service' import { useLoadingCache, useSetLoadingCache } from '@/services/states' @@ -42,6 +42,7 @@ import parseTraffic from '@/utils/parse-traffic' import { ProfileBox } from './profile-box' import { ProxiesEditorViewer } from './proxies-editor-viewer' +import { QrViewer } from './qr-viewer' const round = keyframes` from { transform: rotate(0deg); } to { transform: rotate(360deg); } @@ -285,6 +286,7 @@ export const ProfileItem = (props: Props) => { const [mergeOpen, setMergeOpen] = useState(false) const [scriptOpen, setScriptOpen] = useState(false) const [confirmOpen, setConfirmOpen] = useState(false) + const [qrOpen, setQrOpen] = useState(false) const loadProfileDocument = useCallback(() => readProfileFile(uid), [uid]) const loadMergeDocument = useCallback( @@ -319,6 +321,11 @@ export const ProfileItem = (props: Props) => { onEdit() } + const onShareQrCode = () => { + setAnchorEl(null) + setQrOpen(true) + } + const onEditFile = () => { setAnchorEl(null) setFileOpen(true) @@ -409,6 +416,7 @@ export const ProfileItem = (props: Props) => { const menuLabels: Record = { home: 'profiles.components.menu.home', select: 'profiles.components.menu.select', + shareQrCode: 'profiles.components.menu.shareQrCode', editInfo: 'profiles.components.menu.editInfo', editFile: 'profiles.components.menu.editFile', editRules: 'profiles.components.menu.editRules', @@ -437,6 +445,11 @@ export const ProfileItem = (props: Props) => { handler: onForceSelect, disabled: false, }, + { + label: menuLabels.shareQrCode, + handler: onShareQrCode, + disabled: false, + }, { label: menuLabels.editInfo, handler: onEditInfo, @@ -978,6 +991,13 @@ export const ProfileItem = (props: Props) => { setConfirmOpen(false) }} /> + {qrOpen && itemData.url && ( + setQrOpen(false)} + /> + )} ) } diff --git a/src/components/profile/qr-viewer.tsx b/src/components/profile/qr-viewer.tsx new file mode 100644 index 000000000..2a65c234b --- /dev/null +++ b/src/components/profile/qr-viewer.tsx @@ -0,0 +1,34 @@ +import { Box, Dialog, DialogContent, DialogTitle } from '@mui/material' +import { QRCodeSVG } from 'qrcode.react' +import { useTranslation } from 'react-i18next' + +interface Props { + open: boolean + value: string + title?: string + onClose: () => void +} + +export const QrViewer = (props: Props) => { + const { open, value, title, onClose } = props + const { t } = useTranslation() + + return ( + + {title ?? t('profiles.modals.qrViewer.title')} + + + + + + + ) +} diff --git a/src/locales/ar/profiles.json b/src/locales/ar/profiles.json index 9e8a8d28a..5d81ef166 100644 --- a/src/locales/ar/profiles.json +++ b/src/locales/ar/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "Home", "select": "اختيار", + "shareQrCode": "Share QR Code", "editInfo": "تعديل المعلومات", "editFile": "تعديل الملف", "editRules": "تعديل القواعد", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "وحدة التحكم للسكريبت" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/locales/de/profiles.json b/src/locales/de/profiles.json index 16da1157d..4beeec90d 100644 --- a/src/locales/de/profiles.json +++ b/src/locales/de/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "Startseite", "select": "Verwenden", + "shareQrCode": "Share QR Code", "editInfo": "Informationen bearbeiten", "editFile": "Datei bearbeiten", "editRules": "Regeln bearbeiten", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "Skript-Konsole-Ausgabe" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/locales/en/profiles.json b/src/locales/en/profiles.json index 41bfd88cc..c4461dead 100644 --- a/src/locales/en/profiles.json +++ b/src/locales/en/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "Home", "select": "Select", + "shareQrCode": "Share QR Code", "editInfo": "Edit Info", "editFile": "Edit File", "editRules": "Edit Rules", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "Script Console" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/locales/es/profiles.json b/src/locales/es/profiles.json index d56eca5e7..e70887a6f 100644 --- a/src/locales/es/profiles.json +++ b/src/locales/es/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "Hogar", "select": "Usar", + "shareQrCode": "Share QR Code", "editInfo": "Editar información", "editFile": "Editar archivo", "editRules": "Editar reglas", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "Salida de la consola del script" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/locales/fa/profiles.json b/src/locales/fa/profiles.json index 00972e68b..68b80465c 100644 --- a/src/locales/fa/profiles.json +++ b/src/locales/fa/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "Home", "select": "انتخاب", + "shareQrCode": "Share QR Code", "editInfo": "ویرایش اطلاعات", "editFile": "ویرایش فایل", "editRules": "ویرایش قوانین", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "کنسول اسکریپت" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/locales/id/profiles.json b/src/locales/id/profiles.json index e80463a5f..801350cba 100644 --- a/src/locales/id/profiles.json +++ b/src/locales/id/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "Home", "select": "Pilih", + "shareQrCode": "Share QR Code", "editInfo": "Ubah Info", "editFile": "Ubah Berkas", "editRules": "Ubah Aturan", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "Konsol Skrip" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/locales/jp/profiles.json b/src/locales/jp/profiles.json index b9cc3489e..70ef312fe 100644 --- a/src/locales/jp/profiles.json +++ b/src/locales/jp/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "ホーム", "select": "使用する", + "shareQrCode": "Share QR Code", "editInfo": "情報を編集", "editFile": "ファイルを編集", "editRules": "ルールを編集", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "スクリプトコンソール出力" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/locales/ko/profiles.json b/src/locales/ko/profiles.json index 9b3647cbe..9ab2eaed4 100644 --- a/src/locales/ko/profiles.json +++ b/src/locales/ko/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "홈", "select": "선택", + "shareQrCode": "Share QR Code", "editInfo": "정보 편집", "editFile": "파일 편집", "editRules": "규칙 편집", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "스크립트 콘솔" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/locales/ru/profiles.json b/src/locales/ru/profiles.json index 227fbeacb..8a72682c7 100644 --- a/src/locales/ru/profiles.json +++ b/src/locales/ru/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "Главная", "select": "Выбрать", + "shareQrCode": "Share QR Code", "editInfo": "Изменить информацию", "editFile": "Изменить файл", "editRules": "Редактировать правила", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "Консоль скрипта" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/locales/tr/profiles.json b/src/locales/tr/profiles.json index fe904ebe5..8d636593c 100644 --- a/src/locales/tr/profiles.json +++ b/src/locales/tr/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "Ana Sayfa", "select": "Seç", + "shareQrCode": "Share QR Code", "editInfo": "Bilgileri Düzenle", "editFile": "Dosyayı Düzenle", "editRules": "Kuralları Düzenle", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "Betik Konsolu" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/locales/tt/profiles.json b/src/locales/tt/profiles.json index 28a0ef5db..84d2dbe0a 100644 --- a/src/locales/tt/profiles.json +++ b/src/locales/tt/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "Home", "select": "Сайлау", + "shareQrCode": "Share QR Code", "editInfo": "Мәгълүматны үзгәртү", "editFile": "Файлны үзгәртү", "editRules": "Кагыйдәләрне үзгәртү", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "Скрипт консоле" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/locales/zh/profiles.json b/src/locales/zh/profiles.json index 8fefb3092..ca4607f55 100644 --- a/src/locales/zh/profiles.json +++ b/src/locales/zh/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "首 页", "select": "使用", + "shareQrCode": "分享二维码", "editInfo": "编辑信息", "editFile": "编辑文件", "editRules": "编辑规则", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "脚本控制台输出" + }, + "qrViewer": { + "title": "订阅二维码" } } } diff --git a/src/locales/zhtw/profiles.json b/src/locales/zhtw/profiles.json index 9c2946113..4ab505c2c 100644 --- a/src/locales/zhtw/profiles.json +++ b/src/locales/zhtw/profiles.json @@ -59,6 +59,7 @@ "menu": { "home": "首 頁", "select": "使用", + "shareQrCode": "Share QR Code", "editInfo": "編輯資訊", "editFile": "編輯檔案", "editRules": "編輯規則", @@ -177,6 +178,9 @@ }, "logViewer": { "title": "指令碼控制台輸出" + }, + "qrViewer": { + "title": "Subscription QR Code" } } } diff --git a/src/types/generated/i18n-keys.ts b/src/types/generated/i18n-keys.ts index 10f7d28fe..529188cf8 100644 --- a/src/types/generated/i18n-keys.ts +++ b/src/types/generated/i18n-keys.ts @@ -142,6 +142,7 @@ export const translationKeys = [ 'profiles.components.fileInput.chooseFile', 'profiles.components.menu.home', 'profiles.components.menu.select', + 'profiles.components.menu.shareQrCode', 'profiles.components.menu.editInfo', 'profiles.components.menu.editFile', 'profiles.components.menu.editRules', @@ -210,6 +211,7 @@ export const translationKeys = [ 'profiles.modals.confirmDelete.title', 'profiles.modals.confirmDelete.message', 'profiles.modals.logViewer.title', + 'profiles.modals.qrViewer.title', 'proxies.page.modes.rule', 'proxies.page.modes.global', 'proxies.page.modes.direct', diff --git a/src/types/generated/i18n-resources.ts b/src/types/generated/i18n-resources.ts index 6d1fd870e..ff067b820 100644 --- a/src/types/generated/i18n-resources.ts +++ b/src/types/generated/i18n-resources.ts @@ -226,6 +226,7 @@ export interface TranslationResources { home: string openFile: string select: string + shareQrCode: string update: string updateViaProxy: string } @@ -337,6 +338,9 @@ export interface TranslationResources { } title: string } + qrViewer: { + title: string + } } page: { actions: {