Adding vote action from details screen

This commit is contained in:
Maxime NATUREL 2023-02-02 09:48:56 +01:00
parent 922b8092ac
commit f855a36022
7 changed files with 117 additions and 25 deletions

View file

@ -54,6 +54,7 @@ import im.vector.app.features.crypto.verification.SupportedVerificationMethodsPr
import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction
import im.vector.app.features.home.room.detail.error.RoomNotFound
import im.vector.app.features.home.room.detail.location.RedactLiveLocationShareEventUseCase
import im.vector.app.features.home.room.detail.poll.VoteToPollUseCase
import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandler
import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
@ -90,7 +91,6 @@ import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.events.model.RelationType
import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode
import org.matrix.android.sdk.api.session.events.model.isAttachmentMessage
@ -154,6 +154,7 @@ class TimelineViewModel @AssistedInject constructor(
timelineFactory: TimelineFactory,
private val spaceStateHandler: SpaceStateHandler,
private val voiceBroadcastHelper: VoiceBroadcastHelper,
private val voteToPollUseCase: VoteToPollUseCase,
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener, LocationSharingServiceConnection.Callback {
@ -1235,15 +1236,11 @@ class TimelineViewModel @AssistedInject constructor(
private fun handleVoteToPoll(action: RoomDetailAction.VoteToPoll) {
if (room == null) return
// Do not allow to vote unsent local echo of the poll event
if (LocalEcho.isLocalEchoId(action.eventId)) return
// Do not allow to vote the same option twice
room.getTimelineEvent(action.eventId)?.let { pollTimelineEvent ->
val currentVote = pollTimelineEvent.annotations?.pollResponseSummary?.aggregatedContent?.myVote
if (currentVote != action.optionKey) {
room.sendService().voteToPoll(action.eventId, action.optionKey)
}
}
voteToPollUseCase.execute(
roomId = room.roomId,
pollEventId = action.eventId,
optionId = action.optionKey,
)
}
private fun handleEndPoll(eventId: String) {

View file

@ -0,0 +1,51 @@
/*
* 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.home.room.detail.poll
import im.vector.app.core.di.ActiveSessionHolder
import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.room.getTimelineEvent
import javax.inject.Inject
// TODO add unit tests
class VoteToPollUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
) {
fun execute(roomId: String, pollEventId: String, optionId: String) {
// Do not allow to vote unsent local echo of the poll event
if (LocalEcho.isLocalEchoId(pollEventId)) return
val room = activeSessionHolder.getActiveSession()
.roomService()
.getRoom(roomId)
room?.getTimelineEvent(pollEventId)?.let { pollTimelineEvent ->
val currentVote = pollTimelineEvent
.annotations
?.pollResponseSummary
?.aggregatedContent
?.myVote
if (currentVote != optionId) {
room.sendService().voteToPoll(
pollEventId = pollEventId,
answerId = optionId
)
}
}
}
}

View file

@ -0,0 +1,23 @@
/*
* 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.detail.ui
import im.vector.app.core.platform.VectorViewModelAction
sealed interface RoomPollDetailAction : VectorViewModelAction {
data class Vote(val pollEventId: String, val optionId: String) : RoomPollDetailAction
}

View file

@ -22,7 +22,7 @@ import javax.inject.Inject
class RoomPollDetailController @Inject constructor() : TypedEpoxyController<RoomPollDetailViewState>() {
interface Callback {
fun vote(pollId: String, optionId: String)
fun vote(pollEventId: String, optionId: String)
}
var callback: Callback? = null

View file

@ -40,7 +40,9 @@ data class RoomPollDetailArgs(
) : Parcelable
@AndroidEntryPoint
class RoomPollDetailFragment : VectorBaseFragment<FragmentRoomPollDetailBinding>() {
class RoomPollDetailFragment :
VectorBaseFragment<FragmentRoomPollDetailBinding>(),
RoomPollDetailController.Callback {
@Inject lateinit var roomPollDetailController: RoomPollDetailController
@ -54,17 +56,22 @@ class RoomPollDetailFragment : VectorBaseFragment<FragmentRoomPollDetailBinding>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupDetailView()
// TODO add link to go to timeline message + create a ViewNavigator
}
override fun onDestroyView() {
roomPollDetailController.callback = null
views.pollDetailRecyclerView.cleanup()
super.onDestroyView()
}
private fun setupDetailView() {
roomPollDetailController.callback = this
views.pollDetailRecyclerView.configureWith(
roomPollDetailController,
hasFixedSize = true,
)
// TODO setup callback in controller for vote action
}
override fun onDestroyView() {
views.pollDetailRecyclerView.cleanup()
super.onDestroyView()
}
private fun setupToolbar(isEnded: Boolean? = null) {
@ -85,4 +92,8 @@ class RoomPollDetailFragment : VectorBaseFragment<FragmentRoomPollDetailBinding>
setupToolbar(state.pollDetail.isEnded)
roomPollDetailController.setData(state)
}
override fun vote(pollEventId: String, optionId: String) {
viewModel.handle(RoomPollDetailAction.Vote(pollEventId = pollEventId, optionId = optionId))
}
}

View file

@ -72,7 +72,7 @@ abstract class RoomPollDetailItem : VectorEpoxyModel<RoomPollDetailItem.Holder>(
val relatedEventId = eventId
if (canVote && relatedEventId != null) {
callback?.vote(pollId = relatedEventId, optionId = optionViewState.optionId)
callback?.vote(pollEventId = relatedEventId, optionId = optionViewState.optionId)
}
}

View file

@ -23,19 +23,19 @@ 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.platform.EmptyAction
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.auth.ReAuthState
import im.vector.app.features.auth.ReAuthViewModel
import im.vector.app.features.home.room.detail.poll.VoteToPollUseCase
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
// TODO add unit tests
class RoomPollDetailViewModel @AssistedInject constructor(
@Assisted initialState: RoomPollDetailViewState,
private val getTimelineEventUseCase: GetTimelineEventUseCase,
private val roomPollDetailMapper: RoomPollDetailMapper,
) : VectorViewModel<RoomPollDetailViewState, EmptyAction, RoomPollDetailViewEvent>(initialState) {
private val voteToPollUseCase: VoteToPollUseCase,
) : VectorViewModel<RoomPollDetailViewState, RoomPollDetailAction, RoomPollDetailViewEvent>(initialState) {
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<RoomPollDetailViewModel, RoomPollDetailViewState> {
@ -58,7 +58,17 @@ class RoomPollDetailViewModel @AssistedInject constructor(
.launchIn(viewModelScope)
}
override fun handle(action: EmptyAction) {
// do nothing for now
override fun handle(action: RoomPollDetailAction) {
when (action) {
is RoomPollDetailAction.Vote -> handleVote(action)
}
}
private fun handleVote(vote: RoomPollDetailAction.Vote) = withState { state ->
voteToPollUseCase.execute(
roomId = state.roomId,
pollEventId = vote.pollEventId,
optionId = vote.optionId,
)
}
}