mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
Merge remote-tracking branch 'origin/master'
* origin/master: feat(i18n): add export task list string to localization files feat(ipc): improve error handling and streamline IPC initialization feat(startup): refactor startup logic and move initialization to StartupService feat(ipc): implement IPC handlers for app control, data, exec, global shortcuts, system, and Jira feat: add placeholder text support to daily summary and inline markdown components feat: don't install api test plugin chore(deps): bump the npm_and_yarn group across 4 directories with 1 update # Conflicts: # electron/ipc-handler.ts # src/app/app.component.ts
This commit is contained in:
commit
5bd59e2760
20 changed files with 809 additions and 688 deletions
|
|
@ -1,34 +1,13 @@
|
|||
// FRONTEND EVENTS
|
||||
// ---------------
|
||||
import {
|
||||
app,
|
||||
dialog,
|
||||
globalShortcut,
|
||||
ipcMain,
|
||||
IpcMainEvent,
|
||||
ProgressBarOptions,
|
||||
shell,
|
||||
} from 'electron';
|
||||
import { IPC } from './shared-with-frontend/ipc-events.const';
|
||||
import { lockscreen } from './lockscreen';
|
||||
import { errorHandlerWithFrontendInform } from './error-handler-with-frontend-inform';
|
||||
import { JiraCfg } from '../src/app/features/issue/providers/jira/jira.model';
|
||||
import { sendJiraRequest, setupRequestHeadersForImages } from './jira';
|
||||
import { KeyboardConfig } from '../src/app/features/config/keyboard-config.model';
|
||||
import { log } from 'electron-log/main';
|
||||
import { exec } from 'child_process';
|
||||
import { getWin } from './main-window';
|
||||
import { quitApp, showOrFocus } from './various-shared';
|
||||
import { loadSimpleStoreAll, saveSimpleStore } from './simple-store';
|
||||
import {
|
||||
getIsLocked,
|
||||
setIsMinimizeToTray,
|
||||
setIsTrayShowCurrentTask,
|
||||
setIsTrayShowCurrentCountdown,
|
||||
} from './shared-state';
|
||||
import { BACKUP_DIR, BACKUP_DIR_WINSTORE } from './backup';
|
||||
import { pluginNodeExecutor } from './plugin-node-executor';
|
||||
import { GlobalConfigState } from '../src/app/features/config/global-config.model';
|
||||
import {
|
||||
initAppControlIpc,
|
||||
initAppDataIpc,
|
||||
initExecIpc,
|
||||
initGlobalShortcutsIpc,
|
||||
initJiraIpc,
|
||||
initSystemIpc,
|
||||
} from './ipc-handlers';
|
||||
|
||||
export const initIpcInterfaces = (): void => {
|
||||
// Initialize plugin node executor (registers IPC handlers)
|
||||
|
|
@ -38,245 +17,11 @@ export const initIpcInterfaces = (): void => {
|
|||
if (!pluginNodeExecutor) {
|
||||
log('Warning: Plugin node executor failed to initialize');
|
||||
}
|
||||
// HANDLER
|
||||
// -------
|
||||
ipcMain.handle(IPC.GET_PATH, (ev, name: string) => {
|
||||
return app.getPath(name as Parameters<typeof app.getPath>[0]);
|
||||
});
|
||||
ipcMain.handle(IPC.GET_BACKUP_PATH, () => {
|
||||
if (process?.windowsStore) {
|
||||
return BACKUP_DIR_WINSTORE;
|
||||
} else {
|
||||
return BACKUP_DIR;
|
||||
}
|
||||
});
|
||||
|
||||
// BACKEND EVENTS
|
||||
// --------------
|
||||
// ...
|
||||
|
||||
// ON EVENTS
|
||||
// ---------
|
||||
ipcMain.on(IPC.SHUTDOWN_NOW, quitApp);
|
||||
ipcMain.on(IPC.EXIT, (ev, exitCode: number) => app.exit(exitCode));
|
||||
ipcMain.on(IPC.RELAUNCH, () => app.relaunch());
|
||||
ipcMain.on(IPC.OPEN_DEV_TOOLS, () => getWin().webContents.openDevTools());
|
||||
ipcMain.on(
|
||||
IPC.SHOW_EMOJI_PANEL,
|
||||
() => app.isEmojiPanelSupported() && app.showEmojiPanel(),
|
||||
);
|
||||
ipcMain.on(IPC.RELOAD_MAIN_WIN, () => getWin().reload());
|
||||
ipcMain.on(IPC.OPEN_PATH, (ev, path: string) => shell.openPath(path));
|
||||
ipcMain.on(IPC.OPEN_EXTERNAL, (ev, url: string) => shell.openExternal(url));
|
||||
ipcMain.on(IPC.TRANSFER_SETTINGS_TO_ELECTRON, (ev, cfg: GlobalConfigState) => {
|
||||
setIsMinimizeToTray(cfg.misc.isMinimizeToTray);
|
||||
setIsTrayShowCurrentTask(cfg.misc.isTrayShowCurrentTask);
|
||||
setIsTrayShowCurrentCountdown(cfg.misc.isTrayShowCurrentCountdown);
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC.SAVE_FILE_DIALOG, async (ev, { filename, data }) => {
|
||||
const result = await dialog.showSaveDialog(getWin(), {
|
||||
defaultPath: filename,
|
||||
filters: [
|
||||
{ name: 'JSON Files', extensions: ['json'] },
|
||||
{ name: 'All Files', extensions: ['*'] },
|
||||
],
|
||||
});
|
||||
|
||||
if (!result.canceled && result.filePath) {
|
||||
const fs = await import('fs');
|
||||
await fs.promises.writeFile(result.filePath, data, 'utf-8');
|
||||
return { success: true, path: result.filePath };
|
||||
}
|
||||
return { success: false };
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC.SHARE_NATIVE, async () => {
|
||||
// Desktop platforms use the share dialog instead of native share
|
||||
// This allows for more flexibility and better UX with social media options
|
||||
return { success: false, error: 'Native share not available on desktop' };
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.LOCK_SCREEN, () => {
|
||||
if (getIsLocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
lockscreen();
|
||||
} catch (e) {
|
||||
errorHandlerWithFrontendInform(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.SET_PROGRESS_BAR, (ev, { progress, progressBarMode }) => {
|
||||
const mainWin = getWin();
|
||||
if (mainWin) {
|
||||
if (progressBarMode === 'none') {
|
||||
mainWin.setProgressBar(-1);
|
||||
} else {
|
||||
mainWin.setProgressBar(Math.min(Math.max(progress, 0), 1), {
|
||||
mode: progressBarMode as ProgressBarOptions['mode'],
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.FLASH_FRAME, (ev) => {
|
||||
const mainWin = getWin();
|
||||
if (mainWin) {
|
||||
mainWin.flashFrame(false);
|
||||
mainWin.flashFrame(true);
|
||||
|
||||
mainWin.once('focus', () => {
|
||||
mainWin.flashFrame(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.REGISTER_GLOBAL_SHORTCUTS_EVENT, (ev, cfg) => {
|
||||
registerShowAppShortCuts(cfg);
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.JIRA_SETUP_IMG_HEADERS, (ev, { jiraCfg }: { jiraCfg: JiraCfg }) => {
|
||||
setupRequestHeadersForImages(jiraCfg);
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.JIRA_MAKE_REQUEST_EVENT, (ev, request) => {
|
||||
sendJiraRequest(request);
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.SHOW_OR_FOCUS, () => {
|
||||
const mainWin = getWin();
|
||||
showOrFocus(mainWin);
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.EXEC, execWithFrontendErrorHandlerInform);
|
||||
|
||||
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
||||
function registerShowAppShortCuts(cfg: KeyboardConfig): void {
|
||||
// unregister all previous
|
||||
globalShortcut.unregisterAll();
|
||||
const GLOBAL_KEY_CFG_KEYS: (keyof KeyboardConfig)[] = [
|
||||
'globalShowHide',
|
||||
'globalToggleTaskStart',
|
||||
'globalAddNote',
|
||||
'globalAddTask',
|
||||
];
|
||||
|
||||
if (cfg) {
|
||||
const mainWin = getWin();
|
||||
Object.keys(cfg)
|
||||
.filter((key: string) =>
|
||||
GLOBAL_KEY_CFG_KEYS.includes(key as keyof KeyboardConfig),
|
||||
)
|
||||
.forEach((key: string) => {
|
||||
let actionFn: () => void;
|
||||
const shortcut = cfg[key as keyof KeyboardConfig];
|
||||
|
||||
switch (key) {
|
||||
case 'globalShowHide':
|
||||
actionFn = () => {
|
||||
if (mainWin.isFocused()) {
|
||||
// we need to blur the window for windows
|
||||
mainWin.blur();
|
||||
mainWin.hide();
|
||||
} else {
|
||||
showOrFocus(mainWin);
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case 'globalToggleTaskStart':
|
||||
actionFn = () => {
|
||||
mainWin.webContents.send(IPC.TASK_TOGGLE_START);
|
||||
};
|
||||
break;
|
||||
|
||||
case 'globalAddNote':
|
||||
actionFn = () => {
|
||||
showOrFocus(mainWin);
|
||||
mainWin.webContents.send(IPC.ADD_NOTE);
|
||||
};
|
||||
break;
|
||||
|
||||
case 'globalAddTask':
|
||||
actionFn = () => {
|
||||
showOrFocus(mainWin);
|
||||
// NOTE: delay slightly to make sure app is ready
|
||||
mainWin.webContents.send(IPC.SHOW_ADD_TASK_BAR);
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
actionFn = () => undefined;
|
||||
}
|
||||
|
||||
if (shortcut && shortcut.length > 0) {
|
||||
try {
|
||||
const ret = globalShortcut.register(shortcut, actionFn) as unknown;
|
||||
if (!ret) {
|
||||
errorHandlerWithFrontendInform(
|
||||
'Global Shortcut registration failed: ' + shortcut,
|
||||
shortcut,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
errorHandlerWithFrontendInform(
|
||||
'Global Shortcut registration failed: ' + shortcut,
|
||||
{ e, shortcut },
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
initAppDataIpc();
|
||||
initAppControlIpc();
|
||||
initSystemIpc();
|
||||
initJiraIpc();
|
||||
initGlobalShortcutsIpc();
|
||||
initExecIpc();
|
||||
};
|
||||
|
||||
const COMMAND_MAP_PROP = 'allowedCommands';
|
||||
|
||||
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
||||
async function execWithFrontendErrorHandlerInform(
|
||||
ev: IpcMainEvent,
|
||||
command: string,
|
||||
): Promise<void> {
|
||||
log('trying to run command ' + command);
|
||||
const existingData = await loadSimpleStoreAll();
|
||||
const allowedCommands: string[] = (existingData[COMMAND_MAP_PROP] as string[]) || [];
|
||||
|
||||
if (!Array.isArray(allowedCommands)) {
|
||||
throw new Error('allowedCommands is no array ???');
|
||||
}
|
||||
if (allowedCommands.includes(command)) {
|
||||
exec(command, (err) => {
|
||||
if (err) {
|
||||
errorHandlerWithFrontendInform(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const mainWin = getWin();
|
||||
const res = await dialog.showMessageBox(mainWin, {
|
||||
type: 'question',
|
||||
buttons: ['Cancel', 'Yes, execute!'],
|
||||
defaultId: 2,
|
||||
title: 'Super Productivity – Exec',
|
||||
message:
|
||||
'Do you want to execute this command? ONLY confirm if you are sure you know what you are doing!!',
|
||||
detail: command,
|
||||
checkboxLabel: 'Remember my answer',
|
||||
checkboxChecked: true,
|
||||
});
|
||||
const { response, checkboxChecked } = res;
|
||||
|
||||
if (response === 1) {
|
||||
if (checkboxChecked) {
|
||||
await saveSimpleStore(COMMAND_MAP_PROP, [...allowedCommands, command]);
|
||||
}
|
||||
exec(command, (err) => {
|
||||
if (err) {
|
||||
errorHandlerWithFrontendInform(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
69
electron/ipc-handlers/app-control.ts
Normal file
69
electron/ipc-handlers/app-control.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { app, ipcMain, ProgressBarOptions } from 'electron';
|
||||
import { IPC } from '../shared-with-frontend/ipc-events.const';
|
||||
import { getWin } from '../main-window';
|
||||
import { quitApp, showOrFocus } from '../various-shared';
|
||||
import {
|
||||
getIsLocked,
|
||||
setIsMinimizeToTray,
|
||||
setIsTrayShowCurrentTask,
|
||||
setIsTrayShowCurrentCountdown,
|
||||
} from '../shared-state';
|
||||
import { lockscreen } from '../lockscreen';
|
||||
import { errorHandlerWithFrontendInform } from '../error-handler-with-frontend-inform';
|
||||
import { GlobalConfigState } from '../../src/app/features/config/global-config.model';
|
||||
|
||||
export const initAppControlIpc = (): void => {
|
||||
ipcMain.on(IPC.SHUTDOWN_NOW, quitApp);
|
||||
ipcMain.on(IPC.EXIT, (ev, exitCode: number) => app.exit(exitCode));
|
||||
ipcMain.on(IPC.RELAUNCH, () => app.relaunch());
|
||||
ipcMain.on(IPC.OPEN_DEV_TOOLS, () => getWin().webContents.openDevTools());
|
||||
ipcMain.on(IPC.RELOAD_MAIN_WIN, () => getWin().reload());
|
||||
|
||||
ipcMain.on(IPC.TRANSFER_SETTINGS_TO_ELECTRON, (ev, cfg: GlobalConfigState) => {
|
||||
setIsMinimizeToTray(cfg.misc.isMinimizeToTray);
|
||||
setIsTrayShowCurrentTask(cfg.misc.isTrayShowCurrentTask);
|
||||
setIsTrayShowCurrentCountdown(cfg.misc.isTrayShowCurrentCountdown);
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.SHOW_OR_FOCUS, () => {
|
||||
const mainWin = getWin();
|
||||
showOrFocus(mainWin);
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.LOCK_SCREEN, () => {
|
||||
if (getIsLocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
lockscreen();
|
||||
} catch (e) {
|
||||
errorHandlerWithFrontendInform(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.SET_PROGRESS_BAR, (ev, { progress, progressBarMode }) => {
|
||||
const mainWin = getWin();
|
||||
if (mainWin) {
|
||||
if (progressBarMode === 'none') {
|
||||
mainWin.setProgressBar(-1);
|
||||
} else {
|
||||
mainWin.setProgressBar(Math.min(Math.max(progress, 0), 1), {
|
||||
mode: progressBarMode as ProgressBarOptions['mode'],
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.FLASH_FRAME, (ev) => {
|
||||
const mainWin = getWin();
|
||||
if (mainWin) {
|
||||
mainWin.flashFrame(false);
|
||||
mainWin.flashFrame(true);
|
||||
|
||||
mainWin.once('focus', () => {
|
||||
mainWin.flashFrame(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
17
electron/ipc-handlers/app-data.ts
Normal file
17
electron/ipc-handlers/app-data.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { app, ipcMain } from 'electron';
|
||||
import { IPC } from '../shared-with-frontend/ipc-events.const';
|
||||
import { BACKUP_DIR, BACKUP_DIR_WINSTORE } from '../backup';
|
||||
|
||||
export const initAppDataIpc = (): void => {
|
||||
ipcMain.handle(IPC.GET_PATH, (ev, name: string) => {
|
||||
return app.getPath(name as Parameters<typeof app.getPath>[0]);
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC.GET_BACKUP_PATH, () => {
|
||||
if (process?.windowsStore) {
|
||||
return BACKUP_DIR_WINSTORE;
|
||||
} else {
|
||||
return BACKUP_DIR;
|
||||
}
|
||||
});
|
||||
};
|
||||
58
electron/ipc-handlers/exec.ts
Normal file
58
electron/ipc-handlers/exec.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import { dialog, ipcMain, IpcMainEvent } from 'electron';
|
||||
import { IPC } from '../shared-with-frontend/ipc-events.const';
|
||||
import { exec } from 'child_process';
|
||||
import { log } from 'electron-log/main';
|
||||
import { loadSimpleStoreAll, saveSimpleStore } from '../simple-store';
|
||||
import { getWin } from '../main-window';
|
||||
import { errorHandlerWithFrontendInform } from '../error-handler-with-frontend-inform';
|
||||
|
||||
const COMMAND_MAP_PROP = 'allowedCommands';
|
||||
|
||||
export const initExecIpc = (): void => {
|
||||
ipcMain.on(IPC.EXEC, execWithFrontendErrorHandlerInform);
|
||||
};
|
||||
|
||||
const execWithFrontendErrorHandlerInform = async (
|
||||
ev: IpcMainEvent,
|
||||
command: string,
|
||||
): Promise<void> => {
|
||||
log('trying to run command ' + command);
|
||||
const existingData = await loadSimpleStoreAll();
|
||||
const allowedCommands: string[] = (existingData[COMMAND_MAP_PROP] as string[]) || [];
|
||||
|
||||
if (!Array.isArray(allowedCommands)) {
|
||||
throw new Error('Invalid configuration: allowedCommands must be an array');
|
||||
}
|
||||
if (allowedCommands.includes(command)) {
|
||||
exec(command, (err) => {
|
||||
if (err) {
|
||||
errorHandlerWithFrontendInform(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const mainWin = getWin();
|
||||
const res = await dialog.showMessageBox(mainWin, {
|
||||
type: 'question',
|
||||
buttons: ['Cancel', 'Yes, execute!'],
|
||||
defaultId: 2,
|
||||
title: 'Super Productivity – Exec',
|
||||
message:
|
||||
'Do you want to execute this command? ONLY confirm if you are sure you know what you are doing!!',
|
||||
detail: command,
|
||||
checkboxLabel: 'Remember my answer',
|
||||
checkboxChecked: true,
|
||||
});
|
||||
const { response, checkboxChecked } = res;
|
||||
|
||||
if (response === 1) {
|
||||
if (checkboxChecked) {
|
||||
await saveSimpleStore(COMMAND_MAP_PROP, [...allowedCommands, command]);
|
||||
}
|
||||
exec(command, (err) => {
|
||||
if (err) {
|
||||
errorHandlerWithFrontendInform(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
88
electron/ipc-handlers/global-shortcuts.ts
Normal file
88
electron/ipc-handlers/global-shortcuts.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { globalShortcut, ipcMain } from 'electron';
|
||||
import { IPC } from '../shared-with-frontend/ipc-events.const';
|
||||
import { KeyboardConfig } from '../../src/app/features/config/keyboard-config.model';
|
||||
import { getWin } from '../main-window';
|
||||
import { showOrFocus } from '../various-shared';
|
||||
import { errorHandlerWithFrontendInform } from '../error-handler-with-frontend-inform';
|
||||
|
||||
export const initGlobalShortcutsIpc = (): void => {
|
||||
ipcMain.on(IPC.REGISTER_GLOBAL_SHORTCUTS_EVENT, (ev, cfg) => {
|
||||
registerShowAppShortCuts(cfg);
|
||||
});
|
||||
};
|
||||
|
||||
const registerShowAppShortCuts = (cfg: KeyboardConfig): void => {
|
||||
// unregister all previous
|
||||
globalShortcut.unregisterAll();
|
||||
const GLOBAL_KEY_CFG_KEYS: (keyof KeyboardConfig)[] = [
|
||||
'globalShowHide',
|
||||
'globalToggleTaskStart',
|
||||
'globalAddNote',
|
||||
'globalAddTask',
|
||||
];
|
||||
|
||||
if (cfg) {
|
||||
const mainWin = getWin();
|
||||
Object.keys(cfg)
|
||||
.filter((key: string) => GLOBAL_KEY_CFG_KEYS.includes(key as keyof KeyboardConfig))
|
||||
.forEach((key: string) => {
|
||||
let actionFn: () => void;
|
||||
const shortcut = cfg[key as keyof KeyboardConfig];
|
||||
|
||||
switch (key) {
|
||||
case 'globalShowHide':
|
||||
actionFn = () => {
|
||||
if (mainWin.isFocused()) {
|
||||
// we need to blur the window for windows
|
||||
mainWin.blur();
|
||||
mainWin.hide();
|
||||
} else {
|
||||
showOrFocus(mainWin);
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case 'globalToggleTaskStart':
|
||||
actionFn = () => {
|
||||
mainWin.webContents.send(IPC.TASK_TOGGLE_START);
|
||||
};
|
||||
break;
|
||||
|
||||
case 'globalAddNote':
|
||||
actionFn = () => {
|
||||
showOrFocus(mainWin);
|
||||
mainWin.webContents.send(IPC.ADD_NOTE);
|
||||
};
|
||||
break;
|
||||
|
||||
case 'globalAddTask':
|
||||
actionFn = () => {
|
||||
showOrFocus(mainWin);
|
||||
// NOTE: delay slightly to make sure app is ready
|
||||
mainWin.webContents.send(IPC.SHOW_ADD_TASK_BAR);
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
actionFn = () => undefined;
|
||||
}
|
||||
|
||||
if (shortcut && shortcut.length > 0) {
|
||||
try {
|
||||
const ret = globalShortcut.register(shortcut, actionFn) as unknown;
|
||||
if (!ret) {
|
||||
errorHandlerWithFrontendInform(
|
||||
'Global Shortcut registration failed: ' + shortcut,
|
||||
shortcut,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
errorHandlerWithFrontendInform(
|
||||
'Global Shortcut registration failed: ' + shortcut,
|
||||
{ e, shortcut },
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
6
electron/ipc-handlers/index.ts
Normal file
6
electron/ipc-handlers/index.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export { initAppControlIpc } from './app-control';
|
||||
export { initAppDataIpc } from './app-data';
|
||||
export { initExecIpc } from './exec';
|
||||
export { initGlobalShortcutsIpc } from './global-shortcuts';
|
||||
export { initJiraIpc } from './jira';
|
||||
export { initSystemIpc } from './system';
|
||||
14
electron/ipc-handlers/jira.ts
Normal file
14
electron/ipc-handlers/jira.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { ipcMain } from 'electron';
|
||||
import { IPC } from '../shared-with-frontend/ipc-events.const';
|
||||
import { JiraCfg } from '../../src/app/features/issue/providers/jira/jira.model';
|
||||
import { sendJiraRequest, setupRequestHeadersForImages } from '../jira';
|
||||
|
||||
export const initJiraIpc = (): void => {
|
||||
ipcMain.on(IPC.JIRA_SETUP_IMG_HEADERS, (ev, { jiraCfg }: { jiraCfg: JiraCfg }) => {
|
||||
setupRequestHeadersForImages(jiraCfg);
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.JIRA_MAKE_REQUEST_EVENT, (ev, request) => {
|
||||
sendJiraRequest(request);
|
||||
});
|
||||
};
|
||||
36
electron/ipc-handlers/system.ts
Normal file
36
electron/ipc-handlers/system.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { app, dialog, ipcMain, shell } from 'electron';
|
||||
import { IPC } from '../shared-with-frontend/ipc-events.const';
|
||||
import { getWin } from '../main-window';
|
||||
|
||||
export const initSystemIpc = (): void => {
|
||||
ipcMain.on(IPC.OPEN_PATH, (ev, path: string) => shell.openPath(path));
|
||||
ipcMain.on(IPC.OPEN_EXTERNAL, (ev, url: string) => shell.openExternal(url));
|
||||
|
||||
ipcMain.on(
|
||||
IPC.SHOW_EMOJI_PANEL,
|
||||
() => app.isEmojiPanelSupported() && app.showEmojiPanel(),
|
||||
);
|
||||
|
||||
ipcMain.handle(IPC.SAVE_FILE_DIALOG, async (ev, { filename, data }) => {
|
||||
const result = await dialog.showSaveDialog(getWin(), {
|
||||
defaultPath: filename,
|
||||
filters: [
|
||||
{ name: 'JSON Files', extensions: ['json'] },
|
||||
{ name: 'All Files', extensions: ['*'] },
|
||||
],
|
||||
});
|
||||
|
||||
if (!result.canceled && result.filePath) {
|
||||
const fs = await import('fs');
|
||||
await fs.promises.writeFile(result.filePath, data, 'utf-8');
|
||||
return { success: true, path: result.filePath };
|
||||
}
|
||||
return { success: false };
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC.SHARE_NATIVE, async () => {
|
||||
// Desktop platforms use the share dialog instead of native share
|
||||
// This allows for more flexibility and better UX with social media options
|
||||
return { success: false, error: 'Native share not available on desktop' };
|
||||
});
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue