feat: need to fix a persist for group members but group with selected contacts working now
This commit is contained in:
parent
c19bd3da3d
commit
5a2fdb0e83
|
@ -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())
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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 ->
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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()
|
||||||
}
|
}
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue