Surfaces three events without a full notification center: - service worker (public/sw.js) gains push + notificationclick handlers (RTL, focuses an existing tab or opens the target URL). - usePushNotifications hook: permission + VAPID subscribe/unsubscribe against the backend push endpoints. - opt-in UI both ways: a one-time dismissible post-login banner (PushPermissionBanner, rendered in MyLayout) and a permanent toggle (PushSettingToggle) on buyer profile settings and seller store settings. - useBadgeCounts polls /api/notif/v1/badges/; small red badges on the bottom-nav profile icon (chat + order updates), the profile sidebar (پیامها / سفارشها) and the seller dashboard sidebar. Visiting orders clears its badge. Degrades gracefully: if the backend endpoints are absent the counts fall back to zero and nothing throws. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
51 lines
2.1 KiB
TypeScript
51 lines
2.1 KiB
TypeScript
import { usePushNotifications } from "~/hooks/usePushNotifications";
|
||
|
||
/**
|
||
* A settings row with a switch to enable/disable web push notifications.
|
||
* Mirrors the existing theme-toggle row styling on the settings pages.
|
||
*/
|
||
export function PushSettingToggle({ className = "" }: { className?: string }) {
|
||
const { supported, permission, isSubscribed, subscribe, disable, busy } =
|
||
usePushNotifications();
|
||
|
||
const blocked = permission === "denied";
|
||
const checked = isSubscribed && permission === "granted";
|
||
|
||
const onToggle = () => {
|
||
if (busy || blocked) return;
|
||
if (checked) {
|
||
disable();
|
||
} else {
|
||
subscribe();
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className={className}>
|
||
<div className="flex w-full items-center justify-between">
|
||
<div className="flex flex-col gap-1">
|
||
<p className="text-R12 lg:text-B16 font-bold">اعلانها</p>
|
||
<p className="text-R10 lg:text-R12 text-GRAY">
|
||
{blocked
|
||
? "اعلانها در مرورگر مسدود شدهاند. از تنظیمات مرورگر اجازه دهید."
|
||
: !supported
|
||
? "مرورگر شما از اعلان پشتیبانی نمیکند."
|
||
: "دریافت اعلان برای پیامها و وضعیت سفارشها"}
|
||
</p>
|
||
</div>
|
||
<label className="relative inline-flex items-center cursor-pointer">
|
||
<input
|
||
type="checkbox"
|
||
checked={checked}
|
||
onChange={onToggle}
|
||
disabled={busy || blocked || !supported}
|
||
className="sr-only peer"
|
||
aria-label="اعلانها"
|
||
/>
|
||
<div className="w-11 h-6 bg-WHITE2 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-WHITE after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-WHITE after:border-GRAY3 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-VITROWN_BLUE border peer-disabled:opacity-50"></div>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|