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