diff --git a/changelog.d/7864.wip b/changelog.d/7864.wip
new file mode 100644
index 0000000000..4dc55708be
--- /dev/null
+++ b/changelog.d/7864.wip
@@ -0,0 +1 @@
+[Poll] Render active polls list of a room
diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 8c5874f233..0bd7d0d97e 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -2335,6 +2335,7 @@
- "One person"
- "%1$d people"
+ Poll history
Uploads
Leave Room
Leave
@@ -3190,6 +3191,8 @@
Voters see results as soon as they have voted
Closed poll
Results are only revealed when you end the poll
+ Active polls
+ There are no active polls in this room
Share location
diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
index d22ab51e7a..911bbfa4a3 100644
--- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
+++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
@@ -84,6 +84,7 @@ import im.vector.app.features.roomprofile.banned.RoomBannedMemberListViewModel
import im.vector.app.features.roomprofile.members.RoomMemberListViewModel
import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsViewModel
import im.vector.app.features.roomprofile.permissions.RoomPermissionsViewModel
+import im.vector.app.features.roomprofile.polls.RoomPollsViewModel
import im.vector.app.features.roomprofile.settings.RoomSettingsViewModel
import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel
import im.vector.app.features.roomprofile.uploads.RoomUploadsViewModel
@@ -697,4 +698,9 @@ interface MavericksViewModelModule {
@IntoMap
@MavericksViewModelKey(SetLinkViewModel::class)
fun setLinkViewModelFactory(factory: SetLinkViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
+
+ @Binds
+ @IntoMap
+ @MavericksViewModelKey(RoomPollsViewModel::class)
+ fun roomPollsViewModelFactory(factory: RoomPollsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
index 526d676dee..3c37c92650 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
@@ -36,6 +36,7 @@ import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment
import im.vector.app.features.roomprofile.members.RoomMemberListFragment
import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsFragment
import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment
+import im.vector.app.features.roomprofile.polls.RoomPollsFragment
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment
import im.vector.lib.core.utils.compat.getParcelableCompat
@@ -98,6 +99,7 @@ class RoomProfileActivity :
RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings()
RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias()
RoomProfileSharedAction.OpenRoomPermissionsSettings -> openRoomPermissions()
+ RoomProfileSharedAction.OpenRoomPolls -> openRoomPolls()
RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads()
RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers()
RoomProfileSharedAction.OpenRoomNotificationSettings -> openRoomNotificationSettings()
@@ -126,6 +128,10 @@ class RoomProfileActivity :
finish()
}
+ private fun openRoomPolls() {
+ addFragmentToBackstack(views.simpleFragmentContainer, RoomPollsFragment::class.java, roomProfileArgs)
+ }
+
private fun openRoomUploads() {
addFragmentToBackstack(views.simpleFragmentContainer, RoomUploadsFragment::class.java, roomProfileArgs)
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
index eb43a345f2..30bd6c7ed3 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
@@ -18,6 +18,7 @@
package im.vector.app.features.roomprofile
import com.airbnb.epoxy.TypedEpoxyController
+import im.vector.app.BuildConfig
import im.vector.app.R
import im.vector.app.core.epoxy.expandableTextItem
import im.vector.app.core.epoxy.profiles.buildProfileAction
@@ -56,6 +57,7 @@ class RoomProfileController @Inject constructor(
fun onMemberListClicked()
fun onBannedMemberListClicked()
fun onNotificationsClicked()
+ fun onPollHistoryClicked()
fun onUploadsClicked()
fun createShortcut()
fun onSettingsClicked()
@@ -263,6 +265,15 @@ class RoomProfileController @Inject constructor(
action = { callback?.onBannedMemberListClicked() }
)
}
+ if (BuildConfig.DEBUG) {
+ // WIP, will be in release when related screens will be finished
+ buildProfileAction(
+ id = "poll_history",
+ title = stringProvider.getString(R.string.room_profile_section_more_polls),
+ icon = R.drawable.ic_attachment_poll,
+ action = { callback?.onPollHistoryClicked() }
+ )
+ }
buildProfileAction(
id = "uploads",
title = stringProvider.getString(R.string.room_profile_section_more_uploads),
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
index f4394111ab..51885dbf39 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
@@ -269,6 +269,10 @@ class RoomProfileFragment :
roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomNotificationSettings)
}
+ override fun onPollHistoryClicked() {
+ roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomPolls)
+ }
+
override fun onUploadsClicked() {
roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomUploads)
}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt
index 7d62bb86a1..b243ceb206 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt
@@ -25,6 +25,7 @@ sealed class RoomProfileSharedAction : VectorSharedAction {
object OpenRoomSettings : RoomProfileSharedAction()
object OpenRoomAliasesSettings : RoomProfileSharedAction()
object OpenRoomPermissionsSettings : RoomProfileSharedAction()
+ object OpenRoomPolls : RoomProfileSharedAction()
object OpenRoomUploads : RoomProfileSharedAction()
object OpenRoomMembers : RoomProfileSharedAction()
object OpenBannedRoomMembers : RoomProfileSharedAction()
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/GetPollsUseCase.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/GetPollsUseCase.kt
new file mode 100644
index 0000000000..d35d192e04
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/GetPollsUseCase.kt
@@ -0,0 +1,65 @@
+/*
+ * 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
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import javax.inject.Inject
+
+class GetPollsUseCase @Inject constructor() {
+
+ fun execute(filter: RoomPollsFilterType): Flow> {
+ // TODO unmock and add unit tests
+ return when (filter) {
+ RoomPollsFilterType.ACTIVE -> getActivePolls()
+ RoomPollsFilterType.ENDED -> emptyFlow()
+ }.map { it.sortedByDescending { poll -> poll.creationTimestamp } }
+ }
+
+ private fun getActivePolls(): Flow> {
+ return flowOf(
+ listOf(
+ PollSummary.ActivePoll(
+ id = "id1",
+ // 2022/06/28 UTC+1
+ creationTimestamp = 1656367200000,
+ title = "Which charity would you like to support?"
+ ),
+ PollSummary.ActivePoll(
+ id = "id2",
+ // 2022/06/26 UTC+1
+ creationTimestamp = 1656194400000,
+ title = "Which sport should the pupils do this year?"
+ ),
+ PollSummary.ActivePoll(
+ id = "id3",
+ // 2022/06/24 UTC+1
+ creationTimestamp = 1656021600000,
+ title = "What type of food should we have at the party?"
+ ),
+ PollSummary.ActivePoll(
+ id = "id4",
+ // 2022/06/22 UTC+1
+ creationTimestamp = 1655848800000,
+ title = "What film should we show at the end of the year party?"
+ ),
+ )
+ )
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/PollSummary.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/PollSummary.kt
new file mode 100644
index 0000000000..3eb45c6144
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/PollSummary.kt
@@ -0,0 +1,25 @@
+/*
+ * 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
+
+sealed interface PollSummary {
+ data class ActivePoll(
+ val id: String,
+ val creationTimestamp: Long,
+ val title: String,
+ ) : PollSummary
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsAction.kt
new file mode 100644
index 0000000000..5f074bdd6f
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsAction.kt
@@ -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
+
+import im.vector.app.core.platform.VectorViewModelAction
+
+sealed interface RoomPollsAction : VectorViewModelAction {
+ data class SetFilter(val filter: RoomPollsFilterType) : RoomPollsAction
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsFilterType.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsFilterType.kt
new file mode 100644
index 0000000000..39f1163536
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsFilterType.kt
@@ -0,0 +1,22 @@
+/*
+ * 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
+
+enum class RoomPollsFilterType {
+ ACTIVE,
+ ENDED,
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsFragment.kt
new file mode 100644
index 0000000000..5c150f4391
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsFragment.kt
@@ -0,0 +1,73 @@
+/*
+ * 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
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.airbnb.mvrx.args
+import com.airbnb.mvrx.fragmentViewModel
+import com.google.android.material.tabs.TabLayoutMediator
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
+import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.databinding.FragmentRoomPollsBinding
+import im.vector.app.features.roomprofile.RoomProfileArgs
+
+@AndroidEntryPoint
+class RoomPollsFragment : VectorBaseFragment() {
+
+ private val roomProfileArgs: RoomProfileArgs by args()
+
+ private val viewModel: RoomPollsViewModel by fragmentViewModel()
+
+ private var tabLayoutMediator: TabLayoutMediator? = null
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomPollsBinding {
+ return FragmentRoomPollsBinding.inflate(inflater, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ setupToolbar()
+ setupTabs()
+ }
+
+ override fun onDestroyView() {
+ views.roomPollsViewPager.adapter = null
+ tabLayoutMediator?.detach()
+ tabLayoutMediator = null
+ super.onDestroyView()
+ }
+
+ private fun setupToolbar() {
+ setupToolbar(views.roomPollsToolbar)
+ .allowBack()
+ }
+
+ private fun setupTabs() {
+ views.roomPollsViewPager.adapter = RoomPollsPagerAdapter(this)
+
+ tabLayoutMediator = TabLayoutMediator(views.roomPollsTabs, views.roomPollsViewPager) { tab, position ->
+ when (position) {
+ 0 -> tab.text = getString(R.string.room_polls_active)
+ }
+ }.also { it.attach() }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsPagerAdapter.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsPagerAdapter.kt
new file mode 100644
index 0000000000..5472782079
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsPagerAdapter.kt
@@ -0,0 +1,36 @@
+/*
+ * 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
+
+import androidx.fragment.app.Fragment
+import androidx.viewpager2.adapter.FragmentStateAdapter
+import im.vector.app.features.roomprofile.polls.active.RoomActivePollsFragment
+
+class RoomPollsPagerAdapter(
+ private val fragment: Fragment
+) : FragmentStateAdapter(fragment) {
+
+ override fun getItemCount() = 1
+
+ override fun createFragment(position: Int): Fragment {
+ return instantiateFragment(RoomActivePollsFragment::class.java.name)
+ }
+
+ private fun instantiateFragment(fragmentName: String): Fragment {
+ return fragment.childFragmentManager.fragmentFactory.instantiate(fragment.requireContext().classLoader, fragmentName)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewEvent.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewEvent.kt
new file mode 100644
index 0000000000..231123563a
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewEvent.kt
@@ -0,0 +1,21 @@
+/*
+ * 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
+
+import im.vector.app.core.platform.VectorViewEvents
+
+sealed class RoomPollsViewEvent : VectorViewEvents
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewModel.kt
new file mode 100644
index 0000000000..7bc06894fa
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewModel.kt
@@ -0,0 +1,63 @@
+/*
+ * 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
+
+import androidx.annotation.VisibleForTesting
+import com.airbnb.mvrx.MavericksViewModelFactory
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+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 kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+class RoomPollsViewModel @AssistedInject constructor(
+ @Assisted initialState: RoomPollsViewState,
+ private val getPollsUseCase: GetPollsUseCase,
+) : VectorViewModel(initialState) {
+
+ @AssistedFactory
+ interface Factory : MavericksAssistedViewModelFactory {
+ override fun create(initialState: RoomPollsViewState): RoomPollsViewModel
+ }
+
+ companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory()
+
+ @VisibleForTesting
+ var pollsCollectionJob: Job? = null
+
+ override fun handle(action: RoomPollsAction) {
+ when (action) {
+ is RoomPollsAction.SetFilter -> handleSetFilter(action.filter)
+ }
+ }
+
+ override fun onCleared() {
+ pollsCollectionJob = null
+ super.onCleared()
+ }
+
+ private fun handleSetFilter(filter: RoomPollsFilterType) {
+ pollsCollectionJob?.cancel()
+ pollsCollectionJob = getPollsUseCase.execute(filter)
+ .onEach { setState { copy(polls = it) } }
+ .launchIn(viewModelScope)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewState.kt
new file mode 100644
index 0000000000..74794c99b1
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/RoomPollsViewState.kt
@@ -0,0 +1,28 @@
+/*
+ * 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
+
+import com.airbnb.mvrx.MavericksState
+import im.vector.app.features.roomprofile.RoomProfileArgs
+
+data class RoomPollsViewState(
+ val roomId: String,
+ val polls: List = emptyList(),
+) : MavericksState {
+
+ constructor(roomProfileArgs: RoomProfileArgs) : this(roomId = roomProfileArgs.roomId)
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/active/ActivePollItem.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/active/ActivePollItem.kt
new file mode 100644
index 0000000000..35b1ecd6e1
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/active/ActivePollItem.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.active
+
+import android.widget.TextView
+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 ActivePollItem : VectorEpoxyModel(R.layout.item_poll) {
+
+ @EpoxyAttribute
+ lateinit var formattedDate: String
+
+ @EpoxyAttribute
+ lateinit var title: String
+
+ @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
+ var clickListener: ClickListener? = null
+
+ override fun bind(holder: Holder) {
+ super.bind(holder)
+ holder.view.onClick(clickListener)
+ holder.date.text = formattedDate
+ holder.title.text = title
+ }
+
+ class Holder : VectorEpoxyHolder() {
+ val date by bind(R.id.pollActiveDate)
+ val title by bind(R.id.pollActiveTitle)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/active/RoomActivePollsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/active/RoomActivePollsController.kt
new file mode 100644
index 0000000000..7a7c818693
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/active/RoomActivePollsController.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.active
+
+import com.airbnb.epoxy.TypedEpoxyController
+import im.vector.app.core.date.DateFormatKind
+import im.vector.app.core.date.VectorDateFormatter
+import im.vector.app.features.roomprofile.polls.PollSummary
+import javax.inject.Inject
+
+class RoomActivePollsController @Inject constructor(
+ val dateFormatter: VectorDateFormatter,
+) : TypedEpoxyController>() {
+
+ interface Listener {
+ fun onPollClicked(pollId: String)
+ }
+
+ var listener: Listener? = null
+
+ override fun buildModels(data: List?) {
+ if (data.isNullOrEmpty()) {
+ return
+ }
+
+ val host = this
+ for (poll in data) {
+ activePollItem {
+ id(poll.id)
+ formattedDate(host.dateFormatter.format(poll.creationTimestamp, DateFormatKind.TIMELINE_DAY_DIVIDER))
+ title(poll.title)
+ clickListener {
+ host.listener?.onPollClicked(poll.id)
+ }
+ }
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/active/RoomActivePollsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/active/RoomActivePollsFragment.kt
new file mode 100644
index 0000000000..61c7e961bd
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/active/RoomActivePollsFragment.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.active
+
+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 dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
+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.RoomPollsAction
+import im.vector.app.features.roomprofile.polls.RoomPollsFilterType
+import im.vector.app.features.roomprofile.polls.RoomPollsViewModel
+import timber.log.Timber
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class RoomActivePollsFragment :
+ VectorBaseFragment(),
+ RoomActivePollsController.Listener {
+
+ @Inject
+ lateinit var roomActivePollsController: RoomActivePollsController
+
+ 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()
+ }
+
+ private fun setupList() {
+ roomActivePollsController.listener = this
+ views.roomPollsList.configureWith(roomActivePollsController)
+ views.roomPollsEmptyTitle.text = getString(R.string.room_polls_active_no_item)
+ }
+
+ override fun onDestroyView() {
+ cleanUpList()
+ super.onDestroyView()
+ }
+
+ private fun cleanUpList() {
+ views.roomPollsList.cleanup()
+ roomActivePollsController.listener = null
+ }
+
+ override fun onResume() {
+ super.onResume()
+ viewModel.handle(RoomPollsAction.SetFilter(RoomPollsFilterType.ACTIVE))
+ }
+
+ override fun invalidate() = withState(viewModel) { viewState ->
+ renderList(viewState.polls.filterIsInstance(PollSummary.ActivePoll::class.java))
+ }
+
+ private fun renderList(polls: List) {
+ roomActivePollsController.setData(polls)
+ views.roomPollsEmptyTitle.isVisible = polls.isEmpty()
+ }
+
+ override fun onPollClicked(pollId: String) {
+ // TODO navigate to details
+ Timber.d("poll with id $pollId clicked")
+ }
+}
diff --git a/vector/src/main/res/layout/fragment_room_polls.xml b/vector/src/main/res/layout/fragment_room_polls.xml
new file mode 100644
index 0000000000..396d6fd8c5
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_room_polls.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/fragment_room_polls_list.xml b/vector/src/main/res/layout/fragment_room_polls_list.xml
new file mode 100644
index 0000000000..8eb27e5e00
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_room_polls_list.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/layout/item_poll.xml b/vector/src/main/res/layout/item_poll.xml
new file mode 100644
index 0000000000..956ecf9b3c
--- /dev/null
+++ b/vector/src/main/res/layout/item_poll.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/test/java/im/vector/app/features/roomprofile/polls/RoomPollsViewModelTest.kt b/vector/src/test/java/im/vector/app/features/roomprofile/polls/RoomPollsViewModelTest.kt
new file mode 100644
index 0000000000..0dce2dd6e0
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/roomprofile/polls/RoomPollsViewModelTest.kt
@@ -0,0 +1,76 @@
+/*
+ * 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
+
+import com.airbnb.mvrx.test.MavericksTestRule
+import im.vector.app.test.test
+import im.vector.app.test.testDispatcher
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import kotlinx.coroutines.flow.flowOf
+import org.amshove.kluent.shouldNotBeNull
+import org.junit.Rule
+import org.junit.Test
+
+private const val ROOM_ID = "room-id"
+
+class RoomPollsViewModelTest {
+
+ @get:Rule
+ val mavericksTestRule = MavericksTestRule(testDispatcher = testDispatcher)
+
+ private val fakeGetPollsUseCase = mockk()
+ private val initialState = RoomPollsViewState(ROOM_ID)
+
+ private fun createViewModel(): RoomPollsViewModel {
+ return RoomPollsViewModel(
+ initialState = initialState,
+ getPollsUseCase = fakeGetPollsUseCase,
+ )
+ }
+
+ @Test
+ fun `given SetFilter action when handle then useCase is called with given filter and viewState is updated`() {
+ // Given
+ val filter = RoomPollsFilterType.ACTIVE
+ val action = RoomPollsAction.SetFilter(filter = filter)
+ val polls = listOf(givenAPollSummary())
+ every { fakeGetPollsUseCase.execute(any()) } returns flowOf(polls)
+ val viewModel = createViewModel()
+ val expectedViewState = initialState.copy(polls = polls)
+
+ // When
+ val viewModelTest = viewModel.test()
+ viewModel.pollsCollectionJob = null
+ viewModel.handle(action)
+
+ // Then
+ viewModelTest
+ .assertLatestState(expectedViewState)
+ .finish()
+ viewModel.pollsCollectionJob.shouldNotBeNull()
+ verify {
+ viewModel.pollsCollectionJob?.cancel()
+ fakeGetPollsUseCase.execute(filter)
+ }
+ }
+
+ private fun givenAPollSummary(): PollSummary {
+ return mockk()
+ }
+}