Calendar Integration UX Patterns for Privacy-First Desktop Meeting Apps — Auto-Capture, Sync, and Notification Design
Calendar Integration UX for Privacy-First Desktop Meeting Apps
Date: 2026-03-20 Context: Supports MOKA-272 (calendar integration frontend + meeting auto-capture magic moment) and MOKA-265 (Remindr MVP gates). Remindr is a Tauri 2 desktop app with local-first audio capture (Whisper + pyannote), processing everything on-device.
Executive Summary
- The “magic moment” for meeting apps is zero-friction auto-capture — the app detects a meeting, starts recording, and delivers structured notes without the user doing anything
- Two dominant architectures: bot-based (Otter, Fireflies, tl;dv) vs local capture (Granola, Krisp, Remindr). Bot-based has UX friction (visible to participants); local capture is invisible and privacy-preserving
- Remindr’s competitive edge: 100% local processing (Whisper + pyannote + llama.cpp) vs Granola (local capture but cloud transcription). This is the strongest privacy story in the market
- Calendar integration via OAuth2 PKCE (Google Calendar) + CalDAV (Apple Calendar) is the standard for desktop apps. Tauri has proven patterns via
tauri-plugin-google-authand local redirect server - The ideal UX flow: Calendar sync -> Meeting detection -> Notification toast -> One-click or auto-start -> Auto-stop -> Notes generation — all without leaving the desktop
1. Competitive Landscape: Calendar Integration Approaches
Bot-Based Tools (Join meeting as participant)
| Tool | Calendar Method | Detection | Start Flow | Privacy |
|---|---|---|---|---|
| Otter.ai | OAuth (Google, Outlook) | Auto-detect from calendar | Bot auto-joins via calendar link | Bot visible to all participants |
| Fireflies.ai | OAuth (Google, Outlook, iCloud) | Calendar event scan | Bot joins automatically | Bot visible, can be blocked by host |
| tl;dv | Google Meet/Zoom native | Calendar integration | Bot records via platform | Bot visible |
| Fathom | Zoom/Google Meet | Plugin + calendar | Auto-record via plugin | Plugin visible in toolbar |
Local Capture Tools (No meeting bot)
| Tool | Calendar Method | Detection | Start Flow | Privacy |
|---|---|---|---|---|
| Granola | Manual start (no calendar sync) | None — user clicks “Start” | Manual activation required | Invisible to participants, but cloud transcription |
| Krisp | Calendar-aware notifications | Calendar scan | Notification prompt | Local noise processing, cloud for notes |
| MacWhisper | Calendar event matching | Meeting detected notification | Auto-start with countdown | Local Whisper transcription |
| Circleback | Calendar sync (Google/Outlook) | Auto-detect from calendar | Auto-start with “Don’t Record” option | Local capture, cloud processing |
| Remindr | To be built (MOKA-272) | Calendar event + audio activity | Notification + auto/manual | 100% local — strongest in market |
Key Insight
Granola — the market leader in local capture — deliberately chose no calendar integration, relying on manual start. This is both a strength (simplicity) and a weakness (friction). Remindr can differentiate by combining Granola’s invisible capture with intelligent calendar-driven auto-detection.
2. Recommended UX Architecture for Remindr
The “Magic Moment” Flow
Calendar Event Approaching (T-2min)
|
v
[Notification Toast]
"Meeting: Weekly Standup (2:00 PM)"
[Start Memory] [Skip] [Auto-start ON]
|
v (if auto-start or user clicks)
[Recording Indicator]
Minimal tray icon + subtle pulse
Audio capture: mic + system audio
|
v (meeting ends or silence detected)
[Auto-Stop + Processing]
Whisper transcription (local)
pyannote diarization (local)
llama.cpp summarization (local)
|
v
[Memory Created Notification]
"Memory saved: Weekly Standup"
[View] [Dismiss]
Three Operating Modes
| Mode | Behavior | Target User |
|---|---|---|
| Manual | Click “Start Memory” for each meeting | Privacy-conscious, selective recording |
| Notify + Confirm | Notification toast before each calendar meeting, one-click to start | Default — balances automation with control |
| Auto-Capture | Automatically starts recording when calendar meeting + audio activity detected | Power users who want zero-friction |
Default should be “Notify + Confirm” — gives users control while reducing friction. Auto-capture is opt-in due to Remindr’s privacy-first positioning.
3. Calendar Sync Implementation Patterns
Google Calendar (Primary)
Method: OAuth2 with PKCE via local redirect server
Tauri Implementation:
1. User clicks "Connect Google Calendar" in Settings
2. Tauri spawns tiny local HTTP server (e.g., port 48271)
3. Opens browser: accounts.google.com/o/oauth2/v2/auth
- client_id: Remindr desktop app
- redirect_uri: http://localhost:48271/callback
- scope: calendar.readonly, calendar.events.readonly
- code_challenge: PKCE S256
4. User grants permission in browser
5. Browser redirects to localhost, Tauri captures auth code
6. Exchange code for access + refresh tokens
7. Store tokens in local encrypted store (Tauri keyring)
8. Sync events via Google Calendar API (polling every 5min)
Existing Tauri ecosystem support:
tauri-plugin-google-auth(crates.io) — full OAuth2 PKCE for Tauri v2webbrowsercrate for cross-platform browser launchtiny_httpfor local redirect server
Permissions needed: calendar.readonly and calendar.events.readonly — read-only, no write access. This aligns with privacy-first positioning.
Apple Calendar (Secondary)
Method: Local CalDAV/EventKit (no cloud auth needed for on-device calendars)
macOS Implementation:
1. Request calendar access via macOS EventKit (Rust FFI or Swift bridge)
2. User sees native macOS permission dialog
3. Read events from all local calendars (iCloud, Google via CalDAV, Exchange)
4. No tokens needed — OS-level permission
Advantage: On macOS, Apple Calendar already aggregates Google/Outlook/Exchange calendars. If user has Google Calendar synced to Apple Calendar, Remindr can access it via EventKit without separate Google OAuth.
Tauri 2 approach: Use Tauri’s Swift plugin bridge (tauri-plugin-swift) to call EventKit APIs from Rust.
Microsoft Outlook (Tertiary)
Method: Microsoft Graph API with OAuth2 PKCE (same pattern as Google)
Scope: Calendars.Read (read-only)
Priority Order
- Google Calendar via OAuth2 PKCE — largest user base, well-documented Tauri plugins
- Apple Calendar via EventKit — native macOS feel, zero-friction for Apple users
- Outlook via Microsoft Graph — enterprise users, post-MVP
4. Meeting Detection & Auto-Start Patterns
Detection Signals
| Signal | Reliability | Implementation |
|---|---|---|
| Calendar event time match | High | Compare current time with synced events (T-2min to T+5min window) |
| Calendar event has meeting link | High | Regex match for zoom.us, meet.google.com, teams.microsoft.com URLs |
| Audio activity detected | Medium | Monitor system audio output for voice activity (VAD) |
| Meeting app process running | Medium | Check for Zoom, Meet, Teams processes (macOS: NSWorkspace, Windows: process list) |
| Microphone active | Medium | Monitor microphone usage (Notion uses this pattern) |
Recommended combination: Calendar event time + meeting link + audio activity = high-confidence meeting detection.
Notification Toast Design
Based on competitive analysis (MacWhisper, Circleback, Notion patterns):
Timing: Appears T-1min before calendar event start (or immediately when audio detected during event window)
Layout:
+------------------------------------------+
| [Remindr icon] Meeting Detected |
| |
| Weekly Standup with @team |
| 2:00 PM - 2:30 PM |
| |
| [Start Memory] [Skip] [Settings] |
| |
| [ ] Always auto-capture this meeting |
+------------------------------------------+
Behavior:
- Auto-dismiss after 10 seconds (MacWhisper pattern) if no action → default to NOT recording (privacy-first)
- “Always auto-capture” checkbox = per-event-series opt-in for recurring meetings
- Toast position: top-right (macOS convention), above other notifications
- Sound: None (meeting is about to start, don’t add noise)
Auto-Stop Signals
| Signal | Trigger |
|---|---|
| Calendar event end time reached | Primary — stop at scheduled end + 5min grace |
| Extended silence detected (>60s) | Secondary — meeting likely ended early |
| Meeting app process closed | Secondary — user left the call |
| User manually stops | Always available via tray icon or keyboard shortcut |
Grace period: 5 minutes after scheduled end before auto-stop, with notification: “Meeting ran over. [Keep Recording] [Stop & Save]“
5. Post-Meeting “Memory Created” Experience
This is the second magic moment — the user sees their meeting captured and summarized without any effort.
Flow
Meeting ends (auto-stop or manual)
|
v
[Processing indicator in tray]
"Creating memory..." (Whisper -> pyannote -> llama.cpp)
~30-90 seconds for a 30-min meeting (local processing)
|
v
[Notification Toast]
"Memory saved: Weekly Standup"
"5 action items detected"
[View Memory] [Dismiss]
|
v (user clicks View)
[Memory Detail View]
- Meeting title (from calendar)
- Participants (from calendar invite + diarization)
- Full transcript with speaker labels
- AI summary (key points, decisions, action items)
- Timeline with highlights
Participant Matching
Calendar integration enables a powerful feature: matching diarized speakers to calendar attendees.
Calendar attendees: [Alice, Bob, Carol]
Diarization output: [Speaker 1, Speaker 2, Speaker 3]
→ User maps once: Speaker 1 = Alice, Speaker 2 = Bob, Speaker 3 = Carol
→ Remindr remembers voice profiles for future meetings
This is a significant UX advantage over Granola (which has no calendar data for participant context).
6. Privacy Considerations for Calendar Integration
Data Minimization
| Data | Store locally? | Send to cloud? | Retention |
|---|---|---|---|
| Calendar events (title, time, attendees) | Yes (SQLite) | Never | Until user deletes |
| Meeting links/URLs | Yes (for detection) | Never | Ephemeral (deleted after meeting) |
| OAuth tokens | Yes (encrypted keyring) | Only to Google/Microsoft for auth | Until revoked |
| Audio recording | Yes (during processing) | Never | Deleted after transcription |
| Transcript + summary | Yes (SQLite) | Never (unless Cloud mode via Remindr Gateway) | Until user deletes |
User Controls
- Granular calendar selection: Choose which calendars to monitor (e.g., “Work” but not “Personal”)
- Per-meeting opt-out: “Skip” on notification toast
- Block list: Never prompt for meetings with specific attendees or titles matching patterns
- Meeting link filter: Only detect meetings on selected platforms (Zoom, Meet, Teams)
- Auto-capture requires explicit opt-in per meeting series, never globally default-on
Privacy Messaging
For the Settings UI:
“Remindr reads your calendar locally to detect when meetings start. Calendar data never leaves your device. You choose which meetings to capture — Remindr never records without your knowledge.”
7. Technical Architecture for Tauri 2
Component Diagram
+-------------------+ +---------------------+
| React Frontend | | Tauri Rust Core |
| | | |
| Calendar Settings |<--->| calendar_sync.rs |
| Meeting Notifier | | - OAuth2 PKCE |
| Memory Timeline | | - Event polling |
| | | - EventKit bridge |
+-------------------+ | |
| meeting_detector.rs |
| - Event time match |
| - Audio VAD |
| - Process monitor |
| |
| notification.rs |
| - Native toast |
| - Auto-dismiss timer |
+---------------------+
|
+---------------------+
| SQLite (local) |
| - calendar_events |
| - memories |
| - speaker_profiles |
| - capture_preferences|
+---------------------+
SQLite Schema Additions
-- Calendar sources (Google, Apple, Outlook)
CREATE TABLE calendar_sources (
id TEXT PRIMARY KEY,
provider TEXT NOT NULL, -- 'google' | 'apple' | 'outlook'
account_email TEXT,
encrypted_tokens TEXT, -- encrypted access + refresh tokens
calendars_selected TEXT, -- JSON array of calendar IDs to monitor
last_synced_at TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
-- Synced calendar events (ephemeral, rolling 7-day window)
CREATE TABLE calendar_events (
id TEXT PRIMARY KEY,
source_id TEXT REFERENCES calendar_sources(id),
external_id TEXT NOT NULL, -- Google/Apple event ID
title TEXT,
start_at TEXT NOT NULL,
end_at TEXT NOT NULL,
attendees TEXT, -- JSON array of {name, email}
meeting_link TEXT, -- extracted Zoom/Meet/Teams URL
auto_capture INTEGER DEFAULT 0, -- per-event-series preference
UNIQUE(source_id, external_id)
);
-- Link memories to calendar events
ALTER TABLE memories ADD COLUMN calendar_event_id TEXT REFERENCES calendar_events(id);
Tauri Commands
// Calendar management
#[tauri::command]
async fn connect_google_calendar() -> Result<CalendarSource, Error>;
#[tauri::command]
async fn disconnect_calendar(source_id: String) -> Result<(), Error>;
#[tauri::command]
async fn get_upcoming_meetings(hours: u32) -> Result<Vec<CalendarEvent>, Error>;
#[tauri::command]
async fn set_auto_capture(event_series_id: String, enabled: bool) -> Result<(), Error>;
// Meeting detection (called by background service)
#[tauri::command]
async fn check_meeting_status() -> Result<MeetingStatus, Error>;
// Returns: NoMeeting | MeetingDetected(event) | MeetingActive(event)
8. Implementation Priority
Phase 1: Calendar Sync (Week 1-2)
- Google Calendar OAuth2 PKCE connection
- Event polling (5-min interval)
- Settings UI: connect/disconnect, calendar selection
- SQLite schema for calendar_sources + calendar_events
Phase 2: Meeting Detection + Notification (Week 2-3)
- Background event time matching (T-2min detection window)
- Native notification toast with Start/Skip/Settings
- Meeting link extraction and platform detection
- Auto-dismiss after 10 seconds
Phase 3: Auto-Capture + Auto-Stop (Week 3-4)
- Audio activity detection (VAD) during meeting window
- Per-meeting-series auto-capture preference
- Auto-stop on calendar end time + 5min grace
- Silence detection fallback (60s threshold)
Phase 4: Participant Matching (Post-MVP)
- Calendar attendees → diarization speaker mapping
- Voice profile learning (map once, remember)
- Enriched memory view with named speakers
Phase 5: Apple Calendar + Outlook (Post-MVP)
- macOS EventKit integration via Tauri Swift bridge
- Microsoft Graph API OAuth2 PKCE
- Unified calendar view across providers
9. Key UX Decisions for Product Team
| Decision | Recommendation | Rationale |
|---|---|---|
| Default mode for new users | Notify + Confirm | Privacy-first: never record without explicit action |
| Auto-dismiss notification behavior | Don’t record if dismissed/ignored | Consent-by-action, not consent-by-inaction |
| Calendar sync frequency | Every 5 minutes | Balance between freshness and battery/API limits |
| Detection window | T-2min to T+5min | Covers early/late joiners without false positives |
| Auto-stop grace period | 5 minutes past scheduled end | Meetings often run over |
| Meeting link requirement | Optional (enhances confidence but not required) | Some meetings are phone calls or in-person |
| Recording indicator | Tray icon with subtle pulse | Always visible but non-intrusive (legal requirement in many jurisdictions) |
Sources
- Granola Security & Privacy Architecture
- Reclaim.ai: Top 18 AI Meeting Assistants (2026)
- Meetergo: Best AI Note Taker Apps (2026)
- Circleback: Record Meetings with Desktop App
- MacWhisper: Automatic Meeting Recordings
- DEV: Building Google Calendar OAuth for Desktop App (Tauri)
- tauri-plugin-google-auth (crates.io)
- Google: OAuth 2.0 for Desktop Apps
- ModelsLab: Parakeet.cpp vs Whisper (2026)
- OpenWhispr: Privacy-First Voice-to-Text
- Notion: AI Meeting Notes
- Eleken: Calendar UI Examples (33 Designs)
- Internal: MOKA-342 (Remindr Competitive Landscape), MOKA-343 (Desktop Monetization)