Skip to content
192 linesCodeBlameRaw
1// The settings window's work: show what the app has, and keep each change
2// the moment it is made. Everything it asks is a command in
3// src/bridge.rs, answered only to this window.
4(function () {
5 var SWITCHES = ["startHidden", "closeToTray", "notifications", "flash", "badge", "systemTitleBar", "autoUpdate"];
6 var current = null;
7
8 function byId(id) {
9 return document.getElementById(id);
10 }
11
12 function invoke(command, args) {
13 var core = window.__TAURI__ && window.__TAURI__.core;
14 if (core && typeof core.invoke === "function") return core.invoke(command, args || {});
15 return Promise.reject(new Error("This page belongs inside the g1t desktop app."));
16 }
17
18 function fail(error) {
19 var box = byId("error");
20 box.textContent = typeof error === "string" ? error : (error && error.message) || "That could not be changed.";
21 box.hidden = false;
22 }
23
24 function hostOf(server) {
25 try {
26 return new URL(server).host;
27 } catch (error) {
28 return server;
29 }
30 }
31
32 // What changes between Windows, macOS and Linux.
33 function platform(name) {
34 byId("flash-label").textContent =
35 name === "macos" ? "Bounce the dock icon" : name === "windows" ? "Flash the taskbar icon" : "Ask for attention";
36 // macOS keeps its menu bar at the top of the screen, whichever title bar the window has.
37 if (name === "macos") byId("system-title-bar-note").textContent = "In place of g1t's own, which sits under the window's buttons.";
38 if (name === "macos") byId("zoom-note").textContent = "The View menu changes it too: ⌘+, ⌘− and ⌘0.";
39 else byId("zoom-note").textContent = "The View menu changes it too: Ctrl+=, Ctrl+− and Ctrl+0.";
40 }
41
42 function render(view) {
43 current = view;
44 byId("error").hidden = true;
45 byId("openAtLogin").checked = !!view.openAtLogin;
46 SWITCHES.forEach(function (key) {
47 byId(key).checked = !!view[key];
48 });
49 // Starting in the tray only means something when g1t starts at sign-in.
50 byId("startHidden").disabled = !view.openAtLogin;
51 byId("startHidden").closest(".setting").classList.toggle("off", !view.openAtLogin);
52 // The title bar changes when g1t restarts.
53 byId("restart-row").hidden = !view.titleBarChanged;
54 var zoom = byId("zoom");
55 zoom.value = String(view.zoom);
56 if (zoom.value === "") zoom.value = "1";
57 byId("version").textContent = "g1t " + view.version;
58 if (!checking) updateStatus(view);
59 byId("server").textContent = hostOf(view.server);
60 }
61
62 function settingsOf() {
63 var settings = {};
64 SWITCHES.forEach(function (key) {
65 settings[key] = byId(key).checked;
66 });
67 settings.zoom = Number(byId("zoom").value) || 1;
68 return settings;
69 }
70
71 function save() {
72 invoke("save_settings", { settings: settingsOf() }).then(render, function (error) {
73 fail(error);
74 if (current) render(current);
75 });
76 }
77
78 SWITCHES.forEach(function (key) {
79 byId(key).addEventListener("change", save);
80 });
81 byId("zoom").addEventListener("change", save);
82 byId("openAtLogin").addEventListener("change", function (event) {
83 invoke("set_open_at_login", { on: event.target.checked }).then(render, function (error) {
84 fail(error);
85 if (current) render(current);
86 });
87 });
88
89 // Updates: where this copy stands, said in the window, so a check from
90 // the menu or the tray is answered here and cannot be missed.
91 var checking = false;
92
93 function when(ms) {
94 var date = new Date(ms);
95 var today = new Date().toDateString() === date.toDateString();
96 var time = date.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
97 return today ? "today at " + time : date.toLocaleDateString([], { month: "short", day: "numeric" }) + " at " + time;
98 }
99
100 function ready(version) {
101 byId("update-status").textContent = "Version " + version + " is downloaded and installs when g1t restarts.";
102 byId("restart").hidden = false;
103 }
104
105 function updateStatus(view) {
106 if (view.updateReady) return ready(view.updateReady);
107 byId("restart").hidden = true;
108 byId("update-status").textContent = view.checkedAt
109 ? "Up to date. Last checked " + when(view.checkedAt) + "."
110 : view.autoUpdate
111 ? "Not checked yet since g1t started; it checks on its own shortly."
112 : "Checking on its own is off.";
113 }
114
115 function check() {
116 if (checking) return;
117 checking = true;
118 var button = byId("check");
119 var status = byId("update-status");
120 button.disabled = true;
121 status.textContent = "Checking, and downloading any new version…";
122 invoke("check_for_updates")
123 .then(function (answer) {
124 if (answer.indexOf("ready:") === 0) ready(answer.slice(6));
125 else status.textContent = "Up to date. Last checked " + when(Date.now()) + ".";
126 })
127 .catch(function (error) {
128 var text = typeof error === "string" ? error : "";
129 status.textContent = /sending request|connect|dns|timed out/i.test(text) || !text
130 ? "Could not reach g1t.sh to check. Check your connection and try again."
131 : "Could not check: " + text;
132 })
133 .then(function () {
134 checking = false;
135 button.disabled = false;
136 });
137 }
138
139 byId("check").addEventListener("click", check);
140 byId("restart").addEventListener("click", function () {
141 byId("restart").disabled = true;
142 byId("update-status").textContent = "Installing…";
143 invoke("restart_to_update").catch(function (error) {
144 byId("restart").disabled = false;
145 fail(error);
146 });
147 });
148
149 // The menu's or tray's "Check for updates…" with this window open, and a
150 // download finishing in the background (src/settings.rs).
151 var events = window.__TAURI__ && window.__TAURI__.event;
152 if (events && typeof events.listen === "function") {
153 events.listen("g1t-check-updates", check).catch(function () {});
154 events
155 .listen("g1t-update-ready", function (event) {
156 if (!checking && typeof event.payload === "string") ready(event.payload);
157 })
158 .catch(function () {});
159 }
160
161 byId("restart-app").addEventListener("click", function () {
162 byId("restart-app").disabled = true;
163 invoke("restart").catch(function (error) {
164 byId("restart-app").disabled = false;
165 fail(error);
166 });
167 });
168
169 byId("change-server").addEventListener("click", function () {
170 invoke("change_server").catch(fail);
171 });
172
173 // Links out, in the system's browser (src/bridge.js does the same on the site).
174 document.addEventListener("click", function (event) {
175 var anchor = event.target && event.target.closest ? event.target.closest("a[href]") : null;
176 if (!anchor) return;
177 event.preventDefault();
178 invoke("open_url", { url: anchor.href }).catch(function () {});
179 });
180
181 invoke("get_settings")
182 .then(function (view) {
183 platform(view.platform);
184 render(view);
185 // Opened by "Check for updates…" in a menu.
186 if (new URLSearchParams(location.search).has("check")) {
187 byId("updates-row").scrollIntoView({ block: "center" });
188 check();
189 }
190 })
191 .catch(fail);
192})();