Skip to content

Commit

Permalink
Merge pull request #169 from dtutimes/dev
Browse files Browse the repository at this point in the history
notification impl for teams platform
  • Loading branch information
dvishal485 authored Aug 26, 2024
2 parents c4add14 + acde654 commit f0b6bd9
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 2 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# This is a basic workflow to help you get started with Actions

name: Deploy on push

# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the "main" branch
push:
branches: [ "dev" ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: self-hosted

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v4

- name: Get to work dir
run: cd $GITHUB_WORKSPACE

- name: Install dependencies
run: npm i

- name: Run build task
run: npm run build

- name: Deploy to web
run: rsync -avz --delete dist/ ~/nix_frontend_dev
98 changes: 98 additions & 0 deletions public/notification-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/// <reference lib="webworker" />

/** @type {ServiceWorkerGlobalScope} */
const service_worker = self;

function urlB64ToUint8Array(base64String) {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");

const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
const outputData = outputArray.map((output, index) =>
rawData.charCodeAt(index),
);

return outputData;
}

service_worker.addEventListener("activate", async () => {
console.log("Hello from service worker");
try {
/** @type {PushSubscriptionOptions} */
const options = {
applicationServerKey:
"BCOsRaxpJeR0KyIPIg1rHx3pUtWVsGDGOxH65dDkqyU5ycF-CjPJxuqiXF4M0LpUMG_rk_YxSZX34uHbrV5umJQ",
userVisibleOnly: true,
};
console.log("Hello from service worker second time");

const subscription =
await service_worker.registration.pushManager.subscribe(options);

let subscribe_json = subscription.toJSON();
console.log("Subscription", subscribe_json);

const body = JSON.stringify(subscribe_json);

await fetch("https://team.dtutimes.com/api/v1/notification/subscribe", {
method: "POST",
body,
headers: {
"Content-Type": "application/json",
},
})
.then((res) => res.text())
.then(console.log)
.catch(console.error);
} catch (err) {
console.log("Error", err);
}
});

service_worker.addEventListener("push", function (event) {
const data = event.data.json();
if (data) {
pushNotification(
data.title,
{
icon: "https://dtutimes.com/favicon.ico",
body: data.body,
},
service_worker.registration,
);
} else {
console.log("No data!");
}
});

/**
* @param title {string}
* @param options {NotificationOptions}
* @param swRegistration {ServiceWorkerRegistration}
*/
const pushNotification = (title, options, swRegistration) => {
console.log("Push Notification", title, options);
swRegistration.showNotification(title, options);
};

service_worker.addEventListener("notificationclick", (event) => {
event.notification.close();
event.waitUntil(
service_worker.clients
.matchAll({
type: "window",
})
.then((clientList) => {
for (const client of clientList) {
if (
client.url === "https://team.dtutimes.com/notification" &&
"focus" in client
)
return client.focus();
}
if (service_worker.clients.openWindow)
return service_worker.clients.openWindow("/notification");
}),
);
});
33 changes: 32 additions & 1 deletion src/pages/Notification/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { ErrorContext } from "@/contexts/error";
import API from "@/services/API";
import { INotification } from "@/types/notification";
import React, { useEffect, useState } from "react";
import {
setup_notification,
disable_notification,
setup_present,
} from "./notification-engine";

interface NotificationCardProps {
notif: INotification;
Expand Down Expand Up @@ -56,8 +61,11 @@ export default function NotificationPage() {
const { setError } = React.useContext(ErrorContext);

const [notifications, setNotifs] = useState<INotification[]>(null);
const [status, setStatus] = useState<boolean>(null);

useEffect(() => {
setup_present().then(setStatus);

const notifications = "/notification";
API.get(notifications)
.then((response) => {
Expand All @@ -69,7 +77,7 @@ export default function NotificationPage() {
});
}, []);

if (notifications === null)
if (notifications === null || status === null)
return (
<div className="flex w-full h-screen justify-center items-center">
<Spinner />
Expand All @@ -79,6 +87,29 @@ export default function NotificationPage() {
return (
<div className="max-w-4xl mx-auto py-12">
<h1>Latest Updates</h1>
<div>
{status ? (
<button
className="ml-2"
onClick={() => {
setStatus(null);
disable_notification().then(setup_present).then(setStatus);
}}
>
Disable Notifications
</button>
) : (
<button
className="mr-2"
onClick={() => {
setStatus(null);
setup_notification().then(setup_present).then(setStatus);
}}
>
Get alerts on Notification!
</button>
)}
</div>
{notifications.map((notif) => {
return <NotificationCard key={notif._id} notif={notif} />;
})}
Expand Down
61 changes: 61 additions & 0 deletions src/pages/Notification/notification-engine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
class Notification {
static notify = window.Notification;

public static async requestPermission() {
if (!Notification.isSupported()) {
throw new Error("Notification API is not supported");
}
const permission = await Notification.notify.requestPermission();
console.log("Notification API permission", permission);
return permission;
}

public static isSupported() {
return Notification.notify !== undefined;
}
}

class BgService {
static bg = window.navigator;

public static isSupported() {
return BgService.bg !== undefined;
}

public static async registerServiceWorker(swPath: string) {
const registration = await BgService.bg.serviceWorker.register(swPath);
console.log("Service worker registered", registration);
return registration;
}

public static async unregisterServiceWorker() {
const registrations = await BgService.getRegistrations();
registrations.forEach((registration) => registration.unregister());
return registrations;
}

public static async getRegistrations() {
const registration = await BgService.bg.serviceWorker.getRegistrations();
console.log("Service worker registered", registration);
return registration;
}
}

export async function setup_notification() {
Notification.requestPermission();

const worker = await BgService.registerServiceWorker(
"https://team.dtutimes.com/notification-service.js",
);

console.log("Service worker registered", worker);
}

export async function disable_notification() {
await BgService.unregisterServiceWorker();
}

export async function setup_present() {
const services = await BgService.getRegistrations();
return services.length > 0;
}
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
}
},
"include": [
"src"
"src",
"public/notification-service.js"
],
"references": [
{
Expand Down

0 comments on commit f0b6bd9

Please sign in to comment.