Skip to content
Open
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
42 changes: 42 additions & 0 deletions MeetingBar/Core/Models/MBEvent+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,46 @@ public extension Array where Element == MBEvent {
}
return nextEvent
}

/// Returns the event that is currently in progress.
///
/// Unlike `nextEvent()`, this does not depend on ongoing visibility settings.
/// - Parameter linkRequired: If `true`, only events with a meeting link are considered.
/// - Returns: The currently running event that passes filters, or `nil`.
func currentEvent(linkRequired: Bool = false) -> MBEvent? {
let now = Date()

for event in self {
guard event.startDate <= now, event.endDate > now else {
continue
}
if Defaults[.dismissedEvents].contains(where: { $0.id == event.id }) {
continue
}
if event.isAllDay {
continue
}
if event.meetingLink == nil, linkRequired {
continue
}
if event.participationStatus == .declined {
continue
}
if event.participationStatus == .pending,
Defaults[.showPendingEvents] == .hide || Defaults[.showPendingEvents] == .show_inactive {
continue
}
if event.participationStatus == .tentative,
Defaults[.showTentativeEvents] == .hide || Defaults[.showTentativeEvents] == .show_inactive {
continue
}
if event.status == .canceled {
continue
}

return event
}

return nil
}
}
7 changes: 7 additions & 0 deletions MeetingBar/Extensions/KeyboardShortcutsNames.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@ import KeyboardShortcuts
extension KeyboardShortcuts.Name: @unchecked @retroactive Sendable {}

extension KeyboardShortcuts.Name {
/// Global shortcut used to create an ad-hoc meeting.
static let createMeetingShortcut = Self("createMeetingShortcut")
/// Global shortcut used to open the status bar menu.
static let openMenuShortcut = Self("openMenuShortcut")
/// Global shortcut used to join the nearest meeting (current or next).
static let joinEventShortcut = Self("joinEventShortcut")
Comment on lines +18 to 19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix joinEventShortcut doc to match actual behavior.

The comment says “nearest meeting (current or next)”, but current wiring in MeetingBar/UI/StatusBar/StatusBarItemController.swift (setupKeyboardShortcuts) maps .joinEventShortcut to joinNextMeeting(). Update the doc to avoid API confusion.

✏️ Suggested doc fix
-    /// Global shortcut used to join the nearest meeting (current or next).
+    /// Global shortcut used to join the next meeting.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MeetingBar/Extensions/KeyboardShortcutsNames.swift` around lines 18 - 19,
Update the documentation for the static constant joinEventShortcut to reflect
its actual behavior: change the comment so it states that this global shortcut
triggers joining the next meeting (not the nearest/current), since
StatusBarItemController.setupKeyboardShortcuts maps .joinEventShortcut to
joinNextMeeting(); locate the constant in KeyboardShortcutsNames.swift
(joinEventShortcut) and replace the misleading phrase "nearest meeting (current
or next)" with wording that clearly indicates it starts/join the next scheduled
meeting.

/// Global shortcut used to join only the currently running meeting.
static let joinCurrentEventShortcut = Self("joinCurrentEventShortcut")
/// Global shortcut used to open a meeting link from clipboard.
static let openClipboardShortcut = Self("openClipboardShortcut")
/// Global shortcut used to toggle status bar meeting title visibility.
static let toggleMeetingTitleVisibilityShortcut = Self("toggleMeetingTitleVisibilityShortcut")
}
5 changes: 4 additions & 1 deletion MeetingBar/UI/StatusBar/MenuBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,13 @@ struct MenuBuilder {

let joinItem = NSMenuItem(
title: itemTitle,
action: #selector(StatusBarItemController.joinNextMeeting),
action: nextEvent.startDate < now
? #selector(StatusBarItemController.joinCurrentMeeting)
: #selector(StatusBarItemController.joinNextMeeting),
keyEquivalent: ""
)
joinItem.target = target
joinItem.setShortcut(for: nextEvent.startDate < now ? .joinCurrentEventShortcut : .joinEventShortcut)
items.append(joinItem)
}

Expand Down
16 changes: 16 additions & 0 deletions MeetingBar/UI/StatusBar/StatusBarItemController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ final class StatusBarItemController {
private func setupKeyboardShortcuts() {
KeyboardShortcuts.onKeyUp(for: .createMeetingShortcut, action: createMeeting)

KeyboardShortcuts.onKeyUp(for: .joinCurrentEventShortcut) {
Task { @MainActor in self.joinCurrentMeeting() }
}

KeyboardShortcuts.onKeyUp(for: .joinEventShortcut) {
Task { @MainActor in self.joinNextMeeting() }
}
Expand Down Expand Up @@ -400,6 +404,18 @@ final class StatusBarItemController {
createMeeting()
}

@objc
/// Joins the meeting link for the event currently in progress.
///
/// If there is no active event, a user-facing notification is shown.
func joinCurrentMeeting() {
if let currentEvent = events.currentEvent() {
currentEvent.openMeeting()
} else {
sendNotification("status_bar_section_join_current_meeting".loco(), "next_meeting_empty_message".loco())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use current-meeting-specific notification copy here.

The “no active event” branch uses "next_meeting_empty_message", which can produce misleading text in the current-meeting path. Prefer dedicated current-meeting title/message localization keys for this branch.

💡 Suggested adjustment
-            sendNotification("status_bar_section_join_current_meeting".loco(), "next_meeting_empty_message".loco())
+            sendNotification("current_meeting_empty_title".loco(), "current_meeting_empty_message".loco())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sendNotification("status_bar_section_join_current_meeting".loco(), "next_meeting_empty_message".loco())
sendNotification("current_meeting_empty_title".loco(), "current_meeting_empty_message".loco())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MeetingBar/UI/StatusBar/StatusBarItemController.swift` at line 415, Replace
the generic next-meeting copy in the current-meeting "no active event" branch by
using a current-meeting-specific localization key instead of
"next_meeting_empty_message" in the sendNotification call; update the call in
StatusBarItemController where
sendNotification("status_bar_section_join_current_meeting".loco(),
"next_meeting_empty_message".loco()) is used to reference a dedicated key like
"current_meeting_empty_message" (and add that localization entry if it doesn’t
exist) so the title ("status_bar_section_join_current_meeting") and message
reflect the current-meeting context.

}
}

@objc
func joinNextMeeting() {
if let nextEvent = events.nextEvent() {
Expand Down
8 changes: 8 additions & 0 deletions MeetingBar/UI/Views/Preferences/GeneralTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ struct ShortcutsSection: View {
Text("preferences_general_shortcut_create_meeting".loco())
KeyboardShortcuts.Recorder(for: .createMeetingShortcut)

Text("status_bar_section_join_current_meeting".loco() + ":")
KeyboardShortcuts.Recorder(for: .joinCurrentEventShortcut)

Text("preferences_general_shortcut_join_next".loco())
KeyboardShortcuts.Recorder(for: .joinEventShortcut)

Expand Down Expand Up @@ -74,6 +77,11 @@ struct ShortcutsModal: View {
Spacer()
KeyboardShortcuts.Recorder(for: .createMeetingShortcut)
}
HStack {
Text("status_bar_section_join_current_meeting".loco() + ":")
Spacer()
KeyboardShortcuts.Recorder(for: .joinCurrentEventShortcut)
}
HStack {
Text("preferences_general_shortcut_join_next".loco())
Spacer()
Expand Down
28 changes: 28 additions & 0 deletions MeetingBarTests/NextEventTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,32 @@
let array = [future, running]
XCTAssertEqual(array.nextEvent(), running)
}

func test_currentEvent_returnsRunningEvent() {

Check warning

Code scanning / Tailor (reported by Codacy)

Function names should be lowerCamelCase Warning

Function names should be lowerCamelCase
let running = makeFakeEvent(
id: "RUN",
start: now.addingTimeInterval(-120),
end: now.addingTimeInterval(600),
withLink: true
)
let future = makeFakeEvent(
id: "FUT",
start: now.addingTimeInterval(120),
end: now.addingTimeInterval(600),
withLink: true
)

XCTAssertEqual([future, running].currentEvent(), running)
}

func test_currentEvent_respectsLinkRequirement() {

Check warning

Code scanning / Tailor (reported by Codacy)

Function names should be lowerCamelCase Warning

Function names should be lowerCamelCase
let runningWithoutLink = makeFakeEvent(
id: "NO-LINK",
start: now.addingTimeInterval(-120),
end: now.addingTimeInterval(600),
withLink: false
)

XCTAssertNil([runningWithoutLink].currentEvent(linkRequired: true))
}
}
14 changes: 14 additions & 0 deletions MeetingBarTests/StatusBarItem/MenuBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,23 @@

XCTAssertEqual(MenuBuilder.plainTitles(of: items)[0],
"status_bar_section_join_current_meeting".loco())
XCTAssertEqual(items[0].action, #selector(StatusBarItemController.joinCurrentMeeting))
XCTAssertTrue(items.contains { $0.action == #selector(StatusBarItemController.createMeetingAction) })
}

func test_joinSectionFutureEventUsesJoinNextAction() {
let future = makeFakeEvent(
id: "F",
start: Date().addingTimeInterval(300),
end: Date().addingTimeInterval(1_200)

Check warning

Code scanning / Swiftlint (reported by Codacy)

Underscores should be used as thousand separator in large decimal numbers. Warning

Underscores should be used as thousand separator in large decimal numbers.
)
let items = MenuBuilder(target: Dummy())
.buildJoinSection(nextEvent: future)

XCTAssertEqual(items[0].title, "status_bar_section_join_next_meeting".loco())
XCTAssertEqual(items[0].action, #selector(StatusBarItemController.joinNextMeeting))
}

func test_joinSectionWithoutEvent() {
let items = MenuBuilder(target: Dummy())
.buildJoinSection(nextEvent: nil)
Expand Down
Loading