diff options
Diffstat (limited to 'src/components/NotificationOverlay.tsx')
| -rw-r--r-- | src/components/NotificationOverlay.tsx | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/src/components/NotificationOverlay.tsx b/src/components/NotificationOverlay.tsx new file mode 100644 index 0000000..965f19d --- /dev/null +++ b/src/components/NotificationOverlay.tsx @@ -0,0 +1,117 @@ +import { useEffect, useRef } from 'react'; +import { sendNotification } from '@tauri-apps/plugin-notification'; +import { useTaskStore } from '../store/taskStore'; + +interface NotificationOverlayProps { + visible: boolean; + taskId: string | null; + onDismiss: () => void; +} + +export function NotificationOverlay({ visible, taskId, onDismiss }: NotificationOverlayProps) { + const tasks = useTaskStore((s) => s.tasks); + const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null); + + const task = tasks.find((t) => t.id === taskId) ?? null; + + useEffect(() => { + if (!visible) return; + + // OS notification + const body = task ? `"${task.name}" session complete.` : 'Session complete. Time for a break.'; + try { + sendNotification({ title: 'Solstice', body }); + } catch { + // Notifications may not be permitted in dev; ignore errors + } + + // Auto-dismiss after 4 seconds + timerRef.current = setTimeout(() => { + onDismiss(); + }, 4000); + + return () => { + if (timerRef.current) { + clearTimeout(timerRef.current); + } + }; + }, [visible, task, onDismiss]); + + if (!visible) return null; + + return ( + <div + onClick={onDismiss} + style={{ + position: 'fixed', + inset: 0, + display: 'flex', + alignItems: 'flex-start', + justifyContent: 'center', + paddingTop: '40px', + zIndex: 200, + pointerEvents: 'auto', + }} + > + <div + onClick={(e) => e.stopPropagation()} + style={{ + background: 'var(--ink-2)', + border: '1px solid var(--line-2)', + borderRadius: 'var(--r-3)', + boxShadow: 'var(--shadow-3)', + padding: '24px 32px', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: '8px', + animation: 'slideDown 0.2s ease', + minWidth: '280px', + }} + > + <span + style={{ + fontFamily: 'var(--font-display)', + fontStyle: 'italic', + fontSize: '28px', + fontWeight: 400, + color: 'var(--fg-1)', + lineHeight: 1.1, + }} + > + Session complete + </span> + {task && ( + <span + style={{ + fontFamily: 'var(--font-sans)', + fontSize: '13px', + color: 'var(--fg-3)', + }} + > + {task.name} + </span> + )} + <span + style={{ + fontFamily: 'var(--font-sans)', + fontSize: '11px', + color: 'var(--fg-4)', + marginTop: '4px', + textTransform: 'uppercase', + letterSpacing: '0.1em', + }} + > + Click to dismiss + </span> + </div> + + <style>{` + @keyframes slideDown { + from { opacity: 0; transform: translateY(-16px); } + to { opacity: 1; transform: translateY(0); } + } + `}</style> + </div> + ); +} |
