Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions NextcloudTalk/Chat/BaseChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,15 @@ import Toast
NotificationPresenter.shared().present(text: NSLocalizedString("Message unpinned", comment: ""), dismissAfterDelay: 5.0, includedStyle: .success)
}

internal func prepareVoiceMessageForResend(_ message: NCChatMessage) {
message.sendingFailed = false
message.isOfflineMessage = false

// Like a resent text message, the message is sent now: it needs to be placed at the end of the chat
// and must not expire (getTemporaryMessages) based on the timestamp of the failed attempt
message.timestamp = Int(Date().timeIntervalSince1970)
}

func didPressResend(for message: NCChatMessage) {
// Make sure there's no unread message separator, as the indexpath could be invalid after removing a message
self.removeUnreadMessagesSeparator()
Expand All @@ -1371,6 +1380,13 @@ import Toast
if message.messageType != kMessageTypeVoiceMessage {
self.sendChatMessage(message: originalMessage, withParentMessage: message.parent, messageParameters: message.messageParametersJSONString ?? "", silently: message.isSilent)
} else {
// Show and store the message as sending again, so it can be marked as failed again
self.prepareVoiceMessageForResend(message)

RLMRealm.writeTransaction { realm in
realm.addOrUpdate(NCChatMessage(value: message))
}

if NCDatabaseManager.sharedInstance().roomHasTalkCapability(.chatReferenceId, for: room) {
self.appendTemporaryMessage(temporaryMessage: message)
}
Expand Down Expand Up @@ -2245,11 +2261,33 @@ import Toast
try await ChatFileUploader.upload(upload)
NCLog.log("Successfully uploaded and shared \(upload.fileName)")
} catch {
if let referenceId = upload.referenceId {
self.markTemporaryMessageAsFailed(referenceId: referenceId)
}

self.presentUploadError(error, for: upload)
}
}
}

internal func markTemporaryMessageAsFailed(referenceId: String) {
// Allow to resend or delete the message, instead of showing it as sending until it expires
RLMRealm.writeTransaction { _ in
if let managedTemporaryMessage = NCChatMessage.objects(where: "referenceId = %@ AND isTemporary = true", referenceId).firstObject() as? NCChatMessage {
managedTemporaryMessage.sendingFailed = true
managedTemporaryMessage.isOfflineMessage = false
}
}

self.modifyMessageWith(referenceId: referenceId) { message in
// The message might have been received from the server in the meantime
guard message.isTemporary else { return }

message.sendingFailed = true
message.isOfflineMessage = false
}
}

private func presentUploadError(_ error: Error, for upload: ChatFileUpload) {
let message: String

Expand All @@ -2270,6 +2308,9 @@ import Toast
case ChatFileUploadError.tooManyRequests:
NCLog.log("Too many requests while uploading")
message = NSLocalizedString("Too many requests, please try again later", comment: "")
case ChatFileUploadError.uploadFailed(_, let errorDescription) where !errorDescription.isEmpty:
NCLog.log("Failed to upload \(upload.fileName). Error: \(errorDescription)")
message = errorDescription
default:
NCLog.log("Failed to upload \(upload.fileName). Error: \(error.localizedDescription)")
message = NSLocalizedString("Unknown error occurred", comment: "")
Expand Down
54 changes: 54 additions & 0 deletions NextcloudTalkTests/Unit/Chat/UnitBaseChatViewControllerTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,58 @@ final class UnitBaseChatViewControllerTest: TestBaseRealm {
XCTAssertEqual(baseController.getCellHeight(for: testMessage, with: 300), 171.0)
}

func testMarkTemporaryMessageAsFailed() throws {
let temporaryMessage = NCChatMessage()
temporaryMessage.internalId = "temp-1"
temporaryMessage.referenceId = "reference-1"
temporaryMessage.messageType = "voice-message"
temporaryMessage.isTemporary = true

// A message received from the server with the same reference id
let receivedMessage = NCChatMessage()
receivedMessage.messageId = 1
receivedMessage.internalId = "internal-1"
receivedMessage.referenceId = "reference-1"

try? realm.transaction {
realm.add(temporaryMessage)
realm.add(receivedMessage)
}

baseController.markTemporaryMessageAsFailed(referenceId: "reference-1")

XCTAssertTrue(temporaryMessage.sendingFailed)
XCTAssertFalse(temporaryMessage.isOfflineMessage)
XCTAssertFalse(receivedMessage.sendingFailed)
}

func testMarkTemporaryMessageAsFailedIgnoresReceivedMessage() throws {
let receivedMessage = NCChatMessage()
receivedMessage.messageId = 1
receivedMessage.internalId = "internal-1"
receivedMessage.referenceId = "reference-1"

try? realm.transaction {
realm.add(receivedMessage)
}

baseController.markTemporaryMessageAsFailed(referenceId: "reference-1")

XCTAssertFalse(receivedMessage.sendingFailed)
}

func testPrepareVoiceMessageForResendAssignsNewTimestamp() throws {
let message = NCChatMessage()
message.sendingFailed = true
message.isOfflineMessage = true
message.timestamp = Int(Date().timeIntervalSince1970) - (60 * 60 * 13)

let before = Int(Date().timeIntervalSince1970)
baseController.prepareVoiceMessageForResend(message)

XCTAssertFalse(message.sendingFailed)
XCTAssertFalse(message.isOfflineMessage)
XCTAssertGreaterThanOrEqual(message.timestamp, before)
}

}
Loading