From 384e7f674d9ac1fad4e7f08a627b9ea35ed5c09e Mon Sep 17 00:00:00 2001 From: Maxime NATUREL <46314705+mnaturel@users.noreply.github.com> Date: Thu, 2 Feb 2023 15:55:43 +0100 Subject: [PATCH] Adding go to timeline event button --- .../src/main/res/values/strings.xml | 1 + .../domain/GetEndedPollEventIdUseCase.kt | 38 ++++++++++++++ .../polls/detail/ui/RoomPollDetail.kt | 1 + .../detail/ui/RoomPollDetailController.kt | 14 ++++++ .../polls/detail/ui/RoomPollDetailFragment.kt | 11 +++- .../polls/detail/ui/RoomPollDetailMapper.kt | 50 +++++++++++++++---- .../detail/ui/RoomPollDetailNavigator.kt | 36 +++++++++++++ .../detail/ui/RoomPollGoToTimelineItem.kt | 42 ++++++++++++++++ .../res/layout/item_poll_go_to_timeline.xml | 20 ++++++++ 9 files changed, 203 insertions(+), 10 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/domain/GetEndedPollEventIdUseCase.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailNavigator.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollGoToTimelineItem.kt create mode 100644 vector/src/main/res/layout/item_poll_go_to_timeline.xml diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index e690f06bbb..1e540b3360 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3207,6 +3207,7 @@ Displaying polls Load more polls Error fetching polls. + View poll in timeline Share location diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/domain/GetEndedPollEventIdUseCase.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/domain/GetEndedPollEventIdUseCase.kt new file mode 100644 index 0000000000..e32c1be963 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/domain/GetEndedPollEventIdUseCase.kt @@ -0,0 +1,38 @@ +/* + * 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.domain + +import im.vector.app.core.di.ActiveSessionHolder +import org.matrix.android.sdk.api.session.events.model.RelationType +import org.matrix.android.sdk.api.session.events.model.isPollEnd +import javax.inject.Inject + +// TODO add unit tests +class GetEndedPollEventIdUseCase @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, +) { + + fun execute(roomId: String, startPollEventId: String): String? { + return activeSessionHolder.getActiveSession() + .roomService() + .getRoom(roomId) + ?.timelineService() + ?.getTimelineEventsRelatedTo(RelationType.REFERENCE, startPollEventId) + ?.find { it.root.isPollEnd() } + ?.eventId + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetail.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetail.kt index 3389a5f473..91d52ca08d 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetail.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetail.kt @@ -20,5 +20,6 @@ import im.vector.app.features.poll.PollItemViewState data class RoomPollDetail( val isEnded: Boolean, + val endedPollEventId: String?, val pollItemViewState: PollItemViewState, ) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailController.kt index 61ef41b14f..d2c5dfb8b1 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailController.kt @@ -17,12 +17,14 @@ package im.vector.app.features.roomprofile.polls.detail.ui import com.airbnb.epoxy.TypedEpoxyController +import java.util.UUID import javax.inject.Inject class RoomPollDetailController @Inject constructor() : TypedEpoxyController() { interface Callback { fun vote(pollEventId: String, optionId: String) + fun goToTimelineEvent(eventId: String) } var callback: Callback? = null @@ -41,5 +43,17 @@ class RoomPollDetailController @Inject constructor() : TypedEpoxyController(), RoomPollDetailController.Callback { + @Inject lateinit var viewNavigator: RoomPollDetailNavigator @Inject lateinit var roomPollDetailController: RoomPollDetailController private val viewModel: RoomPollDetailViewModel by fragmentViewModel() @@ -58,7 +60,6 @@ class RoomPollDetailFragment : super.onViewCreated(view, savedInstanceState) setupToolbar(isEnded = roomPollDetailArgs.isEnded) setupDetailView() - // TODO add link to go to timeline message + create a ViewNavigator } override fun onDestroyView() { @@ -93,4 +94,12 @@ class RoomPollDetailFragment : override fun vote(pollEventId: String, optionId: String) { viewModel.handle(RoomPollDetailAction.Vote(pollEventId = pollEventId, optionId = optionId)) } + + override fun goToTimelineEvent(eventId: String) = withState(viewModel) { state -> + viewNavigator.goToTimelineEvent( + context = requireContext(), + roomId = state.roomId, + eventId = eventId, + ) + } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailMapper.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailMapper.kt index 931b38f243..7818703716 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailMapper.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailMapper.kt @@ -19,6 +19,9 @@ 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 im.vector.app.features.home.room.detail.timeline.item.PollResponseData +import im.vector.app.features.roomprofile.polls.detail.domain.GetEndedPollEventIdUseCase +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.timeline.TimelineEvent import timber.log.Timber @@ -28,6 +31,7 @@ import javax.inject.Inject class RoomPollDetailMapper @Inject constructor( private val pollResponseDataFactory: PollResponseDataFactory, private val pollItemViewStateFactory: PollItemViewStateFactory, + private val getEndedPollEventIdUseCase: GetEndedPollEventIdUseCase, ) { fun map(timelineEvent: TimelineEvent): RoomPollDetail? { @@ -36,16 +40,13 @@ class RoomPollDetailMapper @Inject constructor( 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, + val isPollEnded = pollResponseData?.isClosed.orFalse() + val endedPollEventId = getEndedPollEventId( + isPollEnded, + startPollEventId = eventId, + roomId = timelineEvent.roomId, ) + convertToRoomPollDetail(content, pollResponseData, isPollEnded, endedPollEventId) } else { Timber.w("missing mandatory info about poll event with id=$eventId") null @@ -57,4 +58,35 @@ class RoomPollDetailMapper @Inject constructor( } return result.getOrNull() } + + private fun convertToRoomPollDetail( + content: MessagePollContent, + pollResponseData: PollResponseData?, + isPollEnded: Boolean, + endedPollEventId: String?, + ): RoomPollDetail { + // we assume the poll has been sent + val pollItemViewState = pollItemViewStateFactory.create( + pollContent = content, + pollResponseData = pollResponseData, + isSent = true, + ) + return RoomPollDetail( + isEnded = isPollEnded, + pollItemViewState = pollItemViewState, + endedPollEventId = endedPollEventId, + ) + } + + private fun getEndedPollEventId( + isPollEnded: Boolean, + startPollEventId: String, + roomId: String, + ): String? { + return if (isPollEnded) { + getEndedPollEventIdUseCase.execute(startPollEventId = startPollEventId, roomId = roomId) + } else { + null + } + } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailNavigator.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailNavigator.kt new file mode 100644 index 0000000000..402b9c4468 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollDetailNavigator.kt @@ -0,0 +1,36 @@ +/* + * 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 android.content.Context +import im.vector.app.features.navigation.Navigator +import javax.inject.Inject + +// TODO add unit tests +class RoomPollDetailNavigator @Inject constructor( + private val navigator: Navigator, +) { + + fun goToTimelineEvent(context: Context, roomId: String, eventId: String) { + navigator.openRoom( + context = context, + roomId = roomId, + eventId = eventId, + buildTask = true, + ) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollGoToTimelineItem.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollGoToTimelineItem.kt new file mode 100644 index 0000000000..59a5539a4f --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/detail/ui/RoomPollGoToTimelineItem.kt @@ -0,0 +1,42 @@ +/* + * 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 android.widget.Button +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 RoomPollGoToTimelineItem : VectorEpoxyModel(R.layout.item_poll_go_to_timeline) { + + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) + var clickListener: ClickListener? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.goToTimelineButton.onClick(clickListener) + } + + class Holder : VectorEpoxyHolder() { + val goToTimelineButton by bind