Require Face ID to open the app and to send
This commit is contained in:
@@ -15,6 +15,18 @@ final class AppModel: ObservableObject {
|
||||
@Published var moneroReady = false
|
||||
@Published var moneroAddress: String?
|
||||
@Published var moneroAccounts: [MoneroAccount] = []
|
||||
/// Whether the app is behind the biometric lock screen right now.
|
||||
@Published var locked = false
|
||||
|
||||
/// Face ID/Touch ID lock. On by default when the device has biometrics;
|
||||
/// persisted across launches.
|
||||
var faceIDEnabled: Bool {
|
||||
get { UserDefaults.standard.object(forKey: "koins.faceID") as? Bool ?? true }
|
||||
set {
|
||||
UserDefaults.standard.set(newValue, forKey: "koins.faceID")
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
/// Rough time remaining for the Monero sync, derived from observed block rate.
|
||||
@Published var syncEta: String?
|
||||
|
||||
@@ -41,6 +53,22 @@ final class AppModel: ObservableObject {
|
||||
|
||||
var symbol: String { chain.symbol }
|
||||
|
||||
// MARK: - Biometric lock
|
||||
|
||||
func authenticate() async {
|
||||
if await Biometrics.authenticate(reason: "Unlock Koins") {
|
||||
locked = false
|
||||
}
|
||||
}
|
||||
|
||||
/// Requires biometrics for sensitive actions when the lock is enabled; a
|
||||
/// device without biometrics is not asked (the wallet is still usable).
|
||||
func requireAuth(reason: String) async throws {
|
||||
guard faceIDEnabled, Biometrics.isAvailable else { return }
|
||||
let ok = await Biometrics.authenticate(reason: reason)
|
||||
if !ok { throw XRPError.rpc("Authentication failed") }
|
||||
}
|
||||
|
||||
// MARK: - Cache
|
||||
|
||||
/// Restores the last-known state for the chain on screen instantly; the
|
||||
@@ -78,6 +106,9 @@ final class AppModel: ObservableObject {
|
||||
if xrpWallet != nil || moneroReady {
|
||||
chain = xrpWallet != nil ? .xrp : .xmr
|
||||
}
|
||||
if xrpWallet != nil || xmrWallet != nil, faceIDEnabled, Biometrics.isAvailable {
|
||||
locked = true
|
||||
}
|
||||
showCached(chain)
|
||||
await refresh(force: true)
|
||||
startAutoRefresh()
|
||||
@@ -334,6 +365,7 @@ final class AppModel: ObservableObject {
|
||||
}
|
||||
|
||||
func revealSeed() async throws -> String {
|
||||
try await requireAuth(reason: "Reveal your recovery phrase")
|
||||
if chain == .xrp {
|
||||
guard let seed = WalletStore.secret(.xrp) else {
|
||||
throw XRPError.rpc("No stored secret for this wallet")
|
||||
@@ -347,6 +379,7 @@ final class AppModel: ObservableObject {
|
||||
// MARK: - Sends
|
||||
|
||||
func send(to: String, amount: String, tag: String, priority: TransactionPriority) async throws {
|
||||
try await requireAuth(reason: "Authorize this payment")
|
||||
let destinationTag = UInt32(tag.trimmingCharacters(in: .whitespaces))
|
||||
if chain == .xrp {
|
||||
guard let seed = WalletStore.secret(.xrp) else {
|
||||
|
||||
Reference in New Issue
Block a user