diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/editor/general.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/editor/general.tsx index c7a5615fcf..f0d8199b72 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/editor/general.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/editor/general.tsx @@ -24,7 +24,13 @@ import { useLiveData, useService, } from '@toeverything/infra'; -import { type ChangeEvent, useCallback, useMemo, useState } from 'react'; +import { + type ChangeEvent, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; import { menu, menuTrigger, searchInput, settingWrapper } from './style.css'; @@ -92,29 +98,32 @@ const getFontFamily = (font: string) => `${font}, ${fontStyleOptions[0].value}`; const FontMenuItems = ({ onSelect }: { onSelect: (font: string) => void }) => { const systemFontFamily = useService(SystemFontFamilyService).systemFontFamily; - const systemFontList = useLiveData(systemFontFamily.fontList$); + useEffect(() => { + systemFontFamily.loadFontList(); + systemFontFamily.clearSearch(); + }, [systemFontFamily]); + const isLoading = useLiveData(systemFontFamily.isLoading$); const result = useLiveData(systemFontFamily.result$); const searchText = useLiveData(systemFontFamily.searchText$); - const [inputValue, setInputValue] = useState(''); - const onInputChange = useCallback((e: ChangeEvent) => { - setInputValue(e.target.value); - }, []); + const onInputChange = useCallback( + (e: ChangeEvent) => { + systemFontFamily.search(e.target.value); + }, + [systemFontFamily] + ); const onInputKeyDown = useCallback( (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - systemFontFamily.search(inputValue); - } else if (e.key === 'Backspace' || e.key === 'Escape') { - systemFontFamily.clearSearch(); - } + e.stopPropagation(); // avoid typeahead search built-in in the menu }, - [inputValue, systemFontFamily] + [] ); + return (
void }) => { result.map(font => ( )) - ) : searchText && searchText.length > 0 ? ( -
not found
) : ( - systemFontList.map(font => ( - - )) +
not found
)} diff --git a/packages/frontend/core/src/modules/system-font-family/entities/system-font-family.ts b/packages/frontend/core/src/modules/system-font-family/entities/system-font-family.ts index bd08e6d8e8..8412878dbf 100644 --- a/packages/frontend/core/src/modules/system-font-family/entities/system-font-family.ts +++ b/packages/frontend/core/src/modules/system-font-family/entities/system-font-family.ts @@ -1,79 +1,61 @@ -import { DebugLogger } from '@affine/debug'; import { apis } from '@affine/electron-api'; -import { Entity, LiveData } from '@toeverything/infra'; import { - debounceTime, - distinctUntilChanged, - of, - shareReplay, - switchMap, - tap, -} from 'rxjs'; - -const logger = new DebugLogger('affine:system-font-family'); + effect, + Entity, + fromPromise, + LiveData, + mapInto, + onComplete, + onStart, +} from '@toeverything/infra'; +import { exhaustMap } from 'rxjs'; export class SystemFontFamily extends Entity { constructor() { super(); - this.loadFontList().catch(error => { - logger.error('Failed to load system font list', error); - }); } readonly searchText$ = new LiveData(null); readonly isLoading$ = new LiveData(false); readonly fontList$ = new LiveData([]); - readonly result$ = LiveData.from( - this.searchText$.pipe( - distinctUntilChanged(), - debounceTime(500), - switchMap(searchText => { - if (!searchText) { - return of([]); + readonly result$ = LiveData.computed(get => { + const fontList = get(this.fontList$); + const searchText = get(this.searchText$); + if (!searchText) { + return fontList; + } + + const filteredFonts = fontList.filter(font => + font.toLowerCase().includes(searchText.toLowerCase()) + ); + return filteredFonts; + }).throttleTime(500); + + loadFontList = effect( + exhaustMap(() => { + return fromPromise(async () => { + if (!apis?.fontList) { + return []; } - return this.fontList$.pipe( - tap(() => { - this.isLoading$.next(true); - }), - switchMap(fontList => { - const filteredFonts = fontList.filter(font => - font.toLowerCase().includes(searchText.toLowerCase()) - ); - this.isLoading$.next(false); - return of(filteredFonts); - }) - ); - }), - shareReplay({ - bufferSize: 1, - refCount: true, - }) - ), - [] + return apis.fontList.getSystemFonts(); + }).pipe( + mapInto(this.fontList$), + // TODO: catchErrorInto(this.error$), + onStart(() => { + this.isLoading$.next(true); + }), + onComplete(() => { + this.isLoading$.next(false); + }) + ); + }) ); - async loadFontList() { - if (!apis?.fontList) { - return; - } - try { - this.isLoading$.next(true); - const fontList = await apis.fontList.getSystemFonts(); - this.fontList$.next(fontList); - } catch (error) { - logger.error('Failed to load system font list', error); - } finally { - this.isLoading$.next(false); - } - } - search(searchText: string) { - if (!this.searchText$.value) return; this.searchText$.next(searchText); } clearSearch() { - if (!this.searchText$.value) return; this.searchText$.next(null); } }