View Issue Details

IDProjectCategoryView StatusLast Update
0012150Talerwallet (iOS App)public2026-11-15 19:38
Reporterbot-cg [agent]  
PrioritynormalSeverityminor 
Status new 
Summary0012150: TalDir contacts can be added and edited but never deleted: wallet-core's deleteContact is not called anywhere (patch attached)
DescriptionA contact, once added -- including one added by mistake or with a typo in the
alias -- cannot be removed.

Views/TalDir/ContactsView.swift lists the contacts; a row opens
AddContactView, whose only button saves. Model/Model+TalDir.swift defines
getContacts and addContact and nothing else for contacts. wallet-core has had
the operation all along (WalletApiOperation.DeleteContact, request
{ contact: ContactEntry }); the app has no request struct for it and no
control that would use one.

Since a contact's identity is its alias, correcting a mistyped alias adds a
second entry and the wrong one stays.

Patch (contacts-delete.diff): a DeleteContact request and
WalletModel.deleteContact(), and a trailing swipe "Delete" on the rows of
both sections of the contact list (these are List rows, so the swipe works),
followed by a reload. The patch compiles (BUILD SUCCEEDED together with the
other patches of this tree); it has NOT been run against a wallet with
contacts yet.

TalDir is compiled into the GNU_TALER and TALER_NIGHTLY flavours only.
TagsNo tags attached.
Attached Files
contacts-delete.diff (3,201 bytes)   
diff --git a/TalerWallet1/Model/Model+TalDir.swift b/TalerWallet1/Model/Model+TalDir.swift
index 31ec8a24..f86edf0f 100644
--- a/TalerWallet1/Model/Model+TalDir.swift
+++ b/TalerWallet1/Model/Model+TalDir.swift
@@ -66,6 +66,16 @@ fileprivate struct AddContact: WalletBackendFormattedRequest {
     func args() -> Args { Args(contact: contact) }
     struct Response: Decodable {}
 }
+/// A request to delete a contact
+fileprivate struct DeleteContact: WalletBackendFormattedRequest {
+    var contact: ContactEntry
+    var operation: String { "deleteContact" }
+    struct Args: Encodable {
+        var contact: ContactEntry
+    }
+    func args() -> Args { Args(contact: contact) }
+    struct Response: Decodable {}
+}
 // MARK: - Contact calls
 extension WalletModel {
     nonisolated func getContacts(viewHandles: Bool = false)
@@ -85,6 +95,12 @@ extension WalletModel {
 //        controller.progressToken = request.operation
         let _ = try await sendRequest(request, viewHandles: viewHandles)
     }
+
+    nonisolated func deleteContact(_ contact: ContactEntry, viewHandles: Bool = false)
+      async throws {   // M for MainActor
+        let request = DeleteContact(contact: contact)
+        let _ = try await sendRequest(request, viewHandles: viewHandles)
+    }
 }
 // MARK: - Alias Types
 struct TaldirAliasType: Codable, Hashable {
diff --git a/TalerWallet1/Views/TalDir/ContactsView.swift b/TalerWallet1/Views/TalDir/ContactsView.swift
index 3faac812..7d893912 100644
--- a/TalerWallet1/Views/TalDir/ContactsView.swift
+++ b/TalerWallet1/Views/TalDir/ContactsView.swift
@@ -96,6 +96,22 @@ struct ContactsView: View {
         String(localized: "TitleContacts", defaultValue: "Contacts")
     }
 
+    /// wallet-core has had `deleteContact` all along; nothing in the app called it,
+    /// so a contact - including a mistyped one - could never be removed
+    @ViewBuilder func deleteButton(_ contact: ContactEntry) -> some View {
+        Button(role: .destructive) {
+            Task { // runs on MainActor
+                if let _ = try? await model.deleteContact(contact) {
+                    await controller.loadContacts(stack.push())
+                }
+            }
+        } label: {
+            Label("Delete", systemImage: "trash")
+        }
+        .tint(WalletColors().negative)
+        .accessibilityIdentifier("button.contact.delete")
+    }
+
     var body: some View {
 #if PRINT_CHANGES
         let _ = Self._printChanges()
@@ -120,6 +136,7 @@ struct ContactsView: View {
                                      contact: contact,
                                     talerURI: talerURI,
                                      subject: subject)
+                            .swipeActions(edge: .trailing) { deleteButton(contact) }
                     }
                 }
             }
@@ -132,6 +149,7 @@ struct ContactsView: View {
                                      contact: contact,
                                     talerURI: talerURI,
                                      subject: subject)
+                            .swipeActions(edge: .trailing) { deleteButton(contact) }
                     }
                 }
             }
contacts-delete.diff (3,201 bytes)   

Required notifications

Address or linkRemarkAdded byNotifiedActions

Relationships

child of 0012147 new iOS wallet: state the user can never delete -- audit against the rule that every item must be individually removable [meta] 

Activities

Issue History

Date ModifiedUsernameFieldChange
2026-11-15 19:38bot-cgNew Issue
2026-11-15 19:38bot-cgRelationship addedchild of 0012147
2026-11-15 19:38bot-cgImplementer => MarcS
2026-11-15 19:38bot-cgReviewer => bernaalp
2026-11-15 19:38bot-cgDeployed by => mikolai
2026-11-15 19:38bot-cgVerifier => Florian Dold
2026-11-15 19:38bot-cgCommunicator => CM_7
2026-11-15 19:38bot-cgFile Added: contacts-delete.diff