Skip to content
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
142 changes: 142 additions & 0 deletions app/src/components/updater.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { createSignal, onMount, Show } from "solid-js";
import * as Tauri from "../tauri/constants";
import Tooltip from "./tooltip";

const module = Tauri.DESKTOP ? await import("../tauri/update") : undefined;

type Status =
| {
type: "available";
version: string;
}
| {
type: "downloading";
version: string;
size?: number;
progress: number;
}
| {
type: "downloaded";
version: string;
size: number;
}
| {
type: "installing";
version: string;
}
| {
type: "error";
error: string;
};

export default function UpdaterIcon() {
const [status, setStatus] = createSignal<Status | undefined>();

onMount(async () => {
// Import the update module
const updater = (await module)?.update;
if (!updater) return;

// Subscribe to status changes
const dispose = updater.status.subscribe((newStatus) => {
setStatus(newStatus);
});

return () => dispose();
});

const handleClick = async () => {
const s = status();
if (!s) return;

const module = await import("../tauri/update");

if (s.type === "available") {
module.update.download();
} else if (s.type === "downloaded") {
module.update.install();
}
};

const tooltipContent = () => {
const s = status();
if (!s) return "";

if (s.type === "error") return `Error: ${s.error}`;
if (s.type === "available") return `Update ${s.version} available`;
if (s.type === "downloading") return `Downloading ${s.version}...`;
if (s.type === "downloaded") return `Install ${s.version} now`;
if (s.type === "installing") return `Installing ${s.version}...`;
return "";
};

const progressPercent = () => {
const s = status();
if (!s || s.type !== "downloading" || !s.size) return 0;
return (s.progress / s.size) * 100;
};

return (
<Show when={status()}>
<Tooltip content={tooltipContent()} position="bottom">
<button
type="button"
onClick={handleClick}
class="p-2 text-white hover:text-gray-300 hover:bg-gray-700 rounded-lg transition-all cursor-pointer relative"
classList={{
"text-red-500": status()?.type === "error",
"text-blue-500": status()?.type === "available" || status()?.type === "downloading",
"text-green-500": status()?.type === "downloaded",
"text-yellow-500": status()?.type === "installing",
}}
>
<Show when={status()?.type === "error"}>
<span class="icon-[mdi--alert-circle]" />
</Show>
<Show when={status()?.type === "available"}>
<span class="icon-[mdi--download]" />
</Show>
<Show when={status()?.type === "downloading"}>
<svg class="w-6 h-6" viewBox="0 0 24 24" aria-label="Downloading update">
<title>Downloading update</title>
<circle
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="2"
fill="none"
opacity="0.2"
/>
<circle
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-dasharray={`${2 * Math.PI * 10}`}
stroke-dashoffset={`${2 * Math.PI * 10 * (1 - progressPercent() / 100)}`}
stroke-linecap="round"
transform="rotate(-90 12 12)"
/>
<path
fill="currentColor"
d="M12 6v8l4 2"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
/>
</svg>
</Show>
<Show when={status()?.type === "downloaded"}>
<span class="icon-[mdi--download-circle]" />
</Show>
<Show when={status()?.type === "installing"}>
<span class="icon-[mdi--loading] animate-spin" />
</Show>
</button>
</Tooltip>
</Show>
);
}
5 changes: 3 additions & 2 deletions app/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import "@kixelated/hang/support/element";

import * as Tauri from "./tauri";

if (Tauri.ENABLED && Tauri.DESKTOP) {
import("./tauri/update").then((module) => module.run());
// Import update module early if on desktop to ensure it runs even if UI breaks
if (Tauri.DESKTOP) {
import("./tauri/update");
}

import solid from "@kixelated/signals/solid";
Expand Down
28 changes: 17 additions & 11 deletions app/src/layout/web.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type JSX, Show } from "solid-js";
import { type JSX, Match, Switch } from "solid-js";
import Divider from "../components/divider";
import Tooltip from "../components/tooltip";
import UpdaterIcon from "../components/updater";
import * as Tauri from "../tauri";
import { Logo } from "./logo";

Expand All @@ -11,16 +12,21 @@ export default function Web(props: { children: JSX.Element; link?: string }) {
<Logo link={props.link} />
<div id="support" />
<nav class="rounded p-3 flex items-center gap-3">
<Show when={!Tauri.ENABLED}>
<Tooltip content="Download app" position="bottom">
<a
href="/download"
class="p-2 text-white hover:text-gray-300 hover:bg-gray-700 rounded-lg transition-all cursor-pointer"
>
<span class="icon-[mdi--download]" />
</a>
</Tooltip>
</Show>
<Switch>
<Match when={!Tauri.ENABLED}>
<Tooltip content="Download app" position="bottom">
<a
href="/download"
class="p-2 text-white hover:text-gray-300 hover:bg-gray-700 rounded-lg transition-all cursor-pointer"
>
<span class="icon-[mdi--download]" />
</a>
</Tooltip>
</Match>
<Match when={Tauri.DESKTOP}>
<UpdaterIcon />
</Match>
</Switch>
<Tooltip content="Account settings" position="bottom">
<a
href="/account"
Expand Down
4 changes: 2 additions & 2 deletions app/src/room/gl/background.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import backgroundFragSource from "./background.frag?raw";
import backgroundVertSource from "./background.vert?raw";
import type { GLContext } from "./context";
import { Attribute, Shader, Uniform1f, Uniform2f } from "./shader";
import backgroundFragSource from "./shaders/background.frag?raw";
import backgroundVertSource from "./shaders/background.vert?raw";

export class BackgroundRenderer {
#glContext: GLContext;
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions app/src/room/gl/border.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Broadcast } from "../broadcast";
import { Canvas } from "../canvas";
import borderFragSource from "./border.frag?raw";
import borderVertSource from "./border.vert?raw";
import type { Camera } from "./camera";
import { Attribute, Shader, Uniform1f, Uniform2f, Uniform4f, UniformMatrix4fv } from "./shader";
import borderFragSource from "./shaders/border.frag?raw";
import borderVertSource from "./shaders/border.vert?raw";

export class BorderRenderer {
#canvas: Canvas;
Expand Down
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions app/src/room/gl/broadcast.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Broadcast } from "../broadcast";
import { Canvas } from "../canvas";
import broadcastFragSource from "./broadcast.frag?raw";
import broadcastVertSource from "./broadcast.vert?raw";
import type { Camera } from "./camera";
import { Attribute, Shader, Uniform1f, Uniform1i, Uniform2f, Uniform4f, UniformMatrix4fv } from "./shader";
import broadcastFragSource from "./shaders/broadcast.frag?raw";
import broadcastVertSource from "./shaders/broadcast.vert?raw";

export class BroadcastRenderer {
#canvas: Canvas;
Expand Down
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions app/src/room/gl/outline.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Broadcast } from "../broadcast";
import { Canvas } from "../canvas";
import type { Camera } from "./camera";
import outlineFragSource from "./outline.frag?raw";
import outlineVertSource from "./outline.vert?raw";
import { Attribute, Shader, Uniform1f, Uniform2f, Uniform3f, Uniform4f, UniformMatrix4fv } from "./shader";
import outlineFragSource from "./shaders/outline.frag?raw";
import outlineVertSource from "./shaders/outline.vert?raw";

export class OutlineRenderer {
#canvas: Canvas;
Expand Down
File renamed without changes.
14 changes: 14 additions & 0 deletions app/src/tauri/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const ENABLED = !!import.meta.env.TAURI_ENV_PLATFORM && "__TAURI_INTERNALS__" in window;

export const MOBILE =
ENABLED && (import.meta.env.TAURI_ENV_PLATFORM === "android" || import.meta.env.TAURI_ENV_PLATFORM === "ios");
export const DESKTOP =
ENABLED &&
(import.meta.env.TAURI_ENV_PLATFORM === "linux" ||
import.meta.env.TAURI_ENV_PLATFORM === "windows" ||
import.meta.env.TAURI_ENV_PLATFORM === "darwin");
export const ANDROID = ENABLED && import.meta.env.TAURI_ENV_PLATFORM === "android";
export const IOS = ENABLED && import.meta.env.TAURI_ENV_PLATFORM === "ios";
export const LINUX = ENABLED && import.meta.env.TAURI_ENV_PLATFORM === "linux";
export const WINDOWS = ENABLED && import.meta.env.TAURI_ENV_PLATFORM === "windows";
export const MACOS = ENABLED && import.meta.env.TAURI_ENV_PLATFORM === "darwin";
17 changes: 2 additions & 15 deletions app/src/tauri/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
export const ENABLED = !!import.meta.env.TAURI_ENV_PLATFORM && "__TAURI_INTERNALS__" in window;

export const MOBILE =
ENABLED && (import.meta.env.TAURI_ENV_PLATFORM === "android" || import.meta.env.TAURI_ENV_PLATFORM === "ios");
export const DESKTOP =
ENABLED &&
(import.meta.env.TAURI_ENV_PLATFORM === "linux" ||
import.meta.env.TAURI_ENV_PLATFORM === "windows" ||
import.meta.env.TAURI_ENV_PLATFORM === "darwin");
export const ANDROID = ENABLED && import.meta.env.TAURI_ENV_PLATFORM === "android";
export const IOS = ENABLED && import.meta.env.TAURI_ENV_PLATFORM === "ios";
export const LINUX = ENABLED && import.meta.env.TAURI_ENV_PLATFORM === "linux";
export const WINDOWS = ENABLED && import.meta.env.TAURI_ENV_PLATFORM === "windows";
export const MACOS = ENABLED && import.meta.env.TAURI_ENV_PLATFORM === "darwin";

import { ENABLED } from "./constants";
// Only load/export the Tauri modules if we're in a Tauri environment
export const Api = ENABLED ? await import("@tauri-apps/api") : null;
export const Opener = ENABLED ? await import("@tauri-apps/plugin-opener") : null;
export * from "./constants";
Loading
Loading