Browse Source

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

pull/113/head
Abhishek Banthia 3 years ago
parent
commit
1bb31de20b
  1. 4
      Clocker/Clocker.xcodeproj/project.pbxproj
  2. 101
      Clocker/ClockerUnitTests/ReviewControllerTests.swift
  3. 12
      Clocker/ClockerUnitTests/ThemerTests.swift
  4. 14
      Clocker/Overall App/Themer.swift
  5. 16
      Clocker/Panel/Rate Controller/ReviewController.swift
  6. 2
      Clocker/Preferences/Menu Bar/StatusItemHandler.swift

4
Clocker/Clocker.xcodeproj/project.pbxproj

@ -125,6 +125,7 @@
35C36FA12259ED6D002FA5C6 /* EventCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C36F9E2259ED6D002FA5C6 /* EventCenter.swift */; }; 35C36FA12259ED6D002FA5C6 /* EventCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C36F9E2259ED6D002FA5C6 /* EventCenter.swift */; };
35C36FA22259ED6D002FA5C6 /* RemindersHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C36F9F2259ED6D002FA5C6 /* RemindersHandler.swift */; }; 35C36FA22259ED6D002FA5C6 /* RemindersHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C36F9F2259ED6D002FA5C6 /* RemindersHandler.swift */; };
35C36FA42259EEC2002FA5C6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C36FA32259EEC2002FA5C6 /* AppDelegate.swift */; }; 35C36FA42259EEC2002FA5C6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C36FA32259EEC2002FA5C6 /* AppDelegate.swift */; };
35D23E3727F27E2E00C6DD55 /* ReviewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35D23E3627F27E2E00C6DD55 /* ReviewControllerTests.swift */; };
35DFBCEF26A8468900D6648B /* ConfigExport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35DFBCEE26A8468900D6648B /* ConfigExport.swift */; }; 35DFBCEF26A8468900D6648B /* ConfigExport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35DFBCEE26A8468900D6648B /* ConfigExport.swift */; };
35E65125268EDD2E00E3E1E3 /* Toasty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35E65124268EDD2E00E3E1E3 /* Toasty.swift */; }; 35E65125268EDD2E00E3E1E3 /* Toasty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35E65124268EDD2E00E3E1E3 /* Toasty.swift */; };
9A0385BB269E3434003B5E72 /* StandardMenubarHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0385BA269E3434003B5E72 /* StandardMenubarHandlerTests.swift */; }; 9A0385BB269E3434003B5E72 /* StandardMenubarHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0385BA269E3434003B5E72 /* StandardMenubarHandlerTests.swift */; };
@ -365,6 +366,7 @@
35C36F9E2259ED6D002FA5C6 /* EventCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventCenter.swift; sourceTree = "<group>"; }; 35C36F9E2259ED6D002FA5C6 /* EventCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventCenter.swift; sourceTree = "<group>"; };
35C36F9F2259ED6D002FA5C6 /* RemindersHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemindersHandler.swift; sourceTree = "<group>"; }; 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; }; 35C36FA32259EEC2002FA5C6 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = SOURCE_ROOT; };
35D23E3627F27E2E00C6DD55 /* ReviewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewControllerTests.swift; sourceTree = "<group>"; };
35DFBCEE26A8468900D6648B /* ConfigExport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigExport.swift; sourceTree = "<group>"; }; 35DFBCEE26A8468900D6648B /* ConfigExport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigExport.swift; sourceTree = "<group>"; };
35E65124268EDD2E00E3E1E3 /* Toasty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toasty.swift; 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>"; }; 9A0385BA269E3434003B5E72 /* StandardMenubarHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardMenubarHandlerTests.swift; sourceTree = "<group>"; };
@ -887,6 +889,7 @@
35584D1327EF8EB5006E3EAD /* ThemerTests.swift */, 35584D1327EF8EB5006E3EAD /* ThemerTests.swift */,
35584D1727F0B019006E3EAD /* DateFormatterManagerTests.swift */, 35584D1727F0B019006E3EAD /* DateFormatterManagerTests.swift */,
35584D1927F0B64E006E3EAD /* AppDelegateTests.swift */, 35584D1927F0B64E006E3EAD /* AppDelegateTests.swift */,
35D23E3627F27E2E00C6DD55 /* ReviewControllerTests.swift */,
); );
path = ClockerUnitTests; path = ClockerUnitTests;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1273,6 +1276,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
35584D1427EF8EB5006E3EAD /* ThemerTests.swift in Sources */, 35584D1427EF8EB5006E3EAD /* ThemerTests.swift in Sources */,
35D23E3727F27E2E00C6DD55 /* ReviewControllerTests.swift in Sources */,
35584D1A27F0B64E006E3EAD /* AppDelegateTests.swift in Sources */, 35584D1A27F0B64E006E3EAD /* AppDelegateTests.swift in Sources */,
9A0385BB269E3434003B5E72 /* StandardMenubarHandlerTests.swift in Sources */, 9A0385BB269E3434003B5E72 /* StandardMenubarHandlerTests.swift in Sources */,
35584D1827F0B019006E3EAD /* DateFormatterManagerTests.swift in Sources */, 35584D1827F0B019006E3EAD /* DateFormatterManagerTests.swift in Sources */,

101
Clocker/ClockerUnitTests/ReviewControllerTests.swift

@ -0,0 +1,101 @@
// Copyright © 2015 Abhishek Banthia
import XCTest
@testable import Clocker
class ReviewControllerTests: XCTestCase {
func testDebuggingMode() throws {
guard let mockDefaults = UserDefaults(suiteName: "com.test.Clocker") else {
return
}
ReviewController.applicationDidLaunch(mockDefaults)
// Call it again to ensure Keys.install
ReviewController.applicationDidLaunch(mockDefaults)
ReviewController.setPreviewMode(true)
XCTAssertTrue(ReviewController.canPrompt())
}
func testPromptNotDisplayedInFirstWeekSinceInstall() {
guard let mockDefaults = UserDefaults(suiteName: "com.test.Clocker1") else {
return
}
// Set key install time
ReviewController.applicationDidLaunch(mockDefaults)
// Explicitly set preview mode to false
ReviewController.setPreviewMode(false)
XCTAssertFalse(ReviewController.canPrompt())
}
func testPrompDisplayedAfterFirstWeekOfInstall() {
let dateChunk = TimeChunk(seconds: 0,
minutes: 0,
hours: 0,
days: 8,
weeks: 0,
months: 0,
years: 0)
let oldDate = Date().subtract(dateChunk)
guard let mockDefaults = UserDefaults(suiteName: "com.test.Clocker2") else {
return
}
mockDefaults.set(oldDate, forKey: "install")
ReviewController.applicationDidLaunch(mockDefaults)
// Explicitly set preview mode to false
ReviewController.setPreviewMode(false)
XCTAssertNil(mockDefaults.object(forKey:"last-prompt"))
XCTAssertNil(mockDefaults.object(forKey:"last-version"))
XCTAssertTrue(ReviewController.canPrompt())
}
func testPrompDisplayedAfterThreeMonths() {
let dateChunk = TimeChunk(seconds: 0,
minutes: 0,
hours: 0,
days: 98,
weeks: 0,
months: 0,
years: 0)
let minInstall = Date().subtract(dateChunk)
let promptChunk = TimeChunk(seconds: 0,
minutes: 0,
hours: 0,
days: 91,
weeks: 0,
months: 0,
years: 0)
let lastPromptDate = Date().subtract(promptChunk)
guard let mockDefaults = UserDefaults(suiteName: "com.test.Clocker3") else {
return
}
mockDefaults.set(minInstall, forKey: "install")
mockDefaults.set("test-version", forKey: "last-version")
mockDefaults.set(lastPromptDate, forKey: "last-prompt")
ReviewController.applicationDidLaunch(mockDefaults)
// Explicitly set preview mode to false
ReviewController.setPreviewMode(false)
XCTAssertNotNil(mockDefaults.object(forKey:"last-prompt"))
XCTAssertNotNil(mockDefaults.object(forKey:"last-version"))
XCTAssertTrue(ReviewController.canPrompt())
}
func testPrompted() {
guard let mockDefaults = UserDefaults(suiteName: "com.test.Clocker4") else {
return
}
ReviewController.applicationDidLaunch(mockDefaults)
ReviewController.prompt()
XCTAssertNotNil(mockDefaults.object(forKey:"last-prompt"))
XCTAssertNotNil(mockDefaults.object(forKey:"last-version"))
}
}

12
Clocker/ClockerUnitTests/ThemerTests.swift

@ -101,7 +101,7 @@ class ThemerTests: XCTestCase {
let expectedSunsetImageName = "sunset.fill" let expectedSunsetImageName = "sunset.fill"
let expectedRemoveImageName = "xmark" let expectedRemoveImageName = "xmark"
let expectedExtraOptionsImage = "ExtraWhite" let expectedExtraOptionsImage = "ExtraWhite"
let expectedMenubarOnboardingImage = "Light Menubar" let expectedMenubarOnboardingImage = "Dark Menubar"
let expectedExtraOptionsHighlightedImage = "ExtraWhiteHighlighted" let expectedExtraOptionsHighlightedImage = "ExtraWhiteHighlighted"
let expectedSharingImage = "square.and.arrow.up.on.square.fill" let expectedSharingImage = "square.and.arrow.up.on.square.fill"
let expectedCurrentLocationImage = "location.fill" let expectedCurrentLocationImage = "location.fill"
@ -153,13 +153,13 @@ class ThemerTests: XCTestCase {
func testSystemTheme() throws { func testSystemTheme() throws {
let currentSystemTheme = let currentSystemTheme =
UserDefaults.standard.string(forKey: "AppleUserInterfaceStyle")?.lowercased().contains("dark") ?? false ? Themer.Theme.dark : Themer.Theme.light UserDefaults.standard.string(forKey: "AppleInterfaceStyle")?.lowercased().contains("dark") ?? false ? Themer.Theme.dark : Themer.Theme.light
let subject = Themer(index: 2) // 2 is for system theme let subject = Themer(index: 2) // 2 is for system theme
let expectedSliderKnobColor = currentSystemTheme == .light ? NSColor(deviceRed: 255.0, green: 255.0, blue: 255, alpha: 0.9) : NSColor(deviceRed: 0.0, green: 0.0, blue: 0, alpha: 0.9) let expectedSliderKnobColor = currentSystemTheme == .light ? NSColor(deviceRed: 255.0, green: 255.0, blue: 255, alpha: 0.9) : NSColor(deviceRed: 0.0, green: 0.0, blue: 0, alpha: 0.9)
let expectedSliderRightColor = currentSystemTheme == .dark ? NSColor.white : NSColor.gray let expectedSliderRightColor = currentSystemTheme == .dark ? NSColor.white : NSColor.gray
let expectedBackgroundColor = currentSystemTheme == .dark ? NSColor(deviceRed: 42.0 / 255.0, green: 42.0 / 255.0, blue: 42.0 / 255.0, alpha: 1.0) : NSColor.white let expectedBackgroundColor = currentSystemTheme == .dark ? NSColor.windowBackgroundColor : NSColor.white
let expectedTextColor = NSColor.textColor let expectedTextColor = NSColor.textColor
let expectedTextBackgroundColor = currentSystemTheme == .dark ? NSColor(deviceRed: 42.0 / 255.0, green: 55.0 / 255.0, blue: 62.0 / 255.0, alpha: 1.0) : NSColor(deviceRed: 241.0 / 255.0, green: 241.0 / 255.0, blue: 241.0 / 255.0, alpha: 1.0) let expectedTextBackgroundColor = currentSystemTheme == .light ? NSColor(deviceRed: 42.0 / 255.0, green: 55.0 / 255.0, blue: 62.0 / 255.0, alpha: 1.0) : NSColor.controlBackgroundColor
let expectedShutdownImageName = "ellipsis.circle" let expectedShutdownImageName = "ellipsis.circle"
let expectedPreferenceImageName = "plus" let expectedPreferenceImageName = "plus"
@ -220,7 +220,7 @@ class ThemerTests: XCTestCase {
func testSolarizedLightTheme() throws { func testSolarizedLightTheme() throws {
let subject = Themer(index: 3) // 3 is for solarized light theme let subject = Themer(index: 3) // 3 is for solarized light theme
let expectedSliderKnobColor = NSColor(deviceRed: 0.0, green: 0.0, blue: 0, alpha: 0.9) let expectedSliderKnobColor = NSColor(deviceRed: 255.0, green: 255.0, blue: 255, alpha: 0.9)
let expectedSliderRightColor = NSColor.gray let expectedSliderRightColor = NSColor.gray
let expectedBackgroundColor = NSColor(deviceRed: 253.0 / 255.0, green: 246.0 / 255.0, blue: 227.0 / 255.0, alpha: 1.0) let expectedBackgroundColor = NSColor(deviceRed: 253.0 / 255.0, green: 246.0 / 255.0, blue: 227.0 / 255.0, alpha: 1.0)
let expectedTextColor = NSColor.black let expectedTextColor = NSColor.black
@ -299,7 +299,7 @@ class ThemerTests: XCTestCase {
let expectedSunsetImageName = "sunset.fill" let expectedSunsetImageName = "sunset.fill"
let expectedRemoveImageName = "xmark" let expectedRemoveImageName = "xmark"
let expectedExtraOptionsImage = "ExtraWhite" let expectedExtraOptionsImage = "ExtraWhite"
let expectedMenubarOnboardingImage = "Light Menubar" let expectedMenubarOnboardingImage = "Dark Menubar"
let expectedExtraOptionsHighlightedImage = "ExtraWhiteHighlighted" let expectedExtraOptionsHighlightedImage = "ExtraWhiteHighlighted"
let expectedSharingImage = "square.and.arrow.up.on.square.fill" let expectedSharingImage = "square.and.arrow.up.on.square.fill"
let expectedCurrentLocationImage = "location.fill" let expectedCurrentLocationImage = "location.fill"

14
Clocker/Overall App/Themer.swift

@ -95,12 +95,12 @@ extension Themer {
func sliderKnobColor() -> NSColor { func sliderKnobColor() -> NSColor {
switch themeIndex { switch themeIndex {
case .light: case .light, .solarizedLight:
return NSColor(deviceRed: 255.0, green: 255.0, blue: 255, alpha: 0.9) return NSColor(deviceRed: 255.0, green: 255.0, blue: 255, alpha: 0.9)
case .dark, .solarizedDark:
return NSColor(deviceRed: 0.0, green: 0.0, blue: 0, alpha: 0.9)
case .system: case .system:
return retrieveCurrentSystem() == .light ? NSColor(deviceRed: 255.0, green: 255.0, blue: 255, alpha: 0.9) : NSColor(deviceRed: 0.0, green: 0.0, blue: 0, alpha: 0.9) return retrieveCurrentSystem() == .light ? NSColor(deviceRed: 255.0, green: 255.0, blue: 255, alpha: 0.9) : NSColor(deviceRed: 0.0, green: 0.0, blue: 0, alpha: 0.9)
default:
return NSColor(deviceRed: 0.0, green: 0.0, blue: 0, alpha: 0.9)
} }
} }
@ -108,6 +108,8 @@ extension Themer {
switch themeIndex { switch themeIndex {
case .dark: case .dark:
return NSColor.white return NSColor.white
case .system:
return retrieveCurrentSystem() == .dark ? NSColor.white : NSColor.gray
default: default:
return NSColor.gray return NSColor.gray
} }
@ -258,8 +260,10 @@ extension Themer {
switch themeIndex { switch themeIndex {
case .system: case .system:
return NSImage(named: NSImage.Name("Dynamic Menubar"))! return NSImage(named: NSImage.Name("Dynamic Menubar"))!
default: case .light, .solarizedLight:
return retrieveCurrentSystem() == .dark ? NSImage(named: NSImage.Name("Dark Menubar"))! : NSImage(named: NSImage.Name("Light Menubar"))! return NSImage(named: NSImage.Name("Light Menubar"))!
case .dark, .solarizedDark:
return NSImage(named: NSImage.Name("Dark Menubar"))!
} }
} }

16
Clocker/Panel/Rate Controller/ReviewController.swift

@ -5,7 +5,6 @@ import StoreKit
final class ReviewController { final class ReviewController {
private static var storage = UserDefaults.standard private static var storage = UserDefaults.standard
private static let version: String = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "N/A"
private static var debugging = false private static var debugging = false
private enum Keys { private enum Keys {
@ -14,15 +13,16 @@ final class ReviewController {
static let install = "install" static let install = "install"
} }
class func applicationDidLaunch(_ defaults: UserDefaults = UserDefaults.standard) { class func applicationDidLaunch(_ defaults: UserDefaults) {
if ProcessInfo.processInfo.arguments.contains(CLUITestingLaunchArgument) { if ProcessInfo.processInfo.arguments.contains(CLUITestingLaunchArgument) {
debugging = true debugging = true
} }
storage = defaults storage = defaults
guard defaults.object(forKey: Keys.install) == nil else { return } if defaults.object(forKey: Keys.install) == nil {
defaults.set(Date(), forKey: Keys.install) defaults.set(Date(), forKey: Keys.install)
}
} }
class func setPreviewMode(_ value: Bool) { class func setPreviewMode(_ value: Bool) {
@ -31,11 +31,13 @@ final class ReviewController {
class func prompted() { class func prompted() {
storage.set(Date(), forKey: Keys.lastPrompt) storage.set(Date(), forKey: Keys.lastPrompt)
storage.set(version, forKey: Keys.lastVersion) storage.set(Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String, forKey: Keys.lastVersion)
} }
class func canPrompt() -> Bool { class func canPrompt() -> Bool {
guard debugging == false else { return true } if debugging == true {
return true
}
let day: TimeInterval = -1 * 60 * 60 * 24 let day: TimeInterval = -1 * 60 * 60 * 24
let minInstall: TimeInterval = day * 7 let minInstall: TimeInterval = day * 7
@ -54,7 +56,7 @@ final class ReviewController {
let minInterval: TimeInterval = day * 90 let minInterval: TimeInterval = day * 90
// never prompt w/in the same version // never prompt w/in the same version
return lastVersion != version return lastVersion != (Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String)
// limit all types of prompts to at least 1mo intervals // limit all types of prompts to at least 1mo intervals
&& lastPrompt.timeIntervalSinceNow < minInterval && lastPrompt.timeIntervalSinceNow < minInterval
} }

2
Clocker/Preferences/Menu Bar/StatusItemHandler.swift

@ -207,7 +207,7 @@ class StatusItemHandler: NSObject {
return false return false
} }
return timezonesSupportingSeconds.isEmpty return timezonesSupportingSeconds.isEmpty == false
} }
private func calculateFireDate() -> Date? { private func calculateFireDate() -> Date? {

Loading…
Cancel
Save