From 9f06df569bcdff5dc3182aa02f62b4a0bb5694e9 Mon Sep 17 00:00:00 2001
From: Taras <tarassmakula@gmail.com>
Date: Thu, 3 Mar 2022 14:25:48 +0200
Subject: [PATCH] Add success/error handling for invites

---
 .../circles/extensions/FragmentExtensions.kt  | 49 ++++++++++++-------
 .../InviteMembersDialogFragment.kt            | 22 +++++++--
 .../group_invite/InviteMembersViewModel.kt    |  8 +++
 app/src/main/res/drawable/ic_error.xml        |  5 ++
 .../main/res/layout/error_snack_bar_view.xml  | 12 +++--
 .../res/layout/success_snack_bar_view.xml     | 44 +++++++++++++++++
 app/src/main/res/values/strings.xml           |  1 +
 7 files changed, 114 insertions(+), 27 deletions(-)
 create mode 100644 app/src/main/res/drawable/ic_error.xml
 create mode 100644 app/src/main/res/layout/success_snack_bar_view.xml

diff --git a/app/src/main/java/com/futo/circles/extensions/FragmentExtensions.kt b/app/src/main/java/com/futo/circles/extensions/FragmentExtensions.kt
index a6defc9a7..fed2da545 100644
--- a/app/src/main/java/com/futo/circles/extensions/FragmentExtensions.kt
+++ b/app/src/main/java/com/futo/circles/extensions/FragmentExtensions.kt
@@ -15,26 +15,37 @@ import com.google.android.material.snackbar.Snackbar
 
 
 @SuppressLint("InflateParams")
-fun Fragment.showError(message: String) {
-    view?.let {
-        val snack: Snackbar = Snackbar.make(it, message, Snackbar.LENGTH_LONG)
-        val customSnackView = layoutInflater.inflate(R.layout.error_snack_bar_view, null)
-        snack.view.setBackgroundColor(Color.TRANSPARENT)
-
-        val snackLayout = snack.view as Snackbar.SnackbarLayout
-        snackLayout.setPadding(0, 0, 0, 0)
-
-        customSnackView.findViewById<TextView>(R.id.tvErrorMessage).also { textView ->
-            textView.text = message
-        }
-        snackLayout.addView(customSnackView, 0)
-
-        val layoutParams = (snack.view.layoutParams as? FrameLayout.LayoutParams)?.also { params ->
-            params.gravity = Gravity.TOP
-        }
-        snack.view.layoutParams = layoutParams
-        snack.show()
+private fun Fragment.showBar(message: String, isError: Boolean, showOnActivity: Boolean) {
+    val parentView = if (showOnActivity) activity?.findViewById(android.R.id.content) else view
+    parentView ?: return
+
+    val snack: Snackbar = Snackbar.make(parentView, message, Snackbar.LENGTH_LONG)
+    snack.view.setBackgroundColor(Color.TRANSPARENT)
+
+    val snackLayout = snack.view as Snackbar.SnackbarLayout
+    snackLayout.setPadding(0, 0, 0, 0)
+
+    val customSnackView = layoutInflater.inflate(
+        if (isError) R.layout.error_snack_bar_view else R.layout.success_snack_bar_view,
+        null
+    ).apply {
+        findViewById<TextView>(R.id.tvMessage)?.text = message
+    }
+    snackLayout.addView(customSnackView, 0)
+
+    val layoutParams = (snack.view.layoutParams as? FrameLayout.LayoutParams)?.also { params ->
+        params.gravity = Gravity.TOP
     }
+    snack.view.layoutParams = layoutParams
+    snack.show()
+}
+
+fun Fragment.showError(message: String, showOnActivity: Boolean = false) {
+    showBar(message, true, showOnActivity)
+}
+
+fun Fragment.showSuccess(message: String, showOnActivity: Boolean = false) {
+    showBar(message, false, showOnActivity)
 }
 
 fun Fragment.setEnabledViews(enabled: Boolean) {
diff --git a/app/src/main/java/com/futo/circles/feature/group_invite/InviteMembersDialogFragment.kt b/app/src/main/java/com/futo/circles/feature/group_invite/InviteMembersDialogFragment.kt
index d3d8587d8..37bfee48e 100644
--- a/app/src/main/java/com/futo/circles/feature/group_invite/InviteMembersDialogFragment.kt
+++ b/app/src/main/java/com/futo/circles/feature/group_invite/InviteMembersDialogFragment.kt
@@ -6,9 +6,7 @@ import androidx.navigation.fragment.navArgs
 import com.futo.circles.R
 import com.futo.circles.base.BaseFullscreenDialogFragment
 import com.futo.circles.databinding.InviteMembersDialogFragmentBinding
-import com.futo.circles.extensions.getQueryTextChangeStateFlow
-import com.futo.circles.extensions.observeData
-import com.futo.circles.extensions.setVisibility
+import com.futo.circles.extensions.*
 import com.futo.circles.feature.group_invite.list.search.InviteMembersSearchListAdapter
 import com.futo.circles.feature.group_invite.list.selected.SelectedUsersListAdapter
 import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -39,6 +37,10 @@ class InviteMembersDialogFragment :
         binding.toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
         setupLists()
         setupObservers()
+        binding.btnInvite.setOnClickWithLoading {
+            viewModel.invite()
+            setLoadingState(true)
+        }
     }
 
     private fun setupLists() {
@@ -58,6 +60,20 @@ class InviteMembersDialogFragment :
         viewModel.selectedUsersLiveData.observeData(this) { items ->
             selectedUsersListAdapter.submitList(items)
             binding.selectedUserDivider.setVisibility(items.isNotEmpty())
+            binding.btnInvite.setButtonEnabled(items.isNotEmpty())
         }
+        viewModel.inviteResultLiveData.observeResponse(this,
+            success = {
+                showSuccess(getString(R.string.invitation_sent), true)
+                activity?.onBackPressed()
+            },
+            error = { message -> showError(message) },
+            onRequestInvoked = { setLoadingState(false) }
+        )
+    }
+
+    private fun setLoadingState(isLoading: Boolean) {
+        setEnabledViews(!isLoading)
+        binding.btnInvite.setIsLoading(isLoading)
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/futo/circles/feature/group_invite/InviteMembersViewModel.kt b/app/src/main/java/com/futo/circles/feature/group_invite/InviteMembersViewModel.kt
index 2597b2fbf..fe1dd8106 100644
--- a/app/src/main/java/com/futo/circles/feature/group_invite/InviteMembersViewModel.kt
+++ b/app/src/main/java/com/futo/circles/feature/group_invite/InviteMembersViewModel.kt
@@ -3,6 +3,8 @@ package com.futo.circles.feature.group_invite
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.asLiveData
+import com.futo.circles.extensions.Response
+import com.futo.circles.extensions.launchBg
 import com.futo.circles.extensions.launchUi
 import com.futo.circles.feature.group_invite.data_source.InviteMembersDataSource
 import com.futo.circles.model.CirclesUser
@@ -19,6 +21,8 @@ class InviteMembersViewModel(
 
     val selectedUsersLiveData = dataSource.selectedUsersFlow.asLiveData()
 
+    val inviteResultLiveData = MutableLiveData<Response<Unit>>()
+
     fun initSearchListener(queryFlow: StateFlow<String>) {
         launchUi {
             queryFlow
@@ -33,4 +37,8 @@ class InviteMembersViewModel(
         dataSource.toggleUserSelect(user)
     }
 
+    fun invite() {
+        launchBg { inviteResultLiveData.postValue(dataSource.inviteUsers(this)) }
+    }
+
 }
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_error.xml b/app/src/main/res/drawable/ic_error.xml
new file mode 100644
index 000000000..3c9a4b352
--- /dev/null
+++ b/app/src/main/res/drawable/ic_error.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp" android:tint="#FFFFFF"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
+</vector>
diff --git a/app/src/main/res/layout/error_snack_bar_view.xml b/app/src/main/res/layout/error_snack_bar_view.xml
index 06992b481..45d2e0986 100644
--- a/app/src/main/res/layout/error_snack_bar_view.xml
+++ b/app/src/main/res/layout/error_snack_bar_view.xml
@@ -2,6 +2,7 @@
 <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/tooltipCard"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_margin="16dp"
@@ -14,16 +15,16 @@
         android:padding="8dp">
 
         <ImageView
-            android:id="@+id/ivError"
+            android:id="@+id/ivIcon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:src="@android:drawable/stat_notify_error"
+            android:src="@drawable/ic_error"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent" />
 
         <TextView
-            android:id="@+id/tvErrorMessage"
+            android:id="@+id/tvMessage"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_marginStart="16dp"
@@ -32,10 +33,11 @@
             android:textColor="@color/white"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toEndOf="@id/ivError"
+            app:layout_constraintStart_toEndOf="@id/ivIcon"
             app:layout_constraintTop_toTopOf="parent"
             tools:text="asdasdasdasdadsaasdsadasdasdasdasasdasdasdasdasasdsdasddasdasdasdasdadssdasdadsasdadasddas" />
 
     </androidx.constraintlayout.widget.ConstraintLayout>
 
-</androidx.cardview.widget.CardView>
\ No newline at end of file
+</androidx.cardview.widget.CardView>
+
diff --git a/app/src/main/res/layout/success_snack_bar_view.xml b/app/src/main/res/layout/success_snack_bar_view.xml
new file mode 100644
index 000000000..d1d1e6aa9
--- /dev/null
+++ b/app/src/main/res/layout/success_snack_bar_view.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/tooltipCard"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_margin="16dp"
+    app:cardBackgroundColor="@color/blue"
+    app:cardCornerRadius="8dp">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:padding="8dp">
+
+        <ImageView
+            android:id="@+id/ivIcon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_check_circle"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:tint="@color/white" />
+
+        <TextView
+            android:id="@+id/tvMessage"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dp"
+            android:ellipsize="end"
+            android:maxLines="2"
+            android:textColor="@color/white"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@id/ivIcon"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:text="asdasdasdasdadsaasdsadasdasdasdasasdasdasdasdasasdsdasddasdasdasdasdadssdasdadsasdadasddas" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.cardview.widget.CardView>
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ab747ba9b..29d3e5f92 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -33,6 +33,7 @@
     <string name="suggestion">Suggestions</string>
     <string name="no_results">No results</string>
     <string name="invite">Invite</string>
+    <string name="invitation_sent">Invitation sent</string>
 
     <plurals name="member_plurals">
         <item quantity="one">%d member</item>
-- 
GitLab