InviteUsersFragment.kt

package chat.rocket.android.inviteusers.ui

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.inviteusers.presentation.InviteUsersPresenter
import chat.rocket.android.inviteusers.presentation.InviteUsersView
import chat.rocket.android.members.adapter.MembersAdapter
import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import com.google.android.material.chip.Chip
import dagger.android.support.AndroidSupportInjection
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.fragment_invite_users.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject

fun newInstance(chatRoomId: String): Fragment = InviteUsersFragment().apply {
    arguments = Bundle(1).apply {
        putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
    }
}

internal const val TAG_INVITE_USERS_FRAGMENT = "InviteUsersFragment"
private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"

class InviteUsersFragment : Fragment(), InviteUsersView {
    @Inject lateinit var presenter: InviteUsersPresenter
    @Inject lateinit var analyticsManager: AnalyticsManager
    private val compositeDisposable = CompositeDisposable()
    private val adapter: MembersAdapter = MembersAdapter { processSelectedMember(it) }
    private lateinit var chatRoomId: String
    private var memberList = arrayListOf<MemberUiModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        AndroidSupportInjection.inject(this)

        arguments?.run {
            chatRoomId = getString(BUNDLE_CHAT_ROOM_ID, "")
        }
            ?: requireNotNull(arguments) { "no arguments supplied when the fragment was instantiated" }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? = container?.inflate(R.layout.fragment_invite_users)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setupToolBar()
        setupListeners()
        setupRecyclerView()
        subscribeEditTexts()

        analyticsManager.logScreenView(ScreenViewEvent.InviteUsers)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        unsubscribeEditTexts()
    }

    override fun showLoading() {
        view_loading?.isVisible = true
    }

    override fun hideLoading() {
        view_loading?.isVisible = false
    }

    override fun showMessage(resId: Int) {
        ui { showToast(resId) }
    }

    override fun showMessage(message: String) {
        ui { showToast(message) }
    }

    override fun showGenericErrorMessage() {
        showMessage(getString(R.string.msg_generic_error))
    }

    override fun showUserSuggestion(dataSet: List<MemberUiModel>) {
        adapter.clearData()
        adapter.prependData(dataSet)
        text_member_not_found?.isVisible = false
        recycler_view?.isVisible = true
    }

    override fun showNoUserSuggestion() {
        recycler_view?.isVisible = false
        text_member_not_found?.isVisible = true
    }

    override fun showSuggestionViewInProgress() {
        recycler_view?.isVisible = false
        text_member_not_found?.isVisible = false
        showLoading()
    }

    override fun hideSuggestionViewInProgress() {
        hideLoading()
    }

    override fun usersInvitedSuccessfully() {
        memberList.clear()
        activity?.onBackPressed()
        showMessage(getString(R.string.mgs_users_invited_successfully))
    }

    override fun enableUserInput() {
        text_invite_users.isEnabled = true
    }

    override fun disableUserInput() {
        text_invite_users.isEnabled = false
    }

    private fun setupToolBar() {
        (activity as ChatRoomActivity).setupToolbarTitle((getString(R.string.msg_invite_users)))
    }

    private fun setupRecyclerView() {
        ui {
            recycler_view.layoutManager =
                LinearLayoutManager(context, RecyclerView.VERTICAL, false)
            recycler_view.addItemDecoration(
                DividerItemDecoration(it, DividerItemDecoration.HORIZONTAL)
            )
            recycler_view.adapter = adapter
        }
    }

    private fun setupListeners() {
        button_invite_user.setOnClickListener {
            if (memberList.isNotEmpty()) {
                presenter.inviteUsers(chatRoomId, memberList)
            } else {
                showMessage(R.string.mgs_choose_at_least_one_user)
            }
        }
    }

    private fun subscribeEditTexts() {
        val inviteMembersDisposable = text_invite_users.asObservable()
            .debounce(300, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
            .filter { t -> t.isNotBlank() }
            .subscribe {
                if (it.length >= 3) {
                    presenter.searchUser(it.toString())
                }
            }
        compositeDisposable.addAll(inviteMembersDisposable)
    }

    private fun unsubscribeEditTexts() = compositeDisposable.dispose()

    private fun processSelectedMember(member: MemberUiModel) {
        if (memberList.any { it.username == member.username }) {
            showMessage(getString(R.string.msg_member_already_added))
        } else {
            text_invite_users.setText("")
            addMember(member)
            addChip(member)
            chip_group_member.isVisible = true
            processBackgroundOfInviteUsersButton()
        }
    }

    private fun addMember(member: MemberUiModel) {
        memberList.add(member)
    }

    private fun removeMember(username: String) {
        memberList.remove(memberList.find { it.username == username })
    }

    private fun addChip(member: MemberUiModel) {
        val chip = Chip(context)
        chip.text = member.username
        chip.isCloseIconVisible = true
        chip.setChipBackgroundColorResource(R.color.icon_grey)
        setupChipOnCloseIconClickListener(chip)
        chip_group_member.addView(chip)
    }

    private fun setupChipOnCloseIconClickListener(chip: Chip) {
        chip.setOnCloseIconClickListener {
            removeChip(it)
            removeMember((it as Chip).text.toString())
            // whenever we remove a chip we should process the chip group visibility.
            processChipGroupVisibility()
            processBackgroundOfInviteUsersButton()
        }
    }

    private fun removeChip(chip: View) {
        chip_group_member.removeView(chip)
    }

    private fun processChipGroupVisibility() {
        chip_group_member.isVisible = memberList.isNotEmpty()
    }

    private fun processBackgroundOfInviteUsersButton() {
        if (memberList.isEmpty()) {
            text_invite_users.alpha = 0.4F
        } else {
            text_invite_users.alpha = 1F
        }
    }
}