diff --git a/Balance.xcodeproj/project.pbxproj b/Balance.xcodeproj/project.pbxproj index 224aa8c7..3545efde 100644 --- a/Balance.xcodeproj/project.pbxproj +++ b/Balance.xcodeproj/project.pbxproj @@ -500,6 +500,9 @@ E605EF882006AC8F00A95632 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E605EF872006AC8E00A95632 /* RxSwift.framework */; }; E605EF8A2006B6BF00A95632 /* RxCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E605EF892006B6BF00A95632 /* RxCocoa.framework */; }; E605EF8F2006BF3C00A95632 /* UIViewController+Present.swift in Sources */ = {isa = PBXBuildFile; fileRef = E605EF8D2006BF3600A95632 /* UIViewController+Present.swift */; }; + E62D5129201269B3009289CF /* Collection+Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = E605EF852006851300A95632 /* Collection+Index.swift */; }; + E62D512B2012846E009289CF /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E62D512A2012846E009289CF /* Dictionary.swift */; }; + E62D512C2012846E009289CF /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = E62D512A2012846E009289CF /* Dictionary.swift */; }; E653123E1FE8C20700F57977 /* NewExchangeAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E653123D1FE8C20600F57977 /* NewExchangeAPI.swift */; }; E65312451FE8C22E00F57977 /* BITTREXApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65312411FE8C22D00F57977 /* BITTREXApi.swift */; }; E653124C1FE8C28D00F57977 /* BITTREXCurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65312491FE8C28D00F57977 /* BITTREXCurrency.swift */; }; @@ -523,6 +526,8 @@ E65312901FE8C4E100F57977 /* BITTREXInstitution.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65312861FE8C47F00F57977 /* BITTREXInstitution.swift */; }; E65312921FE8C53C00F57977 /* BITTREXApiTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E653127A1FE8C40A00F57977 /* BITTREXApiTest.swift */; }; E65312931FE8C69800F57977 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F078421F4F8EC700D0054C /* TestHelpers.swift */; }; + E66017B62011587C0093C70A /* PreferencesSecurityViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E66017B52011587C0093C70A /* PreferencesSecurityViewModel.swift */; }; + E66017B72011589C0093C70A /* PreferencesSecurityViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E66017B52011587C0093C70A /* PreferencesSecurityViewModel.swift */; }; E6B0416C1FFECFB200192319 /* AddAccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6B0416B1FFECFB200192319 /* AddAccountDelegate.swift */; }; E6B0416D1FFED03300192319 /* AddAccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6B0416B1FFECFB200192319 /* AddAccountDelegate.swift */; }; E6C1AC4620057ABE0018D6AA /* ReconnectAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6C1AC43200577BC0018D6AA /* ReconnectAccountViewController.swift */; }; @@ -976,6 +981,7 @@ E605EF872006AC8E00A95632 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = ""; }; E605EF892006B6BF00A95632 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = ""; }; E605EF8D2006BF3600A95632 /* UIViewController+Present.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Present.swift"; sourceTree = ""; }; + E62D512A2012846E009289CF /* Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = ""; }; E653123D1FE8C20600F57977 /* NewExchangeAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewExchangeAPI.swift; sourceTree = ""; }; E65312411FE8C22D00F57977 /* BITTREXApi.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BITTREXApi.swift; sourceTree = ""; }; E65312491FE8C28D00F57977 /* BITTREXCurrency.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BITTREXCurrency.swift; path = "Balance/Shared/Data Model/APIs/BITTREX/DTO/BITTREXCurrency.swift"; sourceTree = SOURCE_ROOT; }; @@ -992,6 +998,7 @@ E65312851FE8C47F00F57977 /* BITTREXApiError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BITTREXApiError.swift; sourceTree = ""; }; E65312861FE8C47F00F57977 /* BITTREXInstitution.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BITTREXInstitution.swift; sourceTree = ""; }; E65312871FE8C47F00F57977 /* BITTREXApiAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BITTREXApiAction.swift; sourceTree = ""; }; + E66017B52011587C0093C70A /* PreferencesSecurityViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesSecurityViewModel.swift; sourceTree = ""; }; E6B0416B1FFECFB200192319 /* AddAccountDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AddAccountDelegate.swift; path = AddCredentialBasedAccountViewController/AddAccountDelegate.swift; sourceTree = ""; }; E6C1AC43200577BC0018D6AA /* ReconnectAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReconnectAccountViewController.swift; sourceTree = ""; }; E6C1AC4720058EC10018D6AA /* ReconnectAccountCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReconnectAccountCell.swift; sourceTree = ""; }; @@ -1157,6 +1164,7 @@ 530422841ED38E1A001CA499 /* View Models */ = { isa = PBXGroup; children = ( + E66017B42011585A0093C70A /* Preferences */, 530422891ED39742001CA499 /* TabViewModel.swift */, 532B6FF11E9613520010D81D /* AccountsTabViewModel.swift */, 530422931ED39EE7001CA499 /* TransactionsTabViewModel.swift */, @@ -1771,6 +1779,7 @@ 53EB77B91CF3DF5100F01903 /* Extensions */ = { isa = PBXGroup; children = ( + E605EF852006851300A95632 /* Collection+Index.swift */, 533A3B1C1DF23C2B005D3C89 /* Array.swift */, 53EB77BA1CF3DF6E00F01903 /* Color.swift */, 533558251DDA860C00D91F4D /* Data.swift */, @@ -1786,6 +1795,7 @@ 534DDFD71F4CCE9400E6C37F /* URLRequest.swift */, A4E4571E1F790442002E2879 /* Double.swift */, A467DFC41F98EBFD0063882C /* Currency+MasterCurrency.swift */, + E62D512A2012846E009289CF /* Dictionary.swift */, ); path = Extensions; sourceTree = ""; @@ -2081,7 +2091,6 @@ A456ED131FB31A1A006EE529 /* Extensions */ = { isa = PBXGroup; children = ( - E605EF8C2006BEFD00A95632 /* Foundation */, E605EF8B2006BEF700A95632 /* UIKit */, ); path = Extensions; @@ -2371,6 +2380,14 @@ path = BITTREX; sourceTree = ""; }; + E66017B42011585A0093C70A /* Preferences */ = { + isa = PBXGroup; + children = ( + E66017B52011587C0093C70A /* PreferencesSecurityViewModel.swift */, + ); + path = Preferences; + sourceTree = ""; + }; E6C1AC45200578C00018D6AA /* Reconnect */ = { isa = PBXGroup; children = ( @@ -2930,9 +2947,11 @@ 40A90EA9200F9A4C0063B2F5 /* BlankStateView.swift in Sources */, 53552A291ED7252900DD95F9 /* Error.swift in Sources */, 53552A331ED72B5700DD95F9 /* FMDatabaseAdditionsVariadic.swift in Sources */, + E66017B72011589C0093C70A /* PreferencesSecurityViewModel.swift in Sources */, 53552A011ED7251200DD95F9 /* Testing.swift in Sources */, 53552A031ED7251200DD95F9 /* Database.swift in Sources */, 535529F61ED724FA00DD95F9 /* AsyncOperation.swift in Sources */, + E62D512C2012846E009289CF /* Dictionary.swift in Sources */, 53552A001ED7251200DD95F9 /* ObjC.m in Sources */, 2D9AA1E41FB5AED300058491 /* Analytics.swift in Sources */, 53552A301ED72B5700DD95F9 /* FMDatabasePool.m in Sources */, @@ -3063,6 +3082,7 @@ 53F6383F1C5FFF3200864346 /* Transaction.swift in Sources */, DCDF77BD1BE6C6C6004AF8AE /* PreferencesGeneralViewController.swift in Sources */, E653123E1FE8C20700F57977 /* NewExchangeAPI.swift in Sources */, + E62D512B2012846E009289CF /* Dictionary.swift in Sources */, A4430A931F94EFB300F2FFFA /* OpenTheme.swift in Sources */, 532550831D06F9090045404E /* View.swift in Sources */, 283D18351DC8F6240044BF51 /* PreferencesSecurityViewController.swift in Sources */, @@ -3106,6 +3126,7 @@ 53294FA31D1DDD9500F08059 /* CCNStatusItemDropView.m in Sources */, 53BB42E71CE849E70002D473 /* Shortcut.swift in Sources */, 53F0785D1ED73B7D00E7EDF9 /* Element.swift in Sources */, + E66017B62011587C0093C70A /* PreferencesSecurityViewModel.swift in Sources */, 532B6FE51E95D5C40010D81D /* Swizzling.swift in Sources */, 530F57B41DCD72680083F3A7 /* FMDatabaseQueue.m in Sources */, A4430AA51F95F47300F2FFFA /* SimpleCache.swift in Sources */, @@ -3201,6 +3222,7 @@ 530415BE1DFE1A6E00F0679C /* NSTextView.swift in Sources */, 53DC85B11E678223007C72EB /* FileManager.swift in Sources */, 532121A51EE8B91600F4812A /* Zip.swift in Sources */, + E62D5129201269B3009289CF /* Collection+Index.swift in Sources */, 53E74A571DD53CCB00AD8876 /* TimeInterval.swift in Sources */, 53368DD81E3FFD4200B7A199 /* PaintCodeDropdown.swift in Sources */, 5373B4791FAB7AD1007432DC /* PriceTickerTabTableCells.swift in Sources */, diff --git a/Balance/iOS/Extensions/Foundation/Collection+Index.swift b/Balance/Shared/Data Model/Extensions/Collection+Index.swift similarity index 100% rename from Balance/iOS/Extensions/Foundation/Collection+Index.swift rename to Balance/Shared/Data Model/Extensions/Collection+Index.swift diff --git a/Balance/Shared/Data Model/Extensions/Dictionary.swift b/Balance/Shared/Data Model/Extensions/Dictionary.swift new file mode 100644 index 00000000..f6bc38f6 --- /dev/null +++ b/Balance/Shared/Data Model/Extensions/Dictionary.swift @@ -0,0 +1,17 @@ +// +// Dictionary.swift +// Balance +// +// Created by Eli Pacheco Hoyos on 1/19/18. +// Copyright © 2018 Balanced Software, Inc. All rights reserved. +// + +import Foundation + +extension Dictionary where Value: Comparable { + var sortedByValue:[(Key,Value)] {return Array(self).sorted{$0.1 < $1.1}} +} + +extension Dictionary where Key: Comparable { + var sortedByKey:[(Key,Value)] {return Array(self).sorted{$0.0 < $1.0}} +} diff --git a/Balance/Shared/Data Model/Singleton/AppLock.swift b/Balance/Shared/Data Model/Singleton/AppLock.swift index c7b4e063..1c5636b7 100644 --- a/Balance/Shared/Data Model/Singleton/AppLock.swift +++ b/Balance/Shared/Data Model/Singleton/AppLock.swift @@ -9,8 +9,50 @@ import Foundation import LocalAuthentication -class AppLock { - var locked = false +fileprivate struct AutenticationInterval { + + let interval: TimeInterval + let fromDate: Date + + var toDate: Date { + return fromDate.addingTimeInterval(interval) + } + + var isValid: Bool { + let currentDate = Date() + let fromDatePosition = currentDate.compare(fromDate) + let isValidFromDate = fromDatePosition == .orderedDescending || fromDatePosition == .orderedSame + let isValidToDate = currentDate.compare(toDate) == .orderedAscending + + return isValidFromDate && isValidToDate + + } + + init(timeInterval: Double) { + fromDate = Date() + interval = timeInterval + } + +} + +class AppLock: AppLockServicesProtocol { + + private var interval: AutenticationInterval? + private var appLocked = false + + var locked: Bool { + set { + appLocked = newValue + } + + get { + guard skipBlock else { + return appLocked + } + + return false + } + } var password: String? { get { @@ -35,6 +77,7 @@ class AppLock { if let string = keychain[KeychainAccounts.AppLock, KeychainKeys.LockEnabled] { return string == "true" } + return false } set { @@ -45,6 +88,10 @@ class AppLock { var lockOnSleep: Bool { get { + guard !skipBlock else { + return false + } + if let string = keychain[KeychainAccounts.AppLock, KeychainKeys.LockOnSleep] { return string == "true" } @@ -60,6 +107,10 @@ class AppLock { var lockOnScreenSaver: Bool { get { + guard !skipBlock else { + return false + } + if let string = keychain[KeychainAccounts.AppLock, KeychainKeys.LockOnScreenSaver] { return string == "true" } @@ -75,6 +126,10 @@ class AppLock { var lockOnPopoverClose: Bool { get { + guard !skipBlock else { + return false + } + if let string = keychain[KeychainAccounts.AppLock, KeychainKeys.LockOnPopoverClose] { return string == "true" } @@ -90,9 +145,14 @@ class AppLock { var touchIdEnabled: Bool { get { + guard !skipBlock else { + return false + } + if let string = keychain[KeychainAccounts.AppLock, KeychainKeys.TouchIdEnabled] { return string == "true" } + return false } set { @@ -144,6 +204,21 @@ class AppLock { #endif } + func lock(until timeInterval: TimeInterval?) { + guard let timeInterval = timeInterval else { + self.interval = nil + return + } + + let interval = AutenticationInterval(timeInterval: timeInterval) + guard interval.isValid else { + self.interval = nil + return + } + + self.interval = interval + } + func authenticateTouchId(reason: String, completion: @escaping (_ success: Bool, _ error: Error?) -> Void) { if #available(OSX 10.12.2, *) { let context = LAContext() @@ -208,4 +283,38 @@ class AppLock { } #endif + +} + +extension AppLock { + + var lockAfterMinutes: Bool { + return interval?.isValid ?? false + } + + var lockInterval: TimeInterval? { + return interval?.interval + } + + var shouldPrepareBlock: Bool { + guard let interval = interval else { + #if os(OSX) + return appLocked + #else + return lockEnabled + #endif + } + + return !interval.isValid + } + + private var skipBlock: Bool { + guard let interval = interval, + interval.isValid else { + return false + } + + return true + } + } diff --git a/Balance/Shared/View Models/Preferences/PreferencesSecurityViewModel.swift b/Balance/Shared/View Models/Preferences/PreferencesSecurityViewModel.swift new file mode 100644 index 00000000..b8f4420e --- /dev/null +++ b/Balance/Shared/View Models/Preferences/PreferencesSecurityViewModel.swift @@ -0,0 +1,113 @@ +// +// PreferencesSecurityViewModel.swift +// BalancemacOS +// +// Created by Eli Pacheco Hoyos on 1/18/18. +// Copyright © 2018 Balanced Software, Inc. All rights reserved. +// + +protocol AppLockServicesProtocol { + var lockAfterMinutes: Bool { get } + var lockInterval: TimeInterval? { get } + func lock(until timeInterval: TimeInterval?) +} + +protocol TimeServicesProtocol { + var currentTime: Date? { get } + var timeIntervals: [String] { get } +} + +extension TimeServicesProtocol { + + var intervals: [String: TimeInterval] { + return [ + "15 Minutes" : TimeInterval.minute * Double(15), + "30 Minutes" : TimeInterval.minute * Double(30), + "45 Minutes" : TimeInterval.minute * Double(45), + "50 Minutes" : TimeInterval.minute * Double(50), + "1 Hour" : TimeInterval.hour, + "2 Hour" : TimeInterval.hour * Double(2), + "3 Hour" : TimeInterval.hour * Double(3), + "5 Hour" : TimeInterval.hour * Double(5), + "8 Hour" : TimeInterval.hour * Double(8), + "1 Day" : TimeInterval.day + ] + } + +} + +class TimeServices: TimeServicesProtocol { + + var currentTime: Date? { + return Date() + } + + var timeIntervals: [String] { + return intervals.sortedByValue.map { $0.0 } + } + + var lockInterval: TimeInterval? { + return appLock.lockInterval + } + + var lockAfterMinutes: Bool { + return appLock.lockAfterMinutes + } + + func lock(until timeInterval: TimeInterval?) { + appLock.lock(until: timeInterval) + } + +} + +class PreferencesSecurityViewModel { + + let timeServices: TimeServicesProtocol + let appLockServices: AppLockServicesProtocol + + var timeIntervals: [String] { + return timeServices.timeIntervals + } + + var currentTimeInterval: String { + return timeIntervals[selectedTimeInterval] + } + + var isLockAfterMinutesSelected: Bool { + return appLockServices.lockAfterMinutes + } + + var selectedTimeInterval: Int { + guard let lockInterval = appLockServices.lockInterval else { + return 0 + } + + for (index, intervalKey) in timeIntervals.enumerated() { + if timeServices.intervals[intervalKey] == lockInterval { + return index + } + } + + return 0 + } + + init(timeServices: TimeServicesProtocol? = nil, appLockServices: AppLockServicesProtocol? = nil) { + self.timeServices = timeServices ?? TimeServices() + self.appLockServices = appLockServices ?? appLock + } + + func selectTimeInterval(at index: Int) { + guard let selectedInterval = timeIntervals[safe: index], + let timeInterval = timeServices.intervals[selectedInterval] else { + print("Cant create interval for selected option") + return + } + + appLockServices.lock(until: timeInterval) + } + + func removeSkipBlock() { + appLockServices.lock(until: nil) + } + +} diff --git a/Balance/iOS/View Controllers/RootViewController.swift b/Balance/iOS/View Controllers/RootViewController.swift index 5d675369..6875e077 100644 --- a/Balance/iOS/View Controllers/RootViewController.swift +++ b/Balance/iOS/View Controllers/RootViewController.swift @@ -33,7 +33,7 @@ internal final class RootViewController: UIViewController private var viewHasAppeared = false private var shouldPresentUnlockViewController: Bool { - return appLock.lockEnabled // TODO: && check if logged in + return appLock.shouldPrepareBlock // TODO: && check if logged in } // MARK: Initialization diff --git a/Balance/macOS/AppDelegate.swift b/Balance/macOS/AppDelegate.swift index 0d4a76a6..431d3a89 100644 --- a/Balance/macOS/AppDelegate.swift +++ b/Balance/macOS/AppDelegate.swift @@ -244,7 +244,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { statusItem.drawBorder = CurrentTheme.type == .light statusItem.shouldShowHandler = { statusItem in - if appLock.locked && appLock.touchIdAvailable && appLock.touchIdEnabled { + if appLock.shouldPrepareBlock && appLock.touchIdAvailable && appLock.touchIdEnabled { self.promptTouchId() return false } @@ -340,7 +340,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } func promptTouchId() { - if appLock.locked && appLock.touchIdAvailable && appLock.touchIdEnabled { + if appLock.shouldPrepareBlock && appLock.touchIdAvailable && appLock.touchIdEnabled { appLock.authenticateTouchId(reason: "unlock Balance") { success, error in async(after: 0.1) { self.showPopover(force: true) diff --git a/Balance/macOS/View Controllers/Main App/PopoverViewController.swift b/Balance/macOS/View Controllers/Main App/PopoverViewController.swift index 945252cb..cc82d575 100644 --- a/Balance/macOS/View Controllers/Main App/PopoverViewController.swift +++ b/Balance/macOS/View Controllers/Main App/PopoverViewController.swift @@ -93,7 +93,8 @@ class PopoverViewController: NSViewController { NotificationCenter.postOnMainThread(name: Notifications.PopoverWillShow) - if appLock.locked { + if appLock.shouldPrepareBlock { + lockUserInterface(animated: false) lockController.willDisplayPopover() } } diff --git a/Balance/macOS/View Controllers/Preferences/PreferencesSecurityViewController.swift b/Balance/macOS/View Controllers/Preferences/PreferencesSecurityViewController.swift index e06d9813..d0f18350 100644 --- a/Balance/macOS/View Controllers/Preferences/PreferencesSecurityViewController.swift +++ b/Balance/macOS/View Controllers/Preferences/PreferencesSecurityViewController.swift @@ -5,7 +5,9 @@ import Foundation -class PreferencesSecurityViewController: NSViewController { +class PreferencesSecurityViewController: NSViewController, NSComboBoxDelegate { + + fileprivate let viewModel = PreferencesSecurityViewModel() //Password fileprivate let passwordTitleField = LabelField() @@ -21,6 +23,8 @@ class PreferencesSecurityViewController: NSViewController { fileprivate let lockSleepCheckBox = Button() fileprivate let lockScreenSaverCheckBox = Button() fileprivate let lockCloseCheckBox = Button() + private let lockEveryTimeCheckBox = Button() + private let comboBox = ComboBox() fileprivate let secondDividerBox = NSBox() @@ -153,13 +157,36 @@ class PreferencesSecurityViewController: NSViewController { make.left.equalTo(lockScreenSaverCheckBox) } + lockEveryTimeCheckBox.setButtonType(.switch) + lockEveryTimeCheckBox.font = NSFont.systemFont(ofSize: 12) + lockEveryTimeCheckBox.title = "Lock after" + lockEveryTimeCheckBox.setAccessibilityLabel("Lock password after") + lockEveryTimeCheckBox.action = #selector(lockEveryTime) + lockEveryTimeCheckBox.target = self + self.view.addSubview(lockEveryTimeCheckBox) + lockEveryTimeCheckBox.snp.makeConstraints{ make in + make.top.equalTo(lockCloseCheckBox.snp.bottom).offset(12) + make.left.equalTo(lockCloseCheckBox) + } + + comboBox.addItems(withObjectValues: viewModel.timeIntervals) + comboBox.selectItem(at: viewModel.selectedTimeInterval) + comboBox.delegate = self + comboBox.isEditable = false + self.view.addSubview(comboBox) + comboBox.snp.makeConstraints { make in + make.centerY.equalTo(lockEveryTimeCheckBox.snp.centerY) + make.right.equalToSuperview().inset(30) + make.width.equalTo(100) + } + if appLock.touchIdAvailable { secondDividerBox.title = "" secondDividerBox.boxType = .separator self.view.addSubview(secondDividerBox) secondDividerBox.snp.makeConstraints { make in make.height.equalTo(1) - make.top.equalTo(lockCloseCheckBox.snp.bottom).offset(15) + make.top.equalTo(lockEveryTimeCheckBox.snp.bottom).offset(15) make.left.equalTo(self.view).offset(20) make.right.equalTo(self.view).offset(-20) } @@ -205,7 +232,7 @@ class PreferencesSecurityViewController: NSViewController { make.right.equalTo(self.view).offset(-20) } } else { - self.view.setFrameSize(NSSize(width: 500, height: 190)) + self.view.setFrameSize(NSSize(width: 500, height: appLock.touchIdEnabled ? 230 : 190)) } updateButtonStates() @@ -239,7 +266,9 @@ class PreferencesSecurityViewController: NSViewController { lockSleepCheckBox.isEnabled = appLock.lockEnabled lockScreenSaverCheckBox.isEnabled = appLock.lockEnabled lockCloseCheckBox.isEnabled = appLock.lockEnabled - + lockEveryTimeCheckBox.state = viewModel.isLockAfterMinutesSelected ? .on : .off + comboBox.isEnabled = viewModel.isLockAfterMinutesSelected ? true : false + toggleTouchIDButton.isEnabled = lockEnabled if appLock.touchIdEnabled { toggleTouchIDButton.title = "Disable" @@ -256,6 +285,8 @@ class PreferencesSecurityViewController: NSViewController { touchIDIconImageView.alphaValue = appLock.lockEnabled ? 1.0 : 0.4 touchIDExplanationField.alphaValue = appLock.lockEnabled ? 1.0 : 0.4 } + + } @objc func showSetPasswordSheet(){ @@ -295,6 +326,17 @@ class PreferencesSecurityViewController: NSViewController { sender.state = appLock.lockOnPopoverClose ? .on : .off } + @objc func lockEveryTime(_ sender:NSButton) { + let enabled = (sender.state == .on) + comboBox.isEnabled = enabled + + if enabled { + viewModel.selectTimeInterval(at: 0) + } else { + viewModel.removeSkipBlock() + } + } + @objc func disableTouchID() { appLock.authenticateTouchId(reason: "disable Touch ID unlocking") { success, error in if success { @@ -316,4 +358,13 @@ class PreferencesSecurityViewController: NSViewController { @objc func windowDidEndSheet(notification: Notification) { updateButtonStates() } + + func comboBoxSelectionDidChange(_ notification: Notification) { + guard let comboBox = notification.object as? NSComboBox else { + return + } + + let selectedIndex = comboBox.indexOfSelectedItem + viewModel.selectTimeInterval(at: selectedIndex) + } }