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

feat: Add global shortcuts to trigger widget action #160

Merged
merged 1 commit into from
Feb 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 35 additions & 7 deletions app/main/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
"use strict";

const { app } = require("electron");
const { app, globalShortcut } = require("electron");
const server = require("./server");

let mainWindow;

async function onServerReady() {
function registerGlobalShortcut(accelerator, id = null) {
const result = globalShortcut.register(accelerator, () => {
server.send({ type: "shortcut", accelerator });
});
server.send({ type: "registerGlobalShortcut", data: { id, result } });
return result;
}

function registerGlobalShortcuts(shortcuts) {
shortcuts.forEach(registerGlobalShortcut);
}

function onRegisterShortcut({ accelerator, id }) {
return registerGlobalShortcut(accelerator, id);
}

function onUnregisterShortcut({ shortcuts }) {
globalShortcut.unregisterAll();
registerGlobalShortcuts(shortcuts);
}

async function onServerReady(serverState) {
const tray = require("./tray");
mainWindow = require("./window/mainWindow");
const settings = require("../server/libs/settings");
mainWindow({ showOnLoad: await settings.get("app.openOnStartup") });
registerGlobalShortcuts(serverState.shortcuts);
tray();
}

Expand All @@ -19,14 +41,20 @@ function init() {
});

app.whenReady().then(() => {
server.start(onServerReady);
server.start({ onServerReady, onRegisterShortcut, onUnregisterShortcut });
});
}

function onQuit() {
globalShortcut.unregisterAll();
server.stop();
}

app.requestSingleInstanceLock() ? init() : app.quit();

app.on("second-instance", () => mainWindow());
app.on("quit", onQuit);

app.on("quit", () => server.stop());
process.on("SIGINT", () => server.stop());
process.on("SIGTERM", () => server.stop());
process.on("SIGKILL", () => server.stop());
process.on("SIGINT", onQuit);
process.on("SIGTERM", onQuit);
process.on("SIGKILL", onQuit);
23 changes: 18 additions & 5 deletions app/main/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const rootPath = path.resolve(__dirname, "../..");
const serverPath = path.join(__dirname, "../server");
const serverBin = path.join(serverPath, "index.js");

let events = {};
let server = null;

function bufferToString(buffer) {
Expand All @@ -25,9 +26,11 @@ function stderr(buffer) {
console.error(colors.redBright("[server]"), bufferToString(buffer));
}

function start(onStared = null) {
function start(settings = {}) {
if (server) return server;

events = { ...events, ...settings };

const argv = process.argv.slice(2);
server = fork(serverBin, argv, { stdio: ["pipe", "pipe", "pipe", "ipc"] });

Expand All @@ -39,10 +42,19 @@ function start(onStared = null) {
code === 42 && quit();
});

onStared &&
server.on("message", (message) => {
if (message === "started") onStared();
});
server.on("message", (event) => {
if (event.type === "registerShortcut") {
events.onRegisterShortcut(event.data);
} else if (event.type === "unregisterShortcut") {
events.onUnregisterShortcut(event.data);
} else if (event.type === "start") {
events.onServerReady(event.data);
}
});
}

function send(message) {
server && server.send(message);
}

function restart() {
Expand Down Expand Up @@ -75,5 +87,6 @@ if (watch) {
module.exports = {
start,
stop,
send,
restart,
};
9 changes: 9 additions & 0 deletions app/server/api/panels.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,13 @@ module.exports = {
this.notify(`panels.${payload.event}`, payload.panel);
return payload;
},
getShortcuts() {
return panels.getShortcuts();
},
registerShortcut(accelerator) {
return panels.registerShortcut(accelerator);
},
unregisterShortcut(accelerator) {
return panels.unregisterShortcut(accelerator);
},
};
2 changes: 1 addition & 1 deletion app/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function uriDecode(req, res, next) {

function onStarted() {
if (typeof process.send === "function") {
process.send("started");
require("./onStart");
}
}

Expand Down
1 change: 1 addition & 0 deletions app/server/libs/loggers.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function createLogger({ group, console = false } = {}) {
dirname: logsPath,
maxSize: "42m",
maxFiles: "7d",
level: "debug",
}),
],
});
Expand Down
51 changes: 51 additions & 0 deletions app/server/libs/panels.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ function remove(panel) {
return true;
});
store.set("panels", panels);
unregisterShortcut(null);
return pos;
}

Expand Down Expand Up @@ -310,6 +311,53 @@ function importArchive(panel, archive) {
return { error: "Unsupported file format" };
}

function getShortcuts() {
const shortcuts = [];

panels.forEach(({ widgets }) => {
widgets.forEach((widget) => {
if (widget.shortcutName && !shortcuts.includes(widget.shortcutName)) {
shortcuts.push(widget.shortcutName);
}
});
});

return shortcuts;
}

let registerShortcutId = null;
let registerShortcutResolve = null;

process.on("message", ({ type, data }) => {
if (type === "registerGlobalShortcut" && registerShortcutId === data.id) {
registerShortcutResolve(data);
}
});

function registerShortcut(accelerator) {
registerShortcutId = uuid();
const shortcuts = getShortcuts();
if (shortcuts.includes(accelerator)) {
return { id: registerShortcutId, result: true };
}
return new Promise((resolve) => {
registerShortcutResolve = resolve;
process.send({
type: "registerShortcut",
data: { id: registerShortcutId, accelerator },
});
});
}

function unregisterShortcut(accelerator) {
registerShortcutId = uuid();
process.send({
type: "unregisterShortcut",
data: { id: registerShortcutId, accelerator, shortcuts: getShortcuts() },
});
return { id: registerShortcutId, result: true };
}

module.exports = {
add,
set,
Expand All @@ -321,8 +369,11 @@ module.exports = {
removeWidget,
importArchive,
exportWidget,
getShortcuts,
getWidgetFiles,
duplicateWidget,
moveWidgetToPanel,
removeWidgetComponent,
registerShortcut,
unregisterShortcut,
};
1 change: 1 addition & 0 deletions app/server/libs/twitch/events.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[
{ "name": "onShortcut", "tags": [] },
{ "name": "onAction", "tags": ["user", "message", "date"] },
{ "name": "onBan", "tags": ["user"] },
{ "name": "onBits", "tags": ["user", "message", "amount", "total"] },
Expand Down
16 changes: 14 additions & 2 deletions app/server/libs/twitch/pushActions.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
const stores = require("../../../stores");
const { push } = require("../actions");
const loggers = require("../loggers");

const logger = loggers.get("server");

const types = {
AnimeTimeline: "anime",
SceneList: "obs",
ToggleScene: "obs",
GoToScene: "obs",
ToggleScene: "obs",
AnimeTimeline: "anime",
};

function isInvalidShortcut(widget, eventProps) {
return (
eventProps.accelerator && widget.shortcutName !== eventProps.accelerator
);
}

function isInvalidCommand(widget, eventProps) {
return eventProps.command && widget.commandName !== eventProps.command.name;
}
Expand All @@ -17,6 +26,8 @@ function isInvalidReward(widget, eventProps) {
}

module.exports = function pushActions(eventName, eventProps) {
logger.debug("pushActions", { eventName, eventProps });

stores.panels.get("panels").forEach(({ widgets }) => {
widgets.forEach((widget) => {
if (!widget.component) return;
Expand All @@ -27,6 +38,7 @@ module.exports = function pushActions(eventName, eventProps) {
if (widget.eventName !== eventName) return;
if (isInvalidReward(widget, eventProps)) return;
if (isInvalidCommand(widget, eventProps)) return;
if (isInvalidShortcut(widget, eventProps)) return;

push({ type, widget, eventProps });
});
Expand Down
10 changes: 10 additions & 0 deletions app/server/onStart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const pushActions = require("./libs/twitch/pushActions");
const { getShortcuts } = require("./libs/panels");

process.send({ type: "start", data: { shortcuts: getShortcuts() } });

process.on("message", (event) => {
if (event.type === "shortcut") {
pushActions("onShortcut", event);
}
});
10 changes: 7 additions & 3 deletions app/static/locales/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@
"file-use-count": "File used by {{count}} widget",
"file-use-count_plural": "File used by {{count}} widgets",
"ask-purge": "Delete {{count}} files not in use?",
"ask-remove-asset": "Remove \"{{filename}}\" from the timeline?"
"ask-remove-asset": "Remove \"{{filename}}\" from the timeline?",
"global-shortcut-reserved": "This shortcut is reserved by the system!",
"type-shortcut-here": "Type shortcut here..."
},
"words": {
"settings": "settings",
Expand Down Expand Up @@ -151,7 +153,8 @@
"close": "close",
"confirm": "confirm",
"export": "export",
"import": "import"
"import": "import",
"shortcut": "shortcut"
},
"obs": {
"scene-list": "OBS | Scene list",
Expand Down Expand Up @@ -206,7 +209,8 @@
"onSubGift": "Sub gift",
"onCommand": "Command",
"onRedemption": "Redemption",
"onBits": "Bits"
"onBits": "Bits",
"onShortcut": "Shortcut"
}
},
"labels": {
Expand Down
10 changes: 7 additions & 3 deletions app/static/locales/es/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@
"file-use-count": "Archivo utilizado por {{count}} widget",
"file-use-count_plural": "Archivo utilizado por {{count}} widgets",
"ask-purge": "¿Borrar {{count}} archivos no utilizados?",
"ask-remove-asset": "¿Eliminar \"{{filename}}\" de la línea de tiempo?"
"ask-remove-asset": "¿Eliminar \"{{filename}}\" de la línea de tiempo?",
"global-shortcut-reserved": "Este acceso directo está reservado por el sistema!",
"type-shortcut-here": "Escriba el acceso directo aquí..."
},
"words": {
"settings": "opciones",
Expand Down Expand Up @@ -150,7 +152,8 @@
"close": "cerrar",
"confirm": "confirmar",
"export": "exportar",
"import": "importar"
"import": "importar",
"shortcut": "shortcut"
},
"obs": {
"scene-list": "OBS | Lista de escenas",
Expand Down Expand Up @@ -205,7 +208,8 @@
"onSubGift": "Sub gift",
"onCommand": "Comando",
"onRedemption": "Redemption",
"onBits": "Bits"
"onBits": "Bits",
"onShortcut": "Shortcut"
}
},
"labels": {
Expand Down
10 changes: 7 additions & 3 deletions app/static/locales/fr/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@
"file-use-count": "Fichier utilisé par {{count}} widget",
"file-use-count_plural": "Fichier utilisé par {{count}} widgets",
"ask-purge": "Supprimer {{count}} fichiers non utiliser ?",
"ask-remove-asset": "Supprimer \"{{filename}}\" de la timeline ?"
"ask-remove-asset": "Supprimer \"{{filename}}\" de la timeline ?",
"global-shortcut-reserved": "Ce raccourci est réservé par le système!",
"type-shortcut-here": "Tapez le raccourci ici..."
},
"words": {
"settings": "options",
Expand Down Expand Up @@ -151,7 +153,8 @@
"close": "fermer",
"confirm": "confirmer",
"export": "exporter",
"import": "importer"
"import": "importer",
"shortcut": "shortcut"
},
"obs": {
"scene-list": "OBS | Liste des scènes",
Expand Down Expand Up @@ -206,7 +209,8 @@
"onSubGift": "Sub gift",
"onCommand": "Command",
"onRedemption": "Redemption",
"onBits": "Bits"
"onBits": "Bits",
"onShortcut": "Shortcut"
}
},
"labels": {
Expand Down
5 changes: 5 additions & 0 deletions front-src/client/api/panels.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ export default {
exportWidget: (panel, widget) => emit("panels.exportWidget", panel, widget),
exportPanel: (panel) => emit("panels.exportPanel", panel),
importArchive: (panel, widget) => emit("panels.importArchive", panel, widget),
getShortcuts: () => emit("panels.getShortcuts"),
registerShortcut: (accelerator) =>
emit("panels.registerShortcut", accelerator),
unregisterShortcut: (accelerator) =>
emit("panels.unregisterShortcut", accelerator),
};
Loading