Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 23 additions & 36 deletions App/StudyAppApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,26 +134,30 @@ struct StudyAppApp: App {
BackupCommands(viewModel: backupViewModel)
}

MenuBarExtra(isInserted: menuBarItemInserted) {
MenuBarContent(
summary: menuBarSummary,
onOpenLecture: { moduleID in
Self.bringToFront()
navigation.show(module: moduleID, section: .slides, intent: .resumeSlides)
},
onReviewCards: {
Self.bringToFront()
// Starts with the module that has the most due cards, like „Lernen starten“ on Today.
if let moduleID = todayViewModel.dueCounts.max(by: { $0.value < $1.value })?.key {
navigation.show(module: moduleID, section: .flashcards)
}
},
onOpenApp: { Self.bringToFront() }
)
.tint(Theme.accent)
.preferredColorScheme(appearance.colorScheme)
MenuBarExtra(isInserted: $showsMenuBarItem) {
MenuBarSummaryReader(calendar: calendarViewModel, today: todayViewModel) { summary in
MenuBarContent(
summary: summary,
onOpenLecture: { moduleID in
Self.bringToFront()
navigation.show(module: moduleID, section: .slides, intent: .resumeSlides)
},
onReviewCards: {
Self.bringToFront()
// Starts with the module that has the most due cards, like „Lernen starten“ on Today.
if let moduleID = todayViewModel.dueCounts.max(by: { $0.value < $1.value })?.key {
navigation.show(module: moduleID, section: .flashcards)
}
},
onOpenApp: { Self.bringToFront() }
)
.tint(Theme.accent)
.preferredColorScheme(appearance.colorScheme)
}
} label: {
MenuBarLabel(summary: menuBarSummary)
MenuBarSummaryReader(calendar: calendarViewModel, today: todayViewModel) { summary in
MenuBarLabel(summary: summary)
}
}
.menuBarExtraStyle(.window)

Expand Down Expand Up @@ -193,23 +197,6 @@ struct StudyAppApp: App {
await notificationsViewModel.refresh()
}

/// Keeps the menu bar item out of the unit test host: on CI runners its status item
/// blocks the launch, so the test runner never connects.
private var menuBarItemInserted: Binding<Bool> {
Binding(
get: { showsMenuBarItem && !Self.isRunningAsTestHost },
set: { showsMenuBarItem = $0 }
)
}

private static var isRunningAsTestHost: Bool {
ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil
}

private var menuBarSummary: MenuBarSummary {
MenuBarSummary(calendar: calendarViewModel, dueCards: todayViewModel.totalDue)
}

/// Shows the main window, e.g. after a click in the menu bar.
private static func bringToFront() {
NSApplication.shared.activate()
Expand Down
12 changes: 12 additions & 0 deletions Presentation/MenuBar/MenuBarContent.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import AppKit
import SwiftUI

/// Builds the summary inside a view, so only the menu bar item observes the calendar and the due cards.
/// Reading them in the App body would rebuild every scene on each change, already while the app launches.
struct MenuBarSummaryReader<Content: View>: View {
let calendar: StudyCalendarViewModel
let today: TodayViewModel
@ViewBuilder let content: (MenuBarSummary) -> Content

var body: some View {
content(MenuBarSummary(calendar: calendar, dueCards: today.totalDue))
}
}

/// The menu bar item's label: an icon, plus the remaining minutes while a lecture runs.
struct MenuBarLabel: View {
let summary: MenuBarSummary
Expand Down
Loading