fix: refactor application event observing on web
This commit is contained in:
@@ -31,6 +31,7 @@ import {
|
||||
AutolockService,
|
||||
IOService,
|
||||
RouteService,
|
||||
RouteServiceInterface,
|
||||
ThemeManager,
|
||||
WebAlertService,
|
||||
} from '@standardnotes/ui-services'
|
||||
@@ -50,7 +51,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
private onVisibilityChange: () => void
|
||||
private mobileWebReceiver?: MobileWebReceiver
|
||||
private androidBackHandler?: AndroidBackHandler
|
||||
public readonly routeService: RouteService
|
||||
public readonly routeService: RouteServiceInterface
|
||||
|
||||
constructor(
|
||||
deviceInterface: WebOrDesktopDevice,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { PaneController } from './PaneController'
|
||||
import { RouteType, storage, StorageKey } from '@standardnotes/ui-services'
|
||||
import { storage, StorageKey } from '@standardnotes/ui-services'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
|
||||
import { destroyAllObjectProperties } from '@/Utils'
|
||||
import {
|
||||
ApplicationEvent,
|
||||
DeinitSource,
|
||||
WebOrDesktopDeviceInterface,
|
||||
InternalEventBus,
|
||||
@@ -31,10 +30,11 @@ import { NavigationController, NavigationControllerPersistableValue } from './Na
|
||||
import { FilePreviewModalController } from './FilePreviewModalController'
|
||||
import { SelectedItemsController, SelectionControllerPersistableValue } from './SelectedItemsController'
|
||||
import { HistoryModalController } from './NoteHistory/HistoryModalController'
|
||||
import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane'
|
||||
import { LinkingController } from './LinkingController'
|
||||
import { MasterPersistedValue, PersistenceKey, PersistenceService } from './Abstract/PersistenceService'
|
||||
import { CrossControllerEvent } from './CrossControllerEvent'
|
||||
import { EventObserverInterface } from '@/Event/EventObserverInterface'
|
||||
import { ApplicationEventObserver } from '@/Event/ApplicationEventObserver'
|
||||
|
||||
export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
readonly enableUnfinishedFeatures: boolean = window?.enabledUnfinishedFeatures
|
||||
@@ -70,6 +70,7 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
private itemCounter: ItemCounterInterface
|
||||
private subscriptionManager: SubscriptionClientInterface
|
||||
private persistenceService: PersistenceService
|
||||
private applicationEventObserver: EventObserverInterface
|
||||
|
||||
constructor(public application: WebApplication, private device: WebOrDesktopDeviceInterface) {
|
||||
this.eventBus = new InternalEventBus()
|
||||
@@ -145,6 +146,16 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
|
||||
this.historyModalController = new HistoryModalController(this.application, this.eventBus)
|
||||
|
||||
this.applicationEventObserver = new ApplicationEventObserver(
|
||||
application.routeService,
|
||||
this.purchaseFlowController,
|
||||
this.accountMenuController,
|
||||
this.preferencesController,
|
||||
this.syncStatusController,
|
||||
application.sync,
|
||||
application.sessions,
|
||||
)
|
||||
|
||||
this.addAppEventObserver()
|
||||
|
||||
if (this.device.appVersion.includes('-beta')) {
|
||||
@@ -240,42 +251,9 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
|
||||
}
|
||||
|
||||
addAppEventObserver() {
|
||||
this.unsubAppEventObserver = this.application.addEventObserver(async (eventName) => {
|
||||
switch (eventName) {
|
||||
case ApplicationEvent.Launched:
|
||||
{
|
||||
const route = this.application.routeService.getRoute()
|
||||
if (route.type === RouteType.Purchase) {
|
||||
this.purchaseFlowController.openPurchaseFlow()
|
||||
}
|
||||
if (route.type === RouteType.Settings) {
|
||||
const user = this.application.getUser()
|
||||
if (user === undefined) {
|
||||
this.accountMenuController.setShow(true)
|
||||
this.accountMenuController.setCurrentPane(AccountMenuPane.SignIn)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
this.preferencesController.openPreferences()
|
||||
this.preferencesController.setCurrentPane(route.settingsParams.panel)
|
||||
}
|
||||
}
|
||||
break
|
||||
case ApplicationEvent.SignedIn:
|
||||
{
|
||||
const route = this.application.routeService.getRoute()
|
||||
if (route.type === RouteType.Settings) {
|
||||
this.preferencesController.openPreferences()
|
||||
this.preferencesController.setCurrentPane(route.settingsParams.panel)
|
||||
}
|
||||
}
|
||||
break
|
||||
case ApplicationEvent.SyncStatusChanged:
|
||||
this.syncStatusController.update(this.application.sync.getSyncStatus())
|
||||
break
|
||||
}
|
||||
})
|
||||
this.unsubAppEventObserver = this.application.addEventObserver(
|
||||
this.applicationEventObserver.handle.bind(this.applicationEventObserver),
|
||||
)
|
||||
}
|
||||
|
||||
persistValues = (): void => {
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
||||
import { RouteServiceInterface, RouteType } from '@standardnotes/ui-services'
|
||||
import { ApplicationEvent, SessionsClientInterface, SyncClientInterface, SyncOpStatus, User } from '@standardnotes/snjs'
|
||||
|
||||
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
|
||||
import { PreferencesController } from '@/Controllers/PreferencesController'
|
||||
import { PurchaseFlowController } from '@/Controllers/PurchaseFlow/PurchaseFlowController'
|
||||
import { SyncStatusController } from '@/Controllers/SyncStatusController'
|
||||
|
||||
import { ApplicationEventObserver } from './ApplicationEventObserver'
|
||||
import { RouteParserInterface } from '@standardnotes/ui-services/dist/Route/RouteParserInterface'
|
||||
import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane'
|
||||
|
||||
describe('ApplicationEventObserver', () => {
|
||||
let routeService: RouteServiceInterface
|
||||
let purchaseFlowController: PurchaseFlowController
|
||||
let accountMenuController: AccountMenuController
|
||||
let preferencesController: PreferencesController
|
||||
let syncStatusController: SyncStatusController
|
||||
let syncClient: SyncClientInterface
|
||||
let sessionManager: SessionsClientInterface
|
||||
|
||||
const createObserver = () =>
|
||||
new ApplicationEventObserver(
|
||||
routeService,
|
||||
purchaseFlowController,
|
||||
accountMenuController,
|
||||
preferencesController,
|
||||
syncStatusController,
|
||||
syncClient,
|
||||
sessionManager,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
routeService = {} as jest.Mocked<RouteServiceInterface>
|
||||
routeService.getRoute = jest.fn().mockReturnValue({
|
||||
type: RouteType.None,
|
||||
} as jest.Mocked<RouteParserInterface>)
|
||||
|
||||
purchaseFlowController = {} as jest.Mocked<PurchaseFlowController>
|
||||
purchaseFlowController.openPurchaseFlow = jest.fn()
|
||||
|
||||
accountMenuController = {} as jest.Mocked<AccountMenuController>
|
||||
accountMenuController.setShow = jest.fn()
|
||||
accountMenuController.setCurrentPane = jest.fn()
|
||||
|
||||
preferencesController = {} as jest.Mocked<PreferencesController>
|
||||
preferencesController.openPreferences = jest.fn()
|
||||
preferencesController.setCurrentPane = jest.fn()
|
||||
|
||||
syncStatusController = {} as jest.Mocked<SyncStatusController>
|
||||
syncStatusController.update = jest.fn()
|
||||
|
||||
syncClient = {} as jest.Mocked<SyncClientInterface>
|
||||
syncClient.getSyncStatus = jest.fn().mockReturnValue({} as jest.Mocked<SyncOpStatus>)
|
||||
|
||||
sessionManager = {} as jest.Mocked<SessionsClientInterface>
|
||||
sessionManager.getUser = jest.fn().mockReturnValue({} as jest.Mocked<User>)
|
||||
})
|
||||
|
||||
describe('Upon Application Launched', () => {
|
||||
it('should open up the purchase flow', async () => {
|
||||
routeService.getRoute = jest.fn().mockReturnValue({
|
||||
type: RouteType.Purchase,
|
||||
} as jest.Mocked<RouteParserInterface>)
|
||||
|
||||
await createObserver().handle(ApplicationEvent.Launched)
|
||||
|
||||
expect(purchaseFlowController.openPurchaseFlow).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should open up settings if user is logged in', async () => {
|
||||
routeService.getRoute = jest.fn().mockReturnValue({
|
||||
type: RouteType.Settings,
|
||||
settingsParams: {
|
||||
panel: 'general',
|
||||
},
|
||||
} as jest.Mocked<RouteParserInterface>)
|
||||
|
||||
await createObserver().handle(ApplicationEvent.Launched)
|
||||
|
||||
expect(preferencesController.openPreferences).toHaveBeenCalled()
|
||||
expect(preferencesController.setCurrentPane).toHaveBeenCalledWith('general')
|
||||
})
|
||||
|
||||
it('should open up sign in if user is not logged in and tries to access settings', async () => {
|
||||
sessionManager.getUser = jest.fn().mockReturnValue(undefined)
|
||||
routeService.getRoute = jest.fn().mockReturnValue({
|
||||
type: RouteType.Settings,
|
||||
settingsParams: {
|
||||
panel: 'general',
|
||||
},
|
||||
} as jest.Mocked<RouteParserInterface>)
|
||||
|
||||
await createObserver().handle(ApplicationEvent.Launched)
|
||||
|
||||
expect(accountMenuController.setShow).toHaveBeenCalledWith(true)
|
||||
expect(accountMenuController.setCurrentPane).toHaveBeenCalledWith(AccountMenuPane.SignIn)
|
||||
expect(preferencesController.openPreferences).not.toHaveBeenCalled()
|
||||
expect(preferencesController.setCurrentPane).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Upon Signing In', () => {
|
||||
it('should open up settings', async () => {
|
||||
routeService.getRoute = jest.fn().mockReturnValue({
|
||||
type: RouteType.Settings,
|
||||
settingsParams: {
|
||||
panel: 'general',
|
||||
},
|
||||
} as jest.Mocked<RouteParserInterface>)
|
||||
|
||||
await createObserver().handle(ApplicationEvent.SignedIn)
|
||||
|
||||
expect(preferencesController.openPreferences).toHaveBeenCalled()
|
||||
expect(preferencesController.setCurrentPane).toHaveBeenCalledWith('general')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Upon Sync Status Changing', () => {
|
||||
it('should inform the sync controller', async () => {
|
||||
await createObserver().handle(ApplicationEvent.SyncStatusChanged)
|
||||
|
||||
expect(syncStatusController.update).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,66 @@
|
||||
import { RouteServiceInterface, RouteType } from '@standardnotes/ui-services'
|
||||
import { ApplicationEvent, SessionsClientInterface, SyncClientInterface } from '@standardnotes/snjs'
|
||||
|
||||
import { PurchaseFlowController } from '@/Controllers/PurchaseFlow/PurchaseFlowController'
|
||||
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
|
||||
import { PreferencesController } from '@/Controllers/PreferencesController'
|
||||
import { SyncStatusController } from '@/Controllers/SyncStatusController'
|
||||
import { AccountMenuPane } from '@/Components/AccountMenu/AccountMenuPane'
|
||||
|
||||
import { EventObserverInterface } from './EventObserverInterface'
|
||||
|
||||
export class ApplicationEventObserver implements EventObserverInterface {
|
||||
constructor(
|
||||
private routeService: RouteServiceInterface,
|
||||
private purchaseFlowController: PurchaseFlowController,
|
||||
private accountMenuController: AccountMenuController,
|
||||
private preferencesController: PreferencesController,
|
||||
private syncStatusController: SyncStatusController,
|
||||
private syncClient: SyncClientInterface,
|
||||
private sessionManager: SessionsClientInterface,
|
||||
) {}
|
||||
|
||||
async handle(event: ApplicationEvent): Promise<void> {
|
||||
switch (event) {
|
||||
case ApplicationEvent.Launched:
|
||||
{
|
||||
const route = this.routeService.getRoute()
|
||||
switch (route.type) {
|
||||
case RouteType.Purchase:
|
||||
this.purchaseFlowController.openPurchaseFlow()
|
||||
|
||||
break
|
||||
case RouteType.Settings: {
|
||||
const user = this.sessionManager.getUser()
|
||||
if (user === undefined) {
|
||||
this.accountMenuController.setShow(true)
|
||||
this.accountMenuController.setCurrentPane(AccountMenuPane.SignIn)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
this.preferencesController.openPreferences()
|
||||
this.preferencesController.setCurrentPane(route.settingsParams.panel)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case ApplicationEvent.SignedIn:
|
||||
{
|
||||
const route = this.routeService.getRoute()
|
||||
switch (route.type) {
|
||||
case RouteType.Settings:
|
||||
this.preferencesController.openPreferences()
|
||||
this.preferencesController.setCurrentPane(route.settingsParams.panel)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
case ApplicationEvent.SyncStatusChanged:
|
||||
this.syncStatusController.update(this.syncClient.getSyncStatus())
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ApplicationEvent } from '@standardnotes/snjs/dist/@types'
|
||||
|
||||
export interface EventObserverInterface {
|
||||
handle(event: ApplicationEvent): Promise<void>
|
||||
}
|
||||
Reference in New Issue
Block a user