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