feat: add inviting members to group scaffolding, need to wire up the storage to create job and modify group state

This commit is contained in:
0x330a 2023-11-17 20:10:19 +11:00
parent 9275a2b0ec
commit cc24542af3
6 changed files with 163 additions and 30 deletions

View File

@ -1274,6 +1274,12 @@ open class Storage(
}
}
override fun inviteClosedGroupMembers(groupSessionId: String, invitees: List<String>) {
// don't try to process invitee acceptance if we aren't admin
if (configFactory.userGroups?.getClosedGroup(groupSessionId)?.hasAdminKey() != true) return
val infoConfig = configFactory
}
override fun setServerCapabilities(server: String, capabilities: List<String>) {
return DatabaseComponent.get(context).lokiAPIDatabase().setServerCapabilities(server, capabilities)
}

View File

@ -57,10 +57,6 @@ class CreateGroupViewModel @Inject constructor(
)
}
storage.getAllContacts().forEach { contact ->
members.add(contact)
}
if (members.size <= 1) {
_viewState.postValue(
currentState.copy(

View File

@ -41,12 +41,6 @@ import org.thoughtcrime.securesms.ui.NavigationBar
import org.thoughtcrime.securesms.ui.PreviewTheme
data class CreateGroupState (
var groupName: String,
var groupDescription: String,
val members: MutableSet<Contact>
)
@Composable
fun CreateGroup(
viewState: ViewState,

View File

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.groups.compose
import android.content.Context
import android.widget.Toast
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -22,6 +24,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@ -35,12 +38,19 @@ import app.cash.molecule.RecompositionMode.Immediate
import app.cash.molecule.launchMolecule
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.result.NavResult
import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.result.ResultRecipient
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import network.loki.messenger.R
import network.loki.messenger.libsession_util.util.GroupMember
import org.session.libsession.database.StorageProtocol
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.jobs.InviteContactsJob
import org.session.libsession.messaging.jobs.JobQueue
import org.thoughtcrime.securesms.groups.ContactList
import org.thoughtcrime.securesms.groups.destinations.EditClosedGroupInviteScreenDestination
import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
import org.thoughtcrime.securesms.ui.NavigationBar
@ -51,13 +61,21 @@ import org.thoughtcrime.securesms.ui.PreviewTheme
@Destination
fun EditClosedGroupScreen(
navigator: DestinationsNavigator,
resultSelectContact: ResultRecipient<EditClosedGroupInviteScreenDestination, ContactList>,
viewModel: EditGroupViewModel,
onFinish: () -> Unit
) {
val group by viewModel.viewState.collectAsState()
val context = LocalContext.current
val viewState = group.viewState
val eventSink = group.eventSink
resultSelectContact.onNavResult { navResult ->
if (navResult is NavResult.Value) {
eventSink(EditGroupEvent.InviteContacts(context, navResult.value))
}
}
EditGroupView(
onBack = {
onFinish()
@ -65,7 +83,7 @@ fun EditClosedGroupScreen(
onInvite = {
navigator.navigate(EditClosedGroupInviteScreenDestination)
},
viewState = viewState as EditGroupViewState.Group
viewState = viewState
)
}
@ -73,16 +91,31 @@ fun EditClosedGroupScreen(
@Composable
@Destination
fun EditClosedGroupInviteScreen(
navigator: DestinationsNavigator,
resultNavigator: ResultBackNavigator<ContactList>,
viewModel: EditGroupInviteViewModel,
) {
val state by viewModel.viewState.collectAsState()
val viewState = state.viewState
val currentMemberSessionIds = viewState.currentMembers.map { it.memberSessionId }
val eventSink = state.eventSink
SelectContacts(
viewState.allContacts
.filterNot { it.sessionID in currentMemberSessionIds }
.toSet(),
onBack = { resultNavigator.navigateBack() },
onContactsSelected = {
resultNavigator.navigateBack(ContactList(it))
},
)
}
class EditGroupViewModel @AssistedInject constructor(
@Assisted private val groupSessionId: String,
private val storage: StorageProtocol): ViewModel() {
private val storage: StorageProtocol,
): ViewModel() {
val viewState = viewModelScope.launchMolecule(Immediate) {
@ -113,14 +146,32 @@ class EditGroupViewModel @AssistedInject constructor(
val description = closedGroup.description
EditGroupState(
EditGroupViewState.Group(
EditGroupViewState(
groupName = name,
groupDescription = description,
memberStateList = closedGroupMembers,
admin = closedGroup.isUserAdmin
)
) { event ->
when (event) {
is EditGroupEvent.InviteContacts -> {
val sessionIds = event.contacts
val invite = InviteContactsJob(
groupSessionId,
sessionIds.contacts.map(Contact::sessionID).toTypedArray()
)
storage.inviteClosedGroupMembers(
groupSessionId,
sessionIds.contacts.map(Contact::sessionID)
)
JobQueue.shared.add(invite)
Toast.makeText(
event.context,
"Inviting ${event.contacts.contacts.size}",
Toast.LENGTH_LONG
).show()
}
}
}
}
@ -136,6 +187,33 @@ class EditGroupInviteViewModel @AssistedInject constructor(
private val storage: StorageProtocol
): ViewModel() {
val viewState = viewModelScope.launchMolecule(Immediate) {
val currentUserId = rememberSaveable {
storage.getUserPublicKey()!!
}
val contacts = remember {
storage.getAllContacts()
}
val closedGroupMembers = remember {
storage.getMembers(groupSessionId).map { member ->
MemberViewModel(
memberName = member.name,
memberSessionId = member.sessionId,
currentUser = member.sessionId == currentUserId,
memberState = memberStateOf(member)
)
}
}
EditGroupInviteState(
EditGroupInviteViewState(closedGroupMembers, contacts)
) { event ->
}
}
@AssistedFactory
interface Factory {
@ -148,7 +226,7 @@ class EditGroupInviteViewModel @AssistedInject constructor(
fun EditGroupView(
onBack: ()->Unit,
onInvite: ()->Unit,
viewState: EditGroupViewState.Group,
viewState: EditGroupViewState,
) {
val scaffoldState = rememberScaffoldState()
@ -238,7 +316,12 @@ fun EditGroupView(
data class EditGroupState(
val viewState: EditGroupViewState,
val eventSink: (Unit)->Unit
val eventSink: (EditGroupEvent) -> Unit
)
data class EditGroupInviteState(
val viewState: EditGroupInviteViewState,
val eventSink: (Unit) -> Unit
)
data class MemberViewModel(
@ -268,15 +351,23 @@ fun memberStateOf(member: GroupMember): MemberState = when {
else -> MemberState.Member
}
sealed class EditGroupViewState {
data class Group(
val groupName: String,
val groupDescription: String?,
val memberStateList: List<MemberViewModel>,
val admin: Boolean
): EditGroupViewState()
data class EditGroupViewState(
val groupName: String,
val groupDescription: String?,
val memberStateList: List<MemberViewModel>,
val admin: Boolean
)
sealed class EditGroupEvent {
data class InviteContacts(val context: Context,
val contacts: ContactList): EditGroupEvent()
}
data class EditGroupInviteViewState(
val currentMembers: List<MemberViewModel>,
val allContacts: Set<Contact>
)
@Preview
@Composable
fun PreviewList() {
@ -288,7 +379,7 @@ fun PreviewList() {
false
)
val viewState = EditGroupViewState.Group(
val viewState = EditGroupViewState(
"Preview",
"This is a preview description",
listOf(oneMember),

View File

@ -1,12 +1,18 @@
package org.thoughtcrime.securesms.groups.compose
import androidx.annotation.StringRes
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@ -16,22 +22,29 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush.Companion.verticalGradient
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import network.loki.messenger.R
import org.session.libsession.messaging.contacts.Contact
import org.thoughtcrime.securesms.home.search.getSearchName
import org.thoughtcrime.securesms.ui.CloseIcon
import org.thoughtcrime.securesms.ui.NavigationBar
import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.SearchBar
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SelectContacts(
contactListState: Set<Contact>,
onBack: ()->Unit,
onClose: ()->Unit,
onClose: (()->Unit)? = null,
onContactsSelected: (Set<Contact>) -> Unit,
@StringRes okButtonResId: Int = R.string.ok
) {
var queryFilter by remember { mutableStateOf("") }
@ -55,7 +68,11 @@ fun SelectContacts(
NavigationBar(
title = stringResource(id = R.string.activity_create_closed_group_select_contacts),
onBack = onBack,
actionElement = { CloseIcon(onClose) }
actionElement = {
if (onClose != null) {
CloseIcon(onClose)
}
}
)
LazyColumn(modifier = Modifier.weight(1f)) {
@ -70,15 +87,43 @@ fun SelectContacts(
onListUpdated = { selected = it },
)
}
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxWidth()
.background(
verticalGradient(
0f to Color.Transparent,
0.2f to MaterialTheme.colors.primaryVariant,
)
)
) {
OutlinedButton(
onClick = { onContactsSelected(selected) },
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp).defaultMinSize(minWidth = 128.dp),
border = BorderStroke(1.dp, MaterialTheme.colors.onPrimary),
shape = CircleShape,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp)) {
Text(stringResource(id = R.string.ok))
colors = ButtonDefaults.outlinedButtonColors(
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.onPrimary,
)
) {
Text(
stringResource(id = okButtonResId)
)
}
}
}
}
@Preview
@Composable
fun previewSelectContacts(
@PreviewParameter(ThemeResPreviewParameterProvider::class) themeRes: Int
) {
val empty = emptySet<Contact>()
PreviewTheme(themeResId = themeRes) {
SelectContacts(contactListState = empty, onBack = { /*TODO*/ }, onContactsSelected = {})
}
}

View File

@ -165,6 +165,7 @@ interface StorageProtocol {
fun setGroupInviteCompleteIfNeeded(approved: Boolean, invitee: String, closedGroup: SessionId)
fun getLibSessionClosedGroup(groupSessionId: String): GroupInfo.ClosedGroupInfo?
fun getClosedGroupDisplayInfo(groupSessionId: String): GroupDisplayInfo?
fun inviteClosedGroupMembers(groupSessionId: String, invitees: List<String>)
// Groups
fun getAllGroups(includeInactive: Boolean): List<GroupRecord>