Loading presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt +5 −5 Original line number Diff line number Diff line Loading @@ -54,11 +54,11 @@ import com.moez.QKSMS.common.util.extensions.setBackgroundTint import com.moez.QKSMS.common.util.extensions.setTint import com.moez.QKSMS.common.util.extensions.setVisible import com.moez.QKSMS.common.util.extensions.showKeyboard import com.moez.QKSMS.feature.compose.editing.Chip import com.moez.QKSMS.feature.compose.editing.ChipsAdapter import com.moez.QKSMS.feature.compose.editing.ComposeItem import com.moez.QKSMS.feature.compose.editing.ComposeItemAdapter import com.moez.QKSMS.model.Attachment import com.moez.QKSMS.model.Contact import com.uber.autodispose.android.lifecycle.scope import com.uber.autodispose.autoDisposable import dagger.android.AndroidInjection Loading Loading @@ -91,7 +91,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView { override val queryBackspaceIntent: Observable<*> by lazy { search.backspaces } override val queryEditorActionIntent: Observable<Int> by lazy { search.editorActions() } override val chipSelectedIntent: Subject<ComposeItem> by lazy { contactsAdapter.itemSelected } override val chipDeletedIntent: Subject<Contact> by lazy { chipsAdapter.chipDeleted } override val chipDeletedIntent: Subject<Chip> by lazy { chipsAdapter.chipDeleted } override val menuReadyIntent: Observable<Unit> = menu.map { Unit } override val optionsItemIntent: Subject<Int> = PublishSubject.create() override val sendAsGroupIntent by lazy { sendAsGroupBackground.clicks() } Loading Loading @@ -212,16 +212,16 @@ class ComposeActivity : QkThemedActivity(), ComposeView { toolbar.menu.findItem(R.id.next)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty() toolbar.menu.findItem(R.id.clear)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty() if (chipsAdapter.data.isEmpty() && state.selectedContacts.isNotEmpty()) { if (chipsAdapter.data.isEmpty() && state.selectedChips.isNotEmpty()) { message.showKeyboard() } chipsAdapter.data = state.selectedContacts chipsAdapter.data = state.selectedChips contactsAdapter.data = state.composeItems loading.setVisible(state.loading) sendAsGroup.setVisible(state.editingMode && state.selectedContacts.size >= 2) sendAsGroup.setVisible(state.editingMode && state.selectedChips.size >= 2) sendAsGroupSwitch.isChecked = state.sendAsGroup messageList.setVisible(state.sendAsGroup) Loading presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeState.kt +2 −2 Original line number Diff line number Diff line Loading @@ -19,9 +19,9 @@ package com.moez.QKSMS.feature.compose import com.moez.QKSMS.compat.SubscriptionInfoCompat import com.moez.QKSMS.feature.compose.editing.Chip import com.moez.QKSMS.feature.compose.editing.ComposeItem import com.moez.QKSMS.model.Attachment import com.moez.QKSMS.model.Contact import com.moez.QKSMS.model.Conversation import com.moez.QKSMS.model.Message import io.realm.RealmResults Loading @@ -32,7 +32,7 @@ data class ComposeState( val searching: Boolean = false, val composeItems: List<ComposeItem> = ArrayList(), val selectedConversation: Long = 0, val selectedContacts: List<Contact> = ArrayList(), val selectedChips: List<Chip> = ArrayList(), val sendAsGroup: Boolean = true, val conversationtitle: String = "", val loading: Boolean = false, Loading presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt +2 −2 Original line number Diff line number Diff line Loading @@ -22,9 +22,9 @@ import android.net.Uri import androidx.annotation.StringRes import androidx.core.view.inputmethod.InputContentInfoCompat import com.moez.QKSMS.common.base.QkView import com.moez.QKSMS.feature.compose.editing.Chip import com.moez.QKSMS.feature.compose.editing.ComposeItem import com.moez.QKSMS.model.Attachment import com.moez.QKSMS.model.Contact import io.reactivex.Observable import io.reactivex.subjects.Subject Loading @@ -35,7 +35,7 @@ interface ComposeView : QkView<ComposeState> { val queryBackspaceIntent: Observable<*> val queryEditorActionIntent: Observable<Int> val chipSelectedIntent: Subject<ComposeItem> val chipDeletedIntent: Subject<Contact> val chipDeletedIntent: Subject<Chip> val menuReadyIntent: Observable<Unit> val optionsItemIntent: Observable<Int> val sendAsGroupIntent: Observable<*> Loading presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt +32 −36 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.moez.QKSMS.extensions.isImage import com.moez.QKSMS.extensions.isVideo import com.moez.QKSMS.extensions.mapNotNull import com.moez.QKSMS.extensions.removeAccents import com.moez.QKSMS.feature.compose.editing.Chip import com.moez.QKSMS.feature.compose.editing.ComposeItem import com.moez.QKSMS.feature.compose.editing.ComposeItem.* import com.moez.QKSMS.filter.ContactFilter Loading Loading @@ -118,11 +119,11 @@ class ComposeViewModel @Inject constructor( private val attachments: Subject<List<Attachment>> = BehaviorSubject.createDefault(sharedAttachments) private val contactGroups: Observable<List<ContactGroup>> by lazy { contactsRepo.getUnmanagedContactGroups() } private val contacts: Observable<List<Contact>> by lazy { contactsRepo.getUnmanagedContacts() } private val contactsReducer: Subject<(List<Contact>) -> List<Contact>> = PublishSubject.create() private val chipsReducer: Subject<(List<Chip>) -> List<Chip>> = PublishSubject.create() private val conversation: Subject<Conversation> = BehaviorSubject.create() private val messages: Subject<List<Message>> = BehaviorSubject.create() private val recents: Observable<List<Conversation>> by lazy { conversationRepo.getUnmanagedConversations() } private val selectedContacts: Subject<List<Contact>> = BehaviorSubject.createDefault(listOf()) private val selectedChips: Subject<List<Chip>> = BehaviorSubject.createDefault(listOf()) private val searchResults: Subject<List<Message>> = BehaviorSubject.create() private val searchSelection: Subject<Long> = BehaviorSubject.createDefault(-1) private val starredContacts: Observable<List<Contact>> by lazy { contactsRepo.getUnmanagedContacts(true) } Loading @@ -133,9 +134,9 @@ class ComposeViewModel @Inject constructor( ?.asObservable() ?: Observable.empty() val selectedConversation = selectedContacts val selectedConversation = selectedChips .skipWhile { it.isEmpty() } .map { contacts -> contacts.map { it.numbers.firstOrNull()?.address ?: "" } } .map { chips -> chips.map { it.address } } .distinctUntilChanged() .doOnNext { newState { copy(loading = true) } } .observeOn(Schedulers.io()) Loading Loading @@ -177,15 +178,15 @@ class ComposeViewModel @Inject constructor( .subscribe(conversation::onNext) if (address.isNotBlank()) { selectedContacts.onNext(listOf(Contact(numbers = RealmList(PhoneNumber(address))))) selectedChips.onNext(listOf(Chip(address))) } disposables += contactsReducer .scan(listOf<Contact>()) { previousState, reducer -> reducer(previousState) } .doOnNext { contacts -> newState { copy(selectedContacts = contacts) } } disposables += chipsReducer .scan(listOf<Chip>()) { previousState, reducer -> reducer(previousState) } .doOnNext { chips -> newState { copy(selectedChips = chips) } } .skipUntil(state.filter { state -> state.editingMode }) .takeUntil(state.filter { state -> !state.editingMode }) .subscribe(selectedContacts::onNext) .subscribe(selectedChips::onNext) // When the conversation changes, mark read, and update the threadId and the messages for the adapter disposables += conversation Loading Loading @@ -256,8 +257,8 @@ class ComposeViewModel @Inject constructor( // that have already been selected Observables .combineLatest( view.queryChangedIntent, recents, starredContacts, contactGroups, contacts, selectedContacts ) { query, recents, starredContacts, contactGroups, contacts, selectedContacts -> view.queryChangedIntent, recents, starredContacts, contactGroups, contacts, selectedChips ) { query, recents, starredContacts, contactGroups, contacts, selectedChips -> val composeItems = mutableListOf<ComposeItem>() if (query.isBlank()) { composeItems += recents.map(::Recent) Loading @@ -276,7 +277,7 @@ class ComposeViewModel @Inject constructor( // cache the result instead of doing it for each contact val normalizedQuery = query.removeAccents() composeItems += starredContacts .filterNot { contact -> selectedContacts.contains(contact) } .filterNot { contact -> selectedChips.map { it.contact }.contains(contact) } .filter { contact -> contactFilter.filter(contact, normalizedQuery) } .map(::Starred) Loading @@ -285,7 +286,7 @@ class ComposeViewModel @Inject constructor( .map(::Group) composeItems += contacts .filterNot { contact -> selectedContacts.contains(contact) } .filterNot { contact -> selectedChips.map { it.contact }.contains(contact) } .filter { contact -> contactFilter.filter(contact, normalizedQuery) } .map(::Person) } Loading @@ -301,47 +302,42 @@ class ComposeViewModel @Inject constructor( // Backspaces should delete the most recent contact if there's no text input // Close the activity if user presses back view.queryBackspaceIntent .withLatestFrom(selectedContacts, view.queryChangedIntent) { event, contacts, query -> .withLatestFrom(selectedChips, view.queryChangedIntent) { event, contacts, query -> if (contacts.isNotEmpty() && query.isEmpty()) { contactsReducer.onNext { it.dropLast(1) } chipsReducer.onNext { it.dropLast(1) } } } .autoDisposable(view.scope()) .subscribe() // Enter the first contact suggestion if the enter button is pressed // Enter the first contact suggestion if the enter button is pressed, and enter contacts that are selected view.queryEditorActionIntent .filter { actionId -> actionId == EditorInfo.IME_ACTION_DONE } .withLatestFrom(state) { _, state -> state } .mapNotNull { state -> state.composeItems.firstOrNull() } .mergeWith(view.chipSelectedIntent) .autoDisposable(view.scope()) .subscribe { state -> .subscribe { composeItem -> newState { copy(searching = false) } state.composeItems.firstOrNull()?.let { composeItem -> contactsReducer.onNext { contacts -> contacts + composeItem.getContacts() } chipsReducer.onNext { chips -> chips + composeItem.getContacts().map { Chip(it.numbers.first()?.address.orEmpty(), it) } } } // Update the list of selected contacts when a new contact is selected or an existing one is deselected Observable.merge( view.chipDeletedIntent.doOnNext { contact -> contactsReducer.onNext { contacts -> view.chipDeletedIntent .skipUntil(state.filter { state -> state.editingMode }) .takeUntil(state.filter { state -> !state.editingMode }) .autoDisposable(view.scope()) .subscribe { contact -> chipsReducer.onNext { contacts -> val result = contacts.filterNot { it == contact } if (result.isEmpty()) { newState { copy(searching = true) } } result } }, view.chipSelectedIntent.doOnNext { composeItem -> newState { copy(searching = false) } contactsReducer.onNext { contacts -> contacts.toMutableList().apply { addAll(composeItem.getContacts()) } } }) .skipUntil(state.filter { state -> state.editingMode }) .takeUntil(state.filter { state -> !state.editingMode }) .autoDisposable(view.scope()) .subscribe() // When the menu is loaded, trigger a new state so that the menu options can be rendered correctly view.menuReadyIntent Loading Loading @@ -667,12 +663,12 @@ class ComposeViewModel @Inject constructor( .filter { permissionManager.hasSendSms().also { if (!it) view.requestSmsPermission() } } .withLatestFrom(view.textChangedIntent) { _, body -> body } .map { body -> body.toString() } .withLatestFrom(state, attachments, conversation, selectedContacts) { body, state, attachments, conversation, contacts -> .withLatestFrom(state, attachments, conversation, selectedChips) { body, state, attachments, conversation, chips -> val subId = state.subscription?.subscriptionId ?: -1 val addresses = when (conversation.recipients.isNotEmpty()) { true -> conversation.recipients.map { it.address } false -> contacts.mapNotNull { it.numbers.firstOrNull()?.address } false -> chips.map { chip -> chip.address } } val delay = when (prefs.sendDelay.get()) { Preferences.SEND_DELAY_SHORT -> 3000 Loading presentation/src/main/java/com/moez/QKSMS/feature/compose/editing/Chip.kt 0 → 100644 +26 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 Moez Bhatti <moez.bhatti@gmail.com> * * This file is part of QKSMS. * * QKSMS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * QKSMS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with QKSMS. If not, see <http://www.gnu.org/licenses/>. */ package com.moez.QKSMS.feature.compose.editing import com.moez.QKSMS.model.Contact data class Chip( val address: String, val contact: Contact? = null ) Loading
presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt +5 −5 Original line number Diff line number Diff line Loading @@ -54,11 +54,11 @@ import com.moez.QKSMS.common.util.extensions.setBackgroundTint import com.moez.QKSMS.common.util.extensions.setTint import com.moez.QKSMS.common.util.extensions.setVisible import com.moez.QKSMS.common.util.extensions.showKeyboard import com.moez.QKSMS.feature.compose.editing.Chip import com.moez.QKSMS.feature.compose.editing.ChipsAdapter import com.moez.QKSMS.feature.compose.editing.ComposeItem import com.moez.QKSMS.feature.compose.editing.ComposeItemAdapter import com.moez.QKSMS.model.Attachment import com.moez.QKSMS.model.Contact import com.uber.autodispose.android.lifecycle.scope import com.uber.autodispose.autoDisposable import dagger.android.AndroidInjection Loading Loading @@ -91,7 +91,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView { override val queryBackspaceIntent: Observable<*> by lazy { search.backspaces } override val queryEditorActionIntent: Observable<Int> by lazy { search.editorActions() } override val chipSelectedIntent: Subject<ComposeItem> by lazy { contactsAdapter.itemSelected } override val chipDeletedIntent: Subject<Contact> by lazy { chipsAdapter.chipDeleted } override val chipDeletedIntent: Subject<Chip> by lazy { chipsAdapter.chipDeleted } override val menuReadyIntent: Observable<Unit> = menu.map { Unit } override val optionsItemIntent: Subject<Int> = PublishSubject.create() override val sendAsGroupIntent by lazy { sendAsGroupBackground.clicks() } Loading Loading @@ -212,16 +212,16 @@ class ComposeActivity : QkThemedActivity(), ComposeView { toolbar.menu.findItem(R.id.next)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty() toolbar.menu.findItem(R.id.clear)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty() if (chipsAdapter.data.isEmpty() && state.selectedContacts.isNotEmpty()) { if (chipsAdapter.data.isEmpty() && state.selectedChips.isNotEmpty()) { message.showKeyboard() } chipsAdapter.data = state.selectedContacts chipsAdapter.data = state.selectedChips contactsAdapter.data = state.composeItems loading.setVisible(state.loading) sendAsGroup.setVisible(state.editingMode && state.selectedContacts.size >= 2) sendAsGroup.setVisible(state.editingMode && state.selectedChips.size >= 2) sendAsGroupSwitch.isChecked = state.sendAsGroup messageList.setVisible(state.sendAsGroup) Loading
presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeState.kt +2 −2 Original line number Diff line number Diff line Loading @@ -19,9 +19,9 @@ package com.moez.QKSMS.feature.compose import com.moez.QKSMS.compat.SubscriptionInfoCompat import com.moez.QKSMS.feature.compose.editing.Chip import com.moez.QKSMS.feature.compose.editing.ComposeItem import com.moez.QKSMS.model.Attachment import com.moez.QKSMS.model.Contact import com.moez.QKSMS.model.Conversation import com.moez.QKSMS.model.Message import io.realm.RealmResults Loading @@ -32,7 +32,7 @@ data class ComposeState( val searching: Boolean = false, val composeItems: List<ComposeItem> = ArrayList(), val selectedConversation: Long = 0, val selectedContacts: List<Contact> = ArrayList(), val selectedChips: List<Chip> = ArrayList(), val sendAsGroup: Boolean = true, val conversationtitle: String = "", val loading: Boolean = false, Loading
presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt +2 −2 Original line number Diff line number Diff line Loading @@ -22,9 +22,9 @@ import android.net.Uri import androidx.annotation.StringRes import androidx.core.view.inputmethod.InputContentInfoCompat import com.moez.QKSMS.common.base.QkView import com.moez.QKSMS.feature.compose.editing.Chip import com.moez.QKSMS.feature.compose.editing.ComposeItem import com.moez.QKSMS.model.Attachment import com.moez.QKSMS.model.Contact import io.reactivex.Observable import io.reactivex.subjects.Subject Loading @@ -35,7 +35,7 @@ interface ComposeView : QkView<ComposeState> { val queryBackspaceIntent: Observable<*> val queryEditorActionIntent: Observable<Int> val chipSelectedIntent: Subject<ComposeItem> val chipDeletedIntent: Subject<Contact> val chipDeletedIntent: Subject<Chip> val menuReadyIntent: Observable<Unit> val optionsItemIntent: Observable<Int> val sendAsGroupIntent: Observable<*> Loading
presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt +32 −36 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.moez.QKSMS.extensions.isImage import com.moez.QKSMS.extensions.isVideo import com.moez.QKSMS.extensions.mapNotNull import com.moez.QKSMS.extensions.removeAccents import com.moez.QKSMS.feature.compose.editing.Chip import com.moez.QKSMS.feature.compose.editing.ComposeItem import com.moez.QKSMS.feature.compose.editing.ComposeItem.* import com.moez.QKSMS.filter.ContactFilter Loading Loading @@ -118,11 +119,11 @@ class ComposeViewModel @Inject constructor( private val attachments: Subject<List<Attachment>> = BehaviorSubject.createDefault(sharedAttachments) private val contactGroups: Observable<List<ContactGroup>> by lazy { contactsRepo.getUnmanagedContactGroups() } private val contacts: Observable<List<Contact>> by lazy { contactsRepo.getUnmanagedContacts() } private val contactsReducer: Subject<(List<Contact>) -> List<Contact>> = PublishSubject.create() private val chipsReducer: Subject<(List<Chip>) -> List<Chip>> = PublishSubject.create() private val conversation: Subject<Conversation> = BehaviorSubject.create() private val messages: Subject<List<Message>> = BehaviorSubject.create() private val recents: Observable<List<Conversation>> by lazy { conversationRepo.getUnmanagedConversations() } private val selectedContacts: Subject<List<Contact>> = BehaviorSubject.createDefault(listOf()) private val selectedChips: Subject<List<Chip>> = BehaviorSubject.createDefault(listOf()) private val searchResults: Subject<List<Message>> = BehaviorSubject.create() private val searchSelection: Subject<Long> = BehaviorSubject.createDefault(-1) private val starredContacts: Observable<List<Contact>> by lazy { contactsRepo.getUnmanagedContacts(true) } Loading @@ -133,9 +134,9 @@ class ComposeViewModel @Inject constructor( ?.asObservable() ?: Observable.empty() val selectedConversation = selectedContacts val selectedConversation = selectedChips .skipWhile { it.isEmpty() } .map { contacts -> contacts.map { it.numbers.firstOrNull()?.address ?: "" } } .map { chips -> chips.map { it.address } } .distinctUntilChanged() .doOnNext { newState { copy(loading = true) } } .observeOn(Schedulers.io()) Loading Loading @@ -177,15 +178,15 @@ class ComposeViewModel @Inject constructor( .subscribe(conversation::onNext) if (address.isNotBlank()) { selectedContacts.onNext(listOf(Contact(numbers = RealmList(PhoneNumber(address))))) selectedChips.onNext(listOf(Chip(address))) } disposables += contactsReducer .scan(listOf<Contact>()) { previousState, reducer -> reducer(previousState) } .doOnNext { contacts -> newState { copy(selectedContacts = contacts) } } disposables += chipsReducer .scan(listOf<Chip>()) { previousState, reducer -> reducer(previousState) } .doOnNext { chips -> newState { copy(selectedChips = chips) } } .skipUntil(state.filter { state -> state.editingMode }) .takeUntil(state.filter { state -> !state.editingMode }) .subscribe(selectedContacts::onNext) .subscribe(selectedChips::onNext) // When the conversation changes, mark read, and update the threadId and the messages for the adapter disposables += conversation Loading Loading @@ -256,8 +257,8 @@ class ComposeViewModel @Inject constructor( // that have already been selected Observables .combineLatest( view.queryChangedIntent, recents, starredContacts, contactGroups, contacts, selectedContacts ) { query, recents, starredContacts, contactGroups, contacts, selectedContacts -> view.queryChangedIntent, recents, starredContacts, contactGroups, contacts, selectedChips ) { query, recents, starredContacts, contactGroups, contacts, selectedChips -> val composeItems = mutableListOf<ComposeItem>() if (query.isBlank()) { composeItems += recents.map(::Recent) Loading @@ -276,7 +277,7 @@ class ComposeViewModel @Inject constructor( // cache the result instead of doing it for each contact val normalizedQuery = query.removeAccents() composeItems += starredContacts .filterNot { contact -> selectedContacts.contains(contact) } .filterNot { contact -> selectedChips.map { it.contact }.contains(contact) } .filter { contact -> contactFilter.filter(contact, normalizedQuery) } .map(::Starred) Loading @@ -285,7 +286,7 @@ class ComposeViewModel @Inject constructor( .map(::Group) composeItems += contacts .filterNot { contact -> selectedContacts.contains(contact) } .filterNot { contact -> selectedChips.map { it.contact }.contains(contact) } .filter { contact -> contactFilter.filter(contact, normalizedQuery) } .map(::Person) } Loading @@ -301,47 +302,42 @@ class ComposeViewModel @Inject constructor( // Backspaces should delete the most recent contact if there's no text input // Close the activity if user presses back view.queryBackspaceIntent .withLatestFrom(selectedContacts, view.queryChangedIntent) { event, contacts, query -> .withLatestFrom(selectedChips, view.queryChangedIntent) { event, contacts, query -> if (contacts.isNotEmpty() && query.isEmpty()) { contactsReducer.onNext { it.dropLast(1) } chipsReducer.onNext { it.dropLast(1) } } } .autoDisposable(view.scope()) .subscribe() // Enter the first contact suggestion if the enter button is pressed // Enter the first contact suggestion if the enter button is pressed, and enter contacts that are selected view.queryEditorActionIntent .filter { actionId -> actionId == EditorInfo.IME_ACTION_DONE } .withLatestFrom(state) { _, state -> state } .mapNotNull { state -> state.composeItems.firstOrNull() } .mergeWith(view.chipSelectedIntent) .autoDisposable(view.scope()) .subscribe { state -> .subscribe { composeItem -> newState { copy(searching = false) } state.composeItems.firstOrNull()?.let { composeItem -> contactsReducer.onNext { contacts -> contacts + composeItem.getContacts() } chipsReducer.onNext { chips -> chips + composeItem.getContacts().map { Chip(it.numbers.first()?.address.orEmpty(), it) } } } // Update the list of selected contacts when a new contact is selected or an existing one is deselected Observable.merge( view.chipDeletedIntent.doOnNext { contact -> contactsReducer.onNext { contacts -> view.chipDeletedIntent .skipUntil(state.filter { state -> state.editingMode }) .takeUntil(state.filter { state -> !state.editingMode }) .autoDisposable(view.scope()) .subscribe { contact -> chipsReducer.onNext { contacts -> val result = contacts.filterNot { it == contact } if (result.isEmpty()) { newState { copy(searching = true) } } result } }, view.chipSelectedIntent.doOnNext { composeItem -> newState { copy(searching = false) } contactsReducer.onNext { contacts -> contacts.toMutableList().apply { addAll(composeItem.getContacts()) } } }) .skipUntil(state.filter { state -> state.editingMode }) .takeUntil(state.filter { state -> !state.editingMode }) .autoDisposable(view.scope()) .subscribe() // When the menu is loaded, trigger a new state so that the menu options can be rendered correctly view.menuReadyIntent Loading Loading @@ -667,12 +663,12 @@ class ComposeViewModel @Inject constructor( .filter { permissionManager.hasSendSms().also { if (!it) view.requestSmsPermission() } } .withLatestFrom(view.textChangedIntent) { _, body -> body } .map { body -> body.toString() } .withLatestFrom(state, attachments, conversation, selectedContacts) { body, state, attachments, conversation, contacts -> .withLatestFrom(state, attachments, conversation, selectedChips) { body, state, attachments, conversation, chips -> val subId = state.subscription?.subscriptionId ?: -1 val addresses = when (conversation.recipients.isNotEmpty()) { true -> conversation.recipients.map { it.address } false -> contacts.mapNotNull { it.numbers.firstOrNull()?.address } false -> chips.map { chip -> chip.address } } val delay = when (prefs.sendDelay.get()) { Preferences.SEND_DELAY_SHORT -> 3000 Loading
presentation/src/main/java/com/moez/QKSMS/feature/compose/editing/Chip.kt 0 → 100644 +26 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 Moez Bhatti <moez.bhatti@gmail.com> * * This file is part of QKSMS. * * QKSMS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * QKSMS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with QKSMS. If not, see <http://www.gnu.org/licenses/>. */ package com.moez.QKSMS.feature.compose.editing import com.moez.QKSMS.model.Contact data class Chip( val address: String, val contact: Contact? = null )