diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift
index d6b022948..2f85f3c13 100644
--- a/Config/BuildSettings.swift
+++ b/Config/BuildSettings.swift
@@ -409,7 +409,7 @@ final class BuildSettings: NSObject {
// MARK: - Voice Broadcast
static let voiceBroadcastChunkLength: Int = 120
- static let voiceBroadcastMaxLength: UInt64 = 144000
+ static let voiceBroadcastMaxLength: UInt = 14400 // 240min.
// MARK: - MXKAppSettings
static let enableBotCreation: Bool = false
diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_time_left.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_time_left.imageset/Contents.json
new file mode 100644
index 000000000..6dbed5648
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_time_left.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "voice_broadcast_time_left.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_time_left.imageset/voice_broadcast_time_left.svg b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_time_left.imageset/voice_broadcast_time_left.svg
new file mode 100644
index 000000000..82b9eb425
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_time_left.imageset/voice_broadcast_time_left.svg
@@ -0,0 +1,3 @@
+
diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings
index 68a4f873d..e9295cc9e 100644
--- a/Riot/Assets/en.lproj/Vector.strings
+++ b/Riot/Assets/en.lproj/Vector.strings
@@ -2202,6 +2202,7 @@ Tap the + to start adding people.";
"voice_broadcast_playback_loading_error" = "Unable to play this voice broadcast.";
"voice_broadcast_live" = "Live";
"voice_broadcast_tile" = "Voice broadcast";
+"voice_broadcast_time_left" = "%@ left";
// Mark: - Version check
diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift
index 94352b0be..dcf78a2e1 100644
--- a/Riot/Generated/Images.swift
+++ b/Riot/Generated/Images.swift
@@ -347,6 +347,7 @@ internal class Asset: NSObject {
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 voiceBroadcastTimeLeft = ImageAsset(name: "voice_broadcast_time_left")
internal static let launchScreenLogo = ImageAsset(name: "launch_screen_logo")
}
@objcMembers
diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift
index 41b878426..f773146b2 100644
--- a/Riot/Generated/Strings.swift
+++ b/Riot/Generated/Strings.swift
@@ -9155,6 +9155,10 @@ public class VectorL10n: NSObject {
public static var voiceBroadcastTile: String {
return VectorL10n.tr("Vector", "voice_broadcast_tile")
}
+ /// %@ left
+ public static func voiceBroadcastTimeLeft(_ p1: String) -> String {
+ return VectorL10n.tr("Vector", "voice_broadcast_time_left", p1)
+ }
/// Can't start a new voice broadcast
public static var voiceBroadcastUnauthorizedTitle: String {
return VectorL10n.tr("Vector", "voice_broadcast_unauthorized_title")
diff --git a/Riot/Modules/Room/TimelineCells/SizableCell/SizableBaseRoomCell.swift b/Riot/Modules/Room/TimelineCells/SizableCell/SizableBaseRoomCell.swift
index f33762144..b8ba675b2 100644
--- a/Riot/Modules/Room/TimelineCells/SizableCell/SizableBaseRoomCell.swift
+++ b/Riot/Modules/Room/TimelineCells/SizableCell/SizableBaseRoomCell.swift
@@ -65,6 +65,12 @@ class SizableBaseRoomCell: BaseRoomCell, SizableBaseRoomCellType {
return self.height(for: roomBubbleCellData, fitting: maxWidth)
}
+
+ override func prepareForReuse() {
+ cleanContentVC()
+
+ super.prepareForReuse()
+ }
// MARK - SizableBaseRoomCellType
@@ -173,10 +179,21 @@ class SizableBaseRoomCell: BaseRoomCell, SizableBaseRoomCellType {
}
return height
- }
-
+ }
+
+ private func cleanContentVC() {
+ contentVC?.removeFromParent()
+ contentVC?.view.removeFromSuperview()
+ contentVC?.didMove(toParent: nil)
+ contentVC = nil
+ }
+
+ // MARK: - Public
+
func addContentViewController(_ controller: UIViewController, on contentView: UIView) {
controller.view.invalidateIntrinsicContentSize()
+
+ cleanContentVC()
let parent = vc_parentViewController
parent?.addChild(controller)
@@ -185,13 +202,4 @@ class SizableBaseRoomCell: BaseRoomCell, SizableBaseRoomCellType {
contentVC = controller
}
-
- override func prepareForReuse() {
- contentVC?.removeFromParent()
- contentVC?.view.removeFromSuperview()
- contentVC?.didMove(toParent: nil)
- contentVC = nil
-
- super.prepareForReuse()
- }
}
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 8987cb1de..f673bebee 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
@@ -27,7 +27,7 @@ class VoiceBroadcastPlaybackPlainCell: SizableBaseRoomCell, RoomCellReactionsDis
let bubbleData = cellData as? RoomBubbleCellData,
let event = bubbleData.events.last,
let voiceBroadcastContent = VoiceBroadcastInfo(fromJSON: event.content),
- voiceBroadcastContent.state == VoiceBroadcastInfo.State.started.rawValue,
+ voiceBroadcastContent.state == VoiceBroadcastInfoState.started.rawValue,
let controller = VoiceBroadcastPlaybackProvider.shared.buildVoiceBroadcastPlaybackVCForEvent(event,
senderDisplayName: bubbleData.senderDisplayName,
voiceBroadcastState: bubbleData.voiceBroadcastState)
diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainCell.swift
index a65254be5..43047cfba 100644
--- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainCell.swift
+++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainCell.swift
@@ -28,7 +28,7 @@ class VoiceBroadcastRecorderPlainCell: SizableBaseRoomCell, RoomCellReactionsDis
let bubbleData = cellData as? RoomBubbleCellData,
let event = bubbleData.events.last,
let voiceBroadcastContent = VoiceBroadcastInfo(fromJSON: event.content),
- voiceBroadcastContent.state == VoiceBroadcastInfo.State.started.rawValue,
+ voiceBroadcastContent.state == VoiceBroadcastInfoState.started.rawValue,
let view = VoiceBroadcastRecorderProvider.shared.buildVoiceBroadcastRecorderViewForEvent(event, senderDisplayName: bubbleData.senderDisplayName) else {
return
}
diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift
index 53721ea00..31dd0045b 100644
--- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift
+++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift
@@ -33,7 +33,7 @@ public protocol VoiceBroadcastAggregatorDelegate: AnyObject {
func voiceBroadcastAggregatorDidEndLoading(_ aggregator: VoiceBroadcastAggregator)
func voiceBroadcastAggregator(_ aggregator: VoiceBroadcastAggregator, didFailWithError: Error)
func voiceBroadcastAggregator(_ aggregator: VoiceBroadcastAggregator, didReceiveChunk: VoiceBroadcastChunk)
- func voiceBroadcastAggregator(_ aggregator: VoiceBroadcastAggregator, didReceiveState: VoiceBroadcastInfo.State)
+ func voiceBroadcastAggregator(_ aggregator: VoiceBroadcastAggregator, didReceiveState: VoiceBroadcastInfoState)
func voiceBroadcastAggregatorDidUpdateData(_ aggregator: VoiceBroadcastAggregator)
}
@@ -64,7 +64,7 @@ public class VoiceBroadcastAggregator {
}
private(set) var launchState: VoiceBroadcastAggregatorLaunchState = .idle
- public private(set) var voiceBroadcastState: VoiceBroadcastInfo.State
+ public private(set) var voiceBroadcastState: VoiceBroadcastInfoState
public var delegate: VoiceBroadcastAggregatorDelegate?
deinit {
@@ -73,7 +73,7 @@ public class VoiceBroadcastAggregator {
}
}
- public init(session: MXSession, room: MXRoom, voiceBroadcastStartEventId: String, voiceBroadcastState: VoiceBroadcastInfo.State) throws {
+ public init(session: MXSession, room: MXRoom, voiceBroadcastStartEventId: String, voiceBroadcastState: VoiceBroadcastInfoState) throws {
self.session = session
self.room = room
self.voiceBroadcastStartEventId = voiceBroadcastStartEventId
@@ -118,7 +118,7 @@ public class VoiceBroadcastAggregator {
event.stateKey == self.voiceBroadcastSenderId,
let voiceBroadcastInfo = VoiceBroadcastInfo(fromJSON: event.content),
(event.eventId == self.voiceBroadcastStartEventId || voiceBroadcastInfo.voiceBroadcastId == self.voiceBroadcastStartEventId),
- let state = VoiceBroadcastInfo.State(rawValue: voiceBroadcastInfo.state) else {
+ let state = VoiceBroadcastInfoState(rawValue: voiceBroadcastInfo.state) else {
return
}
diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.swift
index b2bc1afe4..5e6218f29 100644
--- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.swift
+++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.swift
@@ -19,36 +19,29 @@ import Foundation
extension VoiceBroadcastInfo {
// MARK: - Constants
- public enum State: String {
- case started
- case paused
- case resumed
- case stopped
- }
-
// MARK: - Public
@objc static func isStarted(for name: String) -> Bool {
- return name == State.started.rawValue
+ return name == VoiceBroadcastInfoState.started.rawValue
}
@objc static func isStopped(for name: String) -> Bool {
- return name == State.stopped.rawValue
+ return name == VoiceBroadcastInfoState.stopped.rawValue
}
@objc static func startedValue() -> String {
- return State.started.rawValue
+ return VoiceBroadcastInfoState.started.rawValue
}
@objc static func pausedValue() -> String {
- return State.paused.rawValue
+ return VoiceBroadcastInfoState.paused.rawValue
}
@objc static func resumedValue() -> String {
- return State.resumed.rawValue
+ return VoiceBroadcastInfoState.resumed.rawValue
}
@objc static func stoppedValue() -> String {
- return State.stopped.rawValue
+ return VoiceBroadcastInfoState.stopped.rawValue
}
}
diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfoState.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfoState.swift
new file mode 100644
index 000000000..e808ddeb3
--- /dev/null
+++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfoState.swift
@@ -0,0 +1,22 @@
+//
+// 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.
+//
+
+public enum VoiceBroadcastInfoState: String {
+ case started
+ case paused
+ case resumed
+ case stopped
+}
diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift
index e6d6171a8..6a3072ec3 100644
--- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift
+++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift
@@ -25,13 +25,13 @@ public class VoiceBroadcastService: NSObject {
public let room: MXRoom
public private(set) var voiceBroadcastId: String?
- public private(set) var state: VoiceBroadcastInfo.State
+ public private(set) var state: VoiceBroadcastInfoState
// Mechanism to process one call of sendVoiceBroadcastInfo() at a time
private let asyncTaskQueue: MXAsyncTaskQueue
// MARK: - Setup
- public init(room: MXRoom, state: VoiceBroadcastInfo.State) {
+ public init(room: MXRoom, state: VoiceBroadcastInfoState) {
self.room = room
self.state = state
self.asyncTaskQueue = MXAsyncTaskQueue(label: "VoiceBroadcastServiceQueueEventSerialQueue-" + MXTools.generateSecret())
@@ -47,7 +47,7 @@ public class VoiceBroadcastService: NSObject {
/// - Parameters:
/// - completion: A closure called when the operation completes. Provides the event id of the event generated on the home server on success.
func startVoiceBroadcast(completion: @escaping (MXResponse) -> Void) {
- sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.started) { [weak self] response in
+ sendVoiceBroadcastInfo(state: VoiceBroadcastInfoState.started) { [weak self] response in
guard let self = self else { return }
switch response {
@@ -64,21 +64,21 @@ public class VoiceBroadcastService: NSObject {
/// - Parameters:
/// - completion: A closure called when the operation completes. Provides the event id of the event generated on the home server on success.
func pauseVoiceBroadcast(completion: @escaping (MXResponse) -> Void) {
- sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.paused, completion: completion)
+ sendVoiceBroadcastInfo(state: VoiceBroadcastInfoState.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.
func resumeVoiceBroadcast(completion: @escaping (MXResponse) -> Void) {
- sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.resumed, completion: completion)
+ sendVoiceBroadcastInfo(state: VoiceBroadcastInfoState.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.
func stopVoiceBroadcast(completion: @escaping (MXResponse) -> Void) {
- sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State.stopped, completion: completion)
+ sendVoiceBroadcastInfo(state: VoiceBroadcastInfoState.stopped, completion: completion)
}
func getState() -> String {
@@ -121,7 +121,7 @@ public class VoiceBroadcastService: NSObject {
// MARK: - Private
- private func allowedStates(from state: VoiceBroadcastInfo.State) -> [VoiceBroadcastInfo.State] {
+ private func allowedStates(from state: VoiceBroadcastInfoState) -> [VoiceBroadcastInfoState] {
switch state {
case .started:
return [.paused, .stopped]
@@ -134,7 +134,7 @@ public class VoiceBroadcastService: NSObject {
}
}
- private func sendVoiceBroadcastInfo(state: VoiceBroadcastInfo.State, completion: @escaping (MXResponse) -> Void) {
+ private func sendVoiceBroadcastInfo(state: VoiceBroadcastInfoState, completion: @escaping (MXResponse) -> Void) {
guard let userId = self.room.mxSession.myUserId else {
completion(.failure(VoiceBroadcastServiceError.missingUserId))
return
@@ -156,7 +156,7 @@ public class VoiceBroadcastService: NSObject {
voiceBroadcastInfo.state = state.rawValue
- if state != VoiceBroadcastInfo.State.started {
+ if state != VoiceBroadcastInfoState.started {
guard let voiceBroadcastId = self.voiceBroadcastId else {
completion(.failure(VoiceBroadcastServiceError.notStarted))
taskCompleted()
diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastServiceProvider.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastServiceProvider.swift
index e39c838b7..83051e1d5 100644
--- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastServiceProvider.swift
+++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastServiceProvider.swift
@@ -70,9 +70,9 @@ class VoiceBroadcastServiceProvider {
}
}
- private func createVoiceBroadcastService(for room: MXRoom, state: VoiceBroadcastInfo.State) {
+ private func createVoiceBroadcastService(for room: MXRoom, state: VoiceBroadcastInfoState) {
- let voiceBroadcastService = VoiceBroadcastService(room: room, state: VoiceBroadcastInfo.State.stopped)
+ let voiceBroadcastService = VoiceBroadcastService(room: room, state: VoiceBroadcastInfoState.stopped)
self.currentVoiceBroadcastService = voiceBroadcastService
@@ -95,22 +95,22 @@ class VoiceBroadcastServiceProvider {
private func setupVoiceBroadcastService(for room: MXRoom, completion: @escaping (VoiceBroadcastService?) -> Void) {
self.getLastVoiceBroadcastInfo(for: room) { event in
guard let voiceBroadcastInfoEvent = event else {
- self.createVoiceBroadcastService(for: room, state: VoiceBroadcastInfo.State.stopped)
+ self.createVoiceBroadcastService(for: room, state: VoiceBroadcastInfoState.stopped)
completion(self.currentVoiceBroadcastService)
return
}
guard let voiceBroadcastInfo = VoiceBroadcastInfo(fromJSON: voiceBroadcastInfoEvent.content) else {
- self.createVoiceBroadcastService(for: room, state: VoiceBroadcastInfo.State.stopped)
+ self.createVoiceBroadcastService(for: room, state: VoiceBroadcastInfoState.stopped)
completion(self.currentVoiceBroadcastService)
return
}
- if voiceBroadcastInfo.state == VoiceBroadcastInfo.State.stopped.rawValue {
- self.createVoiceBroadcastService(for: room, state: VoiceBroadcastInfo.State.stopped)
+ if voiceBroadcastInfo.state == VoiceBroadcastInfoState.stopped.rawValue {
+ self.createVoiceBroadcastService(for: room, state: VoiceBroadcastInfoState.stopped)
completion(self.currentVoiceBroadcastService)
} else if voiceBroadcastInfoEvent.stateKey == room.mxSession.myUserId {
- self.createVoiceBroadcastService(for: room, state: VoiceBroadcastInfo.State(rawValue: voiceBroadcastInfo.state) ?? VoiceBroadcastInfo.State.stopped)
+ self.createVoiceBroadcastService(for: room, state: VoiceBroadcastInfoState(rawValue: voiceBroadcastInfo.state) ?? VoiceBroadcastInfoState.stopped)
completion(self.currentVoiceBroadcastService)
} else {
completion(nil)
diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift
index d353e2f55..652d6f7b2 100644
--- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift
+++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift
@@ -22,7 +22,7 @@ struct VoiceBroadcastPlaybackCoordinatorParameters {
let session: MXSession
let room: MXRoom
let voiceBroadcastStartEvent: MXEvent
- let voiceBroadcastState: VoiceBroadcastInfo.State
+ let voiceBroadcastState: VoiceBroadcastInfoState
let senderDisplayName: String?
}
diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift
index 29b6252df..eaae1b11f 100644
--- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift
+++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift
@@ -47,7 +47,7 @@ import Foundation
let parameters = VoiceBroadcastPlaybackCoordinatorParameters(session: session,
room: room,
voiceBroadcastStartEvent: event,
- voiceBroadcastState: VoiceBroadcastInfo.State(rawValue: voiceBroadcastState) ?? VoiceBroadcastInfo.State.stopped,
+ voiceBroadcastState: VoiceBroadcastInfoState(rawValue: voiceBroadcastState) ?? VoiceBroadcastInfoState.stopped,
senderDisplayName: senderDisplayName)
guard let coordinator = try? VoiceBroadcastPlaybackCoordinator(parameters: parameters) else {
return nil
diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift
index d43a78d17..52fbe9b6c 100644
--- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift
+++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift
@@ -40,6 +40,19 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic
private var acceptProgressUpdates: Bool = true
private var isActuallyPaused: Bool = false
+ private var isPlayingLastChunk: Bool {
+ let chunks = reorderVoiceBroadcastChunks(chunks: Array(voiceBroadcastAggregator.voiceBroadcast.chunks))
+ guard let chunkDuration = chunks.last?.duration else {
+ return false
+ }
+
+ return state.bindings.progress + 1000 >= state.playingState.duration - Float(chunkDuration)
+ }
+
+ private var isLivePlayback: Bool {
+ return (!isPlaybackInitialized || isPlayingLastChunk) && (state.broadcastState == .started || state.broadcastState == .resumed)
+ }
+
// MARK: Public
// MARK: - Setup
@@ -127,7 +140,7 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic
// Check if the broadcast is over before stopping everything
// If not, the player should not stopped. The view state must be move to buffering
- if state.broadcastState == .stopped {
+ if state.broadcastState == .stopped, isPlayingLastChunk {
stop()
} else {
state.playbackState = .buffering
@@ -221,7 +234,7 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic
audioPlayer.play()
} else {
self.state.playbackState = .playing
- self.state.playingState.isLive = self.isLivePlayback()
+ self.state.playingState.isLive = self.isLivePlayback
}
}
} else {
@@ -308,19 +321,6 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic
state.bindings.progress = Float(progress)
}
-
- private func isPlayingLastChunk() -> Bool {
- let chunks = reorderVoiceBroadcastChunks(chunks: Array(voiceBroadcastAggregator.voiceBroadcast.chunks))
- guard let chunkDuration = chunks.last?.duration else {
- return false
- }
-
- return state.bindings.progress + 1000 >= state.playingState.duration - Float(chunkDuration)
- }
-
- private func isLivePlayback() -> Bool {
- return (!isPlaybackInitialized || isPlayingLastChunk()) && (state.broadcastState == .started || state.broadcastState == .resumed)
- }
private func handleWaitingLiveData() {
// Handle specifically the case where we were waiting data to start playing a live playback
@@ -352,11 +352,11 @@ extension VoiceBroadcastPlaybackViewModel: VoiceBroadcastAggregatorDelegate {
voiceBroadcastChunkQueue.append(didReceiveChunk)
}
- func voiceBroadcastAggregator(_ aggregator: VoiceBroadcastAggregator, didReceiveState: VoiceBroadcastInfo.State) {
+ func voiceBroadcastAggregator(_ aggregator: VoiceBroadcastAggregator, didReceiveState: VoiceBroadcastInfoState) {
state.broadcastState = didReceiveState
// Handle the live icon appearance
- state.playingState.isLive = isLivePlayback()
+ state.playingState.isLive = isLivePlayback
}
func voiceBroadcastAggregatorDidUpdateData(_ aggregator: VoiceBroadcastAggregator) {
@@ -377,7 +377,7 @@ extension VoiceBroadcastPlaybackViewModel: VoiceMessageAudioPlayerDelegate {
func audioPlayerDidStartPlaying(_ audioPlayer: VoiceMessageAudioPlayer) {
state.playbackState = .playing
- state.playingState.isLive = isLivePlayback()
+ state.playingState.isLive = isLivePlayback
isPlaybackInitialized = true
}
diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift
index 7aae493fb..18d80d3af 100644
--- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift
+++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift
@@ -44,7 +44,7 @@ struct VoiceBroadcastPlayingState {
struct VoiceBroadcastPlaybackViewState: BindableState {
var details: VoiceBroadcastPlaybackDetails
- var broadcastState: VoiceBroadcastInfo.State
+ var broadcastState: VoiceBroadcastInfoState
var playbackState: VoiceBroadcastPlaybackState
var playingState: VoiceBroadcastPlayingState
var bindings: VoiceBroadcastPlaybackViewStateBindings
diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift
index f2e28e5da..10538095d 100644
--- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift
+++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift
@@ -34,7 +34,13 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol {
private var chunkFile: AVAudioFile! = nil
private var chunkFrames: AVAudioFrameCount = 0
private var chunkFileNumber: Int = 0
-
+
+ private var currentElapsedTime: UInt = 0 // Time in seconds.
+ private var currentRemainingTime: UInt { // Time in seconds.
+ BuildSettings.voiceBroadcastMaxLength - currentElapsedTime
+ }
+ private var elapsedTimeTimer: Timer?
+
// MARK: Public
weak var serviceDelegate: VoiceBroadcastRecorderServiceDelegate?
@@ -67,12 +73,14 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol {
}
try audioEngine.start()
+ startTimer()
// Disable the sleep mode during the recording until we are able to handle it
UIApplication.shared.isIdleTimerDisabled = true
} catch {
MXLog.debug("[VoiceBroadcastRecorderService] startRecordingVoiceBroadcast error", context: error)
stopRecordingVoiceBroadcast()
+ invalidateTimer()
}
}
@@ -81,6 +89,7 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol {
audioEngine.stop()
audioEngine.inputNode.removeTap(onBus: audioNodeBus)
UIApplication.shared.isIdleTimerDisabled = false
+ invalidateTimer()
voiceBroadcastService?.stopVoiceBroadcast(success: { [weak self] _ in
MXLog.debug("[VoiceBroadcastRecorderService] Stopped")
@@ -110,6 +119,7 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol {
func pauseRecordingVoiceBroadcast() {
audioEngine.pause()
UIApplication.shared.isIdleTimerDisabled = false
+ invalidateTimer()
voiceBroadcastService?.pauseVoiceBroadcast(success: { [weak self] _ in
guard let self = self else { return }
@@ -126,6 +136,7 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol {
func resumeRecordingVoiceBroadcast() {
try? audioEngine.start()
+ startTimer()
voiceBroadcastService?.resumeVoiceBroadcast(success: { [weak self] _ in
guard let self = self else { return }
@@ -143,12 +154,14 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol {
private func resetValues() {
chunkFrames = 0
chunkFileNumber = 0
+ currentElapsedTime = 0
}
/// Release the service
private func tearDownVoiceBroadcastService() {
resetValues()
session.tearDownVoiceBroadcastService()
+ invalidateTimer()
do {
try AVAudioSession.sharedInstance().setActive(false)
@@ -157,6 +170,31 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol {
}
}
+ /// Start ElapsedTimeTimer.
+ private func startTimer() {
+ elapsedTimeTimer = Timer.scheduledTimer(timeInterval: 1.0,
+ target: self,
+ selector: #selector(updateCurrentElapsedTimeValue),
+ userInfo: nil,
+ repeats: true)
+ }
+
+ /// Invalidate ElapsedTimeTimer.
+ private func invalidateTimer() {
+ elapsedTimeTimer?.invalidate()
+ elapsedTimeTimer = nil
+ }
+
+ /// Update currentElapsedTime value.
+ @objc private func updateCurrentElapsedTimeValue() {
+ guard currentRemainingTime > 0 else {
+ stopRecordingVoiceBroadcast()
+ return
+ }
+ currentElapsedTime += 1
+ serviceDelegate?.voiceBroadcastRecorderService(self, didUpdateRemainingTime: self.currentRemainingTime)
+ }
+
/// Write audio buffer to chunk file.
private func writeBuffer(_ buffer: AVAudioPCMBuffer) {
let sampleRate = buffer.format.sampleRate
diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/VoiceBroadcastRecorderServiceProtocol.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/VoiceBroadcastRecorderServiceProtocol.swift
index 7b97eb83a..e457eb843 100644
--- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/VoiceBroadcastRecorderServiceProtocol.swift
+++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/VoiceBroadcastRecorderServiceProtocol.swift
@@ -18,6 +18,7 @@ import Foundation
protocol VoiceBroadcastRecorderServiceDelegate: AnyObject {
func voiceBroadcastRecorderService(_ service: VoiceBroadcastRecorderServiceProtocol, didUpdateState state: VoiceBroadcastRecorderState)
+ func voiceBroadcastRecorderService(_ service: VoiceBroadcastRecorderServiceProtocol, didUpdateRemainingTime remainingTime: UInt)
}
protocol VoiceBroadcastRecorderServiceProtocol {
diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift
index 411ce0333..6c2c21e3c 100644
--- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift
+++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift
@@ -53,6 +53,14 @@ struct VoiceBroadcastRecorderView: View {
} icon: {
Image(uiImage: Asset.Images.voiceBroadcastTileLive.image)
}
+
+ Label {
+ Text(viewModel.viewState.currentRecordingState.remainingTimeLabel)
+ .foregroundColor(theme.colors.secondaryContent)
+ .font(theme.fonts.caption1)
+ } icon: {
+ Image(uiImage: Asset.Images.voiceBroadcastTimeLeft.image)
+ }
}.frame(maxWidth: .infinity, alignment: .leading)
Label {
diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderModels.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderModels.swift
index 7a2566aad..cb807a430 100644
--- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderModels.swift
+++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderModels.swift
@@ -35,9 +35,15 @@ struct VoiceBroadcastRecorderDetails {
let avatarData: AvatarInputProtocol
}
+struct VoiceBroadcastRecordingState {
+ var remainingTime: UInt
+ var remainingTimeLabel: String
+}
+
struct VoiceBroadcastRecorderViewState: BindableState {
var details: VoiceBroadcastRecorderDetails
var recordingState: VoiceBroadcastRecorderState
+ var currentRecordingState: VoiceBroadcastRecordingState
var bindings: VoiceBroadcastRecorderViewStateBindings
}
diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderScreenState.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderScreenState.swift
index bc915d36a..c2b57dc5c 100644
--- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderScreenState.swift
+++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderScreenState.swift
@@ -32,7 +32,8 @@ enum MockVoiceBroadcastRecorderScreenState: MockScreenState, CaseIterable {
var screenView: ([Any], AnyView) {
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()))
+ let recordingState = VoiceBroadcastRecordingState(remainingTime: BuildSettings.voiceBroadcastMaxLength, remainingTimeLabel: "1h 20m 47s left")
+ let viewModel = MockVoiceBroadcastRecorderViewModel(initialViewState: VoiceBroadcastRecorderViewState(details: details, recordingState: .started, currentRecordingState: recordingState, bindings: VoiceBroadcastRecorderViewStateBindings()))
return (
[false, viewModel],
diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderViewModel.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderViewModel.swift
index 6e1444162..ba9690bfb 100644
--- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderViewModel.swift
+++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/VoiceBroadcastRecorderViewModel.swift
@@ -34,8 +34,10 @@ class VoiceBroadcastRecorderViewModel: VoiceBroadcastRecorderViewModelType, Voic
init(details: VoiceBroadcastRecorderDetails,
recorderService: VoiceBroadcastRecorderServiceProtocol) {
self.voiceBroadcastRecorderService = recorderService
+ let currentRecordingState = VoiceBroadcastRecorderViewModel.currentRecordingState(from: BuildSettings.voiceBroadcastMaxLength)
super.init(initialViewState: VoiceBroadcastRecorderViewState(details: details,
recordingState: .stopped,
+ currentRecordingState: currentRecordingState,
bindings: VoiceBroadcastRecorderViewStateBindings()))
self.voiceBroadcastRecorderService.serviceDelegate = self
@@ -77,10 +79,27 @@ class VoiceBroadcastRecorderViewModel: VoiceBroadcastRecorderViewModelType, Voic
self.state.recordingState = .resumed
voiceBroadcastRecorderService.resumeRecordingVoiceBroadcast()
}
+
+ private func updateRemainingTime(_ remainingTime: UInt) {
+ state.currentRecordingState = VoiceBroadcastRecorderViewModel.currentRecordingState(from: remainingTime)
+ }
+
+ private static func currentRecordingState(from remainingTime: UInt) -> VoiceBroadcastRecordingState {
+ let time = TimeInterval(Double(remainingTime))
+ let formatter = DateComponentsFormatter()
+ formatter.unitsStyle = .abbreviated
+
+ return VoiceBroadcastRecordingState(remainingTime: remainingTime,
+ remainingTimeLabel: VectorL10n.voiceBroadcastTimeLeft(formatter.string(from: time) ?? "0s"))
+ }
}
extension VoiceBroadcastRecorderViewModel: VoiceBroadcastRecorderServiceDelegate {
func voiceBroadcastRecorderService(_ service: VoiceBroadcastRecorderServiceProtocol, didUpdateState state: VoiceBroadcastRecorderState) {
self.state.recordingState = state
}
+
+ func voiceBroadcastRecorderService(_ service: VoiceBroadcastRecorderServiceProtocol, didUpdateRemainingTime remainingTime: UInt) {
+ self.updateRemainingTime(remainingTime)
+ }
}
diff --git a/RiotSwiftUI/target.yml b/RiotSwiftUI/target.yml
index 99c555cc8..dbcf4854a 100644
--- a/RiotSwiftUI/target.yml
+++ b/RiotSwiftUI/target.yml
@@ -63,6 +63,7 @@ targets:
- path: ../Riot/Modules/Analytics/AnalyticsScreen.swift
- path: ../Riot/Modules/LocationSharing/LocationAuthorizationStatus.swift
- path: ../Riot/Modules/QRCode/QRCodeGenerator.swift
+ - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfoState.swift
- path: ../Riot/Assets/en.lproj/Untranslated.strings
buildPhase: resources
- path: ../Riot/Assets/Images.xcassets
diff --git a/RiotSwiftUI/targetUITests.yml b/RiotSwiftUI/targetUITests.yml
index e2db2be61..533efab5f 100644
--- a/RiotSwiftUI/targetUITests.yml
+++ b/RiotSwiftUI/targetUITests.yml
@@ -72,6 +72,7 @@ targets:
- path: ../Riot/Modules/Analytics/AnalyticsScreen.swift
- path: ../Riot/Modules/LocationSharing/LocationAuthorizationStatus.swift
- path: ../Riot/Modules/QRCode/QRCodeGenerator.swift
+ - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfoState.swift
- path: ../Riot/Assets/en.lproj/Untranslated.strings
buildPhase: resources
- path: ../Riot/Assets/Images.xcassets
diff --git a/changelog.d/pr-7103.feature b/changelog.d/pr-7103.feature
new file mode 100644
index 000000000..4ce05d7cb
--- /dev/null
+++ b/changelog.d/pr-7103.feature
@@ -0,0 +1 @@
+Add the left time in the Voice Broadcast tile recorder.
diff --git a/changelog.d/pr-7105.bugfix b/changelog.d/pr-7105.bugfix
new file mode 100644
index 000000000..c1125fcaa
--- /dev/null
+++ b/changelog.d/pr-7105.bugfix
@@ -0,0 +1 @@
+Fix scroll issues with VoiceBroadcast and Poll cells