diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 43507e60ce..6120398566 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3193,6 +3193,8 @@
     <string name="closed_poll_option_description">Results are only revealed when you end the poll</string>
     <string name="room_polls_active">Active polls</string>
     <string name="room_polls_active_no_item">There are no active polls in this room</string>
+    <string name="room_polls_ended">Past polls</string>
+    <string name="room_polls_ended_no_item">There are no past polls in this room</string>
 
     <!-- Location -->
     <string name="location_activity_title_static_sharing">Share location</string>
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
index d35d192e04..06915c7b6a 100644
--- 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
@@ -17,19 +17,16 @@
 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<List<PollSummary>> {
+    fun execute(): Flow<List<PollSummary>> {
         // TODO unmock and add unit tests
-        return when (filter) {
-            RoomPollsFilterType.ACTIVE -> getActivePolls()
-            RoomPollsFilterType.ENDED -> emptyFlow()
-        }.map { it.sortedByDescending { poll -> poll.creationTimestamp } }
+        return getActivePolls()
+                .map { it.sortedByDescending { poll -> poll.creationTimestamp } }
     }
 
     private fun getActivePolls(): Flow<List<PollSummary.ActivePoll>> {
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
index 3eb45c6144..939ee3ffa0 100644
--- 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
@@ -22,4 +22,10 @@ sealed interface PollSummary {
             val creationTimestamp: Long,
             val title: String,
     ) : PollSummary
+
+    data class EndedPoll(
+            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
index 5f074bdd6f..c18142a306 100644
--- 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
@@ -18,6 +18,4 @@ 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
-}
+sealed interface RoomPollsAction : VectorViewModelAction
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
index 5c150f4391..95aa5d0d95 100644
--- 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
@@ -67,6 +67,7 @@ class RoomPollsFragment : VectorBaseFragment<FragmentRoomPollsBinding>() {
         tabLayoutMediator = TabLayoutMediator(views.roomPollsTabs, views.roomPollsViewPager) { tab, position ->
             when (position) {
                 0 -> tab.text = getString(R.string.room_polls_active)
+                1 -> tab.text = getString(R.string.room_polls_ended)
             }
         }.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
index 5472782079..2dc6fd4369 100644
--- 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
@@ -19,15 +19,19 @@ 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
+import im.vector.app.features.roomprofile.polls.ended.RoomEndedPollsFragment
 
 class RoomPollsPagerAdapter(
         private val fragment: Fragment
 ) : FragmentStateAdapter(fragment) {
 
-    override fun getItemCount() = 1
+    override fun getItemCount() = 2
 
     override fun createFragment(position: Int): Fragment {
-        return instantiateFragment(RoomActivePollsFragment::class.java.name)
+        return when (position) {
+            0 -> instantiateFragment(RoomActivePollsFragment::class.java.name)
+            else -> instantiateFragment(RoomEndedPollsFragment::class.java.name)
+        }
     }
 
     private fun instantiateFragment(fragmentName: String): Fragment {
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
index 7bc06894fa..95cb4717ca 100644
--- 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
@@ -16,7 +16,6 @@
 
 package im.vector.app.features.roomprofile.polls
 
-import androidx.annotation.VisibleForTesting
 import com.airbnb.mvrx.MavericksViewModelFactory
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
@@ -24,7 +23,6 @@ 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
 
@@ -40,24 +38,17 @@ class RoomPollsViewModel @AssistedInject constructor(
 
     companion object : MavericksViewModelFactory<RoomPollsViewModel, RoomPollsViewState> by hiltMavericksViewModelFactory()
 
-    @VisibleForTesting
-    var pollsCollectionJob: Job? = null
-
-    override fun handle(action: RoomPollsAction) {
-        when (action) {
-            is RoomPollsAction.SetFilter -> handleSetFilter(action.filter)
-        }
+    init {
+        observePolls()
     }
 
-    override fun onCleared() {
-        pollsCollectionJob = null
-        super.onCleared()
-    }
-
-    private fun handleSetFilter(filter: RoomPollsFilterType) {
-        pollsCollectionJob?.cancel()
-        pollsCollectionJob = getPollsUseCase.execute(filter)
+    private fun observePolls() {
+        getPollsUseCase.execute()
                 .onEach { setState { copy(polls = it) } }
                 .launchIn(viewModelScope)
     }
+
+    override fun handle(action: RoomPollsAction) {
+        // do nothing for now
+    }
 }
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
index 61c7e961bd..dc735c79be 100644
--- 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
@@ -16,77 +16,19 @@
 
 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
+import im.vector.app.features.roomprofile.polls.list.RoomPollsListFragment
 
 @AndroidEntryPoint
-class RoomActivePollsFragment :
-        VectorBaseFragment<FragmentRoomPollsListBinding>(),
-        RoomActivePollsController.Listener {
+class RoomActivePollsFragment : RoomPollsListFragment() {
 
-    @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 getEmptyListTitle(): String {
+        return getString(R.string.room_polls_active_no_item)
     }
 
-    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<PollSummary.ActivePoll>) {
-        roomActivePollsController.setData(polls)
-        views.roomPollsEmptyTitle.isVisible = polls.isEmpty()
-    }
-
-    override fun onPollClicked(pollId: String) {
-        // TODO navigate to details
-        Timber.d("poll with id $pollId clicked")
+    override fun getRoomPollsFilter(): RoomPollsFilterType {
+        return RoomPollsFilterType.ACTIVE
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/polls/ended/RoomEndedPollsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/ended/RoomEndedPollsFragment.kt
new file mode 100644
index 0000000000..bad1a4e2da
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/ended/RoomEndedPollsFragment.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.ended
+
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
+import im.vector.app.features.roomprofile.polls.RoomPollsFilter
+import im.vector.app.features.roomprofile.polls.list.RoomPollsListFragment
+
+@AndroidEntryPoint
+class RoomEndedPollsFragment : RoomPollsListFragment() {
+
+    override fun getEmptyListTitle(): String {
+        return getString(R.string.room_polls_ended_no_item)
+    }
+
+    override fun getRoomPollsFilter(): RoomPollsFilter {
+        return RoomPollsFilter.ENDED
+    }
+}
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/list/RoomPollItem.kt
similarity index 90%
rename from vector/src/main/java/im/vector/app/features/roomprofile/polls/active/ActivePollItem.kt
rename to vector/src/main/java/im/vector/app/features/roomprofile/polls/list/RoomPollItem.kt
index 35b1ecd6e1..ac2b9cf3c0 100644
--- 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/list/RoomPollItem.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package im.vector.app.features.roomprofile.polls.active
+package im.vector.app.features.roomprofile.polls.list
 
 import android.widget.TextView
 import com.airbnb.epoxy.EpoxyAttribute
@@ -26,7 +26,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
 import im.vector.app.core.epoxy.onClick
 
 @EpoxyModelClass
-abstract class ActivePollItem : VectorEpoxyModel<ActivePollItem.Holder>(R.layout.item_poll) {
+abstract class RoomPollItem : VectorEpoxyModel<RoomPollItem.Holder>(R.layout.item_poll) {
 
     @EpoxyAttribute
     lateinit var formattedDate: String
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/list/RoomPollsController.kt
similarity index 50%
rename from vector/src/main/java/im/vector/app/features/roomprofile/polls/active/RoomActivePollsController.kt
rename to vector/src/main/java/im/vector/app/features/roomprofile/polls/list/RoomPollsController.kt
index 7a7c818693..e24241f0af 100644
--- 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/list/RoomPollsController.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package im.vector.app.features.roomprofile.polls.active
+package im.vector.app.features.roomprofile.polls.list
 
 import com.airbnb.epoxy.TypedEpoxyController
 import im.vector.app.core.date.DateFormatKind
@@ -22,9 +22,9 @@ import im.vector.app.core.date.VectorDateFormatter
 import im.vector.app.features.roomprofile.polls.PollSummary
 import javax.inject.Inject
 
-class RoomActivePollsController @Inject constructor(
+class RoomPollsController @Inject constructor(
         val dateFormatter: VectorDateFormatter,
-) : TypedEpoxyController<List<PollSummary.ActivePoll>>() {
+) : TypedEpoxyController<List<PollSummary>>() {
 
     interface Listener {
         fun onPollClicked(pollId: String)
@@ -32,20 +32,39 @@ class RoomActivePollsController @Inject constructor(
 
     var listener: Listener? = null
 
-    override fun buildModels(data: List<PollSummary.ActivePoll>?) {
+    override fun buildModels(data: List<PollSummary>?) {
         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)
-                }
+            when (poll) {
+                is PollSummary.ActivePoll -> buildActivePollItem(poll)
+                is PollSummary.EndedPoll -> buildEndedPollItem(poll)
+            }
+        }
+    }
+
+    private fun buildActivePollItem(poll: PollSummary.ActivePoll) {
+        val host = this
+        roomPollItem {
+            id(poll.id)
+            formattedDate(host.dateFormatter.format(poll.creationTimestamp, DateFormatKind.TIMELINE_DAY_DIVIDER))
+            title(poll.title)
+            clickListener {
+                host.listener?.onPollClicked(poll.id)
+            }
+        }
+    }
+
+    private fun buildEndedPollItem(poll: PollSummary.EndedPoll) {
+        val host = this
+        roomPollItem {
+            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/list/RoomPollsListFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/polls/list/RoomPollsListFragment.kt
new file mode 100644
index 0000000000..f408f1c781
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/polls/list/RoomPollsListFragment.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.list
+
+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 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.RoomPollsFilter
+import im.vector.app.features.roomprofile.polls.RoomPollsViewModel
+import timber.log.Timber
+import javax.inject.Inject
+
+abstract class RoomPollsListFragment :
+        VectorBaseFragment<FragmentRoomPollsListBinding>(),
+        RoomPollsController.Listener {
+
+    @Inject
+    lateinit var roomPollsController: RoomPollsController
+
+    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()
+    }
+
+    abstract fun getEmptyListTitle(): String
+
+    abstract fun getRoomPollsFilter(): RoomPollsFilter
+
+    private fun setupList() {
+        roomPollsController.listener = this
+        views.roomPollsList.configureWith(roomPollsController)
+        views.roomPollsEmptyTitle.text = getEmptyListTitle()
+    }
+
+    override fun onDestroyView() {
+        cleanUpList()
+        super.onDestroyView()
+    }
+
+    private fun cleanUpList() {
+        views.roomPollsList.cleanup()
+        roomPollsController.listener = null
+    }
+
+    override fun invalidate() = withState(viewModel) { viewState ->
+        when (getRoomPollsFilter()) {
+            RoomPollsFilter.ACTIVE -> renderList(viewState.polls.filterIsInstance(PollSummary.ActivePoll::class.java))
+            RoomPollsFilter.ENDED -> renderList(viewState.polls.filterIsInstance(PollSummary.EndedPoll::class.java))
+        }
+    }
+
+    private fun renderList(polls: List<PollSummary>) {
+        roomPollsController.setData(polls)
+        views.roomPollsEmptyTitle.isVisible = polls.isEmpty()
+    }
+
+    override fun onPollClicked(pollId: String) {
+        // TODO navigate to details
+        Timber.d("poll with id $pollId clicked")
+    }
+}