add desktop api to front

This commit is contained in:
Anton Bracke 2022-02-22 16:57:56 +01:00
parent 1425513452
commit 4e243151dd
No known key found for this signature in database
GPG Key ID: B1222603899C6B25
21 changed files with 174 additions and 67 deletions

View File

@ -27,7 +27,8 @@
"electron-settings": "^4.0.2",
"electron-updater": "^4.6.5",
"electron-util": "^0.17.2",
"electron-window-state": "^5.0.3"
"electron-window-state": "^5.0.3",
"node-fetch": "^3.2.0"
},
"devDependencies": {
"@types/auto-launch": "^5.0.2",

View File

@ -3,25 +3,26 @@ import electronIsDev from "electron-is-dev";
import { createAndShowNotification } from "./notification";
import { Server } from "./preload-local-app/types";
import settings, { SettingsData } from "./settings";
import { loadShortcuts, saveShortcut, setShortcutsEnabled } from "./shortcuts";
import { getWindow, hideAppView, showAppView } from "./window";
import { loadShortcuts, setShortcutsEnabled } from "./shortcuts";
import { getAppView, getWindow, hideAppView, showAppView } from "./window";
// import fetch from "node-fetch";
export function emitMuteToggle() {
const mainWindow = getWindow();
if (!mainWindow) {
const appView = getAppView();
if (!appView) {
throw new Error("Main window not found");
}
mainWindow.webContents.send("app:on-camera-toggle");
appView.webContents.send("app:on-camera-toggle");
}
export function emitCameraToggle() {
const mainWindow = getWindow();
if (!mainWindow) {
const appView = getAppView();
if (!appView) {
throw new Error("Main window not found");
}
mainWindow.webContents.send("app:on-mute-toggle");
appView.webContents.send("app:on-mute-toggle");
}
export default () => {
@ -68,7 +69,7 @@ export default () => {
try {
// TODO: add proper test to see if server url is valid and points to a real WA server
await fetch(`${server.url}/iframe_api.js`);
// await fetch(`${server.url}/iframe_api.js`);
} catch (e) {
console.error(e);
return new Error("Invalid server url");
@ -76,7 +77,7 @@ export default () => {
const newServer = {
...server,
_id: `${servers.length + 1}`,
_id: `${Date.now()}-${servers.length + 1}`,
};
servers.push(newServer);
settings.set("servers", servers);

View File

@ -10,4 +10,4 @@ const api: WorkAdventureDesktopApi = {
onCameraToggle: (callback) => ipcRenderer.on("app:on-camera-toggle", callback),
};
contextBridge.exposeInMainWorld("WorkAdventureDesktopApi", api);
contextBridge.exposeInMainWorld("WAD", api);

View File

@ -16,4 +16,4 @@ const api: WorkAdventureLocalAppApi = {
setShortcutsEnabled: (enabled) => ipcRenderer.invoke("local-app:setShortcutsEnabled", enabled),
};
contextBridge.exposeInMainWorld("WorkAdventureDesktopApi", api);
contextBridge.exposeInMainWorld("WAD", api);

View File

@ -1,7 +1,6 @@
import { globalShortcut } from "electron";
import settings, { SettingsData } from "./settings";
import { emitCameraToggle, emitMuteToggle } from "./ipc";
import { createAndShowNotification } from "./notification";
export function setShortcutsEnabled(enabled: boolean) {
if (enabled) {
@ -16,18 +15,15 @@ export function loadShortcuts() {
const shortcuts = settings.get("shortcuts");
// // mute key
if (shortcuts?.mute_toggle && shortcuts.mute_toggle.length > 0) {
globalShortcut.register(shortcuts.mute_toggle, () => {
emitMuteToggle();
createAndShowNotification({ body: "Toggled mute" }); // TODO
});
}
if (shortcuts?.camera_toggle && shortcuts.camera_toggle.length > 0) {
globalShortcut.register(shortcuts.camera_toggle, () => {
emitCameraToggle();
createAndShowNotification({ body: "Toggled camera" }); // TODO
});
}
}

View File

@ -17,6 +17,19 @@ export function getAppView() {
return appView;
}
function resizeAppView() {
if (!mainWindow || !appView) {
return;
}
appView.setBounds({
x: sidebarWidth,
y: 0,
width: mainWindow.getBounds().width - sidebarWidth,
height: mainWindow.getBounds().height,
});
}
export async function createWindow() {
// do not re-create window if still existing
if (mainWindow) {
@ -41,6 +54,7 @@ export async function createWindow() {
preload: path.resolve(__dirname, "..", "dist", "preload-local-app", "preload.js"),
},
});
mainWindow.setMenu(null);
// Let us register listeners on the window, so we can update the state
// automatically (the listeners will be removed when the window is closed)
@ -74,16 +88,8 @@ export async function createWindow() {
preload: path.resolve(__dirname, "..", "dist", "preload-app", "preload.js"),
},
});
appView.setBounds({
x: sidebarWidth,
y: 0,
width: mainWindow.getBounds().width - sidebarWidth,
height: mainWindow.getBounds().height,
});
appView.setAutoResize({
width: true,
height: true,
});
resizeAppView();
mainWindow.on("resize", resizeAppView);
mainWindow.once("ready-to-show", () => {
mainWindow?.show();
@ -91,7 +97,7 @@ export async function createWindow() {
// appView?.webContents.openDevTools({
// mode: "detach",
// });
mainWindow?.webContents.openDevTools({ mode: "detach" });
// mainWindow?.webContents.openDevTools({ mode: "detach" });
}
});

View File

@ -843,6 +843,11 @@ crypto-random-string@^2.0.0:
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
data-uri-to-buffer@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz#b5db46aea50f6176428ac05b73be39a57701a64b"
integrity sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==
debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
@ -1455,6 +1460,14 @@ fd-slicer@~1.1.0:
dependencies:
pend "~1.2.0"
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.1.4.tgz#e8c6567f80ad7fc22fd302e7dcb72bafde9c1717"
integrity sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==
dependencies:
node-domexception "^1.0.0"
web-streams-polyfill "^3.0.3"
figures@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
@ -1506,6 +1519,13 @@ form-data@^4.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
formdata-polyfill@^4.0.10:
version "4.0.10"
resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423"
integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
dependencies:
fetch-blob "^3.1.2"
fs-extra@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1"
@ -2246,6 +2266,20 @@ node-addon-api@^1.6.3:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d"
integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==
node-domexception@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
node-fetch@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.2.0.tgz#59390db4e489184fa35d4b74caf5510e8dfbaf3b"
integrity sha512-8xeimMwMItMw8hRrOl3C9/xzU49HV/yE6ORew/l+dxWimO5A4Ra8ld2rerlJvc/O7et5Z1zrWsPX43v1QBjCxw==
dependencies:
data-uri-to-buffer "^4.0.0"
fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
@ -3085,6 +3119,11 @@ verror@^1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
web-streams-polyfill@^3.0.3:
version "3.2.0"
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965"
integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==
which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"

View File

@ -23,6 +23,10 @@
display: flex;
}
a:hover {
text-decoration: none !important;
}
*::-webkit-scrollbar {
width: 10px;
height: 10px;

View File

@ -8,6 +8,7 @@
const Home = () => import("~/views/Home.svelte");
const AddServer = () => import("~/views/AddServer.svelte");
const Settings = () => import("~/views/Settings.svelte");
const Server = () => import("~/views/Server.svelte");
let insideElectron = api.desktop;
</script>
@ -19,6 +20,7 @@
<LazyRoute path="/" component={Home} delayMs={500}>Loading ...</LazyRoute>
<LazyRoute path="/server/add" component={AddServer} delayMs={500}>Loading ...</LazyRoute>
<LazyRoute path="/settings" component={Settings} delayMs={500}>Loading ...</LazyRoute>
<LazyRoute path="/server/:id" component={Server} delayMs={500}>Loading ...</LazyRoute>
<Route>
<h3>404</h3>
<p>No Route could be matched.</p>

View File

@ -9,7 +9,6 @@
{title}
</label>
<slot />
<!-- <input class="shadow appearance-none border border-red-500 rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" id="password" type="password" placeholder="******************"> -->
{#if description.length > 0}
<p class="text-gray-200 text-xs mt-1 italic">{description}</p>
{/if}

View File

@ -1,18 +0,0 @@
<script>
import { navigate } from "svelte-navigator";
import { api } from "~/lib/ipc";
let to = "/";
let clazz = "";
export { clazz as class, to };
async function click() {
await api.showLocalApp();
navigate(to);
}
</script>
<div on:click={click} class={`${clazz}`}>
<slot />
</div>

View File

@ -1,8 +1,8 @@
<script lang="ts">
import { onMount } from "svelte";
import { Link } from "svelte-navigator";
import Link from "~/lib/Link.svelte";
import { servers, selectedServer, selectServer, loadServers } from "~/store";
import { servers, selectedServer, loadServers } from "~/store";
import CogIcon from "~/assets/nes.icons/cog.svg";
import { api } from "~/lib/ipc";
@ -34,17 +34,16 @@
<aside class="flex flex-col bg-gray-700 items-center">
<div class="flex flex-col mt-4 space-y-4 overflow-y-auto pb-4">
{#each $servers as server, i}
<div class="flex flex-col items-center justify-center ">
<Link to="/server/{server._id}" class="flex flex-col items-center justify-center ">
<div
class={`w-16 h-16 p-1 rounded-md flex cursor-pointer text-light-50 border-4 border-transparent text-gray-200 hover:text-gray-500`}
class:border-gray-400={$selectedServer === server._id}
on:click={() => selectServer(server)}
>
<div class={`flex w-full h-full text-center items-center justify-center rounded-md ${getServerColor(i)}`}>
{server.name.slice(0,2).toLocaleUpperCase()}
</div>
</div>
</div>
</Link>
{/each}
</div>
<Link

View File

@ -1,5 +1,5 @@
import type { WorkAdventureLocalAppApi, SettingsData, Server } from "@wa-preload-local-app";
export { WorkAdventureLocalAppApi, SettingsData, Server };
// TODO fix type
export const api = (window as any)?.WorkAdventureDesktopApi as WorkAdventureLocalAppApi;
export const api = window?.WAD;

View File

@ -6,11 +6,11 @@ export const newServer = writable<Omit<Server, "_id">>({
url: "",
});
export const servers = writable<Server[]>([]);
export const selectedServer = writable<string | undefined>("");
export const selectedServer = writable<string>("");
export async function selectServer(server: Server) {
await api.selectServer(server._id);
selectedServer.set(server._id);
export async function selectServer(serverId: string) {
await api.selectServer(serverId);
selectedServer.set(serverId);
}
export async function addServer() {
@ -20,7 +20,7 @@ export async function addServer() {
}
newServer.set({ name: "", url: "" });
servers.update((s) => [...s, addedServer]);
await selectServer(addedServer);
await selectServer(addedServer._id);
}
export async function loadServers() {

View File

@ -0,0 +1,25 @@
<script lang="ts">
import { onDestroy, onMount } from "svelte";
import { useParams } from "svelte-navigator";
import { selectServer, servers } from "~/store";
import { api } from "~/lib/ipc";
const params = useParams();
params.subscribe((_params) => {
selectServer(_params.id);
});
onMount(async () => {
await selectServer($params.id);
});
onDestroy(async () => {
await api.showLocalApp();
});
$: server = $servers.find(({ _id }) => _id === $params.id);
</script>
<div class="hidden m-auto text-gray-400">Server: "{server.name}"</div>

7
desktop/local-app/src/window.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
import type { WorkAdventureLocalAppApi } from "@wa-preload-local-app";
declare global {
interface Window {
WAD: WorkAdventureLocalAppApi;
}
}

View File

@ -0,0 +1,38 @@
import { isSilentStore, requestedCameraState, requestedMicrophoneState } from "../../Stores/MediaStore";
import { get } from "svelte/store";
class DesktopApi {
isSilent: boolean = false;
init() {
if (!window?.WAD?.desktop) {
return;
}
console.log("Yipee you are using the desktop app ;)");
window.WAD.onMuteToggle(() => {
if (this.isSilent) return;
if (get(requestedMicrophoneState) === true) {
requestedMicrophoneState.disableMicrophone();
} else {
requestedMicrophoneState.enableMicrophone();
}
});
window.WAD.onCameraToggle(() => {
if (this.isSilent) return;
if (get(requestedCameraState) === true) {
requestedCameraState.disableWebcam();
} else {
requestedCameraState.enableWebcam();
}
});
isSilentStore.subscribe((value) => {
this.isSilent = value;
});
}
}
export const desktopApi = new DesktopApi();

View File

@ -182,13 +182,6 @@ const wa = {
export type WorkAdventureApi = typeof wa;
declare global {
interface Window {
WA: WorkAdventureApi;
}
let WA: WorkAdventureApi;
}
window.WA = wa;
window.addEventListener(

View File

@ -16,6 +16,7 @@ import { coWebsiteManager } from "./WebRtc/CoWebsiteManager";
import { localUserStore } from "./Connexion/LocalUserStore";
import { ErrorScene } from "./Phaser/Reconnecting/ErrorScene";
import { iframeListener } from "./Api/IframeListener";
import { desktopApi } from "./Api/desktop/index";
import { SelectCharacterMobileScene } from "./Phaser/Login/SelectCharacterMobileScene";
import { HdpiManager } from "./Phaser/Services/HdpiManager";
import { waScaleManager } from "./Phaser/Services/WaScaleManager";
@ -154,6 +155,7 @@ coWebsiteManager.onResize.subscribe(() => {
});
iframeListener.init();
desktopApi.init();
const app = new App({
target: HtmlUtils.getElementByIdOrFail("game-overlay"),

10
front/src/window.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
import { WorkAdventureApi } from "./iframe_api";
import { WorkAdventureDesktopApi } from "@wa-preload-app";
declare global {
interface Window {
WA: WorkAdventureApi;
WAD: WorkAdventureDesktopApi;
}
let WA: WorkAdventureApi;
}

View File

@ -27,7 +27,10 @@
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
"paths": {
"@wa-preload-app": ["../desktop/electron/src/preload-app/types.ts"],
}
},
"exclude": [
"node_modules",