refactor: pass sub controllers to controllers instead of passing global controller manager (#1061)

This commit is contained in:
Mo
2022-06-01 12:56:30 -05:00
committed by GitHub
parent 721cf8df35
commit a87e3b98e2
40 changed files with 672 additions and 591 deletions

View File

@@ -1,14 +1,27 @@
import { DeinitSource } from '@standardnotes/snjs'
import { CrossControllerEvent } from '../CrossControllerEvent'
import { InternalEventBus, InternalEventPublishStrategy } from '@standardnotes/snjs'
import { WebApplication } from '../../Application/Application'
import { Disposer } from '@/Types/Disposer'
export abstract class AbstractViewController {
dealloced = false
protected disposers: Disposer[] = []
constructor(public application: WebApplication, public viewControllerManager?: AbstractViewController) {}
constructor(public application: WebApplication, protected eventBus: InternalEventBus) {}
deinit(_source: DeinitSource): void {
protected async publishEventSync(name: CrossControllerEvent): Promise<void> {
await this.eventBus.publishSync({ type: name, payload: undefined }, InternalEventPublishStrategy.SEQUENCE)
}
deinit(): void {
this.dealloced = true
;(this.application as unknown) = undefined
;(this.viewControllerManager as unknown) = undefined
;(this.eventBus as unknown) = undefined
for (const disposer of this.disposers) {
disposer()
}
;(this.disposers as unknown) = undefined
}
}

View File

@@ -1,5 +1,3 @@
import { AbstractViewController } from './AbstractViewController'
export function isControllerDealloced(state: AbstractViewController): boolean {
return state.dealloced == undefined || state.dealloced === true
export function isControllerDealloced(controller: { dealloced: boolean }): boolean {
return controller.dealloced == undefined || controller.dealloced === true
}

View File

@@ -1,6 +1,6 @@
import { destroyAllObjectProperties, isDev } from '@/Utils'
import { action, computed, makeObservable, observable, runInAction } from 'mobx'
import { ApplicationEvent, ContentType, DeinitSource, SNNote, SNTag } from '@standardnotes/snjs'
import { ApplicationEvent, ContentType, InternalEventBus, SNNote, SNTag } from '@standardnotes/snjs'
import { WebApplication } from '@/Application/Application'
import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane'
import { AbstractViewController } from '../Abstract/AbstractViewController'
@@ -21,15 +21,16 @@ export class AccountMenuController extends AbstractViewController {
shouldAnimateCloseMenu = false
currentPane = AccountMenuPane.GeneralMenu
override deinit(source: DeinitSource) {
super.deinit(source)
override deinit() {
super.deinit()
;(this.notesAndTags as unknown) = undefined
destroyAllObjectProperties(this)
}
constructor(application: WebApplication, private appEventListeners: (() => void)[]) {
super(application)
constructor(application: WebApplication, eventBus: InternalEventBus) {
super(application, eventBus)
makeObservable(this, {
show: observable,
signingOut: observable,
@@ -60,12 +61,7 @@ export class AccountMenuController extends AbstractViewController {
notesAndTagsCount: computed,
})
this.addAppLaunchedEventObserver()
this.streamNotesAndTags()
}
addAppLaunchedEventObserver = (): void => {
this.appEventListeners.push(
this.disposers.push(
this.application.addEventObserver(async () => {
runInAction(() => {
if (isDev && window.devAccountServer) {
@@ -77,10 +73,8 @@ export class AccountMenuController extends AbstractViewController {
})
}, ApplicationEvent.Launched),
)
}
streamNotesAndTags = (): void => {
this.appEventListeners.push(
this.disposers.push(
this.application.streamItems([ContentType.Note, ContentType.Tag], () => {
runInAction(() => {
this.notesAndTags = this.application.items.getItems([ContentType.Note, ContentType.Tag])

View File

@@ -0,0 +1,4 @@
export enum CrossControllerEvent {
TagChanged = 'TagChanged',
ActiveEditorChanged = 'ActiveEditorChanged',
}

View File

@@ -1,6 +1,6 @@
import { WebApplication } from '@/Application/Application'
import { destroyAllObjectProperties } from '@/Utils'
import { ApplicationEvent, DeinitSource, FeatureIdentifier, FeatureStatus } from '@standardnotes/snjs'
import { ApplicationEvent, FeatureIdentifier, FeatureStatus, InternalEventBus } from '@standardnotes/snjs'
import { action, makeObservable, observable, runInAction, when } from 'mobx'
import { AbstractViewController } from './Abstract/AbstractViewController'
@@ -10,8 +10,8 @@ export class FeaturesController extends AbstractViewController {
hasFiles: boolean
premiumAlertFeatureName: string | undefined
override deinit(source: DeinitSource) {
super.deinit(source)
override deinit() {
super.deinit()
;(this.showPremiumAlert as unknown) = undefined
;(this.closePremiumAlert as unknown) = undefined
;(this.hasFolders as unknown) = undefined
@@ -22,8 +22,8 @@ export class FeaturesController extends AbstractViewController {
destroyAllObjectProperties(this)
}
constructor(application: WebApplication, appObservers: (() => void)[]) {
super(application)
constructor(application: WebApplication, eventBus: InternalEventBus) {
super(application, eventBus)
this.hasFolders = this.isEntitledToFolders()
this.hasSmartViews = this.isEntitledToSmartViews()
@@ -43,7 +43,7 @@ export class FeaturesController extends AbstractViewController {
this.showPremiumAlert = this.showPremiumAlert.bind(this)
this.closePremiumAlert = this.closePremiumAlert.bind(this)
appObservers.push(
this.disposers.push(
application.addEventObserver(async (event) => {
switch (event) {
case ApplicationEvent.FeaturesUpdated:

View File

@@ -1,3 +1,4 @@
import { FilePreviewModalController } from './FilePreviewModalController'
import {
PopoverFileItemAction,
PopoverFileItemActionType,
@@ -13,12 +14,13 @@ import {
ClassicFileSaver,
parseFileName,
} from '@standardnotes/filepicker'
import { ChallengeReason, ClientDisplayableError, ContentType, FileItem } from '@standardnotes/snjs'
import { ChallengeReason, ClientDisplayableError, ContentType, FileItem, InternalEventBus } from '@standardnotes/snjs'
import { addToast, dismissToast, ToastType, updateToast } from '@standardnotes/stylekit'
import { action, computed, makeObservable, observable, reaction } from 'mobx'
import { WebApplication } from '../Application/Application'
import { AbstractViewController } from './Abstract/AbstractViewController'
import { ViewControllerManager } from '../Services/ViewControllerManager/ViewControllerManager'
import { NotesController } from './NotesController'
import { SelectedItemsController } from './SelectedItemsController'
const UnprotectedFileActions = [PopoverFileItemActionType.ToggleFileProtection]
const NonMutatingFileActions = [PopoverFileItemActionType.DownloadFile, PopoverFileItemActionType.PreviewFile]
@@ -31,12 +33,21 @@ export class FilesController extends AbstractViewController {
showFileContextMenu = false
fileContextMenuLocation: FileContextMenuLocation = { x: 0, y: 0 }
override deinit(): void {
super.deinit()
;(this.notesController as unknown) = undefined
;(this.selectionController as unknown) = undefined
;(this.filePreviewModalController as unknown) = undefined
}
constructor(
application: WebApplication,
override viewControllerManager: ViewControllerManager,
appObservers: (() => void)[],
private notesController: NotesController,
private selectionController: SelectedItemsController,
private filePreviewModalController: FilePreviewModalController,
eventBus: InternalEventBus,
) {
super(application, viewControllerManager)
super(application, eventBus)
makeObservable(this, {
allFiles: observable,
@@ -52,13 +63,16 @@ export class FilesController extends AbstractViewController {
setFileContextMenuLocation: action,
})
appObservers.push(
this.disposers.push(
application.streamItems(ContentType.File, () => {
this.reloadAllFiles()
this.reloadAttachedFiles()
}),
)
this.disposers.push(
reaction(
() => viewControllerManager.notesController.selectedNotes,
() => notesController.selectedNotes,
() => {
this.reloadAttachedFiles()
},
@@ -67,7 +81,7 @@ export class FilesController extends AbstractViewController {
}
get selectedFiles(): FileItem[] {
return this.viewControllerManager.selectionController.getSelectedItems<FileItem>(ContentType.File)
return this.selectionController.getSelectedItems<FileItem>(ContentType.File)
}
setShowFileContextMenu = (enabled: boolean) => {
@@ -83,7 +97,7 @@ export class FilesController extends AbstractViewController {
}
reloadAttachedFiles = () => {
const note = this.viewControllerManager.notesController.firstSelectedNote
const note = this.notesController.firstSelectedNote
if (note) {
this.attachedFiles = this.application.items.getFilesForNote(note)
}
@@ -109,7 +123,7 @@ export class FilesController extends AbstractViewController {
}
attachFileToNote = async (file: FileItem) => {
const note = this.viewControllerManager.notesController.firstSelectedNote
const note = this.notesController.firstSelectedNote
if (!note) {
addToast({
type: ToastType.Error,
@@ -122,7 +136,7 @@ export class FilesController extends AbstractViewController {
}
detachFileFromNote = async (file: FileItem) => {
const note = this.viewControllerManager.notesController.firstSelectedNote
const note = this.notesController.firstSelectedNote
if (!note) {
addToast({
type: ToastType.Error,
@@ -197,7 +211,7 @@ export class FilesController extends AbstractViewController {
await this.renameFile(file, action.payload.name)
break
case PopoverFileItemActionType.PreviewFile:
this.viewControllerManager.filePreviewModalController.activate(
this.filePreviewModalController.activate(
file,
currentTab === PopoverTabs.AllFiles ? this.allFiles : this.attachedFiles,
)

View File

@@ -4,7 +4,6 @@ import {
ApplicationEvent,
CollectionSort,
ContentType,
DeinitSource,
findInArray,
NoteViewController,
PrefKey,
@@ -13,13 +12,21 @@ import {
SNTag,
SystemViewId,
DisplayOptions,
InternalEventBus,
InternalEventHandlerInterface,
InternalEventInterface,
} from '@standardnotes/snjs'
import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'
import { WebApplication } from '../../Application/Application'
import { AbstractViewController } from '../Abstract/AbstractViewController'
import { ViewControllerManager } from '../../Services/ViewControllerManager/ViewControllerManager'
import { ViewControllerManagerEvent } from '../../Services/ViewControllerManager/ViewControllerManagerEvent'
import { WebDisplayOptions } from './WebDisplayOptions'
import { NavigationController } from '../Navigation/NavigationController'
import { CrossControllerEvent } from '../CrossControllerEvent'
import { SearchOptionsController } from '../SearchOptionsController'
import { SelectedItemsController } from '../SelectedItemsController'
import { NotesController } from '../NotesController'
import { NoteTagsController } from '../NoteTagsController'
import { WebAppEvent } from '@/Application/WebAppEvent'
const MinNoteCellHeight = 51.0
const DefaultListNumNotes = 20
@@ -27,7 +34,7 @@ const ElementIdSearchBar = 'search-bar'
const ElementIdScrollContainer = 'notes-scrollable'
const SupportsFileSelectionState = false
export class ItemListController extends AbstractViewController {
export class ItemListController extends AbstractViewController implements InternalEventHandlerInterface {
completedFullSync = false
noteFilterText = ''
notes: SNNote[] = []
@@ -55,11 +62,16 @@ export class ItemListController extends AbstractViewController {
}
private reloadItemsPromise?: Promise<unknown>
override deinit(source: DeinitSource) {
super.deinit(source)
override deinit() {
super.deinit()
;(this.noteFilterText as unknown) = undefined
;(this.notes as unknown) = undefined
;(this.renderedItems as unknown) = undefined
;(this.navigationController as unknown) = undefined
;(this.searchOptionsController as unknown) = undefined
;(this.selectionController as unknown) = undefined
;(this.notesController as unknown) = undefined
;(this.noteTagsController as unknown) = undefined
;(window.onresize as unknown) = undefined
destroyAllObjectProperties(this)
@@ -67,18 +79,27 @@ export class ItemListController extends AbstractViewController {
constructor(
application: WebApplication,
override viewControllerManager: ViewControllerManager,
appObservers: (() => void)[],
private navigationController: NavigationController,
private searchOptionsController: SearchOptionsController,
private selectionController: SelectedItemsController,
private notesController: NotesController,
private noteTagsController: NoteTagsController,
eventBus: InternalEventBus,
) {
super(application, viewControllerManager)
super(application, eventBus)
eventBus.addEventHandler(this, CrossControllerEvent.TagChanged)
eventBus.addEventHandler(this, CrossControllerEvent.ActiveEditorChanged)
this.resetPagination()
appObservers.push(
this.disposers.push(
application.streamItems<SNNote>(ContentType.Note, () => {
void this.reloadItems()
}),
)
this.disposers.push(
application.streamItems<SNTag>([ContentType.Tag], async ({ changed, inserted }) => {
const tags = [...changed, ...inserted]
@@ -87,28 +108,34 @@ export class ItemListController extends AbstractViewController {
void this.reloadItems()
if (
viewControllerManager.navigationController.selected &&
findInArray(tags, 'uuid', viewControllerManager.navigationController.selected.uuid)
) {
if (this.navigationController.selected && findInArray(tags, 'uuid', this.navigationController.selected.uuid)) {
/** Tag title could have changed */
this.reloadPanelTitle()
}
}),
)
this.disposers.push(
application.addEventObserver(async () => {
void this.reloadPreferences()
}, ApplicationEvent.PreferencesChanged),
)
this.disposers.push(
application.addEventObserver(async () => {
this.application.noteControllerGroup.closeAllNoteControllers()
void this.selectFirstItem()
this.setCompletedFullSync(false)
}, ApplicationEvent.SignedIn),
)
this.disposers.push(
application.addEventObserver(async () => {
void this.reloadItems().then(() => {
if (
this.notes.length === 0 &&
viewControllerManager.navigationController.selected instanceof SmartView &&
viewControllerManager.navigationController.selected.uuid === SystemViewId.AllNotes &&
this.navigationController.selected instanceof SmartView &&
this.navigationController.selected.uuid === SystemViewId.AllNotes &&
this.noteFilterText === '' &&
!this.getActiveNoteController()
) {
@@ -117,28 +144,28 @@ export class ItemListController extends AbstractViewController {
})
this.setCompletedFullSync(true)
}, ApplicationEvent.CompletedFullSync),
)
this.disposers.push(
application.addWebEventObserver((webEvent) => {
if (webEvent === WebAppEvent.EditorFocused) {
this.setShowDisplayOptionsMenu(false)
}
}),
)
this.disposers.push(
reaction(
() => [
viewControllerManager.searchOptionsController.includeProtectedContents,
viewControllerManager.searchOptionsController.includeArchived,
viewControllerManager.searchOptionsController.includeTrashed,
this.searchOptionsController.includeProtectedContents,
this.searchOptionsController.includeArchived,
this.searchOptionsController.includeTrashed,
],
() => {
this.reloadNotesDisplayOptions()
void this.reloadItems()
},
),
viewControllerManager.addObserver(async (eventName) => {
if (eventName === ViewControllerManagerEvent.TagChanged) {
this.handleTagChange()
} else if (eventName === ViewControllerManagerEvent.ActiveEditorChanged) {
this.handleEditorChange().catch(console.error)
} else if (eventName === ViewControllerManagerEvent.EditorFocused) {
this.setShowDisplayOptionsMenu(false)
}
}),
)
makeObservable(this, {
@@ -170,6 +197,14 @@ export class ItemListController extends AbstractViewController {
}
}
async handleEvent(event: InternalEventInterface): Promise<void> {
if (event.type === CrossControllerEvent.TagChanged) {
this.handleTagChange()
} else if (event.type === CrossControllerEvent.ActiveEditorChanged) {
this.handleEditorChange().catch(console.error)
}
}
public getActiveNoteController(): NoteViewController | undefined {
return this.application.noteControllerGroup.activeNoteViewController
}
@@ -200,8 +235,8 @@ export class ItemListController extends AbstractViewController {
if (this.isFiltering) {
const resultCount = this.notes.length
title = `${resultCount} search results`
} else if (this.viewControllerManager.navigationController.selected) {
title = `${this.viewControllerManager.navigationController.selected.title}`
} else if (this.navigationController.selected) {
title = `${this.navigationController.selected.title}`
}
this.panelTitle = title
@@ -218,7 +253,7 @@ export class ItemListController extends AbstractViewController {
}
private async performReloadItems() {
const tag = this.viewControllerManager.navigationController.selected
const tag = this.navigationController.selected
if (!tag) {
return
}
@@ -241,17 +276,16 @@ export class ItemListController extends AbstractViewController {
}
private async recomputeSelectionAfterItemsReload() {
const viewControllerManager = this.viewControllerManager
const activeController = this.getActiveNoteController()
const activeNote = activeController?.note
const isSearching = this.noteFilterText.length > 0
const hasMultipleItemsSelected = viewControllerManager.selectionController.selectedItemsCount >= 2
const hasMultipleItemsSelected = this.selectionController.selectedItemsCount >= 2
if (hasMultipleItemsSelected) {
return
}
const selectedItem = Object.values(viewControllerManager.selectionController.selectedItems)[0]
const selectedItem = Object.values(this.selectionController.selectedItems)[0]
const isSelectedItemFile =
this.items.includes(selectedItem) && selectedItem && selectedItem.content_type === ContentType.File
@@ -280,25 +314,25 @@ export class ItemListController extends AbstractViewController {
}
const showTrashedNotes =
(viewControllerManager.navigationController.selected instanceof SmartView &&
viewControllerManager.navigationController.selected?.uuid === SystemViewId.TrashedNotes) ||
viewControllerManager?.searchOptionsController.includeTrashed
(this.navigationController.selected instanceof SmartView &&
this.navigationController.selected?.uuid === SystemViewId.TrashedNotes) ||
this.searchOptionsController.includeTrashed
const showArchivedNotes =
(viewControllerManager.navigationController.selected instanceof SmartView &&
viewControllerManager.navigationController.selected.uuid === SystemViewId.ArchivedNotes) ||
viewControllerManager.searchOptionsController.includeArchived ||
(this.navigationController.selected instanceof SmartView &&
this.navigationController.selected.uuid === SystemViewId.ArchivedNotes) ||
this.searchOptionsController.includeArchived ||
this.application.getPreference(PrefKey.NotesShowArchived, false)
if ((activeNote.trashed && !showTrashedNotes) || (activeNote.archived && !showArchivedNotes)) {
await this.selectNextItemOrCreateNewNote()
} else if (!this.viewControllerManager.selectionController.selectedItems[activeNote.uuid]) {
await this.viewControllerManager.selectionController.selectItem(activeNote.uuid).catch(console.error)
} else if (!this.selectionController.selectedItems[activeNote.uuid]) {
await this.selectionController.selectItem(activeNote.uuid).catch(console.error)
}
}
reloadNotesDisplayOptions = () => {
const tag = this.viewControllerManager.navigationController.selected
const tag = this.navigationController.selected
const searchText = this.noteFilterText.toLowerCase()
const isSearching = searchText.length
@@ -306,8 +340,8 @@ export class ItemListController extends AbstractViewController {
let includeTrashed: boolean
if (isSearching) {
includeArchived = this.viewControllerManager.searchOptionsController.includeArchived
includeTrashed = this.viewControllerManager.searchOptionsController.includeTrashed
includeArchived = this.searchOptionsController.includeArchived
includeTrashed = this.searchOptionsController.includeTrashed
} else {
includeArchived = this.displayOptions.includeArchived ?? false
includeTrashed = this.displayOptions.includeTrashed ?? false
@@ -324,7 +358,7 @@ export class ItemListController extends AbstractViewController {
includeProtected: this.displayOptions.includeProtected,
searchQuery: {
query: searchText,
includeProtectedNoteText: this.viewControllerManager.searchOptionsController.includeProtectedContents,
includeProtectedNoteText: this.searchOptionsController.includeProtectedContents,
},
}
@@ -387,13 +421,10 @@ export class ItemListController extends AbstractViewController {
}
createNewNote = async () => {
this.viewControllerManager.notesController.unselectNotes()
this.notesController.unselectNotes()
if (
this.viewControllerManager.navigationController.isInSmartView() &&
!this.viewControllerManager.navigationController.isInHomeView()
) {
await this.viewControllerManager.navigationController.selectHomeNavigationView()
if (this.navigationController.isInSmartView() && !this.navigationController.isInHomeView()) {
await this.navigationController.selectHomeNavigationView()
}
let title = `Note ${this.notes.length + 1}`
@@ -401,16 +432,13 @@ export class ItemListController extends AbstractViewController {
title = this.noteFilterText
}
await this.viewControllerManager.notesController.createNewNoteController(title)
await this.notesController.createNewNoteController(title)
this.viewControllerManager.noteTagsController.reloadTagsForCurrentNote()
this.noteTagsController.reloadTagsForCurrentNote()
}
createPlaceholderNote = () => {
if (
this.viewControllerManager.navigationController.isInSmartView() &&
!this.viewControllerManager.navigationController.isInHomeView()
) {
if (this.navigationController.isInSmartView() && !this.navigationController.isInHomeView()) {
return
}
@@ -487,7 +515,7 @@ export class ItemListController extends AbstractViewController {
},
{ userTriggered = false, scrollIntoView = true },
): Promise<void> => {
await this.viewControllerManager.selectionController.selectItem(item.uuid, userTriggered)
await this.selectionController.selectItem(item.uuid, userTriggered)
if (scrollIntoView) {
const itemElement = document.getElementById(item.uuid)
@@ -514,7 +542,7 @@ export class ItemListController extends AbstractViewController {
const displayableItems = this.items
const currentIndex = displayableItems.findIndex((candidate) => {
return candidate.uuid === this.viewControllerManager.selectionController.lastSelectedItem?.uuid
return candidate.uuid === this.selectionController.lastSelectedItem?.uuid
})
let nextIndex = currentIndex + 1
@@ -554,11 +582,11 @@ export class ItemListController extends AbstractViewController {
selectPreviousItem = () => {
const displayableItems = this.items
if (!this.viewControllerManager.selectionController.lastSelectedItem) {
if (!this.selectionController.lastSelectedItem) {
return
}
const currentIndex = displayableItems.indexOf(this.viewControllerManager.selectionController.lastSelectedItem)
const currentIndex = displayableItems.indexOf(this.selectionController.lastSelectedItem)
let previousIndex = currentIndex - 1

View File

@@ -11,20 +11,20 @@ import {
UuidString,
isSystemView,
FindItem,
DeinitSource,
SystemViewId,
InternalEventBus,
InternalEventPublishStrategy,
} from '@standardnotes/snjs'
import { action, computed, makeAutoObservable, makeObservable, observable, runInAction } from 'mobx'
import { WebApplication } from '../../Application/Application'
import { FeaturesController } from '../FeaturesController'
import { AbstractViewController } from '../Abstract/AbstractViewController'
import { destroyAllObjectProperties } from '@/Utils'
import { ViewControllerManager } from '../../Services/ViewControllerManager/ViewControllerManager'
import { ViewControllerManagerEvent } from '../../Services/ViewControllerManager/ViewControllerManagerEvent'
import { isValidFutureSiblings, rootTags, tagSiblings } from './Utils'
import { AnyTag } from './AnyTagType'
import { CrossControllerEvent } from '../CrossControllerEvent'
export class TagsController extends AbstractViewController {
export class NavigationController extends AbstractViewController {
tags: SNTag[] = []
smartViews: SmartView[] = []
allNotesCount_ = 0
@@ -43,13 +43,8 @@ export class TagsController extends AbstractViewController {
private readonly tagsCountsState: TagsCountsState
constructor(
application: WebApplication,
override viewControllerManager: ViewControllerManager,
appEventListeners: (() => void)[],
private features: FeaturesController,
) {
super(application)
constructor(application: WebApplication, private featuresController: FeaturesController, eventBus: InternalEventBus) {
super(application, eventBus)
this.tagsCountsState = new TagsCountsState(this.application)
@@ -99,7 +94,7 @@ export class TagsController extends AbstractViewController {
setContextMenuMaxHeight: action,
})
appEventListeners.push(
this.disposers.push(
this.application.streamItems([ContentType.Tag, ContentType.SmartView], ({ changed, removed }) => {
runInAction(() => {
this.tags = this.application.items.getDisplayableTags()
@@ -131,7 +126,7 @@ export class TagsController extends AbstractViewController {
}),
)
appEventListeners.push(
this.disposers.push(
this.application.items.addNoteCountChangeObserver((tagUuid) => {
if (!tagUuid) {
this.setAllNotesCount(this.application.items.allCountableNotesCount())
@@ -145,15 +140,16 @@ export class TagsController extends AbstractViewController {
)
}
override deinit(source: DeinitSource) {
super.deinit(source)
;(this.features as unknown) = undefined
override deinit() {
super.deinit()
;(this.featuresController as unknown) = undefined
;(this.tags as unknown) = undefined
;(this.smartViews as unknown) = undefined
;(this.selected_ as unknown) = undefined
;(this.previouslySelected_ as unknown) = undefined
;(this.editing_ as unknown) = undefined
;(this.addingSubtagTo as unknown) = undefined
;(this.featuresController as unknown) = undefined
destroyAllObjectProperties(this)
}
@@ -369,10 +365,13 @@ export class TagsController extends AbstractViewController {
return
}
await this.viewControllerManager.notifyEvent(ViewControllerManagerEvent.TagChanged, {
tag,
previousTag: this.previouslySelected_,
})
await this.eventBus.publishSync(
{
type: CrossControllerEvent.TagChanged,
payload: { tag, previousTag: this.previouslySelected_ },
},
InternalEventPublishStrategy.SEQUENCE,
)
}
public async selectHomeNavigationView(): Promise<void> {
@@ -473,8 +472,8 @@ export class TagsController extends AbstractViewController {
const isSmartViewTitle = this.application.items.isSmartViewTitle(newTitle)
if (isSmartViewTitle) {
if (!this.features.hasSmartViews) {
await this.features.showPremiumAlert(SMART_TAGS_FEATURE_NAME)
if (!this.featuresController.hasSmartViews) {
await this.featuresController.showPremiumAlert(SMART_TAGS_FEATURE_NAME)
return
}
}

View File

@@ -1,5 +1,5 @@
import { storage, StorageKey } from '@/Services/LocalStorage'
import { ApplicationEvent } from '@standardnotes/snjs'
import { ApplicationEvent, InternalEventBus } from '@standardnotes/snjs'
import { runInAction, makeObservable, observable, action } from 'mobx'
import { WebApplication } from '../Application/Application'
import { AbstractViewController } from './Abstract/AbstractViewController'
@@ -7,17 +7,20 @@ import { AbstractViewController } from './Abstract/AbstractViewController'
export class NoAccountWarningController extends AbstractViewController {
show: boolean
constructor(application: WebApplication, appObservers: (() => void)[]) {
super(application)
constructor(application: WebApplication, eventBus: InternalEventBus) {
super(application, eventBus)
this.show = application.hasAccount() ? false : storage.get(StorageKey.ShowNoAccountWarning) ?? true
appObservers.push(
this.disposers.push(
application.addEventObserver(async () => {
runInAction(() => {
this.show = false
})
}, ApplicationEvent.SignedIn),
)
this.disposers.push(
application.addEventObserver(async () => {
if (application.hasAccount()) {
runInAction(() => {

View File

@@ -1,10 +1,18 @@
import { ElementIds } from '@/Constants/ElementIDs'
import { destroyAllObjectProperties } from '@/Utils'
import { ApplicationEvent, ContentType, DeinitSource, PrefKey, SNNote, SNTag, UuidString } from '@standardnotes/snjs'
import {
ApplicationEvent,
ContentType,
InternalEventBus,
PrefKey,
SNNote,
SNTag,
UuidString,
} from '@standardnotes/snjs'
import { action, computed, makeObservable, observable } from 'mobx'
import { WebApplication } from '../Application/Application'
import { AbstractViewController } from './Abstract/AbstractViewController'
import { ViewControllerManager } from '../Services/ViewControllerManager/ViewControllerManager'
import { ItemListController } from './ItemList/ItemListController'
export class NoteTagsController extends AbstractViewController {
autocompleteInputFocused = false
@@ -16,21 +24,19 @@ export class NoteTagsController extends AbstractViewController {
tags: SNTag[] = []
tagsContainerMaxWidth: number | 'auto' = 0
addNoteToParentFolders: boolean
private itemListController!: ItemListController
override deinit(source: DeinitSource) {
super.deinit(source)
override deinit() {
super.deinit()
;(this.tags as unknown) = undefined
;(this.autocompleteTagResults as unknown) = undefined
;(this.itemListController as unknown) = undefined
destroyAllObjectProperties(this)
}
constructor(
application: WebApplication,
override viewControllerManager: ViewControllerManager,
appEventListeners: (() => void)[],
) {
super(application, viewControllerManager)
constructor(application: WebApplication, eventBus: InternalEventBus) {
super(application, eventBus)
makeObservable(this, {
autocompleteInputFocused: observable,
@@ -55,13 +61,17 @@ export class NoteTagsController extends AbstractViewController {
})
this.addNoteToParentFolders = application.getPreference(PrefKey.NoteAddToParentFolders, true)
}
appEventListeners.push(
application.streamItems(ContentType.Tag, () => {
public setServicestPostConstruction(itemListController: ItemListController) {
this.itemListController = itemListController
this.disposers.push(
this.application.streamItems(ContentType.Tag, () => {
this.reloadTagsForCurrentNote()
}),
application.addSingleEventObserver(ApplicationEvent.PreferencesChanged, async () => {
this.addNoteToParentFolders = application.getPreference(PrefKey.NoteAddToParentFolders, true)
this.application.addSingleEventObserver(ApplicationEvent.PreferencesChanged, async () => {
this.addNoteToParentFolders = this.application.getPreference(PrefKey.NoteAddToParentFolders, true)
}),
)
}
@@ -151,7 +161,7 @@ export class NoteTagsController extends AbstractViewController {
searchActiveNoteAutocompleteTags(): void {
const newResults = this.application.items.searchTags(
this.autocompleteSearchQuery,
this.viewControllerManager.contentListController.activeControllerNote,
this.itemListController.activeControllerNote,
)
this.setAutocompleteTagResults(newResults)
}
@@ -161,7 +171,7 @@ export class NoteTagsController extends AbstractViewController {
}
reloadTagsForCurrentNote(): void {
const activeNote = this.viewControllerManager.contentListController.activeControllerNote
const activeNote = this.itemListController.activeControllerNote
if (activeNote) {
const tags = this.application.items.getSortedTagsForNote(activeNote)
@@ -177,7 +187,7 @@ export class NoteTagsController extends AbstractViewController {
}
async addTagToActiveNote(tag: SNTag): Promise<void> {
const activeNote = this.viewControllerManager.contentListController.activeControllerNote
const activeNote = this.itemListController.activeControllerNote
if (activeNote) {
await this.application.items.addTagToNote(activeNote, tag, this.addNoteToParentFolders)
@@ -187,7 +197,7 @@ export class NoteTagsController extends AbstractViewController {
}
async removeTagFromActiveNote(tag: SNTag): Promise<void> {
const activeNote = this.viewControllerManager.contentListController.activeControllerNote
const activeNote = this.itemListController.activeControllerNote
if (activeNote) {
await this.application.mutator.changeItem(tag, (mutator) => {

View File

@@ -2,11 +2,15 @@ import { destroyAllObjectProperties } from '@/Utils'
import { confirmDialog } from '@/Services/AlertService'
import { StringEmptyTrash, Strings, StringUtils } from '@/Constants/Strings'
import { MENU_MARGIN_FROM_APP_BORDER } from '@/Constants/Constants'
import { SNNote, NoteMutator, ContentType, SNTag, DeinitSource, TagMutator } from '@standardnotes/snjs'
import { SNNote, NoteMutator, ContentType, SNTag, TagMutator, InternalEventBus } from '@standardnotes/snjs'
import { makeObservable, observable, action, computed, runInAction } from 'mobx'
import { WebApplication } from '../Application/Application'
import { ViewControllerManager } from '../Services/ViewControllerManager/ViewControllerManager'
import { AbstractViewController } from './Abstract/AbstractViewController'
import { SelectedItemsController } from './SelectedItemsController'
import { ItemListController } from './ItemList/ItemListController'
import { NoteTagsController } from './NoteTagsController'
import { NavigationController } from './Navigation/NavigationController'
import { CrossControllerEvent } from './CrossControllerEvent'
export class NotesController extends AbstractViewController {
lastSelectedNote: SNNote | undefined
@@ -19,22 +23,27 @@ export class NotesController extends AbstractViewController {
contextMenuMaxHeight: number | 'auto' = 'auto'
showProtectedWarning = false
showRevisionHistoryModal = false
private itemListController!: ItemListController
override deinit(source: DeinitSource) {
super.deinit(source)
override deinit() {
super.deinit()
;(this.lastSelectedNote as unknown) = undefined
;(this.onActiveEditorChanged as unknown) = undefined
;(this.selectionController as unknown) = undefined
;(this.noteTagsController as unknown) = undefined
;(this.navigationController as unknown) = undefined
;(this.itemListController as unknown) = undefined
destroyAllObjectProperties(this)
}
constructor(
application: WebApplication,
public override viewControllerManager: ViewControllerManager,
private onActiveEditorChanged: () => Promise<void>,
appEventListeners: (() => void)[],
private selectionController: SelectedItemsController,
private noteTagsController: NoteTagsController,
private navigationController: NavigationController,
eventBus: InternalEventBus,
) {
super(application, viewControllerManager)
super(application, eventBus)
makeObservable(this, {
contextMenuOpen: observable,
@@ -55,17 +64,21 @@ export class NotesController extends AbstractViewController {
setShowRevisionHistoryModal: action,
unselectNotes: action,
})
}
appEventListeners.push(
application.streamItems<SNNote>(ContentType.Note, ({ changed, inserted, removed }) => {
public setServicestPostConstruction(itemListController: ItemListController) {
this.itemListController = itemListController
this.disposers.push(
this.application.streamItems<SNNote>(ContentType.Note, ({ changed, inserted, removed }) => {
runInAction(() => {
for (const removedNote of removed) {
this.viewControllerManager.selectionController.deselectItem(removedNote)
this.selectionController.deselectItem(removedNote)
}
for (const note of [...changed, ...inserted]) {
if (this.viewControllerManager.selectionController.isItemSelected(note)) {
this.viewControllerManager.selectionController.updateReferenceOfSelectedItem(note)
if (this.selectionController.isItemSelected(note)) {
this.selectionController.updateReferenceOfSelectedItem(note)
}
}
})
@@ -80,7 +93,7 @@ export class NotesController extends AbstractViewController {
for (const selectedId of selectedUuids) {
if (!activeNoteUuids.includes(selectedId)) {
this.viewControllerManager.selectionController.deselectItem({ uuid: selectedId })
this.selectionController.deselectItem({ uuid: selectedId })
}
}
}),
@@ -88,7 +101,7 @@ export class NotesController extends AbstractViewController {
}
public get selectedNotes(): SNNote[] {
return this.viewControllerManager.selectionController.getSelectedItems<SNNote>(ContentType.Note)
return this.selectionController.getSelectedItems<SNNote>(ContentType.Note)
}
get firstSelectedNote(): SNNote | undefined {
@@ -108,7 +121,7 @@ export class NotesController extends AbstractViewController {
}
async openNote(noteUuid: string): Promise<void> {
if (this.viewControllerManager.contentListController.activeControllerNote?.uuid === noteUuid) {
if (this.itemListController.activeControllerNote?.uuid === noteUuid) {
return
}
@@ -120,13 +133,13 @@ export class NotesController extends AbstractViewController {
await this.application.noteControllerGroup.createNoteController(noteUuid)
this.viewControllerManager.noteTagsController.reloadTagsForCurrentNote()
this.noteTagsController.reloadTagsForCurrentNote()
await this.onActiveEditorChanged()
await this.publishEventSync(CrossControllerEvent.ActiveEditorChanged)
}
async createNewNoteController(title?: string) {
const selectedTag = this.viewControllerManager.navigationController.selected
const selectedTag = this.navigationController.selected
const activeRegularTagUuid = selectedTag && selectedTag instanceof SNTag ? selectedTag.uuid : undefined
@@ -262,7 +275,7 @@ export class NotesController extends AbstractViewController {
if (permanently) {
for (const note of this.getSelectedNotesList()) {
await this.application.mutator.deleteItem(note)
this.viewControllerManager.selectionController.deselectItem(note)
this.selectionController.deselectItem(note)
}
} else {
await this.changeSelectedNotes((mutator) => {
@@ -294,7 +307,7 @@ export class NotesController extends AbstractViewController {
})
runInAction(() => {
this.viewControllerManager.selectionController.setSelectedItems({})
this.selectionController.setSelectedItems({})
this.contextMenuOpen = false
})
}
@@ -311,11 +324,11 @@ export class NotesController extends AbstractViewController {
}
unselectNotes(): void {
this.viewControllerManager.selectionController.setSelectedItems({})
this.selectionController.setSelectedItems({})
}
getSpellcheckStateForNote(note: SNNote) {
return note.spellcheck != undefined ? note.spellcheck : this.viewControllerManager.isGlobalSpellcheckEnabled()
return note.spellcheck != undefined ? note.spellcheck : this.application.isGlobalSpellcheckEnabled()
}
async toggleGlobalSpellcheckForNote(note: SNNote) {
@@ -358,7 +371,7 @@ export class NotesController extends AbstractViewController {
isTagInSelectedNotes(tag: SNTag): boolean {
const selectedNotes = this.getSelectedNotesList()
return selectedNotes.every((note) =>
this.viewControllerManager.getItemTags(note).find((noteTag) => noteTag.uuid === tag.uuid),
this.application.getItemTags(note).find((noteTag) => noteTag.uuid === tag.uuid),
)
}

View File

@@ -1,4 +1,5 @@
import { loadPurchaseFlowUrl } from '@/Components/PurchaseFlow/PurchaseFlowFunctions'
import { InternalEventBus } from '@standardnotes/snjs'
import { action, makeObservable, observable } from 'mobx'
import { WebApplication } from '../../Application/Application'
import { AbstractViewController } from '../Abstract/AbstractViewController'
@@ -8,8 +9,8 @@ export class PurchaseFlowController extends AbstractViewController {
isOpen = false
currentPane = PurchaseFlowPane.CreateAccount
constructor(application: WebApplication) {
super(application)
constructor(application: WebApplication, eventBus: InternalEventBus) {
super(application, eventBus)
makeObservable(this, {
isOpen: observable,

View File

@@ -1,4 +1,4 @@
import { ApplicationEvent } from '@standardnotes/snjs'
import { ApplicationEvent, InternalEventBus } from '@standardnotes/snjs'
import { makeObservable, observable, action, runInAction } from 'mobx'
import { WebApplication } from '../Application/Application'
import { AbstractViewController } from './Abstract/AbstractViewController'
@@ -8,8 +8,8 @@ export class SearchOptionsController extends AbstractViewController {
includeArchived = false
includeTrashed = false
constructor(application: WebApplication, appObservers: (() => void)[]) {
super(application)
constructor(application: WebApplication, eventBus: InternalEventBus) {
super(application, eventBus)
makeObservable(this, {
includeProtectedContents: observable,
@@ -22,7 +22,7 @@ export class SearchOptionsController extends AbstractViewController {
refreshIncludeProtectedContents: action,
})
appObservers.push(
this.disposers.push(
this.application.addEventObserver(async () => {
this.refreshIncludeProtectedContents()
}, ApplicationEvent.UnprotectedSessionBegan),

View File

@@ -1,22 +1,35 @@
import { ListableContentItem } from '@/Components/ContentListView/Types/ListableContentItem'
import { ChallengeReason, ContentType, KeyboardModifier, FileItem, SNNote, UuidString } from '@standardnotes/snjs'
import {
ChallengeReason,
ContentType,
KeyboardModifier,
FileItem,
SNNote,
UuidString,
InternalEventBus,
} from '@standardnotes/snjs'
import { action, computed, makeObservable, observable, runInAction } from 'mobx'
import { WebApplication } from '../Application/Application'
import { AbstractViewController } from './Abstract/AbstractViewController'
import { ViewControllerManager } from '../Services/ViewControllerManager/ViewControllerManager'
import { ItemListController } from './ItemList/ItemListController'
import { NotesController } from './NotesController'
type SelectedItems = Record<UuidString, ListableContentItem>
export class SelectedItemsController extends AbstractViewController {
lastSelectedItem: ListableContentItem | undefined
selectedItems: SelectedItems = {}
private itemListController!: ItemListController
private notesController!: NotesController
constructor(
application: WebApplication,
override viewControllerManager: ViewControllerManager,
appObservers: (() => void)[],
) {
super(application)
override deinit(): void {
super.deinit()
;(this.itemListController as unknown) = undefined
;(this.notesController as unknown) = undefined
}
constructor(application: WebApplication, eventBus: InternalEventBus) {
super(application, eventBus)
makeObservable(this, {
selectedItems: observable,
@@ -26,9 +39,14 @@ export class SelectedItemsController extends AbstractViewController {
selectItem: action,
setSelectedItems: action,
})
}
appObservers.push(
application.streamItems<SNNote | FileItem>(
public setServicestPostConstruction(itemListController: ItemListController, notesController: NotesController) {
this.itemListController = itemListController
this.notesController = notesController
this.disposers.push(
this.application.streamItems<SNNote | FileItem>(
[ContentType.Note, ContentType.File],
({ changed, inserted, removed }) => {
runInAction(() => {
@@ -82,7 +100,7 @@ export class SelectedItemsController extends AbstractViewController {
}
private selectItemsRange = async (selectedItem: ListableContentItem): Promise<void> => {
const items = this.viewControllerManager.contentListController.renderedItems
const items = this.itemListController.renderedItems
const lastSelectedItemIndex = items.findIndex((item) => item.uuid == this.lastSelectedItem?.uuid)
const selectedItemIndex = items.findIndex((item) => item.uuid == selectedItem.uuid)
@@ -171,7 +189,7 @@ export class SelectedItemsController extends AbstractViewController {
if (this.selectedItemsCount === 1) {
const item = Object.values(this.selectedItems)[0]
if (item.content_type === ContentType.Note) {
await this.viewControllerManager.notesController.openNote(item.uuid)
await this.notesController.openNote(item.uuid)
}
}

View File

@@ -3,7 +3,7 @@ import {
ApplicationEvent,
ClientDisplayableError,
convertTimestampToMilliseconds,
DeinitSource,
InternalEventBus,
} from '@standardnotes/snjs'
import { action, computed, makeObservable, observable } from 'mobx'
import { WebApplication } from '../../Application/Application'
@@ -15,16 +15,16 @@ export class SubscriptionController extends AbstractViewController {
userSubscription: Subscription | undefined = undefined
availableSubscriptions: AvailableSubscriptions | undefined = undefined
override deinit(source: DeinitSource) {
super.deinit(source)
override deinit() {
super.deinit()
;(this.userSubscription as unknown) = undefined
;(this.availableSubscriptions as unknown) = undefined
destroyAllObjectProperties(this)
}
constructor(application: WebApplication, appObservers: (() => void)[]) {
super(application)
constructor(application: WebApplication, eventBus: InternalEventBus) {
super(application, eventBus)
makeObservable(this, {
userSubscription: observable,
@@ -39,15 +39,21 @@ export class SubscriptionController extends AbstractViewController {
setAvailableSubscriptions: action,
})
appObservers.push(
this.disposers.push(
application.addEventObserver(async () => {
if (application.hasAccount()) {
this.getSubscriptionInfo().catch(console.error)
}
}, ApplicationEvent.Launched),
)
this.disposers.push(
application.addEventObserver(async () => {
this.getSubscriptionInfo().catch(console.error)
}, ApplicationEvent.SignedIn),
)
this.disposers.push(
application.addEventObserver(async () => {
this.getSubscriptionInfo().catch(console.error)
}, ApplicationEvent.UserRolesChanged),