MeetingBar Documentation

repository·master·Indexed 26 days ago

https://github.com/leits/meetingbar

A lightweight macOS menu-bar application for displaying and joining calendar meetings. It integrates with macOS Calendar and Google Calendar, supporting over 50 meeting services. Documentation covers installation via Mac App Store and Homebrew, AppleScript and Shortcuts automation, and detailed technical architecture including the 'policy + adapter' pattern, calendar synchronization, and notification reconciliation.

Tokens
3.4K
Snippets
3
Records
19
Agent score
40%

What's inside MeetingBar

  1. Understand the MeetingBar Architecture and Data Flow

    master

    MeetingBar is a macOS menu-bar app (AppKit/SwiftUI) that synchronizes calendar data (Apple Calendar via EventKit and Google Calendar via OAuth2) to display upcoming events in the system status bar.

    Top-level Data Flow

    1. Calendar Providers: EKEventStore (Apple) and GCEventStore (Google) fetch data.
    2. CalendarSync: A @MainActor ObservableObject that uses a Combine pipeline to merge triggers (Defaults changes, a 3-minute timer, and manual refreshes). It throttles updates and publishes [MBCalendar], [MBEvent], and ProviderHealth.
    3. AppModel: An @MainActor ObservableObject that holds the AppState (events, calendars, active provider) driven by AppAction via AppEnvironment.
    4. Consumers:
      • StatusBarItemController & MenuBuilder: Render the menu bar based on StatusBarMenuState.
      • NotificationScheduler: Manages event-based notifications and delayed tasks.
  2. Explore the MeetingBar Directory Structure

    master

    The codebase is organized into functional modules:

    • App/: Process lifecycle, OS integration, AppModel, AppIntent (Shortcuts), and URLHandler.
    • Calendar/: Calendar data models (MBEvent, MBCalendar), filtering, and providers (EventKit, Google). CalendarSync manages the refresh pipeline.
    • Meetings/: Meeting URL detection and opening logic (MeetingOpener, MeetingLinkDetector).
    • Notifications/: Scheduling and reconciling UNNotification requests (NotificationScheduler, NotificationActionRunner).
    • Settings/: Persistent settings via AppSettings.
    • UI/:
      • StatusBar/: Menu bar item and menu construction.
      • Views/: SwiftUI views (e.g., DayTimelineView, FullscreenNotification).
    • Utilities/: Logging, Keychain, and AppleScript runners.
  3. Automate MeetingBar with AppleScript and Shortcuts

    master
    MeetingBar can be integrated into your workflows using macOS Shortcuts and AppleScript. You can run custom AppleScript commands to trigger actions, such as pausing music when joining a meeting.
  4. Implement Settings with Defaults discipline

    master

    All persistent settings are managed via the Defaults library and defined in Extensions/DefaultsKeys.swift.

    The Rule: Read Defaults only at boundaries (in adapters), never deep inside pure policies. This ensures policies remain hostless-testable.

    To add a setting to a policy, create a settings struct and a static var current factory in the adapter file that snapshots the required Defaults values.

    extension StatusBarPresentationSettings {
        static var current: StatusBarPresentationSettings {
            StatusBarPresentationSettings(
                hasSelectedCalendars: !Defaults[.selectedCalendarIDs].isEmpty,
                showEventMaxTimeUntilEventEnabled: Defaults[.showEventMaxTimeUntilEventEnabled],
                showEventMaxTimeUntilEventThreshold: Defaults[.showEventMaxTimeUntilEventThreshold]
            )
        }
    }
  5. Configure Calendar Providers

    master

    MeetingBar supports two primary calendar sources. After installation, follow the onboarding process to select your preferred provider:

    • macOS Calendar: Uses any account synchronized with Calendar.app (e.g., iCloud, Google, Exchange, Office 365, Yahoo, AOL).
    • Google Calendar: Connects directly to Google Calendar via OAuth.
  6. Install MeetingBar

    master

    MeetingBar requires macOS 12.0 or later. You can install it using one of the following methods:

    • Mac App Store: Download directly from the Mac App Store.
    • Homebrew: Use the --cask flag to install.
    • Manual download: Download the latest .dmg file from the GitHub releases page.
    brew install --cask meetingbar
  7. Implement a new Calendar Provider

    master

    To add a new calendar provider (e.g., Microsoft Graph), implement the EventStore protocol. You must map the provider's specific events into the MBEvent type and expose calendars as MBCalendar.

    Crucial Rule: Do not allow provider-specific types to leak past the EventStore boundary. The rest of the application must interact only with provider-agnostic types to maintain architecture integrity.

  8. Implement new logic using the 'policy + adapter' pattern

    master

    To keep logic testable and fast, MeetingBar uses a separation between pure decision-making (policies) and app-specific side effects (adapters).

    1. Create the Policy (Foo.swift): This file lives in the MeetingBarLogic SwiftPM target. It must only import Foundation. It should contain plain data (structs/enums) and pure functions that return values based on primitive inputs (Date, Int, String). Do not import Defaults, AppKit, or MBEvent here.
    2. Create the Adapter (Foo+MeetingBar.swift): This file bridges the policy to the main app. It can import Defaults, MBEvent, and AppKit. Use this file to implement extension FooSettings { static var current: FooSettings { ... } } to read from Defaults, and to write mappers like init(MBEvent).
    3. Testing: Write tests for the pure logic in MeetingBarLogicTests/ using hostless tests. This allows for fast execution without calendar permissions or AppKit requirements.
  9. Build, Test, and Lint the project

    master

    Use the following make commands to manage the development lifecycle:

    | Command | Description | |---|---|<br>|---|---|make build | Debug build | | make build-quiet | Debug build with filtered output | | make build-release | Release build | | make test | Full suite with coverage (host + logic) | | make test-quiet | Full suite with filtered output | | make test-logic | Hostless logic tests only (fast) | | make test-logic-quiet | Hostless logic tests with filtered output | | make lint | Run SwiftLint | | make validate-strings | Verify every .loco() key exists in en.lproj/Localizable.strings | | make open | Open the project in Xcode |

    make build
    make test
    make lint
    make validate-strings
  10. Run Hostless vs Host tests

    master

    MeetingBar distinguishes between tests that require the full app environment and those that run against pure logic.

    SuiteLocationCommandUse Case
    HostlessMeetingBarLogicTests/make test-logicPolicies, formatters, link detection, plan generation
    HostMeetingBarTests/make testMenuBuilder, status item rendering, AppKit/NSImage usage

    Best Practice: Default to hostless tests. If a test requires launching the app, it is a signal that you are testing a service rather than a pure policy.

  11. Follow the Architecture Rule for New Work

    master

    When adding new features, follow this target shape to maintain simplicity:

    1. UI sends actions.
    2. AppModel coordinates.
    3. Feature components own workflows.
    4. Policies decide.
    5. macOS integrations execute side effects.

    Guidelines:

    • Use existing MeetingBar naming conventions (e.g., CalendarSync, MeetingOpener, NotificationScheduler, AppSettings, WindowCoordinator) instead of generic architectural labels.
    • A new type should only be created if it provides a workflow a clear owner or makes a decision testable.
  12. Understand the Notification Reconciler model

    master

    MeetingBar uses a reconciliation model for notifications rather than scheduling them directly upon every event change.

    1. Planning: NotificationPlanner.plan takes events and settings to produce a [NotificationPlan] containing specific kinds like .eventStart or .eventEnd.
    2. Reconciliation: NotificationScheduler.reconcile compares the desired plan against the actual pending requests in UNUserNotificationCenter.
    3. Identifiers: Notifications use stable identifiers following the pattern mb-plan-<eventID>-<kind>. This makes the reconciliation process idempotent; calling it multiple times with the same state results in no changes.
    4. Actions: NotificationActionRunner handles in-app actions (fullscreen, auto-join, etc.). It uses a NotificationRecordStore to ensure actions are not re-fired during subsequent reconciliations.