diff --git a/Clocker/Clocker.xcodeproj/project.pbxproj b/Clocker/Clocker.xcodeproj/project.pbxproj index 2541f1e..d99160f 100755 --- a/Clocker/Clocker.xcodeproj/project.pbxproj +++ b/Clocker/Clocker.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ 3531F7C426936C8300DF0111 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3531F7C026936C6E00DF0111 /* GoogleService-Info.plist */; }; 3531F7C526936C8400DF0111 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3531F7C026936C6E00DF0111 /* GoogleService-Info.plist */; }; 3531F7F52693882300DF0111 /* Keys.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3531F7F42693882300DF0111 /* Keys.plist */; }; - 3531F7F72693882300DF0111 /* Keys.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3531F7F42693882300DF0111 /* Keys.plist */; }; 3531F80626938D7700DF0111 /* GoogleUtilities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3531F7FC26938D7600DF0111 /* GoogleUtilities.framework */; }; 3531F80726938D7700DF0111 /* GoogleUtilities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3531F7FC26938D7600DF0111 /* GoogleUtilities.framework */; }; 3531F80826938D7700DF0111 /* GoogleUtilities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3531F7FC26938D7600DF0111 /* GoogleUtilities.framework */; }; @@ -47,6 +46,7 @@ 3531F82126938D7700DF0111 /* GoogleDataTransport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3531F80526938D7700DF0111 /* GoogleDataTransport.framework */; }; 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 */; }; 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 */; }; @@ -158,8 +158,6 @@ 9AB6F1642259D1B900A44663 /* ParentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB6F1632259D1B800A44663 /* ParentViewController.swift */; }; 9AB6F1672259D23200A44663 /* PermissionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AB6F1662259D23200A44663 /* PermissionsViewController.swift */; }; 9AB89E031CE97A4900EC8EB1 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9AB89E021CE97A4900EC8EB1 /* Media.xcassets */; }; - 9ABF454E268FCF10002C779B /* Keys.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9ABF454D268FCF10002C779B /* Keys.plist */; }; - 9ABF454F268FCF10002C779B /* Keys.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9ABF454D268FCF10002C779B /* Keys.plist */; }; 9ABF455B268FDABA002C779B /* CopyToClipboardTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ABF4559268FDABA002C779B /* CopyToClipboardTests.swift */; }; 9ABFB3801CA6882000E10745 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ABFB37F1CA6882000E10745 /* ApplicationServices.framework */; }; 9AC678E41C1ABAB9003B4F6B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AC678E31C1ABAB9003B4F6B /* QuartzCore.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; @@ -271,6 +269,8 @@ 3531F80326938D7700DF0111 /* FirebaseInstallations.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FirebaseInstallations.framework; path = Frameworks/Firebase/FirebaseInstallations.framework; sourceTree = ""; }; 3531F80426938D7700DF0111 /* leveldb-library.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "leveldb-library.framework"; path = "Frameworks/Firebase/leveldb-library.framework"; sourceTree = ""; }; 3531F80526938D7700DF0111 /* GoogleDataTransport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleDataTransport.framework; path = Frameworks/Firebase/GoogleDataTransport.framework; sourceTree = ""; }; + 353B5BC42698B78A0023858D /* UpcomingEventStatusItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpcomingEventStatusItemView.swift; sourceTree = ""; }; + 353B5BC72698D4BB0023858D /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Localizable.strings; sourceTree = ""; }; 3569A44E25441F320087E254 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; 357391852507277500D30819 /* TimeMarkerViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeMarkerViewItem.swift; sourceTree = ""; }; 357391862507277500D30819 /* HourMarkerViewItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HourMarkerViewItem.xib; sourceTree = ""; }; @@ -422,7 +422,6 @@ 9AB6F1632259D1B800A44663 /* ParentViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParentViewController.swift; sourceTree = ""; }; 9AB6F1662259D23200A44663 /* PermissionsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PermissionsViewController.swift; sourceTree = ""; }; 9AB89E021CE97A4900EC8EB1 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; - 9ABF454D268FCF10002C779B /* Keys.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Keys.plist; path = Internal/Keys.plist; sourceTree = ""; }; 9ABF4559268FDABA002C779B /* CopyToClipboardTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CopyToClipboardTests.swift; sourceTree = ""; }; 9ABFB37F1CA6882000E10745 /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; }; 9AC678E31C1ABAB9003B4F6B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; @@ -548,6 +547,7 @@ 3508CC99259A0001000E3530 /* StatusItemView.swift */, 3508CC9E259A000E000E3530 /* StatusItemHandler.swift */, 3508CCA9259A0027000E3530 /* StatusContainerView.swift */, + 353B5BC42698B78A0023858D /* UpcomingEventStatusItemView.swift */, ); path = "Menu Bar"; sourceTree = ""; @@ -882,7 +882,6 @@ DD4F7BF913C30F9F00825C6E = { isa = PBXGroup; children = ( - 9ABF454D268FCF10002C779B /* Keys.plist */, 35B2FEE4259A2C25005DA84D /* CoreModelKit */, 35B2FED4259A2244005DA84D /* CoreLoggerKit */, 35B2FEB1259A1649005DA84D /* StartupKit */, @@ -1110,6 +1109,7 @@ ar, "pt-BR", "zh-Hant", + hr, ); mainGroup = DD4F7BF913C30F9F00825C6E; productRefGroup = DD4F7C0513C30F9F00825C6E /* Products */; @@ -1183,14 +1183,12 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9ABF454E268FCF10002C779B /* Keys.plist in Resources */, 9A13BAEA1CA88A76007C6CBE /* Localizable.strings in Resources */, 357391882507277500D30819 /* HourMarkerViewItem.xib in Resources */, 9AB6F15E2259D08300A44663 /* iVersion.bundle in Resources */, 35C36F972259EBB1002FA5C6 /* AppFeedbackWindow.xib in Resources */, 9A13BAE01CA882FA007C6CBE /* InfoPlist.strings in Resources */, 35C36F912259EAF4002FA5C6 /* Preferences.storyboard in Resources */, - 3531F7F52693882300DF0111 /* Keys.plist in Resources */, 9AB89E031CE97A4900EC8EB1 /* Media.xcassets in Resources */, 9A13BAD61CA87F08007C6CBE /* Panel.xib in Resources */, 35C36F6B2259E0E1002FA5C6 /* FloatingWindow.xib in Resources */, @@ -1313,6 +1311,7 @@ 35C36F582259DD8A002FA5C6 /* PanelTableView.swift in Sources */, 35C36F0F225961DA002FA5C6 /* TimePeriodCollection.swift in Sources */, 35C36F18225961DA002FA5C6 /* Date+Comparators.swift in Sources */, + 353B5BC52698B78A0023858D /* UpcomingEventStatusItemView.swift in Sources */, 35C36FA02259ED6D002FA5C6 /* CalendarHandler.swift in Sources */, 35C36F1A225961DA002FA5C6 /* Date+Manipulations.swift in Sources */, 35C36F572259DD8A002FA5C6 /* TimezoneDataSource.swift in Sources */, @@ -1422,6 +1421,7 @@ 9AA522D723416E6000C9E005 /* it */, 3569A44E25441F320087E254 /* pt-BR */, 35A6A4B925C5DEF300356073 /* zh-Hant */, + 353B5BC72698D4BB0023858D /* hr */, ); name = Localizable.strings; path = Clocker; @@ -1568,7 +1568,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = 21.07.02; + MARKETING_VERSION = 21.07.03; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = com.abhishek.Clocker; @@ -2138,7 +2138,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = 21.07.02; + MARKETING_VERSION = 21.07.03; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-ObjC"; "OTHER_SWIFT_FLAGS[arch=*]" = "-D DEBUG"; @@ -2219,7 +2219,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.13; - MARKETING_VERSION = 21.07.02; + MARKETING_VERSION = 21.07.03; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-ObjC"; "OTHER_SWIFT_FLAGS[arch=*]" = "-D RELEASE"; diff --git a/Clocker/Clocker/en.lproj/Panel.xib b/Clocker/Clocker/en.lproj/Panel.xib index df39e73..eab5b31 100755 --- a/Clocker/Clocker/en.lproj/Panel.xib +++ b/Clocker/Clocker/en.lproj/Panel.xib @@ -13,11 +13,14 @@ + + + @@ -42,15 +45,15 @@ - - + + - + - + @@ -260,7 +263,7 @@ - + @@ -309,13 +312,13 @@ - + - + - + @@ -342,15 +345,70 @@ + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + - + + - + diff --git a/Clocker/Clocker/hr.lproj/Localizable.strings b/Clocker/Clocker/hr.lproj/Localizable.strings new file mode 100644 index 0000000..9974b2b --- /dev/null +++ b/Clocker/Clocker/hr.lproj/Localizable.strings @@ -0,0 +1,165 @@ +/* + Localizable.strings + Clocker + + Created by Abhishek Banthia on 7/9/21. + +*/ + +"CFBundleDisplayName" = "Clocker"; +"Thank you for helping make Clocker even better!" = "Hvala što pomažeš poboljšati Clocker!"; +"iRateMessageTitle" = "Ocijeni %@"; +"iRateAppMessage" = "Ako voliš koristiti %@, ocijeni program. Postupak ocjenjivanja traje manje od jedne minute. Hvala na podršci!"; +"iRateGameMessage" = "Ako voliš igrati %@, ocijeni program. Postupak ocjenjivanja traje manje od jedne minute. Hvala na podršci!"; +"iRateCancelButton" = "Ne, hvala"; +"iRateRateButton" = "Ocijeni"; +"iRateRemindButton" = "Podsjeti me kasnije"; +"iRateUpdateMessage" = "Sada aktualizirati?"; +"ClockerVersion" = "Verzija %@"; +"CLFeedbackAlertTitle" = "Hvala što pomažeš poboljšati Clocker!"; +"app-name" = "Clocker"; +"start-at-login" = "Pokreni nakon prijave"; +"setup-steps" = "Za postavljanje programa Clocker potrebna su samo tri koraka"; +"Permissions-Header" = "Dozvole"; +"See your next Calendar event here." = "Ovdje pogledaj svoje sljedeće kalendarske događaje."; +"Click here to start." = "Pritisni ovdje za pokretanje."; +"Reminders Access" = "Pristup podsjetnicima"; +"Calendar Access" = "Pristup kalendaru"; +"Permissions" = "Dozvole"; +"Calendar Detail" = "Predstojeći događaji iz tvojih osobnih i zajedničkih kalendara mogu se prikazati u traci izbornika i na ploči."; +"Reminders Detail" = "Postavi podsjetnike u tvoju vremensku zonu mjesta. Tvoji se podsjetnici spremaju u standardni program Podsjetnici."; +"Privacy Text" = "Ovo možeš kasnije promijeniti u odjeljku Privatnost u Postavkama sustava."; +"Granted Button Text" = "Dozvoljeno"; +"Denied Button Text" = "Zabranjeno"; +"Grant Button Text" = "Dozvoli"; + +// Welcome Onboarding +"It only takes 3 steps to setup Clocker." = "Za postavljanje programa Clocker potrebna su samo tri koraka."; +"Get Started" = "Početak"; + +// Tab Item Titles +"Preferences Tab" = "Postavke"; +"Appearance Tab" = "Izgled"; +"Calendar Tab" = "Kalendar"; +"About Tab" = "Informacije"; +"Permissions Tab" = "Dozvole"; + +// General Preferences Screen +"Start at Login" = "Pokreni Clocker nakon prijave"; +"Sort by Time Difference" = "Razvrstaj po vremenskoj ralzici"; +"Sort by Name" = "Razvrstaj po imenu"; +"Sort by Label" = "Razvrstaj po oznaci"; +"Search Field Placeholder" = "Upiši grad, državu ili zemlju"; +"No Timezone Selected" = "Odaberi vremensku zonu!"; +"Max Timezones Selected" = "Ograničeno na 100 vremenskih zona!"; +"Max Search Characters" = "Ograničeno na 50 znakova!"; +"Add Button Title" = "Dodaj"; +"Close Button Title" = "Zatvori"; + +// Onboarding +"Open Clocker At Login" = "Otvori Clocker nakon prijave"; +"Launch Clocker" = "Pokreni Clocker"; + +// Welcome Onboarding +"It only takes 3 steps to set up Clocker." = "Za postavljanje programa Clocker potrebna su samo tri koraka."; +"Get Started" = "Početak"; + +// Permissions +"Calendar Access Title" = "Pristup kalendaru"; +"Reminders Access Title" = "Pristup podsjetnicima"; +"Later Config Description" = "Mogu se kasnije konfigurirati u postavkama sustava."; +"Back" = "Natrag"; +"Continue" = "Nastavi"; +"Clocker is more useful when it can display events from your calendars." = "Clocker je korisniji kad može prikazivati događaje u tvojim kalendarima."; +"Clocker is more useful when it can display events from your calendars. You can change this setting in System Preferences › Security & Privacy › Privacy." = "Clocker je korisniji kad može prikazivati događaje u tvojim kalendarima. Ovu postavku možeš promijeniti u Postavke sustava › Sigurnost i privatnost › Privatnost."; +/* Text for button that takes the user to the System Preferences app. In case the user hasn't given Calendar/Reminders access permission, this button takes you to the System Preferences app where the user can give proper permissions to Clocker. */ +"Launch Preferences" = "Pokreni postavke"; +"Grant Access" = "Dopusti pristup"; +"Upcoming events from your personal and shared calendars can be shown in the menubar and the panel." = "Predstojeći događaji iz tvojih osobnih i zajedničkih kalendara mogu se prikazati u traci izbornika i na ploči."; +"Granted" = "Dodijeljeno"; +"Denied" = "Odbijeno"; +"Grant" = "Odobrenje"; +"Unexpected" = "Neočekivano"; + +// Onboarding Search +"Quick Add Locations" = "Brzo dodavanje mjesta"; +"More search options in Clocker Preferences." = "Daljnje opcije pretage u Clocker postavkama."; +"Enter 3 or more characters for locations you'll like to add" = "Upiši tri ili više znakova za mjesta koja želiš dodati"; + +// Start at Login +"Launch at Login" = "Pokreni nakon prijave"; +"This can be configured later in Clocker Preferences." = "Ovo se može kasnije konfigurirati u Clocker postavkama."; +"Should Clocker open automatically on startup?" = "Želiš li automatski pokrenuti Clocker nakon pokretanja računala?"; + +// Final Onboarding Screen +"You're all set!" = "Sve je spremno!"; +"Thank you for the details." = "Hvala ti na detalju."; +"You'll see a clock icon in your Menu Bar when you launch the app. If you'd like to see a dock icon, go to Preferences." = "Kad pokreneš program, u traci izbornika vidjet ćeš ikonu sata. Ako želiš vidjeti ikonu u traci programa, idi na Postavke."; +"If you'd like to help us localize the app in your language or receive infrequent app-related updates, please enter your email!" = "Ako želiš pomoći prevesti program na tvoj jezik ili primati rijetke obavijesti o novim verzijama programa, upiši tvoju e-mail adresu!"; + +// Appearance Tab +"Panel Theme" = "Tema ploče"; +"Favourite a timezone to enable menubar display options." = "Odaberi vremensku zonu za aktiviranje opcija prikaza trake izbornika."; +"Main Panel Options" = "Opcije glavne ploče"; +"Time Format" = "Format za vrijeme"; +"Day Display Options" = "Opcije za prikaz dana"; +"Show Future Slider" = "Prikaži klizač budućnosti"; +"Show Sunrise/Sunset" = "Prikaži izlazak/zalazak sunca"; +"Display the time in seconds" = "Prikaži vrijeme u sekundama"; +"Larger Text" = "Veći tekst"; +"Future Slider Range" = "Klizač za raspon budućnosti"; +"Include Date" = "Uključi datum"; +"Include Day" = "Uključi dan"; +"Include Place Name" = "Uključi ime mjesta"; +"Menubar Display Options" = "Opcije prikaza izbornika"; +/* Appears in Preferences -> Appearance which allows the user to switch between the compact and standard menubar mode. It doesn't lead anywhere. */ +"Menubar Mode" = "Modus izbornika"; +"Preview" = " Pregled"; +"Miscellaneous" = "Razno"; + +// Empty View +"No places added" = "Nema dodanih mjesta"; + +// Panel +"No upcoming event." = "Nema predstojećih događaja."; +"You have no events scheduled for tomorrow." = "Za sutra nemaš zakazanih događaja."; + +// Review +"Enjoy using Clocker?" = "Voliš Clocker?"; + +// App Feedback +"Tell us what you think!" = "Javi nam što misliš!"; +"Contact Information (Optional)" = "Kontaktni podaci (opcionalno)"; +"Contact fields are optional! Your contact information will let us contact you in case we need more information or can help!" = "Polja za kontakt nisu obavezna! Tvoji kontaktni podaci omogućit će nam da te kontaktiramo u slučaju da trebamo daljnje informacije ili ako možemo pomoći!"; + +// About View Screen +"Feedback is always welcome:" = "Uvijek se radujemo povratnim informacijama:"; + +// Calendars View +"Upcoming Event View Options" = "Opcije prikaza predstojećih događaja"; +"Show Upcoming Event View" = "Prikaži prikaz predstojećih događaja"; +"Show All Day Meetings" = "Prikaži cjelodnevne sastanke"; +"Show Next Meeting Title in Menubar" = "Prikaži sljedeći sastanak u traci izbornika"; +"Truncate menubar text longer than" = "Skrati tekst izbone trake koji je duži od"; +"characters" = "znakova"; +"Show events from" = "Prikaži događaje od"; +"If meeting title is \"Meeting with Neel\" and truncate length is set to 5, text in menubar will appear as \"Meeti...\"" = "Ako je naslov sastanka „Sastanak s Vladimirom”, a kraćenje je postavljeno na 5, tekst na traci pojavit će se kao „Sasta …”"; + +// Notes Popover +"Reminder Set" = "Postavljanje podsjetnika"; +"Successfully set." = "Uspješno postavljeno."; + +// Errors +"You're offline, maybe?" = "Možda nemaš vezu s internetom?"; +"Try again, maybe?" = "Želiš li ponovo pokušati?"; +"The Internet connection appears to be offline." = "Čini se da ne postoji veza s internetom."; + +// UI Tests +"New Zealand" = "Novi Zeland"; +"Florida" = "Florida"; +"San Francisco" = "San Francisco"; + +// DST changes +"Daylights Saving transition will occur in < 24 hours" = "Daylights Saving transition will occur in < 24 hours"; + +"Copied to Clipboard" = "Copied to Clipboard"; diff --git a/Clocker/Events and Reminders/CalendarHandler.swift b/Clocker/Events and Reminders/CalendarHandler.swift index 80a4028..a0b1e2b 100644 --- a/Clocker/Events and Reminders/CalendarHandler.swift +++ b/Clocker/Events and Reminders/CalendarHandler.swift @@ -158,7 +158,7 @@ extension EventCenter { let relevantEvents = filteredEvents[autoupdatingCalendar.startOfDay(for: Date())] ?? [] let filteredEvent = relevantEvents.filter { - $0.event.isAllDay == false && $0.event.startDate.timeIntervalSinceNow > 0 + $0.event.isAllDay == false && $0.event.startDate.timeIntervalSinceNow > -300 }.first if let firstEvent = filteredEvent { @@ -424,4 +424,17 @@ struct EventInfo { let isAllDay: Bool let isSingleDay: Bool let meetingURL: URL? + + func metadataForMeeting() -> String { + let timeIntervalSinceNowForMeeting = event.startDate.timeIntervalSinceNow + if (timeIntervalSinceNowForMeeting < 0 && timeIntervalSinceNowForMeeting > -300) { + return "started \(event.startDate.shortTimeAgoSinceNow) ago." + } else { + 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())" + } + } } diff --git a/Clocker/Onboarding/OnboardingSearchController.swift b/Clocker/Onboarding/OnboardingSearchController.swift index 9c30da4..59c0f80 100644 --- a/Clocker/Onboarding/OnboardingSearchController.swift +++ b/Clocker/Onboarding/OnboardingSearchController.swift @@ -61,7 +61,7 @@ class OnboardingSearchController: NSViewController { resultsTableView.reloadData() func setupUndoButton() { - let font = NSFont(name: "Avenir", size: 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) diff --git a/Clocker/Overall App/Themer.swift b/Clocker/Overall App/Themer.swift index e31a40c..70db6c9 100644 --- a/Clocker/Overall App/Themer.swift +++ b/Clocker/Overall App/Themer.swift @@ -105,7 +105,7 @@ extension Themer { return themeIndex == .dark ? NSColor.white : NSColor.gray } - func mainControlColor() -> NSColor { + func mainBackgroundColor() -> NSColor { if #available(macOS 10.14, *) { switch themeIndex { case .light: @@ -113,28 +113,29 @@ extension Themer { case .dark: return NSColor(deviceRed: 42.0 / 255.0, green: 42.0 / 255.0, blue: 42.0 / 255.0, alpha: 1.0) case .system: - return NSColor.controlBackgroundColor + return retrieveCurrentSystem() == .light ? NSColor.white : NSColor.windowBackgroundColor } } - return themeIndex == .light ? NSColor.white : NSColor(deviceRed: 42.0 / 255.0, green: 42.0 / 255.0, blue: 42.0 / 255.0, alpha: 1.0) + return themeIndex == .light ? NSColor.white : NSColor(deviceRed: 55.0 / 255.0, green: 71.0 / 255.0, blue: 79.0 / 255.0, alpha: 1.0) } - func mainBackgroundColor() -> NSColor { - if #available(macOS 10.14, *) { - switch themeIndex { - case .light: - return NSColor.white - case .dark: - return NSColor(deviceRed: 42.0 / 255.0, green: 42.0 / 255.0, blue: 42.0 / 255.0, alpha: 1.0) - case .system: - return NSColor.windowBackgroundColor + private func retrieveCurrentSystem() -> Theme { + if #available(OSX 10.15, *) { + let appearanceDescription = NSApplication.shared.effectiveAppearance.debugDescription.lowercased() + if appearanceDescription.contains("dark") { + return .dark + } + } else if #available(OSX 10.14, *) { + if let appleInterfaceStyle = UserDefaults.standard.object(forKey: "AppleInterfaceStyle") as? String { + if appleInterfaceStyle.lowercased().contains("dark") { + return .dark + } } } - - return themeIndex == .light ? NSColor.white : NSColor(deviceRed: 55.0 / 255.0, green: 71.0 / 255.0, blue: 79.0 / 255.0, alpha: 1.0) + return .light } - + func mainTextColor() -> NSColor { if #available(macOS 10.14, *) { switch themeIndex { @@ -433,7 +434,7 @@ extension Themer { case .dark: return NSColor(deviceRed: 42.0 / 255.0, green: 55.0 / 255.0, blue: 62.0 / 255.0, alpha: 1.0) case .system: - return NSColor.controlBackgroundColor + return retrieveCurrentSystem() == .light ? NSColor(deviceRed: 241.0 / 255.0, green: 241.0 / 255.0, blue: 241.0 / 255.0, alpha: 1.0) : NSColor.controlBackgroundColor } } @@ -450,8 +451,21 @@ 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 symbolImage(for name: String) -> NSImage? { + private func symbolImage(for name: String) -> NSImage? { assert(name.isEmpty == false) if #available(OSX 11.0, *) { diff --git a/Clocker/Panel/ParentPanelController+ModernSlider.swift b/Clocker/Panel/ParentPanelController+ModernSlider.swift index a834997..3483152 100644 --- a/Clocker/Panel/ParentPanelController+ModernSlider.swift +++ b/Clocker/Panel/ParentPanelController+ModernSlider.swift @@ -18,6 +18,12 @@ extension ParentPanelController: NSCollectionViewDataSource { extension ParentPanelController { func setupModernSliderIfNeccessary() { if modernSlider != nil { + goBackwardsButton.image = Themer.shared().goBackwardsImage() + goForwardButton.image = Themer.shared().goForwardsImage() + + goForwardButton.isContinuous = true + goBackwardsButton.isContinuous = true + modernSlider.enclosingScrollView?.scrollerInsets = NSEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) modernSlider.enclosingScrollView?.backgroundColor = NSColor.clear modernSlider.setAccessibility("ModernSlider") @@ -78,10 +84,9 @@ extension ParentPanelController { direction: .forward)! if shouldUpdate { -// modernSliderLabel.stringValue = timezoneFormattedStringRepresentation(hourQuarterDate) + modernSliderLabel.stringValue = timezoneFormattedStringRepresentation(hourQuarterDate) } else { -// let fullString = NSMutableAttributedString(string: "Time Scroller") -// modernSliderLabel.attributedStringValue = fullString + modernSliderLabel.stringValue = "Time Scroller" } return hourQuarterDate @@ -93,12 +98,12 @@ extension ParentPanelController { if index > (centerPoint + 1) { let remainder = (index % (centerPoint + 1)) let nextDate = Calendar.current.date(byAdding: .minute, value: remainder * 15, to: closestQuarterTimeRepresentation ?? Date())! -// modernSliderLabel.stringValue = timezoneFormattedStringRepresentation(nextDate) + modernSliderLabel.stringValue = timezoneFormattedStringRepresentation(nextDate) 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) + modernSliderLabel.stringValue = timezoneFormattedStringRepresentation(previousDate) return previousDate.minutes(from: Date()) } else { setModernSliderLabel(true) diff --git a/Clocker/Panel/ParentPanelController.swift b/Clocker/Panel/ParentPanelController.swift index d8f72b3..8f26526 100644 --- a/Clocker/Panel/ParentPanelController.swift +++ b/Clocker/Panel/ParentPanelController.swift @@ -92,6 +92,8 @@ class ParentPanelController: NSWindowController { @IBOutlet var modernSlider: NSCollectionView! @IBOutlet var modernSliderLabel: NSTextField! @IBOutlet var modernContainerView: ModernSliderContainerView! + @IBOutlet var goBackwardsButton: NSButton! + @IBOutlet var goForwardButton: NSButton! var defaultPreferences: [Data] { return DataStore.shared().timezones() @@ -840,11 +842,7 @@ class ParentPanelController: NSWindowController { return } - let timeSince = Date().timeAgo(since: upcomingEvent.event.startDate) - let withoutAn = timeSince.replacingOccurrences(of: "an", with: CLEmptyString) - let withoutAgo = withoutAn.replacingOccurrences(of: "ago", with: CLEmptyString) - - self.setCalendarButtonTitle(buttonTitle: "in \(withoutAgo.lowercased())") + self.setCalendarButtonTitle(buttonTitle: upcomingEvent.metadataForMeeting()) if upcomingEvent.meetingURL != nil { self.whiteRemoveButton.image = Themer.shared().videoCallImage() diff --git a/Clocker/Panel/UI/TimezoneDataSource.swift b/Clocker/Panel/UI/TimezoneDataSource.swift index c518c34..af5a0c5 100644 --- a/Clocker/Panel/UI/TimezoneDataSource.swift +++ b/Clocker/Panel/UI/TimezoneDataSource.swift @@ -148,7 +148,7 @@ extension TimezoneDataSource: NSTableViewDataSource, NSTableViewDelegate { }) if #available(OSX 11.0, *) { - swipeToDelete.image = Themer.shared().symbolImage(for: "trash.fill") + swipeToDelete.image = Themer.shared().filledTrashImage() } else { swipeToDelete.image = NSImage(named: NSImage.Name("Trash")) diff --git a/Clocker/Preferences/Menu Bar/StatusContainerView.swift b/Clocker/Preferences/Menu Bar/StatusContainerView.swift index c5508fb..ad61627 100644 --- a/Clocker/Preferences/Menu Bar/StatusContainerView.swift +++ b/Clocker/Preferences/Menu Bar/StatusContainerView.swift @@ -51,7 +51,6 @@ func compactWidth(for timezone: TimezoneData) -> Int { totalWidth += 20 } - print("-- Compact Width is \(totalWidth)") return totalWidth } diff --git a/Clocker/Preferences/Menu Bar/UpcomingEventStatusItemView.swift b/Clocker/Preferences/Menu Bar/UpcomingEventStatusItemView.swift new file mode 100644 index 0000000..6b0dd41 --- /dev/null +++ b/Clocker/Preferences/Menu Bar/UpcomingEventStatusItemView.swift @@ -0,0 +1,89 @@ +// Copyright © 2015 Abhishek Banthia + +import Foundation +import AppKit + +class UpcomingEventStatusItemView: NSView { + static let containerWidth = 70 + + private let nextEventField = NSTextField(labelWithString: "Next Event") + private let etaField = NSTextField(labelWithString: "5 mins") + var dataObject: EventInfo! { + didSet { + initialSetup() + } + } + private var timeAttributes: [NSAttributedString.Key: AnyObject] { + let textColor = hasDarkAppearance ? NSColor.white : NSColor.black + + let attributes = [ + NSAttributedString.Key.font: compactModeTimeFont, + NSAttributedString.Key.foregroundColor: textColor, + NSAttributedString.Key.backgroundColor: NSColor.clear, + NSAttributedString.Key.paragraphStyle: defaultParagraphStyle, + ] + return attributes + } + + private var textFontAttributes: [NSAttributedString.Key: Any] { + let textColor = hasDarkAppearance ? NSColor.white : NSColor.black + + let textFontAttributes = [ + NSAttributedString.Key.font: NSFont.boldSystemFont(ofSize: 10), + NSAttributedString.Key.foregroundColor: textColor, + NSAttributedString.Key.backgroundColor: NSColor.clear, + NSAttributedString.Key.paragraphStyle: defaultParagraphStyle, + ] + return textFontAttributes + } + + override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + + [etaField, nextEventField].forEach { + $0.wantsLayer = true + $0.applyDefaultStyle() + $0.translatesAutoresizingMaskIntoConstraints = false + addSubview($0) + } + + etaField.disableWrapping() + + NSLayoutConstraint.activate([ + nextEventField.leadingAnchor.constraint(equalTo: leadingAnchor), + nextEventField.trailingAnchor.constraint(equalTo: trailingAnchor), + nextEventField.topAnchor.constraint(equalTo: topAnchor, constant: 7), + nextEventField.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.35), + ]) + + NSLayoutConstraint.activate([ + etaField.leadingAnchor.constraint(equalTo: leadingAnchor), + etaField.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0), + etaField.topAnchor.constraint(equalTo: nextEventField.bottomAnchor), + etaField.bottomAnchor.constraint(equalTo: bottomAnchor), + ]) + } + + 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) + } + + override func mouseDown(with event: NSEvent) { + super.mouseDown(with: event) + guard let mainDelegate = NSApplication.shared.delegate as? AppDelegate else { + return + } + + mainDelegate.togglePanel(event) + } +} diff --git a/Clocker/Preferences/Preferences.storyboard b/Clocker/Preferences/Preferences.storyboard index b6718b1..887f9b0 100644 --- a/Clocker/Preferences/Preferences.storyboard +++ b/Clocker/Preferences/Preferences.storyboard @@ -590,7 +590,7 @@ - + @@ -909,11 +909,11 @@ - + - + @@ -1491,7 +1491,7 @@ - + @@ -1499,7 +1499,7 @@ - + @@ -1556,13 +1556,13 @@ DQ - + - - + + - + @@ -1652,14 +1652,14 @@ DQ - + - + @@ -1707,7 +1707,7 @@ DQ