Skip to content

Commit

Permalink
Restore EKEventStore integration
Browse files Browse the repository at this point in the history
  • Loading branch information
leits committed Mar 29, 2022
1 parent 9605f23 commit aaca46b
Show file tree
Hide file tree
Showing 13 changed files with 457 additions and 433 deletions.
22 changes: 17 additions & 5 deletions MeetingBar.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
140D84322493A3FE0055E1CE /* StatusBarItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140D84312493A3FE0055E1CE /* StatusBarItemController.swift */; };
140D84342493A4180055E1CE /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140D84332493A4180055E1CE /* Constants.swift */; };
140D843A2493A6240055E1CE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140D84392493A6240055E1CE /* Helpers.swift */; };
140D843D2493B7BF0055E1CE /* EventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140D843C2493B7BF0055E1CE /* EventStore.swift */; };
140D843D2493B7BF0055E1CE /* EKEventStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140D843C2493B7BF0055E1CE /* EKEventStore.swift */; };
140D843F2493B7CB0055E1CE /* DefaultsKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140D843E2493B7CB0055E1CE /* DefaultsKeys.swift */; };
140D84412493B8030055E1CE /* KeyboardShortcutsNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140D84402493B8030055E1CE /* KeyboardShortcutsNames.swift */; };
1418907C26F23CCD00DCD9B1 /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = 1418907B26F23CCD00DCD9B1 /* KeyboardShortcuts */; };
Expand Down Expand Up @@ -46,6 +46,7 @@
40DC7051250E5DB500217DD9 /* AutoLauncher.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 40DC7040250E5B9C00217DD9 /* AutoLauncher.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
40DC7054250E5E1000217DD9 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40DC7053250E5E1000217DD9 /* ServiceManagement.framework */; };
40E60313250E81EA005986C7 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40E60312250E81EA005986C7 /* main.swift */; };
4654E66227F24A3F001848AB /* Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4654E66127F24A3F001848AB /* Common.swift */; };
A7B68FA325CDE9E200CA3A68 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7B68FA225CDE9E200CA3A68 /* URL.swift */; };
E23954682628BF89003ECCB3 /* BrowserConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E23954672628BF89003ECCB3 /* BrowserConfigView.swift */; };
E249D534259BBC3800429BF1 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = E249D533259BBC3800429BF1 /* String.swift */; };
Expand Down Expand Up @@ -78,7 +79,7 @@
140D84312493A3FE0055E1CE /* StatusBarItemController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarItemController.swift; sourceTree = "<group>"; };
140D84332493A4180055E1CE /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
140D84392493A6240055E1CE /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
140D843C2493B7BF0055E1CE /* EventStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventStore.swift; sourceTree = "<group>"; };
140D843C2493B7BF0055E1CE /* EKEventStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EKEventStore.swift; sourceTree = "<group>"; };
140D843E2493B7CB0055E1CE /* DefaultsKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultsKeys.swift; sourceTree = "<group>"; };
140D84402493B8030055E1CE /* KeyboardShortcutsNames.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardShortcutsNames.swift; sourceTree = "<group>"; };
14158D3A246C65AD0006436C /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
Expand Down Expand Up @@ -125,6 +126,7 @@
40DC704C250E5BB100217DD9 /* AutoLauncher.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AutoLauncher.entitlements; sourceTree = "<group>"; };
40DC7053250E5E1000217DD9 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; };
40E60312250E81EA005986C7 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
4654E66127F24A3F001848AB /* Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = "<group>"; };
A7B68FA225CDE9E200CA3A68 /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
D6D68C5327BCF93D009E8469 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
E23954672628BF89003ECCB3 /* BrowserConfigView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowserConfigView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -208,8 +210,8 @@
1452C9AD25AF5B1000C46CDF /* Views */,
140D84312493A3FE0055E1CE /* StatusBarItemController.swift */,
140D84332493A4180055E1CE /* Constants.swift */,
14B08ACF2749AB96002E3892 /* GCEventStore.swift */,
14C59B8425CF45340019C9E4 /* Store.swift */,
4654E66027F24983001848AB /* EventStores */,
140D84392493A6240055E1CE /* Helpers.swift */,
14879B2A24E6889C00DB0A7E /* Notifications.swift */,
149AD20F25A8967C004976F1 /* Extensions */,
Expand Down Expand Up @@ -280,7 +282,6 @@
149AD20F25A8967C004976F1 /* Extensions */ = {
isa = PBXGroup;
children = (
140D843C2493B7BF0055E1CE /* EventStore.swift */,
140D84402493B8030055E1CE /* KeyboardShortcutsNames.swift */,
140D843E2493B7CB0055E1CE /* DefaultsKeys.swift */,
E249D533259BBC3800429BF1 /* String.swift */,
Expand Down Expand Up @@ -324,6 +325,16 @@
name = Frameworks;
sourceTree = "<group>";
};
4654E66027F24983001848AB /* EventStores */ = {
isa = PBXGroup;
children = (
140D843C2493B7BF0055E1CE /* EKEventStore.swift */,
14B08ACF2749AB96002E3892 /* GCEventStore.swift */,
4654E66127F24A3F001848AB /* Common.swift */,
);
name = EventStores;
sourceTree = "<group>";
};
E4DD0F092529D0D20029E395 /* XCConfig */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -514,7 +525,8 @@
E23954682628BF89003ECCB3 /* BrowserConfigView.swift in Sources */,
1452C9E025AF894E00C46CDF /* CalendarsScreen.swift in Sources */,
140D84412493B8030055E1CE /* KeyboardShortcutsNames.swift in Sources */,
140D843D2493B7BF0055E1CE /* EventStore.swift in Sources */,
4654E66227F24A3F001848AB /* Common.swift in Sources */,
140D843D2493B7BF0055E1CE /* EKEventStore.swift in Sources */,
1452C9CE25AF868E00C46CDF /* CalendarsTab.swift in Sources */,
14E4110E246F2F9200F73ACF /* Preferences.swift in Sources */,
1452C9CA25AF861300C46CDF /* BookmarksTab.swift in Sources */,
Expand Down
34 changes: 21 additions & 13 deletions MeetingBar/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import PromiseKit
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {
var statusBarItem: StatusBarItemController!
var eventStore: EventStore!

var selectedCalendarIDsObserver: DefaultsObservation?
var showEventDetailsObserver: DefaultsObservation?
Expand Down Expand Up @@ -59,8 +60,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
var changelogWindow: NSWindow!

func applicationDidFinishLaunching(_: Notification) {
NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(handleURLEvent(getURLEvent:replyEvent:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL))

// AppStore sync
completeStoreTransactions()
checkAppSource()
Expand All @@ -73,12 +72,22 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
Defaults[.appVersion] = appVersion
}

if GCEventStore.shared.isAuthed {
if true {
eventStore = GCEventStore.shared

NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(handleURLEvent(getURLEvent:replyEvent:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL))
} else {
eventStore = EKEventStore.shared

NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.eventStoreChanged), name: .EKEventStoreChanged, object: EKEventStore.shared)
}

if eventStore.isAuthed {
setup()
} else {
Defaults[.selectedCalendarIDs] = []
setup()
_ = GCEventStore.shared.signIn().done {
_ = eventStore.signIn().done {
self.statusBarItem.loadCalendars()
}
}
Expand Down Expand Up @@ -144,8 +153,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
Defaults[.hideMeetingTitle].toggle()
}

// NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.eventStoreChanged), name: .EKEventStoreChanged, object: statusBarItem.eventStore)

showEventDetailsObserver = Defaults.observe(.showEventDetails) { change in
if change.oldValue != change.newValue {
NSLog("Change showEventDetails from \(change.oldValue) to \(change.newValue)")
Expand Down Expand Up @@ -363,7 +370,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
// built-in method EKEventStore.event(withIdentifier:) is broken
// temporary allow to open only the last event
if let nextEvent = getNextEvent(events: statusBarItem.events) {
if nextEvent.eventIdentifier == (eventID as! String) {
if nextEvent.ID == (eventID as! String) {
NSLog("Join \(nextEvent.title) event from notication")
openEvent(nextEvent)
}
Expand Down Expand Up @@ -407,6 +414,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
* MARK: - Actions
* ------------------------
*/

extension AppDelegate {
@objc
func handleURLEvent(getURLEvent event: NSAppleEventDescriptor, replyEvent _: NSAppleEventDescriptor) {
Expand Down Expand Up @@ -473,12 +481,12 @@ extension AppDelegate {
statusBarItem.updateMenu()
}

// @objc
// func eventStoreChanged(_: NSNotification) {
// NSLog("Store changed. Update status bar menu.")
// statusBarItem.updateTitle()
// statusBarItem.updateMenu()
// }
@objc
func eventStoreChanged(_: NSNotification) {
NSLog("Store changed. Update status bar menu.")
statusBarItem.updateTitle()
statusBarItem.updateMenu()
}

@objc
private func fetchEvents() {
Expand Down
189 changes: 188 additions & 1 deletion MeetingBar/Common.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,191 @@
// Copyright © 2022 Andrii Leitsius. All rights reserved.
//

import Foundation
import AppKit
import Defaults
import PromiseKit
import SwiftyJSON

protocol EventStore {
var isAuthed: Bool { get }

func signIn() -> Promise<Void>

func signOut() -> Promise<Void>

func fetchAllCalendars() -> Promise<[MBCalendar]>

func fetchEventsForDateRange(calendars: [MBCalendar], dateFrom: Date, dateTo: Date) -> Promise<[MBEvent]>
}

class MBCalendar: Hashable {
let title: String
let ID: String
let source: String?
let email: String?
var selected: Bool = false
let color: NSColor

init(title: String, ID: String, source: String?, email: String?, color: NSColor) {
self.title = title
self.ID = ID
self.source = source
self.email = email
self.color = color
}

func hash(into hasher: inout Hasher) {
hasher.combine(ID)
}

static func == (lhs: MBCalendar, rhs: MBCalendar) -> Bool {
lhs.ID == rhs.ID
}
}

enum MBEventStatus: Int {
case none = 0
case confirmed = 1
case tentative = 2
case canceled = 3
}

class MBEventOrganizer {
let name: String
let email: String?

init(email: String? = nil, name: String?) {
self.email = email
self.name = name ?? email ?? "status_bar_submenu_attendees_no_name".loco()
}
}

enum MBEventAttendeeStatus: Int {
case unknown = 0
case pending = 1
case accepted = 2
case declined = 3
case tentative = 4
case delegated = 5
case completed = 6
case inProcess = 7
}

class MBEventAttendee {
let name: String
let email: String?
let status: MBEventAttendeeStatus
var optional: Bool = false
let isCurrentUser: Bool

init(email: String?, name: String? = nil, status: MBEventAttendeeStatus, optional: Bool = false, isCurrentUser: Bool = false) {
self.email = email
self.name = name ?? email ?? "status_bar_submenu_attendees_no_name".loco()
self.status = status
self.optional = optional
self.isCurrentUser = isCurrentUser
}
}

class MBEvent {
let ID: String
let calendar: MBCalendar
let title: String
var status: MBEventStatus
var participationStatus: MBEventAttendeeStatus = .unknown
var meetingLink: MeetingLink?
var organizer: MBEventOrganizer?
let url: URL?
let notes: String?
let location: String?
let startDate: Date
let endDate: Date
let isAllDay: Bool
var attendees: [MBEventAttendee] = []

init(ID: String, title: String?, status: MBEventStatus, notes: String?, location: String?, url: URL?, organizer: MBEventOrganizer?, attendees: [MBEventAttendee] = [], startDate: Date, endDate: Date, isAllDay: Bool, calendar: MBCalendar) {
self.calendar = calendar
self.ID = ID
self.title = title ?? "status_bar_no_title".loco()
self.status = status

self.notes = notes
self.location = location
self.url = url

self.organizer = organizer
self.attendees = attendees
self.startDate = startDate
self.endDate = endDate
self.isAllDay = isAllDay

if let currentUser = attendees.first(where: { $0.isCurrentUser }) {
participationStatus = currentUser.status
}

let linkFields = [
location,
url?.absoluteString,
notes,
].compactMap { $0 }

for linkField in linkFields {
if var detectedLink = detectLink(linkField) {
if detectedLink.service == .meet,
let account = getEmailAccount(calendar.source),
let urlEncodedAccount = account.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
{
detectedLink.url = URL(string: (detectedLink.url.absoluteString) + "?authuser=\(urlEncodedAccount)")!
}
meetingLink = detectedLink
break
}
}
}
}

func filterEvents(_ events: [MBEvent]) -> [MBEvent] {
var filteredCalendarEvents: [MBEvent] = []

for calendarEvent in events {
// Filter events base on custom user regexes
for pattern in Defaults[.filterEventRegexes] {
if let regex = try? NSRegularExpression(pattern: pattern) {
if !hasMatch(text: calendarEvent.title, regex: regex) {
continue
}
}
}

if calendarEvent.isAllDay {
// Filter all day events
switch Defaults[.allDayEvents] {
case .show:
break
case .show_with_meeting_link_only:
if calendarEvent.meetingLink?.url == nil { continue } // Skip this event
case .hide:
continue // Skip this event
}
} else {
// Filter not for all day events
switch Defaults[.nonAllDayEvents] {
case .show, .show_inactive_without_meeting_link:
break
case .hide_without_meeting_link:
if calendarEvent.meetingLink?.url == nil { continue } // Skip this event
}
}

// Filter pending events
switch Defaults[.showPendingEvents] {
case .show, .show_inactive, .show_underlined:
break
case .hide:
if calendarEvent.participationStatus == .pending { continue } // Skip this event
}

filteredCalendarEvents.append(calendarEvent)
}
return filteredCalendarEvents
}
3 changes: 1 addition & 2 deletions MeetingBar/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,7 @@ enum JoinEventNotificationTime: Int, Codable {
enum UtilsRegex {
static let emailAddress = try! NSRegularExpression(pattern: #"(.+@.+)"#)
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)

static let linkDetection = try! NSRegularExpression(pattern: #"(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?"#, options: .caseInsensitive)
}

public enum AutoLauncher {
Expand Down
Loading

0 comments on commit aaca46b

Please sign in to comment.