feat: prioritize loading latest selected items (#1930)
This commit is contained in:
@@ -1,20 +1,12 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { ShouldPersistNoteStateKey } from '@/Components/Preferences/Panes/General/Persistence'
|
||||
import { ApplicationEvent, InternalEventBus } from '@standardnotes/snjs'
|
||||
import { ApplicationEvent, ContentType, InternalEventBus } from '@standardnotes/snjs'
|
||||
import { PersistedStateValue, StorageKey } from '@standardnotes/ui-services'
|
||||
import { CrossControllerEvent } from '../CrossControllerEvent'
|
||||
|
||||
const MasterPersistenceKey = 'master-persistence-key'
|
||||
|
||||
export enum PersistenceKey {
|
||||
SelectedItemsController = 'selected-items-controller',
|
||||
NavigationController = 'navigation-controller',
|
||||
ItemListController = 'item-list-controller',
|
||||
}
|
||||
|
||||
export type MasterPersistedValue = Record<PersistenceKey, unknown>
|
||||
|
||||
export class PersistenceService {
|
||||
private unsubAppEventObserver: () => void
|
||||
private didHydrateOnce = false
|
||||
|
||||
constructor(private application: WebApplication, private eventBus: InternalEventBus) {
|
||||
this.unsubAppEventObserver = this.application.addEventObserver(async (eventName) => {
|
||||
@@ -27,31 +19,54 @@ export class PersistenceService {
|
||||
}
|
||||
|
||||
async onAppEvent(eventName: ApplicationEvent) {
|
||||
if (eventName === ApplicationEvent.LocalDataLoaded) {
|
||||
let shouldHydrateState = this.application.getValue(ShouldPersistNoteStateKey)
|
||||
if (eventName === ApplicationEvent.LocalDataLoaded && !this.didHydrateOnce) {
|
||||
this.hydratePersistedValues()
|
||||
this.didHydrateOnce = true
|
||||
} else if (eventName === ApplicationEvent.LocalDataIncrementalLoad) {
|
||||
const canHydrate = this.application.items.getItems([ContentType.Note, ContentType.Tag]).length > 0
|
||||
|
||||
if (typeof shouldHydrateState === 'undefined') {
|
||||
this.application.setValue(ShouldPersistNoteStateKey, true)
|
||||
shouldHydrateState = true
|
||||
if (!canHydrate) {
|
||||
return
|
||||
}
|
||||
|
||||
this.eventBus.publish({
|
||||
type: CrossControllerEvent.HydrateFromPersistedValues,
|
||||
payload: shouldHydrateState ? this.getPersistedValues() : undefined,
|
||||
})
|
||||
this.hydratePersistedValues()
|
||||
this.didHydrateOnce = true
|
||||
}
|
||||
}
|
||||
|
||||
persistValues(values: MasterPersistedValue): void {
|
||||
get persistenceEnabled() {
|
||||
return this.application.getValue(ShouldPersistNoteStateKey) ?? true
|
||||
}
|
||||
|
||||
hydratePersistedValues = () => {
|
||||
this.eventBus.publish({
|
||||
type: CrossControllerEvent.HydrateFromPersistedValues,
|
||||
payload: this.persistenceEnabled ? this.getPersistedValues() : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
persistValues(values: PersistedStateValue): void {
|
||||
if (!this.application.isDatabaseLoaded()) {
|
||||
return
|
||||
}
|
||||
|
||||
this.application.setValue(MasterPersistenceKey, values)
|
||||
if (!this.persistenceEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
this.application.setValue(StorageKey.MasterStatePersistenceKey, values)
|
||||
}
|
||||
|
||||
getPersistedValues(): MasterPersistedValue {
|
||||
return this.application.getValue(MasterPersistenceKey) as MasterPersistedValue
|
||||
clearPersistedValues(): void {
|
||||
if (!this.application.isDatabaseLoaded()) {
|
||||
return
|
||||
}
|
||||
|
||||
this.application.removeValue(StorageKey.MasterStatePersistenceKey)
|
||||
}
|
||||
|
||||
getPersistedValues(): PersistedStateValue {
|
||||
return this.application.getValue(StorageKey.MasterStatePersistenceKey) as PersistedStateValue
|
||||
}
|
||||
|
||||
deinit() {
|
||||
|
||||
@@ -37,7 +37,6 @@ import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import dayjs from 'dayjs'
|
||||
import { LinkingController } from '../LinkingController'
|
||||
import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||
import { Persistable } from '../Abstract/Persistable'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
|
||||
const MinNoteCellHeight = 51.0
|
||||
@@ -55,14 +54,7 @@ enum ItemsReloadSource {
|
||||
FilterTextChange,
|
||||
}
|
||||
|
||||
export type ItemListControllerPersistableValue = {
|
||||
displayOptions: DisplayOptions
|
||||
}
|
||||
|
||||
export class ItemListController
|
||||
extends AbstractViewController
|
||||
implements Persistable<ItemListControllerPersistableValue>, InternalEventHandlerInterface
|
||||
{
|
||||
export class ItemListController extends AbstractViewController implements InternalEventHandlerInterface {
|
||||
completedFullSync = false
|
||||
noteFilterText = ''
|
||||
notes: SNNote[] = []
|
||||
@@ -126,14 +118,6 @@ export class ItemListController
|
||||
}),
|
||||
)
|
||||
|
||||
this.disposers.push(
|
||||
reaction(
|
||||
() => [this.navigationController.selected],
|
||||
() => {
|
||||
void this.reloadDisplayPreferences()
|
||||
},
|
||||
),
|
||||
)
|
||||
this.disposers.push(
|
||||
application.streamItems<SNTag>([ContentType.Tag], async ({ changed, inserted }) => {
|
||||
const tags = [...changed, ...inserted]
|
||||
@@ -142,10 +126,9 @@ export class ItemListController
|
||||
if (!didReloadItems) {
|
||||
/** A tag could have changed its relationships, so we need to reload the filter */
|
||||
this.reloadNotesDisplayOptions()
|
||||
void this.reloadItems(ItemsReloadSource.ItemStream)
|
||||
}
|
||||
|
||||
void this.reloadItems(ItemsReloadSource.ItemStream)
|
||||
|
||||
if (this.navigationController.selected && findInArray(tags, 'uuid', this.navigationController.selected.uuid)) {
|
||||
/** Tag title could have changed */
|
||||
this.reloadPanelTitle()
|
||||
@@ -233,8 +216,6 @@ export class ItemListController
|
||||
|
||||
optionsSubtitle: computed,
|
||||
activeControllerItem: computed,
|
||||
|
||||
hydrateFromPersistedValue: action,
|
||||
})
|
||||
|
||||
window.onresize = () => {
|
||||
@@ -242,21 +223,6 @@ export class ItemListController
|
||||
}
|
||||
}
|
||||
|
||||
getPersistableValue = (): ItemListControllerPersistableValue => {
|
||||
return {
|
||||
displayOptions: this.displayOptions,
|
||||
}
|
||||
}
|
||||
|
||||
hydrateFromPersistedValue = (state: ItemListControllerPersistableValue | undefined) => {
|
||||
if (!state) {
|
||||
return
|
||||
}
|
||||
if (state.displayOptions) {
|
||||
this.displayOptions = state.displayOptions
|
||||
}
|
||||
}
|
||||
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === CrossControllerEvent.TagChanged) {
|
||||
const payload = event.payload as { userTriggered: boolean }
|
||||
@@ -535,6 +501,7 @@ export class ItemListController
|
||||
}
|
||||
newDisplayOptions.sortBy = sortBy
|
||||
|
||||
const currentSortDirection = this.displayOptions.sortDirection
|
||||
newDisplayOptions.sortDirection =
|
||||
useBoolean(
|
||||
selectedTag?.preferences?.sortReverse,
|
||||
@@ -613,18 +580,14 @@ export class ItemListController
|
||||
|
||||
await this.reloadItems(ItemsReloadSource.DisplayOptionsChange)
|
||||
|
||||
if (
|
||||
newDisplayOptions.sortBy !== currentSortBy &&
|
||||
this.shouldSelectFirstItem(ItemsReloadSource.DisplayOptionsChange)
|
||||
) {
|
||||
const didSortByChange = currentSortBy !== this.displayOptions.sortBy
|
||||
const didSortDirectionChange = currentSortDirection !== this.displayOptions.sortDirection
|
||||
const didSortPrefChange = didSortByChange || didSortDirectionChange
|
||||
|
||||
if (didSortPrefChange && this.shouldSelectFirstItem(ItemsReloadSource.DisplayOptionsChange)) {
|
||||
await this.selectFirstItem()
|
||||
}
|
||||
|
||||
this.eventBus.publish({
|
||||
type: CrossControllerEvent.RequestValuePersistence,
|
||||
payload: undefined,
|
||||
})
|
||||
|
||||
return { didReloadItems: true }
|
||||
}
|
||||
|
||||
@@ -825,9 +788,12 @@ export class ItemListController
|
||||
|
||||
this.resetPagination()
|
||||
|
||||
this.reloadNotesDisplayOptions()
|
||||
const { didReloadItems } = await this.reloadDisplayPreferences()
|
||||
|
||||
await this.reloadItems(userTriggered ? ItemsReloadSource.UserTriggeredTagChange : ItemsReloadSource.TagChange)
|
||||
if (!didReloadItems) {
|
||||
this.reloadNotesDisplayOptions()
|
||||
void this.reloadItems(userTriggered ? ItemsReloadSource.UserTriggeredTagChange : ItemsReloadSource.TagChange)
|
||||
}
|
||||
}
|
||||
|
||||
onFilterEnter = () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { confirmDialog } from '@standardnotes/ui-services'
|
||||
import { confirmDialog, NavigationControllerPersistableValue } from '@standardnotes/ui-services'
|
||||
import { STRING_DELETE_TAG } from '@/Constants/Strings'
|
||||
import { MAX_MENU_SIZE_MULTIPLIER, MENU_MARGIN_FROM_APP_BORDER, SMART_TAGS_FEATURE_NAME } from '@/Constants/Constants'
|
||||
import {
|
||||
@@ -28,10 +28,6 @@ import { AbstractViewController } from '../Abstract/AbstractViewController'
|
||||
import { Persistable } from '../Abstract/Persistable'
|
||||
import { TagListSectionType } from '@/Components/Tags/TagListSection'
|
||||
|
||||
export type NavigationControllerPersistableValue = {
|
||||
selectedTagUuid: AnyTag['uuid']
|
||||
}
|
||||
|
||||
export class NavigationController
|
||||
extends AbstractViewController
|
||||
implements Persistable<NavigationControllerPersistableValue>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
isFile,
|
||||
Uuids,
|
||||
} from '@standardnotes/snjs'
|
||||
import { SelectionControllerPersistableValue } from '@standardnotes/ui-services'
|
||||
import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'
|
||||
import { WebApplication } from '../Application/Application'
|
||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
@@ -18,10 +19,6 @@ import { Persistable } from './Abstract/Persistable'
|
||||
import { CrossControllerEvent } from './CrossControllerEvent'
|
||||
import { ItemListController } from './ItemList/ItemListController'
|
||||
|
||||
export type SelectionControllerPersistableValue = {
|
||||
selectedUuids: UuidString[]
|
||||
}
|
||||
|
||||
export class SelectedItemsController
|
||||
extends AbstractViewController
|
||||
implements Persistable<SelectionControllerPersistableValue>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { PaneController } from './PaneController'
|
||||
import { storage, StorageKey, ToastService, ToastServiceInterface } from '@standardnotes/ui-services'
|
||||
import {
|
||||
PersistedStateValue,
|
||||
PersistenceKey,
|
||||
storage,
|
||||
StorageKey,
|
||||
ToastService,
|
||||
ToastServiceInterface,
|
||||
} from '@standardnotes/ui-services'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
|
||||
import { destroyAllObjectProperties } from '@/Utils'
|
||||
@@ -18,7 +25,7 @@ import { ActionsMenuController } from './ActionsMenuController'
|
||||
import { FeaturesController } from './FeaturesController'
|
||||
import { FilesController } from './FilesController'
|
||||
import { NotesController } from './NotesController'
|
||||
import { ItemListController, ItemListControllerPersistableValue } from './ItemList/ItemListController'
|
||||
import { ItemListController } from './ItemList/ItemListController'
|
||||
import { NoAccountWarningController } from './NoAccountWarningController'
|
||||
import { PreferencesController } from './PreferencesController'
|
||||
import { PurchaseFlowController } from './PurchaseFlow/PurchaseFlowController'
|
||||
@@ -26,12 +33,12 @@ import { QuickSettingsController } from './QuickSettingsController'
|
||||
import { SearchOptionsController } from './SearchOptionsController'
|
||||
import { SubscriptionController } from './Subscription/SubscriptionController'
|
||||
import { SyncStatusController } from './SyncStatusController'
|
||||
import { NavigationController, NavigationControllerPersistableValue } from './Navigation/NavigationController'
|
||||
import { NavigationController } from './Navigation/NavigationController'
|
||||
import { FilePreviewModalController } from './FilePreviewModalController'
|
||||
import { SelectedItemsController, SelectionControllerPersistableValue } from './SelectedItemsController'
|
||||
import { SelectedItemsController } from './SelectedItemsController'
|
||||
import { HistoryModalController } from './NoteHistory/HistoryModalController'
|
||||
import { LinkingController } from './LinkingController'
|
||||
import { MasterPersistedValue, PersistenceKey, PersistenceService } from './Abstract/PersistenceService'
|
||||
import { PersistenceService } from './Abstract/PersistenceService'
|
||||
import { CrossControllerEvent } from './CrossControllerEvent'
|
||||
import { EventObserverInterface } from '@/Event/EventObserverInterface'
|
||||
import { ApplicationEventObserver } from '@/Event/ApplicationEventObserver'
|
||||
@@ -263,29 +270,40 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
}
|
||||
|
||||
persistValues = (): void => {
|
||||
const values: MasterPersistedValue = {
|
||||
const values: PersistedStateValue = {
|
||||
[PersistenceKey.SelectedItemsController]: this.selectionController.getPersistableValue(),
|
||||
[PersistenceKey.NavigationController]: this.navigationController.getPersistableValue(),
|
||||
[PersistenceKey.ItemListController]: this.itemListController.getPersistableValue(),
|
||||
}
|
||||
|
||||
this.persistenceService.persistValues(values)
|
||||
|
||||
const selectedItemsState = values['selected-items-controller']
|
||||
const navigationSelectionState = values['navigation-controller']
|
||||
const launchPriorityUuids: string[] = []
|
||||
if (selectedItemsState.selectedUuids.length) {
|
||||
launchPriorityUuids.push(...selectedItemsState.selectedUuids)
|
||||
}
|
||||
if (navigationSelectionState.selectedTagUuid) {
|
||||
launchPriorityUuids.push(navigationSelectionState.selectedTagUuid)
|
||||
}
|
||||
this.application.sync.setLaunchPriorityUuids(launchPriorityUuids)
|
||||
}
|
||||
|
||||
hydrateFromPersistedValues = (values: MasterPersistedValue | undefined): void => {
|
||||
const itemListState = values?.[PersistenceKey.ItemListController] as ItemListControllerPersistableValue
|
||||
this.itemListController.hydrateFromPersistedValue(itemListState)
|
||||
clearPersistedValues = (): void => {
|
||||
this.persistenceService.clearPersistedValues()
|
||||
}
|
||||
|
||||
const selectedItemsState = values?.[PersistenceKey.SelectedItemsController] as SelectionControllerPersistableValue
|
||||
this.selectionController.hydrateFromPersistedValue(selectedItemsState)
|
||||
|
||||
const navigationState = values?.[PersistenceKey.NavigationController] as NavigationControllerPersistableValue
|
||||
hydrateFromPersistedValues = (values: PersistedStateValue | undefined): void => {
|
||||
const navigationState = values?.[PersistenceKey.NavigationController]
|
||||
this.navigationController.hydrateFromPersistedValue(navigationState)
|
||||
|
||||
const selectedItemsState = values?.[PersistenceKey.SelectedItemsController]
|
||||
this.selectionController.hydrateFromPersistedValue(selectedItemsState)
|
||||
}
|
||||
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === CrossControllerEvent.HydrateFromPersistedValues) {
|
||||
this.hydrateFromPersistedValues(event.payload as MasterPersistedValue | undefined)
|
||||
this.hydrateFromPersistedValues(event.payload as PersistedStateValue | undefined)
|
||||
} else if (event.type === CrossControllerEvent.RequestValuePersistence) {
|
||||
this.persistValues()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user