diff --git a/CHANGELOG.md b/CHANGELOG.md index 8132510c..c5fafce8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ For next releases info look here: * Fix readability of the statusbar text in multi-screen setups (#354) * Detect hidden menubar icon (#429) * Added feature to snooze the notification +* Meetings without a meeting links can now be displayed in fullscreen notifications. A new setting has been added to control that behaviour. +* Preferences changes: + * Added a section "Notification" + * Added a new parameter related to full screen notification that you can disable to receives notification for meetings without links (Only for meetings with a link) +* In the fullscreen notification, don't show the Meeting Link button if there is no meeting link. + + * Fix crash due to meeting attendees without an email address (#460) ## Version 3.10.0 diff --git a/MeetingBar/ActionsOnEventStart.swift b/MeetingBar/ActionsOnEventStart.swift index 9eda2039..909728b8 100644 --- a/MeetingBar/ActionsOnEventStart.swift +++ b/MeetingBar/ActionsOnEventStart.swift @@ -50,13 +50,12 @@ class ActionsOnEventStart: NSObject { } // - if let nextEvent = getNextEvent(events: app.statusBarItem.events, linkRequired: true) { - let now = Date() + let fullscreenNotificationMeetingLinkOnly = Defaults[.fullscreenNotificationMeetingLinkOnly] + if let nextEvent = getNextEvent(events: app.statusBarItem.events, linkRequired: fullscreenNotificationMeetingLinkOnly) { + let now = Date() let startEndRange = nextEvent.startDate ... nextEvent.endDate - let timeInterval = nextEvent.startDate.timeIntervalSince(now) - let allDayCandidate = nextEvent.isAllDay && startEndRange.contains(now) /* @@ -65,9 +64,9 @@ class ActionsOnEventStart: NSObject { * ------------------------ */ let actionTimeForFullscreenNotification = Double(Defaults[.fullscreenNotificationTime].rawValue) - let nonAlldayCandidateForFullscreenNotification = (timeInterval > -15 && timeInterval < actionTimeForFullscreenNotification) + let nonAllDayCandidateForFullscreenNotification = (timeInterval > -15 && timeInterval < actionTimeForFullscreenNotification) - if fullscreenNotificationActive && (nonAlldayCandidateForFullscreenNotification || allDayCandidate) { + if fullscreenNotificationActive && (nonAllDayCandidateForFullscreenNotification || allDayCandidate) { var events = Defaults[.processedEventsForFullscreenNotification] let matchedEvent = events.first { $0.id == nextEvent.ID } @@ -76,9 +75,7 @@ class ActionsOnEventStart: NSObject { // we will remove the the current event from the scheduled events, so that we can run the script again -> // this is an edge case when the event was already notified for, but scheduled for a later time. if matchedEvent == nil || matchedEvent?.lastModifiedDate != nextEvent.lastModifiedDate { - if nextEvent.meetingLink != nil { - app.openFullscreenNotificationWindow(event: nextEvent) - } + app.openFullscreenNotificationWindow(event: nextEvent) // update the executed events if matchedEvent != nil { diff --git a/MeetingBar/Extensions/DefaultsKeys.swift b/MeetingBar/Extensions/DefaultsKeys.swift index 1a0b35f0..74c29f2a 100644 --- a/MeetingBar/Extensions/DefaultsKeys.swift +++ b/MeetingBar/Extensions/DefaultsKeys.swift @@ -29,6 +29,7 @@ extension Defaults.Keys { static let endOfEventNotificationTime = Key("endOfEventNotificationTime", default: .atEnd) static let fullscreenNotification = Key("fullscreenNotification", default: false) + static let fullscreenNotificationMeetingLinkOnly = Key("fullscreenNotificationMeetingLinkOnly", default: false) static let fullscreenNotificationTime = Key("fullscreenNotificationTime", default: .atStart) static let processedEventsForFullscreenNotification = Key<[ProcessedEvent]>("processedEventsForFullscreenNotification", default: []) diff --git a/MeetingBar/Helpers.swift b/MeetingBar/Helpers.swift index 820bf6d4..4b874c70 100644 --- a/MeetingBar/Helpers.swift +++ b/MeetingBar/Helpers.swift @@ -254,17 +254,17 @@ func openLinkFromClipboard() { } } -func generateFakeEvent() -> MBEvent { +func generateFakeEvent(includeMeetingLink: Bool) -> MBEvent { let calendar = MBCalendar(title: "Fake calendar", ID: "fake_cal", source: nil, email: nil, color: .black) let event = MBEvent( ID: "test_event", lastModifiedDate: nil, - title: "Test event", + title: includeMeetingLink ? "Test Event (with Meeting link)" : "Test Event (without Meeting link)", status: .confirmed, notes: nil, location: nil, - url: URL(string: "https://zoom.us/j/5551112222")!, + url: includeMeetingLink ? URL(string: "https://zoom.us/j/5551112222")! : nil, organizer: nil, startDate: Calendar.current.date(byAdding: .minute, value: 3, to: Date())!, endDate: Calendar.current.date(byAdding: .minute, value: 33, to: Date())!, diff --git a/MeetingBar/MeetingBar.entitlements b/MeetingBar/MeetingBar.entitlements index 267414e0..56a838a8 100644 --- a/MeetingBar/MeetingBar.entitlements +++ b/MeetingBar/MeetingBar.entitlements @@ -2,8 +2,6 @@ - com.apple.developer.usernotifications.time-sensitive - com.apple.security.app-sandbox com.apple.security.files.user-selected.read-write diff --git a/MeetingBar/Resources /Localization /en.lproj/Localizable.strings b/MeetingBar/Resources /Localization /en.lproj/Localizable.strings index 00787a12..51eb031b 100644 --- a/MeetingBar/Resources /Localization /en.lproj/Localizable.strings +++ b/MeetingBar/Resources /Localization /en.lproj/Localizable.strings @@ -281,8 +281,10 @@ // MARK: - Shared +"preferences_notifications_section_title" = "Notifications"; "shared_send_notification_toggle" = "Send a system notification"; "shared_fullscreen_notification_toggle" = "Show a fullscreen notification"; +"shared_fullscreen_notification_meeting_link_only_toggle" = "Only for meetings with a link"; "shared_send_notification_no_alert_style_tip" = "⚠️ Your macOS notification settings for MeetingBar are not set to alert. Do so if you want persistent notifications."; "shared_send_notification_disabled_tip" = "⚠️ Your macOS notification settings for MeetingBar are off. Turn on notifications in the macOS system settings to keep track of meetings."; "shared_automatic_event_join_toggle" = "Automatically join next event meeting"; diff --git a/MeetingBar/Resources /Localization /fr.lproj/Localizable.strings b/MeetingBar/Resources /Localization /fr.lproj/Localizable.strings index 9235230d..b76c6c29 100644 --- a/MeetingBar/Resources /Localization /fr.lproj/Localizable.strings +++ b/MeetingBar/Resources /Localization /fr.lproj/Localizable.strings @@ -69,8 +69,10 @@ // MARK: - Shared +"preferences_notifications_section_title" = "Notifications"; "shared_send_notification_toggle" = "Envoyer une notification système"; "shared_fullscreen_notification_toggle" = "Affiche une notification plein écran"; +"shared_fullscreen_notification_meeting_link_only_toggle" = "Uniquement pour les évènement avec des liens de réunion"; "access_screen_access_granted_click_ok_title" = "Cliquez sur « OK » dans la fenêtre popup de macOS."; "access_screen_access_screen_access_denied_go_to_title" = "Allez dans les"; "access_screen_access_denied_checkbox_title" = "et sélectionnez une case à cocher près de MeetingBar."; diff --git a/MeetingBar/Views/Changelog/Changelog.swift b/MeetingBar/Views/Changelog/Changelog.swift index 19c2aa49..315cb2e9 100644 --- a/MeetingBar/Views/Changelog/Changelog.swift +++ b/MeetingBar/Views/Changelog/Changelog.swift @@ -178,6 +178,11 @@ struct ChangelogView: View { Text("🌍 Translation into Slovak and Dutch") } } + if compareVersions("4.10.0", lastRevisedVersionInChangelog) { + Section(header: Text("Version 4.10")) { + Text("🖥️ Meetings without a meeting links can now be displayed in fullscreen notifications.") + } + } } }.listStyle(SidebarListStyle()) Button("general_close".loco(), action: close) diff --git a/MeetingBar/Views/FullscreenNotification.swift b/MeetingBar/Views/FullscreenNotification.swift index 870b48a7..0d95d4c7 100644 --- a/MeetingBar/Views/FullscreenNotification.swift +++ b/MeetingBar/Views/FullscreenNotification.swift @@ -18,14 +18,16 @@ struct FullscreenNotification: View { Rectangle.semiOpaqueWindow() VStack { HStack { - Image(nsImage: getIconForMeetingService(event.meetingLink?.service)) - .resizable().frame(width: 25, height: 25) + if event.meetingLink != nil { + Image(nsImage: getIconForMeetingService(event.meetingLink?.service)) + .resizable().frame(width: 25, height: 25) + } Text(event.title).font(.title) } VStack(spacing: 10) { Text(getEventDateString(event)) }.padding(15) - + // display location of the event, very useful if you // have a lot of meetings in a building with a lot of meeting rooms if let location = event.location { @@ -33,14 +35,16 @@ struct FullscreenNotification: View { Text(location) }.padding(15) } - + HStack(spacing: 30) { Button(action: dismiss) { Text("general_close".loco()).padding(.vertical, 5).padding(.horizontal, 20) } - Button(action: joinEvent) { - Text("notifications_meetingbar_join_event_action".loco()).padding(.vertical, 5).padding(.horizontal, 25) - }.background(Color.accentColor).cornerRadius(5) + if event.meetingLink != nil { + Button(action: joinEvent) { + Text("notifications_meetingbar_join_event_action".loco()).padding(.vertical, 5).padding(.horizontal, 25) + }.background(Color.accentColor).cornerRadius(5) + } } } } @@ -81,5 +85,6 @@ struct VisualEffect: NSViewRepresentable { } #Preview { - FullscreenNotification(event: generateFakeEvent(), window: nil) + FullscreenNotification(event: generateFakeEvent(includeMeetingLink: true), window: nil) + FullscreenNotification(event: generateFakeEvent(includeMeetingLink: false), window: nil) } diff --git a/MeetingBar/Views/Preferences/GeneralTab.swift b/MeetingBar/Views/Preferences/GeneralTab.swift index c2e8d73c..dae2ac0e 100644 --- a/MeetingBar/Views/Preferences/GeneralTab.swift +++ b/MeetingBar/Views/Preferences/GeneralTab.swift @@ -17,8 +17,7 @@ struct GeneralTab: View { Section { LaunchAtLoginANDPreferredLanguagePicker() Divider() - JoinEventNotificationPicker() - FullscreenNotificationPicker() + NotificationsSection() Divider() } Section { @@ -30,25 +29,38 @@ struct GeneralTab: View { } } +struct NotificationsSection: View { + var body: some View { + Text("preferences_notifications_section_title".loco()).font(.headline).bold() + Section { + JoinEventNotificationPicker() + FullscreenNotificationPicker() + }.padding(.leading, 30) + } +} + struct ShortcutsSection: View { @State var showingModal = false var body: some View { - HStack { - Text("preferences_general_shortcut_create_meeting".loco()) - KeyboardShortcuts.Recorder(for: .createMeetingShortcut) + Text("preferences_general_option_shortcuts".loco()).font(.headline).bold() + Section { + HStack { + Text("preferences_general_shortcut_create_meeting".loco()) + KeyboardShortcuts.Recorder(for: .createMeetingShortcut) - Text("preferences_general_shortcut_join_next".loco()) - KeyboardShortcuts.Recorder(for: .joinEventShortcut) + Text("preferences_general_shortcut_join_next".loco()) + KeyboardShortcuts.Recorder(for: .joinEventShortcut) - Spacer() + Spacer() - Button(action: { self.showingModal.toggle() }) { - Text("preferences_general_all_shortcut".loco()) - }.sheet(isPresented: $showingModal) { - ShortcutsModal() + Button(action: { self.showingModal.toggle() }) { + Text("preferences_general_all_shortcut".loco()) + }.sheet(isPresented: $showingModal) { + ShortcutsModal() + } } - } + }.padding(.leading, 30) } } diff --git a/MeetingBar/Views/Shared.swift b/MeetingBar/Views/Shared.swift index 7f97ec49..b39c976e 100644 --- a/MeetingBar/Views/Shared.swift +++ b/MeetingBar/Views/Shared.swift @@ -37,17 +37,23 @@ struct AutomaticEventJoinPicker: View { struct FullscreenNotificationPicker: View { @Default(.fullscreenNotification) var fullscreenNotification + @Default(.fullscreenNotificationMeetingLinkOnly) var fullscreenNotificationMeetingLinkOnly @Default(.fullscreenNotificationTime) var fullscreenNotificationTime var body: some View { - HStack { - Toggle("shared_fullscreen_notification_toggle".loco(), isOn: $fullscreenNotification) - Picker("", selection: $fullscreenNotificationTime) { - Text("general_when_event_starts".loco()).tag(TimeBeforeEvent.atStart) - Text("general_one_minute_before".loco()).tag(TimeBeforeEvent.minuteBefore) - Text("general_three_minute_before".loco()).tag(TimeBeforeEvent.threeMinuteBefore) - Text("general_five_minute_before".loco()).tag(TimeBeforeEvent.fiveMinuteBefore) - }.frame(width: 220, alignment: .leading).labelsHidden().disabled(!fullscreenNotification) + VStack(alignment: .leading, spacing: 15) { + HStack { + Toggle("shared_fullscreen_notification_toggle".loco(), isOn: $fullscreenNotification) + Picker("", selection: $fullscreenNotificationTime) { + Text("general_when_event_starts".loco()).tag(TimeBeforeEvent.atStart) + Text("general_one_minute_before".loco()).tag(TimeBeforeEvent.minuteBefore) + Text("general_three_minute_before".loco()).tag(TimeBeforeEvent.threeMinuteBefore) + Text("general_five_minute_before".loco()).tag(TimeBeforeEvent.fiveMinuteBefore) + }.frame(width: 220, alignment: .leading).labelsHidden().disabled(!fullscreenNotification) + } + Section { + Toggle("shared_fullscreen_notification_meeting_link_only_toggle".loco(), isOn: $fullscreenNotificationMeetingLinkOnly).disabled(!fullscreenNotification) + }.padding(.leading, 30) } } } @@ -74,13 +80,15 @@ struct JoinEventNotificationPicker: View { }.frame(width: 220, alignment: .leading).labelsHidden().disabled(!joinEventNotification) } - if noAlertStyle, !disabled, joinEventNotification { - Text("shared_send_notification_no_alert_style_tip".loco()).foregroundColor(.gray).font(.system(size: 12)) - } + Section { + if noAlertStyle, !disabled, joinEventNotification { + Text("shared_send_notification_no_alert_style_tip".loco()).foregroundColor(.gray).font(.system(size: 12)) + } - if disabled, joinEventNotification { - Text("shared_send_notification_disabled_tip".loco()).foregroundColor(.gray).font(.system(size: 12)) - } + if disabled, joinEventNotification { + Text("shared_send_notification_disabled_tip".loco()).foregroundColor(.gray).font(.system(size: 12)) + } + }.padding(.leading, 25) } } struct endEventNotificationPicker: View {