ui update and some backend functionality adding in accordance with research on adhd and ux design

This commit is contained in:
2026-02-14 17:21:37 -06:00
parent 4d3a9fbd54
commit fb480eacb2
32 changed files with 9549 additions and 248 deletions

View File

@@ -0,0 +1,104 @@
'use client';
import { useEffect, useState } from 'react';
import api from '@/lib/api';
function urlBase64ToUint8Array(base64String: string): Uint8Array {
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;
}
export default function PushNotificationToggle() {
const [supported, setSupported] = useState(false);
const [enabled, setEnabled] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
const check = async () => {
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
setLoading(false);
return;
}
setSupported(true);
try {
const reg = await navigator.serviceWorker.ready;
const sub = await reg.pushManager.getSubscription();
setEnabled(!!sub);
} catch {
// ignore
}
setLoading(false);
};
check();
}, []);
const toggle = async () => {
if (loading) return;
setLoading(true);
try {
const reg = await navigator.serviceWorker.ready;
if (enabled) {
// Unsubscribe
const sub = await reg.pushManager.getSubscription();
if (sub) {
await api.notifications.unsubscribe(sub.endpoint);
await sub.unsubscribe();
}
setEnabled(false);
} else {
// Subscribe
const permission = await Notification.requestPermission();
if (permission !== 'granted') {
setLoading(false);
return;
}
const { public_key } = await api.notifications.getVapidPublicKey();
const sub = await reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(public_key).buffer as ArrayBuffer,
});
const subJson = sub.toJSON();
await api.notifications.subscribe(subJson);
setEnabled(true);
}
} catch (err) {
console.error('Push notification toggle failed:', err);
}
setLoading(false);
};
if (!supported) return null;
return (
<div className="flex items-center justify-between bg-white rounded-xl p-4 shadow-sm">
<div>
<h3 className="font-semibold text-gray-900 text-sm">Push Notifications</h3>
<p className="text-xs text-gray-500">Get reminders on this device</p>
</div>
<button
onClick={toggle}
disabled={loading}
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
enabled ? 'bg-indigo-600' : 'bg-gray-300'
} ${loading ? 'opacity-50' : ''}`}
>
<span
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
enabled ? 'translate-x-6' : 'translate-x-1'
}`}
/>
</button>
</div>
);
}