From 12fc75198021bdb636131484201a1f72771c36d4 Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Tue, 25 Oct 2022 09:56:02 +0200 Subject: [PATCH 01/17] Voice Broadcast - Fix the chunk sequence number index Bug: The first chunk index was 2 instead of 1 --- .../Service/MatrixSDK/VoiceBroadcastRecorderService.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift index d75f69830..7a4701840 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift @@ -33,7 +33,7 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { private var chunkFile: AVAudioFile! = nil private var chunkFrames: AVAudioFrameCount = 0 - private var chunkFileNumber: Int = 1 + private var chunkFileNumber: Int = 0 // MARK: Public @@ -123,7 +123,7 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { /// Reset chunk values. private func resetValues() { chunkFrames = 0 - chunkFileNumber = 1 + chunkFileNumber = 0 } /// Write audio buffer to chunk file. @@ -150,6 +150,7 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { // FIXME: Manage error return } + chunkFileNumber += 1 let temporaryFileName = "VoiceBroadcastChunk-\(roomId)-\(chunkFileNumber)" let fileUrl = directory .appendingPathComponent(temporaryFileName) @@ -165,9 +166,9 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { chunkFile = try? AVAudioFile(forWriting: fileUrl, settings: settings) if chunkFile != nil { - chunkFileNumber += 1 chunkFrames = 0 } else { + chunkFileNumber -= 1 stopRecordingVoiceBroadcast() // FIXME: Manage error ? } From b97e10cb664c36ae3b1cd924fd5601b9c756f23c Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Tue, 25 Oct 2022 15:58:06 +0200 Subject: [PATCH 02/17] Update voice broadcast tiles UI (#6965) --- .../Contents.json | 12 +++++ .../voice_broadcast_tile_live.svg | 7 +++ .../Contents.json | 12 +++++ .../voice_broadcast_tile_mic.svg | 4 ++ Riot/Assets/en.lproj/Untranslated.strings | 1 - Riot/Assets/en.lproj/Vector.strings | 2 + Riot/Generated/Images.swift | 2 + Riot/Generated/Strings.swift | 8 +++ Riot/Generated/UntranslatedStrings.swift | 4 -- Riot/Modules/Room/RoomViewController.m | 12 ++--- .../RoomTimelineCellIdentifier.h | 12 ++--- .../Bubble/BubbleRoomTimelineCellProvider.m | 24 ++++----- ...BroadcastPlaybackIncomingBubbleCell.swift} | 2 +- ...comingWithPaginationTitleBubbleCell.swift} | 2 +- ...IncomingWithoutSenderInfoBubbleCell.swift} | 2 +- ...tgoingWithPaginationTitleBubbleCell.swift} | 2 +- ...OutgoingWithoutSenderInfoBubbleCell.swift} | 2 +- .../VoiceBroadcastPlaybackBubbleCell.swift} | 4 +- ...oiceBroadcastPlaybackPlainBubbleCell.swift | 37 +++++++++++++ ...oiceBroadcastRecorderPlainBubbleCell.swift | 37 +++++++++++++ .../VoiceBroadcastPlaybackPlainCell.swift} | 6 +-- ...laybackWithPaginationTitlePlainCell.swift} | 2 +- ...tPlaybackWithoutSenderInfoPlainCell.swift} | 2 +- ...RecorderWithPaginationTitlePlainCell.swift | 2 +- ...stRecorderWithoutSenderInfoPlainCell.swift | 2 +- .../Plain/PlainRoomTimelineCellProvider.h | 2 +- .../Plain/PlainRoomTimelineCellProvider.m | 28 +++++----- .../VoiceBroadcastPlaybackCoordinator.swift | 2 +- .../View/VoiceBroadcastPlaybackView.swift | 52 ++++++++++++------- .../VoiceBroadcastPlaybackModels.swift | 8 +-- .../VoiceBroadcastPlaybackScreenState.swift | 2 +- .../VoiceBroadcastRecorderCoordinator.swift | 2 +- .../View/VoiceBroadcastRecorderView.swift | 40 ++++++++++++-- .../VoiceBroadcastRecorderModels.swift | 1 + .../VoiceBroadcastRecorderScreenState.swift | 2 +- 35 files changed, 248 insertions(+), 93 deletions(-) create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_live.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_live.imageset/voice_broadcast_tile_live.svg create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_mic.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_mic.imageset/voice_broadcast_tile_mic.svg rename Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/{Incoming/VoiceBroadcastIncomingBubbleCell.swift => Playback/Incoming/VoiceBroadcastPlaybackIncomingBubbleCell.swift} (92%) rename Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/{Incoming/VoiceBroadcastIncomingWithPaginationTitleBubbleCell.swift => Playback/Incoming/VoiceBroadcastPlaybackIncomingWithPaginationTitleBubbleCell.swift} (87%) rename Riot/Modules/Room/TimelineCells/Styles/{Plain/Cells/VoiceBroadcast/VoiceBroadcastWithoutSenderInfoPlainCell.swift => Bubble/Cells/VoiceBroadcast/Playback/Incoming/VoiceBroadcastPlaybackIncomingWithoutSenderInfoBubbleCell.swift} (87%) rename Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/{Outgoing/VoiceBroadcastOutgoingWithPaginationTitleBubbleCell.swift => Playback/Outgoing/VoiceBroadcastPlaybackOutgoingWithPaginationTitleBubbleCell.swift} (85%) rename Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/{Outgoing/VoiceBroadcastOutgoingWithoutSenderInfoBubbleCell.swift => Playback/Outgoing/VoiceBroadcastPlaybackOutgoingWithoutSenderInfoBubbleCell.swift} (92%) rename Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/{VoiceBroadcastBubbleCell.swift => Playback/VoiceBroadcastPlaybackBubbleCell.swift} (96%) create mode 100644 Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainBubbleCell.swift create mode 100644 Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainBubbleCell.swift rename Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/{VoiceBroadcastPlainCell.swift => Playback/VoiceBroadcastPlaybackPlainCell.swift} (90%) rename Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/{VoiceBroadcastWithPaginationTitlePlainCell.swift => Playback/VoiceBroadcastPlaybackWithPaginationTitlePlainCell.swift} (88%) rename Riot/Modules/Room/TimelineCells/Styles/{Bubble/Cells/VoiceBroadcast/Incoming/VoiceBroadcastIncomingWithoutSenderInfoBubbleCell.swift => Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackWithoutSenderInfoPlainCell.swift} (88%) diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_live.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_live.imageset/Contents.json new file mode 100644 index 000000000..9a7c05104 --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_live.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "voice_broadcast_tile_live.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_live.imageset/voice_broadcast_tile_live.svg b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_live.imageset/voice_broadcast_tile_live.svg new file mode 100644 index 000000000..5728fc8d4 --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_live.imageset/voice_broadcast_tile_live.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_mic.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_mic.imageset/Contents.json new file mode 100644 index 000000000..eeeba86ae --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_mic.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "voice_broadcast_tile_mic.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_mic.imageset/voice_broadcast_tile_mic.svg b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_mic.imageset/voice_broadcast_tile_mic.svg new file mode 100644 index 000000000..2f3deca13 --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_tile_mic.imageset/voice_broadcast_tile_mic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Riot/Assets/en.lproj/Untranslated.strings b/Riot/Assets/en.lproj/Untranslated.strings index 6d9320f4a..9ff00a53d 100644 --- a/Riot/Assets/en.lproj/Untranslated.strings +++ b/Riot/Assets/en.lproj/Untranslated.strings @@ -19,4 +19,3 @@ // MARK: Onboarding Personalization WIP "image_picker_action_files" = "Choose from files"; -"voice_broadcast_in_timeline_title" = "Voice broadcast detected (under active development)"; diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index bbf95979f..d9e22ea92 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2195,6 +2195,8 @@ Tap the + to start adding people."; "voice_broadcast_blocked_by_someone_else_message" = "Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one."; "voice_broadcast_already_in_progress_message" = "You are already recording a voice broadcast. Please end your current voice broadcast to start a new one."; "voice_broadcast_playback_loading_error" = "Unable to play this voice broadcast."; +"voice_broadcast_live" = "Live"; +"voice_broadcast_tile" = "Voice broadcast"; // Mark: - Version check diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index 99c2c7041..ffe446c70 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -341,6 +341,8 @@ internal class Asset: NSObject { internal static let voiceBroadcastRecord = ImageAsset(name: "voice_broadcast_record") internal static let voiceBroadcastRecordPause = ImageAsset(name: "voice_broadcast_record_pause") internal static let voiceBroadcastStop = ImageAsset(name: "voice_broadcast_stop") + internal static let voiceBroadcastTileLive = ImageAsset(name: "voice_broadcast_tile_live") + internal static let voiceBroadcastTileMic = ImageAsset(name: "voice_broadcast_tile_mic") internal static let launchScreenLogo = ImageAsset(name: "launch_screen_logo") } @objcMembers diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 4a6cd2f1b..2254407c7 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -9059,6 +9059,10 @@ public class VectorL10n: NSObject { public static var voiceBroadcastBlockedBySomeoneElseMessage: String { return VectorL10n.tr("Vector", "voice_broadcast_blocked_by_someone_else_message") } + /// Live + public static var voiceBroadcastLive: String { + return VectorL10n.tr("Vector", "voice_broadcast_live") + } /// You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions. public static var voiceBroadcastPermissionDeniedMessage: String { return VectorL10n.tr("Vector", "voice_broadcast_permission_denied_message") @@ -9067,6 +9071,10 @@ public class VectorL10n: NSObject { public static var voiceBroadcastPlaybackLoadingError: String { return VectorL10n.tr("Vector", "voice_broadcast_playback_loading_error") } + /// Voice broadcast + public static var voiceBroadcastTile: String { + return VectorL10n.tr("Vector", "voice_broadcast_tile") + } /// Can't start a new voice broadcast public static var voiceBroadcastUnauthorizedTitle: String { return VectorL10n.tr("Vector", "voice_broadcast_unauthorized_title") diff --git a/Riot/Generated/UntranslatedStrings.swift b/Riot/Generated/UntranslatedStrings.swift index 1f417c770..f273877eb 100644 --- a/Riot/Generated/UntranslatedStrings.swift +++ b/Riot/Generated/UntranslatedStrings.swift @@ -14,10 +14,6 @@ public extension VectorL10n { static var imagePickerActionFiles: String { return VectorL10n.tr("Untranslated", "image_picker_action_files") } - /// Voice broadcast detected (under active development) - static var voiceBroadcastInTimelineTitle: String { - return VectorL10n.tr("Untranslated", "voice_broadcast_in_timeline_title") - } } // swiftlint:enable function_parameter_count identifier_name line_length type_body_length diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 4782f6fde..428b4a215 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -3237,30 +3237,30 @@ static CGSize kThreadListBarButtonItemImageSize; { if (bubbleData.isPaginationFirstBubble) { - cellIdentifier = RoomTimelineCellIdentifierIncomingVoiceBroadcastWithPaginationTitle; + cellIdentifier = RoomTimelineCellIdentifierIncomingVoiceBroadcastPlaybackWithPaginationTitle; } else if (bubbleData.shouldHideSenderInformation) { - cellIdentifier = RoomTimelineCellIdentifierIncomingVoiceBroadcastWithoutSenderInfo; + cellIdentifier = RoomTimelineCellIdentifierIncomingVoiceBroadcastPlaybackWithoutSenderInfo; } else { - cellIdentifier = RoomTimelineCellIdentifierIncomingVoiceBroadcast; + cellIdentifier = RoomTimelineCellIdentifierIncomingVoiceBroadcastPlayback; } } else { if (bubbleData.isPaginationFirstBubble) { - cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceBroadcastWithPaginationTitle; + cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlaybackWithPaginationTitle; } else if (bubbleData.shouldHideSenderInformation) { - cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceBroadcastWithoutSenderInfo; + cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlaybackWithoutSenderInfo; } else { - cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceBroadcast; + cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlayback; } } } diff --git a/Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h b/Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h index 3348df0e6..5a91d01e3 100644 --- a/Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h +++ b/Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h @@ -170,13 +170,13 @@ typedef NS_ENUM(NSUInteger, RoomTimelineCellIdentifier) { // - Voice broadcast // -- Incoming - RoomTimelineCellIdentifierIncomingVoiceBroadcast, - RoomTimelineCellIdentifierIncomingVoiceBroadcastWithoutSenderInfo, - RoomTimelineCellIdentifierIncomingVoiceBroadcastWithPaginationTitle, + RoomTimelineCellIdentifierIncomingVoiceBroadcastPlayback, + RoomTimelineCellIdentifierIncomingVoiceBroadcastPlaybackWithoutSenderInfo, + RoomTimelineCellIdentifierIncomingVoiceBroadcastPlaybackWithPaginationTitle, // -- Outgoing - RoomTimelineCellIdentifierOutgoingVoiceBroadcast, - RoomTimelineCellIdentifierOutgoingVoiceBroadcastWithoutSenderInfo, - RoomTimelineCellIdentifierOutgoingVoiceBroadcastWithPaginationTitle, + RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlayback, + RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlaybackWithoutSenderInfo, + RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlaybackWithPaginationTitle, // - Voice broadcast recorder RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorder, diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m index c747476ee..4cfb03de1 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m @@ -135,12 +135,12 @@ - (void)registerVoiceBroadcastCellsForTableView:(UITableView*)tableView { // Incoming - [tableView registerClass:VoiceBroadcastIncomingBubbleCell.class forCellReuseIdentifier:VoiceBroadcastIncomingBubbleCell.defaultReuseIdentifier]; - [tableView registerClass:VoiceBroadcastIncomingWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:VoiceBroadcastIncomingWithoutSenderInfoBubbleCell.defaultReuseIdentifier]; - [tableView registerClass:VoiceBroadcastIncomingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:VoiceBroadcastIncomingWithPaginationTitleBubbleCell.defaultReuseIdentifier]; + [tableView registerClass:VoiceBroadcastPlaybackIncomingBubbleCell.class forCellReuseIdentifier:VoiceBroadcastPlaybackIncomingBubbleCell.defaultReuseIdentifier]; + [tableView registerClass:VoiceBroadcastPlaybackIncomingWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:VoiceBroadcastPlaybackIncomingWithoutSenderInfoBubbleCell.defaultReuseIdentifier]; + [tableView registerClass:VoiceBroadcastPlaybackIncomingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:VoiceBroadcastPlaybackIncomingWithPaginationTitleBubbleCell.defaultReuseIdentifier]; // Outgoing - [tableView registerClass:VoiceBroadcastOutgoingWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:VoiceBroadcastOutgoingWithoutSenderInfoBubbleCell.defaultReuseIdentifier]; - [tableView registerClass:VoiceBroadcastOutgoingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:VoiceBroadcastOutgoingWithPaginationTitleBubbleCell.defaultReuseIdentifier]; + [tableView registerClass:VoiceBroadcastPlaybackOutgoingWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:VoiceBroadcastPlaybackOutgoingWithoutSenderInfoBubbleCell.defaultReuseIdentifier]; + [tableView registerClass:VoiceBroadcastPlaybackOutgoingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:VoiceBroadcastPlaybackOutgoingWithPaginationTitleBubbleCell.defaultReuseIdentifier]; } - (void)registerVoiceBroadcastRecorderCellsForTableView:(UITableView*)tableView @@ -311,17 +311,17 @@ }; } -- (NSDictionary*)voiceBroadcastCellsMapping +- (NSDictionary*)voiceBroadcastPlaybackCellsMapping { return @{ // Incoming - @(RoomTimelineCellIdentifierIncomingVoiceBroadcast) : VoiceBroadcastIncomingBubbleCell.class, - @(RoomTimelineCellIdentifierIncomingVoiceBroadcastWithoutSenderInfo) : VoiceBroadcastIncomingWithoutSenderInfoBubbleCell.class, - @(RoomTimelineCellIdentifierIncomingVoiceBroadcastWithPaginationTitle) : VoiceBroadcastIncomingWithPaginationTitleBubbleCell.class, + @(RoomTimelineCellIdentifierIncomingVoiceBroadcastPlayback) : VoiceBroadcastPlaybackIncomingBubbleCell.class, + @(RoomTimelineCellIdentifierIncomingVoiceBroadcastPlaybackWithoutSenderInfo) : VoiceBroadcastPlaybackIncomingWithoutSenderInfoBubbleCell.class, + @(RoomTimelineCellIdentifierIncomingVoiceBroadcastPlaybackWithPaginationTitle) : VoiceBroadcastPlaybackIncomingWithPaginationTitleBubbleCell.class, // Outgoing - @(RoomTimelineCellIdentifierOutgoingVoiceBroadcast) : VoiceBroadcastOutgoingWithoutSenderInfoBubbleCell.class, - @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastWithoutSenderInfo) : VoiceBroadcastOutgoingWithoutSenderInfoBubbleCell.class, - @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastWithPaginationTitle) : VoiceBroadcastOutgoingWithPaginationTitleBubbleCell.class, + @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlayback) : VoiceBroadcastPlaybackOutgoingWithoutSenderInfoBubbleCell.class, + @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlaybackWithoutSenderInfo) : VoiceBroadcastPlaybackOutgoingWithoutSenderInfoBubbleCell.class, + @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlaybackWithPaginationTitle) : VoiceBroadcastPlaybackOutgoingWithPaginationTitleBubbleCell.class, }; } diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Incoming/VoiceBroadcastIncomingBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Incoming/VoiceBroadcastPlaybackIncomingBubbleCell.swift similarity index 92% rename from Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Incoming/VoiceBroadcastIncomingBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Incoming/VoiceBroadcastPlaybackIncomingBubbleCell.swift index f46acbae1..fda3dbd6d 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Incoming/VoiceBroadcastIncomingBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Incoming/VoiceBroadcastPlaybackIncomingBubbleCell.swift @@ -16,7 +16,7 @@ import Foundation -class VoiceBroadcastIncomingBubbleCell: VoiceBroadcastBubbleCell, BubbleIncomingRoomCellProtocol { +class VoiceBroadcastPlaybackIncomingBubbleCell: VoiceBroadcastPlaybackBubbleCell, BubbleIncomingRoomCellProtocol { override func setupViews() { super.setupViews() diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Incoming/VoiceBroadcastIncomingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Incoming/VoiceBroadcastPlaybackIncomingWithPaginationTitleBubbleCell.swift similarity index 87% rename from Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Incoming/VoiceBroadcastIncomingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Incoming/VoiceBroadcastPlaybackIncomingWithPaginationTitleBubbleCell.swift index 6bbb10d9a..979ccd27f 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Incoming/VoiceBroadcastIncomingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Incoming/VoiceBroadcastPlaybackIncomingWithPaginationTitleBubbleCell.swift @@ -16,7 +16,7 @@ import Foundation -class VoiceBroadcastIncomingWithPaginationTitleBubbleCell: VoiceBroadcastIncomingBubbleCell { +class VoiceBroadcastPlaybackIncomingWithPaginationTitleBubbleCell: VoiceBroadcastPlaybackIncomingBubbleCell { override func setupViews() { super.setupViews() diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/VoiceBroadcastWithoutSenderInfoPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Incoming/VoiceBroadcastPlaybackIncomingWithoutSenderInfoBubbleCell.swift similarity index 87% rename from Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/VoiceBroadcastWithoutSenderInfoPlainCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Incoming/VoiceBroadcastPlaybackIncomingWithoutSenderInfoBubbleCell.swift index 6f3ec9110..7a99e2ecb 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/VoiceBroadcastWithoutSenderInfoPlainCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Incoming/VoiceBroadcastPlaybackIncomingWithoutSenderInfoBubbleCell.swift @@ -16,7 +16,7 @@ import Foundation -class VoiceBroadcastWithoutSenderInfoPlainCell: VoiceBroadcastPlainCell { +class VoiceBroadcastPlaybackIncomingWithoutSenderInfoBubbleCell: VoiceBroadcastPlaybackIncomingBubbleCell { override func setupViews() { super.setupViews() diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Outgoing/VoiceBroadcastOutgoingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Outgoing/VoiceBroadcastPlaybackOutgoingWithPaginationTitleBubbleCell.swift similarity index 85% rename from Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Outgoing/VoiceBroadcastOutgoingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Outgoing/VoiceBroadcastPlaybackOutgoingWithPaginationTitleBubbleCell.swift index 72f69e4d7..34bf80670 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Outgoing/VoiceBroadcastOutgoingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Outgoing/VoiceBroadcastPlaybackOutgoingWithPaginationTitleBubbleCell.swift @@ -16,7 +16,7 @@ import Foundation -class VoiceBroadcastOutgoingWithPaginationTitleBubbleCell: VoiceBroadcastOutgoingWithoutSenderInfoBubbleCell { +class VoiceBroadcastPlaybackOutgoingWithPaginationTitleBubbleCell: VoiceBroadcastPlaybackOutgoingWithoutSenderInfoBubbleCell { override func setupViews() { super.setupViews() diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Outgoing/VoiceBroadcastOutgoingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Outgoing/VoiceBroadcastPlaybackOutgoingWithoutSenderInfoBubbleCell.swift similarity index 92% rename from Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Outgoing/VoiceBroadcastOutgoingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Outgoing/VoiceBroadcastPlaybackOutgoingWithoutSenderInfoBubbleCell.swift index b149647b6..3616469e9 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Outgoing/VoiceBroadcastOutgoingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/Outgoing/VoiceBroadcastPlaybackOutgoingWithoutSenderInfoBubbleCell.swift @@ -16,7 +16,7 @@ import Foundation -class VoiceBroadcastOutgoingWithoutSenderInfoBubbleCell: VoiceBroadcastBubbleCell, BubbleOutgoingRoomCellProtocol { +class VoiceBroadcastPlaybackOutgoingWithoutSenderInfoBubbleCell: VoiceBroadcastPlaybackBubbleCell, BubbleOutgoingRoomCellProtocol { override func setupViews() { super.setupViews() diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/VoiceBroadcastBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackBubbleCell.swift similarity index 96% rename from Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/VoiceBroadcastBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackBubbleCell.swift index 67db62e88..2de4341db 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/VoiceBroadcastBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackBubbleCell.swift @@ -16,7 +16,7 @@ import UIKit -class VoiceBroadcastBubbleCell: VoiceBroadcastPlainCell { +class VoiceBroadcastPlaybackBubbleCell: VoiceBroadcastPlaybackPlainCell { // MARK: - Properties @@ -95,7 +95,7 @@ class VoiceBroadcastBubbleCell: VoiceBroadcastPlainCell { } // MARK: - RoomCellTimestampDisplayable -extension VoiceBroadcastBubbleCell: TimestampDisplayable { +extension VoiceBroadcastPlaybackBubbleCell: TimestampDisplayable { func addTimestampView(_ timestampView: UIView) { guard let messageBubbleBackgroundView = self.getBubbleBackgroundView() else { diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainBubbleCell.swift new file mode 100644 index 000000000..365a15956 --- /dev/null +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainBubbleCell.swift @@ -0,0 +1,37 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +class VoiceBroadcastPlaybackPlainBubbleCell: VoiceBroadcastPlaybackBubbleCell { + + override func setupViews() { + super.setupViews() + + // TODO: VB update margins attributes + let leftMargin: CGFloat = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left + BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.left + let rightMargin: CGFloat = 15 + BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.right + + roomCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin + roomCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin + } + + override func update(theme: Theme) { + super.update(theme: theme) + + self.bubbleBackgroundColor = theme.roomCellIncomingBubbleBackgroundColor + } +} diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainBubbleCell.swift new file mode 100644 index 000000000..69f94e8fa --- /dev/null +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainBubbleCell.swift @@ -0,0 +1,37 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +class VoiceBroadcastRecorderPlainBubbleCell: VoiceBroadcastRecorderBubbleCell { + + override func setupViews() { + super.setupViews() + + // TODO: VB update margins attributes + let leftMargin: CGFloat = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left + BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.left + let rightMargin: CGFloat = 15 + BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.right + + roomCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin + roomCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin + } + + override func update(theme: Theme) { + super.update(theme: theme) + + self.bubbleBackgroundColor = theme.roomCellIncomingBubbleBackgroundColor + } +} diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/VoiceBroadcastPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainCell.swift similarity index 90% rename from Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/VoiceBroadcastPlainCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainCell.swift index 14c602c4c..a32d32906 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/VoiceBroadcastPlainCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainCell.swift @@ -16,13 +16,13 @@ import Foundation -class VoiceBroadcastPlainCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCellReadMarkerDisplayable { +class VoiceBroadcastPlaybackPlainCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCellReadMarkerDisplayable { private var event: MXEvent? override func render(_ cellData: MXKCellData!) { super.render(cellData) - + guard let contentView = roomCellContentView?.innerContentView, let bubbleData = cellData as? RoomBubbleCellData, let event = bubbleData.events.last, @@ -54,4 +54,4 @@ class VoiceBroadcastPlainCell: SizableBaseRoomCell, RoomCellReactionsDisplayable } } -extension VoiceBroadcastPlainCell: RoomCellThreadSummaryDisplayable {} +extension VoiceBroadcastPlaybackPlainCell: RoomCellThreadSummaryDisplayable {} diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/VoiceBroadcastWithPaginationTitlePlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackWithPaginationTitlePlainCell.swift similarity index 88% rename from Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/VoiceBroadcastWithPaginationTitlePlainCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackWithPaginationTitlePlainCell.swift index fa3c3bc50..09f0bcff5 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/VoiceBroadcastWithPaginationTitlePlainCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackWithPaginationTitlePlainCell.swift @@ -16,7 +16,7 @@ import Foundation -class VoiceBroadcastWithPaginationTitlePlainCell: VoiceBroadcastPlainCell { +class VoiceBroadcastPlaybackWithPaginationTitlePlainCell: VoiceBroadcastPlaybackPlainBubbleCell { override func setupViews() { super.setupViews() diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Incoming/VoiceBroadcastIncomingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackWithoutSenderInfoPlainCell.swift similarity index 88% rename from Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Incoming/VoiceBroadcastIncomingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackWithoutSenderInfoPlainCell.swift index 4f123da7d..41f98f81a 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceBroadcast/Incoming/VoiceBroadcastIncomingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackWithoutSenderInfoPlainCell.swift @@ -16,7 +16,7 @@ import Foundation -class VoiceBroadcastIncomingWithoutSenderInfoBubbleCell: VoiceBroadcastIncomingBubbleCell { +class VoiceBroadcastPlaybackWithoutSenderInfoPlainCell: VoiceBroadcastPlaybackPlainBubbleCell { override func setupViews() { super.setupViews() diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderWithPaginationTitlePlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderWithPaginationTitlePlainCell.swift index 4247f306c..5c0bb9143 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderWithPaginationTitlePlainCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderWithPaginationTitlePlainCell.swift @@ -16,7 +16,7 @@ import Foundation -class VoiceBroadcastRecorderWithPaginationTitlePlainCell: VoiceBroadcastRecorderPlainCell { +class VoiceBroadcastRecorderWithPaginationTitlePlainCell: VoiceBroadcastRecorderPlainBubbleCell { override func setupViews() { super.setupViews() diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderWithoutSenderInfoPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderWithoutSenderInfoPlainCell.swift index 172b10aee..797ab8c57 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderWithoutSenderInfoPlainCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderWithoutSenderInfoPlainCell.swift @@ -16,7 +16,7 @@ import Foundation -class VoiceBroadcastRecorderWithoutSenderInfoPlainCell: VoiceBroadcastRecorderPlainCell { +class VoiceBroadcastRecorderWithoutSenderInfoPlainCell: VoiceBroadcastRecorderPlainBubbleCell { override func setupViews() { super.setupViews() diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.h index b1e85a621..21d8b11bc 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.h +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.h @@ -56,7 +56,7 @@ NS_ASSUME_NONNULL_BEGIN - (NSDictionary*)locationCellsMapping; -- (NSDictionary*)voiceBroadcastCellsMapping; +- (NSDictionary*)voiceBroadcastPlaybackCellsMapping; - (NSDictionary*)voiceBroadcastRecorderCellsMapping; diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m index 4813b539d..83b835579 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m @@ -276,14 +276,14 @@ - (void)registerVoiceBroadcastCellsForTableView:(UITableView*)tableView { - [tableView registerClass:VoiceBroadcastPlainCell.class forCellReuseIdentifier:VoiceBroadcastPlainCell.defaultReuseIdentifier]; - [tableView registerClass:VoiceBroadcastWithoutSenderInfoPlainCell.class forCellReuseIdentifier:VoiceBroadcastWithoutSenderInfoPlainCell.defaultReuseIdentifier]; - [tableView registerClass:VoiceBroadcastWithPaginationTitlePlainCell.class forCellReuseIdentifier:VoiceBroadcastWithPaginationTitlePlainCell.defaultReuseIdentifier]; + [tableView registerClass:VoiceBroadcastPlaybackPlainBubbleCell.class forCellReuseIdentifier:VoiceBroadcastPlaybackPlainBubbleCell.defaultReuseIdentifier]; + [tableView registerClass:VoiceBroadcastPlaybackWithoutSenderInfoPlainCell.class forCellReuseIdentifier:VoiceBroadcastPlaybackWithoutSenderInfoPlainCell.defaultReuseIdentifier]; + [tableView registerClass:VoiceBroadcastPlaybackWithPaginationTitlePlainCell.class forCellReuseIdentifier:VoiceBroadcastPlaybackWithPaginationTitlePlainCell.defaultReuseIdentifier]; } - (void)registerVoiceBroadcastRecorderCellsForTableView:(UITableView*)tableView { - [tableView registerClass:VoiceBroadcastRecorderPlainCell.class forCellReuseIdentifier:VoiceBroadcastRecorderPlainCell.defaultReuseIdentifier]; + [tableView registerClass:VoiceBroadcastRecorderPlainBubbleCell.class forCellReuseIdentifier:VoiceBroadcastRecorderPlainBubbleCell.defaultReuseIdentifier]; [tableView registerClass:VoiceBroadcastRecorderWithoutSenderInfoPlainCell.class forCellReuseIdentifier:VoiceBroadcastRecorderWithoutSenderInfoPlainCell.defaultReuseIdentifier]; [tableView registerClass:VoiceBroadcastRecorderWithPaginationTitlePlainCell.class forCellReuseIdentifier:VoiceBroadcastRecorderWithPaginationTitlePlainCell.defaultReuseIdentifier]; } @@ -346,8 +346,8 @@ NSDictionary *locationCellsMapping = [self locationCellsMapping]; [cellClasses addEntriesFromDictionary:locationCellsMapping]; - NSDictionary *voiceBroadcastCellsMapping = [self voiceBroadcastCellsMapping]; - [cellClasses addEntriesFromDictionary:voiceBroadcastCellsMapping]; + NSDictionary *voiceBroadcastPlaybackCellsMapping = [self voiceBroadcastPlaybackCellsMapping]; + [cellClasses addEntriesFromDictionary:voiceBroadcastPlaybackCellsMapping]; NSDictionary *voiceBroadcastRecorderCellsMapping = [self voiceBroadcastRecorderCellsMapping]; [cellClasses addEntriesFromDictionary:voiceBroadcastRecorderCellsMapping]; @@ -574,17 +574,17 @@ }; } -- (NSDictionary*)voiceBroadcastCellsMapping +- (NSDictionary*)voiceBroadcastPlaybackCellsMapping { return @{ // Incoming - @(RoomTimelineCellIdentifierIncomingVoiceBroadcast) : VoiceBroadcastPlainCell.class, - @(RoomTimelineCellIdentifierIncomingVoiceBroadcastWithoutSenderInfo) : VoiceBroadcastWithoutSenderInfoPlainCell.class, - @(RoomTimelineCellIdentifierIncomingVoiceBroadcastWithPaginationTitle) : VoiceBroadcastWithPaginationTitlePlainCell.class, + @(RoomTimelineCellIdentifierIncomingVoiceBroadcastPlayback) : VoiceBroadcastPlaybackPlainBubbleCell.class, + @(RoomTimelineCellIdentifierIncomingVoiceBroadcastPlaybackWithoutSenderInfo) : VoiceBroadcastPlaybackWithoutSenderInfoPlainCell.class, + @(RoomTimelineCellIdentifierIncomingVoiceBroadcastPlaybackWithPaginationTitle) : VoiceBroadcastPlaybackWithPaginationTitlePlainCell.class, // Outoing - @(RoomTimelineCellIdentifierOutgoingVoiceBroadcast) : VoiceBroadcastPlainCell.class, - @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastWithoutSenderInfo) : VoiceBroadcastWithoutSenderInfoPlainCell.class, - @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastWithPaginationTitle) : VoiceBroadcastWithPaginationTitlePlainCell.class + @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlayback) : VoiceBroadcastPlaybackPlainBubbleCell.class, + @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlaybackWithoutSenderInfo) : VoiceBroadcastPlaybackWithoutSenderInfoPlainCell.class, + @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastPlaybackWithPaginationTitle) : VoiceBroadcastPlaybackWithPaginationTitlePlainCell.class }; } @@ -592,7 +592,7 @@ { return @{ // Outoing - @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorder) : VoiceBroadcastRecorderPlainCell.class, + @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorder) : VoiceBroadcastRecorderPlainBubbleCell.class, @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorderWithoutSenderInfo) : VoiceBroadcastRecorderWithoutSenderInfoPlainCell.class, @(RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorderWithPaginationTitle) : VoiceBroadcastRecorderWithPaginationTitlePlainCell.class }; diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift index 4184f0d63..499b43d59 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift @@ -48,7 +48,7 @@ final class VoiceBroadcastPlaybackCoordinator: Coordinator, Presentable { let voiceBroadcastAggregator = try VoiceBroadcastAggregator(session: parameters.session, room: parameters.room, voiceBroadcastStartEventId: parameters.voiceBroadcastStartEvent.eventId, voiceBroadcastState: parameters.voiceBroadcastState) - let details = VoiceBroadcastPlaybackDetails(senderDisplayName: parameters.senderDisplayName) + let details = VoiceBroadcastPlaybackDetails(senderDisplayName: parameters.senderDisplayName, avatarData: parameters.room.avatarData) viewModel = VoiceBroadcastPlaybackViewModel(details: details, mediaServiceProvider: VoiceMessageMediaServiceProvider.sharedProvider, cacheManager: VoiceMessageAttachmentCacheManager.sharedManager, diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift index 04ade8a77..a16c83471 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift @@ -45,33 +45,49 @@ struct VoiceBroadcastPlaybackView: View { var body: some View { let details = viewModel.viewState.details - VStack(alignment: .center, spacing: 16.0) { + VStack(alignment: .center) { - HStack { - Text(details.senderDisplayName ?? "") - //Text(VectorL10n.voiceBroadcastInTimelineTitle) - .font(theme.fonts.bodySB) - .foregroundColor(theme.colors.primaryContent) + HStack (alignment: .top) { + AvatarImage(avatarData: viewModel.viewState.details.avatarData, size: .xSmall) + + VStack(alignment: .leading, spacing: 0) { + Text(details.avatarData.displayName ?? details.avatarData.matrixItemId) + .font(theme.fonts.bodySB) + .foregroundColor(theme.colors.primaryContent) + Label { + Text(details.senderDisplayName ?? details.avatarData.matrixItemId) + .foregroundColor(theme.colors.secondaryContent) + .font(theme.fonts.caption1) + } icon: { + Image(uiImage: Asset.Images.voiceBroadcastTileMic.image) + } + Label { + Text(VectorL10n.voiceBroadcastTile) + .foregroundColor(theme.colors.secondaryContent) + .font(theme.fonts.caption1) + } icon: { + Image(uiImage: Asset.Images.voiceBroadcastTileLive.image) + } + }.frame(maxWidth: .infinity, alignment: .leading) if viewModel.viewState.broadcastState == .live { Button { viewModel.send(viewAction: .playLive) } label: { - HStack { - Image(uiImage: Asset.Images.voiceBroadcastLive.image) - .renderingMode(.original) - Text("Live") - .font(theme.fonts.bodySB) + Label { + Text(VectorL10n.voiceBroadcastLive) + .font(theme.fonts.caption1SB) .foregroundColor(Color.white) + } icon: { + Image(uiImage: Asset.Images.voiceBroadcastLive.image) } - } - .padding(5.0) - .background(RoundedRectangle(cornerRadius: 4, style: .continuous) - .fill(backgroundColor)) + .padding(.horizontal, 5) + .background(RoundedRectangle(cornerRadius: 4, style: .continuous).fill(backgroundColor)) .accessibilityIdentifier("liveButton") } } - + .frame(maxWidth: .infinity, alignment: .leading) + if viewModel.viewState.playbackState == .error { VoiceBroadcastPlaybackErrorView() } else { @@ -101,13 +117,9 @@ struct VoiceBroadcastPlaybackView: View { } .activityIndicator(show: viewModel.viewState.playbackState == .buffering) } - } .padding([.horizontal, .top], 2.0) .padding([.bottom]) - .alert(item: $viewModel.alertInfo) { info in - info.alert - } } } diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift index 09a12b87d..3fed0075f 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift @@ -34,6 +34,7 @@ enum VoiceBroadcastPlaybackState { struct VoiceBroadcastPlaybackDetails { let senderDisplayName: String? + let avatarData: AvatarInputProtocol } enum VoiceBroadcastState { @@ -51,12 +52,5 @@ struct VoiceBroadcastPlaybackViewState: BindableState { } struct VoiceBroadcastPlaybackViewStateBindings { - // TODO: Neeeded? - var alertInfo: AlertInfo? -} - -enum VoiceBroadcastPlaybackAlertType { - // TODO: What is it? - case failedClosingVoiceBroadcast } diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift index 72a15185f..f4fabadb1 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift @@ -42,7 +42,7 @@ enum MockVoiceBroadcastPlaybackScreenState: MockScreenState, CaseIterable { /// Generate the view struct for the screen state. var screenView: ([Any], AnyView) { - let details = VoiceBroadcastPlaybackDetails(senderDisplayName: "Alice") + let details = VoiceBroadcastPlaybackDetails(senderDisplayName: "Alice", avatarData: AvatarInput(mxContentUri: "", matrixItemId: "!fakeroomid:matrix.org", displayName: "The name of the room")) let viewModel = MockVoiceBroadcastPlaybackViewModel(initialViewState: VoiceBroadcastPlaybackViewState(details: details, broadcastState: .live, playbackState: .stopped, bindings: VoiceBroadcastPlaybackViewStateBindings())) return ( diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift index c13524e13..56f0854aa 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift @@ -45,7 +45,7 @@ final class VoiceBroadcastRecorderCoordinator: Coordinator, Presentable { voiceBroadcastRecorderService = VoiceBroadcastRecorderService(session: parameters.session, roomId: parameters.room.matrixItemId) - let details = VoiceBroadcastRecorderDetails(senderDisplayName: parameters.senderDisplayName) + let details = VoiceBroadcastRecorderDetails(senderDisplayName: parameters.senderDisplayName, avatarData: parameters.room.avatarData) let viewModel = VoiceBroadcastRecorderViewModel(details: details, recorderService: voiceBroadcastRecorderService) voiceBroadcastRecorderViewModel = viewModel diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift index 71fb41cc1..411ce0333 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift @@ -23,6 +23,13 @@ struct VoiceBroadcastRecorderView: View { @Environment(\.theme) private var theme: ThemeSwiftUI + private var backgroundColor: Color { + if viewModel.viewState.recordingState != .paused { + return theme.colors.alert + } + return theme.colors.quarterlyContent + } + // MARK: Public @ObservedObject var viewModel: VoiceBroadcastRecorderViewModel.Context @@ -30,10 +37,35 @@ struct VoiceBroadcastRecorderView: View { var body: some View { let details = viewModel.viewState.details - VStack(alignment: .leading, spacing: 16.0) { - Text(details.senderDisplayName ?? "") - .font(theme.fonts.bodySB) - .foregroundColor(theme.colors.primaryContent) + VStack(alignment: .center) { + + HStack(alignment: .top) { + AvatarImage(avatarData: viewModel.viewState.details.avatarData, size: .xSmall) + + VStack(alignment: .leading, spacing: 0) { + Text(details.avatarData.displayName ?? details.avatarData.matrixItemId) + .font(theme.fonts.bodySB) + .foregroundColor(theme.colors.primaryContent) + Label { + Text(VectorL10n.voiceBroadcastTile) + .foregroundColor(theme.colors.secondaryContent) + .font(theme.fonts.caption1) + } icon: { + Image(uiImage: Asset.Images.voiceBroadcastTileLive.image) + } + }.frame(maxWidth: .infinity, alignment: .leading) + + Label { + Text(VectorL10n.voiceBroadcastLive) + .font(theme.fonts.caption1SB) + .foregroundColor(Color.white) + } icon: { + Image(uiImage: Asset.Images.voiceBroadcastLive.image) + } + .padding(.horizontal, 5) + .background(RoundedRectangle(cornerRadius: 4, style: .continuous).fill(backgroundColor)) + .accessibilityIdentifier("liveButton") + } HStack(alignment: .top, spacing: 16.0) { Button { diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderModels.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderModels.swift index b88021bfe..7a2566aad 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderModels.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderModels.swift @@ -32,6 +32,7 @@ enum VoiceBroadcastRecorderState { struct VoiceBroadcastRecorderDetails { let senderDisplayName: String? + let avatarData: AvatarInputProtocol } struct VoiceBroadcastRecorderViewState: BindableState { diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderScreenState.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderScreenState.swift index baa9488f4..bc915d36a 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderScreenState.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderScreenState.swift @@ -31,7 +31,7 @@ enum MockVoiceBroadcastRecorderScreenState: MockScreenState, CaseIterable { } var screenView: ([Any], AnyView) { - let details = VoiceBroadcastRecorderDetails(senderDisplayName: "") + let details = VoiceBroadcastRecorderDetails(senderDisplayName: "", avatarData: AvatarInput(mxContentUri: "", matrixItemId: "!fakeroomid:matrix.org", displayName: "The name of the room")) let viewModel = MockVoiceBroadcastRecorderViewModel(initialViewState: VoiceBroadcastRecorderViewState(details: details, recordingState: .started, bindings: VoiceBroadcastRecorderViewStateBindings())) return ( From 202d7abba0d07dce2c605a9badcd1537838da39f Mon Sep 17 00:00:00 2001 From: Vri Date: Thu, 20 Oct 2022 13:59:45 +0000 Subject: [PATCH 03/17] Translated using Weblate (German) Currently translated at 100.0% (2307 of 2307 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 6fc34bb32..f675ac91c 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -2639,3 +2639,11 @@ // Send Media Actions "wysiwyg_composer_start_action_media_picker" = "Fotobibliothek"; "settings_labs_enable_wysiwyg_composer" = "Probiere den Rich-Text-Editor aus (bald auch mit Plain-Text-Modus)"; +"wysiwyg_composer_start_action_voice_broadcast" = "Sprachübertragung"; +"voice_broadcast_already_in_progress_message" = "Du zeichnest bereits eine Sprachübertragung auf. Bitte beende die laufende Übertragung, um eine neue zu beginnen."; +"voice_broadcast_blocked_by_someone_else_message" = "Jemand anderes nimmt bereits eine Sprachübertragung auf. Warte auf das Ende der Übertragung, bevor du eine neue startest."; +"voice_broadcast_permission_denied_message" = "Du hast nicht die nötigen Berechtigungen, um eine Sprachübertragung in diesem Raum zu starten. Kontaktiere einen Raumadministrator, um deine Berechtigungen anzupassen."; + +// Mark: - Voice broadcast +"voice_broadcast_unauthorized_title" = "Sprachübertragung kann nicht gestartet werden"; +"settings_labs_enable_voice_broadcast" = "Sprachübertragung (in aktiver Entwicklung). Momentan erkennen wir nur Sprachübertragungen im Verlauf, es ist nicht möglich tatsächlich Sprachübertragungen zu tätigen oder wiederzugeben"; From 745429089a8d3eb2f5d75a253ac7379828888038 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Tue, 25 Oct 2022 16:51:03 +0200 Subject: [PATCH 04/17] Avoid unnecessary send state request (#6970) --- .../VoiceBroadcastSDK/VoiceBroadcastService.swift | 5 +++++ .../Service/MatrixSDK/VoiceBroadcastRecorderService.swift | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift index 81cbc51af..610667ab4 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift @@ -130,6 +130,11 @@ public class VoiceBroadcastService: NSObject { return nil } + guard state != self.state else { + completion(.failure(VoiceBroadcastServiceError.unknown)) + return nil + } + let stateKey = userId let voiceBroadcastInfo = VoiceBroadcastInfo() diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift index 7a4701840..ad16ee308 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift @@ -98,9 +98,10 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { guard let self = self else { return } // Send current chunk - self.sendChunkFile(at: self.chunkFile.url, sequence: self.chunkFileNumber) - self.chunkFile = nil - + if self.chunkFile != nil { + self.sendChunkFile(at: self.chunkFile.url, sequence: self.chunkFileNumber) + self.chunkFile = nil + } }, failure: { error in MXLog.error("[VoiceBroadcastRecorderService] Failed to pause voice broadcast", context: error) }) From b26fdd974ed02ce48cab5f70d63b3f1f9494a1f4 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 26 Oct 2022 08:51:41 +0200 Subject: [PATCH 05/17] Add voice broadcast initial state in bubble data (#6972) - Add voice broadcast initial state in bubble data - Remove the local record after sending --- .../Room/CellData/RoomBubbleCellData.h | 2 + .../Room/CellData/RoomBubbleCellData.m | 45 ++++++++++++++----- .../VoiceBroadcastPlaybackPlainCell.swift | 5 ++- .../VoiceBroadcastAggregator.swift | 2 +- .../VoiceBroadcastSDK/VoiceBroadcastInfo.h | 7 +-- .../VoiceBroadcastSDK/VoiceBroadcastInfo.m | 14 +++--- .../VoiceBroadcastInfo.swift | 16 +++++++ .../VoiceBroadcastService.swift | 2 +- .../VoiceBroadcastPlaybackProvider.swift | 20 +-------- .../VoiceBroadcastRecorderService.swift | 10 +++-- 10 files changed, 76 insertions(+), 47 deletions(-) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.h b/Riot/Modules/Room/CellData/RoomBubbleCellData.h index 94f7346aa..8b3a49a5f 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.h +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.h @@ -105,6 +105,8 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag) */ @property(nonatomic) NSInteger componentIndexOfSentMessageTick; +@property(nonatomic, strong) NSString *voiceBroadcastState; + /** Indicate that both the text message layout and any additional content height are no longer valid and should be recomputed before presentation in a bubble cell. This could be due diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index adcd6692e..712604203 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -186,23 +186,45 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat else if ([event.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]) { VoiceBroadcastInfo *voiceBroadcastInfo = [VoiceBroadcastInfo modelFromJSON: event.content]; + + // Check if the state event corresponds to the beginning of a voice broadcast if ([VoiceBroadcastInfo isStartedFor:voiceBroadcastInfo.state]) { - // This state event corresponds to the beginning of a voice broadcast - // Check whether this is a local live broadcast to display it with the recorder view or not - // Note: Because of race condition, the voiceBroadcastService may be running without id here (the sync response may be received before - // the success of the event sending), in that case, we will display a recorder view by default to let the user be able to stop a potential record. - if ([event.sender isEqualToString: self.mxSession.myUserId] && - [voiceBroadcastInfo.deviceId isEqualToString:self.mxSession.myDeviceId] && - self.mxSession.voiceBroadcastService != nil && - ([event.eventId isEqualToString: self.mxSession.voiceBroadcastService.voiceBroadcastInfoEventId] || - self.mxSession.voiceBroadcastService.voiceBroadcastInfoEventId == nil)) + // Retrieve the most recent voice broadcast info. + MXEvent *lastVoiceBroadcastInfoEvent = [roomDataSource.roomState stateEventsWithType:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType].lastObject; + if (event.originServerTs > lastVoiceBroadcastInfoEvent.originServerTs) { - self.tag = RoomBubbleCellDataTagVoiceBroadcastRecord; + lastVoiceBroadcastInfoEvent = event; + } + + VoiceBroadcastInfo *lastVoiceBroadcastInfo = [VoiceBroadcastInfo modelFromJSON: lastVoiceBroadcastInfoEvent.content]; + + // Handle the specific case where the state event is a started voice broadcast (the voiceBroadcastId is the event id itself). + if (!lastVoiceBroadcastInfo.voiceBroadcastId) + { + lastVoiceBroadcastInfo.voiceBroadcastId = lastVoiceBroadcastInfoEvent.eventId; + } + + // Check if the voice broadcast is still alive. + if ([lastVoiceBroadcastInfo.voiceBroadcastId isEqualToString:event.eventId] && ![VoiceBroadcastInfo isStoppedFor:lastVoiceBroadcastInfo.state]) + { + // Check whether this broadcast is sent from the currrent session to display it with the recorder view or not. + if ([event.stateKey isEqualToString:self.mxSession.myUserId] && + [voiceBroadcastInfo.deviceId isEqualToString:self.mxSession.myDeviceId]) + { + self.tag = RoomBubbleCellDataTagVoiceBroadcastRecord; + } + else + { + self.tag = RoomBubbleCellDataTagVoiceBroadcastPlayback; + } + + self.voiceBroadcastState = lastVoiceBroadcastInfo.state; } else { self.tag = RoomBubbleCellDataTagVoiceBroadcastPlayback; + self.voiceBroadcastState = VoiceBroadcastInfo.stoppedValue; } } else @@ -213,8 +235,9 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat { // This state event corresponds to the end of a voice broadcast // Force the tag of the potential cellData which corresponds to the started event to switch the display from recorder to listener - id bubbleData = [roomDataSource cellDataOfEventWithEventId:voiceBroadcastInfo.eventId]; + RoomBubbleCellData *bubbleData = [roomDataSource cellDataOfEventWithEventId:voiceBroadcastInfo.voiceBroadcastId]; bubbleData.tag = RoomBubbleCellDataTagVoiceBroadcastPlayback; + bubbleData.voiceBroadcastState = VoiceBroadcastInfo.stoppedValue; } } self.collapsable = NO; diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainCell.swift index a32d32906..8987cb1de 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainCell.swift @@ -28,7 +28,10 @@ class VoiceBroadcastPlaybackPlainCell: SizableBaseRoomCell, RoomCellReactionsDis let event = bubbleData.events.last, let voiceBroadcastContent = VoiceBroadcastInfo(fromJSON: event.content), voiceBroadcastContent.state == VoiceBroadcastInfo.State.started.rawValue, - let controller = VoiceBroadcastPlaybackProvider.shared.buildVoiceBroadcastPlaybackVCForEvent(event, senderDisplayName: bubbleData.senderDisplayName) else { + let controller = VoiceBroadcastPlaybackProvider.shared.buildVoiceBroadcastPlaybackVCForEvent(event, + senderDisplayName: bubbleData.senderDisplayName, + voiceBroadcastState: bubbleData.voiceBroadcastState) + else { return } diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift index 1de022904..fb90d834d 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift @@ -110,7 +110,7 @@ public class VoiceBroadcastAggregator { guard let event = roomState?.stateEvents(with: .custom(VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType))?.last, event.stateKey == self.voiceBroadcastSenderId, let voiceBroadcastInfo = VoiceBroadcastInfo(fromJSON: event.content), - (event.eventId == self.voiceBroadcastStartEventId || voiceBroadcastInfo.eventId == self.voiceBroadcastStartEventId), + (event.eventId == self.voiceBroadcastStartEventId || voiceBroadcastInfo.voiceBroadcastId == self.voiceBroadcastStartEventId), let state = VoiceBroadcastInfo.State(rawValue: voiceBroadcastInfo.state) else { return } diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.h b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.h index 36b963e47..71781d927 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.h +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.h @@ -32,15 +32,12 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) NSInteger chunkLength; /// The event id of the started voice broadcast info state event. -@property (nonatomic, strong, nullable) NSString* eventId; - -/// The event used to build the MXBeaconInfo. -@property (nonatomic, readonly, nullable) MXEvent *originalEvent; +@property (nonatomic, strong, nullable) NSString* voiceBroadcastId; - (instancetype)initWithDeviceId:(NSString *)deviceId state:(NSString *)state chunkLength:(NSInteger)chunkLength - eventId:(NSString *)eventId; + voiceBroadcastId:(NSString *)voiceBroadcastId; @end diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.m b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.m index 51a50876c..eaaaa9047 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.m +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.m @@ -22,14 +22,14 @@ - (instancetype)initWithDeviceId:(NSString *)deviceId state:(NSString *)state chunkLength:(NSInteger)chunkLength - eventId:(NSString *)eventId + voiceBroadcastId:(NSString *)voiceBroadcastId { if (self = [super init]) { _deviceId = deviceId; _state = state; _chunkLength = chunkLength; - _eventId = eventId; + _voiceBroadcastId = voiceBroadcastId; } return self; @@ -55,7 +55,7 @@ MXJSONModelSetInteger(chunkLength, JSONDictionary[VoiceBroadcastSettings.voiceBroadcastContentKeyChunkLength]); } - NSString *eventId; + NSString *voiceBroadcastId; if (JSONDictionary[kMXEventRelationRelatesToKey]) { MXEventContentRelatesTo *relatesTo; @@ -63,11 +63,11 @@ if (relatesTo && [relatesTo.relationType isEqualToString:MXEventRelationTypeReference]) { - eventId = relatesTo.eventId; + voiceBroadcastId = relatesTo.eventId; } } - return [[VoiceBroadcastInfo alloc] initWithDeviceId:deviceId state:state chunkLength:chunkLength eventId:eventId]; + return [[VoiceBroadcastInfo alloc] initWithDeviceId:deviceId state:state chunkLength:chunkLength voiceBroadcastId:voiceBroadcastId]; } - (NSDictionary *)JSONDictionary @@ -78,8 +78,8 @@ JSONDictionary[VoiceBroadcastSettings.voiceBroadcastContentKeyState] = self.state; - if (_eventId) { - MXEventContentRelatesTo *relatesTo = [[MXEventContentRelatesTo alloc] initWithRelationType:MXEventRelationTypeReference eventId:_eventId]; + if (_voiceBroadcastId) { + MXEventContentRelatesTo *relatesTo = [[MXEventContentRelatesTo alloc] initWithRelationType:MXEventRelationTypeReference eventId:_voiceBroadcastId]; JSONDictionary[kMXEventRelationRelatesToKey] = relatesTo.JSONDictionary; } else { diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.swift index 3515a5b59..b2bc1afe4 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.swift +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.swift @@ -35,4 +35,20 @@ extension VoiceBroadcastInfo { @objc static func isStopped(for name: String) -> Bool { return name == State.stopped.rawValue } + + @objc static func startedValue() -> String { + return State.started.rawValue + } + + @objc static func pausedValue() -> String { + return State.paused.rawValue + } + + @objc static func resumedValue() -> String { + return State.resumed.rawValue + } + + @objc static func stoppedValue() -> String { + return State.stopped.rawValue + } } diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift index 610667ab4..2078f07e1 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift @@ -149,7 +149,7 @@ public class VoiceBroadcastService: NSObject { return nil } - voiceBroadcastInfo.eventId = voiceBroadcastInfoEventId + voiceBroadcastInfo.voiceBroadcastId = voiceBroadcastInfoEventId } else { voiceBroadcastInfo.chunkLength = BuildSettings.voiceBroadcastChunkLength } diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift index 5167a2364..7ca72c413 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift @@ -26,7 +26,7 @@ class VoiceBroadcastPlaybackProvider { /// Create or retrieve the voiceBroadcast timeline coordinator for this event and return /// a view to be displayed in the timeline - func buildVoiceBroadcastPlaybackVCForEvent(_ event: MXEvent, senderDisplayName: String?) -> UIViewController? { + func buildVoiceBroadcastPlaybackVCForEvent(_ event: MXEvent, senderDisplayName: String?, voiceBroadcastState: String) -> UIViewController? { guard let session = session, let room = session.room(withRoomId: event.roomId) else { return nil } @@ -35,26 +35,10 @@ class VoiceBroadcastPlaybackProvider { return coordinator.toPresentable() } - let dispatchGroup = DispatchGroup() - dispatchGroup.enter() - var voiceBroadcastState = VoiceBroadcastInfo.State.stopped - - room.state { roomState in - if let stateEvent = roomState?.stateEvents(with: .custom(VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType))?.last, - stateEvent.stateKey == event.stateKey, - let voiceBroadcastInfo = VoiceBroadcastInfo(fromJSON: stateEvent.content), - (stateEvent.eventId == event.eventId || voiceBroadcastInfo.eventId == event.eventId), - let state = VoiceBroadcastInfo.State(rawValue: voiceBroadcastInfo.state) { - voiceBroadcastState = state - } - - dispatchGroup.leave() - } - let parameters = VoiceBroadcastPlaybackCoordinatorParameters(session: session, room: room, voiceBroadcastStartEvent: event, - voiceBroadcastState: voiceBroadcastState, + voiceBroadcastState: VoiceBroadcastInfo.State(rawValue: voiceBroadcastState) ?? VoiceBroadcastInfo.State.stopped, senderDisplayName: senderDisplayName) guard let coordinator = try? VoiceBroadcastPlaybackCoordinator(parameters: parameters) else { return nil diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift index ad16ee308..8b6c20373 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift @@ -204,6 +204,9 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { convertAACToM4A(at: url) { [weak self] convertedUrl in guard let self = self else { return } + // Delete the source file. + self.deleteRecording(at: url) + if let convertedUrl = convertedUrl { dispatchGroup.notify(queue: .main) { self.voiceBroadcastService?.sendChunkOfVoiceBroadcast(audioFileLocalURL: convertedUrl, @@ -212,11 +215,12 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { samples: nil, sequence: UInt(sequence)) { eventId in MXLog.debug("[VoiceBroadcastRecorderService] Send voice broadcast chunk with success.") - if eventId != nil { - self.deleteRecording(at: url) - } + self.deleteRecording(at: convertedUrl) } failure: { error in MXLog.error("[VoiceBroadcastRecorderService] Failed to send voice broadcast chunk.", context: error) + // Do not delete the file to be sent if request failed, the retry flow will need it + // There's no manual mechanism to clean it up afterwards but the tmp folder + // they live in will eventually be deleted by the system } } } From f08ebbaa29e21a1cb292be03a60a4fde74fbfdd8 Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Wed, 26 Oct 2022 12:28:40 +0200 Subject: [PATCH 06/17] Voice Broadcast: log and block unexpected state change --- .../VoiceBroadcastService.swift | 36 +++++++++++++------ .../VoiceBroadcastServiceError.swift | 1 + 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift index 2078f07e1..08afca647 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift @@ -23,7 +23,7 @@ public class VoiceBroadcastService: NSObject { // MARK: - Properties - public private(set) var voiceBroadcastInfoEventId: String? + public private(set) var voiceBroadcastId: String? public let room: MXRoom public private(set) var state: VoiceBroadcastInfo.State @@ -50,7 +50,7 @@ public class VoiceBroadcastService: NSObject { switch response { case .success((let eventIdResponse)): - self.voiceBroadcastInfoEventId = eventIdResponse + self.voiceBroadcastId = eventIdResponse completion(.success(eventIdResponse)) case .failure(let error): completion(.failure(error)) @@ -108,12 +108,12 @@ public class VoiceBroadcastService: NSObject { sequence: UInt, success: @escaping ((String?) -> Void), failure: @escaping ((Error?) -> Void)) { - guard let voiceBroadcastInfoEventId = self.voiceBroadcastInfoEventId else { + guard let voiceBroadcastId = self.voiceBroadcastId else { return failure(VoiceBroadcastServiceError.notStarted) } self.room.sendChunkOfVoiceBroadcast(localURL: audioFileLocalURL, - voiceBroadcastInfoEventId: voiceBroadcastInfoEventId, + voiceBroadcastId: voiceBroadcastId, mimeType: mimeType, duration: duration, samples: samples, @@ -124,14 +124,28 @@ public class VoiceBroadcastService: NSObject { // MARK: - Private + private func allowedStates(from state: VoiceBroadcastInfo.State) -> [VoiceBroadcastInfo.State] { + switch state { + case .started: + return [.paused, .stopped] + case .paused: + return [.resumed, .stopped] + case .resumed: + return [.paused, .stopped] + case .stopped: + return [.started] + } + } + private func sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State, completion: @escaping (MXResponse) -> Void) -> MXHTTPOperation? { guard let userId = self.room.mxSession.myUserId else { completion(.failure(VoiceBroadcastServiceError.missingUserId)) return nil } - guard state != self.state else { - completion(.failure(VoiceBroadcastServiceError.unknown)) + guard self.allowedStates(from: self.state).contains(state) else { + MXLog.warning("[VoiceBroadcastService] sendVoiceBroadcastInfo: unexpected state change \(self.state) -> \(state)") + completion(.failure(VoiceBroadcastServiceError.unexpectedState)) return nil } @@ -144,12 +158,12 @@ public class VoiceBroadcastService: NSObject { voiceBroadcastInfo.state = state.rawValue if state != VoiceBroadcastInfo.State.started { - guard let voiceBroadcastInfoEventId = self.voiceBroadcastInfoEventId else { + guard let voiceBroadcastId = self.voiceBroadcastId else { completion(.failure(VoiceBroadcastServiceError.notStarted)) return nil } - voiceBroadcastInfo.voiceBroadcastId = voiceBroadcastInfoEventId + voiceBroadcastInfo.voiceBroadcastId = voiceBroadcastId } else { voiceBroadcastInfo.chunkLength = BuildSettings.voiceBroadcastChunkLength } @@ -252,7 +266,7 @@ extension MXRoom { /// Send a voice broadcast to the room. /// - Parameters: /// - localURL: the local filesystem path of the file to send. - /// - voiceBroadcastInfoEventId: The id of the voice broadcast info event. + /// - voiceBroadcastId: The event id of the started voice broadcast info state event /// - mimeType: (optional) the mime type of the file. Defaults to `audio/ogg`. /// - duration: the length of the voice message in milliseconds /// - samples: an array of floating point values normalized to [0, 1] @@ -262,7 +276,7 @@ extension MXRoom { /// - failure: A closure called when the operation fails. /// - Returns: a `MXHTTPOperation` instance. @nonobjc @discardableResult func sendChunkOfVoiceBroadcast(localURL: URL, - voiceBroadcastInfoEventId: String, + voiceBroadcastId: String, mimeType: String?, duration: UInt, samples: [Float]?, @@ -274,7 +288,7 @@ extension MXRoom { guard let relatesTo = MXEventContentRelatesTo(relationType: MXEventRelationTypeReference, - eventId: voiceBroadcastInfoEventId).jsonDictionary() as? [String: Any] else { + eventId: voiceBroadcastId).jsonDictionary() as? [String: Any] else { failure(VoiceBroadcastServiceError.unknown) return nil } diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastServiceError.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastServiceError.swift index 55d0820fa..70f3851e0 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastServiceError.swift +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastServiceError.swift @@ -21,6 +21,7 @@ public enum VoiceBroadcastServiceError: Int, Error { case missingUserId case roomNotFound case notStarted + case unexpectedState case unknown } From 3a538edec5bb20e0c2406f354e9af740700d7060 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 26 Oct 2022 16:13:25 +0200 Subject: [PATCH 07/17] new line --- .../Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index f886276fd..4524f0d67 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -97,6 +97,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp subView.trailingAnchor.constraint(equalTo: self.trailingAnchor), subView.bottomAnchor.constraint(equalTo: self.bottomAnchor) ]) + cancellables = [ hostingViewController.heightPublisher .removeDuplicates() From 9b1c3bd53e48b0291226e4640653617e9f7e21af Mon Sep 17 00:00:00 2001 From: aringenbach Date: Tue, 25 Oct 2022 16:40:52 +0200 Subject: [PATCH 08/17] Enable WYSIWYG plain text support --- .../xcshareddata/swiftpm/Package.resolved | 6 +- .../Contents.json | 23 +++ .../action_formatting_disabled.png | Bin 0 -> 513 bytes .../action_formatting_disabled@2x.png | Bin 0 -> 894 bytes .../action_formatting_disabled@3x.png | Bin 0 -> 1141 bytes .../Contents.json | 23 +++ .../action_formatting_enabled.png | Bin 0 -> 530 bytes .../action_formatting_enabled@2x.png | Bin 0 -> 895 bytes .../action_formatting_enabled@3x.png | Bin 0 -> 1181 bytes Riot/Generated/Images.swift | 2 + Riot/Managers/Settings/RiotSettings.swift | 3 + Riot/Modules/Room/RoomViewController.m | 9 +- Riot/Modules/Room/RoomViewController.swift | 5 + .../WysiwygInputToolbarView.swift | 18 +- ...poserCreateActionListBridgePresenter.swift | 13 +- .../ComposerCreateActionListCoordinator.swift | 10 +- ...kComposerCreateActionListScreenState.swift | 5 +- .../ComposerCreateActionListModels.swift | 11 ++ .../View/ComposerCreateActionList.swift | 63 ++++++- .../ComposerCreateActionListViewModel.swift | 2 + .../Composer/Model/ComposerViewState.swift | 1 + .../Modules/Room/Composer/View/Composer.swift | 160 ++++++++++-------- .../ViewModel/ComposerViewModel.swift | 9 + .../ViewModel/ComposerViewModelProtocol.swift | 1 + changelog.d/6980.change | 1 + project.yml | 2 +- 26 files changed, 284 insertions(+), 83 deletions(-) create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_formatting_disabled.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_formatting_disabled.imageset/action_formatting_disabled.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_formatting_disabled.imageset/action_formatting_disabled@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_formatting_disabled.imageset/action_formatting_disabled@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/action_formatting_enabled.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/action_formatting_enabled@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/action_formatting_enabled@3x.png create mode 100644 changelog.d/6980.change diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved index ef28187e4..f08816ba8 100644 --- a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -23,7 +23,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift", "state" : { - "revision" : "d5ef7054fb43924d5b92d5d627347ca2bc333717" + "revision" : "b3a8468294c9e69083f5b2978d8f2adeb2d02dee" } }, { @@ -49,8 +49,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { - "revision" : "48254824bb4248676bf7ce56014ff57b142b77eb", - "version" : "1.0.2" + "revision" : "f504716c27d2e5d4144fa4794b12129301d17729", + "version" : "1.0.3" } }, { diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_disabled.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_disabled.imageset/Contents.json new file mode 100644 index 000000000..dbee5479f --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_disabled.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "action_formatting_disabled.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "action_formatting_disabled@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "action_formatting_disabled@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_disabled.imageset/action_formatting_disabled.png b/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_disabled.imageset/action_formatting_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..f7ef2b190aa889ab23308e4cdd50bec6e7236e5a GIT binary patch literal 513 zcmV+c0{;DpP)7K*#;F1dsxUdT!U7Usb*Z*S)9+mD+A7~rpgzbOt}xqx3T=Ba<@FN_)CEn%an_9ce;FD`h~4x?5vt zcoc7u6Pp1<{pa~uH*Mn%7-Jd_8~oARi%y>IlOj$B#>)|Z$U1wmV$UD%`q+@KvOF3# zUFIz#qS0<(@QD)F_9OI)?eHv2js#P}L~VMXSbI2yen71HFtReogVB5kx9bx#&?_`` z7*<15g5I?x_z~&+u~>gla_+Qqqxnj?s6z1uJN z6G0Tmzq5%yz@)@O>&cQH6g;%#ED9To2SI550HO8hsRp#RrzWRjD;V+M(e&V5LGfVQ z6B?iOPZ=^z>UD#&`}2Q7(XS?=WEu@7-FiEL}pI0595%zP$P-kOL4-&=UkqfRo-VpFl}89t>#@ zp1eRb@gN7QM>P@olb~gHoY_(-ba%SSFjLm~C7WG#rrYhqyYKVv%m5aP#bU8oEEbE! zV$lsji_lga$q?oYfsr)8lKjp}%te0XdS3|!X$gVaiw}F4$0mc?2)y2<&Vfl7!ni=m z;z-K%oR5+&iwWvi zV>t$-B6Jl|l!F0`3PeDLqV{Ek-hly(3Di~`&qR!#Dh!#nSK}FI#+X3FMJ~7jC4UQQ zN+5E?sltfO%dMSt^G7eqqiZ6lm0SK{otAGTf@IcSp>By%r@#*bMbK^eMg$7Cc2Zc& z4wn50A(#*9q1*Dc3naC6=FV&$A|G-It(LD{Ac?h?m#+;xtNpR@k{Xh0Xcsi0RUo;w zlTyPOU@E95zeZY@MfryE(r3HOHs5CER=e z67N>N;K%w;>~(G(If>EJS8-y0d+n5P%nmNn7Uz@+RNy<`PK7v^-%Q6sn0gL5`|$cw zSG`Cg9}U;{k-6A~+QHz5V(i2bh~IRdKNWQ})K@Zr(zS!<-xd&8-tv@DM{0X}q&n!3 zvmbG#*^?m?s9ZZZfAA>c%I%{k>;G(cL6!;h$MPPXxf#nSO7bK*+LMWbMqmT_C>3S6r(FN=PWdd=l>|spSgXLGQ_m`lM_Sx|%{*^K%px&HhcI}%215&QVcV)K7_Iz;N4ThvnT|>+D~fgikOcJc&m_RRop78(AK^43Rx%a`Bp? zL?CaS^@cQ+mpl6l*kzVF`}hlI@NyZCgx*^ulnAtXqkld@*E=@n literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/Contents.json new file mode 100644 index 000000000..198b65f6e --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "action_formatting_enabled.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "action_formatting_enabled@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "action_formatting_enabled@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/action_formatting_enabled.png b/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/action_formatting_enabled.png new file mode 100644 index 0000000000000000000000000000000000000000..06798aaa7399730b59830b0325eaed397943a7a0 GIT binary patch literal 530 zcmV+t0`2{YP)mHxNY60p0+rpe$G5!m*;wj1F-MF-we(__Z=W8Bgp1k_2Nz^Mq401?bX%r`|#B>ja<>IG`+Mq z19X8{3kfH+rCio^!n6cz=cd4|3EpHg46W(JY6%FD>jn%*reRGK@d%)F_}DL}#ik1Q z)g$=TlB6;B9g)VqoC%%G0z#VQkhVaZ98zTOPK@^(63Te{jmPVLWtlp zH_591S%9sndR<_?L=p8pf!`(~tl7@MPRfykpTO26pLv$FI@a`dCqLK_{A9~E)Uz9# zA}RNQ(<>PgUqBD;yxK0t{YadFA+WKLN0>o;bv6<3A{W@QYZIgjur-B*_0Ap5Pg>{k z;|8{%k5{IlWoO?CjSCdQ+E|ah4++@a6Wg7xF@yPOYjX6|8%jbiZrYIwmAj-gK@hs| zFA=e6aONAwkP$0mb+xEmy3D_<0beKtSz)O)SpfLXi?~Q_`yph8x{zplZtXul0SB3w U1+z}KPyhe`07*qoM6N<$f*D!oD*ylh literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/action_formatting_enabled@2x.png b/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/action_formatting_enabled@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b2aec071e10e3496550d54b88dda8d8ab3f7d41b GIT binary patch literal 895 zcmV-_1AzRAP);jPTX@Ur&>^Tu`>oEdMunN%5Gjp8>WCHu@5N9uw^6ot_!73n0 zY8@jq)~=(h?j#!|5PQG`n}GD~BW&fP)J-2ErUUcx`sFv4vA`xEkwHrxa=ASb$xt8T z@Z(sC8|I(P^A*?xSk|dJ{8xAU{*>S%_dAoNvIwYO^>!@lBs$AINYn<9AIyM)yuSj^ z7eL%LBC%Qb#NmDnC@+kLZC^|E7EolJs$=Ix*HR(|X3W=8y#*9lrz+a=>2uGlkjtf! zwp5?5;3Z&Yoo4!s=T)5MoS<)`i$AD{mw=gd=0K1qbK3~A)5{)13*#lAsOr2E@_b&K z+wgE?%?TcsT7!xdTBk~%H3(X3QEz@@iLvf=mf2ci2#!=#P26+kJ3wz4>7m9MRT^0wOc+vi;(eLM7Iz>i$U=Dv)*(ZR>Q| zesLQENG&^-ulrrG)(IL1hjS93W?tK)iQIEP+kt|00;F4!3$_Ob1SZFry}5e(zWbee z;!>87PeGFVi?**c6W1@lw}~j`+~-U;gOjyp5Znyou;VlvuUPS%nLCIZQKyl>OV-`w zDoB`&Zo^NbqTfmdq+5R&oGMRa+e!98l^mTv;ej>dm(Bx-;^=?ryhhbWoksMfX~s(h z%=?q#u~1t$8)mG0CPw%5>2kq%5=d(tlnMxNx(h{x)+3<*=r2YT^002ovPDHLkV1oIzl28Bu literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/action_formatting_enabled@3x.png b/Riot/Assets/Images.xcassets/Room/Actions/action_formatting_enabled.imageset/action_formatting_enabled@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..414aa321fe0f20953c7cab5606657df6c7b4d064 GIT binary patch literal 1181 zcmV;O1Y-M%P)kI8 zW?trRg?T9ml7n@gGJFWAfSL4xjSk}rd z|FuiY_Yy%WYfo90Fc!D@%Tf_sTfP^8)8&onTXz zJ{VGYUT*irwH#T#Gn-dgJAa7oB16VeTWp44p{ZELy%* zfr`mlV$p(#{b_+Fd4}>xvsf##Do{m1o-ay2cR`b4n8_z=U=e))3s@6qXzhyOMSJi_ zcuw?FdNoUyZ%v?~wM$XuJ~DVtq}o8VV)@ntQd)aO?jxfjCn{=1U>GZFF=6fe(#PNd z%AsL$Ki^ISDXrZurY+xi1ik7UEV3Lgz#}H3$an;?48856-+|0r1RISYjguO#Fm@uu zXauEsY$aKH0>&)gs6Y{bahrWArnZM-FoecQjUJyo*TEpj;2X&5O#J;UPenwxVGN83 zgoqnNsvfj=y|SHvLFUezEq=>g#Y2h7x(#DsWQY~pIRuNUK2KR%R54t7$R}b0t9&B@ zNl$8wdbDlqJ`^scDC!q?W;E8kXl9aDuG&T7CNFl43y<8kx(C)_3|!Qtdp6HHSJjC? zK8b5#DuNPubAnkkGT?59zt++frasLyA4Uhm some View { + HStack { + Rectangle() + .foregroundColor(.clear) + .frame(width: 50, height: 30, alignment: .center) + .overlay( + Rectangle() + .foregroundColor(configuration.isOn + ? theme.colors.accent.opacity(0.5) + : theme.colors.primaryContent.opacity(0.25)) + .cornerRadius(7) + .padding(.all, 8) + ) + .overlay( + Circle() + .foregroundColor(configuration.isOn + ? theme.colors.accent + : theme.colors.background) + .padding(.all, 3) + .offset(x: configuration.isOn ? 11 : -11, y: 0) + .shadow(radius: configuration.isOn ? 0.0 : 2.0) + .animation(Animation.linear(duration: 0.1)) + + ).cornerRadius(20) + .onTapGesture { configuration.isOn.toggle() } + } + } +} diff --git a/RiotSwiftUI/Modules/Room/Composer/CreateActionList/ViewModel/ComposerCreateActionListViewModel.swift b/RiotSwiftUI/Modules/Room/Composer/CreateActionList/ViewModel/ComposerCreateActionListViewModel.swift index bd063b1b2..93fa9950b 100644 --- a/RiotSwiftUI/Modules/Room/Composer/CreateActionList/ViewModel/ComposerCreateActionListViewModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/CreateActionList/ViewModel/ComposerCreateActionListViewModel.swift @@ -35,6 +35,8 @@ class ComposerCreateActionListViewModel: ComposerCreateActionListViewModelType, switch viewAction { case .selectAction(let action): callback?(.done(action)) + case .toggleTextFormatting(let enabled): + callback?(.toggleTextFormatting(enabled)) } } } diff --git a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerViewState.swift b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerViewState.swift index 0f8ad1fdc..e9e893f42 100644 --- a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerViewState.swift +++ b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerViewState.swift @@ -19,6 +19,7 @@ import Foundation struct ComposerViewState: BindableState { var eventSenderDisplayName: String? var sendMode: ComposerSendMode = .send + var textFormattingEnabled: Bool = RiotSettings.shared.enableWysiwygTextFormatting var placeholder: String? } diff --git a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift index 624c84638..0647be03d 100644 --- a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift +++ b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift @@ -83,73 +83,10 @@ struct Composer: View { var body: some View { VStack(spacing: 8) { - let rect = RoundedRectangle(cornerRadius: cornerRadius) - VStack(spacing: 12) { - if viewModel.viewState.shouldDisplayContext { - HStack { - if let imageName = viewModel.viewState.contextImageName { - Image(imageName) - .foregroundColor(theme.colors.tertiaryContent) - } - if let contextDescription = viewModel.viewState.contextDescription { - Text(contextDescription) - .accessibilityIdentifier("contextDescription") - .font(.system(size: 12, weight: .medium)) - .foregroundColor(theme.colors.secondaryContent) - } - Spacer() - Button { - viewModel.send(viewAction: .cancel) - } label: { - Image(Asset.Images.inputCloseIcon.name) - .foregroundColor(theme.colors.tertiaryContent) - } - .accessibilityIdentifier("cancelButton") - } - .padding(.top, 8) - .padding(.horizontal, horizontalPadding) - } - HStack(alignment: .top, spacing: 0) { - WysiwygComposerView( - focused: $focused, - content: wysiwygViewModel.content, - replaceText: wysiwygViewModel.replaceText, - select: wysiwygViewModel.select, - didUpdateText: wysiwygViewModel.didUpdateText - ) - .tintColor(theme.colors.accent) - .placeholder(viewModel.viewState.placeholder, color: theme.colors.tertiaryContent) - .frame(height: wysiwygViewModel.idealHeight) - .onAppear { - wysiwygViewModel.setup() - } - Button { - wysiwygViewModel.maximised.toggle() - } label: { - Image(toggleButtonImageName) - .resizable() - .foregroundColor(theme.colors.tertiaryContent) - .frame(width: 16, height: 16) - } - .accessibilityIdentifier(toggleButtonAcccessibilityIdentifier) - .padding(.leading, 12) - .padding(.trailing, 4) - } - .padding(.horizontal, horizontalPadding) - .padding(.top, topPadding) - .padding(.bottom, verticalPadding) + if viewModel.viewState.textFormattingEnabled { + composerContainer } - .clipShape(rect) - .overlay(rect.stroke(borderColor, lineWidth: 1)) - .animation(.easeInOut(duration: 0.1), value: wysiwygViewModel.idealHeight) - .padding(.horizontal, horizontalPadding) - .padding(.top, 8) - .onTapGesture { - if !focused { - focused = true - } - } - HStack(spacing: 0) { + HStack(alignment: .bottom, spacing: 0) { Button { showSendMediaActions() } label: { @@ -162,13 +99,21 @@ struct Composer: View { .background(Circle().fill(theme.colors.system)) .padding(.trailing, 8) .accessibilityLabel(VectorL10n.create) - FormattingToolbar(formatItems: formatItems) { type in - wysiwygViewModel.apply(type.action) + if viewModel.viewState.textFormattingEnabled { + FormattingToolbar(formatItems: formatItems) { type in + wysiwygViewModel.apply(type.action) + } + .frame(height: 44) + Spacer() + } else { + composerContainer } - .frame(height: 44) - Spacer() Button { - sendMessageAction(wysiwygViewModel.content) + if wysiwygViewModel.plainTextMode { + sendMessageAction(wysiwygViewModel.plainTextModeContent) + } else { + sendMessageAction(wysiwygViewModel.content) + } wysiwygViewModel.clearContent() } label: { if viewModel.viewState.sendMode == .edit { @@ -193,6 +138,79 @@ struct Composer: View { .padding(.bottom, 4) } } + + private var composerContainer: some View { + let rect = RoundedRectangle(cornerRadius: cornerRadius) + return VStack(spacing: 12) { + if viewModel.viewState.shouldDisplayContext { + HStack { + if let imageName = viewModel.viewState.contextImageName { + Image(imageName) + .foregroundColor(theme.colors.tertiaryContent) + } + if let contextDescription = viewModel.viewState.contextDescription { + Text(contextDescription) + .accessibilityIdentifier("contextDescription") + .font(.system(size: 12, weight: .medium)) + .foregroundColor(theme.colors.secondaryContent) + } + Spacer() + Button { + viewModel.send(viewAction: .cancel) + } label: { + Image(Asset.Images.inputCloseIcon.name) + .foregroundColor(theme.colors.tertiaryContent) + } + .accessibilityIdentifier("cancelButton") + } + .padding(.top, 8) + .padding(.horizontal, horizontalPadding) + } + HStack(alignment: .top, spacing: 0) { + WysiwygComposerView( + focused: $focused, + content: wysiwygViewModel.content, + replaceText: wysiwygViewModel.replaceText, + select: wysiwygViewModel.select, + didUpdateText: wysiwygViewModel.didUpdateText + ) + .tintColor(theme.colors.accent) + .placeholder(viewModel.viewState.placeholder, color: theme.colors.tertiaryContent) + .frame(height: wysiwygViewModel.idealHeight) + .onAppear { + if wysiwygViewModel.isContentEmpty { + wysiwygViewModel.setup() + } + } + if viewModel.viewState.textFormattingEnabled { + Button { + wysiwygViewModel.maximised.toggle() + } label: { + Image(toggleButtonImageName) + .resizable() + .foregroundColor(theme.colors.tertiaryContent) + .frame(width: 16, height: 16) + } + .accessibilityIdentifier(toggleButtonAcccessibilityIdentifier) + .padding(.leading, 12) + .padding(.trailing, 4) + } + } + .padding(.horizontal, horizontalPadding) + .padding(.top, topPadding) + .padding(.bottom, verticalPadding) + } + .clipShape(rect) + .overlay(rect.stroke(borderColor, lineWidth: 1)) + .animation(.easeInOut(duration: 0.1), value: wysiwygViewModel.idealHeight) + .padding(.horizontal, horizontalPadding) + .padding(.top, 8) + .onTapGesture { + if !focused { + focused = true + } + } + } } // MARK: Previews diff --git a/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift b/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift index 1e44ed049..78d3028f2 100644 --- a/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift @@ -35,6 +35,15 @@ final class ComposerViewModel: ComposerViewModelType, ComposerViewModelProtocol state.sendMode = newValue } } + + var textFormattingEnabled: Bool { + get { + state.textFormattingEnabled + } + set { + state.textFormattingEnabled = newValue + } + } var eventSenderDisplayName: String? { get { diff --git a/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModelProtocol.swift b/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModelProtocol.swift index 70d943dc7..2bf05c457 100644 --- a/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModelProtocol.swift +++ b/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModelProtocol.swift @@ -20,6 +20,7 @@ protocol ComposerViewModelProtocol { var context: ComposerViewModelType.Context { get } var callback: ((ComposerViewModelResult) -> Void)? { get set } var sendMode: ComposerSendMode { get set } + var textFormattingEnabled: Bool { get set } var eventSenderDisplayName: String? { get set } var placeholder: String? { get set } } diff --git a/changelog.d/6980.change b/changelog.d/6980.change new file mode 100644 index 000000000..88d3df0f9 --- /dev/null +++ b/changelog.d/6980.change @@ -0,0 +1 @@ +Labs: Rich text-editor - Add support for plain text mode diff --git a/project.yml b/project.yml index 391e91acc..46c196127 100644 --- a/project.yml +++ b/project.yml @@ -53,7 +53,7 @@ packages: branch: main WysiwygComposer: url: https://github.com/matrix-org/matrix-wysiwyg-composer-swift - revision: d5ef7054fb43924d5b92d5d627347ca2bc333717 + revision: b3a8468294c9e69083f5b2978d8f2adeb2d02dee DeviceKit: url: https://github.com/devicekit/DeviceKit majorVersion: 4.7.0 From 1ffbc75fcf8cf0349f31f5e46b38f67a256b2dd0 Mon Sep 17 00:00:00 2001 From: aringenbach Date: Wed, 26 Oct 2022 15:53:39 +0200 Subject: [PATCH 09/17] Remove change on Apple swift-collections revision --- Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved index f08816ba8..c1246bc53 100644 --- a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -49,8 +49,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections", "state" : { - "revision" : "f504716c27d2e5d4144fa4794b12129301d17729", - "version" : "1.0.3" + "revision" : "48254824bb4248676bf7ce56014ff57b142b77eb", + "version" : "1.0.2" } }, { From 26caa7ed48e47e3633fb6f495614d68086fcead6 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 26 Oct 2022 17:09:26 +0200 Subject: [PATCH 10/17] removed RiotSettings a non RiotSwiftUI reference from the ViewState code --- .../Modules/Room/Composer/Model/ComposerViewState.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerViewState.swift b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerViewState.swift index e9e893f42..5dfdc6bc5 100644 --- a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerViewState.swift +++ b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerViewState.swift @@ -19,13 +19,13 @@ import Foundation struct ComposerViewState: BindableState { var eventSenderDisplayName: String? var sendMode: ComposerSendMode = .send - var textFormattingEnabled: Bool = RiotSettings.shared.enableWysiwygTextFormatting + var textFormattingEnabled = true var placeholder: String? } extension ComposerViewState { var shouldDisplayContext: Bool { - return sendMode == .edit || sendMode == .reply + sendMode == .edit || sendMode == .reply } var contextDescription: String? { From 19939ae4207721b64eb165bbf13cfb709f5703af Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 26 Oct 2022 17:35:59 +0200 Subject: [PATCH 11/17] fixed a test --- .../Test/Unit/ComposerCreateActionListTests.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Room/Composer/CreateActionList/Test/Unit/ComposerCreateActionListTests.swift b/RiotSwiftUI/Modules/Room/Composer/CreateActionList/Test/Unit/ComposerCreateActionListTests.swift index 33258467b..35532a212 100644 --- a/RiotSwiftUI/Modules/Room/Composer/CreateActionList/Test/Unit/ComposerCreateActionListTests.swift +++ b/RiotSwiftUI/Modules/Room/Composer/CreateActionList/Test/Unit/ComposerCreateActionListTests.swift @@ -23,7 +23,13 @@ class ComposerCreateActionListTests: XCTestCase { var context: ComposerCreateActionListViewModel.Context! override func setUpWithError() throws { - viewModel = ComposerCreateActionListViewModel(initialViewState: ComposerCreateActionListViewState(actions: ComposerCreateAction.allCases)) + viewModel = ComposerCreateActionListViewModel( + initialViewState: ComposerCreateActionListViewState( + actions: ComposerCreateAction.allCases, + wysiwygEnabled: true, + bindings: ComposerCreateActionListBindings(textFormattingEnabled: true) + ) + ) context = viewModel.context } From 3d855db758a466bfa4a4e1076f4ebecab95c5e21 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Thu, 27 Oct 2022 17:15:18 +0200 Subject: [PATCH 12/17] Avoid simultaneous state changes (#6986) --- .../VoiceBroadcastService.swift | 141 ++++++++---------- .../VoiceBroadcastRecorderService.swift | 1 - 2 files changed, 66 insertions(+), 76 deletions(-) diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift index 08afca647..e6d6171a8 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift @@ -23,15 +23,18 @@ public class VoiceBroadcastService: NSObject { // MARK: - Properties - public private(set) var voiceBroadcastId: String? public let room: MXRoom + public private(set) var voiceBroadcastId: String? public private(set) var state: VoiceBroadcastInfo.State + // Mechanism to process one call of sendVoiceBroadcastInfo() at a time + private let asyncTaskQueue: MXAsyncTaskQueue // MARK: - Setup public init(room: MXRoom, state: VoiceBroadcastInfo.State) { self.room = room self.state = state + self.asyncTaskQueue = MXAsyncTaskQueue(label: "VoiceBroadcastServiceQueueEventSerialQueue-" + MXTools.generateSecret()) } // MARK: - Constants @@ -43,9 +46,8 @@ public class VoiceBroadcastService: NSObject { /// Start a voice broadcast. /// - Parameters: /// - completion: A closure called when the operation completes. Provides the event id of the event generated on the home server on success. - /// - Returns: a `MXHTTPOperation` instance. - func startVoiceBroadcast(completion: @escaping (MXResponse) -> Void) -> MXHTTPOperation? { - return sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.started) { [weak self] response in + func startVoiceBroadcast(completion: @escaping (MXResponse) -> Void) { + sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.started) { [weak self] response in guard let self = self else { return } switch response { @@ -61,25 +63,22 @@ public class VoiceBroadcastService: NSObject { /// Pause a voice broadcast. /// - Parameters: /// - completion: A closure called when the operation completes. Provides the event id of the event generated on the home server on success. - /// - Returns: a `MXHTTPOperation` instance. - func pauseVoiceBroadcast(completion: @escaping (MXResponse) -> Void) -> MXHTTPOperation? { - return sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.paused, completion: completion) + func pauseVoiceBroadcast(completion: @escaping (MXResponse) -> Void) { + sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.paused, completion: completion) } /// resume a voice broadcast. /// - Parameters: /// - completion: A closure called when the operation completes. Provides the event id of the event generated on the home server on success. - /// - Returns: a `MXHTTPOperation` instance. - func resumeVoiceBroadcast(completion: @escaping (MXResponse) -> Void) -> MXHTTPOperation? { - return sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.resumed, completion: completion) + func resumeVoiceBroadcast(completion: @escaping (MXResponse) -> Void) { + sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.resumed, completion: completion) } /// stop a voice broadcast info. /// - Parameters: /// - completion: A closure called when the operation completes. Provides the event id of the event generated on the home server on success. - /// - Returns: a `MXHTTPOperation` instance. - func stopVoiceBroadcast(completion: @escaping (MXResponse) -> Void) -> MXHTTPOperation? { - return sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.stopped, completion: completion) + func stopVoiceBroadcast(completion: @escaping (MXResponse) -> Void) { + sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.stopped, completion: completion) } func getState() -> String { @@ -104,7 +103,6 @@ public class VoiceBroadcastService: NSObject { func sendChunkOfVoiceBroadcast(audioFileLocalURL: URL, mimeType: String?, duration: UInt, - samples: [Float]?, sequence: UInt, success: @escaping ((String?) -> Void), failure: @escaping ((Error?) -> Void)) { @@ -116,7 +114,6 @@ public class VoiceBroadcastService: NSObject { voiceBroadcastId: voiceBroadcastId, mimeType: mimeType, duration: duration, - samples: samples, sequence: sequence, success: success, failure: failure) @@ -137,52 +134,58 @@ public class VoiceBroadcastService: NSObject { } } - private func sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State, completion: @escaping (MXResponse) -> Void) -> MXHTTPOperation? { + private func sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State, completion: @escaping (MXResponse) -> Void) { guard let userId = self.room.mxSession.myUserId else { completion(.failure(VoiceBroadcastServiceError.missingUserId)) - return nil + return } - guard self.allowedStates(from: self.state).contains(state) else { - MXLog.warning("[VoiceBroadcastService] sendVoiceBroadcastInfo: unexpected state change \(self.state) -> \(state)") - completion(.failure(VoiceBroadcastServiceError.unexpectedState)) - return nil - } - - let stateKey = userId - - let voiceBroadcastInfo = VoiceBroadcastInfo() - - voiceBroadcastInfo.deviceId = self.room.mxSession.myDeviceId - - voiceBroadcastInfo.state = state.rawValue - - if state != VoiceBroadcastInfo.State.started { - guard let voiceBroadcastId = self.voiceBroadcastId else { - completion(.failure(VoiceBroadcastServiceError.notStarted)) - return nil + asyncTaskQueue.async { (taskCompleted) in + guard self.allowedStates(from: self.state).contains(state) else { + MXLog.warning("[VoiceBroadcastService] sendVoiceBroadcastInfo: unexpected state change \(self.state) -> \(state)") + completion(.failure(VoiceBroadcastServiceError.unexpectedState)) + taskCompleted() + return } - voiceBroadcastInfo.voiceBroadcastId = voiceBroadcastId - } else { - voiceBroadcastInfo.chunkLength = BuildSettings.voiceBroadcastChunkLength - } - - guard let stateEventContent = voiceBroadcastInfo.jsonDictionary() as? [String: Any] else { - completion(.failure(VoiceBroadcastServiceError.unknown)) - return nil - } - - return self.room.sendStateEvent(.custom(VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType), - content: stateEventContent, stateKey: stateKey) { [weak self] response in - guard let self = self else { return } + let stateKey = userId - switch response { - case .success(let object): - self.state = state - completion(.success(object)) - case .failure(let error): - completion(.failure(error)) + let voiceBroadcastInfo = VoiceBroadcastInfo() + + voiceBroadcastInfo.deviceId = self.room.mxSession.myDeviceId + + voiceBroadcastInfo.state = state.rawValue + + if state != VoiceBroadcastInfo.State.started { + guard let voiceBroadcastId = self.voiceBroadcastId else { + completion(.failure(VoiceBroadcastServiceError.notStarted)) + taskCompleted() + return + } + + voiceBroadcastInfo.voiceBroadcastId = voiceBroadcastId + } else { + voiceBroadcastInfo.chunkLength = BuildSettings.voiceBroadcastChunkLength + } + + guard let stateEventContent = voiceBroadcastInfo.jsonDictionary() as? [String: Any] else { + completion(.failure(VoiceBroadcastServiceError.unknown)) + taskCompleted() + return + } + + self.room.sendStateEvent(.custom(VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType), + content: stateEventContent, stateKey: stateKey) { [weak self] response in + guard let self = self else { return } + + switch response { + case .success(let object): + self.state = state + completion(.success(object)) + case .failure(let error): + completion(.failure(error)) + } + taskCompleted() } } } @@ -195,10 +198,8 @@ extension VoiceBroadcastService { /// - Parameters: /// - success: A closure called when the operation is complete. /// - failure: A closure called when the operation fails. - /// - Returns: a `MXHTTPOperation` instance. - @discardableResult - @objc public func startVoiceBroadcast(success: @escaping (String?) -> Void, failure: @escaping (Error) -> Void) -> MXHTTPOperation? { - return self.startVoiceBroadcast { response in + @objc public func startVoiceBroadcast(success: @escaping (String?) -> Void, failure: @escaping (Error) -> Void) { + self.startVoiceBroadcast { response in switch response { case .success(let object): success(object) @@ -212,10 +213,8 @@ extension VoiceBroadcastService { /// - Parameters: /// - success: A closure called when the operation is complete. /// - failure: A closure called when the operation fails. - /// - Returns: a `MXHTTPOperation` instance. - @discardableResult - @objc public func pauseVoiceBroadcast(success: @escaping (String?) -> Void, failure: @escaping (Error) -> Void) -> MXHTTPOperation? { - return self.pauseVoiceBroadcast { response in + @objc public func pauseVoiceBroadcast(success: @escaping (String?) -> Void, failure: @escaping (Error) -> Void) { + self.pauseVoiceBroadcast { response in switch response { case .success(let object): success(object) @@ -229,10 +228,8 @@ extension VoiceBroadcastService { /// - Parameters: /// - success: A closure called when the operation is complete. /// - failure: A closure called when the operation fails. - /// - Returns: a `MXHTTPOperation` instance. - @discardableResult - @objc public func resumeVoiceBroadcast(success: @escaping (String?) -> Void, failure: @escaping (Error) -> Void) -> MXHTTPOperation? { - return self.resumeVoiceBroadcast { response in + @objc public func resumeVoiceBroadcast(success: @escaping (String?) -> Void, failure: @escaping (Error) -> Void) { + self.resumeVoiceBroadcast { response in switch response { case .success(let object): success(object) @@ -246,10 +243,8 @@ extension VoiceBroadcastService { /// - Parameters: /// - success: A closure called when the operation is complete. /// - failure: A closure called when the operation fails. - /// - Returns: a `MXHTTPOperation` instance. - @discardableResult - @objc public func stopVoiceBroadcast(success: @escaping (String?) -> Void, failure: @escaping (Error) -> Void) -> MXHTTPOperation? { - return self.stopVoiceBroadcast { response in + @objc public func stopVoiceBroadcast(success: @escaping (String?) -> Void, failure: @escaping (Error) -> Void) { + self.stopVoiceBroadcast { response in switch response { case .success(let object): success(object) @@ -279,14 +274,10 @@ extension MXRoom { voiceBroadcastId: String, mimeType: String?, duration: UInt, - samples: [Float]?, threadId: String? = nil, sequence: UInt, success: @escaping ((String?) -> Void), failure: @escaping ((Error?) -> Void)) -> MXHTTPOperation? { - let boxedSamples = samples?.compactMap { NSNumber(value: $0) } - - guard let relatesTo = MXEventContentRelatesTo(relationType: MXEventRelationTypeReference, eventId: voiceBroadcastId).jsonDictionary() as? [String: Any] else { failure(VoiceBroadcastServiceError.unknown) @@ -300,7 +291,7 @@ extension MXRoom { VoiceBroadcastSettings.voiceBroadcastContentKeyChunkType: sequenceValue], mimeType: mimeType, duration: duration, - samples: boxedSamples, + samples: nil, threadId: threadId, localEcho: nil, success: success, diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift index 8b6c20373..0a9d4745e 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift @@ -212,7 +212,6 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { self.voiceBroadcastService?.sendChunkOfVoiceBroadcast(audioFileLocalURL: convertedUrl, mimeType: "audio/mp4", duration: UInt(duration * 1000), - samples: nil, sequence: UInt(sequence)) { eventId in MXLog.debug("[VoiceBroadcastRecorderService] Send voice broadcast chunk with success.") self.deleteRecording(at: convertedUrl) From 64793d217b2dfb1d5f9c86b1d8850312eb1e10af Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Thu, 27 Oct 2022 18:03:25 +0200 Subject: [PATCH 13/17] No customization for emptycell (#7000) --- .../DataSources/HomeMessagesSearchDataSource.m | 2 +- Riot/Modules/Room/DataSources/RoomDataSource.m | 2 +- Riot/Modules/Room/MXKRoomViewController.m | 16 ++++++++-------- Riot/Modules/Room/RoomViewController.m | 4 ++-- .../Search/DataSources/RoomSearchDataSource.m | 2 +- Riot/Utils/EventFormatter.m | 3 ++- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m b/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m index f2f2cd406..1488377f5 100644 --- a/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m +++ b/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m @@ -152,7 +152,7 @@ UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath]; // Finalize cell view customization here - if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { MXKRoomBubbleTableViewCell *bubbleCell = (MXKRoomBubbleTableViewCell*)cell; diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 842623818..5d76bd5f6 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -395,7 +395,7 @@ const CGFloat kTypingCellHeight = 24; id cellDecorator = [RoomTimelineConfiguration shared].currentStyle.cellDecorator; // Finalize cell view customization here - if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { MXKRoomBubbleTableViewCell *bubbleCell = (MXKRoomBubbleTableViewCell*)cell; [self resetAccessibilityForCell:bubbleCell]; diff --git a/Riot/Modules/Room/MXKRoomViewController.m b/Riot/Modules/Room/MXKRoomViewController.m index b0f547bc4..0f464349f 100644 --- a/Riot/Modules/Room/MXKRoomViewController.m +++ b/Riot/Modules/Room/MXKRoomViewController.m @@ -1857,7 +1857,7 @@ CGFloat localPositionOfEvent = 0.0; - if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell *)cell; @@ -2301,7 +2301,7 @@ CGFloat eventBottomPosition = eventTopPosition + cell.frame.size.height; // Compute accurate event positions in case of bubble with multiple components - if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell *)cell; NSArray *bubbleComponents = roomBubbleTableViewCell.bubbleData.bubbleComponents; @@ -2599,11 +2599,11 @@ roomDataSource.showBubblesDateTime = !roomDataSource.showBubblesDateTime; MXLogDebug(@" -> Turn %@ cells date", roomDataSource.showBubblesDateTime ? @"ON" : @"OFF"); } - else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnAttachmentView] && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnAttachmentView] && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { [self showAttachmentInCell:(MXKRoomBubbleTableViewCell *)cell]; } - else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellLongPressOnProgressView] && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellLongPressOnProgressView] && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell *)cell; @@ -2714,7 +2714,7 @@ } } } - else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellLongPressOnEvent] && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellLongPressOnEvent] && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { [self dismissKeyboard]; @@ -3084,7 +3084,7 @@ return; } - if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell *)cell; selectedText = roomBubbleTableViewCell.bubbleData.textMessage; @@ -3623,7 +3623,7 @@ // Keep here the image view used to display the attachment in the selected cell. // Note: Only `MXKRoomBubbleTableViewCell` and `MXKSearchTableViewCell` are supported for the moment. - if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { self.openedAttachmentImageView = ((MXKRoomBubbleTableViewCell *)cell).attachmentView.imageView; } @@ -3801,7 +3801,7 @@ }]; - if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { // Start animation in case of download MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell *)cell; diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 504ddc1ff..d9f43da5f 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5270,7 +5270,7 @@ static CGSize kThreadListBarButtonItemImageSize; } } - if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell*)cell; if (roomBubbleTableViewCell.readMarkerView) @@ -6522,7 +6522,7 @@ static CGSize kThreadListBarButtonItemImageSize; if (self.roomDataSource.isLive && !self.roomDataSource.isPeeking && self.roomDataSource.showReadMarker && self.roomDataSource.room.accountData.readMarkerEventId) { UITableViewCell *cell = [self.bubblesTableView visibleCells].firstObject; - if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell*)cell; // Check whether the read marker is inside the first displayed cell. diff --git a/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m b/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m index 9c0530a7b..2790df090 100644 --- a/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m +++ b/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m @@ -131,7 +131,7 @@ UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath]; // Finalize cell view customization here - if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && ![cell isKindOfClass:MXKRoomEmptyBubbleTableViewCell.class]) { MXKRoomBubbleTableViewCell *bubbleCell = (MXKRoomBubbleTableViewCell*)cell; diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index 80efe2f99..2a1fe4879 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -273,7 +273,8 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; return [self renderString:displayText forEvent:event]; } } else if ([event.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]) { - MXLogDebug(@"VB incoming build string") + // do not show voice broadcast info in the timeline + return nil; } } From 58042e7562b7ac916aa5aad9b8d310c31da7668b Mon Sep 17 00:00:00 2001 From: giomfo Date: Fri, 28 Oct 2022 12:46:08 +0200 Subject: [PATCH 14/17] Voice Broadcast - BugFix - send the last chunk (#7002) * Voice Broadcast - BugFix - send the last chunk with the right sequence number - we reset now and teardown the service only after the last chunk is sent --- .../VoiceBroadcastRecorderService.swift | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift index 0a9d4745e..2a1ce4d1c 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift @@ -70,8 +70,6 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { audioEngine.stop() audioEngine.inputNode.removeTap(onBus: audioNodeBus) - resetValues() - voiceBroadcastService?.stopVoiceBroadcast(success: { [weak self] _ in MXLog.debug("[VoiceBroadcastRecorderService] Stopped") @@ -82,12 +80,18 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { // Send current chunk if self.chunkFile != nil { - self.sendChunkFile(at: self.chunkFile.url, sequence: self.chunkFileNumber) + self.sendChunkFile(at: self.chunkFile.url, sequence: self.chunkFileNumber) { + self.tearDownVoiceBroadcastService() + } + } else { + self.tearDownVoiceBroadcastService() } - - self.session.tearDownVoiceBroadcastService() }, failure: { error in MXLog.error("[VoiceBroadcastRecorderService] Failed to stop voice broadcast", context: error) + // Discard the service on VoiceBroadcastService error. We keep the service in case of other error type + if error as? VoiceBroadcastServiceError != nil { + self.tearDownVoiceBroadcastService() + } }) } @@ -127,6 +131,12 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { chunkFileNumber = 0 } + /// Release the service + private func tearDownVoiceBroadcastService() { + resetValues() + session.tearDownVoiceBroadcastService() + } + /// Write audio buffer to chunk file. private func writeBuffer(_ buffer: AVAudioPCMBuffer) { let sampleRate = buffer.format.sampleRate @@ -176,9 +186,11 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { } /// Send chunk file to the server. - private func sendChunkFile(at url: URL, sequence: Int) { - guard let voiceBroadcastService = voiceBroadcastService else { + private func sendChunkFile(at url: URL, sequence: Int, completion: (() -> Void)? = nil) { + guard voiceBroadcastService != nil else { // FIXME: Manage error + MXLog.debug("[VoiceBroadcastRecorderService] sendChunkFile: service is not available") + completion?() return } @@ -202,7 +214,10 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { } convertAACToM4A(at: url) { [weak self] convertedUrl in - guard let self = self else { return } + guard let self = self else { + completion?() + return + } // Delete the source file. self.deleteRecording(at: url) @@ -215,11 +230,13 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { sequence: UInt(sequence)) { eventId in MXLog.debug("[VoiceBroadcastRecorderService] Send voice broadcast chunk with success.") self.deleteRecording(at: convertedUrl) + completion?() } failure: { error in MXLog.error("[VoiceBroadcastRecorderService] Failed to send voice broadcast chunk.", context: error) // Do not delete the file to be sent if request failed, the retry flow will need it // There's no manual mechanism to clean it up afterwards but the tmp folder // they live in will eventually be deleted by the system + completion?() } } } From e8c8323655f4010fe9fe093c27031e02cc6aabfc Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Fri, 28 Oct 2022 14:54:50 +0200 Subject: [PATCH 15/17] Bug Fix : Crash if the room has avatar and voice broadcast tiles --- .../Coordinator/VoiceBroadcastPlaybackCoordinator.swift | 4 +++- .../Coordinator/VoiceBroadcastRecorderCoordinator.swift | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift index 499b43d59..ee7b51e4e 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift @@ -61,7 +61,9 @@ final class VoiceBroadcastPlaybackCoordinator: Coordinator, Presentable { func start() { } func toPresentable() -> UIViewController { - VectorHostingController(rootView: VoiceBroadcastPlaybackView(viewModel: viewModel.context)) + let view = VoiceBroadcastPlaybackView(viewModel: viewModel.context) + .addDependency(AvatarService.instantiate(mediaManager: parameters.session.mediaManager)) + return VectorHostingController(rootView: view) } func canEndVoiceBroadcast() -> Bool { diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift index 56f0854aa..e5e0afe3c 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift @@ -56,7 +56,9 @@ final class VoiceBroadcastRecorderCoordinator: Coordinator, Presentable { func start() { } func toPresentable() -> UIViewController { - VectorHostingController(rootView: VoiceBroadcastRecorderView(viewModel: voiceBroadcastRecorderViewModel.context)) + let view = VoiceBroadcastRecorderView(viewModel: voiceBroadcastRecorderViewModel.context) + .addDependency(AvatarService.instantiate(mediaManager: parameters.session.mediaManager)) + return VectorHostingController(rootView: view) } func pauseRecording() { From 67453cb0cbfcc2e5b08fde7309fe332f2e1b6b50 Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Mon, 31 Oct 2022 11:42:51 +0100 Subject: [PATCH 16/17] Voice broadcast - Disable the sleep mode during the recording until we are able to handle it Currently go to "sleep mode" pauses the voice broadcast recording --- .../Service/MatrixSDK/VoiceBroadcastRecorderService.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift index 2a1ce4d1c..0ad1fa682 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift @@ -63,12 +63,16 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { } try? audioEngine.start() + + // Disable the sleep mode during the recording until we are able to handle it + UIApplication.shared.isIdleTimerDisabled = true } func stopRecordingVoiceBroadcast() { MXLog.debug("[VoiceBroadcastRecorderService] Stop recording voice broadcast") audioEngine.stop() audioEngine.inputNode.removeTap(onBus: audioNodeBus) + UIApplication.shared.isIdleTimerDisabled = false voiceBroadcastService?.stopVoiceBroadcast(success: { [weak self] _ in MXLog.debug("[VoiceBroadcastRecorderService] Stopped") @@ -97,6 +101,7 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { func pauseRecordingVoiceBroadcast() { audioEngine.pause() + UIApplication.shared.isIdleTimerDisabled = false voiceBroadcastService?.pauseVoiceBroadcast(success: { [weak self] _ in guard let self = self else { return } @@ -118,7 +123,8 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { guard let self = self else { return } // Update state - self.serviceDelegate?.voiceBroadcastRecorderService(self, didUpdateState: .started) + self.serviceDelegate?.voiceBroadcastRecorderService(self, didUpdateState: .resumed) + UIApplication.shared.isIdleTimerDisabled = true }, failure: { error in MXLog.error("[VoiceBroadcastRecorderService] Failed to resume voice broadcast", context: error) }) From 0742641bebc39df45b7f911497e879a9bf3a81fe Mon Sep 17 00:00:00 2001 From: aringenbach Date: Mon, 7 Nov 2022 15:24:39 +0100 Subject: [PATCH 17/17] Remove indication about plain text mode coming soon --- Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Generated/Strings.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index c0adc30cb..a168812bd 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -797,7 +797,7 @@ Tap the + to start adding people."; "settings_labs_enable_new_session_manager" = "New session manager"; "settings_labs_enable_new_client_info_feature" = "Record the client name, version, and url to recognise sessions more easily in session manager"; "settings_labs_enable_new_app_layout" = "New Application Layout"; -"settings_labs_enable_wysiwyg_composer" = "Try out the rich text editor (plain text mode coming soon)"; +"settings_labs_enable_wysiwyg_composer" = "Try out the rich text editor"; "settings_labs_enable_voice_broadcast" = "Voice broadcast (under active development)"; "settings_version" = "Version %@"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index c64940371..1ff68cf26 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -7547,7 +7547,7 @@ public class VectorL10n: NSObject { public static var settingsLabsEnableVoiceBroadcast: String { return VectorL10n.tr("Vector", "settings_labs_enable_voice_broadcast") } - /// Try out the rich text editor (plain text mode coming soon) + /// Try out the rich text editor public static var settingsLabsEnableWysiwygComposer: String { return VectorL10n.tr("Vector", "settings_labs_enable_wysiwyg_composer") }