feat(labs): super editor (#2001)
This commit is contained in:
95
packages/web/src/javascripts/Application/Application.spec.ts
Normal file
95
packages/web/src/javascripts/Application/Application.spec.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
||||
import {
|
||||
Environment,
|
||||
FeatureIdentifier,
|
||||
namespacedKey,
|
||||
Platform,
|
||||
RawStorageKey,
|
||||
SNComponent,
|
||||
SNComponentManager,
|
||||
SNLog,
|
||||
SNTag,
|
||||
} from '@standardnotes/snjs'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { WebOrDesktopDevice } from './Device/WebOrDesktopDevice'
|
||||
|
||||
describe('web application', () => {
|
||||
let application: WebApplication
|
||||
let componentManager: SNComponentManager
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
SNLog.onLog = console.log
|
||||
SNLog.onError = console.error
|
||||
|
||||
beforeEach(() => {
|
||||
const identifier = '123'
|
||||
|
||||
window.matchMedia = jest.fn().mockReturnValue({ matches: true, addListener: jest.fn() })
|
||||
|
||||
const device = {
|
||||
environment: Environment.Desktop,
|
||||
appVersion: '1.2.3',
|
||||
setApplication: jest.fn(),
|
||||
openDatabase: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
getRawStorageValue: jest.fn().mockImplementation((key) => {
|
||||
if (key === namespacedKey(identifier, RawStorageKey.SnjsVersion)) {
|
||||
return '10.0.0'
|
||||
}
|
||||
return undefined
|
||||
}),
|
||||
setRawStorageValue: jest.fn(),
|
||||
} as unknown as jest.Mocked<WebOrDesktopDevice>
|
||||
|
||||
application = new WebApplication(device, Platform.MacWeb, identifier, 'https://sync', 'https://socket')
|
||||
|
||||
componentManager = {} as jest.Mocked<SNComponentManager>
|
||||
componentManager.legacyGetDefaultEditor = jest.fn()
|
||||
Object.defineProperty(application, 'componentManager', { value: componentManager })
|
||||
|
||||
application.prepareForLaunch({ receiveChallenge: jest.fn() })
|
||||
})
|
||||
|
||||
describe('geDefaultEditorIdentifier', () => {
|
||||
it('should return plain editor if no default tag editor or component editor', () => {
|
||||
const editorIdentifier = application.geDefaultEditorIdentifier()
|
||||
|
||||
expect(editorIdentifier).toEqual(FeatureIdentifier.PlainEditor)
|
||||
})
|
||||
|
||||
it('should return pref key based value if available', () => {
|
||||
application.getPreference = jest.fn().mockReturnValue(FeatureIdentifier.SuperEditor)
|
||||
|
||||
const editorIdentifier = application.geDefaultEditorIdentifier()
|
||||
|
||||
expect(editorIdentifier).toEqual(FeatureIdentifier.SuperEditor)
|
||||
})
|
||||
|
||||
it('should return default tag identifier if tag supplied', () => {
|
||||
const tag = {
|
||||
preferences: {
|
||||
editorIdentifier: FeatureIdentifier.SuperEditor,
|
||||
},
|
||||
} as jest.Mocked<SNTag>
|
||||
|
||||
const editorIdentifier = application.geDefaultEditorIdentifier(tag)
|
||||
|
||||
expect(editorIdentifier).toEqual(FeatureIdentifier.SuperEditor)
|
||||
})
|
||||
|
||||
it('should return legacy editor identifier', () => {
|
||||
const editor = {
|
||||
legacyIsDefaultEditor: jest.fn().mockReturnValue(true),
|
||||
identifier: FeatureIdentifier.MarkdownProEditor,
|
||||
} as unknown as jest.Mocked<SNComponent>
|
||||
|
||||
componentManager.legacyGetDefaultEditor = jest.fn().mockReturnValue(editor)
|
||||
|
||||
const editorIdentifier = application.geDefaultEditorIdentifier()
|
||||
|
||||
expect(editorIdentifier).toEqual(FeatureIdentifier.MarkdownProEditor)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
DeinitSource,
|
||||
Platform,
|
||||
SNApplication,
|
||||
ItemGroupController,
|
||||
removeFromArray,
|
||||
DesktopDeviceInterface,
|
||||
isDesktopDevice,
|
||||
@@ -20,6 +19,8 @@ import {
|
||||
MobileUnlockTiming,
|
||||
InternalEventBus,
|
||||
DecryptedItem,
|
||||
EditorIdentifier,
|
||||
FeatureIdentifier,
|
||||
} from '@standardnotes/snjs'
|
||||
import { makeObservable, observable } from 'mobx'
|
||||
import { PanelResizedData } from '@/Types/PanelResizedData'
|
||||
@@ -40,6 +41,8 @@ import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import { setCustomViewportHeight } from '@/setViewportHeightWithFallback'
|
||||
import { WebServices } from './WebServices'
|
||||
import { FeatureName } from '@/Controllers/FeatureName'
|
||||
import { ItemGroupController } from '@/Components/NoteView/Controller/ItemGroupController'
|
||||
import { VisibilityObserver } from './VisibilityObserver'
|
||||
|
||||
export type WebEventObserver = (event: WebAppEvent, data?: unknown) => void
|
||||
|
||||
@@ -47,10 +50,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
private webServices!: WebServices
|
||||
private webEventObservers: WebEventObserver[] = []
|
||||
public itemControllerGroup: ItemGroupController
|
||||
private onVisibilityChange: () => void
|
||||
private mobileWebReceiver?: MobileWebReceiver
|
||||
private androidBackHandler?: AndroidBackHandler
|
||||
public readonly routeService: RouteServiceInterface
|
||||
private visibilityObserver?: VisibilityObserver
|
||||
|
||||
constructor(
|
||||
deviceInterface: WebOrDesktopDevice,
|
||||
@@ -106,14 +109,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
}
|
||||
}
|
||||
|
||||
this.onVisibilityChange = () => {
|
||||
const visible = document.visibilityState === 'visible'
|
||||
const event = visible ? WebAppEvent.WindowDidFocus : WebAppEvent.WindowDidBlur
|
||||
this.notifyWebEvent(event)
|
||||
}
|
||||
|
||||
if (!isDesktopApplication()) {
|
||||
document.addEventListener('visibilitychange', this.onVisibilityChange)
|
||||
this.visibilityObserver = new VisibilityObserver((event) => {
|
||||
this.notifyWebEvent(event)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,8 +143,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
|
||||
this.webEventObservers.length = 0
|
||||
|
||||
document.removeEventListener('visibilitychange', this.onVisibilityChange)
|
||||
;(this.onVisibilityChange as unknown) = undefined
|
||||
if (this.visibilityObserver) {
|
||||
this.visibilityObserver.deinit()
|
||||
this.visibilityObserver = undefined
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error while deiniting application', error)
|
||||
}
|
||||
@@ -379,4 +380,13 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
showAccountMenu(): void {
|
||||
this.getViewControllerManager().accountMenuController.setShow(true)
|
||||
}
|
||||
|
||||
geDefaultEditorIdentifier(currentTag?: SNTag): EditorIdentifier {
|
||||
return (
|
||||
currentTag?.preferences?.editorIdentifier ||
|
||||
this.getPreference(PrefKey.DefaultEditorIdentifier) ||
|
||||
this.componentManager.legacyGetDefaultEditor()?.identifier ||
|
||||
FeatureIdentifier.PlainEditor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { WebAppEvent } from '@standardnotes/snjs'
|
||||
|
||||
export class VisibilityObserver {
|
||||
private raceTimeout?: ReturnType<typeof setTimeout>
|
||||
|
||||
constructor(private onEvent: (event: WebAppEvent) => void) {
|
||||
/**
|
||||
* Browsers may handle focus and visibilitychange events differently.
|
||||
* Focus better handles window focus events but may not handle tab switching.
|
||||
* We will listen for both and debouce notifying so that the most recent event wins.
|
||||
*/
|
||||
document.addEventListener('visibilitychange', this.onVisibilityChange)
|
||||
window.addEventListener('focus', this.onFocusEvent, false)
|
||||
}
|
||||
|
||||
onVisibilityChange = () => {
|
||||
const visible = document.visibilityState === 'visible'
|
||||
const event = visible ? WebAppEvent.WindowDidFocus : WebAppEvent.WindowDidBlur
|
||||
this.notifyEvent(event)
|
||||
}
|
||||
|
||||
onFocusEvent = () => {
|
||||
this.notifyEvent(WebAppEvent.WindowDidFocus)
|
||||
}
|
||||
|
||||
private notifyEvent(event: WebAppEvent): void {
|
||||
if (this.raceTimeout) {
|
||||
clearTimeout(this.raceTimeout)
|
||||
}
|
||||
this.raceTimeout = setTimeout(() => {
|
||||
this.onEvent(event)
|
||||
}, 250)
|
||||
}
|
||||
|
||||
deinit(): void {
|
||||
document.removeEventListener('visibilitychange', this.onVisibilityChange)
|
||||
window.removeEventListener('focus', this.onFocusEvent)
|
||||
;(this.onEvent as unknown) = undefined
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user