diff --git a/README.md b/README.md index 861a86de1..0e943d176 100644 --- a/README.md +++ b/README.md @@ -22,25 +22,25 @@ npm run lint If you are deploying in a subdirectory, add it to `publicPath` in vue.config.js, eg: `publicPath: "/app/time-tracker/",` -* `npx prettier --write ./sw_scripts/` - -...to make sure the service worker scripts are in proper form - * Update the CHANGELOG.md & the version in package.json, run `npm install`, and commit. * Tag wth the new version: `git tag 0.1.0`. -* If production, change src/constants/app.ts DEFAULT_*_SERVER to be PROD. +* If production, change src/constants/app.ts DEFAULT_*_SERVER to be "PROD" and package.json to remove "_Test". * `npm run build` +* `npx prettier --write ./sw_scripts/` + +...to make sure the service worker scripts are in proper form. It's only important if you changed something in that directory. + * `cp sw_scripts/[ns]* dist/` ... to copy the contents of the `sw_scripts` folder to the `dist` folder - except additional_scripts.js. * `rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntu@endorser.ch:time-safari` -* Revert src/constants/app.ts, increment version, add "-beta", `npm install`, and commit. +* Revert src/constants/app.ts and/or package.json, edit package.json to increment version & add "-beta", `npm install`, and commit. @@ -112,10 +112,10 @@ To add an icon, add to main.ts and reference with `fa` element and `icon` attrib ### Clear/Reset data & restart -* Clear cache for site. (In Chrome, go to `chrome://settings/cookies` and "all site data and permissions"; in Firefox, go to `about:preferences` and search for "cache" then "Manage Data".) -* Unregister service worker (in Chrome, go to `chrome://serviceworker-internals/`; in Firefox, go to `about:serviceworkers`). -* Clear notification permission (in Chrome, go to `chrome://settings/content/notifications`; in Firefox, go to `about:preferences` and search for "notifications"). -* Clear Cache Storage (in Chrome, in dev tools under Application; in Firefox, in dev tools under Storage). +* Clear cache for site. (In Chrome, go to `chrome://settings/cookies` and "all site data and permissions"; in Firefox, go to `about:preferences` and search for "cache" then "Manage Data", and also manually remove the IndexedDB data if the DBs still show.) +* Clear notification permission. (in Chrome, go to `chrome://settings/content/notifications`; in Firefox, go to `about:preferences` and search for "notifications".) +* Unregister service worker. (in Chrome, go to `chrome://serviceworker-internals/`; in Firefox, go to `about:serviceworkers`.) +* Clear Cache Storage. (in Chrome, in dev tools under Application; in Firefox, in dev tools under Storage.) diff --git a/package.json b/package.json index a24cb46a4..1d35ad2b3 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "TimeSafari", + "name": "TimeSafari_Test", "version": "0.1.8-beta", "private": true, "scripts": { diff --git a/project.task.yaml b/project.task.yaml index a80de786c..ba36658ec 100644 --- a/project.task.yaml +++ b/project.task.yaml @@ -2,29 +2,30 @@ tasks: - 08 notifications : - - make the app behave correctly when App Notifications are turned off - - write troubleshooting docs for notifications - - hide the "App Notifications" toggle when they switch notifications - - prompt user to install on their home screen https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getInstalledRelatedApps + - get the rest of our Android devices to work + - insert tooling (exportable logs?) so that we can see problems and troubleshoot as we onboard + - get an error registering notifications on Firefox and subscription info is null + - if navigator.serviceWorker is null, then tell the user to wait + - Android DuckDuckGo asked for my permissions, got error, won't download DB + - Android Chrome won't ask permission, will download log but always empty + - Firefox works + - Local install works after cleared out cache in Chrome + - create troubleshooting notification: + - server gets signal to send a normal notification back immediately + - add windows & mac help at OS & browser level, in HelpNotificationsView.vue (linked from account page) + maybe tell them to pause, after first turn-on and after test + maybe Google Play permissions + - prompt user to install on their home screen https://benborgers.com/posts/pwa-detect-installed - warn if they're using the web (android only?) https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getInstalledRelatedApps https://web.dev/articles/get-installed-related-apps - - add windows & mac help at OS & browser level -- back-and-forth on discovery & project pages led to "You need an identity to load your projects." error on product page when I had an identity -- fix the projects on /discover to show the issuer (currently all "Someone Anonymous") +- .2 fix the projects on /discover to show the issuer (currently all "Someone Anonymous") - .3 bug - make or edit a project, choose "Include location", and see the map display shows on top of the bottom icons assignee-group:ui -- Got error adding on Firefox user #0 as contact for themselves - .5 If notifications are not enabled, add message to front page with link/button to enable -- 01 server - show all claim details when issued by the issuer -- enhance help page instructions for friends to get notifications, for debugging notifications -- add way to test a push notification -- help instructions for PWA install problems (secret failed, must reinstall) -- look at other examples for better UI friend.tech - - show VC details... somehow: - 01 show my VCs - most interesting, or via search - 01 allow download of each VC (& confirmations, to show that they actually own their data) @@ -44,9 +45,14 @@ tasks: - Other features - donation vs give, show offers, show give & outstanding totals, show network view, restrict registration, connect to contacts blocks: ref:https://raw.githubusercontent.com/trentlarson/lives-of-gifts/master/project.yaml#kickstarter%20for%20time -- 01 send visibility signal as a VC and store it -- remove 'rowid' references (that are sqlite-specific) - make identicons for contacts into more-memorable faces (and maybe change project identicons, too) +- 04 split out notification logic & tests from web-push-subscription logic & tests +- 01 server - show all claim details when issued by the issuer +- bug - got error adding on Firefox user #0 as contact for themselves +- bug - back-and-forth on discovery & project pages led to "You need an identity to load your projects." error on product page when I had an identity +- 01 send visibility signal as a VC and store it +- 04 remove 'rowid' references (that are sqlite-specific); may involve server +- 04 look at other examples for better UI friend.tech - 01 make the prod build copy the sw_scripts - .5 Add start date to project - .3 check that Android shows "back" buttons on screens without bottom tray @@ -83,6 +89,7 @@ tasks: - 24 Move to Vite - 32 accept images for projects - 32 accept images for contacts +- import project interactions from GitHub/GitLab and manage signing - linking between projects or plans : - show total time given to & from a project diff --git a/src/App.vue b/src/App.vue index 05bbe0a98..85489c8d9 100644 --- a/src/App.vue +++ b/src/App.vue @@ -281,7 +281,7 @@ interface VapidResponse { }; } -import { AppString } from "@/constants/app"; +import { DEFAULT_PUSH_SERVER } from "@/constants/app"; import { db } from "@/db/index"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; @@ -302,7 +302,7 @@ export default class App extends Vue { try { await db.open(); const settings = await db.settings.get(MASTER_SETTINGS_KEY); - let pushUrl: string = AppString.DEFAULT_PUSH_SERVER; + let pushUrl = DEFAULT_PUSH_SERVER; if (settings?.webPushServer) { pushUrl = settings.webPushServer; } @@ -373,6 +373,7 @@ export default class App extends Vue { } private askPermission(): Promise { + console.log("Requesting permission for notifications:", navigator); if (!("serviceWorker" in navigator && navigator.serviceWorker.controller)) { return Promise.reject("Service worker not available."); } @@ -445,7 +446,18 @@ export default class App extends Vue { } }) .then(() => { - console.log("Subscription data sent to server."); + console.log( + "Subscription data sent to server and all finished successfully.", + ); + this.$notify( + { + group: "alert", + type: "success", + title: "Notifications Turned On", + text: "Notifications are on. You should see one on your device; if not, see the 'Troubleshoot' page.", + }, + -1, + ); }) .catch((error) => { console.error( @@ -462,9 +474,7 @@ export default class App extends Vue { "An error occurred setting notification permissions:", error, ); - alert( - "Some error occurred setting notification permissions. See logs.", - ); + alert("Some error occurred setting notification permissions."); }); } @@ -511,11 +521,7 @@ export default class App extends Vue { resolve(); }) .catch((error) => { - console.error( - "Subscription or server communication failed:", - error, - options, - ); + console.error("Push subscription failed:", error, options); // Inform the user about the issue alert( diff --git a/src/components/GiftedDialog.vue b/src/components/GiftedDialog.vue index 1dfb153ef..e23624633 100644 --- a/src/components/GiftedDialog.vue +++ b/src/components/GiftedDialog.vue @@ -123,9 +123,7 @@ export default class GiftedDialog extends Vue { group: "alert", type: "danger", title: "Error", - text: - err.message || - "There was an error retrieving the latest sweet, sweet action.", + text: err.message || "There was an error retrieving your settings.", }, -1, ); diff --git a/src/components/OfferDialog.vue b/src/components/OfferDialog.vue index 0ab370041..b61bde829 100644 --- a/src/components/OfferDialog.vue +++ b/src/components/OfferDialog.vue @@ -105,9 +105,7 @@ export default class OfferDialog extends Vue { group: "alert", type: "danger", title: "Error", - text: - err.message || - "There was an error retrieving the latest sweet, sweet action.", + text: err.message || "There was an error retrieving your settings.", }, -1, ); diff --git a/src/components/TopMessage.vue b/src/components/TopMessage.vue index 843374109..bccc2fec4 100644 --- a/src/components/TopMessage.vue +++ b/src/components/TopMessage.vue @@ -50,7 +50,7 @@ export default class TopMessage extends Vue { title: "Error Detecting Server", text: JSON.stringify(err), }, - 10000, + -1, ); } } diff --git a/src/constants/app.ts b/src/constants/app.ts index c92709bd5..16758064c 100644 --- a/src/constants/app.ts +++ b/src/constants/app.ts @@ -4,22 +4,20 @@ * See also ../libs/veramo/setup.ts */ export enum AppString { - APP_NAME = "Time Safari", - PROD_ENDORSER_API_SERVER = "https://api.endorser.ch", TEST_ENDORSER_API_SERVER = "https://test-api.endorser.ch", LOCAL_ENDORSER_API_SERVER = "http://localhost:3000", - DEFAULT_ENDORSER_API_SERVER = TEST_ENDORSER_API_SERVER, - - // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values PROD_PUSH_SERVER = "https://timesafari.app", TEST1_PUSH_SERVER = "https://test.timesafari.app", TEST2_PUSH_SERVER = "https://timesafari-pwa.anomalistlabs.com", - - DEFAULT_PUSH_SERVER = TEST1_PUSH_SERVER, } +export const DEFAULT_ENDORSER_API_SERVER = AppString.TEST_ENDORSER_API_SERVER; + +export const DEFAULT_PUSH_SERVER = + window.location.protocol + "//" + window.location.host; + /** * The possible values for "group" and "type" are in App.vue. * From the notiwind package diff --git a/src/db/index.ts b/src/db/index.ts index 37d6cf517..c15f6c928 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,18 +1,20 @@ import BaseDexie, { Table } from "dexie"; import { encrypted, Encryption } from "@pvermeer/dexie-encrypted-addon"; import { Account, AccountsSchema } from "./tables/accounts"; -import { Contact, ContactsSchema } from "./tables/contacts"; +import { Contact, ContactSchema } from "./tables/contacts"; +import { Log, LogSchema } from "@/db/tables/logs"; import { MASTER_SETTINGS_KEY, Settings, SettingsSchema, } from "./tables/settings"; -import { AppString } from "@/constants/app"; +import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app"; // Define types for tables that hold sensitive and non-sensitive data type SensitiveTables = { accounts: Table }; type NonsensitiveTables = { contacts: Table; + logs: Table; settings: Table; }; @@ -26,7 +28,11 @@ export const accountsDB = new BaseDexie("TimeSafariAccounts") as SensitiveDexie; const SensitiveSchemas = { ...AccountsSchema }; export const db = new BaseDexie("TimeSafari") as NonsensitiveDexie; -const NonsensitiveSchemas = { ...ContactsSchema, ...SettingsSchema }; +const NonsensitiveSchemas = { + ...ContactSchema, + ...LogSchema, + ...SettingsSchema, +}; // Manage the encryption key. If not present in localStorage, create and store it. const secret = @@ -38,12 +44,14 @@ encrypted(accountsDB, { secretKey: secret }); // Define the schema for our databases accountsDB.version(1).stores(SensitiveSchemas); -db.version(1).stores(NonsensitiveSchemas); +// v1 was contacts & settings +// v2 added logs +db.version(2).stores(NonsensitiveSchemas); // Event handler to initialize the non-sensitive database with default settings db.on("populate", () => { db.settings.add({ id: MASTER_SETTINGS_KEY, - apiServer: AppString.DEFAULT_ENDORSER_API_SERVER, + apiServer: DEFAULT_ENDORSER_API_SERVER, }); }); diff --git a/src/db/tables/contacts.ts b/src/db/tables/contacts.ts index b68450576..8ffd2fb83 100644 --- a/src/db/tables/contacts.ts +++ b/src/db/tables/contacts.ts @@ -6,6 +6,6 @@ export interface Contact { registered?: boolean; } -export const ContactsSchema = { +export const ContactSchema = { contacts: "&did, name, publicKeyBase64, registered, seesMe", }; diff --git a/src/db/tables/logs.ts b/src/db/tables/logs.ts new file mode 100644 index 000000000..9207a2fa5 --- /dev/null +++ b/src/db/tables/logs.ts @@ -0,0 +1,10 @@ +export interface Log { + message: string; +} + +export const LogSchema = { + // Currently keyed by "date" because A) today's log data is what we need so we append, and + // B) we don't want it to grow so we remove everything if this is the first entry today. + // See safari-notifications.js logMessage for the associated logic. + logs: "date, message", +}; diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index f459c0ed6..1a19d0594 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -133,6 +133,9 @@ Notification status may have changed. Revisit this page to see the latest setting. + + Troubleshoot notifications here. +

Data

@@ -306,13 +309,6 @@ Switch Identity - -

Claim Server

diff --git a/src/views/ContactAmountsView.vue b/src/views/ContactAmountsView.vue index a3dfc732b..52412ae34 100644 --- a/src/views/ContactAmountsView.vue +++ b/src/views/ContactAmountsView.vue @@ -178,6 +178,7 @@ export default class ContactAmountssView extends Vue { } // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err: any) { + console.log("Error retrieving settings or gives.", err); this.$notify( { group: "alert", @@ -185,7 +186,7 @@ export default class ContactAmountssView extends Vue { title: "Error", text: err.userMessage || - "There was an error retrieving the latest sweet, sweet action.", + "There was an error retrieving your settings and/or contacts and/or gives.", }, -1, ); diff --git a/src/views/ContactGiftingView.vue b/src/views/ContactGiftingView.vue index 2d2bcb830..35bec2541 100644 --- a/src/views/ContactGiftingView.vue +++ b/src/views/ContactGiftingView.vue @@ -145,6 +145,7 @@ export default class ContactGiftingView extends Vue { this.allContacts = await db.contacts.toArray(); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err: any) { + console.log("Error retrieving settings & contacts:", err); this.$notify( { group: "alert", @@ -152,7 +153,7 @@ export default class ContactGiftingView extends Vue { title: "Error", text: err.message || - "There was an error retrieving the latest sweet, sweet action.", + "There was an error retrieving your settings and/or contacts.", }, -1, ); diff --git a/src/views/HelpNotificationsView.vue b/src/views/HelpNotificationsView.vue index c734ba434..77b284b4f 100644 --- a/src/views/HelpNotificationsView.vue +++ b/src/views/HelpNotificationsView.vue @@ -22,10 +22,16 @@
-

Here are things to try to get notifications working.

+

Here are ways to get notifications working.

Test

Somehow call the service-worker self.showNotification

+

Check OS-level permissions

@@ -54,7 +60,10 @@
Make sure your OS (above) supports it.

Mobile Phone - Android

-
Chrome requires version 50.
+
+ Chrome requires version 50. Hold icon, select "App info", and enable + notifications. +
@@ -66,12 +75,34 @@

Auto-detection

Show results of auto-detection whether they're turned on

+ + + + diff --git a/src/views/HelpView.vue b/src/views/HelpView.vue index 6caae3049..53ea28449 100644 --- a/src/views/HelpView.vue +++ b/src/views/HelpView.vue @@ -182,6 +182,15 @@ different page.

+

+ Where do I get help with notifications? +

+

+ Here. +

+

How do I access even more functionality?

diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 3c973b3af..daf47c45a 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -234,6 +234,7 @@ export default class HomeView extends Vue { // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err: any) { + console.log("Error retrieving settings and/or feed.", err); this.$notify( { group: "alert", @@ -241,7 +242,7 @@ export default class HomeView extends Vue { title: "Error", text: err.userMessage || - "There was an error retrieving the latest sweet, sweet action.", + "There was an error retrieving your settings and/or the latest activity.", }, -1, ); diff --git a/sw_scripts/additional-scripts.js b/sw_scripts/additional-scripts.js index fe16c1802..79b42a72f 100644 --- a/sw_scripts/additional-scripts.js +++ b/sw_scripts/additional-scripts.js @@ -5,67 +5,103 @@ importScripts( ); self.addEventListener("install", (event) => { - console.log(new Date().toISOString(), "Installing service worker:", event); + console.log("Service worker got install. Importing scripts...", event); importScripts( "safari-notifications.js", "nacl.js", "noble-curves.js", "noble-hashes.js", ); + console.log("Service worker imported scripts."); }); +function logConsoleAndDb(message, arg1, arg2) { + console.log(`${new Date().toISOString()} ${message}`, arg1, arg2); + let fullMessage = `${new Date().toISOString()} ${message}`; + if (arg1) { + fullMessage += `\n${JSON.stringify(arg1)}`; + } + if (arg2) { + fullMessage += `\n${JSON.stringify(arg2)}`; + } + self.appendDailyLog(fullMessage); +} + self.addEventListener("push", function (event) { - console.log(new Date().toISOString(), "Received push event:", event); + let text = null; + if (event.data) { + text = event.data.text(); + } + logConsoleAndDb("Service worker received a push event.", text, event); event.waitUntil( (async () => { try { let payload; - if (event.data) { - payload = JSON.parse(event.data.text()); + if (text) { + try { + payload = JSON.parse(text); + } catch (e) { + // don't use payload since it is not JSON + } + } + + // This is shared with the notification-test code and should be a constant. Look for the same name in HelpNotificationsView.vue + // Use something other than "Daily Update" https://gitea.anomalistdesign.com/trent_larson/py-push-server/src/commit/3c0e196c11bc98060ec5934e99e7dbd591b5da4d/app.py#L213 + const DIRECT_PUSH_TITLE = "DIRECT_NOTIFICATION"; + + let title = "Generic Notification"; + let message = "Got some empty message."; + if (payload && payload.title == DIRECT_PUSH_TITLE) { + // skip any search logic and show the message directly + title = "Direct Message"; + message = payload.message || "No details were provided."; + } else { + title = + payload && payload.title ? payload.title : "Unknown Notification"; + message = await self.getNotificationCount(); } - const message = await self.getNotificationCount(); if (message) { - const title = payload && payload.title ? payload.title : "Message"; const options = { body: message, icon: payload ? payload.icon : "icon.png", badge: payload ? payload.badge : "badge.png", }; await self.registration.showNotification(title, options); - console.log(new Date().toISOString(), "Notified user:", options); + logConsoleAndDb("Notified user:", options); } else { - console.log(new Date().toISOString(), "No notification message."); + logConsoleAndDb("No notification message."); } } catch (error) { - console.error(new Date().toISOString(), "Error with push event", event, " - ", error); + logConsoleAndDb("Error with push event", event, error); } })(), ); }); self.addEventListener("message", (event) => { - console.log(new Date().toISOString(), "Service worker message:", event); + logConsoleAndDb("Service worker got a message...", event); if (event.data && event.data.type === "SEND_LOCAL_DATA") { self.secret = event.data.data; event.ports[0].postMessage({ success: true }); } - console.log(new Date().toISOString(), "Service worker posted message."); + logConsoleAndDb("Service worker posted a message."); }); self.addEventListener("activate", (event) => { - console.log(new Date().toISOString(), "Service worker activating...", event); + logConsoleAndDb("Service worker activating...", event); // see https://developer.mozilla.org/en-US/docs/Web/API/Clients/claim // and https://web.dev/articles/service-worker-lifecycle#clientsclaim event.waitUntil(clients.claim()); - console.log(new Date().toISOString(), "Service worker activated."); + logConsoleAndDb("Service worker is activated."); }); self.addEventListener("fetch", (event) => { - console.log(new Date().toISOString(), "Got fetch event:", event); + logConsoleAndDb("Service worker got fetch event.", event); }); self.addEventListener("error", (event) => { - console.error(new Date().toISOString(), "Error in Service Worker:", event); + logConsoleAndDb("Service worker error", event); + console.error("Full Error:", event); console.error("Message:", event.message); console.error("File:", event.filename); console.error("Line:", event.lineno); diff --git a/sw_scripts/safari-notifications.js b/sw_scripts/safari-notifications.js index 32c12e02b..0d0eb9c3b 100644 --- a/sw_scripts/safari-notifications.js +++ b/sw_scripts/safari-notifications.js @@ -395,12 +395,42 @@ async function setMostRecentNotified(id) { data["lastNotifiedClaimId"] = id; await updateRecord(store, data); } else { - console.error("IndexedDB settings record not found."); + console.error( + "safari-notifications setMostRecentNotified IndexedDB settings record not found", + ); } transaction.oncomplete = () => db.close(); } catch (error) { - console.error("IndexedDB error:", error); + console.error( + "safari-notifications setMostRecentNotified IndexedDB error", + error, + ); + } +} + +async function appendDailyLog(message) { + try { + const db = await openIndexedDB("TimeSafari"); + const transaction = db.transaction("logs", "readwrite"); + const store = transaction.objectStore("logs"); + // will only keep one day's worth of logs + const todayKey = new Date().toDateString(); + const previous = await getRecord(store, todayKey); + if (!previous) { + await store.clear(); // clear out anything older than today + } + let fullMessage = (previous && previous.message) || ""; + if (fullMessage) { + fullMessage += "\n"; + } + fullMessage += message; + await updateRecord(store, { date: todayKey, message: fullMessage }); + transaction.oncomplete = () => db.close(); + return true; + } catch (error) { + console.error("safari-notifications logMessage IndexedDB error", error); + return false; } } @@ -420,6 +450,7 @@ function getRecord(store, key) { }); } +// Note that this assumes there is only one record in the store. function updateRecord(store, data) { return new Promise((resolve, reject) => { const request = store.put(data); @@ -430,20 +461,23 @@ function updateRecord(store, data) { async function fetchAllAccounts() { return new Promise((resolve, reject) => { - let openRequest = indexedDB.open("TimeSafariAccounts"); + const openRequest = indexedDB.open("TimeSafariAccounts"); openRequest.onupgradeneeded = function (event) { - let db = event.target.result; + const db = event.target.result; if (!db.objectStoreNames.contains("accounts")) { db.createObjectStore("accounts", { keyPath: "id" }); } + if (!db.objectStoreNames.contains("worker_log")) { + db.createObjectStore("worker_log"); + } }; openRequest.onsuccess = function (event) { - let db = event.target.result; - let transaction = db.transaction("accounts", "readonly"); - let objectStore = transaction.objectStore("accounts"); - let getAllRequest = objectStore.getAll(); + const db = event.target.result; + const transaction = db.transaction("accounts", "readonly"); + const objectStore = transaction.objectStore("accounts"); + const getAllRequest = objectStore.getAll(); getAllRequest.onsuccess = function () { resolve(getAllRequest.result); @@ -460,76 +494,74 @@ async function fetchAllAccounts() { } async function getNotificationCount() { - let secret = null; let accounts = []; let result = null; - if ("secret" in self) { - secret = self.secret; - const secretUint8Array = self.decodeBase64(secret); - // 1 is our master settings ID; see MASTER_SETTINGS_KEY - const settings = await getSettingById(1); - let lastNotifiedClaimId = null; - if ("lastNotifiedClaimId" in settings) { - lastNotifiedClaimId = settings["lastNotifiedClaimId"]; + // 1 is our master settings ID; see MASTER_SETTINGS_KEY + const settings = await getSettingById(1); + let lastNotifiedClaimId = null; + if ("lastNotifiedClaimId" in settings) { + lastNotifiedClaimId = settings["lastNotifiedClaimId"]; + } + const activeDid = settings["activeDid"]; + accounts = await fetchAllAccounts(); + let activeAccount = null; + for (let i = 0; i < accounts.length; i++) { + if (accounts[i]["did"] == activeDid) { + activeAccount = accounts[i]; + break; } - const activeDid = settings["activeDid"]; - accounts = await fetchAllAccounts(); - let did = null; - for (var i = 0; i < accounts.length; i++) { - let account = accounts[i]; - let did = account["did"]; - if (did == activeDid) { - let publicKeyHex = account["publicKeyHex"]; - let identity = account["identity"]; - const messageWithNonceAsUint8Array = self.decodeBase64(identity); - const nonce = messageWithNonceAsUint8Array.slice(0, 24); - const message = messageWithNonceAsUint8Array.slice(24, identity.length); - const decoder = new TextDecoder("utf-8"); - const decrypted = self.secretbox.open(message, nonce, secretUint8Array); - - const msg = decoder.decode(decrypted); - const identifier = JSON.parse(JSON.parse(msg)); - - const headers = { - "Content-Type": "application/json", - }; - - headers["Authorization"] = "Bearer " + (await accessToken(identifier)); - - let response = await fetch( - settings["apiServer"] + "/api/v2/report/claims", - { - method: "GET", - headers: headers, - }, - ); - if (response.status == 200) { - let json = await response.json(); - let claims = json["data"]; - let newClaims = 0; - for (var i = 0; i < claims.length; i++) { - let claim = claims[i]; - if (claim["id"] === lastNotifiedClaimId) { - break; - } - newClaims++; - } - if (newClaims > 0) { - result = `There are ${newClaims} new activities on TimeSafari`; - } - const most_recent_notified = claims[0]["id"]; - await setMostRecentNotified(most_recent_notified); - } else { - console.error( - "The service worker got a bad response status when fetching claims:", - response.status, - response, - ); - } + } + + const headers = { + "Content-Type": "application/json", + }; + + const identity = activeAccount && activeAccount["identity"]; + if (identity && "secret" in self) { + const secret = self.secret; + const secretUint8Array = self.decodeBase64(secret); + const messageWithNonceAsUint8Array = self.decodeBase64(identity); + const nonce = messageWithNonceAsUint8Array.slice(0, 24); + const message = messageWithNonceAsUint8Array.slice(24, identity.length); + const decoder = new TextDecoder("utf-8"); + const decrypted = self.secretbox.open(message, nonce, secretUint8Array); + const msg = decoder.decode(decrypted); + const identifier = JSON.parse(JSON.parse(msg)); + + headers["Authorization"] = "Bearer " + (await accessToken(identifier)); + } + + const response = await fetch( + settings["apiServer"] + "/api/v2/report/claims", + { + method: "GET", + headers: headers, + }, + ); + if (response.status == 200) { + const json = await response.json(); + const claims = json["data"]; + let newClaims = 0; + for (let i = 0; i < claims.length; i++) { + const claim = claims[i]; + if (claim["id"] === lastNotifiedClaimId) { break; } + newClaims++; + } + if (newClaims > 0) { + result = `There are ${newClaims} new activities on Time Safari`; } + const most_recent_notified = claims[0]["id"]; + await setMostRecentNotified(most_recent_notified); + } else { + console.error( + "safari-notifications getNotificationsCount got a bad response status when fetching claims", + response.status, + response, + ); } + return result; }