mirror of
https://git.unlock-music.dev/um/um-react.git
synced 2024-11-23 20:42:17 +00:00
fix: settings dirty state tracking + toast on save/discard (#38)
This commit is contained in:
parent
7a363d1dc5
commit
48b8932a1d
@ -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 (
|
||||
<Flex flexDir="column" flex={1}>
|
||||
@ -104,7 +125,16 @@ export function Settings() {
|
||||
<VStack mt="4" alignItems="flex-start" w="full">
|
||||
<Flex flexDir="row" gap="2" w="full">
|
||||
<Center>
|
||||
<Box color="gray">设置会在保存后生效。</Box>
|
||||
{isSettingsNotSaved ? (
|
||||
<Box color="gray">
|
||||
有未储存的更改{' '}
|
||||
<chakra.span color="red" wordBreak="keep-all">
|
||||
设定将在保存后生效
|
||||
</chakra.span>
|
||||
</Box>
|
||||
) : (
|
||||
<Box color="gray">设定将在保存后生效</Box>
|
||||
)}
|
||||
</Center>
|
||||
<Spacer />
|
||||
<HStack gap="2" justifyContent="flex-end">
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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<ProductionSettings>) => {
|
||||
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<Omit<StagingQMCv2Key, 'id'>[]>) {
|
||||
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<Omit<StagingKWMv2Key, 'id'>[]>) {
|
||||
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,
|
||||
|
Loading…
Reference in New Issue
Block a user