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
25 changes: 23 additions & 2 deletions MeetingBar/Core/Managers/EventManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,32 @@ public class EventManager: ObservableObject {
let dateFrom = Calendar.current.startOfDay(for: Date())
var dateTo: Date

let calendar = Calendar.current
switch Defaults[.showEventsForPeriod] {
case .today:
dateTo = Calendar.current.date(byAdding: .day, value: 1, to: dateFrom)!
dateTo = calendar.date(byAdding: .day, value: 1, to: dateFrom)!
case .today_n_tomorrow:
dateTo = Calendar.current.date(byAdding: .day, value: 2, to: dateFrom)!
dateTo = calendar.date(byAdding: .day, value: 2, to: dateFrom)!
case .fiveDays:
dateTo = calendar.date(byAdding: .day, value: 5, to: dateFrom)!
case .sevenDays:
dateTo = calendar.date(byAdding: .day, value: 7, to: dateFrom)!
case .workWeek:
let weekday = calendar.component(.weekday, from: dateFrom) // 1 = Sunday, 7 = Saturday
if weekday == 1 {
// Sunday: next week Mon–Fri
let nextMonday = calendar.date(byAdding: .day, value: 1, to: dateFrom)!
dateTo = calendar.date(byAdding: .day, value: 5, to: nextMonday)!
} else if weekday == 7 {
// Saturday: next week Mon–Fri
let nextMonday = calendar.date(byAdding: .day, value: 2, to: dateFrom)!
dateTo = calendar.date(byAdding: .day, value: 5, to: nextMonday)!
} else {
// Monday (2) through Friday (6): this week through end of Friday
let daysUntilFriday = 6 - weekday
let friday = calendar.date(byAdding: .day, value: daysUntilFriday, to: dateFrom)!
dateTo = calendar.date(byAdding: .day, value: 1, to: friday)!
}
}

let selectedCalendars = fromCalendars.filter { Defaults[.selectedCalendarIDs].contains($0.id) }
Expand Down
24 changes: 21 additions & 3 deletions MeetingBar/Core/Models/MBEvent+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,30 @@ public extension Array where Element == MBEvent {
let startPeriod = Calendar.current.date(byAdding: .minute, value: 1, to: now)!
var endPeriod: Date

let todayMidnight = Calendar.current.startOfDay(for: now)
let calendar = Calendar.current
let todayMidnight = calendar.startOfDay(for: now)
switch Defaults[.showEventsForPeriod] {
case .today:
endPeriod = Calendar.current.date(byAdding: .day, value: 1, to: todayMidnight)!
endPeriod = calendar.date(byAdding: .day, value: 1, to: todayMidnight)!
case .today_n_tomorrow:
endPeriod = Calendar.current.date(byAdding: .day, value: 2, to: todayMidnight)!
endPeriod = calendar.date(byAdding: .day, value: 2, to: todayMidnight)!
case .fiveDays:
endPeriod = calendar.date(byAdding: .day, value: 5, to: todayMidnight)!
case .sevenDays:
endPeriod = calendar.date(byAdding: .day, value: 7, to: todayMidnight)!
case .workWeek:
let weekday = calendar.component(.weekday, from: todayMidnight) // 1 = Sunday, 7 = Saturday
if weekday == 1 {
let nextMonday = calendar.date(byAdding: .day, value: 1, to: todayMidnight)!
endPeriod = calendar.date(byAdding: .day, value: 5, to: nextMonday)!
} else if weekday == 7 {
let nextMonday = calendar.date(byAdding: .day, value: 2, to: todayMidnight)!
endPeriod = calendar.date(byAdding: .day, value: 5, to: nextMonday)!
} else {
let daysUntilFriday = 6 - weekday
let friday = calendar.date(byAdding: .day, value: daysUntilFriday, to: todayMidnight)!
endPeriod = calendar.date(byAdding: .day, value: 1, to: friday)!
}
}

// Filter out passed or not started events
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@
"preferences_appearance_events_show_events_for_title" = "Show events for";
"preferences_appearance_events_show_events_for_today_value" = "today";
"preferences_appearance_events_show_events_for_today_tomorrow_value" = "today and tomorrow";
"preferences_appearance_events_show_events_for_5_days_value" = "5 days";
"preferences_appearance_events_show_events_for_7_days_value" = "7 days";
"preferences_appearance_events_show_events_for_work_week_value" = "work week (Mon–Fri)";
"preferences_appearance_events_non_all_day_title" = "Other events:";
"preferences_appearance_events_value_inactive_without_meeting_link" = "show those without meeting links as inactive";
"preferences_appearance_events_value_hide_without_meeting_link" = "hide all without meeting links";
Expand Down
48 changes: 48 additions & 0 deletions MeetingBar/UI/StatusBar/StatusBarItemController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,33 @@ final class StatusBarItemController {
* ------------------------
*/

/// Returns the start-of-day dates for each day in the given period, from today (or next Monday for work week on weekend).
private static func daysInPeriod(_ period: ShowEventsForPeriod, from today: Date, calendar: Calendar) -> [Date] {
switch period {
case .fiveDays:
return (0 ..< 5).map { calendar.date(byAdding: .day, value: $0, to: today)! }
case .sevenDays:
return (0 ..< 7).map { calendar.date(byAdding: .day, value: $0, to: today)! }
case .workWeek:
let weekday = calendar.component(.weekday, from: today) // 1 = Sunday, 7 = Saturday
if weekday == 1 {
// Sunday: next week Mon–Fri
let nextMonday = calendar.date(byAdding: .day, value: 1, to: today)!
return (0 ..< 5).map { calendar.date(byAdding: .day, value: $0, to: nextMonday)! }
} else if weekday == 7 {
// Saturday: next week Mon–Fri
let nextMonday = calendar.date(byAdding: .day, value: 2, to: today)!
return (0 ..< 5).map { calendar.date(byAdding: .day, value: $0, to: nextMonday)! }
} else {
// Monday (2) through Friday (6): today through Friday
let daysUntilFriday = 6 - weekday
return (0 ... daysUntilFriday).map { calendar.date(byAdding: .day, value: $0, to: today)! }
}
case .today, .today_n_tomorrow:
return [today]
}
}
Comment on lines +324 to +349
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix .today_n_tomorrow to include tomorrow in daysInPeriod.

The helper’s switch returns only today for .today_n_tomorrow, which is incorrect if the helper is reused for that period.

✅ Suggested fix
-        case .today, .today_n_tomorrow:
-            return [today]
+        case .today:
+            return [today]
+        case .today_n_tomorrow:
+            return [today, calendar.date(byAdding: .day, value: 1, to: today)!]
📝 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
/// Returns the start-of-day dates for each day in the given period, from today (or next Monday for work week on weekend).
private static func daysInPeriod(_ period: ShowEventsForPeriod, from today: Date, calendar: Calendar) -> [Date] {
switch period {
case .fiveDays:
return (0 ..< 5).map { calendar.date(byAdding: .day, value: $0, to: today)! }
case .sevenDays:
return (0 ..< 7).map { calendar.date(byAdding: .day, value: $0, to: today)! }
case .workWeek:
let weekday = calendar.component(.weekday, from: today) // 1 = Sunday, 7 = Saturday
if weekday == 1 {
// Sunday: next week Mon–Fri
let nextMonday = calendar.date(byAdding: .day, value: 1, to: today)!
return (0 ..< 5).map { calendar.date(byAdding: .day, value: $0, to: nextMonday)! }
} else if weekday == 7 {
// Saturday: next week Mon–Fri
let nextMonday = calendar.date(byAdding: .day, value: 2, to: today)!
return (0 ..< 5).map { calendar.date(byAdding: .day, value: $0, to: nextMonday)! }
} else {
// Monday (2) through Friday (6): today through Friday
let daysUntilFriday = 6 - weekday
return (0 ... daysUntilFriday).map { calendar.date(byAdding: .day, value: $0, to: today)! }
}
case .today, .today_n_tomorrow:
return [today]
}
}
/// Returns the start-of-day dates for each day in the given period, from today (or next Monday for work week on weekend).
private static func daysInPeriod(_ period: ShowEventsForPeriod, from today: Date, calendar: Calendar) -> [Date] {
switch period {
case .fiveDays:
return (0 ..< 5).map { calendar.date(byAdding: .day, value: $0, to: today)! }
case .sevenDays:
return (0 ..< 7).map { calendar.date(byAdding: .day, value: $0, to: today)! }
case .workWeek:
let weekday = calendar.component(.weekday, from: today) // 1 = Sunday, 7 = Saturday
if weekday == 1 {
// Sunday: next week Mon–Fri
let nextMonday = calendar.date(byAdding: .day, value: 1, to: today)!
return (0 ..< 5).map { calendar.date(byAdding: .day, value: $0, to: nextMonday)! }
} else if weekday == 7 {
// Saturday: next week Mon–Fri
let nextMonday = calendar.date(byAdding: .day, value: 2, to: today)!
return (0 ..< 5).map { calendar.date(byAdding: .day, value: $0, to: nextMonday)! }
} else {
// Monday (2) through Friday (6): today through Friday
let daysUntilFriday = 6 - weekday
return (0 ... daysUntilFriday).map { calendar.date(byAdding: .day, value: $0, to: today)! }
}
case .today:
return [today]
case .today_n_tomorrow:
return [today, calendar.date(byAdding: .day, value: 1, to: today)!]
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MeetingBar/UI/StatusBar/StatusBarItemController.swift` around lines 324 -
349, daysInPeriod currently returns only today for the .today_n_tomorrow case;
update the .today_n_tomorrow branch in the daysInPeriod(_:,from:,calendar:)
function so it returns an array containing today and tomorrow by using
calendar.date(byAdding: .day, value: 1, to: today) to compute tomorrow (preserve
ordering [today, tomorrow] and safe unwrap similar to other cases).


func updateMenu() {
// Don't update the menu while it's open to avoid flickering
if statusItem.menu != nil {
Expand Down Expand Up @@ -368,6 +395,27 @@ final class StatusBarItemController {
let tomorrowEvents = events.filter { Calendar.current.isDate($0.startDate, inSameDayAs: tomorrow) }
statusItemMenu.items += builder.buildDateSection(date: tomorrow, title: "status_bar_section_tomorrow".loco(), events: tomorrowEvents)

case .fiveDays, .sevenDays, .workWeek:
let calendar = Calendar.current
let dayDates = Self.daysInPeriod(Defaults[.showEventsForPeriod], from: today, calendar: calendar)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "E, d MMM"
dateFormatter.locale = I18N.instance.locale
for (index, dayStart) in dayDates.enumerated() {
if index > 0 {
statusItemMenu.addItem(NSMenuItem.separator())
}
let dayEvents = events.filter { calendar.isDate($0.startDate, inSameDayAs: dayStart) }
let sectionTitle: String
if calendar.isDate(dayStart, inSameDayAs: today) {
sectionTitle = "status_bar_section_today".loco()
} else if calendar.isDate(dayStart, inSameDayAs: tomorrow) {
sectionTitle = "status_bar_section_tomorrow".loco()
} else {
sectionTitle = dateFormatter.string(from: dayStart)
}
statusItemMenu.items += builder.buildDateSection(date: dayStart, title: sectionTitle, events: dayEvents)
Comment on lines +398 to +417
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid duplicated date labels in multi‑day sections.

buildDateSection already appends the formatted date, so using a full date as sectionTitle yields headers like “Tue, 25 Feb (Tue, 25 Feb):”. Consider passing a weekday-only title instead.

✅ Suggested fix
-                let dateFormatter = DateFormatter()
-                dateFormatter.dateFormat = "E, d MMM"
-                dateFormatter.locale = I18N.instance.locale
+                let weekdayFormatter = DateFormatter()
+                weekdayFormatter.dateFormat = "EEEE"
+                weekdayFormatter.locale = I18N.instance.locale
                 for (index, dayStart) in dayDates.enumerated() {
@@
                     } else if calendar.isDate(dayStart, inSameDayAs: tomorrow) {
                         sectionTitle = "status_bar_section_tomorrow".loco()
                     } else {
-                        sectionTitle = dateFormatter.string(from: dayStart)
+                        sectionTitle = weekdayFormatter.string(from: dayStart)
                     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MeetingBar/UI/StatusBar/StatusBarItemController.swift` around lines 398 -
417, The section title for multi-day periods uses a full date string but
buildDateSection(date: title: events:) already appends the formatted date,
causing duplicated labels; change the else branch that sets sectionTitle (using
dateFormatter) to produce only the weekday (e.g., use a DateFormatter with
format "E" or fetch the localized weekday name) so sectionTitle is just the
weekday name, keeping the existing special-cases for today/tomorrow and leaving
builder.buildDateSection as-is.

}
}
} else {
let text = "status_bar_empty_calendar_message".loco()
Expand Down
6 changes: 6 additions & 0 deletions MeetingBar/UI/Views/Preferences/AppearanceTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ struct EventsSection: View {
Text(
"preferences_appearance_events_show_events_for_today_tomorrow_value".loco()
).tag(ShowEventsForPeriod.today_n_tomorrow)
Text("preferences_appearance_events_show_events_for_5_days_value".loco()).tag(
ShowEventsForPeriod.fiveDays)
Text("preferences_appearance_events_show_events_for_7_days_value".loco()).tag(
ShowEventsForPeriod.sevenDays)
Text("preferences_appearance_events_show_events_for_work_week_value".loco()).tag(
ShowEventsForPeriod.workWeek)
}

Picker(
Expand Down
3 changes: 3 additions & 0 deletions MeetingBar/Utilities/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ enum PastEventsAppereance: String, Defaults.Serializable, Codable, CaseIterable
enum ShowEventsForPeriod: String, Defaults.Serializable, Codable, CaseIterable {
case today
case today_n_tomorrow
case fiveDays
case sevenDays
case workWeek
}

enum OngoingEventVisibility: String, Defaults.Serializable, Codable, CaseIterable {
Expand Down
39 changes: 39 additions & 0 deletions MeetingBarTests/NextEventTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,43 @@
let array = [future, running]
XCTAssertEqual(array.nextEvent(), running)
}

func test_fiveDaysPeriod_includesEventWithinFiveDays_excludesEventAfter() {

Check warning

Code scanning / Tailor (reported by Codacy)

Function names should be lowerCamelCase Warning

Function names should be lowerCamelCase
Defaults[.showEventsForPeriod] = .fiveDays
let calendar = Calendar.current
let todayStart = calendar.startOfDay(for: now)
let oneHour: TimeInterval = 3600
let oneDay: TimeInterval = 24 * 3600

let withinFive = makeFakeEvent(
id: "within",
start: todayStart.addingTimeInterval(2 * oneDay + oneHour),
end: todayStart.addingTimeInterval(2 * oneDay + 2 * oneHour),
withLink: true
)
let afterFive = makeFakeEvent(
id: "after",
start: todayStart.addingTimeInterval(6 * oneDay),
end: todayStart.addingTimeInterval(6 * oneDay + oneHour),
withLink: true
)
let array = [afterFive, withinFive]
XCTAssertEqual(array.nextEvent(), withinFive)
}

func test_sevenDaysPeriod_includesEventWithinSevenDays() {

Check warning

Code scanning / Tailor (reported by Codacy)

Function names should be lowerCamelCase Warning

Function names should be lowerCamelCase
Defaults[.showEventsForPeriod] = .sevenDays
let calendar = Calendar.current
let todayStart = calendar.startOfDay(for: now)
let oneHour: TimeInterval = 3600
let oneDay: TimeInterval = 24 * 3600

let inSixDays = makeFakeEvent(
id: "six",
start: todayStart.addingTimeInterval(6 * oneDay + oneHour),
end: todayStart.addingTimeInterval(6 * oneDay + 2 * oneHour),
withLink: true
)
XCTAssertEqual([inSixDays].nextEvent(), inSixDays)
}
}
Loading