feat(WIP): group creation / navigation / passing and sharing state in vm

This commit is contained in:
0x330a 2023-11-01 16:39:34 +11:00
parent 82a55d256c
commit baf2157331
12 changed files with 339 additions and 89 deletions

View File

@ -47,12 +47,12 @@ android {
useLibrary 'org.apache.http.legacy' useLibrary 'org.apache.http.legacy'
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_11
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '11'
} }
packagingOptions { packagingOptions {
@ -207,6 +207,7 @@ dependencies {
implementation("com.google.dagger:hilt-android:2.46.1") implementation("com.google.dagger:hilt-android:2.46.1")
kapt("com.google.dagger:hilt-android-compiler:2.46") kapt("com.google.dagger:hilt-android-compiler:2.46")
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
implementation "io.github.raamcosta.compose-destinations:core:$composeDestinationsVersion" implementation "io.github.raamcosta.compose-destinations:core:$composeDestinationsVersion"
ksp "io.github.raamcosta.compose-destinations:ksp:$composeDestinationsVersion" ksp "io.github.raamcosta.compose-destinations:ksp:$composeDestinationsVersion"

View File

@ -1,40 +1,42 @@
package org.thoughtcrime.securesms.groups package org.thoughtcrime.securesms.groups
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
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.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier 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.fragment.app.viewModels import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.lifecycleScope import com.ramcosta.composedestinations.DestinationsNavHost
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.dependency
import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.result.ResultRecipient
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import network.loki.messenger.databinding.FragmentCreateGroupBinding import network.loki.messenger.databinding.FragmentCreateGroupBinding
import org.session.libsession.utilities.Device import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.utilities.recipients.Recipient
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.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.groups.compose.CreateGroup import org.thoughtcrime.securesms.groups.compose.CreateGroup
import org.thoughtcrime.securesms.groups.compose.CreateGroupState import org.thoughtcrime.securesms.groups.compose.CreateGroupNavGraph
import org.thoughtcrime.securesms.groups.compose.SelectContacts
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.ui.AppTheme import org.thoughtcrime.securesms.ui.AppTheme
import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class CreateGroupFragment : Fragment() { class CreateGroupFragment : Fragment() {
@Inject
lateinit var device: Device
private lateinit var binding: FragmentCreateGroupBinding private lateinit var binding: FragmentCreateGroupBinding
private val viewModel: CreateGroupViewModel by viewModels()
lateinit var delegate: NewConversationDelegate lateinit var delegate: NewConversationDelegate
@ -43,49 +45,77 @@ class CreateGroupFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
return ComposeView(requireContext()).apply { return ComposeView(requireContext()).apply {
val getDelegate = { delegate }
setContent { setContent {
// this is kind of annoying to require an initial state in the fragment and the VM AppTheme {
val currentState = viewModel.viewState.observeAsState(initial = ViewState.DEFAULT) DestinationsNavHost(navGraph = NavGraphs.createGroup, dependenciesContainerBuilder = {
// create group state might be useful in future for adding members and returning dependency(getDelegate)
// to the create group state with a copy or something })
CreateGroupScreen(currentState.value, createGroupState = CreateGroupState("", "", emptySet())) }
} }
} }
} }
private fun openConversationActivity(context: Context, recipient: Recipient) { }
val intent = Intent(context, ConversationActivityV2::class.java)
intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address) @CreateGroupNavGraph(start = true)
context.startActivity(intent) @Composable
} @Destination
fun CreateGroupScreen(
@Composable navigator: DestinationsNavigator,
fun CreateGroupScreen(viewState: ViewState, resultSelectContact: ResultRecipient<SelectContactScreenDestination, Contact?>,
createGroupState: CreateGroupState, viewModel: CreateGroupViewModel = hiltViewModel(),
modifier: Modifier = Modifier) { getDelegate: () -> NewConversationDelegate
AppTheme { ) {
CreateGroup( val viewState by viewModel.viewState.observeAsState(ViewState.DEFAULT)
viewState, val lifecycleScope = rememberCoroutineScope()
createGroupState, val context = LocalContext.current
onCreate = { newGroup -> val currentGroupState = viewModel.createGroupState
// launch something to create here
// dunno if we want to key this here as a launched effect on some property :thinking: CreateGroup(
lifecycleScope.launch(Dispatchers.IO) { viewState,
val groupRecipient = viewModel.tryCreateGroup(newGroup) currentGroupState,
groupRecipient?.let { recipient -> onCreate = { newGroup ->
openConversationActivity(requireContext(), recipient) // launch something to create here
delegate.onDialogClosePressed() // dunno if we want to key this here as a launched effect on some property :thinking:
} lifecycleScope.launch(Dispatchers.IO) {
} val groupRecipient = viewModel.tryCreateGroup(newGroup)
}, groupRecipient?.let { recipient ->
onClose = { // launch conversation with this new group
delegate.onDialogClosePressed() val intent = Intent(context, ConversationActivityV2::class.java)
}, intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address)
onBack = { context.startActivity(intent)
delegate.onDialogBackPressed() getDelegate().onDialogClosePressed()
} }
) }
} },
} onSelectContact = {
navigator.navigate(SelectContactScreenDestination)
},
onClose = {
getDelegate().onDialogClosePressed()
},
onBack = {
getDelegate().onDialogBackPressed()
}
)
}
@CreateGroupNavGraph
@Composable
@Destination
fun SelectContactScreen(
resultNavigator: ResultBackNavigator<Contact?>,
viewModel: CreateGroupViewModel = hiltViewModel(),
getDelegate: () -> NewConversationDelegate
) {
val contacts by viewModel.contacts.observeAsState(initial = emptyList())
val currentMembers by viewModel.createGroupState.observeAsState()
SelectContacts(
contacts - currentMembers?.members.orEmpty(),
onBack = { resultNavigator.navigateBack(null) },
onClose = { getDelegate().onDialogClosePressed() }
)
} }

View File

@ -3,9 +3,8 @@ package org.thoughtcrime.securesms.groups
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.liveData
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.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
@ -23,8 +22,14 @@ class CreateGroupViewModel @Inject constructor(
private val _viewState = MutableLiveData(ViewState.DEFAULT) private val _viewState = MutableLiveData(ViewState.DEFAULT)
val viewState: LiveData<ViewState> = _viewState val viewState: LiveData<ViewState> = _viewState
val createGroupState: MutableLiveData<CreateGroupState> = MutableLiveData(CreateGroupState("","", emptySet()))
val contacts = liveData {
emit(storage.getAllContacts().toList())
}
init { init {
viewModelScope.launch { // viewModelScope.launch {
// threadDb.approvedConversationList.use { openCursor -> // threadDb.approvedConversationList.use { openCursor ->
// val reader = threadDb.readerFor(openCursor) // val reader = threadDb.readerFor(openCursor)
// val recipients = mutableListOf<Recipient>() // val recipients = mutableListOf<Recipient>()
@ -36,7 +41,7 @@ class CreateGroupViewModel @Inject constructor(
// .filter { !it.isGroupRecipient && it.hasApprovedMe() && it.address.serialize() != textSecurePreferences.getLocalNumber() } // .filter { !it.isGroupRecipient && it.hasApprovedMe() && it.address.serialize() != textSecurePreferences.getLocalNumber() }
// } // }
// } // }
} // }
} }
fun tryCreateGroup(createGroupState: CreateGroupState): Recipient? { fun tryCreateGroup(createGroupState: CreateGroupState): Recipient? {

View File

@ -2,11 +2,14 @@ package org.thoughtcrime.securesms.groups.compose
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope 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
@ -14,7 +17,7 @@ import androidx.compose.material.MaterialTheme
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
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
@ -22,49 +25,79 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
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.Address import org.session.libsession.utilities.Address
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.home.search.getSearchName
import org.thoughtcrime.securesms.ui.Avatar import org.thoughtcrime.securesms.ui.Avatar
import org.thoughtcrime.securesms.ui.LocalPreviewMode import org.thoughtcrime.securesms.ui.LocalPreviewMode
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
@Composable @Composable
fun EmptyPlaceholder(modifier: Modifier = Modifier) { fun EmptyPlaceholder(modifier: Modifier = Modifier) {
Column { Column(modifier) {
Text( Text(
text = stringResource(id = R.string.conversation_settings_group_members), text = stringResource(id = R.string.activity_create_closed_group_empty_placeholer),
textAlign = TextAlign.Center,
modifier = Modifier modifier = Modifier
.align(Alignment.Start) .fillMaxWidth()
.padding(vertical = 8.dp)
)
// TODO group list representation
Text(
text = stringResource(id = R.string.activity_create_closed_group_not_enough_group_members_error),
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(vertical = 8.dp) .padding(vertical = 8.dp)
) )
} }
} }
@OptIn(ExperimentalGlideComposeApi::class)
fun LazyListScope.memberList( fun LazyListScope.memberList(
contacts: List<Contact>, contacts: List<Contact>,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onDelete: (Contact) -> Unit onDelete: (Contact) -> Unit
) { ) {
item {
Text(
text = stringResource(id = R.string.conversation_settings_group_members),
style = MaterialTheme.typography.subtitle2,
modifier = modifier
.padding(vertical = 8.dp)
)
}
if (contacts.isEmpty()) { if (contacts.isEmpty()) {
item { item {
EmptyPlaceholder(modifier.fillParentMaxWidth()) EmptyPlaceholder(modifier.fillMaxWidth())
} }
} else { } else {
items(contacts) { contact -> items(contacts) { contact ->
Row(modifier) { Row(modifier.fillMaxWidth()) {
ContactPhoto(contact, modifier = Modifier.size(48.dp)) ContactPhoto(
contact,
modifier = Modifier
.size(48.dp)
.align(CenterVertically)
)
Text(
text = contact.getSearchName(),
fontWeight = FontWeight.Bold,
modifier = Modifier
.weight(1f)
.padding(16.dp)
.align(CenterVertically)
)
Image(
painterResource(id = R.drawable.ic_baseline_close_24),
null,
Modifier
.size(32.dp)
.align(CenterVertically)
.clickable {
onDelete(contact)
}
)
} }
} }
} }
@ -86,9 +119,34 @@ fun RowScope.ContactPhoto(contact: Contact, modifier: Modifier = Modifier) {
) )
} else { } else {
val context = LocalContext.current val context = LocalContext.current
// Ideally we migrate to something that doesn't require recipient, or get contact photo another way
val recipient = remember(contact) { val recipient = remember(contact) {
Recipient.from(context, Address.fromSerialized(contact.sessionID), false) Recipient.from(context, Address.fromSerialized(contact.sessionID), false)
} }
Avatar(recipient) Avatar(recipient)
} }
} }
@Preview
@Composable
fun PreviewMemberList(
@PreviewParameter(ThemeResPreviewParameterProvider::class) themeResId: Int
) {
val random = "05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234"
val previewMembers = setOf(
Contact(random).apply {
name = "Person"
}
)
PreviewTheme(themeResId = themeResId) {
LazyColumn {
memberList(
previewMembers.toList(),
Modifier.padding(vertical = 8.dp, horizontal = 24.dp)
) { deleted ->
}
}
}
}

View File

@ -1,16 +1,18 @@
package org.thoughtcrime.securesms.groups.compose package org.thoughtcrime.securesms.groups.compose
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
@ -25,42 +27,44 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.MutableLiveData
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.ui.CellWithPaddingAndMargin
import org.thoughtcrime.securesms.ui.Divider
import org.thoughtcrime.securesms.ui.EditableAvatar import org.thoughtcrime.securesms.ui.EditableAvatar
import org.thoughtcrime.securesms.ui.NavigationBar import org.thoughtcrime.securesms.ui.NavigationBar
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
data class CreateGroupState ( data class CreateGroupState (
val groupName: String, var groupName: String,
val groupDescription: String, var groupDescription: String,
val members: Set<Contact> val members: MutableSet<Contact>
) )
@Composable @Composable
fun CreateGroup( fun CreateGroup(
viewState: ViewState, viewState: ViewState,
createGroupState: CreateGroupState, createGroupState: MutableLiveData<CreateGroupState>,
onCreate: (CreateGroupState) -> Unit, onCreate: (CreateGroupState) -> Unit,
onSelectContact: () -> Unit,
onBack: () -> Unit, onBack: () -> Unit,
onClose: () -> Unit, onClose: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
var name by remember { mutableStateOf(createGroupState.groupName) } var name by createGroupState
var description by remember { mutableStateOf(createGroupState.groupDescription) } var description by remember { mutableStateOf(createGroupState.groupDescription) }
var members by remember { mutableStateOf(createGroupState.members) } var members by remember { mutableStateOf(createGroupState.members) }
val scrollState = rememberScrollState()
val lazyState = rememberLazyListState() val lazyState = rememberLazyListState()
val onDeleteMember = { contact: Contact -> val onDeleteMember = { contact: Contact ->
@ -112,6 +116,50 @@ fun CreateGroup(
contentDescription = descriptionDescription contentDescription = descriptionDescription
}, },
) )
CellWithPaddingAndMargin(padding = 0.dp) {
Column(Modifier.fillMaxSize()) {
// Select Contacts
val padding = Modifier
.padding(8.dp)
.fillMaxWidth()
Row(padding.clickable {
onSelectContact()
}) {
Image(
painterResource(id = R.drawable.ic_person_white_24dp),
null,
Modifier
.padding(4.dp)
.align(Alignment.CenterVertically)
)
Text(
stringResource(id = R.string.activity_create_closed_group_select_contacts),
Modifier
.padding(4.dp)
.align(Alignment.CenterVertically)
)
}
Divider()
// Add account ID or ONS
Row(padding) {
Image(
painterResource(id = R.drawable.ic_baseline_add_24),
null,
Modifier
.padding(4.dp)
.align(Alignment.CenterVertically)
)
Text(
stringResource(id = R.string.activity_create_closed_group_add_account_or_ons),
Modifier
.padding(4.dp)
.align(Alignment.CenterVertically)
)
}
}
}
} }
} }
// Group list // Group list
@ -155,7 +203,6 @@ fun CreateGroup(
@Preview @Preview
@Composable @Composable
fun ClosedGroupPreview( fun ClosedGroupPreview(
@PreviewParameter(ThemeResPreviewParameterProvider::class) themeResId: Int
) { ) {
val random = "05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234" val random = "05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234"
val previewMembers = setOf( val previewMembers = setOf(
@ -163,12 +210,13 @@ fun ClosedGroupPreview(
name = "Person" name = "Person"
} }
) )
PreviewTheme(themeResId) { PreviewTheme(R.style.Theme_Session_DayNight_NoActionBar_Test) {
CreateGroup( CreateGroup(
viewState = ViewState(false, null), viewState = ViewState(false, null),
createGroupState = CreateGroupState("Group Name", "Test Group Description", previewMembers), createGroupState = CreateGroupState("Group Name", "Test Group Description", previewMembers),
onCreate = {}, onCreate = {},
onClose = {}, onClose = {},
onSelectContact = {},
onBack = {}, onBack = {},
) )
} }

View File

@ -0,0 +1,8 @@
package org.thoughtcrime.securesms.groups.compose
import com.ramcosta.composedestinations.annotation.NavGraph
@NavGraph
annotation class CreateGroupNavGraph(
val start: Boolean = false
)

View File

@ -0,0 +1,52 @@
package org.thoughtcrime.securesms.groups.compose
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import network.loki.messenger.R
import org.session.libsession.messaging.contacts.Contact
import org.thoughtcrime.securesms.home.search.getSearchName
import org.thoughtcrime.securesms.ui.NavigationBar
import org.thoughtcrime.securesms.ui.SearchBar
@Composable
fun SelectContacts(
contactListState: List<Contact>,
onBack: ()->Unit,
onClose: ()->Unit,
) {
var queryFilter by remember { mutableStateOf("") }
// May introduce more advanced filters
val filtered = if (queryFilter.isEmpty()) contactListState
else {
contactListState
.filter { contact ->
contact.getSearchName()
.contains(queryFilter)
}
}
Column {
NavigationBar(
title = stringResource(id = R.string.activity_create_closed_group_select_contacts),
onBack = onBack,
onClose = onClose
)
LazyColumn {
item {
// Search Bar
SearchBar(queryFilter, onValueChanged = { value -> queryFilter = value })
}
}
}
}

View File

@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.ButtonColors import androidx.compose.material.ButtonColors
import androidx.compose.material.Card import androidx.compose.material.Card
import androidx.compose.material.Colors import androidx.compose.material.Colors
@ -34,6 +35,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -225,6 +227,41 @@ fun EditableAvatar(
} }
} }
@Composable
fun SearchBar(
query: String,
onValueChanged: (String) -> Unit,
modifier: Modifier = Modifier
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 24.dp)
.background(MaterialTheme.colors.primaryVariant, RoundedCornerShape(100))
) {
Image(
painterResource(id = R.drawable.ic_search_24),
contentDescription = null,
colorFilter = ColorFilter.tint(
MaterialTheme.colors.onPrimary
),
modifier = Modifier.size(20.dp)
)
BasicTextField(
singleLine = true,
// label = { Text(text = stringResource(id = R.string.search_contacts_hint),modifier=Modifier.padding(0.dp)) },
value = query,
onValueChange = onValueChanged,
modifier = Modifier
.padding(start = 8.dp)
.padding(4.dp)
.weight(1f),
)
}
}
@Composable @Composable
fun NavigationBar( fun NavigationBar(
// //
@ -296,3 +333,11 @@ fun PreviewNavigationBar(@PreviewParameter(provider = ThemeResPreviewParameterPr
NavigationBar(title = "Create Group", onBack = {}, onClose = {}) NavigationBar(title = "Create Group", onBack = {}, onClose = {})
} }
} }
@Composable
@Preview
fun PreviewSearchBar(@PreviewParameter(provider = ThemeResPreviewParameterProvider::class) themeResId: Int) {
PreviewTheme(themeResId = themeResId) {
SearchBar("", {})
}
}

View File

@ -778,6 +778,7 @@
<string name="activity_create_closed_group_group_name_missing_error">Please enter a group name</string> <string name="activity_create_closed_group_group_name_missing_error">Please enter a group name</string>
<string name="activity_create_closed_group_group_name_too_long_error">Please enter a shorter group name</string> <string name="activity_create_closed_group_group_name_too_long_error">Please enter a shorter group name</string>
<string name="activity_create_closed_group_not_enough_group_members_error">Please pick at least 1 group member</string> <string name="activity_create_closed_group_not_enough_group_members_error">Please pick at least 1 group member</string>
<string name="activity_create_closed_group_empty_placeholer">You haven\'t added any members</string>
<string name="activity_create_closed_group_too_many_group_members_error">A closed group cannot have more than 100 members</string> <string name="activity_create_closed_group_too_many_group_members_error">A closed group cannot have more than 100 members</string>
<string name="activity_join_public_chat_title">Join Open Group</string> <string name="activity_join_public_chat_title">Join Open Group</string>
<string name="activity_join_public_chat_error">Couldn\'t join group</string> <string name="activity_join_public_chat_error">Couldn\'t join group</string>
@ -1073,4 +1074,6 @@
<string name="dialog_clear_all_messages_clear">Clear</string> <string name="dialog_clear_all_messages_clear">Clear</string>
<string name="dialog_clear_all_messages_cancel">Cancel</string> <string name="dialog_clear_all_messages_cancel">Cancel</string>
<string name="media_overview_activity__clear_media">Clear All</string> <string name="media_overview_activity__clear_media">Clear All</string>
<string name="activity_create_closed_group_select_contacts">Select Contacts</string>
<string name="activity_create_closed_group_add_account_or_ons">Add Account ID or ONS</string>
</resources> </resources>

View File

@ -30,11 +30,11 @@ android {
} }
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_11
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '11'
} }
} }

View File

@ -12,8 +12,8 @@ android {
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_11
} }
} }

View File

@ -9,8 +9,8 @@ android {
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_11
} }
} }