ok
This commit is contained in:
28
public/service-worker.js
Normal file
28
public/service-worker.js
Normal 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}`)
|
||||
);
|
||||
});
|
||||
15
public/sw.js
15
public/sw.js
@@ -1,12 +1,9 @@
|
||||
if ("serviceWorker" in navigator) {
|
||||
window.addEventListener("load", () => {
|
||||
navigator.serviceWorker
|
||||
.register("/service-worker.js")
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/service-worker.js')
|
||||
.then((registration) => {
|
||||
console.log(
|
||||
"Service Worker registered with scope:",
|
||||
registration.scope
|
||||
);
|
||||
});
|
||||
console.log('Service Worker registered with scope:', registration.scope);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Service Worker registration failed:', error);
|
||||
});
|
||||
}
|
||||
|
||||
97
src/App.js
97
src/App.js
@@ -9,8 +9,8 @@ import {
|
||||
useLocation,
|
||||
} from "react-router-dom";
|
||||
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 ScanMeja from "./pages/ScanMeja";
|
||||
@@ -138,20 +138,74 @@ function App() {
|
||||
setGuestSides(sessionLeft.guestSideList);
|
||||
};
|
||||
|
||||
const checkNotifications = async (userId) => {
|
||||
try {
|
||||
const permissionGranted =
|
||||
await NotificationService.requestNotificationPermission(setModal);
|
||||
if (permissionGranted) {
|
||||
await SubscriptionService.subscribeUserToNotifications(userId);
|
||||
// const checkNotifications = async (userId) => {
|
||||
// try {
|
||||
// const permissionGranted =
|
||||
// await NotificationService.requestNotificationPermission(setModal);
|
||||
// if (permissionGranted) {
|
||||
// await SubscriptionService.subscribeUserToNotifications(userId);
|
||||
// } else {
|
||||
// setModal("blocked_notification");
|
||||
// console.log("req notif");
|
||||
// }
|
||||
// } catch (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 {
|
||||
setModal("blocked_notification");
|
||||
console.log("req notif");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error handling notifications:", error);
|
||||
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(() => {
|
||||
if (socket == null) return;
|
||||
|
||||
@@ -226,7 +280,7 @@ function App() {
|
||||
console.log("getting guest side");
|
||||
setDeviceType("clerk");
|
||||
|
||||
checkNotifications(data.data.user.userId);
|
||||
// checkNotifications(data.data.user.userId);
|
||||
} else {
|
||||
setDeviceType("guestDevice");
|
||||
}
|
||||
@@ -278,12 +332,16 @@ function App() {
|
||||
// Function to open the modal
|
||||
const setModal = (content, params = {}) => {
|
||||
// Prepare query parameters
|
||||
const queryParams = new URLSearchParams({
|
||||
modal: content,
|
||||
...params, // Spread additional parameters
|
||||
}).toString();
|
||||
const queryParams = new URLSearchParams(location.search);
|
||||
|
||||
// Update the modal and any additional params
|
||||
queryParams.set("modal", content);
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
queryParams.set(key, value);
|
||||
});
|
||||
|
||||
// Update URL with new parameters
|
||||
navigate(`?${queryParams}`, { replace: true });
|
||||
navigate(`?${queryParams.toString()}`, { replace: true });
|
||||
|
||||
// Prevent scrolling when modal is open
|
||||
document.body.style.overflow = "hidden";
|
||||
@@ -292,6 +350,7 @@ function App() {
|
||||
setModalContent(content);
|
||||
};
|
||||
|
||||
|
||||
// Function to close the modal
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
|
||||
@@ -37,6 +37,7 @@ const Modal = ({ shop, isOpen, onClose, modalContent }) => {
|
||||
×
|
||||
</button>
|
||||
{modalContent === "req_notification" && <NotificationBlocked />}
|
||||
{modalContent === "blocked_notification" && <NotificationBlocked />}
|
||||
{modalContent === "edit_tables" && <TablesPage shop={shop} />}
|
||||
{modalContent === "new_transaction" && (
|
||||
<Transaction propsShopId={shop.cafeId} />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// src/CafePage.js
|
||||
|
||||
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 SearchInput from "../components/SearchInput";
|
||||
@@ -33,6 +33,7 @@ function CafePage({
|
||||
removeConnectedGuestSides,
|
||||
setModal,
|
||||
}) {
|
||||
const location = useLocation();
|
||||
const [searchParams] = useSearchParams();
|
||||
const token = searchParams.get("token");
|
||||
const { shopId, tableCode } = useParams();
|
||||
@@ -50,10 +51,15 @@ function CafePage({
|
||||
const [filterId, setFilterId] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (user.cafeId != null && user.cafeId != shopId) {
|
||||
navigate("/" + user.cafeId);
|
||||
if (user.cafeId != null && user.cafeId !== shopId) {
|
||||
// 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(() => {
|
||||
if (token) {
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
// NotificationBlocked.js
|
||||
import React from "react";
|
||||
|
||||
const NotificationBlocked = () => {
|
||||
return (
|
||||
<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}>
|
||||
It looks like notifications are currently blocked in your browser.
|
||||
Enabling notifications will help you receive important updates, such as
|
||||
new orders or alerts, directly on your device.
|
||||
It looks like you’ve got notifications turned off. Turning them on will
|
||||
make sure you get important updates, like new orders or alerts, right on
|
||||
your device.
|
||||
</p>
|
||||
<h3 style={styles.instructionsHeader}>To enable notifications:</h3>
|
||||
<h3 style={styles.instructionsHeader}>Here’s how to turn them on:</h3>
|
||||
<ol style={styles.instructions}>
|
||||
<li>Open Chrome and go to our café's website.</li>
|
||||
<li>Tap the menu (three dots) in the top-right corner.</li>
|
||||
@@ -19,13 +18,12 @@ const NotificationBlocked = () => {
|
||||
> <strong>Notifications</strong>.
|
||||
</li>
|
||||
<li>
|
||||
Find our café's site in the list and change the setting to{" "}
|
||||
<strong>Allow</strong>.
|
||||
Find our café in the list and set it to <strong>Allow</strong>.
|
||||
</li>
|
||||
</ol>
|
||||
<p style={styles.footer}>
|
||||
Once you enable notifications, you'll start receiving updates right
|
||||
away! If you need help, feel free to ask!
|
||||
Once you’ve turned on notifications, you’ll start getting updates
|
||||
instantly. Need a hand? Just ask!
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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));
|
||||
},
|
||||
};
|
||||
@@ -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",
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user