Browse Source

Merge branch 'master' of https://github.com/n0shake/Clocker

pull/101/head
Abhishek Banthia 3 years ago
parent
commit
b39a0bedf2
  1. 2
      Clocker/AppDelegate.swift
  2. 40
      Clocker/Clocker.xcodeproj/project.pbxproj
  3. 3
      Clocker/Clocker.xcodeproj/xcshareddata/xcschemes/Clocker.xcscheme
  4. 2
      Clocker/Clocker/ca.lproj/Localizable.strings
  5. 2
      Clocker/Clocker/de.lproj/Localizable.strings
  6. 2
      Clocker/Clocker/en.lproj/Localizable.strings
  7. 340
      Clocker/Clocker/en.lproj/Panel.xib
  8. 2
      Clocker/Clocker/es.lproj/Localizable.strings
  9. 2
      Clocker/Clocker/fr.lproj/Localizable.strings
  10. 2
      Clocker/Clocker/hi.lproj/Localizable.strings
  11. 2
      Clocker/Clocker/hr.lproj/Localizable.strings
  12. 2
      Clocker/Clocker/it.lproj/Localizable.strings
  13. 2
      Clocker/Clocker/ja.lproj/Localizable.strings
  14. 2
      Clocker/Clocker/ko.lproj/Localizable.strings
  15. 2
      Clocker/Clocker/nl.lproj/Localizable.strings
  16. 2
      Clocker/Clocker/pt-BR.lproj/Localizable.strings
  17. 2
      Clocker/Clocker/ru.lproj/Localizable.strings
  18. 2
      Clocker/Clocker/zh-Hans.lproj/Localizable.strings
  19. 2
      Clocker/Clocker/zh-Hant.lproj/Localizable.strings
  20. 4
      Clocker/ClockerUITests/AboutUsTests.swift
  21. 2
      Clocker/ClockerUITests/CopyToClipboardTests.swift
  22. 37
      Clocker/ClockerUITests/PermissionsTests.swift
  23. 32
      Clocker/ClockerUnitTests/StandardMenubarHandlerTests.swift
  24. 8
      Clocker/CoreModelKit/Sources/CoreModelKit/TimezoneData.swift
  25. 6
      Clocker/Dependencies/Date Additions/TimePeriodChain.swift
  26. 88
      Clocker/Events and Reminders/CalendarHandler.swift
  27. 3
      Clocker/Events and Reminders/RemindersHandler.swift
  28. 6
      Clocker/Menu Bar/StatusContainerView.swift
  29. 2
      Clocker/Menu Bar/StatusItemHandler.swift
  30. 12
      Clocker/Onboarding/OnboardingSearchController.swift
  31. 19
      Clocker/Overall App/ConfigExport.swift
  32. 2
      Clocker/Overall App/NetworkManager.swift
  33. 28
      Clocker/Overall App/Themer.swift
  34. 2
      Clocker/Panel/Data Layer/TimezoneDataOperations.swift
  35. 1
      Clocker/Panel/FloatingWindowController.swift
  36. 8
      Clocker/Panel/Notes Popover/NotesPopover.swift
  37. 24
      Clocker/Panel/PanelController.swift
  38. 43
      Clocker/Panel/ParentPanelController+ModernSlider.swift
  39. 206
      Clocker/Panel/ParentPanelController.swift
  40. 4
      Clocker/Panel/Rate Controller/ReviewController.swift
  41. 4
      Clocker/Panel/Rate Controller/UpcomingEventView.swift
  42. 145
      Clocker/Panel/UI/FloatingWindow.xib
  43. 6
      Clocker/Panel/UI/TimezoneCellView.swift
  44. 2
      Clocker/Panel/UI/TimezoneDataSource.swift
  45. 38
      Clocker/Panel/Upcoming Events/ParentPanelController+UpcomingEvents.swift
  46. 131
      Clocker/Panel/Upcoming Events/UpcomingEventViewItem.swift
  47. 99
      Clocker/Panel/Upcoming Events/UpcomingEventViewItem.xib
  48. 64
      Clocker/Panel/Upcoming Events/UpcomingEventsDataSource.swift
  49. 6
      Clocker/Preferences/About/AboutViewController.swift
  50. 31
      Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift
  51. 18
      Clocker/Preferences/Appearance/AppearanceViewController.swift
  52. 8
      Clocker/Preferences/Calendar/CalendarViewController.swift
  53. 7
      Clocker/Preferences/General/PreferencesDataSource.swift
  54. 21
      Clocker/Preferences/General/PreferencesViewController.swift
  55. 2
      Clocker/Preferences/General/SearchDataSource.swift
  56. 6
      Clocker/Preferences/Menu Bar/StatusContainerView.swift
  57. 2
      Clocker/Preferences/Menu Bar/StatusItemHandler.swift
  58. 15
      Clocker/Preferences/Menu Bar/UpcomingEventStatusItemView.swift
  59. 4
      Clocker/Preferences/OneWindowController.swift
  60. 12
      Clocker/Preferences/Preferences.storyboard

2
Clocker/AppDelegate.swift

@ -16,7 +16,7 @@ open class AppDelegate: NSObject, NSApplicationDelegate {
panelObserver?.invalidate()
}
override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) {
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) {
if let path = keyPath, path == PreferencesConstants.hotKeyPathIdentifier {
let hotKeyCenter = PTHotKeyCenter.shared()

40
Clocker/Clocker.xcodeproj/project.pbxproj

@ -46,6 +46,10 @@
3531F82226938D7700DF0111 /* GoogleDataTransport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3531F80526938D7700DF0111 /* GoogleDataTransport.framework */; };
3531F82326938D7700DF0111 /* GoogleDataTransport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3531F80526938D7700DF0111 /* GoogleDataTransport.framework */; };
353B5BC52698B78A0023858D /* UpcomingEventStatusItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 353B5BC42698B78A0023858D /* UpcomingEventStatusItemView.swift */; };
3548C45A26BECF1B00AFB533 /* UpcomingEventViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3548C45926BECF1B00AFB533 /* UpcomingEventViewItem.xib */; };
3548C45D26BEEF4C00AFB533 /* ParentPanelController+UpcomingEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3548C45C26BEEF4C00AFB533 /* ParentPanelController+UpcomingEvents.swift */; };
3548C45F26BEEFB400AFB533 /* UpcomingEventsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3548C45E26BEEFB400AFB533 /* UpcomingEventsDataSource.swift */; };
3548C46126BEEFE400AFB533 /* UpcomingEventViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3548C46026BEEFE400AFB533 /* UpcomingEventViewItem.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 */; };
@ -119,6 +123,7 @@
35C36FA22259ED6D002FA5C6 /* RemindersHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C36F9F2259ED6D002FA5C6 /* RemindersHandler.swift */; };
35C36FA42259EEC2002FA5C6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C36FA32259EEC2002FA5C6 /* AppDelegate.swift */; };
35DFBCEF26A8468900D6648B /* ConfigExport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35DFBCEE26A8468900D6648B /* ConfigExport.swift */; };
35DFBCF126A8479000D6648B /* Keys.plist in Resources */ = {isa = PBXBuildFile; fileRef = 35DFBCF026A8479000D6648B /* Keys.plist */; };
35E65125268EDD2E00E3E1E3 /* Toasty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35E65124268EDD2E00E3E1E3 /* Toasty.swift */; };
9A0385BB269E3434003B5E72 /* StandardMenubarHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0385BA269E3434003B5E72 /* StandardMenubarHandlerTests.swift */; };
9A0385C0269E8891003B5E72 /* PermissionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0385BF269E8891003B5E72 /* PermissionsTests.swift */; };
@ -272,6 +277,10 @@
3531F80526938D7700DF0111 /* GoogleDataTransport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleDataTransport.framework; path = Frameworks/Firebase/GoogleDataTransport.framework; sourceTree = "<group>"; };
353B5BC42698B78A0023858D /* UpcomingEventStatusItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpcomingEventStatusItemView.swift; sourceTree = "<group>"; };
353B5BC72698D4BB0023858D /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Localizable.strings; sourceTree = "<group>"; };
3548C45926BECF1B00AFB533 /* UpcomingEventViewItem.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UpcomingEventViewItem.xib; sourceTree = "<group>"; };
3548C45C26BEEF4C00AFB533 /* ParentPanelController+UpcomingEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParentPanelController+UpcomingEvents.swift"; sourceTree = "<group>"; };
3548C45E26BEEFB400AFB533 /* UpcomingEventsDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpcomingEventsDataSource.swift; sourceTree = "<group>"; };
3548C46026BEEFE400AFB533 /* UpcomingEventViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpcomingEventViewItem.swift; sourceTree = "<group>"; };
3569A44E25441F320087E254 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
357391852507277500D30819 /* TimeMarkerViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeMarkerViewItem.swift; sourceTree = "<group>"; };
357391862507277500D30819 /* HourMarkerViewItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HourMarkerViewItem.xib; sourceTree = "<group>"; };
@ -348,6 +357,7 @@
35C36F9F2259ED6D002FA5C6 /* RemindersHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemindersHandler.swift; sourceTree = "<group>"; };
35C36FA32259EEC2002FA5C6 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = SOURCE_ROOT; };
35DFBCEE26A8468900D6648B /* ConfigExport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigExport.swift; sourceTree = "<group>"; };
35DFBCF026A8479000D6648B /* Keys.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Keys.plist; path = ../Internal/Keys.plist; sourceTree = "<group>"; };
35E65124268EDD2E00E3E1E3 /* Toasty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toasty.swift; sourceTree = "<group>"; };
9A0385BA269E3434003B5E72 /* StandardMenubarHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardMenubarHandlerTests.swift; sourceTree = "<group>"; };
9A0385BF269E8891003B5E72 /* PermissionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionsTests.swift; sourceTree = "<group>"; };
@ -534,6 +544,17 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
3548C45B26BEEF2100AFB533 /* Upcoming Events */ = {
isa = PBXGroup;
children = (
3548C45C26BEEF4C00AFB533 /* ParentPanelController+UpcomingEvents.swift */,
3548C45E26BEEFB400AFB533 /* UpcomingEventsDataSource.swift */,
3548C46026BEEFE400AFB533 /* UpcomingEventViewItem.swift */,
3548C45926BECF1B00AFB533 /* UpcomingEventViewItem.xib */,
);
path = "Upcoming Events";
sourceTree = "<group>";
};
35C36EB522595834002FA5C6 /* Dependencies */ = {
isa = PBXGroup;
children = (
@ -599,6 +620,7 @@
35C36F242259D64D002FA5C6 /* Panel */ = {
isa = PBXGroup;
children = (
3548C45B26BEEF2100AFB533 /* Upcoming Events */,
35C36F632259DE9D002FA5C6 /* Rate Controller */,
35C36F522259DC9B002FA5C6 /* UI */,
35C36F512259DC85002FA5C6 /* Data Layer */,
@ -889,6 +911,7 @@
DD4F7BF913C30F9F00825C6E = {
isa = PBXGroup;
children = (
35DFBCF026A8479000D6648B /* Keys.plist */,
35B2FEE4259A2C25005DA84D /* CoreModelKit */,
35B2FED4259A2244005DA84D /* CoreLoggerKit */,
35B2FEB1259A1649005DA84D /* StartupKit */,
@ -1200,6 +1223,8 @@
3531F7C226936C6E00DF0111 /* GoogleService-Info.plist in Resources */,
35C36F17225961DA002FA5C6 /* DateTools.bundle in Resources */,
35C36EF322595F14002FA5C6 /* WelcomeView.xib in Resources */,
3548C45A26BECF1B00AFB533 /* UpcomingEventViewItem.xib in Resources */,
35DFBCF126A8479000D6648B /* Keys.plist in Resources */,
35C36EF822595F14002FA5C6 /* Onboarding.storyboard in Resources */,
35C36F612259DE67002FA5C6 /* NotesPopover.xib in Resources */,
9A3169C11D2CC5AA0079FDF8 /* com.abhishek.ClockerHelper.plist in Resources */,
@ -1278,6 +1303,7 @@
35C36F672259DF4C002FA5C6 /* ReviewController.swift in Sources */,
35C36F472259D892002FA5C6 /* Reach.swift in Sources */,
35C36EF222595F14002FA5C6 /* OnboardingWelcomeViewController.swift in Sources */,
3548C45F26BEEFB400AFB533 /* UpcomingEventsDataSource.swift in Sources */,
35C36F732259E1AA002FA5C6 /* FloatingWindowController.swift in Sources */,
35C36F6F2259E185002FA5C6 /* CustomSliderCell.swift in Sources */,
35C36EF122595F14002FA5C6 /* OnboardingPermissionsViewController.swift in Sources */,
@ -1292,6 +1318,7 @@
35C36F16225961DA002FA5C6 /* Date+Inits.swift in Sources */,
35C36F4F2259D981002FA5C6 /* AppDefaults.swift in Sources */,
35C36F5D2259DD96002FA5C6 /* TimezoneDataOperations.swift in Sources */,
3548C45D26BEEF4C00AFB533 /* ParentPanelController+UpcomingEvents.swift in Sources */,
3508CC942599FFEC000E3530 /* MenubarHandler.swift in Sources */,
35C36F14225961DA002FA5C6 /* Integer+DateTools.swift in Sources */,
35C36FA22259ED6D002FA5C6 /* RemindersHandler.swift in Sources */,
@ -1335,6 +1362,7 @@
35C36F2022596253002FA5C6 /* OneWindowController.swift in Sources */,
35C36F0E225961DA002FA5C6 /* Date+Bundle.swift in Sources */,
9AB6F1672259D23200A44663 /* PermissionsViewController.swift in Sources */,
3548C46126BEEFE400AFB533 /* UpcomingEventViewItem.swift in Sources */,
9AB6F1642259D1B900A44663 /* ParentViewController.swift in Sources */,
35C36F1C225961DA002FA5C6 /* TimePeriodChain.swift in Sources */,
3508CCAA259A0027000E3530 /* StatusContainerView.swift in Sources */,
@ -1542,7 +1570,7 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 97;
CURRENT_PROJECT_VERSION = 98;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
@ -1575,7 +1603,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 21.07.03;
MARKETING_VERSION = 21.08.01;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = com.abhishek.Clocker;
@ -2111,7 +2139,7 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 97;
CURRENT_PROJECT_VERSION = 98;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
@ -2145,7 +2173,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 21.07.03;
MARKETING_VERSION = 21.08.01;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "-ObjC";
"OTHER_SWIFT_FLAGS[arch=*]" = "-D DEBUG";
@ -2193,7 +2221,7 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 97;
CURRENT_PROJECT_VERSION = 98;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
@ -2226,7 +2254,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 21.07.03;
MARKETING_VERSION = 21.08.01;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "-ObjC";
"OTHER_SWIFT_FLAGS[arch=*]" = "-D RELEASE";

3
Clocker/Clocker.xcodeproj/xcshareddata/xcschemes/Clocker.xcscheme

@ -78,6 +78,9 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableAddressSanitizer = "YES"
enableASanStackUseAfterReturn = "YES"
enableUBSanitizer = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"

2
Clocker/Clocker/ca.lproj/Localizable.strings

@ -161,3 +161,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/de.lproj/Localizable.strings

@ -161,3 +161,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/en.lproj/Localizable.strings

@ -163,3 +163,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

340
Clocker/Clocker/en.lproj/Panel.xib

@ -1,16 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17701"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="PanelController" customModule="Clocker" customModuleProvider="target">
<connections>
<outlet property="backgroundView" destination="6" id="20"/>
<outlet property="calendarButton" destination="OmF-0e-AfS" id="tok-uM-llD"/>
<outlet property="calendarColorView" destination="KFy-Xe-qhq" id="Qta-Vq-YyP"/>
<outlet property="futureSlider" destination="Vf2-uI-yf3" id="waZ-HW-18U"/>
<outlet property="futureSliderView" destination="C7R-Dq-MFr" id="IsR-aV-vjD"/>
<outlet property="goBackwardsButton" destination="aW8-hr-4Zh" id="EWT-EA-AGP"/>
@ -21,9 +20,9 @@
<outlet property="modernContainerView" destination="8W7-rS-Uob" id="qgZ-SS-ayy"/>
<outlet property="modernSlider" destination="lxA-64-3QU" id="kS5-ub-7gV"/>
<outlet property="modernSliderLabel" destination="e2d-EI-Dm0" id="eby-rL-B21"/>
<outlet property="nextEventLabel" destination="rld-Ag-KL1" id="GTY-j3-A1g"/>
<outlet property="pinButton" destination="YXE-4J-5cn" id="k6V-HK-7XG"/>
<outlet property="preferencesButton" destination="Ctq-BV-GPN" id="cdL-5h-qmx"/>
<outlet property="resetModernSliderButton" destination="TK5-db-7bd" id="kFe-rX-u1L"/>
<outlet property="reviewView" destination="TDn-Kk-KkV" id="YdI-Y4-zrg"/>
<outlet property="rightButton" destination="w3d-CG-aBO" id="rcF-Gw-CtM"/>
<outlet property="roundedDateView" destination="I2Z-NZ-795" id="Oa9-q9-gZx"/>
@ -32,8 +31,8 @@
<outlet property="shutdownButton" destination="1cR-pI-osG" id="ZFz-9l-yNi"/>
<outlet property="sliderDatePicker" destination="zN8-JF-j7O" id="c06-EE-0kh"/>
<outlet property="stackView" destination="OZA-6o-SbE" id="lIT-4b-8WZ"/>
<outlet property="upcomingEventView" destination="Akn-cR-W4G" id="SKK-Ai-Vas"/>
<outlet property="whiteRemoveButton" destination="R8q-7U-hbU" id="a9E-WB-pML"/>
<outlet property="upcomingEventCollectionView" destination="Tp2-pl-yIm" id="gLE-03-1cZ"/>
<outlet property="upcomingEventContainerView" destination="Akn-cR-W4G" id="1cy-z6-392"/>
<outlet property="window" destination="5" id="7"/>
</connections>
</customObject>
@ -44,22 +43,22 @@
<windowCollectionBehavior key="collectionBehavior" moveToActiveSpace="YES" ignoresCycle="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="1000" y="379" width="350" height="460"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<view key="contentView" focusRingType="none" id="6" customClass="BackgroundPanelView" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="350" height="440"/>
<rect key="frame" x="0.0" y="0.0" width="370" height="450"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView focusRingType="none" distribution="fill" orientation="vertical" alignment="leading" spacing="0.0" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OZA-6o-SbE">
<rect key="frame" x="0.0" y="0.0" width="350" height="440"/>
<rect key="frame" x="0.0" y="0.0" width="370" height="450"/>
<subviews>
<scrollView focusRingType="none" borderType="none" autohidesScrollers="YES" horizontalLineScroll="96" horizontalPageScroll="1" verticalLineScroll="96" verticalPageScroll="1" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" scrollerKnobStyle="light" translatesAutoresizingMaskIntoConstraints="NO" id="4Gd-Nv-fXr">
<rect key="frame" x="-29" y="175" width="379" height="265"/>
<rect key="frame" x="0.0" y="185" width="370" height="265"/>
<clipView key="contentView" focusRingType="none" drawsBackground="NO" copiesOnScroll="NO" id="4MZ-Di-yNR">
<rect key="frame" x="0.0" y="0.0" width="379" height="265"/>
<rect key="frame" x="0.0" y="0.0" width="370" height="265"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="95" viewBased="YES" id="dFw-ts-8OZ" customClass="PanelTableView" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="379" height="265"/>
<rect key="frame" x="0.0" y="0.0" width="370" height="265"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="1"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -78,11 +77,11 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="timeZoneCell" id="qbN-ba-fho" customClass="TimezoneCellView" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="1" y="0.0" width="338" height="111"/>
<rect key="frame" x="11" y="0.0" width="347" height="111"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" verticalCompressionResistancePriority="749" tag="102" preferredMaxLayoutWidth="72" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QUd-7D-q14">
<rect key="frame" x="28" y="50" width="180" height="22"/>
<rect key="frame" x="28" y="50" width="189" height="22"/>
<constraints>
<constraint firstAttribute="height" constant="22" identifier="relative-day-height" id="JkG-8f-zhJ"/>
</constraints>
@ -93,7 +92,7 @@
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" tag="101" preferredMaxLayoutWidth="110" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vnv-J2-7r1">
<rect key="frame" x="206" y="67" width="114" height="35"/>
<rect key="frame" x="215" y="67" width="114" height="35"/>
<constraints>
<constraint firstAttribute="height" constant="35" identifier="height" id="Bwf-I6-2Pc"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="110" id="cQV-gJ-zMz"/>
@ -105,7 +104,7 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" preferredMaxLayoutWidth="50" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uOg-ij-alO">
<rect key="frame" x="236" y="49" width="84" height="20"/>
<rect key="frame" x="245" y="49" width="84" height="20"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="89H-r0-Dnh"/>
<constraint firstAttribute="width" constant="80" identifier="width" id="8KH-Ks-d4T"/>
@ -117,7 +116,7 @@
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="6At-J8-gzZ">
<rect key="frame" x="28" y="0.0" width="310" height="22"/>
<rect key="frame" x="28" y="0.0" width="319" height="22"/>
<constraints>
<constraint firstAttribute="height" constant="22" id="eYP-TP-f1a"/>
</constraints>
@ -128,7 +127,7 @@
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="sML-fJ-nbv">
<rect key="frame" x="216" y="49" width="20" height="20"/>
<rect key="frame" x="225" y="49" width="20" height="20"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="2NE-mf-cN5"/>
<constraint firstAttribute="width" constant="20" id="EXa-Tf-sR6"/>
@ -136,7 +135,7 @@
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageAlignment="right" imageScaling="proportionallyDown" id="cfP-C4-ENJ"/>
</imageView>
<textField verticalHuggingPriority="751" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="751" tag="100" preferredMaxLayoutWidth="150" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="etF-33-bCB">
<rect key="frame" x="28" y="79" width="172" height="20"/>
<rect key="frame" x="28" y="79" width="181" height="20"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" placeholderString="Timezone Name" id="rnh-AP-ooc">
<font key="font" size="15" name="Avenir-Medium"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -214,11 +213,11 @@
</connections>
</tableCellView>
<tableCellView identifier="addCell" id="rb3-TW-xSb" customClass="AddTableViewCell" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="1" y="112" width="338" height="100"/>
<rect key="frame" x="11" y="112" width="347" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="AzA-5G-LR7">
<rect key="frame" x="129" y="10" width="80" height="80"/>
<rect key="frame" x="134" y="10" width="80" height="80"/>
<constraints>
<constraint firstAttribute="height" constant="80" id="bRv-Of-5l3"/>
<constraint firstAttribute="width" constant="80" id="cRC-Fx-WLR"/>
@ -263,10 +262,10 @@
</scroller>
</scrollView>
<customView wantsLayer="YES" focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="C7R-Dq-MFr" userLabel="Slider View">
<rect key="frame" x="0.0" y="145" width="350" height="30"/>
<rect key="frame" x="0.0" y="245" width="350" height="30"/>
<subviews>
<slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Vf2-uI-yf3">
<rect key="frame" x="7" y="6" width="201" height="19"/>
<rect key="frame" x="7" y="2" width="201" height="23"/>
<constraints>
<constraint firstAttribute="height" constant="15" id="cS7-i2-mHU"/>
</constraints>
@ -312,7 +311,7 @@
</constraints>
</customView>
<customView wantsLayer="YES" focusRingType="none" id="8W7-rS-Uob" userLabel="Modern Slider" customClass="ModernSliderContainerView" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="0.0" y="165" width="350" height="70"/>
<rect key="frame" x="0.0" y="175" width="350" height="70"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<scrollView wantsLayer="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasVerticalScroller="NO" horizontalScrollElasticity="allowed" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="htc-pO-AqH">
@ -327,7 +326,7 @@
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" id="mgM-vQ-fB7">
<size key="itemSize" width="10" height="50"/>
</collectionViewFlowLayout>
<color key="primaryBackgroundColor" red="0.071000002324581146" green="0.9580000638961792" blue="0.10900000482797623" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<color key="primaryBackgroundColor" red="0.070588235294117646" green="0.95686274509803915" blue="0.10980392156862745" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="dataSource" destination="-2" id="Exd-5i-4cB"/>
<outlet property="delegate" destination="-2" id="r49-5r-Rx5"/>
@ -366,6 +365,20 @@
<constraint firstAttribute="trailing" secondItem="e2d-EI-Dm0" secondAttribute="trailing" id="vFB-mV-hbB"/>
</constraints>
</customView>
<button wantsLayer="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TK5-db-7bd">
<rect key="frame" x="215" y="15" width="15" height="15"/>
<constraints>
<constraint firstAttribute="width" constant="15" id="JkE-Dc-CMn"/>
<constraint firstAttribute="height" constant="15" id="U52-GS-JMp"/>
</constraints>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="PowerIcon-White" imagePosition="only" alignment="center" imageScaling="proportionallyUpOrDown" inset="2" id="ba3-eN-C02">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="resetModernSlider:" target="-2" id="Nvd-lK-hK3"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aW8-hr-4Zh">
<rect key="frame" x="75" y="0.0" width="45" height="20"/>
<constraints>
@ -375,6 +388,7 @@
<buttonCell key="cell" type="bevel" title="&lt;&lt;" bezelStyle="rounded" alignment="center" imageScaling="proportionallyDown" inset="2" id="cLW-iV-S4k">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent"></string>
</buttonCell>
<connections>
<action selector="goBackward:" target="-2" id="oE4-v9-7LC"/>
@ -389,6 +403,7 @@
<buttonCell key="cell" type="bevel" title="&gt;&gt;" bezelStyle="rounded" alignment="center" imageScaling="proportionallyDown" inset="2" id="hsm-i0-5Ct">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent"></string>
</buttonCell>
<connections>
<action selector="goForward:" target="-2" id="ax0-sN-nD8"/>
@ -407,171 +422,147 @@
<constraint firstAttribute="bottom" secondItem="hma-wI-Sc9" secondAttribute="bottom" id="TDr-Pa-iYA"/>
<constraint firstAttribute="bottom" secondItem="htc-pO-AqH" secondAttribute="bottom" priority="750" constant="30" id="YCJ-zB-dvc"/>
<constraint firstAttribute="trailing" secondItem="htc-pO-AqH" secondAttribute="trailing" id="esR-My-uoD"/>
<constraint firstItem="hma-wI-Sc9" firstAttribute="top" secondItem="TK5-db-7bd" secondAttribute="bottom" constant="-5" id="pIb-qt-jKw"/>
<constraint firstItem="TK5-db-7bd" firstAttribute="trailing" secondItem="hma-wI-Sc9" secondAttribute="trailing" constant="5" id="yhv-uP-Oih"/>
</constraints>
<accessibility identifier="Modern Slider Container"/>
</customView>
<customView identifier="Review Cell" focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="TDn-Kk-KkV" userLabel="Review View">
<rect key="frame" x="0.0" y="115" width="350" height="50"/>
<rect key="frame" x="0.0" y="115" width="370" height="60"/>
<subviews>
<stackView focusRingType="none" distribution="fill" orientation="horizontal" alignment="centerY" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uys-ww-duA">
<rect key="frame" x="25" y="25" width="300" height="20"/>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="56t-G6-K0e" customClass="ClockerTextBackgroundView" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="10" y="5" width="348" height="50"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsExpansionToolTips="YES" preferredMaxLayoutWidth="185" translatesAutoresizingMaskIntoConstraints="NO" id="8Qj-Y9-Okf">
<rect key="frame" x="-2" y="0.0" width="304" height="20"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="300" id="a0R-zl-Wu7"/>
<constraint firstAttribute="height" constant="20" id="uOe-5v-8AR"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="Enjoy using Clocker?" id="mjq-qB-etG">
<font key="font" size="12" name="Avenir-Book"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView focusRingType="none" distribution="fill" orientation="horizontal" alignment="centerY" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uys-ww-duA">
<rect key="frame" x="24" y="25" width="300" height="20"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsExpansionToolTips="YES" preferredMaxLayoutWidth="185" translatesAutoresizingMaskIntoConstraints="NO" id="8Qj-Y9-Okf">
<rect key="frame" x="-2" y="0.0" width="304" height="20"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="300" id="a0R-zl-Wu7"/>
<constraint firstAttribute="height" constant="20" id="uOe-5v-8AR"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="Enjoy using Clocker?" id="mjq-qB-etG">
<font key="font" size="12" name="Avenir-Book"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
</customSpacing>
<accessibility identifier="ReviewLabel"/>
</stackView>
<stackView focusRingType="none" distribution="fillEqually" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1AK-MW-qsc">
<rect key="frame" x="74" y="2" width="200" height="21"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WVY-D9-AAX">
<rect key="frame" x="-7" y="-7" width="110" height="33"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="4zI-xY-iLf"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="96" id="weK-At-NNy"/>
</constraints>
<buttonCell key="cell" type="push" title="Not Really" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="OAa-sj-koh">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="actionOnNegativeFeedback:" target="-2" id="5FU-oG-fas"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="w3d-CG-aBO">
<rect key="frame" x="97" y="-6" width="110" height="32"/>
<buttonCell key="cell" type="push" title="Yes!" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="VE1-Cu-JUr">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="actionOnPositiveFeedback:" target="-2" id="382-CW-Y6O"/>
</connections>
</button>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
</customSpacing>
<accessibility identifier="ReviewLabel"/>
</stackView>
<stackView focusRingType="none" distribution="fillEqually" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1AK-MW-qsc">
<rect key="frame" x="75" y="2" width="200" height="21"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WVY-D9-AAX">
<rect key="frame" x="-6" y="-7" width="108" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="4zI-xY-iLf"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="96" id="weK-At-NNy"/>
</constraints>
<buttonCell key="cell" type="push" title="Not Really" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="OAa-sj-koh">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Avenir-Light"/>
</buttonCell>
<connections>
<action selector="actionOnNegativeFeedback:" target="-2" id="5FU-oG-fas"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="w3d-CG-aBO">
<rect key="frame" x="98" y="-7" width="108" height="32"/>
<buttonCell key="cell" type="push" title="Yes!" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="VE1-Cu-JUr">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Avenir-Light"/>
</buttonCell>
<connections>
<action selector="actionOnPositiveFeedback:" target="-2" id="382-CW-Y6O"/>
</connections>
</button>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<constraints>
<constraint firstItem="uys-ww-duA" firstAttribute="top" secondItem="56t-G6-K0e" secondAttribute="top" constant="5" id="HCN-eh-0eI"/>
<constraint firstAttribute="bottom" secondItem="1AK-MW-qsc" secondAttribute="bottom" constant="2" id="U6I-ls-PBN"/>
<constraint firstItem="uys-ww-duA" firstAttribute="centerX" secondItem="56t-G6-K0e" secondAttribute="centerX" id="g9H-lz-4tz"/>
<constraint firstItem="1AK-MW-qsc" firstAttribute="centerX" secondItem="56t-G6-K0e" secondAttribute="centerX" id="hHF-Qb-8M1"/>
</constraints>
</customView>
</subviews>
<constraints>
<constraint firstItem="uys-ww-duA" firstAttribute="centerX" secondItem="TDn-Kk-KkV" secondAttribute="centerX" id="7WZ-eh-0jh"/>
<constraint firstItem="1AK-MW-qsc" firstAttribute="centerX" secondItem="TDn-Kk-KkV" secondAttribute="centerX" id="EVY-MI-gle"/>
<constraint firstItem="uys-ww-duA" firstAttribute="top" secondItem="TDn-Kk-KkV" secondAttribute="top" constant="5" id="I4w-qJ-pEs"/>
<constraint firstAttribute="bottom" secondItem="1AK-MW-qsc" secondAttribute="bottom" constant="2" id="bqA-no-OXh"/>
<constraint firstAttribute="height" constant="50" id="gsB-Rw-4Wo"/>
<constraint firstItem="56t-G6-K0e" firstAttribute="leading" secondItem="TDn-Kk-KkV" secondAttribute="leading" constant="10" id="3Hy-PW-q1p"/>
<constraint firstAttribute="trailing" secondItem="56t-G6-K0e" secondAttribute="trailing" constant="12" id="5Gx-5Q-eSd"/>
<constraint firstAttribute="bottom" secondItem="56t-G6-K0e" secondAttribute="bottom" constant="5" id="QtU-aQ-iZX"/>
<constraint firstItem="56t-G6-K0e" firstAttribute="top" secondItem="TDn-Kk-KkV" secondAttribute="top" constant="5" id="ZsZ-3v-bpP"/>
<constraint firstAttribute="height" constant="60" id="gsB-Rw-4Wo"/>
</constraints>
<accessibility description="ReviewCell" identifier="ReviewCell"/>
</customView>
<customView wantsLayer="YES" focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="Akn-cR-W4G" userLabel="Upcoming Event View" customClass="UpcomingEventView" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="0.0" y="40" width="350" height="75"/>
<rect key="frame" x="0.0" y="40" width="370" height="75"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Etf-aa-udM" customClass="ClockerTextBackgroundView" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="10" y="10" width="330" height="55"/>
<subviews>
<button focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OmF-0e-AfS">
<rect key="frame" x="23" y="11" width="267" height="18"/>
<constraints>
<constraint firstAttribute="height" constant="18" id="ouD-k5-ZNV"/>
</constraints>
<buttonCell key="cell" type="bevel" title="All Day Event" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingHead" truncatesLastVisibleLine="YES" state="on" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="BRO-ih-6GZ">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="12" name="Avenir-Heavy"/>
</buttonCell>
<accessibility identifier="CalendarAccessory"/>
<connections>
<action selector="calendarButtonAction:" target="-2" id="qH2-O4-fpb"/>
</connections>
</button>
<customView wantsLayer="YES" focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="KFy-Xe-qhq">
<rect key="frame" x="10" y="11" width="3" height="34"/>
<constraints>
<constraint firstAttribute="width" constant="3" id="iW8-dm-8UQ"/>
<constraint firstAttribute="height" constant="34" id="jE3-Wf-6GX"/>
</constraints>
</customView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rld-Ag-KL1">
<rect key="frame" x="21" y="26" width="271" height="24"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="aer-Hh-Lga"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="left" title="Nandita Jaiswal's 35th birthday" placeholderString="" id="gWF-HH-zj0">
<font key="font" size="14" name="Avenir-Light"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" red="1" green="0.5" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" tag="55" translatesAutoresizingMaskIntoConstraints="NO" id="R8q-7U-hbU">
<rect key="frame" x="290" y="10" width="35" height="35"/>
<constraints>
<constraint firstAttribute="width" constant="35" id="HPk-IL-BH6"/>
<constraint firstAttribute="height" constant="35" id="cKO-Aj-Fq6"/>
</constraints>
<buttonCell key="cell" type="bevel" bezelStyle="rounded" image="Remove" imagePosition="only" alignment="center" imageScaling="proportionallyDown" inset="2" id="Qhe-aW-bBW">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="dismissNextEventLabel:" target="-2" id="c3V-HV-7OP"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="OmF-0e-AfS" firstAttribute="leading" secondItem="rld-Ag-KL1" secondAttribute="leading" id="78b-Ic-Ont"/>
<constraint firstItem="rld-Ag-KL1" firstAttribute="leading" secondItem="KFy-Xe-qhq" secondAttribute="trailing" constant="10" id="Dbc-Ua-6LK"/>
<constraint firstItem="OmF-0e-AfS" firstAttribute="bottom" secondItem="KFy-Xe-qhq" secondAttribute="bottom" id="EaO-VK-aSp"/>
<constraint firstItem="rld-Ag-KL1" firstAttribute="centerY" secondItem="Etf-aa-udM" secondAttribute="centerY" constant="-10" id="ZL3-an-mCL"/>
<constraint firstItem="R8q-7U-hbU" firstAttribute="centerY" secondItem="Etf-aa-udM" secondAttribute="centerY" id="cZc-vc-rKd"/>
<constraint firstItem="R8q-7U-hbU" firstAttribute="leading" secondItem="rld-Ag-KL1" secondAttribute="trailing" id="d5O-iy-zGi"/>
<constraint firstItem="KFy-Xe-qhq" firstAttribute="centerY" secondItem="Etf-aa-udM" secondAttribute="centerY" id="gjc-cr-6nS"/>
<constraint firstItem="KFy-Xe-qhq" firstAttribute="leading" secondItem="Etf-aa-udM" secondAttribute="leading" constant="10" id="hZu-c0-P4T"/>
<constraint firstItem="R8q-7U-hbU" firstAttribute="leading" secondItem="OmF-0e-AfS" secondAttribute="trailing" id="n3q-Am-kYG"/>
<constraint firstAttribute="trailing" secondItem="R8q-7U-hbU" secondAttribute="trailing" constant="5" id="ysB-dm-dbJ"/>
</constraints>
</customView>
<scrollView wantsLayer="YES" focusRingType="none" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5BT-DM-xru">
<rect key="frame" x="0.0" y="0.0" width="370" height="75"/>
<clipView key="contentView" drawsBackground="NO" id="QHw-sR-BYe">
<rect key="frame" x="0.0" y="0.0" width="370" height="75"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView id="Tp2-pl-yIm">
<rect key="frame" x="0.0" y="0.0" width="370" height="75"/>
<autoresizingMask key="autoresizingMask" heightSizable="YES"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumInteritemSpacing="10" minimumLineSpacing="10" scrollDirection="horizontal" id="g4p-GT-v9Y">
<size key="itemSize" width="50" height="50"/>
<edgeInsets key="sectionInset" left="10" right="5" top="0.0" bottom="0.0"/>
</collectionViewFlowLayout>
<color key="primaryBackgroundColor" red="0.070588235294117646" green="0.95686274509803915" blue="0.10980392156862745" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</collectionView>
</subviews>
<color key="backgroundColor" name="Accent Color"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Akw-Iw-iVY">
<rect key="frame" x="-100" y="-100" width="370" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="MXo-gV-Bdf">
<rect key="frame" x="-100" y="-100" width="16" height="75"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="Etf-aa-udM" secondAttribute="trailing" constant="10" id="6TF-6x-Waq"/>
<constraint firstItem="Etf-aa-udM" firstAttribute="top" secondItem="Akn-cR-W4G" secondAttribute="top" constant="10" id="6Zz-hK-knx"/>
<constraint firstItem="Etf-aa-udM" firstAttribute="leading" secondItem="Akn-cR-W4G" secondAttribute="leading" constant="10" id="JSl-8d-7xg"/>
<constraint firstAttribute="height" constant="75" id="JUg-Of-bAv"/>
<constraint firstAttribute="bottom" secondItem="Etf-aa-udM" secondAttribute="bottom" constant="10" id="ikw-nw-27q"/>
<constraint firstAttribute="width" constant="350" id="oPv-7I-Y3n"/>
<constraint firstAttribute="bottom" secondItem="5BT-DM-xru" secondAttribute="bottom" id="Kq0-6J-4rc"/>
<constraint firstItem="5BT-DM-xru" firstAttribute="leading" secondItem="Akn-cR-W4G" secondAttribute="leading" id="jCJ-jE-SF5"/>
<constraint firstAttribute="trailing" secondItem="5BT-DM-xru" secondAttribute="trailing" id="rIv-zJ-6DJ"/>
<constraint firstItem="5BT-DM-xru" firstAttribute="top" secondItem="Akn-cR-W4G" secondAttribute="top" id="zo2-i3-2uS"/>
</constraints>
<accessibility identifier="UpcomingEventView"/>
</customView>
<customView wantsLayer="YES" focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="B8X-sx-cjT">
<rect key="frame" x="0.0" y="0.0" width="350" height="40"/>
<rect key="frame" x="0.0" y="0.0" width="370" height="40"/>
<subviews>
<button toolTip="Close Clocker" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1cR-pI-osG">
<rect key="frame" x="309" y="7" width="30" height="26"/>
<rect key="frame" x="320" y="7" width="30" height="26"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="WSi-Xj-Kfl"/>
<constraint firstAttribute="width" constant="30" id="j2s-C4-pK5"/>
</constraints>
<buttonCell key="cell" type="recessed" bezelStyle="recessed" image="PowerIcon" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Fp1-89-Nwm">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
<behavior key="behavior" lightByContents="YES"/>
<font key="font" metaFont="systemBold" size="12"/>
</buttonCell>
<accessibility identifier="Close"/>
@ -580,9 +571,9 @@
</connections>
</button>
<button toolTip="Switch between Menubar/Floating mode." verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YXE-4J-5cn">
<rect key="frame" x="275" y="10" width="29" height="19"/>
<rect key="frame" x="220" y="10" width="30" height="19"/>
<constraints>
<constraint firstAttribute="width" constant="29" id="fWo-31-i3M"/>
<constraint firstAttribute="width" constant="30" id="fWo-31-i3M"/>
<constraint firstAttribute="height" constant="18" id="v7I-PR-eTK"/>
</constraints>
<buttonCell key="cell" type="recessed" bezelStyle="recessed" image="Float-White" imagePosition="only" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="iwC-c9-CBS">
@ -595,7 +586,7 @@
</connections>
</button>
<button toolTip="Share Clocker!" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Hc7-fH-V7i">
<rect key="frame" x="240" y="10" width="30" height="21"/>
<rect key="frame" x="125" y="10" width="30" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="Bnf-E1-uaE"/>
<constraint firstAttribute="width" constant="30" id="pke-4M-ob5"/>
@ -610,9 +601,9 @@
</connections>
</button>
<button toolTip="Open Preferences" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ctq-BV-GPN">
<rect key="frame" x="8" y="10" width="29" height="19"/>
<rect key="frame" x="20" y="10" width="30" height="19"/>
<constraints>
<constraint firstAttribute="width" constant="29" id="oaD-Rr-qht"/>
<constraint firstAttribute="width" constant="30" id="oaD-Rr-qht"/>
<constraint firstAttribute="height" constant="18" id="t4M-6L-Vqi"/>
</constraints>
<buttonCell key="cell" type="recessed" bezelStyle="recessed" imagePosition="only" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="SIv-Ve-UkG">
@ -628,15 +619,15 @@
</button>
</subviews>
<constraints>
<constraint firstItem="YXE-4J-5cn" firstAttribute="leading" secondItem="Hc7-fH-V7i" secondAttribute="trailing" constant="65" id="0Zd-fd-Ove"/>
<constraint firstItem="Ctq-BV-GPN" firstAttribute="centerY" secondItem="B8X-sx-cjT" secondAttribute="centerY" id="6Q7-jc-cTL"/>
<constraint firstItem="YXE-4J-5cn" firstAttribute="leading" secondItem="Hc7-fH-V7i" secondAttribute="trailing" constant="5" id="GF2-UV-dDg"/>
<constraint firstItem="YXE-4J-5cn" firstAttribute="centerY" secondItem="B8X-sx-cjT" secondAttribute="centerY" id="OHe-Tc-b0V"/>
<constraint firstItem="Hc7-fH-V7i" firstAttribute="leading" secondItem="Ctq-BV-GPN" secondAttribute="trailing" constant="75" id="bL5-Kt-zaC"/>
<constraint firstAttribute="height" constant="40" id="iYW-fj-EE2"/>
<constraint firstItem="1cR-pI-osG" firstAttribute="centerY" secondItem="B8X-sx-cjT" secondAttribute="centerY" id="oVJ-Xa-cTw"/>
<constraint firstAttribute="trailing" secondItem="1cR-pI-osG" secondAttribute="trailing" constant="11" id="rb9-gW-QZS"/>
<constraint firstItem="1cR-pI-osG" firstAttribute="leading" secondItem="YXE-4J-5cn" secondAttribute="trailing" constant="5" id="tCt-DB-tt2"/>
<constraint firstAttribute="trailing" secondItem="1cR-pI-osG" secondAttribute="trailing" constant="20" id="rb9-gW-QZS"/>
<constraint firstItem="Hc7-fH-V7i" firstAttribute="centerY" secondItem="B8X-sx-cjT" secondAttribute="centerY" constant="-1" id="vPp-mQ-wgE"/>
<constraint firstItem="Ctq-BV-GPN" firstAttribute="leading" secondItem="B8X-sx-cjT" secondAttribute="leading" constant="8" id="ydm-oF-sVc"/>
<constraint firstItem="Ctq-BV-GPN" firstAttribute="leading" secondItem="B8X-sx-cjT" secondAttribute="leading" constant="20" id="ydm-oF-sVc"/>
</constraints>
</customView>
</subviews>
@ -689,7 +680,10 @@
<image name="Extra" width="700" height="700"/>
<image name="Float-White" width="15" height="13"/>
<image name="PowerIcon" width="350" height="350"/>
<image name="Remove" width="700" height="700"/>
<image name="PowerIcon-White" width="350" height="350"/>
<image name="SharingDarkIcon" width="19" height="22"/>
<namedColor name="Accent Color">
<color red="0.092000000178813934" green="0.0" blue="0.99900001287460327" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

2
Clocker/Clocker/es.lproj/Localizable.strings

@ -163,3 +163,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/fr.lproj/Localizable.strings

@ -162,3 +162,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/hi.lproj/Localizable.strings

@ -157,3 +157,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/hr.lproj/Localizable.strings

@ -163,3 +163,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/it.lproj/Localizable.strings

@ -162,3 +162,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/ja.lproj/Localizable.strings

@ -161,3 +161,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/ko.lproj/Localizable.strings

@ -164,3 +164,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/nl.lproj/Localizable.strings

@ -161,3 +161,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/pt-BR.lproj/Localizable.strings

@ -162,3 +162,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/ru.lproj/Localizable.strings

@ -154,3 +154,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/zh-Hans.lproj/Localizable.strings

@ -155,3 +155,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

2
Clocker/Clocker/zh-Hant.lproj/Localizable.strings

@ -162,3 +162,5 @@
"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours";
"Copied to Clipboard" = "Copied to Clipboard";
"No upcoming events for today!" = "No upcoming events for today!";
"Great going." = "Great going.";

4
Clocker/ClockerUITests/AboutUsTests.swift

@ -32,12 +32,12 @@ class AboutUsTests: XCTestCase {
app.buttons["Preferences"].click()
tapAboutTab()
app.checkBoxes["ClockerPrivateFeedback"].click()
app.buttons["Send Feedback"].click()
XCTAssertFalse(app.progressIndicators["ProgressIndicator"].exists)
sleep(2) // Wait for Toast to disappear
// Close window

2
Clocker/ClockerUITests/CopyToClipboardTests.swift

@ -32,7 +32,7 @@ class CopyToClipboardTests: XCTestCase {
let actualValue = NSPasteboard.general.string(forType: .string) ?? "Empty Pasteboard"
XCTAssert(expectedValue == actualValue,
"Clipboard value (\(actualValue)) doesn't match expected result: \(expectedValue)")
// Test full copy
let cellCount = app.tables["FloatingTableView"].cells.count
var clipboardValue = String()

37
Clocker/ClockerUITests/PermissionsTests.swift

@ -19,29 +19,29 @@ class PermissionsTests: XCTestCase {
}
app.tapMenubarIcon()
app/*@START_MENU_TOKEN@*/.buttons["Preferences"]/*[[".dialogs[\"Clocker Panel\"].buttons[\"Preferences\"]",".buttons[\"Preferences\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.click()
app/*@START_MENU_TOKEN@*/ .buttons["Preferences"]/*[[".dialogs[\"Clocker Panel\"].buttons[\"Preferences\"]",".buttons[\"Preferences\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/ .click()
let clockerWindow = app.windows["Clocker"]
// Check Permissions first
let permissionsTab = clockerWindow.toolbars.buttons["Permissions"]
permissionsTab.click()
let grantButton = clockerWindow.buttons["CalendarGrantAccessButton"].firstMatch
if grantButton.title == "Granted" || grantButton.title == "Denied" {
return
}
let calendarButton = clockerWindow.toolbars.buttons["Calendar"]
calendarButton.click()
let showUpcomingEventView = clockerWindow.staticTexts["UpcomingEventView"]
XCTAssertFalse(showUpcomingEventView.isHittable)
clockerWindow.buttons["Grant Access"].click()
clockerWindow.buttons["CalendarGrantAccessButton"].firstMatch.click()
addUIInterruptionMonitor(withDescription: "Calendars Access") { alert -> Bool in
let alertButton = alert.buttons["OK"]
if alertButton.exists {
@ -50,33 +50,33 @@ class PermissionsTests: XCTestCase {
}
return false
}
calendarButton.click()
XCTAssertTrue(showUpcomingEventView.isHittable)
}
func testAcceptingRemindersPermissions() {
if app.tables["FloatingTableView"].exists {
app.tapMenubarIcon()
app.buttons["FloatingPin"].click()
}
app.tapMenubarIcon()
app/*@START_MENU_TOKEN@*/.buttons["Preferences"]/*[[".dialogs[\"Clocker Panel\"].buttons[\"Preferences\"]",".buttons[\"Preferences\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.click()
app/*@START_MENU_TOKEN@*/ .buttons["Preferences"]/*[[".dialogs[\"Clocker Panel\"].buttons[\"Preferences\"]",".buttons[\"Preferences\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/ .click()
let clockerWindow = app.windows["Clocker"]
// Check Permissions first
let permissionsTab = clockerWindow.toolbars.buttons["Permissions"]
permissionsTab.click()
let grantButton = clockerWindow.buttons["RemindersGrantAccessButton"].firstMatch
if grantButton.title == "Granted" || grantButton.title == "Denied" {
return
}
clockerWindow.buttons["RemindersGrantAccessButton"].firstMatch.click()
addUIInterruptionMonitor(withDescription: "Reminders Access") { alert -> Bool in
let alertButton = alert.buttons["OK"]
if alertButton.exists {
@ -86,5 +86,4 @@ class PermissionsTests: XCTestCase {
return false
}
}
}

32
Clocker/ClockerUnitTests/StandardMenubarHandlerTests.swift

@ -1,12 +1,11 @@
// Copyright © 2015 Abhishek Banthia
import XCTest
import CoreModelKit
import XCTest
@testable import Clocker
class StandardMenubarHandlerTests: XCTestCase {
private let mumbai = ["customLabel": "Ghar",
"formattedAddress": "Mumbai",
"place_id": "ChIJwe1EZjDG5zsRaYxkjY_tpF0",
@ -14,48 +13,48 @@ class StandardMenubarHandlerTests: XCTestCase {
"nextUpdate": "",
"latitude": "19.0759837",
"longitude": "72.8776559"]
func testValidStandardMenubarHandler_returnMenubarTitle() {
// Wipe all timezones from UserDefaults
UserDefaults.standard.setValue(nil, forKey: CLDefaultPreferenceKey)
// Save a menubar selected timezone
let dataObject = TimezoneData(with: mumbai)
dataObject.isFavourite = 1
let operationsObject = TimezoneDataOperations(with: dataObject)
operationsObject.saveObject()
let menubarTimezones = DataStore.shared().menubarTimezones()
XCTAssertTrue(menubarTimezones?.count == 1)
// Set standard menubar in Prefs
UserDefaults.standard.set(1, forKey: CLMenubarCompactMode)
let menubarHandler = MenubarHandler()
let menubarString = menubarHandler.titleForMenubar() ?? ""
// Test menubar string is present
XCTAssertTrue(menubarString.count > 0)
XCTAssertTrue(menubarString.contains("Ghar"))
// Set default back to compact menubar
UserDefaults.standard.set(0, forKey: CLMenubarCompactMode)
}
func testUnfavouritedTimezone_returnEmptyMenubarTimezoneCount() {
// Wipe all timezones from UserDefaults
UserDefaults.standard.setValue(nil, forKey: CLDefaultPreferenceKey)
// Save a menubar selected timezone
let dataObject = TimezoneData(with: mumbai)
dataObject.isFavourite = 0
let operationsObject = TimezoneDataOperations(with: dataObject)
operationsObject.saveObject()
let menubarTimezones = DataStore.shared().menubarTimezones()
XCTAssertTrue(menubarTimezones?.count == 0)
}
func testUnfavouritedTimezone_returnNilMenubarString() {
// Wipe all timezones from UserDefaults
UserDefaults.standard.setValue(nil, forKey: CLDefaultPreferenceKey)
@ -63,17 +62,16 @@ class StandardMenubarHandlerTests: XCTestCase {
let emptyMenubarString = menubarHandler.titleForMenubar()
// Returns early because DataStore.menubarTimezones is nil
XCTAssertNil(emptyMenubarString)
// Save a menubar selected timezone
let dataObject = TimezoneData(with: mumbai)
dataObject.isFavourite = 0
let operationsObject = TimezoneDataOperations(with: dataObject)
operationsObject.saveObject()
let menubarString = menubarHandler.titleForMenubar() ?? ""
// Test menubar string is absent
XCTAssertTrue(menubarString.count == 0)
}
}

8
Clocker/CoreModelKit/Sources/CoreModelKit/TimezoneData.swift

@ -85,7 +85,7 @@ public class TimezoneData: NSObject, NSCoding {
public var isSystemTimezone = false
public var overrideFormat: TimezoneOverride = .globalFormat
override public init() {
public override init() {
selectionType = .timezone
isFavourite = 0
note = ModelConstants.emptyString
@ -277,7 +277,7 @@ public class TimezoneData: NSObject, NSCoding {
return formatInString.contains("ss")
}
override public var hash: Int {
public override var hash: Int {
guard let placeIdentifier = placeID, let timezone = timezoneID else {
return -1
}
@ -289,14 +289,14 @@ public class TimezoneData: NSObject, NSCoding {
return lhs.placeID == rhs.placeID
}
override public func isEqual(to object: Any?) -> Bool {
public override func isEqual(to object: Any?) -> Bool {
if let other = object as? TimezoneData {
return placeID == other.placeID
}
return false
}
override public func isEqual(_ object: Any?) -> Bool {
public override func isEqual(_ object: Any?) -> Bool {
guard let compared = object as? TimezoneData else {
return false
}

6
Clocker/Dependencies/Date Additions/TimePeriodChain.swift

@ -138,15 +138,15 @@ open class TimePeriodChain: TimePeriodGroup {
_end = _end?.addingTimeInterval(duration)
}
override public func map<T>(_ transform: (TimePeriodProtocol) throws -> T) rethrows -> [T] {
public override func map<T>(_ transform: (TimePeriodProtocol) throws -> T) rethrows -> [T] {
return try periods.map(transform)
}
override public func filter(_ isIncluded: (TimePeriodProtocol) throws -> Bool) rethrows -> [TimePeriodProtocol] {
public override func filter(_ isIncluded: (TimePeriodProtocol) throws -> Bool) rethrows -> [TimePeriodProtocol] {
return try periods.filter(isIncluded)
}
override internal func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, TimePeriodProtocol) throws -> Result) rethrows -> Result {
internal override func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, TimePeriodProtocol) throws -> Result) rethrows -> Result {
return try periods.reduce(initialResult, nextPartialResult)
}

88
Clocker/Events and Reminders/CalendarHandler.swift

@ -157,12 +157,32 @@ extension EventCenter {
let relevantEvents = filteredEvents[autoupdatingCalendar.startOfDay(for: Date())] ?? []
let filteredEvent = relevantEvents.filter {
let filteredEvents = relevantEvents.filter {
$0.event.isAllDay == false && $0.event.startDate.timeIntervalSinceNow > -300
}.first
}
if filteredEvents.count == 1 { return filteredEvents.first }
// If there are multipl events coming up, prefer the ones the currentUser has accepted
let acceptedEvents = filteredEvents.filter {
$0.attendeStatus == .accepted
}
let optionalEvents = filteredEvents.filter {
$0.attendeStatus == .tentative
}
if let firstAcceptedEvent = acceptedEvents.first {
return firstAcceptedEvent
}
// If there are no accepted events, prefer the first optional event
if acceptedEvents.isEmpty, !optionalEvents.isEmpty {
return optionalEvents.first
}
if let firstEvent = filteredEvent {
return firstEvent
// Otherwise check if there's a filtered event at all and return it
if let first = filteredEvents.first {
return first
}
let filteredAllDayEvent = relevantEvents.filter {
@ -172,6 +192,19 @@ extension EventCenter {
return filteredAllDayEvent
}
func upcomingEventsForDay(_: [EventInfo]) -> [EventInfo]? {
if calendarAccessDenied() || calendarAccessNotDetermined() {
return nil
}
let todayEvents = filteredEvents[autoupdatingCalendar.startOfDay(for: Date())] ?? []
let tomorrowEvents = filteredEvents[autoupdatingCalendar.startOfDay(for: Date().addingTimeInterval(86400))] ?? []
let relevantEvents = todayEvents + tomorrowEvents
return relevantEvents.filter {
$0.event.startDate.timeIntervalSinceNow > -300
}
}
func initializeStoreIfNeccesary() {
if eventStore == nil {
eventStore = EKEventStore()
@ -333,13 +366,15 @@ 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 eventParticipationStatus = attendingStatusForUser(event)
let meetingURL = retrieveMeetingURL(event)
let eventInfo = EventInfo(event: event,
isStartDate: isStartDate,
isEndDate: isEndDate,
isAllDay: isAllDay,
isSingleDay: isSingleDay,
meetingURL: meetingURL)
meetingURL: meetingURL,
attendeStatus: eventParticipationStatus)
return eventInfo
}
@ -354,7 +389,9 @@ extension EventCenter {
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/") {
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 {
@ -380,9 +417,9 @@ extension EventCenter {
|| actualLink.contains("public.senfcall.de")
|| actualLink.contains("youcanbook.me/zoom/")
|| actualLink.contains("workplace.com/groupcall")
{
if let zoomLink = result.url {
return zoomLink
|| actualLink.contains("bluejeans.com/") {
if let meetingLink = result.url {
return meetingLink
}
}
}
@ -392,7 +429,7 @@ extension EventCenter {
private func retrieveMeetingURL(_ event: EKEvent) -> URL? {
if EventCenter.dataDetector == nil {
var dataDetector: NSDataDetector? = nil
var dataDetector: NSDataDetector?
do {
dataDetector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
} catch {
@ -416,6 +453,23 @@ extension EventCenter {
return nil
}
private func attendingStatusForUser(_ event: EKEvent) -> EKParticipantStatus {
// First check if the current user is the organizer
if event.organizer?.isCurrentUser == true {
return event.organizer?.participantStatus ?? .unknown
}
guard let attendes = event.attendees else {
return .unknown
}
for attende in attendes where attende.isCurrentUser {
return attende.participantStatus
}
return .unknown
}
}
struct CalendarInfo {
@ -430,17 +484,23 @@ struct EventInfo {
let isAllDay: Bool
let isSingleDay: Bool
let meetingURL: URL?
let attendeStatus: EKParticipantStatus
func metadataForMeeting() -> String {
let timeIntervalSinceNowForMeeting = event.startDate.timeIntervalSinceNow
if (timeIntervalSinceNowForMeeting < 0 && timeIntervalSinceNowForMeeting > -300) {
return "started \(event.startDate.shortTimeAgoSinceNow) ago."
} else {
if timeIntervalSinceNowForMeeting < 0, timeIntervalSinceNowForMeeting > -300 {
return "started \(event.startDate.shortTimeAgoSinceNow) ago."
} else if event.startDate.isToday {
let timeSince = Date().timeAgo(since: event.startDate)
let withoutAn = timeSince.replacingOccurrences(of: "an", with: CLEmptyString)
let withoutAgo = withoutAn.replacingOccurrences(of: "ago", with: CLEmptyString)
return "in \(withoutAgo.lowercased())"
} else if event.startDate.isTomorrow {
let hoursUntil = event.startDate.hoursUntil
return "in \(hoursUntil)h"
}
return "Error"
}
}

3
Clocker/Events and Reminders/RemindersHandler.swift

@ -51,8 +51,7 @@ extension EventCenter {
timezone: String,
alertIndex: Int,
reminderDate: Date,
additionalNotes: String?) -> Bool
{
additionalNotes: String?) -> Bool {
initializeStoreIfNeccesary()
if reminderAccessNotDetermined() || reminderAccessDenied() {

6
Clocker/Menu Bar/StatusContainerView.swift

@ -31,12 +31,10 @@ func compactWidth(for timezone: TimezoneData) -> Int {
if timeFormat == DateFormat.twelveHour
|| timeFormat == DateFormat.twelveHourWithSeconds
|| timeFormat == DateFormat.twelveHourWithZero
|| timeFormat == DateFormat.twelveHourWithSeconds
{
|| timeFormat == DateFormat.twelveHourWithSeconds {
totalWidth += 20
} else if timeFormat == DateFormat.twentyFourHour
|| timeFormat == DateFormat.twentyFourHourWithSeconds
{
|| timeFormat == DateFormat.twentyFourHourWithSeconds {
totalWidth += 0
}

2
Clocker/Menu Bar/StatusItemHandler.swift

@ -185,7 +185,7 @@ class StatusItemHandler: NSObject {
if let strongSelf = self {
strongSelf.performTimerWork()
}
})
})
// Tolerance, even a small amount, has a positive imapct on the power usage. As a rule, we set it to 10% of the interval
menubarTimer?.tolerance = shouldDisplaySeconds ? 0.5 : 20

12
Clocker/Onboarding/OnboardingSearchController.swift

@ -27,8 +27,8 @@ class OnboardingSearchController: NSViewController {
private var geocodingKey: String = {
guard let path = Bundle.main.path(forResource: "Keys", ofType: "plist"),
let dictionary = NSDictionary(contentsOfFile: path),
let apiKey = dictionary["GeocodingKey"] as? String
let dictionary = NSDictionary(contentsOfFile: path),
let apiKey = dictionary["GeocodingKey"] as? String
else {
assertionFailure("Unable to find the API key")
return ""
@ -61,7 +61,7 @@ class OnboardingSearchController: NSViewController {
resultsTableView.reloadData()
func setupUndoButton() {
let font = NSFont(name: "Avenir", size: 13) ?? NSFont.systemFont(ofSize: 13)
let font = NSFont(name: "Avenir", size: 13) ?? NSFont.systemFont(ofSize: 13)
let attributes = [NSAttributedString.Key.foregroundColor: NSColor.linkColor,
NSAttributedString.Key.font: font]
undoButton.attributedTitle = NSAttributedString(string: "UNDO", attributes: attributes)
@ -375,7 +375,7 @@ class OnboardingSearchController: NSViewController {
self.findLocalSearchResultsForTimezones()
self.prepareUIForPresentingResults()
}
})
})
}
private func presentErrorMessage(_ errorMessage: String) {
@ -516,8 +516,8 @@ extension OnboardingSearchController: NSSearchFieldDelegate {
// return true if the action was handled; otherwise false
return false
}
func searchFieldDidEndSearching(_ sender: NSSearchField) {
self.search(sender)
search(sender)
}
}

19
Clocker/Overall App/ConfigExport.swift

@ -1,10 +1,9 @@
// Copyright © 2015 Abhishek Banthia
import Foundation
import CoreModelKit
import Foundation
struct ConfigExport {
private func generateJSONFromDefaults() {
let selectedKeys: Set<String> = Set([
CLShowOnboardingFlow,
@ -30,17 +29,17 @@ struct ConfigExport {
CLDefaultMenubarMode,
CLInstallHomeIndicatorObject,
CLSwitchToCompactModeAlert,
CLDisplayDSTTransitionInfo
CLDisplayDSTTransitionInfo,
])
let dictionaryRep = UserDefaults.standard.dictionaryRepresentation()
var clockerPrefs: [String:Any] = [:]
var clockerPrefs: [String: Any] = [:]
for (key, value) in dictionaryRep {
if selectedKeys.contains(key) {
print("Key is \(key) and value is \(value)")
clockerPrefs[key] = value
}
}
do {
let decodeJSON: [[String: Any]] = DataStore.shared().timezones().compactMap { data -> [String: Any]? in
guard let customObject = TimezoneData.customObject(from: data) else { return nil }
@ -58,10 +57,10 @@ struct ConfigExport {
]
return timezoneDictionary
}
let timezoneDict = ["Timezones": decodeJSON]
clockerPrefs.merge(timezoneDict) { (current, _) in current}
clockerPrefs.merge(timezoneDict) { current, _ in current }
guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileUrl = documentDirectoryUrl.appendingPathComponent("Persons.json")
// Transform array into data and save it into file
@ -71,13 +70,11 @@ struct ConfigExport {
} catch {
print(error)
}
let json = try JSONSerialization.data(withJSONObject: clockerPrefs, options: .prettyPrinted)
print(json)
} catch {
print("Failure Observed \(error.localizedDescription)")
}
}
}

2
Clocker/Overall App/NetworkManager.swift

@ -41,7 +41,7 @@ extension NetworkManager {
let session = URLSession(configuration: configuration)
guard let encodedPath = path.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let url = URL(string: encodedPath)
let url = URL(string: encodedPath)
else {
completionHandler(nil, unableToGenerateURL)
return nil

28
Clocker/Overall App/Themer.swift

@ -149,7 +149,7 @@ extension Themer {
}
return .light
}
func mainTextColor() -> NSColor {
if #available(macOS 10.14, *) {
switch themeIndex {
@ -210,8 +210,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 {
@ -277,7 +277,7 @@ extension Themer {
if let symbolImage = symbolImage(for: "xmark") {
return symbolImage
}
if #available(macOS 10.14, *) {
switch themeIndex {
case .light, .solarizedLight:
@ -411,7 +411,7 @@ extension Themer {
if let privacyTabSFImage = symbolImage(for: "lock") {
return privacyTabSFImage
}
if #available(macOS 10.14, *) {
switch themeIndex {
case .light, .solarizedLight:
@ -430,7 +430,7 @@ extension Themer {
if let appearanceTabImage = symbolImage(for: "eye") {
return appearanceTabImage
}
if #available(macOS 10.14, *) {
switch themeIndex {
case .light, .solarizedLight:
@ -449,7 +449,7 @@ extension Themer {
if let calendarTabImage = symbolImage(for: "calendar") {
return calendarTabImage
}
if #available(macOS 10.14, *) {
switch themeIndex {
case .light, .solarizedLight:
@ -463,11 +463,11 @@ extension Themer {
return themeIndex == .light ? NSImage(named: NSImage.Name("Calendar Tab Icon"))! : NSImage(named: NSImage.Name("Calendar Tab Dark"))!
}
func generalTabImage() -> NSImage? {
return symbolImage(for: "gearshape")
}
func aboutTabImage() -> NSImage? {
return symbolImage(for: "info.circle")
}
@ -501,20 +501,24 @@ extension Themer {
return nil
}
}
func filledTrashImage() -> NSImage? {
return symbolImage(for: "trash.fill")
}
// Modern Slider
func goBackwardsImage() -> NSImage? {
return symbolImage(for: "gobackward.15")
}
func goForwardsImage() -> NSImage? {
return symbolImage(for: "goforward.15")
}
func resetModernSliderImage() -> NSImage? {
return symbolImage(for: "xmark.circle.fill")
}
private func symbolImage(for name: String) -> NSImage? {
assert(name.isEmpty == false)

2
Clocker/Panel/Data Layer/TimezoneDataOperations.swift

@ -368,7 +368,7 @@ extension TimezoneDataOperations {
to: Date())
guard let lat = dataObject.latitude,
let long = dataObject.longitude
let long = dataObject.longitude
else {
assertionFailure("Data was unexpectedly nil.")
return

1
Clocker/Panel/FloatingWindowController.swift

@ -121,6 +121,7 @@ class FloatingWindowController: ParentPanelController {
window?.contentView?.layer?.masksToBounds = true
window?.isOpaque = false
window?.backgroundColor = NSColor.clear
window?.collectionBehavior = .canJoinAllSpaces
}
func startWindowTimer() {

8
Clocker/Panel/Notes Popover/NotesPopover.swift

@ -224,7 +224,7 @@ class NotesPopover: NSViewController {
}
completionHandler(completedDate)
})
})
} catch {
assertionFailure("Failed to successfully initialize DataDetector")
completionHandler(nil)
@ -322,8 +322,7 @@ class NotesPopover: NSViewController {
}
private func updateTimezoneInDefaultPreferences(with override: Int,
_: OverrideType)
{
_: OverrideType) {
let timezones = DataStore.shared().timezones()
var timezoneObjects: [TimezoneData] = []
@ -392,8 +391,7 @@ class NotesPopover: NSViewController {
timezone: model.timezone(),
alertIndex: alertIndex,
reminderDate: reminderPicker.dateValue,
additionalNotes: model.note)
{
additionalNotes: model.note) {
showSuccessMessage()
}
}

24
Clocker/Panel/PanelController.swift

@ -99,6 +99,7 @@ class PanelController: ParentPanelController {
sliderDatePicker.dateValue = Date()
closestQuarterTimeRepresentation = findClosestQuarterTimeApproximation()
modernSliderLabel.stringValue = "Time Scroller"
resetModernSliderButton.isHidden = true
if modernSlider != nil {
let indexPaths: Set<IndexPath> = Set([IndexPath(item: modernSlider.numberOfItems(inSection: 0) / 2, section: 0)])
@ -153,8 +154,7 @@ class PanelController: ParentPanelController {
}
if let statusWindow = statusBackgroundWindow,
let statusButton = statusView
{
let statusButton = statusView {
var statusItemFrame = statusWindow.convertToScreen(statusButton.frame)
var statusItemScreen = NSScreen.main
var testPoint = statusItemFrame.origin
@ -186,16 +186,16 @@ class PanelController: ParentPanelController {
let preferences = DataStore.shared().timezones()
guard let theme = DataStore.shared().retrieve(key: CLThemeKey) as? NSNumber,
let displayFutureSliderKey = DataStore.shared().retrieve(key: CLThemeKey) as? NSNumber,
let showAppInForeground = DataStore.shared().retrieve(key: CLShowAppInForeground) as? NSNumber,
let relativeDateKey = DataStore.shared().retrieve(key: CLRelativeDateKey) as? NSNumber,
let fontSize = DataStore.shared().retrieve(key: CLUserFontSizePreference) as? NSNumber,
let sunriseTime = DataStore.shared().retrieve(key: CLSunriseSunsetTime) as? NSNumber,
let showDayInMenu = DataStore.shared().retrieve(key: CLShowDayInMenu) as? NSNumber,
let showDateInMenu = DataStore.shared().retrieve(key: CLShowDateInMenu) as? NSNumber,
let showPlaceInMenu = DataStore.shared().retrieve(key: CLShowPlaceInMenu) as? NSNumber,
let showUpcomingEventView = DataStore.shared().retrieve(key: CLShowUpcomingEventView) as? String,
let country = Locale.autoupdatingCurrent.regionCode
let displayFutureSliderKey = DataStore.shared().retrieve(key: CLThemeKey) as? NSNumber,
let showAppInForeground = DataStore.shared().retrieve(key: CLShowAppInForeground) as? NSNumber,
let relativeDateKey = DataStore.shared().retrieve(key: CLRelativeDateKey) as? NSNumber,
let fontSize = DataStore.shared().retrieve(key: CLUserFontSizePreference) as? NSNumber,
let sunriseTime = DataStore.shared().retrieve(key: CLSunriseSunsetTime) as? NSNumber,
let showDayInMenu = DataStore.shared().retrieve(key: CLShowDayInMenu) as? NSNumber,
let showDateInMenu = DataStore.shared().retrieve(key: CLShowDateInMenu) as? NSNumber,
let showPlaceInMenu = DataStore.shared().retrieve(key: CLShowPlaceInMenu) as? NSNumber,
let showUpcomingEventView = DataStore.shared().retrieve(key: CLShowUpcomingEventView) as? String,
let country = Locale.autoupdatingCurrent.regionCode
else {
return
}

43
Clocker/Panel/ParentPanelController+ModernSlider.swift

@ -18,15 +18,17 @@ extension ParentPanelController: NSCollectionViewDataSource {
extension ParentPanelController {
func setupModernSliderIfNeccessary() {
if modernSlider != nil {
resetModernSliderButton.image = Themer.shared().resetModernSliderImage()
goBackwardsButton.image = Themer.shared().goBackwardsImage()
goForwardButton.image = Themer.shared().goForwardsImage()
goForwardButton.isContinuous = true
goBackwardsButton.isContinuous = true
goBackwardsButton.toolTip = "Navigate 15 mins back"
goForwardButton.toolTip = "Navigate 15 mins forward"
modernSlider.enclosingScrollView?.scrollerInsets = NSEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
modernSlider.enclosingScrollView?.backgroundColor = NSColor.clear
modernSlider.setAccessibility("ModernSlider")
@ -35,13 +37,13 @@ extension ParentPanelController {
selector: #selector(collectionViewDidScroll(_:)),
name: NSView.boundsDidChangeNotification,
object: modernSlider.superview)
// Set the modern slider label!
closestQuarterTimeRepresentation = findClosestQuarterTimeApproximation()
if let unwrappedClosetQuarterTime = closestQuarterTimeRepresentation {
modernSliderLabel.stringValue = timezoneFormattedStringRepresentation(unwrappedClosetQuarterTime)
}
// Make sure modern slider is centered horizontally!
let indexPaths: Set<IndexPath> = Set([IndexPath(item: modernSlider.numberOfItems(inSection: 0) / 2, section: 0)])
modernSlider.scrollToItems(at: indexPaths, scrollPosition: .centeredHorizontally)
@ -56,6 +58,27 @@ extension ParentPanelController {
navigateModernSliderToSpecificIndex(-1)
}
private func animateButton(_ hidden: Bool) {
NSAnimationContext.runAnimationGroup({ context in
context.duration = 0.5
context.timingFunction = CAMediaTimingFunction(name: hidden ? CAMediaTimingFunctionName.easeOut : CAMediaTimingFunctionName.easeIn)
resetModernSliderButton.animator().alphaValue = hidden ? 0.0 : 1.0
}, completionHandler: { [weak self] in
guard let strongSelf = self else { return }
strongSelf.resetModernSliderButton.animator().isHidden = hidden
})
}
@IBAction func resetModernSlider(_: NSButton) {
closestQuarterTimeRepresentation = findClosestQuarterTimeApproximation()
modernSliderLabel.stringValue = "Time Scroller"
animateButton(true)
if modernSlider != nil {
let indexPaths: Set<IndexPath> = Set([IndexPath(item: modernSlider.numberOfItems(inSection: 0) / 2, section: 0)])
modernSlider.scrollToItems(at: indexPaths, scrollPosition: .centeredHorizontally)
}
}
private func navigateModernSliderToSpecificIndex(_ index: Int) {
guard let contentView = modernSlider.superview as? NSClipView else {
return
@ -101,14 +124,24 @@ extension ParentPanelController {
let remainder = (index % (centerPoint + 1))
let nextDate = Calendar.current.date(byAdding: .minute, value: remainder * 15, to: closestQuarterTimeRepresentation ?? Date())!
modernSliderLabel.stringValue = timezoneFormattedStringRepresentation(nextDate)
if resetModernSliderButton.isHidden {
animateButton(false)
}
return nextDate.minutes(from: Date()) + 1
} else if index < centerPoint {
let remainder = centerPoint - index + 1
let previousDate = Calendar.current.date(byAdding: .minute, value: -1 * remainder * 15, to: closestQuarterTimeRepresentation ?? Date())!
modernSliderLabel.stringValue = timezoneFormattedStringRepresentation(previousDate)
if resetModernSliderButton.isHidden {
animateButton(false)
}
return previousDate.minutes(from: Date())
} else {
modernSliderLabel.stringValue = "Time Scroller"
if !resetModernSliderButton.isHidden {
animateButton(true)
}
return 0
}
}

206
Clocker/Panel/ParentPanelController.swift

@ -55,20 +55,12 @@ class ParentPanelController: NSWindowController {
@IBOutlet var scrollViewHeight: NSLayoutConstraint!
@IBOutlet var calendarColorView: NSView!
@IBOutlet var futureSliderView: NSView!
@IBOutlet var upcomingEventView: NSView?
@IBOutlet var reviewView: NSView!
@IBOutlet var leftField: NSTextField!
@IBOutlet var nextEventLabel: NSTextField!
@IBOutlet var whiteRemoveButton: NSButton!
@IBOutlet var sharingButton: NSButton!
@IBOutlet var leftButton: NSButton!
@ -81,8 +73,6 @@ class ParentPanelController: NSWindowController {
@IBOutlet var pinButton: NSButton!
@IBOutlet var calendarButton: NSButton!
@IBOutlet var sliderDatePicker: NSDatePicker!
@IBOutlet var roundedDateView: NSView!
@ -95,6 +85,12 @@ class ParentPanelController: NSWindowController {
@IBOutlet var modernContainerView: ModernSliderContainerView!
@IBOutlet var goBackwardsButton: NSButton!
@IBOutlet var goForwardButton: NSButton!
@IBOutlet var resetModernSliderButton: NSButton!
// Upcoming Events
@IBOutlet var upcomingEventCollectionView: NSCollectionView!
@IBOutlet var upcomingEventContainerView: NSView!
public var upcomingEventsDataSource: UpcomingEventsDataSource!
var defaultPreferences: [Data] {
return DataStore.shared().timezones()
@ -165,7 +161,7 @@ class ParentPanelController: NSWindowController {
pinButton.image = sharedThemer.pinImage()
sharingButton.image = sharedThemer.sharingImage()
if let upcomingView = upcomingEventView {
if let upcomingView = upcomingEventContainerView {
upcomingView.setAccessibility("UpcomingEventView")
}
@ -219,6 +215,7 @@ class ParentPanelController: NSWindowController {
}
setupModernSliderIfNeccessary()
setupUpcomingEventViewCollectionViewIfNeccesary()
if roundedDateView != nil {
setupRoundedDateView()
@ -281,9 +278,9 @@ class ParentPanelController: NSWindowController {
let showUpcomingEventView = DataStore.shared().shouldDisplay(ViewType.upcomingEventView)
if showUpcomingEventView == false {
upcomingEventView?.isHidden = true
upcomingEventContainerView?.isHidden = true
} else {
upcomingEventView?.isHidden = false
upcomingEventContainerView?.isHidden = false
setupUpcomingEventView()
eventStoreChangedNotification = NotificationCenter.default.addObserver(forName: NSNotification.Name.EKEventStoreChanged, object: self, queue: OperationQueue.main) { _ in
self.fetchCalendarEvents()
@ -314,11 +311,7 @@ class ParentPanelController: NSWindowController {
if eventCenter.calendarAccessGranted() {
// Nice. Events will be retrieved when we open the panel
} else if eventCenter.calendarAccessNotDetermined() {
nextEventLabel.stringValue = NSLocalizedString("See your next Calendar event here.",
comment: "Next Event Label for no Calendar access")
setCalendarButtonTitle(buttonTitle: NSLocalizedString("Click here to start.",
comment: "Button Title for no Calendar access"))
calendarColorView.layer?.backgroundColor = NSColor(red: 97 / 255.0, green: 194 / 255.0, blue: 80 / 255.0, alpha: 1.0).cgColor
upcomingEventCollectionView.reloadData()
} else {
removeUpcomingEventView()
}
@ -351,11 +344,8 @@ class ParentPanelController: NSWindowController {
@objc func themeChanged() {
let sharedThemer = Themer.shared()
if upcomingEventView?.isHidden == false {
upcomingEventView?.layer?.backgroundColor = NSColor.clear.cgColor
nextEventLabel.textColor = sharedThemer.mainTextColor()
whiteRemoveButton.image = sharedThemer.removeImage()
setCalendarButtonTitle(buttonTitle: calendarButton.title)
if upcomingEventContainerView?.isHidden == false {
upcomingEventContainerView?.layer?.backgroundColor = NSColor.clear.cgColor
}
shutdownButton.image = sharedThemer.shutdownImage()
@ -377,20 +367,6 @@ class ParentPanelController: NSWindowController {
morePopover = NSPopover()
}
private func setCalendarButtonTitle(buttonTitle: String) {
let style = NSMutableParagraphStyle()
style.alignment = .left
style.lineBreakMode = .byTruncatingTail
if let boldFont = NSFont(name: "Avenir", size: 12) {
let attributes = [NSAttributedString.Key.foregroundColor: NSColor.lightGray, NSAttributedString.Key.paragraphStyle: style, NSAttributedString.Key.font: boldFont]
let attributedString = NSAttributedString(string: buttonTitle, attributes: attributes)
calendarButton.attributedTitle = attributedString
calendarButton.toolTip = attributedString.string
}
}
func screenHeight() -> CGFloat {
guard let main = NSScreen.main else { return 100 }
@ -428,9 +404,8 @@ class ParentPanelController: NSWindowController {
if let note = object?.note, note.isEmpty == false {
newHeight += 20
} else if DataStore.shared().shouldDisplay(.dstTransitionInfo),
let obj = object,
TimezoneDataOperations(with: obj).nextDaylightSavingsTransitionIfAvailable(with: futureSliderValue) != nil
{
let obj = object,
TimezoneDataOperations(with: obj).nextDaylightSavingsTransitionIfAvailable(with: futureSliderValue) != nil {
newHeight += 20
}
}
@ -490,21 +465,19 @@ class ParentPanelController: NSWindowController {
scrollViewHeight.constant = (screenHeight() - 100)
}
}
if DataStore.shared().shouldDisplay(.futureSlider) {
let isModernSliderDisplayed = DataStore.shared().retrieve(key: CLDisplayFutureSliderKey) as? NSNumber ?? 0
if isModernSliderDisplayed == 0 {
if scrollViewHeight.constant >= (screenHeight() - 200) {
scrollViewHeight.constant = (screenHeight() - 300)
}
} else {
if scrollViewHeight.constant >= (screenHeight() - 200) {
scrollViewHeight.constant = (screenHeight() - 200)
}
}
}
if DataStore.shared().shouldDisplay(.futureSlider) {
let isModernSliderDisplayed = DataStore.shared().retrieve(key: CLDisplayFutureSliderKey) as? NSNumber ?? 0
if isModernSliderDisplayed == 0 {
if scrollViewHeight.constant >= (screenHeight() - 200) {
scrollViewHeight.constant = (screenHeight() - 300)
}
} else {
if scrollViewHeight.constant >= (screenHeight() - 200) {
scrollViewHeight.constant = (screenHeight() - 200)
}
}
}
}
func updateDefaultPreferences() {
@ -571,7 +544,7 @@ class ParentPanelController: NSWindowController {
@IBAction func openPreferences(_: NSButton) {
updatePopoverDisplayState() // Popover's class has access to all timezones. Need to close the popover, so that we don't have two copies of selections
openPreferences()
openPreferencesWindow()
}
func deleteTimezone(at row: Int) {
@ -619,9 +592,8 @@ class ParentPanelController: NSWindowController {
let current = preferences[$0]
if $0 < mainTableView.numberOfRows,
let cellView = mainTableView.view(atColumn: 0, row: $0, makeIfNecessary: false) as? TimezoneCellView,
let model = TimezoneData.customObject(from: current)
{
let cellView = mainTableView.view(atColumn: 0, row: $0, makeIfNecessary: false) as? TimezoneCellView,
let model = TimezoneData.customObject(from: current) {
if let futureSliderCell = futureSlider.cell as? CustomSliderCell, futureSliderCell.tracking == true {
return
}
@ -641,8 +613,7 @@ class ParentPanelController: NSWindowController {
if let note = model.note, !note.isEmpty {
cellView.noteLabel.stringValue = note
} else if DataStore.shared().shouldDisplay(.dstTransitionInfo),
let value = TimezoneDataOperations(with: model).nextDaylightSavingsTransitionIfAvailable(with: futureSliderValue)
{
let value = TimezoneDataOperations(with: model).nextDaylightSavingsTransitionIfAvailable(with: futureSliderValue) {
cellView.noteLabel.stringValue = value
} else {
cellView.noteLabel.stringValue = CLEmptyString
@ -712,37 +683,38 @@ class ParentPanelController: NSWindowController {
mainTableView.reloadData()
}
private func openPreferences() {
@objc private func openPreferencesWindow() {
oneWindow?.showWindow(nil)
NSApp.activate(ignoringOtherApps: true)
}
@IBAction func dismissNextEventLabel(_: NSButton) {
let eventCenter = EventCenter.sharedCenter()
let now = Date()
if let events = eventCenter.eventsForDate[NSCalendar.autoupdatingCurrent.startOfDay(for: now)], events.isEmpty == false {
if let upcomingEvent = eventCenter.nextOccuring(events), let meetingLink = upcomingEvent.meetingURL {
NSWorkspace.shared.open(meetingLink)
}
} else {
removeUpcomingEventView()
}
let eventCenter = EventCenter.sharedCenter()
let now = Date()
if let events = eventCenter.eventsForDate[NSCalendar.autoupdatingCurrent.startOfDay(for: now)], events.isEmpty == false {
if let upcomingEvent = eventCenter.nextOccuring(events), let meetingLink = upcomingEvent.meetingURL {
NSWorkspace.shared.open(meetingLink)
}
} else {
removeUpcomingEventView()
}
}
func removeUpcomingEventView() {
OperationQueue.main.addOperation {
if self.stackView.arrangedSubviews.contains(self.upcomingEventView!), self.upcomingEventView?.isHidden == false {
self.upcomingEventView?.isHidden = true
UserDefaults.standard.set("NO", forKey: CLShowUpcomingEventView)
Logger.log(object: ["Removed": "YES"], for: "Removed Upcoming Event View")
if self.upcomingEventCollectionView != nil {
if self.stackView.arrangedSubviews.contains(self.upcomingEventContainerView!), self.upcomingEventContainerView?.isHidden == false {
self.upcomingEventContainerView?.isHidden = true
UserDefaults.standard.set("NO", forKey: CLShowUpcomingEventView)
Logger.log(object: ["Removed": "YES"], for: "Removed Upcoming Event View")
}
}
}
}
@IBAction func calendarButtonAction(_: NSButton) {
if calendarButton.title == NSLocalizedString("Click here to start.",
comment: "Button Title for no Calendar access")
{
@IBAction func calendarButtonAction(_ sender: NSButton) {
if sender.title == NSLocalizedString("Click here to start.",
comment: "Button Title for no Calendar access") {
showPermissionsWindow()
} else {
retrieveCalendarEvents()
@ -813,8 +785,8 @@ class ParentPanelController: NSWindowController {
func showUpcomingEventView() {
OperationQueue.main.addOperation {
if let upcomingView = self.upcomingEventView, upcomingView.isHidden {
self.upcomingEventView?.isHidden = false
if let upcomingView = self.upcomingEventContainerView, upcomingView.isHidden {
self.upcomingEventContainerView?.isHidden = false
UserDefaults.standard.set("YES", forKey: CLShowUpcomingEventView)
Logger.log(object: ["Shown": "YES"], for: "Added Upcoming Event View")
self.themeChanged()
@ -832,71 +804,29 @@ class ParentPanelController: NSWindowController {
if let events = eventCenter.eventsForDate[NSCalendar.autoupdatingCurrent.startOfDay(for: now)], events.isEmpty == false {
OperationQueue.main.addOperation {
guard let upcomingEvent = eventCenter.nextOccuring(events) else {
self.setPlaceholdersForUpcomingCalendarView()
if #available(OSX 10.14, *) {
PerfLogger.endMarker("Fetch Calendar Events")
}
if self.upcomingEventCollectionView != nil,
let upcomingEvents = eventCenter.upcomingEventsForDay(events) {
self.upcomingEventsDataSource.updateEventsDataSource(upcomingEvents)
self.upcomingEventCollectionView.reloadData()
return
}
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)
if #available(OSX 10.14, *) {
PerfLogger.endMarker("Fetch Calendar Events")
}
return
}
self.setCalendarButtonTitle(buttonTitle: upcomingEvent.metadataForMeeting())
if upcomingEvent.meetingURL != nil {
self.whiteRemoveButton.image = Themer.shared().videoCallImage()
}
if #available(OSX 10.14, *) {
PerfLogger.endMarker("Fetch Calendar Events")
}
}
} else {
setPlaceholdersForUpcomingCalendarView()
if upcomingEventCollectionView != nil {
upcomingEventsDataSource.updateEventsDataSource([])
upcomingEventCollectionView.reloadData()
return
}
if #available(OSX 10.14, *) {
PerfLogger.endMarker("Fetch Calendar Events")
}
}
}
private func setPlaceholdersForUpcomingCalendarView() {
let eventCenter = EventCenter.sharedCenter()
var tomorrow = DateComponents()
tomorrow.day = 1
guard let tomorrowDate = Calendar.autoupdatingCurrent.date(byAdding: tomorrow, to: Date()) else {
setCalendarButtonTitle(buttonTitle: "You have no events scheduled for tomorrow.")
return
}
nextEventLabel.stringValue = NSLocalizedString("No upcoming event.",
comment: "Title when there's no upcoming event")
calendarColorView.layer?.backgroundColor = NSColor(red: 97 / 255.0, green: 194 / 255.0, blue: 80 / 255.0, alpha: 1.0).cgColor
let events = eventCenter.filteredEvents[NSCalendar.autoupdatingCurrent.startOfDay(for: tomorrowDate)]
if let count = events?.count, count > 1 {
let suffix = "events coming up tomorrow."
setCalendarButtonTitle(buttonTitle: "\(count) \(suffix)")
} else if let first = events?.first?.event.title {
setCalendarButtonTitle(buttonTitle: "\(first) coming up.")
} else {
setCalendarButtonTitle(buttonTitle: NSLocalizedString("You have no events scheduled for tomorrow.",
comment: "Title when there's no event scheduled for tomorrow"))
}
}
// If the popover is displayed, close it
// Called when preferences are going to be displayed!
func updatePopoverDisplayState() {
@ -941,10 +871,6 @@ class ParentPanelController: NSWindowController {
} else {
updateReviewView()
ReviewController.prompt()
if let countryCode = Locale.autoupdatingCurrent.regionCode {
Logger.log(object: ["CurrentCountry": countryCode], for: "Remind Later for Feedback")
}
}
}
@ -1046,7 +972,7 @@ class ParentPanelController: NSWindowController {
@objc func openCrowdin() {
guard let localizationURL = URL(string: AboutUsConstants.CrowdInLocalizationLink),
let languageCode = Locale.preferredLanguages.first else { return }
let languageCode = Locale.preferredLanguages.first else { return }
NSWorkspace.shared.open(localizationURL)
@ -1071,6 +997,8 @@ class ParentPanelController: NSWindowController {
action: #selector(reportIssue), keyEquivalent: "")
let localizeClocker = NSMenuItem(title: "Localize Clocker...",
action: #selector(openCrowdin), keyEquivalent: "")
let openPreferences = NSMenuItem(title: "Preferences",
action: #selector(openPreferencesWindow), keyEquivalent: "")
let appDisplayName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") ?? "Clocker"
let shortVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") ?? "N/A"
@ -1079,6 +1007,7 @@ class ParentPanelController: NSWindowController {
let versionInfo = "\(appDisplayName) \(shortVersion) (\(longVersion))"
let clockerVersionInfo = NSMenuItem(title: versionInfo, action: nil, keyEquivalent: "")
clockerVersionInfo.isEnabled = false
menuItem.addItem(openPreferences)
menuItem.addItem(rateClocker)
menuItem.addItem(sendFeedback)
menuItem.addItem(localizeClocker)
@ -1123,14 +1052,13 @@ extension ParentPanelController: NSSharingServicePickerDelegate {
newProposedServices.append(contentsOf: filteredServices)
return newProposedServices
}
private func retrieveAllTimes() -> String {
var clipboardCopy = String()
let timezones = DataStore.shared().timezones()
stride(from: 0, to: timezones.count, by: 1).forEach {
if $0 < mainTableView.numberOfRows,
let cellView = mainTableView.view(atColumn: 0, row: $0, makeIfNecessary: false) as? TimezoneCellView
{
let cellView = mainTableView.view(atColumn: 0, row: $0, makeIfNecessary: false) as? TimezoneCellView {
clipboardCopy.append("\(cellView.customName.stringValue) - \(cellView.time.stringValue)\n")
}
}

4
Clocker/Panel/Rate Controller/ReviewController.swift

@ -42,12 +42,12 @@ final class ReviewController {
// Check if the app has been installed for atleast 7 days
guard let install = storage.object(forKey: Keys.install) as? Date,
install.timeIntervalSinceNow < minInstall
install.timeIntervalSinceNow < minInstall
else { return false }
// If we have never been prompted before, go ahead and prompt
guard let lastPrompt = storage.object(forKey: Keys.lastPrompt) as? Date,
let lastVersion = storage.object(forKey: Keys.lastVersion) as? String
let lastVersion = storage.object(forKey: Keys.lastVersion) as? String
else { return true }
// Minimum interval between two versions should be 45

4
Clocker/Panel/Rate Controller/UpcomingEventView.swift

@ -68,10 +68,6 @@ class ThinScroller: NSScroller {
return 15
}
override class func awakeFromNib() {
super.awakeFromNib()
}
override func drawKnobSlot(in _: NSRect, highlight _: Bool) {
// Leaving this empty to prevent background drawing
}

145
Clocker/Panel/UI/FloatingWindow.xib

@ -3,19 +3,17 @@
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="FloatingWindowController" customModule="Clocker" customModuleProvider="target">
<connections>
<outlet property="calendarButton" destination="8q4-YZ-gkb" id="jJa-d9-fdQ"/>
<outlet property="calendarColorView" destination="BU6-aE-Les" id="NRS-pe-Oxq"/>
<outlet property="futureSlider" destination="XQc-HF-TrM" id="r17-F5-qEj"/>
<outlet property="futureSliderView" destination="6vv-g2-Z63" id="7ld-Or-ilq"/>
<outlet property="leftButton" destination="7ID-ll-0YN" id="Msx-3N-nDQ"/>
<outlet property="leftField" destination="DHg-ca-UOi" id="eOW-Ml-DE8"/>
<outlet property="mainTableView" destination="3js-Fl-DdU" id="6Lu-oW-cuZ"/>
<outlet property="nextEventLabel" destination="A9a-aM-BNn" id="d2X-dP-Xbi"/>
<outlet property="pinButton" destination="KWj-8n-2K0" id="FUW-0f-glB"/>
<outlet property="preferencesButton" destination="iFk-kj-d5a" id="1bK-Ld-MwV"/>
<outlet property="reviewView" destination="HBv-Eh-z9y" id="vt7-in-yQF"/>
@ -26,8 +24,8 @@
<outlet property="shutdownButton" destination="M9q-iF-MaE" id="43B-sx-8Oh"/>
<outlet property="sliderDatePicker" destination="Ngk-LZ-J2y" id="kfm-g4-diy"/>
<outlet property="stackView" destination="Bn4-fy-Lm6" id="bdu-HH-FT3"/>
<outlet property="upcomingEventView" destination="Z63-pM-8OA" id="8mP-u3-cFx"/>
<outlet property="whiteRemoveButton" destination="0xk-Sz-MQU" id="NVY-9C-W2H"/>
<outlet property="upcomingEventCollectionView" destination="1tF-ue-rfI" id="nkq-Ov-eUj"/>
<outlet property="upcomingEventContainerView" destination="7cF-j3-JRf" id="9M5-NS-aB6"/>
<outlet property="window" destination="1gY-Kq-Pzj" id="aEC-1h-f24"/>
</connections>
</customObject>
@ -46,22 +44,22 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView focusRingType="none" distribution="fill" orientation="vertical" alignment="leading" spacing="0.0" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Bn4-fy-Lm6">
<rect key="frame" x="0.0" y="0.0" width="379" height="460"/>
<rect key="frame" x="0.0" y="0.0" width="429" height="460"/>
<subviews>
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="81" horizontalPageScroll="10" verticalLineScroll="81" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="khu-OS-PzP">
<rect key="frame" x="9" y="195" width="370" height="265"/>
<rect key="frame" x="0.0" y="195" width="429" height="265"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="2rS-3B-A3C">
<rect key="frame" x="0.0" y="0.0" width="370" height="265"/>
<rect key="frame" x="0.0" y="0.0" width="429" height="265"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="80" viewBased="YES" id="3js-Fl-DdU" customClass="PanelTableView" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="370" height="265"/>
<rect key="frame" x="0.0" y="0.0" width="429" height="265"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="1"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="338" minWidth="303" maxWidth="1000" id="009-6N-KRz">
<tableColumn width="337.80335195531063" minWidth="303" maxWidth="1000" id="009-6N-KRz">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
@ -294,7 +292,7 @@
</constraints>
</customView>
<customView identifier="Review Cell" translatesAutoresizingMaskIntoConstraints="NO" id="HBv-Eh-z9y">
<rect key="frame" x="0.0" y="115" width="379" height="50"/>
<rect key="frame" x="0.0" y="115" width="429" height="50"/>
<subviews>
<stackView distribution="fill" orientation="horizontal" alignment="centerY" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" fixedFrame="YES" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Y83-Ew-CDw">
<rect key="frame" x="80" y="30" width="185" height="20"/>
@ -362,78 +360,43 @@
</constraints>
<accessibility description="Review Cell"/>
</customView>
<customView wantsLayer="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Z63-pM-8OA" customClass="UpcomingEventView" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="0.0" y="40" width="379" height="75"/>
<customView wantsLayer="YES" focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="7cF-j3-JRf" userLabel="Upcoming Event View">
<rect key="frame" x="0.0" y="40" width="370" height="75"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="PpA-GH-1PV" customClass="ClockerTextBackgroundView" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="10" y="10" width="359" height="55"/>
<subviews>
<button focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8q4-YZ-gkb">
<rect key="frame" x="21" y="11" width="298" height="18"/>
<constraints>
<constraint firstAttribute="height" constant="18" id="IIB-zJ-L1u"/>
</constraints>
<buttonCell key="cell" type="bevel" title="All Day Event" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingHead" truncatesLastVisibleLine="YES" state="on" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="URj-5B-okQ">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="12" name="Avenir-Heavy"/>
</buttonCell>
<connections>
<action selector="calendarButtonAction:" target="-2" id="9kM-QX-h4O"/>
</connections>
</button>
<customView wantsLayer="YES" focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="BU6-aE-Les">
<rect key="frame" x="10" y="11" width="3" height="34"/>
<constraints>
<constraint firstAttribute="height" constant="34" id="G0S-Au-0t0"/>
<constraint firstAttribute="width" constant="3" id="o03-al-nbF"/>
</constraints>
</customView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="A9a-aM-BNn">
<rect key="frame" x="21" y="26" width="280" height="24"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="Ted-52-5mp"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="left" title="Nandita Jaiswal's 35th birthday" placeholderString="" id="nBm-hz-L5t">
<font key="font" size="14" name="Avenir-Light"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" red="1" green="0.5" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" tag="55" translatesAutoresizingMaskIntoConstraints="NO" id="0xk-Sz-MQU">
<rect key="frame" x="319" y="10" width="35" height="35"/>
<constraints>
<constraint firstAttribute="width" constant="35" id="n4G-W9-0Xp"/>
<constraint firstAttribute="height" constant="35" id="viN-NC-XR0"/>
</constraints>
<buttonCell key="cell" type="bevel" bezelStyle="rounded" image="Remove" imagePosition="only" alignment="center" imageScaling="proportionallyDown" inset="2" id="3iw-XB-l87">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="dismissNextEventLabel:" target="-2" id="Gwd-aN-9CO"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="BU6-aE-Les" firstAttribute="centerY" secondItem="PpA-GH-1PV" secondAttribute="centerY" id="09c-hS-Kdy"/>
<constraint firstItem="0xk-Sz-MQU" firstAttribute="leading" secondItem="A9a-aM-BNn" secondAttribute="trailing" constant="20" id="1KT-TD-KeH"/>
<constraint firstItem="8q4-YZ-gkb" firstAttribute="leading" secondItem="A9a-aM-BNn" secondAttribute="leading" constant="-2" id="4lf-X6-VyZ"/>
<constraint firstItem="A9a-aM-BNn" firstAttribute="leading" secondItem="BU6-aE-Les" secondAttribute="trailing" constant="10" id="4mn-mF-aDr"/>
<constraint firstItem="A9a-aM-BNn" firstAttribute="centerY" secondItem="PpA-GH-1PV" secondAttribute="centerY" constant="-10" id="96S-1w-1cF"/>
<constraint firstItem="8q4-YZ-gkb" firstAttribute="bottom" secondItem="BU6-aE-Les" secondAttribute="bottom" id="JBT-HC-8XD"/>
<constraint firstItem="0xk-Sz-MQU" firstAttribute="centerY" secondItem="PpA-GH-1PV" secondAttribute="centerY" id="U5W-rp-ddf"/>
<constraint firstItem="0xk-Sz-MQU" firstAttribute="leading" secondItem="8q4-YZ-gkb" secondAttribute="trailing" id="csx-qD-s3k"/>
<constraint firstItem="BU6-aE-Les" firstAttribute="leading" secondItem="PpA-GH-1PV" secondAttribute="leading" constant="10" id="u6L-TR-aGX"/>
<constraint firstAttribute="trailing" secondItem="0xk-Sz-MQU" secondAttribute="trailing" constant="5" id="w17-Hh-USc"/>
</constraints>
</customView>
<scrollView wantsLayer="YES" focusRingType="none" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7rh-Lu-bXY">
<rect key="frame" x="0.0" y="0.0" width="370" height="75"/>
<clipView key="contentView" drawsBackground="NO" id="03w-56-nAh">
<rect key="frame" x="0.0" y="0.0" width="370" height="75"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView id="1tF-ue-rfI">
<rect key="frame" x="0.0" y="0.0" width="370" height="75"/>
<autoresizingMask key="autoresizingMask" heightSizable="YES"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumInteritemSpacing="10" minimumLineSpacing="10" scrollDirection="horizontal" id="8dR-oy-HpS">
<size key="itemSize" width="50" height="50"/>
<edgeInsets key="sectionInset" left="10" right="5" top="0.0" bottom="0.0"/>
</collectionViewFlowLayout>
<color key="primaryBackgroundColor" red="0.070588235289999995" green="0.95686274510000002" blue="0.10980392160000001" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</collectionView>
</subviews>
<color key="backgroundColor" name="Accent Color"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="MFX-8H-T4e">
<rect key="frame" x="-100" y="-100" width="370" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="Tcm-wM-yRg">
<rect key="frame" x="-100" y="-100" width="16" height="75"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
<constraints>
<constraint firstAttribute="height" constant="75" id="2wB-jF-5u4"/>
<constraint firstItem="PpA-GH-1PV" firstAttribute="top" secondItem="Z63-pM-8OA" secondAttribute="top" constant="10" id="bpv-rJ-2r6"/>
<constraint firstItem="PpA-GH-1PV" firstAttribute="leading" secondItem="Z63-pM-8OA" secondAttribute="leading" constant="10" id="e16-5F-0oB"/>
<constraint firstAttribute="trailing" secondItem="PpA-GH-1PV" secondAttribute="trailing" constant="10" id="nHH-4s-dG2"/>
<constraint firstAttribute="bottom" secondItem="PpA-GH-1PV" secondAttribute="bottom" constant="10" id="tHy-Ku-DWJ"/>
<constraint firstAttribute="height" constant="75" id="5Gb-Xg-seV"/>
<constraint firstItem="7rh-Lu-bXY" firstAttribute="top" secondItem="7cF-j3-JRf" secondAttribute="top" id="Ibp-cf-eH1"/>
<constraint firstAttribute="trailing" secondItem="7rh-Lu-bXY" secondAttribute="trailing" id="cHh-NY-5xT"/>
<constraint firstAttribute="bottom" secondItem="7rh-Lu-bXY" secondAttribute="bottom" id="erG-3T-NxN"/>
<constraint firstItem="7rh-Lu-bXY" firstAttribute="leading" secondItem="7cF-j3-JRf" secondAttribute="leading" id="fPm-mQ-0kM"/>
</constraints>
<accessibility identifier="UpcomingEventView"/>
</customView>
@ -441,7 +404,7 @@
<rect key="frame" x="0.0" y="0.0" width="350" height="40"/>
<subviews>
<button toolTip="Close Clocker" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="M9q-iF-MaE">
<rect key="frame" x="309" y="7" width="30" height="26"/>
<rect key="frame" x="300" y="7" width="30" height="26"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="Aj8-oI-SG6"/>
<constraint firstAttribute="width" constant="30" id="n8F-JR-9KP"/>
@ -456,7 +419,7 @@
</connections>
</button>
<button toolTip="Switch between Menubar/Floating mode." verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KWj-8n-2K0">
<rect key="frame" x="278" y="10" width="29" height="19"/>
<rect key="frame" x="219" y="10" width="29" height="19"/>
<constraints>
<constraint firstAttribute="height" constant="18" id="8yd-3c-2Nc"/>
<constraint firstAttribute="width" constant="29" id="D7P-eC-faV"/>
@ -471,7 +434,7 @@
</connections>
</button>
<button toolTip="Share Clocker!" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="GMo-5B-bnh">
<rect key="frame" x="248" y="7" width="30" height="26"/>
<rect key="frame" x="119" y="7" width="30" height="26"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="WBC-l8-Gc1"/>
<constraint firstAttribute="width" constant="30" id="h85-UM-Kfy"/>
@ -486,7 +449,7 @@
</connections>
</button>
<button toolTip="Open Preferences" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iFk-kj-d5a">
<rect key="frame" x="8" y="10" width="29" height="19"/>
<rect key="frame" x="20" y="10" width="29" height="19"/>
<constraints>
<constraint firstAttribute="width" constant="29" id="Rbf-3O-59g"/>
<constraint firstAttribute="height" constant="18" id="eTh-dT-52B"/>
@ -504,15 +467,15 @@
</button>
</subviews>
<constraints>
<constraint firstItem="iFk-kj-d5a" firstAttribute="leading" secondItem="YQL-kL-NAx" secondAttribute="leading" constant="8" id="NW8-KV-vPc"/>
<constraint firstItem="KWj-8n-2K0" firstAttribute="leading" secondItem="GMo-5B-bnh" secondAttribute="trailing" constant="70" id="0wr-fp-XKy"/>
<constraint firstItem="GMo-5B-bnh" firstAttribute="leading" secondItem="iFk-kj-d5a" secondAttribute="trailing" constant="70" id="6Kp-gC-dhg"/>
<constraint firstItem="iFk-kj-d5a" firstAttribute="leading" secondItem="YQL-kL-NAx" secondAttribute="leading" constant="20" id="NW8-KV-vPc"/>
<constraint firstItem="GMo-5B-bnh" firstAttribute="centerY" secondItem="YQL-kL-NAx" secondAttribute="centerY" id="Pfa-z9-wko"/>
<constraint firstItem="KWj-8n-2K0" firstAttribute="centerY" secondItem="YQL-kL-NAx" secondAttribute="centerY" id="iVM-fu-Mx5"/>
<constraint firstItem="M9q-iF-MaE" firstAttribute="leading" secondItem="KWj-8n-2K0" secondAttribute="trailing" constant="2" id="inS-B0-hUa"/>
<constraint firstItem="M9q-iF-MaE" firstAttribute="centerY" secondItem="YQL-kL-NAx" secondAttribute="centerY" id="qhP-cY-vh9"/>
<constraint firstAttribute="height" constant="40" id="qwB-I2-tKo"/>
<constraint firstAttribute="trailing" secondItem="M9q-iF-MaE" secondAttribute="trailing" constant="11" id="rOf-Hs-lyB"/>
<constraint firstAttribute="trailing" secondItem="M9q-iF-MaE" secondAttribute="trailing" constant="20" id="rOf-Hs-lyB"/>
<constraint firstItem="iFk-kj-d5a" firstAttribute="centerY" secondItem="YQL-kL-NAx" secondAttribute="centerY" id="uOd-vQ-asj"/>
<constraint firstItem="KWj-8n-2K0" firstAttribute="leading" secondItem="GMo-5B-bnh" secondAttribute="trailing" id="x4c-WW-tGu"/>
</constraints>
</customView>
</subviews>
@ -520,8 +483,6 @@
<constraint firstItem="HBv-Eh-z9y" firstAttribute="leading" secondItem="Bn4-fy-Lm6" secondAttribute="leading" id="1gK-nB-cgS"/>
<constraint firstAttribute="trailing" secondItem="khu-OS-PzP" secondAttribute="trailing" id="AgT-oA-uZ1"/>
<constraint firstItem="khu-OS-PzP" firstAttribute="top" secondItem="Bn4-fy-Lm6" secondAttribute="top" id="AxZ-D1-B8f"/>
<constraint firstAttribute="trailing" secondItem="Z63-pM-8OA" secondAttribute="trailing" id="IBs-CV-u71"/>
<constraint firstItem="Z63-pM-8OA" firstAttribute="leading" secondItem="Bn4-fy-Lm6" secondAttribute="leading" id="ev6-Wj-PFx"/>
<constraint firstAttribute="trailing" secondItem="HBv-Eh-z9y" secondAttribute="trailing" id="i0H-Z4-5gF"/>
</constraints>
<visibilityPriorities>
@ -562,7 +523,9 @@
<image name="Float" width="15" height="13"/>
<image name="NSShareTemplate" width="15" height="17"/>
<image name="PowerIcon" width="350" height="350"/>
<image name="Remove" width="700" height="700"/>
<image name="Sunrise" width="700" height="700"/>
<namedColor name="Accent Color">
<color red="0.092000000178813934" green="0.0" blue="0.99900001287460327" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

6
Clocker/Panel/UI/TimezoneCellView.swift

@ -39,7 +39,7 @@ class TimezoneCellView: NSTableCellView {
func setupLayout() {
guard let relativeFont = relativeDate.font,
let sunriseFont = sunriseSetTime.font
let sunriseFont = sunriseSetTime.font
else {
assertionFailure("Unable to convert to NSString")
return
@ -128,7 +128,7 @@ class TimezoneCellView: NSTableCellView {
}
guard let customFont = customName.font,
let timeFont = time.font
let timeFont = time.font
else {
assertionFailure("User Font Size is in unexpectedly nil")
return
@ -165,7 +165,7 @@ class TimezoneCellView: NSTableCellView {
}
guard let panelTableView = searchView as? PanelTableView,
let enclosingScroller = panelTableView.enclosingScrollView
let enclosingScroller = panelTableView.enclosingScrollView
else {
// We might be coming from the preview tableview!
return

2
Clocker/Panel/UI/TimezoneDataSource.swift

@ -144,7 +144,7 @@ extension TimezoneDataSource: NSTableViewDataSource, NSTableViewDelegate {
panelController.deleteTimezone(at: row)
}
})
})
if #available(OSX 11.0, *) {
swipeToDelete.image = Themer.shared().filledTrashImage()

38
Clocker/Panel/Upcoming Events/ParentPanelController+UpcomingEvents.swift

@ -0,0 +1,38 @@
// Copyright © 2015 Abhishek Banthia
import Foundation
var avenirBookFont: NSFont {
if let avenirFont = NSFont(name: "Avenir-Book", size: 13) {
return avenirFont
}
return NSFont.systemFont(ofSize: 13)
}
protocol UpcomingEventPanelDelegate: AnyObject {
func didRemoveCalendarView()
func didClickSupplementaryButton(_ sender: NSButton)
}
extension ParentPanelController {
func setupUpcomingEventViewCollectionViewIfNeccesary() {
if upcomingEventCollectionView != nil {
upcomingEventsDataSource = UpcomingEventsDataSource(self, EventCenter.sharedCenter())
upcomingEventCollectionView.enclosingScrollView?.scrollerInsets = NSEdgeInsetsZero
upcomingEventCollectionView.enclosingScrollView?.backgroundColor = NSColor.clear
upcomingEventCollectionView.setAccessibility("UpcomingEventCollectionView")
upcomingEventCollectionView.dataSource = upcomingEventsDataSource
upcomingEventCollectionView.delegate = upcomingEventsDataSource
}
}
}
extension ParentPanelController: UpcomingEventPanelDelegate {
func didRemoveCalendarView() {
removeUpcomingEventView()
}
func didClickSupplementaryButton(_ sender: NSButton) {
calendarButtonAction(sender)
}
}

131
Clocker/Panel/Upcoming Events/UpcomingEventViewItem.swift

@ -0,0 +1,131 @@
// Copyright © 2015 Abhishek Banthia
import Foundation
class UpcomingEventViewItem: NSCollectionViewItem {
static let reuseIdentifier = NSUserInterfaceItemIdentifier("UpcomingEventViewItem")
@IBOutlet var calendarColorView: NSView!
@IBOutlet var leadingConstraint: NSLayoutConstraint!
@IBOutlet var eventTitleLabel: NSTextField!
@IBOutlet var eventSubtitleButton: NSButton!
@IBOutlet var supplementaryButtonWidthConstraint: NSLayoutConstraint!
@IBOutlet var zoomButton: NSButton!
private static let SupplementaryButtonWidth: CGFloat = 24.0
private static let EventLeadingConstraint: CGFloat = 10.0
private var meetingLink: URL?
private weak var panelDelegate: UpcomingEventPanelDelegate?
override func viewDidLoad() {
zoomButton.target = self
zoomButton.action = #selector(zoomButtonAction(_:))
}
override func prepareForReuse() {
zoomButton.image = nil
eventTitleLabel.stringValue = ""
eventSubtitleButton.stringValue = ""
}
override var acceptsFirstResponder: Bool {
return false
}
// MARK: Setup UI
func setup(_ title: String,
_ subtitle: String,
_ color: NSColor,
_ link: URL?,
_ delegate: UpcomingEventPanelDelegate?,
_ isCancelled: Bool) {
if leadingConstraint.constant != UpcomingEventViewItem.EventLeadingConstraint / 2 {
leadingConstraint.animator().constant = UpcomingEventViewItem.EventLeadingConstraint / 2
}
panelDelegate = delegate
meetingLink = link
setupLabels(title, isCancelled)
setupSupplementaryButton(link)
setCalendarButtonTitle(buttonTitle: subtitle)
calendarColorView.layer?.backgroundColor = color.cgColor
}
private func setupLabels(_ title: String, _ cancellationState: Bool) {
let attributes: [NSAttributedString.Key: Any] = cancellationState ? [NSAttributedString.Key.strikethroughStyle: NSUnderlineStyle.single.rawValue,
NSAttributedString.Key.strikethroughColor: NSColor.gray] : [:]
let attributedString = NSAttributedString(string: title, attributes: attributes)
eventTitleLabel.attributedStringValue = attributedString
eventTitleLabel.toolTip = title
}
private func setupSupplementaryButton(_ meetingURL: URL?) {
guard meetingURL != nil else {
zoomButton.image = nil
supplementaryButtonWidthConstraint.constant = 0.0
return
}
zoomButton.isHidden = false
zoomButton.image = Themer.shared().videoCallImage()
if supplementaryButtonWidthConstraint.constant != UpcomingEventViewItem.SupplementaryButtonWidth {
supplementaryButtonWidthConstraint.constant = UpcomingEventViewItem.SupplementaryButtonWidth
}
}
func setupUndeterminedState(_ delegate: UpcomingEventPanelDelegate?) {
panelDelegate = delegate
setAlternateState(NSLocalizedString("See your next Calendar event here.", comment: "Next Event Label for no Calendar access"),
NSLocalizedString("Click here to start.", comment: "Button Title for no Calendar access"),
NSColor.systemBlue,
Themer.shared().removeImage())
}
func setupEmptyState() {
setAlternateState(NSLocalizedString("No upcoming events for today!", comment: "Next Event Label with no upcoming event"),
NSLocalizedString("Great going.", comment: "Button Title for no upcoming event"),
NSColor.systemGreen,
nil)
}
private func setAlternateState(_ title: String, _ buttonTitle: String, _ color: NSColor, _ image: NSImage? = nil) {
if leadingConstraint.constant != UpcomingEventViewItem.EventLeadingConstraint {
leadingConstraint.animator().constant = UpcomingEventViewItem.EventLeadingConstraint
}
eventTitleLabel.stringValue = title
setCalendarButtonTitle(buttonTitle: buttonTitle)
calendarColorView.layer?.backgroundColor = color.cgColor
zoomButton.image = image
}
private func setCalendarButtonTitle(buttonTitle: String) {
let style = NSMutableParagraphStyle()
style.alignment = .left
style.lineBreakMode = .byTruncatingTail
if let boldFont = NSFont(name: "Avenir", size: 11) {
let attributes = [NSAttributedString.Key.foregroundColor: NSColor.gray, NSAttributedString.Key.paragraphStyle: style, NSAttributedString.Key.font: boldFont]
let attributedString = NSAttributedString(string: buttonTitle, attributes: attributes)
eventSubtitleButton.attributedTitle = attributedString
eventSubtitleButton.toolTip = attributedString.string
}
}
// MARK: Button Actions
@IBAction func calendarButtonAction(_ sender: NSButton) {
panelDelegate?.didClickSupplementaryButton(sender)
}
@objc func zoomButtonAction(_: Any) {
if let meetingURL = meetingLink {
NSWorkspace.shared.open(meetingURL)
} else {
panelDelegate?.didRemoveCalendarView()
}
}
}

99
Clocker/Panel/Upcoming Events/UpcomingEventViewItem.xib

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner"/>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Hz6-mo-xeY">
<rect key="frame" x="0.0" y="0.0" width="250" height="75"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="aM6-TI-1os" customClass="ClockerTextBackgroundView" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="250" height="75"/>
<subviews>
<button focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Khp-8n-4FA">
<rect key="frame" x="16" y="21" width="198" height="18"/>
<constraints>
<constraint firstAttribute="height" constant="18" id="asu-Dd-XhW"/>
</constraints>
<buttonCell key="cell" type="bevel" title="All Day Event" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingHead" truncatesLastVisibleLine="YES" state="on" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="JOt-0Q-EAr">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="10" name="Avenir-Heavy"/>
</buttonCell>
<connections>
<action selector="calendarButtonAction:" target="p0M-E4-898" id="Gua-lA-sNh"/>
</connections>
</button>
<customView wantsLayer="YES" focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="Bbu-oG-vAb">
<rect key="frame" x="5" y="21" width="2" height="34"/>
<constraints>
<constraint firstAttribute="width" constant="2" id="Qhx-45-1Hc"/>
<constraint firstAttribute="height" constant="34" id="xeh-m2-Fhh"/>
</constraints>
</customView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fTt-gC-72b">
<rect key="frame" x="15" y="33" width="201" height="24"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="BA4-1v-xiZ"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="left" title="Nandita Jaiswal's 35th birthday" placeholderString="" id="Dtv-o3-gqd">
<font key="font" size="12" name="Avenir-Book"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" red="1" green="0.5" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
</textFieldCell>
</textField>
<button translatesAutoresizingMaskIntoConstraints="NO" id="qRe-Ao-1fr">
<rect key="frame" x="216" y="26" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="ZRs-Vt-FOM"/>
<constraint firstAttribute="height" constant="24" id="ouc-t4-DO2"/>
</constraints>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="Appearance" imagePosition="only" alignment="center" imageScaling="proportionallyUpOrDown" inset="2" id="8Sv-kp-Zff">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
</subviews>
<constraints>
<constraint firstItem="qRe-Ao-1fr" firstAttribute="leading" secondItem="fTt-gC-72b" secondAttribute="trailing" constant="2" id="161-lb-rqc"/>
<constraint firstItem="fTt-gC-72b" firstAttribute="top" secondItem="Bbu-oG-vAb" secondAttribute="top" constant="-2" id="FeN-R0-t1A"/>
<constraint firstItem="Khp-8n-4FA" firstAttribute="bottom" secondItem="Bbu-oG-vAb" secondAttribute="bottom" id="LQD-54-bUh"/>
<constraint firstItem="Bbu-oG-vAb" firstAttribute="leading" secondItem="aM6-TI-1os" secondAttribute="leading" constant="5" id="Myg-8W-znN"/>
<constraint firstAttribute="trailing" secondItem="qRe-Ao-1fr" secondAttribute="trailing" constant="10" id="Myk-Ff-8WP"/>
<constraint firstItem="Khp-8n-4FA" firstAttribute="trailing" secondItem="fTt-gC-72b" secondAttribute="trailing" id="URW-gt-WbF"/>
<constraint firstItem="Khp-8n-4FA" firstAttribute="leading" secondItem="fTt-gC-72b" secondAttribute="leading" constant="-1" id="VyL-Kt-2aT"/>
<constraint firstItem="fTt-gC-72b" firstAttribute="leading" secondItem="Bbu-oG-vAb" secondAttribute="trailing" constant="10" id="arv-Oe-yih"/>
<constraint firstItem="qRe-Ao-1fr" firstAttribute="centerY" secondItem="aM6-TI-1os" secondAttribute="centerY" id="vdC-gb-1wa"/>
<constraint firstItem="Bbu-oG-vAb" firstAttribute="centerY" secondItem="aM6-TI-1os" secondAttribute="centerY" id="wkl-Vo-0kU"/>
</constraints>
</customView>
</subviews>
<constraints>
<constraint firstItem="aM6-TI-1os" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" id="2AJ-xz-nf9"/>
<constraint firstItem="aM6-TI-1os" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" id="D6T-PQ-RdE"/>
<constraint firstAttribute="bottom" secondItem="aM6-TI-1os" secondAttribute="bottom" id="DmC-7T-lrT"/>
<constraint firstAttribute="trailing" secondItem="aM6-TI-1os" secondAttribute="trailing" id="aCZ-sV-Mnd"/>
</constraints>
<point key="canvasLocation" x="147" y="238.5"/>
</customView>
<customObject id="p0M-E4-898" customClass="UpcomingEventViewItem" customModule="Clocker" customModuleProvider="target">
<connections>
<outlet property="calendarColorView" destination="Bbu-oG-vAb" id="tBe-V1-qmC"/>
<outlet property="eventSubtitleButton" destination="Khp-8n-4FA" id="mXV-EZ-2AW"/>
<outlet property="eventTitleLabel" destination="fTt-gC-72b" id="bOh-LO-tiH"/>
<outlet property="leadingConstraint" destination="Myg-8W-znN" id="OUR-Na-THS"/>
<outlet property="supplementaryButtonWidthConstraint" destination="ZRs-Vt-FOM" id="S8K-B3-ZLb"/>
<outlet property="view" destination="Hz6-mo-xeY" id="9S0-wg-csq"/>
<outlet property="zoomButton" destination="qRe-Ao-1fr" id="0IC-Fx-l3Q"/>
</connections>
</customObject>
</objects>
<resources>
<image name="Appearance" width="350" height="350"/>
</resources>
</document>

64
Clocker/Panel/Upcoming Events/UpcomingEventsDataSource.swift

@ -0,0 +1,64 @@
// Copyright © 2015 Abhishek Banthia
import Foundation
class UpcomingEventsDataSource: NSObject, NSCollectionViewDataSource, NSCollectionViewDelegateFlowLayout {
private var upcomingEvents: [EventInfo] = []
private var eventCenter: EventCenter!
private weak var delegate: UpcomingEventPanelDelegate?
private static let panelWidth: CGFloat = 350.0
init(_ panelDelegate: UpcomingEventPanelDelegate?, _ center: EventCenter) {
super.init()
delegate = panelDelegate
eventCenter = center
}
func updateEventsDataSource(_ events: [EventInfo]) {
upcomingEvents = events
}
func collectionView(_: NSCollectionView, numberOfItemsInSection _: Int) -> Int {
if eventCenter.calendarAccessDenied() || eventCenter.calendarAccessNotDetermined() || upcomingEvents.isEmpty {
return 1
}
return upcomingEvents.count
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: UpcomingEventViewItem.reuseIdentifier, for: indexPath) as! UpcomingEventViewItem
if eventCenter.calendarAccessNotDetermined() {
item.setupUndeterminedState(delegate)
return item
}
if upcomingEvents.isEmpty {
item.setupEmptyState()
return item
}
let currentEventInfo = upcomingEvents[indexPath.item]
let upcomingEventSubtitle = currentEventInfo.isAllDay ? "All-Day" : currentEventInfo.metadataForMeeting()
item.setup(currentEventInfo.event.title,
upcomingEventSubtitle,
currentEventInfo.event.calendar.color,
currentEventInfo.meetingURL,
delegate,
currentEventInfo.event.status == .canceled)
return item
}
func collectionView(_ collectionView: NSCollectionView, layout _: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
if eventCenter.calendarAccessNotDetermined() {
return NSSize(width: UpcomingEventsDataSource.panelWidth - 25, height: collectionView.frame.height - 15)
} else if upcomingEvents.isEmpty {
return NSSize(width: UpcomingEventsDataSource.panelWidth - 25, height: collectionView.frame.height - 15)
} else {
let currentEventInfo = upcomingEvents[indexPath.item]
let bufferWidth: CGFloat = currentEventInfo.meetingURL != nil ? 60.0 : 20.0
let attributedString = NSAttributedString(string: currentEventInfo.event.title, attributes: [NSAttributedString.Key.font: avenirBookFont])
let maxWidth = min(attributedString.size().width + bufferWidth, UpcomingEventsDataSource.panelWidth / 2)
return NSSize(width: maxWidth, height: collectionView.frame.height - 20)
}
}
}

6
Clocker/Preferences/About/AboutViewController.swift

@ -98,7 +98,7 @@ class AboutViewController: ParentViewController {
@IBAction func openMyTwitter(_: Any) {
guard let twitterURL = URL(string: AboutUsConstants.TwitterLink),
let countryCode = Locale.autoupdatingCurrent.regionCode else { return }
let countryCode = Locale.autoupdatingCurrent.regionCode else { return }
NSWorkspace.shared.open(twitterURL)
@ -109,7 +109,7 @@ class AboutViewController: ParentViewController {
@IBAction func viewSource(_: Any) {
guard let sourceURL = URL(string: AboutUsConstants.AppStoreLink),
let countryCode = Locale.autoupdatingCurrent.regionCode else { return }
let countryCode = Locale.autoupdatingCurrent.regionCode else { return }
NSWorkspace.shared.open(sourceURL)
@ -131,7 +131,7 @@ class AboutViewController: ParentViewController {
@IBAction func openGitHub(_: Any) {
guard let localizationURL = URL(string: AboutUsConstants.CrowdInLocalizationLink),
let languageCode = Locale.preferredLanguages.first else { return }
let languageCode = Locale.preferredLanguages.first else { return }
NSWorkspace.shared.open(localizationURL)

31
Clocker/Preferences/App Feedback/AppFeedbackWindowController.swift

@ -114,7 +114,7 @@ class AppFeedbackWindowController: NSWindowController {
let feedbackInfo = retrieveDataForSending()
Logger.info("About to send \(feedbackInfo)")
sendDataToFirebase(feedbackInfo: feedbackInfo)
showSucccessOnSendingInfo()
}
@ -127,30 +127,30 @@ class AppFeedbackWindowController: NSWindowController {
let cleanedUpString = feedbackTextView.string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
if cleanedUpString.isEmpty {
self.window?.contentView?.makeToast(AppFeedbackConstants.CLFeedbackNotEnteredErrorMessage)
window?.contentView?.makeToast(AppFeedbackConstants.CLFeedbackNotEnteredErrorMessage)
isActivityInProgress = false
return false
}
return true
}
private func generateUserPreferences() -> String {
let preferences = DataStore.shared().timezones()
guard let theme = DataStore.shared().retrieve(key: CLThemeKey) as? NSNumber,
let displayFutureSliderKey = DataStore.shared().retrieve(key: CLThemeKey) as? NSNumber,
let relativeDateKey = DataStore.shared().retrieve(key: CLRelativeDateKey) as? NSNumber,
let country = Locale.autoupdatingCurrent.regionCode
let displayFutureSliderKey = DataStore.shared().retrieve(key: CLThemeKey) as? NSNumber,
let relativeDateKey = DataStore.shared().retrieve(key: CLRelativeDateKey) as? NSNumber,
let country = Locale.autoupdatingCurrent.regionCode
else {
return "Error"
}
let selectedTimezones = preferences.compactMap { data -> String? in
guard let timezoneObject = TimezoneData.customObject(from: data) else {
return nil
}
return "Timezone: \(timezoneObject.timezone()) Name: \(timezoneObject.formattedAddress ?? "No") Favourited: \((timezoneObject.isFavourite == 1) ? "Yes": "No") Note: \(timezoneObject.note ?? "No Note") System: \(timezoneObject.isSystemTimezone ? "Yes": "No")"
return "Timezone: \(timezoneObject.timezone()) Name: \(timezoneObject.formattedAddress ?? "No") Favourited: \((timezoneObject.isFavourite == 1) ? "Yes" : "No") Note: \(timezoneObject.note ?? "No Note") System: \(timezoneObject.isSystemTimezone ? "Yes" : "No")"
}
var relativeDate = "Relative"
@ -160,30 +160,29 @@ class AppFeedbackWindowController: NSWindowController {
} else if relativeDateKey.isEqual(to: NSNumber(value: 2)) {
relativeDate = "Date"
}
var themeInfo = "Light"
if theme.isEqual(to: NSNumber(value: 1)) {
themeInfo = "Dark"
} else if theme.isEqual(to: NSNumber(value: 2)) {
themeInfo = "System"
}
var futureSlider = "Modern"
if displayFutureSliderKey.isEqual(to: NSNumber(value: 1)) {
futureSlider = "Legacy"
} else if theme.isEqual(to: NSNumber(value: 2)) {
futureSlider = "Hidden"
}
return """
"Theme: \(themeInfo), "Display Future Slider": \(futureSlider), "Relative Date": \(relativeDate), "Country": \(country), "Timezones": \(selectedTimezones)
"""
"Theme: \(themeInfo), "Display Future Slider": \(futureSlider), "Relative Date": \(relativeDate), "Country": \(country), "Timezones": \(selectedTimezones)
"""
}
private func retrieveDataForSending() -> [String: String] {
guard let shortVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String,
let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String
let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String
else {
return [:]
}
@ -286,7 +285,7 @@ class AppFeedbackWindowController: NSWindowController {
@IBAction func navigateToSupportTwitter(_: Any) {
guard let twitterURL = URL(string: AboutUsConstants.TwitterLink),
let countryCode = Locale.autoupdatingCurrent.regionCode else { return }
let countryCode = Locale.autoupdatingCurrent.regionCode else { return }
NSWorkspace.shared.open(twitterURL)

18
Clocker/Preferences/Appearance/AppearanceViewController.swift

@ -62,10 +62,8 @@ class AppearanceViewController: ParentViewController {
"6 days",
"7 days",
])
if #available(macOS 11.0, *) {
} else {
if #available(macOS 11.0, *) {} else {
theme.font = NSFont.systemFont(ofSize: 13)
}
@ -126,8 +124,7 @@ class AppearanceViewController: ParentViewController {
sliderDayRangePopup.selectItem(at: selectedIndex.intValue)
}
if #available(macOS 10.14, *) {
} else {
if #available(macOS 10.14, *) {} else {
theme.removeItem(at: 2)
}
@ -153,7 +150,7 @@ class AppearanceViewController: ParentViewController {
@IBOutlet var menubarModeLabel: NSTextField!
@IBOutlet var previewLabel: NSTextField!
@IBOutlet var miscelleaneousLabel: NSTextField!
@IBOutlet weak var dstTransitionField: NSTextField!
@IBOutlet var dstTransitionField: NSTextField!
// Panel Preview
@IBOutlet var previewPanelTableView: NSTableView!
@ -180,8 +177,8 @@ class AppearanceViewController: ParentViewController {
previewLabel, miscelleaneousLabel, dstTransitionField].forEach {
$0?.textColor = Themer.shared().mainTextColor()
}
self.previewPanelTableView.backgroundColor = Themer.shared().mainBackgroundColor()
previewPanelTableView.backgroundColor = Themer.shared().mainBackgroundColor()
}
@IBAction func timeFormatSelectionChanged(_ sender: NSPopUpButton) {
@ -191,8 +188,7 @@ class AppearanceViewController: ParentViewController {
refresh(panel: true, floating: true)
if let selectedFormat = sender.selectedItem?.title,
selectedFormat.contains("ss")
{
selectedFormat.contains("ss") {
print("Seconds are contained")
guard let panelController = PanelController.panel() else { return }
panelController.pauseTimer()

8
Clocker/Preferences/Calendar/CalendarViewController.swift

@ -215,7 +215,7 @@ class CalendarViewController: ParentViewController {
[headerLabel, upcomingEventView, allDayMeetingsLabel,
showNextMeetingLabel, nextMeetingAccessoryLabel, truncateTextLabel,
showEventsFromLabel, charactersField, truncateAccessoryLabel].forEach { $0?.textColor = Themer.shared().mainTextColor() }
calendarsTableView.backgroundColor = Themer.shared().mainBackgroundColor()
truncateTextField.backgroundColor = Themer.shared().mainBackgroundColor()
}
@ -243,15 +243,13 @@ extension CalendarViewController: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? {
if let currentSource = calendars[row] as? String,
let message = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "sourceCellView"), owner: self) as? SourceTableViewCell
{
let message = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "sourceCellView"), owner: self) as? SourceTableViewCell {
message.sourceName.stringValue = currentSource
return message
}
if let currentSource = calendars[row] as? CalendarInfo,
let calendarCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "calendarCellView"), owner: self) as? CalendarTableViewCell
{
let calendarCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "calendarCellView"), owner: self) as? CalendarTableViewCell {
calendarCell.calendarName.stringValue = currentSource.calendar.title
calendarCell.calendarSelected.state = currentSource.selected ? NSControl.StateValue.on : NSControl.StateValue.off
calendarCell.calendarSelected.target = self

7
Clocker/Preferences/General/PreferencesDataSource.swift

@ -104,8 +104,7 @@ extension PreferencesDataSource: NSTableViewDataSource {
var selectedDataSource: TimezoneData?
if selectedTimezones.count > row,
let model = TimezoneData.customObject(from: selectedTimezones[row])
{
let model = TimezoneData.customObject(from: selectedTimezones[row]) {
selectedDataSource = model
}
@ -164,7 +163,7 @@ extension PreferencesDataSource: NSTableViewDataSource {
"Old Label": dataObject.customLabel ?? "Error",
"New Label": formattedValue,
],
for: "Custom Label Changed")
for: "Custom Label Changed")
dataObject.setLabel(formattedValue)
@ -177,7 +176,7 @@ extension PreferencesDataSource: NSTableViewDataSource {
"Selected Timezone Count": selectedTimezones.count,
"Current Row": row,
],
for: "Error in selected row count")
for: "Error in selected row count")
}
}

21
Clocker/Preferences/General/PreferencesViewController.swift

@ -42,8 +42,8 @@ class PreferencesViewController: ParentViewController {
private var geocodingKey: String = {
guard let path = Bundle.main.path(forResource: "Keys", ofType: "plist"),
let dictionary = NSDictionary(contentsOfFile: path),
let apiKey = dictionary["GeocodingKey"] as? String
let dictionary = NSDictionary(contentsOfFile: path),
let apiKey = dictionary["GeocodingKey"] as? String
else {
// assertionFailure("Unable to find the API key")
return ""
@ -338,10 +338,9 @@ extension PreferencesViewController: NSTableViewDataSource, NSTableViewDelegate
for: "favouriteRemoved")
if let appDelegate = NSApplication.shared.delegate as? AppDelegate,
let menubarFavourites = DataStore.shared().menubarTimezones(),
menubarFavourites.isEmpty,
DataStore.shared().shouldDisplay(.showMeetingInMenubar) == false
{
let menubarFavourites = DataStore.shared().menubarTimezones(),
menubarFavourites.isEmpty,
DataStore.shared().shouldDisplay(.showMeetingInMenubar) == false {
appDelegate.invalidateMenubarTimer(true)
}
@ -459,7 +458,7 @@ extension PreferencesViewController {
self.prepareUIForPresentingResults()
}
})
})
}
}
@ -884,7 +883,7 @@ extension PreferencesViewController {
let system = NSTimeZone.system
guard let object1 = TimezoneData.customObject(from: obj1),
let object2 = TimezoneData.customObject(from: obj2)
let object2 = TimezoneData.customObject(from: obj2)
else {
assertionFailure("Data was unexpectedly nil")
return false
@ -912,7 +911,7 @@ extension PreferencesViewController {
let sortedLabels = selectedTimeZones.sorted { obj1, obj2 -> Bool in
guard let object1 = TimezoneData.customObject(from: obj1),
let object2 = TimezoneData.customObject(from: obj2)
let object2 = TimezoneData.customObject(from: obj2)
else {
assertionFailure("Data was unexpectedly nil")
return false
@ -936,7 +935,7 @@ extension PreferencesViewController {
let sortedByAddress = selectedTimeZones.sorted { obj1, obj2 -> Bool in
guard let object1 = TimezoneData.customObject(from: obj1),
let object2 = TimezoneData.customObject(from: obj2)
let object2 = TimezoneData.customObject(from: obj2)
else {
assertionFailure("Data was unexpectedly nil")
return false
@ -1003,7 +1002,7 @@ extension PreferencesViewController: PreferenceSelectionUpdates {
let sortedTimezones = selectedTimeZones.sorted { obj1, obj2 -> Bool in
guard let object1 = TimezoneData.customObject(from: obj1),
let object2 = TimezoneData.customObject(from: obj2)
let object2 = TimezoneData.customObject(from: obj2)
else {
assertionFailure("Data was unexpectedly nil")
return false

2
Clocker/Preferences/General/SearchDataSource.swift

@ -78,7 +78,7 @@ class SearchDataSource: NSObject {
if index >= filteredArray.count {
return nil
}
return filteredArray[index % filteredArray.count]
}

6
Clocker/Preferences/Menu Bar/StatusContainerView.swift

@ -33,12 +33,10 @@ func compactWidth(for timezone: TimezoneData) -> Int {
if timeFormat == DateFormat.twelveHour
|| timeFormat == DateFormat.twelveHourWithSeconds
|| timeFormat == DateFormat.twelveHourWithZero
|| timeFormat == DateFormat.twelveHourWithSeconds
{
|| timeFormat == DateFormat.twelveHourWithSeconds {
totalWidth += 20
} else if timeFormat == DateFormat.twentyFourHour
|| timeFormat == DateFormat.twentyFourHourWithSeconds
{
|| timeFormat == DateFormat.twentyFourHourWithSeconds {
totalWidth += 0
}

2
Clocker/Preferences/Menu Bar/StatusItemHandler.swift

@ -188,7 +188,7 @@ class StatusItemHandler: NSObject {
if let strongSelf = self {
strongSelf.refresh()
}
})
})
// Tolerance, even a small amount, has a positive imapct on the power usage. As a rule, we set it to 10% of the interval
menubarTimer?.tolerance = shouldDisplaySeconds ? 0.5 : 20

15
Clocker/Preferences/Menu Bar/UpcomingEventStatusItemView.swift

@ -1,11 +1,11 @@
// Copyright © 2015 Abhishek Banthia
import Foundation
import AppKit
import Foundation
class UpcomingEventStatusItemView: NSView {
static let containerWidth = 70
private let nextEventField = NSTextField(labelWithString: "Next Event")
private let etaField = NSTextField(labelWithString: "5 mins")
var dataObject: EventInfo! {
@ -13,6 +13,7 @@ class UpcomingEventStatusItemView: NSView {
initialSetup()
}
}
private var timeAttributes: [NSAttributedString.Key: AnyObject] {
let textColor = hasDarkAppearance ? NSColor.white : NSColor.black
@ -36,7 +37,7 @@ class UpcomingEventStatusItemView: NSView {
]
return textFontAttributes
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
@ -63,16 +64,16 @@ class UpcomingEventStatusItemView: NSView {
etaField.bottomAnchor.constraint(equalTo: bottomAnchor),
])
}
required init?(coder: NSCoder) {
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func initialSetup() {
nextEventField.attributedStringValue = NSAttributedString(string: "Next Event", attributes: textFontAttributes)
etaField.attributedStringValue = NSAttributedString(string: dataObject.metadataForMeeting(), attributes: timeAttributes)
}
func updateWithNextEventInfo(_ metadata: String) {
nextEventField.attributedStringValue = NSAttributedString(string: "Next Event", attributes: textFontAttributes)
etaField.attributedStringValue = NSAttributedString(string: metadata, attributes: timeAttributes)

4
Clocker/Preferences/OneWindowController.swift

@ -84,11 +84,11 @@ class OneWindowController: NSWindowController {
var identifierTOImageMapping: [String: NSImage] = ["Appearance Tab": themer.appearanceTabImage(),
"Calendar Tab": themer.calendarTabImage(),
"Permissions Tab": themer.privacyTabImage()]
if let prefsTabImage = themer.generalTabImage() {
identifierTOImageMapping["Preferences Tab"] = prefsTabImage
}
if let aboutTabImage = themer.aboutTabImage() {
identifierTOImageMapping["About Tab"] = aboutTabImage
}

12
Clocker/Preferences/Preferences.storyboard

@ -1512,7 +1512,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<searchField toolTip="Search a timezone" wantsLayer="YES" focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Dha-h9-Nd0" customClass="ClockerSearchField" customModule="Clocker" customModuleProvider="target">
<rect key="frame" x="8" y="97" width="320" height="23"/>
<rect key="frame" x="8" y="73" width="320" height="23"/>
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" focusRingType="none" placeholderString="Enter a city, state, country name" usesSingleLineMode="YES" maximumRecents="5" id="ikU-Tm-0WZ">
<font key="font" size="13" name="Avenir-Light"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -1569,9 +1569,9 @@ DQ
</connections>
</button>
<scrollView focusRingType="none" borderType="none" autohidesScrollers="YES" horizontalLineScroll="32" horizontalPageScroll="10" verticalLineScroll="32" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0wY-ff-FLW">
<rect key="frame" x="8" y="30" width="320" height="57"/>
<rect key="frame" x="8" y="30" width="320" height="33"/>
<clipView key="contentView" drawsBackground="NO" id="rGc-3M-cCq">
<rect key="frame" x="0.0" y="0.0" width="320" height="57"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="33"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" rowHeight="30" rowSizeStyle="automatic" viewBased="YES" id="xkl-2X-ZCb">
@ -1665,14 +1665,14 @@ DQ
</scroller>
</scrollView>
<progressIndicator wantsLayer="YES" focusRingType="none" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="0A5-gp-lay">
<rect key="frame" x="160" y="78" width="16" height="16"/>
<rect key="frame" x="160" y="66" width="16" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="fgE-77-Vda"/>
<constraint firstAttribute="width" constant="16" id="pwe-em-e0a"/>
</constraints>
</progressIndicator>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xgb-wU-8RU">
<rect key="frame" x="18" y="48" width="300" height="22"/>
<rect key="frame" x="18" y="36" width="300" height="22"/>
<constraints>
<constraint firstAttribute="height" constant="22" id="zqt-d8-yas"/>
</constraints>
@ -1901,6 +1901,7 @@ CA
<accessibility identifier="StartAtLogin"/>
<connections>
<action selector="loginPreferenceChanged:" target="ZT5-cA-xLj" id="ufi-Mp-Peg"/>
<binding destination="3tb-nb-dV1" name="value" keyPath="values.startAtLogin" id="b3h-OK-4dO"/>
</connections>
</button>
<customView focusRingType="none" appearanceType="aqua" translatesAutoresizingMaskIntoConstraints="NO" id="PbD-dt-goz" customClass="SRRecorderControl">
@ -1991,6 +1992,7 @@ CA
<outlet property="timezoneTableView" destination="p9D-mN-dTa" id="YX4-5I-LjL"/>
</connections>
</viewController>
<userDefaultsController id="3tb-nb-dV1"/>
</objects>
<point key="canvasLocation" x="747" y="1244"/>
</scene>

Loading…
Cancel
Save