From 663d4ff4804c24d70f5d24c0a57f1cb3b4663a0c Mon Sep 17 00:00:00 2001
From: Taras Smakula <tarassmakula@gmail.com>
Date: Fri, 8 Mar 2024 12:49:38 +0200
Subject: [PATCH] Add loading for removing session

---
 .../feature/people/PeopleDataSource.kt        |  5 +--
 .../ActiveSessionsViewModel.kt                | 37 ++++++++++++++++++-
 .../auth/model/ActiveSessionListItem.kt       |  3 +-
 .../auth/view/ActiveSessionInfoView.kt        | 10 +++--
 .../res/layout/view_active_session_info.xml   | 10 +++++
 5 files changed, 55 insertions(+), 10 deletions(-)

diff --git a/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt b/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt
index bd0b1f183..7a2a90a27 100644
--- a/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt
+++ b/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt
@@ -6,7 +6,6 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.withContext
 import org.futo.circles.core.feature.room.knoks.KnockRequestsDataSource
 import org.futo.circles.core.feature.select_users.SearchUserDataSource
@@ -28,9 +27,7 @@ class PeopleDataSource @Inject constructor(
 
     private fun getKnockRequestCountFlow(): Flow<Int> =
         knockRequestsDataSource.getKnockRequestsListItemsLiveData(peopleCategoryDataSource.getProfileRoomId())
-            ?.map {
-                it.size
-            }?.asFlow() ?: flowOf()
+            .map { it.size }.asFlow()
 
     private fun getProfileSpaceInvitesCountFlow() =
         getSpacesLiveData(listOf(Membership.INVITE)).map { it.size }.asFlow()
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsViewModel.kt
index 882340381..3e43ea8df 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsViewModel.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsViewModel.kt
@@ -1,8 +1,13 @@
 package org.futo.circles.auth.feature.active_sessions
 
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.asLiveData
 import dagger.hilt.android.lifecycle.HiltViewModel
+import org.futo.circles.auth.model.ActiveSession
+import org.futo.circles.auth.model.ActiveSessionListItem
+import org.futo.circles.auth.model.SessionHeader
 import org.futo.circles.core.base.SingleEventLiveData
 import org.futo.circles.core.extensions.Response
 import org.futo.circles.core.extensions.launchBg
@@ -13,18 +18,39 @@ class ActiveSessionsViewModel @Inject constructor(
     private val dataSource: ActiveSessionsDataSource
 ) : ViewModel() {
 
-    val activeSessionsLiveData = dataSource.getActiveSessionsFlow().asLiveData()
     val removeSessionLiveData = SingleEventLiveData<Response<Unit?>>()
     val resetKeysLiveData = SingleEventLiveData<Response<Unit?>>()
     val startReAuthEventLiveData = dataSource.startReAuthEventLiveData
 
+    private val loadingItemsIdsList = MutableLiveData<Set<String>>(emptySet())
+
+    val activeSessionsLiveData = MediatorLiveData<List<ActiveSessionListItem>>().also {
+        it.addSource(loadingItemsIdsList) { loadingItemsValue ->
+            val currentList = it.value ?: emptyList()
+            it.postValue(
+                currentList.map { item ->
+                    when (item) {
+                        is ActiveSession -> item.copy(isLoading = loadingItemsValue.contains(item.id))
+                        is SessionHeader -> item
+                    }
+                }
+            )
+        }
+        it.addSource(dataSource.getActiveSessionsFlow().asLiveData()) { value ->
+            it.postValue(value)
+        }
+    }
+
+
     fun onSessionClicked(deviceId: String) {
         dataSource.toggleOptionsVisibilityFor(deviceId)
     }
 
     fun removeSession(deviceId: String) {
         launchBg {
+            toggleItemLoading(deviceId)
             val deactivateResult = dataSource.removeSession(deviceId)
+            toggleItemLoading(deviceId)
             removeSessionLiveData.postValue(deactivateResult)
         }
     }
@@ -35,4 +61,13 @@ class ActiveSessionsViewModel @Inject constructor(
             resetKeysLiveData.postValue(resetKeysResult)
         }
     }
+
+    private fun toggleItemLoading(id: String) {
+        val currentSet = loadingItemsIdsList.value?.toMutableSet() ?: return
+        val newLoadingSet = currentSet.apply {
+            if (this.contains(id)) remove(id)
+            else add(id)
+        }
+        loadingItemsIdsList.postValue(newLoadingSet)
+    }
 }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/model/ActiveSessionListItem.kt b/auth/src/main/java/org/futo/circles/auth/model/ActiveSessionListItem.kt
index 555d5483f..079abecff 100644
--- a/auth/src/main/java/org/futo/circles/auth/model/ActiveSessionListItem.kt
+++ b/auth/src/main/java/org/futo/circles/auth/model/ActiveSessionListItem.kt
@@ -18,7 +18,8 @@ data class ActiveSession(
     val cryptoDeviceInfo: CryptoDeviceInfo,
     val canVerify: Boolean,
     val isResetKeysVisible: Boolean,
-    val isOptionsVisible: Boolean
+    val isOptionsVisible: Boolean,
+    val isLoading: Boolean = false
 ) : ActiveSessionListItem() {
     override val id: String = cryptoDeviceInfo.deviceId
 
diff --git a/auth/src/main/java/org/futo/circles/auth/view/ActiveSessionInfoView.kt b/auth/src/main/java/org/futo/circles/auth/view/ActiveSessionInfoView.kt
index e63080807..a7463cf0b 100644
--- a/auth/src/main/java/org/futo/circles/auth/view/ActiveSessionInfoView.kt
+++ b/auth/src/main/java/org/futo/circles/auth/view/ActiveSessionInfoView.kt
@@ -6,6 +6,7 @@ import android.view.LayoutInflater
 import androidx.constraintlayout.widget.ConstraintLayout
 import org.futo.circles.auth.databinding.ViewActiveSessionInfoBinding
 import org.futo.circles.auth.feature.active_sessions.list.ActiveSessionClickListener
+import org.futo.circles.auth.model.ActiveSession
 import org.futo.circles.core.extensions.setIsVisible
 
 class ActiveSessionInfoView(
@@ -32,17 +33,18 @@ class ActiveSessionInfoView(
     }
 
     fun setData(
-        activeSession: org.futo.circles.auth.model.ActiveSession,
+        activeSession: ActiveSession,
         listener: ActiveSessionClickListener
     ) {
         deviceId = activeSession.id
         activeSessionClickListener = listener
         with(binding) {
+            vLoading.setIsVisible(activeSession.isLoading)
             tvFingerprint.text = activeSession.cryptoDeviceInfo.fingerprint() ?: ""
             tvPublicKey.text = activeSession.cryptoDeviceInfo.identityKey() ?: ""
-            btnVerify.setIsVisible(activeSession.canVerify)
-            btnRemove.setIsVisible(!activeSession.isCurrentSession())
-            btnResetKeys.setIsVisible(activeSession.isResetKeysVisible)
+            btnVerify.setIsVisible(activeSession.canVerify && !activeSession.isLoading)
+            btnRemove.setIsVisible(!activeSession.isCurrentSession() && !activeSession.isLoading)
+            btnResetKeys.setIsVisible(activeSession.isResetKeysVisible && !activeSession.isLoading)
         }
     }
 }
\ No newline at end of file
diff --git a/auth/src/main/res/layout/view_active_session_info.xml b/auth/src/main/res/layout/view_active_session_info.xml
index 18adfc80e..d0b08721c 100644
--- a/auth/src/main/res/layout/view_active_session_info.xml
+++ b/auth/src/main/res/layout/view_active_session_info.xml
@@ -97,4 +97,14 @@
         app:layout_constraintStart_toEndOf="@id/btnRemove"
         app:layout_constraintTop_toBottomOf="@+id/tvPublicKey" />
 
+    <ProgressBar
+        android:id="@+id/vLoading"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="4dp"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/tvPublicKey" />
+
 </merge>
\ No newline at end of file
-- 
GitLab