mirror of
https://github.com/element-hq/element-android
synced 2024-12-18 07:12:47 +03:00
Render the details of the poll
This commit is contained in:
parent
afe036dd9d
commit
d3df58c607
14 changed files with 241 additions and 88 deletions
|
@ -85,6 +85,7 @@ import im.vector.app.features.roomprofile.members.RoomMemberListViewModel
|
||||||
import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewModel
|
import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewModel
|
||||||
import im.vector.app.features.roomprofile.permissions.RoomPermissionsViewModel
|
import im.vector.app.features.roomprofile.permissions.RoomPermissionsViewModel
|
||||||
import im.vector.app.features.roomprofile.polls.RoomPollsViewModel
|
import im.vector.app.features.roomprofile.polls.RoomPollsViewModel
|
||||||
|
import im.vector.app.features.roomprofile.polls.detail.ui.RoomPollDetailViewModel
|
||||||
import im.vector.app.features.roomprofile.settings.RoomSettingsViewModel
|
import im.vector.app.features.roomprofile.settings.RoomSettingsViewModel
|
||||||
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel
|
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel
|
||||||
import im.vector.app.features.roomprofile.uploads.RoomUploadsViewModel
|
import im.vector.app.features.roomprofile.uploads.RoomUploadsViewModel
|
||||||
|
@ -703,4 +704,9 @@ interface MavericksViewModelModule {
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@MavericksViewModelKey(RoomPollsViewModel::class)
|
@MavericksViewModelKey(RoomPollsViewModel::class)
|
||||||
fun roomPollsViewModelFactory(factory: RoomPollsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
fun roomPollsViewModelFactory(factory: RoomPollsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@MavericksViewModelKey(RoomPollDetailViewModel::class)
|
||||||
|
fun roomPollDetailViewModelFactory(factory: RoomPollDetailViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,7 +255,11 @@ class MessageItemFactory @Inject constructor(
|
||||||
attributes: AbsMessageItem.Attributes,
|
attributes: AbsMessageItem.Attributes,
|
||||||
isEnded: Boolean,
|
isEnded: Boolean,
|
||||||
): PollItem {
|
): PollItem {
|
||||||
val pollViewState = pollItemViewStateFactory.create(pollContent, informationData)
|
val pollViewState = pollItemViewStateFactory.create(
|
||||||
|
pollContent = pollContent,
|
||||||
|
pollResponseData = informationData.pollResponseAggregatedSummary,
|
||||||
|
isSent = informationData.sendState.isSent(),
|
||||||
|
)
|
||||||
|
|
||||||
return PollItem_()
|
return PollItem_()
|
||||||
.attributes(attributes)
|
.attributes(attributes)
|
||||||
|
|
|
@ -18,9 +18,8 @@ package im.vector.app.features.home.room.detail.timeline.factory
|
||||||
|
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.PollResponseData
|
import im.vector.app.features.home.room.detail.timeline.item.PollResponseData
|
||||||
import im.vector.app.features.poll.PollViewState
|
import im.vector.app.features.poll.PollItemViewState
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.PollCreationInfo
|
import org.matrix.android.sdk.api.session.room.model.message.PollCreationInfo
|
||||||
|
@ -33,27 +32,27 @@ class PollItemViewStateFactory @Inject constructor(
|
||||||
|
|
||||||
fun create(
|
fun create(
|
||||||
pollContent: MessagePollContent,
|
pollContent: MessagePollContent,
|
||||||
informationData: MessageInformationData,
|
pollResponseData: PollResponseData?,
|
||||||
): PollViewState {
|
isSent: Boolean,
|
||||||
|
): PollItemViewState {
|
||||||
val pollCreationInfo = pollContent.getBestPollCreationInfo()
|
val pollCreationInfo = pollContent.getBestPollCreationInfo()
|
||||||
|
|
||||||
val question = pollCreationInfo?.question?.getBestQuestion().orEmpty()
|
val question = pollCreationInfo?.question?.getBestQuestion().orEmpty()
|
||||||
|
|
||||||
val pollResponseSummary = informationData.pollResponseAggregatedSummary
|
val totalVotes = pollResponseData?.totalVotes ?: 0
|
||||||
val totalVotes = pollResponseSummary?.totalVotes ?: 0
|
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
!informationData.sendState.isSent() -> {
|
!isSent -> {
|
||||||
createSendingPollViewState(question, pollCreationInfo)
|
createSendingPollViewState(question, pollCreationInfo)
|
||||||
}
|
}
|
||||||
informationData.pollResponseAggregatedSummary?.isClosed.orFalse() -> {
|
pollResponseData?.isClosed.orFalse() -> {
|
||||||
createEndedPollViewState(question, pollCreationInfo, pollResponseSummary, totalVotes)
|
createEndedPollViewState(question, pollCreationInfo, pollResponseData, totalVotes)
|
||||||
}
|
}
|
||||||
pollContent.getBestPollCreationInfo()?.isUndisclosed().orFalse() -> {
|
pollContent.getBestPollCreationInfo()?.isUndisclosed().orFalse() -> {
|
||||||
createUndisclosedPollViewState(question, pollCreationInfo, pollResponseSummary)
|
createUndisclosedPollViewState(question, pollCreationInfo, pollResponseData)
|
||||||
}
|
}
|
||||||
informationData.pollResponseAggregatedSummary?.myVote?.isNotEmpty().orFalse() -> {
|
pollResponseData?.myVote?.isNotEmpty().orFalse() -> {
|
||||||
createVotedPollViewState(question, pollCreationInfo, pollResponseSummary, totalVotes)
|
createVotedPollViewState(question, pollCreationInfo, pollResponseData, totalVotes)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
createReadyPollViewState(question, pollCreationInfo, totalVotes)
|
createReadyPollViewState(question, pollCreationInfo, totalVotes)
|
||||||
|
@ -61,8 +60,8 @@ class PollItemViewStateFactory @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSendingPollViewState(question: String, pollCreationInfo: PollCreationInfo?): PollViewState {
|
private fun createSendingPollViewState(question: String, pollCreationInfo: PollCreationInfo?): PollItemViewState {
|
||||||
return PollViewState(
|
return PollItemViewState(
|
||||||
question = question,
|
question = question,
|
||||||
votesStatus = stringProvider.getString(R.string.poll_no_votes_cast),
|
votesStatus = stringProvider.getString(R.string.poll_no_votes_cast),
|
||||||
canVote = false,
|
canVote = false,
|
||||||
|
@ -73,51 +72,51 @@ class PollItemViewStateFactory @Inject constructor(
|
||||||
private fun createEndedPollViewState(
|
private fun createEndedPollViewState(
|
||||||
question: String,
|
question: String,
|
||||||
pollCreationInfo: PollCreationInfo?,
|
pollCreationInfo: PollCreationInfo?,
|
||||||
pollResponseSummary: PollResponseData?,
|
pollResponseData: PollResponseData?,
|
||||||
totalVotes: Int,
|
totalVotes: Int,
|
||||||
): PollViewState {
|
): PollItemViewState {
|
||||||
val totalVotesText = if (pollResponseSummary?.hasEncryptedRelatedEvents.orFalse()) {
|
val totalVotesText = if (pollResponseData?.hasEncryptedRelatedEvents.orFalse()) {
|
||||||
stringProvider.getString(R.string.unable_to_decrypt_some_events_in_poll)
|
stringProvider.getString(R.string.unable_to_decrypt_some_events_in_poll)
|
||||||
} else {
|
} else {
|
||||||
stringProvider.getQuantityString(R.plurals.poll_total_vote_count_after_ended, totalVotes, totalVotes)
|
stringProvider.getQuantityString(R.plurals.poll_total_vote_count_after_ended, totalVotes, totalVotes)
|
||||||
}
|
}
|
||||||
return PollViewState(
|
return PollItemViewState(
|
||||||
question = question,
|
question = question,
|
||||||
votesStatus = totalVotesText,
|
votesStatus = totalVotesText,
|
||||||
canVote = false,
|
canVote = false,
|
||||||
optionViewStates = pollOptionViewStateFactory.createPollEndedOptions(pollCreationInfo, pollResponseSummary),
|
optionViewStates = pollOptionViewStateFactory.createPollEndedOptions(pollCreationInfo, pollResponseData),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createUndisclosedPollViewState(
|
private fun createUndisclosedPollViewState(
|
||||||
question: String,
|
question: String,
|
||||||
pollCreationInfo: PollCreationInfo?,
|
pollCreationInfo: PollCreationInfo?,
|
||||||
pollResponseSummary: PollResponseData?
|
pollResponseData: PollResponseData?
|
||||||
): PollViewState {
|
): PollItemViewState {
|
||||||
return PollViewState(
|
return PollItemViewState(
|
||||||
question = question,
|
question = question,
|
||||||
votesStatus = stringProvider.getString(R.string.poll_undisclosed_not_ended),
|
votesStatus = stringProvider.getString(R.string.poll_undisclosed_not_ended),
|
||||||
canVote = true,
|
canVote = true,
|
||||||
optionViewStates = pollOptionViewStateFactory.createPollUndisclosedOptions(pollCreationInfo, pollResponseSummary),
|
optionViewStates = pollOptionViewStateFactory.createPollUndisclosedOptions(pollCreationInfo, pollResponseData),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createVotedPollViewState(
|
private fun createVotedPollViewState(
|
||||||
question: String,
|
question: String,
|
||||||
pollCreationInfo: PollCreationInfo?,
|
pollCreationInfo: PollCreationInfo?,
|
||||||
pollResponseSummary: PollResponseData?,
|
pollResponseData: PollResponseData?,
|
||||||
totalVotes: Int
|
totalVotes: Int
|
||||||
): PollViewState {
|
): PollItemViewState {
|
||||||
val totalVotesText = if (pollResponseSummary?.hasEncryptedRelatedEvents.orFalse()) {
|
val totalVotesText = if (pollResponseData?.hasEncryptedRelatedEvents.orFalse()) {
|
||||||
stringProvider.getString(R.string.unable_to_decrypt_some_events_in_poll)
|
stringProvider.getString(R.string.unable_to_decrypt_some_events_in_poll)
|
||||||
} else {
|
} else {
|
||||||
stringProvider.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_voted, totalVotes, totalVotes)
|
stringProvider.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_voted, totalVotes, totalVotes)
|
||||||
}
|
}
|
||||||
return PollViewState(
|
return PollItemViewState(
|
||||||
question = question,
|
question = question,
|
||||||
votesStatus = totalVotesText,
|
votesStatus = totalVotesText,
|
||||||
canVote = true,
|
canVote = true,
|
||||||
optionViewStates = pollOptionViewStateFactory.createPollVotedOptions(pollCreationInfo, pollResponseSummary),
|
optionViewStates = pollOptionViewStateFactory.createPollVotedOptions(pollCreationInfo, pollResponseData),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,13 +124,13 @@ class PollItemViewStateFactory @Inject constructor(
|
||||||
question: String,
|
question: String,
|
||||||
pollCreationInfo: PollCreationInfo?,
|
pollCreationInfo: PollCreationInfo?,
|
||||||
totalVotes: Int
|
totalVotes: Int
|
||||||
): PollViewState {
|
): PollItemViewState {
|
||||||
val totalVotesText = if (totalVotes == 0) {
|
val totalVotesText = if (totalVotes == 0) {
|
||||||
stringProvider.getString(R.string.poll_no_votes_cast)
|
stringProvider.getString(R.string.poll_no_votes_cast)
|
||||||
} else {
|
} else {
|
||||||
stringProvider.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_not_voted, totalVotes, totalVotes)
|
stringProvider.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_not_voted, totalVotes, totalVotes)
|
||||||
}
|
}
|
||||||
return PollViewState(
|
return PollItemViewState(
|
||||||
question = question,
|
question = question,
|
||||||
votesStatus = totalVotesText,
|
votesStatus = totalVotesText,
|
||||||
canVote = true,
|
canVote = true,
|
||||||
|
|
|
@ -18,7 +18,7 @@ package im.vector.app.features.poll
|
||||||
|
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState
|
import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState
|
||||||
|
|
||||||
data class PollViewState(
|
data class PollItemViewState(
|
||||||
val question: String,
|
val question: String,
|
||||||
val votesStatus: String,
|
val votesStatus: String,
|
||||||
val canVote: Boolean,
|
val canVote: Boolean,
|
|
@ -16,14 +16,9 @@
|
||||||
|
|
||||||
package im.vector.app.features.roomprofile.polls.detail.ui
|
package im.vector.app.features.roomprofile.polls.detail.ui
|
||||||
|
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState
|
import im.vector.app.features.poll.PollItemViewState
|
||||||
|
|
||||||
data class RoomPollDetail(
|
data class RoomPollDetail(
|
||||||
val eventId: String,
|
|
||||||
val question: String,
|
|
||||||
val canVote: Boolean,
|
|
||||||
val votesStatusSummary: String,
|
|
||||||
val optionViewStates: List<PollOptionViewState>,
|
|
||||||
val hasBeenEdited: Boolean,
|
|
||||||
val isEnded: Boolean,
|
val isEnded: Boolean,
|
||||||
|
val pollItemViewState: PollItemViewState,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.detail.ui
|
|
||||||
|
|
||||||
import im.vector.app.core.platform.VectorViewModelAction
|
|
||||||
|
|
||||||
sealed interface RoomPollDetailAction : VectorViewModelAction {
|
|
||||||
|
|
||||||
}
|
|
|
@ -17,22 +17,29 @@
|
||||||
package im.vector.app.features.roomprofile.polls.detail.ui
|
package im.vector.app.features.roomprofile.polls.detail.ui
|
||||||
|
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.PollItem_
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RoomPollDetailController @Inject constructor()
|
class RoomPollDetailController @Inject constructor() : TypedEpoxyController<RoomPollDetailViewState>() {
|
||||||
: TypedEpoxyController<RoomPollDetailViewState>() {
|
|
||||||
|
interface Callback {
|
||||||
|
fun vote(pollId: String, optionId: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
var callback: Callback? = null
|
||||||
|
|
||||||
override fun buildModels(viewState: RoomPollDetailViewState?) {
|
override fun buildModels(viewState: RoomPollDetailViewState?) {
|
||||||
viewState ?: return
|
val pollDetail = viewState?.pollDetail ?: return
|
||||||
|
val pollItemViewState = pollDetail.pollItemViewState
|
||||||
|
val host = this
|
||||||
|
|
||||||
PollItem_()
|
roomPollDetailItem {
|
||||||
/*
|
id(viewState.pollId)
|
||||||
.eventId(pollSummary.id)
|
eventId(viewState.pollId)
|
||||||
.pollQuestion(pollSummary.title.toEpoxyCharSequence())
|
question(pollItemViewState.question)
|
||||||
.canVote(viewState.canVoteSelectedPoll())
|
canVote(pollItemViewState.canVote)
|
||||||
.optionViewStates(pollSummary.optionViewStates)
|
votesStatus(pollItemViewState.votesStatus)
|
||||||
.ended(viewState.canVoteSelectedPoll().not())
|
optionViewStates(pollItemViewState.optionViewStates.orEmpty())
|
||||||
*/
|
callback(host.callback)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ class RoomPollDetailFragment : VectorBaseFragment<FragmentRoomPollDetailBinding>
|
||||||
roomPollDetailController,
|
roomPollDetailController,
|
||||||
hasFixedSize = true,
|
hasFixedSize = true,
|
||||||
)
|
)
|
||||||
|
// TODO setup callback in controller for vote action
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
@ -75,15 +76,13 @@ class RoomPollDetailFragment : VectorBaseFragment<FragmentRoomPollDetailBinding>
|
||||||
|
|
||||||
setupToolbar(views.roomPollDetailToolbar)
|
setupToolbar(views.roomPollDetailToolbar)
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.allowBack()
|
.allowBack(useCross = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
state.pollDetail ?: return@withState
|
state.pollDetail ?: return@withState
|
||||||
|
|
||||||
// TODO should we update the title when the poll status changes?
|
// TODO should we update the title when the poll status changes?
|
||||||
setupToolbar(state.pollDetail.isEnded)
|
setupToolbar(state.pollDetail.isEnded)
|
||||||
|
roomPollDetailController.setData(state)
|
||||||
// TODO update data of the controller
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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.detail.ui
|
||||||
|
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
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.VectorEpoxyHolder
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.item.PollOptionView
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState
|
||||||
|
|
||||||
|
@EpoxyModelClass
|
||||||
|
abstract class RoomPollDetailItem : VectorEpoxyModel<RoomPollDetailItem.Holder>(R.layout.item_timeline_event_poll) {
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var question: String? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var callback: RoomPollDetailController.Callback? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var eventId: String? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var canVote: Boolean = false
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var votesStatus: String? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
lateinit var optionViewStates: List<PollOptionViewState>
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var ended: Boolean = false
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
super.bind(holder)
|
||||||
|
|
||||||
|
holder.questionTextView.text = question
|
||||||
|
holder.votesStatusTextView.text = votesStatus
|
||||||
|
holder.optionsContainer.removeAllViews()
|
||||||
|
holder.optionsContainer.isVisible = optionViewStates.isNotEmpty()
|
||||||
|
for (option in optionViewStates) {
|
||||||
|
val optionView = PollOptionView(holder.view.context)
|
||||||
|
holder.optionsContainer.addView(optionView)
|
||||||
|
optionView.render(option)
|
||||||
|
optionView.setOnClickListener { onOptionClicked(option) }
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.endedPollTextView.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onOptionClicked(optionViewState: PollOptionViewState) {
|
||||||
|
val relatedEventId = eventId
|
||||||
|
|
||||||
|
if (canVote && relatedEventId != null) {
|
||||||
|
callback?.vote(pollId = relatedEventId, optionId = optionViewState.optionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
val questionTextView by bind<TextView>(R.id.questionTextView)
|
||||||
|
val optionsContainer by bind<LinearLayout>(R.id.optionsContainer)
|
||||||
|
val votesStatusTextView by bind<TextView>(R.id.optionsVotesStatusTextView)
|
||||||
|
val endedPollTextView by bind<TextView>(R.id.endedPollTextView)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* 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.detail.ui
|
||||||
|
|
||||||
|
import im.vector.app.core.extensions.getVectorLastMessageContent
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.factory.PollItemViewStateFactory
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.helper.PollResponseDataFactory
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
// TODO add unit tests
|
||||||
|
class RoomPollDetailMapper @Inject constructor(
|
||||||
|
private val pollResponseDataFactory: PollResponseDataFactory,
|
||||||
|
private val pollItemViewStateFactory: PollItemViewStateFactory,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun map(timelineEvent: TimelineEvent): RoomPollDetail? {
|
||||||
|
val eventId = timelineEvent.root.eventId.orEmpty()
|
||||||
|
val result = runCatching {
|
||||||
|
val content = timelineEvent.getVectorLastMessageContent()
|
||||||
|
val pollResponseData = pollResponseDataFactory.create(timelineEvent)
|
||||||
|
return if (eventId.isNotEmpty() && content is MessagePollContent) {
|
||||||
|
// we assume poll message has been sent here
|
||||||
|
val pollItemViewState = pollItemViewStateFactory.create(
|
||||||
|
pollContent = content,
|
||||||
|
pollResponseData = pollResponseData,
|
||||||
|
isSent = true,
|
||||||
|
)
|
||||||
|
RoomPollDetail(
|
||||||
|
isEnded = pollResponseData?.isClosed == true,
|
||||||
|
pollItemViewState = pollItemViewState,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Timber.w("missing mandatory info about poll event with id=$eventId")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isFailure) {
|
||||||
|
Timber.w("failed to map event with id $eventId")
|
||||||
|
}
|
||||||
|
return result.getOrNull()
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,16 +16,33 @@
|
||||||
|
|
||||||
package im.vector.app.features.roomprofile.polls.detail.ui
|
package im.vector.app.features.roomprofile.polls.detail.ui
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.event.GetTimelineEventUseCase
|
import im.vector.app.core.event.GetTimelineEventUseCase
|
||||||
|
import im.vector.app.core.platform.EmptyAction
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.features.auth.ReAuthState
|
||||||
|
import im.vector.app.features.auth.ReAuthViewModel
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
class RoomPollDetailViewModel @AssistedInject constructor(
|
class RoomPollDetailViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: RoomPollDetailViewState,
|
@Assisted initialState: RoomPollDetailViewState,
|
||||||
private val getTimelineEventUseCase: GetTimelineEventUseCase,
|
private val getTimelineEventUseCase: GetTimelineEventUseCase,
|
||||||
) : VectorViewModel<RoomPollDetailViewState, RoomPollDetailAction, RoomPollDetailViewEvent>(initialState) {
|
private val roomPollDetailMapper: RoomPollDetailMapper,
|
||||||
|
) : VectorViewModel<RoomPollDetailViewState, EmptyAction, RoomPollDetailViewEvent>(initialState) {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory : MavericksAssistedViewModelFactory<RoomPollDetailViewModel, RoomPollDetailViewState> {
|
||||||
|
override fun create(initialState: RoomPollDetailViewState): RoomPollDetailViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MavericksViewModelFactory<RoomPollDetailViewModel, RoomPollDetailViewState> by hiltMavericksViewModelFactory()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
observePollDetails(
|
observePollDetails(
|
||||||
|
@ -36,10 +53,12 @@ class RoomPollDetailViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
private fun observePollDetails(pollId: String, roomId: String) {
|
private fun observePollDetails(pollId: String, roomId: String) {
|
||||||
getTimelineEventUseCase.execute(roomId = roomId, eventId = pollId)
|
getTimelineEventUseCase.execute(roomId = roomId, eventId = pollId)
|
||||||
|
.map { roomPollDetailMapper.map(it) }
|
||||||
|
.onEach { setState { copy(pollDetail = it) } }
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handle(action: RoomPollDetailAction) {
|
override fun handle(action: EmptyAction) {
|
||||||
// TODO handle go to timeline action
|
// do nothing for now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.app.features.roomprofile.polls.detail.ui
|
package im.vector.app.features.roomprofile.polls.detail.ui
|
||||||
|
|
||||||
import com.airbnb.mvrx.MavericksState
|
import com.airbnb.mvrx.MavericksState
|
||||||
|
import im.vector.app.features.poll.PollItemViewState
|
||||||
|
|
||||||
data class RoomPollDetailViewState(
|
data class RoomPollDetailViewState(
|
||||||
val pollId: String,
|
val pollId: String,
|
||||||
|
|
|
@ -24,12 +24,13 @@
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/pollDetailRecyclerView"
|
android:id="@+id/pollDetailRecyclerView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:layout_marginTop="32dp"
|
android:layout_marginTop="32dp"
|
||||||
android:paddingHorizontal="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/appBarLayout"
|
app:layout_constraintTop_toBottomOf="@id/appBarLayout"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
tools:itemCount="1"
|
tools:itemCount="1"
|
||||||
tools:listitem="@layout/item_timeline_event_poll" />
|
tools:listitem="@layout/item_timeline_event_poll" />
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ package im.vector.app.features.home.room.detail.timeline.factory
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState
|
import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.PollVoteSummaryData
|
import im.vector.app.features.home.room.detail.timeline.item.PollVoteSummaryData
|
||||||
import im.vector.app.features.poll.PollViewState
|
import im.vector.app.features.poll.PollItemViewState
|
||||||
import im.vector.app.test.fakes.FakeStringProvider
|
import im.vector.app.test.fakes.FakeStringProvider
|
||||||
import im.vector.app.test.fixtures.PollFixture.A_MESSAGE_INFORMATION_DATA
|
import im.vector.app.test.fixtures.PollFixture.A_MESSAGE_INFORMATION_DATA
|
||||||
import im.vector.app.test.fixtures.PollFixture.A_POLL_CONTENT
|
import im.vector.app.test.fixtures.PollFixture.A_POLL_CONTENT
|
||||||
|
@ -57,7 +57,7 @@ class PollItemViewStateFactoryTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
pollViewState shouldBeEqualTo PollViewState(
|
pollViewState shouldBeEqualTo PollItemViewState(
|
||||||
question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "",
|
question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "",
|
||||||
votesStatus = fakeStringProvider.instance.getString(R.string.poll_no_votes_cast),
|
votesStatus = fakeStringProvider.instance.getString(R.string.poll_no_votes_cast),
|
||||||
canVote = false,
|
canVote = false,
|
||||||
|
@ -90,7 +90,7 @@ class PollItemViewStateFactoryTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
pollViewState shouldBeEqualTo PollViewState(
|
pollViewState shouldBeEqualTo PollItemViewState(
|
||||||
question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "",
|
question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "",
|
||||||
votesStatus = fakeStringProvider.instance.getQuantityString(R.plurals.poll_total_vote_count_after_ended, 0, 0),
|
votesStatus = fakeStringProvider.instance.getQuantityString(R.plurals.poll_total_vote_count_after_ended, 0, 0),
|
||||||
canVote = false,
|
canVote = false,
|
||||||
|
@ -155,7 +155,7 @@ class PollItemViewStateFactoryTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
pollViewState shouldBeEqualTo PollViewState(
|
pollViewState shouldBeEqualTo PollItemViewState(
|
||||||
question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "",
|
question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "",
|
||||||
votesStatus = fakeStringProvider.instance.getString(R.string.poll_undisclosed_not_ended),
|
votesStatus = fakeStringProvider.instance.getString(R.string.poll_undisclosed_not_ended),
|
||||||
canVote = true,
|
canVote = true,
|
||||||
|
@ -204,7 +204,7 @@ class PollItemViewStateFactoryTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
pollViewState shouldBeEqualTo PollViewState(
|
pollViewState shouldBeEqualTo PollItemViewState(
|
||||||
question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "",
|
question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "",
|
||||||
votesStatus = fakeStringProvider.instance.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_voted, 1, 1),
|
votesStatus = fakeStringProvider.instance.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_voted, 1, 1),
|
||||||
canVote = true,
|
canVote = true,
|
||||||
|
@ -286,7 +286,7 @@ class PollItemViewStateFactoryTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
pollViewState shouldBeEqualTo PollViewState(
|
pollViewState shouldBeEqualTo PollItemViewState(
|
||||||
question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "",
|
question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "",
|
||||||
votesStatus = fakeStringProvider.instance.getString(R.string.poll_no_votes_cast),
|
votesStatus = fakeStringProvider.instance.getString(R.string.poll_no_votes_cast),
|
||||||
canVote = true,
|
canVote = true,
|
||||||
|
|
Loading…
Reference in a new issue