Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
DominikTo authored Jun 19, 2024
2 parents e66a6a8 + 4fd4864 commit 9f4485d
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 52 deletions.
5 changes: 3 additions & 2 deletions MeetingBar/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
}

// Reschedule next notification with updated event name visibility
removePendingNotificationRequests()
removePendingNotificationRequests(withID: notificationIDs.event_starts)
removePendingNotificationRequests(withID: notificationIDs.event_ends)
if let nextEvent = getNextEvent(events: self.statusBarItem.events) {
scheduleEventNotification(nextEvent)
}
Expand Down Expand Up @@ -167,7 +168,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
scheduleEventNotification(nextEvent)
}
} else {
removePendingNotificationRequests()
removePendingNotificationRequests(withID: notificationIDs.event_starts)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "zm_page_icon.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions MeetingBar/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ enum TimeBeforeEvent: Int, Defaults.Serializable, Codable {
case fiveMinuteBefore = 300
}

enum TimeBeforeEventEnd: Int, Defaults.Serializable, Codable {
case atEnd = 5
case minuteBefore = 60
case threeMinuteBefore = 180
case fiveMinuteBefore = 300
}

enum UtilsRegex {
static let outlookSafeLinkRegex = try! NSRegularExpression(pattern: #"https://[\S]+\.safelinks\.protection\.outlook\.com/[\S]+url=([\S]*)"#)
static let linkDetection = try! NSRegularExpression(pattern: #"(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?"#, options: .caseInsensitive)
Expand Down Expand Up @@ -177,6 +184,11 @@ enum WindowTitles {
static let changelog = "windows_title_changelog".loco()
}

enum notificationIDs {
static let event_starts = "NEXT_EVENT"
static let event_ends = "EVENT_ENDS"
}

let eventStartScriptPlaceholder = """
# the method to be called with the following parameters for the next meeting.
#
Expand Down
3 changes: 3 additions & 0 deletions MeetingBar/Extensions/DefaultsKeys.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ extension Defaults.Keys {
static let joinEventNotification = Key<Bool>("joinEventNotification", default: true)
static let joinEventNotificationTime = Key<TimeBeforeEvent>("joinEventNotificationTime", default: .atStart)

static let endOfEventNotification = Key<Bool>("endOfEventNotification", default: true)
static let endOfEventNotificationTime = Key<TimeBeforeEventEnd>("endOfEventNotificationTime", default: .atEnd)

static let fullscreenNotification = Key<Bool>("fullscreenNotification", default: false)
static let fullscreenNotificationTime = Key<TimeBeforeEvent>("fullscreenNotificationTime", default: .atStart)
static let processedEventsForFullscreenNotification = Key<[ProcessedEvent]>("processedEventsForFullscreenNotification", default: [])
Expand Down
6 changes: 6 additions & 0 deletions MeetingBar/MeetingServices.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ enum MeetingServices: String, Codable, CaseIterable {
case suitConference = "Suit Conference"
case doxyMe = "Doxy.me"
case calcom = "Cal Video"
case zmPage = "zm.page"
case other = "Other"

var localizedValue: String {
Expand Down Expand Up @@ -323,6 +324,7 @@ struct LinksRegex {
let suitConference = try! NSRegularExpression(pattern: #"https?://([a-z0-9.]+)?conference\.istesuit\.com/[^\s]*+"#)
let doxyMe = try! NSRegularExpression(pattern: #"https://([a-z0-9.]+)?doxy\.me/[^\s]*"#)
let calcom = try! NSRegularExpression(pattern: #"https?://app.cal\.com/video/[A-Za-z0-9./]+"#)
let zmPage = try! NSRegularExpression(pattern: #"https?://([a-zA-Z0-9.]+)\.zm\.page"#)
}

func getRegexForMeetingService(_ service: MeetingServices) -> NSRegularExpression? {
Expand Down Expand Up @@ -609,6 +611,10 @@ func getIconForMeetingService(_ meetingService: MeetingServices?) -> NSImage {
image = NSImage(named: "doxy_me_icon")!
image.size = NSSize(width: 16, height: 16)

case .some(.zmPage):
image = NSImage(named: "zm_page_icon")!
image.size = NSSize(width: 16, height: 16)

// tested and verified
case .none:
image = NSImage(named: "no_online_session")!
Expand Down
141 changes: 95 additions & 46 deletions MeetingBar/Notifications.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func registerNotificationCategories() {
notificationCenter.getNotificationCategories { _ in }
}

func sendUserNotification(_ title: String, _ text: String, _ categoryIdentier: String? = nil) {
func sendUserNotification(_ title: String, _ text: String) {
requestNotificationAuthorization() // By the apple best practices

let center = UNUserNotificationCenter.current()
Expand All @@ -69,13 +69,7 @@ func sendUserNotification(_ title: String, _ text: String, _ categoryIdentier: S
content.title = title
content.body = text

let identifier: String
if let categoryIdentier = categoryIdentier {
content.categoryIdentifier = categoryIdentier
identifier = categoryIdentier
} else {
identifier = UUID().uuidString
}
let identifier = UUID().uuidString

let request = UNNotificationRequest(identifier: identifier, content: content, trigger: nil)
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [identifier])
Expand Down Expand Up @@ -139,57 +133,111 @@ func displayAlert(title: String, text: String) {
}

func scheduleEventNotification(_ event: MBEvent) {
if !Defaults[.joinEventNotification] && !Defaults[.endOfEventNotification] {
return
}

requestNotificationAuthorization() // By the apple best practices

let now = Date()
let notificationTime = Double(Defaults[.joinEventNotificationTime].rawValue)
let timeInterval = event.startDate.timeIntervalSince(now) - notificationTime

if timeInterval < 0.5 {
return
}
// Event start notification
if Defaults[.joinEventNotification] {
let notificationTime = Double(Defaults[.joinEventNotificationTime].rawValue)
let timeInterval = event.startDate.timeIntervalSince(now) - notificationTime

removePendingNotificationRequests()
if timeInterval < 0.5 {
return
}

let center = UNUserNotificationCenter.current()
removePendingNotificationRequests(withID: notificationIDs.event_starts)

let content = UNMutableNotificationContent()
if Defaults[.hideMeetingTitle] {
content.title = "general_meeting".loco()
} else {
content.title = event.title
}
if #available(macOS 12.0, *) {
content.interruptionLevel = .timeSensitive
}
let center = UNUserNotificationCenter.current()

let content = UNMutableNotificationContent()
if Defaults[.hideMeetingTitle] {
content.title = "general_meeting".loco()
} else {
content.title = event.title
}
if #available(macOS 12.0, *) {
content.interruptionLevel = .timeSensitive
}

switch Defaults[.joinEventNotificationTime] {
case .atStart:
content.body = "notifications_event_start_soon_body".loco()
case .minuteBefore:
content.body = "notifications_event_start_one_minute_body".loco()
case .threeMinuteBefore:
content.body = "notifications_event_start_three_minutes_body".loco()
case .fiveMinuteBefore:
content.body = "notifications_event_start_five_minutes_body".loco()
switch Defaults[.joinEventNotificationTime] {
case .atStart:
content.body = "notifications_event_start_soon_body".loco()
case .minuteBefore:
content.body = "notifications_event_start_one_minute_body".loco()
case .threeMinuteBefore:
content.body = "notifications_event_start_three_minutes_body".loco()
case .fiveMinuteBefore:
content.body = "notifications_event_start_five_minutes_body".loco()
}
content.categoryIdentifier = "EVENT"
content.sound = UNNotificationSound.default
content.userInfo = ["eventID": event.ID]
content.threadIdentifier = "meetingbar"

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
let request = UNNotificationRequest(identifier: notificationIDs.event_starts, content: content, trigger: trigger)
center.add(request) { error in
if let error = error {
NSLog("%@", "request \(request.identifier) could not be added because of error \(error)")
}
}
}
content.categoryIdentifier = "EVENT"
content.sound = UNNotificationSound.default
content.userInfo = ["eventID": event.ID]
content.threadIdentifier = "meetingbar"

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
let request = UNNotificationRequest(identifier: "NEXT_EVENT", content: content, trigger: trigger)
center.add(request) { error in
if let error = error {
NSLog("%@", "request \(request.identifier) could not be added because of error \(error)")
// Event end notification
if Defaults[.endOfEventNotification] {
let notificationTime = Double(Defaults[.endOfEventNotificationTime].rawValue)
let timeInterval = event.endDate.timeIntervalSince(now) - notificationTime

if timeInterval < 0.5 {
return
}

let center = UNUserNotificationCenter.current()

let content = UNMutableNotificationContent()
if Defaults[.hideMeetingTitle] {
content.title = "general_meeting".loco()
} else {
content.title = event.title
}
if #available(macOS 12.0, *) {
content.interruptionLevel = .timeSensitive
}

switch Defaults[.endOfEventNotificationTime] {
// TODO: notification localization
case .atEnd:
content.body = "Event ends soon"
case .minuteBefore:
content.body = "Event ends in one minute"
case .threeMinuteBefore:
content.body = "Event ends in three minutes"
case .fiveMinuteBefore:
content.body = "Event ends in five minutes"
}
// content.categoryIdentifier = "EVENT"
content.sound = UNNotificationSound.default
content.userInfo = ["eventID": event.ID]
content.threadIdentifier = "meetingbar"

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
let request = UNNotificationRequest(identifier: notificationIDs.event_starts, content: content, trigger: trigger)
center.add(request) { error in
if let error = error {
NSLog("%@", "request \(request.identifier) could not be added because of error \(error)")
}
}
}
}

func snoozeEventNotification(_ event: MBEvent, _ interval: NotificationEventTimeAction) {
requestNotificationAuthorization() // By the apple best practices
removePendingNotificationRequests()
removePendingNotificationRequests(withID: notificationIDs.event_starts)

let now = Date()
let center = UNUserNotificationCenter.current()
Expand All @@ -216,7 +264,7 @@ func snoozeEventNotification(_ event: MBEvent, _ interval: NotificationEventTime
}

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
let request = UNNotificationRequest(identifier: "NEXT_EVENT", content: content, trigger: trigger)
let request = UNNotificationRequest(identifier: notificationIDs.event_starts, content: content, trigger: trigger)
center.add(request) { error in
if let error = error {
NSLog("%@", "request \(request) could not be added because of error \(error)")
Expand All @@ -226,9 +274,10 @@ func snoozeEventNotification(_ event: MBEvent, _ interval: NotificationEventTime
}
}

func removePendingNotificationRequests() {
func removePendingNotificationRequests(withID: String) {
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests()
center.removePendingNotificationRequests(withIdentifiers: [withID])
// center.removeAllPendingNotificationRequests()
}

func removeDeliveredNotifications() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,8 @@

// MARK: - Shared

"shared_send_notification_toggle" = "Wyślij powiadomienie aby dołączyć do następnego spotkania";
"shared_fullscreen_notification_toggle" = "Show a fullscreen notification";
"shared_send_notification_toggle" = "Wyślij powiadomienie systemowe";
"shared_fullscreen_notification_toggle" = "Pokaż pełnoekranowe powiadomienie";
"general_when_event_starts" = "gdy wydarzenie się rozpocznie";
"general_one_minute_before" = "1 minutę przed";
"general_three_minute_before" = "3 minuty przed";
Expand Down
2 changes: 1 addition & 1 deletion MeetingBar/StatusBarItemController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ class StatusBarItemController {
switch nextEventState {
case .none:
if Defaults[.joinEventNotification] {
removePendingNotificationRequests()
removePendingNotificationRequests(withID: notificationIDs.event_starts)
removeDeliveredNotifications()
}
title = "🏁"
Expand Down
1 change: 1 addition & 0 deletions MeetingBar/Views/Preferences/AdvancedTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Defaults
struct AdvancedTab: View {
var body: some View {
VStack(alignment: .leading) {
endEventNotificationPicker()
AutomaticEventJoinPicker()
Divider()
ScriptSection()
Expand Down
Loading

0 comments on commit 9f4485d

Please sign in to comment.