This commit is contained in:
client perkafean
2024-09-28 02:53:22 +00:00
parent 69b3fe4347
commit 3869b4d589
8 changed files with 137 additions and 101 deletions

28
public/service-worker.js Normal file
View File

@@ -0,0 +1,28 @@
// public/firebase-messaging-sw.js
self.addEventListener('push', function(event) {
const data = event.data.json();
const options = {
body: data.body,
icon: 'https://i.pinimg.com/originals/3c/44/67/3c446785968272f79aaac7ace5ded0fe.jpg', // Path to your icon
badge: 'badge.png', // Path to your badge
data: { // Add data to the notification
cafeId: data.cafeId,
transactionId: data.transactionId,
}
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
self.addEventListener('notificationclick', (event) => {
event.notification.close(); // Close the notification
const { cafeId, transactionId } = event.notification.data; // Get the notification data
// Open the URL with the cafeId and transactionId
event.waitUntil(
clients.openWindow(`https://r7l7gs-3000.csb.app/${cafeId}modal=new_transaction&transactionId=${transactionId}`)
);
});

View File

@@ -1,12 +1,9 @@
if ("serviceWorker" in navigator) { if ('serviceWorker' in navigator) {
window.addEventListener("load", () => { navigator.serviceWorker.register('/service-worker.js')
navigator.serviceWorker .then((registration) => {
.register("/service-worker.js") console.log('Service Worker registered with scope:', registration.scope);
.then((registration) => { })
console.log( .catch((error) => {
"Service Worker registered with scope:", console.error('Service Worker registration failed:', error);
registration.scope });
);
});
});
} }

View File

@@ -9,8 +9,8 @@ import {
useLocation, useLocation,
} from "react-router-dom"; } from "react-router-dom";
import socket from "./services/socketService"; import socket from "./services/socketService";
import { SubscriptionService } from "./services/subscriptionService";
import { NotificationService } from "./services/notificationService"; import API_BASE_URL from "./config.js";
import Dashboard from "./pages/Dashboard"; import Dashboard from "./pages/Dashboard";
import ScanMeja from "./pages/ScanMeja"; import ScanMeja from "./pages/ScanMeja";
@@ -138,20 +138,74 @@ function App() {
setGuestSides(sessionLeft.guestSideList); setGuestSides(sessionLeft.guestSideList);
}; };
const checkNotifications = async (userId) => { // const checkNotifications = async (userId) => {
try { // try {
const permissionGranted = // const permissionGranted =
await NotificationService.requestNotificationPermission(setModal); // await NotificationService.requestNotificationPermission(setModal);
if (permissionGranted) { // if (permissionGranted) {
await SubscriptionService.subscribeUserToNotifications(userId); // await SubscriptionService.subscribeUserToNotifications(userId);
} else { // } else {
setModal("blocked_notification"); // setModal("blocked_notification");
console.log("req notif"); // console.log("req notif");
} // }
} catch (error) { // } catch (error) {
console.error("Error handling notifications:", error); // console.error("Error handling notifications:", error);
// }
// };
useEffect(() => {
const getVapidKey = async () => {
const response = await fetch(`${API_BASE_URL}/vapid-key`);
const { publicVapidKey } = await response.json();
return publicVapidKey;
};
const askNotificationPermission = async () => {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
console.log('Notification permission granted.');
const publicVapidKey = await getVapidKey();
await subscribeUser(publicVapidKey);
} else {
console.error('Notification permission denied.');
}
};
// Utility function to convert base64 to Uint8Array
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
return Uint8Array.from([...rawData].map(char => char.charCodeAt(0)));
}
const subscribeUser = async (publicVapidKey) => {
try {
const registration = await navigator.serviceWorker.register('/service-worker.js');
console.log('Service Worker registered with scope:', registration.scope);
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicVapidKey),
});
await fetch(`${API_BASE_URL}/subscribe`, {
method: 'POST',
body: JSON.stringify(subscription),
headers: {
'Content-Type': 'application/json',
},
});
} catch (error) {
console.error('Subscription failed:', error);
}
};
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
await askNotificationPermission();
});
} }
}; }, []);
useEffect(() => { useEffect(() => {
if (socket == null) return; if (socket == null) return;
@@ -226,7 +280,7 @@ function App() {
console.log("getting guest side"); console.log("getting guest side");
setDeviceType("clerk"); setDeviceType("clerk");
checkNotifications(data.data.user.userId); // checkNotifications(data.data.user.userId);
} else { } else {
setDeviceType("guestDevice"); setDeviceType("guestDevice");
} }
@@ -278,19 +332,24 @@ function App() {
// Function to open the modal // Function to open the modal
const setModal = (content, params = {}) => { const setModal = (content, params = {}) => {
// Prepare query parameters // Prepare query parameters
const queryParams = new URLSearchParams({ const queryParams = new URLSearchParams(location.search);
modal: content,
...params, // Spread additional parameters // Update the modal and any additional params
}).toString(); queryParams.set("modal", content);
Object.entries(params).forEach(([key, value]) => {
queryParams.set(key, value);
});
// Update URL with new parameters // Update URL with new parameters
navigate(`?${queryParams}`, { replace: true }); navigate(`?${queryParams.toString()}`, { replace: true });
// Prevent scrolling when modal is open // Prevent scrolling when modal is open
document.body.style.overflow = "hidden"; document.body.style.overflow = "hidden";
setIsModalOpen(true); setIsModalOpen(true);
setModalContent(content); setModalContent(content);
}; };
// Function to close the modal // Function to close the modal
const closeModal = () => { const closeModal = () => {

View File

@@ -37,6 +37,7 @@ const Modal = ({ shop, isOpen, onClose, modalContent }) => {
× ×
</button> </button>
{modalContent === "req_notification" && <NotificationBlocked />} {modalContent === "req_notification" && <NotificationBlocked />}
{modalContent === "blocked_notification" && <NotificationBlocked />}
{modalContent === "edit_tables" && <TablesPage shop={shop} />} {modalContent === "edit_tables" && <TablesPage shop={shop} />}
{modalContent === "new_transaction" && ( {modalContent === "new_transaction" && (
<Transaction propsShopId={shop.cafeId} /> <Transaction propsShopId={shop.cafeId} />

View File

@@ -1,7 +1,7 @@
// src/CafePage.js // src/CafePage.js
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useParams, useSearchParams, useNavigate } from "react-router-dom"; import { useParams, useSearchParams, useNavigate, useLocation } from "react-router-dom";
import "../App.css"; import "../App.css";
import SearchInput from "../components/SearchInput"; import SearchInput from "../components/SearchInput";
@@ -33,6 +33,7 @@ function CafePage({
removeConnectedGuestSides, removeConnectedGuestSides,
setModal, setModal,
}) { }) {
const location = useLocation();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const token = searchParams.get("token"); const token = searchParams.get("token");
const { shopId, tableCode } = useParams(); const { shopId, tableCode } = useParams();
@@ -50,10 +51,15 @@ function CafePage({
const [filterId, setFilterId] = useState(0); const [filterId, setFilterId] = useState(0);
useEffect(() => { useEffect(() => {
if (user.cafeId != null && user.cafeId != shopId) { if (user.cafeId != null && user.cafeId !== shopId) {
navigate("/" + user.cafeId); // Preserve existing query parameters
const currentParams = new URLSearchParams(location.search).toString();
// Navigate to the new cafeId while keeping existing params
navigate(`/${user.cafeId}?${currentParams}`, { replace: true });
} }
}, [user]); }, [user, shopId]);
useEffect(() => { useEffect(() => {
if (token) { if (token) {

View File

@@ -1,16 +1,15 @@
// NotificationBlocked.js
import React from "react"; import React from "react";
const NotificationBlocked = () => { const NotificationBlocked = () => {
return ( return (
<div style={styles.container}> <div style={styles.container}>
<h2 style={styles.header}>Notifications Blocked</h2> <h2 style={styles.header}>Heads Up! Notifications Are Off</h2>
<p style={styles.message}> <p style={styles.message}>
It looks like notifications are currently blocked in your browser. It looks like youve got notifications turned off. Turning them on will
Enabling notifications will help you receive important updates, such as make sure you get important updates, like new orders or alerts, right on
new orders or alerts, directly on your device. your device.
</p> </p>
<h3 style={styles.instructionsHeader}>To enable notifications:</h3> <h3 style={styles.instructionsHeader}>Heres how to turn them on:</h3>
<ol style={styles.instructions}> <ol style={styles.instructions}>
<li>Open Chrome and go to our café's website.</li> <li>Open Chrome and go to our café's website.</li>
<li>Tap the menu (three dots) in the top-right corner.</li> <li>Tap the menu (three dots) in the top-right corner.</li>
@@ -19,13 +18,12 @@ const NotificationBlocked = () => {
&gt; <strong>Notifications</strong>. &gt; <strong>Notifications</strong>.
</li> </li>
<li> <li>
Find our café's site in the list and change the setting to{" "} Find our café in the list and set it to <strong>Allow</strong>.
<strong>Allow</strong>.
</li> </li>
</ol> </ol>
<p style={styles.footer}> <p style={styles.footer}>
Once you enable notifications, you'll start receiving updates right Once youve turned on notifications, youll start getting updates
away! If you need help, feel free to ask! instantly. Need a hand? Just ask!
</p> </p>
</div> </div>
); );

View File

@@ -1,26 +0,0 @@
import API_BASE_URL from "../config.js";
export const NotificationService = {
async fetchVapidPublicKey() {
const response = await fetch(API_BASE_URL + "/vapid-public-key"); // Adjust URL if necessary
const data = await response.json();
return data.publicKey;
},
async requestNotificationPermission(setModal) {
if (!("Notification" in window)) {
throw new Error("This browser does not support desktop notification");
}
setModal("req_notification");
const permission = await Notification.requestPermission();
return permission === "granted";
},
urlB64ToUint8Array(base64String) {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding)
.replace(/-/g, "+")
.replace(/_/g, "/");
const rawData = window.atob(base64);
return Uint8Array.from(rawData, (char) => char.charCodeAt(0));
},
};

View File

@@ -1,27 +0,0 @@
import API_BASE_URL from "../config.js";
import { NotificationService } from "./notificationService";
export const SubscriptionService = {
async subscribeUserToNotifications(userId) {
const registration = await navigator.serviceWorker.ready;
const publicKey = await NotificationService.fetchVapidPublicKey(); // Fetch the public key
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: NotificationService.urlB64ToUint8Array(publicKey),
});
await this.saveSubscription(userId, subscription);
console.log("User is subscribed:", subscription);
},
async saveSubscription(userId, subscription) {
await fetch(API_BASE_URL + "/subscribe", {
method: "POST",
body: JSON.stringify({ userId, subscription }),
headers: {
"Content-Type": "application/json",
},
});
},
};