refactor: notes state management (#1031)
This commit is contained in:
@@ -88,9 +88,7 @@ export const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
|
|||||||
|
|
||||||
const transactions: TransactionalMutation[] = []
|
const transactions: TransactionalMutation[] = []
|
||||||
|
|
||||||
if (application.getAppState().getActiveNoteController()?.isTemplateNote) {
|
await application.getAppState().notesView.insertCurrentIfTemplate()
|
||||||
await application.getAppState().getActiveNoteController().insertTemplatedNote()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (note.locked) {
|
if (note.locked) {
|
||||||
application.alertService.alert(STRING_EDIT_LOCKED_ATTEMPT).catch(console.error)
|
application.alertService.alert(STRING_EDIT_LOCKED_ATTEMPT).catch(console.error)
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ export class NoteGroupView extends PureComponent<Props, State> {
|
|||||||
const controllerGroup = this.application.noteControllerGroup
|
const controllerGroup = this.application.noteControllerGroup
|
||||||
this.removeChangeObserver = this.application.noteControllerGroup.addActiveControllerChangeObserver(() => {
|
this.removeChangeObserver = this.application.noteControllerGroup.addActiveControllerChangeObserver(() => {
|
||||||
const controllers = controllerGroup.noteControllers
|
const controllers = controllerGroup.noteControllers
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
controllers: controllers,
|
controllers: controllers,
|
||||||
})
|
})
|
||||||
@@ -63,7 +62,7 @@ export class NoteGroupView extends PureComponent<Props, State> {
|
|||||||
{!this.state.showMultipleSelectedNotes && (
|
{!this.state.showMultipleSelectedNotes && (
|
||||||
<>
|
<>
|
||||||
{this.state.controllers.map((controller) => {
|
{this.state.controllers.map((controller) => {
|
||||||
return <NoteView application={this.application} controller={controller} />
|
return <NoteView key={controller.note.uuid} application={this.application} controller={controller} />
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export const NoteTag = observer(({ appState, tag }: Props) => {
|
|||||||
(event: MouseEvent) => {
|
(event: MouseEvent) => {
|
||||||
if (tagClicked && event.target !== deleteTagRef.current) {
|
if (tagClicked && event.target !== deleteTagRef.current) {
|
||||||
setTagClicked(false)
|
setTagClicked(false)
|
||||||
appState.selectedTag = tag
|
appState.tags.selected = tag
|
||||||
} else {
|
} else {
|
||||||
setTagClicked(true)
|
setTagClicked(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const NotesList: FunctionComponent<Props> = observer(
|
|||||||
if (hideTags) {
|
if (hideTags) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
const selectedTag = appState.selectedTag
|
const selectedTag = appState.tags.selected
|
||||||
if (!selectedTag) {
|
if (!selectedTag) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const DeletePermanentlyButton = ({ closeOnBlur, onClick }: DeletePermanentlyButt
|
|||||||
const iconClass = 'color-neutral mr-2'
|
const iconClass = 'color-neutral mr-2'
|
||||||
const iconClassDanger = 'color-danger mr-2'
|
const iconClassDanger = 'color-danger mr-2'
|
||||||
const iconClassWarning = 'color-warning mr-2'
|
const iconClassWarning = 'color-warning mr-2'
|
||||||
|
const iconClassSuccess = 'color-success mr-2'
|
||||||
|
|
||||||
const getWordCount = (text: string) => {
|
const getWordCount = (text: string) => {
|
||||||
if (text.trim().length === 0) {
|
if (text.trim().length === 0) {
|
||||||
@@ -400,8 +401,8 @@ export const NotesOptions = observer(({ application, appState, closeOnBlur }: No
|
|||||||
await appState.notes.setTrashSelectedNotes(false)
|
await appState.notes.setTrashSelectedNotes(false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon type="restore" className={iconClass} />
|
<Icon type="restore" className={iconClassSuccess} />
|
||||||
Restore
|
<span className="color-success">Restore</span>
|
||||||
</button>
|
</button>
|
||||||
<DeletePermanentlyButton
|
<DeletePermanentlyButton
|
||||||
closeOnBlur={closeOnBlur}
|
closeOnBlur={closeOnBlur}
|
||||||
|
|||||||
@@ -35,12 +35,13 @@ export const NotesView: FunctionComponent<Props> = observer(({ application, appS
|
|||||||
optionsSubtitle,
|
optionsSubtitle,
|
||||||
panelTitle,
|
panelTitle,
|
||||||
renderedNotes,
|
renderedNotes,
|
||||||
selectedNotes,
|
|
||||||
searchBarElement,
|
searchBarElement,
|
||||||
paginate,
|
paginate,
|
||||||
panelWidth,
|
panelWidth,
|
||||||
} = appState.notesView
|
} = appState.notesView
|
||||||
|
|
||||||
|
const { selectedNotes } = appState.notes
|
||||||
|
|
||||||
const createNewNote = useCallback(() => appState.notesView.createNewNote(), [appState])
|
const createNewNote = useCallback(() => appState.notesView.createNewNote(), [appState])
|
||||||
const onFilterEnter = useCallback(() => appState.notesView.onFilterEnter(), [appState])
|
const onFilterEnter = useCallback(() => appState.notesView.onFilterEnter(), [appState])
|
||||||
const clearFilterText = useCallback(() => appState.notesView.clearFilterText(), [appState])
|
const clearFilterText = useCallback(() => appState.notesView.clearFilterText(), [appState])
|
||||||
|
|||||||
@@ -6,18 +6,13 @@ import {
|
|||||||
ApplicationEvent,
|
ApplicationEvent,
|
||||||
ContentType,
|
ContentType,
|
||||||
DeinitSource,
|
DeinitSource,
|
||||||
NoteViewController,
|
|
||||||
PrefKey,
|
PrefKey,
|
||||||
SNNote,
|
SNNote,
|
||||||
SmartView,
|
|
||||||
SNTag,
|
SNTag,
|
||||||
SystemViewId,
|
|
||||||
removeFromArray,
|
removeFromArray,
|
||||||
Uuid,
|
|
||||||
PayloadEmitSource,
|
|
||||||
WebOrDesktopDeviceInterface,
|
WebOrDesktopDeviceInterface,
|
||||||
} from '@standardnotes/snjs'
|
} from '@standardnotes/snjs'
|
||||||
import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx'
|
import { action, IReactionDisposer, makeObservable, observable, reaction } from 'mobx'
|
||||||
import { ActionsMenuState } from './ActionsMenuState'
|
import { ActionsMenuState } from './ActionsMenuState'
|
||||||
import { FeaturesState } from './FeaturesState'
|
import { FeaturesState } from './FeaturesState'
|
||||||
import { FilesState } from './FilesState'
|
import { FilesState } from './FilesState'
|
||||||
@@ -56,7 +51,7 @@ export enum EventSource {
|
|||||||
Script,
|
Script,
|
||||||
}
|
}
|
||||||
|
|
||||||
type ObserverCallback = (event: AppStateEvent, data?: any) => Promise<void>
|
type ObserverCallback = (event: AppStateEvent, data?: unknown) => Promise<void>
|
||||||
|
|
||||||
export class AppState extends AbstractState {
|
export class AppState extends AbstractState {
|
||||||
readonly enableUnfinishedFeatures: boolean = window?.enabledUnfinishedFeatures
|
readonly enableUnfinishedFeatures: boolean = window?.enabledUnfinishedFeatures
|
||||||
@@ -68,8 +63,6 @@ export class AppState extends AbstractState {
|
|||||||
onVisibilityChange: () => void
|
onVisibilityChange: () => void
|
||||||
showBetaWarning: boolean
|
showBetaWarning: boolean
|
||||||
|
|
||||||
private multiEditorSupport = false
|
|
||||||
|
|
||||||
readonly accountMenu: AccountMenuState
|
readonly accountMenu: AccountMenuState
|
||||||
readonly actionsMenu = new ActionsMenuState()
|
readonly actionsMenu = new ActionsMenuState()
|
||||||
readonly features: FeaturesState
|
readonly features: FeaturesState
|
||||||
@@ -116,7 +109,6 @@ export class AppState extends AbstractState {
|
|||||||
this.notesView = new NotesViewState(application, this, this.appEventObserverRemovers)
|
this.notesView = new NotesViewState(application, this, this.appEventObserverRemovers)
|
||||||
this.files = new FilesState(application)
|
this.files = new FilesState(application)
|
||||||
this.addAppEventObserver()
|
this.addAppEventObserver()
|
||||||
this.streamNotesAndTags()
|
|
||||||
this.onVisibilityChange = () => {
|
this.onVisibilityChange = () => {
|
||||||
const visible = document.visibilityState === 'visible'
|
const visible = document.visibilityState === 'visible'
|
||||||
const event = visible ? AppStateEvent.WindowDidFocus : AppStateEvent.WindowDidBlur
|
const event = visible ? AppStateEvent.WindowDidFocus : AppStateEvent.WindowDidBlur
|
||||||
@@ -131,8 +123,6 @@ export class AppState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
selectedTag: computed,
|
|
||||||
|
|
||||||
showBetaWarning: observable,
|
showBetaWarning: observable,
|
||||||
isSessionsModalVisible: observable,
|
isSessionsModalVisible: observable,
|
||||||
preferences: observable,
|
preferences: observable,
|
||||||
@@ -236,47 +226,6 @@ export class AppState extends AbstractState {
|
|||||||
return this.device.appVersion
|
return this.device.appVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
async openNewNote(title?: string) {
|
|
||||||
if (!this.multiEditorSupport) {
|
|
||||||
this.closeActiveNoteController()
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedTag = this.selectedTag
|
|
||||||
|
|
||||||
const activeRegularTagUuid = selectedTag && selectedTag instanceof SNTag ? selectedTag.uuid : undefined
|
|
||||||
|
|
||||||
await this.application.noteControllerGroup.createNoteView(undefined, title, activeRegularTagUuid)
|
|
||||||
}
|
|
||||||
|
|
||||||
getActiveNoteController() {
|
|
||||||
return this.application.noteControllerGroup.noteControllers[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
getNoteControllers() {
|
|
||||||
return this.application.noteControllerGroup.noteControllers
|
|
||||||
}
|
|
||||||
|
|
||||||
closeNoteController(controller: NoteViewController) {
|
|
||||||
this.application.noteControllerGroup.closeNoteView(controller)
|
|
||||||
}
|
|
||||||
|
|
||||||
closeActiveNoteController() {
|
|
||||||
this.application.noteControllerGroup.closeActiveNoteView()
|
|
||||||
}
|
|
||||||
|
|
||||||
closeAllNoteControllers() {
|
|
||||||
this.application.noteControllerGroup.closeAllNoteViews()
|
|
||||||
}
|
|
||||||
|
|
||||||
noteControllerForNote(uuid: Uuid) {
|
|
||||||
for (const controller of this.getNoteControllers()) {
|
|
||||||
if (controller.note.uuid === uuid) {
|
|
||||||
return controller
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
isGlobalSpellcheckEnabled(): boolean {
|
isGlobalSpellcheckEnabled(): boolean {
|
||||||
return this.application.getPreference(PrefKey.EditorSpellcheck, true)
|
return this.application.getPreference(PrefKey.EditorSpellcheck, true)
|
||||||
}
|
}
|
||||||
@@ -309,62 +258,6 @@ export class AppState extends AbstractState {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public get selectedTag(): SNTag | SmartView | undefined {
|
|
||||||
return this.tags.selected
|
|
||||||
}
|
|
||||||
|
|
||||||
public set selectedTag(tag: SNTag | SmartView | undefined) {
|
|
||||||
this.tags.selected = tag
|
|
||||||
}
|
|
||||||
|
|
||||||
streamNotesAndTags() {
|
|
||||||
this.application.streamItems<SNNote | SNTag>(
|
|
||||||
[ContentType.Note, ContentType.Tag],
|
|
||||||
async ({ changed, inserted, removed, source }) => {
|
|
||||||
if (![PayloadEmitSource.PreSyncSave, PayloadEmitSource.RemoteRetrieved].includes(source)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const removedNotes = removed.filter((i) => i.content_type === ContentType.Note)
|
|
||||||
|
|
||||||
for (const removedNote of removedNotes) {
|
|
||||||
const noteController = this.noteControllerForNote(removedNote.uuid)
|
|
||||||
if (noteController) {
|
|
||||||
this.closeNoteController(noteController)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const changedOrInserted = [...changed, ...inserted].filter((i) => i.content_type === ContentType.Note)
|
|
||||||
|
|
||||||
const selectedTag = this.tags.selected
|
|
||||||
|
|
||||||
const isBrowswingTrashedNotes =
|
|
||||||
selectedTag instanceof SmartView && selectedTag.uuid === SystemViewId.TrashedNotes
|
|
||||||
|
|
||||||
const isBrowsingArchivedNotes =
|
|
||||||
selectedTag instanceof SmartView && selectedTag.uuid === SystemViewId.ArchivedNotes
|
|
||||||
|
|
||||||
for (const note of changedOrInserted) {
|
|
||||||
const noteController = this.noteControllerForNote(note.uuid)
|
|
||||||
if (!noteController) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (note.trashed && !isBrowswingTrashedNotes && !this.searchOptions.includeTrashed) {
|
|
||||||
this.closeNoteController(noteController)
|
|
||||||
} else if (
|
|
||||||
note.archived &&
|
|
||||||
!isBrowsingArchivedNotes &&
|
|
||||||
!this.searchOptions.includeArchived &&
|
|
||||||
!this.application.getPreference(PrefKey.NotesShowArchived, false)
|
|
||||||
) {
|
|
||||||
this.closeNoteController(noteController)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
addAppEventObserver() {
|
addAppEventObserver() {
|
||||||
this.unsubAppEventObserver = this.application.addEventObserver(async (eventName) => {
|
this.unsubAppEventObserver = this.application.addEventObserver(async (eventName) => {
|
||||||
switch (eventName) {
|
switch (eventName) {
|
||||||
@@ -412,7 +305,7 @@ export class AppState extends AbstractState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async notifyEvent(eventName: AppStateEvent, data?: any) {
|
async notifyEvent(eventName: AppStateEvent, data?: unknown) {
|
||||||
/**
|
/**
|
||||||
* Timeout is particularly important so we can give all initial
|
* Timeout is particularly important so we can give all initial
|
||||||
* controllers a chance to construct before propogting any events *
|
* controllers a chance to construct before propogting any events *
|
||||||
|
|||||||
@@ -62,10 +62,6 @@ export class NoteTagsState extends AbstractState {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
get activeNote(): SNNote | undefined {
|
|
||||||
return this.appState.notes.activeNoteController?.note
|
|
||||||
}
|
|
||||||
|
|
||||||
get autocompleteTagHintVisible(): boolean {
|
get autocompleteTagHintVisible(): boolean {
|
||||||
return (
|
return (
|
||||||
this.autocompleteSearchQuery !== '' &&
|
this.autocompleteSearchQuery !== '' &&
|
||||||
@@ -149,7 +145,10 @@ export class NoteTagsState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchActiveNoteAutocompleteTags(): void {
|
searchActiveNoteAutocompleteTags(): void {
|
||||||
const newResults = this.application.items.searchTags(this.autocompleteSearchQuery, this.activeNote)
|
const newResults = this.application.items.searchTags(
|
||||||
|
this.autocompleteSearchQuery,
|
||||||
|
this.appState.notesView.activeControllerNote,
|
||||||
|
)
|
||||||
this.setAutocompleteTagResults(newResults)
|
this.setAutocompleteTagResults(newResults)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +157,8 @@ export class NoteTagsState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reloadTags(): void {
|
reloadTags(): void {
|
||||||
const { activeNote } = this
|
const activeNote = this.appState.notesView.activeControllerNote
|
||||||
|
|
||||||
if (activeNote) {
|
if (activeNote) {
|
||||||
const tags = this.application.items.getSortedTagsForNote(activeNote)
|
const tags = this.application.items.getSortedTagsForNote(activeNote)
|
||||||
this.setTags(tags)
|
this.setTags(tags)
|
||||||
@@ -173,7 +173,7 @@ export class NoteTagsState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addTagToActiveNote(tag: SNTag): Promise<void> {
|
async addTagToActiveNote(tag: SNTag): Promise<void> {
|
||||||
const { activeNote } = this
|
const activeNote = this.appState.notesView.activeControllerNote
|
||||||
|
|
||||||
if (activeNote) {
|
if (activeNote) {
|
||||||
await this.application.items.addTagToNote(activeNote, tag, this.addNoteToParentFolders)
|
await this.application.items.addTagToNote(activeNote, tag, this.addNoteToParentFolders)
|
||||||
@@ -183,7 +183,8 @@ export class NoteTagsState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeTagFromActiveNote(tag: SNTag): Promise<void> {
|
async removeTagFromActiveNote(tag: SNTag): Promise<void> {
|
||||||
const { activeNote } = this
|
const activeNote = this.appState.notesView.activeControllerNote
|
||||||
|
|
||||||
if (activeNote) {
|
if (activeNote) {
|
||||||
await this.application.mutator.changeItem(tag, (mutator) => {
|
await this.application.mutator.changeItem(tag, (mutator) => {
|
||||||
mutator.removeItemAsRelationship(activeNote)
|
mutator.removeItemAsRelationship(activeNote)
|
||||||
|
|||||||
@@ -3,16 +3,7 @@ import { confirmDialog } from '@/Services/AlertService'
|
|||||||
import { KeyboardModifier } from '@/Services/IOService'
|
import { KeyboardModifier } from '@/Services/IOService'
|
||||||
import { StringEmptyTrash, Strings, StringUtils } from '@/Strings'
|
import { StringEmptyTrash, Strings, StringUtils } from '@/Strings'
|
||||||
import { MENU_MARGIN_FROM_APP_BORDER } from '@/Constants'
|
import { MENU_MARGIN_FROM_APP_BORDER } from '@/Constants'
|
||||||
import {
|
import { UuidString, SNNote, NoteMutator, ContentType, SNTag, ChallengeReason, DeinitSource } from '@standardnotes/snjs'
|
||||||
UuidString,
|
|
||||||
SNNote,
|
|
||||||
NoteMutator,
|
|
||||||
ContentType,
|
|
||||||
SNTag,
|
|
||||||
ChallengeReason,
|
|
||||||
NoteViewController,
|
|
||||||
DeinitSource,
|
|
||||||
} from '@standardnotes/snjs'
|
|
||||||
import { makeObservable, observable, action, computed, runInAction } from 'mobx'
|
import { makeObservable, observable, action, computed, runInAction } from 'mobx'
|
||||||
import { WebApplication } from '../Application'
|
import { WebApplication } from '../Application'
|
||||||
import { AppState } from './AppState'
|
import { AppState } from './AppState'
|
||||||
@@ -81,11 +72,21 @@ export class NotesState extends AbstractState {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
get activeNoteController(): NoteViewController | undefined {
|
this.application.noteControllerGroup.addActiveControllerChangeObserver(() => {
|
||||||
return this.application.noteControllerGroup.noteControllers[0]
|
const controllers = this.application.noteControllerGroup.noteControllers
|
||||||
|
|
||||||
|
const activeNoteUuids = controllers.map((c) => c.note.uuid)
|
||||||
|
|
||||||
|
const selectedUuids = this.getSelectedNotesList().map((n) => n.uuid)
|
||||||
|
|
||||||
|
for (const selectedId of selectedUuids) {
|
||||||
|
if (!activeNoteUuids.includes(selectedId)) {
|
||||||
|
delete this.selectedNotes[selectedId]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
get selectedNotesCount(): number {
|
get selectedNotesCount(): number {
|
||||||
@@ -132,11 +133,18 @@ export class NotesState extends AbstractState {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.selectedNotes[uuid]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const hasMeta = this.io.activeModifiers.has(KeyboardModifier.Meta)
|
const hasMeta = this.io.activeModifiers.has(KeyboardModifier.Meta)
|
||||||
const hasCtrl = this.io.activeModifiers.has(KeyboardModifier.Ctrl)
|
const hasCtrl = this.io.activeModifiers.has(KeyboardModifier.Ctrl)
|
||||||
const hasShift = this.io.activeModifiers.has(KeyboardModifier.Shift)
|
const hasShift = this.io.activeModifiers.has(KeyboardModifier.Shift)
|
||||||
|
|
||||||
if (userTriggered && (hasMeta || hasCtrl)) {
|
const isMultipleSelectSingle = userTriggered && (hasMeta || hasCtrl)
|
||||||
|
const isMultipleSelectRange = userTriggered && hasShift
|
||||||
|
|
||||||
|
if (isMultipleSelectSingle) {
|
||||||
if (this.selectedNotes[uuid]) {
|
if (this.selectedNotes[uuid]) {
|
||||||
delete this.selectedNotes[uuid]
|
delete this.selectedNotes[uuid]
|
||||||
} else if (await this.application.authorizeNoteAccess(note)) {
|
} else if (await this.application.authorizeNoteAccess(note)) {
|
||||||
@@ -145,7 +153,7 @@ export class NotesState extends AbstractState {
|
|||||||
this.lastSelectedNote = note
|
this.lastSelectedNote = note
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (userTriggered && hasShift) {
|
} else if (isMultipleSelectRange) {
|
||||||
await this.selectNotesRange(note)
|
await this.selectNotesRange(note)
|
||||||
} else {
|
} else {
|
||||||
const shouldSelectNote = this.selectedNotesCount > 1 || !this.selectedNotes[uuid]
|
const shouldSelectNote = this.selectedNotesCount > 1 || !this.selectedNotes[uuid]
|
||||||
@@ -165,7 +173,7 @@ export class NotesState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async openNote(noteUuid: string): Promise<void> {
|
private async openNote(noteUuid: string): Promise<void> {
|
||||||
if (this.activeNoteController?.note?.uuid === noteUuid) {
|
if (this.appState.notesView.activeControllerNote?.uuid === noteUuid) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,16 +183,21 @@ export class NotesState extends AbstractState {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.activeNoteController) {
|
await this.application.noteControllerGroup.createNoteController(noteUuid)
|
||||||
this.application.noteControllerGroup.closeActiveNoteView()
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.application.noteControllerGroup.createNoteView(noteUuid)
|
|
||||||
|
|
||||||
this.appState.noteTags.reloadTags()
|
this.appState.noteTags.reloadTags()
|
||||||
|
|
||||||
await this.onActiveEditorChanged()
|
await this.onActiveEditorChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async createNewNoteController(title?: string) {
|
||||||
|
const selectedTag = this.appState.tags.selected
|
||||||
|
|
||||||
|
const activeRegularTagUuid = selectedTag && selectedTag instanceof SNTag ? selectedTag.uuid : undefined
|
||||||
|
|
||||||
|
await this.application.noteControllerGroup.createNoteController(undefined, title, activeRegularTagUuid)
|
||||||
|
}
|
||||||
|
|
||||||
setContextMenuOpen(open: boolean): void {
|
setContextMenuOpen(open: boolean): void {
|
||||||
this.contextMenuOpen = open
|
this.contextMenuOpen = open
|
||||||
}
|
}
|
||||||
@@ -249,7 +262,7 @@ export class NotesState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async changeSelectedNotes(mutate: (mutator: NoteMutator) => void): Promise<void> {
|
async changeSelectedNotes(mutate: (mutator: NoteMutator) => void): Promise<void> {
|
||||||
await this.application.mutator.changeItems(Object.values(this.selectedNotes), mutate, false)
|
await this.application.mutator.changeItems(this.getSelectedNotesList(), mutate, false)
|
||||||
this.application.sync.sync().catch(console.error)
|
this.application.sync.sync().catch(console.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +303,7 @@ export class NotesState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async deleteNotes(permanently: boolean): Promise<boolean> {
|
async deleteNotes(permanently: boolean): Promise<boolean> {
|
||||||
if (Object.values(this.selectedNotes).some((note) => note.locked)) {
|
if (this.getSelectedNotesList().some((note) => note.locked)) {
|
||||||
const text = StringUtils.deleteLockedNotesAttempt(this.selectedNotesCount)
|
const text = StringUtils.deleteLockedNotesAttempt(this.selectedNotesCount)
|
||||||
this.application.alertService.alert(text).catch(console.error)
|
this.application.alertService.alert(text).catch(console.error)
|
||||||
return false
|
return false
|
||||||
@@ -299,7 +312,7 @@ export class NotesState extends AbstractState {
|
|||||||
const title = Strings.trashNotesTitle
|
const title = Strings.trashNotesTitle
|
||||||
let noteTitle = undefined
|
let noteTitle = undefined
|
||||||
if (this.selectedNotesCount === 1) {
|
if (this.selectedNotesCount === 1) {
|
||||||
const selectedNote = Object.values(this.selectedNotes)[0]
|
const selectedNote = this.getSelectedNotesList()[0]
|
||||||
noteTitle = selectedNote.title.length ? `'${selectedNote.title}'` : 'this note'
|
noteTitle = selectedNote.title.length ? `'${selectedNote.title}'` : 'this note'
|
||||||
}
|
}
|
||||||
const text = StringUtils.deleteNotes(permanently, this.selectedNotesCount, noteTitle)
|
const text = StringUtils.deleteNotes(permanently, this.selectedNotesCount, noteTitle)
|
||||||
@@ -312,7 +325,7 @@ export class NotesState extends AbstractState {
|
|||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
if (permanently) {
|
if (permanently) {
|
||||||
for (const note of Object.values(this.selectedNotes)) {
|
for (const note of this.getSelectedNotesList()) {
|
||||||
await this.application.mutator.deleteItem(note)
|
await this.application.mutator.deleteItem(note)
|
||||||
delete this.selectedNotes[note.uuid]
|
delete this.selectedNotes[note.uuid]
|
||||||
}
|
}
|
||||||
@@ -334,7 +347,7 @@ export class NotesState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setArchiveSelectedNotes(archived: boolean): Promise<void> {
|
async setArchiveSelectedNotes(archived: boolean): Promise<void> {
|
||||||
if (Object.values(this.selectedNotes).some((note) => note.locked)) {
|
if (this.getSelectedNotesList().some((note) => note.locked)) {
|
||||||
this.application.alertService
|
this.application.alertService
|
||||||
.alert(StringUtils.archiveLockedNotesAttempt(archived, this.selectedNotesCount))
|
.alert(StringUtils.archiveLockedNotesAttempt(archived, this.selectedNotesCount))
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
@@ -352,7 +365,7 @@ export class NotesState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setProtectSelectedNotes(protect: boolean): Promise<void> {
|
async setProtectSelectedNotes(protect: boolean): Promise<void> {
|
||||||
const selectedNotes = Object.values(this.selectedNotes)
|
const selectedNotes = this.getSelectedNotesList()
|
||||||
if (protect) {
|
if (protect) {
|
||||||
await this.application.mutator.protectNotes(selectedNotes)
|
await this.application.mutator.protectNotes(selectedNotes)
|
||||||
this.setShowProtectedWarning(true)
|
this.setShowProtectedWarning(true)
|
||||||
@@ -382,7 +395,7 @@ export class NotesState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addTagToSelectedNotes(tag: SNTag): Promise<void> {
|
async addTagToSelectedNotes(tag: SNTag): Promise<void> {
|
||||||
const selectedNotes = Object.values(this.selectedNotes)
|
const selectedNotes = this.getSelectedNotesList()
|
||||||
const parentChainTags = this.application.items.getTagParentChain(tag)
|
const parentChainTags = this.application.items.getTagParentChain(tag)
|
||||||
const tagsToAdd = [...parentChainTags, tag]
|
const tagsToAdd = [...parentChainTags, tag]
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
@@ -398,7 +411,7 @@ export class NotesState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeTagFromSelectedNotes(tag: SNTag): Promise<void> {
|
async removeTagFromSelectedNotes(tag: SNTag): Promise<void> {
|
||||||
const selectedNotes = Object.values(this.selectedNotes)
|
const selectedNotes = this.getSelectedNotesList()
|
||||||
await this.application.mutator.changeItem(tag, (mutator) => {
|
await this.application.mutator.changeItem(tag, (mutator) => {
|
||||||
for (const note of selectedNotes) {
|
for (const note of selectedNotes) {
|
||||||
mutator.removeItemAsRelationship(note)
|
mutator.removeItemAsRelationship(note)
|
||||||
@@ -408,7 +421,7 @@ export class NotesState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isTagInSelectedNotes(tag: SNTag): boolean {
|
isTagInSelectedNotes(tag: SNTag): boolean {
|
||||||
const selectedNotes = Object.values(this.selectedNotes)
|
const selectedNotes = this.getSelectedNotesList()
|
||||||
return selectedNotes.every((note) => this.appState.getNoteTags(note).find((noteTag) => noteTag.uuid === tag.uuid))
|
return selectedNotes.every((note) => this.appState.getNoteTags(note).find((noteTag) => noteTag.uuid === tag.uuid))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,6 +441,10 @@ export class NotesState extends AbstractState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getSelectedNotesList(): SNNote[] {
|
||||||
|
return Object.values(this.selectedNotes)
|
||||||
|
}
|
||||||
|
|
||||||
private get io() {
|
private get io() {
|
||||||
return this.application.io
|
return this.application.io
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import {
|
|||||||
DeinitSource,
|
DeinitSource,
|
||||||
findInArray,
|
findInArray,
|
||||||
NotesDisplayCriteria,
|
NotesDisplayCriteria,
|
||||||
|
NoteViewController,
|
||||||
PrefKey,
|
PrefKey,
|
||||||
SmartView,
|
SmartView,
|
||||||
SNNote,
|
SNNote,
|
||||||
SNTag,
|
SNTag,
|
||||||
SystemViewId,
|
SystemViewId,
|
||||||
UuidString,
|
|
||||||
} from '@standardnotes/snjs'
|
} from '@standardnotes/snjs'
|
||||||
import { action, autorun, computed, makeObservable, observable, reaction } from 'mobx'
|
import { action, computed, makeObservable, observable, reaction } from 'mobx'
|
||||||
import { AppState, AppStateEvent } from '.'
|
import { AppState, AppStateEvent } from '.'
|
||||||
import { WebApplication } from '../Application'
|
import { WebApplication } from '../Application'
|
||||||
import { AbstractState } from './AbstractState'
|
import { AbstractState } from './AbstractState'
|
||||||
@@ -47,7 +47,6 @@ export class NotesViewState extends AbstractState {
|
|||||||
panelWidth = 0
|
panelWidth = 0
|
||||||
renderedNotes: SNNote[] = []
|
renderedNotes: SNNote[] = []
|
||||||
searchSubmitted = false
|
searchSubmitted = false
|
||||||
selectedNotes: Record<UuidString, SNNote> = {}
|
|
||||||
showDisplayOptionsMenu = false
|
showDisplayOptionsMenu = false
|
||||||
displayOptions = {
|
displayOptions = {
|
||||||
sortBy: CollectionSort.CreatedAt,
|
sortBy: CollectionSort.CreatedAt,
|
||||||
@@ -61,13 +60,13 @@ export class NotesViewState extends AbstractState {
|
|||||||
hideNotePreview: false,
|
hideNotePreview: false,
|
||||||
hideEditorIcon: false,
|
hideEditorIcon: false,
|
||||||
}
|
}
|
||||||
|
private reloadNotesPromise?: Promise<unknown>
|
||||||
|
|
||||||
override deinit(source: DeinitSource) {
|
override deinit(source: DeinitSource) {
|
||||||
super.deinit(source)
|
super.deinit(source)
|
||||||
;(this.noteFilterText as unknown) = undefined
|
;(this.noteFilterText as unknown) = undefined
|
||||||
;(this.notes as unknown) = undefined
|
;(this.notes as unknown) = undefined
|
||||||
;(this.renderedNotes as unknown) = undefined
|
;(this.renderedNotes as unknown) = undefined
|
||||||
;(this.selectedNotes as unknown) = undefined
|
|
||||||
;(window.onresize as unknown) = undefined
|
;(window.onresize as unknown) = undefined
|
||||||
|
|
||||||
destroyAllObjectProperties(this)
|
destroyAllObjectProperties(this)
|
||||||
@@ -80,65 +79,45 @@ export class NotesViewState extends AbstractState {
|
|||||||
|
|
||||||
appObservers.push(
|
appObservers.push(
|
||||||
application.streamItems<SNNote>(ContentType.Note, () => {
|
application.streamItems<SNNote>(ContentType.Note, () => {
|
||||||
this.reloadNotes()
|
void this.reloadNotes()
|
||||||
|
|
||||||
const activeNote = appState.notes.activeNoteController?.note
|
|
||||||
|
|
||||||
if (appState.notes.selectedNotesCount < 2) {
|
|
||||||
if (activeNote) {
|
|
||||||
const browsingTrashedNotes =
|
|
||||||
appState.selectedTag instanceof SmartView && appState.selectedTag?.uuid === SystemViewId.TrashedNotes
|
|
||||||
|
|
||||||
if (activeNote.trashed && !browsingTrashedNotes && !appState?.searchOptions.includeTrashed) {
|
|
||||||
this.selectNextOrCreateNew()
|
|
||||||
} else if (!this.selectedNotes[activeNote.uuid]) {
|
|
||||||
this.selectNote(activeNote).catch(console.error)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.selectFirstNote()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
application.streamItems<SNTag>([ContentType.Tag], async ({ changed, inserted }) => {
|
application.streamItems<SNTag>([ContentType.Tag], async ({ changed, inserted }) => {
|
||||||
const tags = [...changed, ...inserted]
|
const tags = [...changed, ...inserted]
|
||||||
|
|
||||||
/** A tag could have changed its relationships, so we need to reload the filter */
|
/** A tag could have changed its relationships, so we need to reload the filter */
|
||||||
this.reloadNotesDisplayOptions()
|
this.reloadNotesDisplayOptions()
|
||||||
this.reloadNotes()
|
|
||||||
|
|
||||||
if (appState.selectedTag && findInArray(tags, 'uuid', appState.selectedTag.uuid)) {
|
void this.reloadNotes()
|
||||||
|
|
||||||
|
if (appState.tags.selected && findInArray(tags, 'uuid', appState.tags.selected.uuid)) {
|
||||||
/** Tag title could have changed */
|
/** Tag title could have changed */
|
||||||
this.reloadPanelTitle()
|
this.reloadPanelTitle()
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
application.addEventObserver(async () => {
|
application.addEventObserver(async () => {
|
||||||
this.reloadPreferences()
|
void this.reloadPreferences()
|
||||||
}, ApplicationEvent.PreferencesChanged),
|
}, ApplicationEvent.PreferencesChanged),
|
||||||
application.addEventObserver(async () => {
|
application.addEventObserver(async () => {
|
||||||
appState.closeAllNoteControllers()
|
this.application.noteControllerGroup.closeAllNoteControllers()
|
||||||
this.selectFirstNote()
|
void this.selectFirstNote()
|
||||||
this.setCompletedFullSync(false)
|
this.setCompletedFullSync(false)
|
||||||
}, ApplicationEvent.SignedIn),
|
}, ApplicationEvent.SignedIn),
|
||||||
application.addEventObserver(async () => {
|
application.addEventObserver(async () => {
|
||||||
this.reloadNotes()
|
void this.reloadNotes().then(() => {
|
||||||
if (
|
if (
|
||||||
this.notes.length === 0 &&
|
this.notes.length === 0 &&
|
||||||
appState.selectedTag instanceof SmartView &&
|
appState.tags.selected instanceof SmartView &&
|
||||||
appState.selectedTag.uuid === SystemViewId.AllNotes &&
|
appState.tags.selected.uuid === SystemViewId.AllNotes &&
|
||||||
this.noteFilterText === '' &&
|
this.noteFilterText === '' &&
|
||||||
!appState.notes.activeNoteController
|
!this.getActiveNoteController()
|
||||||
) {
|
) {
|
||||||
this.createPlaceholderNote()?.catch(console.error)
|
this.createPlaceholderNote()?.catch(console.error)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
this.setCompletedFullSync(true)
|
this.setCompletedFullSync(true)
|
||||||
}, ApplicationEvent.CompletedFullSync),
|
}, ApplicationEvent.CompletedFullSync),
|
||||||
|
|
||||||
autorun(() => {
|
|
||||||
if (appState.notes.selectedNotes) {
|
|
||||||
this.syncSelectedNotes()
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
reaction(
|
reaction(
|
||||||
() => [
|
() => [
|
||||||
appState.searchOptions.includeProtectedContents,
|
appState.searchOptions.includeProtectedContents,
|
||||||
@@ -147,7 +126,7 @@ export class NotesViewState extends AbstractState {
|
|||||||
],
|
],
|
||||||
() => {
|
() => {
|
||||||
this.reloadNotesDisplayOptions()
|
this.reloadNotesDisplayOptions()
|
||||||
this.reloadNotes()
|
void this.reloadNotes()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -170,7 +149,6 @@ export class NotesViewState extends AbstractState {
|
|||||||
notesToDisplay: observable,
|
notesToDisplay: observable,
|
||||||
panelTitle: observable,
|
panelTitle: observable,
|
||||||
renderedNotes: observable,
|
renderedNotes: observable,
|
||||||
selectedNotes: observable,
|
|
||||||
showDisplayOptionsMenu: observable,
|
showDisplayOptionsMenu: observable,
|
||||||
|
|
||||||
reloadNotes: action,
|
reloadNotes: action,
|
||||||
@@ -179,7 +157,6 @@ export class NotesViewState extends AbstractState {
|
|||||||
resetPagination: action,
|
resetPagination: action,
|
||||||
setCompletedFullSync: action,
|
setCompletedFullSync: action,
|
||||||
setNoteFilterText: action,
|
setNoteFilterText: action,
|
||||||
syncSelectedNotes: action,
|
|
||||||
setShowDisplayOptionsMenu: action,
|
setShowDisplayOptionsMenu: action,
|
||||||
onFilterEnter: action,
|
onFilterEnter: action,
|
||||||
handleFilterTextChanged: action,
|
handleFilterTextChanged: action,
|
||||||
@@ -192,6 +169,14 @@ export class NotesViewState extends AbstractState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getActiveNoteController(): NoteViewController | undefined {
|
||||||
|
return this.application.noteControllerGroup.activeNoteViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
public get activeControllerNote(): SNNote | undefined {
|
||||||
|
return this.getActiveNoteController()?.note
|
||||||
|
}
|
||||||
|
|
||||||
setCompletedFullSync = (completed: boolean) => {
|
setCompletedFullSync = (completed: boolean) => {
|
||||||
this.completedFullSync = completed
|
this.completedFullSync = completed
|
||||||
}
|
}
|
||||||
@@ -208,36 +193,96 @@ export class NotesViewState extends AbstractState {
|
|||||||
return !!this.noteFilterText && this.noteFilterText.length > 0
|
return !!this.noteFilterText && this.noteFilterText.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
get activeEditorNote() {
|
|
||||||
return this.appState.notes.activeNoteController?.note
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadPanelTitle = () => {
|
reloadPanelTitle = () => {
|
||||||
let title = this.panelTitle
|
let title = this.panelTitle
|
||||||
|
|
||||||
if (this.isFiltering) {
|
if (this.isFiltering) {
|
||||||
const resultCount = this.notes.length
|
const resultCount = this.notes.length
|
||||||
title = `${resultCount} search results`
|
title = `${resultCount} search results`
|
||||||
} else if (this.appState.selectedTag) {
|
} else if (this.appState.tags.selected) {
|
||||||
title = `${this.appState.selectedTag.title}`
|
title = `${this.appState.tags.selected.title}`
|
||||||
}
|
}
|
||||||
|
|
||||||
this.panelTitle = title
|
this.panelTitle = title
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadNotes = () => {
|
reloadNotes = async (): Promise<void> => {
|
||||||
const tag = this.appState.selectedTag
|
if (this.reloadNotesPromise) {
|
||||||
|
await this.reloadNotesPromise
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reloadNotesPromise = this.performReloadNotes()
|
||||||
|
|
||||||
|
await this.reloadNotesPromise
|
||||||
|
}
|
||||||
|
|
||||||
|
private async performReloadNotes() {
|
||||||
|
const tag = this.appState.tags.selected
|
||||||
if (!tag) {
|
if (!tag) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const notes = this.application.items.getDisplayableNotes()
|
const notes = this.application.items.getDisplayableNotes()
|
||||||
|
|
||||||
const renderedNotes = notes.slice(0, this.notesToDisplay)
|
const renderedNotes = notes.slice(0, this.notesToDisplay)
|
||||||
|
|
||||||
this.notes = notes
|
this.notes = notes
|
||||||
|
|
||||||
this.renderedNotes = renderedNotes
|
this.renderedNotes = renderedNotes
|
||||||
|
|
||||||
|
await this.recomputeSelectionAfterNotesReload()
|
||||||
|
|
||||||
this.reloadPanelTitle()
|
this.reloadPanelTitle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async recomputeSelectionAfterNotesReload() {
|
||||||
|
const appState = this.appState
|
||||||
|
const activeController = this.getActiveNoteController()
|
||||||
|
const activeNote = activeController?.note
|
||||||
|
const isSearching = this.noteFilterText.length > 0
|
||||||
|
const hasMultipleNotesSelected = appState.notes.selectedNotesCount >= 2
|
||||||
|
|
||||||
|
if (hasMultipleNotesSelected) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!activeNote) {
|
||||||
|
await this.selectFirstNote()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeController.isTemplateNote) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const noteExistsInUpdatedResults = this.notes.find((note) => note.uuid === activeNote.uuid)
|
||||||
|
if (!noteExistsInUpdatedResults && !isSearching) {
|
||||||
|
this.application.noteControllerGroup.closeNoteController(activeController)
|
||||||
|
|
||||||
|
this.selectNextNote()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const showTrashedNotes =
|
||||||
|
(appState.tags.selected instanceof SmartView && appState.tags.selected?.uuid === SystemViewId.TrashedNotes) ||
|
||||||
|
appState?.searchOptions.includeTrashed
|
||||||
|
|
||||||
|
const showArchivedNotes =
|
||||||
|
(appState.tags.selected instanceof SmartView && appState.tags.selected.uuid === SystemViewId.ArchivedNotes) ||
|
||||||
|
appState.searchOptions.includeArchived ||
|
||||||
|
this.application.getPreference(PrefKey.NotesShowArchived, false)
|
||||||
|
|
||||||
|
if ((activeNote.trashed && !showTrashedNotes) || (activeNote.archived && !showArchivedNotes)) {
|
||||||
|
await this.selectNextOrCreateNew()
|
||||||
|
} else if (!this.appState.notes.selectedNotes[activeNote.uuid]) {
|
||||||
|
await this.selectNoteWithScrollHandling(activeNote).catch(console.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reloadNotesDisplayOptions = () => {
|
reloadNotesDisplayOptions = () => {
|
||||||
const tag = this.appState.selectedTag
|
const tag = this.appState.tags.selected
|
||||||
|
|
||||||
const searchText = this.noteFilterText.toLowerCase()
|
const searchText = this.noteFilterText.toLowerCase()
|
||||||
const isSearching = searchText.length
|
const isSearching = searchText.length
|
||||||
@@ -266,10 +311,11 @@ export class NotesViewState extends AbstractState {
|
|||||||
includeProtectedNoteText: this.appState.searchOptions.includeProtectedContents,
|
includeProtectedNoteText: this.appState.searchOptions.includeProtectedContents,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
this.application.items.setNotesDisplayCriteria(criteria)
|
this.application.items.setNotesDisplayCriteria(criteria)
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadPreferences = () => {
|
reloadPreferences = async () => {
|
||||||
const freshDisplayOptions = {} as DisplayOptions
|
const freshDisplayOptions = {} as DisplayOptions
|
||||||
const currentSortBy = this.displayOptions.sortBy
|
const currentSortBy = this.displayOptions.sortBy
|
||||||
let sortBy = this.application.getPreference(PrefKey.SortNotesBy, CollectionSort.CreatedAt)
|
let sortBy = this.application.getPreference(PrefKey.SortNotesBy, CollectionSort.CreatedAt)
|
||||||
@@ -287,6 +333,7 @@ export class NotesViewState extends AbstractState {
|
|||||||
freshDisplayOptions.hideDate = this.application.getPreference(PrefKey.NotesHideDate, false)
|
freshDisplayOptions.hideDate = this.application.getPreference(PrefKey.NotesHideDate, false)
|
||||||
freshDisplayOptions.hideTags = this.application.getPreference(PrefKey.NotesHideTags, true)
|
freshDisplayOptions.hideTags = this.application.getPreference(PrefKey.NotesHideTags, true)
|
||||||
freshDisplayOptions.hideEditorIcon = this.application.getPreference(PrefKey.NotesHideEditorIcon, false)
|
freshDisplayOptions.hideEditorIcon = this.application.getPreference(PrefKey.NotesHideEditorIcon, false)
|
||||||
|
|
||||||
const displayOptionsChanged =
|
const displayOptionsChanged =
|
||||||
freshDisplayOptions.sortBy !== this.displayOptions.sortBy ||
|
freshDisplayOptions.sortBy !== this.displayOptions.sortBy ||
|
||||||
freshDisplayOptions.sortReverse !== this.displayOptions.sortReverse ||
|
freshDisplayOptions.sortReverse !== this.displayOptions.sortReverse ||
|
||||||
@@ -296,12 +343,14 @@ export class NotesViewState extends AbstractState {
|
|||||||
freshDisplayOptions.hideProtected !== this.displayOptions.hideProtected ||
|
freshDisplayOptions.hideProtected !== this.displayOptions.hideProtected ||
|
||||||
freshDisplayOptions.hideEditorIcon !== this.displayOptions.hideEditorIcon ||
|
freshDisplayOptions.hideEditorIcon !== this.displayOptions.hideEditorIcon ||
|
||||||
freshDisplayOptions.hideTags !== this.displayOptions.hideTags
|
freshDisplayOptions.hideTags !== this.displayOptions.hideTags
|
||||||
|
|
||||||
this.displayOptions = freshDisplayOptions
|
this.displayOptions = freshDisplayOptions
|
||||||
|
|
||||||
if (displayOptionsChanged) {
|
if (displayOptionsChanged) {
|
||||||
this.reloadNotesDisplayOptions()
|
this.reloadNotesDisplayOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reloadNotes()
|
await this.reloadNotes()
|
||||||
|
|
||||||
const width = this.application.getPreference(PrefKey.NotesPanelWidth)
|
const width = this.application.getPreference(PrefKey.NotesPanelWidth)
|
||||||
if (width) {
|
if (width) {
|
||||||
@@ -309,28 +358,29 @@ export class NotesViewState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (freshDisplayOptions.sortBy !== currentSortBy) {
|
if (freshDisplayOptions.sortBy !== currentSortBy) {
|
||||||
this.selectFirstNote()
|
await this.selectFirstNote()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createNewNote = async () => {
|
createNewNote = async () => {
|
||||||
this.appState.notes.unselectNotes()
|
this.appState.notes.unselectNotes()
|
||||||
|
|
||||||
let title = `Note ${this.notes.length + 1}`
|
let title = `Note ${this.notes.length + 1}`
|
||||||
if (this.isFiltering) {
|
if (this.isFiltering) {
|
||||||
title = this.noteFilterText
|
title = this.noteFilterText
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.appState.openNewNote(title)
|
await this.appState.notes.createNewNoteController(title)
|
||||||
|
|
||||||
this.reloadNotes()
|
|
||||||
this.appState.noteTags.reloadTags()
|
this.appState.noteTags.reloadTags()
|
||||||
}
|
}
|
||||||
|
|
||||||
createPlaceholderNote = () => {
|
createPlaceholderNote = () => {
|
||||||
const selectedTag = this.appState.selectedTag
|
const selectedTag = this.appState.tags.selected
|
||||||
if (selectedTag && selectedTag instanceof SmartView && selectedTag.uuid !== SystemViewId.AllNotes) {
|
if (selectedTag && selectedTag instanceof SmartView && selectedTag.uuid !== SystemViewId.AllNotes) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.createNewNote()
|
return this.createNewNote()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,7 +413,8 @@ export class NotesViewState extends AbstractState {
|
|||||||
|
|
||||||
paginate = () => {
|
paginate = () => {
|
||||||
this.notesToDisplay += this.pageSize
|
this.notesToDisplay += this.pageSize
|
||||||
this.reloadNotes()
|
|
||||||
|
void this.reloadNotes()
|
||||||
|
|
||||||
if (this.searchSubmitted) {
|
if (this.searchSubmitted) {
|
||||||
this.application.getDesktopService()?.searchText(this.noteFilterText)
|
this.application.getDesktopService()?.searchText(this.noteFilterText)
|
||||||
@@ -390,8 +441,13 @@ export class NotesViewState extends AbstractState {
|
|||||||
return document.getElementById(ELEMENT_ID_SCROLL_CONTAINER)
|
return document.getElementById(ELEMENT_ID_SCROLL_CONTAINER)
|
||||||
}
|
}
|
||||||
|
|
||||||
selectNote = async (note: SNNote, userTriggered?: boolean, scrollIntoView = true): Promise<void> => {
|
selectNoteWithScrollHandling = async (
|
||||||
|
note: SNNote,
|
||||||
|
userTriggered?: boolean,
|
||||||
|
scrollIntoView = true,
|
||||||
|
): Promise<void> => {
|
||||||
await this.appState.notes.selectNote(note.uuid, userTriggered)
|
await this.appState.notes.selectNote(note.uuid, userTriggered)
|
||||||
|
|
||||||
if (scrollIntoView) {
|
if (scrollIntoView) {
|
||||||
const noteElement = document.getElementById(`note-${note.uuid}`)
|
const noteElement = document.getElementById(`note-${note.uuid}`)
|
||||||
noteElement?.scrollIntoView({
|
noteElement?.scrollIntoView({
|
||||||
@@ -400,10 +456,12 @@ export class NotesViewState extends AbstractState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectFirstNote = () => {
|
selectFirstNote = async () => {
|
||||||
const note = this.getFirstNonProtectedNote()
|
const note = this.getFirstNonProtectedNote()
|
||||||
|
|
||||||
if (note) {
|
if (note) {
|
||||||
this.selectNote(note, false, false).catch(console.error)
|
await this.selectNoteWithScrollHandling(note, false, false)
|
||||||
|
|
||||||
this.resetScrollPosition()
|
this.resetScrollPosition()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -411,33 +469,37 @@ export class NotesViewState extends AbstractState {
|
|||||||
selectNextNote = () => {
|
selectNextNote = () => {
|
||||||
const displayableNotes = this.notes
|
const displayableNotes = this.notes
|
||||||
const currentIndex = displayableNotes.findIndex((candidate) => {
|
const currentIndex = displayableNotes.findIndex((candidate) => {
|
||||||
return candidate.uuid === this.activeEditorNote?.uuid
|
return candidate.uuid === this.activeControllerNote?.uuid
|
||||||
})
|
})
|
||||||
|
|
||||||
if (currentIndex + 1 < displayableNotes.length) {
|
if (currentIndex + 1 < displayableNotes.length) {
|
||||||
const nextNote = displayableNotes[currentIndex + 1]
|
const nextNote = displayableNotes[currentIndex + 1]
|
||||||
this.selectNote(nextNote).catch(console.error)
|
|
||||||
|
this.selectNoteWithScrollHandling(nextNote).catch(console.error)
|
||||||
|
|
||||||
const nextNoteElement = document.getElementById(`note-${nextNote.uuid}`)
|
const nextNoteElement = document.getElementById(`note-${nextNote.uuid}`)
|
||||||
nextNoteElement?.focus()
|
nextNoteElement?.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectNextOrCreateNew = () => {
|
selectNextOrCreateNew = async () => {
|
||||||
const note = this.getFirstNonProtectedNote()
|
const note = this.getFirstNonProtectedNote()
|
||||||
|
|
||||||
if (note) {
|
if (note) {
|
||||||
this.selectNote(note, false, false).catch(console.error)
|
await this.selectNoteWithScrollHandling(note, false, false).catch(console.error)
|
||||||
} else {
|
} else {
|
||||||
this.appState.closeActiveNoteController()
|
await this.createNewNote()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectPreviousNote = () => {
|
selectPreviousNote = () => {
|
||||||
const displayableNotes = this.notes
|
const displayableNotes = this.notes
|
||||||
|
|
||||||
if (this.activeEditorNote) {
|
if (this.activeControllerNote) {
|
||||||
const currentIndex = displayableNotes.indexOf(this.activeEditorNote)
|
const currentIndex = displayableNotes.indexOf(this.activeControllerNote)
|
||||||
if (currentIndex - 1 >= 0) {
|
if (currentIndex - 1 >= 0) {
|
||||||
const previousNote = displayableNotes[currentIndex - 1]
|
const previousNote = displayableNotes[currentIndex - 1]
|
||||||
this.selectNote(previousNote).catch(console.error)
|
this.selectNoteWithScrollHandling(previousNote).catch(console.error)
|
||||||
const previousNoteElement = document.getElementById(`note-${previousNote.uuid}`)
|
const previousNoteElement = document.getElementById(`note-${previousNote.uuid}`)
|
||||||
previousNoteElement?.focus()
|
previousNoteElement?.focus()
|
||||||
return true
|
return true
|
||||||
@@ -450,16 +512,17 @@ export class NotesViewState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setNoteFilterText = (text: string) => {
|
setNoteFilterText = (text: string) => {
|
||||||
|
if (text === this.noteFilterText) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.noteFilterText = text
|
this.noteFilterText = text
|
||||||
this.handleFilterTextChanged()
|
this.handleFilterTextChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
syncSelectedNotes = () => {
|
|
||||||
this.selectedNotes = this.appState.notes.selectedNotes
|
|
||||||
}
|
|
||||||
|
|
||||||
handleEditorChange = async () => {
|
handleEditorChange = async () => {
|
||||||
const activeNote = this.appState.getActiveNoteController()?.note
|
const activeNote = this.application.noteControllerGroup.activeNoteViewController?.note
|
||||||
|
|
||||||
if (activeNote && activeNote.conflictOf) {
|
if (activeNote && activeNote.conflictOf) {
|
||||||
this.application.mutator
|
this.application.mutator
|
||||||
.changeAndSaveItem(activeNote, (mutator) => {
|
.changeAndSaveItem(activeNote, (mutator) => {
|
||||||
@@ -481,6 +544,11 @@ export class NotesViewState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleTagChange = () => {
|
handleTagChange = () => {
|
||||||
|
const activeNoteController = this.getActiveNoteController()
|
||||||
|
if (activeNoteController?.isTemplateNote) {
|
||||||
|
this.application.noteControllerGroup.closeNoteController(activeNoteController)
|
||||||
|
}
|
||||||
|
|
||||||
this.resetScrollPosition()
|
this.resetScrollPosition()
|
||||||
|
|
||||||
this.setShowDisplayOptionsMenu(false)
|
this.setShowDisplayOptionsMenu(false)
|
||||||
@@ -491,21 +559,9 @@ export class NotesViewState extends AbstractState {
|
|||||||
|
|
||||||
this.resetPagination()
|
this.resetPagination()
|
||||||
|
|
||||||
/* Capture db load state before beginning reloadNotes,
|
|
||||||
since this status may change during reload */
|
|
||||||
const dbLoaded = this.application.isDatabaseLoaded()
|
|
||||||
this.reloadNotesDisplayOptions()
|
this.reloadNotesDisplayOptions()
|
||||||
this.reloadNotes()
|
|
||||||
|
|
||||||
const hasSomeNotes = this.notes.length > 0
|
void this.reloadNotes()
|
||||||
|
|
||||||
if (hasSomeNotes) {
|
|
||||||
this.selectFirstNote()
|
|
||||||
} else if (dbLoaded) {
|
|
||||||
if (this.activeEditorNote && !this.notes.includes(this.activeEditorNote)) {
|
|
||||||
this.appState.closeActiveNoteController()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onFilterEnter = () => {
|
onFilterEnter = () => {
|
||||||
@@ -519,12 +575,24 @@ export class NotesViewState extends AbstractState {
|
|||||||
this.application.getDesktopService()?.searchText(this.noteFilterText)
|
this.application.getDesktopService()?.searchText(this.noteFilterText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async insertCurrentIfTemplate(): Promise<void> {
|
||||||
|
const controller = this.getActiveNoteController()
|
||||||
|
|
||||||
|
if (!controller) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controller.isTemplateNote) {
|
||||||
|
await controller.insertTemplatedNote()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleFilterTextChanged = () => {
|
handleFilterTextChanged = () => {
|
||||||
if (this.searchSubmitted) {
|
if (this.searchSubmitted) {
|
||||||
this.searchSubmitted = false
|
this.searchSubmitted = false
|
||||||
}
|
}
|
||||||
this.reloadNotesDisplayOptions()
|
this.reloadNotesDisplayOptions()
|
||||||
this.reloadNotes()
|
void this.reloadNotes()
|
||||||
}
|
}
|
||||||
|
|
||||||
clearFilterText = () => {
|
clearFilterText = () => {
|
||||||
|
|||||||
@@ -137,19 +137,25 @@ export class TagsState extends AbstractState {
|
|||||||
|
|
||||||
this.smartViews = this.application.items.getSmartViews()
|
this.smartViews = this.application.items.getSmartViews()
|
||||||
|
|
||||||
const selectedTag = this.selected_
|
const currrentSelectedTag = this.selected_
|
||||||
|
|
||||||
if (selectedTag && !isSystemView(selectedTag as SmartView)) {
|
if (!currrentSelectedTag) {
|
||||||
if (FindItem(removed, selectedTag.uuid)) {
|
this.setSelectedTagInstance(this.smartViews[0])
|
||||||
this.selected_ = this.smartViews[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
const updated = FindItem(changed, selectedTag.uuid)
|
return
|
||||||
if (updated) {
|
}
|
||||||
this.selected_ = updated as AnyTag
|
|
||||||
}
|
if (isSystemView(currrentSelectedTag as SmartView)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FindItem(removed, currrentSelectedTag.uuid)) {
|
||||||
|
this.setSelectedTagInstance(this.smartViews[0])
|
||||||
} else {
|
} else {
|
||||||
this.selected_ = this.smartViews[0]
|
const updated = FindItem(changed, currrentSelectedTag.uuid)
|
||||||
|
if (updated) {
|
||||||
|
this.setSelectedTagInstance(updated as AnyTag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
@@ -379,6 +385,11 @@ export class TagsState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.previouslySelected_ = this.selected_
|
this.previouslySelected_ = this.selected_
|
||||||
|
|
||||||
|
this.setSelectedTagInstance(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
private setSelectedTagInstance(tag: AnyTag | undefined): void {
|
||||||
this.selected_ = tag
|
this.selected_ = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
"@standardnotes/filepicker": "1.14.8",
|
"@standardnotes/filepicker": "1.14.8",
|
||||||
"@standardnotes/icons": "^1.1.7",
|
"@standardnotes/icons": "^1.1.7",
|
||||||
"@standardnotes/sncrypto-web": "1.9.2",
|
"@standardnotes/sncrypto-web": "1.9.2",
|
||||||
"@standardnotes/snjs": "2.109.3",
|
"@standardnotes/snjs": "2.109.4",
|
||||||
"@standardnotes/stylekit": "5.27.0",
|
"@standardnotes/stylekit": "5.27.0",
|
||||||
"@zip.js/zip.js": "^2.4.10",
|
"@zip.js/zip.js": "^2.4.10",
|
||||||
"mobx": "^6.5.0",
|
"mobx": "^6.5.0",
|
||||||
|
|||||||
@@ -2401,10 +2401,10 @@
|
|||||||
buffer "^6.0.3"
|
buffer "^6.0.3"
|
||||||
libsodium-wrappers "^0.7.9"
|
libsodium-wrappers "^0.7.9"
|
||||||
|
|
||||||
"@standardnotes/snjs@2.109.3":
|
"@standardnotes/snjs@2.109.4":
|
||||||
version "2.109.3"
|
version "2.109.4"
|
||||||
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.109.3.tgz#a6fcd8af317caf340c6099b5e7cfb36f1ec7a1ba"
|
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.109.4.tgz#46c1915b2c8d43e0806fd9ad8bb9868f2ae3a3e8"
|
||||||
integrity sha512-RIV22G99ZuolxrHTrh12i9IicxsXYZ9kK0mzfG1uO3E7BtFyvKgaoX5Yugi305Q0WsLzf4bWaeAuWl7i3nTJ0Q==
|
integrity sha512-DLoLR9RI517zz0N+sA6iHYY8bJ1rGVgOFyKWQVHjJajPwnCllEvETLGKz4IodfAao4lxU9Q3xPQ31q6VyCvWxQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@standardnotes/auth" "^3.18.16"
|
"@standardnotes/auth" "^3.18.16"
|
||||||
"@standardnotes/common" "^1.21.0"
|
"@standardnotes/common" "^1.21.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user