Files
ontime/apps/electron/src/menu/applicationMenu.js
T
2024-12-13 10:39:45 +01:00

344 lines
9.6 KiB
JavaScript

const { Menu, shell } = require('electron');
const {
linkToGitHub,
linkToDocs,
linkToDiscord,
isProduction,
isMac,
releaseTag,
projectsPath,
corruptProjectsPath,
crashLogPath,
stylesPath,
externalPath,
downloadPath,
} = require('../externals.js');
/**
* Creates the application menu
* @param {function} askToQuit - function for quitting process
* @param {string} clientUrl - base url for the application
* @param {string} serverUrl - base url for the application
* @param {function} redirectWindow - function to redirect main window content
* @param {function} showDialog - asks the react app to show a user dialog
* @param {function} download - function to download a resource from url
* @returns {Menu} - application menu
*/
function getApplicationMenu(askToQuit, clientUrl, serverUrl, redirectWindow, showDialog, download) {
const template = [
...(isMac ? [makeMacMenu(askToQuit)] : []),
makeFileMenu(serverUrl, redirectWindow, showDialog, download),
makeEditMenu(),
makeViewMenu(clientUrl),
makeSettingsMenu(redirectWindow),
makeHelpMenu(redirectWindow),
...(isProduction ? [] : [{ label: 'Dev', submenu: [{ role: 'toggleDevTools' }] }]),
];
return Menu.buildFromTemplate(template);
}
/**
* Utility function generates the app menu (macOS only)
* @param {function} askToQuit - function for quitting process
* @returns {Object}
*/
function makeMacMenu(askToQuit) {
return {
label: 'Ontime',
submenu: [
{ role: 'about', label: 'About Ontime' },
{ type: 'separator' },
{ role: 'hide', label: 'Hide Ontime' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{
label: 'Quit',
click: askToQuit,
accelerator: isMac ? 'Cmd+Q' : 'Alt+F4',
},
],
};
}
/**
* Utility function generates the edit menu
* @returns {Object}
*/
function makeEditMenu() {
return {
label: 'Edit',
submenu: [
{ label: 'Cut', accelerator: 'CmdOrCtrl+X', role: 'cut' },
{ label: 'Copy', accelerator: 'CmdOrCtrl+C', role: 'copy' },
{ label: 'Paste', accelerator: 'CmdOrCtrl+V', role: 'paste' },
{ label: 'Select All', accelerator: 'CmdOrCtrl+A', role: 'selectAll' },
],
};
}
/**
* Utility function generates the file menu
* @param {string} serverUrl - base url for the application
* @param {function} redirectWindow - function to redirect main window content
* @param {function} showDialog - asks the react app to show a user dialog
* @param {function} download - function to download a resource from url
* @returns {Object}
*/
function makeFileMenu(serverUrl, redirectWindow, showDialog, download) {
const downloadProject = () => {
try {
download(serverUrl + downloadPath);
} catch (_error) {
/** unhandled error */
}
};
return {
label: 'File',
submenu: [
{
label: 'New project...',
click: () => redirectWindow('/editor?settings=project__manage&new=true'),
},
{
label: 'Load...',
click: () => showDialog('welcome'),
},
{
label: 'Quick start...',
click: () => redirectWindow('/editor?settings=project__create'),
},
{
label: 'Edit project info...',
click: () => redirectWindow('/editor?settings=project__data'),
},
{
label: 'Manage projects',
click: () => redirectWindow('/editor?settings=project__manage'),
},
{
label: 'Download project',
click: downloadProject,
},
{ type: 'separator' },
{
label: 'Open directory',
submenu: [
makeItemOpenInDesktop('Projects', projectsPath),
makeItemOpenInDesktop('Corrupted projects', corruptProjectsPath),
makeItemOpenInDesktop('Crash logs', crashLogPath),
makeItemOpenInDesktop('CSS override', stylesPath),
makeItemOpenInDesktop('External', externalPath),
],
},
{ type: 'separator' },
{ role: isMac ? 'close' : 'quit' },
],
};
}
/**
* Utility function generates the views menu
* @param {string} clientUrl - base url for the application
* @returns {Object}
*/
function makeViewMenu(clientUrl) {
return {
label: 'Views',
submenu: [
makeItemOpenInBrowser('Public', `${clientUrl}/public`),
makeItemOpenInBrowser('Lower Thirds', `${clientUrl}/lower`),
{ type: 'separator' },
makeItemOpenInBrowser('Timer', `${clientUrl}/timer`),
makeItemOpenInBrowser('Minimal Timer', `${clientUrl}/minimal`),
makeItemOpenInBrowser('Clock', `${clientUrl}/clock`),
makeItemOpenInBrowser('Backstage', `${clientUrl}/backstage`),
makeItemOpenInBrowser('Timeline (beta)', `${clientUrl}/timeline`),
makeItemOpenInBrowser('Studio Clock', `${clientUrl}/studio`),
makeItemOpenInBrowser('Countdown', `${clientUrl}/countdown`),
{ type: 'separator' },
makeItemOpenInBrowser('Editor', `${clientUrl}/editor`),
makeItemOpenInBrowser('Cuesheet', `${clientUrl}/cuesheet`),
makeItemOpenInBrowser('Operator', `${clientUrl}/op`),
makeItemOpenInBrowser('Project info', `${clientUrl}/info`),
{ type: 'separator' },
{ role: 'forceReload' },
{ type: 'separator' },
{ role: 'resetZoom' },
// NOTE: I still contend this zoomin mess is an electron bug
{ role: 'zoomIn', accelerator: 'CmdOrCtrl+Plus' },
{ role: 'zoomIn', accelerator: 'CmdOrCtrl+=', visible: false },
{ role: 'zoomIn', accelerator: 'CmdOrCtrl+numadd', visible: false },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' },
],
};
}
/**
* Utility function generates the settings menu
* @param {function} redirectWindow - function to redirect main window content
* @returns {Object}
*/
function makeSettingsMenu(redirectWindow) {
return {
label: 'Settings',
submenu: [
{
label: 'Open Settings',
accelerator: 'CommandOrControl+,',
click: () => redirectWindow('/editor?settings=project'),
},
{
label: 'Project',
submenu: [
{
label: 'Project data',
click: () => redirectWindow('/editor?settings=project__data'),
},
{
label: 'Manage projects',
click: () => redirectWindow('/editor?settings=project__manage'),
},
],
},
{
label: 'App Settings',
submenu: [
{
label: 'General settings',
click: () => redirectWindow('/editor?settings=general__settings'),
},
{
label: 'Editor settings',
click: () => redirectWindow('/editor?settings=general__editor'),
},
{
label: 'View settings',
click: () => redirectWindow('/editor?settings=general__view'),
},
],
},
{
label: 'Feature Settings',
submenu: [
{
label: 'Custom fields',
click: () => redirectWindow('/editor?settings=feature_settings__custom'),
},
{
label: 'URL presets',
click: () => redirectWindow('/editor?settings=feature_settings__urlpresets'),
},
],
},
{
label: 'Data Sources',
submenu: [
{
label: 'Import spreadsheet',
click: () => redirectWindow('/editor?settings=sources__xlsx'),
},
{
label: 'Sync with Google Sheet',
click: () => redirectWindow('/editor?settings=sources__gsheet'),
},
],
},
{
label: 'Integrations',
submenu: [
{
label: 'OSC settings',
click: () => redirectWindow('/editor?settings=integrations__osc'),
},
{
label: 'HTTP settings',
click: () => redirectWindow('/editor?settings=integrations__http'),
},
],
},
{
label: 'Network',
submenu: [
{
label: 'Event log',
click: () => redirectWindow('/editor?settings=network__log'),
},
{
label: 'Manage clients',
click: () => redirectWindow('/editor?settings=network__clients'),
},
],
},
],
};
}
/**
* Utility function generates the help menu
* @param {function} redirectWindow - function to redirect main window content
* @returns {Object}
*/
function makeHelpMenu(redirectWindow) {
return {
role: 'help',
submenu: [
{
label: `Ontime ${releaseTag}`,
click: () => redirectWindow('/editor?settings=about'),
},
{
type: 'separator',
},
makeItemOpenInBrowser('See on github', linkToGitHub),
makeItemOpenInBrowser('Online documentation', linkToDocs),
makeItemOpenInBrowser('Join us on Discord', linkToDiscord),
],
};
}
/**
* Utility function to safely open a URL in the default browser
* @param {string} label
* @param {string} url
* @returns {object} - MenuItem
*/
function makeItemOpenInBrowser(label, url) {
return {
label: `${label}`,
click: async () => {
try {
await shell.openExternal(url);
} catch (_error) {
/** unhandled error */
}
},
};
}
/**
* Utility function to open a file in the OS explorer / finder
* @param {string} label
* @param {string} path
* @returns {object} - MenuItem
*/
function makeItemOpenInDesktop(label, path) {
return {
label,
click: () => {
try {
shell.openPath(path);
} catch (_error) {
/** unhandled error */
}
},
};
}
module.exports = { getApplicationMenu };