feat: update group creation composables and let contact parcelize

This commit is contained in:
0x330a 2023-11-02 17:34:52 +11:00
parent fabe2d44da
commit 6c3ac9bc5d
No known key found for this signature in database
GPG Key ID: 267811D6E6A2698C
5 changed files with 69 additions and 26 deletions

View File

@ -7,15 +7,14 @@ import android.view.ViewGroup
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import com.ramcosta.composedestinations.DestinationsNavHost import com.ramcosta.composedestinations.DestinationsNavHost
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.dependency import com.ramcosta.composedestinations.navigation.dependency
import com.ramcosta.composedestinations.result.NavResult
import com.ramcosta.composedestinations.result.ResultBackNavigator import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.result.ResultRecipient import com.ramcosta.composedestinations.result.ResultRecipient
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -25,6 +24,7 @@ import org.thoughtcrime.securesms.conversation.start.NewConversationDelegate
import org.thoughtcrime.securesms.groups.compose.CreateGroup import org.thoughtcrime.securesms.groups.compose.CreateGroup
import org.thoughtcrime.securesms.groups.compose.CreateGroupNavGraph import org.thoughtcrime.securesms.groups.compose.CreateGroupNavGraph
import org.thoughtcrime.securesms.groups.compose.SelectContacts import org.thoughtcrime.securesms.groups.compose.SelectContacts
import org.thoughtcrime.securesms.groups.compose.StateUpdate
import org.thoughtcrime.securesms.groups.compose.ViewState import org.thoughtcrime.securesms.groups.compose.ViewState
import org.thoughtcrime.securesms.groups.destinations.SelectContactScreenDestination import org.thoughtcrime.securesms.groups.destinations.SelectContactScreenDestination
import org.thoughtcrime.securesms.ui.AppTheme import org.thoughtcrime.securesms.ui.AppTheme
@ -64,11 +64,19 @@ fun CreateGroupScreen(
getDelegate: () -> NewConversationDelegate getDelegate: () -> NewConversationDelegate
) { ) {
val viewState by viewModel.viewState.observeAsState(ViewState.DEFAULT) val viewState by viewModel.viewState.observeAsState(ViewState.DEFAULT)
val lifecycleScope = rememberCoroutineScope()
val context = LocalContext.current resultSelectContact.onNavResult { navResult ->
when (navResult) {
is NavResult.Value -> {
viewModel.updateState(StateUpdate.AddContact(navResult.value))
}
is NavResult.Canceled -> { /* do nothing */ }
}
}
CreateGroup( CreateGroup(
viewState, viewState,
viewModel::updateState,
onClose = { onClose = {
getDelegate().onDialogClosePressed() getDelegate().onDialogClosePressed()
}, },
@ -90,11 +98,12 @@ fun SelectContactScreen(
val viewState by viewModel.viewState.observeAsState(ViewState.DEFAULT) val viewState by viewModel.viewState.observeAsState(ViewState.DEFAULT)
val currentMembers = viewState.members val currentMembers = viewState.members
val contacts by viewModel.contacts.observeAsState(initial = emptyList()) val contacts by viewModel.contacts.observeAsState(initial = emptySet())
SelectContacts( SelectContacts(
contacts - currentMembers, contacts - currentMembers,
onBack = { resultNavigator.navigateBack() }, onBack = { resultNavigator.navigateBack() },
onClose = { getDelegate().onDialogClosePressed() } onClose = { getDelegate().onDialogClosePressed() },
onContactsSelected = {}
) )
} }

View File

@ -8,6 +8,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.Storage
import org.thoughtcrime.securesms.groups.compose.StateUpdate
import org.thoughtcrime.securesms.groups.compose.ViewState import org.thoughtcrime.securesms.groups.compose.ViewState
import javax.inject.Inject import javax.inject.Inject
@ -16,10 +17,30 @@ class CreateGroupViewModel @Inject constructor(
private val storage: Storage, private val storage: Storage,
) : ViewModel() { ) : ViewModel() {
private val _viewState = MutableLiveData(ViewState.DEFAULT) private fun create() {
val viewState: LiveData<ViewState> = _viewState tryCreateGroup()
}
val contacts = liveData { emit(storage.getAllContacts()) } private inline fun <reified T> MutableLiveData<T>.update(body: T.() -> T) {
this.postValue(body(this.value!!))
}
private val _viewState = MutableLiveData(ViewState.DEFAULT.copy())
val viewState: LiveData<ViewState> = _viewState
fun updateState(stateUpdate: StateUpdate) {
when (stateUpdate) {
is StateUpdate.AddContact -> _viewState.update { copy(members = members + stateUpdate.value) }
is StateUpdate.Description -> _viewState.update { copy(description = stateUpdate.value) }
is StateUpdate.Name -> _viewState.update { copy(name = stateUpdate.value) }
is StateUpdate.RemoveContact -> _viewState.update { copy(members = members - stateUpdate.value) }
StateUpdate.Create -> { tryCreateGroup() }
}
}
val contacts
get() = liveData { emit(storage.getAllContacts()) }
fun tryCreateGroup(): Recipient? { fun tryCreateGroup(): Recipient? {
@ -46,7 +67,10 @@ class CreateGroupViewModel @Inject constructor(
if (members.size <= 1) { if (members.size <= 1) {
_viewState.postValue( _viewState.postValue(
currentState.copy(isLoading = false, error = R.string.activity_create_closed_group_not_enough_group_members_error) currentState.copy(
isLoading = false,
error = R.string.activity_create_closed_group_not_enough_group_members_error
)
) )
} }

View File

@ -32,7 +32,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
import org.thoughtcrime.securesms.groups.compose.ViewState.StateUpdate
import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
import org.thoughtcrime.securesms.ui.Divider import org.thoughtcrime.securesms.ui.Divider
import org.thoughtcrime.securesms.ui.EditableAvatar import org.thoughtcrime.securesms.ui.EditableAvatar
@ -49,6 +48,7 @@ data class CreateGroupState (
@Composable @Composable
fun CreateGroup( fun CreateGroup(
viewState: ViewState, viewState: ViewState,
updateState: (StateUpdate) -> Unit,
onSelectContact: () -> Unit, onSelectContact: () -> Unit,
onBack: () -> Unit, onBack: () -> Unit,
onClose: () -> Unit, onClose: () -> Unit,
@ -80,7 +80,7 @@ fun CreateGroup(
val nameDescription = stringResource(id = R.string.AccessibilityId_closed_group_edit_group_name) val nameDescription = stringResource(id = R.string.AccessibilityId_closed_group_edit_group_name)
OutlinedTextField( OutlinedTextField(
value = viewState.name, value = viewState.name,
onValueChange = { viewState.updateState(StateUpdate.Name(it)) }, onValueChange = { updateState(StateUpdate.Name(it)) },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
@ -93,7 +93,7 @@ fun CreateGroup(
val descriptionDescription = stringResource(id = R.string.AccessibilityId_closed_group_edit_group_description) val descriptionDescription = stringResource(id = R.string.AccessibilityId_closed_group_edit_group_description)
OutlinedTextField( OutlinedTextField(
value = viewState.description, value = viewState.description,
onValueChange = { viewState.updateState(StateUpdate.Description(it)) }, onValueChange = { updateState(StateUpdate.Description(it)) },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
@ -150,13 +150,13 @@ fun CreateGroup(
} }
// Group list // Group list
memberList(contacts = viewState.members, modifier = Modifier.padding(vertical = 8.dp, horizontal = 24.dp)) { deletedContact -> memberList(contacts = viewState.members, modifier = Modifier.padding(vertical = 8.dp, horizontal = 24.dp)) { deletedContact ->
viewState.updateState(StateUpdate.RemoveContact(deletedContact)) updateState(StateUpdate.RemoveContact(deletedContact))
} }
} }
// Create button // Create button
val createDescription = stringResource(id = R.string.AccessibilityId_create_closed_group_create_button) val createDescription = stringResource(id = R.string.AccessibilityId_create_closed_group_create_button)
OutlinedButton( OutlinedButton(
onClick = { viewState.create() }, onClick = { updateState(StateUpdate.Create) },
enabled = viewState.canCreate, enabled = viewState.canCreate,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
@ -203,6 +203,7 @@ fun ClosedGroupPreview(
viewState = ViewState.DEFAULT.copy( viewState = ViewState.DEFAULT.copy(
// override any preview parameters // override any preview parameters
), ),
updateState = {},
onSelectContact = {}, onSelectContact = {},
onBack = {}, onBack = {},
onClose = {}, onClose = {},
@ -216,22 +217,21 @@ data class ViewState(
val name: String = "", val name: String = "",
val description: String = "", val description: String = "",
val members: List<Contact> = emptyList(), val members: List<Contact> = emptyList(),
val updateState: (StateUpdate)->Unit,
val create: ()->Unit,
) { ) {
val canCreate val canCreate
get() = name.isNotEmpty() && members.isNotEmpty() get() = name.isNotEmpty() && members.isNotEmpty()
companion object { companion object {
val DEFAULT = ViewState(false, null, updateState = {}, create = {}) val DEFAULT = ViewState(false, null)
} }
sealed class StateUpdate { }
data class Name(val value: String): StateUpdate()
data class Description(val value: String): StateUpdate()
data class RemoveContact(val value: Contact): StateUpdate()
data class AddContact(val value: Contact): StateUpdate()
}
sealed class StateUpdate {
data object Create: StateUpdate()
data class Name(val value: String): StateUpdate()
data class Description(val value: String): StateUpdate()
data class RemoveContact(val value: Contact): StateUpdate()
data class AddContact(val value: Contact): StateUpdate()
} }

View File

@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.groups.compose
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -16,21 +17,23 @@ import org.thoughtcrime.securesms.ui.SearchBar
@Composable @Composable
fun SelectContacts( fun SelectContacts(
contactListState: List<Contact>, contactListState: Set<Contact>,
onBack: ()->Unit, onBack: ()->Unit,
onClose: ()->Unit, onClose: ()->Unit,
onContactsSelected: (List<Contact>) -> Unit,
) { ) {
var queryFilter by remember { mutableStateOf("") } var queryFilter by remember { mutableStateOf("") }
// May introduce more advanced filters // May introduce more advanced filters
val filtered = if (queryFilter.isEmpty()) contactListState val filtered = if (queryFilter.isEmpty()) contactListState.toList()
else { else {
contactListState contactListState
.filter { contact -> .filter { contact ->
contact.getSearchName() contact.getSearchName()
.contains(queryFilter) .contains(queryFilter)
} }
.toList()
} }
Column { Column {
@ -45,6 +48,10 @@ fun SelectContacts(
// Search Bar // Search Bar
SearchBar(queryFilter, onValueChanged = { value -> queryFilter = value }) SearchBar(queryFilter, onValueChanged = { value -> queryFilter = value })
} }
items(filtered) { contact ->
}
} }
} }

View File

@ -32,6 +32,9 @@ class Contact(
*/ */
var nickname: String? = null, var nickname: String? = null,
): Parcelable { ): Parcelable {
constructor(id: String): this(sessionID = id)
/** /**
* The name to display in the UI. For local use only. * The name to display in the UI. For local use only.
*/ */