From 0dd1abb9262a9ecf28ac85c99958e238d1459a95 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 6 Dec 2022 13:02:02 +0100
Subject: [PATCH 1/6] Rename method

---
 .../app/features/crypto/quads/SharedSecureStorageActivity.kt  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt
index d393636a8e6..aea87beea94 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt
@@ -59,7 +59,7 @@ class SharedSecureStorageActivity :
 
         views.toolbar.visibility = View.GONE
 
-        viewModel.observeViewEvents { observeViewEvents(it) }
+        viewModel.observeViewEvents { onViewEvents(it) }
 
         viewModel.onEach { renderState(it) }
     }
@@ -85,7 +85,7 @@ class SharedSecureStorageActivity :
         showFragment(fragment)
     }
 
-    private fun observeViewEvents(it: SharedSecureStorageViewEvent?) {
+    private fun onViewEvents(it: SharedSecureStorageViewEvent) {
         when (it) {
             is SharedSecureStorageViewEvent.Dismiss -> {
                 finish()

From 9c79d234440310bf41e4964c78dc48e8bbb89c15 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Fri, 16 Dec 2022 21:02:33 +0100
Subject: [PATCH 2/6] Ensure event are not sent if the lifecycle state is not
 RESUMED

---
 .../app/core/platform/VectorBaseActivity.kt   | 22 ++++++++++++-------
 .../VectorBaseBottomSheetDialogFragment.kt    | 21 ++++++++++++------
 .../app/core/platform/VectorBaseFragment.kt   | 22 ++++++++++++-------
 .../im/vector/app/features/MainActivity.kt    |  8 +++----
 .../media/VectorAttachmentViewerActivity.kt   | 16 +++++++++-----
 .../settings/VectorSettingsBaseFragment.kt    | 22 +++++++++++++------
 6 files changed, 70 insertions(+), 41 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
index 4e5116eda9c..a87eb92b133 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
@@ -41,6 +41,7 @@ import androidx.fragment.app.FragmentManager
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
 import androidx.preference.PreferenceManager
 import androidx.viewbinding.ViewBinding
 import com.airbnb.mvrx.MavericksView
@@ -91,6 +92,7 @@ import im.vector.app.features.themes.ActivityOtherThemes
 import im.vector.app.features.themes.ThemeUtils
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.failure.GlobalError
@@ -123,14 +125,18 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
     protected val viewModelProvider
         get() = ViewModelProvider(this, viewModelFactory)
 
-    fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(observer: (T) -> Unit) {
-        viewEvents
-                .stream()
-                .onEach {
-                    hideWaitingView()
-                    observer(it)
-                }
-                .launchIn(lifecycleScope)
+    fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(
+            observer: (T) -> Unit,
+    ) {
+        lifecycleScope.launch {
+            repeatOnLifecycle(Lifecycle.State.RESUMED) {
+                viewEvents.stream()
+                        .collect {
+                            hideWaitingView()
+                            observer(it)
+                        }
+            }
+        }
     }
 
     var toolbar: ToolbarConfig? = null
diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt
index ec6f3288f85..ad7a86c8991 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt
@@ -26,8 +26,10 @@ import android.view.ViewGroup
 import android.widget.FrameLayout
 import androidx.annotation.CallSuper
 import androidx.annotation.FloatRange
+import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
 import androidx.viewbinding.ViewBinding
 import com.airbnb.mvrx.MavericksView
 import com.google.android.material.bottomsheet.BottomSheetBehavior
@@ -43,6 +45,7 @@ import im.vector.app.features.analytics.plan.MobileScreen
 import io.github.hyuwah.draggableviewlib.Utils
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
 import reactivecircus.flowbinding.android.view.clicks
 import timber.log.Timber
 
@@ -199,12 +202,16 @@ abstract class VectorBaseBottomSheetDialogFragment<VB : ViewBinding> : BottomShe
      * ViewEvents
      * ========================================================================================== */
 
-    protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(observer: (T) -> Unit) {
-        viewEvents
-                .stream()
-                .onEach {
-                    observer(it)
-                }
-                .launchIn(viewLifecycleOwner.lifecycleScope)
+    protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(
+            observer: (T) -> Unit,
+    ) {
+        lifecycleScope.launch {
+            repeatOnLifecycle(Lifecycle.State.RESUMED) {
+                viewEvents.stream()
+                        .collect {
+                            observer(it)
+                        }
+            }
+        }
     }
 }
diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt
index 8fe2d33f6ad..9f79db9c66d 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt
@@ -34,6 +34,7 @@ import androidx.fragment.app.Fragment
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
 import androidx.viewbinding.ViewBinding
 import com.airbnb.mvrx.MavericksView
 import com.bumptech.glide.util.Util.assertMainThread
@@ -53,6 +54,7 @@ import im.vector.app.features.navigation.Navigator
 import im.vector.lib.ui.styles.dialogs.MaterialProgressDialog
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
 import reactivecircus.flowbinding.android.view.clicks
 import timber.log.Timber
 
@@ -272,14 +274,18 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
      * ViewEvents
      * ========================================================================================== */
 
-    protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(observer: (T) -> Unit) {
-        viewEvents
-                .stream()
-                .onEach {
-                    dismissLoadingDialog()
-                    observer(it)
-                }
-                .launchIn(viewLifecycleOwner.lifecycleScope)
+    protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(
+            observer: (T) -> Unit,
+    ) {
+        lifecycleScope.launch {
+            repeatOnLifecycle(Lifecycle.State.RESUMED) {
+                viewEvents.stream()
+                        .collect {
+                            dismissLoadingDialog()
+                            observer(it)
+                        }
+            }
+        }
     }
 
     /* ==========================================================================================
diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt
index 8ce375122e8..cffb1577cf0 100644
--- a/vector/src/main/java/im/vector/app/features/MainActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt
@@ -55,8 +55,6 @@ import im.vector.app.features.themes.ActivityOtherThemes
 import im.vector.app.features.ui.UiStateRepository
 import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import kotlinx.parcelize.Parcelize
@@ -142,9 +140,9 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
         startAppViewModel.onEach {
             renderState(it)
         }
-        startAppViewModel.viewEvents.stream()
-                .onEach(::handleViewEvents)
-                .launchIn(lifecycleScope)
+        startAppViewModel.observeViewEvents {
+            handleViewEvents(it)
+        }
 
         startAppViewModel.handle(StartAppAction.StartApp)
     }
diff --git a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
index 089fdcebd40..9f9488e35db 100644
--- a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
@@ -29,7 +29,9 @@ import androidx.core.transition.addListener
 import androidx.core.view.ViewCompat
 import androidx.core.view.isInvisible
 import androidx.core.view.isVisible
+import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
 import androidx.transition.Transition
 import com.airbnb.mvrx.viewModel
 import dagger.hilt.android.AndroidEntryPoint
@@ -50,8 +52,6 @@ import im.vector.lib.attachmentviewer.AttachmentViewerActivity
 import im.vector.lib.core.utils.compat.getParcelableArrayListExtraCompat
 import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import kotlinx.parcelize.Parcelize
@@ -239,10 +239,14 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
     }
 
     private fun observeViewEvents() {
-        viewModel.viewEvents
-                .stream()
-                .onEach(::handleViewEvents)
-                .launchIn(lifecycleScope)
+        lifecycleScope.launch {
+            repeatOnLifecycle(Lifecycle.State.RESUMED) {
+                viewModel
+                        .viewEvents
+                        .stream()
+                        .collect(::handleViewEvents)
+            }
+        }
     }
 
     private fun handleViewEvents(event: VectorAttachmentViewerViewEvents) {
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt
index 38ba949a492..6299d8962d0 100644
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt
@@ -20,7 +20,9 @@ import android.content.Context
 import android.os.Bundle
 import android.view.View
 import androidx.annotation.CallSuper
+import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
 import androidx.preference.PreferenceFragmentCompat
 import com.airbnb.mvrx.MavericksView
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -35,6 +37,7 @@ import im.vector.app.features.analytics.AnalyticsTracker
 import im.vector.app.features.analytics.plan.MobileScreen
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.session.Session
 import reactivecircus.flowbinding.android.view.clicks
 import timber.log.Timber
@@ -66,13 +69,18 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), Maverick
      * ViewEvents
      * ========================================================================================== */
 
-    protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(observer: (T) -> Unit) {
-        viewEvents
-                .stream()
-                .onEach {
-                    observer(it)
-                }
-                .launchIn(viewLifecycleOwner.lifecycleScope)
+    protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(
+            observer: (T) -> Unit,
+    ) {
+        lifecycleScope.launch {
+            repeatOnLifecycle(state) {
+            repeatOnLifecycle(Lifecycle.State.RESUMED) {
+                viewEvents.stream()
+                        .collect {
+                            observer(it)
+                        }
+            }
+        }
     }
 
     /* ==========================================================================================

From 71bd4f457a8093683c6b9d046352e032c92d21eb Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 7 Dec 2022 17:48:25 +0100
Subject: [PATCH 3/6] Ensure posted events from the ViewModel are consumed
 (once) by the UI Inspired from
 https://github.com/Kotlin/kotlinx.coroutines/issues/3002

---
 .../app/core/platform/VectorBaseActivity.kt   |  4 +-
 .../VectorBaseBottomSheetDialogFragment.kt    |  4 +-
 .../app/core/platform/VectorBaseFragment.kt   |  4 +-
 .../app/core/platform/VectorViewModel.kt      |  9 +--
 .../im/vector/app/core/utils/SharedEvent.kt   | 58 +++++++++++++++++++
 .../media/VectorAttachmentViewerActivity.kt   |  3 +-
 .../settings/VectorSettingsBaseFragment.kt    |  5 +-
 7 files changed, 77 insertions(+), 10 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/core/utils/SharedEvent.kt

diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
index a87eb92b133..1e29dfff5ed 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
@@ -128,9 +128,11 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
     fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(
             observer: (T) -> Unit,
     ) {
+        val tag = this@VectorBaseActivity::class.simpleName.toString()
         lifecycleScope.launch {
             repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                viewEvents.stream()
+                viewEvents
+                        .stream(tag)
                         .collect {
                             hideWaitingView()
                             observer(it)
diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt
index ad7a86c8991..a44fb1c9acb 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseBottomSheetDialogFragment.kt
@@ -205,9 +205,11 @@ abstract class VectorBaseBottomSheetDialogFragment<VB : ViewBinding> : BottomShe
     protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(
             observer: (T) -> Unit,
     ) {
+        val tag = this@VectorBaseBottomSheetDialogFragment::class.simpleName.toString()
         lifecycleScope.launch {
             repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                viewEvents.stream()
+                viewEvents
+                        .stream(tag)
                         .collect {
                             observer(it)
                         }
diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt
index 9f79db9c66d..a82cef54e56 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseFragment.kt
@@ -277,9 +277,11 @@ abstract class VectorBaseFragment<VB : ViewBinding> : Fragment(), MavericksView
     protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(
             observer: (T) -> Unit,
     ) {
+        val tag = this@VectorBaseFragment::class.simpleName.toString()
         lifecycleScope.launch {
             repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                viewEvents.stream()
+                viewEvents
+                        .stream(tag)
                         .collect {
                             dismissLoadingDialog()
                             observer(it)
diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt
index c9d58f9545e..3dd38c455fb 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt
@@ -18,15 +18,16 @@ package im.vector.app.core.platform
 
 import com.airbnb.mvrx.MavericksState
 import com.airbnb.mvrx.MavericksViewModel
-import im.vector.app.core.utils.DataSource
-import im.vector.app.core.utils.PublishDataSource
+import im.vector.app.core.utils.EventQueue
+import im.vector.app.core.utils.SharedEvents
 
 abstract class VectorViewModel<S : MavericksState, VA : VectorViewModelAction, VE : VectorViewEvents>(initialState: S) :
         MavericksViewModel<S>(initialState) {
 
     // Used to post transient events to the View
-    protected val _viewEvents = PublishDataSource<VE>()
-    val viewEvents: DataSource<VE> = _viewEvents
+    protected val _viewEvents = EventQueue<VE>(capacity = 64)
+    val viewEvents: SharedEvents<VE>
+        get() = _viewEvents
 
     abstract fun handle(action: VA)
 }
diff --git a/vector/src/main/java/im/vector/app/core/utils/SharedEvent.kt b/vector/src/main/java/im/vector/app/core/utils/SharedEvent.kt
new file mode 100644
index 00000000000..e712769c485
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/utils/SharedEvent.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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.core.utils
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.transform
+import java.util.concurrent.CopyOnWriteArraySet
+
+interface SharedEvents<out T> {
+    fun stream(consumerId: String): Flow<T>
+}
+
+class EventQueue<T>(capacity: Int) : SharedEvents<T> {
+
+    private val innerQueue = MutableSharedFlow<OneTimeEvent<T>>(replay = capacity)
+
+    fun post(event: T) {
+        innerQueue.tryEmit(OneTimeEvent(event))
+    }
+
+    override fun stream(consumerId: String): Flow<T> = innerQueue.filterNotHandledBy(consumerId)
+}
+
+/**
+ * Event designed to be delivered only once to a concrete entity,
+ * but it can also be delivered to multiple different entities.
+ *
+ * Keeps track of who has already handled its content.
+ */
+private class OneTimeEvent<out T>(private val content: T) {
+
+    private val handlers = CopyOnWriteArraySet<String>()
+
+    /**
+     * @param asker Used to identify, whether this "asker" has already handled this Event.
+     * @return Event content or null if it has been already handled by asker
+     */
+    fun getIfNotHandled(asker: String): T? = if (handlers.add(asker)) content else null
+}
+
+private fun <T> Flow<OneTimeEvent<T>>.filterNotHandledBy(consumerId: String): Flow<T> = transform { event ->
+    event.getIfNotHandled(consumerId)?.let { emit(it) }
+}
diff --git a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
index 9f9488e35db..0d240b376be 100644
--- a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
@@ -239,11 +239,12 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
     }
 
     private fun observeViewEvents() {
+        val tag = this::class.simpleName.toString()
         lifecycleScope.launch {
             repeatOnLifecycle(Lifecycle.State.RESUMED) {
                 viewModel
                         .viewEvents
-                        .stream()
+                        .stream(tag)
                         .collect(::handleViewEvents)
             }
         }
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt
index 6299d8962d0..724807a81ec 100644
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsBaseFragment.kt
@@ -72,10 +72,11 @@ abstract class VectorSettingsBaseFragment : PreferenceFragmentCompat(), Maverick
     protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(
             observer: (T) -> Unit,
     ) {
+        val tag = this@VectorSettingsBaseFragment::class.simpleName.toString()
         lifecycleScope.launch {
-            repeatOnLifecycle(state) {
             repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                viewEvents.stream()
+                viewEvents
+                        .stream(tag)
                         .collect {
                             observer(it)
                         }

From 9768430d5c19cc78a7d21c7dce375bb39890d26b Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 19 Dec 2022 18:32:07 +0100
Subject: [PATCH 4/6] Fix test compilation issue

---
 vector/src/test/java/im/vector/app/test/Extensions.kt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/test/java/im/vector/app/test/Extensions.kt b/vector/src/test/java/im/vector/app/test/Extensions.kt
index 2fbab3b71b9..0b1a22f75cd 100644
--- a/vector/src/test/java/im/vector/app/test/Extensions.kt
+++ b/vector/src/test/java/im/vector/app/test/Extensions.kt
@@ -28,7 +28,7 @@ fun String.trimIndentOneLine() = trimIndent().replace("\n", "")
 fun <S : MavericksState, VA : VectorViewModelAction, VE : VectorViewEvents> VectorViewModel<S, VA, VE>.test(): ViewModelTest<S, VE> {
     val testResultCollectingScope = CoroutineScope(Dispatchers.Unconfined)
     val state = stateFlow.test(testResultCollectingScope)
-    val viewEvents = viewEvents.stream().test(testResultCollectingScope)
+    val viewEvents = viewEvents.stream("test").test(testResultCollectingScope)
     return ViewModelTest(state, viewEvents)
 }
 

From 7b1724f6dd1aa3651fc7a36890565cf9d213aa27 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 6 Jan 2023 15:13:01 +0100
Subject: [PATCH 5/6] changelog

---
 changelog.d/7724.bugfix | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 changelog.d/7724.bugfix

diff --git a/changelog.d/7724.bugfix b/changelog.d/7724.bugfix
new file mode 100644
index 00000000000..685f7ad4e25
--- /dev/null
+++ b/changelog.d/7724.bugfix
@@ -0,0 +1 @@
+ Observe ViewEvents only when resumed and ensure ViewEvents are not lost.

From e9d1de8fbac8d93f6a98aa3cc706f6ad16ea8f10 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 6 Jan 2023 17:36:40 +0100
Subject: [PATCH 6/6] Fix compilation issue after rebase.

---
 .../core/platform/VectorBaseDialogFragment.kt | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseDialogFragment.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseDialogFragment.kt
index 5a817b989e9..34e233aa7aa 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseDialogFragment.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseDialogFragment.kt
@@ -23,8 +23,10 @@ import android.view.View
 import android.view.ViewGroup
 import androidx.annotation.CallSuper
 import androidx.fragment.app.DialogFragment
+import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
 import androidx.viewbinding.ViewBinding
 import com.airbnb.mvrx.MavericksView
 import dagger.hilt.android.EntryPointAccessors
@@ -37,6 +39,7 @@ import im.vector.app.features.analytics.plan.MobileScreen
 import im.vector.app.features.themes.ThemeUtils
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
 import reactivecircus.flowbinding.android.view.clicks
 import timber.log.Timber
 
@@ -145,11 +148,15 @@ abstract class VectorBaseDialogFragment<VB : ViewBinding> : DialogFragment(), Ma
      * ========================================================================================== */
 
     protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(observer: (T) -> Unit) {
-        viewEvents
-                .stream()
-                .onEach {
-                    observer(it)
-                }
-                .launchIn(viewLifecycleOwner.lifecycleScope)
+        val tag = this@VectorBaseDialogFragment::class.simpleName.toString()
+        lifecycleScope.launch {
+            repeatOnLifecycle(Lifecycle.State.RESUMED) {
+                viewEvents
+                        .stream(tag)
+                        .collect {
+                            observer(it)
+                        }
+            }
+        }
     }
 }