Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Poll history: load more polls (PSG-1093) #7303

Merged
merged 23 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Riot/Assets/en.lproj/Vector.strings
Original file line number Diff line number Diff line change
Expand Up @@ -2303,6 +2303,7 @@ Tap the + to start adding people.";
"poll_history_no_active_poll_period_text" = "There are no active polls for the past %@ days. Load more polls to view polls for previous months";
"poll_history_no_past_poll_period_text" = "There are no past polls for the past %@ days. Load more polls to view polls for previous months";
"poll_history_load_more" = "Load more polls";
"poll_history_fetching_error" = "Error fetching polls.";

// MARK: - Polls

Expand Down
4 changes: 4 additions & 0 deletions Riot/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4819,6 +4819,10 @@ public class VectorL10n: NSObject {
public static var pollHistoryActiveSegmentTitle: String {
return VectorL10n.tr("Vector", "poll_history_active_segment_title")
}
/// Error fetching polls.
public static var pollHistoryFetchingError: String {
return VectorL10n.tr("Vector", "poll_history_fetching_error")
}
/// Load more polls
public static var pollHistoryLoadMore: String {
return VectorL10n.tr("Vector", "poll_history_load_more")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ final class PollHistoryCoordinator: Coordinator, Presentable {

func start() {
MXLog.debug("[PollHistoryCoordinator] did start.")
pollHistoryViewModel.completion = { [weak self] result in
self?.completion?()
pollHistoryViewModel.completion = { _ in

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ enum MockPollHistoryScreenState: MockScreenState, CaseIterable {
// mock that screen.
case active
case past
case activeEmpty
case pastEmpty
case activeNoMoreContent
case contentLoading
case empty
case emptyLoading
case emptyNoMoreContent
case loading

/// The associated screen
Expand All @@ -37,34 +40,40 @@ enum MockPollHistoryScreenState: MockScreenState, CaseIterable {

/// Generate the view struct for the screen state.
var screenView: ([Any], AnyView) {
let pollHistoryMode: PollHistoryMode
var pollHistoryMode: PollHistoryMode = .active
let pollService = MockPollHistoryService()

switch self {
case .active:
pollHistoryMode = .active
case .activeNoMoreContent:
pollHistoryMode = .active
pollService.hasNextBatch = false
case .past:
pollHistoryMode = .past
case .activeEmpty:
case .contentLoading:
pollService.nextBatchPublishers.append(MockPollPublisher.loadingPolls)
case .empty:
pollHistoryMode = .active
pollService.nextBatchPublisher = Empty(completeImmediately: true,
outputType: TimelinePollDetails.self,
failureType: Error.self).eraseToAnyPublisher()
case .pastEmpty:
pollHistoryMode = .past
pollService.nextBatchPublisher = Empty(completeImmediately: true,
outputType: TimelinePollDetails.self,
failureType: Error.self).eraseToAnyPublisher()
pollService.nextBatchPublishers = [MockPollPublisher.emptyPolls]
case .emptyLoading:
pollService.nextBatchPublishers = [MockPollPublisher.emptyPolls, MockPollPublisher.loadingPolls]
case .emptyNoMoreContent:
pollService.hasNextBatch = false
pollService.nextBatchPublishers = [MockPollPublisher.emptyPolls]
case .loading:
pollHistoryMode = .active
pollService.nextBatchPublisher = Empty(completeImmediately: false,
outputType: TimelinePollDetails.self,
failureType: Error.self).eraseToAnyPublisher()
pollService.nextBatchPublishers = [MockPollPublisher.loadingPolls]
}

let viewModel = PollHistoryViewModel(mode: pollHistoryMode, pollService: pollService)

// can simulate service and viewModel actions here if needs be.
switch self {
case .contentLoading, .emptyLoading:
viewModel.process(viewAction: .loadMoreContent)
default:
break
}

return (
[pollHistoryMode, viewModel],
Expand All @@ -73,3 +82,17 @@ enum MockPollHistoryScreenState: MockScreenState, CaseIterable {
)
}
}

enum MockPollPublisher {
static var emptyPolls: AnyPublisher<TimelinePollDetails, Error> {
Empty<TimelinePollDetails, Error>(completeImmediately: true).eraseToAnyPublisher()
}

static var loadingPolls: AnyPublisher<TimelinePollDetails, Error> {
Empty<TimelinePollDetails, Error>(completeImmediately: false).eraseToAnyPublisher()
}

static var failure: AnyPublisher<TimelinePollDetails, Error> {
Fail(error: NSError(domain: "fake", code: 1)).eraseToAnyPublisher()
}
}
6 changes: 5 additions & 1 deletion RiotSwiftUI/Modules/Room/PollHistory/PollHistoryModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ enum PollHistoryConstants {
}

enum PollHistoryViewModelResult: Equatable {
#warning("e.g. show poll detail")

}

// MARK: View
Expand All @@ -33,6 +33,7 @@ enum PollHistoryMode: CaseIterable {

struct PollHistoryViewBindings {
var mode: PollHistoryMode
var alertInfo: AlertInfo<Bool>?
}

struct PollHistoryViewState: BindableState {
Expand All @@ -44,9 +45,12 @@ struct PollHistoryViewState: BindableState {
var isLoading = false
var canLoadMoreContent = true
var polls: [TimelinePollDetails]?
var syncStartDate: Date = .init()
var syncedUpTo: Date = .distantFuture
}

enum PollHistoryViewAction {
case viewAppeared
case segmentDidChange
case loadMoreContent
}
59 changes: 45 additions & 14 deletions RiotSwiftUI/Modules/Room/PollHistory/PollHistoryViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ final class PollHistoryViewModel: PollHistoryViewModelType, PollHistoryViewModel
init(mode: PollHistoryMode, pollService: PollHistoryServiceProtocol) {
self.pollService = pollService
super.init(initialViewState: PollHistoryViewState(mode: mode))
state.canLoadMoreContent = pollService.hasNextBatch
}

// MARK: - Public
Expand All @@ -37,30 +38,45 @@ final class PollHistoryViewModel: PollHistoryViewModelType, PollHistoryViewModel
switch viewAction {
case .viewAppeared:
setupUpdateSubscriptions()
fetchFirstBatch()
fetchContent()
case .segmentDidChange:
updateViewState()
case .loadMoreContent:
fetchContent()
}
}
}

private extension PollHistoryViewModel {
func fetchFirstBatch() {
func fetchContent() {
state.isLoading = true

pollService
.nextBatch()
.collect()
.sink { [weak self] _ in
#warning("Handle errors")
self?.state.isLoading = false
.sink { [weak self] completion in
self?.handleBatchEnded(completion: completion)
} receiveValue: { [weak self] polls in
self?.polls = polls
self?.updateViewState()
self?.add(polls: polls)
}
.store(in: &subcriptions)
}

func handleBatchEnded(completion: Subscribers.Completion<Error>) {
state.isLoading = false
state.canLoadMoreContent = pollService.hasNextBatch

switch completion {
case .finished:
break
case .failure:
polls = polls ?? []
state.bindings.alertInfo = .init(id: true, title: VectorL10n.pollHistoryFetchingError)
}

updateViewState()
}

func setupUpdateSubscriptions() {
subcriptions.removeAll()

Expand All @@ -73,9 +89,15 @@ private extension PollHistoryViewModel {
.store(in: &subcriptions)

pollService
.pollErrors
.sink { detail in
#warning("Handle errors")
.fetchedUpTo
.weakAssign(to: \.state.syncedUpTo, on: self)
.store(in: &subcriptions)

pollService
.livePolls
.sink { [weak self] livePoll in
self?.add(polls: [livePoll])
self?.updateViewState()
}
.store(in: &subcriptions)
}
Expand All @@ -88,6 +110,10 @@ private extension PollHistoryViewModel {
polls?[pollIndex] = poll
}

func add(polls: [TimelinePollDetails]) {
self.polls = (self.polls ?? []) + polls
}

func updateViewState() {
let renderedPolls: [TimelinePollDetails]?

Expand All @@ -104,17 +130,22 @@ private extension PollHistoryViewModel {

extension PollHistoryViewModel.Context {
var emptyPollsText: String {
let days = PollHistoryConstants.chunkSizeInDays

switch (viewState.bindings.mode, viewState.canLoadMoreContent) {
case (.active, true):
return VectorL10n.pollHistoryNoActivePollPeriodText("\(days)")
return VectorL10n.pollHistoryNoActivePollPeriodText("\(syncedPastDays)")
case (.active, false):
return VectorL10n.pollHistoryNoActivePollText
case (.past, true):
return VectorL10n.pollHistoryNoPastPollPeriodText("\(days)")
return VectorL10n.pollHistoryNoPastPollPeriodText("\(syncedPastDays)")
case (.past, false):
return VectorL10n.pollHistoryNoPastPollText
}
}

var syncedPastDays: Int {
guard let days = Calendar.current.dateComponents([.day], from: viewState.syncedUpTo, to: viewState.syncStartDate).day else {
return 0
}
return max(0, days)
}
}
Loading