import { KeyboardKey, KeyboardModifier } from '@/Services/IOService' import { WebApplication } from '@/UIModels/Application' import { AppState } from '@/UIModels/AppState' import { PANEL_NAME_NOTES } from '@/Constants' import { PrefKey, SystemViewId } from '@standardnotes/snjs' import { observer } from 'mobx-react-lite' import { ChangeEventHandler, FunctionComponent, KeyboardEventHandler, useCallback, useEffect, useMemo, useRef, useState, } from 'react' import ContentList from '@/Components/ContentListView/ContentList' import NoAccountWarningWrapper from '@/Components/NoAccountWarning/NoAccountWarning' import SearchOptions from '@/Components/SearchOptions/SearchOptions' import PanelResizer, { PanelSide, ResizeFinishCallback, PanelResizeType } from '@/Components/PanelResizer/PanelResizer' import { Disclosure, DisclosureButton, DisclosurePanel } from '@reach/disclosure' import { useCloseOnBlur } from '@/Hooks/useCloseOnBlur' import ContentListOptionsMenu from './ContentListOptionsMenu' type Props = { application: WebApplication appState: AppState } const ContentListView: FunctionComponent = ({ application, appState }) => { const itemsViewPanelRef = useRef(null) const displayOptionsMenuRef = useRef(null) const { completedFullSync, noteFilterText, optionsSubtitle, panelTitle, renderedItems, setNoteFilterText, searchBarElement, selectNextItem, selectPreviousItem, onFilterEnter, clearFilterText, paginate, panelWidth, createNewNote, } = appState.contentListView const { selectedItems } = appState.selectedItems const [showDisplayOptionsMenu, setShowDisplayOptionsMenu] = useState(false) const [focusedSearch, setFocusedSearch] = useState(false) const [closeDisplayOptMenuOnBlur] = useCloseOnBlur(displayOptionsMenuRef, setShowDisplayOptionsMenu) const isFilesSmartView = useMemo( () => appState.tags.selected?.uuid === SystemViewId.Files, [appState.tags.selected?.uuid], ) const addNewItem = useCallback(() => { if (isFilesSmartView) { void appState.files.uploadNewFile() } else { void createNewNote() } }, [appState.files, createNewNote, isFilesSmartView]) 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() addNewItem() }, }) const nextNoteKeyObserver = application.io.addKeyObserver({ key: KeyboardKey.Down, elements: [document.body, ...(searchBarElement ? [searchBarElement] : [])], onKeyDown: () => { if (searchBarElement === document.activeElement) { searchBarElement?.blur() } selectNextItem() }, }) const previousNoteKeyObserver = application.io.addKeyObserver({ key: KeyboardKey.Up, element: document.body, onKeyDown: () => { selectPreviousItem() }, }) const searchKeyObserver = application.io.addKeyObserver({ key: 'f', modifiers: [KeyboardModifier.Meta, KeyboardModifier.Shift], onKeyDown: () => { if (searchBarElement) { searchBarElement.focus() } }, }) return () => { newNoteKeyObserver() nextNoteKeyObserver() previousNoteKeyObserver() searchKeyObserver() } }, [addNewItem, application.io, createNewNote, searchBarElement, selectNextItem, selectPreviousItem]) const onNoteFilterTextChange: ChangeEventHandler = useCallback( (e) => { setNoteFilterText(e.target.value) }, [setNoteFilterText], ) const onSearchFocused = useCallback(() => setFocusedSearch(true), []) const onSearchBlurred = useCallback(() => setFocusedSearch(false), []) const onNoteFilterKeyUp: KeyboardEventHandler = useCallback( (e) => { 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]) const addButtonLabel = useMemo( () => (isFilesSmartView ? 'Upload file' : 'Create a new note in the selected tag'), [isFilesSmartView], ) return (
{panelTitle}
{noteFilterText && ( )}
{(focusedSearch || noteFilterText) && (
)}
Options
{optionsSubtitle}
{showDisplayOptionsMenu && ( )}
{completedFullSync && !renderedItems.length ?

No items.

: null} {!completedFullSync && !renderedItems.length ?

Loading...

: null} {renderedItems.length ? ( ) : null}
{itemsViewPanelRef.current && ( )}
) } export default observer(ContentListView)