From 9db5d94a14ff3e1b5765f5a49728381b3cbc9461 Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sun, 2 Jul 2023 15:32:47 +0100 Subject: [PATCH 1/9] fix: error handling when there are no results --- src/util/DatabaseKeyExtractor.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/util/DatabaseKeyExtractor.ts b/src/util/DatabaseKeyExtractor.ts index 5158820..0a4b8e9 100644 --- a/src/util/DatabaseKeyExtractor.ts +++ b/src/util/DatabaseKeyExtractor.ts @@ -32,7 +32,12 @@ export class DatabaseKeyExtractor { return null; } - const keys = db.exec('select file_path, ekey from `audio_file_ekey_table`')[0].values; + const result = db.exec('select file_path, ekey from audio_file_ekey_table'); + if (result.length === 0) { + return []; + } + + const keys = result[0].values; return keys.map(([path, ekey]) => ({ // strip dir name name: getFileName(String(path)), From da29b389ea780524ce90888e0cd4b15def13e5c3 Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sun, 2 Jul 2023 15:35:01 +0100 Subject: [PATCH 2/9] fix: typo --- src/crypto/{pasreKuwo.ts => parseKuwo.ts} | 0 src/features/settings/settingsSelector.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/crypto/{pasreKuwo.ts => parseKuwo.ts} (100%) diff --git a/src/crypto/pasreKuwo.ts b/src/crypto/parseKuwo.ts similarity index 100% rename from src/crypto/pasreKuwo.ts rename to src/crypto/parseKuwo.ts diff --git a/src/features/settings/settingsSelector.ts b/src/features/settings/settingsSelector.ts index 42d61b7..7e2a038 100644 --- a/src/features/settings/settingsSelector.ts +++ b/src/features/settings/settingsSelector.ts @@ -1,4 +1,4 @@ -import { parseKuwoHeader } from '~/crypto/pasreKuwo'; +import { parseKuwoHeader } from '~/crypto/parseKuwo'; import type { RootState } from '~/store'; import { closestByLevenshtein } from '~/util/levenshtein'; import { hasOwn } from '~/util/objects'; From 71d5b656e2bda979669c593d4f520105faab3108 Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sun, 2 Jul 2023 15:35:54 +0100 Subject: [PATCH 3/9] fix: update key placeholders (#38) --- .../settings/panels/KWMv2/KWMv2EKeyItem.tsx | 7 ++++++- .../settings/panels/QMCv2/QMCv2EKeyItem.tsx | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/features/settings/panels/KWMv2/KWMv2EKeyItem.tsx b/src/features/settings/panels/KWMv2/KWMv2EKeyItem.tsx index 35244b4..f9969a2 100644 --- a/src/features/settings/panels/KWMv2/KWMv2EKeyItem.tsx +++ b/src/features/settings/panels/KWMv2/KWMv2EKeyItem.tsx @@ -53,7 +53,12 @@ export const KWMv2EKeyItem = memo(({ id, ekey, quality, rid, i }: StagingKWMv2Ke - updateKey('ekey', e)} /> + updateKey('ekey', e)} + /> {ekey.length || '?'} diff --git a/src/features/settings/panels/QMCv2/QMCv2EKeyItem.tsx b/src/features/settings/panels/QMCv2/QMCv2EKeyItem.tsx index bf133d0..305e4de 100644 --- a/src/features/settings/panels/QMCv2/QMCv2EKeyItem.tsx +++ b/src/features/settings/panels/QMCv2/QMCv2EKeyItem.tsx @@ -30,13 +30,23 @@ export const QMCv2EKeyItem = memo(({ id, name, ekey, i }: { id: string; name: st - updateKey('name', e)} /> + updateKey('name', e)} + /> - updateKey('ekey', e)} /> + updateKey('ekey', e)} + /> {ekey.length || '?'} From a5e3a0f52e8e6a9070397e8f603a9f04b26bbfbd Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sun, 2 Jul 2023 15:36:59 +0100 Subject: [PATCH 4/9] fix: consistent wording on "secret import" and add 3-dots in menu (#38) --- src/components/ImportSecretModal.tsx | 2 +- src/features/settings/panels/PanelKWMv2Key.tsx | 2 +- src/features/settings/panels/PanelQMCv2Key.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ImportSecretModal.tsx b/src/components/ImportSecretModal.tsx index 2a83999..2dd9870 100644 --- a/src/components/ImportSecretModal.tsx +++ b/src/components/ImportSecretModal.tsx @@ -28,7 +28,7 @@ export function ImportSecretModal({ clientName, children, show, onClose, onImpor - 导入密钥数据库 + 从文件导入密钥
diff --git a/src/features/settings/panels/PanelKWMv2Key.tsx b/src/features/settings/panels/PanelKWMv2Key.tsx index 9c41ac4..42289a4 100644 --- a/src/features/settings/panels/PanelKWMv2Key.tsx +++ b/src/features/settings/panels/PanelKWMv2Key.tsx @@ -88,7 +88,7 @@ export function PanelKWMv2Key() { }> setShowImportModal(true)} icon={}> - 从文件导入密钥 + 从文件导入密钥… }> diff --git a/src/features/settings/panels/PanelQMCv2Key.tsx b/src/features/settings/panels/PanelQMCv2Key.tsx index 2d9d67a..32bcffd 100644 --- a/src/features/settings/panels/PanelQMCv2Key.tsx +++ b/src/features/settings/panels/PanelQMCv2Key.tsx @@ -110,7 +110,7 @@ export function PanelQMCv2Key() { }> setShowImportModal(true)} icon={}> - 从文件导入密钥 + 从文件导入密钥… }> From f089f929302d66710c52d3eaabae7be86a24ee43 Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sun, 2 Jul 2023 15:38:52 +0100 Subject: [PATCH 5/9] fix: settings dirty state tracking + toast on save/discard (#38) --- src/features/settings/Settings.tsx | 38 ++++++++++++++++++++--- src/features/settings/settingsSelector.ts | 2 ++ src/features/settings/settingsSlice.ts | 20 ++++++++++-- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/features/settings/Settings.tsx b/src/features/settings/Settings.tsx index a59155b..6e3ab6a 100644 --- a/src/features/settings/Settings.tsx +++ b/src/features/settings/Settings.tsx @@ -1,4 +1,5 @@ import { + chakra, Box, Button, Center, @@ -20,13 +21,15 @@ import { Text, VStack, useBreakpointValue, + useToast, } from '@chakra-ui/react'; import { PanelQMCv2Key } from './panels/PanelQMCv2Key'; import { useState } from 'react'; import { MdExpandMore, MdMenu, MdOutlineSettingsBackupRestore } from 'react-icons/md'; -import { useAppDispatch } from '~/hooks'; +import { useAppDispatch, useAppSelector } from '~/hooks'; import { commitStagingChange, discardStagingChanges } from './settingsSlice'; import { PanelKWMv2Key } from './panels/PanelKWMv2Key'; +import { selectIsSettingsNotSaved } from './settingsSelector'; const TABS: { name: string; Tab: () => JSX.Element }[] = [ { name: 'QMCv2 密钥', Tab: PanelQMCv2Key }, @@ -38,6 +41,7 @@ const TABS: { name: string; Tab: () => JSX.Element }[] = [ ]; export function Settings() { + const toast = useToast(); const dispatch = useAppDispatch(); const isLargeWidthDevice = useBreakpointValue({ @@ -49,8 +53,25 @@ export function Settings() { const handleTabChange = (idx: number) => { setTabIndex(idx); }; - const handleResetSettings = () => dispatch(discardStagingChanges()); - const handleApplySettings = () => dispatch(commitStagingChange()); + const handleResetSettings = () => { + dispatch(discardStagingChanges()); + + toast({ + status: 'info', + title: '未储存的设定已舍弃', + description: '已还原到更改前的状态。', + isClosable: true, + }); + }; + const handleApplySettings = () => { + dispatch(commitStagingChange()); + toast({ + status: 'success', + title: '设定已应用', + isClosable: true, + }); + }; + const isSettingsNotSaved = useAppSelector(selectIsSettingsNotSaved); return ( @@ -104,7 +125,16 @@ export function Settings() {
- 设置会在保存后生效。 + {isSettingsNotSaved ? ( + + 有未储存的更改{' '} + + 设定将在保存后生效 + + + ) : ( + 设定将在保存后生效 + )}
diff --git a/src/features/settings/settingsSelector.ts b/src/features/settings/settingsSelector.ts index 7e2a038..6b695e5 100644 --- a/src/features/settings/settingsSelector.ts +++ b/src/features/settings/settingsSelector.ts @@ -4,6 +4,8 @@ import { closestByLevenshtein } from '~/util/levenshtein'; import { hasOwn } from '~/util/objects'; import { kwm2StagingToProductionKey } from './keyFormats'; +export const selectIsSettingsNotSaved = (state: RootState) => state.settings.dirty; + export const selectStagingQMCv2Settings = (state: RootState) => state.settings.staging.qmc2; export const selectFinalQMCv2Settings = (state: RootState) => state.settings.production.qmc2; diff --git a/src/features/settings/settingsSlice.ts b/src/features/settings/settingsSlice.ts index e5722e2..10bbaf1 100644 --- a/src/features/settings/settingsSlice.ts +++ b/src/features/settings/settingsSlice.ts @@ -37,16 +37,18 @@ export interface ProductionSettings { } export interface SettingsState { + dirty: boolean; staging: StagingSettings; production: ProductionSettings; } const initialState: SettingsState = { + dirty: false, staging: { - qmc2: { allowFuzzyNameSearch: false, keys: [] }, + qmc2: { allowFuzzyNameSearch: true, keys: [] }, kwm2: { keys: [] }, }, production: { - qmc2: { allowFuzzyNameSearch: false, keys: {} }, + qmc2: { allowFuzzyNameSearch: true, keys: {} }, kwm2: { keys: {} }, }, }; @@ -77,6 +79,7 @@ export const settingsSlice = createSlice({ reducers: { setProductionChanges: (_state, { payload }: PayloadAction) => { return { + dirty: false, production: payload, staging: productionToStaging(payload), }; @@ -84,14 +87,17 @@ export const settingsSlice = createSlice({ // qmc2AddKey(state) { state.staging.qmc2.keys.push({ id: nanoid(), name: '', ekey: '' }); + state.dirty = true; }, qmc2ImportKeys(state, { payload }: PayloadAction[]>) { const newItems = payload.map((item) => ({ id: nanoid(), ...item })); state.staging.qmc2.keys.push(...newItems); + state.dirty = true; }, qmc2DeleteKey(state, { payload: { id } }: PayloadAction<{ id: string }>) { const qmc2 = state.staging.qmc2; qmc2.keys = qmc2.keys.filter((item) => item.id !== id); + state.dirty = true; }, qmc2UpdateKey( state, @@ -100,25 +106,31 @@ export const settingsSlice = createSlice({ const keyItem = state.staging.qmc2.keys.find((item) => item.id === id); if (keyItem) { keyItem[field] = value; + state.dirty = true; } }, qmc2ClearKeys(state) { state.staging.qmc2.keys = []; + state.dirty = true; }, qmc2AllowFuzzyNameSearch(state, { payload: { enable } }: PayloadAction<{ enable: boolean }>) { state.staging.qmc2.allowFuzzyNameSearch = enable; + state.dirty = true; }, // TODO: reuse the logic somehow? kwm2AddKey(state) { state.staging.kwm2.keys.push({ id: nanoid(), ekey: '', quality: '', rid: '' }); + state.dirty = true; }, kwm2ImportKeys(state, { payload }: PayloadAction[]>) { const newItems = payload.map((item) => ({ id: nanoid(), ...item })); state.staging.kwm2.keys.push(...newItems); + state.dirty = true; }, kwm2DeleteKey(state, { payload: { id } }: PayloadAction<{ id: string }>) { const kwm2 = state.staging.kwm2; kwm2.keys = kwm2.keys.filter((item) => item.id !== id); + state.dirty = true; }, kwm2UpdateKey( state, @@ -127,18 +139,22 @@ export const settingsSlice = createSlice({ const keyItem = state.staging.kwm2.keys.find((item) => item.id === id); if (keyItem) { keyItem[field] = value; + state.dirty = true; } }, kwm2ClearKeys(state) { state.staging.kwm2.keys = []; + state.dirty = true; }, // discardStagingChanges: (state) => { + state.dirty = false; state.staging = productionToStaging(state.production); }, commitStagingChange: (state) => { const production = stagingToProduction(state.staging); return { + dirty: false, // Sync back to staging staging: productionToStaging(production), production, From 60547f0a0a93d1b604a5e471e9c31aea64341978 Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sun, 2 Jul 2023 15:39:14 +0100 Subject: [PATCH 6/9] fix: warn when 0 keys imported (#38) --- src/features/settings/panels/PanelKWMv2Key.tsx | 10 ++++++++-- src/features/settings/panels/PanelQMCv2Key.tsx | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/features/settings/panels/PanelKWMv2Key.tsx b/src/features/settings/panels/PanelKWMv2Key.tsx index 42289a4..ba6d4e7 100644 --- a/src/features/settings/panels/PanelKWMv2Key.tsx +++ b/src/features/settings/panels/PanelKWMv2Key.tsx @@ -49,8 +49,14 @@ export function PanelKWMv2Key() { const fileBuffer = await file.arrayBuffer(); keys = MMKVParser.parseKuwoEKey(new DataView(fileBuffer)); } - - if (keys) { + if (keys?.length === 0) { + toast({ + title: '未导入密钥', + description: '选择的密钥数据库文件未发现任何可用的密钥。', + isClosable: true, + status: 'warning', + }); + } else if (keys) { dispatch(kwm2ImportKeys(keys)); setShowImportModal(false); toast({ diff --git a/src/features/settings/panels/PanelQMCv2Key.tsx b/src/features/settings/panels/PanelQMCv2Key.tsx index 32bcffd..c4be627 100644 --- a/src/features/settings/panels/PanelQMCv2Key.tsx +++ b/src/features/settings/panels/PanelQMCv2Key.tsx @@ -71,14 +71,20 @@ export function PanelQMCv2Key() { qmc2Keys = Array.from(map.entries(), ([name, ekey]) => ({ name: getFileName(name), ekey })); } - if (qmc2Keys) { + if (qmc2Keys?.length === 0) { + toast({ + title: '未导入密钥', + description: '选择的密钥数据库文件未发现任何可用的密钥。', + isClosable: true, + status: 'warning', + }); + } else if (qmc2Keys) { dispatch(qmc2ImportKeys(qmc2Keys)); setShowImportModal(false); toast({ title: `导入成功 (${qmc2Keys.length})`, description: '记得保存更改来应用。', isClosable: true, - duration: 5000, status: 'success', }); } else { From b3d99289d10596e2c1227b4caef8e7bfb330d939 Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sun, 2 Jul 2023 15:40:13 +0100 Subject: [PATCH 7/9] fix: consistent wording for qmc setting tab title (#38) --- src/features/settings/panels/PanelQMCv2Key.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/settings/panels/PanelQMCv2Key.tsx b/src/features/settings/panels/PanelQMCv2Key.tsx index c4be627..06d5885 100644 --- a/src/features/settings/panels/PanelQMCv2Key.tsx +++ b/src/features/settings/panels/PanelQMCv2Key.tsx @@ -99,7 +99,7 @@ export function PanelQMCv2Key() { return ( - QMCv2 密钥 + QMCv2 解密密钥 From c434268ef77fb9849a1c4efa3f962e0903252249 Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sun, 2 Jul 2023 15:40:35 +0100 Subject: [PATCH 8/9] fix: add tooltip to hint user to check when unsure (#38) --- src/features/settings/panels/PanelQMCv2Key.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/features/settings/panels/PanelQMCv2Key.tsx b/src/features/settings/panels/PanelQMCv2Key.tsx index 06d5885..c309b2c 100644 --- a/src/features/settings/panels/PanelQMCv2Key.tsx +++ b/src/features/settings/panels/PanelQMCv2Key.tsx @@ -147,6 +147,7 @@ export function PanelQMCv2Key() { 」算法计算相似程度。 若密钥数量过多,匹配时可能会造成浏览器卡顿或无响应一段时间。 + 若不确定,请勾选该项。 } > From 45c484c5b18ce58df1bf6b167d1f22312826d122 Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sun, 2 Jul 2023 15:43:35 +0100 Subject: [PATCH 9/9] ci: use bookworm base image --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 027ff27..483cb38 100644 --- a/.drone.yml +++ b/.drone.yml @@ -5,7 +5,7 @@ name: default steps: - name: test & build - image: node:18.16.0-bullseye + image: node:18.16.1-bookworm commands: # - git config --global --add safe.directory "/drone/src" - corepack enable @@ -17,7 +17,7 @@ steps: npm_config_registry: https://registry.npmmirror.com - name: publish - image: node:18.16.0-bullseye + image: node:18.16.1-bookworm environment: DRONE_GITEA_SERVER: https://git.unlock-music.dev GITEA_API_KEY: