UI: Final surgical fix for iPhone XR - 480p Baseline 3.0, CSS filter removal, and structure repair
This commit is contained in:
parent
c3f9a4fc9a
commit
5f87c79b58
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
@ -291,19 +291,19 @@ export function UserHeader({ user, preferences, onModalStateChange }: UserHeader
|
||||
onLoadedData={() => setIsVideoLoaded(true)}
|
||||
onPlay={() => setIsVideoPlaying(true)}
|
||||
className={cn(
|
||||
"absolute inset-0 w-full h-full object-cover scale-110 brightness-[1.1] dark:brightness-[0.85] transition-all duration-[2000ms] ease-in-out",
|
||||
"absolute inset-0 w-full h-full object-cover scale-110 transition-opacity duration-[1500ms] ease-in-out",
|
||||
isVideoPlaying
|
||||
? "opacity-70 dark:opacity-50 blur-0"
|
||||
? "opacity-70 dark:opacity-50"
|
||||
: isVideoLoaded
|
||||
? "opacity-30 blur-[4px]"
|
||||
: "opacity-10 blur-sm"
|
||||
? "opacity-30"
|
||||
: "opacity-10"
|
||||
)}
|
||||
>
|
||||
<source src="/videos/smoke.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
{/* Vignette/Readability Overlay */}
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-black/10 via-transparent to-black/30 dark:from-black/40 dark:via-transparent dark:to-black/50" />
|
||||
{/* Vignette/Readability Overlay - Reinforced for contrast */}
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-black/30 via-transparent to-black/50 dark:from-black/60 dark:via-transparent dark:to-black/70 mix-blend-multiply" />
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 h-16 sm:h-20 flex items-center justify-between relative z-50">
|
||||
@ -381,226 +381,226 @@ export function UserHeader({ user, preferences, onModalStateChange }: UserHeader
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Side Menu Integration */}
|
||||
<SideMenu
|
||||
isOpen={isSideMenuOpen}
|
||||
onClose={() => setIsSideMenuOpen(false)}
|
||||
user={user}
|
||||
userName={userName}
|
||||
/>
|
||||
{/* Side Menu Integration */}
|
||||
<SideMenu
|
||||
isOpen={isSideMenuOpen}
|
||||
onClose={() => setIsSideMenuOpen(false)}
|
||||
user={user}
|
||||
userName={userName}
|
||||
/>
|
||||
|
||||
{/* Reminder Settings Dialog */}
|
||||
<Dialog open={showReminderDialog} onOpenChange={setShowReminderDialog}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2 font-bold tracking-tight">
|
||||
<Bell className="h-5 w-5 text-indigo-400" />
|
||||
Notification Settings
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
{/* Reminder Settings Dialog */}
|
||||
<Dialog open={showReminderDialog} onOpenChange={setShowReminderDialog}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2 font-bold tracking-tight">
|
||||
<Bell className="h-5 w-5 text-indigo-400" />
|
||||
Notification Settings
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 py-4 px-1">
|
||||
{/* Enable/Disable Toggle */}
|
||||
<div className={cn(
|
||||
"flex items-center justify-between p-4 rounded-2xl border transition-all",
|
||||
theme === 'light' ? "bg-slate-50 border-slate-100" : "bg-white/5 border-white/5"
|
||||
)}>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={cn(
|
||||
"p-2.5 rounded-xl",
|
||||
reminderSettings.enabled ? "bg-indigo-500/20 text-indigo-400" : "bg-slate-500/20 text-slate-400"
|
||||
)}>
|
||||
{reminderSettings.enabled ? <BellRing className="h-5 w-5" /> : <BellOff className="h-5 w-5" />}
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-bold">
|
||||
{reminderSettings.enabled ? 'Enabled' : 'Disabled'}
|
||||
</span>
|
||||
<span className="text-[10px] opacity-60">
|
||||
{reminderSettings.enabled ? 'Reminders active' : 'Turn on to get alerts'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-4 py-4 px-1">
|
||||
{/* Enable/Disable Toggle */}
|
||||
<div className={cn(
|
||||
"flex items-center justify-between p-4 rounded-2xl border transition-all",
|
||||
theme === 'light' ? "bg-slate-50 border-slate-100" : "bg-white/5 border-white/5"
|
||||
)}>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={cn(
|
||||
"p-2.5 rounded-xl",
|
||||
reminderSettings.enabled ? "bg-indigo-500/20 text-indigo-400" : "bg-slate-500/20 text-slate-400"
|
||||
)}>
|
||||
{reminderSettings.enabled ? <BellRing className="h-5 w-5" /> : <BellOff className="h-5 w-5" />}
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-bold">
|
||||
{reminderSettings.enabled ? 'Enabled' : 'Disabled'}
|
||||
</span>
|
||||
<span className="text-[10px] opacity-60">
|
||||
{reminderSettings.enabled ? 'Reminders active' : 'Turn on to get alerts'}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleToggleReminders}
|
||||
disabled={!isSupported || (permission === 'denied' && !reminderSettings.enabled)}
|
||||
className={cn(
|
||||
"relative w-12 h-6 rounded-full transition-all duration-300 shadow-inner",
|
||||
reminderSettings.enabled ? "bg-indigo-500" : "bg-slate-400/30",
|
||||
(!isSupported || (permission === 'denied' && !reminderSettings.enabled)) && "opacity-50 cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
<div className={cn(
|
||||
"absolute top-1 w-4 h-4 rounded-full bg-white shadow-md transition-all duration-300",
|
||||
reminderSettings.enabled ? "left-7" : "left-1"
|
||||
)} />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleToggleReminders}
|
||||
disabled={!isSupported || (permission === 'denied' && !reminderSettings.enabled)}
|
||||
className={cn(
|
||||
"relative w-12 h-6 rounded-full transition-all duration-300 shadow-inner",
|
||||
reminderSettings.enabled ? "bg-indigo-500" : "bg-slate-400/30",
|
||||
(!isSupported || (permission === 'denied' && !reminderSettings.enabled)) && "opacity-50 cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
<div className={cn(
|
||||
"absolute top-1 w-4 h-4 rounded-full bg-white shadow-md transition-all duration-300",
|
||||
reminderSettings.enabled ? "left-7" : "left-1"
|
||||
)} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Frequency Selection */}
|
||||
{reminderSettings.enabled && (
|
||||
<div className="space-y-3">
|
||||
<div className="text-[10px] font-bold uppercase tracking-widest opacity-40 px-1">Reminder Frequency</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<button
|
||||
onClick={() => handleFrequencyChange('daily')}
|
||||
className={cn(
|
||||
"p-4 rounded-2xl border text-sm font-bold transition-all flex flex-col items-center gap-1",
|
||||
localFrequency === 'daily'
|
||||
? 'bg-indigo-500 border-indigo-500 text-white shadow-lg shadow-indigo-500/25 scale-[1.02]'
|
||||
: 'bg-background border-border hover:border-indigo-500/50'
|
||||
)}
|
||||
>
|
||||
<span>Daily</span>
|
||||
<span className="text-[10px] font-normal opacity-70">Once a day</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleFrequencyChange('hourly')}
|
||||
className={cn(
|
||||
"p-4 rounded-2xl border text-sm font-bold transition-all flex flex-col items-center gap-1",
|
||||
localFrequency === 'hourly'
|
||||
? 'bg-indigo-500 border-indigo-500 text-white shadow-lg shadow-indigo-500/25 scale-[1.02]'
|
||||
: 'bg-background border-border hover:border-indigo-500/50'
|
||||
)}
|
||||
>
|
||||
<span>Hourly</span>
|
||||
<span className="text-[10px] font-normal opacity-70">Window alerts</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Time Picker (Only for Daily) */}
|
||||
{reminderSettings.enabled && localFrequency === 'daily' && (
|
||||
<div className="space-y-3 animate-in fade-in slide-in-from-top-2 duration-300">
|
||||
<div className="text-[10px] font-bold uppercase tracking-widest opacity-40 px-1">Preferred Time</div>
|
||||
<div className="flex gap-2 p-1 bg-muted/30 rounded-2xl border border-border/50">
|
||||
<div className="flex-1">
|
||||
<Select
|
||||
value={hourString}
|
||||
onValueChange={(val) => updateTime(val, minuteString, currentAmpm)}
|
||||
>
|
||||
<SelectTrigger className="border-none bg-transparent shadow-none hover:bg-white/5 h-12 rounded-xl">
|
||||
<SelectValue placeholder="Hour" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="rounded-xl">
|
||||
{hoursOptions.map((h) => (
|
||||
<SelectItem key={h} value={h} className="rounded-lg">{h}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="flex-1">
|
||||
<Select
|
||||
value={minuteString}
|
||||
onValueChange={(val) => updateTime(hourString, val, currentAmpm)}
|
||||
>
|
||||
<SelectTrigger className="border-none bg-transparent shadow-none hover:bg-white/5 h-12 rounded-xl">
|
||||
<SelectValue placeholder="Min" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="rounded-xl">
|
||||
{minutesOptions.map((m) => (
|
||||
<SelectItem key={m} value={m} className="rounded-lg">{m}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="w-24 px-1">
|
||||
<Select
|
||||
value={currentAmpm}
|
||||
onValueChange={(val) => updateTime(hourString, minuteString, val)}
|
||||
>
|
||||
<SelectTrigger className="border-none bg-indigo-500/10 text-indigo-400 font-bold shadow-none hover:bg-indigo-500/20 h-10 mt-1 rounded-lg">
|
||||
<SelectValue placeholder="AM/PM" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="rounded-xl">
|
||||
<SelectItem value="AM" className="rounded-lg">AM</SelectItem>
|
||||
<SelectItem value="PM" className="rounded-lg">PM</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Hourly Alerts Window */}
|
||||
{reminderSettings.enabled && localFrequency === 'hourly' && (
|
||||
<div className="space-y-4 animate-in fade-in slide-in-from-top-2 duration-300">
|
||||
<div className="space-y-2">
|
||||
<div className="text-[10px] font-bold uppercase tracking-widest opacity-40 px-1">Start Time</div>
|
||||
<HourlyTimePicker
|
||||
value={reminderSettings.hourlyStart || '09:00'}
|
||||
onChange={async (newTime) => {
|
||||
const [h, m] = newTime.split(':');
|
||||
const end = (reminderSettings.hourlyEnd || '21:00').split(':');
|
||||
const newSettings = { ...reminderSettings, hourlyStart: newTime, hourlyEnd: `${end[0]}:${m}` };
|
||||
setReminderSettings(newSettings);
|
||||
await saveReminderSettings(newSettings);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="text-[10px] font-bold uppercase tracking-widest opacity-40 px-1">End Time</div>
|
||||
<HourlyTimePicker
|
||||
value={reminderSettings.hourlyEnd || '21:00'}
|
||||
onChange={async (newTime) => {
|
||||
const [h, m] = newTime.split(':');
|
||||
const start = (reminderSettings.hourlyStart || '09:00').split(':');
|
||||
const newSettings = { ...reminderSettings, hourlyEnd: newTime, hourlyStart: `${start[0]}:${m}` };
|
||||
setReminderSettings(newSettings);
|
||||
await saveReminderSettings(newSettings);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 p-3 bg-indigo-500/5 rounded-2xl border border-indigo-500/10">
|
||||
<Sparkles className="h-4 w-4 text-indigo-400" />
|
||||
<p className="text-[10px] text-indigo-400 uppercase font-black tracking-widest">Reminders every 60 minutes</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Notification Permission Sync */}
|
||||
{reminderSettings.enabled && isSupported && (
|
||||
<div className="pt-2">
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const result = await requestPermission();
|
||||
if (result === 'granted') {
|
||||
try {
|
||||
const res = await fetch('/api/notifications/test', { method: 'POST' });
|
||||
if (res.ok) alert("Success! Push notifications active.");
|
||||
} catch (err) { console.error(err); }
|
||||
} else alert("Please enable notifications in settings.");
|
||||
}}
|
||||
{/* Frequency Selection */}
|
||||
{reminderSettings.enabled && (
|
||||
<div className="space-y-3">
|
||||
<div className="text-[10px] font-bold uppercase tracking-widest opacity-40 px-1">Reminder Frequency</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<button
|
||||
onClick={() => handleFrequencyChange('daily')}
|
||||
className={cn(
|
||||
"w-full h-12 rounded-2xl font-bold transition-all shadow-md group",
|
||||
permission === 'granted'
|
||||
? 'bg-emerald-500/10 text-emerald-500 hover:bg-emerald-500/20'
|
||||
: 'bg-emerald-600 text-white hover:bg-emerald-500 hover:scale-[1.02]'
|
||||
"p-4 rounded-2xl border text-sm font-bold transition-all flex flex-col items-center gap-1",
|
||||
localFrequency === 'daily'
|
||||
? 'bg-indigo-500 border-indigo-500 text-white shadow-lg shadow-indigo-500/25 scale-[1.02]'
|
||||
: 'bg-background border-border hover:border-indigo-500/50'
|
||||
)}
|
||||
>
|
||||
<Bell className="mr-2 h-4 w-4 group-hover:animate-bounce" />
|
||||
{permission === 'granted' ? 'Permissions Verified' : 'Enable Push Alerts'}
|
||||
</Button>
|
||||
<span>Daily</span>
|
||||
<span className="text-[10px] font-normal opacity-70">Once a day</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleFrequencyChange('hourly')}
|
||||
className={cn(
|
||||
"p-4 rounded-2xl border text-sm font-bold transition-all flex flex-col items-center gap-1",
|
||||
localFrequency === 'hourly'
|
||||
? 'bg-indigo-500 border-indigo-500 text-white shadow-lg shadow-indigo-500/25 scale-[1.02]'
|
||||
: 'bg-background border-border hover:border-indigo-500/50'
|
||||
)}
|
||||
>
|
||||
<span>Hourly</span>
|
||||
<span className="text-[10px] font-normal opacity-70">Window alerts</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{permission === 'denied' && (
|
||||
<div className="p-4 bg-red-500/10 border border-red-500/20 rounded-2xl">
|
||||
<p className="text-xs text-red-500 text-center font-medium leading-relaxed">
|
||||
Browser notifications are currently blocked. To get reminders, please update your site settings.
|
||||
</p>
|
||||
{/* Time Picker (Only for Daily) */}
|
||||
{reminderSettings.enabled && localFrequency === 'daily' && (
|
||||
<div className="space-y-3 animate-in fade-in slide-in-from-top-2 duration-300">
|
||||
<div className="text-[10px] font-bold uppercase tracking-widest opacity-40 px-1">Preferred Time</div>
|
||||
<div className="flex gap-2 p-1 bg-muted/30 rounded-2xl border border-border/50">
|
||||
<div className="flex-1">
|
||||
<Select
|
||||
value={hourString}
|
||||
onValueChange={(val) => updateTime(val, minuteString, currentAmpm)}
|
||||
>
|
||||
<SelectTrigger className="border-none bg-transparent shadow-none hover:bg-white/5 h-12 rounded-xl">
|
||||
<SelectValue placeholder="Hour" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="rounded-xl">
|
||||
{hoursOptions.map((h) => (
|
||||
<SelectItem key={h} value={h} className="rounded-lg">{h}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="flex-1">
|
||||
<Select
|
||||
value={minuteString}
|
||||
onValueChange={(val) => updateTime(hourString, val, currentAmpm)}
|
||||
>
|
||||
<SelectTrigger className="border-none bg-transparent shadow-none hover:bg-white/5 h-12 rounded-xl">
|
||||
<SelectValue placeholder="Min" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="rounded-xl">
|
||||
{minutesOptions.map((m) => (
|
||||
<SelectItem key={m} value={m} className="rounded-lg">{m}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="w-24 px-1">
|
||||
<Select
|
||||
value={currentAmpm}
|
||||
onValueChange={(val) => updateTime(hourString, minuteString, val)}
|
||||
>
|
||||
<SelectTrigger className="border-none bg-indigo-500/10 text-indigo-400 font-bold shadow-none hover:bg-indigo-500/20 h-10 mt-1 rounded-lg">
|
||||
<SelectValue placeholder="AM/PM" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="rounded-xl">
|
||||
<SelectItem value="AM" className="rounded-lg">AM</SelectItem>
|
||||
<SelectItem value="PM" className="rounded-lg">PM</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</header>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Hourly Alerts Window */}
|
||||
{reminderSettings.enabled && localFrequency === 'hourly' && (
|
||||
<div className="space-y-4 animate-in fade-in slide-in-from-top-2 duration-300">
|
||||
<div className="space-y-2">
|
||||
<div className="text-[10px] font-bold uppercase tracking-widest opacity-40 px-1">Start Time</div>
|
||||
<HourlyTimePicker
|
||||
value={reminderSettings.hourlyStart || '09:00'}
|
||||
onChange={async (newTime) => {
|
||||
const [h, m] = newTime.split(':');
|
||||
const end = (reminderSettings.hourlyEnd || '21:00').split(':');
|
||||
const newSettings = { ...reminderSettings, hourlyStart: newTime, hourlyEnd: `${end[0]}:${m}` };
|
||||
setReminderSettings(newSettings);
|
||||
await saveReminderSettings(newSettings);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="text-[10px] font-bold uppercase tracking-widest opacity-40 px-1">End Time</div>
|
||||
<HourlyTimePicker
|
||||
value={reminderSettings.hourlyEnd || '21:00'}
|
||||
onChange={async (newTime) => {
|
||||
const [h, m] = newTime.split(':');
|
||||
const start = (reminderSettings.hourlyStart || '09:00').split(':');
|
||||
const newSettings = { ...reminderSettings, hourlyEnd: newTime, hourlyStart: `${start[0]}:${m}` };
|
||||
setReminderSettings(newSettings);
|
||||
await saveReminderSettings(newSettings);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 p-3 bg-indigo-500/5 rounded-2xl border border-indigo-500/10">
|
||||
<Sparkles className="h-4 w-4 text-indigo-400" />
|
||||
<p className="text-[10px] text-indigo-400 uppercase font-black tracking-widest">Reminders every 60 minutes</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Notification Permission Sync */}
|
||||
{reminderSettings.enabled && isSupported && (
|
||||
<div className="pt-2">
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const result = await requestPermission();
|
||||
if (result === 'granted') {
|
||||
try {
|
||||
const res = await fetch('/api/notifications/test', { method: 'POST' });
|
||||
if (res.ok) alert("Success! Push notifications active.");
|
||||
} catch (err) { console.error(err); }
|
||||
} else alert("Please enable notifications in settings.");
|
||||
}}
|
||||
className={cn(
|
||||
"w-full h-12 rounded-2xl font-bold transition-all shadow-md group",
|
||||
permission === 'granted'
|
||||
? 'bg-emerald-500/10 text-emerald-500 hover:bg-emerald-500/20'
|
||||
: 'bg-emerald-600 text-white hover:bg-emerald-500 hover:scale-[1.02]'
|
||||
)}
|
||||
>
|
||||
<Bell className="mr-2 h-4 w-4 group-hover:animate-bounce" />
|
||||
{permission === 'granted' ? 'Permissions Verified' : 'Enable Push Alerts'}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{permission === 'denied' && (
|
||||
<div className="p-4 bg-red-500/10 border border-red-500/20 rounded-2xl">
|
||||
<p className="text-xs text-red-500 text-center font-medium leading-relaxed">
|
||||
Browser notifications are currently blocked. To get reminders, please update your site settings.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user