Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sped up extremely slow loading of Cropper and context menus #1208

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 2
jobs:
build:
macos:
xcode: '13.0.0'
xcode: '13.4.1'
steps:
- checkout
- run: yarn
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_modules
/app/dist
/dist
/dist-js
.DS_Store
Binary file added .yarn/install-state.gz
Binary file not shown.
5 changes: 5 additions & 0 deletions .yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


yarn-path ".yarn/releases/yarn-1.22.21.cjs"
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
72 changes: 42 additions & 30 deletions main/aperture.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import {windowManager} from './windows/manager';
import {setRecordingTray, setPausedTray, disableTray, resetTray} from './tray';
import {setCropperShortcutAction} from './global-accelerators';
import {settings} from './common/settings';
import {track} from './common/analytics';
import {plugins} from './plugins';
import {getAudioDevices, getSelectedInputDeviceId} from './utils/devices';
import {showError} from './utils/errors';
import {RecordServiceContext, RecordServiceState} from './plugins/service-context';
import {setCurrentRecording, updatePluginState, stopCurrentRecording} from './recording-history';
import {Recording} from './video';
import {ApertureOptions, StartRecordingOptions} from './common/types';
import {InstalledPlugin} from './plugins/plugin';
import {RecordService, RecordServiceHook} from './plugins/service';
import {getCurrentDurationStart, getOverallDuration, setCurrentDurationStart, setOverallDuration} from './utils/track-duration';
import { windowManager } from './windows/manager';
import { setRecordingTray, setPausedTray, disableTray, resetTray } from './tray';
import { setCropperShortcutAction } from './global-accelerators';
import { settings } from './common/settings';
import { track } from './common/analytics';
import { plugins } from './plugins';
import { getAudioDevices, getSelectedInputDeviceId } from './utils/devices';
import { showError } from './utils/errors';
import { RecordServiceContext, RecordServiceState } from './plugins/service-context';
import { setCurrentRecording, updatePluginState, stopCurrentRecording } from './recording-history';
import { Recording } from './video';
import { ApertureOptions, StartRecordingOptions } from './common/types';
import { InstalledPlugin } from './plugins/plugin';
import { RecordService, RecordServiceHook } from './plugins/service';
import { getCurrentDurationStart, getOverallDuration, setCurrentDurationStart, setOverallDuration } from './utils/track-duration';

const createAperture = require('aperture');
const aperture = createAperture();

let recordingPlugins: Array<{plugin: InstalledPlugin; service: RecordService}> = [];
let recordingPlugins: Array<{ plugin: InstalledPlugin; service: RecordService }> = [];
const serviceState = new Map<string, RecordServiceState>();
let apertureOptions: ApertureOptions;
let recordingName: string | undefined;
Expand All @@ -30,7 +30,7 @@ const setRecordingName = (name: string) => {
const serializeEditPluginState = () => {
const result: Record<string, Record<string, Record<string, unknown> | undefined>> = {};

for (const {plugin, service} of recordingPlugins) {
for (const { plugin, service } of recordingPlugins) {
if (!result[plugin.name]) {
result[plugin.name] = {};
}
Expand All @@ -41,7 +41,7 @@ const serializeEditPluginState = () => {
return result;
};

const callPlugins = async (method: RecordServiceHook) => Promise.all(recordingPlugins.map(async ({plugin, service}) => {
const callPlugins = async (method: RecordServiceHook) => Promise.all(recordingPlugins.map(async ({ plugin, service }) => {
if (service[method] && typeof service[method] === 'function') {
try {
await service[method]?.(
Expand All @@ -53,7 +53,7 @@ const callPlugins = async (method: RecordServiceHook) => Promise.all(recordingPl
})
);
} catch (error) {
showError(error as any, {title: `Something went wrong while using the plugin “${plugin.prettyName}”`, plugin});
showError(error as any, { title: `Something went wrong while using the plugin “${plugin.prettyName}”`, plugin });
}
}
}));
Expand All @@ -74,13 +74,14 @@ export const startRecording = async (options: StartRecordingOptions) => {
}

past = Date.now();
console.log(`Starting recording ${(Date.now() - past) / 1000}s`);
recordingName = undefined;

windowManager.preferences?.close();
windowManager.cropper?.disable();
disableTray();

const {cropperBounds, screenBounds, displayId} = options;
const { cropperBounds, screenBounds, displayId } = options;

cropperBounds.y = screenBounds.height - (cropperBounds.y + cropperBounds.height);

Expand All @@ -98,14 +99,20 @@ export const startRecording = async (options: StartRecordingOptions) => {
highlightClicks,
screenId: displayId
};
console.log(`before recordaudio ${(Date.now() - past) / 1000}s`);

if (recordAudio) {
// In case for some reason the default audio device is not set
// use the first available device for recording
console.log(`getting audio ${(Date.now() - past) / 1000}s`);
const audioInputDeviceId = getSelectedInputDeviceId();
console.log(`getSelectedInputDeviceId ${(Date.now() - past) / 1000}s`);

if (audioInputDeviceId) {
apertureOptions.audioDeviceId = audioInputDeviceId;
} else {
console.log(`getting defaultaudio ${(Date.now() - past) / 1000}s`);

const [defaultAudioDevice] = await getAudioDevices();
apertureOptions.audioDeviceId = defaultAudioDevice?.id;
}
Expand All @@ -125,20 +132,25 @@ export const startRecording = async (options: StartRecordingOptions) => {
const validServices = plugin.config.validServices;
return plugin.recordServicesWithStatus
// Make sure service is valid and enabled
.filter(({title, isEnabled}) => isEnabled && validServices.includes(title))
.map(service => ({plugin, service}));
.filter(({ title, isEnabled }) => isEnabled && validServices.includes(title))
.map(service => ({ plugin, service }));
}
);

for (const {service, plugin} of recordingPlugins) {
serviceState.set(service.title, {persistedState: {}});
for (const { service, plugin } of recordingPlugins) {
serviceState.set(service.title, { persistedState: {} });
track(`plugins/used/record/${plugin.name}`);
}
console.log(`callplugins b4 ${(Date.now() - past) / 1000}s`);

await callPlugins('willStartRecording');
console.log(`callplugins after ${(Date.now() - past) / 1000}s`);


try {
const filePath = await aperture.startRecording(apertureOptions);
console.log(`aperture startrecording ${(Date.now() - past) / 1000}s`);

setOverallDuration(0);
setCurrentDurationStart(Date.now());

Expand All @@ -150,7 +162,7 @@ export const startRecording = async (options: StartRecordingOptions) => {
});
} catch (error) {
track('recording/stopped/error');
showError(error as any, {title: 'Recording error', plugin: undefined});
showError(error as any, { title: 'Recording error', plugin: undefined });
past = undefined;
cleanup();
return;
Expand All @@ -174,7 +186,7 @@ export const startRecording = async (options: StartRecordingOptions) => {
// Make sure it doesn't catch the error of ending the recording
if (past) {
track('recording/stopped/error');
showError(error, {title: 'Recording error', plugin: undefined});
showError(error, { title: 'Recording error', plugin: undefined });
past = undefined;
cleanup();
}
Expand All @@ -201,7 +213,7 @@ export const stopRecording = async () => {
setCurrentDurationStart(0);
} catch (error) {
track('recording/stopped/error');
showError(error as any, {title: 'Recording error', plugin: undefined});
showError(error as any, { title: 'Recording error', plugin: undefined });
cleanup();
return;
}
Expand Down Expand Up @@ -237,7 +249,7 @@ export const stopRecordingWithNoEdit = async () => {
setCurrentDurationStart(0);
} catch (error) {
track('recording/quit/error');
showError(error as any, {title: 'Recording error', plugin: undefined});
showError(error as any, { title: 'Recording error', plugin: undefined });
cleanup();
return;
}
Expand Down Expand Up @@ -266,7 +278,7 @@ export const pauseRecording = async () => {
console.log(`Paused recording after ${(Date.now() - past) / 1000}s`);
} catch (error) {
track('recording/paused/error');
showError(error as any, {title: 'Recording error', plugin: undefined});
showError(error as any, { title: 'Recording error', plugin: undefined });
cleanup();
}
};
Expand All @@ -286,7 +298,7 @@ export const resumeRecording = async () => {
console.log(`Resume recording after ${(Date.now() - past) / 1000}s`);
} catch (error) {
track('recording/resumed/error');
showError(error as any, {title: 'Recording error', plugin: undefined});
showError(error as any, { title: 'Recording error', plugin: undefined });
cleanup();
}
};
27 changes: 19 additions & 8 deletions main/global-accelerators.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import {globalShortcut} from 'electron';
import {ipcMain as ipc} from 'electron-better-ipc';
import {settings} from './common/settings';
import {windowManager} from './windows/manager';
import { globalShortcut } from 'electron';
import { ipcMain as ipc } from 'electron-better-ipc';
import { settings } from './common/settings';
import { windowManager } from './windows/manager';

const openCropper = () => {
if (!windowManager.cropper?.isOpen()) {
let cropperwindow = windowManager.cropper?.isOpen()
if (!cropperwindow) {
windowManager.cropper?.open();
} else {
if (cropperwindow.isVisible())
cropperwindow?.hide()
else cropperwindow?.show()
}
};

Expand All @@ -31,6 +36,7 @@ export const setCropperShortcutAction = (action = openCropper) => {
const registerShortcut = (shortcut: string, action: () => void) => {
try {
globalShortcut.register(shortcut, action);
console.log('Registered shortcut successfully', shortcut, action);
} catch (error) {
console.error('Error registering shortcut', shortcut, action, error);
}
Expand All @@ -50,31 +56,36 @@ const registerFromStore = () => {
};

export const initializeGlobalAccelerators = () => {
ipc.answerRenderer('update-shortcut', ({setting, shortcut}) => {
ipc.answerRenderer('update-shortcut', ({ setting, shortcut }) => {
const oldShortcut = settings.get<string, string>(`shortcuts.${setting}`);

try {
if (oldShortcut && oldShortcut !== shortcut && globalShortcut.isRegistered(oldShortcut)) {
console.log('Unregistering old shortcut', oldShortcut);
globalShortcut.unregister(oldShortcut);
}
} catch (error) {
console.error('Error unregistering old shortcutAccelerator', error);
} finally {
if (shortcut && shortcut !== oldShortcut) {
console.log('Registering new shortcut', shortcut);
settings.set(`shortcuts.${setting}`, shortcut);
const handler = handlers.get(setting);

console.log(handler)
if (settings.get('enableShortcuts') && handler) {
console.log('Registering new shortcut2', shortcut);
registerShortcut(shortcut, handler);
}
} else if (!shortcut) {
// @ts-expect-error
settings.delete(`shortcuts.${setting}`);
console.log('No shortcut, unregistering shortcut', oldShortcut);
}
}
});

ipc.answerRenderer('toggle-shortcuts', ({enabled}) => {
ipc.answerRenderer('toggle-shortcuts', ({ enabled }) => {
console.log('Toggling shortcuts', enabled);
if (enabled) {
registerFromStore();
} else {
Expand Down
22 changes: 12 additions & 10 deletions main/menus/common.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import delay from 'delay';
import {app, dialog} from 'electron';
import {openNewGitHubIssue} from 'electron-util';
import { app, dialog } from 'electron';
import { openNewGitHubIssue } from 'electron-util';
import macosRelease from '../utils/macos-release';
import {supportedVideoExtensions} from '../common/constants';
import {getCurrentMenuItem, MenuItemId} from './utils';
import {openFiles} from '../utils/open-files';
import {windowManager} from '../windows/manager';
import { supportedVideoExtensions } from '../common/constants';
import { getCurrentMenuItem, MenuItemId } from './utils';
import { openFiles } from '../utils/open-files';
import { windowManager } from '../windows/manager';

export const getPreferencesMenuItem = () => ({
id: MenuItemId.preferences,
label: 'Preferences…',
accelerator: 'Command+,',
click: () => windowManager.preferences?.open()
click: () =>
windowManager.preferences?.open()

});

export const getAboutMenuItem = () => ({
id: MenuItemId.about,
label: `About ${app.name}`,
click: () => {
windowManager.cropper?.close();
// windowManager.cropper?.close();
app.focus();
app.showAboutPanel();
}
Expand All @@ -34,8 +36,8 @@ export const getOpenFileMenuItem = () => ({
await delay(200);

app.focus();
const {canceled, filePaths} = await dialog.showOpenDialog({
filters: [{name: 'Videos', extensions: supportedVideoExtensions}],
const { canceled, filePaths } = await dialog.showOpenDialog({
filters: [{ name: 'Videos', extensions: supportedVideoExtensions }],
properties: ['openFile', 'multiSelections']
});

Expand Down
29 changes: 16 additions & 13 deletions main/tray.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
'use strict';

import {Tray} from 'electron';
import {KeyboardEvent} from 'electron/main';
import { Tray } from 'electron';
import { KeyboardEvent } from 'electron/main';
import path from 'path';
import {getCogMenu} from './menus/cog';
import {getRecordMenu} from './menus/record';
import {track} from './common/analytics';
import {openFiles} from './utils/open-files';
import {windowManager} from './windows/manager';
import {pauseRecording, resumeRecording, stopRecording} from './aperture';
import { getCogMenu } from './menus/cog';
import { getRecordMenu } from './menus/record';
import { track } from './common/analytics';
import { openFiles } from './utils/open-files';
import { windowManager } from './windows/manager';
import { pauseRecording, resumeRecording, stopRecording } from './aperture';

let tray: Tray;
let trayAnimation: NodeJS.Timeout | undefined;

let cogMenu = getCogMenu()
const openContextMenu = async () => {
tray.popUpContextMenu(await getCogMenu());
tray.popUpContextMenu(await cogMenu);
};

let recordMenu = getRecordMenu(false)
const openRecordingContextMenu = async () => {
tray.popUpContextMenu(await getRecordMenu(false));
tray.popUpContextMenu(await recordMenu);
};

let pausedMenu = getRecordMenu(true)
const openPausedContextMenu = async () => {
tray.popUpContextMenu(await getRecordMenu(true));
tray.popUpContextMenu(await pausedMenu);
};

const openCropperWindow = () => windowManager.cropper?.open();
Expand Down Expand Up @@ -58,13 +61,13 @@ export const resetTray = () => {
};

export const setRecordingTray = () => {
animateIcon();

tray.removeAllListeners('right-click');

// TODO: figure out why this is marked as missing. It's defined properly in the electron.d.ts file
tray.once('click', onRecordingTrayClick);
tray.on('right-click', openRecordingContextMenu);
animateIcon();
};

export const setPausedTray = () => {
Expand All @@ -89,7 +92,7 @@ const onRecordingTrayClick = (event: KeyboardEvent) => {
};

const animateIcon = async () => new Promise<void>(resolve => {
const interval = 20;
const interval = 50;
let i = 0;

const next = () => {
Expand Down