Merge pull request 'Daily service-worker logging' (#100) from sw-log into master
Reviewed-on: #100
This commit was merged in pull request #100.
This commit is contained in:
20
README.md
20
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/",`
|
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.
|
* Update the CHANGELOG.md & the version in package.json, run `npm install`, and commit.
|
||||||
|
|
||||||
* Tag wth the new version: `git tag 0.1.0`.
|
* 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`
|
* `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/`
|
* `cp sw_scripts/[ns]* dist/`
|
||||||
|
|
||||||
... to copy the contents of the `sw_scripts` folder to the `dist` folder - except additional_scripts.js.
|
... 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`
|
* `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/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".)
|
* 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.)
|
||||||
* 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 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).
|
* Clear Cache Storage. (in Chrome, in dev tools under Application; in Firefox, in dev tools under Storage.)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "TimeSafari",
|
"name": "TimeSafari_Test",
|
||||||
"version": "0.1.8-beta",
|
"version": "0.1.8-beta",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -2,29 +2,30 @@
|
|||||||
tasks:
|
tasks:
|
||||||
|
|
||||||
- 08 notifications :
|
- 08 notifications :
|
||||||
- make the app behave correctly when App Notifications are turned off
|
- get the rest of our Android devices to work
|
||||||
- write troubleshooting docs for notifications
|
- insert tooling (exportable logs?) so that we can see problems and troubleshoot as we onboard
|
||||||
- hide the "App Notifications" toggle when they switch notifications
|
- get an error registering notifications on Firefox and subscription info is null
|
||||||
- prompt user to install on their home screen https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getInstalledRelatedApps
|
- 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?)
|
- warn if they're using the web (android only?)
|
||||||
https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getInstalledRelatedApps
|
https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getInstalledRelatedApps
|
||||||
https://web.dev/articles/get-installed-related-apps
|
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
|
- .2 fix the projects on /discover to show the issuer (currently all "Someone Anonymous")
|
||||||
- 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
|
- .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
|
- .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:
|
- show VC details... somehow:
|
||||||
- 01 show my VCs - most interesting, or via search
|
- 01 show my VCs - most interesting, or via search
|
||||||
- 01 allow download of each VC (& confirmations, to show that they actually own their data)
|
- 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
|
- 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
|
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)
|
- 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
|
- 01 make the prod build copy the sw_scripts
|
||||||
- .5 Add start date to project
|
- .5 Add start date to project
|
||||||
- .3 check that Android shows "back" buttons on screens without bottom tray
|
- .3 check that Android shows "back" buttons on screens without bottom tray
|
||||||
@@ -83,6 +89,7 @@ tasks:
|
|||||||
- 24 Move to Vite
|
- 24 Move to Vite
|
||||||
- 32 accept images for projects
|
- 32 accept images for projects
|
||||||
- 32 accept images for contacts
|
- 32 accept images for contacts
|
||||||
|
- import project interactions from GitHub/GitLab and manage signing
|
||||||
|
|
||||||
- linking between projects or plans :
|
- linking between projects or plans :
|
||||||
- show total time given to & from a project
|
- show total time given to & from a project
|
||||||
|
|||||||
28
src/App.vue
28
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 { db } from "@/db/index";
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
|
|
||||||
@@ -302,7 +302,7 @@ export default class App extends Vue {
|
|||||||
try {
|
try {
|
||||||
await db.open();
|
await db.open();
|
||||||
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||||
let pushUrl: string = AppString.DEFAULT_PUSH_SERVER;
|
let pushUrl = DEFAULT_PUSH_SERVER;
|
||||||
if (settings?.webPushServer) {
|
if (settings?.webPushServer) {
|
||||||
pushUrl = settings.webPushServer;
|
pushUrl = settings.webPushServer;
|
||||||
}
|
}
|
||||||
@@ -373,6 +373,7 @@ export default class App extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private askPermission(): Promise<NotificationPermission> {
|
private askPermission(): Promise<NotificationPermission> {
|
||||||
|
console.log("Requesting permission for notifications:", navigator);
|
||||||
if (!("serviceWorker" in navigator && navigator.serviceWorker.controller)) {
|
if (!("serviceWorker" in navigator && navigator.serviceWorker.controller)) {
|
||||||
return Promise.reject("Service worker not available.");
|
return Promise.reject("Service worker not available.");
|
||||||
}
|
}
|
||||||
@@ -445,7 +446,18 @@ export default class App extends Vue {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(() => {
|
.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) => {
|
.catch((error) => {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -462,9 +474,7 @@ export default class App extends Vue {
|
|||||||
"An error occurred setting notification permissions:",
|
"An error occurred setting notification permissions:",
|
||||||
error,
|
error,
|
||||||
);
|
);
|
||||||
alert(
|
alert("Some error occurred setting notification permissions.");
|
||||||
"Some error occurred setting notification permissions. See logs.",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,11 +521,7 @@ export default class App extends Vue {
|
|||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error(
|
console.error("Push subscription failed:", error, options);
|
||||||
"Subscription or server communication failed:",
|
|
||||||
error,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Inform the user about the issue
|
// Inform the user about the issue
|
||||||
alert(
|
alert(
|
||||||
|
|||||||
@@ -123,9 +123,7 @@ export default class GiftedDialog extends Vue {
|
|||||||
group: "alert",
|
group: "alert",
|
||||||
type: "danger",
|
type: "danger",
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text:
|
text: err.message || "There was an error retrieving your settings.",
|
||||||
err.message ||
|
|
||||||
"There was an error retrieving the latest sweet, sweet action.",
|
|
||||||
},
|
},
|
||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -105,9 +105,7 @@ export default class OfferDialog extends Vue {
|
|||||||
group: "alert",
|
group: "alert",
|
||||||
type: "danger",
|
type: "danger",
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text:
|
text: err.message || "There was an error retrieving your settings.",
|
||||||
err.message ||
|
|
||||||
"There was an error retrieving the latest sweet, sweet action.",
|
|
||||||
},
|
},
|
||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export default class TopMessage extends Vue {
|
|||||||
title: "Error Detecting Server",
|
title: "Error Detecting Server",
|
||||||
text: JSON.stringify(err),
|
text: JSON.stringify(err),
|
||||||
},
|
},
|
||||||
10000,
|
-1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,22 +4,20 @@
|
|||||||
* See also ../libs/veramo/setup.ts
|
* See also ../libs/veramo/setup.ts
|
||||||
*/
|
*/
|
||||||
export enum AppString {
|
export enum AppString {
|
||||||
APP_NAME = "Time Safari",
|
|
||||||
|
|
||||||
PROD_ENDORSER_API_SERVER = "https://api.endorser.ch",
|
PROD_ENDORSER_API_SERVER = "https://api.endorser.ch",
|
||||||
TEST_ENDORSER_API_SERVER = "https://test-api.endorser.ch",
|
TEST_ENDORSER_API_SERVER = "https://test-api.endorser.ch",
|
||||||
LOCAL_ENDORSER_API_SERVER = "http://localhost:3000",
|
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",
|
PROD_PUSH_SERVER = "https://timesafari.app",
|
||||||
TEST1_PUSH_SERVER = "https://test.timesafari.app",
|
TEST1_PUSH_SERVER = "https://test.timesafari.app",
|
||||||
TEST2_PUSH_SERVER = "https://timesafari-pwa.anomalistlabs.com",
|
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.
|
* The possible values for "group" and "type" are in App.vue.
|
||||||
* From the notiwind package
|
* From the notiwind package
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import BaseDexie, { Table } from "dexie";
|
import BaseDexie, { Table } from "dexie";
|
||||||
import { encrypted, Encryption } from "@pvermeer/dexie-encrypted-addon";
|
import { encrypted, Encryption } from "@pvermeer/dexie-encrypted-addon";
|
||||||
import { Account, AccountsSchema } from "./tables/accounts";
|
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 {
|
import {
|
||||||
MASTER_SETTINGS_KEY,
|
MASTER_SETTINGS_KEY,
|
||||||
Settings,
|
Settings,
|
||||||
SettingsSchema,
|
SettingsSchema,
|
||||||
} from "./tables/settings";
|
} 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
|
// Define types for tables that hold sensitive and non-sensitive data
|
||||||
type SensitiveTables = { accounts: Table<Account> };
|
type SensitiveTables = { accounts: Table<Account> };
|
||||||
type NonsensitiveTables = {
|
type NonsensitiveTables = {
|
||||||
contacts: Table<Contact>;
|
contacts: Table<Contact>;
|
||||||
|
logs: Table<Log>;
|
||||||
settings: Table<Settings>;
|
settings: Table<Settings>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -26,7 +28,11 @@ export const accountsDB = new BaseDexie("TimeSafariAccounts") as SensitiveDexie;
|
|||||||
const SensitiveSchemas = { ...AccountsSchema };
|
const SensitiveSchemas = { ...AccountsSchema };
|
||||||
|
|
||||||
export const db = new BaseDexie("TimeSafari") as NonsensitiveDexie;
|
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.
|
// Manage the encryption key. If not present in localStorage, create and store it.
|
||||||
const secret =
|
const secret =
|
||||||
@@ -38,12 +44,14 @@ encrypted(accountsDB, { secretKey: secret });
|
|||||||
|
|
||||||
// Define the schema for our databases
|
// Define the schema for our databases
|
||||||
accountsDB.version(1).stores(SensitiveSchemas);
|
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
|
// Event handler to initialize the non-sensitive database with default settings
|
||||||
db.on("populate", () => {
|
db.on("populate", () => {
|
||||||
db.settings.add({
|
db.settings.add({
|
||||||
id: MASTER_SETTINGS_KEY,
|
id: MASTER_SETTINGS_KEY,
|
||||||
apiServer: AppString.DEFAULT_ENDORSER_API_SERVER,
|
apiServer: DEFAULT_ENDORSER_API_SERVER,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ export interface Contact {
|
|||||||
registered?: boolean;
|
registered?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ContactsSchema = {
|
export const ContactSchema = {
|
||||||
contacts: "&did, name, publicKeyBase64, registered, seesMe",
|
contacts: "&did, name, publicKeyBase64, registered, seesMe",
|
||||||
};
|
};
|
||||||
|
|||||||
10
src/db/tables/logs.ts
Normal file
10
src/db/tables/logs.ts
Normal file
@@ -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",
|
||||||
|
};
|
||||||
@@ -133,6 +133,9 @@
|
|||||||
Notification status may have changed. Revisit this page to see the
|
Notification status may have changed. Revisit this page to see the
|
||||||
latest setting.
|
latest setting.
|
||||||
</div>
|
</div>
|
||||||
|
<router-link class="px-4 text-sm text-blue-500" to="/help-notifications">
|
||||||
|
Troubleshoot notifications here.
|
||||||
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="text-sm uppercase font-semibold mb-3">Data</h3>
|
<h3 class="text-sm uppercase font-semibold mb-3">Data</h3>
|
||||||
@@ -306,13 +309,6 @@
|
|||||||
Switch Identity
|
Switch Identity
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<button
|
|
||||||
@click="alertWebPushSubscription()"
|
|
||||||
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
|
|
||||||
>
|
|
||||||
Show Subscription from Web Push Server
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="flex py-4">
|
<div class="flex py-4">
|
||||||
<h2 class="text-slate-500 text-sm font-bold mb-2">Claim Server</h2>
|
<h2 class="text-slate-500 text-sm font-bold mb-2">Claim Server</h2>
|
||||||
<input
|
<input
|
||||||
@@ -1048,13 +1044,5 @@ export default class AccountViewView extends Vue {
|
|||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
alertWebPushSubscription() {
|
|
||||||
console.log(
|
|
||||||
"Web push subscription:",
|
|
||||||
JSON.parse(JSON.stringify(this.subscription)), // gives more info than plain console logging
|
|
||||||
);
|
|
||||||
alert(JSON.stringify(this.subscription));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ export default class ContactAmountssView extends Vue {
|
|||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
console.log("Error retrieving settings or gives.", err);
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
@@ -185,7 +186,7 @@ export default class ContactAmountssView extends Vue {
|
|||||||
title: "Error",
|
title: "Error",
|
||||||
text:
|
text:
|
||||||
err.userMessage ||
|
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,
|
-1,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ export default class ContactGiftingView extends Vue {
|
|||||||
this.allContacts = await db.contacts.toArray();
|
this.allContacts = await db.contacts.toArray();
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
console.log("Error retrieving settings & contacts:", err);
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
@@ -152,7 +153,7 @@ export default class ContactGiftingView extends Vue {
|
|||||||
title: "Error",
|
title: "Error",
|
||||||
text:
|
text:
|
||||||
err.message ||
|
err.message ||
|
||||||
"There was an error retrieving the latest sweet, sweet action.",
|
"There was an error retrieving your settings and/or contacts.",
|
||||||
},
|
},
|
||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,10 +22,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p>Here are things to try to get notifications working.</p>
|
<p>Here are ways to get notifications working.</p>
|
||||||
|
|
||||||
<h2 class="text-xl font-semibold">Test</h2>
|
<h2 class="text-xl font-semibold">Test</h2>
|
||||||
<p>Somehow call the service-worker self.showNotification</p>
|
<p>Somehow call the service-worker self.showNotification</p>
|
||||||
|
<button
|
||||||
|
@click="showTestNotification()"
|
||||||
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
|
||||||
|
>
|
||||||
|
See a Test Notification
|
||||||
|
</button>
|
||||||
|
|
||||||
<h2 class="text-xl font-semibold">Check OS-level permissions</h2>
|
<h2 class="text-xl font-semibold">Check OS-level permissions</h2>
|
||||||
<div>
|
<div>
|
||||||
@@ -54,7 +60,10 @@
|
|||||||
<div>Make sure your OS (above) supports it.</div>
|
<div>Make sure your OS (above) supports it.</div>
|
||||||
|
|
||||||
<h3 class="text-lg font-semibold">Mobile Phone - Android</h3>
|
<h3 class="text-lg font-semibold">Mobile Phone - Android</h3>
|
||||||
<div>Chrome requires version 50.</div>
|
<div>
|
||||||
|
Chrome requires version 50. Hold icon, select "App info", and enable
|
||||||
|
notifications.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -66,12 +75,34 @@
|
|||||||
|
|
||||||
<h2 class="text-xl font-semibold">Auto-detection</h2>
|
<h2 class="text-xl font-semibold">Auto-detection</h2>
|
||||||
<p>Show results of auto-detection whether they're turned on</p>
|
<p>Show results of auto-detection whether they're turned on</p>
|
||||||
|
|
||||||
|
<button
|
||||||
|
@click="alertWebPushSubscription()"
|
||||||
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
|
||||||
|
>
|
||||||
|
Show Subscription from Web Push Server
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
@click="sendTestWebPushMessage()"
|
||||||
|
class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
|
||||||
|
>
|
||||||
|
Send Myself a Test Web Push Message
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import axios from "axios";
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
|
||||||
import QuickNav from "@/components/QuickNav.vue";
|
import QuickNav from "@/components/QuickNav.vue";
|
||||||
|
import { db } from "@/db/index";
|
||||||
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
|
import { DEFAULT_PUSH_SERVER } from "@/constants/app";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const Buffer = require("buffer/").Buffer;
|
||||||
|
|
||||||
interface Notification {
|
interface Notification {
|
||||||
group: string;
|
group: string;
|
||||||
@@ -83,5 +114,140 @@ interface Notification {
|
|||||||
@Component({ components: { QuickNav } })
|
@Component({ components: { QuickNav } })
|
||||||
export default class HelpNotificationsView extends Vue {
|
export default class HelpNotificationsView extends Vue {
|
||||||
$notify!: (notification: Notification, timeout?: number) => void;
|
$notify!: (notification: Notification, timeout?: number) => void;
|
||||||
|
|
||||||
|
subscription: PushSubscription | null = null;
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
try {
|
||||||
|
const registration = await navigator.serviceWorker.ready;
|
||||||
|
this.subscription = await registration.pushManager.getSubscription();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Mount error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
alertWebPushSubscription() {
|
||||||
|
console.log(
|
||||||
|
"Web push subscription:",
|
||||||
|
JSON.parse(JSON.stringify(this.subscription)), // gives more info than plain console logging
|
||||||
|
);
|
||||||
|
alert(JSON.stringify(this.subscription));
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendTestWebPushMessage() {
|
||||||
|
if (!this.subscription) {
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Not Subscribed",
|
||||||
|
text: "You must enable notifications before testing the web push.",
|
||||||
|
},
|
||||||
|
-1,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.open();
|
||||||
|
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||||
|
let pushUrl: string = DEFAULT_PUSH_SERVER as string;
|
||||||
|
if (settings?.webPushServer) {
|
||||||
|
pushUrl = settings.webPushServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is shared with the service worker and should be a constant. Look for the same name in additional-scripts.js
|
||||||
|
// 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";
|
||||||
|
|
||||||
|
const auth = Buffer.from(this.subscription.getKey("auth"));
|
||||||
|
const authB64 = auth
|
||||||
|
.toString("base64")
|
||||||
|
.replace(/\+/g, "-")
|
||||||
|
.replace(/\//g, "_")
|
||||||
|
.replace(/=+$/, "");
|
||||||
|
const p256dh = Buffer.from(this.subscription.getKey("p256dh"));
|
||||||
|
const p256dhB64 = p256dh
|
||||||
|
.toString("base64")
|
||||||
|
.replace(/\+/g, "-")
|
||||||
|
.replace(/\//g, "_")
|
||||||
|
.replace(/=+$/, "");
|
||||||
|
const newPayload = {
|
||||||
|
endpoint: this.subscription.endpoint,
|
||||||
|
keys: {
|
||||||
|
auth: authB64,
|
||||||
|
p256dh: p256dhB64,
|
||||||
|
},
|
||||||
|
message: "This is a test message, triggered by you.",
|
||||||
|
title: DIRECT_PUSH_TITLE,
|
||||||
|
};
|
||||||
|
console.log("Sending a test web push message:", newPayload);
|
||||||
|
const payloadStr = JSON.stringify(newPayload);
|
||||||
|
const response = await axios.post(
|
||||||
|
pushUrl + "/web-push/send-test",
|
||||||
|
payloadStr,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Got response from web push server:", response);
|
||||||
|
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "success",
|
||||||
|
title: "Test Web Push Sent",
|
||||||
|
text: "Check your device for the test web push message.",
|
||||||
|
},
|
||||||
|
-1,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Got an error sending test notification:", error);
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Error Sending Test",
|
||||||
|
text: "Got an error sending the test web push notification.",
|
||||||
|
},
|
||||||
|
-1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showTestNotification() {
|
||||||
|
navigator.serviceWorker.ready
|
||||||
|
.then((registration) => {
|
||||||
|
return registration.showNotification("It Worked", {
|
||||||
|
body: "This is your test notification.",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "success",
|
||||||
|
title: "Sent",
|
||||||
|
text: "A notification was triggered, so one should show on your device soon.",
|
||||||
|
},
|
||||||
|
5000,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Got a notification error:", error);
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Failed",
|
||||||
|
text: "Got an error sending a notification.",
|
||||||
|
},
|
||||||
|
-1,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -182,6 +182,15 @@
|
|||||||
different page.
|
different page.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-xl font-semibold">
|
||||||
|
Where do I get help with notifications?
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
<router-link class="text-blue-500" to="/help-notifications"
|
||||||
|
>Here.</router-link
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2 class="text-xl font-semibold">
|
<h2 class="text-xl font-semibold">
|
||||||
How do I access even more functionality?
|
How do I access even more functionality?
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -234,6 +234,7 @@ export default class HomeView extends Vue {
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
console.log("Error retrieving settings and/or feed.", err);
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
@@ -241,7 +242,7 @@ export default class HomeView extends Vue {
|
|||||||
title: "Error",
|
title: "Error",
|
||||||
text:
|
text:
|
||||||
err.userMessage ||
|
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,
|
-1,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,67 +5,103 @@ importScripts(
|
|||||||
);
|
);
|
||||||
|
|
||||||
self.addEventListener("install", (event) => {
|
self.addEventListener("install", (event) => {
|
||||||
console.log(new Date().toISOString(), "Installing service worker:", event);
|
console.log("Service worker got install. Importing scripts...", event);
|
||||||
importScripts(
|
importScripts(
|
||||||
"safari-notifications.js",
|
"safari-notifications.js",
|
||||||
"nacl.js",
|
"nacl.js",
|
||||||
"noble-curves.js",
|
"noble-curves.js",
|
||||||
"noble-hashes.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) {
|
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(
|
event.waitUntil(
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
let payload;
|
let payload;
|
||||||
if (event.data) {
|
if (text) {
|
||||||
payload = JSON.parse(event.data.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) {
|
if (message) {
|
||||||
const title = payload && payload.title ? payload.title : "Message";
|
|
||||||
const options = {
|
const options = {
|
||||||
body: message,
|
body: message,
|
||||||
icon: payload ? payload.icon : "icon.png",
|
icon: payload ? payload.icon : "icon.png",
|
||||||
badge: payload ? payload.badge : "badge.png",
|
badge: payload ? payload.badge : "badge.png",
|
||||||
};
|
};
|
||||||
await self.registration.showNotification(title, options);
|
await self.registration.showNotification(title, options);
|
||||||
console.log(new Date().toISOString(), "Notified user:", options);
|
logConsoleAndDb("Notified user:", options);
|
||||||
} else {
|
} else {
|
||||||
console.log(new Date().toISOString(), "No notification message.");
|
logConsoleAndDb("No notification message.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(new Date().toISOString(), "Error with push event", event, " - ", error);
|
logConsoleAndDb("Error with push event", event, error);
|
||||||
}
|
}
|
||||||
})(),
|
})(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener("message", (event) => {
|
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") {
|
if (event.data && event.data.type === "SEND_LOCAL_DATA") {
|
||||||
self.secret = event.data.data;
|
self.secret = event.data.data;
|
||||||
event.ports[0].postMessage({ success: true });
|
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) => {
|
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
|
// see https://developer.mozilla.org/en-US/docs/Web/API/Clients/claim
|
||||||
// and https://web.dev/articles/service-worker-lifecycle#clientsclaim
|
// and https://web.dev/articles/service-worker-lifecycle#clientsclaim
|
||||||
event.waitUntil(clients.claim());
|
event.waitUntil(clients.claim());
|
||||||
console.log(new Date().toISOString(), "Service worker activated.");
|
logConsoleAndDb("Service worker is activated.");
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener("fetch", (event) => {
|
self.addEventListener("fetch", (event) => {
|
||||||
console.log(new Date().toISOString(), "Got fetch event:", event);
|
logConsoleAndDb("Service worker got fetch event.", event);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener("error", (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("Message:", event.message);
|
||||||
console.error("File:", event.filename);
|
console.error("File:", event.filename);
|
||||||
console.error("Line:", event.lineno);
|
console.error("Line:", event.lineno);
|
||||||
|
|||||||
@@ -395,12 +395,42 @@ async function setMostRecentNotified(id) {
|
|||||||
data["lastNotifiedClaimId"] = id;
|
data["lastNotifiedClaimId"] = id;
|
||||||
await updateRecord(store, data);
|
await updateRecord(store, data);
|
||||||
} else {
|
} else {
|
||||||
console.error("IndexedDB settings record not found.");
|
console.error(
|
||||||
|
"safari-notifications setMostRecentNotified IndexedDB settings record not found",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction.oncomplete = () => db.close();
|
transaction.oncomplete = () => db.close();
|
||||||
} catch (error) {
|
} 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) {
|
function updateRecord(store, data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const request = store.put(data);
|
const request = store.put(data);
|
||||||
@@ -430,20 +461,23 @@ function updateRecord(store, data) {
|
|||||||
|
|
||||||
async function fetchAllAccounts() {
|
async function fetchAllAccounts() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let openRequest = indexedDB.open("TimeSafariAccounts");
|
const openRequest = indexedDB.open("TimeSafariAccounts");
|
||||||
|
|
||||||
openRequest.onupgradeneeded = function (event) {
|
openRequest.onupgradeneeded = function (event) {
|
||||||
let db = event.target.result;
|
const db = event.target.result;
|
||||||
if (!db.objectStoreNames.contains("accounts")) {
|
if (!db.objectStoreNames.contains("accounts")) {
|
||||||
db.createObjectStore("accounts", { keyPath: "id" });
|
db.createObjectStore("accounts", { keyPath: "id" });
|
||||||
}
|
}
|
||||||
|
if (!db.objectStoreNames.contains("worker_log")) {
|
||||||
|
db.createObjectStore("worker_log");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
openRequest.onsuccess = function (event) {
|
openRequest.onsuccess = function (event) {
|
||||||
let db = event.target.result;
|
const db = event.target.result;
|
||||||
let transaction = db.transaction("accounts", "readonly");
|
const transaction = db.transaction("accounts", "readonly");
|
||||||
let objectStore = transaction.objectStore("accounts");
|
const objectStore = transaction.objectStore("accounts");
|
||||||
let getAllRequest = objectStore.getAll();
|
const getAllRequest = objectStore.getAll();
|
||||||
|
|
||||||
getAllRequest.onsuccess = function () {
|
getAllRequest.onsuccess = function () {
|
||||||
resolve(getAllRequest.result);
|
resolve(getAllRequest.result);
|
||||||
@@ -460,76 +494,74 @@ async function fetchAllAccounts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getNotificationCount() {
|
async function getNotificationCount() {
|
||||||
let secret = null;
|
|
||||||
let accounts = [];
|
let accounts = [];
|
||||||
let result = null;
|
let result = null;
|
||||||
if ("secret" in self) {
|
// 1 is our master settings ID; see MASTER_SETTINGS_KEY
|
||||||
secret = self.secret;
|
const settings = await getSettingById(1);
|
||||||
const secretUint8Array = self.decodeBase64(secret);
|
let lastNotifiedClaimId = null;
|
||||||
// 1 is our master settings ID; see MASTER_SETTINGS_KEY
|
if ("lastNotifiedClaimId" in settings) {
|
||||||
const settings = await getSettingById(1);
|
lastNotifiedClaimId = settings["lastNotifiedClaimId"];
|
||||||
let lastNotifiedClaimId = null;
|
}
|
||||||
if ("lastNotifiedClaimId" in settings) {
|
const activeDid = settings["activeDid"];
|
||||||
lastNotifiedClaimId = settings["lastNotifiedClaimId"];
|
accounts = await fetchAllAccounts();
|
||||||
}
|
let activeAccount = null;
|
||||||
const activeDid = settings["activeDid"];
|
for (let i = 0; i < accounts.length; i++) {
|
||||||
accounts = await fetchAllAccounts();
|
if (accounts[i]["did"] == activeDid) {
|
||||||
let did = null;
|
activeAccount = accounts[i];
|
||||||
for (var i = 0; i < accounts.length; i++) {
|
break;
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user