refactor: pass sub controllers to controllers instead of passing global controller manager (#1061)
This commit is contained in:
@@ -17,8 +17,15 @@ import {
|
|||||||
DesktopDeviceInterface,
|
DesktopDeviceInterface,
|
||||||
isDesktopDevice,
|
isDesktopDevice,
|
||||||
DeinitMode,
|
DeinitMode,
|
||||||
|
PrefKey,
|
||||||
|
SNTag,
|
||||||
|
ContentType,
|
||||||
|
DecryptedItemInterface,
|
||||||
} from '@standardnotes/snjs'
|
} from '@standardnotes/snjs'
|
||||||
import { makeObservable, observable } from 'mobx'
|
import { makeObservable, observable } from 'mobx'
|
||||||
|
import { PanelResizedData } from '@/Types/PanelResizedData'
|
||||||
|
import { WebAppEvent } from './WebAppEvent'
|
||||||
|
import { isDesktopApplication } from '@/Utils'
|
||||||
|
|
||||||
type WebServices = {
|
type WebServices = {
|
||||||
viewControllerManager: ViewControllerManager
|
viewControllerManager: ViewControllerManager
|
||||||
@@ -29,19 +36,14 @@ type WebServices = {
|
|||||||
io: IOService
|
io: IOService
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum WebAppEvent {
|
export type WebEventObserver = (event: WebAppEvent, data?: unknown) => void
|
||||||
NewUpdateAvailable = 'NewUpdateAvailable',
|
|
||||||
DesktopWindowGainedFocus = 'DesktopWindowGainedFocus',
|
|
||||||
DesktopWindowLostFocus = 'DesktopWindowLostFocus',
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WebEventObserver = (event: WebAppEvent) => void
|
|
||||||
|
|
||||||
export class WebApplication extends SNApplication {
|
export class WebApplication extends SNApplication {
|
||||||
private webServices!: WebServices
|
private webServices!: WebServices
|
||||||
private webEventObservers: WebEventObserver[] = []
|
private webEventObservers: WebEventObserver[] = []
|
||||||
public noteControllerGroup: NoteGroupController
|
public noteControllerGroup: NoteGroupController
|
||||||
public iconsController: IconsController
|
public iconsController: IconsController
|
||||||
|
private onVisibilityChange: () => void
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
deviceInterface: WebOrDesktopDevice,
|
deviceInterface: WebOrDesktopDevice,
|
||||||
@@ -70,6 +72,16 @@ export class WebApplication extends SNApplication {
|
|||||||
deviceInterface.setApplication(this)
|
deviceInterface.setApplication(this)
|
||||||
this.noteControllerGroup = new NoteGroupController(this)
|
this.noteControllerGroup = new NoteGroupController(this)
|
||||||
this.iconsController = new IconsController()
|
this.iconsController = new IconsController()
|
||||||
|
|
||||||
|
this.onVisibilityChange = () => {
|
||||||
|
const visible = document.visibilityState === 'visible'
|
||||||
|
const event = visible ? WebAppEvent.WindowDidFocus : WebAppEvent.WindowDidBlur
|
||||||
|
this.notifyWebEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDesktopApplication()) {
|
||||||
|
document.addEventListener('visibilitychange', this.onVisibilityChange)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override deinit(mode: DeinitMode, source: DeinitSource): void {
|
override deinit(mode: DeinitMode, source: DeinitSource): void {
|
||||||
@@ -94,6 +106,9 @@ export class WebApplication extends SNApplication {
|
|||||||
;(this.noteControllerGroup as unknown) = undefined
|
;(this.noteControllerGroup as unknown) = undefined
|
||||||
|
|
||||||
this.webEventObservers.length = 0
|
this.webEventObservers.length = 0
|
||||||
|
|
||||||
|
document.removeEventListener('visibilitychange', this.onVisibilityChange)
|
||||||
|
;(this.onVisibilityChange as unknown) = undefined
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error while deiniting application', error)
|
console.error('Error while deiniting application', error)
|
||||||
}
|
}
|
||||||
@@ -105,17 +120,26 @@ export class WebApplication extends SNApplication {
|
|||||||
|
|
||||||
public addWebEventObserver(observer: WebEventObserver): () => void {
|
public addWebEventObserver(observer: WebEventObserver): () => void {
|
||||||
this.webEventObservers.push(observer)
|
this.webEventObservers.push(observer)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
removeFromArray(this.webEventObservers, observer)
|
removeFromArray(this.webEventObservers, observer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public notifyWebEvent(event: WebAppEvent): void {
|
public notifyWebEvent(event: WebAppEvent, data?: unknown): void {
|
||||||
for (const observer of this.webEventObservers) {
|
for (const observer of this.webEventObservers) {
|
||||||
observer(event)
|
observer(event, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
publishPanelDidResizeEvent(name: string, collapsed: boolean) {
|
||||||
|
const data: PanelResizedData = {
|
||||||
|
panel: name,
|
||||||
|
collapsed: collapsed,
|
||||||
|
}
|
||||||
|
this.notifyWebEvent(WebAppEvent.PanelResized, data)
|
||||||
|
}
|
||||||
|
|
||||||
public getViewControllerManager(): ViewControllerManager {
|
public getViewControllerManager(): ViewControllerManager {
|
||||||
return this.webServices.viewControllerManager
|
return this.webServices.viewControllerManager
|
||||||
}
|
}
|
||||||
@@ -163,4 +187,23 @@ export class WebApplication extends SNApplication {
|
|||||||
|
|
||||||
return this.user.signOut()
|
return this.user.signOut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isGlobalSpellcheckEnabled(): boolean {
|
||||||
|
return this.getPreference(PrefKey.EditorSpellcheck, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
public getItemTags(item: DecryptedItemInterface) {
|
||||||
|
return this.items.itemsReferencingItem(item).filter((ref) => {
|
||||||
|
return ref.content_type === ContentType.Tag
|
||||||
|
}) as SNTag[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public get version(): string {
|
||||||
|
return (this.deviceInterface as WebOrDesktopDevice).appVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleGlobalSpellcheck() {
|
||||||
|
const currentValue = this.isGlobalSpellcheckEnabled()
|
||||||
|
return this.setPreference(PrefKey.EditorSpellcheck, !currentValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
app/assets/javascripts/Application/WebAppEvent.ts
Normal file
9
app/assets/javascripts/Application/WebAppEvent.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export enum WebAppEvent {
|
||||||
|
NewUpdateAvailable = 'NewUpdateAvailable',
|
||||||
|
EditorFocused = 'EditorFocused',
|
||||||
|
BeganBackupDownload = 'BeganBackupDownload',
|
||||||
|
EndedBackupDownload = 'EndedBackupDownload',
|
||||||
|
PanelResized = 'PanelResized',
|
||||||
|
WindowDidFocus = 'WindowDidFocus',
|
||||||
|
WindowDidBlur = 'WindowDidBlur',
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ApplicationEvent } from '@standardnotes/snjs'
|
import { ApplicationEvent } from '@standardnotes/snjs'
|
||||||
import { WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
import { ViewControllerManager, ViewControllerManagerEvent } from '@/Services/ViewControllerManager'
|
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||||
import { autorun, IReactionDisposer, IReactionPublic } from 'mobx'
|
import { autorun, IReactionDisposer, IReactionPublic } from 'mobx'
|
||||||
import { Component } from 'react'
|
import { Component } from 'react'
|
||||||
|
|
||||||
@@ -9,7 +9,6 @@ export type PureComponentProps = Partial<Record<string, any>>
|
|||||||
|
|
||||||
export abstract class PureComponent<P = PureComponentProps, S = PureComponentState> extends Component<P, S> {
|
export abstract class PureComponent<P = PureComponentProps, S = PureComponentState> extends Component<P, S> {
|
||||||
private unsubApp!: () => void
|
private unsubApp!: () => void
|
||||||
private unsubState!: () => void
|
|
||||||
private reactionDisposers: IReactionDisposer[] = []
|
private reactionDisposers: IReactionDisposer[] = []
|
||||||
|
|
||||||
constructor(props: P, protected application: WebApplication) {
|
constructor(props: P, protected application: WebApplication) {
|
||||||
@@ -18,18 +17,17 @@ export abstract class PureComponent<P = PureComponentProps, S = PureComponentSta
|
|||||||
|
|
||||||
override componentDidMount() {
|
override componentDidMount() {
|
||||||
this.addAppEventObserver()
|
this.addAppEventObserver()
|
||||||
this.addViewControllerManagerObserver()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit(): void {
|
deinit(): void {
|
||||||
this.unsubApp?.()
|
this.unsubApp?.()
|
||||||
this.unsubState?.()
|
|
||||||
for (const disposer of this.reactionDisposers) {
|
for (const disposer of this.reactionDisposers) {
|
||||||
disposer()
|
disposer()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reactionDisposers.length = 0
|
this.reactionDisposers.length = 0
|
||||||
;(this.unsubApp as unknown) = undefined
|
;(this.unsubApp as unknown) = undefined
|
||||||
;(this.unsubState as unknown) = undefined
|
|
||||||
;(this.application as unknown) = undefined
|
;(this.application as unknown) = undefined
|
||||||
;(this.props as unknown) = undefined
|
;(this.props as unknown) = undefined
|
||||||
;(this.state as unknown) = undefined
|
;(this.state as unknown) = undefined
|
||||||
@@ -47,16 +45,6 @@ export abstract class PureComponent<P = PureComponentProps, S = PureComponentSta
|
|||||||
this.reactionDisposers.push(autorun(view))
|
this.reactionDisposers.push(autorun(view))
|
||||||
}
|
}
|
||||||
|
|
||||||
addViewControllerManagerObserver() {
|
|
||||||
this.unsubState = this.application.getViewControllerManager().addObserver(async (eventName, data) => {
|
|
||||||
this.onViewControllerManagerEvent(eventName, data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onViewControllerManagerEvent(_eventName: ViewControllerManagerEvent, _data: unknown) {
|
|
||||||
/** Optional override */
|
|
||||||
}
|
|
||||||
|
|
||||||
addAppEventObserver() {
|
addAppEventObserver() {
|
||||||
if (this.application.isStarted()) {
|
if (this.application.isStarted()) {
|
||||||
this.onAppStart().catch(console.error)
|
this.onAppStart().catch(console.error)
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
|
|||||||
<Icon type="help" className={iconClassName} />
|
<Icon type="help" className={iconClassName} />
|
||||||
Help & feedback
|
Help & feedback
|
||||||
</div>
|
</div>
|
||||||
<span className="color-neutral">v{viewControllerManager.version}</span>
|
<span className="color-neutral">v{application.version}</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{user ? (
|
{user ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||||
import { getPlatformString, getWindowUrlParams } from '@/Utils'
|
import { getPlatformString, getWindowUrlParams } from '@/Utils'
|
||||||
import { ViewControllerManagerEvent } from '@/Services/ViewControllerManager'
|
|
||||||
import { ApplicationEvent, Challenge, removeFromArray } from '@standardnotes/snjs'
|
import { ApplicationEvent, Challenge, removeFromArray } from '@standardnotes/snjs'
|
||||||
import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/Constants/Constants'
|
import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/Constants/Constants'
|
||||||
import { alertDialog } from '@/Services/AlertService'
|
import { alertDialog } from '@/Services/AlertService'
|
||||||
import { WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
|
import { WebAppEvent } from '@/Application/WebAppEvent'
|
||||||
import Navigation from '@/Components/Navigation/Navigation'
|
import Navigation from '@/Components/Navigation/Navigation'
|
||||||
import NoteGroupView from '@/Components/NoteGroupView/NoteGroupView'
|
import NoteGroupView from '@/Components/NoteGroupView/NoteGroupView'
|
||||||
import Footer from '@/Components/Footer/Footer'
|
import Footer from '@/Components/Footer/Footer'
|
||||||
@@ -120,8 +120,8 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
|||||||
}, [application, onAppLaunch, onAppStart])
|
}, [application, onAppLaunch, onAppStart])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const removeObserver = application.getViewControllerManager().addObserver(async (eventName, data) => {
|
const removeObserver = application.addWebEventObserver(async (eventName, data) => {
|
||||||
if (eventName === ViewControllerManagerEvent.PanelResized) {
|
if (eventName === WebAppEvent.PanelResized) {
|
||||||
const { panel, collapsed } = data as PanelResizedData
|
const { panel, collapsed } = data as PanelResizedData
|
||||||
let appClass = ''
|
let appClass = ''
|
||||||
if (panel === PANEL_NAME_NOTES && collapsed) {
|
if (panel === PANEL_NAME_NOTES && collapsed) {
|
||||||
@@ -131,7 +131,7 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
|||||||
appClass += ' collapsed-navigation'
|
appClass += ' collapsed-navigation'
|
||||||
}
|
}
|
||||||
setAppClass(appClass)
|
setAppClass(appClass)
|
||||||
} else if (eventName === ViewControllerManagerEvent.WindowDidFocus) {
|
} else if (eventName === WebAppEvent.WindowDidFocus) {
|
||||||
if (!(await application.isLocked())) {
|
if (!(await application.isLocked())) {
|
||||||
application.sync.sync().catch(console.error)
|
application.sync.sync().catch(console.error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
|
|||||||
|
|
||||||
const transactions: TransactionalMutation[] = []
|
const transactions: TransactionalMutation[] = []
|
||||||
|
|
||||||
await application.getViewControllerManager().contentListController.insertCurrentIfTemplate()
|
await application.getViewControllerManager().itemListController.insertCurrentIfTemplate()
|
||||||
|
|
||||||
if (note.locked) {
|
if (note.locked) {
|
||||||
application.alertService.alert(STRING_EDIT_LOCKED_ATTEMPT).catch(console.error)
|
application.alertService.alert(STRING_EDIT_LOCKED_ATTEMPT).catch(console.error)
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ const ContentList: FunctionComponent<Props> = ({
|
|||||||
selectedItems,
|
selectedItems,
|
||||||
paginate,
|
paginate,
|
||||||
}) => {
|
}) => {
|
||||||
const { selectPreviousItem, selectNextItem } = viewControllerManager.contentListController
|
const { selectPreviousItem, selectNextItem } = viewControllerManager.itemListController
|
||||||
const { hideTags, hideDate, hideNotePreview, hideEditorIcon } =
|
const { hideTags, hideDate, hideNotePreview, hideEditorIcon } =
|
||||||
viewControllerManager.contentListController.webDisplayOptions
|
viewControllerManager.itemListController.webDisplayOptions
|
||||||
const { sortBy } = viewControllerManager.contentListController.displayOptions
|
const { sortBy } = viewControllerManager.itemListController.displayOptions
|
||||||
|
|
||||||
const onScroll: UIEventHandler = useCallback(
|
const onScroll: UIEventHandler = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const ContentListItem: FunctionComponent<AbstractListItemProps> = (props) => {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const tags = props.viewControllerManager.getItemTags(props.item)
|
const tags = props.application.getItemTags(props.item)
|
||||||
|
|
||||||
const isNavigatingOnlyTag = selectedTag instanceof SNTag && tags.length === 1
|
const isNavigatingOnlyTag = selectedTag instanceof SNTag && tags.length === 1
|
||||||
if (isNavigatingOnlyTag) {
|
if (isNavigatingOnlyTag) {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const ContentListView: FunctionComponent<Props> = ({ application, viewController
|
|||||||
paginate,
|
paginate,
|
||||||
panelWidth,
|
panelWidth,
|
||||||
createNewNote,
|
createNewNote,
|
||||||
} = viewControllerManager.contentListController
|
} = viewControllerManager.itemListController
|
||||||
|
|
||||||
const { selectedItems } = viewControllerManager.selectionController
|
const { selectedItems } = viewControllerManager.selectionController
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ const ContentListView: FunctionComponent<Props> = ({ application, viewController
|
|||||||
(width, _lastLeft, _isMaxWidth, isCollapsed) => {
|
(width, _lastLeft, _isMaxWidth, isCollapsed) => {
|
||||||
application.setPreference(PrefKey.NotesPanelWidth, width).catch(console.error)
|
application.setPreference(PrefKey.NotesPanelWidth, width).catch(console.error)
|
||||||
viewControllerManager.noteTagsController.reloadTagsContainerMaxWidth()
|
viewControllerManager.noteTagsController.reloadTagsContainerMaxWidth()
|
||||||
viewControllerManager.panelDidResize(PANEL_NAME_NOTES, isCollapsed)
|
application.publishPanelDidResizeEvent(PANEL_NAME_NOTES, isCollapsed)
|
||||||
},
|
},
|
||||||
[viewControllerManager, application],
|
[viewControllerManager, application],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { WebAppEvent, WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
|
import { WebAppEvent } from '@/Application/WebAppEvent'
|
||||||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||||
import { PureComponent } from '@/Components/Abstract/PureComponent'
|
import { PureComponent } from '@/Components/Abstract/PureComponent'
|
||||||
import { destroyAllObjectProperties, preventRefreshing } from '@/Utils'
|
import { destroyAllObjectProperties, preventRefreshing } from '@/Utils'
|
||||||
@@ -12,7 +13,6 @@ import {
|
|||||||
} from '@/Constants/Strings'
|
} from '@/Constants/Strings'
|
||||||
import { alertDialog, confirmDialog } from '@/Services/AlertService'
|
import { alertDialog, confirmDialog } from '@/Services/AlertService'
|
||||||
import AccountMenu from '@/Components/AccountMenu/AccountMenu'
|
import AccountMenu from '@/Components/AccountMenu/AccountMenu'
|
||||||
import { ViewControllerManagerEvent } from '@/Services/ViewControllerManager'
|
|
||||||
import Icon from '@/Components/Icon/Icon'
|
import Icon from '@/Components/Icon/Icon'
|
||||||
import QuickSettingsMenu from '@/Components/QuickSettingsMenu/QuickSettingsMenu'
|
import QuickSettingsMenu from '@/Components/QuickSettingsMenu/QuickSettingsMenu'
|
||||||
import SyncResolutionMenu from '@/Components/SyncResolutionMenu/SyncResolutionMenu'
|
import SyncResolutionMenu from '@/Components/SyncResolutionMenu/SyncResolutionMenu'
|
||||||
@@ -64,9 +64,34 @@ class Footer extends PureComponent<Props, State> {
|
|||||||
showQuickSettingsMenu: false,
|
showQuickSettingsMenu: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.webEventListenerDestroyer = props.application.addWebEventObserver((event) => {
|
this.webEventListenerDestroyer = props.application.addWebEventObserver((event, data) => {
|
||||||
if (event === WebAppEvent.NewUpdateAvailable) {
|
const statusService = this.application.status
|
||||||
this.onNewUpdateAvailable()
|
|
||||||
|
switch (event) {
|
||||||
|
case WebAppEvent.NewUpdateAvailable:
|
||||||
|
this.onNewUpdateAvailable()
|
||||||
|
break
|
||||||
|
case WebAppEvent.EditorFocused:
|
||||||
|
if ((data as any).eventSource === EditorEventSource.UserInteraction) {
|
||||||
|
this.closeAccountMenu()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case WebAppEvent.BeganBackupDownload:
|
||||||
|
statusService.setMessage('Saving local backup…')
|
||||||
|
break
|
||||||
|
case WebAppEvent.EndedBackupDownload: {
|
||||||
|
const successMessage = 'Successfully saved backup.'
|
||||||
|
const errorMessage = 'Unable to save local backup.'
|
||||||
|
statusService.setMessage((data as any).success ? successMessage : errorMessage)
|
||||||
|
|
||||||
|
const twoSeconds = 2000
|
||||||
|
setTimeout(() => {
|
||||||
|
if (statusService.message === successMessage || statusService.message === errorMessage) {
|
||||||
|
statusService.setMessage('')
|
||||||
|
}
|
||||||
|
}, twoSeconds)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -133,33 +158,6 @@ class Footer extends PureComponent<Props, State> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override onViewControllerManagerEvent(eventName: ViewControllerManagerEvent, data: any) {
|
|
||||||
const statusService = this.application.status
|
|
||||||
switch (eventName) {
|
|
||||||
case ViewControllerManagerEvent.EditorFocused:
|
|
||||||
if (data.eventSource === EditorEventSource.UserInteraction) {
|
|
||||||
this.closeAccountMenu()
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case ViewControllerManagerEvent.BeganBackupDownload:
|
|
||||||
statusService.setMessage('Saving local backup…')
|
|
||||||
break
|
|
||||||
case ViewControllerManagerEvent.EndedBackupDownload: {
|
|
||||||
const successMessage = 'Successfully saved backup.'
|
|
||||||
const errorMessage = 'Unable to save local backup.'
|
|
||||||
statusService.setMessage(data.success ? successMessage : errorMessage)
|
|
||||||
|
|
||||||
const twoSeconds = 2000
|
|
||||||
setTimeout(() => {
|
|
||||||
if (statusService.message === successMessage || statusService.message === errorMessage) {
|
|
||||||
statusService.setMessage('')
|
|
||||||
}
|
|
||||||
}, twoSeconds)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override async onAppKeyChange() {
|
override async onAppKeyChange() {
|
||||||
super.onAppKeyChange().catch(console.error)
|
super.onAppKeyChange().catch(console.error)
|
||||||
this.reloadPasscodeStatus().catch(console.error)
|
this.reloadPasscodeStatus().catch(console.error)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
|
|||||||
(width, _lastLeft, _isMaxWidth, isCollapsed) => {
|
(width, _lastLeft, _isMaxWidth, isCollapsed) => {
|
||||||
application.setPreference(PrefKey.TagsPanelWidth, width).catch(console.error)
|
application.setPreference(PrefKey.TagsPanelWidth, width).catch(console.error)
|
||||||
viewControllerManager.noteTagsController.reloadTagsContainerMaxWidth()
|
viewControllerManager.noteTagsController.reloadTagsContainerMaxWidth()
|
||||||
viewControllerManager.panelDidResize(PANEL_NAME_NAVIGATION, isCollapsed)
|
application.publishPanelDidResizeEvent(PANEL_NAME_NAVIGATION, isCollapsed)
|
||||||
},
|
},
|
||||||
[application, viewControllerManager],
|
[application, viewControllerManager],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
} from './TransactionFunctions'
|
} from './TransactionFunctions'
|
||||||
import { reloadFont } from './FontFunctions'
|
import { reloadFont } from './FontFunctions'
|
||||||
import { NoteViewProps } from './NoteViewProps'
|
import { NoteViewProps } from './NoteViewProps'
|
||||||
|
import { WebAppEvent } from '@/Application/WebAppEvent'
|
||||||
|
|
||||||
const MINIMUM_STATUS_DURATION = 400
|
const MINIMUM_STATUS_DURATION = 400
|
||||||
const TEXTAREA_DEBOUNCE = 100
|
const TEXTAREA_DEBOUNCE = 100
|
||||||
@@ -588,7 +589,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
|||||||
|
|
||||||
onContentFocus = () => {
|
onContentFocus = () => {
|
||||||
if (this.lastEditorFocusEventSource) {
|
if (this.lastEditorFocusEventSource) {
|
||||||
this.application.getViewControllerManager().editorDidFocus(this.lastEditorFocusEventSource)
|
this.application.notifyWebEvent(WebAppEvent.EditorFocused, { eventSource: this.lastEditorFocusEventSource })
|
||||||
}
|
}
|
||||||
this.lastEditorFocusEventSource = undefined
|
this.lastEditorFocusEventSource = undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const Defaults: FunctionComponent<Props> = ({ application }) => {
|
|||||||
|
|
||||||
const toggleSpellcheck = () => {
|
const toggleSpellcheck = () => {
|
||||||
setSpellcheck(!spellcheck)
|
setSpellcheck(!spellcheck)
|
||||||
application.getViewControllerManager().toggleGlobalSpellcheck().catch(console.error)
|
application.toggleGlobalSpellcheck().catch(console.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import Icon from '@/Components/Icon/Icon'
|
import Icon from '@/Components/Icon/Icon'
|
||||||
import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
||||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||||
import { TagsController } from '@/Controllers/Navigation/TagsController'
|
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
import { FunctionComponent } from 'react'
|
import { FunctionComponent } from 'react'
|
||||||
import { useDrop } from 'react-dnd'
|
import { useDrop } from 'react-dnd'
|
||||||
import { DropItem, DropProps, ItemTypes } from './DragNDrop'
|
import { DropItem, DropProps, ItemTypes } from './DragNDrop'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
tagsState: TagsController
|
tagsState: NavigationController
|
||||||
featuresState: FeaturesController
|
featuresState: FeaturesController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Icon from '@/Components/Icon/Icon'
|
import Icon from '@/Components/Icon/Icon'
|
||||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||||
import { TagsController } from '@/Controllers/Navigation/TagsController'
|
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||||
import '@reach/tooltip/styles.css'
|
import '@reach/tooltip/styles.css'
|
||||||
import { SmartView, SystemViewId, IconType, isSystemView } from '@standardnotes/snjs'
|
import { SmartView, SystemViewId, IconType, isSystemView } from '@standardnotes/snjs'
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
view: SmartView
|
view: SmartView
|
||||||
tagsState: TagsController
|
tagsState: NavigationController
|
||||||
features: FeaturesController
|
features: FeaturesController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { TAG_FOLDERS_FEATURE_NAME } from '@/Constants/Constants'
|
|||||||
import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
||||||
import { KeyboardKey } from '@/Services/IOService'
|
import { KeyboardKey } from '@/Services/IOService'
|
||||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||||
import { TagsController } from '@/Controllers/Navigation/TagsController'
|
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||||
import '@reach/tooltip/styles.css'
|
import '@reach/tooltip/styles.css'
|
||||||
import { SNTag } from '@standardnotes/snjs'
|
import { SNTag } from '@standardnotes/snjs'
|
||||||
import { computed } from 'mobx'
|
import { computed } from 'mobx'
|
||||||
@@ -23,7 +23,7 @@ import { DropItem, DropProps, ItemTypes } from './DragNDrop'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
tag: SNTag
|
tag: SNTag
|
||||||
tagsState: TagsController
|
tagsState: NavigationController
|
||||||
features: FeaturesController
|
features: FeaturesController
|
||||||
level: number
|
level: number
|
||||||
onContextMenu: (tag: SNTag, posX: number, posY: number) => void
|
onContextMenu: (tag: SNTag, posX: number, posY: number) => void
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import IconButton from '@/Components/Button/IconButton'
|
import IconButton from '@/Components/Button/IconButton'
|
||||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||||
import { TagsController } from '@/Controllers/Navigation/TagsController'
|
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
import { FunctionComponent } from 'react'
|
import { FunctionComponent } from 'react'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
tags: TagsController
|
tags: NavigationController
|
||||||
features: FeaturesController
|
features: FeaturesController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 { WebApplication } from '../../Application/Application'
|
||||||
|
import { Disposer } from '@/Types/Disposer'
|
||||||
|
|
||||||
export abstract class AbstractViewController {
|
export abstract class AbstractViewController {
|
||||||
dealloced = false
|
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.dealloced = true
|
||||||
;(this.application as unknown) = undefined
|
;(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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { AbstractViewController } from './AbstractViewController'
|
export function isControllerDealloced(controller: { dealloced: boolean }): boolean {
|
||||||
|
return controller.dealloced == undefined || controller.dealloced === true
|
||||||
export function isControllerDealloced(state: AbstractViewController): boolean {
|
|
||||||
return state.dealloced == undefined || state.dealloced === true
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { destroyAllObjectProperties, isDev } from '@/Utils'
|
import { destroyAllObjectProperties, isDev } from '@/Utils'
|
||||||
import { action, computed, makeObservable, observable, runInAction } from 'mobx'
|
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 { WebApplication } from '@/Application/Application'
|
||||||
import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane'
|
import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane'
|
||||||
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||||
@@ -21,15 +21,16 @@ export class AccountMenuController extends AbstractViewController {
|
|||||||
shouldAnimateCloseMenu = false
|
shouldAnimateCloseMenu = false
|
||||||
currentPane = AccountMenuPane.GeneralMenu
|
currentPane = AccountMenuPane.GeneralMenu
|
||||||
|
|
||||||
override deinit(source: DeinitSource) {
|
override deinit() {
|
||||||
super.deinit(source)
|
super.deinit()
|
||||||
;(this.notesAndTags as unknown) = undefined
|
;(this.notesAndTags as unknown) = undefined
|
||||||
|
|
||||||
destroyAllObjectProperties(this)
|
destroyAllObjectProperties(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(application: WebApplication, private appEventListeners: (() => void)[]) {
|
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||||
super(application)
|
super(application, eventBus)
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
show: observable,
|
show: observable,
|
||||||
signingOut: observable,
|
signingOut: observable,
|
||||||
@@ -60,12 +61,7 @@ export class AccountMenuController extends AbstractViewController {
|
|||||||
notesAndTagsCount: computed,
|
notesAndTagsCount: computed,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.addAppLaunchedEventObserver()
|
this.disposers.push(
|
||||||
this.streamNotesAndTags()
|
|
||||||
}
|
|
||||||
|
|
||||||
addAppLaunchedEventObserver = (): void => {
|
|
||||||
this.appEventListeners.push(
|
|
||||||
this.application.addEventObserver(async () => {
|
this.application.addEventObserver(async () => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
if (isDev && window.devAccountServer) {
|
if (isDev && window.devAccountServer) {
|
||||||
@@ -77,10 +73,8 @@ export class AccountMenuController extends AbstractViewController {
|
|||||||
})
|
})
|
||||||
}, ApplicationEvent.Launched),
|
}, ApplicationEvent.Launched),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
streamNotesAndTags = (): void => {
|
this.disposers.push(
|
||||||
this.appEventListeners.push(
|
|
||||||
this.application.streamItems([ContentType.Note, ContentType.Tag], () => {
|
this.application.streamItems([ContentType.Note, ContentType.Tag], () => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.notesAndTags = this.application.items.getItems([ContentType.Note, ContentType.Tag])
|
this.notesAndTags = this.application.items.getItems([ContentType.Note, ContentType.Tag])
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export enum CrossControllerEvent {
|
||||||
|
TagChanged = 'TagChanged',
|
||||||
|
ActiveEditorChanged = 'ActiveEditorChanged',
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
import { destroyAllObjectProperties } from '@/Utils'
|
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 { action, makeObservable, observable, runInAction, when } from 'mobx'
|
||||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||||
|
|
||||||
@@ -10,8 +10,8 @@ export class FeaturesController extends AbstractViewController {
|
|||||||
hasFiles: boolean
|
hasFiles: boolean
|
||||||
premiumAlertFeatureName: string | undefined
|
premiumAlertFeatureName: string | undefined
|
||||||
|
|
||||||
override deinit(source: DeinitSource) {
|
override deinit() {
|
||||||
super.deinit(source)
|
super.deinit()
|
||||||
;(this.showPremiumAlert as unknown) = undefined
|
;(this.showPremiumAlert as unknown) = undefined
|
||||||
;(this.closePremiumAlert as unknown) = undefined
|
;(this.closePremiumAlert as unknown) = undefined
|
||||||
;(this.hasFolders as unknown) = undefined
|
;(this.hasFolders as unknown) = undefined
|
||||||
@@ -22,8 +22,8 @@ export class FeaturesController extends AbstractViewController {
|
|||||||
destroyAllObjectProperties(this)
|
destroyAllObjectProperties(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(application: WebApplication, appObservers: (() => void)[]) {
|
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||||
super(application)
|
super(application, eventBus)
|
||||||
|
|
||||||
this.hasFolders = this.isEntitledToFolders()
|
this.hasFolders = this.isEntitledToFolders()
|
||||||
this.hasSmartViews = this.isEntitledToSmartViews()
|
this.hasSmartViews = this.isEntitledToSmartViews()
|
||||||
@@ -43,7 +43,7 @@ export class FeaturesController extends AbstractViewController {
|
|||||||
this.showPremiumAlert = this.showPremiumAlert.bind(this)
|
this.showPremiumAlert = this.showPremiumAlert.bind(this)
|
||||||
this.closePremiumAlert = this.closePremiumAlert.bind(this)
|
this.closePremiumAlert = this.closePremiumAlert.bind(this)
|
||||||
|
|
||||||
appObservers.push(
|
this.disposers.push(
|
||||||
application.addEventObserver(async (event) => {
|
application.addEventObserver(async (event) => {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case ApplicationEvent.FeaturesUpdated:
|
case ApplicationEvent.FeaturesUpdated:
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { FilePreviewModalController } from './FilePreviewModalController'
|
||||||
import {
|
import {
|
||||||
PopoverFileItemAction,
|
PopoverFileItemAction,
|
||||||
PopoverFileItemActionType,
|
PopoverFileItemActionType,
|
||||||
@@ -13,12 +14,13 @@ import {
|
|||||||
ClassicFileSaver,
|
ClassicFileSaver,
|
||||||
parseFileName,
|
parseFileName,
|
||||||
} from '@standardnotes/filepicker'
|
} 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 { addToast, dismissToast, ToastType, updateToast } from '@standardnotes/stylekit'
|
||||||
import { action, computed, makeObservable, observable, reaction } from 'mobx'
|
import { action, computed, makeObservable, observable, reaction } from 'mobx'
|
||||||
import { WebApplication } from '../Application/Application'
|
import { WebApplication } from '../Application/Application'
|
||||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||||
import { ViewControllerManager } from '../Services/ViewControllerManager/ViewControllerManager'
|
import { NotesController } from './NotesController'
|
||||||
|
import { SelectedItemsController } from './SelectedItemsController'
|
||||||
|
|
||||||
const UnprotectedFileActions = [PopoverFileItemActionType.ToggleFileProtection]
|
const UnprotectedFileActions = [PopoverFileItemActionType.ToggleFileProtection]
|
||||||
const NonMutatingFileActions = [PopoverFileItemActionType.DownloadFile, PopoverFileItemActionType.PreviewFile]
|
const NonMutatingFileActions = [PopoverFileItemActionType.DownloadFile, PopoverFileItemActionType.PreviewFile]
|
||||||
@@ -31,12 +33,21 @@ export class FilesController extends AbstractViewController {
|
|||||||
showFileContextMenu = false
|
showFileContextMenu = false
|
||||||
fileContextMenuLocation: FileContextMenuLocation = { x: 0, y: 0 }
|
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(
|
constructor(
|
||||||
application: WebApplication,
|
application: WebApplication,
|
||||||
override viewControllerManager: ViewControllerManager,
|
private notesController: NotesController,
|
||||||
appObservers: (() => void)[],
|
private selectionController: SelectedItemsController,
|
||||||
|
private filePreviewModalController: FilePreviewModalController,
|
||||||
|
eventBus: InternalEventBus,
|
||||||
) {
|
) {
|
||||||
super(application, viewControllerManager)
|
super(application, eventBus)
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
allFiles: observable,
|
allFiles: observable,
|
||||||
@@ -52,13 +63,16 @@ export class FilesController extends AbstractViewController {
|
|||||||
setFileContextMenuLocation: action,
|
setFileContextMenuLocation: action,
|
||||||
})
|
})
|
||||||
|
|
||||||
appObservers.push(
|
this.disposers.push(
|
||||||
application.streamItems(ContentType.File, () => {
|
application.streamItems(ContentType.File, () => {
|
||||||
this.reloadAllFiles()
|
this.reloadAllFiles()
|
||||||
this.reloadAttachedFiles()
|
this.reloadAttachedFiles()
|
||||||
}),
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
reaction(
|
reaction(
|
||||||
() => viewControllerManager.notesController.selectedNotes,
|
() => notesController.selectedNotes,
|
||||||
() => {
|
() => {
|
||||||
this.reloadAttachedFiles()
|
this.reloadAttachedFiles()
|
||||||
},
|
},
|
||||||
@@ -67,7 +81,7 @@ export class FilesController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get selectedFiles(): FileItem[] {
|
get selectedFiles(): FileItem[] {
|
||||||
return this.viewControllerManager.selectionController.getSelectedItems<FileItem>(ContentType.File)
|
return this.selectionController.getSelectedItems<FileItem>(ContentType.File)
|
||||||
}
|
}
|
||||||
|
|
||||||
setShowFileContextMenu = (enabled: boolean) => {
|
setShowFileContextMenu = (enabled: boolean) => {
|
||||||
@@ -83,7 +97,7 @@ export class FilesController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reloadAttachedFiles = () => {
|
reloadAttachedFiles = () => {
|
||||||
const note = this.viewControllerManager.notesController.firstSelectedNote
|
const note = this.notesController.firstSelectedNote
|
||||||
if (note) {
|
if (note) {
|
||||||
this.attachedFiles = this.application.items.getFilesForNote(note)
|
this.attachedFiles = this.application.items.getFilesForNote(note)
|
||||||
}
|
}
|
||||||
@@ -109,7 +123,7 @@ export class FilesController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
attachFileToNote = async (file: FileItem) => {
|
attachFileToNote = async (file: FileItem) => {
|
||||||
const note = this.viewControllerManager.notesController.firstSelectedNote
|
const note = this.notesController.firstSelectedNote
|
||||||
if (!note) {
|
if (!note) {
|
||||||
addToast({
|
addToast({
|
||||||
type: ToastType.Error,
|
type: ToastType.Error,
|
||||||
@@ -122,7 +136,7 @@ export class FilesController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
detachFileFromNote = async (file: FileItem) => {
|
detachFileFromNote = async (file: FileItem) => {
|
||||||
const note = this.viewControllerManager.notesController.firstSelectedNote
|
const note = this.notesController.firstSelectedNote
|
||||||
if (!note) {
|
if (!note) {
|
||||||
addToast({
|
addToast({
|
||||||
type: ToastType.Error,
|
type: ToastType.Error,
|
||||||
@@ -197,7 +211,7 @@ export class FilesController extends AbstractViewController {
|
|||||||
await this.renameFile(file, action.payload.name)
|
await this.renameFile(file, action.payload.name)
|
||||||
break
|
break
|
||||||
case PopoverFileItemActionType.PreviewFile:
|
case PopoverFileItemActionType.PreviewFile:
|
||||||
this.viewControllerManager.filePreviewModalController.activate(
|
this.filePreviewModalController.activate(
|
||||||
file,
|
file,
|
||||||
currentTab === PopoverTabs.AllFiles ? this.allFiles : this.attachedFiles,
|
currentTab === PopoverTabs.AllFiles ? this.allFiles : this.attachedFiles,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
ApplicationEvent,
|
ApplicationEvent,
|
||||||
CollectionSort,
|
CollectionSort,
|
||||||
ContentType,
|
ContentType,
|
||||||
DeinitSource,
|
|
||||||
findInArray,
|
findInArray,
|
||||||
NoteViewController,
|
NoteViewController,
|
||||||
PrefKey,
|
PrefKey,
|
||||||
@@ -13,13 +12,21 @@ import {
|
|||||||
SNTag,
|
SNTag,
|
||||||
SystemViewId,
|
SystemViewId,
|
||||||
DisplayOptions,
|
DisplayOptions,
|
||||||
|
InternalEventBus,
|
||||||
|
InternalEventHandlerInterface,
|
||||||
|
InternalEventInterface,
|
||||||
} from '@standardnotes/snjs'
|
} from '@standardnotes/snjs'
|
||||||
import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'
|
import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'
|
||||||
import { WebApplication } from '../../Application/Application'
|
import { WebApplication } from '../../Application/Application'
|
||||||
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||||
import { ViewControllerManager } from '../../Services/ViewControllerManager/ViewControllerManager'
|
|
||||||
import { ViewControllerManagerEvent } from '../../Services/ViewControllerManager/ViewControllerManagerEvent'
|
|
||||||
import { WebDisplayOptions } from './WebDisplayOptions'
|
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 MinNoteCellHeight = 51.0
|
||||||
const DefaultListNumNotes = 20
|
const DefaultListNumNotes = 20
|
||||||
@@ -27,7 +34,7 @@ const ElementIdSearchBar = 'search-bar'
|
|||||||
const ElementIdScrollContainer = 'notes-scrollable'
|
const ElementIdScrollContainer = 'notes-scrollable'
|
||||||
const SupportsFileSelectionState = false
|
const SupportsFileSelectionState = false
|
||||||
|
|
||||||
export class ItemListController extends AbstractViewController {
|
export class ItemListController extends AbstractViewController implements InternalEventHandlerInterface {
|
||||||
completedFullSync = false
|
completedFullSync = false
|
||||||
noteFilterText = ''
|
noteFilterText = ''
|
||||||
notes: SNNote[] = []
|
notes: SNNote[] = []
|
||||||
@@ -55,11 +62,16 @@ export class ItemListController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
private reloadItemsPromise?: Promise<unknown>
|
private reloadItemsPromise?: Promise<unknown>
|
||||||
|
|
||||||
override deinit(source: DeinitSource) {
|
override deinit() {
|
||||||
super.deinit(source)
|
super.deinit()
|
||||||
;(this.noteFilterText as unknown) = undefined
|
;(this.noteFilterText as unknown) = undefined
|
||||||
;(this.notes as unknown) = undefined
|
;(this.notes as unknown) = undefined
|
||||||
;(this.renderedItems 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
|
;(window.onresize as unknown) = undefined
|
||||||
|
|
||||||
destroyAllObjectProperties(this)
|
destroyAllObjectProperties(this)
|
||||||
@@ -67,18 +79,27 @@ export class ItemListController extends AbstractViewController {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
application: WebApplication,
|
application: WebApplication,
|
||||||
override viewControllerManager: ViewControllerManager,
|
private navigationController: NavigationController,
|
||||||
appObservers: (() => void)[],
|
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()
|
this.resetPagination()
|
||||||
|
|
||||||
appObservers.push(
|
this.disposers.push(
|
||||||
application.streamItems<SNNote>(ContentType.Note, () => {
|
application.streamItems<SNNote>(ContentType.Note, () => {
|
||||||
void this.reloadItems()
|
void this.reloadItems()
|
||||||
}),
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
application.streamItems<SNTag>([ContentType.Tag], async ({ changed, inserted }) => {
|
application.streamItems<SNTag>([ContentType.Tag], async ({ changed, inserted }) => {
|
||||||
const tags = [...changed, ...inserted]
|
const tags = [...changed, ...inserted]
|
||||||
|
|
||||||
@@ -87,28 +108,34 @@ export class ItemListController extends AbstractViewController {
|
|||||||
|
|
||||||
void this.reloadItems()
|
void this.reloadItems()
|
||||||
|
|
||||||
if (
|
if (this.navigationController.selected && findInArray(tags, 'uuid', this.navigationController.selected.uuid)) {
|
||||||
viewControllerManager.navigationController.selected &&
|
|
||||||
findInArray(tags, 'uuid', viewControllerManager.navigationController.selected.uuid)
|
|
||||||
) {
|
|
||||||
/** Tag title could have changed */
|
/** Tag title could have changed */
|
||||||
this.reloadPanelTitle()
|
this.reloadPanelTitle()
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
application.addEventObserver(async () => {
|
application.addEventObserver(async () => {
|
||||||
void this.reloadPreferences()
|
void this.reloadPreferences()
|
||||||
}, ApplicationEvent.PreferencesChanged),
|
}, ApplicationEvent.PreferencesChanged),
|
||||||
|
)
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
application.addEventObserver(async () => {
|
application.addEventObserver(async () => {
|
||||||
this.application.noteControllerGroup.closeAllNoteControllers()
|
this.application.noteControllerGroup.closeAllNoteControllers()
|
||||||
void this.selectFirstItem()
|
void this.selectFirstItem()
|
||||||
this.setCompletedFullSync(false)
|
this.setCompletedFullSync(false)
|
||||||
}, ApplicationEvent.SignedIn),
|
}, ApplicationEvent.SignedIn),
|
||||||
|
)
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
application.addEventObserver(async () => {
|
application.addEventObserver(async () => {
|
||||||
void this.reloadItems().then(() => {
|
void this.reloadItems().then(() => {
|
||||||
if (
|
if (
|
||||||
this.notes.length === 0 &&
|
this.notes.length === 0 &&
|
||||||
viewControllerManager.navigationController.selected instanceof SmartView &&
|
this.navigationController.selected instanceof SmartView &&
|
||||||
viewControllerManager.navigationController.selected.uuid === SystemViewId.AllNotes &&
|
this.navigationController.selected.uuid === SystemViewId.AllNotes &&
|
||||||
this.noteFilterText === '' &&
|
this.noteFilterText === '' &&
|
||||||
!this.getActiveNoteController()
|
!this.getActiveNoteController()
|
||||||
) {
|
) {
|
||||||
@@ -117,28 +144,28 @@ export class ItemListController extends AbstractViewController {
|
|||||||
})
|
})
|
||||||
this.setCompletedFullSync(true)
|
this.setCompletedFullSync(true)
|
||||||
}, ApplicationEvent.CompletedFullSync),
|
}, ApplicationEvent.CompletedFullSync),
|
||||||
|
)
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
|
application.addWebEventObserver((webEvent) => {
|
||||||
|
if (webEvent === WebAppEvent.EditorFocused) {
|
||||||
|
this.setShowDisplayOptionsMenu(false)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
reaction(
|
reaction(
|
||||||
() => [
|
() => [
|
||||||
viewControllerManager.searchOptionsController.includeProtectedContents,
|
this.searchOptionsController.includeProtectedContents,
|
||||||
viewControllerManager.searchOptionsController.includeArchived,
|
this.searchOptionsController.includeArchived,
|
||||||
viewControllerManager.searchOptionsController.includeTrashed,
|
this.searchOptionsController.includeTrashed,
|
||||||
],
|
],
|
||||||
() => {
|
() => {
|
||||||
this.reloadNotesDisplayOptions()
|
this.reloadNotesDisplayOptions()
|
||||||
void this.reloadItems()
|
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, {
|
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 {
|
public getActiveNoteController(): NoteViewController | undefined {
|
||||||
return this.application.noteControllerGroup.activeNoteViewController
|
return this.application.noteControllerGroup.activeNoteViewController
|
||||||
}
|
}
|
||||||
@@ -200,8 +235,8 @@ export class ItemListController extends AbstractViewController {
|
|||||||
if (this.isFiltering) {
|
if (this.isFiltering) {
|
||||||
const resultCount = this.notes.length
|
const resultCount = this.notes.length
|
||||||
title = `${resultCount} search results`
|
title = `${resultCount} search results`
|
||||||
} else if (this.viewControllerManager.navigationController.selected) {
|
} else if (this.navigationController.selected) {
|
||||||
title = `${this.viewControllerManager.navigationController.selected.title}`
|
title = `${this.navigationController.selected.title}`
|
||||||
}
|
}
|
||||||
|
|
||||||
this.panelTitle = title
|
this.panelTitle = title
|
||||||
@@ -218,7 +253,7 @@ export class ItemListController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async performReloadItems() {
|
private async performReloadItems() {
|
||||||
const tag = this.viewControllerManager.navigationController.selected
|
const tag = this.navigationController.selected
|
||||||
if (!tag) {
|
if (!tag) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -241,17 +276,16 @@ export class ItemListController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async recomputeSelectionAfterItemsReload() {
|
private async recomputeSelectionAfterItemsReload() {
|
||||||
const viewControllerManager = this.viewControllerManager
|
|
||||||
const activeController = this.getActiveNoteController()
|
const activeController = this.getActiveNoteController()
|
||||||
const activeNote = activeController?.note
|
const activeNote = activeController?.note
|
||||||
const isSearching = this.noteFilterText.length > 0
|
const isSearching = this.noteFilterText.length > 0
|
||||||
const hasMultipleItemsSelected = viewControllerManager.selectionController.selectedItemsCount >= 2
|
const hasMultipleItemsSelected = this.selectionController.selectedItemsCount >= 2
|
||||||
|
|
||||||
if (hasMultipleItemsSelected) {
|
if (hasMultipleItemsSelected) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedItem = Object.values(viewControllerManager.selectionController.selectedItems)[0]
|
const selectedItem = Object.values(this.selectionController.selectedItems)[0]
|
||||||
|
|
||||||
const isSelectedItemFile =
|
const isSelectedItemFile =
|
||||||
this.items.includes(selectedItem) && selectedItem && selectedItem.content_type === ContentType.File
|
this.items.includes(selectedItem) && selectedItem && selectedItem.content_type === ContentType.File
|
||||||
@@ -280,25 +314,25 @@ export class ItemListController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const showTrashedNotes =
|
const showTrashedNotes =
|
||||||
(viewControllerManager.navigationController.selected instanceof SmartView &&
|
(this.navigationController.selected instanceof SmartView &&
|
||||||
viewControllerManager.navigationController.selected?.uuid === SystemViewId.TrashedNotes) ||
|
this.navigationController.selected?.uuid === SystemViewId.TrashedNotes) ||
|
||||||
viewControllerManager?.searchOptionsController.includeTrashed
|
this.searchOptionsController.includeTrashed
|
||||||
|
|
||||||
const showArchivedNotes =
|
const showArchivedNotes =
|
||||||
(viewControllerManager.navigationController.selected instanceof SmartView &&
|
(this.navigationController.selected instanceof SmartView &&
|
||||||
viewControllerManager.navigationController.selected.uuid === SystemViewId.ArchivedNotes) ||
|
this.navigationController.selected.uuid === SystemViewId.ArchivedNotes) ||
|
||||||
viewControllerManager.searchOptionsController.includeArchived ||
|
this.searchOptionsController.includeArchived ||
|
||||||
this.application.getPreference(PrefKey.NotesShowArchived, false)
|
this.application.getPreference(PrefKey.NotesShowArchived, false)
|
||||||
|
|
||||||
if ((activeNote.trashed && !showTrashedNotes) || (activeNote.archived && !showArchivedNotes)) {
|
if ((activeNote.trashed && !showTrashedNotes) || (activeNote.archived && !showArchivedNotes)) {
|
||||||
await this.selectNextItemOrCreateNewNote()
|
await this.selectNextItemOrCreateNewNote()
|
||||||
} else if (!this.viewControllerManager.selectionController.selectedItems[activeNote.uuid]) {
|
} else if (!this.selectionController.selectedItems[activeNote.uuid]) {
|
||||||
await this.viewControllerManager.selectionController.selectItem(activeNote.uuid).catch(console.error)
|
await this.selectionController.selectItem(activeNote.uuid).catch(console.error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadNotesDisplayOptions = () => {
|
reloadNotesDisplayOptions = () => {
|
||||||
const tag = this.viewControllerManager.navigationController.selected
|
const tag = this.navigationController.selected
|
||||||
|
|
||||||
const searchText = this.noteFilterText.toLowerCase()
|
const searchText = this.noteFilterText.toLowerCase()
|
||||||
const isSearching = searchText.length
|
const isSearching = searchText.length
|
||||||
@@ -306,8 +340,8 @@ export class ItemListController extends AbstractViewController {
|
|||||||
let includeTrashed: boolean
|
let includeTrashed: boolean
|
||||||
|
|
||||||
if (isSearching) {
|
if (isSearching) {
|
||||||
includeArchived = this.viewControllerManager.searchOptionsController.includeArchived
|
includeArchived = this.searchOptionsController.includeArchived
|
||||||
includeTrashed = this.viewControllerManager.searchOptionsController.includeTrashed
|
includeTrashed = this.searchOptionsController.includeTrashed
|
||||||
} else {
|
} else {
|
||||||
includeArchived = this.displayOptions.includeArchived ?? false
|
includeArchived = this.displayOptions.includeArchived ?? false
|
||||||
includeTrashed = this.displayOptions.includeTrashed ?? false
|
includeTrashed = this.displayOptions.includeTrashed ?? false
|
||||||
@@ -324,7 +358,7 @@ export class ItemListController extends AbstractViewController {
|
|||||||
includeProtected: this.displayOptions.includeProtected,
|
includeProtected: this.displayOptions.includeProtected,
|
||||||
searchQuery: {
|
searchQuery: {
|
||||||
query: searchText,
|
query: searchText,
|
||||||
includeProtectedNoteText: this.viewControllerManager.searchOptionsController.includeProtectedContents,
|
includeProtectedNoteText: this.searchOptionsController.includeProtectedContents,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,13 +421,10 @@ export class ItemListController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createNewNote = async () => {
|
createNewNote = async () => {
|
||||||
this.viewControllerManager.notesController.unselectNotes()
|
this.notesController.unselectNotes()
|
||||||
|
|
||||||
if (
|
if (this.navigationController.isInSmartView() && !this.navigationController.isInHomeView()) {
|
||||||
this.viewControllerManager.navigationController.isInSmartView() &&
|
await this.navigationController.selectHomeNavigationView()
|
||||||
!this.viewControllerManager.navigationController.isInHomeView()
|
|
||||||
) {
|
|
||||||
await this.viewControllerManager.navigationController.selectHomeNavigationView()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = `Note ${this.notes.length + 1}`
|
let title = `Note ${this.notes.length + 1}`
|
||||||
@@ -401,16 +432,13 @@ export class ItemListController extends AbstractViewController {
|
|||||||
title = this.noteFilterText
|
title = this.noteFilterText
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.viewControllerManager.notesController.createNewNoteController(title)
|
await this.notesController.createNewNoteController(title)
|
||||||
|
|
||||||
this.viewControllerManager.noteTagsController.reloadTagsForCurrentNote()
|
this.noteTagsController.reloadTagsForCurrentNote()
|
||||||
}
|
}
|
||||||
|
|
||||||
createPlaceholderNote = () => {
|
createPlaceholderNote = () => {
|
||||||
if (
|
if (this.navigationController.isInSmartView() && !this.navigationController.isInHomeView()) {
|
||||||
this.viewControllerManager.navigationController.isInSmartView() &&
|
|
||||||
!this.viewControllerManager.navigationController.isInHomeView()
|
|
||||||
) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,7 +515,7 @@ export class ItemListController extends AbstractViewController {
|
|||||||
},
|
},
|
||||||
{ userTriggered = false, scrollIntoView = true },
|
{ userTriggered = false, scrollIntoView = true },
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
await this.viewControllerManager.selectionController.selectItem(item.uuid, userTriggered)
|
await this.selectionController.selectItem(item.uuid, userTriggered)
|
||||||
|
|
||||||
if (scrollIntoView) {
|
if (scrollIntoView) {
|
||||||
const itemElement = document.getElementById(item.uuid)
|
const itemElement = document.getElementById(item.uuid)
|
||||||
@@ -514,7 +542,7 @@ export class ItemListController extends AbstractViewController {
|
|||||||
const displayableItems = this.items
|
const displayableItems = this.items
|
||||||
|
|
||||||
const currentIndex = displayableItems.findIndex((candidate) => {
|
const currentIndex = displayableItems.findIndex((candidate) => {
|
||||||
return candidate.uuid === this.viewControllerManager.selectionController.lastSelectedItem?.uuid
|
return candidate.uuid === this.selectionController.lastSelectedItem?.uuid
|
||||||
})
|
})
|
||||||
|
|
||||||
let nextIndex = currentIndex + 1
|
let nextIndex = currentIndex + 1
|
||||||
@@ -554,11 +582,11 @@ export class ItemListController extends AbstractViewController {
|
|||||||
selectPreviousItem = () => {
|
selectPreviousItem = () => {
|
||||||
const displayableItems = this.items
|
const displayableItems = this.items
|
||||||
|
|
||||||
if (!this.viewControllerManager.selectionController.lastSelectedItem) {
|
if (!this.selectionController.lastSelectedItem) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentIndex = displayableItems.indexOf(this.viewControllerManager.selectionController.lastSelectedItem)
|
const currentIndex = displayableItems.indexOf(this.selectionController.lastSelectedItem)
|
||||||
|
|
||||||
let previousIndex = currentIndex - 1
|
let previousIndex = currentIndex - 1
|
||||||
|
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ import {
|
|||||||
UuidString,
|
UuidString,
|
||||||
isSystemView,
|
isSystemView,
|
||||||
FindItem,
|
FindItem,
|
||||||
DeinitSource,
|
|
||||||
SystemViewId,
|
SystemViewId,
|
||||||
|
InternalEventBus,
|
||||||
|
InternalEventPublishStrategy,
|
||||||
} from '@standardnotes/snjs'
|
} from '@standardnotes/snjs'
|
||||||
import { action, computed, makeAutoObservable, makeObservable, observable, runInAction } from 'mobx'
|
import { action, computed, makeAutoObservable, makeObservable, observable, runInAction } from 'mobx'
|
||||||
import { WebApplication } from '../../Application/Application'
|
import { WebApplication } from '../../Application/Application'
|
||||||
import { FeaturesController } from '../FeaturesController'
|
import { FeaturesController } from '../FeaturesController'
|
||||||
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||||
import { destroyAllObjectProperties } from '@/Utils'
|
import { destroyAllObjectProperties } from '@/Utils'
|
||||||
import { ViewControllerManager } from '../../Services/ViewControllerManager/ViewControllerManager'
|
|
||||||
import { ViewControllerManagerEvent } from '../../Services/ViewControllerManager/ViewControllerManagerEvent'
|
|
||||||
import { isValidFutureSiblings, rootTags, tagSiblings } from './Utils'
|
import { isValidFutureSiblings, rootTags, tagSiblings } from './Utils'
|
||||||
import { AnyTag } from './AnyTagType'
|
import { AnyTag } from './AnyTagType'
|
||||||
|
import { CrossControllerEvent } from '../CrossControllerEvent'
|
||||||
|
|
||||||
export class TagsController extends AbstractViewController {
|
export class NavigationController extends AbstractViewController {
|
||||||
tags: SNTag[] = []
|
tags: SNTag[] = []
|
||||||
smartViews: SmartView[] = []
|
smartViews: SmartView[] = []
|
||||||
allNotesCount_ = 0
|
allNotesCount_ = 0
|
||||||
@@ -43,13 +43,8 @@ export class TagsController extends AbstractViewController {
|
|||||||
|
|
||||||
private readonly tagsCountsState: TagsCountsState
|
private readonly tagsCountsState: TagsCountsState
|
||||||
|
|
||||||
constructor(
|
constructor(application: WebApplication, private featuresController: FeaturesController, eventBus: InternalEventBus) {
|
||||||
application: WebApplication,
|
super(application, eventBus)
|
||||||
override viewControllerManager: ViewControllerManager,
|
|
||||||
appEventListeners: (() => void)[],
|
|
||||||
private features: FeaturesController,
|
|
||||||
) {
|
|
||||||
super(application)
|
|
||||||
|
|
||||||
this.tagsCountsState = new TagsCountsState(this.application)
|
this.tagsCountsState = new TagsCountsState(this.application)
|
||||||
|
|
||||||
@@ -99,7 +94,7 @@ export class TagsController extends AbstractViewController {
|
|||||||
setContextMenuMaxHeight: action,
|
setContextMenuMaxHeight: action,
|
||||||
})
|
})
|
||||||
|
|
||||||
appEventListeners.push(
|
this.disposers.push(
|
||||||
this.application.streamItems([ContentType.Tag, ContentType.SmartView], ({ changed, removed }) => {
|
this.application.streamItems([ContentType.Tag, ContentType.SmartView], ({ changed, removed }) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.tags = this.application.items.getDisplayableTags()
|
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) => {
|
this.application.items.addNoteCountChangeObserver((tagUuid) => {
|
||||||
if (!tagUuid) {
|
if (!tagUuid) {
|
||||||
this.setAllNotesCount(this.application.items.allCountableNotesCount())
|
this.setAllNotesCount(this.application.items.allCountableNotesCount())
|
||||||
@@ -145,15 +140,16 @@ export class TagsController extends AbstractViewController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override deinit(source: DeinitSource) {
|
override deinit() {
|
||||||
super.deinit(source)
|
super.deinit()
|
||||||
;(this.features as unknown) = undefined
|
;(this.featuresController as unknown) = undefined
|
||||||
;(this.tags as unknown) = undefined
|
;(this.tags as unknown) = undefined
|
||||||
;(this.smartViews as unknown) = undefined
|
;(this.smartViews as unknown) = undefined
|
||||||
;(this.selected_ as unknown) = undefined
|
;(this.selected_ as unknown) = undefined
|
||||||
;(this.previouslySelected_ as unknown) = undefined
|
;(this.previouslySelected_ as unknown) = undefined
|
||||||
;(this.editing_ as unknown) = undefined
|
;(this.editing_ as unknown) = undefined
|
||||||
;(this.addingSubtagTo as unknown) = undefined
|
;(this.addingSubtagTo as unknown) = undefined
|
||||||
|
;(this.featuresController as unknown) = undefined
|
||||||
|
|
||||||
destroyAllObjectProperties(this)
|
destroyAllObjectProperties(this)
|
||||||
}
|
}
|
||||||
@@ -369,10 +365,13 @@ export class TagsController extends AbstractViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.viewControllerManager.notifyEvent(ViewControllerManagerEvent.TagChanged, {
|
await this.eventBus.publishSync(
|
||||||
tag,
|
{
|
||||||
previousTag: this.previouslySelected_,
|
type: CrossControllerEvent.TagChanged,
|
||||||
})
|
payload: { tag, previousTag: this.previouslySelected_ },
|
||||||
|
},
|
||||||
|
InternalEventPublishStrategy.SEQUENCE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async selectHomeNavigationView(): Promise<void> {
|
public async selectHomeNavigationView(): Promise<void> {
|
||||||
@@ -473,8 +472,8 @@ export class TagsController extends AbstractViewController {
|
|||||||
const isSmartViewTitle = this.application.items.isSmartViewTitle(newTitle)
|
const isSmartViewTitle = this.application.items.isSmartViewTitle(newTitle)
|
||||||
|
|
||||||
if (isSmartViewTitle) {
|
if (isSmartViewTitle) {
|
||||||
if (!this.features.hasSmartViews) {
|
if (!this.featuresController.hasSmartViews) {
|
||||||
await this.features.showPremiumAlert(SMART_TAGS_FEATURE_NAME)
|
await this.featuresController.showPremiumAlert(SMART_TAGS_FEATURE_NAME)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { storage, StorageKey } from '@/Services/LocalStorage'
|
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 { runInAction, makeObservable, observable, action } from 'mobx'
|
||||||
import { WebApplication } from '../Application/Application'
|
import { WebApplication } from '../Application/Application'
|
||||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||||
@@ -7,17 +7,20 @@ import { AbstractViewController } from './Abstract/AbstractViewController'
|
|||||||
export class NoAccountWarningController extends AbstractViewController {
|
export class NoAccountWarningController extends AbstractViewController {
|
||||||
show: boolean
|
show: boolean
|
||||||
|
|
||||||
constructor(application: WebApplication, appObservers: (() => void)[]) {
|
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||||
super(application)
|
super(application, eventBus)
|
||||||
|
|
||||||
this.show = application.hasAccount() ? false : storage.get(StorageKey.ShowNoAccountWarning) ?? true
|
this.show = application.hasAccount() ? false : storage.get(StorageKey.ShowNoAccountWarning) ?? true
|
||||||
|
|
||||||
appObservers.push(
|
this.disposers.push(
|
||||||
application.addEventObserver(async () => {
|
application.addEventObserver(async () => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.show = false
|
this.show = false
|
||||||
})
|
})
|
||||||
}, ApplicationEvent.SignedIn),
|
}, ApplicationEvent.SignedIn),
|
||||||
|
)
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
application.addEventObserver(async () => {
|
application.addEventObserver(async () => {
|
||||||
if (application.hasAccount()) {
|
if (application.hasAccount()) {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
import { ElementIds } from '@/Constants/ElementIDs'
|
import { ElementIds } from '@/Constants/ElementIDs'
|
||||||
import { destroyAllObjectProperties } from '@/Utils'
|
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 { action, computed, makeObservable, observable } from 'mobx'
|
||||||
import { WebApplication } from '../Application/Application'
|
import { WebApplication } from '../Application/Application'
|
||||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||||
import { ViewControllerManager } from '../Services/ViewControllerManager/ViewControllerManager'
|
import { ItemListController } from './ItemList/ItemListController'
|
||||||
|
|
||||||
export class NoteTagsController extends AbstractViewController {
|
export class NoteTagsController extends AbstractViewController {
|
||||||
autocompleteInputFocused = false
|
autocompleteInputFocused = false
|
||||||
@@ -16,21 +24,19 @@ export class NoteTagsController extends AbstractViewController {
|
|||||||
tags: SNTag[] = []
|
tags: SNTag[] = []
|
||||||
tagsContainerMaxWidth: number | 'auto' = 0
|
tagsContainerMaxWidth: number | 'auto' = 0
|
||||||
addNoteToParentFolders: boolean
|
addNoteToParentFolders: boolean
|
||||||
|
private itemListController!: ItemListController
|
||||||
|
|
||||||
override deinit(source: DeinitSource) {
|
override deinit() {
|
||||||
super.deinit(source)
|
super.deinit()
|
||||||
;(this.tags as unknown) = undefined
|
;(this.tags as unknown) = undefined
|
||||||
;(this.autocompleteTagResults as unknown) = undefined
|
;(this.autocompleteTagResults as unknown) = undefined
|
||||||
|
;(this.itemListController as unknown) = undefined
|
||||||
|
|
||||||
destroyAllObjectProperties(this)
|
destroyAllObjectProperties(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||||
application: WebApplication,
|
super(application, eventBus)
|
||||||
override viewControllerManager: ViewControllerManager,
|
|
||||||
appEventListeners: (() => void)[],
|
|
||||||
) {
|
|
||||||
super(application, viewControllerManager)
|
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
autocompleteInputFocused: observable,
|
autocompleteInputFocused: observable,
|
||||||
@@ -55,13 +61,17 @@ export class NoteTagsController extends AbstractViewController {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.addNoteToParentFolders = application.getPreference(PrefKey.NoteAddToParentFolders, true)
|
this.addNoteToParentFolders = application.getPreference(PrefKey.NoteAddToParentFolders, true)
|
||||||
|
}
|
||||||
|
|
||||||
appEventListeners.push(
|
public setServicestPostConstruction(itemListController: ItemListController) {
|
||||||
application.streamItems(ContentType.Tag, () => {
|
this.itemListController = itemListController
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
|
this.application.streamItems(ContentType.Tag, () => {
|
||||||
this.reloadTagsForCurrentNote()
|
this.reloadTagsForCurrentNote()
|
||||||
}),
|
}),
|
||||||
application.addSingleEventObserver(ApplicationEvent.PreferencesChanged, async () => {
|
this.application.addSingleEventObserver(ApplicationEvent.PreferencesChanged, async () => {
|
||||||
this.addNoteToParentFolders = application.getPreference(PrefKey.NoteAddToParentFolders, true)
|
this.addNoteToParentFolders = this.application.getPreference(PrefKey.NoteAddToParentFolders, true)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -151,7 +161,7 @@ export class NoteTagsController extends AbstractViewController {
|
|||||||
searchActiveNoteAutocompleteTags(): void {
|
searchActiveNoteAutocompleteTags(): void {
|
||||||
const newResults = this.application.items.searchTags(
|
const newResults = this.application.items.searchTags(
|
||||||
this.autocompleteSearchQuery,
|
this.autocompleteSearchQuery,
|
||||||
this.viewControllerManager.contentListController.activeControllerNote,
|
this.itemListController.activeControllerNote,
|
||||||
)
|
)
|
||||||
this.setAutocompleteTagResults(newResults)
|
this.setAutocompleteTagResults(newResults)
|
||||||
}
|
}
|
||||||
@@ -161,7 +171,7 @@ export class NoteTagsController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reloadTagsForCurrentNote(): void {
|
reloadTagsForCurrentNote(): void {
|
||||||
const activeNote = this.viewControllerManager.contentListController.activeControllerNote
|
const activeNote = this.itemListController.activeControllerNote
|
||||||
|
|
||||||
if (activeNote) {
|
if (activeNote) {
|
||||||
const tags = this.application.items.getSortedTagsForNote(activeNote)
|
const tags = this.application.items.getSortedTagsForNote(activeNote)
|
||||||
@@ -177,7 +187,7 @@ export class NoteTagsController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addTagToActiveNote(tag: SNTag): Promise<void> {
|
async addTagToActiveNote(tag: SNTag): Promise<void> {
|
||||||
const activeNote = this.viewControllerManager.contentListController.activeControllerNote
|
const activeNote = this.itemListController.activeControllerNote
|
||||||
|
|
||||||
if (activeNote) {
|
if (activeNote) {
|
||||||
await this.application.items.addTagToNote(activeNote, tag, this.addNoteToParentFolders)
|
await this.application.items.addTagToNote(activeNote, tag, this.addNoteToParentFolders)
|
||||||
@@ -187,7 +197,7 @@ export class NoteTagsController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeTagFromActiveNote(tag: SNTag): Promise<void> {
|
async removeTagFromActiveNote(tag: SNTag): Promise<void> {
|
||||||
const activeNote = this.viewControllerManager.contentListController.activeControllerNote
|
const activeNote = this.itemListController.activeControllerNote
|
||||||
|
|
||||||
if (activeNote) {
|
if (activeNote) {
|
||||||
await this.application.mutator.changeItem(tag, (mutator) => {
|
await this.application.mutator.changeItem(tag, (mutator) => {
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ import { destroyAllObjectProperties } from '@/Utils'
|
|||||||
import { confirmDialog } from '@/Services/AlertService'
|
import { confirmDialog } from '@/Services/AlertService'
|
||||||
import { StringEmptyTrash, Strings, StringUtils } from '@/Constants/Strings'
|
import { StringEmptyTrash, Strings, StringUtils } from '@/Constants/Strings'
|
||||||
import { MENU_MARGIN_FROM_APP_BORDER } from '@/Constants/Constants'
|
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 { makeObservable, observable, action, computed, runInAction } from 'mobx'
|
||||||
import { WebApplication } from '../Application/Application'
|
import { WebApplication } from '../Application/Application'
|
||||||
import { ViewControllerManager } from '../Services/ViewControllerManager/ViewControllerManager'
|
|
||||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
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 {
|
export class NotesController extends AbstractViewController {
|
||||||
lastSelectedNote: SNNote | undefined
|
lastSelectedNote: SNNote | undefined
|
||||||
@@ -19,22 +23,27 @@ export class NotesController extends AbstractViewController {
|
|||||||
contextMenuMaxHeight: number | 'auto' = 'auto'
|
contextMenuMaxHeight: number | 'auto' = 'auto'
|
||||||
showProtectedWarning = false
|
showProtectedWarning = false
|
||||||
showRevisionHistoryModal = false
|
showRevisionHistoryModal = false
|
||||||
|
private itemListController!: ItemListController
|
||||||
|
|
||||||
override deinit(source: DeinitSource) {
|
override deinit() {
|
||||||
super.deinit(source)
|
super.deinit()
|
||||||
;(this.lastSelectedNote as unknown) = undefined
|
;(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)
|
destroyAllObjectProperties(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
application: WebApplication,
|
application: WebApplication,
|
||||||
public override viewControllerManager: ViewControllerManager,
|
private selectionController: SelectedItemsController,
|
||||||
private onActiveEditorChanged: () => Promise<void>,
|
private noteTagsController: NoteTagsController,
|
||||||
appEventListeners: (() => void)[],
|
private navigationController: NavigationController,
|
||||||
|
eventBus: InternalEventBus,
|
||||||
) {
|
) {
|
||||||
super(application, viewControllerManager)
|
super(application, eventBus)
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
contextMenuOpen: observable,
|
contextMenuOpen: observable,
|
||||||
@@ -55,17 +64,21 @@ export class NotesController extends AbstractViewController {
|
|||||||
setShowRevisionHistoryModal: action,
|
setShowRevisionHistoryModal: action,
|
||||||
unselectNotes: action,
|
unselectNotes: action,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
appEventListeners.push(
|
public setServicestPostConstruction(itemListController: ItemListController) {
|
||||||
application.streamItems<SNNote>(ContentType.Note, ({ changed, inserted, removed }) => {
|
this.itemListController = itemListController
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
|
this.application.streamItems<SNNote>(ContentType.Note, ({ changed, inserted, removed }) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
for (const removedNote of removed) {
|
for (const removedNote of removed) {
|
||||||
this.viewControllerManager.selectionController.deselectItem(removedNote)
|
this.selectionController.deselectItem(removedNote)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const note of [...changed, ...inserted]) {
|
for (const note of [...changed, ...inserted]) {
|
||||||
if (this.viewControllerManager.selectionController.isItemSelected(note)) {
|
if (this.selectionController.isItemSelected(note)) {
|
||||||
this.viewControllerManager.selectionController.updateReferenceOfSelectedItem(note)
|
this.selectionController.updateReferenceOfSelectedItem(note)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -80,7 +93,7 @@ export class NotesController extends AbstractViewController {
|
|||||||
|
|
||||||
for (const selectedId of selectedUuids) {
|
for (const selectedId of selectedUuids) {
|
||||||
if (!activeNoteUuids.includes(selectedId)) {
|
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[] {
|
public get selectedNotes(): SNNote[] {
|
||||||
return this.viewControllerManager.selectionController.getSelectedItems<SNNote>(ContentType.Note)
|
return this.selectionController.getSelectedItems<SNNote>(ContentType.Note)
|
||||||
}
|
}
|
||||||
|
|
||||||
get firstSelectedNote(): SNNote | undefined {
|
get firstSelectedNote(): SNNote | undefined {
|
||||||
@@ -108,7 +121,7 @@ export class NotesController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async openNote(noteUuid: string): Promise<void> {
|
async openNote(noteUuid: string): Promise<void> {
|
||||||
if (this.viewControllerManager.contentListController.activeControllerNote?.uuid === noteUuid) {
|
if (this.itemListController.activeControllerNote?.uuid === noteUuid) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,13 +133,13 @@ export class NotesController extends AbstractViewController {
|
|||||||
|
|
||||||
await this.application.noteControllerGroup.createNoteController(noteUuid)
|
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) {
|
async createNewNoteController(title?: string) {
|
||||||
const selectedTag = this.viewControllerManager.navigationController.selected
|
const selectedTag = this.navigationController.selected
|
||||||
|
|
||||||
const activeRegularTagUuid = selectedTag && selectedTag instanceof SNTag ? selectedTag.uuid : undefined
|
const activeRegularTagUuid = selectedTag && selectedTag instanceof SNTag ? selectedTag.uuid : undefined
|
||||||
|
|
||||||
@@ -262,7 +275,7 @@ export class NotesController extends AbstractViewController {
|
|||||||
if (permanently) {
|
if (permanently) {
|
||||||
for (const note of this.getSelectedNotesList()) {
|
for (const note of this.getSelectedNotesList()) {
|
||||||
await this.application.mutator.deleteItem(note)
|
await this.application.mutator.deleteItem(note)
|
||||||
this.viewControllerManager.selectionController.deselectItem(note)
|
this.selectionController.deselectItem(note)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.changeSelectedNotes((mutator) => {
|
await this.changeSelectedNotes((mutator) => {
|
||||||
@@ -294,7 +307,7 @@ export class NotesController extends AbstractViewController {
|
|||||||
})
|
})
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.viewControllerManager.selectionController.setSelectedItems({})
|
this.selectionController.setSelectedItems({})
|
||||||
this.contextMenuOpen = false
|
this.contextMenuOpen = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -311,11 +324,11 @@ export class NotesController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unselectNotes(): void {
|
unselectNotes(): void {
|
||||||
this.viewControllerManager.selectionController.setSelectedItems({})
|
this.selectionController.setSelectedItems({})
|
||||||
}
|
}
|
||||||
|
|
||||||
getSpellcheckStateForNote(note: SNNote) {
|
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) {
|
async toggleGlobalSpellcheckForNote(note: SNNote) {
|
||||||
@@ -358,7 +371,7 @@ export class NotesController extends AbstractViewController {
|
|||||||
isTagInSelectedNotes(tag: SNTag): boolean {
|
isTagInSelectedNotes(tag: SNTag): boolean {
|
||||||
const selectedNotes = this.getSelectedNotesList()
|
const selectedNotes = this.getSelectedNotesList()
|
||||||
return selectedNotes.every((note) =>
|
return selectedNotes.every((note) =>
|
||||||
this.viewControllerManager.getItemTags(note).find((noteTag) => noteTag.uuid === tag.uuid),
|
this.application.getItemTags(note).find((noteTag) => noteTag.uuid === tag.uuid),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { loadPurchaseFlowUrl } from '@/Components/PurchaseFlow/PurchaseFlowFunctions'
|
import { loadPurchaseFlowUrl } from '@/Components/PurchaseFlow/PurchaseFlowFunctions'
|
||||||
|
import { InternalEventBus } from '@standardnotes/snjs'
|
||||||
import { action, makeObservable, observable } from 'mobx'
|
import { action, makeObservable, observable } from 'mobx'
|
||||||
import { WebApplication } from '../../Application/Application'
|
import { WebApplication } from '../../Application/Application'
|
||||||
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||||
@@ -8,8 +9,8 @@ export class PurchaseFlowController extends AbstractViewController {
|
|||||||
isOpen = false
|
isOpen = false
|
||||||
currentPane = PurchaseFlowPane.CreateAccount
|
currentPane = PurchaseFlowPane.CreateAccount
|
||||||
|
|
||||||
constructor(application: WebApplication) {
|
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||||
super(application)
|
super(application, eventBus)
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
isOpen: observable,
|
isOpen: observable,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ApplicationEvent } from '@standardnotes/snjs'
|
import { ApplicationEvent, InternalEventBus } from '@standardnotes/snjs'
|
||||||
import { makeObservable, observable, action, runInAction } from 'mobx'
|
import { makeObservable, observable, action, runInAction } from 'mobx'
|
||||||
import { WebApplication } from '../Application/Application'
|
import { WebApplication } from '../Application/Application'
|
||||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||||
@@ -8,8 +8,8 @@ export class SearchOptionsController extends AbstractViewController {
|
|||||||
includeArchived = false
|
includeArchived = false
|
||||||
includeTrashed = false
|
includeTrashed = false
|
||||||
|
|
||||||
constructor(application: WebApplication, appObservers: (() => void)[]) {
|
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||||
super(application)
|
super(application, eventBus)
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
includeProtectedContents: observable,
|
includeProtectedContents: observable,
|
||||||
@@ -22,7 +22,7 @@ export class SearchOptionsController extends AbstractViewController {
|
|||||||
refreshIncludeProtectedContents: action,
|
refreshIncludeProtectedContents: action,
|
||||||
})
|
})
|
||||||
|
|
||||||
appObservers.push(
|
this.disposers.push(
|
||||||
this.application.addEventObserver(async () => {
|
this.application.addEventObserver(async () => {
|
||||||
this.refreshIncludeProtectedContents()
|
this.refreshIncludeProtectedContents()
|
||||||
}, ApplicationEvent.UnprotectedSessionBegan),
|
}, ApplicationEvent.UnprotectedSessionBegan),
|
||||||
|
|||||||
@@ -1,22 +1,35 @@
|
|||||||
import { ListableContentItem } from '@/Components/ContentListView/Types/ListableContentItem'
|
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 { action, computed, makeObservable, observable, runInAction } from 'mobx'
|
||||||
import { WebApplication } from '../Application/Application'
|
import { WebApplication } from '../Application/Application'
|
||||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
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>
|
type SelectedItems = Record<UuidString, ListableContentItem>
|
||||||
|
|
||||||
export class SelectedItemsController extends AbstractViewController {
|
export class SelectedItemsController extends AbstractViewController {
|
||||||
lastSelectedItem: ListableContentItem | undefined
|
lastSelectedItem: ListableContentItem | undefined
|
||||||
selectedItems: SelectedItems = {}
|
selectedItems: SelectedItems = {}
|
||||||
|
private itemListController!: ItemListController
|
||||||
|
private notesController!: NotesController
|
||||||
|
|
||||||
constructor(
|
override deinit(): void {
|
||||||
application: WebApplication,
|
super.deinit()
|
||||||
override viewControllerManager: ViewControllerManager,
|
;(this.itemListController as unknown) = undefined
|
||||||
appObservers: (() => void)[],
|
;(this.notesController as unknown) = undefined
|
||||||
) {
|
}
|
||||||
super(application)
|
|
||||||
|
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||||
|
super(application, eventBus)
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
selectedItems: observable,
|
selectedItems: observable,
|
||||||
@@ -26,9 +39,14 @@ export class SelectedItemsController extends AbstractViewController {
|
|||||||
selectItem: action,
|
selectItem: action,
|
||||||
setSelectedItems: action,
|
setSelectedItems: action,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
appObservers.push(
|
public setServicestPostConstruction(itemListController: ItemListController, notesController: NotesController) {
|
||||||
application.streamItems<SNNote | FileItem>(
|
this.itemListController = itemListController
|
||||||
|
this.notesController = notesController
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
|
this.application.streamItems<SNNote | FileItem>(
|
||||||
[ContentType.Note, ContentType.File],
|
[ContentType.Note, ContentType.File],
|
||||||
({ changed, inserted, removed }) => {
|
({ changed, inserted, removed }) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
@@ -82,7 +100,7 @@ export class SelectedItemsController extends AbstractViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private selectItemsRange = async (selectedItem: ListableContentItem): Promise<void> => {
|
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 lastSelectedItemIndex = items.findIndex((item) => item.uuid == this.lastSelectedItem?.uuid)
|
||||||
const selectedItemIndex = items.findIndex((item) => item.uuid == selectedItem.uuid)
|
const selectedItemIndex = items.findIndex((item) => item.uuid == selectedItem.uuid)
|
||||||
@@ -171,7 +189,7 @@ export class SelectedItemsController extends AbstractViewController {
|
|||||||
if (this.selectedItemsCount === 1) {
|
if (this.selectedItemsCount === 1) {
|
||||||
const item = Object.values(this.selectedItems)[0]
|
const item = Object.values(this.selectedItems)[0]
|
||||||
if (item.content_type === ContentType.Note) {
|
if (item.content_type === ContentType.Note) {
|
||||||
await this.viewControllerManager.notesController.openNote(item.uuid)
|
await this.notesController.openNote(item.uuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
ApplicationEvent,
|
ApplicationEvent,
|
||||||
ClientDisplayableError,
|
ClientDisplayableError,
|
||||||
convertTimestampToMilliseconds,
|
convertTimestampToMilliseconds,
|
||||||
DeinitSource,
|
InternalEventBus,
|
||||||
} from '@standardnotes/snjs'
|
} from '@standardnotes/snjs'
|
||||||
import { action, computed, makeObservable, observable } from 'mobx'
|
import { action, computed, makeObservable, observable } from 'mobx'
|
||||||
import { WebApplication } from '../../Application/Application'
|
import { WebApplication } from '../../Application/Application'
|
||||||
@@ -15,16 +15,16 @@ export class SubscriptionController extends AbstractViewController {
|
|||||||
userSubscription: Subscription | undefined = undefined
|
userSubscription: Subscription | undefined = undefined
|
||||||
availableSubscriptions: AvailableSubscriptions | undefined = undefined
|
availableSubscriptions: AvailableSubscriptions | undefined = undefined
|
||||||
|
|
||||||
override deinit(source: DeinitSource) {
|
override deinit() {
|
||||||
super.deinit(source)
|
super.deinit()
|
||||||
;(this.userSubscription as unknown) = undefined
|
;(this.userSubscription as unknown) = undefined
|
||||||
;(this.availableSubscriptions as unknown) = undefined
|
;(this.availableSubscriptions as unknown) = undefined
|
||||||
|
|
||||||
destroyAllObjectProperties(this)
|
destroyAllObjectProperties(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(application: WebApplication, appObservers: (() => void)[]) {
|
constructor(application: WebApplication, eventBus: InternalEventBus) {
|
||||||
super(application)
|
super(application, eventBus)
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
userSubscription: observable,
|
userSubscription: observable,
|
||||||
@@ -39,15 +39,21 @@ export class SubscriptionController extends AbstractViewController {
|
|||||||
setAvailableSubscriptions: action,
|
setAvailableSubscriptions: action,
|
||||||
})
|
})
|
||||||
|
|
||||||
appObservers.push(
|
this.disposers.push(
|
||||||
application.addEventObserver(async () => {
|
application.addEventObserver(async () => {
|
||||||
if (application.hasAccount()) {
|
if (application.hasAccount()) {
|
||||||
this.getSubscriptionInfo().catch(console.error)
|
this.getSubscriptionInfo().catch(console.error)
|
||||||
}
|
}
|
||||||
}, ApplicationEvent.Launched),
|
}, ApplicationEvent.Launched),
|
||||||
|
)
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
application.addEventObserver(async () => {
|
application.addEventObserver(async () => {
|
||||||
this.getSubscriptionInfo().catch(console.error)
|
this.getSubscriptionInfo().catch(console.error)
|
||||||
}, ApplicationEvent.SignedIn),
|
}, ApplicationEvent.SignedIn),
|
||||||
|
)
|
||||||
|
|
||||||
|
this.disposers.push(
|
||||||
application.addEventObserver(async () => {
|
application.addEventObserver(async () => {
|
||||||
this.getSubscriptionInfo().catch(console.error)
|
this.getSubscriptionInfo().catch(console.error)
|
||||||
}, ApplicationEvent.UserRolesChanged),
|
}, ApplicationEvent.UserRolesChanged),
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import {
|
|||||||
DesktopClientRequiresWebMethods,
|
DesktopClientRequiresWebMethods,
|
||||||
DesktopDeviceInterface,
|
DesktopDeviceInterface,
|
||||||
} from '@standardnotes/snjs'
|
} from '@standardnotes/snjs'
|
||||||
import { WebAppEvent, WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
|
import { WebAppEvent } from '@/Application/WebAppEvent'
|
||||||
|
|
||||||
export class DesktopManager
|
export class DesktopManager
|
||||||
extends ApplicationService
|
extends ApplicationService
|
||||||
@@ -106,11 +107,11 @@ export class DesktopManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
windowGainedFocus(): void {
|
windowGainedFocus(): void {
|
||||||
this.webApplication.notifyWebEvent(WebAppEvent.DesktopWindowGainedFocus)
|
this.webApplication.notifyWebEvent(WebAppEvent.WindowDidFocus)
|
||||||
}
|
}
|
||||||
|
|
||||||
windowLostFocus(): void {
|
windowLostFocus(): void {
|
||||||
this.webApplication.notifyWebEvent(WebAppEvent.DesktopWindowLostFocus)
|
this.webApplication.notifyWebEvent(WebAppEvent.WindowDidBlur)
|
||||||
}
|
}
|
||||||
|
|
||||||
async onComponentInstallationComplete(componentData: DecryptedTransferPayload<ComponentContent>, error: unknown) {
|
async onComponentInstallationComplete(componentData: DecryptedTransferPayload<ComponentContent>, error: unknown) {
|
||||||
@@ -155,10 +156,10 @@ export class DesktopManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
didBeginBackup() {
|
didBeginBackup() {
|
||||||
this.webApplication.getViewControllerManager().beganBackupDownload()
|
this.webApplication.notifyWebEvent(WebAppEvent.BeganBackupDownload)
|
||||||
}
|
}
|
||||||
|
|
||||||
didFinishBackup(success: boolean) {
|
didFinishBackup(success: boolean) {
|
||||||
this.webApplication.getViewControllerManager().endedBackupDownload(success)
|
this.webApplication.notifyWebEvent(WebAppEvent.EndedBackupDownload, { success })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
204
app/assets/javascripts/Services/ViewControllerManager.ts
Normal file
204
app/assets/javascripts/Services/ViewControllerManager.ts
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
import { storage, StorageKey } from '@/Services/LocalStorage'
|
||||||
|
import { WebApplication } from '@/Application/Application'
|
||||||
|
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
|
||||||
|
import { destroyAllObjectProperties } from '@/Utils'
|
||||||
|
import { ApplicationEvent, DeinitSource, WebOrDesktopDeviceInterface, InternalEventBus } from '@standardnotes/snjs'
|
||||||
|
import { action, makeObservable, observable } from 'mobx'
|
||||||
|
import { ActionsMenuController } from '../Controllers/ActionsMenuController'
|
||||||
|
import { FeaturesController } from '../Controllers/FeaturesController'
|
||||||
|
import { FilesController } from '../Controllers/FilesController'
|
||||||
|
import { NotesController } from '../Controllers/NotesController'
|
||||||
|
import { ItemListController } from '../Controllers/ItemList/ItemListController'
|
||||||
|
import { NoteTagsController } from '../Controllers/NoteTagsController'
|
||||||
|
import { NoAccountWarningController } from '../Controllers/NoAccountWarningController'
|
||||||
|
import { PreferencesController } from '../Controllers/PreferencesController'
|
||||||
|
import { PurchaseFlowController } from '../Controllers/PurchaseFlow/PurchaseFlowController'
|
||||||
|
import { QuickSettingsController } from '../Controllers/QuickSettingsController'
|
||||||
|
import { SearchOptionsController } from '../Controllers/SearchOptionsController'
|
||||||
|
import { SubscriptionController } from '../Controllers/Subscription/SubscriptionController'
|
||||||
|
import { SyncStatusController } from '../Controllers/SyncStatusController'
|
||||||
|
import { NavigationController } from '../Controllers/Navigation/NavigationController'
|
||||||
|
import { FilePreviewModalController } from '../Controllers/FilePreviewModalController'
|
||||||
|
import { SelectedItemsController } from '../Controllers/SelectedItemsController'
|
||||||
|
|
||||||
|
export class ViewControllerManager {
|
||||||
|
readonly enableUnfinishedFeatures: boolean = window?.enabledUnfinishedFeatures
|
||||||
|
|
||||||
|
private unsubAppEventObserver!: () => void
|
||||||
|
showBetaWarning: boolean
|
||||||
|
public dealloced = false
|
||||||
|
|
||||||
|
readonly accountMenuController: AccountMenuController
|
||||||
|
readonly actionsMenuController = new ActionsMenuController()
|
||||||
|
readonly featuresController: FeaturesController
|
||||||
|
readonly filePreviewModalController = new FilePreviewModalController()
|
||||||
|
readonly filesController: FilesController
|
||||||
|
readonly noAccountWarningController: NoAccountWarningController
|
||||||
|
readonly notesController: NotesController
|
||||||
|
readonly itemListController: ItemListController
|
||||||
|
readonly noteTagsController: NoteTagsController
|
||||||
|
readonly preferencesController = new PreferencesController()
|
||||||
|
readonly purchaseFlowController: PurchaseFlowController
|
||||||
|
readonly quickSettingsMenuController = new QuickSettingsController()
|
||||||
|
readonly searchOptionsController: SearchOptionsController
|
||||||
|
readonly subscriptionController: SubscriptionController
|
||||||
|
readonly syncStatusController = new SyncStatusController()
|
||||||
|
readonly navigationController: NavigationController
|
||||||
|
readonly selectionController: SelectedItemsController
|
||||||
|
|
||||||
|
public isSessionsModalVisible = false
|
||||||
|
|
||||||
|
private appEventObserverRemovers: (() => void)[] = []
|
||||||
|
private eventBus: InternalEventBus
|
||||||
|
|
||||||
|
constructor(public application: WebApplication, private device: WebOrDesktopDeviceInterface) {
|
||||||
|
this.eventBus = new InternalEventBus()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
this.notesController = new NotesController(
|
||||||
|
application,
|
||||||
|
this.selectionController,
|
||||||
|
this.noteTagsController,
|
||||||
|
this.navigationController,
|
||||||
|
this.eventBus,
|
||||||
|
)
|
||||||
|
|
||||||
|
this.searchOptionsController = new SearchOptionsController(application, this.eventBus)
|
||||||
|
|
||||||
|
this.itemListController = new ItemListController(
|
||||||
|
application,
|
||||||
|
this.navigationController,
|
||||||
|
this.searchOptionsController,
|
||||||
|
this.selectionController,
|
||||||
|
this.notesController,
|
||||||
|
this.noteTagsController,
|
||||||
|
this.eventBus,
|
||||||
|
)
|
||||||
|
|
||||||
|
this.notesController.setServicestPostConstruction(this.itemListController)
|
||||||
|
this.noteTagsController.setServicestPostConstruction(this.itemListController)
|
||||||
|
this.selectionController.setServicestPostConstruction(this.itemListController, this.notesController)
|
||||||
|
|
||||||
|
this.noAccountWarningController = new NoAccountWarningController(application, this.eventBus)
|
||||||
|
|
||||||
|
this.accountMenuController = new AccountMenuController(application, this.eventBus)
|
||||||
|
|
||||||
|
this.subscriptionController = new SubscriptionController(application, this.eventBus)
|
||||||
|
|
||||||
|
this.purchaseFlowController = new PurchaseFlowController(application, this.eventBus)
|
||||||
|
|
||||||
|
this.filesController = new FilesController(
|
||||||
|
application,
|
||||||
|
this.notesController,
|
||||||
|
this.selectionController,
|
||||||
|
this.filePreviewModalController,
|
||||||
|
this.eventBus,
|
||||||
|
)
|
||||||
|
|
||||||
|
this.addAppEventObserver()
|
||||||
|
|
||||||
|
if (this.device.appVersion.includes('-beta')) {
|
||||||
|
this.showBetaWarning = storage.get(StorageKey.ShowBetaWarning) ?? true
|
||||||
|
} else {
|
||||||
|
this.showBetaWarning = false
|
||||||
|
}
|
||||||
|
|
||||||
|
makeObservable(this, {
|
||||||
|
showBetaWarning: observable,
|
||||||
|
isSessionsModalVisible: observable,
|
||||||
|
preferencesController: observable,
|
||||||
|
|
||||||
|
openSessionsModal: action,
|
||||||
|
closeSessionsModal: action,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit(source: DeinitSource): void {
|
||||||
|
this.dealloced = true
|
||||||
|
;(this.application as unknown) = undefined
|
||||||
|
|
||||||
|
if (source === DeinitSource.SignOut) {
|
||||||
|
storage.remove(StorageKey.ShowBetaWarning)
|
||||||
|
this.noAccountWarningController.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.unsubAppEventObserver?.()
|
||||||
|
;(this.unsubAppEventObserver as unknown) = undefined
|
||||||
|
|
||||||
|
this.appEventObserverRemovers.forEach((remover) => remover())
|
||||||
|
this.appEventObserverRemovers.length = 0
|
||||||
|
;(this.device as unknown) = undefined
|
||||||
|
;(this.filePreviewModalController as unknown) = undefined
|
||||||
|
;(this.preferencesController as unknown) = undefined
|
||||||
|
;(this.quickSettingsMenuController as unknown) = undefined
|
||||||
|
;(this.syncStatusController as unknown) = undefined
|
||||||
|
|
||||||
|
this.actionsMenuController.reset()
|
||||||
|
;(this.actionsMenuController as unknown) = undefined
|
||||||
|
|
||||||
|
this.featuresController.deinit()
|
||||||
|
;(this.featuresController as unknown) = undefined
|
||||||
|
|
||||||
|
this.accountMenuController.deinit()
|
||||||
|
;(this.accountMenuController as unknown) = undefined
|
||||||
|
|
||||||
|
this.filesController.deinit()
|
||||||
|
;(this.filesController as unknown) = undefined
|
||||||
|
|
||||||
|
this.noAccountWarningController.deinit()
|
||||||
|
;(this.noAccountWarningController as unknown) = undefined
|
||||||
|
|
||||||
|
this.notesController.deinit()
|
||||||
|
;(this.notesController as unknown) = undefined
|
||||||
|
|
||||||
|
this.itemListController.deinit()
|
||||||
|
;(this.itemListController as unknown) = undefined
|
||||||
|
|
||||||
|
this.noteTagsController.deinit()
|
||||||
|
;(this.noteTagsController as unknown) = undefined
|
||||||
|
|
||||||
|
this.purchaseFlowController.deinit()
|
||||||
|
;(this.purchaseFlowController as unknown) = undefined
|
||||||
|
|
||||||
|
this.searchOptionsController.deinit()
|
||||||
|
;(this.searchOptionsController as unknown) = undefined
|
||||||
|
|
||||||
|
this.subscriptionController.deinit()
|
||||||
|
;(this.subscriptionController as unknown) = undefined
|
||||||
|
|
||||||
|
this.navigationController.deinit()
|
||||||
|
;(this.navigationController as unknown) = undefined
|
||||||
|
|
||||||
|
destroyAllObjectProperties(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
openSessionsModal(): void {
|
||||||
|
this.isSessionsModalVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
closeSessionsModal(): void {
|
||||||
|
this.isSessionsModalVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
addAppEventObserver() {
|
||||||
|
this.unsubAppEventObserver = this.application.addEventObserver(async (eventName) => {
|
||||||
|
switch (eventName) {
|
||||||
|
case ApplicationEvent.Launched:
|
||||||
|
if (window.location.search.includes('purchase=true')) {
|
||||||
|
this.purchaseFlowController.openPurchaseFlow()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case ApplicationEvent.SyncStatusChanged:
|
||||||
|
this.syncStatusController.update(this.application.sync.getSyncStatus())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,298 +0,0 @@
|
|||||||
import { storage, StorageKey } from '@/Services/LocalStorage'
|
|
||||||
import { WebApplication, WebAppEvent } from '@/Application/Application'
|
|
||||||
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
|
|
||||||
import { destroyAllObjectProperties, isDesktopApplication } from '@/Utils'
|
|
||||||
import {
|
|
||||||
ApplicationEvent,
|
|
||||||
ContentType,
|
|
||||||
DeinitSource,
|
|
||||||
PrefKey,
|
|
||||||
SNTag,
|
|
||||||
removeFromArray,
|
|
||||||
WebOrDesktopDeviceInterface,
|
|
||||||
} from '@standardnotes/snjs'
|
|
||||||
import { action, makeObservable, observable } from 'mobx'
|
|
||||||
import { ActionsMenuController } from '../../Controllers/ActionsMenuController'
|
|
||||||
import { FeaturesController } from '../../Controllers/FeaturesController'
|
|
||||||
import { FilesController } from '../../Controllers/FilesController'
|
|
||||||
import { NotesController } from '../../Controllers/NotesController'
|
|
||||||
import { ItemListController } from '../../Controllers/ItemList/ItemListController'
|
|
||||||
import { NoteTagsController } from '../../Controllers/NoteTagsController'
|
|
||||||
import { NoAccountWarningController } from '../../Controllers/NoAccountWarningController'
|
|
||||||
import { PreferencesController } from '../../Controllers/PreferencesController'
|
|
||||||
import { PurchaseFlowController } from '../../Controllers/PurchaseFlow/PurchaseFlowController'
|
|
||||||
import { QuickSettingsController } from '../../Controllers/QuickSettingsController'
|
|
||||||
import { SearchOptionsController } from '../../Controllers/SearchOptionsController'
|
|
||||||
import { SubscriptionController } from '../../Controllers/Subscription/SubscriptionController'
|
|
||||||
import { SyncStatusController } from '../../Controllers/SyncStatusController'
|
|
||||||
import { TagsController } from '../../Controllers/Navigation/TagsController'
|
|
||||||
import { FilePreviewModalController } from '../../Controllers/FilePreviewModalController'
|
|
||||||
import { SelectedItemsController } from '../../Controllers/SelectedItemsController'
|
|
||||||
import { ListableContentItem } from '@/Components/ContentListView/Types/ListableContentItem'
|
|
||||||
import { ViewControllerManagerEvent } from './ViewControllerManagerEvent'
|
|
||||||
import { EditorEventSource } from '../../Types/EditorEventSource'
|
|
||||||
import { PanelResizedData } from '../../Types/PanelResizedData'
|
|
||||||
|
|
||||||
type ObserverCallback = (event: ViewControllerManagerEvent, data?: unknown) => Promise<void>
|
|
||||||
|
|
||||||
export class ViewControllerManager {
|
|
||||||
readonly enableUnfinishedFeatures: boolean = window?.enabledUnfinishedFeatures
|
|
||||||
|
|
||||||
observers: ObserverCallback[] = []
|
|
||||||
locked = true
|
|
||||||
unsubAppEventObserver!: () => void
|
|
||||||
webAppEventDisposer?: () => void
|
|
||||||
onVisibilityChange: () => void
|
|
||||||
showBetaWarning: boolean
|
|
||||||
dealloced = false
|
|
||||||
|
|
||||||
readonly accountMenuController: AccountMenuController
|
|
||||||
readonly actionsMenuController = new ActionsMenuController()
|
|
||||||
readonly featuresController: FeaturesController
|
|
||||||
readonly filePreviewModalController = new FilePreviewModalController()
|
|
||||||
readonly filesController: FilesController
|
|
||||||
readonly noAccountWarningController: NoAccountWarningController
|
|
||||||
readonly notesController: NotesController
|
|
||||||
readonly contentListController: ItemListController
|
|
||||||
readonly noteTagsController: NoteTagsController
|
|
||||||
readonly preferencesController = new PreferencesController()
|
|
||||||
readonly purchaseFlowController: PurchaseFlowController
|
|
||||||
readonly quickSettingsMenuController = new QuickSettingsController()
|
|
||||||
readonly searchOptionsController: SearchOptionsController
|
|
||||||
readonly subscriptionController: SubscriptionController
|
|
||||||
readonly syncStatusController = new SyncStatusController()
|
|
||||||
readonly navigationController: TagsController
|
|
||||||
readonly selectionController: SelectedItemsController
|
|
||||||
|
|
||||||
isSessionsModalVisible = false
|
|
||||||
|
|
||||||
private appEventObserverRemovers: (() => void)[] = []
|
|
||||||
|
|
||||||
constructor(public application: WebApplication, private device: WebOrDesktopDeviceInterface) {
|
|
||||||
this.selectionController = new SelectedItemsController(application, this, this.appEventObserverRemovers)
|
|
||||||
this.notesController = new NotesController(
|
|
||||||
application,
|
|
||||||
this,
|
|
||||||
async () => {
|
|
||||||
await this.notifyEvent(ViewControllerManagerEvent.ActiveEditorChanged)
|
|
||||||
},
|
|
||||||
this.appEventObserverRemovers,
|
|
||||||
)
|
|
||||||
this.featuresController = new FeaturesController(application, this.appEventObserverRemovers)
|
|
||||||
this.navigationController = new TagsController(
|
|
||||||
application,
|
|
||||||
this,
|
|
||||||
this.appEventObserverRemovers,
|
|
||||||
this.featuresController,
|
|
||||||
)
|
|
||||||
this.searchOptionsController = new SearchOptionsController(application, this.appEventObserverRemovers)
|
|
||||||
this.contentListController = new ItemListController(application, this, this.appEventObserverRemovers)
|
|
||||||
this.noteTagsController = new NoteTagsController(application, this, this.appEventObserverRemovers)
|
|
||||||
this.noAccountWarningController = new NoAccountWarningController(application, this.appEventObserverRemovers)
|
|
||||||
this.accountMenuController = new AccountMenuController(application, this.appEventObserverRemovers)
|
|
||||||
this.subscriptionController = new SubscriptionController(application, this.appEventObserverRemovers)
|
|
||||||
this.purchaseFlowController = new PurchaseFlowController(application)
|
|
||||||
this.filesController = new FilesController(application, this, this.appEventObserverRemovers)
|
|
||||||
this.addAppEventObserver()
|
|
||||||
this.onVisibilityChange = () => {
|
|
||||||
const visible = document.visibilityState === 'visible'
|
|
||||||
const event = visible ? ViewControllerManagerEvent.WindowDidFocus : ViewControllerManagerEvent.WindowDidBlur
|
|
||||||
this.notifyEvent(event).catch(console.error)
|
|
||||||
}
|
|
||||||
this.registerVisibilityObservers()
|
|
||||||
|
|
||||||
if (this.device.appVersion.includes('-beta')) {
|
|
||||||
this.showBetaWarning = storage.get(StorageKey.ShowBetaWarning) ?? true
|
|
||||||
} else {
|
|
||||||
this.showBetaWarning = false
|
|
||||||
}
|
|
||||||
|
|
||||||
makeObservable(this, {
|
|
||||||
showBetaWarning: observable,
|
|
||||||
isSessionsModalVisible: observable,
|
|
||||||
preferencesController: observable,
|
|
||||||
|
|
||||||
enableBetaWarning: action,
|
|
||||||
disableBetaWarning: action,
|
|
||||||
openSessionsModal: action,
|
|
||||||
closeSessionsModal: action,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit(source: DeinitSource): void {
|
|
||||||
this.dealloced = true
|
|
||||||
;(this.application as unknown) = undefined
|
|
||||||
|
|
||||||
if (source === DeinitSource.SignOut) {
|
|
||||||
storage.remove(StorageKey.ShowBetaWarning)
|
|
||||||
this.noAccountWarningController.reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.unsubAppEventObserver?.()
|
|
||||||
;(this.unsubAppEventObserver as unknown) = undefined
|
|
||||||
this.observers.length = 0
|
|
||||||
|
|
||||||
this.appEventObserverRemovers.forEach((remover) => remover())
|
|
||||||
this.appEventObserverRemovers.length = 0
|
|
||||||
;(this.device as unknown) = undefined
|
|
||||||
|
|
||||||
this.webAppEventDisposer?.()
|
|
||||||
this.webAppEventDisposer = undefined
|
|
||||||
;(this.filePreviewModalController as unknown) = undefined
|
|
||||||
;(this.preferencesController as unknown) = undefined
|
|
||||||
;(this.quickSettingsMenuController as unknown) = undefined
|
|
||||||
;(this.syncStatusController as unknown) = undefined
|
|
||||||
|
|
||||||
this.actionsMenuController.reset()
|
|
||||||
;(this.actionsMenuController as unknown) = undefined
|
|
||||||
|
|
||||||
this.featuresController.deinit(source)
|
|
||||||
;(this.featuresController as unknown) = undefined
|
|
||||||
|
|
||||||
this.accountMenuController.deinit(source)
|
|
||||||
;(this.accountMenuController as unknown) = undefined
|
|
||||||
|
|
||||||
this.filesController.deinit(source)
|
|
||||||
;(this.filesController as unknown) = undefined
|
|
||||||
|
|
||||||
this.noAccountWarningController.deinit(source)
|
|
||||||
;(this.noAccountWarningController as unknown) = undefined
|
|
||||||
|
|
||||||
this.notesController.deinit(source)
|
|
||||||
;(this.notesController as unknown) = undefined
|
|
||||||
|
|
||||||
this.contentListController.deinit(source)
|
|
||||||
;(this.contentListController as unknown) = undefined
|
|
||||||
|
|
||||||
this.noteTagsController.deinit(source)
|
|
||||||
;(this.noteTagsController as unknown) = undefined
|
|
||||||
|
|
||||||
this.purchaseFlowController.deinit(source)
|
|
||||||
;(this.purchaseFlowController as unknown) = undefined
|
|
||||||
|
|
||||||
this.searchOptionsController.deinit(source)
|
|
||||||
;(this.searchOptionsController as unknown) = undefined
|
|
||||||
|
|
||||||
this.subscriptionController.deinit(source)
|
|
||||||
;(this.subscriptionController as unknown) = undefined
|
|
||||||
|
|
||||||
this.navigationController.deinit(source)
|
|
||||||
;(this.navigationController as unknown) = undefined
|
|
||||||
|
|
||||||
document.removeEventListener('visibilitychange', this.onVisibilityChange)
|
|
||||||
;(this.onVisibilityChange as unknown) = undefined
|
|
||||||
|
|
||||||
destroyAllObjectProperties(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
openSessionsModal(): void {
|
|
||||||
this.isSessionsModalVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
closeSessionsModal(): void {
|
|
||||||
this.isSessionsModalVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
disableBetaWarning() {
|
|
||||||
this.showBetaWarning = false
|
|
||||||
storage.set(StorageKey.ShowBetaWarning, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
enableBetaWarning() {
|
|
||||||
this.showBetaWarning = true
|
|
||||||
storage.set(StorageKey.ShowBetaWarning, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
public get version(): string {
|
|
||||||
return this.device.appVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
isGlobalSpellcheckEnabled(): boolean {
|
|
||||||
return this.application.getPreference(PrefKey.EditorSpellcheck, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
async toggleGlobalSpellcheck() {
|
|
||||||
const currentValue = this.isGlobalSpellcheckEnabled()
|
|
||||||
return this.application.setPreference(PrefKey.EditorSpellcheck, !currentValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
addAppEventObserver() {
|
|
||||||
this.unsubAppEventObserver = this.application.addEventObserver(async (eventName) => {
|
|
||||||
switch (eventName) {
|
|
||||||
case ApplicationEvent.Started:
|
|
||||||
this.locked = true
|
|
||||||
break
|
|
||||||
case ApplicationEvent.Launched:
|
|
||||||
this.locked = false
|
|
||||||
if (window.location.search.includes('purchase=true')) {
|
|
||||||
this.purchaseFlowController.openPurchaseFlow()
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case ApplicationEvent.SyncStatusChanged:
|
|
||||||
this.syncStatusController.update(this.application.sync.getSyncStatus())
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
isLocked() {
|
|
||||||
return this.locked
|
|
||||||
}
|
|
||||||
|
|
||||||
registerVisibilityObservers() {
|
|
||||||
if (isDesktopApplication()) {
|
|
||||||
this.webAppEventDisposer = this.application.addWebEventObserver((event) => {
|
|
||||||
if (event === WebAppEvent.DesktopWindowGainedFocus) {
|
|
||||||
this.notifyEvent(ViewControllerManagerEvent.WindowDidFocus).catch(console.error)
|
|
||||||
} else if (event === WebAppEvent.DesktopWindowLostFocus) {
|
|
||||||
this.notifyEvent(ViewControllerManagerEvent.WindowDidBlur).catch(console.error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
/* Tab visibility listener, web only */
|
|
||||||
document.addEventListener('visibilitychange', this.onVisibilityChange)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addObserver(callback: ObserverCallback): () => void {
|
|
||||||
this.observers.push(callback)
|
|
||||||
|
|
||||||
const thislessObservers = this.observers
|
|
||||||
return () => {
|
|
||||||
removeFromArray(thislessObservers, callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async notifyEvent(eventName: ViewControllerManagerEvent, data?: unknown) {
|
|
||||||
for (const callback of this.observers) {
|
|
||||||
await callback(eventName, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the tags that are referncing this note */
|
|
||||||
public getItemTags(item: ListableContentItem) {
|
|
||||||
return this.application.items.itemsReferencingItem(item).filter((ref) => {
|
|
||||||
return ref.content_type === ContentType.Tag
|
|
||||||
}) as SNTag[]
|
|
||||||
}
|
|
||||||
|
|
||||||
panelDidResize(name: string, collapsed: boolean) {
|
|
||||||
const data: PanelResizedData = {
|
|
||||||
panel: name,
|
|
||||||
collapsed: collapsed,
|
|
||||||
}
|
|
||||||
this.notifyEvent(ViewControllerManagerEvent.PanelResized, data).catch(console.error)
|
|
||||||
}
|
|
||||||
|
|
||||||
editorDidFocus(eventSource: EditorEventSource) {
|
|
||||||
this.notifyEvent(ViewControllerManagerEvent.EditorFocused, { eventSource: eventSource }).catch(console.error)
|
|
||||||
}
|
|
||||||
|
|
||||||
beganBackupDownload() {
|
|
||||||
this.notifyEvent(ViewControllerManagerEvent.BeganBackupDownload).catch(console.error)
|
|
||||||
}
|
|
||||||
|
|
||||||
endedBackupDownload(success: boolean) {
|
|
||||||
this.notifyEvent(ViewControllerManagerEvent.EndedBackupDownload, { success: success }).catch(console.error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
export enum ViewControllerManagerEvent {
|
|
||||||
TagChanged,
|
|
||||||
ActiveEditorChanged,
|
|
||||||
PanelResized,
|
|
||||||
EditorFocused,
|
|
||||||
BeganBackupDownload,
|
|
||||||
EndedBackupDownload,
|
|
||||||
WindowDidFocus,
|
|
||||||
WindowDidBlur,
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export * from './ViewControllerManager'
|
|
||||||
export * from './ViewControllerManagerEvent'
|
|
||||||
1
app/assets/javascripts/Types/Disposer.ts
Normal file
1
app/assets/javascripts/Types/Disposer.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export type Disposer = () => void
|
||||||
@@ -73,10 +73,10 @@
|
|||||||
"@standardnotes/components": "1.8.2",
|
"@standardnotes/components": "1.8.2",
|
||||||
"@standardnotes/filepicker": "1.16.2",
|
"@standardnotes/filepicker": "1.16.2",
|
||||||
"@standardnotes/icons": "^1.1.8",
|
"@standardnotes/icons": "^1.1.8",
|
||||||
"@standardnotes/services": "^1.13.3",
|
|
||||||
"@standardnotes/sncrypto-web": "1.10.1",
|
"@standardnotes/sncrypto-web": "1.10.1",
|
||||||
"@standardnotes/snjs": "^2.114.5",
|
"@standardnotes/snjs": "^2.114.5",
|
||||||
"@standardnotes/stylekit": "5.29.3",
|
"@standardnotes/stylekit": "5.29.3",
|
||||||
|
"@standardnotes/services": "^1.13.6",
|
||||||
"@zip.js/zip.js": "^2.4.10",
|
"@zip.js/zip.js": "^2.4.10",
|
||||||
"mobx": "^6.5.0",
|
"mobx": "^6.5.0",
|
||||||
"mobx-react-lite": "^3.3.0",
|
"mobx-react-lite": "^3.3.0",
|
||||||
|
|||||||
45
yarn.lock
45
yarn.lock
@@ -2366,6 +2366,14 @@
|
|||||||
"@standardnotes/common" "^1.22.0"
|
"@standardnotes/common" "^1.22.0"
|
||||||
jsonwebtoken "^8.5.1"
|
jsonwebtoken "^8.5.1"
|
||||||
|
|
||||||
|
"@standardnotes/auth@^3.19.2":
|
||||||
|
version "3.19.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.19.2.tgz#a2758cdde588190eebd30e567fe7756c6823adcd"
|
||||||
|
integrity sha512-m7MvSN2BHHh8+FZ3tUe6IpoPGzu/I1lmQF8snlYfuBmSxVdwVLTYhIbFAjfi4PST/Rx3FXnaoMnfJSR0k+OmWw==
|
||||||
|
dependencies:
|
||||||
|
"@standardnotes/common" "^1.22.0"
|
||||||
|
jsonwebtoken "^8.5.1"
|
||||||
|
|
||||||
"@standardnotes/common@^1.22.0":
|
"@standardnotes/common@^1.22.0":
|
||||||
version "1.22.0"
|
version "1.22.0"
|
||||||
resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.22.0.tgz#397604fb4b92901bac276940a2647509b70a7ad2"
|
resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.22.0.tgz#397604fb4b92901bac276940a2647509b70a7ad2"
|
||||||
@@ -2420,6 +2428,14 @@
|
|||||||
"@standardnotes/auth" "^3.19.1"
|
"@standardnotes/auth" "^3.19.1"
|
||||||
"@standardnotes/common" "^1.22.0"
|
"@standardnotes/common" "^1.22.0"
|
||||||
|
|
||||||
|
"@standardnotes/features@^1.44.6":
|
||||||
|
version "1.44.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.44.6.tgz#1a7872a7e79026a553d3670f8610b2b79c1f5fa4"
|
||||||
|
integrity sha512-iP0oR4bb16Rx0kSspl0R8rSKY38hF59ExaoMIREf0MGH8WLjwDJyILafGfhxv8NjMeRtpIIXKbK+TokM6A5ZXg==
|
||||||
|
dependencies:
|
||||||
|
"@standardnotes/auth" "^3.19.2"
|
||||||
|
"@standardnotes/common" "^1.22.0"
|
||||||
|
|
||||||
"@standardnotes/filepicker@1.16.2":
|
"@standardnotes/filepicker@1.16.2":
|
||||||
version "1.16.2"
|
version "1.16.2"
|
||||||
resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.16.2.tgz#d6fda94b5578f30e6b4f792c874e3eb11ce58453"
|
resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.16.2.tgz#d6fda94b5578f30e6b4f792c874e3eb11ce58453"
|
||||||
@@ -2473,6 +2489,15 @@
|
|||||||
"@standardnotes/responses" "^1.6.28"
|
"@standardnotes/responses" "^1.6.28"
|
||||||
"@standardnotes/utils" "^1.6.10"
|
"@standardnotes/utils" "^1.6.10"
|
||||||
|
|
||||||
|
"@standardnotes/models@^1.11.1":
|
||||||
|
version "1.11.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@standardnotes/models/-/models-1.11.1.tgz#d622b7cffd7ee4faebcd564d7ae880b372ec7be8"
|
||||||
|
integrity sha512-XKXoV8Pi5iuzrjUGWs4grvxn3m2BQyt49Br+euToOkgvZvW5HIiaCGLwAtvP5S3d3ecgD5EvbLzGJIFEs5F4rw==
|
||||||
|
dependencies:
|
||||||
|
"@standardnotes/features" "^1.44.6"
|
||||||
|
"@standardnotes/responses" "^1.6.29"
|
||||||
|
"@standardnotes/utils" "^1.6.10"
|
||||||
|
|
||||||
"@standardnotes/responses@^1.6.27":
|
"@standardnotes/responses@^1.6.27":
|
||||||
version "1.6.27"
|
version "1.6.27"
|
||||||
resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.27.tgz#3a440090e5cee09f2980df5cc57a60f76adff735"
|
resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.27.tgz#3a440090e5cee09f2980df5cc57a60f76adff735"
|
||||||
@@ -2491,6 +2516,15 @@
|
|||||||
"@standardnotes/common" "^1.22.0"
|
"@standardnotes/common" "^1.22.0"
|
||||||
"@standardnotes/features" "^1.44.5"
|
"@standardnotes/features" "^1.44.5"
|
||||||
|
|
||||||
|
"@standardnotes/responses@^1.6.29":
|
||||||
|
version "1.6.29"
|
||||||
|
resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.6.29.tgz#fac5875bb84e382d7b1acf14c0af77876fe5c41d"
|
||||||
|
integrity sha512-BWrkR6gIWD+dC9/a+ii/pDzWtIlAsgZWICTwKZ34jBgjPSO1svewcHzXwBXILKZd6JvrUf6pglmSt3I9fmsHaQ==
|
||||||
|
dependencies:
|
||||||
|
"@standardnotes/auth" "^3.19.2"
|
||||||
|
"@standardnotes/common" "^1.22.0"
|
||||||
|
"@standardnotes/features" "^1.44.6"
|
||||||
|
|
||||||
"@standardnotes/services@^1.13.3":
|
"@standardnotes/services@^1.13.3":
|
||||||
version "1.13.3"
|
version "1.13.3"
|
||||||
resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.3.tgz#b857a6ed42b15d40c2e9d08b41739f5fb6b0d3e0"
|
resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.3.tgz#b857a6ed42b15d40c2e9d08b41739f5fb6b0d3e0"
|
||||||
@@ -2513,6 +2547,17 @@
|
|||||||
"@standardnotes/responses" "^1.6.28"
|
"@standardnotes/responses" "^1.6.28"
|
||||||
"@standardnotes/utils" "^1.6.10"
|
"@standardnotes/utils" "^1.6.10"
|
||||||
|
|
||||||
|
"@standardnotes/services@^1.13.6":
|
||||||
|
version "1.13.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.13.6.tgz#9ab4d0c3ed0ad3693ba54ac360f4fdaad08f7410"
|
||||||
|
integrity sha512-Vt/hptzJK4D6qUPp/cdhOLlRT57uf3CDjDUwyfnELXDyU8mFTVvS/M1deD1PecNWodsfLH7aDWeMWTKApP6LKg==
|
||||||
|
dependencies:
|
||||||
|
"@standardnotes/auth" "^3.19.2"
|
||||||
|
"@standardnotes/common" "^1.22.0"
|
||||||
|
"@standardnotes/models" "^1.11.1"
|
||||||
|
"@standardnotes/responses" "^1.6.29"
|
||||||
|
"@standardnotes/utils" "^1.6.10"
|
||||||
|
|
||||||
"@standardnotes/settings@^1.14.3":
|
"@standardnotes/settings@^1.14.3":
|
||||||
version "1.14.3"
|
version "1.14.3"
|
||||||
resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.14.3.tgz#021085e8c383a9893a2c49daa74cc0754ccd67b5"
|
resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.14.3.tgz#021085e8c383a9893a2c49daa74cc0754ccd67b5"
|
||||||
|
|||||||
Reference in New Issue
Block a user