How to Implement Recording Start/Stop Notifications in Meetily

Meetily implements recording notifications through a Rust Tauri backend that emits Tauri events and displays OS-level notifications via the NotificationManager, while respecting user preferences stored in the local database.

Meetily is an open-source meeting assistant built with Tauri and React that requires real-time user feedback when recording sessions change state. Implementing recording start/stop notifications involves coordinating the audio command layer, a dedicated notification manager, and user preference settings. This guide walks through the exact implementation using the actual source code from the Zackriya-Solutions/meetily repository.

How Meetily's Notification Architecture Works

The notification system spans both the Rust backend and React frontend. According to the Meetily source code, the NotificationManager in frontend/src-tauri/src/notifications/manager.rs acts as the central authority for displaying OS notifications. When recording commands execute in frontend/src-tauri/src/audio/recording_commands.rs, they emit Tauri events and simultaneously trigger the notification manager to show alerts.

Core Components

  • NotificationManager: Handles OS notification display via tauri-plugin-notification
  • Recording Commands: Emit lifecycle events (recording-started, recording-stopped, etc.)
  • User Settings: Control which notifications appear via frontend/src-tauri/src/notifications/settings.rs
  • Frontend Listeners: React hooks consume events for UI updates

Triggering Notifications from Recording Commands

Recording lifecycle events originate in frontend/src-tauri/src/audio/recording_commands.rs. After successfully starting, stopping, pausing, or resuming a recording, the command emits a Tauri event and invokes the notification manager.

// frontend/src-tauri/src/audio/recording_commands.rs
pub async fn start_recording(app: &AppHandle) -> Result<()> {
    // ... audio pipeline start logic ...
    let meeting = current_meeting_name().await; // e.g., Some("Team Sync")
    
    // Emit event for frontend consumption
    app.emit_all("recording-started", &meeting)?;
    
    // Show OS notification (respects user settings)
    let notif_mgr = NotificationManager::new(app.clone()).await?;
    notif_mgr.show_recording_started(meeting).await?;
    Ok(())
}

The same pattern applies to stop_recording, pause_recording, and resume_recording, emitting recording-stopped, recording-paused, and recording-resumed events respectively.

Creating Notification Payloads

The Notification enum in frontend/src-tauri/src/notifications/types.rs defines the structure of alerts. Helper constructors format user-friendly messages that optionally include the meeting name.

// frontend/src-tauri/src/notifications/types.rs
pub enum Notification {
    RecordingStarted { meeting_name: Option<String> },
    RecordingStopped { meeting_name: Option<String> },
    RecordingPaused { meeting_name: Option<String> },
    RecordingResumed { meeting_name: Option<String> },
}

impl Notification {
    pub fn recording_started(meeting_name: Option<String>) -> Self {
        Self::RecordingStarted { meeting_name }
    }
    
    // Returns title and body for the OS notification
    pub fn title(&self) -> String {
        match self {
            Self::RecordingStarted { .. } => "Recording Started".to_string(),
            Self::RecordingStopped { .. } => "Recording Stopped".to_string(),
            _ => "Recording Update".to_string(),
        }
    }
    
    pub fn body(&self) -> String {
        match self {
            Self::RecordingStarted { meeting_name } => {
                match meeting_name {
                    Some(name) => format!("Meeting: {}", name),
                    None => "Recording has begun".to_string(),
                }
            }
            // ... additional variants
        }
    }
}

Respecting User Preferences

Before displaying any notification, the NotificationManager checks user settings defined in frontend/src-tauri/src/notifications/settings.rs. Users can disable the entire notification system or specific event types.

Key settings structure:

  • recording_notifications: Master toggle for all recording alerts
  • show_recording_started: Specific toggle for start events
  • show_recording_stopped: Specific toggle for stop events
  • show_recording_paused and show_recording_resumed: Granular controls for pause/resume
// frontend/src-tauri/src/notifications/manager.rs
pub async fn show_recording_started(&self, meeting_name: Option<String>) -> Result<()> {
    let settings = self.settings.lock().await;
    
    // Check if user disabled this specific notification
    if !settings.notification_preferences.show_recording_started {
        return Ok(()); // Silently skip
    }
    
    let notification = Notification::recording_started(meeting_name);
    self.send(notification).await
}

Frontend Integration

The React frontend listens for Tauri events to update UI state (such as showing toast badges) while the backend handles OS-level notifications. Listen for events in components like src/components/Sidebar/SidebarProvider.tsx.

// Frontend React component
import { listen } from '@tauri-apps/api/event';

useEffect(() => {
  const unlisten = await listen<string>('recording-started', (event) => {
    toast.success(`Recording started${event.payload ? `: ${event.payload}` : ''}`);
    // Update sidebar state or recording indicator
  });
  
  return () => { unlisten(); };
}, []);

To trigger recording from the frontend and initiate the notification flow:

import { invoke } from '@tauri-apps/api/tauri';

await invoke('start_recording', {
  mic_device_name: selectedMic,
  system_device_name: selectedSystem,
  meeting_name: "Team Sync"
});

Complete Implementation Checklist

To add notification support for recording events in Meetily:

  1. Emit Tauri events from frontend/src-tauri/src/audio/recording_commands.rs using app.emit_all()
  2. Call notification methods such as NotificationManager::show_recording_started() immediately after successful command execution
  3. Define notification payloads in frontend/src-tauri/src/notifications/types.rs with clear titles and optional meeting context
  4. Check user preferences in frontend/src-tauri/src/notifications/settings.rs before displaying any OS notification
  5. Listen for events in the frontend using Tauri's listen API to update React state and display in-app toasts

Summary

Frequently Asked Questions

Where does Meetily store notification preferences?

Notification preferences are persisted in the local database via the settings repository at frontend/src-tauri/src/database/repositories/setting.rs. The NotificationSettings struct in frontend/src-tauri/src/notifications/settings.rs defines the available toggles, including recording_notifications as the master switch and specific flags like show_recording_started for individual events.

Can I customize the notification message content?

Yes. Modify the Notification enum constructors in frontend/src-tauri/src/notifications/types.rs. The recording_started and recording_stopped helper methods accept an optional meeting_name parameter and format the notification title and body. You can extend these constructors to include additional context such as timestamps or participant counts.

What events trigger notifications in Meetily?

The system emits notifications for four recording lifecycle events: recording-started, recording-stopped, recording-paused, and recording-resumed. Each event corresponds to a command in frontend/src-tauri/src/audio/recording_commands.rs and has dedicated methods in the NotificationManager (e.g., show_recording_started(), show_recording_stopped()).

How do I disable notifications for specific recording events only?

Users can disable specific event types without turning off all notifications. The NotificationManager checks individual boolean flags from settings.notification_preferences (such as show_recording_started or show_recording_stopped) before displaying each notification type. Set these flags to false in the settings UI to suppress specific alerts while keeping others active.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →