mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Add loading view
This commit is contained in:
parent
7ca3cbbd5a
commit
d670badbc5
8 changed files with 93 additions and 11 deletions
|
@ -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";
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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] = []
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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...")
|
||||
|
|
|
@ -27,12 +27,16 @@ 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
|
||||
TimelinePollDetails(id: "a\(index)",
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue