feat: add subscribe logic
All checks were successful
Deploy to Netlify / Deploy to Netlify (push) Successful in 1m30s
All checks were successful
Deploy to Netlify / Deploy to Netlify (push) Successful in 1m30s
This commit is contained in:
parent
d4f1bb5dcd
commit
8b57952344
5 changed files with 149 additions and 27 deletions
|
|
@ -31,7 +31,7 @@ export default function InstallApp() {
|
||||||
};
|
};
|
||||||
window.addEventListener("beforeinstallprompt", handler);
|
window.addEventListener("beforeinstallprompt", handler);
|
||||||
|
|
||||||
return () => window.removeEventListener("transitionend", handler);
|
return () => window.removeEventListener("beforeinstallprompt", handler);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onClick = (evt: React.MouseEvent<HTMLButtonElement>) => {
|
const onClick = (evt: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
|
@ -64,11 +64,7 @@ export default function InstallApp() {
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
{/* Install PWA button */}
|
{/* Install PWA button */}
|
||||||
<Button
|
<Button className="w-full" onClick={onClick} disabled={!supportsPWA}>
|
||||||
className="w-full"
|
|
||||||
onClick={onClick}
|
|
||||||
disabled={!supportsPWA}
|
|
||||||
>
|
|
||||||
<Smartphone className="h-4 w-4 sm:h-5 sm:w-5 mr-2" />
|
<Smartphone className="h-4 w-4 sm:h-5 sm:w-5 mr-2" />
|
||||||
Installer l'application
|
Installer l'application
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -118,3 +114,10 @@ export default function InstallApp() {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isInstalled() {
|
||||||
|
const UA = navigator.userAgent;
|
||||||
|
const IOS = UA.match(/iPhone|iPad|iPod/);
|
||||||
|
const standalone = window.matchMedia("(display-mode: standalone)").matches;
|
||||||
|
return !!(standalone || (IOS && !UA.match(/Safari/)));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,47 @@ import {
|
||||||
Trash2,
|
Trash2,
|
||||||
Send,
|
Send,
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
Info,
|
Loader,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import InstallApp from "./install-app";
|
import InstallApp, { isInstalled } from "./install-app";
|
||||||
|
import {
|
||||||
|
isNotificationEnabled,
|
||||||
|
registerNotification,
|
||||||
|
unregisterNotification,
|
||||||
|
} from "~/lib/notification";
|
||||||
|
|
||||||
export default function NotificationSettings() {
|
export default function NotificationSettings() {
|
||||||
|
const [pushEnabled, setPushEnabled] = useState(isNotificationEnabled());
|
||||||
|
const [isRegistering, setIsRegistering] = useState(false);
|
||||||
|
|
||||||
|
function togglePushEnabled() {
|
||||||
|
setIsRegistering(true);
|
||||||
|
if (pushEnabled) {
|
||||||
|
// Unregister notifications
|
||||||
|
unregisterNotification()
|
||||||
|
.then(() => {
|
||||||
|
setPushEnabled(true);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsRegistering(false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Register notifications
|
||||||
|
registerNotification()
|
||||||
|
.then((result) => {
|
||||||
|
if ("error" in result) {
|
||||||
|
console.error("Failed to register notifications:", result.error);
|
||||||
|
} else {
|
||||||
|
setPushEnabled(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsRegistering(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Replace with actual user data
|
// TODO: Replace with actual user data
|
||||||
const [pushEnabled, setPushEnabled] = useState(false);
|
|
||||||
const [events, setEvents] = useState([
|
const [events, setEvents] = useState([
|
||||||
{ id: "new-message", label: "New Message", enabled: true },
|
{ id: "new-message", label: "New Message", enabled: true },
|
||||||
{ id: "comment-reply", label: "Comment Reply", enabled: true },
|
{ id: "comment-reply", label: "Comment Reply", enabled: true },
|
||||||
|
|
@ -87,24 +121,48 @@ export default function NotificationSettings() {
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
{/* <div className="flex items-start sm:items-center justify-between gap-4">
|
{isInstalled() ? (
|
||||||
|
pushEnabled ? (
|
||||||
|
<div className="flex items-start sm:items-center justify-between gap-4">
|
||||||
<div className="space-y-1 flex-1">
|
<div className="space-y-1 flex-1">
|
||||||
<p className="font-medium text-sm sm:text-base">
|
<p className="font-medium text-sm sm:text-base">
|
||||||
Notifications
|
Notifications
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs sm:text-sm text-muted-foreground">
|
<p className="text-xs sm:text-sm text-muted-foreground">
|
||||||
Recevez des notifications même lorsque l’application est fermée.
|
Recevez des notifications même lorsque l’application est
|
||||||
|
fermée.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
checked={pushEnabled}
|
checked={pushEnabled}
|
||||||
onCheckedChange={setPushEnabled}
|
onCheckedChange={togglePushEnabled}
|
||||||
aria-label="Enable push notifications"
|
disabled={isRegistering}
|
||||||
className="flex-shrink-0"
|
className="flex-shrink-0"
|
||||||
/>
|
/>
|
||||||
</div> */}
|
</div>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="w-full"
|
||||||
|
onClick={togglePushEnabled}
|
||||||
|
disabled={isRegistering}
|
||||||
|
>
|
||||||
|
{isRegistering ? (
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<Loader className="animate-spin h-4 w-4" />
|
||||||
|
Activation...
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<Bell className="h-4 w-4" />
|
||||||
|
Activer les notifications
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
<InstallApp />
|
<InstallApp />
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ export default function Profile({ user }: { user: User }) {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="w-full text-white"
|
className="w-full"
|
||||||
onClick={clearCache}
|
onClick={clearCache}
|
||||||
>
|
>
|
||||||
<Trash className="mr-2 h-4 w-4" />
|
<Trash className="mr-2 h-4 w-4" />
|
||||||
|
|
|
||||||
|
|
@ -314,3 +314,22 @@ export const useSubjects = () => {
|
||||||
...props,
|
...props,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* === NOTIFICATIONS API ===
|
||||||
|
*/
|
||||||
|
export const subscribe = async (data: any) => {
|
||||||
|
return makePostRequest(
|
||||||
|
"/notifications/subscribe",
|
||||||
|
data,
|
||||||
|
"Échec de l'abonnement aux notifications"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unsubscribe = async (data: any) => {
|
||||||
|
return makePostRequest(
|
||||||
|
"/notifications/unsubscribe",
|
||||||
|
data,
|
||||||
|
"Échec de la désinscription des notifications"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
||||||
42
app/lib/notification.ts
Normal file
42
app/lib/notification.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { subscribe, unsubscribe } from "./api";
|
||||||
|
|
||||||
|
const STORAGE_KEY = "notification_enabled";
|
||||||
|
|
||||||
|
export async function registerNotification() {
|
||||||
|
const permission = await Notification.requestPermission();
|
||||||
|
if (permission !== "granted") {
|
||||||
|
return {
|
||||||
|
error: "E_PERMISSION_DENIED",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const registration = await navigator.serviceWorker.ready;
|
||||||
|
try {
|
||||||
|
const subscription = await registration.pushManager.subscribe({
|
||||||
|
userVisibleOnly: true,
|
||||||
|
applicationServerKey: process.env.VITE_PUBLIC_VAPID_KEY,
|
||||||
|
});
|
||||||
|
await subscribe(subscription);
|
||||||
|
// Store to local storage
|
||||||
|
localStorage.setItem(STORAGE_KEY, "true");
|
||||||
|
return subscription;
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
error: "Erreur : " + err,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNotificationEnabled() {
|
||||||
|
return localStorage.getItem(STORAGE_KEY) === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unregisterNotification() {
|
||||||
|
const registration = await navigator.serviceWorker.ready;
|
||||||
|
const subscription = await registration.pushManager.getSubscription();
|
||||||
|
if (subscription) {
|
||||||
|
await subscription.unsubscribe();
|
||||||
|
localStorage.removeItem(STORAGE_KEY);
|
||||||
|
await unsubscribe(subscription);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue