Skip to content

Commit

Permalink
Merge pull request #1874 from nextcloud/better-voice-message-handling
Browse files Browse the repository at this point in the history
added temporary voice messages
  • Loading branch information
SystemKeeper authored Dec 18, 2024
2 parents dcba74c + 65eea33 commit b99ba37
Show file tree
Hide file tree
Showing 16 changed files with 313 additions and 64 deletions.
10 changes: 10 additions & 0 deletions NextcloudTalk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,10 @@
DA66583127B6B24E00B46B11 /* UserProfileTableViewController+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA66583027B6B24E00B46B11 /* UserProfileTableViewController+Utils.swift */; };
DA75580F278EEA1000A48A1B /* SettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA75580E278EEA1000A48A1B /* SettingsTableViewController.swift */; };
DA8801A227A2DA00009EF248 /* UserProfileTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA8801A127A2DA00009EF248 /* UserProfileTableViewController.swift */; };
F644A2DD2CE287FA00E2ED81 /* ChatFileUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F644A2DC2CE287FA00E2ED81 /* ChatFileUploader.swift */; };
F644A2DF2CE28C8D00E2ED81 /* NCChatFileStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FF4DA7F2C023FF300C1B952 /* NCChatFileStatus.swift */; };
F644A2E02CE28C9A00E2ED81 /* NCChatFileStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FF4DA7F2C023FF300C1B952 /* NCChatFileStatus.swift */; };
F644A2E12CE28CA500E2ED81 /* NCChatFileStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FF4DA7F2C023FF300C1B952 /* NCChatFileStatus.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -1179,6 +1183,7 @@
DA66583027B6B24E00B46B11 /* UserProfileTableViewController+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserProfileTableViewController+Utils.swift"; sourceTree = "<group>"; };
DA75580E278EEA1000A48A1B /* SettingsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTableViewController.swift; sourceTree = "<group>"; };
DA8801A127A2DA00009EF248 /* UserProfileTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileTableViewController.swift; sourceTree = "<group>"; };
F644A2DC2CE287FA00E2ED81 /* ChatFileUploader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatFileUploader.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
Expand Down Expand Up @@ -2144,6 +2149,7 @@
1F35F8FA2AEEDBC600044BDA /* ChatViewControllerExtension.swift */,
2C4230F62B207AB00013E1FA /* ContextChatViewController.swift */,
1F0B0A712BA264540073FF8D /* MentionSuggestion.swift */,
F644A2DC2CE287FA00E2ED81 /* ChatFileUploader.swift */,
1F2058292CEA404F00AAA673 /* AiSummaryViewController.swift */,
1F20582B2CEA405700AAA673 /* AiSummaryViewController.xib */,
1F205B9F2CEE1B8800AAA673 /* AiSummaryController.swift */,
Expand Down Expand Up @@ -2801,6 +2807,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F644A2E12CE28CA500E2ED81 /* NCChatFileStatus.swift in Sources */,
1F1B504B2B90CF0C00B0F2F4 /* FederatedCapabilities.m in Sources */,
1F77A5F22AB9A436007B6037 /* EmojiUtils.swift in Sources */,
1F77A5FA2AB9A4DF007B6037 /* NCMessageLocationParameter.m in Sources */,
Expand Down Expand Up @@ -3087,6 +3094,7 @@
2CB6ACBC26385A3800D3D641 /* ShareLocationViewController.m in Sources */,
2C04249B2CA33681004772F6 /* AudioPlayerView.swift in Sources */,
1F1B0F462BE047CE003FD766 /* InteractionControlling.swift in Sources */,
F644A2DD2CE287FA00E2ED81 /* ChatFileUploader.swift in Sources */,
2CF338E12CED388B0029CACC /* AvatarView.swift in Sources */,
1FDCC3D429EBF6E700DEB39B /* AvatarImageView.swift in Sources */,
1FB78E262B6AE5A600B0D69D /* FederationInvitation.swift in Sources */,
Expand Down Expand Up @@ -3115,6 +3123,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F644A2E02CE28C9A00E2ED81 /* NCChatFileStatus.swift in Sources */,
1F4DD3ED2571C688007DC98E /* EmojiUtils.swift in Sources */,
1F35F8EB2AEEBC1100044BDA /* UIResponder+SLKAdditions.m in Sources */,
2C62B02424C1BDCF007E460A /* NCAppBranding.m in Sources */,
Expand Down Expand Up @@ -3202,6 +3211,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F644A2DF2CE28C8D00E2ED81 /* NCChatFileStatus.swift in Sources */,
2C1ABDCF257E939600AEDFB6 /* NCContact.m in Sources */,
2CC001DC24A37AD400A20167 /* NCAppBranding.m in Sources */,
2C4446D42658147900DF1DBC /* TalkAccount.m in Sources */,
Expand Down
4 changes: 2 additions & 2 deletions NextcloudTalk/BaseChatTableViewCell+Audio.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ extension BaseChatTableViewCell {
}

func audioPlayerPlayButtonPressed() {
guard let audioFileParameter = message?.file() else {
guard let audioFile = message else {
return
}

self.delegate?.cellWants(toPlayAudioFile: audioFileParameter)
self.delegate?.cellWants(toPlayAudioFile: audioFile)
}

func audioPlayerPauseButtonPressed() {
Expand Down
2 changes: 1 addition & 1 deletion NextcloudTalk/BaseChatTableViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protocol BaseChatTableViewCellDelegate: AnyObject {

func cellWants(toOpenLocation geoLocationRichObject: GeoLocationRichObject)

func cellWants(toPlayAudioFile fileParameter: NCMessageFileParameter)
func cellWants(toPlayAudioFile message: NCChatMessage)
func cellWants(toPauseAudioFile fileParameter: NCMessageFileParameter)
func cellWants(toChangeProgress progress: CGFloat, fromAudioFile fileParameter: NCMessageFileParameter)

Expand Down
177 changes: 130 additions & 47 deletions NextcloudTalk/BaseChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ import SwiftUI

// MARK: - Temporary messages

internal func createTemporaryMessage(message: String, replyTo parentMessage: NCChatMessage?, messageParameters: String, silently: Bool) -> NCChatMessage? {
internal func createTemporaryMessage(message: String, replyTo parentMessage: NCChatMessage?, messageParameters: String, silently: Bool, isVoiceMessage: Bool) -> NCChatMessage? {
let temporaryMessage = NCChatMessage()

temporaryMessage.accountId = self.account.accountId
Expand All @@ -515,14 +515,41 @@ import SwiftUI
temporaryMessage.actorType = "users"
temporaryMessage.timestamp = Int(Date().timeIntervalSince1970)
temporaryMessage.token = room.token
temporaryMessage.message = self.replaceMentionsDisplayNamesWithMentionsKeysInMessage(message: message, parameters: messageParameters)

let referenceId = "temp-\(Date().timeIntervalSince1970 * 1000)"
temporaryMessage.referenceId = NCUtils.sha1(fromString: referenceId)
temporaryMessage.internalId = referenceId
temporaryMessage.isTemporary = true
temporaryMessage.parentId = parentMessage?.internalId
temporaryMessage.messageParametersJSONString = messageParameters

if isVoiceMessage {
var messageParametersDict = [String: Any]()
let parameterId = UUID().uuidString

temporaryMessage.message = message
temporaryMessage.messageType = kMessageTypeVoiceMessage

let fileParameterDict: [String: Any] = [
"id": parameterId,
"type": "file",
"name": message,
"path": messageParameters,
"fileId": parameterId,
"fileName": message,
"filePath": messageParameters,
"fileLocalPath": messageParameters
]

messageParametersDict["file"] = fileParameterDict

if let jsonData = try? JSONSerialization.data(withJSONObject: messageParametersDict, options: []) {
let messageParametersJSONString = String(data: jsonData, encoding: .utf8) ?? ""
temporaryMessage.messageParametersJSONString = messageParametersJSONString
}
} else {
temporaryMessage.messageParametersJSONString = messageParameters
temporaryMessage.message = self.replaceMentionsDisplayNamesWithMentionsKeysInMessage(message: message, parameters: messageParameters)
}
temporaryMessage.isSilent = silently
temporaryMessage.isMarkdownMessage = NCDatabaseManager.sharedInstance().roomHasTalkCapability(kCapabilityMarkdownMessages, for: self.room)

Expand Down Expand Up @@ -991,8 +1018,25 @@ import SwiftUI
self.removeUnreadMessagesSeparator()

self.removePermanentlyTemporaryMessage(temporaryMessage: message)
let originalMessage = self.replaceMessageMentionsKeysWithMentionsDisplayNames(message: message.message, parameters: message.messageParametersJSONString ?? "")
self.sendChatMessage(message: originalMessage, withParentMessage: message.parent, messageParameters: message.messageParametersJSONString ?? "", silently: message.isSilent)
guard var originalMessage = message.message else { return }
if message.messageType != kMessageTypeVoiceMessage {
originalMessage = self.replaceMessageMentionsKeysWithMentionsDisplayNames(message: message.message, parameters: message.messageParametersJSONString ?? "")
self.sendChatMessage(message: originalMessage, withParentMessage: message.parent, messageParameters: message.messageParametersJSONString ?? "", silently: message.isSilent)
} else {
let activeAccount = NCDatabaseManager.sharedInstance().activeAccount()
if NCDatabaseManager.sharedInstance().roomHasTalkCapability(kCapabilityChatReferenceId, for: room) {
self.appendTemporaryMessage(temporaryMessage: message)
}
NCAPIController.sharedInstance().uniqueNameForFileUpload(withName: originalMessage, originalName: true, for: activeAccount, withCompletionBlock: { fileServerURL, fileServerPath, _, _ in
if let fileServerURL, let fileServerPath {
let talkMetaData: [String: String] = ["messageType": "voice-message"]

self.uploadFileAtPath(localPath: message.file().fileStatus!.fileLocalPath!, withFileServerURL: fileServerURL, andFileServerPath: fileServerPath, withMetaData: talkMetaData, temporaryMessage: message)
} else {
NSLog("Could not find unique name for voice message file.")
}
})
}
}

func didPressCopy(for message: NCChatMessage) {
Expand Down Expand Up @@ -1521,7 +1565,7 @@ import SwiftUI

NCAPIController.sharedInstance().uniqueNameForFileUpload(withName: contactFileName, originalName: true, for: self.account) { fileServerURL, fileServerPath, _, _ in
if let fileServerURL, let fileServerPath {
self.uploadFileAtPath(localPath: url.path, withFileServerURL: fileServerURL, andFileServerPath: fileServerPath, withMetaData: nil)
self.uploadFileAtPath(localPath: url.path, withFileServerURL: fileServerURL, andFileServerPath: fileServerPath, withMetaData: nil, temporaryMessage: nil)
} else {
print("Could not find unique name for contact file")
}
Expand Down Expand Up @@ -1695,6 +1739,7 @@ import SwiftUI
}

func shareVoiceMessage() {
guard let recorder = self.recorder else { return }
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH-mm-ss"
let dateString = dateFormatter.string(from: Date())
Expand All @@ -1717,51 +1762,79 @@ import SwiftUI

audioFileName += ".mp3"

NCAPIController.sharedInstance().uniqueNameForFileUpload(withName: audioFileName, originalName: true, for: self.account, withCompletionBlock: { fileServerURL, fileServerPath, _, _ in
if let fileServerURL, let fileServerPath, let recorder = self.recorder {
let talkMetaData: [String: String] = ["messageType": "voice-message"]
self.uploadFileAtPath(localPath: recorder.url.path, withFileServerURL: fileServerURL, andFileServerPath: fileServerPath, withMetaData: talkMetaData)
} else {
NSLog("Could not find unique name for voice message file.")
let activeAccount = NCDatabaseManager.sharedInstance().activeAccount()
let chatFileController = NCChatFileController()
chatFileController.initDownloadDirectory(for: activeAccount)

let tempDirectoryURL = URL(fileURLWithPath: chatFileController.tempDirectoryPath)
let destinationFilePath = tempDirectoryURL.appendingPathComponent(audioFileName).path

if let temporaryMessage = self.createTemporaryMessage(
message: audioFileName,
replyTo: nil,
messageParameters: "\(destinationFilePath)",
silently: false,
isVoiceMessage: true
) {
let movedFileToTemporaryDirectory = chatFileController.moveFileToTemporaryDirectory(
fromSourcePath: recorder.url.path,
destinationPath: destinationFilePath
)

if !movedFileToTemporaryDirectory {
print("Failed to move voice-message to temporary directory.")
return
}
})
}

func uploadFileAtPath(localPath: String, withFileServerURL fileServerURL: String, andFileServerPath fileServerPath: String, withMetaData talkMetaData: [String: String]?) {
NCAPIController.sharedInstance().setupNCCommunication(for: self.account)
if movedFileToTemporaryDirectory, NCDatabaseManager.sharedInstance().roomHasTalkCapability(kCapabilityChatReferenceId, for: room) {
self.appendTemporaryMessage(temporaryMessage: temporaryMessage)
}

NextcloudKit.shared.upload(serverUrlFileName: fileServerURL, fileNameLocalPath: localPath, taskHandler: { _ in
NSLog("Upload task")
}, progressHandler: { progress in
NSLog("Progress:%f", progress.fractionCompleted)
}, completionHandler: { _, _, _, _, _, _, _, error in
NSLog("Upload completed with error code: %ld", error.errorCode)
NCAPIController.sharedInstance().uniqueNameForFileUpload(withName: audioFileName, originalName: true, for: activeAccount, withCompletionBlock: { fileServerURL, fileServerPath, _, _ in
if let fileServerURL, let fileServerPath {
let talkMetaData: [String: String] = ["messageType": "voice-message"]

if error.errorCode == 0 {
NCAPIController.sharedInstance().shareFileOrFolder(for: self.account, atPath: fileServerPath, toRoom: self.room.token, talkMetaData: talkMetaData, withCompletionBlock: { error in
if error != nil {
NSLog("Failed to share voice message")
}
})
} else if error.errorCode == 404 || error.errorCode == 409 {
NCAPIController.sharedInstance().checkOrCreateAttachmentFolder(for: self.account, withCompletionBlock: { created, _ in
if created {
self.uploadFileAtPath(localPath: localPath, withFileServerURL: fileServerURL, andFileServerPath: fileServerPath, withMetaData: talkMetaData)
} else {
NSLog("Failed to check or create attachment folder")
}
})
} else if error.errorCode == 507 {
let alert = UIAlertController(title: NSLocalizedString("Upload failed", comment: ""),
message: error.errorDescription,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default))
self.present(alert, animated: true)
NSLog("Failed to upload voice message due to missing user storage quota")
} else {
NSLog("Failed upload voice message")
self.uploadFileAtPath(localPath: destinationFilePath, withFileServerURL: fileServerURL, andFileServerPath: fileServerPath, withMetaData: talkMetaData, temporaryMessage: temporaryMessage)
} else {
NSLog("Could not find unique name for voice message file.")
}
})
} else {
print("Temporary message could not be created")
}
}

func uploadFileAtPath(localPath: String, withFileServerURL fileServerURL: String, andFileServerPath fileServerPath: String, withMetaData talkMetaData: [String: String]?, temporaryMessage: NCChatMessage?) {

ChatFileUploader.uploadFile(localPath: localPath,
fileServerURL: fileServerURL,
fileServerPath: fileServerPath,
talkMetaData: talkMetaData,
temporaryMessage: temporaryMessage,
room: self.room) { statusCode, errorMessage in
DispatchQueue.main.async {
switch statusCode {
case 200:
NSLog("Successfully uploaded and shared voice message")
case 401:
NSLog("No active account found")
NCUserInterfaceController.sharedInstance().presentAlert(withTitle: NSLocalizedString("Upload failed", comment: ""), withMessage: NSLocalizedString("No active account found", comment: ""))
case 403:
NSLog("Failed to share voice message")
NCUserInterfaceController.sharedInstance().presentAlert(withTitle: NSLocalizedString("Upload failed", comment: ""), withMessage: NSLocalizedString("Failed to share recording", comment: ""))
case 404, 409:
NSLog("Failed to check or create attachment folder")
NCUserInterfaceController.sharedInstance().presentAlert(withTitle: NSLocalizedString("Upload failed", comment: ""), withMessage: NSLocalizedString("Failed to check or create attachment folder", comment: ""))
case 507:
NSLog("User storage quota exceeded")
NCUserInterfaceController.sharedInstance().presentAlert(withTitle: NSLocalizedString("Upload failed", comment: ""),
withMessage: NSLocalizedString("User storage quota exceeded", comment: ""))
default:
NSLog("Failed upload voice message with error code \(statusCode)")
NCUserInterfaceController.sharedInstance().presentAlert(withTitle: NSLocalizedString("Upload failed", comment: ""), withMessage: NSLocalizedString("Unknown error occurred", comment: ""))
}
}
})
}
}

// MARK: - AVAudioRecorder Delegate
Expand Down Expand Up @@ -3415,12 +3488,22 @@ import SwiftUI

// MARK: - VoiceMessageTableViewCellDelegate

public func cellWants(toPlayAudioFile fileParameter: NCMessageFileParameter) {
public func cellWants(toPlayAudioFile message: NCChatMessage) {
guard let fileParameter = message.file() else {
print("No file for message found")
return
}

if fileParameter.fileStatus != nil && fileParameter.fileStatus?.isDownloading ?? false {
print("File already downloading -> skipping new download")
return
}

if let fileStatus = fileParameter.fileStatus, fileStatus.fileLocalPath != nil && FileManager.default.fileExists(atPath: fileParameter.fileStatus?.fileLocalPath ?? "") {
self.setupVoiceMessagePlayer(with: fileParameter.fileStatus!)
return
}

if let voiceMessagesPlayer = self.voiceMessagesPlayer,
let playerAudioFileStatus = self.playerAudioFileStatus,
!voiceMessagesPlayer.isPlaying,
Expand Down
Loading

0 comments on commit b99ba37

Please sign in to comment.