Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 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 () { | |
| The desktop app draws its own title bar, as Slack and Discord do, and the site can call the app again. | 5 | var SWITCHES = ["startHidden", "closeToTray", "notifications", "flash", "badge", "systemTitleBar", "autoUpdate"]; |
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 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"; | |
| The desktop app draws its own title bar, as Slack and Discord do, and the site can call the app again. | 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."; | |
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 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); | |
| The desktop app draws its own title bar, as Slack and Discord do, and the site can call the app again. | 52 | // The title bar changes when g1t restarts. |
| 53 | byId("restart-row").hidden = !view.titleBarChanged; | |
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 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; | |
| Checking for updates in the desktop app answers in its settings window, with a restart beside a downloaded version. | 58 | if (!checking) updateStatus(view); |
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 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 | ||
| Checking for updates in the desktop app answers in its settings window, with a restart beside a downloaded version. | 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; | |
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 118 | var button = byId("check"); |
| 119 | var status = byId("update-status"); | |
| 120 | button.disabled = true; | |
| Checking for updates in the desktop app answers in its settings window, with a restart beside a downloaded version. | 121 | status.textContent = "Checking, and downloading any new version…"; |
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 122 | invoke("check_for_updates") |
| 123 | .then(function (answer) { | |
| Checking for updates in the desktop app answers in its settings window, with a restart beside a downloaded version. | 124 | if (answer.indexOf("ready:") === 0) ready(answer.slice(6)); |
| 125 | else status.textContent = "Up to date. Last checked " + when(Date.now()) + "."; | |
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 126 | }) |
| 127 | .catch(function (error) { | |
| Checking for updates in the desktop app answers in its settings window, with a restart beside a downloaded version. | 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; | |
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 132 | }) |
| 133 | .then(function () { | |
| Checking for updates in the desktop app answers in its settings window, with a restart beside a downloaded version. | 134 | checking = false; |
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 135 | button.disabled = false; |
| 136 | }); | |
| Checking for updates in the desktop app answers in its settings window, with a restart beside a downloaded version. | 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 | }); | |
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 147 | }); |
| 148 | ||
| Checking for updates in the desktop app answers in its settings window, with a restart beside a downloaded version. | 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 | ||
| The desktop app draws its own title bar, as Slack and Discord do, and the site can call the app again. | 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 | ||
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 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); | |
| Checking for updates in the desktop app answers in its settings window, with a restart beside a downloaded version. | 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 | } | |
| The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it. | 190 | }) |
| 191 | .catch(fail); | |
| 192 | })(); |