diff --git a/app/pages/_document.tsx b/app/pages/_document.tsx index 8b825fa..8217839 100644 --- a/app/pages/_document.tsx +++ b/app/pages/_document.tsx @@ -1,4 +1,4 @@ -import { Document, Html, DocumentHead, Main, BlitzScript /*DocumentContext*/ } from "blitz"; +import { Document, Html, DocumentHead, Main, BlitzScript, Head /*DocumentContext*/ } from "blitz"; class MyDocument extends Document { // Only uncomment if you need to customize this behaviour @@ -11,6 +11,19 @@ class MyDocument extends Document { return ( + + + + + + + + + + + + +
diff --git a/app/pages/_offline.tsx b/app/pages/_offline.tsx new file mode 100644 index 0000000..7233ac5 --- /dev/null +++ b/app/pages/_offline.tsx @@ -0,0 +1,22 @@ +import { useRouter } from "blitz"; + +import Layout from "../core/layouts/layout"; + +export default function Offline() { + const router = useRouter(); + return ( + +

+ Oops, looks like you went offline. +

+

+ Once you're back online,{" "} + +

+
+ ); +} diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest new file mode 100644 index 0000000..c7d10c7 --- /dev/null +++ b/public/manifest.webmanifest @@ -0,0 +1,23 @@ +{ + "name": "Shellphone: Your Personal Virtual Phone", + "short_name": "Shellphone", + "lang": "en-US", + "start_url": "/", + "scope": "/", + "shortcuts": [ + { + "name": "Shellphone Messages", + "short_name": "Messages", + "url": "/messages" + }, + { + "name": "Shellphone Calls", + "short_name": "Calls", + "url": "/calls" + } + ], + "display": "standalone", + "orientation": "portrait", + "theme_color": "#663399", + "background_color": "#F9FAFB" +} diff --git a/tsconfig.json b/tsconfig.json index e4883fa..41d73ea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": ["dom", "dom.iterable", "esnext", "webworker"], "baseUrl": "./", "allowJs": true, "skipLibCheck": true, diff --git a/worker/index.js b/worker/index.js deleted file mode 100644 index 50bd606..0000000 --- a/worker/index.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; - -self.addEventListener("push", function (event) { - console.log("event.data.text()", event.data.text()); - const data = JSON.parse(event.data.text()); - event.waitUntil( - registration.showNotification(data.title, { - body: data.message, - icon: "/icons/android-chrome-192x192.png", - }), - ); -}); - -self.addEventListener("notificationclick", function (event) { - event.notification.close(); - event.waitUntil( - clients.matchAll({ type: "window", includeUncontrolled: true }).then(function (clientList) { - if (clientList.length > 0) { - let client = clientList[0]; - for (let i = 0; i < clientList.length; i++) { - if (clientList[i].focused) { - client = clientList[i]; - } - } - return client.focus(); - } - return clients.openWindow("/"); - }), - ); -}); - -// self.addEventListener('pushsubscriptionchange', function(event) { -// event.waitUntil( -// Promise.all([ -// Promise.resolve(event.oldSubscription ? deleteSubscription(event.oldSubscription) : true), -// Promise.resolve(event.newSubscription ? event.newSubscription : subscribePush(registration)) -// .then(function(sub) { return saveSubscription(sub) }) -// ]) -// ) -// }) diff --git a/worker/notifications.ts b/worker/notifications.ts new file mode 100644 index 0000000..0d42bf1 --- /dev/null +++ b/worker/notifications.ts @@ -0,0 +1,53 @@ +import { Routes } from "blitz"; + +const worker = self as unknown as ServiceWorkerGlobalScope & typeof globalThis; + +worker.addEventListener("push", function (event) { + if (!event.data) { + return; + } + + console.log("event.data.text()", event.data.text()); + const data = JSON.parse(event.data.text()); + event.waitUntil( + worker.registration.showNotification(data.title, { + body: data.message, + icon: "/icons/android-chrome-192x192.png", + actions: [ + { title: "Open", action: "open" }, + { title: "Mark as read", action: "mark-as-read" }, + ], + }), + ); +}); + +worker.addEventListener("notificationclick", (event) => { + event.notification.close(); + event.waitUntil( + worker.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => { + if (!event.notification.data) { + return; + } + + switch (event.action) { + case "mark-as-read": + // TODO + return; + case "open": + default: { + const data = JSON.parse(event.notification.data.text()); + const route = Routes.ConversationPage({ recipient: data.recipient }); + const url = `${route.pathname}${route.query}`; + + if (clientList.length > 0) { + const client = clientList.find((client) => client.focused) ?? clientList[0]!; + + client.navigate(url); + return client.focus(); + } + return worker.clients.openWindow(url); + } + } + }), + ); +});