Share sheet: ask to unlock on every gated action, like the app

The sheet remembered one unlock for its whole lifetime, so later account picks
and hidden toggles skipped Face ID. Each switch / each turning-on of hidden
folders now prompts again; one action needing both is a single prompt.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Ayushya Amitabh
2026-10-06 20:24:20 -04:00
co-authored by Claude Sonnet 5.5
parent 02d9d799a6
commit 4ab6c5d85c
4 changed files with 104 additions and 28 deletions
+23 -27
View File
@@ -35,9 +35,6 @@ final class ShareModel: ObservableObject {
let shared: SharedAccounts?
/// One successful unlock covers every gate for the rest of this sheet.
private var unlocked = false
/// Set by the view controller: what each button actually does.
var onUpload: () -> Void = {}
var onSaveForLater: () -> Void = {}
@@ -103,24 +100,30 @@ final class ShareModel: ObservableObject {
func select(_ account: SharedAccount) async {
guard let shared else { return }
notice = nil
if account.id != shared.active?.id, shared.needsUnlockToSwitchAccount {
guard await unlock("Unlock to upload to \(account.displayName)") else {
notice = "Unlock to upload to a different account."
stage = .chooseAccount
return
}
}
NSLog("[ShareExtension] selected %@ (hidden=%@ storage=%@)", account.id, account.hiddenFilter, account.storageScope)
selected = account
externalRoots = []
storageFilter = StorageFilter(raw: account.storageScope)
hiddenFilter = HiddenFilter(raw: account.hiddenFilter)
if hiddenFilter != .hide, shared.needsUnlockForHidden {
if await !unlock("Unlock to show hidden folders") {
hiddenFilter = .hide
var wantedHidden = HiddenFilter(raw: account.hiddenFilter)
// Every selection asks again, like the app does for every switch - no
// unlock is remembered. If one action needs both unlocks, one prompt
// covers both.
let needs = shared.unlockNeeds(choosing: account, showing: wantedHidden)
if needs.needsPrompt {
let reason = needs.switchesAccount
? "Unlock to upload to \(account.displayName)" : "Unlock to show hidden folders"
if await !DeviceAuth.authenticate(reason: reason) {
guard !needs.switchesAccount else {
notice = "Unlock to upload to a different account."
stage = .chooseAccount
return
}
wantedHidden = .hide
notice = "Hidden folders are locked, so they're not shown."
}
}
NSLog("[ShareExtension] selected %@ (hidden=%@ storage=%@)", account.id, wantedHidden.rawValue, account.storageScope)
selected = account
externalRoots = []
storageFilter = StorageFilter(raw: account.storageScope)
hiddenFilter = wantedHidden
stage = .picking
await open("/")
}
@@ -132,8 +135,8 @@ final class ShareModel: ObservableObject {
/// between the two revealing modes.
func setHiddenFilter(_ filter: HiddenFilter) async {
guard filter != hiddenFilter else { return }
if hiddenFilter == .hide, shared?.needsUnlockForHidden == true {
guard await unlock("Unlock to show hidden folders") else {
if shared?.needsUnlockToChangeHidden(from: hiddenFilter, to: filter) == true {
guard await DeviceAuth.authenticate(reason: "Unlock to show hidden folders") else {
notice = "Hidden folders are locked, so they're not shown."
return
}
@@ -150,13 +153,6 @@ final class ShareModel: ObservableObject {
await open("/")
}
private func unlock(_ reason: String) async -> Bool {
if unlocked { return true }
let ok = await DeviceAuth.authenticate(reason: reason)
if ok { unlocked = true }
return ok
}
// MARK: - Browsing
/// A toggle can leave you inside a folder it now hides, so changing one