Skip to content

Commit 112f87b

Browse files
committed
refactor notification-macos
1 parent 4e5bf8f commit 112f87b

17 files changed

+1410
-1252
lines changed

apps/desktop/src/contexts/listener.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ const useHandleDetectEvents = (store: ListenerStore) => {
7474
message: "Mic started",
7575
timeout: { secs: 8, nanos: 0 },
7676
event_id: null,
77+
start_time: null,
78+
participants: null,
79+
event_details: null,
80+
action_label: null,
7781
});
7882
}, 2000);
7983
});

apps/desktop/src/services/event-notification/index.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { commands as notificationCommands } from "@hypr/plugin-notification";
1+
import {
2+
type EventDetails,
3+
commands as notificationCommands,
4+
type Participant,
5+
} from "@hypr/plugin-notification";
26

37
import type * as main from "../../store/tinybase/store/main";
48
import type * as settings from "../../store/tinybase/store/settings";
@@ -11,6 +15,46 @@ const NOTIFIED_EVENTS_TTL_MS = 10 * 60 * 1000; // 10 minutes TTL for cleanup
1115

1216
export type NotifiedEventsMap = Map<string, number>;
1317

18+
function getSessionIdForEvent(
19+
store: main.Store,
20+
eventId: string,
21+
): string | null {
22+
let sessionId: string | null = null;
23+
store.forEachRow("sessions", (rowId, _forEachCell) => {
24+
const session = store.getRow("sessions", rowId);
25+
if (session?.event_id === eventId) {
26+
sessionId = rowId;
27+
}
28+
});
29+
return sessionId;
30+
}
31+
32+
function getParticipantsForSession(
33+
store: main.Store,
34+
sessionId: string,
35+
): Participant[] {
36+
const participants: Participant[] = [];
37+
38+
store.forEachRow("mapping_session_participant", (mappingId, _forEachCell) => {
39+
const mapping = store.getRow("mapping_session_participant", mappingId);
40+
if (mapping?.session_id !== sessionId) return;
41+
42+
const humanId = mapping.human_id as string | undefined;
43+
if (!humanId) return;
44+
45+
const human = store.getRow("humans", humanId);
46+
if (!human) return;
47+
48+
participants.push({
49+
name: (human.name as string) || null,
50+
email: (human.email as string) || "",
51+
status: "Accepted",
52+
});
53+
});
54+
55+
return participants;
56+
}
57+
1458
export function checkEventNotifications(
1559
store: main.Store,
1660
settingsStore: settings.Store,
@@ -47,12 +91,32 @@ export function checkEventNotifications(
4791
const title = String(event.title || "Upcoming Event");
4892
const minutesUntil = Math.ceil(timeUntilStart / 60000);
4993

94+
const eventDetails: EventDetails = {
95+
what: title,
96+
timezone: null,
97+
location:
98+
(event.meeting_link as string) || (event.location as string) || null,
99+
};
100+
101+
let participants: Participant[] | null = null;
102+
const sessionId = getSessionIdForEvent(store, eventId);
103+
if (sessionId) {
104+
const sessionParticipants = getParticipantsForSession(store, sessionId);
105+
if (sessionParticipants.length > 0) {
106+
participants = sessionParticipants;
107+
}
108+
}
109+
50110
void notificationCommands.showNotification({
51111
key: notificationKey,
52112
title: title,
53113
message: `Starting in ${minutesUntil} minute${minutesUntil !== 1 ? "s" : ""}`,
54114
timeout: { secs: 30, nanos: 0 },
55115
event_id: eventId,
116+
start_time: Math.floor(startTime.getTime() / 1000),
117+
participants: participants,
118+
event_details: eventDetails,
119+
action_label: "Start listening",
56120
});
57121
} else if (timeUntilStart <= 0) {
58122
notifiedEvents.delete(notificationKey);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import Cocoa
2+
3+
enum Layout {
4+
static let notificationWidth: CGFloat = 344
5+
static let notificationHeight: CGFloat = 64
6+
static let expandedNotificationHeight: CGFloat = 380
7+
static let rightMargin: CGFloat = 15
8+
static let topMargin: CGFloat = 15
9+
static let slideInOffset: CGFloat = 10
10+
static let buttonOverhang: CGFloat = 8
11+
static let cornerRadius: CGFloat = 14
12+
static let contentPaddingHorizontal: CGFloat = 12
13+
static let contentPaddingVertical: CGFloat = 9
14+
static let expandedPaddingHorizontal: CGFloat = 16
15+
static let expandedPaddingVertical: CGFloat = 14
16+
}
17+
18+
enum Timing {
19+
static let slideIn: TimeInterval = 0.3
20+
static let expansion: TimeInterval = 0.25
21+
static let fadeIn: TimeInterval = 0.15
22+
static let dismiss: TimeInterval = 0.2
23+
static let buttonPress: TimeInterval = 0.08
24+
static let hoverFade: TimeInterval = 0.15
25+
}
26+
27+
enum Fonts {
28+
static let titleSize: CGFloat = 14
29+
static let titleWeight: NSFont.Weight = .semibold
30+
static let bodySize: CGFloat = 11
31+
static let bodyWeight: NSFont.Weight = .regular
32+
static let buttonSize: CGFloat = 12
33+
static let buttonWeight: NSFont.Weight = .medium
34+
static let expandedTitleSize: CGFloat = 15
35+
static let detailLabelSize: CGFloat = 11
36+
static let detailValueSize: CGFloat = 12
37+
static let actionButtonSize: CGFloat = 13
38+
}
39+
40+
enum Colors {
41+
static let buttonNormalBg = NSColor(calibratedWhite: 0.95, alpha: 0.9).cgColor
42+
static let buttonPressedBg = NSColor(calibratedWhite: 0.85, alpha: 0.9).cgColor
43+
static let notificationBg = NSColor(calibratedWhite: 0.92, alpha: 0.85).cgColor
44+
static let actionButtonBg = NSColor(calibratedWhite: 0.35, alpha: 0.95).cgColor
45+
static let closeButtonHoverBg = NSColor(calibratedWhite: 0.95, alpha: 1.0).cgColor
46+
static let closeButtonPressedBg = NSColor(calibratedWhite: 0.9, alpha: 1.0).cgColor
47+
}
48+
49+
enum CloseButtonConfig {
50+
static let size: CGFloat = 20
51+
static let symbolPointSize: CGFloat = 9
52+
}
53+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import Cocoa
2+
3+
struct Participant: Codable {
4+
let name: String?
5+
let email: String
6+
let status: String
7+
}
8+
9+
struct EventDetails: Codable {
10+
let what: String
11+
let timezone: String?
12+
let location: String?
13+
}
14+
15+
struct NotificationPayload: Codable {
16+
let key: String
17+
let title: String
18+
let message: String
19+
let timeoutSeconds: Double
20+
let startTime: Int64?
21+
let participants: [Participant]?
22+
let eventDetails: EventDetails?
23+
let actionLabel: String?
24+
}
25+
26+
enum ParticipantStatusDisplay {
27+
case accepted
28+
case maybe
29+
case declined
30+
31+
init(from string: String) {
32+
switch string.lowercased() {
33+
case "accepted": self = .accepted
34+
case "maybe": self = .maybe
35+
case "declined": self = .declined
36+
default: self = .accepted
37+
}
38+
}
39+
40+
var icon: String {
41+
switch self {
42+
case .accepted: return ""
43+
case .maybe: return "?"
44+
case .declined: return ""
45+
}
46+
}
47+
48+
var color: NSColor {
49+
switch self {
50+
case .accepted: return NSColor.systemGreen
51+
case .maybe: return NSColor.systemYellow
52+
case .declined: return NSColor.systemRed
53+
}
54+
}
55+
}
56+

0 commit comments

Comments
 (0)