Recently Written · git

sbm-extension

sbm bookmarks for Firefox and Chrome: fuzzy search, add the page, sync with bm and the sbm app

git clone https://github.com/equwal/sbm-extension

Log | Files | Refs


options.js (3000 bytes)

1 // The options page: sign in to a sync server, sync now, sign out.
2 "use strict";
3 
4 const api = globalThis.browser ?? globalThis.chrome;
5 const $ = (id) => document.getElementById(id);
6 const DEFAULT_SERVER = "https://sbm.subread.space";
7 
8 // The data that the add-on sends to the server after a sign-in. Firefox 140
9 // and later ask the user for it; other browsers have no such question.
10 const DATA = ["bookmarksInfo", "personallyIdentifyingInfo", "authenticationInfo"];
11 let askData = false;
12 api.permissions.getAll().then((p) => {
13   askData = !!p.data_collection;
14 });
15 
16 function send(msg) {
17   return api.runtime.sendMessage(msg).then((r) => {
18     if (r && r.error) throw new Error(r.error);
19     return r && r.value;
20   });
21 }
22 
23 function say(text) {
24   $("message").textContent = text;
25 }
26 
27 function origin(server) {
28   const s = server.trim().replace(/\/+$/, "");
29   return new URL(s.includes("://") ? s : "https://" + s).origin;
30 }
31 
32 async function show() {
33   const s = await api.storage.local.get(["server", "email", "token", "synced", "error", "text"]);
34   const server = s.server || DEFAULT_SERVER;
35   $("signed-out").hidden = !!s.token;
36   $("signed-in").hidden = !s.token;
37   if (!$("server").value) $("server").value = server;
38   if (!$("email").value) $("email").value = s.email || "";
39   $("signup").href = origin($("server").value || server) + "/signup";
40   $("account").href = server + "/account";
41   const n = Tsv.parse(s.text || "").length;
42   $("who").textContent = "Signed in to " + server + " as " + s.email + ". " + n + " bookmarks" +
43     (s.synced ? ", synced " + new Date(s.synced).toLocaleString() : "") + ".";
44   if (s.error) say("Sync: " + s.error);
45 }
46 
47 $("server").addEventListener("input", () => {
48   try {
49     $("signup").href = origin($("server").value) + "/signup";
50   } catch (e) {
51     // not an address yet
52   }
53 });
54 
55 $("sign-in").addEventListener("submit", async (e) => {
56   e.preventDefault();
57   let want;
58   try {
59     want = { origins: [origin($("server").value) + "/*"] };
60   } catch (err) {
61     say("The sync server is not an address.");
62     return;
63   }
64   if (askData) want.data_collection = DATA;
65   // The browser asks the user. The request must come before any await: only
66   // then does the browser see it as part of the click.
67   const granted = api.permissions.request(want);
68   say("Signing in…");
69   try {
70     if (!(await granted)) {
71       say("Without that permission the add-on cannot sync.");
72       return;
73     }
74     await send({ type: "signIn", server: $("server").value, email: $("email").value, password: $("password").value });
75     $("password").value = "";
76     say("");
77   } catch (err) {
78     say(err.message);
79   }
80   show();
81 });
82 
83 $("sync").addEventListener("click", async () => {
84   say("Syncing…");
85   try {
86     await send({ type: "sync" });
87     say("");
88   } catch (err) {
89     say(err.message);
90   }
91   show();
92 });
93 
94 $("sign-out").addEventListener("click", async () => {
95   await send({ type: "signOut" });
96   say("Signed out. Your bookmarks stay in this browser.");
97   show();
98 });
99 
100 show();