Cover screen on .inactive (#282)
* Make unlock block actor-safe * Cover views on .inactive, lock on .background
This commit is contained in:
parent
0591363b15
commit
e3cfdadf97
|
@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Added
|
||||
|
||||
- Option to lock app when entering background (iOS). [#270](https://github.com/passepartoutvpn/passepartout-apple/pull/270), [#271](https://github.com/passepartoutvpn/passepartout-apple/pull/271), [#273](https://github.com/passepartoutvpn/passepartout-apple/pull/273), [#275](https://github.com/passepartoutvpn/passepartout-apple/pull/275)
|
||||
- Option to lock app when entering background (iOS). [#270](https://github.com/passepartoutvpn/passepartout-apple/pull/270), [#271](https://github.com/passepartoutvpn/passepartout-apple/pull/271), [#273](https://github.com/passepartoutvpn/passepartout-apple/pull/273), [#275](https://github.com/passepartoutvpn/passepartout-apple/pull/275), [#282](https://github.com/passepartoutvpn/passepartout-apple/pull/282)
|
||||
- 3D Touch items (iOS). [#267](https://github.com/passepartoutvpn/passepartout-apple/pull/267)
|
||||
- Ukranian translations (Dmitry Chirkin). [#243](https://github.com/passepartoutvpn/passepartout-apple/pull/243)
|
||||
- Restore DNS "Domain" setting. [#260](https://github.com/passepartoutvpn/passepartout-apple/pull/260)
|
||||
|
|
|
@ -498,23 +498,21 @@ extension View {
|
|||
)
|
||||
}
|
||||
|
||||
private static func themeUnlockScreenBlock(isLocked: Binding<Bool>) {
|
||||
private static func themeUnlockScreenBlock() async -> Bool {
|
||||
let context = LAContext()
|
||||
let policy: LAPolicy = .deviceOwnerAuthentication
|
||||
var error: NSError?
|
||||
guard context.canEvaluatePolicy(policy, error: &error) else {
|
||||
isLocked.wrappedValue = false
|
||||
return
|
||||
return true
|
||||
}
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let isAuthorized = try await context.evaluatePolicy(
|
||||
policy,
|
||||
localizedReason: L10n.Global.Messages.unlockApp
|
||||
)
|
||||
isLocked.wrappedValue = !isAuthorized
|
||||
return isAuthorized
|
||||
} catch {
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,24 +32,36 @@ struct LockableView<Content: View, LockedContent: View>: View {
|
|||
|
||||
let lockedContent: () -> LockedContent
|
||||
|
||||
let unlockBlock: (Binding<Bool>) -> Void
|
||||
let unlockBlock: () async -> Bool
|
||||
|
||||
@Environment(\.scenePhase) private var scenePhase
|
||||
|
||||
@ObservedObject private var lock: Lock = .shared
|
||||
|
||||
private var isLocked: Binding<Bool> {
|
||||
.init {
|
||||
Lock.shared.isActive
|
||||
@Binding private var state: Lock.State
|
||||
|
||||
init(
|
||||
locksInBackground: Binding<Bool>,
|
||||
content: @escaping () -> Content,
|
||||
lockedContent: @escaping () -> LockedContent,
|
||||
unlockBlock: @escaping () async -> Bool
|
||||
) {
|
||||
_locksInBackground = locksInBackground
|
||||
self.content = content
|
||||
self.lockedContent = lockedContent
|
||||
self.unlockBlock = unlockBlock
|
||||
|
||||
_state = .init {
|
||||
Lock.shared.state
|
||||
} set: {
|
||||
Lock.shared.isActive = $0
|
||||
Lock.shared.state = $0
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
content()
|
||||
if isLocked.wrappedValue {
|
||||
if locksInBackground && state != .none {
|
||||
lockedContent()
|
||||
}
|
||||
}.onChange(of: scenePhase, perform: onScenePhase)
|
||||
|
@ -60,6 +72,11 @@ struct LockableView<Content: View, LockedContent: View>: View {
|
|||
case .active:
|
||||
unlockIfNeeded()
|
||||
|
||||
case .inactive:
|
||||
if state == .none {
|
||||
state = .covered
|
||||
}
|
||||
|
||||
case .background:
|
||||
lockIfNeeded()
|
||||
|
||||
|
@ -72,25 +89,44 @@ struct LockableView<Content: View, LockedContent: View>: View {
|
|||
guard locksInBackground else {
|
||||
return
|
||||
}
|
||||
isLocked.wrappedValue = true
|
||||
state = .locked
|
||||
}
|
||||
|
||||
func unlockIfNeeded() {
|
||||
guard locksInBackground else {
|
||||
isLocked.wrappedValue = false
|
||||
state = .none
|
||||
return
|
||||
}
|
||||
guard isLocked.wrappedValue else {
|
||||
switch state {
|
||||
case .none:
|
||||
break
|
||||
|
||||
case .covered:
|
||||
state = .none
|
||||
|
||||
case .locked:
|
||||
Task { @MainActor in
|
||||
guard await unlockBlock() else {
|
||||
return
|
||||
}
|
||||
unlockBlock(isLocked)
|
||||
state = .none
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Lock: ObservableObject {
|
||||
enum State {
|
||||
case none
|
||||
|
||||
case covered
|
||||
|
||||
case locked
|
||||
}
|
||||
|
||||
static let shared = Lock()
|
||||
|
||||
@Published var isActive = true
|
||||
@Published var state: State = .locked
|
||||
|
||||
private init() {
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue