summaryrefslogtreecommitdiff
path: root/src/components/NotificationOverlay.tsx
blob: 2b8861c9211f6fc98e7a54d316ed884efeefa8f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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',
          textAlign: 'center',
          gap: '8px',
          animation: 'slideDown 0.15s ease',
          minWidth: '280px',
          maxWidth: 'calc(100vw - 32px)',
        }}
      >
        <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>
  );
}