From 8b0ddcececd80b760434432e6746b37b12eb7b98 Mon Sep 17 00:00:00 2001 From: Vixalie Date: Tue, 11 Mar 2025 16:41:22 +0800 Subject: [PATCH] completely refactor patterns atom workflow. --- src/context/Patterns.tsx | 9 +++-- .../pattern-editor/PatternHeader.tsx | 11 +++--- .../pattern-library/PatternDetail.module.css | 4 ++- .../pattern-library/PatternDetail.tsx | 8 +++-- .../pattern-library/Patterns.tsx | 36 ++++++++++++------- src/pages/CreatePattern.tsx | 15 ++++---- src/pages/PatternNavigator.tsx | 6 ++-- 7 files changed, 53 insertions(+), 36 deletions(-) diff --git a/src/context/Patterns.tsx b/src/context/Patterns.tsx index f073600..5248654 100644 --- a/src/context/Patterns.tsx +++ b/src/context/Patterns.tsx @@ -1,7 +1,7 @@ import { invoke } from '@tauri-apps/api/core'; import dayjs from 'dayjs'; import { atom, useSetAtom } from 'jotai'; -import { atomWithDefault, atomWithRefresh } from 'jotai/utils'; +import { atomWithRefresh } from 'jotai/utils'; import { get, reduce } from 'lodash-es'; import { v4 } from 'uuid'; import { NotificationType, ToastDuration, useNotification } from '../components/Notifications'; @@ -125,9 +125,6 @@ export const SearchKeywordAtom = atom(null); export const PatternsAtom = atomWithRefresh(async (get) => { try { const keyword = get(SearchKeywordAtom); - if (keyword === null) { - return []; - } const patterns = await invoke('list_patterns', { keyword }); return patterns; } catch (e) { @@ -135,7 +132,7 @@ export const PatternsAtom = atomWithRefresh(async (get) => { } return []; }); -export const SelectedPatternIdAtom = atomWithDefault(null); +export const SelectedPatternIdAtom = atom(null); export const CurrentPatternAtom = atom(async (get) => { try { const patternId = get(SelectedPatternIdAtom); @@ -181,6 +178,7 @@ export function useSavePattern() { try { await invoke('save_pattern', { pattern }); refreshPatterns(); + return true; } catch (error) { console.error('[save pattern]', error); showToast( @@ -190,6 +188,7 @@ export function useSavePattern() { ToastDuration.MEDIUM, ); } + return false; }; return savePattern; diff --git a/src/page-components/pattern-editor/PatternHeader.tsx b/src/page-components/pattern-editor/PatternHeader.tsx index 6a7d135..8895b94 100644 --- a/src/page-components/pattern-editor/PatternHeader.tsx +++ b/src/page-components/pattern-editor/PatternHeader.tsx @@ -1,18 +1,19 @@ import { Icon } from '@iconify/react/dist/iconify.js'; -import { useAtom } from 'jotai'; +import { useAtomValue, useSetAtom } from 'jotai'; import { FC, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import EditableContent from '../../components/EditableContent'; -import { CurrentPatternAtom } from '../../context/Patterns'; +import { CurrentPatternAtom, SelectedPatternIdAtom } from '../../context/Patterns'; import styles from './PatternHeader.module.css'; const PatternHeader: FC = () => { const navigate = useNavigate(); - const [currentPattern, setPattern] = useAtom(CurrentPatternAtom); + const currentPattern = useAtomValue(CurrentPatternAtom); + const setActivePattern = useSetAtom(SelectedPatternIdAtom); const handleClosePattern = useCallback(() => { - setPattern(null); + setActivePattern(null); navigate('/library'); - }, [currentPattern]); + }, []); return (
diff --git a/src/page-components/pattern-library/PatternDetail.module.css b/src/page-components/pattern-library/PatternDetail.module.css index c87a9e9..cfaec95 100644 --- a/src/page-components/pattern-library/PatternDetail.module.css +++ b/src/page-components/pattern-library/PatternDetail.module.css @@ -9,7 +9,9 @@ gap: calc(var(--spacing) * 2); } .empty_promption { - flex: 1; + flex: 2; + border-radius: calc(var(--border-radius) * 2); + background-color: var(--color-surface-container); display: flex; flex-direction: column; align-items: stretch; diff --git a/src/page-components/pattern-library/PatternDetail.tsx b/src/page-components/pattern-library/PatternDetail.tsx index d73e88c..bad9916 100644 --- a/src/page-components/pattern-library/PatternDetail.tsx +++ b/src/page-components/pattern-library/PatternDetail.tsx @@ -1,6 +1,6 @@ import { useAtomValue } from 'jotai'; import { FC } from 'react'; -import { CurrentPatternAtom } from '../../context/Patterns'; +import { CurrentPatternAtom, Pattern } from '../../context/Patterns'; import styles from './PatternDetail.module.css'; const EmptyPromption: FC = () => { @@ -14,10 +14,14 @@ const EmptyPromption: FC = () => { ); }; +const Detail: FC<{ pattern: Pattern }> = ({ pattern }) => { + return
; +}; + const PatternDetail: FC = () => { const currentPattern = useAtomValue(CurrentPatternAtom); - return
{!currentPattern && }
; + return !currentPattern ? : ; }; export default PatternDetail; diff --git a/src/page-components/pattern-library/Patterns.tsx b/src/page-components/pattern-library/Patterns.tsx index ed6c6e0..5ed5cb3 100644 --- a/src/page-components/pattern-library/Patterns.tsx +++ b/src/page-components/pattern-library/Patterns.tsx @@ -1,29 +1,35 @@ import { Icon } from '@iconify/react/dist/iconify.js'; import cx from 'clsx'; -import { useAtom, useAtomValue } from 'jotai'; +import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import { FC, useCallback, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useDebounce } from 'react-use'; import { ScrollArea } from '../../components/ScrollArea'; -import { CurrentPatternAtom, Pattern, PatternsAtom, totalDuration } from '../../context/Patterns'; +import { + Pattern, + PatternsAtom, + SearchKeywordAtom, + SelectedPatternIdAtom, + totalDuration, +} from '../../context/Patterns'; import styles from './Patterns.module.css'; const PatternCard: FC<{ pattern: Pattern }> = ({ pattern }) => { - const [currentPattern, setCurrentPattern] = useAtom(CurrentPatternAtom); + const [selectedId, setSelectedId] = useAtom(SelectedPatternIdAtom); const navigate = useNavigate(); const duration = useMemo(() => { return totalDuration(pattern) / 1000; }, [pattern]); - const selected = useMemo(() => currentPattern?.id === pattern.id, [currentPattern, pattern]); + const selected = useMemo(() => selectedId === pattern.id, [selectedId, pattern]); const handleSingleClick = useCallback(() => { - if (currentPattern?.id === pattern.id) { - setCurrentPattern(null); + if (selectedId === pattern.id) { + setSelectedId(null); } else { - setCurrentPattern(pattern); + setSelectedId(pattern.id); } - }, [pattern, currentPattern]); + }, [pattern, selectedId]); const handleDblClick = useCallback(() => { - setCurrentPattern(pattern); + setSelectedId(pattern.id); navigate('/pattern-editor/edit'); }, [pattern]); @@ -40,8 +46,14 @@ const PatternCard: FC<{ pattern: Pattern }> = ({ pattern }) => { const Patterns: FC = () => { const [rawKeyword, setRawKeyword] = useState(''); - const [keyword, setKeyword] = useState(null); - const patterns = useAtomValue(PatternsAtom(keyword)); + const [keyword, setKeyword] = useAtom(SearchKeywordAtom); + const patterns = useAtomValue(PatternsAtom); + const setPatternSelection = useSetAtom(SelectedPatternIdAtom); + const navigate = useNavigate(); + const createNewAction = useCallback(() => { + setPatternSelection(null); + navigate('/pattern-editor/new'); + }, []); useDebounce( () => { @@ -67,7 +79,7 @@ const Patterns: FC = () => { onChange={(evt) => setRawKeyword(evt.currentTarget.value)} />
- diff --git a/src/pages/CreatePattern.tsx b/src/pages/CreatePattern.tsx index 649f428..16af4a1 100644 --- a/src/pages/CreatePattern.tsx +++ b/src/pages/CreatePattern.tsx @@ -1,16 +1,16 @@ -import { invoke } from '@tauri-apps/api/core'; import cx from 'clsx'; import { useSetAtom } from 'jotai'; import { FC, useActionState } from 'react'; import { useNavigate } from 'react-router-dom'; import { NotificationType, ToastDuration, useNotification } from '../components/Notifications'; -import { CurrentPatternAtom, Pattern } from '../context/Patterns'; +import { Pattern, SelectedPatternIdAtom, useSavePattern } from '../context/Patterns'; import styles from './CreatePattern.module.css'; const CreatePattern: FC = () => { const { showToast } = useNotification(); const navigate = useNavigate(); - const loadPattern = useSetAtom(CurrentPatternAtom); + const loadPattern = useSetAtom(SelectedPatternIdAtom); + const savePattern = useSavePattern(); const [errState, handleFormSubmit] = useActionState(async (state, formData) => { const patternName = formData.get('pattern_name') as string | null; @@ -29,9 +29,8 @@ const CreatePattern: FC = () => { newPattern.name = patternName; try { - await invoke('save_pattern', { pattern: newPattern }); - const reloadedPattern = await invoke('get_pattern', { patternId: newPattern.id }); - if (!reloadedPattern) { + const updated = await savePattern(newPattern); + if (!updated) { showToast( NotificationType.ERROR, 'Failed to reload the created pattern. Please try again.', @@ -42,8 +41,8 @@ const CreatePattern: FC = () => { navigate('/library'); return true; } - loadPattern(reloadedPattern); - navigate('./edit'); + loadPattern(newPattern.id); + navigate('/pattern-editor/edit'); } catch (e) { console.error('[save pattern]', e); loadPattern(null); diff --git a/src/pages/PatternNavigator.tsx b/src/pages/PatternNavigator.tsx index 9f203d4..9f1dbbc 100644 --- a/src/pages/PatternNavigator.tsx +++ b/src/pages/PatternNavigator.tsx @@ -1,12 +1,12 @@ import { useAtomValue } from 'jotai'; import { FC } from 'react'; import { Navigate } from 'react-router-dom'; -import { CurrentPatternAtom } from '../context/Patterns'; +import { SelectedPatternIdAtom } from '../context/Patterns'; const PatternNavigator: FC = () => { - const currentPattern = useAtomValue(CurrentPatternAtom); + const selected = useAtomValue(SelectedPatternIdAtom); - return currentPattern === null ? : ; + return selected === null ? : ; }; export default PatternNavigator;