improve local-app

This commit is contained in:
Anton Bracke 2022-02-22 10:05:21 +01:00
parent 6b208ceb0c
commit ac18aab773
No known key found for this signature in database
GPG Key ID: B1222603899C6B25
30 changed files with 841 additions and 105 deletions

View File

@ -17,6 +17,8 @@
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.30",
"@tsconfig/svelte": "^2.0.1",
"prettier": "^2.5.1",
"prettier-plugin-svelte": "^2.6.0",
"rollup-plugin-svelte-svg": "^1.0.0-beta.6",
"svelte": "^3.44.0",
"svelte-check": "^2.2.7",
"svelte-navigator": "^3.1.5",
@ -26,5 +28,10 @@
"vite": "^2.8.0",
"vite-plugin-windicss": "^1.7.1",
"windicss": "^3.4.3"
},
"dependencies": {
"@16bits/nes.css": "^2.3.2",
"@fontsource/press-start-2p": "^4.5.2",
"hotkeys-js": "^3.8.7"
}
}

View File

@ -1,12 +1,13 @@
<script lang="ts">
import { Router, Route } from "svelte-navigator";
import Sidebar from "./lib/Sidebar.svelte";
import Sidebar from "~/lib/Sidebar.svelte";
import LazyRoute from "./lib/LazyRoute.svelte";
import LazyRoute from "~/lib/LazyRoute.svelte";
const Home = () => import("./views/Home.svelte");
const AddServer = () => import("./views/AddServer.svelte");
const Home = () => import("~/views/Home.svelte");
const AddServer = () => import("~/views/AddServer.svelte");
const Settings = () => import("~/views/Settings.svelte");
let insideElectron = window?.WorkAdventureDesktopApi?.desktop;
</script>
@ -17,6 +18,7 @@
<main class="flex flex-grow">
<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>
<Route>
<h3>404</h3>
<p>No Route could be matched.</p>
@ -28,9 +30,12 @@
{/if}
<style>
@import "@fontsource/press-start-2p/index.css";
@import "@16bits/nes.css/css/nes.min.css";
:root {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
"Helvetica Neue", sans-serif;
font-family: "Press Start 2P", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
"Open Sans", "Helvetica Neue", sans-serif;
}
main {

View File

@ -0,0 +1,3 @@
# NES.icons
Source: https://github.com/nostalgic-css/NES.icons/

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" ?>
<svg version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<rect x="7" y="0" width="1" height="1" fill="#000000" />
<rect x="8" y="0" width="1" height="1" fill="#000000" />
<rect x="2" y="1" width="1" height="1" fill="#000000" />
<rect x="7" y="1" width="1" height="1" fill="#000000" />
<rect x="8" y="1" width="1" height="1" fill="#000000" />
<rect x="13" y="1" width="1" height="1" fill="#000000" />
<rect x="1" y="2" width="1" height="1" fill="#000000" />
<rect x="2" y="2" width="1" height="1" fill="#000000" />
<rect x="3" y="2" width="1" height="1" fill="#000000" />
<rect x="5" y="2" width="1" height="1" fill="#000000" />
<rect x="6" y="2" width="1" height="1" fill="#000000" />
<rect x="7" y="2" width="1" height="1" fill="#000000" />
<rect x="8" y="2" width="1" height="1" fill="#000000" />
<rect x="9" y="2" width="1" height="1" fill="#000000" />
<rect x="10" y="2" width="1" height="1" fill="#000000" />
<rect x="12" y="2" width="1" height="1" fill="#000000" />
<rect x="13" y="2" width="1" height="1" fill="#000000" />
<rect x="14" y="2" width="1" height="1" fill="#000000" />
<rect x="2" y="3" width="1" height="1" fill="#000000" />
<rect x="3" y="3" width="1" height="1" fill="#000000" />
<rect x="4" y="3" width="1" height="1" fill="#000000" />
<rect x="5" y="3" width="1" height="1" fill="#000000" />
<rect x="6" y="3" width="1" height="1" fill="#000000" />
<rect x="7" y="3" width="1" height="1" fill="#000000" />
<rect x="8" y="3" width="1" height="1" fill="#000000" />
<rect x="9" y="3" width="1" height="1" fill="#000000" />
<rect x="10" y="3" width="1" height="1" fill="#000000" />
<rect x="11" y="3" width="1" height="1" fill="#000000" />
<rect x="12" y="3" width="1" height="1" fill="#000000" />
<rect x="13" y="3" width="1" height="1" fill="#000000" />
<rect x="3" y="4" width="1" height="1" fill="#000000" />
<rect x="4" y="4" width="1" height="1" fill="#000000" />
<rect x="11" y="4" width="1" height="1" fill="#000000" />
<rect x="12" y="4" width="1" height="1" fill="#000000" />
<rect x="2" y="5" width="1" height="1" fill="#000000" />
<rect x="3" y="5" width="1" height="1" fill="#000000" />
<rect x="12" y="5" width="1" height="1" fill="#000000" />
<rect x="13" y="5" width="1" height="1" fill="#000000" />
<rect x="2" y="6" width="1" height="1" fill="#000000" />
<rect x="3" y="6" width="1" height="1" fill="#000000" />
<rect x="12" y="6" width="1" height="1" fill="#000000" />
<rect x="13" y="6" width="1" height="1" fill="#000000" />
<rect x="0" y="7" width="1" height="1" fill="#000000" />
<rect x="1" y="7" width="1" height="1" fill="#000000" />
<rect x="2" y="7" width="1" height="1" fill="#000000" />
<rect x="3" y="7" width="1" height="1" fill="#000000" />
<rect x="12" y="7" width="1" height="1" fill="#000000" />
<rect x="13" y="7" width="1" height="1" fill="#000000" />
<rect x="14" y="7" width="1" height="1" fill="#000000" />
<rect x="15" y="7" width="1" height="1" fill="#000000" />
<rect x="0" y="8" width="1" height="1" fill="#000000" />
<rect x="1" y="8" width="1" height="1" fill="#000000" />
<rect x="2" y="8" width="1" height="1" fill="#000000" />
<rect x="3" y="8" width="1" height="1" fill="#000000" />
<rect x="12" y="8" width="1" height="1" fill="#000000" />
<rect x="13" y="8" width="1" height="1" fill="#000000" />
<rect x="14" y="8" width="1" height="1" fill="#000000" />
<rect x="15" y="8" width="1" height="1" fill="#000000" />
<rect x="2" y="9" width="1" height="1" fill="#000000" />
<rect x="3" y="9" width="1" height="1" fill="#000000" />
<rect x="12" y="9" width="1" height="1" fill="#000000" />
<rect x="13" y="9" width="1" height="1" fill="#000000" />
<rect x="2" y="10" width="1" height="1" fill="#000000" />
<rect x="3" y="10" width="1" height="1" fill="#000000" />
<rect x="12" y="10" width="1" height="1" fill="#000000" />
<rect x="13" y="10" width="1" height="1" fill="#000000" />
<rect x="3" y="11" width="1" height="1" fill="#000000" />
<rect x="4" y="11" width="1" height="1" fill="#000000" />
<rect x="11" y="11" width="1" height="1" fill="#000000" />
<rect x="12" y="11" width="1" height="1" fill="#000000" />
<rect x="2" y="12" width="1" height="1" fill="#000000" />
<rect x="3" y="12" width="1" height="1" fill="#000000" />
<rect x="4" y="12" width="1" height="1" fill="#000000" />
<rect x="5" y="12" width="1" height="1" fill="#000000" />
<rect x="6" y="12" width="1" height="1" fill="#000000" />
<rect x="7" y="12" width="1" height="1" fill="#000000" />
<rect x="8" y="12" width="1" height="1" fill="#000000" />
<rect x="9" y="12" width="1" height="1" fill="#000000" />
<rect x="10" y="12" width="1" height="1" fill="#000000" />
<rect x="11" y="12" width="1" height="1" fill="#000000" />
<rect x="12" y="12" width="1" height="1" fill="#000000" />
<rect x="13" y="12" width="1" height="1" fill="#000000" />
<rect x="1" y="13" width="1" height="1" fill="#000000" />
<rect x="2" y="13" width="1" height="1" fill="#000000" />
<rect x="3" y="13" width="1" height="1" fill="#000000" />
<rect x="5" y="13" width="1" height="1" fill="#000000" />
<rect x="6" y="13" width="1" height="1" fill="#000000" />
<rect x="7" y="13" width="1" height="1" fill="#000000" />
<rect x="8" y="13" width="1" height="1" fill="#000000" />
<rect x="9" y="13" width="1" height="1" fill="#000000" />
<rect x="10" y="13" width="1" height="1" fill="#000000" />
<rect x="12" y="13" width="1" height="1" fill="#000000" />
<rect x="13" y="13" width="1" height="1" fill="#000000" />
<rect x="14" y="13" width="1" height="1" fill="#000000" />
<rect x="2" y="14" width="1" height="1" fill="#000000" />
<rect x="7" y="14" width="1" height="1" fill="#000000" />
<rect x="8" y="14" width="1" height="1" fill="#000000" />
<rect x="13" y="14" width="1" height="1" fill="#000000" />
<rect x="7" y="15" width="1" height="1" fill="#000000" />
<rect x="8" y="15" width="1" height="1" fill="#000000" />
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -0,0 +1,16 @@
<script lang="ts">
export let title: string;
export let id: string;
export let description: string = "";
</script>
<div class="mb-6">
<label class="block text-gray-200 text-lg font-bold mb-1" for={id}>
{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}
</div>

View File

@ -0,0 +1,178 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
export let id: string;
export let value: string = "";
const dispatch = createEventDispatcher<{
change: string;
}>();
// https://www.electronjs.org/de/docs/latest/api/accelerator
function keyCodeToElectron(key: string): string {
if (key.match(/^Key[A-Z]/)) {
return key.replace(/^Key/, "").toLocaleUpperCase();
}
if (key.match(/^Digit[0-9]/)) {
return key.replace(/^Digit/, "").toLowerCase();
}
if (key.match(/^Numpad[0-9]/)) {
return key.replace(/^Numpad/, "num").toLowerCase();
}
switch (key) {
// Modifiers
case "ControlLeft":
return "CmdOrCtrl";
case "ControlRight":
return "CmdOrCtrl";
case "AltLeft":
return "Alt";
case "AltRight":
return "AltGr";
case "ScrollLock":
return "Scrolllock";
case "ShiftLeft":
return "Shift";
case "ShiftRight":
return "Shift";
// Specialchars
case "Period":
return ".";
case "Comma":
return ",";
case "Slash":
return "/";
case "Backslash":
return "\\";
case "Minus":
return "-";
case "Equal":
return "=";
case "BracketLeft":
return "[";
case "BracketRight":
return "]";
case "Quote":
return "'";
case "Semicolon":
return ";";
case "IntlBackslash":
return "\\";
case "Backquote":
return "`";
// Numpad
case "NumpadDecimal":
return "numdec";
case "NumpadAdd":
return "numadd";
case "NumpadSubtract":
return "numsub";
case "NumpadMultiply":
return "nummult";
case "NumpadDivide":
return "numdiv";
default:
return key;
}
}
let shortCut: string[] = [];
let recording = false;
let recordingTimeout: NodeJS.Timeout;
let keyInputTimeout: NodeJS.Timeout;
function camelPad(str: string) {
return (
str
// Look for long acronyms and filter out the last letter
.replace(/([A-Z]+)([A-Z][a-z])/g, " $1 $2")
// Look for lower-case letters followed by upper-case letters
.replace(/([a-z\d])([A-Z])/g, "$1 $2")
// Look for lower-case letters followed by numbers
// .replace(/([a-zA-Z])(\d)/g, "$1 $2")
.replace(/^./, (str) => str.toUpperCase())
// Remove any white space left around the word
.trim()
);
}
function resetRecording() {
recording = false;
shortCut = [];
value = "";
}
function stopRecording() {
clearTimeout(recordingTimeout);
recording = false;
value = shortCut.map(keyCodeToElectron).join(" + ");
dispatch("change", value);
}
function startRecording() {
if (recording) {
return;
}
recording = true;
value = "";
shortCut = [];
recordingTimeout = setTimeout(() => {
stopRecording();
}, 1000 * 5);
}
function keyUp(event: KeyboardEvent) {
if (!recording) {
return;
}
shortCut = [...shortCut, event.code];
if (!keyInputTimeout) {
keyInputTimeout = setTimeout(() => {
stopRecording();
keyInputTimeout = undefined;
}, 300);
}
}
</script>
<div
class={`flex items-center w-full h-8 border-1 rounded-md overflow-hidden text-gray-200 text-xs appearance-none focus:outline-none ${
recording ? "border-red-500" : "border-gray-400"
}`}
on:keyup={keyUp}
on:click={startRecording}
tabindex="0"
>
<input
{id}
type="text"
class="flex-grow h-full border-none mx-2 bg-transparent appearance-none focus:outline-none"
disabled
{value}
/>
{#if value.length > 0}
<span
class="flex items-center justify-center w-4 h-4 p-2 mr-1 rounded-full cursor-pointer bg-gray-500 hover:bg-gray-400"
on:click|stopPropagation={resetRecording}>x</span
>
{/if}
<div
class={`flex h-6 items-center px-2 m-0.5 rounded-sm w-28 justify-center cursor-pointer ${
recording ? "bg-red-500" : "hover:bg-gray-400"
}`}
on:click={recording ? stopRecording : startRecording}
>
{#if recording}
<span>recording</span>
{:else}
<span>record</span>
{/if}
</div>
</div>

View File

@ -1,35 +1,35 @@
<script>
import { onMount } from "svelte";
import { onMount } from "svelte";
export let component;
export let delayMs = null;
export let component;
export let delayMs = null;
let loadedComponent = null;
let timeout;
let showFallback = !delayMs;
let loadedComponent = null;
let timeout;
let showFallback = !delayMs;
let props;
$: {
// eslint-disable-next-line no-shadow
const { component, delayMs, ...restProps } = $$props;
props = restProps;
}
let props;
$: {
// eslint-disable-next-line no-shadow
const { component, delayMs, ...restProps } = $$props;
props = restProps;
}
onMount(() => {
if (delayMs) {
timeout = setTimeout(() => {
showFallback = true;
}, delayMs);
}
component().then(module => {
loadedComponent = module.default;
});
return () => clearTimeout(timeout);
});
onMount(() => {
if (delayMs) {
timeout = setTimeout(() => {
showFallback = true;
}, delayMs);
}
component().then((module) => {
loadedComponent = module.default;
});
return () => clearTimeout(timeout);
});
</script>
{#if loadedComponent}
<svelte:component this={loadedComponent} {...props} />
<svelte:component this={loadedComponent} {...props} />
{:else if showFallback}
<slot />
<slot />
{/if}

View File

@ -1,20 +1,20 @@
<script>
import { Route } from "svelte-navigator";
import Lazy from "./Lazy.svelte";
import { Route } from "svelte-navigator";
import Lazy from "~/lib/Lazy.svelte";
export let component;
export let delayMs = null;
export let component;
export let delayMs = null;
let props;
$: {
// eslint-disable-next-line no-shadow
const { component, ...restProps } = $$props;
props = restProps;
}
let props;
$: {
// eslint-disable-next-line no-shadow
const { component, ...restProps } = $$props;
props = restProps;
}
</script>
<Route {...props}>
<Lazy {component} {delayMs}>
<slot />
</Lazy>
</Route>
<Lazy {component} {delayMs}>
<slot />
</Lazy>
</Route>

View File

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

View File

@ -1,16 +1,29 @@
<script lang="ts">
import { onMount } from "svelte";
import Link from "./Link.svelte";
import { servers, selectedServer, selectServer, loadServers } from "../store";
import Link from "~/lib/Link.svelte";
import { servers, selectedServer, selectServer, loadServers, Server } from "../store";
import CogIcon from "~/assets/nes.icons/cog.svg";
let isDevelopment = false;
function getServerColor(i: number) {
const serverColors = ['bg-red-400', 'bg-yellow-500', 'bg-green-500', 'bg-blue-500', 'bg-indigo-500', 'bg-purple-500'];
const serverColors = [
"bg-red-400",
"bg-yellow-500",
"bg-green-500",
"bg-blue-500",
"bg-indigo-500",
"bg-purple-500",
];
return serverColors[i % serverColors.length];
}
selectedServer.subscribe((e) => {
console.log("selected server changed", e);
});
onMount(async () => {
await loadServers();
isDevelopment = await window?.WorkAdventureDesktopApi?.isDevelopment();
@ -20,29 +33,37 @@
<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 text-gray-400">
<div class="flex flex-col items-center justify-center ">
<div
class={`w-12 h-12 rounded-lg flex cursor-pointer text-center items-center justify-center text-light-50 ${getServerColor(i)} ${
$selectedServer === server._id ? "" : ""
}`}
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)}
>
{server.name.slice(0, 2).toLocaleUpperCase()}
<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>
{/each}
<Link
to="/server/add"
class="flex justify-center items-center text-4xl no-underline text-gray-400 cursor-pointer">+</Link
>
</div>
<Link
to="/server/add"
class="flex justify-center items-center text-4xl no-underline text-gray-200 cursor-pointer hover:text-gray-500"
>+</Link
>
<Link
to="/settings"
class="flex mt-auto justify-center items-center text-4xl no-underline cursor-pointer"
>
<CogIcon width="30" height="30" class="fill-gray-200 hover:fill-gray-500" />
</Link>
{#if isDevelopment}
<button class="mt-auto" on:click={() => location.reload()}>Refresh</button>
<button class="text-8px text-red-500 mt-8 mb-4" on:click={() => location.reload()}>Refresh</button>
{/if}
</aside>
<style>
aside {
width: 70px;
width: 80px;
}
</style>

View File

@ -0,0 +1,27 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
export let id: string;
export let value: string = "";
export let required: boolean = false;
const dispatch = createEventDispatcher<{
change: string;
}>();
</script>
<div
class={`flex items-center w-full h-10 border-1 rounded-md overflow-hidden text-gray-200 text-md appearance-none focus:outline-none`}
>
<input
{id}
type="text"
class="flex-grow h-full border-none mx-2 bg-transparent appearance-none focus:outline-none"
{value}
{required}
on:change={(e) => {
value = e.target.value;
dispatch("change", { value });
}}
/>
</div>

View File

@ -0,0 +1,25 @@
<script lang="ts">
export let id: string;
export let value: boolean;
export let title: string = null;
</script>
<div class="flex w-full my-2">
<label for={id} class="flex items-center cursor-pointer">
<div class="relative">
<input {id} type="checkbox" class="sr-only" checked={value} />
<div class="w-10 h-4 bg-gray-400 rounded-full shadow-inner" />
<div class="dot absolute w-6 h-6 bg-gray-500 rounded-full shadow -left-1 -top-1 transition" />
</div>
{#if title}
<div class="ml-4 text-gray-200 text-sm">{title}</div>
{/if}
</label>
</div>
<style>
input:checked ~ .dot {
transform: translateX(100%);
background-color: #0369a1;
}
</style>

View File

@ -1,6 +1,6 @@
import { writable, get } from "svelte/store";
type Server = {
export type Server = {
_id: string;
name: string;
url: string;
@ -11,15 +11,19 @@ export const newServer = writable<Omit<Server, "_id">>({
url: "",
});
export const servers = writable<Server[]>([]);
export const selectedServer = writable<string | undefined>(undefined);
export const selectedServer = writable<string | undefined>("");
export async function selectServer(server: Server) {
await window.WorkAdventureDesktopApi.selectServer(server._id);
selectedServer.set(server._id);
}
export async function addServer(event: Event) {
export async function addServer() {
const addedServer = await window?.WorkAdventureDesktopApi?.addServer(get(newServer));
if (!addedServer?._id) {
console.log(addedServer);
throw new Error(addedServer);
}
newServer.set({ name: "", url: "" });
servers.update((s) => [...s, addedServer]);
await selectServer(addedServer);

4
desktop/local-app/src/svg.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module "*.svg" {
const content: string;
export default content;
}

View File

@ -1,12 +1,36 @@
<script lang="ts">
import { newServer, addServer } from '../store';
import InputField from "~/lib/InputField.svelte";
import TextInput from "~/lib/TextInput.svelte";
import { newServer, addServer } from "~/store";
let error = "";
async function _addServer() {
try {
await addServer();
} catch(e) {
console.log(e);
error = e.message;
}
}
</script>
<div class="flex w-full h-full justify-center items-center">
<form class="flex flex-col justify-center space-y-2" on:submit|preventDefault={addServer}>
<input type="text" class="w-full h-12 px-4 py-2 bg-gray-200 rounded-lg" placeholder="Name" required bind:value={$newServer.name}>
<input type="text" class="w-full h-12 px-4 py-2 bg-gray-200 rounded-lg" placeholder="Url" required bind:value={$newServer.url}>
<input type="submit" value="Add server" />
</form>
<form class="flex flex-col justify-center" on:submit|preventDefault={_addServer}>
<!-- <input type="text" class="w-full h-12 px-4 py-2 bg-gray-200 rounded-lg" placeholder="Url" required > -->
<InputField title="Name" id="name">
<TextInput bind:value={$newServer.name} required id="name" />
</InputField>
<InputField title="Url" id="url">
<TextInput bind:value={$newServer.url} required id="url" />
</InputField>
{#if error}
<div class="text-red-500 text-center mb-2">{error}</div>
{/if}
<input
type="submit"
value="Add server"
class="mt-4 rounded-md p-2 bg-gray-300 cursor-pointer hover:bg-gray-400"
/>
</form>
</div>

View File

@ -1,7 +1,18 @@
<script lang="ts">
import logo from "../../../assets/icons/logo-text.png";
import { onMount } from "svelte";
import Logo from "~/../../assets/icons/logo.svg";
let version = "";
onMount(async () => {
version = await window?.WorkAdventureDesktopApi?.getVersion();
});
</script>
<div class="flex w-full h-full justify-center items-center">
<img src={logo} alt="WorkAdeventure logo" />
<div class="flex flex-col w-full h-full justify-center items-center">
<Logo height="100" class="my-auto" />
<div class="flex my-4 items-center space-x-4">
<span class="text-gray-300 text-lg ">Desktop-App Version: {version}</span>
</div>
</div>

View File

@ -0,0 +1,59 @@
<script lang="ts">
import { onMount } from "svelte";
import { writable } from "svelte/store";
import ToggleSwitch from "~/lib/ToggleSwitch.svelte";
import InputField from "~/lib/InputField.svelte";
import KeyRecord from "~/lib/KeyRecord.svelte";
const shortCuts = writable<Record<string, string> | undefined>({});
onMount(async () => {
shortCuts.set(await window?.WorkAdventureDesktopApi?.getShortcuts());
});
async function saveShortcut(key: string, value: string) {
await window?.WorkAdventureDesktopApi?.saveShortcut(key, value);
}
</script>
<div class="flex flex-col w-full h-full justify-center items-center">
<h1 class="text-gray-200 text-2xl mb-6">Settings</h1>
<div class="flex flex-col justify-start max-w-152">
{#if $shortCuts}
<InputField
id="toggle-camera"
title="Toggle Mute"
description="Set a shortcut to turn your microphone on and off"
>
<KeyRecord
id="toggle-mute"
value={$shortCuts.mute_toggle}
on:change={(e) => saveShortcut("mute_toggle", e.detail)}
/>
</InputField>
<InputField
id="toggle-camera"
title="Toggle Camera"
description="Set a shortcut to turn your camera on and off"
>
<KeyRecord
id="toggle-camera"
value={$shortCuts.camera_toggle}
on:change={(e) => saveShortcut("camera_toggle", e.detail)}
/>
</InputField>
{/if}
<InputField id="toggle-autostart" title="Toggle autostart">
<ToggleSwitch
id="toggle-autostart"
value={true}
title="Autostart WorkAdventure after your PC started"
on:change={(e) => {}}
/>
</InputField>
</div>
</div>

View File

@ -13,7 +13,10 @@
* of JS in `.svelte` files.
*/
"allowJs": true,
"checkJs": true
"checkJs": true,
"paths": {
"~/*": ["./*"]
}
},
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
"references": [{ "path": "./tsconfig.node.json" }]

View File

@ -2,7 +2,8 @@
"compilerOptions": {
"composite": true,
"module": "esnext",
"moduleResolution": "node"
"moduleResolution": "node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View File

@ -1,7 +1,26 @@
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import WindiCSS from "vite-plugin-windicss";
import { svelteSVG } from "rollup-plugin-svelte-svg";
import path from "path";
export default defineConfig({
plugins: [svelte(), WindiCSS()],
resolve: {
alias: {
"~": `${path.resolve(__dirname, "src")}/`,
},
},
plugins: [
svelte(),
svelteSVG({
// optional SVGO options
// pass empty object to enable defaults
svgo: {},
// vite-specific
// https://vitejs.dev/guide/api-plugin.html#plugin-ordering
// enforce: 'pre' | 'post'
enforce: "pre",
}),
WindiCSS(),
],
});

View File

@ -2,6 +2,11 @@
# yarn lockfile v1
"@16bits/nes.css@^2.3.2":
version "2.3.2"
resolved "https://registry.yarnpkg.com/@16bits/nes.css/-/nes.css-2.3.2.tgz#e69db834119b33ae8d3cb044f106a07a17cadd6f"
integrity sha512-nEM5PIth+Bab5JSOa4uUR+PMNUsNTYxA55oVlG3gXI/4LoYtWS767Uv9Pu/KCbHXVvnIjt4ZXt13kZw3083qTw==
"@antfu/utils@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@antfu/utils/-/utils-0.4.0.tgz#df100ed9922d7359bf6c99083765b5207086b9a7"
@ -9,6 +14,11 @@
dependencies:
"@types/throttle-debounce" "^2.1.0"
"@fontsource/press-start-2p@^4.5.2":
version "4.5.2"
resolved "https://registry.yarnpkg.com/@fontsource/press-start-2p/-/press-start-2p-4.5.2.tgz#062d56ce6dcffdd28f8ca80d9e5b86a473a37c15"
integrity sha512-1Wwk85zlUiirPTIw25V3z4wLXbrqaOqDV7g5akjPuthg+hY5Wv0zOPXcHRVBwb+Igh4qVLHUd6O5X27BgWTbkg==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@ -49,6 +59,11 @@
magic-string "^0.25.7"
svelte-hmr "^0.14.9"
"@trysound/sax@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
"@tsconfig/svelte@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@tsconfig/svelte/-/svelte-2.0.1.tgz#0e8d7caa693e9b2afce5e622c0475bb0fd89c12c"
@ -116,6 +131,11 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
boolbase@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@ -156,11 +176,47 @@ chokidar@^3.4.1:
optionalDependencies:
fsevents "~2.3.2"
commander@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
css-select@^4.1.3:
version "4.2.1"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.2.1.tgz#9e665d6ae4c7f9d65dbe69d0316e3221fb274cdd"
integrity sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==
dependencies:
boolbase "^1.0.0"
css-what "^5.1.0"
domhandler "^4.3.0"
domutils "^2.8.0"
nth-check "^2.0.1"
css-tree@^1.1.2, css-tree@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
dependencies:
mdn-data "2.0.14"
source-map "^0.6.1"
css-what@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe"
integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==
csso@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
dependencies:
css-tree "^1.1.2"
debug@^4.3.3:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
@ -178,6 +234,41 @@ detect-indent@^6.0.0:
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6"
integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==
dom-serializer@^1.0.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91"
integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==
dependencies:
domelementtype "^2.0.1"
domhandler "^4.2.0"
entities "^2.0.0"
domelementtype@^2.0.1, domelementtype@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
domhandler@^4.2.0, domhandler@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626"
integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==
dependencies:
domelementtype "^2.2.0"
domutils@^2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
dependencies:
dom-serializer "^1.0.1"
domelementtype "^2.2.0"
domhandler "^4.2.0"
entities@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
es6-promise@^3.1.2:
version "3.3.1"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613"
@ -303,6 +394,11 @@ esbuild@^0.14.14:
esbuild-windows-64 "0.14.23"
esbuild-windows-arm64 "0.14.23"
estree-walker@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
estree-walker@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
@ -379,6 +475,11 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
hotkeys-js@^3.8.7:
version "3.8.7"
resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.8.7.tgz#c16cab978b53d7242f860ca3932e976b92399981"
integrity sha512-ckAx3EkUr5XjDwjEHDorHxRO2Kb7z6Z2Sxul4MbBkN8Nho7XDslQsgMJT+CiJ5Z4TgRxxvKHEpuLE3imzqy4Lg==
import-fresh@^3.2.1:
version "3.3.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
@ -460,6 +561,11 @@ magic-string@^0.25.7:
dependencies:
sourcemap-codec "^1.4.4"
mdn-data@2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
merge2@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
@ -525,6 +631,13 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
nth-check@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2"
integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==
dependencies:
boolbase "^1.0.0"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@ -576,6 +689,11 @@ postcss@^8.4.6:
picocolors "^1.0.0"
source-map-js "^1.0.2"
prettier-plugin-svelte@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.6.0.tgz#0e845b560b55cd1d951d6c50431b4949f8591746"
integrity sha512-NPSRf6Y5rufRlBleok/pqg4+1FyGsL0zYhkYP6hnueeL1J/uCm3OfOZPsLX4zqD9VAdcXfyEL2PYqGv8ZoOSfA==
prettier@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a"
@ -619,6 +737,21 @@ rimraf@^2.5.2:
dependencies:
glob "^7.1.3"
rollup-plugin-svelte-svg@^1.0.0-beta.6:
version "1.0.0-beta.6"
resolved "https://registry.yarnpkg.com/rollup-plugin-svelte-svg/-/rollup-plugin-svelte-svg-1.0.0-beta.6.tgz#0ee3deae2329abb1c90f50b83598aaf0bb0559fc"
integrity sha512-6uJb9kuaqK6p+DvkgphhGN18wvUzdT6h7MQC2B8P1omi9omC9lQC54pwaot21h6z9ibhGPLG9a1XFLeDQth/kg==
dependencies:
rollup-pluginutils "^2.8.2"
svgo "^2.3.1"
rollup-pluginutils@^2.8.2:
version "2.8.2"
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==
dependencies:
estree-walker "^0.6.1"
rollup@^2.59.0:
version "2.67.3"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.67.3.tgz#3f04391fc296f807d067c9081d173e0a33dbd37e"
@ -665,6 +798,11 @@ source-map-js@^1.0.2:
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
source-map@^0.7.3:
version "0.7.3"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
@ -675,6 +813,11 @@ sourcemap-codec@^1.3.0, sourcemap-codec@^1.4.4:
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
stable@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
strip-indent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"
@ -739,6 +882,19 @@ svelte@^3.44.0:
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.46.4.tgz#0c46bc4a3e20a2617a1b7dc43a722f9d6c084a38"
integrity sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg==
svgo@^2.3.1:
version "2.8.0"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
dependencies:
"@trysound/sax" "0.2.0"
commander "^7.2.0"
css-select "^4.1.3"
css-tree "^1.1.3"
csso "^4.2.0"
picocolors "^1.0.0"
stable "^0.1.8"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"

View File

@ -3,11 +3,12 @@ import { app, BrowserWindow, globalShortcut } from "electron";
import { createWindow, getWindow } from "./window";
import { createTray } from "./tray";
import autoUpdater from "./auto-updater";
import updateAutoLaunch from "./update-auto-launch";
import ipc, { emitMutedKeyPress } from "./ipc";
import { updateAutoLaunch } from "./auto-launch";
import ipc from "./ipc";
import settings from "./settings";
import { setLogLevel } from "./log";
import "./serve"; // prepare custom url scheme
import { loadShortcuts } from "./shortcuts";
function init() {
const appLock = app.requestSingleInstanceLock();
@ -56,10 +57,7 @@ function init() {
await createWindow();
createTray();
// TODO
globalShortcut.register("Alt+CommandOrControl+M", () => {
emitMutedKeyPress();
});
loadShortcuts();
});
// Quit when all windows are closed.
@ -81,6 +79,10 @@ function init() {
app.on("quit", () => {
// TODO
});
app.on("will-quit", () => {
globalShortcut.unregisterAll();
});
}
export default {

View File

@ -1,10 +1,10 @@
import AutoLaunch from "auto-launch";
import isDev from "electron-is-dev";
import { app } from "electron";
import electronIsDev from "electron-is-dev";
import settings from "./settings";
export default async () => {
export async function updateAutoLaunch() {
let isAutoLaunchEnabled = settings.get("auto_launch_enabled");
// set default to enabled
@ -14,7 +14,7 @@ export default async () => {
}
// Don't run this in development
if (isDev) {
if (electronIsDev) {
return;
}
@ -38,4 +38,4 @@ export default async () => {
openAtLogin: isAutoLaunchEnabled,
openAsHidden: true,
});
};
}

View File

@ -1,21 +1,32 @@
import { ipcMain } from "electron";
import { ipcMain, app } from "electron";
import electronIsDev from "electron-is-dev";
import { createAndShowNotification } from "./notification";
import { Server } from "./preload-local-app/types";
import settings from "./settings";
import { saveShortcut } from "./shortcuts";
import { getWindow, hideAppView, showAppView } from "./window";
export function emitMutedKeyPress() {
export function emitMuteToggle() {
const mainWindow = getWindow();
if (!mainWindow) {
throw new Error("Main window not found");
}
mainWindow.webContents.send("app:on-muted-key-press");
mainWindow.webContents.send("app:on-camera-toggle");
}
export function emitCameraToggle() {
const mainWindow = getWindow();
if (!mainWindow) {
throw new Error("Main window not found");
}
mainWindow.webContents.send("app:on-mute-toggle");
}
export default () => {
ipcMain.handle("is-development", () => electronIsDev);
ipcMain.handle("get-version", () => (electronIsDev ? "dev" : app.getVersion()));
// app ipc
ipcMain.on("app:notify", (event, txt) => {
@ -30,6 +41,7 @@ export default () => {
ipcMain.handle("local-app:getServers", () => {
return (
settings.get("servers") || [
// TODO: remove this default server
{
_id: "1",
name: "WA Demo",
@ -51,8 +63,17 @@ export default () => {
return true;
});
ipcMain.handle("local-app:addServer", (event, server: Omit<Server, "_id">) => {
ipcMain.handle("local-app:addServer", async (event, server: Omit<Server, "_id">) => {
const servers = settings.get("servers") || [];
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`);
} catch (e) {
console.error(e);
return new Error("Invalid server url");
}
const newServer = {
...server,
_id: `${servers.length + 1}`,
@ -70,4 +91,8 @@ export default () => {
);
return true;
});
ipcMain.handle("local-app:saveShortcut", (event, shortcut, key) => saveShortcut(shortcut, key));
ipcMain.handle("local-app:getShortcuts", (event) => settings.get("shortcuts") || {});
};

View File

@ -1,12 +1,14 @@
import { contextBridge, ipcRenderer, IpcRendererEvent } from "electron";
import { SettingsData } from "src/settings";
import type { WorkAdventureDesktopApi } from "./types";
const api: WorkAdventureDesktopApi = {
desktop: true,
isDevelopment: () => ipcRenderer.invoke("is-development"),
getVersion: () => ipcRenderer.invoke("get-version"),
notify: (txt: string) => ipcRenderer.send("app:notify", txt),
onMutedKeyPress: (callback: (event: IpcRendererEvent) => void) =>
ipcRenderer.on("app:on-muted-key-press", callback),
onMuteToggle: (callback: (event: IpcRendererEvent) => void) => ipcRenderer.on("app:on-mute-toggle", callback),
onCameraToggle: (callback: (event: IpcRendererEvent) => void) => ipcRenderer.on("app:on-camera-toggle", callback),
};
contextBridge.exposeInMainWorld("WorkAdventureDesktopApi", api);

View File

@ -3,6 +3,8 @@ import type { IpcRendererEvent } from "electron";
export type WorkAdventureDesktopApi = {
desktop: boolean;
isDevelopment: () => Promise<boolean>;
getVersion: () => Promise<string>;
notify: (txt: string) => void;
onMutedKeyPress: (callback: (event: IpcRendererEvent) => void) => void;
onMuteToggle: (callback: (event: IpcRendererEvent) => void) => void;
onCameraToggle: (callback: (event: IpcRendererEvent) => void) => void;
};

View File

@ -1,14 +1,19 @@
import { contextBridge, ipcRenderer } from "electron";
import { SettingsData } from "src/settings";
import type { Server, WorkAdventureLocalAppApi } from "./types";
const api: WorkAdventureLocalAppApi = {
desktop: true,
isDevelopment: () => ipcRenderer.invoke("is-development"),
getVersion: () => ipcRenderer.invoke("get-version"),
showLocalApp: () => ipcRenderer.invoke("local-app:showLocalApp"),
getServers: () => ipcRenderer.invoke("local-app:getServers"),
selectServer: (serverId: string) => ipcRenderer.invoke("local-app:selectServer", serverId),
addServer: (server: Omit<Server, "_id">) => ipcRenderer.invoke("local-app:addServer", server),
removeServer: (serverId: Server["_id"]) => ipcRenderer.invoke("local-app:removeServer", serverId),
saveShortcut: (shortcut: keyof SettingsData["shortcuts"], key: string | null) =>
ipcRenderer.invoke("local-app:saveShortcut", shortcut, key),
getShortcuts: () => ipcRenderer.invoke("local-app:getShortcuts"),
};
contextBridge.exposeInMainWorld("WorkAdventureDesktopApi", api);

View File

@ -1,3 +1,5 @@
import { SettingsData } from "src/settings";
export type Server = {
_id: string;
name: string;
@ -7,9 +9,12 @@ export type Server = {
export type WorkAdventureLocalAppApi = {
desktop: boolean;
isDevelopment: () => Promise<boolean>;
getVersion: () => Promise<string>;
showLocalApp: () => Promise<void>;
getServers: () => Promise<Server[]>;
selectServer: (serverId: string) => Promise<Error | boolean>;
addServer: (server: Omit<Server, "_id">) => Promise<Server>;
removeServer: (serverId: Server["_id"]) => Promise<boolean>;
saveShortcut: (shortcut: keyof SettingsData["shortcuts"], key: string | null) => Promise<void>;
getShortcuts: () => Promise<SettingsData["shortcuts"]>;
};

View File

@ -2,10 +2,11 @@ import ElectronLog from "electron-log";
import Settings from "electron-settings";
import type { Server } from "./preload-local-app/types";
type SettingsData = {
export type SettingsData = {
log_level: ElectronLog.LogLevel;
auto_launch_enabled: boolean;
servers: Server[];
shortcuts: Record<"mute_toggle" | "camera_toggle", string | null>;
};
let settings: SettingsData;

29
desktop/src/shortcuts.ts Normal file
View File

@ -0,0 +1,29 @@
import { globalShortcut } from "electron";
import settings, { SettingsData } from "./settings";
import { emitCameraToggle, emitMuteToggle } from "./ipc";
export function loadShortcuts() {
globalShortcut.unregisterAll();
const shortcuts = settings.get("shortcuts");
// // mute key
if (shortcuts?.mute_toggle && shortcuts.mute_toggle.length > 0) {
globalShortcut.register(shortcuts.mute_toggle, () => {
emitMuteToggle();
});
}
if (shortcuts?.camera_toggle && shortcuts.camera_toggle.length > 0) {
globalShortcut.register(shortcuts.camera_toggle, () => {
emitCameraToggle();
});
}
}
export function saveShortcut(shortcut: keyof SettingsData["shortcuts"], key: string | null) {
const shortcuts = settings.get("shortcuts") || <SettingsData["shortcuts"]>{};
shortcuts[shortcut] = key;
settings.set("shortcuts", shortcuts);
loadShortcuts();
}