feat: need to fix a persist for group members but group with selected contacts working now

This commit is contained in:
0x330a 2023-11-03 17:45:57 +11:00
parent c19bd3da3d
commit 5a2fdb0e83
7 changed files with 120 additions and 41 deletions

View File

@ -241,7 +241,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private val viewModel: ConversationViewModel by viewModels { private val viewModel: ConversationViewModel by viewModels {
var threadId = intent.getLongExtra(THREAD_ID, -1L) var threadId = intent.getLongExtra(THREAD_ID, -1L)
if (threadId == -1L) { if (threadId == -1L) {
intent.getParcelableExtra<Address>(ADDRESS)?.let { it -> intent.getParcelableExtra(ADDRESS, Address::class.java)?.let { it ->
threadId = threadDb.getThreadIdIfExistsFor(it.serialize()) threadId = threadDb.getThreadIdIfExistsFor(it.serialize())
if (threadId == -1L) { if (threadId == -1L) {
val sessionId = SessionId(it.serialize()) val sessionId = SessionId(it.serialize())

View File

@ -1,14 +1,17 @@
package org.thoughtcrime.securesms.groups package org.thoughtcrime.securesms.groups
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
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.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
@ -23,6 +26,7 @@ import kotlinx.parcelize.Parcelize
import network.loki.messenger.databinding.FragmentCreateGroupBinding import network.loki.messenger.databinding.FragmentCreateGroupBinding
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
import org.thoughtcrime.securesms.conversation.start.NewConversationDelegate import org.thoughtcrime.securesms.conversation.start.NewConversationDelegate
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
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
@ -59,7 +63,7 @@ class CreateGroupFragment : Fragment() {
} }
@Parcelize @Parcelize
data class ContactList(val contacts: List<Contact>) : Parcelable data class ContactList(val contacts: Set<Contact>) : Parcelable
@CreateGroupNavGraph(start = true) @CreateGroupNavGraph(start = true)
@Composable @Composable
@ -83,6 +87,18 @@ fun CreateGroupScreen(
} }
} }
val context = LocalContext.current
viewState.createdGroup?.let { group ->
SideEffect {
getDelegate().onDialogClosePressed()
val intent = Intent(context, ConversationActivityV2::class.java).apply {
putExtra(ConversationActivityV2.ADDRESS, group.address)
}
context.startActivity(intent)
}
}
CreateGroup( CreateGroup(
viewState, viewState,
viewModel::updateState, viewModel::updateState,

View File

@ -4,9 +4,10 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
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.StateUpdate
import org.thoughtcrime.securesms.groups.compose.ViewState import org.thoughtcrime.securesms.groups.compose.ViewState
@ -17,10 +18,6 @@ class CreateGroupViewModel @Inject constructor(
private val storage: Storage, private val storage: Storage,
) : ViewModel() { ) : ViewModel() {
private fun create() {
tryCreateGroup()
}
private inline fun <reified T> MutableLiveData<T>.update(body: T.() -> T) { private inline fun <reified T> MutableLiveData<T>.update(body: T.() -> T) {
this.postValue(body(this.value!!)) this.postValue(body(this.value!!))
} }
@ -35,14 +32,14 @@ class CreateGroupViewModel @Inject constructor(
is StateUpdate.Description -> _viewState.update { copy(description = stateUpdate.value) } is StateUpdate.Description -> _viewState.update { copy(description = stateUpdate.value) }
is StateUpdate.Name -> _viewState.update { copy(name = stateUpdate.value) } is StateUpdate.Name -> _viewState.update { copy(name = stateUpdate.value) }
is StateUpdate.RemoveContact -> _viewState.update { copy(members = members - stateUpdate.value) } is StateUpdate.RemoveContact -> _viewState.update { copy(members = members - stateUpdate.value) }
StateUpdate.Create -> { tryCreateGroup() } StateUpdate.Create -> { viewModelScope.launch { tryCreateGroup() } }
} }
} }
val contacts val contacts
get() = liveData { emit(storage.getAllContacts()) } get() = liveData { emit(storage.getAllContacts()) }
fun tryCreateGroup(): Recipient? { fun tryCreateGroup() {
val currentState = _viewState.value!! val currentState = _viewState.value!!
@ -55,10 +52,9 @@ class CreateGroupViewModel @Inject constructor(
// do some validation // do some validation
// need a name // need a name
if (name.isEmpty()) { if (name.isEmpty()) {
_viewState.postValue( return _viewState.postValue(
currentState.copy(isLoading = false, error = R.string.error) currentState.copy(isLoading = false, error = R.string.error)
) )
return null
} }
storage.getAllContacts().forEach { contact -> storage.getAllContacts().forEach { contact ->
@ -77,8 +73,14 @@ class CreateGroupViewModel @Inject constructor(
// make a group // make a group
val newGroup = storage.createNewGroup(name, description, members) val newGroup = storage.createNewGroup(name, description, members)
if (!newGroup.isPresent) { if (!newGroup.isPresent) {
_viewState.postValue(currentState.copy(isLoading = false, error = null)) // show a generic couldn't create or something?
return _viewState.postValue(currentState.copy(isLoading = false, error = null))
} else {
return _viewState.postValue(currentState.copy(
isLoading = false,
error = null,
createdGroup = newGroup.get())
)
} }
return newGroup.orNull()
} }
} }

View File

@ -14,6 +14,7 @@ import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButton
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -54,10 +55,54 @@ fun EmptyPlaceholder(modifier: Modifier = Modifier) {
} }
} }
fun LazyListScope.memberList( fun LazyListScope.multiSelectMemberList(
contacts: List<Contact>, contacts: List<Contact>,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onDelete: (Contact) -> Unit selectedContacts: Set<Contact> = emptySet(),
onListUpdated: (Set<Contact>)->Unit = {},
) {
items(contacts) { contact ->
val isSelected = selectedContacts.contains(contact)
val update = {
val newList =
if (isSelected) selectedContacts - contact
else selectedContacts + contact
onListUpdated(newList)
}
Row(modifier = modifier.fillMaxWidth()
.clickable(onClick = update)
.padding(vertical = 8.dp, horizontal = 24.dp),
verticalAlignment = CenterVertically
) {
ContactPhoto(
contact = contact,
modifier = Modifier
.size(48.dp)
)
MemberName(name = contact.getSearchName())
RadioButton(selected = isSelected, onClick = update)
}
}
}
@Composable
fun RowScope.MemberName(
name: String
) = Text(
text = name,
fontWeight = FontWeight.Bold,
modifier = Modifier
.weight(1f)
.padding(16.dp)
.align(CenterVertically)
)
fun LazyListScope.deleteMemberList(
contacts: List<Contact>,
modifier: Modifier = Modifier,
onDelete: (Contact) -> Unit,
) { ) {
item { item {
Text( Text(
@ -80,23 +125,16 @@ fun LazyListScope.memberList(
.size(48.dp) .size(48.dp)
.align(CenterVertically) .align(CenterVertically)
) )
Text( MemberName(name = contact.getSearchName())
text = contact.getSearchName(),
fontWeight = FontWeight.Bold,
modifier = Modifier
.weight(1f)
.padding(16.dp)
.align(CenterVertically)
)
Image( Image(
painterResource(id = R.drawable.ic_baseline_close_24), painterResource(id = R.drawable.ic_baseline_close_24),
null, null,
Modifier modifier = Modifier
.size(32.dp) .size(32.dp)
.align(CenterVertically) .align(CenterVertically)
.clickable { .clickable {
onDelete(contact) onDelete(contact)
} },
) )
} }
} }
@ -141,12 +179,9 @@ fun PreviewMemberList(
) )
PreviewTheme(themeResId = themeResId) { PreviewTheme(themeResId = themeResId) {
LazyColumn { LazyColumn {
memberList( multiSelectMemberList(
previewMembers.toList(), contacts = previewMembers.toList(),
Modifier.padding(vertical = 8.dp, horizontal = 24.dp) )
) { deleted ->
}
} }
} }
} }

View File

@ -32,6 +32,7 @@ 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.session.libsession.utilities.recipients.Recipient
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
@ -149,7 +150,7 @@ fun CreateGroup(
} }
} }
// Group list // Group list
memberList(contacts = viewState.members, modifier = Modifier.padding(vertical = 8.dp, horizontal = 24.dp)) { deletedContact -> deleteMemberList(contacts = viewState.members, modifier = Modifier.padding(vertical = 8.dp, horizontal = 24.dp)) { deletedContact ->
updateState(StateUpdate.RemoveContact(deletedContact)) updateState(StateUpdate.RemoveContact(deletedContact))
} }
} }
@ -218,6 +219,7 @@ 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 createdGroup: Recipient? = null,
) { ) {
val canCreate val canCreate
@ -234,5 +236,5 @@ sealed class StateUpdate {
data class Name(val value: String): StateUpdate() data class Name(val value: String): StateUpdate()
data class Description(val value: String): StateUpdate() data class Description(val value: String): StateUpdate()
data class RemoveContact(val value: Contact): StateUpdate() data class RemoveContact(val value: Contact): StateUpdate()
data class AddContacts(val value: List<Contact>): StateUpdate() data class AddContacts(val value: Set<Contact>): StateUpdate()
} }

View File

@ -1,26 +1,36 @@
package org.thoughtcrime.securesms.groups.compose package org.thoughtcrime.securesms.groups.compose
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
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
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
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.home.search.getSearchName import org.thoughtcrime.securesms.home.search.getSearchName
import org.thoughtcrime.securesms.ui.NavigationBar import org.thoughtcrime.securesms.ui.NavigationBar
import org.thoughtcrime.securesms.ui.SearchBar import org.thoughtcrime.securesms.ui.SearchBar
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun SelectContacts( fun SelectContacts(
contactListState: Set<Contact>, contactListState: Set<Contact>,
onBack: ()->Unit, onBack: ()->Unit,
onClose: ()->Unit, onClose: ()->Unit,
onContactsSelected: (List<Contact>) -> Unit, onContactsSelected: (Set<Contact>) -> Unit,
) { ) {
var queryFilter by remember { mutableStateOf("") } var queryFilter by remember { mutableStateOf("") }
@ -30,12 +40,16 @@ fun SelectContacts(
else { else {
contactListState contactListState
.filter { contact -> .filter { contact ->
contact.getSearchName() contact.getSearchName().lowercase()
.contains(queryFilter) .contains(queryFilter)
} }
.toList() .toList()
} }
var selected by remember {
mutableStateOf(emptySet<Contact>())
}
Column { Column {
NavigationBar( NavigationBar(
title = stringResource(id = R.string.activity_create_closed_group_select_contacts), title = stringResource(id = R.string.activity_create_closed_group_select_contacts),
@ -43,14 +57,24 @@ fun SelectContacts(
onClose = onClose onClose = onClose
) )
LazyColumn { LazyColumn(modifier = Modifier.weight(1f)) {
item { stickyHeader {
// Search Bar // Search Bar
SearchBar(queryFilter, onValueChanged = { value -> queryFilter = value }) SearchBar(queryFilter, onValueChanged = { value -> queryFilter = value })
} }
items(filtered) { contact -> multiSelectMemberList(
contacts = filtered.toList(),
selectedContacts = selected,
onListUpdated = { selected = it },
)
}
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) {
OutlinedButton(
onClick = { onContactsSelected(selected) },
shape = CircleShape,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp)) {
Text(stringResource(id = R.string.ok))
} }
} }
} }

View File

@ -118,7 +118,7 @@ data class ConfigurationSyncJob(val destination: Destination) : Job {
) )
return ConfigMessageInformation( return ConfigMessageInformation(
if (signingKey != null && ed25519PubKey != null) { if (signingKey != null && ed25519PubKey == null) {
SnodeAPI.buildAuthenticatedStoreBatchInfo( SnodeAPI.buildAuthenticatedStoreBatchInfo(
namespace(), namespace(),
message, message,