fixes yo
This commit is contained in:
@@ -83,6 +83,9 @@ export default function SessionRunnerPage() {
|
||||
setCurrentStep(sessionData.current_step);
|
||||
setCurrentStepIndex(sessionData.session.current_step_index);
|
||||
setStatus(sessionData.session.status === 'paused' ? 'paused' : 'active');
|
||||
if (sessionData.session.status !== 'paused') {
|
||||
setIsTimerRunning(true);
|
||||
}
|
||||
|
||||
// Mark previous steps as completed
|
||||
const completed = new Set<number>();
|
||||
@@ -117,7 +120,7 @@ export default function SessionRunnerPage() {
|
||||
return () => {
|
||||
if (timerRef.current) clearInterval(timerRef.current);
|
||||
};
|
||||
}, [isTimerRunning, timerSeconds]);
|
||||
}, [isTimerRunning]);
|
||||
|
||||
const formatTime = (seconds: number) => {
|
||||
const mins = Math.floor(seconds / 60);
|
||||
|
||||
@@ -5,6 +5,7 @@ import api from '@/lib/api';
|
||||
import { VolumeIcon, VolumeOffIcon, SparklesIcon } from '@/components/ui/Icons';
|
||||
import { playStepComplete } from '@/lib/sounds';
|
||||
import { hapticTap } from '@/lib/haptics';
|
||||
import PushNotificationToggle from '@/components/notifications/PushNotificationToggle';
|
||||
|
||||
interface Preferences {
|
||||
sound_enabled: boolean;
|
||||
@@ -13,6 +14,13 @@ interface Preferences {
|
||||
celebration_style: string;
|
||||
}
|
||||
|
||||
interface NotifSettings {
|
||||
discord_webhook: string;
|
||||
discord_enabled: boolean;
|
||||
ntfy_topic: string;
|
||||
ntfy_enabled: boolean;
|
||||
}
|
||||
|
||||
export default function SettingsPage() {
|
||||
const [prefs, setPrefs] = useState<Preferences>({
|
||||
sound_enabled: false,
|
||||
@@ -20,29 +28,57 @@ export default function SettingsPage() {
|
||||
show_launch_screen: true,
|
||||
celebration_style: 'standard',
|
||||
});
|
||||
const [notif, setNotif] = useState<NotifSettings>({
|
||||
discord_webhook: '',
|
||||
discord_enabled: false,
|
||||
ntfy_topic: '',
|
||||
ntfy_enabled: false,
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [saved, setSaved] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
api.preferences.get()
|
||||
.then((data: Preferences) => setPrefs(data))
|
||||
Promise.all([
|
||||
api.preferences.get().then((data: Preferences) => setPrefs(data)),
|
||||
api.notifications.getSettings().then((data) => setNotif({
|
||||
discord_webhook: data.discord_webhook,
|
||||
discord_enabled: data.discord_enabled,
|
||||
ntfy_topic: data.ntfy_topic,
|
||||
ntfy_enabled: data.ntfy_enabled,
|
||||
})),
|
||||
])
|
||||
.catch(() => {})
|
||||
.finally(() => setIsLoading(false));
|
||||
}, []);
|
||||
|
||||
const flashSaved = () => {
|
||||
setSaved(true);
|
||||
setTimeout(() => setSaved(false), 1500);
|
||||
};
|
||||
|
||||
const updatePref = async (key: keyof Preferences, value: boolean | string) => {
|
||||
const updated = { ...prefs, [key]: value };
|
||||
setPrefs(updated);
|
||||
try {
|
||||
await api.preferences.update({ [key]: value });
|
||||
setSaved(true);
|
||||
setTimeout(() => setSaved(false), 1500);
|
||||
flashSaved();
|
||||
} catch {
|
||||
// revert on failure
|
||||
setPrefs(prefs);
|
||||
}
|
||||
};
|
||||
|
||||
const updateNotif = async (updates: Partial<NotifSettings>) => {
|
||||
const prev = { ...notif };
|
||||
const updated = { ...notif, ...updates };
|
||||
setNotif(updated);
|
||||
try {
|
||||
await api.notifications.updateSettings(updates);
|
||||
flashSaved();
|
||||
} catch {
|
||||
setNotif(prev);
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[50vh]">
|
||||
@@ -136,6 +172,75 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Notifications */}
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-3">Notifications</h2>
|
||||
<div className="bg-white rounded-xl shadow-sm divide-y divide-gray-100">
|
||||
{/* Push Notifications */}
|
||||
<PushNotificationToggle />
|
||||
|
||||
{/* ntfy */}
|
||||
<div className="p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900">ntfy</p>
|
||||
<p className="text-sm text-gray-500">Push notifications via ntfy.sh</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => updateNotif({ ntfy_enabled: !notif.ntfy_enabled })}
|
||||
className={`w-12 h-7 rounded-full transition-colors ${
|
||||
notif.ntfy_enabled ? 'bg-indigo-500' : 'bg-gray-300'
|
||||
}`}
|
||||
>
|
||||
<div className={`w-5 h-5 bg-white rounded-full shadow transition-transform ml-1 ${
|
||||
notif.ntfy_enabled ? 'translate-x-5' : ''
|
||||
}`} />
|
||||
</button>
|
||||
</div>
|
||||
{notif.ntfy_enabled && (
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Your ntfy topic ID"
|
||||
value={notif.ntfy_topic}
|
||||
onChange={(e) => setNotif({ ...notif, ntfy_topic: e.target.value })}
|
||||
onBlur={() => updateNotif({ ntfy_topic: notif.ntfy_topic })}
|
||||
className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent text-gray-900 placeholder-gray-400"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Discord */}
|
||||
<div className="p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900">Discord</p>
|
||||
<p className="text-sm text-gray-500">Send notifications to a Discord channel</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => updateNotif({ discord_enabled: !notif.discord_enabled })}
|
||||
className={`w-12 h-7 rounded-full transition-colors ${
|
||||
notif.discord_enabled ? 'bg-indigo-500' : 'bg-gray-300'
|
||||
}`}
|
||||
>
|
||||
<div className={`w-5 h-5 bg-white rounded-full shadow transition-transform ml-1 ${
|
||||
notif.discord_enabled ? 'translate-x-5' : ''
|
||||
}`} />
|
||||
</button>
|
||||
</div>
|
||||
{notif.discord_enabled && (
|
||||
<input
|
||||
type="url"
|
||||
placeholder="Discord webhook URL"
|
||||
value={notif.discord_webhook}
|
||||
onChange={(e) => setNotif({ ...notif, discord_webhook: e.target.value })}
|
||||
onBlur={() => updateNotif({ discord_webhook: notif.discord_webhook })}
|
||||
className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent text-gray-900 placeholder-gray-400"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Celebration Style */}
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-3">Celebration Style</h2>
|
||||
|
||||
Reference in New Issue
Block a user