180 lines
4.7 KiB
TypeScript
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)
|
|
}
|