Files
standardnotes-app-web/packages/desktop/app/application.ts
2022-06-09 14:55:00 -05:00

180 lines
4.7 KiB
TypeScript

import { App, Shell } from 'electron'
import { action, makeObservable, observable } from 'mobx'
import { MessageType } from '../test/TestIpcMessage'
import { createExtensionsServer } from './javascripts/Main/ExtensionsServer'
import { Keychain } from './javascripts/Main/Keychain/Keychain'
import { Store, StoreKeys } from './javascripts/Main/Store'
import { AppName, initializeStrings } from './javascripts/Main/Strings'
import { Paths, Urls } from './javascripts/Main/Types/Paths'
import { isLinux, isMac, isWindows } from './javascripts/Main/Types/Platforms'
import { UpdateState } from './javascripts/Main/UpdateManager'
import { handleTestMessage } from './javascripts/Main/Utils/Testing'
import { isDev, isTesting } from './javascripts/Main/Utils/Utils'
import { createWindowState, WindowState } from './javascripts/Main/Window'
const deepLinkScheme = 'standardnotes'
export class AppState {
readonly version: string
readonly store: Store
readonly startUrl = Urls.indexHtml
readonly isPrimaryInstance: boolean
public willQuitApp = false
public lastBackupDate: number | null = null
public windowState?: WindowState
public deepLinkUrl?: string
public readonly updates: UpdateState
constructor(app: Electron.App) {
this.version = app.getVersion()
this.store = new Store(Paths.userDataDir)
this.isPrimaryInstance = app.requestSingleInstanceLock()
makeObservable(this, {
lastBackupDate: observable,
setBackupCreationDate: action,
})
this.updates = new UpdateState(this)
if (isTesting()) {
handleTestMessage(MessageType.AppStateCall, (method, ...args) => {
;(this as any)[method](...args)
})
}
}
setBackupCreationDate(date: number | null): void {
this.lastBackupDate = date
}
}
export function initializeApplication(args: { app: Electron.App; ipcMain: Electron.IpcMain; shell: Shell }): void {
const { app } = args
app.name = AppName
const state = new AppState(app)
void setupDeepLinking(app)
registerSingleInstanceHandler(app, state)
registerAppEventListeners({
...args,
state,
})
if (isDev()) {
/** Expose the app's state as a global variable. Useful for debugging */
;(global as any).appState = state
}
}
function focusWindow(appState: AppState) {
const window = appState.windowState?.window
if (window) {
if (!window.isVisible()) {
window.show()
}
if (window.isMinimized()) {
window.restore()
}
window.focus()
}
}
function registerSingleInstanceHandler(app: Electron.App, appState: AppState) {
app.on('second-instance', (_event: Event, argv: string[]) => {
if (isWindows()) {
appState.deepLinkUrl = argv.find((arg) => arg.startsWith(deepLinkScheme))
}
/* Someone tried to run a second instance, we should focus our window. */
focusWindow(appState)
})
}
function registerAppEventListeners(args: {
app: Electron.App
ipcMain: Electron.IpcMain
shell: Shell
state: AppState
}) {
const { app, state } = args
app.on('window-all-closed', () => {
if (!isMac()) {
app.quit()
}
})
app.on('before-quit', () => {
state.willQuitApp = true
})
app.on('activate', () => {
const windowState = state.windowState
if (!windowState) {
return
}
windowState.window.show()
})
app.on('open-url', (_event, url) => {
state.deepLinkUrl = url
focusWindow(state)
})
app.on('ready', () => {
if (!state.isPrimaryInstance) {
console.warn('Quiting app and focusing existing instance.')
app.quit()
return
}
void finishApplicationInitialization(args)
})
}
async function setupDeepLinking(app: Electron.App) {
if (!app.isDefaultProtocolClient(deepLinkScheme)) {
app.setAsDefaultProtocolClient(deepLinkScheme)
}
}
async function finishApplicationInitialization({ app, shell, state }: { app: App; shell: Shell; state: AppState }) {
const keychainWindow = await Keychain.ensureKeychainAccess(state.store)
initializeStrings(app.getLocale())
initializeExtensionsServer(state.store)
const windowState = await createWindowState({
shell,
appState: state,
appLocale: app.getLocale(),
teardown() {
state.windowState = undefined
},
})
/**
* Close the keychain window after the main window is created, otherwise the
* app will quit automatically
*/
keychainWindow?.close()
state.windowState = windowState
if ((isWindows() || isLinux()) && state.windowState.trayManager.shouldMinimizeToTray()) {
state.windowState.trayManager.createTrayIcon()
}
void windowState.window.loadURL(state.startUrl)
}
function initializeExtensionsServer(store: Store) {
const host = createExtensionsServer()
store.set(StoreKeys.ExtServerHost, host)
}