From afb3af81adeca6f390ce7f8ad830bc26ce785036 Mon Sep 17 00:00:00 2001 From: Kreedzt Date: Wed, 6 Dec 2023 18:50:41 +0800 Subject: [PATCH] :construction: Kreedzt - add clipboard share logic --- README.md | 2 + README_zhCN.md | 1 + package.json | 3 +- pnpm-lock.yaml | 7 ++++ src/pages/dashboard/HotkeyList.tsx | 26 +++++++++++- src/services/share.ts | 63 ++++++++++++++++++++++++++++++ src/share/types.ts | 6 +++ src/share/utils.ts | 54 +++++++++++++++++++++++++ 8 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 src/services/share.ts diff --git a/README.md b/README.md index 2240e25..2b2d287 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,14 @@ Quick edit `hotkeys.xml` file tool English | [简体中文](README_zhCN.md) ## Quick Start + Download latest [Release](https://github.com/Kreedzt/rwr-hotkey-editor/releases), double-click exe to run app. Available features: - [x] Save by group - [x] Quick overwrite +- [x] Share by clipboard For `Windows 7` users, please [download](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section) webview2 runtime. diff --git a/README_zhCN.md b/README_zhCN.md index c061e95..e8971ad 100644 --- a/README_zhCN.md +++ b/README_zhCN.md @@ -16,6 +16,7 @@ - [x] 分组保存 - [x] 快速覆盖 +- [x] 通过剪贴板分享 针对 `Windows 7` 用户, 请去 [官网](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section) 下载 `webview2` 运行时 diff --git a/package.json b/package.json index da83da5..a28a6e6 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.48.2", "react-router-dom": "^6.11.1", - "styled-components": "^5.3.10" + "styled-components": "^5.3.10", + "zod": "^3.22.4" }, "devDependencies": { "@tauri-apps/cli": "^1.5.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 65ba8fc..f6fc9bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ dependencies: styled-components: specifier: ^5.3.10 version: 5.3.10(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + zod: + specifier: ^3.22.4 + version: 3.22.4 devDependencies: '@tauri-apps/cli': @@ -3344,3 +3347,7 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} dev: true + + /zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + dev: false diff --git a/src/pages/dashboard/HotkeyList.tsx b/src/pages/dashboard/HotkeyList.tsx index 992521a..d01ce72 100644 --- a/src/pages/dashboard/HotkeyList.tsx +++ b/src/pages/dashboard/HotkeyList.tsx @@ -3,10 +3,11 @@ import { Link, useNavigate } from 'react-router-dom'; import HotkeyConfigCard from '../../components/hotkey/HotkeyConfigCard'; import Grid from '@mui/material/Grid'; import Button from '@mui/material/Button'; -import { deleteProfile, getProfile, hotKeyConfig } from '../../store/config'; +import { createProfile, deleteProfile, getProfile, hotKeyConfig } from '../../store/config'; import HotkeyConfigList from '../../components/hotkey/HotkeyConfigList'; import { StoreServiceInst } from '../../services/store'; import { MessageServiceInst } from '../../services/message'; +import { ShareServiceInst } from '../../services/share'; const HotkeyList: FC = () => { const navigate = useNavigate(); @@ -34,6 +35,29 @@ const HotkeyList: FC = () => { await deleteProfile(id); }, []); + const onShare = useCallback(async (id: string) => { + console.log('onShare', id); + const profile = getProfile(id); + if (!profile) { + return; + } + + await ShareServiceInst.share(profile); + MessageServiceInst.success('已复制到剪贴板'); + }, []); + + const onReadShare = useCallback(async () => { + const result = await ShareServiceInst.read(); + + if (!result.isValid) { + MessageServiceInst.error('读取失败, 配置不合法'); + return; + } + + await createProfile(result.profile); + MessageServiceInst.success('已成功读取配置'); + }, []); + return (
diff --git a/src/services/share.ts b/src/services/share.ts new file mode 100644 index 0000000..0174ff1 --- /dev/null +++ b/src/services/share.ts @@ -0,0 +1,63 @@ +import { clipboard } from '@tauri-apps/api'; +import { IHotkeyProfileItem, IShareProfileItem } from '../share/types'; +import { + transformProfileConfig2ShareConfig, + transformShareConfig2ProfileConfig, + validateProfileShareConfig, +} from '../share/utils'; + +export class ShareService { + static instance: ShareService; + + constructor() { + // + } + + static getSelf() { + if (!this.instance) { + this.instance = new ShareService(); + } + + return this.instance; + } + + async share(profile: IHotkeyProfileItem) { + const shareText = transformProfileConfig2ShareConfig(profile); + await clipboard.writeText(JSON.stringify(shareText)); + } + + async read(): Promise< + | { + isValid: false; + } + | { + isValid: true; + profile: IHotkeyProfileItem; + } + > { + const text = await clipboard.readText(); + + if (!text) { + return { + isValid: false, + }; + } + + const isValid = validateProfileShareConfig(text); + + if (!isValid) { + return { + isValid: false, + }; + } + + return { + isValid: true, + profile: transformShareConfig2ProfileConfig( + JSON.parse(text) as IShareProfileItem + ), + }; + } +} + +export const ShareServiceInst = ShareService.getSelf(); diff --git a/src/share/types.ts b/src/share/types.ts index ab8822d..a1ea85e 100644 --- a/src/share/types.ts +++ b/src/share/types.ts @@ -33,3 +33,9 @@ export type IHotkeyConfig = { }; export type IHotKeyProfileCreateItem = Omit; + +// 分享配置 +export interface IShareProfileItem { + type: 'profile', + value: Omit; +} diff --git a/src/share/utils.ts b/src/share/utils.ts index b291068..b9c6b0f 100644 --- a/src/share/utils.ts +++ b/src/share/utils.ts @@ -1,9 +1,11 @@ import { nanoid } from 'nanoid'; +import { z } from 'zod'; import { IHotkeyConfig, IHotkeyProfileItem, IHotkeyRawConfig, IHotkeyRawConfigItem, + IShareProfileItem, } from './types'; export const getInitConfig = (): IHotkeyConfig => { @@ -48,3 +50,55 @@ export const transformProfileConfig2GameConfig = ( }, }; }; + +export const transformProfileConfig2ShareConfig = (profile: IHotkeyProfileItem): IShareProfileItem => { + const shareItem: IShareProfileItem = { + type: 'profile', + value: { + title: profile.title, + config: profile.config + } + }; + + return shareItem; +}; + +const shareProfileSchema = z.object({ + type: z.string(), + value: z.object({ + title: z.string(), + config: z.array(z.object({ + label: z.string(), + value: z.string() + })) + }) +}); + + +export const validateProfileShareConfig = (text: string): boolean => { + if (text.trim() === '') { + return false; + } + + let isValid = true; + + try { + const val = JSON.parse(text); + shareProfileSchema.parse(val); + isValid = true; + } catch(e) { + isValid = false; + } + + return isValid; +} + +export const transformShareConfig2ProfileConfig = (share: IShareProfileItem): IHotkeyProfileItem => { + const profile: IHotkeyProfileItem = { + id: nanoid(), + title: share.value.title, + config: share.value.config + }; + + return profile; +}