mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-27 00:25:22 +00:00
Surface refused media deletions in the affected chat
A refused delete/clear (journal write failure, reservation conflict, path collision, capacity) only logged SecureLogger.error, so a wedged /clear looked like success. Refusals now post a localized system message (content.system.media_delete_refused, 30 locales) into the affected private chat via the existing system-message mechanism, wired at both the per-bubble delete and /clear refusal sites. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -84,6 +84,10 @@ private final class MockChatMediaTransferContext: ChatMediaTransferContext {
|
||||
}
|
||||
|
||||
func addSystemMessage(_ content: String) { systemMessages.append(content) }
|
||||
private(set) var mediaDeletionRefusals: [String] = []
|
||||
func notifyMediaDeletionRefused(messageID: String) {
|
||||
mediaDeletionRefusals.append(messageID)
|
||||
}
|
||||
func notifyUIChanged() { notifyUIChangedCount += 1 }
|
||||
|
||||
// Delivery status & dedup
|
||||
@@ -643,6 +647,8 @@ struct ChatMediaTransferCoordinatorContextTests {
|
||||
#expect(context.removedMessages.isEmpty)
|
||||
#expect(context.cancelledTransfers == ["failed-delete"])
|
||||
#expect(coordinator.messageIDToTransferId[messageID] == nil)
|
||||
// The refusal must be visible in the affected chat, not just logged.
|
||||
#expect(context.mediaDeletionRefusals == [messageID])
|
||||
}
|
||||
|
||||
@Test @MainActor
|
||||
|
||||
@@ -1272,9 +1272,13 @@ struct ChatViewModelPrivateMediaDeletionTests {
|
||||
"images/incoming/\(filename)"
|
||||
]]
|
||||
)
|
||||
let messages = viewModel.privateChats[peerID] ?? []
|
||||
#expect(messages.prefix(2).map(\.id) == [stableID, legacyID])
|
||||
// The refusal is surfaced in the affected chat, not just logged.
|
||||
#expect(messages.last?.sender == "system")
|
||||
#expect(
|
||||
viewModel.privateChats[peerID]?.map(\.id)
|
||||
== [stableID, legacyID]
|
||||
messages.last?.content
|
||||
== String(localized: "content.system.media_delete_refused")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1374,10 +1378,17 @@ struct ChatViewModelPrivateMediaDeletionTests {
|
||||
#expect(
|
||||
transport.deletedPrivateMediaMessageIDBatches == [[incomingID]]
|
||||
)
|
||||
let messages = viewModel.privateChats[peerID] ?? []
|
||||
#expect(
|
||||
viewModel.privateChats[peerID]?.map(\.id)
|
||||
messages.prefix(3).map(\.id)
|
||||
== [incomingID, outgoingID, "ordinary-message"]
|
||||
)
|
||||
// The refused /clear is surfaced in the affected chat.
|
||||
#expect(messages.last?.sender == "system")
|
||||
#expect(
|
||||
messages.last?.content
|
||||
== String(localized: "content.system.media_delete_refused")
|
||||
)
|
||||
#expect(transport.cancelledTransfers == ["failed-clear-outgoing"])
|
||||
#expect(viewModel.messageIDToTransferId[outgoingID] == nil)
|
||||
}
|
||||
@@ -1446,9 +1457,12 @@ struct ChatViewModelPrivateMediaDeletionTests {
|
||||
transport.deletedPrivateMediaRelativePaths
|
||||
== [[incomingID: "images/incoming/\(filename)"]]
|
||||
)
|
||||
let messages = viewModel.privateChats[peerID] ?? []
|
||||
#expect(messages.prefix(2).map(\.id) == [incomingID, outgoingID])
|
||||
#expect(messages.last?.sender == "system")
|
||||
#expect(
|
||||
viewModel.privateChats[peerID]?.map(\.id)
|
||||
== [incomingID, outgoingID]
|
||||
messages.last?.content
|
||||
== String(localized: "content.system.media_delete_refused")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user