fix: clear desktop web cache on first version launch (#1184)

This commit is contained in:
Mo
2022-06-30 09:20:00 -05:00
committed by GitHub
parent 82d13b41ed
commit 7851e12e9e
21 changed files with 286 additions and 256 deletions

View File

@@ -5,7 +5,7 @@
},
"extends": ["../../.eslintrc.json"],
"rules": {
"no-console": "warn",
"no-console": "off",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-var-requires": "off"
},

View File

@@ -0,0 +1,52 @@
import { action, makeObservable, observable } from 'mobx'
import { MessageType } from '../test/TestIpcMessage'
import { Store } from './javascripts/Main/Store/Store'
import { StoreKeys } from './javascripts/Main/Store/StoreKeys'
import { Paths, Urls } from './javascripts/Main/Types/Paths'
import { UpdateState } from './javascripts/Main/UpdateManager'
import { handleTestMessage } from './javascripts/Main/Utils/Testing'
import { isTesting } from './javascripts/Main/Utils/Utils'
import { WindowState } from './javascripts/Main/Window'
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
public lastRunVersion: string
constructor(app: Electron.App) {
this.version = app.getVersion()
this.store = new Store(Paths.userDataDir)
this.isPrimaryInstance = app.requestSingleInstanceLock()
this.lastRunVersion = this.store.get(StoreKeys.LastRunVersion) || 'unknown'
this.store.set(StoreKeys.LastRunVersion, this.version)
makeObservable(this, {
lastBackupDate: observable,
setBackupCreationDate: action,
})
this.updates = new UpdateState(this)
if (isTesting()) {
handleTestMessage(MessageType.AppStateCall, (method, ...args) => {
;(this as any)[method](...args)
})
}
}
public isRunningVersionForFirstTime(): boolean {
return this.lastRunVersion !== this.version
}
setBackupCreationDate(date: number | null): void {
this.lastBackupDate = date
}
}

View File

@@ -1,52 +1,15 @@
import { App, Shell } from 'electron'
import { action, makeObservable, observable } from 'mobx'
import { MessageType } from '../test/TestIpcMessage'
import { AppState } from './AppState'
import { createExtensionsServer } from './javascripts/Main/ExtensionsServer'
import { Keychain } from './javascripts/Main/Keychain/Keychain'
import { Store, StoreKeys } from './javascripts/Main/Store'
import { StoreKeys } from './javascripts/Main/Store/StoreKeys'
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'
import { isDev } from './javascripts/Main/Utils/Utils'
import { createWindowState } 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
@@ -146,7 +109,6 @@ async function finishApplicationInitialization({ app, shell, state }: { app: App
const keychainWindow = await Keychain.ensureKeychainAccess(state.store)
initializeStrings(app.getLocale())
initializeExtensionsServer(state.store)
const windowState = await createWindowState({
shell,
@@ -157,6 +119,14 @@ async function finishApplicationInitialization({ app, shell, state }: { app: App
},
})
if (state.isRunningVersionForFirstTime()) {
console.log('Clearing window cache')
await windowState.window.webContents.session.clearCache()
}
const host = createExtensionsServer()
state.store.set(StoreKeys.ExtServerHost, host)
/**
* Close the keychain window after the main window is created, otherwise the
* app will quit automatically
@@ -171,9 +141,3 @@ async function finishApplicationInitialization({ app, shell, state }: { app: App
void windowState.window.loadURL(state.startUrl)
}
function initializeExtensionsServer(store: Store) {
const host = createExtensionsServer()
store.set(StoreKeys.ExtServerHost, host)
}

View File

@@ -5,7 +5,8 @@ import path from 'path'
import './@types/modules'
import { initializeApplication } from './application'
import { enableExperimentalFeaturesForFileAccessFix } from './enableExperimentalWebFeatures'
import { Store, StoreKeys } from './javascripts/Main/Store'
import { Store } from './javascripts/Main/Store/Store'
import { StoreKeys } from './javascripts/Main/Store/StoreKeys'
import { isSnap } from './javascripts/Main/Types/Constants'
import { Paths } from './javascripts/Main/Types/Paths'
import { setupTesting } from './javascripts/Main/Utils/Testing'

View File

@@ -2,9 +2,9 @@ import { dialog, shell, WebContents } from 'electron'
import { promises as fs } from 'fs'
import path from 'path'
import { AppMessageType, MessageType } from '../../../../test/TestIpcMessage'
import { AppState } from '../../../application'
import { AppState } from '../../../AppState'
import { MessageToWebApp } from '../../Shared/IpcMessages'
import { StoreKeys } from '../Store'
import { StoreKeys } from '../Store/StoreKeys'
import { backups as str } from '../Strings'
import { Paths } from '../Types/Paths'
import {

View File

@@ -6,6 +6,7 @@ import { URL } from 'url'
import { extensions as str } from './Strings'
import { Paths } from './Types/Paths'
import { FileDoesNotExist } from './Utils/FileUtils'
import { app } from 'electron'
const Protocol = 'http'
@@ -61,7 +62,8 @@ async function handleRequest(request: IncomingMessage, response: ServerResponse)
const mimeType = mime.lookup(path.parse(filePath).ext)
response.setHeader('Access-Control-Allow-Origin', '*')
response.setHeader('Cache-Control', 'max-age=604800')
response.setHeader('Cache-Control', 'no-cache')
response.setHeader('ETag', app.getVersion())
response.setHeader('Content-Type', `${mimeType}; charset=utf-8`)
const data = fs.readFileSync(filePath)

View File

@@ -1,7 +1,7 @@
import { FileBackupsDevice, FileBackupsMapping } from '@web/Application/Device/DesktopSnjsExports'
import { AppState } from 'app/application'
import { AppState } from 'app/AppState'
import { shell } from 'electron'
import { StoreKeys } from '../Store'
import { StoreKeys } from '../Store/StoreKeys'
import path from 'path'
import {
deleteFile,

View File

@@ -1,7 +1,8 @@
import { app, BrowserWindow, ipcMain } from 'electron'
import keytar from 'keytar'
import { MessageToMainProcess } from '../../Shared/IpcMessages'
import { Store, StoreKeys } from '../Store'
import { Store } from '../Store/Store'
import { StoreKeys } from '../Store/StoreKeys'
import { AppName } from '../Strings'
import { keychainAccessIsUserConfigurable } from '../Types/Constants'
import { Paths, Urls } from '../Types/Paths'

View File

@@ -1,5 +1,5 @@
import { BrowserWindow } from 'electron'
import { Store } from '../Store'
import { Store } from '../Store/Store'
export interface KeychainInterface {
ensureKeychainAccess(store: Store): Promise<BrowserWindow | undefined>

View File

@@ -1,4 +1,4 @@
import { AppState } from 'app/application'
import { AppState } from 'app/AppState'
import {
app,
BrowserWindow,
@@ -10,7 +10,8 @@ import {
WebContents,
} from 'electron'
import { autorun } from 'mobx'
import { Store, StoreKeys } from '../Store'
import { Store } from '../Store/Store'
import { StoreKeys } from '../Store/StoreKeys'
import { appMenu as str, contextMenu } from '../Strings'
import { TrayManager } from '../TrayManager'
import { autoUpdatingAvailable } from '../Types/Constants'

View File

@@ -19,11 +19,11 @@ import { downloadFile, getJSON } from './Networking'
import { Component, MappingFile, PackageInfo, PackageManagerInterface, SyncTask } from './PackageManagerInterface'
function logMessage(...message: any) {
log.info('PackageManager:', ...message)
log.info('PackageManager[Info]:', ...message)
}
function logError(...message: any) {
console.error('PackageManager:', ...message)
console.error('PackageManager[Error]:', ...message)
}
/**
@@ -357,10 +357,13 @@ async function installComponent(
function pathsForComponent(component: Pick<Component, 'content'>) {
const relativePath = path.join(Paths.extensionsDirRelative, component.content!.package_info.identifier)
const absolutePath = path.join(Paths.userDataDir, relativePath)
const downloadPath = path.join(Paths.tempDir, AppName, 'downloads', component.content!.name + '.zip')
return {
relativePath,
absolutePath: path.join(Paths.userDataDir, relativePath),
downloadPath: path.join(Paths.tempDir, AppName, 'downloads', component.content!.name + '.zip'),
absolutePath,
downloadPath,
}
}

View File

@@ -1,5 +1,6 @@
import { CrossProcessBridge } from '../../Renderer/CrossProcessBridge'
import { Store, StoreKeys } from '../Store'
import { Store } from '../Store/Store'
import { StoreKeys } from '../Store/StoreKeys'
const path = require('path')
const rendererPath = path.join('file://', __dirname, '/renderer.js')

View File

@@ -1,5 +1,6 @@
/* eslint-disable no-inline-comments */
import { Store, StoreKeys } from './Store'
import { Store } from './Store/Store'
import { StoreKeys } from './Store/StoreKeys'
import { isMac } from './Types/Platforms'
import { isDev } from './Utils/Utils'

View File

@@ -1,185 +0,0 @@
import fs from 'fs'
import path from 'path'
import { MessageType } from '../../../test/TestIpcMessage'
import { BackupsDirectoryName } from './Backups/BackupsManager'
import { Language } from './SpellcheckerManager'
import { FileDoesNotExist } from './Utils/FileUtils'
import { handleTestMessage } from './Utils/Testing'
import { ensureIsBoolean, isBoolean, isDev, isTesting } from './Utils/Utils'
const app = process.type === 'browser' ? require('electron').app : require('@electron/remote').app
function logError(...message: any) {
console.error('store:', ...message)
}
export enum StoreKeys {
ExtServerHost = 'extServerHost',
UseSystemMenuBar = 'useSystemMenuBar',
MenuBarVisible = 'isMenuBarVisible',
BackupsLocation = 'backupsLocation',
BackupsDisabled = 'backupsDisabled',
MinimizeToTray = 'minimizeToTray',
EnableAutoUpdate = 'enableAutoUpdates',
ZoomFactor = 'zoomFactor',
SelectedSpellCheckerLanguageCodes = 'selectedSpellCheckerLanguageCodes',
UseNativeKeychain = 'useNativeKeychain',
FileBackupsEnabled = 'fileBackupsEnabled',
FileBackupsLocation = 'fileBackupsLocation',
}
interface StoreData {
[StoreKeys.ExtServerHost]: string
[StoreKeys.UseSystemMenuBar]: boolean
[StoreKeys.MenuBarVisible]: boolean
[StoreKeys.BackupsLocation]: string
[StoreKeys.BackupsDisabled]: boolean
[StoreKeys.MinimizeToTray]: boolean
[StoreKeys.EnableAutoUpdate]: boolean
[StoreKeys.UseNativeKeychain]: boolean | null
[StoreKeys.ZoomFactor]: number
[StoreKeys.SelectedSpellCheckerLanguageCodes]: Set<Language> | null
[StoreKeys.FileBackupsEnabled]: boolean
[StoreKeys.FileBackupsLocation]: string
}
function createSanitizedStoreData(data: any = {}): StoreData {
return {
[StoreKeys.MenuBarVisible]: ensureIsBoolean(data[StoreKeys.MenuBarVisible], true),
[StoreKeys.UseSystemMenuBar]: ensureIsBoolean(data[StoreKeys.UseSystemMenuBar], false),
[StoreKeys.BackupsDisabled]: ensureIsBoolean(data[StoreKeys.BackupsDisabled], false),
[StoreKeys.MinimizeToTray]: ensureIsBoolean(data[StoreKeys.MinimizeToTray], false),
[StoreKeys.EnableAutoUpdate]: ensureIsBoolean(data[StoreKeys.EnableAutoUpdate], true),
[StoreKeys.UseNativeKeychain]: isBoolean(data[StoreKeys.UseNativeKeychain])
? data[StoreKeys.UseNativeKeychain]
: null,
[StoreKeys.ExtServerHost]: data[StoreKeys.ExtServerHost],
[StoreKeys.BackupsLocation]: sanitizeBackupsLocation(data[StoreKeys.BackupsLocation]),
[StoreKeys.ZoomFactor]: sanitizeZoomFactor(data[StoreKeys.ZoomFactor]),
[StoreKeys.SelectedSpellCheckerLanguageCodes]: sanitizeSpellCheckerLanguageCodes(
data[StoreKeys.SelectedSpellCheckerLanguageCodes],
),
[StoreKeys.FileBackupsEnabled]: ensureIsBoolean(data[StoreKeys.FileBackupsEnabled], false),
[StoreKeys.FileBackupsLocation]: data[StoreKeys.FileBackupsLocation],
}
}
function sanitizeZoomFactor(factor?: any): number {
if (typeof factor === 'number' && factor > 0) {
return factor
} else {
return 1
}
}
function sanitizeBackupsLocation(location?: unknown): string {
const defaultPath = path.join(
isTesting() ? app.getPath('userData') : isDev() ? app.getPath('documents') : app.getPath('home'),
BackupsDirectoryName,
)
if (typeof location !== 'string') {
return defaultPath
}
try {
const stat = fs.lstatSync(location)
if (stat.isDirectory()) {
return location
}
/** Path points to something other than a directory */
return defaultPath
} catch (e) {
/** Path does not point to a valid directory */
logError(e)
return defaultPath
}
}
function sanitizeSpellCheckerLanguageCodes(languages?: unknown): Set<Language> | null {
if (!languages) {
return null
}
if (!Array.isArray(languages)) {
return null
}
const set = new Set<Language>()
const validLanguages = Object.values(Language)
for (const language of languages) {
if (validLanguages.includes(language)) {
set.add(language)
}
}
return set
}
export function serializeStoreData(data: StoreData): string {
return JSON.stringify(data, (_key, value) => {
if (value instanceof Set) {
return Array.from(value)
}
return value
})
}
function parseDataFile(filePath: string) {
try {
const fileData = fs.readFileSync(filePath)
const userData = JSON.parse(fileData.toString())
return createSanitizedStoreData(userData)
} catch (error: any) {
console.log('Error reading store file', error)
if (error.code !== FileDoesNotExist) {
logError(error)
}
return createSanitizedStoreData({})
}
}
export class Store {
static instance: Store
readonly path: string
readonly data: StoreData
static getInstance(): Store {
if (!this.instance) {
/**
* Renderer process has to get `app` module via `remote`, whereas the main process
* can get it directly app.getPath('userData') will return a string of the user's
* app data directory path.
* TODO(baptiste): stop using Store in the renderer process.
*/
const userDataPath = app.getPath('userData')
this.instance = new Store(userDataPath)
}
return this.instance
}
static get<T extends keyof StoreData>(key: T): StoreData[T] {
return this.getInstance().get(key)
}
constructor(userDataPath: string) {
this.path = path.join(userDataPath, 'user-preferences.json')
this.data = parseDataFile(this.path)
if (isTesting()) {
handleTestMessage(MessageType.StoreSettingsLocation, () => this.path)
handleTestMessage(MessageType.StoreSet, (key, value) => {
this.set(key, value)
})
}
}
get<T extends keyof StoreData>(key: T): StoreData[T] {
return this.data[key]
}
set<T extends keyof StoreData>(key: T, val: StoreData[T]): void {
this.data[key] = val
fs.writeFileSync(this.path, serializeStoreData(this.data))
}
}

View File

@@ -0,0 +1,52 @@
import fs from 'fs'
import path from 'path'
import { MessageType } from '../../../../test/TestIpcMessage'
import { handleTestMessage } from '../Utils/Testing'
import { isTesting } from '../Utils/Utils'
import { parseDataFile, serializeStoreData } from './createSanitizedStoreData'
import { StoreData } from './StoreKeys'
export const app = process.type === 'browser' ? require('electron').app : require('@electron/remote').app
export function logError(...message: any) {
console.error('store:', ...message)
}
export class Store {
static instance: Store
readonly path: string
readonly data: StoreData
static getInstance(): Store {
if (!this.instance) {
const userDataPath = app.getPath('userData')
this.instance = new Store(userDataPath)
}
return this.instance
}
static get<T extends keyof StoreData>(key: T): StoreData[T] {
return this.getInstance().get(key)
}
constructor(userDataPath: string) {
this.path = path.join(userDataPath, 'user-preferences.json')
this.data = parseDataFile(this.path)
if (isTesting()) {
handleTestMessage(MessageType.StoreSettingsLocation, () => this.path)
handleTestMessage(MessageType.StoreSet, (key, value) => {
this.set(key, value)
})
}
}
get<T extends keyof StoreData>(key: T): StoreData[T] {
return this.data[key]
}
set<T extends keyof StoreData>(key: T, val: StoreData[T]): void {
this.data[key] = val
fs.writeFileSync(this.path, serializeStoreData(this.data))
}
}

View File

@@ -0,0 +1,33 @@
import { Language } from '../SpellcheckerManager'
export enum StoreKeys {
ExtServerHost = 'extServerHost',
UseSystemMenuBar = 'useSystemMenuBar',
MenuBarVisible = 'isMenuBarVisible',
BackupsLocation = 'backupsLocation',
BackupsDisabled = 'backupsDisabled',
MinimizeToTray = 'minimizeToTray',
EnableAutoUpdate = 'enableAutoUpdates',
ZoomFactor = 'zoomFactor',
SelectedSpellCheckerLanguageCodes = 'selectedSpellCheckerLanguageCodes',
UseNativeKeychain = 'useNativeKeychain',
FileBackupsEnabled = 'fileBackupsEnabled',
FileBackupsLocation = 'fileBackupsLocation',
LastRunVersion = 'LastRunVersion',
}
export interface StoreData {
[StoreKeys.ExtServerHost]: string
[StoreKeys.UseSystemMenuBar]: boolean
[StoreKeys.MenuBarVisible]: boolean
[StoreKeys.BackupsLocation]: string
[StoreKeys.BackupsDisabled]: boolean
[StoreKeys.MinimizeToTray]: boolean
[StoreKeys.EnableAutoUpdate]: boolean
[StoreKeys.UseNativeKeychain]: boolean | null
[StoreKeys.ZoomFactor]: number
[StoreKeys.SelectedSpellCheckerLanguageCodes]: Set<Language> | null
[StoreKeys.FileBackupsEnabled]: boolean
[StoreKeys.FileBackupsLocation]: string
[StoreKeys.LastRunVersion]: string
}

View File

@@ -0,0 +1,101 @@
import fs from 'fs'
import path from 'path'
import { BackupsDirectoryName } from '../Backups/BackupsManager'
import { Language } from '../SpellcheckerManager'
import { FileDoesNotExist } from '../Utils/FileUtils'
import { ensureIsBoolean, isBoolean, isDev, isTesting } from '../Utils/Utils'
import { StoreData, StoreKeys } from './StoreKeys'
import { app, logError } from './Store'
export function createSanitizedStoreData(data: any = {}): StoreData {
return {
[StoreKeys.MenuBarVisible]: ensureIsBoolean(data[StoreKeys.MenuBarVisible], true),
[StoreKeys.UseSystemMenuBar]: ensureIsBoolean(data[StoreKeys.UseSystemMenuBar], false),
[StoreKeys.BackupsDisabled]: ensureIsBoolean(data[StoreKeys.BackupsDisabled], false),
[StoreKeys.MinimizeToTray]: ensureIsBoolean(data[StoreKeys.MinimizeToTray], false),
[StoreKeys.EnableAutoUpdate]: ensureIsBoolean(data[StoreKeys.EnableAutoUpdate], true),
[StoreKeys.UseNativeKeychain]: isBoolean(data[StoreKeys.UseNativeKeychain])
? data[StoreKeys.UseNativeKeychain]
: null,
[StoreKeys.ExtServerHost]: data[StoreKeys.ExtServerHost],
[StoreKeys.BackupsLocation]: sanitizeBackupsLocation(data[StoreKeys.BackupsLocation]),
[StoreKeys.ZoomFactor]: sanitizeZoomFactor(data[StoreKeys.ZoomFactor]),
[StoreKeys.SelectedSpellCheckerLanguageCodes]: sanitizeSpellCheckerLanguageCodes(
data[StoreKeys.SelectedSpellCheckerLanguageCodes],
),
[StoreKeys.FileBackupsEnabled]: ensureIsBoolean(data[StoreKeys.FileBackupsEnabled], false),
[StoreKeys.FileBackupsLocation]: data[StoreKeys.FileBackupsLocation],
[StoreKeys.LastRunVersion]: data[StoreKeys.LastRunVersion],
}
}
function sanitizeZoomFactor(factor?: any): number {
if (typeof factor === 'number' && factor > 0) {
return factor
} else {
return 1
}
}
function sanitizeBackupsLocation(location?: unknown): string {
const defaultPath = path.join(
isTesting() ? app.getPath('userData') : isDev() ? app.getPath('documents') : app.getPath('home'),
BackupsDirectoryName,
)
if (typeof location !== 'string') {
return defaultPath
}
try {
const stat = fs.lstatSync(location)
if (stat.isDirectory()) {
return location
}
/** Path points to something other than a directory */
return defaultPath
} catch (e) {
/** Path does not point to a valid directory */
logError(e)
return defaultPath
}
}
function sanitizeSpellCheckerLanguageCodes(languages?: unknown): Set<Language> | null {
if (!languages) {
return null
}
if (!Array.isArray(languages)) {
return null
}
const set = new Set<Language>()
const validLanguages = Object.values(Language)
for (const language of languages) {
if (validLanguages.includes(language)) {
set.add(language)
}
}
return set
}
export function serializeStoreData(data: StoreData): string {
return JSON.stringify(data, (_key, value) => {
if (value instanceof Set) {
return Array.from(value)
}
return value
})
}
export function parseDataFile(filePath: string) {
try {
const fileData = fs.readFileSync(filePath)
const userData = JSON.parse(fileData.toString())
return createSanitizedStoreData(userData)
} catch (error: any) {
console.log('Error reading store file', error)
if (error.code !== FileDoesNotExist) {
logError(error)
}
return createSanitizedStoreData({})
}
}

View File

@@ -1,6 +1,7 @@
import { Menu, Tray } from 'electron'
import path from 'path'
import { Store, StoreKeys } from './Store'
import { Store } from './Store/Store'
import { StoreKeys } from './Store/StoreKeys'
import { AppName, tray as str } from './Strings'
import { isLinux, isWindows } from './Types/Platforms'
import { isDev } from './Utils/Utils'

View File

@@ -4,9 +4,9 @@ import electronLog from 'electron-log'
import { autoUpdater } from 'electron-updater'
import { action, autorun, computed, makeObservable, observable } from 'mobx'
import { MessageType } from '../../../test/TestIpcMessage'
import { AppState } from '../../application'
import { AppState } from '../../AppState'
import { BackupsManagerInterface } from './Backups/BackupsManagerInterface'
import { StoreKeys } from './Store'
import { StoreKeys } from './Store/StoreKeys'
import { updates as str } from './Strings'
import { autoUpdatingAvailable } from './Types/Constants'
import { handleTestMessage } from './Utils/Testing'

View File

@@ -4,7 +4,7 @@ import fs from 'fs'
import { debounce } from 'lodash'
import path from 'path'
import { AppMessageType, MessageType } from '../../../test/TestIpcMessage'
import { AppState } from '../../application'
import { AppState } from '../../AppState'
import { MessageToWebApp } from '../Shared/IpcMessages'
import { createBackupsManager } from './Backups/BackupsManager'
import { BackupsManagerInterface } from './Backups/BackupsManagerInterface'
@@ -16,7 +16,8 @@ import { initializePackageManager } from './Packages/PackageManager'
import { RemoteBridge } from './Remote/RemoteBridge'
import { initializeSearchManager } from './Search/SearchManager'
import { createSpellcheckerManager } from './SpellcheckerManager'
import { Store, StoreKeys } from './Store'
import { Store } from './Store/Store'
import { StoreKeys } from './Store/StoreKeys'
import { createTrayManager, TrayManager } from './TrayManager'
import { Paths } from './Types/Paths'
import { isMac, isWindows } from './Types/Platforms'

View File

@@ -1,5 +1,6 @@
import { BrowserWindow } from 'electron'
import { Store, StoreKeys } from './Store'
import { Store } from './Store/Store'
import { StoreKeys } from './Store/StoreKeys'
export function initializeZoomManager(window: BrowserWindow, store: Store): void {
window.webContents.on('dom-ready', () => {