feat: item linking (#1779)

This commit is contained in:
Aman Harwara
2022-10-11 23:54:00 +05:30
committed by GitHub
parent d22c164e5d
commit e3f28421ff
68 changed files with 2064 additions and 1277 deletions

View File

@@ -99,7 +99,7 @@ export class FilesController extends AbstractViewController {
reloadAttachedFiles = () => {
const note = this.notesController.firstSelectedNote
if (note) {
this.attachedFiles = this.application.items.getFilesForNote(note)
this.attachedFiles = this.application.items.getSortedFilesForItem(note)
}
}

View File

@@ -29,10 +29,10 @@ import { CrossControllerEvent } from '../CrossControllerEvent'
import { SearchOptionsController } from '../SearchOptionsController'
import { SelectedItemsController } from '../SelectedItemsController'
import { NotesController } from '../NotesController'
import { NoteTagsController } from '../NoteTagsController'
import { formatDateAndTimeForNote } from '@/Utils/DateUtils'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import dayjs from 'dayjs'
import { LinkingController } from '../LinkingController'
const MinNoteCellHeight = 51.0
const DefaultListNumNotes = 20
@@ -85,7 +85,6 @@ export class ItemListController extends AbstractViewController implements Intern
;(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)
@@ -97,7 +96,7 @@ export class ItemListController extends AbstractViewController implements Intern
private searchOptionsController: SearchOptionsController,
private selectionController: SelectedItemsController,
private notesController: NotesController,
private noteTagsController: NoteTagsController,
private linkingController: LinkingController,
eventBus: InternalEventBus,
) {
super(application, eventBus)
@@ -228,13 +227,12 @@ export class ItemListController extends AbstractViewController implements Intern
return this.application.itemControllerGroup.activeItemViewController
}
public get activeControllerNote(): SNNote | undefined {
const activeController = this.getActiveItemController()
return activeController instanceof NoteViewController ? activeController.item : undefined
public get activeControllerItem() {
return this.getActiveItemController()?.item
}
async openNote(uuid: string): Promise<void> {
if (this.activeControllerNote?.uuid === uuid) {
if (this.activeControllerItem?.uuid === uuid) {
return
}
@@ -246,7 +244,7 @@ export class ItemListController extends AbstractViewController implements Intern
await this.application.itemControllerGroup.createItemController(note)
this.noteTagsController.reloadTagsForCurrentNote()
this.linkingController.reloadAllLinks()
await this.publishEventSync(CrossControllerEvent.ActiveEditorChanged)
}
@@ -263,6 +261,8 @@ export class ItemListController extends AbstractViewController implements Intern
}
await this.application.itemControllerGroup.createItemController(file)
this.linkingController.reloadAllLinks()
}
setCompletedFullSync = (completed: boolean) => {
@@ -545,7 +545,7 @@ export class ItemListController extends AbstractViewController implements Intern
await this.createNewNoteController(title)
this.noteTagsController.reloadTagsForCurrentNote()
this.linkingController.reloadAllLinks()
}
createPlaceholderNote = () => {

View File

@@ -0,0 +1,339 @@
import { WebApplication } from '@/Application/Application'
import { PopoverFileItemActionType } from '@/Components/AttachedFilesPopover/PopoverFileItemAction'
import { AppPaneId } from '@/Components/ResponsivePane/AppPaneMetadata'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import {
ApplicationEvent,
ContentType,
DecryptedItemInterface,
FileItem,
IconType,
InternalEventBus,
ItemContent,
naturalSort,
PrefKey,
SNNote,
SNTag,
} from '@standardnotes/snjs'
import { action, computed, makeObservable, observable } from 'mobx'
import { AbstractViewController } from './Abstract/AbstractViewController'
import { FilesController } from './FilesController'
import { ItemListController } from './ItemList/ItemListController'
import { NavigationController } from './Navigation/NavigationController'
import { SelectedItemsController } from './SelectedItemsController'
import { SubscriptionController } from './Subscription/SubscriptionController'
export type LinkableItem = DecryptedItemInterface<ItemContent>
export class LinkingController extends AbstractViewController {
tags: SNTag[] = []
files: FileItem[] = []
notesLinkedToItem: SNNote[] = []
notesLinkingToItem: SNNote[] = []
shouldLinkToParentFolders: boolean
isLinkingPanelOpen = false
private itemListController!: ItemListController
private filesController!: FilesController
private subscriptionController!: SubscriptionController
constructor(
application: WebApplication,
private navigationController: NavigationController,
private selectionController: SelectedItemsController,
eventBus: InternalEventBus,
) {
super(application, eventBus)
makeObservable(this, {
tags: observable,
files: observable,
notesLinkedToItem: observable,
notesLinkingToItem: observable,
isLinkingPanelOpen: observable,
allLinkedItems: computed,
isEntitledToNoteLinking: computed,
setIsLinkingPanelOpen: action,
reloadLinkedFiles: action,
reloadLinkedTags: action,
reloadLinkedNotes: action,
reloadNotesLinkingToItem: action,
})
this.shouldLinkToParentFolders = application.getPreference(
PrefKey.NoteAddToParentFolders,
PrefDefaults[PrefKey.NoteAddToParentFolders],
)
this.disposers.push(
this.application.addSingleEventObserver(ApplicationEvent.PreferencesChanged, async () => {
this.shouldLinkToParentFolders = this.application.getPreference(
PrefKey.NoteAddToParentFolders,
PrefDefaults[PrefKey.NoteAddToParentFolders],
)
}),
)
}
public setServicesPostConstruction(
itemListController: ItemListController,
filesController: FilesController,
subscriptionController: SubscriptionController,
) {
this.itemListController = itemListController
this.filesController = filesController
this.subscriptionController = subscriptionController
this.disposers.push(
this.application.streamItems(ContentType.File, () => {
this.reloadLinkedFiles()
}),
this.application.streamItems(ContentType.Tag, () => {
this.reloadLinkedTags()
}),
this.application.streamItems(ContentType.Note, () => {
this.reloadLinkedNotes()
this.reloadNotesLinkingToItem()
}),
)
}
get isEntitledToNoteLinking() {
return !!this.subscriptionController.userSubscription
}
setIsLinkingPanelOpen = (open: boolean) => {
this.isLinkingPanelOpen = open
}
get allLinkedItems() {
return [...this.tags, ...this.files, ...this.notesLinkedToItem]
}
get activeItem() {
return this.itemListController.activeControllerItem
}
reloadAllLinks() {
this.reloadLinkedFiles()
this.reloadLinkedTags()
this.reloadLinkedNotes()
this.reloadNotesLinkingToItem()
}
reloadLinkedFiles() {
if (this.activeItem) {
const files = this.application.items.getSortedFilesForItem(this.activeItem)
this.files = files
}
}
reloadLinkedTags() {
if (this.activeItem) {
const tags = this.application.items.getSortedTagsForItem(this.activeItem)
this.tags = tags
}
}
reloadLinkedNotes() {
if (this.activeItem) {
const notes = this.application.items.getSortedLinkedNotesForItem(this.activeItem)
this.notesLinkedToItem = notes
}
}
reloadNotesLinkingToItem() {
if (this.activeItem) {
const notes = this.application.items.getSortedNotesLinkingToItem(this.activeItem)
this.notesLinkingToItem = notes
}
}
getTitleForLinkedTag = (item: LinkableItem) => {
const isTag = item instanceof SNTag
if (!isTag) {
return
}
const titlePrefix = this.application.items.getTagPrefixTitle(item)
const longTitle = this.application.items.getTagLongTitle(item)
return {
titlePrefix,
longTitle,
}
}
getLinkedItemIcon = (item: LinkableItem): [IconType, string] => {
if (item instanceof SNNote) {
const editorForNote = this.application.componentManager.editorForNote(item)
const [icon, tint] = this.application.iconsController.getIconAndTintForNoteType(
editorForNote?.package_info.note_type,
)
const className = `text-accessory-tint-${tint}`
return [icon, className]
} else if (item instanceof FileItem) {
const icon = this.application.iconsController.getIconForFileType(item.mimeType)
return [icon, 'text-info']
}
return ['hashtag', 'text-info']
}
activateItem = async (item: LinkableItem): Promise<AppPaneId | undefined> => {
this.setIsLinkingPanelOpen(false)
if (item instanceof SNTag) {
await this.navigationController.setSelectedTag(item)
return AppPaneId.Items
} else if (item instanceof SNNote) {
await this.navigationController.selectHomeNavigationView()
const { didSelect } = await this.selectionController.selectItem(item.uuid, true)
if (didSelect) {
return AppPaneId.Editor
}
} else if (item instanceof FileItem) {
await this.filesController.handleFileAction({
type: PopoverFileItemActionType.PreviewFile,
payload: {
file: item,
otherFiles: [],
},
})
}
return undefined
}
unlinkItemFromSelectedItem = async (itemToUnlink: LinkableItem) => {
const selectedItem = this.selectionController.firstSelectedItem
if (!selectedItem) {
return
}
const selectedItemReferencesItemToUnlink =
this.application.items.relationshipTypeForItems(selectedItem, itemToUnlink) === 'direct'
if (selectedItemReferencesItemToUnlink) {
await this.application.items.unlinkItem(selectedItem, itemToUnlink)
} else {
await this.application.items.unlinkItem(itemToUnlink, selectedItem)
}
void this.application.sync.sync()
this.reloadAllLinks()
}
linkItemToSelectedItem = async (itemToLink: LinkableItem) => {
const selectedItem = this.selectionController.firstSelectedItem
if (itemToLink instanceof SNTag) {
await this.addTagToActiveItem(itemToLink)
}
if (selectedItem instanceof SNNote) {
if (itemToLink instanceof FileItem) {
await this.application.items.associateFileWithNote(itemToLink, selectedItem)
} else if (itemToLink instanceof SNNote && this.isEntitledToNoteLinking) {
await this.application.items.linkNoteToNote(selectedItem, itemToLink)
}
} else if (selectedItem instanceof FileItem) {
if (itemToLink instanceof SNNote) {
await this.application.items.associateFileWithNote(selectedItem, itemToLink)
} else if (itemToLink instanceof FileItem) {
await this.application.items.linkFileToFile(itemToLink, selectedItem)
}
}
void this.application.sync.sync()
this.reloadAllLinks()
}
createAndAddNewTag = async (title: string) => {
const newTag = await this.application.mutator.findOrCreateTag(title)
await this.addTagToActiveItem(newTag)
}
addTagToActiveItem = async (tag: SNTag) => {
const activeItem = this.itemListController.activeControllerItem
if (!activeItem) {
return
}
if (activeItem instanceof SNNote) {
await this.application.items.addTagToNote(activeItem, tag, this.shouldLinkToParentFolders)
} else if (activeItem instanceof FileItem) {
await this.application.items.addTagToFile(activeItem, tag, this.shouldLinkToParentFolders)
}
this.reloadLinkedTags()
this.application.sync.sync().catch(console.error)
}
getSearchResults = (searchQuery: string) => {
if (!searchQuery.length) {
return {
linkedResults: [],
unlinkedResults: [],
shouldShowCreateTag: false,
}
}
const searchResults = naturalSort(
this.application.items.getItems([ContentType.Note, ContentType.File, ContentType.Tag]).filter((item) => {
const title = item instanceof SNTag ? this.application.items.getTagLongTitle(item) : item.title
const matchesQuery = title?.toLowerCase().includes(searchQuery.toLowerCase())
const isNotActiveItem = this.activeItem?.uuid !== item.uuid
const isArchivedOrTrashed = item.archived || item.trashed
return matchesQuery && isNotActiveItem && !isArchivedOrTrashed
}),
'title',
)
const isAlreadyLinked = (item: LinkableItem) => {
if (!this.activeItem) {
return false
}
const isItemReferencedByActiveItem = this.application.items
.itemsReferencingItem(item)
.some((linkedItem) => linkedItem.uuid === this.activeItem?.uuid)
const isActiveItemReferencedByItem = this.application.items
.itemsReferencingItem(this.activeItem)
.some((linkedItem) => linkedItem.uuid === item.uuid)
const isAlreadyLinkedToItem =
isItemReferencedByActiveItem || (item.content_type !== ContentType.Note && isActiveItemReferencedByItem)
return isAlreadyLinkedToItem
}
const prioritizeTagResult = (
itemA: DecryptedItemInterface<ItemContent>,
itemB: DecryptedItemInterface<ItemContent>,
) => {
if (itemA.content_type === ContentType.Tag && itemB.content_type !== ContentType.Tag) {
return -1
}
if (itemB.content_type === ContentType.Tag && itemA.content_type !== ContentType.Tag) {
return 1
}
return 0
}
const unlinkedResults = searchResults
.slice(0, 20)
.filter((item) => !isAlreadyLinked(item))
.sort(prioritizeTagResult)
const linkedResults = searchResults.filter(isAlreadyLinked).slice(0, 20)
const isResultExistingTag = (result: LinkableItem) =>
result.content_type === ContentType.Tag && result.title === searchQuery
const shouldShowCreateTag = !linkedResults.find(isResultExistingTag) && !unlinkedResults.find(isResultExistingTag)
return {
unlinkedResults,
linkedResults,
shouldShowCreateTag,
}
}
}

View File

@@ -394,10 +394,18 @@ export class NavigationController extends AbstractViewController {
await this.setSelectedTag(this.homeNavigationView)
}
public async selectFilesView() {
await this.setSelectedTag(this.filesNavigationView)
}
get homeNavigationView(): SmartView {
return this.smartViews[0]
}
get filesNavigationView(): SmartView {
return this.smartViews.find((view) => view.uuid === SystemViewId.Files) as SmartView
}
private setSelectedTagInstance(tag: AnyTag | undefined): void {
runInAction(() => (this.selected_ = tag))
}

View File

@@ -1,244 +0,0 @@
import { ElementIds } from '@/Constants/ElementIDs'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import { destroyAllObjectProperties } from '@/Utils'
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 { ItemListController } from './ItemList/ItemListController'
export class NoteTagsController extends AbstractViewController {
autocompleteInputFocused = false
autocompleteSearchQuery = ''
autocompleteTagHintFocused = false
autocompleteTagResults: SNTag[] = []
focusedTagResultUuid: UuidString | undefined = undefined
focusedTagUuid: UuidString | undefined = undefined
tags: SNTag[] = []
tagsContainerMaxWidth: number | 'auto' = 0
addNoteToParentFolders: boolean
private itemListController!: ItemListController
override deinit() {
super.deinit()
;(this.tags as unknown) = undefined
;(this.autocompleteTagResults as unknown) = undefined
;(this.itemListController as unknown) = undefined
destroyAllObjectProperties(this)
}
constructor(application: WebApplication, eventBus: InternalEventBus) {
super(application, eventBus)
makeObservable(this, {
autocompleteInputFocused: observable,
autocompleteSearchQuery: observable,
autocompleteTagHintFocused: observable,
autocompleteTagResults: observable,
focusedTagUuid: observable,
focusedTagResultUuid: observable,
tags: observable,
tagsContainerMaxWidth: observable,
autocompleteTagHintVisible: computed,
setAutocompleteInputFocused: action,
setAutocompleteSearchQuery: action,
setAutocompleteTagHintFocused: action,
setAutocompleteTagResults: action,
setFocusedTagResultUuid: action,
setFocusedTagUuid: action,
setTags: action,
setTagsContainerMaxWidth: action,
})
this.addNoteToParentFolders = application.getPreference(
PrefKey.NoteAddToParentFolders,
PrefDefaults[PrefKey.NoteAddToParentFolders],
)
}
public setServicesPostConstruction(itemListController: ItemListController) {
this.itemListController = itemListController
this.disposers.push(
this.application.streamItems(ContentType.Tag, () => {
this.reloadTagsForCurrentNote()
}),
this.application.addSingleEventObserver(ApplicationEvent.PreferencesChanged, async () => {
this.addNoteToParentFolders = this.application.getPreference(
PrefKey.NoteAddToParentFolders,
PrefDefaults[PrefKey.NoteAddToParentFolders],
)
}),
)
}
get autocompleteTagHintVisible(): boolean {
return (
this.autocompleteSearchQuery !== '' &&
!this.autocompleteTagResults.some((tagResult) => tagResult.title === this.autocompleteSearchQuery)
)
}
setAutocompleteInputFocused(focused: boolean): void {
this.autocompleteInputFocused = focused
}
setAutocompleteSearchQuery(query: string): void {
this.autocompleteSearchQuery = query
}
setAutocompleteTagHintFocused(focused: boolean): void {
this.autocompleteTagHintFocused = focused
}
setAutocompleteTagResults(results: SNTag[]): void {
this.autocompleteTagResults = results
}
setFocusedTagUuid(tagUuid: UuidString | undefined): void {
this.focusedTagUuid = tagUuid
}
setFocusedTagResultUuid(tagUuid: UuidString | undefined): void {
this.focusedTagResultUuid = tagUuid
}
setTags(tags: SNTag[]): void {
this.tags = tags
}
setTagsContainerMaxWidth(width: number): void {
this.tagsContainerMaxWidth = width
}
clearAutocompleteSearch(): void {
this.setAutocompleteSearchQuery('')
this.setAutocompleteTagResults([])
}
async createAndAddNewTag(): Promise<void> {
const newTag = await this.application.mutator.findOrCreateTag(this.autocompleteSearchQuery)
await this.addTagToActiveNote(newTag)
this.clearAutocompleteSearch()
}
focusNextTag(tag: SNTag): void {
const nextTagIndex = this.getTagIndex(tag, this.tags) + 1
if (nextTagIndex > -1 && this.tags.length > nextTagIndex) {
const nextTag = this.tags[nextTagIndex]
this.setFocusedTagUuid(nextTag.uuid)
}
}
focusNextTagResult(tagResult: SNTag): void {
const nextTagResultIndex = this.getTagIndex(tagResult, this.autocompleteTagResults) + 1
if (nextTagResultIndex > -1 && this.autocompleteTagResults.length > nextTagResultIndex) {
const nextTagResult = this.autocompleteTagResults[nextTagResultIndex]
this.setFocusedTagResultUuid(nextTagResult.uuid)
}
}
focusPreviousTag(tag: SNTag): void {
const previousTagIndex = this.getTagIndex(tag, this.tags) - 1
if (previousTagIndex > -1 && this.tags.length > previousTagIndex) {
const previousTag = this.tags[previousTagIndex]
this.setFocusedTagUuid(previousTag.uuid)
}
}
focusPreviousTagResult(tagResult: SNTag): void {
const previousTagResultIndex = this.getTagIndex(tagResult, this.autocompleteTagResults) - 1
if (previousTagResultIndex > -1 && this.autocompleteTagResults.length > previousTagResultIndex) {
const previousTagResult = this.autocompleteTagResults[previousTagResultIndex]
this.setFocusedTagResultUuid(previousTagResult.uuid)
}
}
searchActiveNoteAutocompleteTags(): void {
const newResults = this.application.items.searchTags(
this.autocompleteSearchQuery,
this.itemListController.activeControllerNote,
)
this.setAutocompleteTagResults(newResults)
}
getTagIndex(tag: SNTag, tagsArr: SNTag[]): number {
return tagsArr.findIndex((t) => t.uuid === tag.uuid)
}
reloadTagsForCurrentNote(): void {
const activeNote = this.itemListController.activeControllerNote
if (activeNote) {
const tags = this.application.items.getSortedTagsForNote(activeNote)
this.setTags(tags)
}
}
reloadTagsContainerMaxWidth(): void {
const editorWidth = document.getElementById(ElementIds.EditorColumn)?.clientWidth
if (editorWidth) {
this.setTagsContainerMaxWidth(editorWidth)
}
}
async addTagToActiveNote(tag: SNTag): Promise<void> {
const activeNote = this.itemListController.activeControllerNote
if (activeNote) {
await this.application.items.addTagToNote(activeNote, tag, this.addNoteToParentFolders)
this.application.sync.sync().catch(console.error)
this.reloadTagsForCurrentNote()
}
}
async removeTagFromActiveNote(tag: SNTag): Promise<void> {
const activeNote = this.itemListController.activeControllerNote
if (activeNote) {
await this.application.mutator.changeItem(tag, (mutator) => {
mutator.removeItemAsRelationship(activeNote)
})
this.application.sync.sync().catch(console.error)
this.reloadTagsForCurrentNote()
}
}
getSortedTagsForNote(note: SNNote): SNTag[] {
const tags = this.application.items.getSortedTagsForNote(note)
const sortFunction = (tagA: SNTag, tagB: SNTag): number => {
const a = this.getLongTitle(tagA)
const b = this.getLongTitle(tagB)
if (a < b) {
return -1
}
if (b > a) {
return 1
}
return 0
}
return tags.sort(sortFunction)
}
getPrefixTitle(tag: SNTag): string | undefined {
return this.application.items.getTagPrefixTitle(tag)
}
getLongTitle(tag: SNTag): string {
return this.application.items.getTagLongTitle(tag)
}
}

View File

@@ -8,7 +8,6 @@ import { WebApplication } from '../Application/Application'
import { AbstractViewController } from './Abstract/AbstractViewController'
import { SelectedItemsController } from './SelectedItemsController'
import { ItemListController } from './ItemList/ItemListController'
import { NoteTagsController } from './NoteTagsController'
import { NavigationController } from './Navigation/NavigationController'
export class NotesController extends AbstractViewController {
@@ -27,7 +26,6 @@ export class NotesController extends AbstractViewController {
super.deinit()
;(this.lastSelectedNote as unknown) = undefined
;(this.selectionController as unknown) = undefined
;(this.noteTagsController as unknown) = undefined
;(this.navigationController as unknown) = undefined
;(this.itemListController as unknown) = undefined
@@ -37,7 +35,6 @@ export class NotesController extends AbstractViewController {
constructor(
application: WebApplication,
private selectionController: SelectedItemsController,
private noteTagsController: NoteTagsController,
private navigationController: NavigationController,
eventBus: InternalEventBus,
) {

View File

@@ -34,6 +34,7 @@ export class SelectedItemsController extends AbstractViewController {
selectedItemsCount: computed,
selectedFiles: computed,
selectedFilesCount: computed,
firstSelectedItem: computed,
selectItem: action,
setSelectedItems: action,
@@ -79,6 +80,10 @@ export class SelectedItemsController extends AbstractViewController {
return this.selectedFiles.length
}
get firstSelectedItem() {
return this.getSelectedItems()[0]
}
getSelectedItems = <T extends ListableContentItem = ListableContentItem>(contentType?: ContentType): T[] => {
return Object.values(this.selectedItems).filter((item) => {
return !contentType ? true : item.content_type === contentType

View File

@@ -17,7 +17,6 @@ import { FeaturesController } from './FeaturesController'
import { FilesController } from './FilesController'
import { NotesController } from './NotesController'
import { ItemListController } from './ItemList/ItemListController'
import { NoteTagsController } from './NoteTagsController'
import { NoAccountWarningController } from './NoAccountWarningController'
import { PreferencesController } from './PreferencesController'
import { PurchaseFlowController } from './PurchaseFlow/PurchaseFlowController'
@@ -31,6 +30,7 @@ import { SelectedItemsController } from './SelectedItemsController'
import { HistoryModalController } from './NoteHistory/HistoryModalController'
import { PreferenceId } from '@/Components/Preferences/PreferencesMenu'
import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane'
import { LinkingController } from './LinkingController'
export class ViewControllerManager {
readonly enableUnfinishedFeatures: boolean = window?.enabledUnfinishedFeatures
@@ -47,7 +47,6 @@ export class ViewControllerManager {
readonly noAccountWarningController: NoAccountWarningController
readonly notesController: NotesController
readonly itemListController: ItemListController
readonly noteTagsController: NoteTagsController
readonly preferencesController = new PreferencesController()
readonly purchaseFlowController: PurchaseFlowController
readonly quickSettingsMenuController = new QuickSettingsController()
@@ -57,6 +56,7 @@ export class ViewControllerManager {
readonly navigationController: NavigationController
readonly selectionController: SelectedItemsController
readonly historyModalController: HistoryModalController
readonly linkingController: LinkingController
public isSessionsModalVisible = false
@@ -74,8 +74,6 @@ export class ViewControllerManager {
this.selectionController = new SelectedItemsController(application, this.eventBus)
this.noteTagsController = new NoteTagsController(application, this.eventBus)
this.featuresController = new FeaturesController(application, this.eventBus)
this.navigationController = new NavigationController(application, this.featuresController, this.eventBus)
@@ -83,25 +81,30 @@ export class ViewControllerManager {
this.notesController = new NotesController(
application,
this.selectionController,
this.noteTagsController,
this.navigationController,
this.eventBus,
)
this.searchOptionsController = new SearchOptionsController(application, this.eventBus)
this.linkingController = new LinkingController(
application,
this.navigationController,
this.selectionController,
this.eventBus,
)
this.itemListController = new ItemListController(
application,
this.navigationController,
this.searchOptionsController,
this.selectionController,
this.notesController,
this.noteTagsController,
this.linkingController,
this.eventBus,
)
this.notesController.setServicesPostConstruction(this.itemListController)
this.noteTagsController.setServicesPostConstruction(this.itemListController)
this.selectionController.setServicesPostConstruction(this.itemListController)
this.noAccountWarningController = new NoAccountWarningController(application, this.eventBus)
@@ -119,6 +122,12 @@ export class ViewControllerManager {
this.eventBus,
)
this.linkingController.setServicesPostConstruction(
this.itemListController,
this.filesController,
this.subscriptionController,
)
this.historyModalController = new HistoryModalController(this.application, this.eventBus)
this.addAppEventObserver()
@@ -180,8 +189,8 @@ export class ViewControllerManager {
this.itemListController.deinit()
;(this.itemListController as unknown) = undefined
this.noteTagsController.deinit()
;(this.noteTagsController as unknown) = undefined
this.linkingController.deinit()
;(this.linkingController as unknown) = undefined
this.purchaseFlowController.deinit()
;(this.purchaseFlowController as unknown) = undefined