add sidebar

This commit is contained in:
Anton Bracke 2022-02-18 01:51:35 +01:00
parent a11d19a457
commit c891fcb1bd
No known key found for this signature in database
GPG Key ID: B1222603899C6B25
12 changed files with 211 additions and 78 deletions

View File

@ -6,7 +6,7 @@
"license": "SEE LICENSE IN LICENSE.txt", "license": "SEE LICENSE IN LICENSE.txt",
"scripts": { "scripts": {
"tsc": "tsup-node ./src/main.ts", "tsc": "tsup-node ./src/main.ts",
"dev": "tsup-node ./src/main.ts ./src/preload/index.ts --watch --onSuccess 'yarn electron dist/main.js'", "dev": "tsup-node ./src/main.ts ./src/sidebar/preload.ts ./src/app/preload.js --watch --onSuccess 'yarn electron dist/main.js'",
"prod": "tsc && node --max-old-space-size=4096 ./dist/server.js", "prod": "tsc && node --max-old-space-size=4096 ./dist/server.js",
"runprod": "node --max-old-space-size=4096 ./dist/server.js", "runprod": "node --max-old-space-size=4096 ./dist/server.js",
"profile": "tsc && node --prof ./dist/server.js", "profile": "tsc && node --prof ./dist/server.js",

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WorkAdventure Desktop Demo</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
}
body {
display: flex;
flex-direction: column;
width: 70px;
background-color: #25292E;
}
#servers {
display: flex;
flex-direction: column;
margin-bottom: 1rem;
}
#servers div {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
margin: 0.5rem;
color: #62727C;
border: 1px solid #62727C;
text-align: center;
border-radius: 50%;
height: 50px;
width: 50px;
}
#servers div:hover {
border-color: #E1E4E8;
color: #E1E4E8;
}
#btn-reload {
margin-top: auto;
}
</style>
</head>
<body>
<div id="servers"></div>
<button id="btn-reload">Refresh</button>
<script src="./index.js" lang="javascript"></script>
</body>
</html>

34
desktop/sidebar/index.js Normal file
View File

@ -0,0 +1,34 @@
// let muted = false;
if (window?.WorkAdventureDesktopApi?.desktop) {
(async () => {
const servers = await window.WorkAdventureDesktopApi.getServers();
servers.forEach((e) => {
const server = document.createElement("div");
server.innerText = e.name;
server.onclick = () => {
window.WorkAdventureDesktopApi.selectServer(e._id);
};
document.getElementById("servers").appendChild(server);
});
})();
}
document.getElementById("btn-reload").onclick = () => {
location.reload();
};
// window?.WorkAdventureDesktopApi?.onMutedKeyPress((event) => {
// if (muted) {
// document.getElementById("demo").innerHTML =
// "Ready to speak! Press ctrl-alt-m to mute.";
// } else {
// document.getElementById("demo").innerHTML =
// "Muted! Press ctrl-alt-m to unmute again.";
// }
// muted = !muted;
// });
// document.getElementById("btn-api").onclick = () => {
// window.WorkAdventureDesktopApi.notify("Hello from website");
// };

View File

@ -2,7 +2,7 @@ import { contextBridge, ipcRenderer, IpcRendererEvent } from "electron";
contextBridge.exposeInMainWorld("WorkAdventureDesktopApi", { contextBridge.exposeInMainWorld("WorkAdventureDesktopApi", {
desktop: true, desktop: true,
notify: (txt: string) => ipcRenderer.send("notify", txt), notify: (txt: string) => ipcRenderer.send("app:notify", txt),
onMutedKeyPress: (callback: (event: IpcRendererEvent) => void) => onMutedKeyPress: (callback: (event: IpcRendererEvent) => void) =>
ipcRenderer.on("on-muted-key-press", callback), ipcRenderer.on("app:on-muted-key-press", callback),
}); });

View File

@ -1,6 +1,7 @@
import { ipcMain } from "electron"; import { ipcMain } from "electron";
import { createAndShowNotification } from "./notification"; import { createAndShowNotification } from "./notification";
import { getWindow } from "./window"; import settings from "./settings";
import { getAppView, getWindow } from "./window";
export function emitMutedKeyPress() { export function emitMutedKeyPress() {
const mainWindow = getWindow(); const mainWindow = getWindow();
@ -8,11 +9,62 @@ export function emitMutedKeyPress() {
throw new Error("Main window not found"); throw new Error("Main window not found");
} }
mainWindow.webContents.send("on-muted-key-press"); mainWindow.webContents.send("app:on-muted-key-press");
} }
export default () => { export default () => {
ipcMain.on("notify", (event, txt) => { ipcMain.on("app:notify", (event, txt) => {
createAndShowNotification({ body: txt }); createAndShowNotification({ body: txt });
}); });
ipcMain.handle("sidebar:getServers", () => {
// TODO: remove
if (!settings.get("servers")) {
settings.set("servers", [
{
_id: "1",
name: "WA Demo",
url: "https://play.staging.workadventu.re/@/tcm/workadventure/wa-village",
},
{
_id: "2",
name: "My Server",
url: "http://play.workadventure.localhost/",
},
]);
}
return settings.get("servers", []);
});
ipcMain.handle("sidebar:selectServer", (event, serverId: string) => {
const appView = getAppView();
if (!appView) {
throw new Error("App view not found");
}
const servers = settings.get("servers", []);
const selectedServer = servers.find((s) => s._id === serverId);
if (!selectedServer) {
return new Error("Server not found");
}
appView.webContents.loadURL(selectedServer.url);
return true;
});
ipcMain.handle(
"sidebar:addServer",
(event, serverName: string, serverUrl: string) => {
const servers = settings.get("servers", []);
servers.push({
_id: `${servers.length + 1}`,
name: serverName,
url: serverUrl,
});
settings.set("servers", servers);
return true;
}
);
}; };

View File

@ -1,5 +1,4 @@
import { dialog, shell } from "electron"; import { dialog, shell } from "electron";
import electronIsDev from "electron-is-dev";
import log from "electron-log"; import log from "electron-log";
import settings from "./settings"; import settings from "./settings";
@ -40,18 +39,12 @@ function onRejection(reason: Error) {
function init() { function init() {
const logLevel = settings.get("log_level", "info"); const logLevel = settings.get("log_level", "info");
log.transports.file.level = logLevel;
log.transports.console.level = logLevel; log.transports.console.level = logLevel;
log.transports.file.level = logLevel;
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log = log.log.bind(log); console.log = log.log.bind(log);
if (!electronIsDev) {
log.transports.file.fileName = "work-adventure.log";
} else {
console.log("Log file is disabled in dev. Using console output instead.");
}
process.on("uncaughtException", onError); process.on("uncaughtException", onError);
process.on("unhandledRejection", onRejection); process.on("unhandledRejection", onRejection);
} }

View File

@ -1,9 +1,16 @@
import ElectronLog from "electron-log"; import ElectronLog from "electron-log";
import Settings from "electron-settings"; import Settings from "electron-settings";
type Server = {
_id: string;
name: string;
url: string;
};
type SettingsData = { type SettingsData = {
log_level: ElectronLog.LogLevel; log_level: ElectronLog.LogLevel;
auto_launch_enabled: boolean; auto_launch_enabled: boolean;
servers: Server[];
}; };
let settings: SettingsData; let settings: SettingsData;

View File

@ -0,0 +1,10 @@
import { contextBridge, ipcRenderer } from "electron";
contextBridge.exposeInMainWorld("WorkAdventureDesktopApi", {
desktop: true,
getServers: () => ipcRenderer.invoke("sidebar:getServers"),
selectServer: (serverId: string) =>
ipcRenderer.invoke("sidebar:selectServer", serverId),
addServer: (serverName: string, serverUrl: string) =>
ipcRenderer.invoke("sidebar:addServer", serverName, serverUrl),
});

View File

@ -20,7 +20,7 @@ export function createTray() {
const trayContextMenu = Menu.buildFromTemplate([ const trayContextMenu = Menu.buildFromTemplate([
{ {
id: "open", id: "open",
label: "Open / Close", label: "Show / Hide",
click() { click() {
const mainWindow = getWindow(); const mainWindow = getWindow();
if (!mainWindow) { if (!mainWindow) {

View File

@ -1,17 +1,20 @@
import { BrowserWindow } from "electron"; import { BrowserView, BrowserWindow } from "electron";
import electronIsDev from "electron-is-dev";
import windowStateKeeper from "electron-window-state"; import windowStateKeeper from "electron-window-state";
import path from "path"; import path from "path";
let mainWindow: BrowserWindow | undefined; let mainWindow: BrowserWindow | undefined;
let appView: BrowserView | undefined;
const url = process.env.PLAY_URL; const sidebarWidth = 70;
// "https://play.staging.workadventu.re/@/tcm/workadventure/wa-village"; // TODO
export function getWindow() { export function getWindow() {
return mainWindow; return mainWindow;
} }
export function getAppView() {
return appView;
}
export function createWindow() { export function createWindow() {
// do not re-create window if still existing // do not re-create window if still existing
if (mainWindow) { if (mainWindow) {
@ -32,13 +35,8 @@ export function createWindow() {
height: windowState.height, height: windowState.height,
autoHideMenuBar: true, autoHideMenuBar: true,
show: false, show: false,
title: "WorkAdventure",
webPreferences: { webPreferences: {
preload: path.join(__dirname, "../dist/preload/index.js"), preload: path.join(__dirname, "../dist/sidebar/preload.js"),
// allowRunningInsecureContent: false,
// contextIsolation: true, // TODO: remove in electron 12
// nodeIntegration: false,
// sandbox: true,
}, },
}); });
@ -47,18 +45,10 @@ export function createWindow() {
// and restore the maximized or full screen state // and restore the maximized or full screen state
windowState.manage(mainWindow); windowState.manage(mainWindow);
mainWindow.on("show", () => {
// TODO
});
mainWindow.on("closed", () => { mainWindow.on("closed", () => {
mainWindow = undefined; mainWindow = undefined;
}); });
mainWindow.once("ready-to-show", () => {
mainWindow?.show();
});
// mainWindow.on('close', async (event) => { // mainWindow.on('close', async (event) => {
// if (!app.confirmedExitPrompt) { // if (!app.confirmedExitPrompt) {
// event.preventDefault(); // Prevents the window from closing // event.preventDefault(); // Prevents the window from closing
@ -77,11 +67,37 @@ export function createWindow() {
// } // }
// }); // });
if (!url || electronIsDev) { appView = new BrowserView({
// TODO webPreferences: {
mainWindow.loadFile("../test-app/index.html"); preload: path.join(__dirname, "../dist/app/index.js"),
mainWindow.webContents.openDevTools(); },
} else { });
mainWindow.loadURL(url); // TODO: load app on demand mainWindow.setBrowserView(appView);
} appView.setBounds({
x: sidebarWidth,
y: 0,
width: mainWindow.getBounds().width - sidebarWidth,
height: mainWindow.getBounds().height,
});
appView.setAutoResize({
width: true,
height: true,
});
mainWindow.once("ready-to-show", () => {
(async () => {
await appView?.webContents.loadURL("https://workadventu.re/"); // TODO: use some splashscreen ?
// appView.webContents.openDevTools({
// mode: "detach",
// });
mainWindow?.show();
// mainWindow?.webContents.openDevTools({ mode: "detach" });
})();
});
mainWindow.webContents.on("did-finish-load", () => {
mainWindow?.setTitle("WorkAdventure Desktop");
});
mainWindow.loadFile("../sidebar/index.html");
} }

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WorkAdventure Desktop Demo</title>
</head>
<body>
<div id="demo">Hello World!</div>
<button id="btn-reload">Refresh</button>
<button id="btn-api">Call api</button>
<script src="./web.js" lang="javascript"></script>
</body>
</html>

View File

@ -1,25 +0,0 @@
let muted = false;
if (window?.WorkAdventureDesktopApi?.desktop) {
document.getElementById("demo").innerHTML =
"Hello Desktop app! Press ctrl-alt-m to mute.";
window?.WorkAdventureDesktopApi?.onMutedKeyPress((event) => {
if (muted) {
document.getElementById("demo").innerHTML =
"Ready to speak! Press ctrl-alt-m to mute.";
} else {
document.getElementById("demo").innerHTML =
"Muted! Press ctrl-alt-m to unmute again.";
}
muted = !muted;
});
}
document.getElementById("btn-reload").onclick = () => {
location.reload();
};
document.getElementById("btn-api").onclick = () => {
window.WorkAdventureDesktopApi.notify("Hello from website");
};