diff --git a/RiotSwiftUI/Modules/Room/PollHistory/Coordinator/PollHistoryCoordinator.swift b/RiotSwiftUI/Modules/Room/PollHistory/Coordinator/PollHistoryCoordinator.swift index 336f034eb..0cdf3629a 100644 --- a/RiotSwiftUI/Modules/Room/PollHistory/Coordinator/PollHistoryCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/PollHistory/Coordinator/PollHistoryCoordinator.swift @@ -36,7 +36,8 @@ final class PollHistoryCoordinator: Coordinator, Presentable { init(parameters: PollHistoryCoordinatorParameters) { self.parameters = parameters - let viewModel = PollHistoryViewModel(mode: parameters.mode) + #warning("replace with the real service after that it's done") + let viewModel = PollHistoryViewModel(mode: parameters.mode, pollService: MockPollHistoryService()) let view = PollHistory(viewModel: viewModel.context) pollHistoryViewModel = viewModel pollHistoryHostingController = VectorHostingController(rootView: view) diff --git a/RiotSwiftUI/Modules/Room/PollHistory/MockPollHistoryScreenState.swift b/RiotSwiftUI/Modules/Room/PollHistory/MockPollHistoryScreenState.swift index 778cbdf12..e85384c4b 100644 --- a/RiotSwiftUI/Modules/Room/PollHistory/MockPollHistoryScreenState.swift +++ b/RiotSwiftUI/Modules/Room/PollHistory/MockPollHistoryScreenState.swift @@ -42,7 +42,7 @@ enum MockPollHistoryScreenState: MockScreenState, CaseIterable { pollHistoryMode = .past } - let viewModel = PollHistoryViewModel(mode: pollHistoryMode) + let viewModel = PollHistoryViewModel(mode: pollHistoryMode, pollService: MockPollHistoryService()) // can simulate service and viewModel actions here if needs be. diff --git a/RiotSwiftUI/Modules/Room/PollHistory/PollHistoryModels.swift b/RiotSwiftUI/Modules/Room/PollHistory/PollHistoryModels.swift index a1bf4f343..c8960add0 100644 --- a/RiotSwiftUI/Modules/Room/PollHistory/PollHistoryModels.swift +++ b/RiotSwiftUI/Modules/Room/PollHistory/PollHistoryModels.swift @@ -37,8 +37,9 @@ struct PollHistoryViewState: BindableState { } var bindings: PollHistoryViewBindings + var polls: [PollListData] = [] } enum PollHistoryViewAction { - #warning("e.g. show poll detail") + case viewAppeared } diff --git a/RiotSwiftUI/Modules/Room/PollHistory/PollHistoryViewModel.swift b/RiotSwiftUI/Modules/Room/PollHistory/PollHistoryViewModel.swift index 6ce689d10..0addb8ef9 100644 --- a/RiotSwiftUI/Modules/Room/PollHistory/PollHistoryViewModel.swift +++ b/RiotSwiftUI/Modules/Room/PollHistory/PollHistoryViewModel.swift @@ -18,16 +18,43 @@ import SwiftUI typealias PollHistoryViewModelType = StateStoreViewModel -class PollHistoryViewModel: PollHistoryViewModelType, PollHistoryViewModelProtocol { +final class PollHistoryViewModel: PollHistoryViewModelType, PollHistoryViewModelProtocol { + private let pollService: PollHistoryServiceProtocol + private var fetchingTask: Task? { + didSet { + oldValue?.cancel() + } + } + var completion: ((PollHistoryViewModelResult) -> Void)? - init(mode: PollHistoryMode) { + init(mode: PollHistoryMode, pollService: PollHistoryServiceProtocol) { + self.pollService = pollService super.init(initialViewState: PollHistoryViewState(mode: mode)) } // MARK: - Public override func process(viewAction: PollHistoryViewAction) { - + switch viewAction { + case .viewAppeared: + fetchingTask = fetchPolls() + } + } +} + +private extension PollHistoryViewModel { + func fetchPolls() -> Task { + Task { + let polls = try await pollService.fetchHistory() + + guard Task.isCancelled == false else { + return + } + + await MainActor.run { + state.polls = polls + } + } } } diff --git a/RiotSwiftUI/Modules/Room/PollHistory/Service/MatrixSDK/PollHistoryService.swift b/RiotSwiftUI/Modules/Room/PollHistory/Service/MatrixSDK/PollHistoryService.swift new file mode 100644 index 000000000..a2dcf256a --- /dev/null +++ b/RiotSwiftUI/Modules/Room/PollHistory/Service/MatrixSDK/PollHistoryService.swift @@ -0,0 +1,24 @@ +// +// Copyright 2023 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 MatrixSDK +import Foundation + +final class PollHistoryService: PollHistoryServiceProtocol { + func fetchHistory() async throws -> [PollListData] { + [] + } +} diff --git a/RiotSwiftUI/Modules/Room/PollHistory/Service/Mock/MockPollHistoryService.swift b/RiotSwiftUI/Modules/Room/PollHistory/Service/Mock/MockPollHistoryService.swift new file mode 100644 index 000000000..62015b8a3 --- /dev/null +++ b/RiotSwiftUI/Modules/Room/PollHistory/Service/Mock/MockPollHistoryService.swift @@ -0,0 +1,27 @@ +// +// Copyright 2023 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. +// + +final class MockPollHistoryService: PollHistoryServiceProtocol { + func fetchHistory() async throws -> [PollListData] { + (1..<10) + .map { index in + PollListData(startDate: .init().addingTimeInterval(-CGFloat(index) * 3600), question: "Do you like the poll number \(index)?") + } + .sorted { poll1, poll2 in + poll1.startDate > poll2.startDate + } + } +} diff --git a/RiotSwiftUI/Modules/Room/PollHistory/Service/PollHistoryServiceProtocol.swift b/RiotSwiftUI/Modules/Room/PollHistory/Service/PollHistoryServiceProtocol.swift new file mode 100644 index 000000000..4bb9b43b5 --- /dev/null +++ b/RiotSwiftUI/Modules/Room/PollHistory/Service/PollHistoryServiceProtocol.swift @@ -0,0 +1,19 @@ +// +// Copyright 2023 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. +// + +protocol PollHistoryServiceProtocol { + func fetchHistory() async throws -> [PollListData] +} diff --git a/RiotSwiftUI/Modules/Room/PollHistory/View/PollHistory.swift b/RiotSwiftUI/Modules/Room/PollHistory/View/PollHistory.swift index 6d46c05e9..9de37b9c0 100644 --- a/RiotSwiftUI/Modules/Room/PollHistory/View/PollHistory.swift +++ b/RiotSwiftUI/Modules/Room/PollHistory/View/PollHistory.swift @@ -39,8 +39,10 @@ struct PollHistory: View { ScrollView { LazyVStack(spacing: 32) { - ForEach(0..<10) { index in - PollListItem(data: .init(startDate: Date(), question: "Poll question number \(index)")) + let enumeratedPolls = Array(viewModel.viewState.polls.enumerated()) + + ForEach(enumeratedPolls, id: \.offset) { _, pollData in + PollListItem(pollData: pollData) } .frame(maxWidth: .infinity, alignment: .leading) @@ -60,10 +62,13 @@ struct PollHistory: View { .background(theme.colors.background.ignoresSafeArea()) .accentColor(theme.colors.accent) .navigationTitle(VectorL10n.pollHistoryTitle) + .onAppear { + viewModel.send(viewAction: .viewAppeared) + } } } -extension PollHistoryMode { +private extension PollHistoryMode { var segmentTitle: String { switch self { case .active: diff --git a/RiotSwiftUI/Modules/Room/PollHistory/View/PollListItem.swift b/RiotSwiftUI/Modules/Room/PollHistory/View/PollListItem.swift index 984cb2b68..dff98ba0a 100644 --- a/RiotSwiftUI/Modules/Room/PollHistory/View/PollListItem.swift +++ b/RiotSwiftUI/Modules/Room/PollHistory/View/PollListItem.swift @@ -24,33 +24,52 @@ struct PollListData { struct PollListItem: View { @Environment(\.theme) private var theme - private let data: PollListData + private let pollData: PollListData - init(data: PollListData) { - self.data = data + init(pollData: PollListData) { + self.pollData = pollData } var body: some View { VStack(alignment: .leading, spacing: 12) { - Text(data.startDate.description) + Text(pollData.formattedDate) .foregroundColor(theme.colors.tertiaryContent) .font(theme.fonts.caption1) - HStack(spacing: 8) { + HStack(alignment: .firstTextBaseline, spacing: 8) { Image(uiImage: Asset.Images.pollHistory.image) .resizable() .frame(width: 16, height: 16) - Text(data.question) + Text(pollData.question) .foregroundColor(theme.colors.primaryContent) .font(theme.fonts.body) + .lineLimit(2) } } } } -struct PollListItem_Previews: PreviewProvider { - static var previews: some View { - PollListItem(data: .init(startDate: .init(), question: "Did you like this poll?")) +private extension PollListData { + var formattedDate: String { + DateFormatter.shortDateFormatter.string(from: startDate) + } +} + +private extension DateFormatter { + static let shortDateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.timeStyle = .none + formatter.dateStyle = .short + formatter.timeZone = .init(identifier: "UTC") + return formatter + }() +} + +// MARK: - Previews + +struct PollListItem_Previews: PreviewProvider { + static var previews: some View { + PollListItem(pollData: .init(startDate: .init(), question: "Did you like this poll?")) } }