mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Merge pull request #7951 from vector-im/feature/mna/poll-history-load-more-ui
[Poll] History list: Load more UI mechanism (PSG-1095)
This commit is contained in:
commit
0cdbceaa00
33 changed files with 1136 additions and 132 deletions
1
changelog.d/7864.wip
Normal file
1
changelog.d/7864.wip
Normal file
|
@ -0,0 +1 @@
|
|||
[Poll] History list: Load more UI mechanism
|
|
@ -3199,8 +3199,19 @@
|
|||
<string name="unable_to_decrypt_some_events_in_poll">Due to decryption errors, some votes may not be counted</string>
|
||||
<string name="room_polls_active">Active polls</string>
|
||||
<string name="room_polls_active_no_item">There are no active polls in this room</string>
|
||||
<plurals name="room_polls_active_no_item_for_loaded_period">
|
||||
<item quantity="one">"There are no active polls for the past day.\nLoad more polls to view polls for previous days."</item>
|
||||
<item quantity="other">"There are no active polls for the past %1$d days.\nLoad more polls to view polls for previous days."</item>
|
||||
</plurals>
|
||||
<string name="room_polls_ended">Past polls</string>
|
||||
<string name="room_polls_ended_no_item">There are no past polls in this room</string>
|
||||
<plurals name="room_polls_ended_no_item_for_loaded_period">
|
||||
<item quantity="one">"There are no past polls for the past day.\nLoad more polls to view polls for previous days."</item>
|
||||
<item quantity="other">"There are no past polls for the past %1$d days.\nLoad more polls to view polls for previous days."</item>
|
||||
</plurals>
|
||||
<string name="room_polls_wait_for_display">Displaying polls</string>
|
||||
<string name="room_polls_load_more">Load more polls</string>
|
||||
<string name="room_polls_loading_error">Error fetching polls.</string>
|
||||
|
||||
<!-- Location -->
|
||||
<string name="location_activity_title_static_sharing">Share location</string>
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.content.ActivityNotFoundException
|
|||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.call.dialpad.DialPadLookup
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsLoadingError
|
||||
import im.vector.app.features.voice.VoiceFailure
|
||||
import im.vector.app.features.voicebroadcast.VoiceBroadcastFailure
|
||||
import im.vector.app.features.voicebroadcast.VoiceBroadcastFailure.RecordingError
|
||||
|
@ -138,6 +139,7 @@ class DefaultErrorFormatter @Inject constructor(
|
|||
stringProvider.getString(R.string.login_signin_matrix_id_error_invalid_matrix_id)
|
||||
is VoiceFailure -> voiceMessageError(throwable)
|
||||
is VoiceBroadcastFailure -> voiceBroadcastMessageError(throwable)
|
||||
is RoomPollsLoadingError -> stringProvider.getString(R.string.room_polls_loading_error)
|
||||
is ActivityNotFoundException ->
|
||||
stringProvider.getString(R.string.error_no_external_application_found)
|
||||
else -> throwable.localizedMessage
|
||||
|
|
|
@ -76,6 +76,8 @@ class RoomProfileActivity :
|
|||
return ActivitySimpleBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
||||
|
||||
override fun initUiAndData() {
|
||||
sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
|
||||
roomProfileArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
|
||||
|
|
|
@ -18,4 +18,6 @@ package im.vector.app.features.roomprofile.polls
|
|||
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
|
||||
sealed interface RoomPollsAction : VectorViewModelAction
|
||||
sealed interface RoomPollsAction : VectorViewModelAction {
|
||||
object LoadMorePolls : RoomPollsAction
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls
|
||||
|
||||
class RoomPollsLoadingError : Throwable()
|
|
@ -18,4 +18,6 @@ package im.vector.app.features.roomprofile.polls
|
|||
|
||||
import im.vector.app.core.platform.VectorViewEvents
|
||||
|
||||
sealed class RoomPollsViewEvent : VectorViewEvents
|
||||
sealed class RoomPollsViewEvent : VectorViewEvents {
|
||||
object LoadingError : RoomPollsViewEvent()
|
||||
}
|
||||
|
|
|
@ -23,12 +23,20 @@ import dagger.assisted.AssistedInject
|
|||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.roomprofile.polls.list.domain.GetLoadedPollsStatusUseCase
|
||||
import im.vector.app.features.roomprofile.polls.list.domain.GetPollsUseCase
|
||||
import im.vector.app.features.roomprofile.polls.list.domain.LoadMorePollsUseCase
|
||||
import im.vector.app.features.roomprofile.polls.list.domain.SyncPollsUseCase
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class RoomPollsViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: RoomPollsViewState,
|
||||
private val getPollsUseCase: GetPollsUseCase,
|
||||
private val getLoadedPollsStatusUseCase: GetLoadedPollsStatusUseCase,
|
||||
private val loadMorePollsUseCase: LoadMorePollsUseCase,
|
||||
private val syncPollsUseCase: SyncPollsUseCase,
|
||||
) : VectorViewModel<RoomPollsViewState, RoomPollsAction, RoomPollsViewEvent>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -39,16 +47,63 @@ class RoomPollsViewModel @AssistedInject constructor(
|
|||
companion object : MavericksViewModelFactory<RoomPollsViewModel, RoomPollsViewState> by hiltMavericksViewModelFactory()
|
||||
|
||||
init {
|
||||
observePolls()
|
||||
val roomId = initialState.roomId
|
||||
updateLoadedPollStatus(roomId)
|
||||
syncPolls(roomId)
|
||||
observePolls(roomId)
|
||||
}
|
||||
|
||||
private fun observePolls() {
|
||||
getPollsUseCase.execute()
|
||||
private fun updateLoadedPollStatus(roomId: String) {
|
||||
val loadedPollsStatus = getLoadedPollsStatusUseCase.execute(roomId)
|
||||
setState {
|
||||
copy(
|
||||
canLoadMore = loadedPollsStatus.canLoadMore,
|
||||
nbLoadedDays = loadedPollsStatus.nbLoadedDays
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun syncPolls(roomId: String) {
|
||||
viewModelScope.launch {
|
||||
setState { copy(isSyncing = true) }
|
||||
val result = runCatching {
|
||||
syncPollsUseCase.execute(roomId)
|
||||
}
|
||||
if (result.isFailure) {
|
||||
_viewEvents.post(RoomPollsViewEvent.LoadingError)
|
||||
}
|
||||
setState { copy(isSyncing = false) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun observePolls(roomId: String) {
|
||||
getPollsUseCase.execute(roomId)
|
||||
.onEach { setState { copy(polls = it) } }
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
override fun handle(action: RoomPollsAction) {
|
||||
// do nothing for now
|
||||
when (action) {
|
||||
RoomPollsAction.LoadMorePolls -> handleLoadMore()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLoadMore() = withState { viewState ->
|
||||
viewModelScope.launch {
|
||||
setState { copy(isLoadingMore = true) }
|
||||
val result = runCatching {
|
||||
val status = loadMorePollsUseCase.execute(viewState.roomId)
|
||||
setState {
|
||||
copy(
|
||||
canLoadMore = status.canLoadMore,
|
||||
nbLoadedDays = status.nbLoadedDays,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (result.isFailure) {
|
||||
_viewEvents.post(RoomPollsViewEvent.LoadingError)
|
||||
}
|
||||
setState { copy(isLoadingMore = false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,11 +18,19 @@ package im.vector.app.features.roomprofile.polls
|
|||
|
||||
import com.airbnb.mvrx.MavericksState
|
||||
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||
import im.vector.app.features.roomprofile.polls.list.ui.PollSummary
|
||||
|
||||
data class RoomPollsViewState(
|
||||
val roomId: String,
|
||||
val polls: List<PollSummary> = emptyList(),
|
||||
val isLoadingMore: Boolean = false,
|
||||
val canLoadMore: Boolean = true,
|
||||
val nbLoadedDays: Int = 0,
|
||||
val isSyncing: Boolean = false,
|
||||
) : MavericksState {
|
||||
|
||||
constructor(roomProfileArgs: RoomProfileArgs) : this(roomId = roomProfileArgs.roomId)
|
||||
|
||||
fun hasNoPolls() = polls.isEmpty()
|
||||
fun hasNoPollsAndCanLoadMore() = !isSyncing && hasNoPolls() && canLoadMore
|
||||
}
|
||||
|
|
|
@ -19,13 +19,17 @@ package im.vector.app.features.roomprofile.polls.active
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.R
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsType
|
||||
import im.vector.app.features.roomprofile.polls.list.RoomPollsListFragment
|
||||
import im.vector.app.features.roomprofile.polls.list.ui.RoomPollsListFragment
|
||||
|
||||
@AndroidEntryPoint
|
||||
class RoomActivePollsFragment : RoomPollsListFragment() {
|
||||
|
||||
override fun getEmptyListTitle(): String {
|
||||
return getString(R.string.room_polls_active_no_item)
|
||||
override fun getEmptyListTitle(canLoadMore: Boolean, nbLoadedDays: Int): String {
|
||||
return if (canLoadMore) {
|
||||
stringProvider.getQuantityString(R.plurals.room_polls_active_no_item_for_loaded_period, nbLoadedDays, nbLoadedDays)
|
||||
} else {
|
||||
getString(R.string.room_polls_active_no_item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRoomPollsType(): RoomPollsType {
|
||||
|
|
|
@ -19,13 +19,17 @@ package im.vector.app.features.roomprofile.polls.ended
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.R
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsType
|
||||
import im.vector.app.features.roomprofile.polls.list.RoomPollsListFragment
|
||||
import im.vector.app.features.roomprofile.polls.list.ui.RoomPollsListFragment
|
||||
|
||||
@AndroidEntryPoint
|
||||
class RoomEndedPollsFragment : RoomPollsListFragment() {
|
||||
|
||||
override fun getEmptyListTitle(): String {
|
||||
return getString(R.string.room_polls_ended_no_item)
|
||||
override fun getEmptyListTitle(canLoadMore: Boolean, nbLoadedDays: Int): String {
|
||||
return if (canLoadMore) {
|
||||
stringProvider.getQuantityString(R.plurals.room_polls_ended_no_item_for_loaded_period, nbLoadedDays, nbLoadedDays)
|
||||
} else {
|
||||
getString(R.string.room_polls_ended_no_item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRoomPollsType(): RoomPollsType {
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentRoomPollsListBinding
|
||||
import im.vector.app.features.roomprofile.polls.PollSummary
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsType
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsViewModel
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class RoomPollsListFragment :
|
||||
VectorBaseFragment<FragmentRoomPollsListBinding>(),
|
||||
RoomPollsController.Listener {
|
||||
|
||||
@Inject
|
||||
lateinit var roomPollsController: RoomPollsController
|
||||
|
||||
private val viewModel: RoomPollsViewModel by parentFragmentViewModel(RoomPollsViewModel::class)
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomPollsListBinding {
|
||||
return FragmentRoomPollsListBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupList()
|
||||
}
|
||||
|
||||
abstract fun getEmptyListTitle(): String
|
||||
|
||||
abstract fun getRoomPollsType(): RoomPollsType
|
||||
|
||||
private fun setupList() {
|
||||
roomPollsController.listener = this
|
||||
views.roomPollsList.configureWith(roomPollsController)
|
||||
views.roomPollsEmptyTitle.text = getEmptyListTitle()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
cleanUpList()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun cleanUpList() {
|
||||
views.roomPollsList.cleanup()
|
||||
roomPollsController.listener = null
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { viewState ->
|
||||
when (getRoomPollsType()) {
|
||||
RoomPollsType.ACTIVE -> renderList(viewState.polls.filterIsInstance(PollSummary.ActivePoll::class.java))
|
||||
RoomPollsType.ENDED -> renderList(viewState.polls.filterIsInstance(PollSummary.EndedPoll::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderList(polls: List<PollSummary>) {
|
||||
roomPollsController.setData(polls)
|
||||
views.roomPollsEmptyTitle.isVisible = polls.isEmpty()
|
||||
}
|
||||
|
||||
override fun onPollClicked(pollId: String) {
|
||||
// TODO navigate to details
|
||||
Timber.d("poll with id $pollId clicked")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.data
|
||||
|
||||
data class LoadedPollsStatus(
|
||||
val canLoadMore: Boolean,
|
||||
val nbLoadedDays: Int,
|
||||
)
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
* Copyright (c) 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.
|
||||
|
@ -14,23 +14,60 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls
|
||||
package im.vector.app.features.roomprofile.polls.list.data
|
||||
|
||||
import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState
|
||||
import im.vector.app.features.roomprofile.polls.list.ui.PollSummary
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class GetPollsUseCase @Inject constructor() {
|
||||
@Singleton
|
||||
class RoomPollDataSource @Inject constructor() {
|
||||
|
||||
fun execute(): Flow<List<PollSummary>> {
|
||||
// TODO unmock and add unit tests
|
||||
return flowOf(getActivePolls() + getEndedPolls())
|
||||
.map { it.sortedByDescending { poll -> poll.creationTimestamp } }
|
||||
private val pollsFlow = MutableSharedFlow<List<PollSummary>>(replay = 1)
|
||||
private val polls = mutableListOf<PollSummary>()
|
||||
private var fakeLoadCounter = 0
|
||||
|
||||
// TODO
|
||||
// unmock using SDK service + add unit tests
|
||||
// after unmock, expose domain layer model (entity) and do the mapping to PollSummary in the UI layer
|
||||
fun getPolls(roomId: String): Flow<List<PollSummary>> {
|
||||
Timber.d("roomId=$roomId")
|
||||
return pollsFlow.asSharedFlow()
|
||||
}
|
||||
|
||||
private fun getActivePolls(): List<PollSummary.ActivePoll> {
|
||||
fun getLoadedPollsStatus(roomId: String): LoadedPollsStatus {
|
||||
Timber.d("roomId=$roomId")
|
||||
return LoadedPollsStatus(
|
||||
canLoadMore = canLoadMore(),
|
||||
nbLoadedDays = fakeLoadCounter * 30,
|
||||
)
|
||||
}
|
||||
|
||||
private fun canLoadMore(): Boolean {
|
||||
return fakeLoadCounter < 2
|
||||
}
|
||||
|
||||
suspend fun loadMorePolls(roomId: String): LoadedPollsStatus {
|
||||
// TODO
|
||||
// unmock using SDK service + add unit tests
|
||||
delay(3000)
|
||||
fakeLoadCounter++
|
||||
when (fakeLoadCounter) {
|
||||
1 -> polls.addAll(getActivePollsPart1() + getEndedPollsPart1())
|
||||
2 -> polls.addAll(getActivePollsPart2() + getEndedPollsPart2())
|
||||
else -> Unit
|
||||
}
|
||||
pollsFlow.emit(polls)
|
||||
return getLoadedPollsStatus(roomId)
|
||||
}
|
||||
|
||||
private fun getActivePollsPart1(): List<PollSummary.ActivePoll> {
|
||||
return listOf(
|
||||
PollSummary.ActivePoll(
|
||||
id = "id1",
|
||||
|
@ -44,6 +81,11 @@ class GetPollsUseCase @Inject constructor() {
|
|||
creationTimestamp = 1656194400000,
|
||||
title = "Which sport should the pupils do this year?"
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getActivePollsPart2(): List<PollSummary.ActivePoll> {
|
||||
return listOf(
|
||||
PollSummary.ActivePoll(
|
||||
id = "id3",
|
||||
// 2022/06/24 UTC+1
|
||||
|
@ -59,7 +101,7 @@ class GetPollsUseCase @Inject constructor() {
|
|||
)
|
||||
}
|
||||
|
||||
private fun getEndedPolls(): List<PollSummary.EndedPoll> {
|
||||
private fun getEndedPollsPart1(): List<PollSummary.EndedPoll> {
|
||||
return listOf(
|
||||
PollSummary.EndedPoll(
|
||||
id = "id1-ended",
|
||||
|
@ -77,6 +119,11 @@ class GetPollsUseCase @Inject constructor() {
|
|||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getEndedPollsPart2(): List<PollSummary.EndedPoll> {
|
||||
return listOf(
|
||||
PollSummary.EndedPoll(
|
||||
id = "id2-ended",
|
||||
// 2022/06/26 UTC+1
|
||||
|
@ -111,4 +158,17 @@ class GetPollsUseCase @Inject constructor() {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun syncPolls(roomId: String) {
|
||||
Timber.d("roomId=$roomId")
|
||||
// TODO
|
||||
// unmock using SDK service + add unit tests
|
||||
if (fakeLoadCounter == 0) {
|
||||
// fake first load
|
||||
loadMorePolls(roomId)
|
||||
} else {
|
||||
// fake sync
|
||||
delay(3000)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.data
|
||||
|
||||
import im.vector.app.features.roomprofile.polls.list.ui.PollSummary
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomPollRepository @Inject constructor(
|
||||
private val roomPollDataSource: RoomPollDataSource,
|
||||
) {
|
||||
|
||||
// TODO after unmock, expose domain layer model (entity) and do the mapping to PollSummary in the UI layer
|
||||
fun getPolls(roomId: String): Flow<List<PollSummary>> {
|
||||
return roomPollDataSource.getPolls(roomId)
|
||||
}
|
||||
|
||||
fun getLoadedPollsStatus(roomId: String): LoadedPollsStatus {
|
||||
return roomPollDataSource.getLoadedPollsStatus(roomId)
|
||||
}
|
||||
|
||||
suspend fun loadMorePolls(roomId: String): LoadedPollsStatus {
|
||||
return roomPollDataSource.loadMorePolls(roomId)
|
||||
}
|
||||
|
||||
suspend fun syncPolls(roomId: String) {
|
||||
return roomPollDataSource.syncPolls(roomId)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.domain
|
||||
|
||||
import im.vector.app.features.roomprofile.polls.list.data.LoadedPollsStatus
|
||||
import im.vector.app.features.roomprofile.polls.list.data.RoomPollRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetLoadedPollsStatusUseCase @Inject constructor(
|
||||
private val roomPollRepository: RoomPollRepository,
|
||||
) {
|
||||
|
||||
fun execute(roomId: String): LoadedPollsStatus {
|
||||
return roomPollRepository.getLoadedPollsStatus(roomId)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.domain
|
||||
|
||||
import im.vector.app.features.roomprofile.polls.list.data.RoomPollRepository
|
||||
import im.vector.app.features.roomprofile.polls.list.ui.PollSummary
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetPollsUseCase @Inject constructor(
|
||||
private val roomPollRepository: RoomPollRepository,
|
||||
) {
|
||||
|
||||
fun execute(roomId: String): Flow<List<PollSummary>> {
|
||||
return roomPollRepository.getPolls(roomId)
|
||||
.map { it.sortedByDescending { poll -> poll.creationTimestamp } }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.domain
|
||||
|
||||
import im.vector.app.features.roomprofile.polls.list.data.LoadedPollsStatus
|
||||
import im.vector.app.features.roomprofile.polls.list.data.RoomPollRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class LoadMorePollsUseCase @Inject constructor(
|
||||
private val roomPollRepository: RoomPollRepository,
|
||||
) {
|
||||
|
||||
suspend fun execute(roomId: String): LoadedPollsStatus {
|
||||
return roomPollRepository.loadMorePolls(roomId)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.domain
|
||||
|
||||
import im.vector.app.features.roomprofile.polls.list.data.RoomPollRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Sync the polls of a given room from last manual loading (see LoadMorePollsUseCase) until now.
|
||||
*/
|
||||
class SyncPollsUseCase @Inject constructor(
|
||||
private val roomPollRepository: RoomPollRepository,
|
||||
) {
|
||||
|
||||
suspend fun execute(roomId: String) {
|
||||
roomPollRepository.syncPolls(roomId)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
* Copyright (c) 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.
|
||||
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls
|
||||
package im.vector.app.features.roomprofile.polls.list.ui
|
||||
|
||||
import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
* Copyright (c) 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.
|
||||
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list
|
||||
package im.vector.app.features.roomprofile.polls.list.ui
|
||||
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.ui
|
||||
|
||||
import android.widget.Button
|
||||
import android.widget.ProgressBar
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.ClickListener
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
|
||||
@EpoxyModelClass
|
||||
abstract class RoomPollLoadMoreItem : VectorEpoxyModel<RoomPollLoadMoreItem.Holder>(R.layout.item_poll_load_more) {
|
||||
|
||||
@EpoxyAttribute
|
||||
var loadingMore: Boolean = false
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
var clickListener: ClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.loadMoreButton.isEnabled = loadingMore.not()
|
||||
holder.loadMoreButton.onClick(clickListener)
|
||||
holder.loadMoreProgressBar.isVisible = loadingMore
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val loadMoreButton by bind<Button>(R.id.roomPollsLoadMore)
|
||||
val loadMoreProgressBar by bind<ProgressBar>(R.id.roomPollsLoadMoreProgress)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
* Copyright (c) 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.
|
||||
|
@ -14,38 +14,45 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list
|
||||
package im.vector.app.features.roomprofile.polls.list.ui
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.date.DateFormatKind
|
||||
import im.vector.app.core.date.VectorDateFormatter
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.roomprofile.polls.PollSummary
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsViewState
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomPollsController @Inject constructor(
|
||||
val dateFormatter: VectorDateFormatter,
|
||||
val stringProvider: StringProvider,
|
||||
) : TypedEpoxyController<List<PollSummary>>() {
|
||||
) : TypedEpoxyController<RoomPollsViewState>() {
|
||||
|
||||
interface Listener {
|
||||
fun onPollClicked(pollId: String)
|
||||
fun onLoadMoreClicked()
|
||||
}
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
override fun buildModels(data: List<PollSummary>?) {
|
||||
if (data.isNullOrEmpty()) {
|
||||
override fun buildModels(viewState: RoomPollsViewState?) {
|
||||
val polls = viewState?.polls
|
||||
if (polls.isNullOrEmpty() || viewState.isSyncing) {
|
||||
return
|
||||
}
|
||||
|
||||
for (poll in data) {
|
||||
for (poll in polls) {
|
||||
when (poll) {
|
||||
is PollSummary.ActivePoll -> buildActivePollItem(poll)
|
||||
is PollSummary.EndedPoll -> buildEndedPollItem(poll)
|
||||
}
|
||||
}
|
||||
|
||||
if (viewState.canLoadMore) {
|
||||
buildLoadMoreItem(viewState.isLoadingMore)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildActivePollItem(poll: PollSummary.ActivePoll) {
|
||||
|
@ -73,4 +80,15 @@ class RoomPollsController @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildLoadMoreItem(isLoadingMore: Boolean) {
|
||||
val host = this
|
||||
roomPollLoadMoreItem {
|
||||
id(UUID.randomUUID().toString())
|
||||
loadingMore(isLoadingMore)
|
||||
clickListener {
|
||||
host.listener?.onLoadMoreClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.databinding.FragmentRoomPollsListBinding
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsAction
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsLoadingError
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsType
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsViewEvent
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsViewModel
|
||||
import im.vector.app.features.roomprofile.polls.RoomPollsViewState
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class RoomPollsListFragment :
|
||||
VectorBaseFragment<FragmentRoomPollsListBinding>(),
|
||||
RoomPollsController.Listener {
|
||||
|
||||
@Inject
|
||||
lateinit var roomPollsController: RoomPollsController
|
||||
|
||||
@Inject
|
||||
lateinit var stringProvider: StringProvider
|
||||
|
||||
private val viewModel: RoomPollsViewModel by parentFragmentViewModel(RoomPollsViewModel::class)
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomPollsListBinding {
|
||||
return FragmentRoomPollsListBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
observeViewEvents()
|
||||
setupList()
|
||||
setupLoadMoreButton()
|
||||
}
|
||||
|
||||
private fun observeViewEvents() {
|
||||
viewModel.observeViewEvents { viewEvent ->
|
||||
when (viewEvent) {
|
||||
RoomPollsViewEvent.LoadingError -> showErrorInSnackbar(RoomPollsLoadingError())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun getEmptyListTitle(canLoadMore: Boolean, nbLoadedDays: Int): String
|
||||
|
||||
abstract fun getRoomPollsType(): RoomPollsType
|
||||
|
||||
private fun setupList() = withState(viewModel) { viewState ->
|
||||
roomPollsController.listener = this
|
||||
views.roomPollsList.configureWith(roomPollsController)
|
||||
views.roomPollsEmptyTitle.text = getEmptyListTitle(
|
||||
canLoadMore = viewState.canLoadMore,
|
||||
nbLoadedDays = viewState.nbLoadedDays,
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupLoadMoreButton() {
|
||||
views.roomPollsLoadMoreWhenEmpty.onClick {
|
||||
onLoadMoreClicked()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
cleanUpList()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun cleanUpList() {
|
||||
views.roomPollsList.cleanup()
|
||||
roomPollsController.listener = null
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { viewState ->
|
||||
val filteredPolls = when (getRoomPollsType()) {
|
||||
RoomPollsType.ACTIVE -> viewState.polls.filterIsInstance(PollSummary.ActivePoll::class.java)
|
||||
RoomPollsType.ENDED -> viewState.polls.filterIsInstance(PollSummary.EndedPoll::class.java)
|
||||
}
|
||||
val updatedViewState = viewState.copy(polls = filteredPolls)
|
||||
renderList(updatedViewState)
|
||||
renderSyncingView(updatedViewState)
|
||||
}
|
||||
|
||||
private fun renderSyncingView(viewState: RoomPollsViewState) {
|
||||
views.roomPollsSyncingTitle.isVisible = viewState.isSyncing
|
||||
views.roomPollsSyncingProgress.isVisible = viewState.isSyncing
|
||||
}
|
||||
|
||||
private fun renderList(viewState: RoomPollsViewState) {
|
||||
roomPollsController.setData(viewState)
|
||||
views.roomPollsEmptyTitle.text = getEmptyListTitle(
|
||||
canLoadMore = viewState.canLoadMore,
|
||||
nbLoadedDays = viewState.nbLoadedDays,
|
||||
)
|
||||
views.roomPollsEmptyTitle.isVisible = !viewState.isSyncing && viewState.hasNoPolls()
|
||||
views.roomPollsLoadMoreWhenEmpty.isVisible = viewState.hasNoPollsAndCanLoadMore()
|
||||
views.roomPollsLoadMoreWhenEmpty.isEnabled = !viewState.isLoadingMore
|
||||
views.roomPollsLoadMoreWhenEmptyProgress.isVisible = viewState.hasNoPollsAndCanLoadMore() && viewState.isLoadingMore
|
||||
}
|
||||
|
||||
override fun onPollClicked(pollId: String) {
|
||||
// TODO navigate to details
|
||||
Timber.d("poll with id $pollId clicked")
|
||||
}
|
||||
|
||||
override fun onLoadMoreClicked() {
|
||||
viewModel.handle(RoomPollsAction.LoadMorePolls)
|
||||
}
|
||||
}
|
|
@ -17,6 +17,34 @@
|
|||
tools:itemCount="5"
|
||||
tools:listitem="@layout/item_poll" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/roomPollsSyncingProgress"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:indeterminateTint="?vctr_content_secondary"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/roomPollsSyncingTitle"
|
||||
app:layout_constraintEnd_toStartOf="@id/roomPollsSyncingTitle"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/roomPollsSyncingTitle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomPollsSyncingTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="9dp"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
android:gravity="center"
|
||||
android:text="@string/room_polls_wait_for_display"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Body"
|
||||
android:textColor="?vctr_content_secondary"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/roomPollsSyncingProgress"
|
||||
app:layout_constraintTop_toTopOf="@id/roomPollsTitleGuideline" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomPollsEmptyTitle"
|
||||
android:layout_width="0dp"
|
||||
|
@ -26,14 +54,39 @@
|
|||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Body"
|
||||
android:textColor="?vctr_content_secondary"
|
||||
android:textSize="17sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/roomPollsEmptyGuideline"
|
||||
app:layout_constraintTop_toTopOf="@id/roomPollsTitleGuideline"
|
||||
tools:text="@string/room_polls_active_no_item" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/roomPollsLoadMoreWhenEmpty"
|
||||
style="@style/Widget.Vector.Button.Text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/room_polls_load_more"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/roomPollsEmptyTitle" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/roomPollsLoadMoreWhenEmptyProgress"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="9dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/roomPollsLoadMoreWhenEmpty"
|
||||
app:layout_constraintStart_toEndOf="@id/roomPollsLoadMoreWhenEmpty"
|
||||
app:layout_constraintTop_toTopOf="@id/roomPollsLoadMoreWhenEmpty" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/roomPollsEmptyGuideline"
|
||||
android:id="@+id/roomPollsTitleGuideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
|
|
30
vector/src/main/res/layout/item_poll_load_more.xml
Normal file
30
vector/src/main/res/layout/item_poll_load_more.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/roomPollsLoadMore"
|
||||
style="@style/Widget.Vector.Button.Text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="38dp"
|
||||
android:layout_marginBottom="46dp"
|
||||
android:padding="0dp"
|
||||
android:text="@string/room_polls_load_more"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/roomPollsLoadMoreProgress"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="9dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/roomPollsLoadMore"
|
||||
app:layout_constraintStart_toEndOf="@id/roomPollsLoadMore"
|
||||
app:layout_constraintTop_toTopOf="@id/roomPollsLoadMore" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -17,8 +17,17 @@
|
|||
package im.vector.app.features.roomprofile.polls
|
||||
|
||||
import com.airbnb.mvrx.test.MavericksTestRule
|
||||
import im.vector.app.features.roomprofile.polls.list.data.LoadedPollsStatus
|
||||
import im.vector.app.features.roomprofile.polls.list.domain.GetLoadedPollsStatusUseCase
|
||||
import im.vector.app.features.roomprofile.polls.list.domain.GetPollsUseCase
|
||||
import im.vector.app.features.roomprofile.polls.list.domain.LoadMorePollsUseCase
|
||||
import im.vector.app.features.roomprofile.polls.list.domain.SyncPollsUseCase
|
||||
import im.vector.app.features.roomprofile.polls.list.ui.PollSummary
|
||||
import im.vector.app.test.test
|
||||
import im.vector.app.test.testDispatcher
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coJustRun
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
|
@ -26,7 +35,7 @@ import kotlinx.coroutines.flow.flowOf
|
|||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
private const val ROOM_ID = "room-id"
|
||||
private const val A_ROOM_ID = "room-id"
|
||||
|
||||
class RoomPollsViewModelTest {
|
||||
|
||||
|
@ -34,21 +43,33 @@ class RoomPollsViewModelTest {
|
|||
val mavericksTestRule = MavericksTestRule(testDispatcher = testDispatcher)
|
||||
|
||||
private val fakeGetPollsUseCase = mockk<GetPollsUseCase>()
|
||||
private val initialState = RoomPollsViewState(ROOM_ID)
|
||||
private val fakeGetLoadedPollsStatusUseCase = mockk<GetLoadedPollsStatusUseCase>()
|
||||
private val fakeLoadMorePollsUseCase = mockk<LoadMorePollsUseCase>()
|
||||
private val fakeSyncPollsUseCase = mockk<SyncPollsUseCase>()
|
||||
private val initialState = RoomPollsViewState(A_ROOM_ID)
|
||||
|
||||
private fun createViewModel(): RoomPollsViewModel {
|
||||
return RoomPollsViewModel(
|
||||
initialState = initialState,
|
||||
getPollsUseCase = fakeGetPollsUseCase,
|
||||
getLoadedPollsStatusUseCase = fakeGetLoadedPollsStatusUseCase,
|
||||
loadMorePollsUseCase = fakeLoadMorePollsUseCase,
|
||||
syncPollsUseCase = fakeSyncPollsUseCase,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given viewModel when created then polls list is observed and viewState is updated`() {
|
||||
fun `given viewModel when created then polls list is observed, sync is launched and viewState is updated`() {
|
||||
// Given
|
||||
val loadedPollsStatus = givenGetLoadedPollsStatusSuccess()
|
||||
givenSyncPollsWithSuccess()
|
||||
val polls = listOf(givenAPollSummary())
|
||||
every { fakeGetPollsUseCase.execute() } returns flowOf(polls)
|
||||
val expectedViewState = initialState.copy(polls = polls)
|
||||
every { fakeGetPollsUseCase.execute(A_ROOM_ID) } returns flowOf(polls)
|
||||
val expectedViewState = initialState.copy(
|
||||
polls = polls,
|
||||
canLoadMore = loadedPollsStatus.canLoadMore,
|
||||
nbLoadedDays = loadedPollsStatus.nbLoadedDays,
|
||||
)
|
||||
|
||||
// When
|
||||
val viewModel = createViewModel()
|
||||
|
@ -59,11 +80,88 @@ class RoomPollsViewModelTest {
|
|||
.assertLatestState(expectedViewState)
|
||||
.finish()
|
||||
verify {
|
||||
fakeGetPollsUseCase.execute()
|
||||
fakeGetPollsUseCase.execute(A_ROOM_ID)
|
||||
}
|
||||
coVerify { fakeSyncPollsUseCase.execute(A_ROOM_ID) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given viewModel and error during sync process when created then error is raised in view event`() {
|
||||
// Given
|
||||
givenGetLoadedPollsStatusSuccess()
|
||||
givenSyncPollsWithError(Exception())
|
||||
val polls = listOf(givenAPollSummary())
|
||||
every { fakeGetPollsUseCase.execute(A_ROOM_ID) } returns flowOf(polls)
|
||||
|
||||
// When
|
||||
val viewModel = createViewModel()
|
||||
val viewModelTest = viewModel.test()
|
||||
|
||||
// Then
|
||||
viewModelTest
|
||||
.assertEvents(RoomPollsViewEvent.LoadingError)
|
||||
.finish()
|
||||
coVerify { fakeSyncPollsUseCase.execute(A_ROOM_ID) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given viewModel when handle load more action then viewState is updated`() {
|
||||
// Given
|
||||
val loadedPollsStatus = givenGetLoadedPollsStatusSuccess()
|
||||
givenSyncPollsWithSuccess()
|
||||
val polls = listOf(givenAPollSummary())
|
||||
every { fakeGetPollsUseCase.execute(A_ROOM_ID) } returns flowOf(polls)
|
||||
val newLoadedPollsStatus = givenLoadMoreWithSuccess()
|
||||
val viewModel = createViewModel()
|
||||
val stateAfterInit = initialState.copy(
|
||||
polls = polls,
|
||||
canLoadMore = loadedPollsStatus.canLoadMore,
|
||||
nbLoadedDays = loadedPollsStatus.nbLoadedDays,
|
||||
)
|
||||
|
||||
// When
|
||||
val viewModelTest = viewModel.test()
|
||||
viewModel.handle(RoomPollsAction.LoadMorePolls)
|
||||
|
||||
// Then
|
||||
viewModelTest
|
||||
.assertStatesChanges(
|
||||
stateAfterInit,
|
||||
{ copy(isLoadingMore = true) },
|
||||
{ copy(canLoadMore = newLoadedPollsStatus.canLoadMore, nbLoadedDays = newLoadedPollsStatus.nbLoadedDays) },
|
||||
{ copy(isLoadingMore = false) },
|
||||
)
|
||||
.finish()
|
||||
coVerify { fakeLoadMorePollsUseCase.execute(A_ROOM_ID) }
|
||||
}
|
||||
|
||||
private fun givenAPollSummary(): PollSummary {
|
||||
return mockk()
|
||||
}
|
||||
|
||||
private fun givenSyncPollsWithSuccess() {
|
||||
coJustRun { fakeSyncPollsUseCase.execute(A_ROOM_ID) }
|
||||
}
|
||||
|
||||
private fun givenSyncPollsWithError(error: Exception) {
|
||||
coEvery { fakeSyncPollsUseCase.execute(A_ROOM_ID) } throws error
|
||||
}
|
||||
|
||||
private fun givenLoadMoreWithSuccess(): LoadedPollsStatus {
|
||||
val loadedPollsStatus = givenALoadedPollsStatus(canLoadMore = false, nbLoadedDays = 20)
|
||||
coEvery { fakeLoadMorePollsUseCase.execute(A_ROOM_ID) } returns loadedPollsStatus
|
||||
return loadedPollsStatus
|
||||
}
|
||||
|
||||
private fun givenGetLoadedPollsStatusSuccess(): LoadedPollsStatus {
|
||||
val loadedPollsStatus = givenALoadedPollsStatus()
|
||||
every { fakeGetLoadedPollsStatusUseCase.execute(A_ROOM_ID) } returns loadedPollsStatus
|
||||
return loadedPollsStatus
|
||||
}
|
||||
|
||||
private fun givenALoadedPollsStatus(canLoadMore: Boolean = true, nbLoadedDays: Int = 10) =
|
||||
LoadedPollsStatus(
|
||||
canLoadMore = canLoadMore,
|
||||
nbLoadedDays = nbLoadedDays,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.data
|
||||
|
||||
import im.vector.app.features.roomprofile.polls.list.ui.PollSummary
|
||||
import io.mockk.coJustRun
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.junit.Test
|
||||
|
||||
private const val A_ROOM_ID = "room-id"
|
||||
|
||||
class RoomPollRepositoryTest {
|
||||
|
||||
private val fakeRoomPollDataSource = mockk<RoomPollDataSource>()
|
||||
|
||||
private val roomPollRepository = RoomPollRepository(
|
||||
roomPollDataSource = fakeRoomPollDataSource,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `given data source when getting polls then correct method of data source is called`() = runTest {
|
||||
// Given
|
||||
val expectedPolls = listOf<PollSummary>()
|
||||
every { fakeRoomPollDataSource.getPolls(A_ROOM_ID) } returns flowOf(expectedPolls)
|
||||
|
||||
// When
|
||||
val result = roomPollRepository.getPolls(A_ROOM_ID).firstOrNull()
|
||||
|
||||
// Then
|
||||
result shouldBeEqualTo expectedPolls
|
||||
verify { fakeRoomPollDataSource.getPolls(A_ROOM_ID) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given data source when getting loaded polls status then correct method of data source is called`() {
|
||||
// Given
|
||||
val expectedStatus = LoadedPollsStatus(
|
||||
canLoadMore = true,
|
||||
nbLoadedDays = 10,
|
||||
)
|
||||
every { fakeRoomPollDataSource.getLoadedPollsStatus(A_ROOM_ID) } returns expectedStatus
|
||||
|
||||
// When
|
||||
val result = roomPollRepository.getLoadedPollsStatus(A_ROOM_ID)
|
||||
|
||||
// Then
|
||||
result shouldBeEqualTo expectedStatus
|
||||
verify { fakeRoomPollDataSource.getLoadedPollsStatus(A_ROOM_ID) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given data source when loading more polls then correct method of data source is called`() = runTest {
|
||||
// Given
|
||||
coJustRun { fakeRoomPollDataSource.loadMorePolls(A_ROOM_ID) }
|
||||
|
||||
// When
|
||||
roomPollRepository.loadMorePolls(A_ROOM_ID)
|
||||
|
||||
// Then
|
||||
coVerify { fakeRoomPollDataSource.loadMorePolls(A_ROOM_ID) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given data source when syncing polls then correct method of data source is called`() = runTest {
|
||||
// Given
|
||||
coJustRun { fakeRoomPollDataSource.syncPolls(A_ROOM_ID) }
|
||||
|
||||
// When
|
||||
roomPollRepository.syncPolls(A_ROOM_ID)
|
||||
|
||||
// Then
|
||||
coVerify { fakeRoomPollDataSource.syncPolls(A_ROOM_ID) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.domain
|
||||
|
||||
import im.vector.app.features.roomprofile.polls.list.data.LoadedPollsStatus
|
||||
import im.vector.app.features.roomprofile.polls.list.data.RoomPollRepository
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.junit.Test
|
||||
|
||||
class GetLoadedPollsStatusUseCaseTest {
|
||||
|
||||
private val fakeRoomPollRepository = mockk<RoomPollRepository>()
|
||||
|
||||
private val getLoadedPollsStatusUseCase = GetLoadedPollsStatusUseCase(
|
||||
roomPollRepository = fakeRoomPollRepository,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `given repo when execute then correct method of repo is called`() {
|
||||
// Given
|
||||
val aRoomId = "roomId"
|
||||
val expectedStatus = LoadedPollsStatus(
|
||||
canLoadMore = true,
|
||||
nbLoadedDays = 10,
|
||||
)
|
||||
every { fakeRoomPollRepository.getLoadedPollsStatus(aRoomId) } returns expectedStatus
|
||||
|
||||
// When
|
||||
val status = getLoadedPollsStatusUseCase.execute(aRoomId)
|
||||
|
||||
// Then
|
||||
status shouldBeEqualTo expectedStatus
|
||||
verify { fakeRoomPollRepository.getLoadedPollsStatus(aRoomId) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.domain
|
||||
|
||||
import im.vector.app.features.roomprofile.polls.list.data.RoomPollRepository
|
||||
import im.vector.app.features.roomprofile.polls.list.ui.PollSummary
|
||||
import im.vector.app.test.fixtures.RoomPollFixture
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.junit.Test
|
||||
|
||||
class GetPollsUseCaseTest {
|
||||
private val fakeRoomPollRepository = mockk<RoomPollRepository>()
|
||||
|
||||
private val getPollsUseCase = GetPollsUseCase(
|
||||
roomPollRepository = fakeRoomPollRepository,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `given repo when execute then correct method of repo is called and polls are sorted most recent first`() = runTest {
|
||||
// Given
|
||||
val aRoomId = "roomId"
|
||||
val poll1 = RoomPollFixture.anActivePollSummary(timestamp = 1)
|
||||
val poll2 = RoomPollFixture.anActivePollSummary(timestamp = 2)
|
||||
val poll3 = RoomPollFixture.anActivePollSummary(timestamp = 3)
|
||||
val polls = listOf<PollSummary>(
|
||||
poll1,
|
||||
poll2,
|
||||
poll3,
|
||||
)
|
||||
every { fakeRoomPollRepository.getPolls(aRoomId) } returns flowOf(polls)
|
||||
val expectedPolls = listOf<PollSummary>(
|
||||
poll3,
|
||||
poll2,
|
||||
poll1,
|
||||
)
|
||||
// When
|
||||
val result = getPollsUseCase.execute(aRoomId).first()
|
||||
|
||||
// Then
|
||||
result shouldBeEqualTo expectedPolls
|
||||
verify { fakeRoomPollRepository.getPolls(aRoomId) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.domain
|
||||
|
||||
import im.vector.app.features.roomprofile.polls.list.data.RoomPollRepository
|
||||
import io.mockk.coJustRun
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class LoadMorePollsUseCaseTest {
|
||||
|
||||
private val fakeRoomPollRepository = mockk<RoomPollRepository>()
|
||||
|
||||
private val loadMorePollsUseCase = LoadMorePollsUseCase(
|
||||
roomPollRepository = fakeRoomPollRepository,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `given repo when execute then correct method of repo is called`() = runTest {
|
||||
// Given
|
||||
val aRoomId = "roomId"
|
||||
coJustRun { fakeRoomPollRepository.loadMorePolls(aRoomId) }
|
||||
|
||||
// When
|
||||
loadMorePollsUseCase.execute(aRoomId)
|
||||
|
||||
// Then
|
||||
coVerify { fakeRoomPollRepository.loadMorePolls(aRoomId) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.roomprofile.polls.list.domain
|
||||
|
||||
import im.vector.app.features.roomprofile.polls.list.data.RoomPollRepository
|
||||
import io.mockk.coJustRun
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class SyncPollsUseCaseTest {
|
||||
|
||||
private val fakeRoomPollRepository = mockk<RoomPollRepository>()
|
||||
|
||||
private val syncPollsUseCase = SyncPollsUseCase(
|
||||
roomPollRepository = fakeRoomPollRepository,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `given repo when execute then correct method of repo is called`() = runTest {
|
||||
// Given
|
||||
val aRoomId = "roomId"
|
||||
coJustRun { fakeRoomPollRepository.syncPolls(aRoomId) }
|
||||
|
||||
// When
|
||||
syncPollsUseCase.execute(aRoomId)
|
||||
|
||||
// Then
|
||||
coVerify { fakeRoomPollRepository.syncPolls(aRoomId) }
|
||||
}
|
||||
}
|
47
vector/src/test/java/im/vector/app/test/fixtures/RoomPollFixture.kt
vendored
Normal file
47
vector/src/test/java/im/vector/app/test/fixtures/RoomPollFixture.kt
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
package im.vector.app.test.fixtures
|
||||
|
||||
import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState
|
||||
import im.vector.app.features.roomprofile.polls.list.ui.PollSummary
|
||||
|
||||
object RoomPollFixture {
|
||||
|
||||
fun anActivePollSummary(
|
||||
id: String = "",
|
||||
timestamp: Long,
|
||||
title: String = "",
|
||||
) = PollSummary.ActivePoll(
|
||||
id = id,
|
||||
creationTimestamp = timestamp,
|
||||
title = title,
|
||||
)
|
||||
|
||||
fun anEndedPollSummary(
|
||||
id: String = "",
|
||||
timestamp: Long,
|
||||
title: String = "",
|
||||
totalVotes: Int,
|
||||
winnerOptions: List<PollOptionViewState.PollEnded>
|
||||
) = PollSummary.EndedPoll(
|
||||
id = id,
|
||||
creationTimestamp = timestamp,
|
||||
title = title,
|
||||
totalVotes = totalVotes,
|
||||
winnerOptions = winnerOptions,
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue