internal: incomplete vault systems behind feature flag (#2340)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ShouldPersistNoteStateKey } from '@/Components/Preferences/Panes/General/Persistence'
|
||||
import { ApplicationEvent, ContentType, InternalEventBus } from '@standardnotes/snjs'
|
||||
import { ApplicationEvent, ContentType, InternalEventBusInterface } from '@standardnotes/snjs'
|
||||
import { PersistedStateValue, StorageKey } from '@standardnotes/ui-services'
|
||||
import { CrossControllerEvent } from '../CrossControllerEvent'
|
||||
|
||||
@@ -8,7 +8,7 @@ export class PersistenceService {
|
||||
private unsubAppEventObserver: () => void
|
||||
private didHydrateOnce = false
|
||||
|
||||
constructor(private application: WebApplication, private eventBus: InternalEventBus) {
|
||||
constructor(private application: WebApplication, private eventBus: InternalEventBusInterface) {
|
||||
this.unsubAppEventObserver = this.application.addEventObserver(async (eventName) => {
|
||||
if (!this.application) {
|
||||
return
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { destroyAllObjectProperties, isDev } from '@/Utils'
|
||||
import { action, computed, makeObservable, observable, runInAction } from 'mobx'
|
||||
import { ApplicationEvent, ContentType, InternalEventBus, SNNote, SNTag } from '@standardnotes/snjs'
|
||||
import { ApplicationEvent, ContentType, InternalEventBusInterface, SNNote, SNTag } from '@standardnotes/snjs'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane'
|
||||
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||
@@ -28,7 +28,7 @@ export class AccountMenuController extends AbstractViewController {
|
||||
destroyAllObjectProperties(this)
|
||||
}
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
constructor(application: WebApplication, eventBus: InternalEventBusInterface) {
|
||||
super(application, eventBus)
|
||||
|
||||
makeObservable(this, {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
ApplicationEvent,
|
||||
FeatureIdentifier,
|
||||
FeatureStatus,
|
||||
InternalEventBus,
|
||||
InternalEventBusInterface,
|
||||
InternalEventInterface,
|
||||
} from '@standardnotes/snjs'
|
||||
import { action, makeObservable, observable, runInAction, when } from 'mobx'
|
||||
@@ -33,7 +33,7 @@ export class FeaturesController extends AbstractViewController {
|
||||
destroyAllObjectProperties(this)
|
||||
}
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
constructor(application: WebApplication, eventBus: InternalEventBusInterface) {
|
||||
super(application, eventBus)
|
||||
|
||||
this.hasFolders = this.isEntitledToFolders()
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
ClientDisplayableError,
|
||||
ContentType,
|
||||
FileItem,
|
||||
InternalEventBus,
|
||||
InternalEventBusInterface,
|
||||
isFile,
|
||||
Platform,
|
||||
} from '@standardnotes/snjs'
|
||||
@@ -68,7 +68,7 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
||||
application: WebApplication,
|
||||
private notesController: NotesController,
|
||||
private filePreviewModalController: FilePreviewModalController,
|
||||
eventBus: InternalEventBus,
|
||||
eventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(application, eventBus)
|
||||
|
||||
@@ -156,7 +156,8 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
||||
return
|
||||
}
|
||||
|
||||
await this.application.items.associateFileWithNote(file, note)
|
||||
await this.application.mutator.associateFileWithNote(file, note)
|
||||
void this.application.sync.sync()
|
||||
}
|
||||
|
||||
detachFileFromNote = async (file: FileItem) => {
|
||||
@@ -168,16 +169,18 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
||||
})
|
||||
return
|
||||
}
|
||||
await this.application.items.disassociateFileWithNote(file, note)
|
||||
await this.application.mutator.disassociateFileWithNote(file, note)
|
||||
void this.application.sync.sync()
|
||||
}
|
||||
|
||||
toggleFileProtection = async (file: FileItem) => {
|
||||
let result: FileItem | undefined
|
||||
if (file.protected) {
|
||||
result = await this.application.mutator.unprotectFile(file)
|
||||
result = await this.application.protections.unprotectFile(file)
|
||||
} else {
|
||||
result = await this.application.mutator.protectFile(file)
|
||||
result = await this.application.protections.protectFile(file)
|
||||
}
|
||||
void this.application.sync.sync()
|
||||
const isProtected = result ? result.protected : file.protected
|
||||
return isProtected
|
||||
}
|
||||
@@ -189,7 +192,8 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
||||
}
|
||||
|
||||
renameFile = async (file: FileItem, fileName: string) => {
|
||||
await this.application.items.renameFile(file, fileName)
|
||||
await this.application.mutator.renameFile(file, fileName)
|
||||
void this.application.sync.sync()
|
||||
}
|
||||
|
||||
handleFileAction = async (
|
||||
@@ -373,7 +377,10 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
||||
return
|
||||
}
|
||||
|
||||
const operation = await this.application.files.beginNewFileUpload(fileToUpload.size)
|
||||
const operation = await this.application.files.beginNewFileUpload(
|
||||
fileToUpload.size,
|
||||
this.application.vaultDisplayService.exclusivelyShownVault,
|
||||
)
|
||||
|
||||
if (operation instanceof ClientDisplayableError) {
|
||||
addToast({
|
||||
@@ -485,12 +492,12 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
|
||||
|
||||
setProtectionForFiles = async (protect: boolean, files: FileItem[]) => {
|
||||
if (protect) {
|
||||
const protectedItems = await this.application.mutator.protectItems(files)
|
||||
const protectedItems = await this.application.protections.protectItems(files)
|
||||
if (protectedItems) {
|
||||
this.setShowProtectedOverlay(true)
|
||||
}
|
||||
} else {
|
||||
const unprotectedItems = await this.application.mutator.unprotectItems(files, ChallengeReason.UnprotectFile)
|
||||
const unprotectedItems = await this.application.protections.unprotectItems(files, ChallengeReason.UnprotectFile)
|
||||
if (unprotectedItems) {
|
||||
this.setShowProtectedOverlay(false)
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ export class ImportModalController {
|
||||
}
|
||||
}
|
||||
const currentDate = new Date()
|
||||
const importTagItem = this.application.mutator.createTemplateItem<TagContent, SNTag>(ContentType.Tag, {
|
||||
const importTagItem = this.application.items.createTemplateItem<TagContent, SNTag>(ContentType.Tag, {
|
||||
title: `Imported on ${currentDate.toLocaleString()}`,
|
||||
expanded: false,
|
||||
iconString: '',
|
||||
|
||||
@@ -2,7 +2,6 @@ import { SNTag } from '@standardnotes/snjs'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { InternalEventBus } from '@standardnotes/services'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { LinkingController } from '../LinkingController'
|
||||
import { NavigationController } from '../Navigation/NavigationController'
|
||||
import { NotesController } from '../NotesController/NotesController'
|
||||
import { SearchOptionsController } from '../SearchOptionsController'
|
||||
@@ -28,7 +27,6 @@ describe('item list controller', () => {
|
||||
|
||||
const searchOptionsController = {} as jest.Mocked<SearchOptionsController>
|
||||
const notesController = {} as jest.Mocked<NotesController>
|
||||
const linkingController = {} as jest.Mocked<LinkingController>
|
||||
const eventBus = new InternalEventBus()
|
||||
|
||||
controller = new ItemListController(
|
||||
@@ -37,7 +35,6 @@ describe('item list controller', () => {
|
||||
searchOptionsController,
|
||||
selectionController,
|
||||
notesController,
|
||||
linkingController,
|
||||
eventBus,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -10,8 +10,6 @@ import {
|
||||
SNNote,
|
||||
SNTag,
|
||||
SystemViewId,
|
||||
DisplayOptions,
|
||||
InternalEventBus,
|
||||
InternalEventHandlerInterface,
|
||||
InternalEventInterface,
|
||||
FileItem,
|
||||
@@ -22,6 +20,8 @@ import {
|
||||
isFile,
|
||||
isSmartView,
|
||||
isSystemView,
|
||||
NotesAndFilesDisplayControllerOptions,
|
||||
InternalEventBusInterface,
|
||||
} from '@standardnotes/snjs'
|
||||
import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'
|
||||
import { WebApplication } from '../../Application/WebApplication'
|
||||
@@ -33,16 +33,18 @@ import { SelectedItemsController } from '../SelectedItemsController'
|
||||
import { NotesController } from '../NotesController/NotesController'
|
||||
import { formatDateAndTimeForNote } from '@/Utils/DateUtils'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
|
||||
import dayjs from 'dayjs'
|
||||
import dayjsAdvancedFormat from 'dayjs/plugin/advancedFormat'
|
||||
dayjs.extend(dayjsAdvancedFormat)
|
||||
import { LinkingController } from '../LinkingController'
|
||||
|
||||
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
import { NoteViewController } from '@/Components/NoteView/Controller/NoteViewController'
|
||||
import { FileViewController } from '@/Components/NoteView/Controller/FileViewController'
|
||||
import { TemplateNoteViewAutofocusBehavior } from '@/Components/NoteView/Controller/TemplateNoteViewControllerOptions'
|
||||
import { ItemsReloadSource } from './ItemsReloadSource'
|
||||
import { VaultDisplayServiceEvent } from '@standardnotes/ui-services'
|
||||
|
||||
const MinNoteCellHeight = 51.0
|
||||
const DefaultListNumNotes = 20
|
||||
@@ -59,7 +61,7 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
renderedItems: ListableContentItem[] = []
|
||||
searchSubmitted = false
|
||||
showDisplayOptionsMenu = false
|
||||
displayOptions: DisplayOptions = {
|
||||
displayOptions: NotesAndFilesDisplayControllerOptions = {
|
||||
sortBy: CollectionSort.CreatedAt,
|
||||
sortDirection: 'dsc',
|
||||
includePinned: true,
|
||||
@@ -96,13 +98,13 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
private searchOptionsController: SearchOptionsController,
|
||||
private selectionController: SelectedItemsController,
|
||||
private notesController: NotesController,
|
||||
private linkingController: LinkingController,
|
||||
eventBus: InternalEventBus,
|
||||
eventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(application, eventBus)
|
||||
|
||||
eventBus.addEventHandler(this, CrossControllerEvent.TagChanged)
|
||||
eventBus.addEventHandler(this, CrossControllerEvent.ActiveEditorChanged)
|
||||
eventBus.addEventHandler(this, VaultDisplayServiceEvent.VaultDisplayOptionsChanged)
|
||||
|
||||
this.resetPagination()
|
||||
|
||||
@@ -222,6 +224,8 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
await this.handleTagChange(payload.userTriggered)
|
||||
} else if (event.type === CrossControllerEvent.ActiveEditorChanged) {
|
||||
this.handleEditorChange().catch(console.error)
|
||||
} else if (event.type === VaultDisplayServiceEvent.VaultDisplayOptionsChanged) {
|
||||
void this.reloadItems(ItemsReloadSource.DisplayOptionsChange)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,7 +493,7 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
includeTrashed = this.displayOptions.includeTrashed ?? false
|
||||
}
|
||||
|
||||
const criteria: DisplayOptions = {
|
||||
const criteria: NotesAndFilesDisplayControllerOptions = {
|
||||
sortBy: this.displayOptions.sortBy,
|
||||
sortDirection: this.displayOptions.sortDirection,
|
||||
tags: tag instanceof SNTag ? [tag] : [],
|
||||
@@ -512,8 +516,9 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
}: {
|
||||
userTriggered: boolean
|
||||
}): Promise<{ didReloadItems: boolean }> => {
|
||||
const newDisplayOptions = {} as DisplayOptions
|
||||
const newDisplayOptions = {} as NotesAndFilesDisplayControllerOptions
|
||||
const newWebDisplayOptions = {} as WebDisplayOptions
|
||||
|
||||
const selectedTag = this.navigationController.selected
|
||||
const isSystemTag = selectedTag && isSmartView(selectedTag) && isSystemView(selectedTag)
|
||||
const selectedTagPreferences = isSystemTag
|
||||
@@ -631,6 +636,7 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
tag: activeRegularTagUuid,
|
||||
createdAt,
|
||||
autofocusBehavior,
|
||||
vault: this.application.vaultDisplayService.exclusivelyShownVault,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -783,7 +789,7 @@ export class ItemListController extends AbstractViewController implements Intern
|
||||
const activeNote = this.application.itemControllerGroup.activeItemViewController?.item
|
||||
|
||||
if (activeNote && activeNote.conflictOf) {
|
||||
this.application.mutator
|
||||
this.application
|
||||
.changeAndSaveItem(activeNote, (mutator) => {
|
||||
mutator.conflictOf = undefined
|
||||
})
|
||||
|
||||
@@ -9,7 +9,11 @@ import {
|
||||
FileToNoteReference,
|
||||
InternalEventBus,
|
||||
SNNote,
|
||||
ItemsClientInterface,
|
||||
ItemManagerInterface,
|
||||
VaultListingInterface,
|
||||
ItemInterface,
|
||||
InternalFeatureService,
|
||||
InternalFeature,
|
||||
} from '@standardnotes/snjs'
|
||||
import { FilesController } from './FilesController'
|
||||
import { ItemListController } from './ItemList/ItemListController'
|
||||
@@ -53,12 +57,20 @@ describe('LinkingController', () => {
|
||||
let subscriptionController: SubscriptionController
|
||||
|
||||
beforeEach(() => {
|
||||
application = {} as jest.Mocked<WebApplication>
|
||||
application = {
|
||||
vaults: {} as jest.Mocked<WebApplication['vaults']>,
|
||||
alerts: {} as jest.Mocked<WebApplication['alerts']>,
|
||||
sync: {} as jest.Mocked<WebApplication['sync']>,
|
||||
mutator: {} as jest.Mocked<WebApplication['mutator']>,
|
||||
} as unknown as jest.Mocked<WebApplication>
|
||||
|
||||
application.getPreference = jest.fn()
|
||||
application.addSingleEventObserver = jest.fn()
|
||||
application.streamItems = jest.fn()
|
||||
application.itemControllerGroup = {} as jest.Mocked<WebApplication['itemControllerGroup']>
|
||||
application.sync.sync = jest.fn()
|
||||
|
||||
Object.defineProperty(application, 'items', { value: {} as jest.Mocked<ItemsClientInterface> })
|
||||
Object.defineProperty(application, 'items', { value: {} as jest.Mocked<ItemManagerInterface> })
|
||||
|
||||
navigationController = {} as jest.Mocked<NavigationController>
|
||||
|
||||
@@ -181,38 +193,73 @@ describe('LinkingController', () => {
|
||||
})
|
||||
|
||||
it('should be true if active item & result are different content type & active item references result', () => {
|
||||
const activeFile = createNote('test', {
|
||||
uuid: 'active-file',
|
||||
const activeNote = createNote('test', {
|
||||
uuid: 'active-note',
|
||||
references: [
|
||||
{
|
||||
reference_type: ContentReferenceType.FileToNote,
|
||||
uuid: 'note-result',
|
||||
uuid: 'file-result',
|
||||
} as FileToNoteReference,
|
||||
],
|
||||
})
|
||||
|
||||
const noteResult = createFile('test', {
|
||||
uuid: 'note-result',
|
||||
const fileResult = createFile('test', {
|
||||
uuid: 'file-result',
|
||||
references: [],
|
||||
})
|
||||
|
||||
const isNoteResultAlreadyLinked = isSearchResultAlreadyLinkedToItem(noteResult, activeFile)
|
||||
const isNoteResultAlreadyLinked = isSearchResultAlreadyLinkedToItem(fileResult, activeNote)
|
||||
expect(isNoteResultAlreadyLinked).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should be false if active item & result are different content type & neither references the other', () => {
|
||||
const activeFile = createNote('test', {
|
||||
const activeNote = createNote('test', {
|
||||
uuid: 'active-file',
|
||||
references: [],
|
||||
})
|
||||
|
||||
const noteResult = createFile('test', {
|
||||
const fileResult = createFile('test', {
|
||||
uuid: 'note-result',
|
||||
references: [],
|
||||
})
|
||||
|
||||
const isNoteResultAlreadyLinked = isSearchResultAlreadyLinkedToItem(noteResult, activeFile)
|
||||
const isNoteResultAlreadyLinked = isSearchResultAlreadyLinkedToItem(fileResult, activeNote)
|
||||
expect(isNoteResultAlreadyLinked).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('linkItems', () => {
|
||||
it('should move file to same vault as note if file does not belong to any vault', async () => {
|
||||
InternalFeatureService.get().enableFeature(InternalFeature.Vaults)
|
||||
|
||||
application.mutator.associateFileWithNote = jest.fn().mockReturnValue({})
|
||||
|
||||
const moveToVaultSpy = (application.vaults.moveItemToVault = jest.fn())
|
||||
|
||||
const note = createNote('test', {
|
||||
uuid: 'note',
|
||||
references: [],
|
||||
})
|
||||
|
||||
const file = createFile('test', {
|
||||
uuid: 'file',
|
||||
references: [],
|
||||
})
|
||||
|
||||
const noteVault = {
|
||||
uuid: 'note-vault',
|
||||
} as jest.Mocked<VaultListingInterface>
|
||||
|
||||
application.vaults.getItemVault = jest.fn().mockImplementation((item: ItemInterface) => {
|
||||
if (item.uuid === note.uuid) {
|
||||
return noteVault
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
|
||||
await linkingController.linkItems(note, file)
|
||||
|
||||
expect(moveToVaultSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { FileItemActionType } from '@/Components/AttachedFilesPopover/PopoverFileItemAction'
|
||||
import { NoteViewController } from '@/Components/NoteView/Controller/NoteViewController'
|
||||
import { AppPaneId } from '@/Components/Panes/AppPaneMetadata'
|
||||
@@ -9,13 +8,14 @@ import {
|
||||
ApplicationEvent,
|
||||
ContentType,
|
||||
FileItem,
|
||||
InternalEventBus,
|
||||
naturalSort,
|
||||
PrefKey,
|
||||
SNNote,
|
||||
SNTag,
|
||||
isFile,
|
||||
isNote,
|
||||
InternalEventBusInterface,
|
||||
isTag,
|
||||
} from '@standardnotes/snjs'
|
||||
import { action, computed, makeObservable, observable } from 'mobx'
|
||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
@@ -25,6 +25,8 @@ import { ItemListController } from './ItemList/ItemListController'
|
||||
import { NavigationController } from './Navigation/NavigationController'
|
||||
import { SelectedItemsController } from './SelectedItemsController'
|
||||
import { SubscriptionController } from './Subscription/SubscriptionController'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { featureTrunkVaultsEnabled } from '@/FeatureTrunk'
|
||||
|
||||
export class LinkingController extends AbstractViewController {
|
||||
shouldLinkToParentFolders: boolean
|
||||
@@ -37,7 +39,7 @@ export class LinkingController extends AbstractViewController {
|
||||
application: WebApplication,
|
||||
private navigationController: NavigationController,
|
||||
private selectionController: SelectedItemsController,
|
||||
eventBus: InternalEventBus,
|
||||
eventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(application, eventBus)
|
||||
|
||||
@@ -165,7 +167,11 @@ export class LinkingController extends AbstractViewController {
|
||||
}
|
||||
|
||||
unlinkItems = async (item: LinkableItem, itemToUnlink: LinkableItem) => {
|
||||
await this.application.items.unlinkItems(item, itemToUnlink)
|
||||
try {
|
||||
await this.application.mutator.unlinkItems(item, itemToUnlink)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
void this.application.sync.sync()
|
||||
}
|
||||
@@ -188,8 +194,36 @@ export class LinkingController extends AbstractViewController {
|
||||
}
|
||||
|
||||
linkItems = async (item: LinkableItem, itemToLink: LinkableItem) => {
|
||||
if (item instanceof SNNote) {
|
||||
if (itemToLink instanceof SNNote && !this.isEntitledToNoteLinking) {
|
||||
const linkNoteAndFile = async (note: SNNote, file: FileItem) => {
|
||||
const updatedFile = await this.application.mutator.associateFileWithNote(file, note)
|
||||
|
||||
if (updatedFile && featureTrunkVaultsEnabled()) {
|
||||
const noteVault = this.application.vaults.getItemVault(note)
|
||||
const fileVault = this.application.vaults.getItemVault(updatedFile)
|
||||
if (noteVault && !fileVault) {
|
||||
await this.application.vaults.moveItemToVault(noteVault, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const linkFileAndFile = async (file1: FileItem, file2: FileItem) => {
|
||||
await this.application.mutator.linkFileToFile(file1, file2)
|
||||
}
|
||||
|
||||
const linkNoteToNote = async (note1: SNNote, note2: SNNote) => {
|
||||
await this.application.mutator.linkNoteToNote(note1, note2)
|
||||
}
|
||||
|
||||
const linkTagToNote = async (tag: SNTag, note: SNNote) => {
|
||||
await this.addTagToItem(tag, note)
|
||||
}
|
||||
|
||||
const linkTagToFile = async (tag: SNTag, file: FileItem) => {
|
||||
await this.addTagToItem(tag, file)
|
||||
}
|
||||
|
||||
if (isNote(item)) {
|
||||
if (isNote(itemToLink) && !this.isEntitledToNoteLinking) {
|
||||
void this.publishCrossControllerEventSync(CrossControllerEvent.DisplayPremiumModal, {
|
||||
featureName: 'Note linking',
|
||||
})
|
||||
@@ -200,22 +234,22 @@ export class LinkingController extends AbstractViewController {
|
||||
await this.ensureActiveItemIsInserted()
|
||||
}
|
||||
|
||||
if (itemToLink instanceof FileItem) {
|
||||
await this.application.items.associateFileWithNote(itemToLink, item)
|
||||
} else if (itemToLink instanceof SNNote) {
|
||||
await this.application.items.linkNoteToNote(item, itemToLink)
|
||||
} else if (itemToLink instanceof SNTag) {
|
||||
await this.addTagToItem(itemToLink, item)
|
||||
if (isFile(itemToLink)) {
|
||||
await linkNoteAndFile(item, itemToLink)
|
||||
} else if (isNote(itemToLink)) {
|
||||
await linkNoteToNote(item, itemToLink)
|
||||
} else if (isTag(itemToLink)) {
|
||||
await linkTagToNote(itemToLink, item)
|
||||
} else {
|
||||
throw Error('Invalid item type')
|
||||
}
|
||||
} else if (item instanceof FileItem) {
|
||||
if (itemToLink instanceof SNNote) {
|
||||
await this.application.items.associateFileWithNote(item, itemToLink)
|
||||
} else if (itemToLink instanceof FileItem) {
|
||||
await this.application.items.linkFileToFile(item, itemToLink)
|
||||
} else if (itemToLink instanceof SNTag) {
|
||||
await this.addTagToItem(itemToLink, item)
|
||||
} else if (isFile(item)) {
|
||||
if (isNote(itemToLink)) {
|
||||
await linkNoteAndFile(itemToLink, item)
|
||||
} else if (isFile(itemToLink)) {
|
||||
await linkFileAndFile(item, itemToLink)
|
||||
} else if (isTag(itemToLink)) {
|
||||
await linkTagToFile(itemToLink, item)
|
||||
} else {
|
||||
throw Error('Invalid item to link')
|
||||
}
|
||||
@@ -248,8 +282,12 @@ export class LinkingController extends AbstractViewController {
|
||||
|
||||
createAndAddNewTag = async (title: string): Promise<SNTag> => {
|
||||
await this.ensureActiveItemIsInserted()
|
||||
|
||||
const vault = this.application.vaultDisplayService.exclusivelyShownVault
|
||||
|
||||
const newTag = await this.application.mutator.findOrCreateTag(title, vault)
|
||||
|
||||
const activeItem = this.activeItem
|
||||
const newTag = await this.application.mutator.findOrCreateTag(title)
|
||||
if (activeItem) {
|
||||
await this.addTagToItem(newTag, activeItem)
|
||||
}
|
||||
@@ -259,9 +297,9 @@ export class LinkingController extends AbstractViewController {
|
||||
|
||||
addTagToItem = async (tag: SNTag, item: FileItem | SNNote) => {
|
||||
if (item instanceof SNNote) {
|
||||
await this.application.items.addTagToNote(item, tag, this.shouldLinkToParentFolders)
|
||||
await this.application.mutator.addTagToNote(item, tag, this.shouldLinkToParentFolders)
|
||||
} else if (item instanceof FileItem) {
|
||||
await this.application.items.addTagToFile(item, tag, this.shouldLinkToParentFolders)
|
||||
await this.application.mutator.addTagToFile(item, tag, this.shouldLinkToParentFolders)
|
||||
}
|
||||
|
||||
this.application.sync.sync().catch(console.error)
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { confirmDialog, CREATE_NEW_TAG_COMMAND, NavigationControllerPersistableValue } from '@standardnotes/ui-services'
|
||||
import {
|
||||
confirmDialog,
|
||||
CREATE_NEW_TAG_COMMAND,
|
||||
NavigationControllerPersistableValue,
|
||||
VaultDisplayServiceEvent,
|
||||
} from '@standardnotes/ui-services'
|
||||
import { STRING_DELETE_TAG } from '@/Constants/Strings'
|
||||
import { MAX_MENU_SIZE_MULTIPLIER, MENU_MARGIN_FROM_APP_BORDER, SMART_TAGS_FEATURE_NAME } from '@/Constants/Constants'
|
||||
import {
|
||||
@@ -10,13 +15,15 @@ import {
|
||||
isSystemView,
|
||||
FindItem,
|
||||
SystemViewId,
|
||||
InternalEventBus,
|
||||
InternalEventPublishStrategy,
|
||||
VectorIconNameOrEmoji,
|
||||
isTag,
|
||||
PrefKey,
|
||||
InternalEventBusInterface,
|
||||
InternalEventHandlerInterface,
|
||||
InternalEventInterface,
|
||||
} from '@standardnotes/snjs'
|
||||
import { action, computed, makeAutoObservable, makeObservable, observable, reaction, runInAction } from 'mobx'
|
||||
import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'
|
||||
import { WebApplication } from '../../Application/WebApplication'
|
||||
import { FeaturesController } from '../FeaturesController'
|
||||
import { destroyAllObjectProperties } from '@/Utils'
|
||||
@@ -27,10 +34,11 @@ import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||
import { Persistable } from '../Abstract/Persistable'
|
||||
import { TagListSectionType } from '@/Components/Tags/TagListSection'
|
||||
import { PaneLayout } from '../PaneController/PaneLayout'
|
||||
import { TagsCountsState } from './TagsCountsState'
|
||||
|
||||
export class NavigationController
|
||||
extends AbstractViewController
|
||||
implements Persistable<NavigationControllerPersistableValue>
|
||||
implements Persistable<NavigationControllerPersistableValue>, InternalEventHandlerInterface
|
||||
{
|
||||
tags: SNTag[] = []
|
||||
smartViews: SmartView[] = []
|
||||
@@ -54,9 +62,15 @@ export class NavigationController
|
||||
|
||||
private readonly tagsCountsState: TagsCountsState
|
||||
|
||||
constructor(application: WebApplication, private featuresController: FeaturesController, eventBus: InternalEventBus) {
|
||||
constructor(
|
||||
application: WebApplication,
|
||||
private featuresController: FeaturesController,
|
||||
eventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(application, eventBus)
|
||||
|
||||
eventBus.addEventHandler(this, VaultDisplayServiceEvent.VaultDisplayOptionsChanged)
|
||||
|
||||
this.tagsCountsState = new TagsCountsState(this.application)
|
||||
this.smartViews = this.application.items.getSmartViews()
|
||||
|
||||
@@ -109,11 +123,9 @@ export class NavigationController
|
||||
|
||||
this.disposers.push(
|
||||
this.application.streamItems([ContentType.Tag, ContentType.SmartView], ({ changed, removed }) => {
|
||||
runInAction(() => {
|
||||
this.tags = this.application.items.getDisplayableTags()
|
||||
this.starredTags = this.tags.filter((tag) => tag.starred)
|
||||
this.smartViews = this.application.items.getSmartViews()
|
||||
this.reloadTags()
|
||||
|
||||
runInAction(() => {
|
||||
const currentSelectedTag = this.selected_
|
||||
|
||||
if (!currentSelectedTag) {
|
||||
@@ -173,6 +185,20 @@ export class NavigationController
|
||||
)
|
||||
}
|
||||
|
||||
private reloadTags(): void {
|
||||
runInAction(() => {
|
||||
this.tags = this.application.items.getDisplayableTags()
|
||||
this.starredTags = this.tags.filter((tag) => tag.starred)
|
||||
this.smartViews = this.application.items.getSmartViews()
|
||||
})
|
||||
}
|
||||
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === VaultDisplayServiceEvent.VaultDisplayOptionsChanged) {
|
||||
this.reloadTags()
|
||||
}
|
||||
}
|
||||
|
||||
private findAndSetTag = (uuid: UuidString) => {
|
||||
const tagToSelect = [...this.tags, ...this.smartViews].find((tag) => tag.uuid === uuid)
|
||||
if (tagToSelect) {
|
||||
@@ -232,7 +258,10 @@ export class NavigationController
|
||||
return
|
||||
}
|
||||
|
||||
const createdTag = (await this.application.mutator.createTagOrSmartView(title)) as SNTag
|
||||
const createdTag = await this.application.mutator.createTagOrSmartView<SNTag>(
|
||||
title,
|
||||
this.application.vaultDisplayService.exclusivelyShownVault,
|
||||
)
|
||||
|
||||
const futureSiblings = this.application.items.getTagChildren(parent)
|
||||
|
||||
@@ -454,7 +483,7 @@ export class NavigationController
|
||||
}
|
||||
|
||||
public async setPanelWidthForTag(tag: SNTag, width: number): Promise<void> {
|
||||
await this.application.mutator.changeAndSaveItem<TagMutator>(tag, (mutator) => {
|
||||
await this.application.changeAndSaveItem<TagMutator>(tag, (mutator) => {
|
||||
mutator.preferences = {
|
||||
...mutator.preferences,
|
||||
panelWidth: width,
|
||||
@@ -468,7 +497,7 @@ export class NavigationController
|
||||
{ userTriggered } = { userTriggered: false },
|
||||
) {
|
||||
if (tag && tag.conflictOf) {
|
||||
this.application.mutator
|
||||
this.application
|
||||
.changeAndSaveItem(tag, (mutator) => {
|
||||
mutator.conflictOf = undefined
|
||||
})
|
||||
@@ -529,7 +558,7 @@ export class NavigationController
|
||||
return
|
||||
}
|
||||
|
||||
this.application.mutator
|
||||
this.application
|
||||
.changeAndSaveItem<TagMutator>(tag, (mutator) => {
|
||||
mutator.expanded = expanded
|
||||
})
|
||||
@@ -537,7 +566,7 @@ export class NavigationController
|
||||
}
|
||||
|
||||
public async setFavorite(tag: SNTag, favorite: boolean) {
|
||||
return this.application.mutator
|
||||
return this.application
|
||||
.changeAndSaveItem<TagMutator>(tag, (mutator) => {
|
||||
mutator.starred = favorite
|
||||
})
|
||||
@@ -545,7 +574,7 @@ export class NavigationController
|
||||
}
|
||||
|
||||
public setIcon(tag: SNTag, icon: VectorIconNameOrEmoji) {
|
||||
this.application.mutator
|
||||
this.application
|
||||
.changeAndSaveItem<TagMutator>(tag, (mutator) => {
|
||||
mutator.iconString = icon as string
|
||||
})
|
||||
@@ -570,7 +599,7 @@ export class NavigationController
|
||||
return
|
||||
}
|
||||
|
||||
const newTag = this.application.mutator.createTemplateItem(ContentType.Tag) as SNTag
|
||||
const newTag = this.application.items.createTemplateItem(ContentType.Tag) as SNTag
|
||||
|
||||
runInAction(() => {
|
||||
this.selectedLocation = 'all'
|
||||
@@ -593,7 +622,10 @@ export class NavigationController
|
||||
})
|
||||
}
|
||||
if (shouldDelete) {
|
||||
this.application.mutator.deleteItem(tag).catch(console.error)
|
||||
this.application.mutator
|
||||
.deleteItem(tag)
|
||||
.then(() => this.application.sync.sync())
|
||||
.catch(console.error)
|
||||
await this.setSelectedTag(this.smartViews[0], 'views')
|
||||
}
|
||||
}
|
||||
@@ -635,36 +667,18 @@ export class NavigationController
|
||||
}
|
||||
}
|
||||
|
||||
const insertedTag = await this.application.mutator.createTagOrSmartView(newTitle)
|
||||
const insertedTag = await this.application.mutator.createTagOrSmartView<SNTag>(
|
||||
newTitle,
|
||||
this.application.vaultDisplayService.exclusivelyShownVault,
|
||||
)
|
||||
this.application.sync.sync().catch(console.error)
|
||||
runInAction(() => {
|
||||
void this.setSelectedTag(insertedTag as SNTag, this.selectedLocation || 'views')
|
||||
void this.setSelectedTag(insertedTag, this.selectedLocation || 'views')
|
||||
})
|
||||
} else {
|
||||
await this.application.mutator.changeAndSaveItem<TagMutator>(tag, (mutator) => {
|
||||
await this.application.changeAndSaveItem<TagMutator>(tag, (mutator) => {
|
||||
mutator.title = newTitle
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TagsCountsState {
|
||||
public counts: { [uuid: string]: number } = {}
|
||||
|
||||
public constructor(private application: WebApplication) {
|
||||
makeAutoObservable(this, {
|
||||
counts: observable.ref,
|
||||
update: action,
|
||||
})
|
||||
}
|
||||
|
||||
public update(tags: SNTag[]) {
|
||||
const newCounts: { [uuid: string]: number } = Object.assign({}, this.counts)
|
||||
|
||||
tags.forEach((tag) => {
|
||||
newCounts[tag.uuid] = this.application.items.countableNotesForTag(tag)
|
||||
})
|
||||
|
||||
this.counts = newCounts
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { SNTag } from '@standardnotes/snjs'
|
||||
import { action, makeAutoObservable, observable } from 'mobx'
|
||||
import { WebApplication } from '../../Application/WebApplication'
|
||||
|
||||
export class TagsCountsState {
|
||||
public counts: { [uuid: string]: number } = {}
|
||||
|
||||
public constructor(private application: WebApplication) {
|
||||
makeAutoObservable(this, {
|
||||
counts: observable.ref,
|
||||
update: action,
|
||||
})
|
||||
}
|
||||
|
||||
public update(tags: SNTag[]) {
|
||||
const newCounts: { [uuid: string]: number } = Object.assign({}, this.counts)
|
||||
|
||||
tags.forEach((tag) => {
|
||||
newCounts[tag.uuid] = this.application.items.countableNotesForTag(tag)
|
||||
})
|
||||
|
||||
this.counts = newCounts
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { storage, StorageKey } from '@standardnotes/ui-services'
|
||||
import { ApplicationEvent, InternalEventBus } from '@standardnotes/snjs'
|
||||
import { ApplicationEvent, InternalEventBusInterface } from '@standardnotes/snjs'
|
||||
import { runInAction, makeObservable, observable, action } from 'mobx'
|
||||
import { WebApplication } from '../Application/WebApplication'
|
||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
@@ -7,7 +7,7 @@ import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
export class NoAccountWarningController extends AbstractViewController {
|
||||
show: boolean
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
constructor(application: WebApplication, eventBus: InternalEventBusInterface) {
|
||||
super(application, eventBus)
|
||||
|
||||
this.show = application.hasAccount() ? false : storage.get(StorageKey.ShowNoAccountWarning) ?? true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { InternalEventBus, SNNote } from '@standardnotes/snjs'
|
||||
import { InternalEventBusInterface, SNNote } from '@standardnotes/snjs'
|
||||
import { OPEN_NOTE_HISTORY_COMMAND } from '@standardnotes/ui-services'
|
||||
import { action, makeObservable, observable } from 'mobx'
|
||||
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||
@@ -13,7 +13,11 @@ export class HistoryModalController extends AbstractViewController {
|
||||
this.note = undefined
|
||||
}
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus, notesController: NotesControllerInterface) {
|
||||
constructor(
|
||||
application: WebApplication,
|
||||
eventBus: InternalEventBusInterface,
|
||||
notesController: NotesControllerInterface,
|
||||
) {
|
||||
super(application, eventBus)
|
||||
|
||||
makeObservable(this, {
|
||||
|
||||
@@ -330,7 +330,7 @@ export class NoteHistoryController {
|
||||
}
|
||||
|
||||
if (didConfirm) {
|
||||
void this.application.mutator.changeAndSaveItem(
|
||||
void this.application.changeAndSaveItem(
|
||||
originalNote,
|
||||
(mutator) => {
|
||||
mutator.setCustomContent(revision.payload.content)
|
||||
@@ -344,11 +344,13 @@ export class NoteHistoryController {
|
||||
restoreRevisionAsCopy = async (revision: NonNullable<SelectedRevision>) => {
|
||||
const originalNote = this.application.items.findSureItem<SNNote>(revision.payload.uuid)
|
||||
|
||||
const duplicatedItem = await this.application.mutator.duplicateItem(originalNote, {
|
||||
const duplicatedItem = await this.application.mutator.duplicateItem(originalNote, false, {
|
||||
...revision.payload.content,
|
||||
title: revision.payload.content.title ? revision.payload.content.title + ' (copy)' : undefined,
|
||||
})
|
||||
|
||||
void this.application.sync.sync()
|
||||
|
||||
this.selectionController.selectItem(duplicatedItem.uuid).catch(console.error)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { NoteMutator, SNNote } from '@standardnotes/models'
|
||||
import { MutationType, NoteMutator, SNNote } from '@standardnotes/models'
|
||||
import { InfoStrings } from '@standardnotes/snjs'
|
||||
import { Deferred } from '@standardnotes/utils'
|
||||
import { EditorSaveTimeoutDebounce } from '../Components/NoteView/Controller/EditorSaveTimeoutDebounce'
|
||||
@@ -106,7 +106,7 @@ export class NoteSyncController {
|
||||
noteMutator.preview_html = undefined
|
||||
}
|
||||
},
|
||||
params.isUserModified,
|
||||
params.isUserModified ? MutationType.UpdateUserTimestamps : MutationType.NoUpdateUserTimestamps,
|
||||
)
|
||||
|
||||
void this.application.sync.sync().then(() => {
|
||||
|
||||
@@ -7,10 +7,11 @@ import {
|
||||
NoteMutator,
|
||||
ContentType,
|
||||
SNTag,
|
||||
InternalEventBus,
|
||||
PrefKey,
|
||||
ApplicationEvent,
|
||||
EditorLineWidth,
|
||||
InternalEventBusInterface,
|
||||
MutationType,
|
||||
} from '@standardnotes/snjs'
|
||||
import { makeObservable, observable, action, computed, runInAction } from 'mobx'
|
||||
import { WebApplication } from '../../Application/WebApplication'
|
||||
@@ -48,7 +49,7 @@ export class NotesController extends AbstractViewController implements NotesCont
|
||||
application: WebApplication,
|
||||
private selectionController: SelectedItemsController,
|
||||
private navigationController: NavigationController,
|
||||
eventBus: InternalEventBus,
|
||||
eventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(application, eventBus)
|
||||
|
||||
@@ -201,7 +202,7 @@ export class NotesController extends AbstractViewController implements NotesCont
|
||||
}
|
||||
|
||||
async changeSelectedNotes(mutate: (mutator: NoteMutator) => void): Promise<void> {
|
||||
await this.application.mutator.changeItems(this.getSelectedNotesList(), mutate, false)
|
||||
await this.application.mutator.changeItems(this.getSelectedNotesList(), mutate, MutationType.NoUpdateUserTimestamps)
|
||||
this.application.sync.sync().catch(console.error)
|
||||
}
|
||||
|
||||
@@ -263,9 +264,8 @@ export class NotesController extends AbstractViewController implements NotesCont
|
||||
) {
|
||||
this.selectionController.selectNextItem()
|
||||
if (permanently) {
|
||||
for (const note of this.getSelectedNotesList()) {
|
||||
await this.application.mutator.deleteItem(note)
|
||||
}
|
||||
await this.application.mutator.deleteItems(this.getSelectedNotesList())
|
||||
void this.application.sync.sync()
|
||||
} else {
|
||||
await this.changeSelectedNotes((mutator) => {
|
||||
mutator.trashed = true
|
||||
@@ -332,12 +332,14 @@ export class NotesController extends AbstractViewController implements NotesCont
|
||||
async setProtectSelectedNotes(protect: boolean): Promise<void> {
|
||||
const selectedNotes = this.getSelectedNotesList()
|
||||
if (protect) {
|
||||
await this.application.mutator.protectNotes(selectedNotes)
|
||||
await this.application.protections.protectNotes(selectedNotes)
|
||||
this.setShowProtectedWarning(true)
|
||||
} else {
|
||||
await this.application.mutator.unprotectNotes(selectedNotes)
|
||||
await this.application.protections.unprotectNotes(selectedNotes)
|
||||
this.setShowProtectedWarning(false)
|
||||
}
|
||||
|
||||
void this.application.sync.sync()
|
||||
}
|
||||
|
||||
unselectNotes(): void {
|
||||
@@ -354,7 +356,7 @@ export class NotesController extends AbstractViewController implements NotesCont
|
||||
(mutator) => {
|
||||
mutator.toggleSpellcheck()
|
||||
},
|
||||
false,
|
||||
MutationType.NoUpdateUserTimestamps,
|
||||
)
|
||||
this.application.sync.sync().catch(console.error)
|
||||
}
|
||||
@@ -371,7 +373,7 @@ export class NotesController extends AbstractViewController implements NotesCont
|
||||
(mutator) => {
|
||||
mutator.editorWidth = editorWidth
|
||||
},
|
||||
false,
|
||||
MutationType.NoUpdateUserTimestamps,
|
||||
)
|
||||
this.application.sync.sync().catch(console.error)
|
||||
}
|
||||
@@ -380,7 +382,7 @@ export class NotesController extends AbstractViewController implements NotesCont
|
||||
const selectedNotes = this.getSelectedNotesList()
|
||||
await Promise.all(
|
||||
selectedNotes.map(async (note) => {
|
||||
await this.application.items.addTagToNote(note, tag, this.shouldLinkToParentFolders)
|
||||
await this.application.mutator.addTagToNote(note, tag, this.shouldLinkToParentFolders)
|
||||
}),
|
||||
)
|
||||
this.application.sync.sync().catch(console.error)
|
||||
@@ -414,7 +416,7 @@ export class NotesController extends AbstractViewController implements NotesCont
|
||||
confirmButtonStyle: 'danger',
|
||||
})
|
||||
) {
|
||||
this.application.mutator.emptyTrash().catch(console.error)
|
||||
await this.application.mutator.emptyTrash()
|
||||
this.application.sync.sync().catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
TOGGLE_LIST_PANE_KEYBOARD_COMMAND,
|
||||
TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND,
|
||||
} from '@standardnotes/ui-services'
|
||||
import { ApplicationEvent, InternalEventBus, PrefKey, removeFromArray } from '@standardnotes/snjs'
|
||||
import { ApplicationEvent, InternalEventBusInterface, PrefKey, removeFromArray } from '@standardnotes/snjs'
|
||||
import { AppPaneId } from '../../Components/Panes/AppPaneMetadata'
|
||||
import { isMobileScreen } from '@/Utils'
|
||||
import { makeObservable, observable, action, computed } from 'mobx'
|
||||
@@ -35,7 +35,7 @@ export class PaneController extends AbstractViewController {
|
||||
listPaneExplicitelyCollapsed = false
|
||||
navigationPaneExplicitelyCollapsed = false
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
constructor(application: WebApplication, eventBus: InternalEventBusInterface) {
|
||||
super(application, eventBus)
|
||||
|
||||
makeObservable(this, {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { InternalEventBus } from '@standardnotes/snjs'
|
||||
import { InternalEventBusInterface } from '@standardnotes/snjs'
|
||||
import { action, computed, makeObservable, observable } from 'mobx'
|
||||
import { PreferenceId, RootQueryParam } from '@standardnotes/ui-services'
|
||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
@@ -10,7 +10,7 @@ export class PreferencesController extends AbstractViewController {
|
||||
private _open = false
|
||||
currentPane: PreferenceId = DEFAULT_PANE
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
constructor(application: WebApplication, eventBus: InternalEventBusInterface) {
|
||||
super(application, eventBus)
|
||||
|
||||
makeObservable<PreferencesController, '_open'>(this, {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LoggingDomain, log } from '@/Logging'
|
||||
import { loadPurchaseFlowUrl } from '@/Components/PurchaseFlow/PurchaseFlowFunctions'
|
||||
import { InternalEventBus, AppleIAPProductId } from '@standardnotes/snjs'
|
||||
import { AppleIAPProductId, InternalEventBusInterface } from '@standardnotes/snjs'
|
||||
import { action, makeObservable, observable } from 'mobx'
|
||||
import { WebApplication } from '../../Application/WebApplication'
|
||||
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||
@@ -10,7 +10,7 @@ export class PurchaseFlowController extends AbstractViewController {
|
||||
isOpen = false
|
||||
currentPane = PurchaseFlowPane.CreateAccount
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
constructor(application: WebApplication, eventBus: InternalEventBusInterface) {
|
||||
super(application, eventBus)
|
||||
|
||||
makeObservable(this, {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { InternalEventBus } from '@standardnotes/snjs'
|
||||
import { InternalEventBusInterface } from '@standardnotes/snjs'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { action, makeObservable, observable } from 'mobx'
|
||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
@@ -7,7 +7,7 @@ export class QuickSettingsController extends AbstractViewController {
|
||||
open = false
|
||||
shouldAnimateCloseMenu = false
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
constructor(application: WebApplication, eventBus: InternalEventBusInterface) {
|
||||
super(application, eventBus)
|
||||
|
||||
makeObservable(this, {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApplicationEvent, InternalEventBus } from '@standardnotes/snjs'
|
||||
import { ApplicationEvent, InternalEventBusInterface } from '@standardnotes/snjs'
|
||||
import { makeObservable, observable, action, runInAction } from 'mobx'
|
||||
import { WebApplication } from '../Application/WebApplication'
|
||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
@@ -8,7 +8,7 @@ export class SearchOptionsController extends AbstractViewController {
|
||||
includeArchived = false
|
||||
includeTrashed = false
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
constructor(application: WebApplication, eventBus: InternalEventBusInterface) {
|
||||
super(application, eventBus)
|
||||
|
||||
makeObservable(this, {
|
||||
|
||||
@@ -8,10 +8,10 @@ import {
|
||||
FileItem,
|
||||
SNNote,
|
||||
UuidString,
|
||||
InternalEventBus,
|
||||
isFile,
|
||||
Uuids,
|
||||
isNote,
|
||||
InternalEventBusInterface,
|
||||
} from '@standardnotes/snjs'
|
||||
import { SelectionControllerPersistableValue } from '@standardnotes/ui-services'
|
||||
import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'
|
||||
@@ -36,7 +36,7 @@ export class SelectedItemsController
|
||||
;(this.itemListController as unknown) = undefined
|
||||
}
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||
constructor(application: WebApplication, eventBus: InternalEventBusInterface) {
|
||||
super(application, eventBus)
|
||||
|
||||
makeObservable(this, {
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
ApplicationEvent,
|
||||
ClientDisplayableError,
|
||||
convertTimestampToMilliseconds,
|
||||
InternalEventBus,
|
||||
InternalEventBusInterface,
|
||||
Invitation,
|
||||
InvitationStatus,
|
||||
SubscriptionClientInterface,
|
||||
@@ -34,7 +34,7 @@ export class SubscriptionController extends AbstractViewController {
|
||||
|
||||
constructor(
|
||||
application: WebApplication,
|
||||
eventBus: InternalEventBus,
|
||||
eventBus: InternalEventBusInterface,
|
||||
private subscriptionManager: SubscriptionClientInterface,
|
||||
) {
|
||||
super(application, eventBus)
|
||||
@@ -188,7 +188,7 @@ export class SubscriptionController extends AbstractViewController {
|
||||
this.setAvailableSubscriptions(subscriptions)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
void error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { InternalEventBusInterface } from '@standardnotes/snjs'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { action, makeObservable, observable } from 'mobx'
|
||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
|
||||
export class VaultSelectionMenuController extends AbstractViewController {
|
||||
open = false
|
||||
shouldAnimateCloseMenu = false
|
||||
|
||||
constructor(application: WebApplication, eventBus: InternalEventBusInterface) {
|
||||
super(application, eventBus)
|
||||
|
||||
makeObservable(this, {
|
||||
open: observable,
|
||||
shouldAnimateCloseMenu: observable,
|
||||
|
||||
setOpen: action,
|
||||
setShouldAnimateCloseMenu: action,
|
||||
toggle: action,
|
||||
closeVaultSelectionMenu: action,
|
||||
})
|
||||
}
|
||||
|
||||
setOpen = (open: boolean): void => {
|
||||
this.open = open
|
||||
}
|
||||
|
||||
setShouldAnimateCloseMenu = (shouldAnimate: boolean): void => {
|
||||
this.shouldAnimateCloseMenu = shouldAnimate
|
||||
}
|
||||
|
||||
toggle = (): void => {
|
||||
if (this.open) {
|
||||
this.closeVaultSelectionMenu()
|
||||
} else {
|
||||
this.setOpen(true)
|
||||
}
|
||||
}
|
||||
|
||||
closeVaultSelectionMenu = (): void => {
|
||||
this.setShouldAnimateCloseMenu(true)
|
||||
setTimeout(() => {
|
||||
this.setOpen(false)
|
||||
this.setShouldAnimateCloseMenu(false)
|
||||
}, 150)
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import { destroyAllObjectProperties } from '@/Utils'
|
||||
import {
|
||||
DeinitSource,
|
||||
WebOrDesktopDeviceInterface,
|
||||
InternalEventBus,
|
||||
SubscriptionClientInterface,
|
||||
InternalEventHandlerInterface,
|
||||
InternalEventInterface,
|
||||
@@ -41,6 +40,7 @@ import { CrossControllerEvent } from './CrossControllerEvent'
|
||||
import { EventObserverInterface } from '@/Event/EventObserverInterface'
|
||||
import { ApplicationEventObserver } from '@/Event/ApplicationEventObserver'
|
||||
import { ImportModalController } from './ImportModalController'
|
||||
import { VaultSelectionMenuController } from './VaultSelectionMenuController'
|
||||
|
||||
export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
readonly enableUnfinishedFeatures: boolean = window?.enabledUnfinishedFeatures
|
||||
@@ -60,6 +60,7 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
readonly preferencesController: PreferencesController
|
||||
readonly purchaseFlowController: PurchaseFlowController
|
||||
readonly quickSettingsMenuController: QuickSettingsController
|
||||
readonly vaultSelectionController: VaultSelectionMenuController
|
||||
readonly searchOptionsController: SearchOptionsController
|
||||
readonly subscriptionController: SubscriptionController
|
||||
readonly syncStatusController = new SyncStatusController()
|
||||
@@ -73,50 +74,52 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
public isSessionsModalVisible = false
|
||||
|
||||
private appEventObserverRemovers: (() => void)[] = []
|
||||
private eventBus: InternalEventBus
|
||||
|
||||
private subscriptionManager: SubscriptionClientInterface
|
||||
private persistenceService: PersistenceService
|
||||
private applicationEventObserver: EventObserverInterface
|
||||
private toastService: ToastServiceInterface
|
||||
|
||||
constructor(public application: WebApplication, private device: WebOrDesktopDeviceInterface) {
|
||||
this.eventBus = new InternalEventBus()
|
||||
const eventBus = application.internalEventBus
|
||||
|
||||
this.persistenceService = new PersistenceService(application, this.eventBus)
|
||||
this.persistenceService = new PersistenceService(application, eventBus)
|
||||
|
||||
this.eventBus.addEventHandler(this, CrossControllerEvent.HydrateFromPersistedValues)
|
||||
this.eventBus.addEventHandler(this, CrossControllerEvent.RequestValuePersistence)
|
||||
eventBus.addEventHandler(this, CrossControllerEvent.HydrateFromPersistedValues)
|
||||
eventBus.addEventHandler(this, CrossControllerEvent.RequestValuePersistence)
|
||||
|
||||
this.subscriptionManager = application.subscriptions
|
||||
|
||||
this.filePreviewModalController = new FilePreviewModalController(application)
|
||||
|
||||
this.quickSettingsMenuController = new QuickSettingsController(application, this.eventBus)
|
||||
this.quickSettingsMenuController = new QuickSettingsController(application, eventBus)
|
||||
|
||||
this.paneController = new PaneController(application, this.eventBus)
|
||||
this.vaultSelectionController = new VaultSelectionMenuController(application, eventBus)
|
||||
|
||||
this.preferencesController = new PreferencesController(application, this.eventBus)
|
||||
this.paneController = new PaneController(application, eventBus)
|
||||
|
||||
this.selectionController = new SelectedItemsController(application, this.eventBus)
|
||||
this.preferencesController = new PreferencesController(application, eventBus)
|
||||
|
||||
this.featuresController = new FeaturesController(application, this.eventBus)
|
||||
this.selectionController = new SelectedItemsController(application, eventBus)
|
||||
|
||||
this.navigationController = new NavigationController(application, this.featuresController, this.eventBus)
|
||||
this.featuresController = new FeaturesController(application, eventBus)
|
||||
|
||||
this.navigationController = new NavigationController(application, this.featuresController, eventBus)
|
||||
|
||||
this.notesController = new NotesController(
|
||||
application,
|
||||
this.selectionController,
|
||||
this.navigationController,
|
||||
this.eventBus,
|
||||
eventBus,
|
||||
)
|
||||
|
||||
this.searchOptionsController = new SearchOptionsController(application, this.eventBus)
|
||||
this.searchOptionsController = new SearchOptionsController(application, eventBus)
|
||||
|
||||
this.linkingController = new LinkingController(
|
||||
application,
|
||||
this.navigationController,
|
||||
this.selectionController,
|
||||
this.eventBus,
|
||||
eventBus,
|
||||
)
|
||||
|
||||
this.itemListController = new ItemListController(
|
||||
@@ -125,26 +128,25 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
this.searchOptionsController,
|
||||
this.selectionController,
|
||||
this.notesController,
|
||||
this.linkingController,
|
||||
this.eventBus,
|
||||
eventBus,
|
||||
)
|
||||
|
||||
this.notesController.setServicesPostConstruction(this.itemListController)
|
||||
this.selectionController.setServicesPostConstruction(this.itemListController)
|
||||
|
||||
this.noAccountWarningController = new NoAccountWarningController(application, this.eventBus)
|
||||
this.noAccountWarningController = new NoAccountWarningController(application, eventBus)
|
||||
|
||||
this.accountMenuController = new AccountMenuController(application, this.eventBus)
|
||||
this.accountMenuController = new AccountMenuController(application, eventBus)
|
||||
|
||||
this.subscriptionController = new SubscriptionController(application, this.eventBus, this.subscriptionManager)
|
||||
this.subscriptionController = new SubscriptionController(application, eventBus, this.subscriptionManager)
|
||||
|
||||
this.purchaseFlowController = new PurchaseFlowController(application, this.eventBus)
|
||||
this.purchaseFlowController = new PurchaseFlowController(application, eventBus)
|
||||
|
||||
this.filesController = new FilesController(
|
||||
application,
|
||||
this.notesController,
|
||||
this.filePreviewModalController,
|
||||
this.eventBus,
|
||||
eventBus,
|
||||
)
|
||||
|
||||
this.linkingController.setServicesPostConstruction(
|
||||
@@ -153,7 +155,7 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
this.subscriptionController,
|
||||
)
|
||||
|
||||
this.historyModalController = new HistoryModalController(this.application, this.eventBus, this.notesController)
|
||||
this.historyModalController = new HistoryModalController(this.application, eventBus, this.notesController)
|
||||
|
||||
this.importModalController = new ImportModalController(this.application, this.navigationController)
|
||||
|
||||
@@ -210,6 +212,7 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
;(this.filePreviewModalController as unknown) = undefined
|
||||
;(this.preferencesController as unknown) = undefined
|
||||
;(this.quickSettingsMenuController as unknown) = undefined
|
||||
;(this.vaultSelectionController as unknown) = undefined
|
||||
;(this.syncStatusController as unknown) = undefined
|
||||
|
||||
this.persistenceService.deinit()
|
||||
|
||||
Reference in New Issue
Block a user