From 6cf3af17d63505be12c1060c16cea2c7d7a41612 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Thu, 1 Jul 2021 22:05:49 -0500 Subject: [PATCH 01/26] Update Panel.xib --- Clocker/Clocker/en.lproj/Panel.xib | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Clocker/Clocker/en.lproj/Panel.xib b/Clocker/Clocker/en.lproj/Panel.xib index e07f290..e10406a 100755 --- a/Clocker/Clocker/en.lproj/Panel.xib +++ b/Clocker/Clocker/en.lproj/Panel.xib @@ -41,7 +41,7 @@ - + @@ -314,7 +314,7 @@ - + From fe78d4d4dbb9eaa8b1b5673e058b6708cc3e9b59 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Thu, 1 Jul 2021 22:07:11 -0500 Subject: [PATCH 02/26] Small updates! --- .../ParentPanelController+ModernSlider.swift | 2 +- .../Rate Controller/UpcomingEventView.swift | 48 ++++++------------- Clocker/Panel/UI/HourMarkerViewItem.xib | 3 +- ...iewItem.swift => TimeMarkerViewItem.swift} | 4 +- 4 files changed, 18 insertions(+), 39 deletions(-) rename Clocker/Panel/UI/{HourMarkerViewItem.swift => TimeMarkerViewItem.swift} (85%) diff --git a/Clocker/Panel/ParentPanelController+ModernSlider.swift b/Clocker/Panel/ParentPanelController+ModernSlider.swift index 2a88eba..270e1a9 100644 --- a/Clocker/Panel/ParentPanelController+ModernSlider.swift +++ b/Clocker/Panel/ParentPanelController+ModernSlider.swift @@ -9,7 +9,7 @@ extension ParentPanelController: NSCollectionViewDataSource { } func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { - let item = collectionView.makeItem(withIdentifier: HourMarkerViewItem.reuseIdentifier, for: indexPath) as! HourMarkerViewItem + let item = collectionView.makeItem(withIdentifier: TimeMarkerViewItem.reuseIdentifier, for: indexPath) as! TimeMarkerViewItem item.setup(with: indexPath.item) return item } diff --git a/Clocker/Panel/Rate Controller/UpcomingEventView.swift b/Clocker/Panel/Rate Controller/UpcomingEventView.swift index 40fcf89..9074051 100644 --- a/Clocker/Panel/Rate Controller/UpcomingEventView.swift +++ b/Clocker/Panel/Rate Controller/UpcomingEventView.swift @@ -88,55 +88,35 @@ class ThinScroller: NSScroller { class DraggableClipView: NSClipView { private var clickPoint: NSPoint! - private var originalOrigin: NSPoint! private var trackingArea: NSTrackingArea? - + override func mouseDown(with event: NSEvent) { - print("2") super.mouseDown(with: event) clickPoint = event.locationInWindow - originalOrigin = bounds.origin - print("Click point \(clickPoint!)") - let keepOn = true - while keepOn { - let newEvent = window?.nextEvent(matching: [.leftMouseDragged, .leftMouseUp]) + var gestureInProgress = true + while gestureInProgress { + let newEvent = window?.nextEvent(matching: [.leftMouseDragged, .leftMouseUp, .leftMouseDown]) switch newEvent?.type { case .leftMouseDragged: - print("Hello 1") - let scale = (superview as? NSScrollView)?.magnification ?? 1.0 - let newPoint = event.locationInWindow - let newOrigin = NSPoint(x: originalOrigin.x + (clickPoint.x - newPoint.x) / scale, - y: originalOrigin.y + (clickPoint.y - newPoint.y) / scale) + let newPoint = newEvent?.locationInWindow + let xCoOrdinate = clickPoint.x - (newPoint?.x ?? 0) + let newOrigin = NSPoint(x: bounds.origin.x + xCoOrdinate, + y: 0) let constrainedRect = constrainBoundsRect(NSRect(origin: newOrigin, size: bounds.size)) scroll(to: constrainedRect.origin) superview?.reflectScrolledClipView(self) - return + case .leftMouseDown: + clickPoint = event.locationInWindow + case .leftMouseUp: + clickPoint = nil + gestureInProgress = false default: - print("Hello2") + print("Default case is happening \(event.type)") } } } - override func mouseDragged(with event: NSEvent) { - print("1") - super.mouseDragged(with: event) - // Account for a magnified parent scrollview. - let scale = (superview as? NSScrollView)?.magnification ?? 1.0 - let newPoint = event.locationInWindow - let newOrigin = NSPoint(x: originalOrigin.x + (clickPoint.x - newPoint.x) / scale, - y: originalOrigin.y + (clickPoint.y - newPoint.y) / scale) - let constrainedRect = constrainBoundsRect(NSRect(origin: newOrigin, size: bounds.size)) - scroll(to: constrainedRect.origin) - superview?.reflectScrolledClipView(self) - } - - override func mouseUp(with event: NSEvent) { - print("3") - super.mouseUp(with: event) - clickPoint = nil - } - override func updateTrackingAreas() { super.updateTrackingAreas() diff --git a/Clocker/Panel/UI/HourMarkerViewItem.xib b/Clocker/Panel/UI/HourMarkerViewItem.xib index 661bb29..f799043 100644 --- a/Clocker/Panel/UI/HourMarkerViewItem.xib +++ b/Clocker/Panel/UI/HourMarkerViewItem.xib @@ -1,7 +1,6 @@ - @@ -27,7 +26,7 @@ - + diff --git a/Clocker/Panel/UI/HourMarkerViewItem.swift b/Clocker/Panel/UI/TimeMarkerViewItem.swift similarity index 85% rename from Clocker/Panel/UI/HourMarkerViewItem.swift rename to Clocker/Panel/UI/TimeMarkerViewItem.swift index fcae20d..6e3653e 100644 --- a/Clocker/Panel/UI/HourMarkerViewItem.swift +++ b/Clocker/Panel/UI/TimeMarkerViewItem.swift @@ -2,13 +2,13 @@ import Cocoa -class HourMarkerViewItem: NSCollectionViewItem { +class TimeMarkerViewItem: NSCollectionViewItem { static let topConstraintIdentifier = "constrainFromTop" static let reuseIdentifier = NSUserInterfaceItemIdentifier("HourMarkerViewItem") @IBOutlet var verticalLineView: NSView! func setup(with index: Int) { - for constraint in view.constraints where constraint.identifier == HourMarkerViewItem.topConstraintIdentifier { + for constraint in view.constraints where constraint.identifier == TimeMarkerViewItem.topConstraintIdentifier { constraint.constant = index % 4 == 0 ? 0 : 20 } verticalLineView.wantsLayer = true From 0016f6e08c30d05444b77aa6c77bf0305134a344 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Thu, 1 Jul 2021 23:36:20 -0500 Subject: [PATCH 03/26] Use exhaustive switch to catch bugs! --- .../Sources/CoreModelKit/TimezoneData.swift | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Clocker/CoreModelKit/Sources/CoreModelKit/TimezoneData.swift b/Clocker/CoreModelKit/Sources/CoreModelKit/TimezoneData.swift index 649384b..3959b3d 100644 --- a/Clocker/CoreModelKit/Sources/CoreModelKit/TimezoneData.swift +++ b/Clocker/CoreModelKit/Sources/CoreModelKit/TimezoneData.swift @@ -298,28 +298,27 @@ public class TimezoneData: NSObject, NSCoding { public func timezoneFormat(_ currentFormat: NSNumber) -> String { let chosenDefault = currentFormat let timeFormat = TimezoneData.values[chosenDefault] ?? DateFormat.twelveHour - - if overrideFormat == .globalFormat { + + switch overrideFormat { + case .globalFormat: return timeFormat - } else if overrideFormat == .twelveHourFormat { + case .twelveHourFormat: return DateFormat.twelveHour - } else if overrideFormat == .twentyFourFormat { + case .twentyFourFormat: return DateFormat.twentyFourHour - } else if overrideFormat == .twelveHourWithSeconds { + case .twelveHourWithSeconds: return DateFormat.twelveHourWithSeconds - } else if overrideFormat == .twentyHourWithSeconds { + case .twentyHourWithSeconds: return DateFormat.twentyFourHourWithSeconds - } else if overrideFormat == .twelveHourPrecedingZero { + case .twelveHourPrecedingZero: return DateFormat.twelveHourWithZero - } else if overrideFormat == .twelveHourPrecedingZeroSeconds { + case .twelveHourPrecedingZeroSeconds: return DateFormat.twelveHourWithZeroSeconds - } else if overrideFormat == .twelveHourWithoutSuffix { + case .twelveHourWithoutSuffix: return DateFormat.twelveHourWithoutSuffix - } else if overrideFormat == .twelveHourWithoutSuffixAndSeconds { + case .twelveHourWithoutSuffixAndSeconds: return DateFormat.twelveHourWithoutSuffixAndSeconds } - - return timeFormat } public func shouldShowSeconds(_ currentFormat: NSNumber) -> Bool { From 2e2d5477c596e7ca104cae6f5dcd269275a45483 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 00:14:55 -0500 Subject: [PATCH 04/26] Add Epoch Time. --- .../CoreModelKit/Sources/CoreModelKit/TimezoneData.swift | 7 +++++++ Clocker/Panel/Data Layer/TimezoneDataOperations.swift | 7 +++++++ Clocker/Panel/Notes Popover/NotesPopover.swift | 3 ++- .../Preferences/Appearance/AppearanceViewController.swift | 6 ++---- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Clocker/CoreModelKit/Sources/CoreModelKit/TimezoneData.swift b/Clocker/CoreModelKit/Sources/CoreModelKit/TimezoneData.swift index 3959b3d..ae92fef 100644 --- a/Clocker/CoreModelKit/Sources/CoreModelKit/TimezoneData.swift +++ b/Clocker/CoreModelKit/Sources/CoreModelKit/TimezoneData.swift @@ -20,6 +20,7 @@ public struct DateFormat { public static let twelveHourWithZeroSeconds = "hh:mm:ss a" public static let twelveHourWithoutSuffix = "hh:mm" public static let twelveHourWithoutSuffixAndSeconds = "hh:mm:ss" + public static let epochTime = "epoch" } // Non-class type cannot conform to NSCoding! @@ -44,6 +45,7 @@ public class TimezoneData: NSObject, NSCoding { case twelveHourPrecedingZeroSeconds = 8 case twelveHourWithoutSuffix = 10 case twelveHourWithoutSuffixAndSeconds = 11 + case epochTime = 12 } static let values = [ @@ -61,6 +63,7 @@ public class TimezoneData: NSObject, NSCoding { // Suffix NSNumber(integerLiteral: 9): DateFormat.twelveHourWithoutSuffix, NSNumber(integerLiteral: 10): DateFormat.twelveHourWithoutSuffixAndSeconds, + NSNumber(integerLiteral: 11): DateFormat.epochTime, ] public var customLabel: String? @@ -266,6 +269,8 @@ public class TimezoneData: NSObject, NSCoding { overrideFormat = .twelveHourWithoutSuffix } else if shouldOverride == 11 { overrideFormat = .twelveHourWithoutSuffixAndSeconds + } else if shouldOverride == 12 { + overrideFormat = .epochTime } else { assertionFailure("Chosen a wrong timezone format") } @@ -318,6 +323,8 @@ public class TimezoneData: NSObject, NSCoding { return DateFormat.twelveHourWithoutSuffix case .twelveHourWithoutSuffixAndSeconds: return DateFormat.twelveHourWithoutSuffixAndSeconds + case .epochTime: + return DateFormat.epochTime } } diff --git a/Clocker/Panel/Data Layer/TimezoneDataOperations.swift b/Clocker/Panel/Data Layer/TimezoneDataOperations.swift index 8a1234d..c6b27fb 100644 --- a/Clocker/Panel/Data Layer/TimezoneDataOperations.swift +++ b/Clocker/Panel/Data Layer/TimezoneDataOperations.swift @@ -31,6 +31,13 @@ extension TimezoneDataOperations { format: dataObject.timezoneFormat(DataStore.shared().timezoneFormat()), timezoneIdentifier: dataObject.timezone(), locale: Locale.autoupdatingCurrent) + + if (dataObject.timezoneFormat(DataStore.shared().timezoneFormat()) == DateFormat.epochTime) { + let timezone = TimeZone(identifier: dataObject.timezone()) + let offset = timezone?.secondsFromGMT(for: newDate) ?? 0 + let value = Int(Date().timeIntervalSince1970 + Double(offset)) + return "\(value)" + } return dateFormatter.string(from: newDate) } diff --git a/Clocker/Panel/Notes Popover/NotesPopover.swift b/Clocker/Panel/Notes Popover/NotesPopover.swift index 31762c8..af340f1 100644 --- a/Clocker/Panel/Notes Popover/NotesPopover.swift +++ b/Clocker/Panel/Notes Popover/NotesPopover.swift @@ -92,7 +92,8 @@ class NotesPopover: NSViewController { "hh:mm:ss a (07:08:09 PM)", "-- 12 Hour w/o AM/PM --", "hh:mm (07:08)", - "hh:mm:ss (07:08:09)"] + "hh:mm:ss (07:08:09)", + "Epoch Time"] timeFormatControl.removeAllItems() timeFormatControl.addItems(withTitles: supportedTimeFormats) diff --git a/Clocker/Preferences/Appearance/AppearanceViewController.swift b/Clocker/Preferences/Appearance/AppearanceViewController.swift index 3ad8786..9fb408b 100644 --- a/Clocker/Preferences/Appearance/AppearanceViewController.swift +++ b/Clocker/Preferences/Appearance/AppearanceViewController.swift @@ -38,7 +38,8 @@ class AppearanceViewController: ParentViewController { "hh:mm:ss a (07:08:09 PM)", "-- 12 Hour w/o AM/PM --", "hh:mm (07:08)", - "hh:mm:ss (07:08:09)"] + "hh:mm:ss (07:08:09)", + "Epoch Time"] timeFormat.removeAllItems() timeFormat.addItems(withTitles: supportedTimeFormats) @@ -179,9 +180,6 @@ class AppearanceViewController: ParentViewController { let selection = NSNumber(value: sender.indexOfSelectedItem) UserDefaults.standard.set(selection, forKey: CLSelectedTimeZoneFormatKey) - - Logger.log(object: ["Time Format": sender.indexOfSelectedItem == 0 ? "12 Hour Format" : "24 Hour Format"], for: "Time Format Selected") - refresh(panel: true, floating: true) if let selectedFormat = sender.selectedItem?.title, From d21f6bb156b8984a45c9d1d86fb82f56c5f560c6 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 00:20:38 -0500 Subject: [PATCH 05/26] Update TimezoneDataSource.swift --- Clocker/Panel/UI/TimezoneDataSource.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Clocker/Panel/UI/TimezoneDataSource.swift b/Clocker/Panel/UI/TimezoneDataSource.swift index 06dbb73..9457903 100644 --- a/Clocker/Panel/UI/TimezoneDataSource.swift +++ b/Clocker/Panel/UI/TimezoneDataSource.swift @@ -157,7 +157,7 @@ extension TimezoneDataSource: NSTableViewDataSource, NSTableViewDelegate { return [] } - func showAlertForDeletingAHomeRow(_ row: Int, _ tableView: NSTableView) { + private func showAlertForDeletingAHomeRow(_ row: Int, _ tableView: NSTableView) { NSApplication.shared.activate(ignoringOtherApps: true) let alert = NSAlert() From 12268277122a014e24ba4a61bec0c280fbc4e95f Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 00:32:23 -0500 Subject: [PATCH 06/26] Add copy functionality. --- Clocker/Panel/UI/TimezoneCellView.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Clocker/Panel/UI/TimezoneCellView.swift b/Clocker/Panel/UI/TimezoneCellView.swift index c29a547..ee020cf 100644 --- a/Clocker/Panel/UI/TimezoneCellView.swift +++ b/Clocker/Panel/UI/TimezoneCellView.swift @@ -198,6 +198,12 @@ class TimezoneCellView: NSTableCellView { } override func mouseDown(with _: NSEvent) { + // Text is copied in the following format: Chicago - 1625185925 + let clipboardCopy = "\(customName.stringValue) - \(time.stringValue)" + let pasteboard = NSPasteboard.general + pasteboard.declareTypes([.string], owner: nil) + pasteboard.setString(clipboardCopy, forType: .string) + window?.endEditing(for: nil) } From 9750bcf04b43c98816dfc678a48283f1fcd90076 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 00:33:17 -0500 Subject: [PATCH 07/26] Update TimezoneCellView.swift --- Clocker/Panel/UI/TimezoneCellView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Clocker/Panel/UI/TimezoneCellView.swift b/Clocker/Panel/UI/TimezoneCellView.swift index ee020cf..0d8254a 100644 --- a/Clocker/Panel/UI/TimezoneCellView.swift +++ b/Clocker/Panel/UI/TimezoneCellView.swift @@ -199,6 +199,7 @@ class TimezoneCellView: NSTableCellView { override func mouseDown(with _: NSEvent) { // Text is copied in the following format: Chicago - 1625185925 + // TODO: Write tests for copy functionality let clipboardCopy = "\(customName.stringValue) - \(time.stringValue)" let pasteboard = NSPasteboard.general pasteboard.declareTypes([.string], owner: nil) From 8250bc70b7ee1e17ef3fb291806c9a3b9a95dac6 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 10:36:20 -0500 Subject: [PATCH 08/26] Copy functionality! --- Clocker/Clocker/ca.lproj/Localizable.strings | 2 + Clocker/Clocker/de.lproj/Localizable.strings | 2 + Clocker/Clocker/en.lproj/Localizable.strings | 1 + Clocker/Clocker/es.lproj/Localizable.strings | 2 + Clocker/Clocker/fr.lproj/Localizable.strings | 2 + Clocker/Clocker/hi.lproj/Localizable.strings | 2 + Clocker/Clocker/it.lproj/Localizable.strings | 2 + Clocker/Clocker/ja.lproj/Localizable.strings | 2 + Clocker/Clocker/ko.lproj/Localizable.strings | 2 + Clocker/Clocker/nl.lproj/Localizable.strings | 2 + .../Clocker/pt-BR.lproj/Localizable.strings | 1 + Clocker/Clocker/ru.lproj/Localizable.strings | 2 + .../Clocker/zh-Hans.lproj/Localizable.strings | 2 + .../Clocker/zh-Hant.lproj/Localizable.strings | 1 + Clocker/Panel/UI/TimezoneCellView.swift | 2 + Clocker/Panel/UI/Toasty.swift | 164 ++++++++++++++++++ 16 files changed, 191 insertions(+) create mode 100644 Clocker/Panel/UI/Toasty.swift diff --git a/Clocker/Clocker/ca.lproj/Localizable.strings b/Clocker/Clocker/ca.lproj/Localizable.strings index 48dbfd8..eb4fd3b 100644 --- a/Clocker/Clocker/ca.lproj/Localizable.strings +++ b/Clocker/Clocker/ca.lproj/Localizable.strings @@ -159,3 +159,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/de.lproj/Localizable.strings b/Clocker/Clocker/de.lproj/Localizable.strings index a4e3131..6269afd 100644 --- a/Clocker/Clocker/de.lproj/Localizable.strings +++ b/Clocker/Clocker/de.lproj/Localizable.strings @@ -159,3 +159,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/en.lproj/Localizable.strings b/Clocker/Clocker/en.lproj/Localizable.strings index 8acf703..f2b6950 100644 --- a/Clocker/Clocker/en.lproj/Localizable.strings +++ b/Clocker/Clocker/en.lproj/Localizable.strings @@ -162,3 +162,4 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/es.lproj/Localizable.strings b/Clocker/Clocker/es.lproj/Localizable.strings index 249fe28..47d6fa9 100644 --- a/Clocker/Clocker/es.lproj/Localizable.strings +++ b/Clocker/Clocker/es.lproj/Localizable.strings @@ -161,3 +161,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/fr.lproj/Localizable.strings b/Clocker/Clocker/fr.lproj/Localizable.strings index f5c60db..e823d0b 100644 --- a/Clocker/Clocker/fr.lproj/Localizable.strings +++ b/Clocker/Clocker/fr.lproj/Localizable.strings @@ -160,3 +160,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/hi.lproj/Localizable.strings b/Clocker/Clocker/hi.lproj/Localizable.strings index 7dd021c..451dfc2 100644 --- a/Clocker/Clocker/hi.lproj/Localizable.strings +++ b/Clocker/Clocker/hi.lproj/Localizable.strings @@ -155,3 +155,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/it.lproj/Localizable.strings b/Clocker/Clocker/it.lproj/Localizable.strings index b9a182e..04adcc3 100644 --- a/Clocker/Clocker/it.lproj/Localizable.strings +++ b/Clocker/Clocker/it.lproj/Localizable.strings @@ -160,3 +160,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/ja.lproj/Localizable.strings b/Clocker/Clocker/ja.lproj/Localizable.strings index f7e4229..53cdfea 100644 --- a/Clocker/Clocker/ja.lproj/Localizable.strings +++ b/Clocker/Clocker/ja.lproj/Localizable.strings @@ -159,3 +159,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/ko.lproj/Localizable.strings b/Clocker/Clocker/ko.lproj/Localizable.strings index 2c49183..50ba241 100644 --- a/Clocker/Clocker/ko.lproj/Localizable.strings +++ b/Clocker/Clocker/ko.lproj/Localizable.strings @@ -162,3 +162,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/nl.lproj/Localizable.strings b/Clocker/Clocker/nl.lproj/Localizable.strings index 48dbfd8..eb4fd3b 100644 --- a/Clocker/Clocker/nl.lproj/Localizable.strings +++ b/Clocker/Clocker/nl.lproj/Localizable.strings @@ -159,3 +159,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/pt-BR.lproj/Localizable.strings b/Clocker/Clocker/pt-BR.lproj/Localizable.strings index 0a4e6e0..ac6f642 100644 --- a/Clocker/Clocker/pt-BR.lproj/Localizable.strings +++ b/Clocker/Clocker/pt-BR.lproj/Localizable.strings @@ -161,3 +161,4 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/ru.lproj/Localizable.strings b/Clocker/Clocker/ru.lproj/Localizable.strings index de3f64e..08b025d 100644 --- a/Clocker/Clocker/ru.lproj/Localizable.strings +++ b/Clocker/Clocker/ru.lproj/Localizable.strings @@ -152,3 +152,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/zh-Hans.lproj/Localizable.strings b/Clocker/Clocker/zh-Hans.lproj/Localizable.strings index e705d48..81dd154 100644 --- a/Clocker/Clocker/zh-Hans.lproj/Localizable.strings +++ b/Clocker/Clocker/zh-Hans.lproj/Localizable.strings @@ -153,3 +153,5 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Clocker/zh-Hant.lproj/Localizable.strings b/Clocker/Clocker/zh-Hant.lproj/Localizable.strings index 5874828..23d4d48 100644 --- a/Clocker/Clocker/zh-Hant.lproj/Localizable.strings +++ b/Clocker/Clocker/zh-Hant.lproj/Localizable.strings @@ -161,3 +161,4 @@ // DST changes "Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Panel/UI/TimezoneCellView.swift b/Clocker/Panel/UI/TimezoneCellView.swift index 0d8254a..2eaf28c 100644 --- a/Clocker/Panel/UI/TimezoneCellView.swift +++ b/Clocker/Panel/UI/TimezoneCellView.swift @@ -205,6 +205,8 @@ class TimezoneCellView: NSTableCellView { pasteboard.declareTypes([.string], owner: nil) pasteboard.setString(clipboardCopy, forType: .string) + self.window?.contentView?.makeToast("Copied to Clipboard".localized()) + window?.endEditing(for: nil) } diff --git a/Clocker/Panel/UI/Toasty.swift b/Clocker/Panel/UI/Toasty.swift new file mode 100644 index 0000000..86455fe --- /dev/null +++ b/Clocker/Panel/UI/Toasty.swift @@ -0,0 +1,164 @@ +// Copyright © 2015 Abhishek Banthia + +import Foundation +import AppKit + +extension CGRect { + static func center(of layer: CALayer) -> CGPoint { + let parentSize = layer.frame.size + return CGPoint(x: parentSize.width / 2, y: parentSize.height / 2) + } + static func center(of parent: NSView) -> CGPoint { + let parentSize = parent.frame.size + return CGPoint(x: parentSize.width / 2, y: parentSize.height / 6) + } +} + +extension String { + func size(with fontSize: CGFloat) -> CGSize { + let attr: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: NSFont.systemFont(ofSize: fontSize)] + let size = NSString(string: self).size(withAttributes: attr) + return size + } +} + +fileprivate class HideAnimationDelegate: NSObject, CAAnimationDelegate { + private weak var view: NSView? + fileprivate init(view: NSView) { + self.view = view + } + fileprivate static func delegate(forView NSView: NSView) -> CAAnimationDelegate { + return HideAnimationDelegate(view: NSView) + } + fileprivate func animationDidStart(_ anim: CAAnimation) { + view?.layer?.opacity = 0.0 + } + func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { + view?.removeFromSuperview() + view = nil + } +} + +func hideAnimation(view: NSView, style: Style) { + let anim = CABasicAnimation(keyPath: "opacity") + let timing = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) + anim.timingFunction = timing + let currentLayerTime = view.layer?.convertTime(CACurrentMediaTime(), from: nil) + anim.beginTime = currentLayerTime! + CFTimeInterval(style.fadeInOutDelay) + anim.duration = CFTimeInterval(style.fadeInOutDuration) + anim.fromValue = 1.0 + anim.toValue = 0.0 + anim.isRemovedOnCompletion = false + anim.delegate = HideAnimationDelegate.delegate(forView: view) + + view.layer?.add(anim, forKey: "hide animation") +} + +public protocol Style { + var fontSize: CGFloat {get} + var horizontalMargin: CGFloat {get} + var verticalMargin: CGFloat {get} + var cornerRadius: CGFloat {get} + var font: NSFont {get} + var backgroundColor: NSColor {get} + var foregroundColor: NSColor {get} + var fadeInOutDuration: CGFloat {get} + var fadeInOutDelay: CGFloat {get} + var labelOriginWithMargin: CGPoint {get} + var activitySize: CGSize {get} +} + +extension Style { + public var labelOriginWithMargin: CGPoint { + return CGPoint(x: horizontalMargin, y: verticalMargin) + } + public var fontSize: CGFloat {return 12} + public var font: NSFont { + if let avenirFont = NSFont(name: "Avenir-Light", size: fontSize) { + return avenirFont + } + return NSFont.systemFont(ofSize: fontSize) + } + public var horizontalMargin: CGFloat {return 10} + public var verticalMargin: CGFloat {return 5} + public var cornerRadius: CGFloat {return 8} + public var backgroundColor: NSColor {return .black} + public var foregroundColor: NSColor {return .white} + public var activitySize: CGSize {return CGSize(width: 100, height: 100)} + public var fadeInOutDuration: CGFloat {return 1.0} + public var fadeInOutDelay: CGFloat {return 1.0} +} + +public struct DefaultStyle: Style { + public static let shared = DefaultStyle() +} + +private struct ToastKeys { + static var ActiveToast = "TSToastActiveToastKey" +} + +class ToastView: NSView { + private let message: String + private let labelSize: CGSize + private let style: Style + init(message: String) { + self.message = message + self.style = DefaultStyle() + self.labelSize = message.size(with: style.fontSize) + let size = CGSize( + width: labelSize.width + style.horizontalMargin*2, + height: labelSize.height + style.verticalMargin*2 + ) + let rect = CGRect(origin: .zero, size: size) + super.init(frame: rect) + wantsLayer = true + } + required init?(coder aDecoder: NSCoder) { fatalError() } + + override func viewDidMoveToSuperview() { + super.viewDidMoveToSuperview() + if superview != nil { + configure() + } + } + + private func configure() { + frame = superview?.bounds ?? NSRect.zero + let rect = CGRect(origin: style.labelOriginWithMargin, size: labelSize) + let sizeWithMargin = CGSize( + width: rect.width + style.horizontalMargin*2, + height: rect.height + style.verticalMargin*2 + ) + let rectWithMargin = CGRect( + origin: .zero, // position is manipulated later anyways + size: sizeWithMargin + ) + // outside Container + let container = CALayer() + container.frame = rectWithMargin + container.position = CGRect.center(of: superview!) + container.backgroundColor = style.backgroundColor.cgColor + container.cornerRadius = style.cornerRadius + layer?.addSublayer(container) + // inside TextLayer + let text = CATextLayer() + text.frame = rect + text.position = CGRect.center(of: container) + text.string = message + text.font = NSFont.systemFont(ofSize: style.fontSize) + text.fontSize = style.fontSize + text.alignmentMode = .center + text.foregroundColor = style.foregroundColor.cgColor + text.backgroundColor = style.backgroundColor.cgColor + text.contentsScale = layer?.contentsScale ?? 0 // For Retina Display + container.addSublayer(text) + } +} + +extension NSView { + public func makeToast(_ message: String) { + let toast = ToastView(message: message) + self.addSubview(toast) + hideAnimation(view: toast, style: DefaultStyle.shared) + } +} From 443de18a51a3a57465e99600280292afc49f4a99 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 10:48:33 -0500 Subject: [PATCH 09/26] Minor order shift. --- .../Panel/Data Layer/TimezoneDataOperations.swift | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Clocker/Panel/Data Layer/TimezoneDataOperations.swift b/Clocker/Panel/Data Layer/TimezoneDataOperations.swift index c6b27fb..94cb558 100644 --- a/Clocker/Panel/Data Layer/TimezoneDataOperations.swift +++ b/Clocker/Panel/Data Layer/TimezoneDataOperations.swift @@ -26,19 +26,21 @@ extension TimezoneDataOperations { assertionFailure("Data was unexpectedly nil") return CLEmptyString } - - let dateFormatter = DateFormatterManager.dateFormatterWithFormat(with: .none, - format: dataObject.timezoneFormat(DataStore.shared().timezoneFormat()), - timezoneIdentifier: dataObject.timezone(), - locale: Locale.autoupdatingCurrent) if (dataObject.timezoneFormat(DataStore.shared().timezoneFormat()) == DateFormat.epochTime) { + print("Slider Value is \(sliderValue)") let timezone = TimeZone(identifier: dataObject.timezone()) let offset = timezone?.secondsFromGMT(for: newDate) ?? 0 let value = Int(Date().timeIntervalSince1970 + Double(offset)) return "\(value)" } + let dateFormatter = DateFormatterManager.dateFormatterWithFormat(with: .none, + format: dataObject.timezoneFormat(DataStore.shared().timezoneFormat()), + timezoneIdentifier: dataObject.timezone(), + locale: Locale.autoupdatingCurrent) + + return dateFormatter.string(from: newDate) } From d6ccd4b82e6d873fc627ddcd41067b3beafec429 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 11:01:06 -0500 Subject: [PATCH 10/26] Update project.pbxproj --- Clocker/Clocker.xcodeproj/project.pbxproj | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Clocker/Clocker.xcodeproj/project.pbxproj b/Clocker/Clocker.xcodeproj/project.pbxproj index 4ef8374..fc78b36 100755 --- a/Clocker/Clocker.xcodeproj/project.pbxproj +++ b/Clocker/Clocker.xcodeproj/project.pbxproj @@ -11,7 +11,7 @@ 3508CC9A259A0001000E3530 /* StatusItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3508CC99259A0001000E3530 /* StatusItemView.swift */; }; 3508CC9F259A000E000E3530 /* StatusItemHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3508CC9E259A000E000E3530 /* StatusItemHandler.swift */; }; 3508CCAA259A0027000E3530 /* StatusContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3508CCA9259A0027000E3530 /* StatusContainerView.swift */; }; - 357391872507277500D30819 /* HourMarkerViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 357391852507277500D30819 /* HourMarkerViewItem.swift */; }; + 357391872507277500D30819 /* TimeMarkerViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 357391852507277500D30819 /* TimeMarkerViewItem.swift */; }; 357391882507277500D30819 /* HourMarkerViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = 357391862507277500D30819 /* HourMarkerViewItem.xib */; }; 3579765E2680208C009DDA6E /* ParentPanelController+ModernSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3579765D2680208C009DDA6E /* ParentPanelController+ModernSlider.swift */; }; 3595FAD0227F88BC0044A12A /* UserDefaults + KVOExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3595FACF227F88BC0044A12A /* UserDefaults + KVOExtensions.swift */; }; @@ -84,6 +84,7 @@ 35C36FA12259ED6D002FA5C6 /* EventCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C36F9E2259ED6D002FA5C6 /* EventCenter.swift */; }; 35C36FA22259ED6D002FA5C6 /* RemindersHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C36F9F2259ED6D002FA5C6 /* RemindersHandler.swift */; }; 35C36FA42259EEC2002FA5C6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C36FA32259EEC2002FA5C6 /* AppDelegate.swift */; }; + 35E65125268EDD2E00E3E1E3 /* Toasty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35E65124268EDD2E00E3E1E3 /* Toasty.swift */; }; 9A0A1C8C20903DBD0012003B /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A9E87651C1FEDC500A7A2DF /* CoreLocation.framework */; }; 9A13BAD61CA87F08007C6CBE /* Panel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9A13BAD81CA87F08007C6CBE /* Panel.xib */; }; 9A13BAE01CA882FA007C6CBE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9A13BAE21CA882FA007C6CBE /* InfoPlist.strings */; }; @@ -229,7 +230,7 @@ 352AF497232E07B400D96FA7 /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi; path = hi.lproj/InfoPlist.strings; sourceTree = ""; }; 352AF499232E07B400D96FA7 /* hi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi; path = hi.lproj/Localizable.strings; sourceTree = ""; }; 3569A44E25441F320087E254 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; - 357391852507277500D30819 /* HourMarkerViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HourMarkerViewItem.swift; sourceTree = ""; }; + 357391852507277500D30819 /* TimeMarkerViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeMarkerViewItem.swift; sourceTree = ""; }; 357391862507277500D30819 /* HourMarkerViewItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HourMarkerViewItem.xib; sourceTree = ""; }; 3579765D2680208C009DDA6E /* ParentPanelController+ModernSlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParentPanelController+ModernSlider.swift"; sourceTree = ""; }; 3595FACF227F88BC0044A12A /* UserDefaults + KVOExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults + KVOExtensions.swift"; sourceTree = ""; }; @@ -304,6 +305,7 @@ 35C36F9E2259ED6D002FA5C6 /* EventCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventCenter.swift; sourceTree = ""; }; 35C36F9F2259ED6D002FA5C6 /* RemindersHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemindersHandler.swift; sourceTree = ""; }; 35C36FA32259EEC2002FA5C6 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = SOURCE_ROOT; }; + 35E65124268EDD2E00E3E1E3 /* Toasty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toasty.swift; sourceTree = ""; }; 9A13BAD71CA87F08007C6CBE /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/Panel.xib; sourceTree = ""; }; 9A13BAE11CA882FA007C6CBE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 9A13BAEB1CA88A76007C6CBE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; @@ -603,8 +605,9 @@ 35C36F552259DD8A002FA5C6 /* TimezoneCellView.swift */, 35C36F532259DD8A002FA5C6 /* TimezoneDataSource.swift */, 35C36F322259D7C3002FA5C6 /* AddTableViewCell.swift */, - 357391852507277500D30819 /* HourMarkerViewItem.swift */, + 357391852507277500D30819 /* TimeMarkerViewItem.swift */, 357391862507277500D30819 /* HourMarkerViewItem.xib */, + 35E65124268EDD2E00E3E1E3 /* Toasty.swift */, ); path = UI; sourceTree = ""; @@ -1153,7 +1156,7 @@ }; C2A632A020EAC5EE00EB6BEA /* SwiftFormat */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; + buildActionMask = 8; files = ( ); inputPaths = ( @@ -1161,9 +1164,9 @@ name = SwiftFormat; outputPaths = ( ); - runOnlyForDeploymentPostprocessing = 0; + runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "if which swiftformat >/dev/null; then\n swiftformat . --swiftversion 5\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n"; + shellScript = "if which swiftformat >/dev/null; then\\n swiftformat . --swiftversion 5\\nelse\\n echo \\\"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\\\"\\nfi\\n\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -1222,7 +1225,7 @@ 35C36EF722595F14002FA5C6 /* FinalOnboardingViewController.swift in Sources */, 35C36FA12259ED6D002FA5C6 /* EventCenter.swift in Sources */, 9A5951BD1C1D0A8D009C17AA /* CommonStrings.m in Sources */, - 357391872507277500D30819 /* HourMarkerViewItem.swift in Sources */, + 357391872507277500D30819 /* TimeMarkerViewItem.swift in Sources */, 35C36F782259E1D0002FA5C6 /* Foundation + Additions.swift in Sources */, 35C36F16225961DA002FA5C6 /* Date+Inits.swift in Sources */, 35C36F4F2259D981002FA5C6 /* AppDefaults.swift in Sources */, @@ -1259,6 +1262,7 @@ 35C36F462259D892002FA5C6 /* DataStore.swift in Sources */, 9ACF618D231DABAE00F5E51E /* SearchDataSource.swift in Sources */, 3508CC9F259A000E000E3530 /* StatusItemHandler.swift in Sources */, + 35E65125268EDD2E00E3E1E3 /* Toasty.swift in Sources */, C2CCCD8220619C4C00F2DFC2 /* LocationController.swift in Sources */, 35C36F4B2259D971002FA5C6 /* UnderlinedButton.swift in Sources */, 9AB6F1562259CF3900A44663 /* CalendarViewController.swift in Sources */, From a9cd538bf6a1992724c6bfe6999c7f4864b2238c Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 11:01:11 -0500 Subject: [PATCH 11/26] Update TimezoneDataOperations.swift --- Clocker/Panel/Data Layer/TimezoneDataOperations.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Clocker/Panel/Data Layer/TimezoneDataOperations.swift b/Clocker/Panel/Data Layer/TimezoneDataOperations.swift index 94cb558..f0d4f1f 100644 --- a/Clocker/Panel/Data Layer/TimezoneDataOperations.swift +++ b/Clocker/Panel/Data Layer/TimezoneDataOperations.swift @@ -28,7 +28,6 @@ extension TimezoneDataOperations { } if (dataObject.timezoneFormat(DataStore.shared().timezoneFormat()) == DateFormat.epochTime) { - print("Slider Value is \(sliderValue)") let timezone = TimeZone(identifier: dataObject.timezone()) let offset = timezone?.secondsFromGMT(for: newDate) ?? 0 let value = Int(Date().timeIntervalSince1970 + Double(offset)) From 7de6ab54c4130304942a80a336bd2699c0b5c331 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 12:41:21 -0500 Subject: [PATCH 12/26] Support meeting invites. --- .../CalendarHandler.swift | 74 ++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/Clocker/Events and Reminders/CalendarHandler.swift b/Clocker/Events and Reminders/CalendarHandler.swift index 846bb3e..2a01866 100644 --- a/Clocker/Events and Reminders/CalendarHandler.swift +++ b/Clocker/Events and Reminders/CalendarHandler.swift @@ -332,14 +332,83 @@ extension EventCenter { let isEndDate = autoupdatingCalendar.isDate(date, inSameDayAs: event.endDate) && (event.startDate.compare(date) == .orderedAscending) let isAllDay = event.isAllDay || (event.startDate.compare(date) == .orderedAscending && event.endDate.compare(nextDate) == .orderedSame) let isSingleDay = event.isAllDay && (event.startDate.compare(date) == .orderedSame && event.endDate.compare(nextDate) == .orderedSame) - + let meetingURL = retrieveMeetingURL(event) let eventInfo = EventInfo(event: event, isStartDate: isStartDate, isEndDate: isEndDate, isAllDay: isAllDay, - isSingleDay: isSingleDay) + isSingleDay: isSingleDay, + meetingURL: meetingURL) return eventInfo } + + static var dataDetector: NSDataDetector? = nil + + // Borrowing logic from Ityscal + @discardableResult + private func findAppropriateURLs(_ description: String) -> URL? { + guard let results = EventCenter.dataDetector?.matches(in: description, options: .reportCompletion, range: NSMakeRange(0, description.count)) else { + return nil + } + for result in results { + if result.resultType == .link, var actualLink = result.url?.absoluteString { + // Check for Zoom links + if actualLink.contains("zoom.us/j/") || actualLink.contains("zoom.us/s/") || actualLink.contains("zoom.us/w/") { + // Create a Zoom App link + let workspace = NSWorkspace.shared + if (workspace.urlForApplication(toOpen: URL(string: "zoommtg://")!) != nil) { + actualLink = actualLink.replacingOccurrences(of: "https://", with: "zoommtg://") + actualLink = actualLink.replacingOccurrences(of: "?", with: "&") + actualLink = actualLink.replacingOccurrences(of: "/j/", with: "/join?confno=") + actualLink = actualLink.replacingOccurrences(of: "/s/", with: "/join?confno=") + actualLink = actualLink.replacingOccurrences(of: "/w/", with: "/join?confno=") + if let appLink = URL(string: actualLink) { + return appLink + } + + } + } else if actualLink.contains("zoommtg://") + || actualLink.contains("meet.google.com/") + || actualLink.contains("hangouts.google.com/") + || actualLink.contains("webex.com/") + || actualLink.contains("gotomeeting.com/join") + || actualLink.contains("ringcentral.com/j") + || actualLink.contains("bigbluebutton.org/gl") + || actualLink.contains("://bigbluebutton.") + || actualLink.contains("://bbb.") + || actualLink.contains("indigo.collocall.de") + || actualLink.contains("public.senfcall.de") + || actualLink.contains("youcanbook.me/zoom/") + || actualLink.contains("workplace.com/groupcall") { + if let zoomLink = result.url { + return zoomLink + } + } + } + } + return nil + } + + private func retrieveMeetingURL(_ event: EKEvent) -> URL? { + if EventCenter.dataDetector == nil { + // TODO: Handle Try-Catch gracefully + EventCenter.dataDetector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) + } + + if let location = event.location { + return findAppropriateURLs(location) + } + + if let url = event.url { + return findAppropriateURLs(url.absoluteString) + } + + if let notes = event.notes { + return findAppropriateURLs(notes) + } + + return nil + } } struct CalendarInfo { @@ -353,4 +422,5 @@ struct EventInfo { let isEndDate: Bool let isAllDay: Bool let isSingleDay: Bool + let meetingURL: URL? } From fa71c58ff4301bdbabc75f051fc4b9e72fb1f76a Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 13:13:50 -0500 Subject: [PATCH 13/26] Return EventInfo instead of EKEvent. --- Clocker/Events and Reminders/CalendarHandler.swift | 6 +++--- Clocker/Panel/ParentPanelController.swift | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Clocker/Events and Reminders/CalendarHandler.swift b/Clocker/Events and Reminders/CalendarHandler.swift index 2a01866..267ff2b 100644 --- a/Clocker/Events and Reminders/CalendarHandler.swift +++ b/Clocker/Events and Reminders/CalendarHandler.swift @@ -150,7 +150,7 @@ extension EventCenter { return menubarText } - func nextOccuring(_: [EventInfo]) -> EKEvent? { + func nextOccuring(_: [EventInfo]) -> EventInfo? { if calendarAccessDenied() || calendarAccessNotDetermined() { return nil } @@ -162,14 +162,14 @@ extension EventCenter { }.first if let firstEvent = filteredEvent { - return firstEvent.event + return firstEvent } let filteredAllDayEvent = relevantEvents.filter { $0.isAllDay }.first - return filteredAllDayEvent?.event + return filteredAllDayEvent } func initializeStoreIfNeccesary() { diff --git a/Clocker/Panel/ParentPanelController.swift b/Clocker/Panel/ParentPanelController.swift index d6ad916..afd1fe6 100644 --- a/Clocker/Panel/ParentPanelController.swift +++ b/Clocker/Panel/ParentPanelController.swift @@ -810,9 +810,9 @@ class ParentPanelController: NSWindowController { return } - self.calendarColorView.layer?.backgroundColor = upcomingEvent.calendar.color.cgColor - self.nextEventLabel.stringValue = upcomingEvent.title - self.nextEventLabel.toolTip = upcomingEvent.title + self.calendarColorView.layer?.backgroundColor = upcomingEvent.event.calendar.color.cgColor + self.nextEventLabel.stringValue = upcomingEvent.event.title + self.nextEventLabel.toolTip = upcomingEvent.event.title if upcomingEvent.isAllDay == true { let title = events.count == 1 ? "All-Day" : "All Day - Total \(events.count) events today" self.setCalendarButtonTitle(buttonTitle: title) @@ -822,7 +822,7 @@ class ParentPanelController: NSWindowController { return } - let timeSince = Date().timeAgo(since: upcomingEvent.startDate) + let timeSince = Date().timeAgo(since: upcomingEvent.event.startDate) let withoutAn = timeSince.replacingOccurrences(of: "an", with: CLEmptyString) let withoutAgo = withoutAn.replacingOccurrences(of: "ago", with: CLEmptyString) From fa7ca265d8bb425280b710321a315632449c7cab Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 13:40:38 -0500 Subject: [PATCH 14/26] Meeting invites! --- .../CalendarHandler.swift | 43 +++++++++---------- Clocker/Overall App/Themer.swift | 13 +++++- Clocker/Panel/ParentPanelController.swift | 14 ++++++ 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Clocker/Events and Reminders/CalendarHandler.swift b/Clocker/Events and Reminders/CalendarHandler.swift index 267ff2b..40e11b1 100644 --- a/Clocker/Events and Reminders/CalendarHandler.swift +++ b/Clocker/Events and Reminders/CalendarHandler.swift @@ -201,7 +201,7 @@ extension EventCenter { for event in events { if selectedCalendars.contains(event.event.calendar.calendarIdentifier) { if filteredEvents[date] == nil { - filteredEvents[date] = [] + filteredEvents[date] = Array() } filteredEvents[date]?.append(event) @@ -341,9 +341,9 @@ extension EventCenter { meetingURL: meetingURL) return eventInfo } - - static var dataDetector: NSDataDetector? = nil - + + static var dataDetector: NSDataDetector? + // Borrowing logic from Ityscal @discardableResult private func findAppropriateURLs(_ description: String) -> URL? { @@ -356,7 +356,7 @@ extension EventCenter { if actualLink.contains("zoom.us/j/") || actualLink.contains("zoom.us/s/") || actualLink.contains("zoom.us/w/") { // Create a Zoom App link let workspace = NSWorkspace.shared - if (workspace.urlForApplication(toOpen: URL(string: "zoommtg://")!) != nil) { + if workspace.urlForApplication(toOpen: URL(string: "zoommtg://")!) != nil { actualLink = actualLink.replacingOccurrences(of: "https://", with: "zoommtg://") actualLink = actualLink.replacingOccurrences(of: "?", with: "&") actualLink = actualLink.replacingOccurrences(of: "/j/", with: "/join?confno=") @@ -365,21 +365,20 @@ extension EventCenter { if let appLink = URL(string: actualLink) { return appLink } - } } else if actualLink.contains("zoommtg://") - || actualLink.contains("meet.google.com/") - || actualLink.contains("hangouts.google.com/") - || actualLink.contains("webex.com/") - || actualLink.contains("gotomeeting.com/join") - || actualLink.contains("ringcentral.com/j") - || actualLink.contains("bigbluebutton.org/gl") - || actualLink.contains("://bigbluebutton.") - || actualLink.contains("://bbb.") - || actualLink.contains("indigo.collocall.de") - || actualLink.contains("public.senfcall.de") - || actualLink.contains("youcanbook.me/zoom/") - || actualLink.contains("workplace.com/groupcall") { + || actualLink.contains("meet.google.com/") + || actualLink.contains("hangouts.google.com/") + || actualLink.contains("webex.com/") + || actualLink.contains("gotomeeting.com/join") + || actualLink.contains("ringcentral.com/j") + || actualLink.contains("bigbluebutton.org/gl") + || actualLink.contains("://bigbluebutton.") + || actualLink.contains("://bbb.") + || actualLink.contains("indigo.collocall.de") + || actualLink.contains("public.senfcall.de") + || actualLink.contains("youcanbook.me/zoom/") + || actualLink.contains("workplace.com/groupcall") { if let zoomLink = result.url { return zoomLink } @@ -388,21 +387,21 @@ extension EventCenter { } return nil } - + private func retrieveMeetingURL(_ event: EKEvent) -> URL? { if EventCenter.dataDetector == nil { // TODO: Handle Try-Catch gracefully EventCenter.dataDetector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) } - + if let location = event.location { return findAppropriateURLs(location) } - + if let url = event.url { return findAppropriateURLs(url.absoluteString) } - + if let notes = event.notes { return findAppropriateURLs(notes) } diff --git a/Clocker/Overall App/Themer.swift b/Clocker/Overall App/Themer.swift index c0ed7ce..e31a40c 100644 --- a/Clocker/Overall App/Themer.swift +++ b/Clocker/Overall App/Themer.swift @@ -187,8 +187,8 @@ extension Themer { return themeIndex == .light - ? NSImage(named: NSImage.Name("Settings"))! - : NSImage(named: NSImage.Name("Settings-White"))! + ? NSImage(named: NSImage.Name("Settings"))! + : NSImage(named: NSImage.Name("Settings-White"))! } func pinImage() -> NSImage { @@ -442,6 +442,15 @@ extension Themer { NSColor(deviceRed: 42.0 / 255.0, green: 55.0 / 255.0, blue: 62.0 / 255.0, alpha: 1.0) } + func videoCallImage() -> NSImage? { + if #available(macOS 11.0, *) { + let symbolConfig = NSImage.SymbolConfiguration(pointSize: 20, weight: .regular) + return symbolImage(for: "video.circle.fill")?.withSymbolConfiguration(symbolConfig) + } else { + return nil + } + } + func symbolImage(for name: String) -> NSImage? { assert(name.isEmpty == false) diff --git a/Clocker/Panel/ParentPanelController.swift b/Clocker/Panel/ParentPanelController.swift index afd1fe6..6bd30aa 100644 --- a/Clocker/Panel/ParentPanelController.swift +++ b/Clocker/Panel/ParentPanelController.swift @@ -696,6 +696,16 @@ class ParentPanelController: NSWindowController { func removeUpcomingEventView() { OperationQueue.main.addOperation { + let eventCenter = EventCenter.sharedCenter() + let now = Date() + if let events = eventCenter.eventsForDate[NSCalendar.autoupdatingCurrent.startOfDay(for: now)], events.isEmpty == false { + guard let upcomingEvent = eventCenter.nextOccuring(events), let meetingLink = upcomingEvent.meetingURL else { + return + } + NSWorkspace.shared.open(meetingLink) + return + } + if self.stackView.arrangedSubviews.contains(self.upcomingEventView!), self.upcomingEventView?.isHidden == false { self.upcomingEventView?.isHidden = true UserDefaults.standard.set("NO", forKey: CLShowUpcomingEventView) @@ -827,6 +837,10 @@ class ParentPanelController: NSWindowController { let withoutAgo = withoutAn.replacingOccurrences(of: "ago", with: CLEmptyString) self.setCalendarButtonTitle(buttonTitle: "in \(withoutAgo.lowercased())") + + if upcomingEvent.meetingURL != nil { + self.whiteRemoveButton.image = Themer.shared().videoCallImage() + } if #available(OSX 10.14, *) { PerfLogger.endMarker("Fetch Calendar Events") From 1e418124fbc23b1df659a9a19a7850bf3be4d279 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 13:48:33 -0500 Subject: [PATCH 15/26] Dispatch queue for refetching events. --- Clocker/Events and Reminders/EventCenter.swift | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Clocker/Events and Reminders/EventCenter.swift b/Clocker/Events and Reminders/EventCenter.swift index f07fb6b..5bc40c7 100644 --- a/Clocker/Events and Reminders/EventCenter.swift +++ b/Clocker/Events and Reminders/EventCenter.swift @@ -16,6 +16,8 @@ class EventCenter: NSObject { var eventsForDate: [Date: [EventInfo]] = [:] var filteredEvents: [Date: [EventInfo]] = [:] + + private let fetchQueue = DispatchQueue(label: "com.abhishek.fetch") @discardableResult class func sharedCenter() -> EventCenter { return shared @@ -41,11 +43,16 @@ class EventCenter: NSObject { private func refetchAll() { Logger.info("\nRefetching events from the store") + eventsForDate = [:] filteredEvents = [:] + autoreleasepool { + fetchQueue.async { + // We get events for a 120 day period. + // If the user uses a calendar often, this will be called frequently + self.fetchEvents(-40, 80) + } + } - // We get events for a 120 day period. - // If the user uses a calendar often, this will be called frequently - fetchEvents(-40, 80) } } From bb4938e580e10fa60a00832c4b5fcdaa5559e2df Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 13:50:21 -0500 Subject: [PATCH 16/26] Fix bug! --- Clocker/Panel/ParentPanelController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Clocker/Panel/ParentPanelController.swift b/Clocker/Panel/ParentPanelController.swift index d6ad916..e09c59d 100644 --- a/Clocker/Panel/ParentPanelController.swift +++ b/Clocker/Panel/ParentPanelController.swift @@ -184,7 +184,9 @@ class ParentPanelController: NSWindowController { if DataStore.shared().timezones().isEmpty || DataStore.shared().shouldDisplay(.futureSlider) == false { futureSliderView.isHidden = true - modernContainerView.isHidden = true + if modernContainerView != nil { + modernContainerView.isHidden = true + } } else if let value = DataStore.shared().retrieve(key: CLDisplayFutureSliderKey) as? NSNumber { if value.intValue == 1 { futureSliderView.isHidden = false From d234eb2020a60d58ce0cb171cee9e7d618d908bb Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 13:53:31 -0500 Subject: [PATCH 17/26] Fix: Unable to remove upcoming event view. --- Clocker/Panel/ParentPanelController.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Clocker/Panel/ParentPanelController.swift b/Clocker/Panel/ParentPanelController.swift index 3a42410..1acd152 100644 --- a/Clocker/Panel/ParentPanelController.swift +++ b/Clocker/Panel/ParentPanelController.swift @@ -701,11 +701,9 @@ class ParentPanelController: NSWindowController { let eventCenter = EventCenter.sharedCenter() let now = Date() if let events = eventCenter.eventsForDate[NSCalendar.autoupdatingCurrent.startOfDay(for: now)], events.isEmpty == false { - guard let upcomingEvent = eventCenter.nextOccuring(events), let meetingLink = upcomingEvent.meetingURL else { - return + if let upcomingEvent = eventCenter.nextOccuring(events), let meetingLink = upcomingEvent.meetingURL { + NSWorkspace.shared.open(meetingLink) } - NSWorkspace.shared.open(meetingLink) - return } if self.stackView.arrangedSubviews.contains(self.upcomingEventView!), self.upcomingEventView?.isHidden == false { From 1ec14277c5bc2a0e158b14c571d114ee679cfcb4 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 13:53:50 -0500 Subject: [PATCH 18/26] Fix tests! --- Clocker/ClockerUITests/AboutUsTests.swift | 2 +- Clocker/ClockerUITests/FloatingWindowTests.swift | 4 ++-- Clocker/ClockerUITests/OnboardingSearchTests.swift | 2 +- Clocker/ClockerUITests/PreferencesTest.swift | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Clocker/ClockerUITests/AboutUsTests.swift b/Clocker/ClockerUITests/AboutUsTests.swift index 3a1bb5c..ae1e172 100644 --- a/Clocker/ClockerUITests/AboutUsTests.swift +++ b/Clocker/ClockerUITests/AboutUsTests.swift @@ -33,7 +33,7 @@ class AboutUsTests: XCTestCase { tapAboutTab() let appDisplayName = "CFBundleDisplayName".localizedString() - let expectedVersion = "\(appDisplayName) 21.02.03 (92)" + let expectedVersion = "\(appDisplayName) 21.07.01 (93)" guard let presentVersion = app.windows["Clocker"].staticTexts["ClockerVersion"].value as? String else { XCTFail("Present version not present") diff --git a/Clocker/ClockerUITests/FloatingWindowTests.swift b/Clocker/ClockerUITests/FloatingWindowTests.swift index 53e8b8b..74cb020 100644 --- a/Clocker/ClockerUITests/FloatingWindowTests.swift +++ b/Clocker/ClockerUITests/FloatingWindowTests.swift @@ -112,9 +112,9 @@ class FloatingWindowTests: XCTestCase { miscTab.click() if floatingSlider { - app.radioGroups["FutureSlider"].radioButtons["No"].click() + app.radioGroups["FutureSlider"].radioButtons["Hide"].click() } else { - app.radioGroups["FutureSlider"].radioButtons["Yes"].click() + app.radioGroups["FutureSlider"].radioButtons["Legacy"].click() } let newFloatingSliderExists = app.sliders["FloatingSlider"].exists diff --git a/Clocker/ClockerUITests/OnboardingSearchTests.swift b/Clocker/ClockerUITests/OnboardingSearchTests.swift index 48566ed..87f2bfd 100644 --- a/Clocker/ClockerUITests/OnboardingSearchTests.swift +++ b/Clocker/ClockerUITests/OnboardingSearchTests.swift @@ -79,7 +79,7 @@ class OnboardingSearchTests: XCTestCase { func testMispelledCityNameSearch() throws { let searchField = app.searchFields["MainSearchField"] - searchField.reset(text: "ajsdkjasdkjhasdkashkjda") + searchField.reset(text: "ajsdkjasdkjhasdkashkjdazasdasdas") searchField.typeKey(XCUIKeyboardKey.return, modifierFlags: XCUIElement.KeyModifierFlags()) sleep(2) // Wait for the query to return diff --git a/Clocker/ClockerUITests/PreferencesTest.swift b/Clocker/ClockerUITests/PreferencesTest.swift index 5c0f1f0..177d78a 100644 --- a/Clocker/ClockerUITests/PreferencesTest.swift +++ b/Clocker/ClockerUITests/PreferencesTest.swift @@ -31,7 +31,7 @@ class PreferencesTest: XCTestCase { return } - app.windows["Clocker"].tables["TimezoneTableView"].tableRows.firstMatch.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0)).click() + app.windows["Clocker"].tables["TimezoneTableView"].tableRows.firstMatch.click() XCTAssertTrue(app.checkBoxes["DeleteTimezone"].isEnabled) } @@ -334,7 +334,7 @@ class PreferencesTest: XCTestCase { if rowQueryCount > 0 { // Table Rows aren't hittable in Xcode 12.0 (10/7/20) and so we need to find a closer co-ordinate and perform click() - let currentElement = clockerWindow.tables["TimezoneTableView"].tableRows.firstMatch.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0)) + let currentElement = clockerWindow.tables["TimezoneTableView"].tableRows.firstMatch currentElement.click() for _ in 0 ..< rowQueryCount { @@ -416,7 +416,7 @@ extension XCTestCase { return } - let currentElement = app.windows["Clocker"].tableRows.firstMatch.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0)) + let currentElement = app.windows["Clocker"].tableRows.firstMatch currentElement.click() while rowQueryCount > 0 { @@ -442,7 +442,7 @@ extension XCTestCase { } private func deleteAtRow(_ rowToDelete: XCUIElement, for _: XCUIApplication, shouldSleep: Bool) { - rowToDelete.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0)).click() + rowToDelete.click() rowToDelete.typeKey(XCUIKeyboardKey.delete, modifierFlags: XCUIElement.KeyModifierFlags()) if shouldSleep { sleep(2) From 9de6e096ceeddc06f041851b3c18d4812ddf6699 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 14:11:38 -0500 Subject: [PATCH 19/26] Minor adjustments. --- Clocker/Panel/ParentPanelController.swift | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Clocker/Panel/ParentPanelController.swift b/Clocker/Panel/ParentPanelController.swift index 3a42410..ceb9c38 100644 --- a/Clocker/Panel/ParentPanelController.swift +++ b/Clocker/Panel/ParentPanelController.swift @@ -1093,9 +1093,27 @@ extension ParentPanelController: NSSharingServicePickerDelegate { } func sharingServicePicker(_: NSSharingServicePicker, sharingServicesForItems _: [Any], proposedSharingServices proposed: [NSSharingService]) -> [NSSharingService] { + let copySharingService = NSSharingService(title: "Copy All Times", image: NSImage(), alternateImage: nil) { + let timezones = DataStore.shared().timezones() + var clipboardCopy = String() + for encodedTimezone in timezones { + if let timezoneObject = TimezoneData.customObject(from: encodedTimezone) { + let operations = TimezoneDataOperations(with: timezoneObject) + clipboardCopy.append("\(timezoneObject.formattedTimezoneLabel()) - \(operations.time(with: 0))") + } + } + + let pasteboard = NSPasteboard.general + pasteboard.declareTypes([.string], owner: nil) + pasteboard.setString(clipboardCopy, forType: .string) + } let allowedServices: Set = Set(["Messages", "Notes"]) - return proposed.filter { service in + let filteredServices = proposed.filter { service in allowedServices.contains(service.title) } + + var newProposedServices: [NSSharingService] = [copySharingService] + newProposedServices.append(contentsOf: filteredServices) + return newProposedServices } } From adfa3ef19b2290db7edcbacf544f860d7fec3e75 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 14:17:08 -0500 Subject: [PATCH 20/26] Update ParentPanelController.swift --- Clocker/Panel/ParentPanelController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Clocker/Panel/ParentPanelController.swift b/Clocker/Panel/ParentPanelController.swift index 9b78d8e..2c3341b 100644 --- a/Clocker/Panel/ParentPanelController.swift +++ b/Clocker/Panel/ParentPanelController.swift @@ -1097,7 +1097,7 @@ extension ParentPanelController: NSSharingServicePickerDelegate { for encodedTimezone in timezones { if let timezoneObject = TimezoneData.customObject(from: encodedTimezone) { let operations = TimezoneDataOperations(with: timezoneObject) - clipboardCopy.append("\(timezoneObject.formattedTimezoneLabel()) - \(operations.time(with: 0))") + clipboardCopy.append("\(timezoneObject.formattedTimezoneLabel()) - \(operations.time(with: 0))\n") } } From a233037295da032569c5890df6755848828b3750 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 14:19:13 -0500 Subject: [PATCH 21/26] Update AboutViewController.swift --- Clocker/Preferences/About/AboutViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Clocker/Preferences/About/AboutViewController.swift b/Clocker/Preferences/About/AboutViewController.swift index 2df4c38..a06a6b5 100644 --- a/Clocker/Preferences/About/AboutViewController.swift +++ b/Clocker/Preferences/About/AboutViewController.swift @@ -47,7 +47,7 @@ class AboutViewController: ParentViewController { } private func underlineTextForActionButton() { - let rangesInOrder = [NSRange(location: 3, length: 8), + let rangesInOrder = [NSRange(location: 3, length: 16), NSRange(location: 7, length: privateFeedback.attributedTitle.length - 7), NSRange(location: 27, length: 33), NSRange(location: 42, length: 14)] @@ -57,7 +57,7 @@ class AboutViewController: ParentViewController { supportClocker, openSourceButton] - let localizedKeys = ["1. @n0shake on Twitter for quick comments", + let localizedKeys = ["1. @clocker_support on Twitter for quick comments", "2. For Private Feedback", "You can support Clocker by leaving a review on the App Store! :)", "Help localize Clocker in your language by clicking here!"] From 5824f4e6daaf6a7d4bd529ed8c2e2e46a6bedc21 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 14:45:49 -0500 Subject: [PATCH 22/26] Update ClockerUnitTests.swift --- Clocker/ClockerUnitTests/ClockerUnitTests.swift | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Clocker/ClockerUnitTests/ClockerUnitTests.swift b/Clocker/ClockerUnitTests/ClockerUnitTests.swift index 903932b..09e0533 100644 --- a/Clocker/ClockerUnitTests/ClockerUnitTests.swift +++ b/Clocker/ClockerUnitTests/ClockerUnitTests.swift @@ -115,8 +115,21 @@ class ClockerUnitTests: XCTestCase { XCTFail("Default preferences aren't in the correct format") return } - let oldCount = currentFavourites.count - + // Check if timezone with test identifier is present. + let filteredCount = currentFavourites.filter { + let timezone = TimezoneData.customObject(from: $0) + return timezone?.placeID == "TestIdentifier" + } + + // California is absent. Add it! + if filteredCount.count == 0 { + let timezoneData = TimezoneData(with: california) + let operationsObject = TimezoneDataOperations(with: timezoneData) + operationsObject.saveObject() + } + + let oldCount = (defaults.object(forKey: CLDefaultPreferenceKey) as? [Data])?.count ?? 0 + currentFavourites = currentFavourites.filter { let timezone = TimezoneData.customObject(from: $0) return timezone?.placeID != "TestIdentifier" From 2d21de00fb94ec76e69ba28925f09acf5a8745f0 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 15:19:56 -0500 Subject: [PATCH 23/26] Add tests for copy functionality. --- .../ClockerUITests/CopyToClipboardTests.swift | 53 +++++++++++++++++++ Clocker/Panel/UI/TimezoneCellView.swift | 1 - 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 Clocker/ClockerUITests/CopyToClipboardTests.swift diff --git a/Clocker/ClockerUITests/CopyToClipboardTests.swift b/Clocker/ClockerUITests/CopyToClipboardTests.swift new file mode 100644 index 0000000..96b5323 --- /dev/null +++ b/Clocker/ClockerUITests/CopyToClipboardTests.swift @@ -0,0 +1,53 @@ +// Copyright © 2015 Abhishek Banthia + +import XCTest + +class CopyToClipboardTests: XCTestCase { + var app: XCUIApplication! + + override func setUp() { + continueAfterFailure = false + app = XCUIApplication() + app.launch() + + if app.tables["FloatingTableView"].exists == false { + app.tapMenubarIcon() + app.buttons["FloatingPin"].click() + } + } + + override func tearDownWithError() throws { + app = nil + } + + func testFullCopy() throws { + let cellCount = app.tables["FloatingTableView"].cells.count + var clipboardValue = String() + for cellIndex in 0.. Date: Fri, 2 Jul 2021 17:26:32 -0500 Subject: [PATCH 24/26] Bringing in some color! --- Clocker/Panel/ParentPanelController.swift | 3 +++ Clocker/Panel/UI/TimezoneDataSource.swift | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Clocker/Panel/ParentPanelController.swift b/Clocker/Panel/ParentPanelController.swift index 2c3341b..35bdc56 100644 --- a/Clocker/Panel/ParentPanelController.swift +++ b/Clocker/Panel/ParentPanelController.swift @@ -614,6 +614,9 @@ class ParentPanelController: NSWindowController { cellView.relativeDate.stringValue = dataOperation.date(with: futureSliderValue, displayType: .panel) cellView.currentLocationIndicator.isHidden = !model.isSystemTimezone cellView.sunriseImage.image = model.isSunriseOrSunset ? Themer.shared().sunriseImage() : Themer.shared().sunsetImage() + if #available(macOS 10.14, *) { + cellView.sunriseImage.contentTintColor = model.isSunriseOrSunset ? NSColor.systemYellow : NSColor.systemOrange + } if let note = model.note, !note.isEmpty { cellView.noteLabel.stringValue = note } else if DataStore.shared().shouldDisplay(.dstTransitionInfo), diff --git a/Clocker/Panel/UI/TimezoneDataSource.swift b/Clocker/Panel/UI/TimezoneDataSource.swift index 9457903..ac42523 100644 --- a/Clocker/Panel/UI/TimezoneDataSource.swift +++ b/Clocker/Panel/UI/TimezoneDataSource.swift @@ -55,6 +55,9 @@ extension TimezoneDataSource: NSTableViewDataSource, NSTableViewDelegate { cellView.sunriseSetTime.stringValue = operation.formattedSunriseTime(with: sliderValue) cellView.sunriseImage.image = currentModel.isSunriseOrSunset ? Themer.shared().sunriseImage() : Themer.shared().sunsetImage() + if #available(macOS 10.14, *) { + cellView.sunriseImage.contentTintColor = currentModel.isSunriseOrSunset ? NSColor.systemYellow : NSColor.systemOrange + } cellView.relativeDate.stringValue = operation.date(with: sliderValue, displayType: .panel) cellView.rowNumber = row cellView.customName.stringValue = currentModel.formattedTimezoneLabel() From 1cf99ad6311e1ad6d15e9801ccf2c67ea8bc331e Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 21:17:48 -0500 Subject: [PATCH 25/26] Update StatusItemHandler.swift --- Clocker/Preferences/Menu Bar/StatusItemHandler.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Clocker/Preferences/Menu Bar/StatusItemHandler.swift b/Clocker/Preferences/Menu Bar/StatusItemHandler.swift index 989b173..cf95c36 100644 --- a/Clocker/Preferences/Menu Bar/StatusItemHandler.swift +++ b/Clocker/Preferences/Menu Bar/StatusItemHandler.swift @@ -17,6 +17,7 @@ class StatusItemHandler: NSObject { var statusItem: NSStatusItem = { let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) + statusItem.toolTip = "Clocker" statusItem.highlightMode = false return statusItem }() From 4cf5c0a32b78563a79b46cab5fb7458acc40d679 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Fri, 2 Jul 2021 21:18:22 -0500 Subject: [PATCH 26/26] Remove Hello Sonali. --- Clocker/Clocker/en.lproj/Panel.xib | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Clocker/Clocker/en.lproj/Panel.xib b/Clocker/Clocker/en.lproj/Panel.xib index e10406a..712f11d 100755 --- a/Clocker/Clocker/en.lproj/Panel.xib +++ b/Clocker/Clocker/en.lproj/Panel.xib @@ -260,7 +260,7 @@ - + @@ -333,7 +333,7 @@ -