diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/Contents.json
new file mode 100644
index 000000000..73c00596a
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_pause.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_pause.imageset/Contents.json
new file mode 100644
index 000000000..4f275b2b0
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_pause.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "voice_broadcast_pause.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_pause.imageset/voice_broadcast_pause.svg b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_pause.imageset/voice_broadcast_pause.svg
new file mode 100644
index 000000000..babd78716
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_pause.imageset/voice_broadcast_pause.svg
@@ -0,0 +1,5 @@
+
diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_play.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_play.imageset/Contents.json
new file mode 100644
index 000000000..6302334b3
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_play.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "voice_broadcast_play.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_play.imageset/voice_broadcast_play.svg b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_play.imageset/voice_broadcast_play.svg
new file mode 100644
index 000000000..65849ae58
--- /dev/null
+++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_play.imageset/voice_broadcast_play.svg
@@ -0,0 +1,4 @@
+
diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift
index 3cc10eb2e..72652d1c8 100644
--- a/Riot/Generated/Images.swift
+++ b/Riot/Generated/Images.swift
@@ -335,6 +335,8 @@ internal class Asset: NSObject {
internal static let tabHome = ImageAsset(name: "tab_home")
internal static let tabPeople = ImageAsset(name: "tab_people")
internal static let tabRooms = ImageAsset(name: "tab_rooms")
+ internal static let voiceBroadcastPause = ImageAsset(name: "voice_broadcast_pause")
+ internal static let voiceBroadcastPlay = ImageAsset(name: "voice_broadcast_play")
internal static let launchScreenLogo = ImageAsset(name: "launch_screen_logo")
}
@objcMembers
diff --git a/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift b/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift
index 8beba56a5..7c65792c3 100644
--- a/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift
+++ b/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift
@@ -70,6 +70,7 @@ enum MockAppScreens {
MockTemplateRoomChatScreenState.self,
MockSpaceSelectorScreenState.self,
MockComposerScreenState.self,
- MockComposerCreateActionListScreenState.self
+ MockComposerCreateActionListScreenState.self,
+ MockTimelineVoiceBroadcastScreenState.self
]
}
diff --git a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/Coordinator/TimelineVoiceBroadcastCoordinator.swift b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/Coordinator/TimelineVoiceBroadcastCoordinator.swift
index 65c618860..30adcfd15 100644
--- a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/Coordinator/TimelineVoiceBroadcastCoordinator.swift
+++ b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/Coordinator/TimelineVoiceBroadcastCoordinator.swift
@@ -51,8 +51,16 @@ final class TimelineVoiceBroadcastCoordinator: Coordinator, Presentable, VoiceBr
viewModel = TimelineVoiceBroadcastViewModel(timelineVoiceBroadcastDetails: buildTimelineVoiceBroadcastFrom(voiceBroadcastAggregator.voiceBroadcast))
- // TODO: manage voicebroacast chunks
- viewModel.completion = { }
+ viewModel.completion = { [weak self] result in
+ guard let self = self else { return }
+
+ switch result {
+ case .played:
+ MXLog.debug("click on play")
+ case .paused:
+ MXLog.debug("click on pause")
+ }
+ }
}
diff --git a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/Service/VoiceBroadcastChunk.swift b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/Service/VoiceBroadcastChunk.swift
new file mode 100644
index 000000000..a464997d1
--- /dev/null
+++ b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/Service/VoiceBroadcastChunk.swift
@@ -0,0 +1,36 @@
+//
+// 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
+
+/// Represents user live location
+struct VoiceBroadcastChunk {
+ var userId: String {
+ avatarData.matrixItemId
+ }
+
+ var displayName: String {
+ avatarData.displayName ?? userId
+ }
+
+ let avatarData: AvatarInputProtocol
+
+ /// Chunk sequence number
+ let sequence: UInt
+
+ // TODO: add attachment here
+ let attachment: NSObject
+}
diff --git a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/Service/VoiceBroadcastPlaybackServiceProtocol.swift b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/Service/VoiceBroadcastPlaybackServiceProtocol.swift
new file mode 100644
index 000000000..6d4ef33e9
--- /dev/null
+++ b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/Service/VoiceBroadcastPlaybackServiceProtocol.swift
@@ -0,0 +1,31 @@
+//
+// 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 Combine
+import CoreLocation
+import Foundation
+
+protocol VoiceBroadcastPlaybackServiceProtocol {
+ /// All shared voice broadcast chunks
+ var voiceBroadcastChunks: [VoiceBroadcastChunk] { get }
+
+ /// Called when voice broadcast chunks are updated.
+ var didUpdateVoiceBroadcastChunks: (([VoiceBroadcastChunk]) -> Void)? { get set }
+
+ func startPlayingVoiceBroadcast()
+
+ func pausePlayingVoiceBroadcast()
+}
diff --git a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastModels.swift b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastModels.swift
index f11cca32b..b29b1bc7c 100644
--- a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastModels.swift
+++ b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastModels.swift
@@ -17,10 +17,16 @@
import Foundation
import SwiftUI
-typealias TimelineVoiceBroadcastViewModelCallback = () -> Void
-
// TODO: add play pause cases
-enum TimelineVoiceBroadcastViewAction { }
+enum TimelineVoiceBroadcastViewAction {
+ case play
+ case pause
+}
+
+enum TimelineVoiceBroadcastViewModelResult {
+ case played
+ case paused
+}
enum TimelineVoiceBroadcastType {
case disclosed
diff --git a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastScreenState.swift b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastScreenState.swift
new file mode 100644
index 000000000..f2ddaf4b3
--- /dev/null
+++ b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastScreenState.swift
@@ -0,0 +1,49 @@
+//
+// 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
+import SwiftUI
+
+/// Using an enum for the screen allows you define the different state cases with
+/// the relevant associated data for each case.
+enum MockTimelineVoiceBroadcastScreenState: MockScreenState, CaseIterable {
+ // A case for each state you want to represent
+ // with specific, minimal associated data that will allow you
+ // mock that screen.
+ case animated
+
+ /// The associated screen
+ var screenType: Any.Type {
+ TimelineVoiceBroadcastView.self
+ }
+
+ /// A list of screen state definitions
+ static var allCases: [MockTimelineVoiceBroadcastScreenState] {
+ [.animated]
+ }
+
+ /// Generate the view struct for the screen state.
+ var screenView: ([Any], AnyView) {
+ let voiceBroadcast = TimelineVoiceBroadcastDetails(closed: false, type: TimelineVoiceBroadcastType.disclosed)
+
+ let viewModel = TimelineVoiceBroadcastViewModel(timelineVoiceBroadcastDetails: voiceBroadcast)
+
+ return (
+ [false, viewModel],
+ AnyView(TimelineVoiceBroadcastView(viewModel: viewModel.context))
+ )
+ }
+}
diff --git a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastViewModel.swift b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastViewModel.swift
index dd546cfcc..129436fe6 100644
--- a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastViewModel.swift
+++ b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastViewModel.swift
@@ -20,13 +20,14 @@ import SwiftUI
typealias TimelineVoiceBroadcastViewModelType = StateStoreViewModel
class TimelineVoiceBroadcastViewModel: TimelineVoiceBroadcastViewModelType, TimelineVoiceBroadcastViewModelProtocol {
+
// MARK: - Properties
// MARK: Private
// MARK: Public
- var completion: TimelineVoiceBroadcastViewModelCallback?
+ var completion: ((TimelineVoiceBroadcastViewModelResult) -> Void)?
// MARK: - Setup
@@ -37,7 +38,22 @@ class TimelineVoiceBroadcastViewModel: TimelineVoiceBroadcastViewModelType, Time
// MARK: - Public
override func process(viewAction: TimelineVoiceBroadcastViewAction) {
- // TODO: add some actions as play pause
+ switch viewAction {
+ case .play:
+ play()
+ case .pause:
+ pause()
+ }
+ }
+
+ /// Listen voice broadcast
+ private func play() {
+ completion?(.played)
+ }
+
+ /// Stop voice broadcast
+ private func pause() {
+ completion?(.paused)
}
// MARK: - TimelineVoiceBroadcastViewModelProtocol
diff --git a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastViewModelProtocol.swift b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastViewModelProtocol.swift
index 80d44c211..014c7208c 100644
--- a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastViewModelProtocol.swift
+++ b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/TimelineVoiceBroadcastViewModelProtocol.swift
@@ -18,7 +18,7 @@ import Foundation
protocol TimelineVoiceBroadcastViewModelProtocol {
var context: TimelineVoiceBroadcastViewModelType.Context { get }
- var completion: (() -> Void)? { get set }
+ var completion: ((TimelineVoiceBroadcastViewModelResult) -> Void)? { get set }
func updateWithVoiceBroadcastDetails(_ voiceBroadcastDetails: TimelineVoiceBroadcastDetails)
}
diff --git a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/View/TimelineVoiceBroadcastView.swift b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/View/TimelineVoiceBroadcastView.swift
index 5235677dc..ececa70a6 100644
--- a/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/View/TimelineVoiceBroadcastView.swift
+++ b/RiotSwiftUI/Modules/Room/TimelineVoiceBroadcast/View/TimelineVoiceBroadcastView.swift
@@ -34,9 +34,25 @@ struct TimelineVoiceBroadcastView: View {
Text(VectorL10n.voiceBroadcastInTimelineTitle)
.font(theme.fonts.bodySB)
.foregroundColor(theme.colors.primaryContent)
- Text(VectorL10n.voiceBroadcastInTimelineBody)
- .font(theme.fonts.body)
- .foregroundColor(theme.colors.primaryContent)
+// Text(VectorL10n.voiceBroadcastInTimelineBody)
+// .font(theme.fonts.body)
+// .foregroundColor(theme.colors.primaryContent)
+
+ HStack(alignment: .top, spacing: 16.0) {
+ Button { viewModel.send(viewAction: .play) } label: {
+ Image("voice_broadcast_play")
+ .renderingMode(.original)
+ }
+ .accessibilityIdentifier("playButton")
+
+ Button { viewModel.send(viewAction: .pause) } label: {
+ Image("voice_broadcast_pause")
+ .renderingMode(.original)
+ }
+ .accessibilityIdentifier("pauseButton")
+
+ }
+
}
.padding([.horizontal, .top], 2.0)
.padding([.bottom])
@@ -48,4 +64,9 @@ struct TimelineVoiceBroadcastView: View {
// MARK: - Previews
-// TODO: Add Voice broadcast preview
+struct TimelineVoiceBroadcastView_Previews: PreviewProvider {
+ static let stateRenderer = MockTimelineVoiceBroadcastScreenState.stateRenderer
+ static var previews: some View {
+ stateRenderer.screenGroup()
+ }
+}