You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-10 22:11:50 +02:00
This commit is contained in:
@@ -60,6 +60,9 @@ import { PackageInfo } from '@joplin/lib/versionInfo';
|
|||||||
import { CustomProtocolHandler } from './utils/customProtocols/handleCustomProtocols';
|
import { CustomProtocolHandler } from './utils/customProtocols/handleCustomProtocols';
|
||||||
import { refreshFolders } from '@joplin/lib/folders-screen-utils';
|
import { refreshFolders } from '@joplin/lib/folders-screen-utils';
|
||||||
import initializeCommandService from './utils/initializeCommandService';
|
import initializeCommandService from './utils/initializeCommandService';
|
||||||
|
import PerformanceLogger from '@joplin/lib/PerformanceLogger';
|
||||||
|
|
||||||
|
const perfLogger = PerformanceLogger.create('app-desktop/app');
|
||||||
|
|
||||||
const pluginClasses = [
|
const pluginClasses = [
|
||||||
require('./plugins/GotoAnything').default,
|
require('./plugins/GotoAnything').default,
|
||||||
@@ -67,6 +70,8 @@ const pluginClasses = [
|
|||||||
|
|
||||||
const appDefaultState = createAppDefaultState(resourceEditWatcherDefaultState);
|
const appDefaultState = createAppDefaultState(resourceEditWatcherDefaultState);
|
||||||
|
|
||||||
|
type StartupTask = { label: string; task: ()=> void|Promise<void> };
|
||||||
|
|
||||||
class Application extends BaseApplication {
|
class Application extends BaseApplication {
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
@@ -411,56 +416,53 @@ class Application extends BaseApplication {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
private buildStartupTasks_() {
|
||||||
public async start(argv: string[], startOptions: StartOptions = null): Promise<any> {
|
const tasks: StartupTask[] = [];
|
||||||
// If running inside a package, the command line, instead of being "node.exe <path> <flags>" is "joplin.exe <flags>" so
|
const addTask = (label: string, task: StartupTask['task']) => {
|
||||||
// insert an extra argument so that they can be processed in a consistent way everywhere.
|
tasks.push({ label, task });
|
||||||
if (!bridge().electronIsDev()) argv.splice(1, 0, '.');
|
};
|
||||||
|
|
||||||
argv = await super.start(argv, startOptions);
|
addTask('set up extra debug logging', () => {
|
||||||
|
reg.logger().info('app.start: doing regular boot');
|
||||||
|
const dir: string = Setting.value('profileDir');
|
||||||
|
|
||||||
await this.setupIntegrationTestUtils();
|
syncDebugLog.enabled = false;
|
||||||
|
|
||||||
bridge().setLogFilePath(Logger.globalLogger.logFilePath());
|
if (dir.endsWith('dev-desktop-2')) {
|
||||||
|
syncDebugLog.addTarget(TargetType.File, {
|
||||||
|
path: `${homedir()}/synclog.txt`,
|
||||||
|
});
|
||||||
|
syncDebugLog.enabled = true;
|
||||||
|
syncDebugLog.info(`Profile dir: ${dir}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await this.applySettingsSideEffects();
|
addTask('set up registry', () => {
|
||||||
|
reg.setDispatch(this.dispatch.bind(this));
|
||||||
|
reg.setShowErrorMessageBoxHandler((message: string) => { bridge().showErrorMessageBox(message); });
|
||||||
|
});
|
||||||
|
|
||||||
if (Setting.value('sync.upgradeState') === Setting.SYNC_UPGRADE_STATE_MUST_DO) {
|
addTask('set up auto updater', () => {
|
||||||
reg.logger().info('app.start: doing upgradeSyncTarget action');
|
this.setupAutoUpdaterService();
|
||||||
bridge().mainWindow().show();
|
});
|
||||||
return { action: 'upgradeSyncTarget' };
|
|
||||||
}
|
|
||||||
|
|
||||||
reg.logger().info('app.start: doing regular boot');
|
addTask('set up AlarmService', () => {
|
||||||
|
AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId }));
|
||||||
const dir: string = Setting.value('profileDir');
|
AlarmService.setLogger(reg.logger());
|
||||||
|
});
|
||||||
syncDebugLog.enabled = false;
|
|
||||||
|
|
||||||
if (dir.endsWith('dev-desktop-2')) {
|
|
||||||
syncDebugLog.addTarget(TargetType.File, {
|
|
||||||
path: `${homedir()}/synclog.txt`,
|
|
||||||
});
|
|
||||||
syncDebugLog.enabled = true;
|
|
||||||
syncDebugLog.info(`Profile dir: ${dir}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setupAutoUpdaterService();
|
|
||||||
|
|
||||||
AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId }));
|
|
||||||
AlarmService.setLogger(reg.logger());
|
|
||||||
|
|
||||||
reg.setDispatch(this.dispatch.bind(this));
|
|
||||||
reg.setShowErrorMessageBoxHandler((message: string) => { bridge().showErrorMessageBox(message); });
|
|
||||||
|
|
||||||
if (Setting.value('flagOpenDevTools')) {
|
if (Setting.value('flagOpenDevTools')) {
|
||||||
bridge().openDevTools();
|
addTask('openDevTools', () => {
|
||||||
|
bridge().openDevTools();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.protocolHandler_ = bridge().electronApp().getCustomProtocolHandler();
|
addTask('set up custom protocol handler', async () => {
|
||||||
this.protocolHandler_.allowReadAccessToDirectory(__dirname); // App bundle directory
|
this.protocolHandler_ = bridge().electronApp().getCustomProtocolHandler();
|
||||||
this.protocolHandler_.allowReadAccessToDirectory(Setting.value('cacheDir'));
|
this.protocolHandler_.allowReadAccessToDirectory(__dirname); // App bundle directory
|
||||||
this.protocolHandler_.allowReadAccessToDirectory(Setting.value('resourceDir'));
|
this.protocolHandler_.allowReadAccessToDirectory(Setting.value('cacheDir'));
|
||||||
|
this.protocolHandler_.allowReadAccessToDirectory(Setting.value('resourceDir'));
|
||||||
|
});
|
||||||
// this.protocolHandler_.allowReadAccessTo(Setting.value('tempDir'));
|
// this.protocolHandler_.allowReadAccessTo(Setting.value('tempDir'));
|
||||||
// For now, this doesn't seem necessary:
|
// For now, this doesn't seem necessary:
|
||||||
// this.protocolHandler_.allowReadAccessTo(Setting.value('profileDir'));
|
// this.protocolHandler_.allowReadAccessTo(Setting.value('profileDir'));
|
||||||
@@ -468,44 +470,52 @@ class Application extends BaseApplication {
|
|||||||
// handler, and, as such, it may make sense to also limit permissions of
|
// handler, and, as such, it may make sense to also limit permissions of
|
||||||
// allowed pages with a Content Security Policy.
|
// allowed pages with a Content Security Policy.
|
||||||
|
|
||||||
PluginManager.instance().dispatch_ = this.dispatch.bind(this);
|
addTask('initialize PluginManager, redux, CommandService, and KeymapService', async () => {
|
||||||
PluginManager.instance().setLogger(reg.logger());
|
PluginManager.instance().dispatch_ = this.dispatch.bind(this);
|
||||||
PluginManager.instance().register(pluginClasses);
|
PluginManager.instance().setLogger(reg.logger());
|
||||||
|
PluginManager.instance().register(pluginClasses);
|
||||||
|
|
||||||
this.initRedux();
|
this.initRedux();
|
||||||
|
|
||||||
PerFolderSortOrderService.initialize();
|
initializeCommandService(this.store(), Setting.value('env') === 'dev');
|
||||||
|
|
||||||
initializeCommandService(this.store(), Setting.value('env') === 'dev');
|
const keymapService = KeymapService.instance();
|
||||||
|
// We only add the commands that appear in the menu because only
|
||||||
|
// those can have a shortcut associated with them.
|
||||||
|
keymapService.initialize(menuCommandNames());
|
||||||
|
|
||||||
const keymapService = KeymapService.instance();
|
try {
|
||||||
// We only add the commands that appear in the menu because only
|
await keymapService.loadCustomKeymap(`${Setting.value('profileDir')}/keymap-desktop.json`);
|
||||||
// those can have a shortcut associated with them.
|
} catch (error) {
|
||||||
keymapService.initialize(menuCommandNames());
|
reg.logger().error(error);
|
||||||
|
}
|
||||||
try {
|
|
||||||
await keymapService.loadCustomKeymap(`${dir}/keymap-desktop.json`);
|
|
||||||
} catch (error) {
|
|
||||||
reg.logger().error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since the settings need to be loaded before the store is
|
|
||||||
// created, it will never receive the SETTING_UPDATE_ALL even,
|
|
||||||
// which mean state.settings will not be initialised. So we
|
|
||||||
// manually call dispatchUpdateAll() to force an update.
|
|
||||||
Setting.dispatchUpdateAll();
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
|
||||||
await refreshFolders((action: any) => this.dispatch(action), '');
|
|
||||||
|
|
||||||
const tags = await Tag.allWithNotes();
|
|
||||||
|
|
||||||
this.dispatch({
|
|
||||||
type: 'TAG_UPDATE_ALL',
|
|
||||||
items: tags,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.setupCustomCss();
|
addTask('initialize PerFolderSortOrderService', () => {
|
||||||
|
PerFolderSortOrderService.initialize();
|
||||||
|
});
|
||||||
|
|
||||||
|
addTask('dispatch initial settings', () => {
|
||||||
|
// Since the settings need to be loaded before the store is
|
||||||
|
// created, it will never receive the SETTING_UPDATE_ALL even,
|
||||||
|
// which mean state.settings will not be initialised. So we
|
||||||
|
// manually call dispatchUpdateAll() to force an update.
|
||||||
|
Setting.dispatchUpdateAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
addTask('update folders and tags', async () => {
|
||||||
|
await refreshFolders((action) => this.dispatch(action), '');
|
||||||
|
|
||||||
|
const tags = await Tag.allWithNotes();
|
||||||
|
this.dispatch({
|
||||||
|
type: 'TAG_UPDATE_ALL',
|
||||||
|
items: tags,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
addTask('set up custom CSS', async () => {
|
||||||
|
await this.setupCustomCss();
|
||||||
|
});
|
||||||
|
|
||||||
// const masterKeys = await MasterKey.all();
|
// const masterKeys = await MasterKey.all();
|
||||||
|
|
||||||
@@ -514,188 +524,237 @@ class Application extends BaseApplication {
|
|||||||
// items: masterKeys,
|
// items: masterKeys,
|
||||||
// });
|
// });
|
||||||
|
|
||||||
const getNotesParent = async () => {
|
addTask('send initial selection to redux', async () => {
|
||||||
let notesParent = parseNotesParent(Setting.value('notesParent'), Setting.value('activeFolderId'));
|
const getNotesParent = async () => {
|
||||||
if (notesParent.type === 'Tag' && !(await Tag.load(notesParent.selectedItemId))) {
|
let notesParent = parseNotesParent(Setting.value('notesParent'), Setting.value('activeFolderId'));
|
||||||
notesParent = {
|
if (notesParent.type === 'Tag' && !(await Tag.load(notesParent.selectedItemId))) {
|
||||||
type: 'Folder',
|
notesParent = {
|
||||||
selectedItemId: Setting.value('activeFolderId'),
|
type: 'Folder',
|
||||||
};
|
selectedItemId: Setting.value('activeFolderId'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return notesParent;
|
||||||
|
};
|
||||||
|
|
||||||
|
const notesParent = await getNotesParent();
|
||||||
|
if (notesParent.type === 'SmartFilter') {
|
||||||
|
this.store().dispatch({
|
||||||
|
type: 'SMART_FILTER_SELECT',
|
||||||
|
id: notesParent.selectedItemId,
|
||||||
|
});
|
||||||
|
} else if (notesParent.type === 'Tag') {
|
||||||
|
this.store().dispatch({
|
||||||
|
type: 'TAG_SELECT',
|
||||||
|
id: notesParent.selectedItemId,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.store().dispatch({
|
||||||
|
type: 'FOLDER_SELECT',
|
||||||
|
id: notesParent.selectedItemId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return notesParent;
|
|
||||||
};
|
|
||||||
|
|
||||||
const notesParent = await getNotesParent();
|
this.store().dispatch({
|
||||||
|
type: 'FOLDER_SET_COLLAPSED_ALL',
|
||||||
|
ids: Setting.value('collapsedFolderIds'),
|
||||||
|
});
|
||||||
|
|
||||||
if (notesParent.type === 'SmartFilter') {
|
|
||||||
this.store().dispatch({
|
this.store().dispatch({
|
||||||
type: 'SMART_FILTER_SELECT',
|
type: 'NOTE_DEVTOOLS_SET',
|
||||||
id: notesParent.selectedItemId,
|
value: Setting.value('flagOpenDevTools'),
|
||||||
});
|
});
|
||||||
} else if (notesParent.type === 'Tag') {
|
|
||||||
this.store().dispatch({
|
|
||||||
type: 'TAG_SELECT',
|
|
||||||
id: notesParent.selectedItemId,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.store().dispatch({
|
|
||||||
type: 'FOLDER_SELECT',
|
|
||||||
id: notesParent.selectedItemId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.store().dispatch({
|
|
||||||
type: 'FOLDER_SET_COLLAPSED_ALL',
|
|
||||||
ids: Setting.value('collapsedFolderIds'),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.store().dispatch({
|
addTask('initializeUserFetcher', async () => {
|
||||||
type: 'NOTE_DEVTOOLS_SET',
|
initializeUserFetcher();
|
||||||
value: Setting.value('flagOpenDevTools'),
|
shim.setInterval(() => { void userFetcher(); }, 1000 * 60 * 60);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Always disable on Mac for now - and disable too for the few apps that may have the flag enabled.
|
addTask('updateTray', () => this.updateTray());
|
||||||
// At present, it only seems to work on Windows.
|
|
||||||
if (shim.isMac()) {
|
|
||||||
Setting.setValue('featureFlag.autoUpdaterServiceEnabled', false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: Auto-update is a misnomer in the code.
|
addTask('set main window state', () => {
|
||||||
// The code below only checks, if a new version is available.
|
if (Setting.value('startMinimized') && Setting.value('showTrayIcon')) {
|
||||||
// We only allow Windows and macOS users to automatically check for updates
|
bridge().mainWindow().hide();
|
||||||
if (!Setting.value('featureFlag.autoUpdaterServiceEnabled')) {
|
} else {
|
||||||
if (shim.isWindows() || shim.isMac()) {
|
bridge().mainWindow().show();
|
||||||
const runAutoUpdateCheck = () => {
|
|
||||||
if (Setting.value('autoUpdateEnabled')) {
|
|
||||||
void checkForUpdates(true, bridge().mainWindow(), { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initial check on startup
|
|
||||||
shim.setTimeout(() => { runAutoUpdateCheck(); }, 5000);
|
|
||||||
// Then every x hours
|
|
||||||
shim.setInterval(() => { runAutoUpdateCheck(); }, 12 * 60 * 60 * 1000);
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
initializeUserFetcher();
|
addTask('start maintenance tasks', () => {
|
||||||
shim.setInterval(() => { void userFetcher(); }, 1000 * 60 * 60);
|
// Always disable on Mac for now - and disable too for the few apps that may have the flag enabled.
|
||||||
|
// At present, it only seems to work on Windows.
|
||||||
|
if (shim.isMac()) {
|
||||||
|
Setting.setValue('featureFlag.autoUpdaterServiceEnabled', false);
|
||||||
|
}
|
||||||
|
|
||||||
this.updateTray();
|
// Note: Auto-update is a misnomer in the code.
|
||||||
|
// The code below only checks, if a new version is available.
|
||||||
|
// We only allow Windows and macOS users to automatically check for updates
|
||||||
|
if (!Setting.value('featureFlag.autoUpdaterServiceEnabled')) {
|
||||||
|
if (shim.isWindows() || shim.isMac()) {
|
||||||
|
const runAutoUpdateCheck = () => {
|
||||||
|
if (Setting.value('autoUpdateEnabled')) {
|
||||||
|
void checkForUpdates(true, bridge().mainWindow(), { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
shim.setTimeout(() => {
|
// Initial check on startup
|
||||||
void AlarmService.garbageCollect();
|
shim.setTimeout(() => { runAutoUpdateCheck(); }, 5000);
|
||||||
}, 1000 * 60 * 60);
|
// Then every x hours
|
||||||
|
shim.setInterval(() => { runAutoUpdateCheck(); }, 12 * 60 * 60 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Setting.value('startMinimized') && Setting.value('showTrayIcon')) {
|
shim.setTimeout(() => {
|
||||||
bridge().mainWindow().hide();
|
void AlarmService.garbageCollect();
|
||||||
} else {
|
}, 1000 * 60 * 60);
|
||||||
bridge().mainWindow().show();
|
void ShareService.instance().maintenance();
|
||||||
}
|
|
||||||
|
|
||||||
void ShareService.instance().maintenance();
|
ResourceService.runInBackground();
|
||||||
|
|
||||||
ResourceService.runInBackground();
|
if (Setting.value('env') === 'dev') {
|
||||||
|
|
||||||
if (Setting.value('env') === 'dev') {
|
|
||||||
void AlarmService.updateAllNotifications();
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
|
|
||||||
void reg.scheduleSync(1000).then(() => {
|
|
||||||
// Wait for the first sync before updating the notifications, since synchronisation
|
|
||||||
// might change the notifications.
|
|
||||||
void AlarmService.updateAllNotifications();
|
void AlarmService.updateAllNotifications();
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
|
||||||
|
void reg.scheduleSync(1000).then(() => {
|
||||||
|
// Wait for the first sync before updating the notifications, since synchronisation
|
||||||
|
// might change the notifications.
|
||||||
|
void AlarmService.updateAllNotifications();
|
||||||
|
|
||||||
void DecryptionWorker.instance().scheduleStart();
|
void DecryptionWorker.instance().scheduleStart();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const clipperLogger = new Logger();
|
RevisionService.instance().runInBackground();
|
||||||
clipperLogger.addTarget(TargetType.File, { path: `${Setting.value('profileDir')}/log-clipper.txt` });
|
this.startRotatingLogMaintenance(Setting.value('profileDir'));
|
||||||
clipperLogger.addTarget(TargetType.Console);
|
|
||||||
|
|
||||||
ClipperServer.instance().initialize(actionApi);
|
|
||||||
ClipperServer.instance().setEnabled(!Setting.value('altInstanceId'));
|
|
||||||
ClipperServer.instance().setLogger(clipperLogger);
|
|
||||||
ClipperServer.instance().setDispatch(this.store().dispatch);
|
|
||||||
|
|
||||||
if (ClipperServer.instance().enabled() && Setting.value('clipperServer.autoStart')) {
|
|
||||||
void ClipperServer.instance().start();
|
|
||||||
}
|
|
||||||
|
|
||||||
ExternalEditWatcher.instance().setLogger(reg.logger());
|
|
||||||
ExternalEditWatcher.instance().initialize(bridge, this.store().dispatch);
|
|
||||||
|
|
||||||
ResourceEditWatcher.instance().initialize(
|
|
||||||
reg.logger(),
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
|
||||||
(action: any) => { this.store().dispatch(action); },
|
|
||||||
(path: string) => bridge().openItem(path),
|
|
||||||
() => this.store().getState().windowId,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Forwards the local event to the global event manager, so that it can
|
|
||||||
// be picked up by the plugin manager.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
|
||||||
ResourceEditWatcher.instance().on('resourceChange', (event: any) => {
|
|
||||||
eventManager.emit(EventName.ResourceChange, event);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
RevisionService.instance().runInBackground();
|
addTask('set up ClipperServer', () => {
|
||||||
|
const clipperLogger = new Logger();
|
||||||
|
clipperLogger.addTarget(TargetType.File, { path: `${Setting.value('profileDir')}/log-clipper.txt` });
|
||||||
|
clipperLogger.addTarget(TargetType.Console);
|
||||||
|
|
||||||
|
ClipperServer.instance().initialize(actionApi);
|
||||||
|
ClipperServer.instance().setEnabled(!Setting.value('altInstanceId'));
|
||||||
|
ClipperServer.instance().setLogger(clipperLogger);
|
||||||
|
ClipperServer.instance().setDispatch(this.store().dispatch);
|
||||||
|
|
||||||
|
if (ClipperServer.instance().enabled() && Setting.value('clipperServer.autoStart')) {
|
||||||
|
void ClipperServer.instance().start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addTask('set up external edit watchers', () => {
|
||||||
|
ExternalEditWatcher.instance().setLogger(reg.logger());
|
||||||
|
ExternalEditWatcher.instance().initialize(bridge, this.store().dispatch);
|
||||||
|
|
||||||
|
ResourceEditWatcher.instance().initialize(
|
||||||
|
reg.logger(),
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
|
(action: any) => { this.store().dispatch(action); },
|
||||||
|
(path: string) => bridge().openItem(path),
|
||||||
|
() => this.store().getState().windowId,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Forwards the local event to the global event manager, so that it can
|
||||||
|
// be picked up by the plugin manager.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
|
ResourceEditWatcher.instance().on('resourceChange', (event: any) => {
|
||||||
|
eventManager.emit(EventName.ResourceChange, event);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Make it available to the console window - useful to call revisionService.collectRevisions()
|
// Make it available to the console window - useful to call revisionService.collectRevisions()
|
||||||
if (Setting.value('env') === 'dev') {
|
if (Setting.value('env') === 'dev') {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
addTask('add debug variables', () => {
|
||||||
(window as any).joplin = {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
revisionService: RevisionService.instance(),
|
(window as any).joplin = {
|
||||||
migrationService: MigrationService.instance(),
|
revisionService: RevisionService.instance(),
|
||||||
decryptionWorker: DecryptionWorker.instance(),
|
migrationService: MigrationService.instance(),
|
||||||
commandService: CommandService.instance(),
|
decryptionWorker: DecryptionWorker.instance(),
|
||||||
pluginService: PluginService.instance(),
|
commandService: CommandService.instance(),
|
||||||
bridge: bridge(),
|
pluginService: PluginService.instance(),
|
||||||
debug: new DebugService(reg.db()),
|
bridge: bridge(),
|
||||||
resourceService: ResourceService.instance(),
|
debug: new DebugService(reg.db()),
|
||||||
searchEngine: SearchEngine.instance(),
|
resourceService: ResourceService.instance(),
|
||||||
ocrService: () => this.ocrService_,
|
searchEngine: SearchEngine.instance(),
|
||||||
};
|
ocrService: () => this.ocrService_,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bridge().addEventListener('nativeThemeUpdated', this.bridge_nativeThemeUpdated);
|
addTask('listen for main process events', () => {
|
||||||
bridge().setOnAllowedExtensionsChangeListener((newExtensions) => {
|
bridge().addEventListener('nativeThemeUpdated', this.bridge_nativeThemeUpdated);
|
||||||
Setting.setValue('linking.extraAllowedExtensions', newExtensions);
|
bridge().setOnAllowedExtensionsChangeListener((newExtensions) => {
|
||||||
|
Setting.setValue('linking.extraAllowedExtensions', newExtensions);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('window-focused', (_event, newWindowId) => {
|
||||||
|
const currentWindowId = this.store().getState().windowId;
|
||||||
|
if (newWindowId !== currentWindowId) {
|
||||||
|
this.dispatch({
|
||||||
|
type: 'WINDOW_FOCUS',
|
||||||
|
windowId: newWindowId,
|
||||||
|
lastWindowId: currentWindowId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on('window-focused', (_event, newWindowId) => {
|
addTask('initPluginService', () => this.initPluginService());
|
||||||
const currentWindowId = this.store().getState().windowId;
|
|
||||||
if (newWindowId !== currentWindowId) {
|
addTask('setupContextMenu', () => {
|
||||||
this.dispatch({
|
this.setupContextMenu();
|
||||||
type: 'WINDOW_FOCUS',
|
|
||||||
windowId: newWindowId,
|
|
||||||
lastWindowId: currentWindowId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.initPluginService();
|
addTask('set up SpellCheckerService', async () => {
|
||||||
|
await SpellCheckerService.instance().initialize(new SpellCheckerServiceDriverNative());
|
||||||
this.setupContextMenu();
|
|
||||||
|
|
||||||
await SpellCheckerService.instance().initialize(new SpellCheckerServiceDriverNative());
|
|
||||||
|
|
||||||
this.startRotatingLogMaintenance(Setting.value('profileDir'));
|
|
||||||
|
|
||||||
await this.setupOcrService();
|
|
||||||
|
|
||||||
eventManager.on(EventName.OcrServiceResourcesProcessed, async () => {
|
|
||||||
await ResourceService.instance().indexNoteResources();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
eventManager.on(EventName.NoteResourceIndexed, async () => {
|
addTask('listen for resource events', () => {
|
||||||
SearchEngine.instance().scheduleSyncTables();
|
eventManager.on(EventName.OcrServiceResourcesProcessed, async () => {
|
||||||
|
await ResourceService.instance().indexNoteResources();
|
||||||
|
});
|
||||||
|
|
||||||
|
eventManager.on(EventName.NoteResourceIndexed, async () => {
|
||||||
|
SearchEngine.instance().scheduleSyncTables();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Used by tests
|
addTask('setupOcrService', () => this.setupOcrService());
|
||||||
ipcRenderer.send('startup-finished');
|
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
|
public async start(argv: string[], startOptions: StartOptions = null): Promise<any> {
|
||||||
|
const startupTask = perfLogger.taskStart('start');
|
||||||
|
|
||||||
|
// If running inside a package, the command line, instead of being "node.exe <path> <flags>" is "joplin.exe <flags>" so
|
||||||
|
// insert an extra argument so that they can be processed in a consistent way everywhere.
|
||||||
|
if (!bridge().electronIsDev()) argv.splice(1, 0, '.');
|
||||||
|
|
||||||
|
|
||||||
|
argv = await super.start(argv, startOptions);
|
||||||
|
|
||||||
|
await this.setupIntegrationTestUtils();
|
||||||
|
|
||||||
|
bridge().setLogFilePath(Logger.globalLogger.logFilePath());
|
||||||
|
await this.applySettingsSideEffects();
|
||||||
|
|
||||||
|
if (Setting.value('sync.upgradeState') === Setting.SYNC_UPGRADE_STATE_MUST_DO) {
|
||||||
|
reg.logger().info('app.start: doing upgradeSyncTarget action');
|
||||||
|
bridge().mainWindow().show();
|
||||||
|
startupTask.onEnd();
|
||||||
|
|
||||||
|
return { action: 'upgradeSyncTarget' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const startupTasks = this.buildStartupTasks_();
|
||||||
|
for (const task of startupTasks) {
|
||||||
|
await perfLogger.track(task.label, async () => task.task());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// void populateDatabase(reg.db(), {
|
// void populateDatabase(reg.db(), {
|
||||||
@@ -749,6 +808,10 @@ class Application extends BaseApplication {
|
|||||||
|
|
||||||
// await runIntegrationTests();
|
// await runIntegrationTests();
|
||||||
|
|
||||||
|
// Used by tests
|
||||||
|
ipcRenderer.send('startup-finished');
|
||||||
|
|
||||||
|
startupTask.onEnd();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,6 +31,7 @@ import FileApiDriverLocal from '@joplin/lib/file-api-driver-local';
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import nodeSqlite = require('sqlite3');
|
import nodeSqlite = require('sqlite3');
|
||||||
import initLib from '@joplin/lib/initLib';
|
import initLib from '@joplin/lib/initLib';
|
||||||
|
import PerformanceLogger from '@joplin/lib/PerformanceLogger';
|
||||||
const pdfJs = require('pdfjs-dist');
|
const pdfJs = require('pdfjs-dist');
|
||||||
const { isAppleSilicon } = require('is-apple-silicon');
|
const { isAppleSilicon } = require('is-apple-silicon');
|
||||||
require('@sentry/electron/renderer');
|
require('@sentry/electron/renderer');
|
||||||
@@ -38,6 +39,8 @@ require('@sentry/electron/renderer');
|
|||||||
// Allows components to use React as a global
|
// Allows components to use React as a global
|
||||||
window.React = React;
|
window.React = React;
|
||||||
|
|
||||||
|
const perfLogger = PerformanceLogger.create('main-html');
|
||||||
|
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
@@ -106,7 +109,7 @@ const main = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
main().catch((error) => {
|
perfLogger.track('main', main).catch((error) => {
|
||||||
const env = bridge().env();
|
const env = bridge().env();
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
||||||
|
@@ -125,10 +125,12 @@ export default class PerformanceLogger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const startTime = performance.now();
|
const startTime = performance.now();
|
||||||
|
this.lastLogTime_ = startTime;
|
||||||
PerformanceLogger.logDebug_(`${name}: Start at ${formatAbsoluteTime(startTime)}`);
|
PerformanceLogger.logDebug_(`${name}: Start at ${formatAbsoluteTime(startTime)}`);
|
||||||
|
|
||||||
const onEnd = () => {
|
const onEnd = () => {
|
||||||
const now = performance.now();
|
const now = performance.now();
|
||||||
|
this.lastLogTime_ = now;
|
||||||
if (hasPerformanceMarkApi) {
|
if (hasPerformanceMarkApi) {
|
||||||
performance.mark(`${name}-end`);
|
performance.mark(`${name}-end`);
|
||||||
performance.measure(name, `${name}-start`, `${name}-end`);
|
performance.measure(name, `${name}-start`, `${name}-end`);
|
||||||
|
@@ -11,8 +11,11 @@ import ItemChangeUtils from './ItemChangeUtils';
|
|||||||
import time from '../time';
|
import time from '../time';
|
||||||
import eventManager, { EventName } from '../eventManager';
|
import eventManager, { EventName } from '../eventManager';
|
||||||
import { ItemChangeEntity } from './database/types';
|
import { ItemChangeEntity } from './database/types';
|
||||||
|
import PerformanceLogger from '../PerformanceLogger';
|
||||||
const { sprintf } = require('sprintf-js');
|
const { sprintf } = require('sprintf-js');
|
||||||
|
|
||||||
|
const perfLogger = PerformanceLogger.create('ResourceService');
|
||||||
|
|
||||||
export default class ResourceService extends BaseService {
|
export default class ResourceService extends BaseService {
|
||||||
|
|
||||||
public static isRunningInBackground_ = false;
|
public static isRunningInBackground_ = false;
|
||||||
@@ -34,6 +37,7 @@ export default class ResourceService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.isIndexing_ = true;
|
this.isIndexing_ = true;
|
||||||
|
const task = perfLogger.taskStart('indexNoteResources');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ItemChange.waitForAllSaved();
|
await ItemChange.waitForAllSaved();
|
||||||
@@ -110,6 +114,7 @@ export default class ResourceService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.isIndexing_ = false;
|
this.isIndexing_ = false;
|
||||||
|
task.onEnd();
|
||||||
|
|
||||||
eventManager.emit(EventName.NoteResourceIndexed);
|
eventManager.emit(EventName.NoteResourceIndexed);
|
||||||
|
|
||||||
@@ -123,6 +128,8 @@ export default class ResourceService extends BaseService {
|
|||||||
|
|
||||||
public async deleteOrphanResources(expiryDelay: number = null) {
|
public async deleteOrphanResources(expiryDelay: number = null) {
|
||||||
if (expiryDelay === null) expiryDelay = Setting.value('revisionService.ttlDays') * 24 * 60 * 60 * 1000;
|
if (expiryDelay === null) expiryDelay = Setting.value('revisionService.ttlDays') * 24 * 60 * 60 * 1000;
|
||||||
|
const task = perfLogger.taskStart('deleteOrphanResources');
|
||||||
|
|
||||||
const resourceIds = await NoteResource.orphanResources(expiryDelay);
|
const resourceIds = await NoteResource.orphanResources(expiryDelay);
|
||||||
this.logger().info('ResourceService::deleteOrphanResources:', resourceIds);
|
this.logger().info('ResourceService::deleteOrphanResources:', resourceIds);
|
||||||
for (let i = 0; i < resourceIds.length; i++) {
|
for (let i = 0; i < resourceIds.length; i++) {
|
||||||
@@ -138,6 +145,8 @@ export default class ResourceService extends BaseService {
|
|||||||
await Resource.delete(resourceId, { sourceDescription: 'deleteOrphanResources' });
|
await Resource.delete(resourceId, { sourceDescription: 'deleteOrphanResources' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task.onEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async autoSetFileSize(resourceId: string, filePath: string, waitTillExists = true) {
|
private static async autoSetFileSize(resourceId: string, filePath: string, waitTillExists = true) {
|
||||||
|
@@ -14,8 +14,10 @@ import { getMasterPassword } from '../e2ee/utils';
|
|||||||
import ResourceService from '../ResourceService';
|
import ResourceService from '../ResourceService';
|
||||||
import { addMasterKey, getEncryptionEnabled, localSyncInfo } from '../synchronizer/syncInfoUtils';
|
import { addMasterKey, getEncryptionEnabled, localSyncInfo } from '../synchronizer/syncInfoUtils';
|
||||||
import { ShareInvitation, SharePermissions, State, stateRootKey, StateShare } from './reducer';
|
import { ShareInvitation, SharePermissions, State, stateRootKey, StateShare } from './reducer';
|
||||||
|
import PerformanceLogger from '../../PerformanceLogger';
|
||||||
|
|
||||||
const logger = Logger.create('ShareService');
|
const logger = Logger.create('ShareService');
|
||||||
|
const perfLogger = PerformanceLogger.create('ShareService');
|
||||||
|
|
||||||
export interface ApiShare {
|
export interface ApiShare {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -514,6 +516,7 @@ export default class ShareService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async maintenance() {
|
public async maintenance() {
|
||||||
|
const task = perfLogger.taskStart('maintenance');
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
let hasError = false;
|
let hasError = false;
|
||||||
|
|
||||||
@@ -537,6 +540,7 @@ export default class ShareService {
|
|||||||
// so we can run the clean up function.
|
// so we can run the clean up function.
|
||||||
if (!hasError) await this.updateNoLongerSharedItems();
|
if (!hasError) await this.updateNoLongerSharedItems();
|
||||||
}
|
}
|
||||||
|
task.onEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user