Add loading view

This commit is contained in:
Alfonso Grillo 2023-01-20 12:11:12 +01:00
parent 7ca3cbbd5a
commit d670badbc5
8 changed files with 93 additions and 11 deletions

View file

@ -2304,6 +2304,7 @@ Tap the + to start adding people.";
// MARK: - Polls history
"poll_history_title" = "Poll history";
"poll_history_loading_text" = "Displaying polls";
"poll_history_active_segment_title" = "Active polls";
"poll_history_past_segment_title" = "Past polls";
"poll_history_no_active_poll_text" = "There are no active polls in this room";

View file

@ -4851,6 +4851,10 @@ public class VectorL10n: NSObject {
public static var pollHistoryActiveSegmentTitle: String {
return VectorL10n.tr("Vector", "poll_history_active_segment_title")
}
/// Displaying polls
public static var pollHistoryLoadingText: String {
return VectorL10n.tr("Vector", "poll_history_loading_text")
}
/// There are no active polls in this room
public static var pollHistoryNoActivePollText: String {
return VectorL10n.tr("Vector", "poll_history_no_active_poll_text")

View file

@ -27,16 +27,43 @@ enum PollHistoryMode: CaseIterable {
case past
}
enum PollHistoryLoadingState {
case idle
case loading(firstLoad: Bool)
}
extension PollHistoryLoadingState {
var isLoadingOnLanding: Bool {
switch self {
case .idle:
return false
case .loading(let firstLoad):
return firstLoad
}
}
var isLoading: Bool {
switch self {
case .idle:
return false
case .loading:
return true
}
}
}
struct PollHistoryViewBindings {
var mode: PollHistoryMode
}
struct PollHistoryViewState: BindableState {
init(mode: PollHistoryMode) {
init(mode: PollHistoryMode, loadingState: PollHistoryLoadingState) {
bindings = .init(mode: mode)
self.loadingState = loadingState
}
var bindings: PollHistoryViewBindings
var loadingState: PollHistoryLoadingState
var polls: [TimelinePollDetails] = []
}

View file

@ -28,7 +28,7 @@ final class PollHistoryViewModel: PollHistoryViewModelType, PollHistoryViewModel
init(mode: PollHistoryMode, pollService: PollHistoryServiceProtocol) {
self.pollService = pollService
super.init(initialViewState: PollHistoryViewState(mode: mode))
super.init(initialViewState: PollHistoryViewState(mode: mode, loadingState: .loading(firstLoad: true)))
}
// MARK: - Public
@ -37,7 +37,7 @@ final class PollHistoryViewModel: PollHistoryViewModelType, PollHistoryViewModel
switch viewAction {
case .viewAppeared:
setupSubscriptions()
pollService.startFetching()
pollService.next()
case .segmentDidChange:
updatePolls()
}
@ -62,6 +62,22 @@ private extension PollHistoryViewModel {
#warning("Handle errors")
}
.store(in: &subcriptions)
pollService
.isFetching
.filter { $0 }
.first()
.sink { isFetching in
self.state.loadingState = .loading(firstLoad: true)
}
.store(in: &subcriptions)
pollService
.isFetching
.sink { isFetching in
self.state.loadingState = isFetching ? .loading(firstLoad: false) : .idle
}
.store(in: &subcriptions)
}
func updatePolls() {

View file

@ -22,6 +22,7 @@ final class PollHistoryService: PollHistoryServiceProtocol {
private let room: MXRoom
private let pollsSubject: PassthroughSubject<TimelinePollDetails, Never> = .init()
private let errorSubject: PassthroughSubject<Error, Never> = .init()
private let isFetchingSubject: PassthroughSubject<Bool, Never> = .init()
private var listner: Any?
private var timeline: MXEventTimeline?
@ -36,12 +37,16 @@ final class PollHistoryService: PollHistoryServiceProtocol {
errorSubject.eraseToAnyPublisher()
}
var isFetching: AnyPublisher<Bool, Never> {
isFetchingSubject.eraseToAnyPublisher()
}
init(room: MXRoom) {
self.room = room
self.targetTimestamp = Date().addingTimeInterval(-TimeInterval(Constants.daysToSync) * Constants.oneDayInSeconds)
}
func startFetching() {
func next() {
guard timeline == nil else {
paginate()
return
@ -81,12 +86,14 @@ private extension PollHistoryService {
return
}
#warning("to be removed probably?")
timeline.resetPagination()
isFetchingSubject.send(true)
timeline.paginate(Constants.pageSize,
direction: .backwards,
onlyFromStore: false) { response in
onlyFromStore: false) { [weak self] response in
self?.isFetchingSubject.send(false)
switch response {
case .success:
#warning("Go on with pagination...")

View file

@ -27,11 +27,15 @@ final class MockPollHistoryService: PollHistoryServiceProtocol {
Empty().eraseToAnyPublisher()
}
func startFetching() {
func next() {
for poll in activePollsData + pastPollsData {
polls.send(poll)
}
}
var isFetching: AnyPublisher<Bool, Never> {
Just(false).eraseToAnyPublisher()
}
var activePollsData: [TimelinePollDetails] = (1..<10)
.map { index in

View file

@ -17,8 +17,17 @@
import Combine
protocol PollHistoryServiceProtocol {
/// Publishes poll data as soon they are found in the timeline.
/// Updates are also published here, so clients needs to address duplicates.
var pollHistory: AnyPublisher<TimelinePollDetails, Never> { get }
/// Publishes whatever errors produced during the sync.
var error: AnyPublisher<Error, Never> { get }
func startFetching()
/// Ask to fetch the next batch of polls.
/// Concrete implementations can decide what a batch is.
func next()
/// Inform the whenever a new batch of polls starts or ends.
var isFetching: AnyPublisher<Bool, Never> { get }
}

View file

@ -31,8 +31,8 @@ struct PollHistory: View {
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 16)
if viewModel.viewState.polls.isEmpty {
noPollsView
if viewModel.viewState.loadingState.isLoadingOnLanding {
loadingView
} else {
pollListView
}
@ -76,7 +76,21 @@ struct PollHistory: View {
.foregroundColor(theme.colors.secondaryContent)
.frame(maxHeight: .infinity)
.padding(.horizontal, 16)
.accessibilityLabel("PollHistory.emptyText")
.accessibilityIdentifier("PollHistory.emptyText")
}
private var loadingView: some View {
HStack(spacing: 8) {
ProgressView()
.progressViewStyle(CircularProgressViewStyle())
Text(VectorL10n.pollHistoryLoadingText)
.font(theme.fonts.body)
.foregroundColor(theme.colors.secondaryContent)
.frame(maxHeight: .infinity)
.accessibilityIdentifier("PollHistory.loadingText")
}
.padding(.horizontal, 16)
}
}