2023-11-08 23:23:20 +01:00
|
|
|
package org.thoughtcrime.securesms.groups.compose
|
|
|
|
|
2023-11-22 02:16:21 +01:00
|
|
|
import android.content.ContentResolver
|
2023-11-17 10:10:19 +01:00
|
|
|
import android.content.Context
|
|
|
|
import android.widget.Toast
|
2023-11-16 07:45:31 +01:00
|
|
|
import androidx.compose.foundation.clickable
|
2023-11-09 08:01:24 +01:00
|
|
|
import androidx.compose.foundation.layout.Column
|
|
|
|
import androidx.compose.foundation.layout.Row
|
2023-11-16 07:45:31 +01:00
|
|
|
import androidx.compose.foundation.layout.Spacer
|
2023-11-15 02:07:07 +01:00
|
|
|
import androidx.compose.foundation.layout.fillMaxHeight
|
2023-11-09 08:01:24 +01:00
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
|
|
import androidx.compose.foundation.layout.padding
|
2023-11-16 07:45:31 +01:00
|
|
|
import androidx.compose.foundation.layout.size
|
2023-11-09 08:01:24 +01:00
|
|
|
import androidx.compose.foundation.lazy.LazyColumn
|
|
|
|
import androidx.compose.foundation.lazy.items
|
2023-11-16 07:45:31 +01:00
|
|
|
import androidx.compose.material.Icon
|
|
|
|
import androidx.compose.material.MaterialTheme
|
2023-11-22 02:16:21 +01:00
|
|
|
import androidx.compose.material.OutlinedButton
|
2023-11-09 08:01:24 +01:00
|
|
|
import androidx.compose.material.Scaffold
|
2023-11-09 02:15:55 +01:00
|
|
|
import androidx.compose.material.Text
|
2023-11-09 08:01:24 +01:00
|
|
|
import androidx.compose.material.rememberScaffoldState
|
2023-11-08 23:23:20 +01:00
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
import androidx.compose.runtime.collectAsState
|
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
import androidx.compose.runtime.remember
|
2023-11-09 08:01:24 +01:00
|
|
|
import androidx.compose.runtime.saveable.rememberSaveable
|
|
|
|
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
2023-11-09 02:15:55 +01:00
|
|
|
import androidx.compose.ui.Modifier
|
2023-11-17 10:10:19 +01:00
|
|
|
import androidx.compose.ui.platform.LocalContext
|
2023-11-16 07:45:31 +01:00
|
|
|
import androidx.compose.ui.res.painterResource
|
2023-11-09 08:01:24 +01:00
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
|
|
import androidx.compose.ui.text.style.TextAlign
|
2023-11-15 02:07:07 +01:00
|
|
|
import androidx.compose.ui.tooling.preview.Preview
|
2023-11-09 08:01:24 +01:00
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
|
import androidx.compose.ui.unit.sp
|
2023-11-08 23:23:20 +01:00
|
|
|
import androidx.lifecycle.ViewModel
|
|
|
|
import androidx.lifecycle.viewModelScope
|
|
|
|
import app.cash.molecule.RecompositionMode.Immediate
|
|
|
|
import app.cash.molecule.launchMolecule
|
|
|
|
import com.ramcosta.composedestinations.annotation.Destination
|
|
|
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
2023-11-17 10:10:19 +01:00
|
|
|
import com.ramcosta.composedestinations.result.NavResult
|
|
|
|
import com.ramcosta.composedestinations.result.ResultBackNavigator
|
|
|
|
import com.ramcosta.composedestinations.result.ResultRecipient
|
2023-11-09 02:15:55 +01:00
|
|
|
import dagger.assisted.Assisted
|
|
|
|
import dagger.assisted.AssistedFactory
|
|
|
|
import dagger.assisted.AssistedInject
|
2023-11-09 08:01:24 +01:00
|
|
|
import network.loki.messenger.R
|
|
|
|
import network.loki.messenger.libsession_util.util.GroupMember
|
2023-11-08 23:23:20 +01:00
|
|
|
import org.session.libsession.database.StorageProtocol
|
2023-11-17 10:10:19 +01:00
|
|
|
import org.session.libsession.messaging.contacts.Contact
|
2023-11-22 02:16:21 +01:00
|
|
|
import org.session.libsession.messaging.jobs.InviteContactsJob
|
|
|
|
import org.session.libsession.messaging.jobs.JobQueue
|
2023-11-17 10:10:19 +01:00
|
|
|
import org.thoughtcrime.securesms.groups.ContactList
|
2023-11-16 07:45:31 +01:00
|
|
|
import org.thoughtcrime.securesms.groups.destinations.EditClosedGroupInviteScreenDestination
|
|
|
|
import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
|
2023-11-09 08:01:24 +01:00
|
|
|
import org.thoughtcrime.securesms.ui.NavigationBar
|
2023-11-15 02:07:07 +01:00
|
|
|
import org.thoughtcrime.securesms.ui.PreviewTheme
|
2023-11-08 23:23:20 +01:00
|
|
|
|
|
|
|
@EditGroupNavGraph(start = true)
|
|
|
|
@Composable
|
|
|
|
@Destination
|
|
|
|
fun EditClosedGroupScreen(
|
|
|
|
navigator: DestinationsNavigator,
|
2023-11-17 10:10:19 +01:00
|
|
|
resultSelectContact: ResultRecipient<EditClosedGroupInviteScreenDestination, ContactList>,
|
2023-11-09 08:01:24 +01:00
|
|
|
viewModel: EditGroupViewModel,
|
|
|
|
onFinish: () -> Unit
|
2023-11-08 23:23:20 +01:00
|
|
|
) {
|
2023-11-09 02:15:55 +01:00
|
|
|
val group by viewModel.viewState.collectAsState()
|
2023-11-17 10:10:19 +01:00
|
|
|
val context = LocalContext.current
|
2023-11-09 02:15:55 +01:00
|
|
|
val viewState = group.viewState
|
|
|
|
val eventSink = group.eventSink
|
|
|
|
|
2023-11-17 10:10:19 +01:00
|
|
|
resultSelectContact.onNavResult { navResult ->
|
|
|
|
if (navResult is NavResult.Value) {
|
|
|
|
eventSink(EditGroupEvent.InviteContacts(context, navResult.value))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-09 08:01:24 +01:00
|
|
|
EditGroupView(
|
|
|
|
onBack = {
|
|
|
|
onFinish()
|
|
|
|
},
|
2023-11-16 07:45:31 +01:00
|
|
|
onInvite = {
|
|
|
|
navigator.navigate(EditClosedGroupInviteScreenDestination)
|
|
|
|
},
|
2023-11-22 02:16:21 +01:00
|
|
|
onReinvite = { contact ->
|
|
|
|
eventSink(EditGroupEvent.ReInviteContact(contact))
|
|
|
|
},
|
2023-11-17 10:10:19 +01:00
|
|
|
viewState = viewState
|
2023-11-09 08:01:24 +01:00
|
|
|
)
|
2023-11-16 07:45:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@EditGroupNavGraph
|
|
|
|
@Composable
|
|
|
|
@Destination
|
|
|
|
fun EditClosedGroupInviteScreen(
|
2023-11-17 10:10:19 +01:00
|
|
|
resultNavigator: ResultBackNavigator<ContactList>,
|
2023-11-16 07:45:31 +01:00
|
|
|
viewModel: EditGroupInviteViewModel,
|
|
|
|
) {
|
2023-11-09 08:01:24 +01:00
|
|
|
|
2023-11-17 10:10:19 +01:00
|
|
|
val state by viewModel.viewState.collectAsState()
|
|
|
|
val viewState = state.viewState
|
|
|
|
val currentMemberSessionIds = viewState.currentMembers.map { it.memberSessionId }
|
|
|
|
|
|
|
|
SelectContacts(
|
|
|
|
viewState.allContacts
|
|
|
|
.filterNot { it.sessionID in currentMemberSessionIds }
|
|
|
|
.toSet(),
|
|
|
|
onBack = { resultNavigator.navigateBack() },
|
|
|
|
onContactsSelected = {
|
|
|
|
resultNavigator.navigateBack(ContactList(it))
|
|
|
|
},
|
|
|
|
)
|
2023-11-09 08:01:24 +01:00
|
|
|
}
|
|
|
|
|
2023-11-13 05:39:04 +01:00
|
|
|
|
|
|
|
class EditGroupViewModel @AssistedInject constructor(
|
|
|
|
@Assisted private val groupSessionId: String,
|
2023-11-22 02:16:21 +01:00
|
|
|
@Assisted private val contentResolver: ContentResolver,
|
2023-11-17 10:10:19 +01:00
|
|
|
private val storage: StorageProtocol,
|
|
|
|
): ViewModel() {
|
2023-11-13 05:39:04 +01:00
|
|
|
|
|
|
|
val viewState = viewModelScope.launchMolecule(Immediate) {
|
|
|
|
|
|
|
|
val currentUserId = rememberSaveable {
|
|
|
|
storage.getUserPublicKey()!!
|
|
|
|
}
|
|
|
|
|
2023-11-22 02:16:21 +01:00
|
|
|
// val closedGroupRecipient by contentResolver
|
|
|
|
// .observeQuery(DatabaseContentProviders.ConversationList.CONTENT_URI)
|
|
|
|
// .collectAsState(initial = null)
|
|
|
|
|
2023-11-13 05:39:04 +01:00
|
|
|
val closedGroupInfo = remember {
|
|
|
|
storage.getLibSessionClosedGroup(groupSessionId)!!
|
|
|
|
}
|
|
|
|
|
|
|
|
val closedGroup = remember(closedGroupInfo) {
|
|
|
|
storage.getClosedGroupDisplayInfo(groupSessionId)!!
|
|
|
|
}
|
|
|
|
|
|
|
|
val closedGroupMembers = remember(closedGroupInfo) {
|
|
|
|
storage.getMembers(groupSessionId).map { member ->
|
|
|
|
MemberViewModel(
|
|
|
|
memberName = member.name,
|
|
|
|
memberSessionId = member.sessionId,
|
|
|
|
currentUser = member.sessionId == currentUserId,
|
|
|
|
memberState = memberStateOf(member)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
val name = closedGroup.name
|
|
|
|
val description = closedGroup.description
|
|
|
|
|
|
|
|
EditGroupState(
|
2023-11-17 10:10:19 +01:00
|
|
|
EditGroupViewState(
|
2023-11-13 05:39:04 +01:00
|
|
|
groupName = name,
|
|
|
|
groupDescription = description,
|
|
|
|
memberStateList = closedGroupMembers,
|
|
|
|
admin = closedGroup.isUserAdmin
|
|
|
|
)
|
|
|
|
) { event ->
|
2023-11-17 10:10:19 +01:00
|
|
|
when (event) {
|
|
|
|
is EditGroupEvent.InviteContacts -> {
|
|
|
|
val sessionIds = event.contacts
|
|
|
|
storage.inviteClosedGroupMembers(
|
|
|
|
groupSessionId,
|
|
|
|
sessionIds.contacts.map(Contact::sessionID)
|
|
|
|
)
|
|
|
|
Toast.makeText(
|
|
|
|
event.context,
|
|
|
|
"Inviting ${event.contacts.contacts.size}",
|
|
|
|
Toast.LENGTH_LONG
|
|
|
|
).show()
|
|
|
|
}
|
2023-11-22 02:16:21 +01:00
|
|
|
is EditGroupEvent.ReInviteContact -> {
|
|
|
|
JobQueue.shared.add(InviteContactsJob(groupSessionId, arrayOf(event.contactSessionId)))
|
|
|
|
}
|
2023-11-17 10:10:19 +01:00
|
|
|
}
|
2023-11-13 05:39:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@AssistedFactory
|
|
|
|
interface Factory {
|
2023-11-22 02:16:21 +01:00
|
|
|
fun create(groupSessionId: String, contentResolver: ContentResolver): EditGroupViewModel
|
2023-11-13 05:39:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-11-16 07:45:31 +01:00
|
|
|
class EditGroupInviteViewModel @AssistedInject constructor(
|
|
|
|
@Assisted private val groupSessionId: String,
|
|
|
|
private val storage: StorageProtocol
|
|
|
|
): ViewModel() {
|
|
|
|
|
2023-11-17 10:10:19 +01:00
|
|
|
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)
|
2023-11-20 06:45:55 +01:00
|
|
|
)
|
2023-11-17 10:10:19 +01:00
|
|
|
}
|
2023-11-16 07:45:31 +01:00
|
|
|
|
|
|
|
@AssistedFactory
|
|
|
|
interface Factory {
|
|
|
|
fun create(groupSessionId: String): EditGroupInviteViewModel
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-11-09 08:01:24 +01:00
|
|
|
@Composable
|
|
|
|
fun EditGroupView(
|
|
|
|
onBack: ()->Unit,
|
2023-11-16 07:45:31 +01:00
|
|
|
onInvite: ()->Unit,
|
2023-11-22 02:16:21 +01:00
|
|
|
onReinvite: (String)->Unit,
|
2023-11-17 10:10:19 +01:00
|
|
|
viewState: EditGroupViewState,
|
2023-11-09 08:01:24 +01:00
|
|
|
) {
|
|
|
|
val scaffoldState = rememberScaffoldState()
|
|
|
|
|
|
|
|
Scaffold(
|
|
|
|
scaffoldState = scaffoldState,
|
|
|
|
topBar = {
|
|
|
|
NavigationBar(
|
|
|
|
title = stringResource(id = R.string.activity_edit_closed_group_title),
|
2023-11-16 07:45:31 +01:00
|
|
|
onBack = onBack,
|
|
|
|
actionElement = {}
|
|
|
|
)
|
2023-11-09 08:01:24 +01:00
|
|
|
}
|
|
|
|
) { paddingValues ->
|
|
|
|
Column(modifier = Modifier.padding(paddingValues)) {
|
|
|
|
// Group name title
|
2023-11-09 02:15:55 +01:00
|
|
|
Text(
|
2023-11-09 08:01:24 +01:00
|
|
|
text = viewState.groupName,
|
|
|
|
modifier = Modifier
|
|
|
|
.fillMaxWidth()
|
2023-11-16 07:45:31 +01:00
|
|
|
.padding(16.dp),
|
2023-11-09 08:01:24 +01:00
|
|
|
fontSize = 26.sp,
|
|
|
|
fontWeight = FontWeight.Bold,
|
|
|
|
textAlign = TextAlign.Center
|
2023-11-09 02:15:55 +01:00
|
|
|
)
|
2023-11-16 07:45:31 +01:00
|
|
|
// Description
|
|
|
|
|
|
|
|
// Invite
|
|
|
|
if (viewState.admin) {
|
|
|
|
CellWithPaddingAndMargin(margin = 16.dp, padding = 16.dp) {
|
|
|
|
Row(
|
|
|
|
modifier = Modifier
|
|
|
|
.fillMaxWidth()
|
|
|
|
.clickable(onClick = onInvite)
|
|
|
|
.padding(horizontal = 8.dp),
|
|
|
|
verticalAlignment = CenterVertically,
|
|
|
|
) {
|
|
|
|
Icon(painterResource(id = R.drawable.ic_add_admins), contentDescription = null)
|
|
|
|
Spacer(modifier = Modifier.size(8.dp))
|
|
|
|
Text(text = stringResource(id = R.string.activity_edit_closed_group_add_members))
|
2023-11-13 05:39:04 +01:00
|
|
|
}
|
|
|
|
}
|
2023-11-09 08:01:24 +01:00
|
|
|
}
|
2023-11-16 07:45:31 +01:00
|
|
|
// members header
|
|
|
|
Text(
|
|
|
|
text = stringResource(id = R.string.conversation_settings_group_members),
|
|
|
|
style = MaterialTheme.typography.subtitle2,
|
|
|
|
modifier = Modifier
|
|
|
|
.padding(vertical = 8.dp, horizontal = 32.dp)
|
|
|
|
)
|
2023-11-09 08:01:24 +01:00
|
|
|
LazyColumn(modifier = Modifier) {
|
|
|
|
|
|
|
|
items(viewState.memberStateList) { member ->
|
|
|
|
Row(
|
|
|
|
Modifier
|
|
|
|
.fillMaxWidth()
|
|
|
|
.padding(vertical = 8.dp, horizontal = 16.dp)) {
|
|
|
|
ContactPhoto(member.memberSessionId)
|
2023-11-13 05:39:04 +01:00
|
|
|
Column(modifier = Modifier
|
|
|
|
.weight(1f)
|
2023-11-15 02:07:07 +01:00
|
|
|
.fillMaxHeight()
|
|
|
|
.padding(horizontal = 8.dp)
|
2023-11-13 05:39:04 +01:00
|
|
|
.align(CenterVertically)) {
|
2023-11-15 02:07:07 +01:00
|
|
|
// Member's name
|
|
|
|
Text(
|
|
|
|
text = member.memberName ?: member.memberSessionId,
|
|
|
|
style = MemberNameStyle,
|
|
|
|
modifier = Modifier
|
|
|
|
.fillMaxWidth()
|
|
|
|
.padding(1.dp)
|
|
|
|
)
|
2023-11-13 05:39:04 +01:00
|
|
|
if (member.memberState !in listOf(MemberState.Member, MemberState.Admin)) {
|
|
|
|
Text(
|
|
|
|
text = member.memberState.toString(),
|
2023-11-15 02:07:07 +01:00
|
|
|
modifier = Modifier
|
|
|
|
.fillMaxWidth()
|
|
|
|
.padding(1.dp)
|
2023-11-13 05:39:04 +01:00
|
|
|
)
|
|
|
|
}
|
2023-11-09 08:01:24 +01:00
|
|
|
}
|
2023-11-22 02:16:21 +01:00
|
|
|
// Resend button
|
|
|
|
if (viewState.admin && member.memberState == MemberState.InviteFailed) {
|
|
|
|
OutlinedButton(onClick = {
|
|
|
|
onReinvite(member.memberSessionId)
|
|
|
|
},) {
|
|
|
|
Text("Re-send")
|
|
|
|
}
|
|
|
|
}
|
2023-11-09 08:01:24 +01:00
|
|
|
}
|
|
|
|
}
|
2023-11-09 02:15:55 +01:00
|
|
|
|
2023-11-09 08:01:24 +01:00
|
|
|
}
|
2023-11-09 02:15:55 +01:00
|
|
|
}
|
|
|
|
}
|
2023-11-08 23:23:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
data class EditGroupState(
|
|
|
|
val viewState: EditGroupViewState,
|
2023-11-17 10:10:19 +01:00
|
|
|
val eventSink: (EditGroupEvent) -> Unit
|
|
|
|
)
|
|
|
|
|
|
|
|
data class EditGroupInviteState(
|
|
|
|
val viewState: EditGroupInviteViewState,
|
2023-11-08 23:23:20 +01:00
|
|
|
)
|
|
|
|
|
2023-11-09 08:01:24 +01:00
|
|
|
data class MemberViewModel(
|
|
|
|
val memberName: String?,
|
|
|
|
val memberSessionId: String,
|
|
|
|
val memberState: MemberState,
|
|
|
|
val currentUser: Boolean,
|
|
|
|
)
|
|
|
|
|
|
|
|
enum class MemberState {
|
|
|
|
InviteSent,
|
|
|
|
Inviting, // maybe just use these in view
|
|
|
|
InviteFailed,
|
|
|
|
PromotionSent,
|
|
|
|
Promoting, // maybe just use these in view
|
|
|
|
PromotionFailed,
|
|
|
|
Admin,
|
|
|
|
Member
|
|
|
|
}
|
|
|
|
|
|
|
|
fun memberStateOf(member: GroupMember): MemberState = when {
|
|
|
|
member.inviteFailed -> MemberState.InviteFailed
|
2023-11-22 02:16:21 +01:00
|
|
|
member.invitePending -> MemberState.InviteSent
|
2023-11-09 08:01:24 +01:00
|
|
|
member.promotionFailed -> MemberState.PromotionFailed
|
2023-11-22 02:16:21 +01:00
|
|
|
member.promotionPending -> MemberState.PromotionSent
|
2023-11-09 08:01:24 +01:00
|
|
|
member.admin -> MemberState.Admin
|
|
|
|
else -> MemberState.Member
|
|
|
|
}
|
|
|
|
|
2023-11-17 10:10:19 +01:00
|
|
|
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()
|
2023-11-22 02:16:21 +01:00
|
|
|
data class ReInviteContact(val contactSessionId: String): EditGroupEvent()
|
2023-11-15 02:07:07 +01:00
|
|
|
}
|
|
|
|
|
2023-11-17 10:10:19 +01:00
|
|
|
data class EditGroupInviteViewState(
|
|
|
|
val currentMembers: List<MemberViewModel>,
|
|
|
|
val allContacts: Set<Contact>
|
|
|
|
)
|
|
|
|
|
2023-11-15 02:07:07 +01:00
|
|
|
@Preview
|
|
|
|
@Composable
|
|
|
|
fun PreviewList() {
|
|
|
|
|
|
|
|
val oneMember = MemberViewModel(
|
|
|
|
"Test User",
|
|
|
|
"05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234",
|
|
|
|
MemberState.InviteSent,
|
|
|
|
false
|
|
|
|
)
|
2023-11-22 02:16:21 +01:00
|
|
|
val twoMember = MemberViewModel(
|
|
|
|
"Test User",
|
|
|
|
"05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1235",
|
|
|
|
MemberState.InviteFailed,
|
|
|
|
false
|
|
|
|
)
|
2023-11-15 02:07:07 +01:00
|
|
|
|
2023-11-17 10:10:19 +01:00
|
|
|
val viewState = EditGroupViewState(
|
2023-11-15 02:07:07 +01:00
|
|
|
"Preview",
|
|
|
|
"This is a preview description",
|
2023-11-22 02:16:21 +01:00
|
|
|
listOf(oneMember, twoMember),
|
2023-11-16 07:45:31 +01:00
|
|
|
true
|
2023-11-15 02:07:07 +01:00
|
|
|
)
|
|
|
|
|
2023-11-16 07:45:31 +01:00
|
|
|
PreviewTheme(themeResId = R.style.Classic_Dark) {
|
|
|
|
EditGroupView(
|
|
|
|
onBack = {},
|
|
|
|
onInvite = {},
|
2023-11-22 02:16:21 +01:00
|
|
|
onReinvite = {},
|
2023-11-16 07:45:31 +01:00
|
|
|
viewState = viewState
|
|
|
|
)
|
2023-11-15 02:07:07 +01:00
|
|
|
}
|
2023-11-08 23:23:20 +01:00
|
|
|
}
|