index.tsx
⎇
Raw
1/* @refresh reload */
2import { createSignal } from "solid-js";
3import { render } from "solid-js/web";
4import "./style.css";
5import "./components/Dialog.css";
6
7import { registerSW } from "virtual:pwa-register";
8import App from "./App";
9import { initCapabilities } from "./capabilities";
10import { clearLocalData, loadDataFromDB } from "./offline";
11import { toast } from "./utils";
12
13const root = document.getElementById("root");
14if (!(root instanceof HTMLElement)) {
15 throw new Error(
16 "Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?",
17 );
18}
19
20navigator?.storage?.persist()?.catch(() => {});
21
22function hasTouch() {
23 return (
24 "ontouchstart" in document.documentElement ||
25 navigator.maxTouchPoints > 0 ||
26 ("msMaxTouchPoints" in navigator &&
27 typeof navigator.msMaxTouchPoints === "number" &&
28 navigator.msMaxTouchPoints > 0)
29 );
30}
31
32if (hasTouch()) {
33 // remove all the :hover stylesheets
34 try {
35 // prevent exception on browsers not supporting DOM styleSheets properly
36 for (const si in document.styleSheets) {
37 const styleSheet = document.styleSheets[si];
38 if (!styleSheet.cssRules) continue;
39
40 for (let ri = styleSheet.cssRules.length - 1; ri >= 0; ri--) {
41 const rule = styleSheet.cssRules[ri];
42 if (!("selectorText" in rule) || typeof rule.selectorText !== "string" || !rule.selectorText) continue;
43
44 if (rule.selectorText.match(":hover")) {
45 styleSheet.deleteRule(ri);
46 }
47 }
48 }
49 } catch {}
50}
51
52function ResetDialog() {
53 const [error, setError] = createSignal<string>();
54 const [resetting, setResetting] = createSignal(false);
55
56 async function reset() {
57 setResetting(true);
58 setError(undefined);
59 const failure = await clearLocalData().catch((e) => e?.toString() ?? "Unknown error");
60 if (failure) {
61 setError(failure);
62 setResetting(false);
63 return;
64 }
65 location.hash = "";
66 location.reload();
67 }
68
69 return (
70 <div style={{ display: "flex", height: "100%", "align-items": "center" }}>
71 <dialog open>
72 <div>
73 Your local version of this application is not compatible with the newly loaded one, click "Reset" to delete
74 all stored settings, files and cached and refresh the page.
75 {error() && <div style={{ color: "rgb(255, 24, 24)" }}>{error()}</div>}
76 <button style={{ margin: "auto" }} disabled={resetting()} onClick={reset}>
77 {resetting() ? "Resetting..." : "Reset"}
78 </button>
79 </div>
80 </dialog>
81 </div>
82 );
83}
84
85const currentVersion = "1";
86const version = localStorage.getItem("app-version");
87if (version && version !== currentVersion) {
88 //TODO: add a way to at least export playlists before resetting
89 root.innerHTML = "";
90 render(() => <ResetDialog />, root);
91} else {
92 if (!version) {
93 try {
94 localStorage.setItem("app-version", currentVersion);
95 } catch {}
96 }
97
98 //the auto-injected registration only ever checks on a cold navigation, which for an installed
99 //player that stays open for days means new deploys are never picked up
100 const updateCheckInterval = 60 * 60 * 1000;
101 registerSW({
102 immediate: true,
103 onRegisteredSW: (_swScriptUrl, registration) => {
104 if (!registration) return;
105 setInterval(() => {
106 if (navigator.onLine) registration.update().catch(() => {});
107 }, updateCheckInterval);
108 },
109 //replaces the default automatic reload, which would cut off playback mid-track
110 onNeedReload: () => toast("A new version was installed, reload the page to use it", "info"),
111 });
112
113 Promise.all([loadDataFromDB(), initCapabilities()])
114 .then(() => {
115 root.innerHTML = "";
116 render(() => <App />, root);
117 })
118 .catch((e) => {
119 root.innerHTML = e.toString();
120 console.error(e);
121 });
122}
123