Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad4fa5de authored by Moez Bhatti's avatar Moez Bhatti
Browse files

Keep a reference to a conversation observable instead of conversation, don't...

Keep a reference to a conversation observable instead of conversation, don't manually call methods in VM from Activity
parent 62e19a15
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
package com.moez.QKSMS.common.util.extensions

import io.reactivex.Observable
import io.realm.Realm
import io.realm.RealmObject
import io.realm.RealmResults

fun RealmObject.insertOrUpdate() {
    val realm = Realm.getDefaultInstance()
    realm.executeTransaction { realm.insertOrUpdate(this) }
    realm.close()
}

fun <T : RealmObject> RealmObject.asObservable(): Observable<T> {
    return asFlowable<T>().toObservable()
}

fun <T : RealmObject> RealmResults<T>.asObservable(): Observable<RealmResults<T>> {
    return asFlowable().toObservable()
}
 No newline at end of file
+15 −14
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ import com.moez.QKSMS.presentation.base.QkActivity
import io.reactivex.Observable
import io.reactivex.subjects.PublishSubject
import io.reactivex.subjects.Subject
import io.realm.RealmResults
import kotlinx.android.synthetic.main.compose_activity.*
import kotlinx.android.synthetic.main.toolbar_chips.*
import javax.inject.Inject
@@ -43,6 +42,7 @@ class ComposeActivity : QkActivity<ComposeViewModel>(), ComposeView {

    private val chipsAdapter by lazy { ChipsAdapter(this, chips) }
    private val contactsAdapter by lazy { ContactAdapter(this) }
    private val messageAdapter by lazy { MessagesAdapter() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
@@ -57,11 +57,13 @@ class ComposeActivity : QkActivity<ComposeViewModel>(), ComposeView {
        contacts.layoutManager = LinearLayoutManager(this)
        contacts.adapter = contactsAdapter

        window.callback = ComposeWindowCallback(window.callback, this)
        setupMessagesAdapter()

        layoutManager = LinearLayoutManager(this)
        layoutManager.stackFromEnd = true
        layoutManager = LinearLayoutManager(this).apply { stackFromEnd = true }
        messageList.layoutManager = layoutManager
        messageList.adapter = messageAdapter

        window.callback = ComposeWindowCallback(window.callback, this)
    }

    override fun render(state: ComposeState) {
@@ -78,34 +80,35 @@ class ComposeActivity : QkActivity<ComposeViewModel>(), ComposeView {
            contactsAdapter.data = state.contacts
        }

        if (messageAdapter.data !== state.messages) {
            messageAdapter.updateData(state.messages)
        }

        if (title != state.title) title = state.title
        if (messageList.adapter == null && state.messages?.isValid == true) messageList.adapter = createAdapter(state.messages)
        if (message.text.toString() != state.draft) message.setText(state.draft)

        send.setTint(if (state.canSend) themeManager.color else resources.getColor(R.color.textTertiary))
        send.isEnabled = state.canSend
    }

    private fun createAdapter(messages: RealmResults<Message>): MessagesAdapter {
        val adapter = MessagesAdapter(messages)
        adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
    private fun setupMessagesAdapter() {
        messageAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
            override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
                super.onItemRangeInserted(positionStart, itemCount)
                viewModel.dataChanged()

                if (positionStart > 0) {
                    adapter.notifyItemChanged(positionStart - 1)
                    messageAdapter.notifyItemChanged(positionStart - 1)
                }

                // If we're at the bottom, scroll down to show new messages
                val lastVisiblePosition = layoutManager.findLastCompletelyVisibleItemPosition()
                if (positionStart >= adapter.itemCount - 1 && lastVisiblePosition == positionStart - 1) {
                if (positionStart >= messageAdapter.itemCount - 1 && lastVisiblePosition == positionStart - 1) {
                    messageList.scrollToPosition(positionStart)
                }
            }
        })

        adapter.longClicks.subscribe { message ->
        messageAdapter.longClicks.subscribe { message ->
            AlertDialog.Builder(this)
                    .setItems(R.array.message_options, { _, row ->
                        when (row) {
@@ -116,7 +119,5 @@ class ComposeActivity : QkActivity<ComposeViewModel>(), ComposeView {
                    })
                    .show()
        }

        return adapter
    }
}
 No newline at end of file
+27 −26
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ import android.telephony.PhoneNumberUtils
import com.moez.QKSMS.R
import com.moez.QKSMS.common.di.AppComponentManager
import com.moez.QKSMS.common.util.ClipboardUtils
import com.moez.QKSMS.common.util.extensions.asObservable
import com.moez.QKSMS.common.util.extensions.makeToast
import com.moez.QKSMS.data.model.Contact
import com.moez.QKSMS.data.model.Conversation
@@ -33,30 +34,35 @@ class ComposeViewModel(threadId: Long) : QkViewModel<ComposeView, ComposeState>(

    private val contacts: List<Contact>

    private var conversation: Conversation? = null
    private val conversation: Observable<Conversation>

    init {
        AppComponentManager.appComponent.inject(this)

        conversation = messageRepo.getConversationAsync(threadId)
                .asObservable<Conversation>()
                .filter { conversation -> conversation.isLoaded }
                .filter { conversation -> conversation.isValid }
                .distinctUntilChanged { conversation -> conversation.id }
                .doOnNext { conversation -> newState { it.copy(title = conversation.getTitle()) } }

        disposables += sendMessage
        disposables += markRead
        disposables += messageRepo.getConversationAsync(threadId)
                .asFlowable<Conversation>()
                .filter { it.isLoaded }
                .subscribe { conversation ->
                    when (conversation.isValid) {
                        true -> {
                            this.conversation = conversation
                            val title = conversation.getTitle()
                            val messages = messageRepo.getMessages(threadId)
                            newState { it.copy(title = title, messages = messages) }
                        }
                    }
                }
        disposables += conversation.subscribe()

        // When the conversation changes, update the messages for the adapter
        // When the message list changes, make sure to mark them read
        disposables += conversation
                .map { conversation -> messageRepo.getMessages(conversation.id) }
                .doOnNext { messages -> newState { it.copy(messages = messages) } }
                .flatMap { messages -> messages.asObservable() }
                .withLatestFrom(conversation, { messages, conversation ->
                    markRead.execute(conversation.id)
                    messages
                })
                .subscribe()

        contacts = contactsRepo.getContacts()

        dataChanged()
    }

    override fun bindView(view: ComposeView) {
@@ -92,12 +98,13 @@ class ComposeViewModel(threadId: Long) : QkViewModel<ComposeView, ComposeState>(
        intents += view.sendIntent
                .withLatestFrom(view.textChangedIntent, { _, body -> body })
                .map { body -> body.toString() }
                .subscribe { body ->
                    val threadId = conversation?.id ?: 0
                    val address = conversation?.contacts?.first()?.address.orEmpty()
                .withLatestFrom(conversation, { body, conversation ->
                    val threadId = conversation.id
                    val address = conversation.contacts.first()?.address.orEmpty()
                    sendMessage.execute(SendMessage.Params(threadId, address, body))
                    newState { it.copy(draft = "", canSend = false) }
                }
                })
                .subscribe()

        intents += view.copyTextIntent.subscribe { message ->
            ClipboardUtils.copy(context, message.body)
@@ -109,10 +116,4 @@ class ComposeViewModel(threadId: Long) : QkViewModel<ComposeView, ComposeState>(
        }
    }

    fun dataChanged() {
        conversation?.id?.let { threadId ->
            markRead.execute(threadId)
        }
    }

}
 No newline at end of file
+1 −2
Original line number Diff line number Diff line
@@ -17,14 +17,13 @@ import com.moez.QKSMS.data.model.Message
import com.moez.QKSMS.presentation.base.QkViewHolder
import io.reactivex.subjects.PublishSubject
import io.reactivex.subjects.Subject
import io.realm.OrderedRealmCollection
import io.realm.RealmRecyclerViewAdapter
import kotlinx.android.synthetic.main.message_list_item_in.view.*
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject

class MessagesAdapter(data: OrderedRealmCollection<Message>?) : RealmRecyclerViewAdapter<Message, QkViewHolder>(data, true) {
class MessagesAdapter : RealmRecyclerViewAdapter<Message, QkViewHolder>(null, true) {

    companion object {
        private val VIEWTYPE_ME = -1