Skip to content

Commit b9786aa

Browse files
authored
Updates (#20)
* Improve the auto-updater. * More updater tweaks.
1 parent 7b9ecec commit b9786aa

22 files changed

Lines changed: 392 additions & 61 deletions

app/src/components/updater.tsx

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { createSignal, onMount, Show } from "solid-js";
2+
import * as Tauri from "../tauri/constants";
3+
import Tooltip from "./tooltip";
4+
5+
const module = Tauri.DESKTOP ? await import("../tauri/update") : undefined;
6+
7+
type Status =
8+
| {
9+
type: "available";
10+
version: string;
11+
}
12+
| {
13+
type: "downloading";
14+
version: string;
15+
size?: number;
16+
progress: number;
17+
}
18+
| {
19+
type: "downloaded";
20+
version: string;
21+
size: number;
22+
}
23+
| {
24+
type: "installing";
25+
version: string;
26+
}
27+
| {
28+
type: "error";
29+
error: string;
30+
};
31+
32+
export default function UpdaterIcon() {
33+
const [status, setStatus] = createSignal<Status | undefined>();
34+
35+
onMount(async () => {
36+
// Import the update module
37+
const updater = (await module)?.update;
38+
if (!updater) return;
39+
40+
// Subscribe to status changes
41+
const dispose = updater.status.subscribe((newStatus) => {
42+
setStatus(newStatus);
43+
});
44+
45+
return () => dispose();
46+
});
47+
48+
const handleClick = async () => {
49+
const s = status();
50+
if (!s) return;
51+
52+
const module = await import("../tauri/update");
53+
54+
if (s.type === "available") {
55+
module.update.download();
56+
} else if (s.type === "downloaded") {
57+
module.update.install();
58+
}
59+
};
60+
61+
const tooltipContent = () => {
62+
const s = status();
63+
if (!s) return "";
64+
65+
if (s.type === "error") return `Error: ${s.error}`;
66+
if (s.type === "available") return `Update ${s.version} available`;
67+
if (s.type === "downloading") return `Downloading ${s.version}...`;
68+
if (s.type === "downloaded") return `Install ${s.version} now`;
69+
if (s.type === "installing") return `Installing ${s.version}...`;
70+
return "";
71+
};
72+
73+
const progressPercent = () => {
74+
const s = status();
75+
if (!s || s.type !== "downloading" || !s.size) return 0;
76+
return (s.progress / s.size) * 100;
77+
};
78+
79+
return (
80+
<Show when={status()}>
81+
<Tooltip content={tooltipContent()} position="bottom">
82+
<button
83+
type="button"
84+
onClick={handleClick}
85+
class="p-2 text-white hover:text-gray-300 hover:bg-gray-700 rounded-lg transition-all cursor-pointer relative"
86+
classList={{
87+
"text-red-500": status()?.type === "error",
88+
"text-blue-500": status()?.type === "available" || status()?.type === "downloading",
89+
"text-green-500": status()?.type === "downloaded",
90+
"text-yellow-500": status()?.type === "installing",
91+
}}
92+
>
93+
<Show when={status()?.type === "error"}>
94+
<span class="icon-[mdi--alert-circle]" />
95+
</Show>
96+
<Show when={status()?.type === "available"}>
97+
<span class="icon-[mdi--download]" />
98+
</Show>
99+
<Show when={status()?.type === "downloading"}>
100+
<svg class="w-6 h-6" viewBox="0 0 24 24" aria-label="Downloading update">
101+
<title>Downloading update</title>
102+
<circle
103+
cx="12"
104+
cy="12"
105+
r="10"
106+
stroke="currentColor"
107+
stroke-width="2"
108+
fill="none"
109+
opacity="0.2"
110+
/>
111+
<circle
112+
cx="12"
113+
cy="12"
114+
r="10"
115+
stroke="currentColor"
116+
stroke-width="2"
117+
fill="none"
118+
stroke-dasharray={`${2 * Math.PI * 10}`}
119+
stroke-dashoffset={`${2 * Math.PI * 10 * (1 - progressPercent() / 100)}`}
120+
stroke-linecap="round"
121+
transform="rotate(-90 12 12)"
122+
/>
123+
<path
124+
fill="currentColor"
125+
d="M12 6v8l4 2"
126+
stroke="currentColor"
127+
stroke-width="1.5"
128+
stroke-linecap="round"
129+
/>
130+
</svg>
131+
</Show>
132+
<Show when={status()?.type === "downloaded"}>
133+
<span class="icon-[mdi--download-circle]" />
134+
</Show>
135+
<Show when={status()?.type === "installing"}>
136+
<span class="icon-[mdi--loading] animate-spin" />
137+
</Show>
138+
</button>
139+
</Tooltip>
140+
</Show>
141+
);
142+
}

app/src/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import "@kixelated/hang/support/element";
33

44
import * as Tauri from "./tauri";
55

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

1011
import solid from "@kixelated/signals/solid";

app/src/layout/web.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { type JSX, Show } from "solid-js";
1+
import { type JSX, Match, Switch } from "solid-js";
22
import Divider from "../components/divider";
33
import Tooltip from "../components/tooltip";
4+
import UpdaterIcon from "../components/updater";
45
import * as Tauri from "../tauri";
56
import { Logo } from "./logo";
67

@@ -11,16 +12,21 @@ export default function Web(props: { children: JSX.Element; link?: string }) {
1112
<Logo link={props.link} />
1213
<div id="support" />
1314
<nav class="rounded p-3 flex items-center gap-3">
14-
<Show when={!Tauri.ENABLED}>
15-
<Tooltip content="Download app" position="bottom">
16-
<a
17-
href="/download"
18-
class="p-2 text-white hover:text-gray-300 hover:bg-gray-700 rounded-lg transition-all cursor-pointer"
19-
>
20-
<span class="icon-[mdi--download]" />
21-
</a>
22-
</Tooltip>
23-
</Show>
15+
<Switch>
16+
<Match when={!Tauri.ENABLED}>
17+
<Tooltip content="Download app" position="bottom">
18+
<a
19+
href="/download"
20+
class="p-2 text-white hover:text-gray-300 hover:bg-gray-700 rounded-lg transition-all cursor-pointer"
21+
>
22+
<span class="icon-[mdi--download]" />
23+
</a>
24+
</Tooltip>
25+
</Match>
26+
<Match when={Tauri.DESKTOP}>
27+
<UpdaterIcon />
28+
</Match>
29+
</Switch>
2430
<Tooltip content="Account settings" position="bottom">
2531
<a
2632
href="/account"

app/src/room/gl/background.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import backgroundFragSource from "./background.frag?raw";
2+
import backgroundVertSource from "./background.vert?raw";
13
import type { GLContext } from "./context";
24
import { Attribute, Shader, Uniform1f, Uniform2f } from "./shader";
3-
import backgroundFragSource from "./shaders/background.frag?raw";
4-
import backgroundVertSource from "./shaders/background.vert?raw";
55

66
export class BackgroundRenderer {
77
#glContext: GLContext;

app/src/room/gl/border.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { Broadcast } from "../broadcast";
22
import { Canvas } from "../canvas";
3+
import borderFragSource from "./border.frag?raw";
4+
import borderVertSource from "./border.vert?raw";
35
import type { Camera } from "./camera";
46
import { Attribute, Shader, Uniform1f, Uniform2f, Uniform4f, UniformMatrix4fv } from "./shader";
5-
import borderFragSource from "./shaders/border.frag?raw";
6-
import borderVertSource from "./shaders/border.vert?raw";
77

88
export class BorderRenderer {
99
#canvas: Canvas;

0 commit comments

Comments
 (0)