-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #40 from Kashyap1ankit/fix/hero
added notification functionality
- Loading branch information
Showing
124 changed files
with
566 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
"use server"; | ||
|
||
import prisma from "@/db"; | ||
import { CheckUser } from "./users/checkUser"; | ||
import webpush from "web-push"; | ||
|
||
const vapidKeys = { | ||
publicKey: process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY, | ||
privateKey: process.env.VAPID_PRIVATE_KEY, | ||
}; | ||
|
||
export async function addNotificationSubscription(subscription: string) { | ||
try { | ||
const response = await CheckUser(); | ||
|
||
if (response.status !== 200 || !response.userId) | ||
throw new Error(response.message); | ||
|
||
await prisma.notification.create({ | ||
data: { | ||
userId: response.userId, | ||
subscription, | ||
}, | ||
}); | ||
|
||
return { | ||
status: 200, | ||
message: "Created subscription", | ||
}; | ||
} catch (error) { | ||
return { | ||
status: 400, | ||
message: (error as Error).message, | ||
}; | ||
} | ||
} | ||
|
||
export async function removeNotificationSubscription() { | ||
try { | ||
const response = await CheckUser(); | ||
|
||
if (response.status !== 200 || !response.userId) | ||
throw new Error(response.message); | ||
|
||
const findPost = await prisma.notification.findFirst({ | ||
where: { | ||
userId: response.userId, | ||
}, | ||
select: { | ||
id: true, | ||
}, | ||
}); | ||
|
||
if (!findPost) throw new Error("No such Notification subscription"); | ||
|
||
await prisma.notification.delete({ | ||
where: { | ||
id: findPost?.id, | ||
userId: response.userId, | ||
}, | ||
}); | ||
|
||
return { | ||
status: 200, | ||
message: "Deleted subscription", | ||
}; | ||
} catch (error) { | ||
return { | ||
status: 400, | ||
message: (error as Error).message, | ||
}; | ||
} | ||
} | ||
|
||
export const getUserSubscription = async () => { | ||
const response = await CheckUser(); | ||
|
||
if (response.status !== 200 || !response.userId) | ||
throw new Error(response.message); | ||
|
||
try { | ||
const res = await prisma.notification.findFirst({ | ||
where: { | ||
userId: response.userId, | ||
}, | ||
}); | ||
return { | ||
status: 200, | ||
message: "Subscription Retrieved", | ||
data: res, | ||
}; | ||
} catch (error) { | ||
return { | ||
status: 400, | ||
message: (error as Error).message, | ||
data: null, | ||
}; | ||
} | ||
}; | ||
|
||
export async function SendNotification(company_name: string) { | ||
try { | ||
const response = await CheckUser(); | ||
|
||
if (response.status !== 200 || !response.userId) | ||
throw new Error(response.message); | ||
|
||
const sub = await prisma.notification.findFirst({ | ||
where: { | ||
userId: response.userId, | ||
}, | ||
}); | ||
|
||
if (!sub) throw new Error("No subscription found"); | ||
|
||
webpush.setVapidDetails( | ||
"mailto:[email protected]", | ||
vapidKeys.publicKey as string, | ||
vapidKeys.privateKey as string, | ||
); | ||
|
||
const allSubscribedUser = await prisma.notification.findMany({ | ||
where: { | ||
userId: { | ||
not: response.userId, | ||
}, | ||
}, | ||
}); | ||
|
||
if (allSubscribedUser.length < 0) | ||
throw new Error("No user to send notification"); | ||
|
||
for (const user of allSubscribedUser) { | ||
await webpush.sendNotification( | ||
JSON.parse(user.subscription), | ||
JSON.stringify({ | ||
message: "New Job Alert", | ||
body: `🔥Vacany at ${company_name}...Apply Fast`, | ||
}), | ||
); | ||
} | ||
|
||
return { | ||
status: 200, | ||
message: "Notification Sent", | ||
}; | ||
} catch (error) { | ||
return { | ||
status: 400, | ||
message: (error as Error).message, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
"use client"; | ||
import { | ||
addNotificationSubscription, | ||
getUserSubscription, | ||
removeNotificationSubscription, | ||
} from "@/app/actions/notification"; | ||
|
||
import { useEffect, useState } from "react"; | ||
import { IoNotifications, IoNotificationsOff } from "react-icons/io5"; | ||
import { toast } from "sonner"; | ||
|
||
function urlB64ToUint8Array(base64String: string): Uint8Array { | ||
const padding = "=".repeat((4 - (base64String.length % 4)) % 4); | ||
const base64 = (base64String + padding) | ||
// eslint-disable-next-line no-useless-escape | ||
.replace(/\-/g, "+") | ||
.replace(/_/g, "/"); | ||
const rawData = window.atob(base64); | ||
const outputArray = new Uint8Array(rawData.length); | ||
for (let i = 0; i < rawData.length; ++i) { | ||
outputArray[i] = rawData.charCodeAt(i); | ||
} | ||
return outputArray; | ||
} | ||
export default function GetNotification() { | ||
const [notificationPermission, setNotificationPermission] = useState< | ||
"denied" | "granted" | "default" | ||
>(window.Notification.permission); | ||
|
||
const [refetch, setRefetch] = useState<boolean>(false); | ||
|
||
const [userSub, setUserSub] = useState<{ | ||
id: string; | ||
userId: string; | ||
subscription: string; | ||
} | null>(null); | ||
|
||
async function showNotificationClick() { | ||
if ("serviceWorker" in navigator && "Notification" in window) { | ||
try { | ||
await window.Notification.requestPermission(); | ||
if (window.Notification.permission === "granted") { | ||
setNotificationPermission("granted"); | ||
await verifyRegistration(); | ||
} else { | ||
setNotificationPermission(window.Notification.permission); | ||
toast.info( | ||
"Permission Not granted . Go to settings & allow notifications", | ||
); | ||
} | ||
} catch (error) { | ||
toast((error as Error).message); | ||
} finally { | ||
setRefetch((prev) => !prev); | ||
} | ||
} | ||
} | ||
|
||
async function verifyRegistration() { | ||
try { | ||
if ("serviceWorker" in navigator) { | ||
let registration = await navigator.serviceWorker.getRegistration(); | ||
|
||
if (!registration) { | ||
registration = await navigator.serviceWorker.register("/sw.js", { | ||
scope: "/", | ||
updateViaCache: "none", | ||
}); | ||
} | ||
|
||
await subscribeUser(registration); | ||
} | ||
} catch (error) { | ||
toast((error as Error).message); | ||
} | ||
} | ||
|
||
async function subscribeUser(registration: ServiceWorkerRegistration) { | ||
try { | ||
console.log(registration); | ||
if (!("Notification" in window)) { | ||
throw new Error("This browser does not support notifications"); | ||
} | ||
|
||
const permission = await Notification.requestPermission(); | ||
if (permission !== "granted") { | ||
throw new Error("Notification permission denied"); | ||
} | ||
|
||
if (!("PushManager" in window)) { | ||
throw new Error("Push messaging is not supported"); | ||
} | ||
|
||
const appServeKey = urlB64ToUint8Array( | ||
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!, | ||
); | ||
|
||
const option = { | ||
userVisibleOnly: true, | ||
applicationServerKey: appServeKey, | ||
}; | ||
|
||
const subcriptionObject = | ||
await registration.pushManager.subscribe(option); | ||
const response = await addNotificationSubscription( | ||
JSON.stringify(subcriptionObject), | ||
); | ||
|
||
if (response.status !== 200) throw new Error(response.message); | ||
|
||
toast.success("Notification Is enabled", { | ||
style: { | ||
backgroundColor: "#65a30d", | ||
color: "white", | ||
borderColor: "#65a30d", | ||
}, | ||
}); | ||
} catch (error) { | ||
toast.error((error as Error).message, { | ||
style: { | ||
backgroundColor: "red", | ||
color: "white", | ||
borderColor: "#334155", | ||
}, | ||
}); | ||
} | ||
} | ||
|
||
async function deleteNotificationPermission() { | ||
if ("serviceWorker" in navigator && "Notification" in window) { | ||
try { | ||
await removeNotificationSubscription(); | ||
setNotificationPermission("denied"); | ||
toast.warning( | ||
"Notifications have been disabled. To stop browser notifications completely, please update your notification settings in the browser", | ||
{ | ||
style: { | ||
backgroundColor: "#334155", | ||
color: "white", | ||
borderColor: "#334155", | ||
}, | ||
}, | ||
); | ||
} catch (error) { | ||
toast((error as Error).message); | ||
} | ||
} | ||
} | ||
|
||
useEffect(() => { | ||
setNotificationPermission(window.Notification.permission); | ||
}, []); | ||
|
||
useEffect(() => { | ||
const getUserSubs = async () => { | ||
const response = await getUserSubscription(); | ||
|
||
if (response.status !== 200) { | ||
setUserSub(null); | ||
return; | ||
} | ||
setUserSub(response.data); | ||
}; | ||
getUserSubs(); | ||
}, [refetch]); | ||
|
||
return ( | ||
<div> | ||
{notificationPermission === "granted" && userSub?.id ? ( | ||
<IoNotifications | ||
className="size-6 cursor-pointer text-white" | ||
onClick={deleteNotificationPermission} | ||
/> | ||
) : ( | ||
<IoNotificationsOff | ||
className="size-6 cursor-pointer text-white" | ||
onClick={showNotificationClick} | ||
/> | ||
)} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.