import { KeyboardKey, KeyboardModifier } from '@/Services/IOService' import { WebApplication } from '@/UIModels/Application' import { AppState } from '@/UIModels/AppState' import { PANEL_NAME_NOTES } from '@/Constants' import { PrefKey } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' import { FunctionComponent } from 'preact' import { useCallback, useEffect, useRef, useState } from 'preact/hooks' import { NoAccountWarning } from '@/Components/NoAccountWarning' import { NotesList } from '@/Components/NotesList' import { NotesListOptionsMenu } from '@/Components/NotesList/NotesListOptionsMenu' import { SearchOptions } from '@/Components/SearchOptions' import { PanelSide, ResizeFinishCallback, PanelResizer, PanelResizeType } from '@/Components/PanelResizer' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' import { isStateDealloced } from '@/UIModels/AppState/AbstractState' type Props = { application: WebApplication appState: AppState } export const NotesView: FunctionComponent = observer(({ application, appState }: Props) => { if (isStateDealloced(appState)) { return null } const notesViewPanelRef = useRef(null) const displayOptionsMenuRef = useRef(null) const { completedFullSync, displayOptions, noteFilterText, optionsSubtitle, panelTitle, renderedNotes, selectedNotes, searchBarElement, paginate, panelWidth, } = appState.notesView const createNewNote = useCallback(() => appState.notesView.createNewNote, [appState]) const onFilterEnter = useCallback(() => appState.notesView.onFilterEnter, [appState]) const clearFilterText = useCallback(() => appState.notesView.clearFilterText, [appState]) const setNoteFilterText = useCallback((text: string) => appState.notesView.setNoteFilterText(text), [appState]) const selectNextNote = useCallback(() => appState.notesView.selectNextNote, [appState]) const selectPreviousNote = useCallback(() => appState.notesView.selectPreviousNote, [appState]) const [showDisplayOptionsMenu, setShowDisplayOptionsMenu] = useState(false) const [focusedSearch, setFocusedSearch] = useState(false) const [closeDisplayOptMenuOnBlur] = useCloseOnBlur(displayOptionsMenuRef, setShowDisplayOptionsMenu) useEffect(() => { /** * In the browser we're not allowed to override cmd/ctrl + n, so we have to * use Control modifier as well. These rules don't apply to desktop, but * probably better to be consistent. */ const newNoteKeyObserver = application.io.addKeyObserver({ key: 'n', modifiers: [KeyboardModifier.Meta, KeyboardModifier.Ctrl], onKeyDown: (event) => { event.preventDefault() createNewNote() }, }) const nextNoteKeyObserver = application.io.addKeyObserver({ key: KeyboardKey.Down, elements: [document.body, ...(searchBarElement ? [searchBarElement] : [])], onKeyDown: () => { if (searchBarElement === document.activeElement) { searchBarElement?.blur() } selectNextNote() }, }) const previousNoteKeyObserver = application.io.addKeyObserver({ key: KeyboardKey.Up, element: document.body, onKeyDown: () => { selectPreviousNote() }, }) const searchKeyObserver = application.io.addKeyObserver({ key: 'f', modifiers: [KeyboardModifier.Meta, KeyboardModifier.Shift], onKeyDown: () => { if (searchBarElement) { searchBarElement.focus() } }, }) return () => { newNoteKeyObserver() nextNoteKeyObserver() previousNoteKeyObserver() searchKeyObserver() } }, [application, createNewNote, selectPreviousNote, searchBarElement, selectNextNote]) const onNoteFilterTextChange = useCallback( (e: Event) => { setNoteFilterText((e.target as HTMLInputElement).value) }, [setNoteFilterText], ) const onSearchFocused = useCallback(() => setFocusedSearch(true), []) const onSearchBlurred = useCallback(() => setFocusedSearch(false), []) const onNoteFilterKeyUp = useCallback( (e: KeyboardEvent) => { if (e.key === KeyboardKey.Enter) { onFilterEnter() } }, [onFilterEnter], ) const panelResizeFinishCallback: ResizeFinishCallback = useCallback( (width, _lastLeft, _isMaxWidth, isCollapsed) => { application.setPreference(PrefKey.NotesPanelWidth, width).catch(console.error) appState.noteTags.reloadTagsContainerMaxWidth() appState.panelDidResize(PANEL_NAME_NOTES, isCollapsed) }, [appState, application], ) const panelWidthEventCallback = useCallback(() => { appState.noteTags.reloadTagsContainerMaxWidth() }, [appState]) const toggleDisplayOptionsMenu = useCallback(() => { setShowDisplayOptionsMenu(!showDisplayOptionsMenu) }, [showDisplayOptionsMenu]) return (
{panelTitle}
{noteFilterText && ( )}
{(focusedSearch || noteFilterText) && (
)}
Options
{optionsSubtitle}
{showDisplayOptionsMenu && ( )}
{completedFullSync && !renderedNotes.length ?

No notes.

: null} {!completedFullSync && !renderedNotes.length ? (

Loading notes...

) : null} {renderedNotes.length ? ( ) : null}
{notesViewPanelRef.current && ( )}
) })