From be28e2f1bf517bdf12ac5797eab6b9153fc70f44 Mon Sep 17 00:00:00 2001
From: Taras Smakula <tarassmakula@gmail.com>
Date: Mon, 29 May 2023 14:12:09 +0300
Subject: [PATCH] Add setup circles

---
 .../circles/di/data_source/AuthDsModule.kt    |  2 +-
 .../org/futo/circles/di/ui/AuthUiModule.kt    |  2 +-
 .../circles/setup/SetupCirclesDataSource.kt   | 40 ++++++++
 .../circles/setup/SetupCirclesFragment.kt     | 94 +++++++++++++++++++
 .../circles/setup/SetupCirclesViewModel.kt    | 42 +++++++++
 .../circles/setup/list/SetupCirclesAdapter.kt | 23 +++++
 .../setup/list/SetupCirclesViewHolder.kt      | 33 +++++++
 .../res/layout/fragment_setup_circles.xml     |  2 +-
 .../res/navigation/nav_graph_start_host.xml   |  2 +-
 9 files changed, 236 insertions(+), 4 deletions(-)
 create mode 100644 app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesDataSource.kt
 create mode 100644 app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesFragment.kt
 create mode 100644 app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesViewModel.kt
 create mode 100644 app/src/main/java/org/futo/circles/feature/circles/setup/list/SetupCirclesAdapter.kt
 create mode 100644 app/src/main/java/org/futo/circles/feature/circles/setup/list/SetupCirclesViewHolder.kt

diff --git a/app/src/main/java/org/futo/circles/di/data_source/AuthDsModule.kt b/app/src/main/java/org/futo/circles/di/data_source/AuthDsModule.kt
index ae30c0745..29831f2b4 100644
--- a/app/src/main/java/org/futo/circles/di/data_source/AuthDsModule.kt
+++ b/app/src/main/java/org/futo/circles/di/data_source/AuthDsModule.kt
@@ -18,7 +18,7 @@ import org.futo.circles.auth.feature.sign_up.subscription_stage.SubscriptionStag
 import org.futo.circles.auth.feature.sign_up.terms.SignupAcceptTermsDataSource
 import org.futo.circles.auth.feature.sign_up.validate_email.ValidateEmailDataSource
 import org.futo.circles.auth.feature.sign_up.validate_token.ValidateTokenDataSource
-import org.futo.circles.feature.sign_up.setup_circles.SetupCirclesDataSource
+import org.futo.circles.feature.circles.setup.SetupCirclesDataSource
 import org.koin.dsl.module
 
 val authDsModule = module {
diff --git a/app/src/main/java/org/futo/circles/di/ui/AuthUiModule.kt b/app/src/main/java/org/futo/circles/di/ui/AuthUiModule.kt
index 513742f4f..9b5cacacc 100644
--- a/app/src/main/java/org/futo/circles/di/ui/AuthUiModule.kt
+++ b/app/src/main/java/org/futo/circles/di/ui/AuthUiModule.kt
@@ -21,7 +21,7 @@ import org.futo.circles.auth.feature.sign_up.validate_token.ValidateTokenViewMod
 import org.futo.circles.auth.model.PasswordModeArg
 import org.futo.circles.auth.model.TermsModeArg
 import org.futo.circles.feature.home.HomeViewModel
-import org.futo.circles.feature.sign_up.setup_circles.SetupCirclesViewModel
+import org.futo.circles.feature.circles.setup.SetupCirclesViewModel
 import org.futo.circles.feature.timeline.poll.CreatePollViewModel
 import org.futo.circles.feature.timeline.post.create.CreatePostViewModel
 import org.futo.circles.feature.timeline.post.info.PostInfoViewModel
diff --git a/app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesDataSource.kt b/app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesDataSource.kt
new file mode 100644
index 000000000..e5e238832
--- /dev/null
+++ b/app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesDataSource.kt
@@ -0,0 +1,40 @@
+package org.futo.circles.feature.circles.setup
+
+import android.content.Context
+import android.net.Uri
+import androidx.lifecycle.MutableLiveData
+import org.futo.circles.R
+import org.futo.circles.core.provider.MatrixSessionProvider
+import org.futo.circles.mapping.notEmptyDisplayName
+import org.futo.circles.model.SetupCircleListItem
+import org.matrix.android.sdk.api.session.getUser
+
+class SetupCirclesDataSource(
+    private val context: Context
+) {
+
+    val circlesLiveData = MutableLiveData(getInitialCirclesList())
+
+    private fun getInitialCirclesList(): List<SetupCircleListItem> =
+        context.resources.getStringArray(R.array.setup_circles_list).mapIndexed { i, name ->
+            SetupCircleListItem(
+                id = i,
+                name = name,
+                userName = getUserName()
+            )
+        }
+
+    private fun getUserName(): String {
+        val session = MatrixSessionProvider.currentSession
+        val userId = session?.myUserId ?: return ""
+        return session.getUser(userId)?.notEmptyDisplayName() ?: ""
+    }
+
+    fun addCirclesCoverImage(id: Int, uri: Uri) {
+        val list = circlesLiveData.value?.map {
+            if (it.id == id) it.copy(coverUri = uri) else it
+        } ?: emptyList()
+
+        circlesLiveData.postValue(list)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesFragment.kt b/app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesFragment.kt
new file mode 100644
index 000000000..71315cefa
--- /dev/null
+++ b/app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesFragment.kt
@@ -0,0 +1,94 @@
+package org.futo.circles.feature.circles.setup
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
+import androidx.recyclerview.widget.DividerItemDecoration
+import by.kirich1409.viewbindingdelegate.viewBinding
+import org.futo.circles.R
+import org.futo.circles.core.extensions.showError
+import org.futo.circles.core.extensions.showSuccess
+import org.futo.circles.core.fragment.HasLoadingState
+import org.futo.circles.core.model.LoadingData
+import org.futo.circles.core.picker.MediaPickerHelper
+import org.futo.circles.core.view.LoadingDialog
+import org.futo.circles.databinding.FragmentSetupCirclesBinding
+import org.futo.circles.extensions.observeData
+import org.futo.circles.extensions.observeResponse
+import org.futo.circles.feature.circles.setup.list.SetupCirclesAdapter
+import org.futo.circles.feature.sign_up.setup_circles.SetupCirclesFragmentDirections
+import org.futo.circles.model.SetupCircleListItem
+import org.koin.androidx.viewmodel.ext.android.viewModel
+
+class SetupCirclesFragment : Fragment(R.layout.fragment_setup_circles), HasLoadingState {
+
+    override val fragment: Fragment = this
+    private val viewModel by viewModel<SetupCirclesViewModel>()
+    private val binding by viewBinding(FragmentSetupCirclesBinding::bind)
+    private val listAdapter by lazy { SetupCirclesAdapter(::onCircleListItemClicked) }
+    private val mediaPickerHelper = MediaPickerHelper(this)
+    private val loadingDialog by lazy { LoadingDialog(requireContext()) }
+
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        setupViews()
+        setupObservers()
+    }
+
+    private fun setupViews() {
+        with(binding) {
+            rvSetupCircles.apply {
+                addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
+                adapter = listAdapter
+            }
+            btnSkip.setOnClickListener { navigateToBottomMenuScreen() }
+            btnSave.setOnClickListener {
+                showLoading()
+                viewModel.createCircles()
+            }
+        }
+    }
+
+    private fun setupObservers() {
+        viewModel.circlesLiveData.observeData(this, ::setCirclesList)
+        viewModel.createCirclesResponseLiveData.observeResponse(this,
+            success = {
+                loadingDialog.dismiss()
+                showSuccess(getString(R.string.circles_created), true)
+                navigateToBottomMenuScreen()
+            },
+            error = {
+                showError(it)
+                loadingDialog.dismiss()
+            }
+        )
+    }
+
+    private fun setCirclesList(list: List<SetupCircleListItem>) {
+        listAdapter.submitList(list)
+    }
+
+    private fun onCircleListItemClicked(circle: SetupCircleListItem) {
+        mediaPickerHelper.showMediaPickerDialog(
+            onImageSelected = { id, uri -> viewModel.addImageForCircle(id, uri) },
+            id = circle.id
+        )
+    }
+
+    private fun showLoading() {
+        startLoading(binding.btnSave)
+        loadingDialog.handleLoading(
+            LoadingData(
+                total = 0,
+                messageId = R.string.configuring_workspace,
+                isLoading = true
+            )
+        )
+    }
+
+    private fun navigateToBottomMenuScreen() {
+        findNavController().navigate(SetupCirclesFragmentDirections.toBottomNavigationFragment())
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesViewModel.kt b/app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesViewModel.kt
new file mode 100644
index 000000000..eef7769d6
--- /dev/null
+++ b/app/src/main/java/org/futo/circles/feature/circles/setup/SetupCirclesViewModel.kt
@@ -0,0 +1,42 @@
+package org.futo.circles.feature.circles.setup
+
+import android.net.Uri
+import androidx.lifecycle.ViewModel
+import kotlinx.coroutines.delay
+import org.futo.circles.core.CREATE_ROOM_DELAY
+import org.futo.circles.core.SingleEventLiveData
+import org.futo.circles.core.room.CreateRoomDataSource
+import org.futo.circles.extensions.Response
+import org.futo.circles.extensions.createResult
+import org.futo.circles.extensions.launchBg
+
+class SetupCirclesViewModel(
+    private val setupCirclesDataSource: SetupCirclesDataSource,
+    private val createRoomDataSource: CreateRoomDataSource
+) : ViewModel() {
+
+    val circlesLiveData = setupCirclesDataSource.circlesLiveData
+    val createCirclesResponseLiveData = SingleEventLiveData<Response<Unit?>>()
+
+    fun createCircles() {
+        val circlesList = circlesLiveData.value ?: return
+        val lastItemIndex = circlesList.size - 1
+        launchBg {
+            val response = createResult {
+                circlesList.forEachIndexed { i, item ->
+                    createRoomDataSource.createCircleWithTimeline(
+                        name = item.name,
+                        iconUri = item.coverUri,
+                        null, false
+                    )
+                    if (i != lastItemIndex) delay(CREATE_ROOM_DELAY)
+                }
+            }
+            createCirclesResponseLiveData.postValue(response)
+        }
+    }
+
+    fun addImageForCircle(id: Int?, uri: Uri) {
+        id?.let { setupCirclesDataSource.addCirclesCoverImage(it, uri) }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/futo/circles/feature/circles/setup/list/SetupCirclesAdapter.kt b/app/src/main/java/org/futo/circles/feature/circles/setup/list/SetupCirclesAdapter.kt
new file mode 100644
index 000000000..661baab8d
--- /dev/null
+++ b/app/src/main/java/org/futo/circles/feature/circles/setup/list/SetupCirclesAdapter.kt
@@ -0,0 +1,23 @@
+package org.futo.circles.feature.circles.setup.list
+
+
+import android.view.ViewGroup
+import org.futo.circles.core.list.BaseRvAdapter
+import org.futo.circles.model.SetupCircleListItem
+
+class SetupCirclesAdapter(
+    private val onCircleClicked: (SetupCircleListItem) -> Unit
+) : BaseRvAdapter<SetupCircleListItem, SetupCirclesViewHolder>(DefaultIdEntityCallback()) {
+
+    override fun onCreateViewHolder(
+        parent: ViewGroup,
+        viewType: Int
+    ): SetupCirclesViewHolder = SetupCirclesViewHolder(
+        parent = parent,
+        onCircleClicked = { position -> onCircleClicked(getItem(position)) }
+    )
+
+    override fun onBindViewHolder(holder: SetupCirclesViewHolder, position: Int) {
+        holder.bind(getItem(position))
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/futo/circles/feature/circles/setup/list/SetupCirclesViewHolder.kt b/app/src/main/java/org/futo/circles/feature/circles/setup/list/SetupCirclesViewHolder.kt
new file mode 100644
index 000000000..3c4784f51
--- /dev/null
+++ b/app/src/main/java/org/futo/circles/feature/circles/setup/list/SetupCirclesViewHolder.kt
@@ -0,0 +1,33 @@
+package org.futo.circles.feature.circles.setup.list
+
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import org.futo.circles.R
+import org.futo.circles.core.list.ViewBindingHolder
+import org.futo.circles.databinding.ListItemSetupCircleBinding
+import org.futo.circles.extensions.onClick
+import org.futo.circles.model.SetupCircleListItem
+
+class SetupCirclesViewHolder(
+    parent: ViewGroup,
+    onCircleClicked: (Int) -> Unit
+) : RecyclerView.ViewHolder(inflate(parent, ListItemSetupCircleBinding::inflate)) {
+
+    private companion object : ViewBindingHolder
+
+    private val binding = baseBinding as ListItemSetupCircleBinding
+
+    init {
+        onClick(itemView) { position -> onCircleClicked(position) }
+    }
+
+    fun bind(data: SetupCircleListItem) {
+        with(binding) {
+            data.coverUri?.let { ivCircleCover.setImageURI(it) }
+                ?: ivCircleCover.setImageResource(R.drawable.add_image_placeholder)
+
+            tvCircleName.text = data.name
+            tvUserName.text = data.userName
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_setup_circles.xml b/app/src/main/res/layout/fragment_setup_circles.xml
index 497b37dc3..9e3a40256 100644
--- a/app/src/main/res/layout/fragment_setup_circles.xml
+++ b/app/src/main/res/layout/fragment_setup_circles.xml
@@ -63,7 +63,7 @@
         app:layout_constraintEnd_toEndOf="@id/guidelineEnd"
         app:layout_constraintStart_toStartOf="@id/guidelineStart" />
 
-    <org.futo.circles.view.LoadingButton
+    <org.futo.circles.core.view.LoadingButton
         android:id="@+id/btnSave"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
diff --git a/app/src/main/res/navigation/nav_graph_start_host.xml b/app/src/main/res/navigation/nav_graph_start_host.xml
index 65887884f..eee0ed259 100644
--- a/app/src/main/res/navigation/nav_graph_start_host.xml
+++ b/app/src/main/res/navigation/nav_graph_start_host.xml
@@ -70,7 +70,7 @@
     </fragment>
     <fragment
         android:id="@+id/setupCirclesFragment"
-        android:name="org.futo.circles.feature.sign_up.setup_circles.SetupCirclesFragment"
+        android:name="org.futo.circles.feature.circles.setup.SetupCirclesFragment"
         android:label="SetupCirclesFragment"
         tools:layout="@layout/fragment_setup_circles">
         <action
-- 
GitLab