Cover screen on .inactive (#282)

* Make unlock block actor-safe

* Cover views on .inactive, lock on .background
This commit is contained in:
Davide De Rosa 2023-04-05 16:31:17 +02:00 committed by GitHub
parent 0591363b15
commit e3cfdadf97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 25 deletions

View File

@ -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)

View File

@ -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
} catch {
}
do {
let isAuthorized = try await context.evaluatePolicy(
policy,
localizedReason: L10n.Global.Messages.unlockApp
)
return isAuthorized
} catch {
return false
}
}
}

View File

@ -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 {
return
switch state {
case .none:
break
case .covered:
state = .none
case .locked:
Task { @MainActor in
guard await unlockBlock() else {
return
}
state = .none
}
}
unlockBlock(isLocked)
}
}
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() {
}