upd: web push

Deskripsi:
- install package
- table database
- nb : masih blm bisa

No Issues
This commit is contained in:
amel
2024-09-18 11:48:57 +08:00
parent 6868085bb1
commit bc0f27a4fc
16 changed files with 581 additions and 3 deletions

15
src/lib/prisma.ts Normal file
View File

@@ -0,0 +1,15 @@
import { PrismaClient } from '@prisma/client'
const prismaClientSingleton = () => {
return new PrismaClient()
}
declare const globalThis: {
prismaGlobal: ReturnType<typeof prismaClientSingleton>;
} & typeof global;
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton()
export default prisma
if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma

View File

@@ -0,0 +1,13 @@
export const urlB64ToUint8Array = (base64String: string) => {
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);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};

46
src/lib/usePWAInstall.ts Normal file
View File

@@ -0,0 +1,46 @@
import { useState, useEffect } from "react";
export function usePWAInstall() {
const [deferredPrompt, setDeferredPrompt] = useState<any>(null);
const [isAppInstalled, setIsAppInstalled] = useState(false);
useEffect(() => {
const beforeInstallHandler = (e: any) => {
e.preventDefault();
setDeferredPrompt(e);
};
const appInstalledHandler = () => {
setIsAppInstalled(true);
};
window.addEventListener("beforeinstallprompt", beforeInstallHandler);
window.addEventListener("appinstalled", appInstalledHandler);
return () => {
window.removeEventListener("beforeinstallprompt", beforeInstallHandler);
window.removeEventListener("appinstalled", appInstalledHandler);
};
}, []);
const handleInstallClick = () => {
if (deferredPrompt) {
deferredPrompt.prompt();
deferredPrompt.userChoice.then((choiceResult: any) => {
if (choiceResult.outcome === "accepted") {
console.log("User accepted the install prompt");
} else {
console.log("User dismissed the install prompt");
}
setDeferredPrompt(null);
});
}
};
return {
deferredPrompt,
isAppInstalled,
handleInstallClick,
};
}

View File

@@ -0,0 +1,75 @@
import { useState, useEffect } from "react";
import { urlB64ToUint8Array } from "./urlB64ToUint8Array";
export function usePushNotifications(publicKey: string) {
const [isSupported, setIsSupported] = useState(false);
const [subscription, setSubscription] = useState<PushSubscription | null>(
null
);
useEffect(() => {
if ("serviceWorker" in navigator && "PushManager" in window) {
setIsSupported(true);
registerServiceWorker();
}
}, []);
const registerServiceWorker = async () => {
try {
const registration = await navigator.serviceWorker.register(
"/wibu_worker.js",
{
scope: "/",
updateViaCache: "none"
}
);
const sub = await registration.pushManager.getSubscription();
if (sub) {
setSubscription(sub);
}
} catch (error) {
console.error("Service Worker registration failed:", error);
}
};
const subscribeToPush = async () => {
try {
const registration = await navigator.serviceWorker.ready;
const sub = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlB64ToUint8Array(publicKey)
});
const res = await fetch("/api/set-subscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sub: sub.toJSON() })
});
if (res.ok) {
setSubscription(sub);
}
} catch (error) {
console.error("Subscription error:", error);
}
};
const unsubscribeFromPush = async () => {
if (subscription) {
await subscription.unsubscribe();
setSubscription(null);
await fetch("/api/unsubscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sub: subscription.toJSON() })
});
}
};
return {
isSupported,
subscription,
subscribeToPush,
unsubscribeFromPush
};
}