From 35aa953cf917fb9367848b227e3f67e33e6cbf92 Mon Sep 17 00:00:00 2001 From: Daniel Standfest Date: Mon, 11 Nov 2024 00:34:12 +0100 Subject: [PATCH] added temporary voice messages related to #1261 Signed-off-by: Daniel Standfest --- NextcloudTalk.xcodeproj/project.pbxproj | 10 + .../BaseChatTableViewCell+Audio.swift | 4 +- NextcloudTalk/BaseChatTableViewCell.swift | 2 +- NextcloudTalk/BaseChatViewController.swift | 179 +++++++++++++----- NextcloudTalk/ChatFileUploader.swift | 63 ++++++ NextcloudTalk/ChatViewController.swift | 2 +- NextcloudTalk/DirectoryTableViewController.m | 2 +- NextcloudTalk/NCAPIController.h | 2 +- NextcloudTalk/NCAPIController.m | 7 +- NextcloudTalk/NCChatController.m | 47 ++++- NextcloudTalk/NCChatFileController.h | 3 + NextcloudTalk/NCChatFileController.m | 30 ++- NextcloudTalk/NCChatFileStatus.swift | 3 +- NextcloudTalk/NCMessageFileParameter.m | 12 ++ NextcloudTalk/en.lproj/Localizable.strings | 9 + .../ShareConfirmationViewController.swift | 2 +- 16 files changed, 312 insertions(+), 65 deletions(-) create mode 100644 NextcloudTalk/ChatFileUploader.swift diff --git a/NextcloudTalk.xcodeproj/project.pbxproj b/NextcloudTalk.xcodeproj/project.pbxproj index 13db96efc..d27d1a5a2 100644 --- a/NextcloudTalk.xcodeproj/project.pbxproj +++ b/NextcloudTalk.xcodeproj/project.pbxproj @@ -607,6 +607,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 */ @@ -1207,6 +1211,7 @@ DA66583027B6B24E00B46B11 /* UserProfileTableViewController+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserProfileTableViewController+Utils.swift"; sourceTree = ""; }; DA75580E278EEA1000A48A1B /* SettingsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTableViewController.swift; sourceTree = ""; }; DA8801A127A2DA00009EF248 /* UserProfileTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileTableViewController.swift; sourceTree = ""; }; + F644A2DC2CE287FA00E2ED81 /* ChatFileUploader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatFileUploader.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -2148,6 +2153,7 @@ 1F35F8FA2AEEDBC600044BDA /* ChatViewControllerExtension.swift */, 2C4230F62B207AB00013E1FA /* ContextChatViewController.swift */, 1F0B0A712BA264540073FF8D /* MentionSuggestion.swift */, + F644A2DC2CE287FA00E2ED81 /* ChatFileUploader.swift */, ); name = Chat; sourceTree = ""; @@ -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 */, @@ -3090,6 +3097,7 @@ 2CB6ACBC26385A3800D3D641 /* ShareLocationViewController.m in Sources */, 2C04249B2CA33681004772F6 /* AudioPlayerView.swift in Sources */, 1F1B0F462BE047CE003FD766 /* InteractionControlling.swift in Sources */, + F644A2DD2CE287FA00E2ED81 /* ChatFileUploader.swift in Sources */, 1FDCC3D429EBF6E700DEB39B /* AvatarImageView.swift in Sources */, 1FB78E262B6AE5A600B0D69D /* FederationInvitation.swift in Sources */, 1FDFC94D2BA50B9100670DF4 /* UIFontExtension.swift in Sources */, @@ -3117,6 +3125,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 */, @@ -3207,6 +3216,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 */, diff --git a/NextcloudTalk/BaseChatTableViewCell+Audio.swift b/NextcloudTalk/BaseChatTableViewCell+Audio.swift index 3cdd66c5c..8109675a7 100644 --- a/NextcloudTalk/BaseChatTableViewCell+Audio.swift +++ b/NextcloudTalk/BaseChatTableViewCell+Audio.swift @@ -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() { diff --git a/NextcloudTalk/BaseChatTableViewCell.swift b/NextcloudTalk/BaseChatTableViewCell.swift index d30d164c0..3647b9bb2 100644 --- a/NextcloudTalk/BaseChatTableViewCell.swift +++ b/NextcloudTalk/BaseChatTableViewCell.swift @@ -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) diff --git a/NextcloudTalk/BaseChatViewController.swift b/NextcloudTalk/BaseChatViewController.swift index d7abc71cc..87b0307ee 100644 --- a/NextcloudTalk/BaseChatViewController.swift +++ b/NextcloudTalk/BaseChatViewController.swift @@ -465,7 +465,7 @@ import QuickLook // 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() let activeAccount = NCDatabaseManager.sharedInstance().activeAccount() @@ -475,14 +475,41 @@ import QuickLook 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) @@ -954,8 +981,25 @@ import QuickLook 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) { @@ -1461,7 +1505,7 @@ import QuickLook NCAPIController.sharedInstance().uniqueNameForFileUpload(withName: contactFileName, originalName: true, for: activeAccount) { 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") } @@ -1567,6 +1611,7 @@ import QuickLook } 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()) @@ -1590,52 +1635,78 @@ import QuickLook audioFileName += ".mp3" let activeAccount = NCDatabaseManager.sharedInstance().activeAccount() - NCAPIController.sharedInstance().uniqueNameForFileUpload(withName: audioFileName, originalName: true, for: activeAccount, 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 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 } - }) + + if movedFileToTemporaryDirectory, NCDatabaseManager.sharedInstance().roomHasTalkCapability(kCapabilityChatReferenceId, for: room) { + self.appendTemporaryMessage(temporaryMessage: temporaryMessage) + } + + 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"] + + 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]?) { - let activeAccount = NCDatabaseManager.sharedInstance().activeAccount() - NCAPIController.sharedInstance().setupNCCommunication(for: activeAccount) - - 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) - - if error.errorCode == 0 { - NCAPIController.sharedInstance().shareFileOrFolder(for: activeAccount, 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: activeAccount, 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") + 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 @@ -3236,12 +3307,22 @@ import QuickLook // 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, diff --git a/NextcloudTalk/ChatFileUploader.swift b/NextcloudTalk/ChatFileUploader.swift new file mode 100644 index 000000000..908146182 --- /dev/null +++ b/NextcloudTalk/ChatFileUploader.swift @@ -0,0 +1,63 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import Foundation +import NextcloudKit + +@objcMembers public class ChatFileUploader: NSObject { + + public static func uploadFile(localPath: String, + fileServerURL: String, + fileServerPath: String, + talkMetaData: [String: String]?, + temporaryMessage: NCChatMessage?, + room: NCRoom, + completion: @escaping (Int, NSString?) -> Void) { + + let activeAccount = NCDatabaseManager.sharedInstance().activeAccount() + + NCAPIController.sharedInstance().setupNCCommunication(for: activeAccount) + + NextcloudKit.shared.upload(serverUrlFileName: fileServerURL, + fileNameLocalPath: localPath, + taskHandler: { _ in + NSLog("Upload task started") + }, + progressHandler: { progress in + NSLog("Upload Progress: \(progress.fractionCompleted * 100)%") + }, + completionHandler: { _, _, _, _, _, _, _, error in + NSLog("Upload completed with error code: \(error.errorCode)") + + switch error.errorCode { + case 0: + NCAPIController.sharedInstance().shareFileOrFolder(for: activeAccount, + atPath: fileServerPath, + toRoom: room.token, + talkMetaData: talkMetaData, + referenceId: temporaryMessage?.referenceId) { shareError in + if let shareError = shareError { + NSLog("Failed to share voice message: \(shareError.localizedDescription)") + completion(403, "Failed to share voice message") + } else { + completion(200, nil) + } + } + case 404, 409: + NCAPIController.sharedInstance().checkOrCreateAttachmentFolder(for: activeAccount) { created, _ in + if created { + uploadFile(localPath: localPath, fileServerURL: fileServerURL, fileServerPath: fileServerPath, talkMetaData: talkMetaData, temporaryMessage: temporaryMessage, room: room, completion: completion) + } else { + completion(404, "Failed to check or create attachment folder") + } + } + case 507: + completion(507, "User storage quota exceeded") + default: + completion(NSInteger(error.errorCode), "Failed to upload voice message with error code: \(error.errorCode)" as NSString) + } + }) + } +} diff --git a/NextcloudTalk/ChatViewController.swift b/NextcloudTalk/ChatViewController.swift index 0f3649c32..b7a086d7f 100644 --- a/NextcloudTalk/ChatViewController.swift +++ b/NextcloudTalk/ChatViewController.swift @@ -527,7 +527,7 @@ import UIKit override func sendChatMessage(message: String, withParentMessage parentMessage: NCChatMessage?, messageParameters: String, silently: Bool) { // Create temporary message - let temporaryMessage = self.createTemporaryMessage(message: message, replyTo: parentMessage, messageParameters: messageParameters, silently: silently) + guard let temporaryMessage = self.createTemporaryMessage(message: message, replyTo: parentMessage, messageParameters: messageParameters, silently: silently, isVoiceMessage: false) else { return } if NCDatabaseManager.sharedInstance().roomHasTalkCapability(kCapabilityChatReferenceId, for: room) { self.appendTemporaryMessage(temporaryMessage: temporaryMessage) diff --git a/NextcloudTalk/DirectoryTableViewController.m b/NextcloudTalk/DirectoryTableViewController.m index aaac5743a..6e2edede4 100644 --- a/NextcloudTalk/DirectoryTableViewController.m +++ b/NextcloudTalk/DirectoryTableViewController.m @@ -190,7 +190,7 @@ - (void)sortItemsInDirectory - (void)shareFileWithPath:(NSString *)path { [self setSharingFileUI]; - [[NCAPIController sharedInstance] shareFileOrFolderForAccount:[[NCDatabaseManager sharedInstance] activeAccount] atPath:path toRoom:_token talkMetaData:nil withCompletionBlock:^(NSError *error) { + [[NCAPIController sharedInstance] shareFileOrFolderForAccount:[[NCDatabaseManager sharedInstance] activeAccount] atPath:path toRoom:_token talkMetaData:nil referenceId: nil withCompletionBlock:^(NSError *error) { if (!error) { [self dismissViewControllerAnimated:YES completion:nil]; } else { diff --git a/NextcloudTalk/NCAPIController.h b/NextcloudTalk/NCAPIController.h index 4dbf0010a..2a541691f 100644 --- a/NextcloudTalk/NCAPIController.h +++ b/NextcloudTalk/NCAPIController.h @@ -233,7 +233,7 @@ extern NSInteger const kReceivedChatMessagesLimit; // DAV client - (void)readFolderForAccount:(TalkAccount *)account atPath:(NSString *)path depth:(NSString *)depth withCompletionBlock:(ReadFolderCompletionBlock)block; -- (void)shareFileOrFolderForAccount:(TalkAccount *)account atPath:(NSString *)path toRoom:(NSString *)token talkMetaData:(NSDictionary *)talkMetaData withCompletionBlock:(ShareFileOrFolderCompletionBlock)block; +- (void)shareFileOrFolderForAccount:(TalkAccount *)account atPath:(NSString *)path toRoom:(NSString *)token talkMetaData:(NSDictionary *)talkMetaData referenceId:(NSString *)referenceId withCompletionBlock:(ShareFileOrFolderCompletionBlock)block; - (void)getFileByFileId:(TalkAccount *)account fileId:(NSString *)fileId withCompletionBlock:(GetFileByFileIdCompletionBlock)block; - (void)uniqueNameForFileUploadWithName:(NSString *)fileName originalName:(BOOL)isOriginalName forAccount:(TalkAccount *)account withCompletionBlock:(GetFileUniqueNameCompletionBlock)block; diff --git a/NextcloudTalk/NCAPIController.m b/NextcloudTalk/NCAPIController.m index fbe05d5e0..314616fd0 100644 --- a/NextcloudTalk/NCAPIController.m +++ b/NextcloudTalk/NCAPIController.m @@ -1904,7 +1904,7 @@ - (void)readFolderForAccount:(TalkAccount *)account atPath:(NSString *)path dept }]; } -- (void)shareFileOrFolderForAccount:(TalkAccount *)account atPath:(NSString *)path toRoom:(NSString *)token talkMetaData:(NSDictionary *)talkMetaData withCompletionBlock:(ShareFileOrFolderCompletionBlock)block +- (void)shareFileOrFolderForAccount:(TalkAccount *)account atPath:(NSString *)path toRoom:(NSString *)token talkMetaData:(NSDictionary *)talkMetaData referenceId:(NSString *)referenceId withCompletionBlock:(ShareFileOrFolderCompletionBlock)block { NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/files_sharing/api/v1/shares", account.server]; @@ -1912,7 +1912,10 @@ - (void)shareFileOrFolderForAccount:(TalkAccount *)account atPath:(NSString *)pa [parameters setObject:path forKey:@"path"]; [parameters setObject:@(10) forKey:@"shareType"]; [parameters setObject:token forKey:@"shareWith"]; - + if (referenceId) { + [parameters setObject:referenceId forKey:@"referenceId"]; + } + if (talkMetaData) { NSError *error = nil; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:talkMetaData diff --git a/NextcloudTalk/NCChatController.m b/NextcloudTalk/NCChatController.m index 623c614b7..b2c92c2f4 100644 --- a/NextcloudTalk/NCChatController.m +++ b/NextcloudTalk/NCChatController.m @@ -742,9 +742,50 @@ - (void)sendChatMessage:(NSString *)message replyTo:(NSInteger)replyTo reference }]; } -- (void)sendChatMessage:(NCChatMessage *)message -{ - [self sendChatMessage:message.sendingMessage replyTo:message.parentMessageId referenceId:message.referenceId silently:message.isSilent]; +- (void)sendChatMessage:(NCChatMessage *)message { + if ([message.messageType isEqualToString:kMessageTypeVoiceMessage]) { + TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount]; + + [[NCAPIController sharedInstance] uniqueNameForFileUploadWithName:message.message + originalName:YES + forAccount:activeAccount + withCompletionBlock:^(NSString *fileServerURL, NSString *fileServerPath, NSInteger _, NSString *__) { + if (fileServerURL && fileServerPath) { + NSDictionary *talkMetaData = @{@"messageType": @"voice-message"}; + + [ChatFileUploader uploadFileWithLocalPath:message.file.fileStatus.fileLocalPath + fileServerURL:fileServerURL + fileServerPath:fileServerPath + talkMetaData:talkMetaData + temporaryMessage:message + room:self.room + completion:^(NSInteger statusCode, NSString *errorMessage) { + switch (statusCode) { + case 200: + NSLog(@"Successfully uploaded and shared voice message."); + break; + case 403: + NSLog(@"Failed to share voice message."); + break; + case 404: + case 409: + NSLog(@"Failed to check or create attachment folder."); + break; + case 507: + NSLog(@"User storage quota exceeded."); + break; + default: + NSLog(@"Failed to upload voice message with error code: %d", statusCode); + break; + } + }]; + } else { + NSLog(@"Could not find unique name for voice message file."); + } + }]; + } else { + [self sendChatMessage:message.sendingMessage replyTo:message.parentMessageId referenceId:message.referenceId silently:message.isSilent]; + } } - (void)checkLastCommonReadMessage:(NSInteger)lastCommonReadMessage diff --git a/NextcloudTalk/NCChatFileController.h b/NextcloudTalk/NCChatFileController.h index e6993e213..eff7028dd 100644 --- a/NextcloudTalk/NCChatFileController.h +++ b/NextcloudTalk/NCChatFileController.h @@ -28,7 +28,10 @@ extern NSString * const NCChatFileControllerDidChangeDownloadProgressNotificatio @property (nonatomic, weak) id delegate; @property (nonatomic, strong) NSString *messageType; @property (nonatomic, strong) NSString *actionType; +@property (nonatomic, strong, readonly) NSString *tempDirectoryPath; +- (void)initDownloadDirectoryForAccount:(TalkAccount *)account; +- (bool)moveFileToTemporaryDirectoryFromSourcePath:(NSString *)sourcePath destinationPath:(NSString *)destinationPath; - (void)downloadFileFromMessage:(NCMessageFileParameter *)fileParameter; - (void)downloadFileWithFileId:(NSString *)fileId; - (void)deleteDownloadDirectoryForAccount:(TalkAccount *)account; diff --git a/NextcloudTalk/NCChatFileController.m b/NextcloudTalk/NCChatFileController.m index 5bd988d27..28ee0b593 100644 --- a/NextcloudTalk/NCChatFileController.m +++ b/NextcloudTalk/NCChatFileController.m @@ -20,7 +20,6 @@ @interface NCChatFileController () @property (nonatomic, strong) NCChatFileStatus *fileStatus; -@property (nonatomic, strong) NSString *tempDirectoryPath; @end @@ -146,7 +145,7 @@ - (void)setModificationDateOnFile:(NSString *)filePath withModificationDate:(NSD - (void)downloadFileFromMessage:(NCMessageFileParameter *)fileParameter { - _fileStatus = [[NCChatFileStatus alloc] initWithFileId:fileParameter.parameterId fileName:fileParameter.name filePath:fileParameter.path]; + _fileStatus = [[NCChatFileStatus alloc] initWithFileId:fileParameter.parameterId fileName:fileParameter.name filePath:fileParameter.path fileLocalPath: nil]; fileParameter.fileStatus = _fileStatus; [self startDownload]; @@ -163,7 +162,7 @@ - (void)downloadFileWithFileId:(NSString *)fileId NSString *filePath = [NSString stringWithFormat:@"%@%@", directoryPath, file.fileName]; - self->_fileStatus = [[NCChatFileStatus alloc] initWithFileId:file.fileId fileName:file.fileName filePath:filePath]; + self->_fileStatus = [[NCChatFileStatus alloc] initWithFileId:file.fileId fileName:file.fileName filePath:filePath fileLocalPath:nil]; [self startDownload]; } else { NSLog(@"An error occurred while getting file with fileId %@: %@", fileId, errorDescription); @@ -172,6 +171,31 @@ - (void)downloadFileWithFileId:(NSString *)fileId }]; } +- (bool)moveFileToTemporaryDirectoryFromSourcePath:(NSString *)sourcePath destinationPath:(NSString *)destinationPath { + TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount]; + [self initDownloadDirectoryForAccount:activeAccount]; + + if (!sourcePath || !destinationPath) { + NSLog(@"Missing file information. File could not be moved."); + return false; + } + + if ([[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) { + NSLog(@"File is already in temporary directory: %@", destinationPath); + return false; + } + + NSError *error = nil; + if ([[NSFileManager defaultManager] moveItemAtPath:sourcePath toPath:destinationPath error:&error]) { + NSLog(@"File successfully moved to: %@", destinationPath); + return true; + } else { + NSLog(@"Error while moving file to temporary directory: %@", error.localizedDescription); + } + + return false; +} + - (void)startDownload { TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount]; diff --git a/NextcloudTalk/NCChatFileStatus.swift b/NextcloudTalk/NCChatFileStatus.swift index 9393360a8..c9161c01e 100644 --- a/NextcloudTalk/NCChatFileStatus.swift +++ b/NextcloudTalk/NCChatFileStatus.swift @@ -15,10 +15,11 @@ import Foundation public var canReportProgress: Bool = false public var downloadProgress: Float = 0 - init(fileId: String, fileName: String, filePath: String) { + init(fileId: String, fileName: String, filePath: String, fileLocalPath: String? = nil) { self.fileId = fileId self.fileName = fileName self.filePath = filePath + self.fileLocalPath = fileLocalPath } public func isStatus(for messageFileParameter: NCMessageFileParameter) -> Bool { diff --git a/NextcloudTalk/NCMessageFileParameter.m b/NextcloudTalk/NCMessageFileParameter.m index 5b7ec0aaf..ba57b6c1c 100644 --- a/NextcloudTalk/NCMessageFileParameter.m +++ b/NextcloudTalk/NCMessageFileParameter.m @@ -20,6 +20,18 @@ - (instancetype)initWithDictionary:(NSDictionary *)parameterDict self.previewImageHeight = [[parameterDict objectForKey:@"preview-image-height"] intValue]; self.width = [[parameterDict objectForKey:@"width"] intValue]; self.height = [[parameterDict objectForKey:@"height"] intValue]; + + // NCChatFileStatus parameters + NSString *fileId = [parameterDict objectForKey:@"fileId"]; + NSString *fileName = [parameterDict objectForKey:@"fileName"]; + NSString *filePath = [parameterDict objectForKey:@"filePath"]; + NSString *fileLocalPath = [parameterDict objectForKey:@"fileLocalPath"]; + + if (fileId && fileName && filePath && fileLocalPath) { + self.fileStatus = [[NCChatFileStatus alloc] initWithFileId:fileId fileName:fileName filePath:filePath fileLocalPath:fileLocalPath]; + } else { + self.fileStatus = nil; + } } return self; diff --git a/NextcloudTalk/en.lproj/Localizable.strings b/NextcloudTalk/en.lproj/Localizable.strings index 366d54722..bb5789d5d 100644 --- a/NextcloudTalk/en.lproj/Localizable.strings +++ b/NextcloudTalk/en.lproj/Localizable.strings @@ -844,6 +844,9 @@ /* No comment provided by engineer. */ "Failed to accept invitation" = "Failed to accept invitation"; +/* No comment provided by engineer. */ +"Failed to check or create attachment folder" = "Failed to check or create attachment folder"; + /* No comment provided by engineer. */ "Failed to clear reminder" = "Failed to clear reminder"; @@ -1183,6 +1186,9 @@ /* No comment provided by engineer. */ "No actions available" = "No actions available"; +/* No comment provided by engineer. */ +"No active account found" = "No active account found"; + /* No comment provided by engineer. */ "No banned users or guests" = "No banned users or guests"; @@ -1807,6 +1813,9 @@ /* No comment provided by engineer. */ "User status supported?" = "User status supported?"; +/* No comment provided by engineer. */ +"User storage quota exceeded" = "User storage quota exceeded"; + /* No comment provided by engineer. */ "Users" = "Users"; diff --git a/ShareExtension/ShareConfirmationViewController.swift b/ShareExtension/ShareConfirmationViewController.swift index 3cf4f36c0..26b5aa56a 100644 --- a/ShareExtension/ShareConfirmationViewController.swift +++ b/ShareExtension/ShareConfirmationViewController.swift @@ -648,7 +648,7 @@ import MBProgressHUD talkMetaData["silent"] = self.shareSilently } - NCAPIController.sharedInstance().shareFileOrFolder(for: self.account, atPath: filePath, toRoom: self.room.token, talkMetaData: talkMetaData) { error in + NCAPIController.sharedInstance().shareFileOrFolder(for: self.account, atPath: filePath, toRoom: self.room.token, talkMetaData: talkMetaData, referenceId: nil) { error in if let error { NCUtils.log(String(format: "Failed to share file. Error: %@", error.localizedDescription)) self.uploadErrors.append(error.localizedDescription)