Skip to content

Commit 0cdbcea

Browse files
authored
Merge pull request #7951 from vector-im/feature/mna/poll-history-load-more-ui
[Poll] History list: Load more UI mechanism (PSG-1095)
2 parents 0ecc291 + 171717b commit 0cdbcea

33 files changed

+1136
-132
lines changed

changelog.d/7864.wip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[Poll] History list: Load more UI mechanism

library/ui-strings/src/main/res/values/strings.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3199,8 +3199,19 @@
31993199
<string name="unable_to_decrypt_some_events_in_poll">Due to decryption errors, some votes may not be counted</string>
32003200
<string name="room_polls_active">Active polls</string>
32013201
<string name="room_polls_active_no_item">There are no active polls in this room</string>
3202+
<plurals name="room_polls_active_no_item_for_loaded_period">
3203+
<item quantity="one">"There are no active polls for the past day.\nLoad more polls to view polls for previous days."</item>
3204+
<item quantity="other">"There are no active polls for the past %1$d days.\nLoad more polls to view polls for previous days."</item>
3205+
</plurals>
32023206
<string name="room_polls_ended">Past polls</string>
32033207
<string name="room_polls_ended_no_item">There are no past polls in this room</string>
3208+
<plurals name="room_polls_ended_no_item_for_loaded_period">
3209+
<item quantity="one">"There are no past polls for the past day.\nLoad more polls to view polls for previous days."</item>
3210+
<item quantity="other">"There are no past polls for the past %1$d days.\nLoad more polls to view polls for previous days."</item>
3211+
</plurals>
3212+
<string name="room_polls_wait_for_display">Displaying polls</string>
3213+
<string name="room_polls_load_more">Load more polls</string>
3214+
<string name="room_polls_loading_error">Error fetching polls.</string>
32043215

32053216
<!-- Location -->
32063217
<string name="location_activity_title_static_sharing">Share location</string>

vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import android.content.ActivityNotFoundException
2020
import im.vector.app.R
2121
import im.vector.app.core.resources.StringProvider
2222
import im.vector.app.features.call.dialpad.DialPadLookup
23+
import im.vector.app.features.roomprofile.polls.RoomPollsLoadingError
2324
import im.vector.app.features.voice.VoiceFailure
2425
import im.vector.app.features.voicebroadcast.VoiceBroadcastFailure
2526
import im.vector.app.features.voicebroadcast.VoiceBroadcastFailure.RecordingError
@@ -138,6 +139,7 @@ class DefaultErrorFormatter @Inject constructor(
138139
stringProvider.getString(R.string.login_signin_matrix_id_error_invalid_matrix_id)
139140
is VoiceFailure -> voiceMessageError(throwable)
140141
is VoiceBroadcastFailure -> voiceBroadcastMessageError(throwable)
142+
is RoomPollsLoadingError -> stringProvider.getString(R.string.room_polls_loading_error)
141143
is ActivityNotFoundException ->
142144
stringProvider.getString(R.string.error_no_external_application_found)
143145
else -> throwable.localizedMessage

vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ class RoomProfileActivity :
7676
return ActivitySimpleBinding.inflate(layoutInflater)
7777
}
7878

79+
override fun getCoordinatorLayout() = views.coordinatorLayout
80+
7981
override fun initUiAndData() {
8082
sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
8183
roomProfileArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return

vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsAction.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,6 @@ package im.vector.app.features.roomprofile.polls
1818

1919
import im.vector.app.core.platform.VectorViewModelAction
2020

21-
sealed interface RoomPollsAction : VectorViewModelAction
21+
sealed interface RoomPollsAction : VectorViewModelAction {
22+
object LoadMorePolls : RoomPollsAction
23+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package im.vector.app.features.roomprofile.polls
18+
19+
class RoomPollsLoadingError : Throwable()

vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewEvent.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,6 @@ package im.vector.app.features.roomprofile.polls
1818

1919
import im.vector.app.core.platform.VectorViewEvents
2020

21-
sealed class RoomPollsViewEvent : VectorViewEvents
21+
sealed class RoomPollsViewEvent : VectorViewEvents {
22+
object LoadingError : RoomPollsViewEvent()
23+
}

vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewModel.kt

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,20 @@ import dagger.assisted.AssistedInject
2323
import im.vector.app.core.di.MavericksAssistedViewModelFactory
2424
import im.vector.app.core.di.hiltMavericksViewModelFactory
2525
import im.vector.app.core.platform.VectorViewModel
26+
import im.vector.app.features.roomprofile.polls.list.domain.GetLoadedPollsStatusUseCase
27+
import im.vector.app.features.roomprofile.polls.list.domain.GetPollsUseCase
28+
import im.vector.app.features.roomprofile.polls.list.domain.LoadMorePollsUseCase
29+
import im.vector.app.features.roomprofile.polls.list.domain.SyncPollsUseCase
2630
import kotlinx.coroutines.flow.launchIn
2731
import kotlinx.coroutines.flow.onEach
32+
import kotlinx.coroutines.launch
2833

2934
class RoomPollsViewModel @AssistedInject constructor(
3035
@Assisted initialState: RoomPollsViewState,
3136
private val getPollsUseCase: GetPollsUseCase,
37+
private val getLoadedPollsStatusUseCase: GetLoadedPollsStatusUseCase,
38+
private val loadMorePollsUseCase: LoadMorePollsUseCase,
39+
private val syncPollsUseCase: SyncPollsUseCase,
3240
) : VectorViewModel<RoomPollsViewState, RoomPollsAction, RoomPollsViewEvent>(initialState) {
3341

3442
@AssistedFactory
@@ -39,16 +47,63 @@ class RoomPollsViewModel @AssistedInject constructor(
3947
companion object : MavericksViewModelFactory<RoomPollsViewModel, RoomPollsViewState> by hiltMavericksViewModelFactory()
4048

4149
init {
42-
observePolls()
50+
val roomId = initialState.roomId
51+
updateLoadedPollStatus(roomId)
52+
syncPolls(roomId)
53+
observePolls(roomId)
4354
}
4455

45-
private fun observePolls() {
46-
getPollsUseCase.execute()
56+
private fun updateLoadedPollStatus(roomId: String) {
57+
val loadedPollsStatus = getLoadedPollsStatusUseCase.execute(roomId)
58+
setState {
59+
copy(
60+
canLoadMore = loadedPollsStatus.canLoadMore,
61+
nbLoadedDays = loadedPollsStatus.nbLoadedDays
62+
)
63+
}
64+
}
65+
66+
private fun syncPolls(roomId: String) {
67+
viewModelScope.launch {
68+
setState { copy(isSyncing = true) }
69+
val result = runCatching {
70+
syncPollsUseCase.execute(roomId)
71+
}
72+
if (result.isFailure) {
73+
_viewEvents.post(RoomPollsViewEvent.LoadingError)
74+
}
75+
setState { copy(isSyncing = false) }
76+
}
77+
}
78+
79+
private fun observePolls(roomId: String) {
80+
getPollsUseCase.execute(roomId)
4781
.onEach { setState { copy(polls = it) } }
4882
.launchIn(viewModelScope)
4983
}
5084

5185
override fun handle(action: RoomPollsAction) {
52-
// do nothing for now
86+
when (action) {
87+
RoomPollsAction.LoadMorePolls -> handleLoadMore()
88+
}
89+
}
90+
91+
private fun handleLoadMore() = withState { viewState ->
92+
viewModelScope.launch {
93+
setState { copy(isLoadingMore = true) }
94+
val result = runCatching {
95+
val status = loadMorePollsUseCase.execute(viewState.roomId)
96+
setState {
97+
copy(
98+
canLoadMore = status.canLoadMore,
99+
nbLoadedDays = status.nbLoadedDays,
100+
)
101+
}
102+
}
103+
if (result.isFailure) {
104+
_viewEvents.post(RoomPollsViewEvent.LoadingError)
105+
}
106+
setState { copy(isLoadingMore = false) }
107+
}
53108
}
54109
}

vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewState.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,19 @@ package im.vector.app.features.roomprofile.polls
1818

1919
import com.airbnb.mvrx.MavericksState
2020
import im.vector.app.features.roomprofile.RoomProfileArgs
21+
import im.vector.app.features.roomprofile.polls.list.ui.PollSummary
2122

2223
data class RoomPollsViewState(
2324
val roomId: String,
2425
val polls: List<PollSummary> = emptyList(),
26+
val isLoadingMore: Boolean = false,
27+
val canLoadMore: Boolean = true,
28+
val nbLoadedDays: Int = 0,
29+
val isSyncing: Boolean = false,
2530
) : MavericksState {
2631

2732
constructor(roomProfileArgs: RoomProfileArgs) : this(roomId = roomProfileArgs.roomId)
33+
34+
fun hasNoPolls() = polls.isEmpty()
35+
fun hasNoPollsAndCanLoadMore() = !isSyncing && hasNoPolls() && canLoadMore
2836
}

vector/src/main/java/im/vector/app/features/roomprofile/polls/active/RoomActivePollsFragment.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@ package im.vector.app.features.roomprofile.polls.active
1919
import dagger.hilt.android.AndroidEntryPoint
2020
import im.vector.app.R
2121
import im.vector.app.features.roomprofile.polls.RoomPollsType
22-
import im.vector.app.features.roomprofile.polls.list.RoomPollsListFragment
22+
import im.vector.app.features.roomprofile.polls.list.ui.RoomPollsListFragment
2323

2424
@AndroidEntryPoint
2525
class RoomActivePollsFragment : RoomPollsListFragment() {
2626

27-
override fun getEmptyListTitle(): String {
28-
return getString(R.string.room_polls_active_no_item)
27+
override fun getEmptyListTitle(canLoadMore: Boolean, nbLoadedDays: Int): String {
28+
return if (canLoadMore) {
29+
stringProvider.getQuantityString(R.plurals.room_polls_active_no_item_for_loaded_period, nbLoadedDays, nbLoadedDays)
30+
} else {
31+
getString(R.string.room_polls_active_no_item)
32+
}
2933
}
3034

3135
override fun getRoomPollsType(): RoomPollsType {

0 commit comments

Comments
 (0)