Фикс: Новый флоу создания групп
This commit is contained in:
@@ -1628,6 +1628,7 @@ fun MainScreen(
|
||||
accountName = accountName,
|
||||
accountUsername = accountUsername,
|
||||
avatarRepository = avatarRepository,
|
||||
dialogDao = RosettaDatabase.getDatabase(context).dialogDao(),
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.GroupSetup } },
|
||||
onGroupOpened = { groupUser ->
|
||||
navStack =
|
||||
|
||||
@@ -24,6 +24,10 @@ import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
@@ -73,9 +77,13 @@ import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.rosetta.messenger.R
|
||||
import com.rosetta.messenger.data.GroupRepository
|
||||
import com.rosetta.messenger.data.MessageRepository
|
||||
import com.rosetta.messenger.database.DialogDao
|
||||
import com.rosetta.messenger.database.RosettaDatabase
|
||||
import com.rosetta.messenger.network.GroupStatus
|
||||
import com.rosetta.messenger.network.SearchUser
|
||||
import com.rosetta.messenger.repository.AvatarRepository
|
||||
import com.rosetta.messenger.ui.components.VerifiedBadge
|
||||
import com.rosetta.messenger.ui.components.AppleEmojiTextField
|
||||
import com.rosetta.messenger.ui.components.KeyboardHeightProvider
|
||||
import com.rosetta.messenger.ui.components.OptimizedEmojiPicker
|
||||
@@ -93,6 +101,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
private enum class GroupSetupStep {
|
||||
MEMBERS,
|
||||
DETAILS,
|
||||
DESCRIPTION
|
||||
}
|
||||
@@ -106,6 +115,7 @@ fun GroupSetupScreen(
|
||||
accountName: String,
|
||||
accountUsername: String,
|
||||
avatarRepository: AvatarRepository? = null,
|
||||
dialogDao: DialogDao? = null,
|
||||
onBack: () -> Unit,
|
||||
onGroupOpened: (SearchUser) -> Unit
|
||||
) {
|
||||
@@ -116,9 +126,53 @@ fun GroupSetupScreen(
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val nameFocusRequester = remember { FocusRequester() }
|
||||
|
||||
var step by rememberSaveable { mutableStateOf(GroupSetupStep.DETAILS) }
|
||||
var step by rememberSaveable { mutableStateOf(GroupSetupStep.MEMBERS) }
|
||||
var title by rememberSaveable { mutableStateOf("") }
|
||||
var description by rememberSaveable { mutableStateOf("") }
|
||||
|
||||
// Members selection state
|
||||
var memberSearchQuery by rememberSaveable { mutableStateOf("") }
|
||||
var selectedMembers by remember { mutableStateOf<List<SearchUser>>(emptyList()) }
|
||||
var allContacts by remember { mutableStateOf<List<SearchUser>>(emptyList()) }
|
||||
val memberSearchFocusRequester = remember { FocusRequester() }
|
||||
|
||||
// Load contacts from database
|
||||
val resolvedDialogDao = dialogDao ?: remember(context) {
|
||||
RosettaDatabase.getDatabase(context).dialogDao()
|
||||
}
|
||||
LaunchedEffect(accountPublicKey) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val dialogs = resolvedDialogDao.getDialogsPaged(accountPublicKey, limit = 200, offset = 0)
|
||||
allContacts = dialogs
|
||||
.filter { dialog ->
|
||||
val key = dialog.opponentKey.lowercase()
|
||||
!key.startsWith("#group:") && !key.startsWith("group:") &&
|
||||
dialog.opponentKey != accountPublicKey &&
|
||||
dialog.opponentKey != "0x000000000000000000000000000000000000000001" &&
|
||||
dialog.opponentKey != "0x000000000000000000000000000000000000000002"
|
||||
}
|
||||
.map { dialog ->
|
||||
SearchUser(
|
||||
publicKey = dialog.opponentKey,
|
||||
title = dialog.opponentTitle,
|
||||
username = dialog.opponentUsername,
|
||||
verified = dialog.verified,
|
||||
online = dialog.isOnline
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val filteredContacts = remember(allContacts, memberSearchQuery) {
|
||||
if (memberSearchQuery.isBlank()) allContacts
|
||||
else {
|
||||
val q = memberSearchQuery.lowercase()
|
||||
allContacts.filter { user ->
|
||||
user.title.lowercase().contains(q) ||
|
||||
user.username.lowercase().contains(q)
|
||||
}
|
||||
}
|
||||
}
|
||||
var selectedAvatarUri by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
var isLoading by rememberSaveable { mutableStateOf(false) }
|
||||
var errorText by rememberSaveable { mutableStateOf<String?>(null) }
|
||||
@@ -153,7 +207,7 @@ fun GroupSetupScreen(
|
||||
val disabledActionColor = if (isDarkTheme) Color(0xFF5A5A5E) else Color(0xFFC7C7CC)
|
||||
val disabledActionContentColor = if (isDarkTheme) Color(0xFFAAAAAF) else Color(0xFF8E8E93)
|
||||
val groupAvatarCameraButtonColor =
|
||||
if (isDarkTheme) sectionColor else Color(0xFF8CC9F6)
|
||||
if (isDarkTheme) sectionColor else accentColor
|
||||
val groupAvatarCameraIconColor =
|
||||
if (isDarkTheme) accentColor else Color.White
|
||||
|
||||
@@ -233,19 +287,25 @@ fun GroupSetupScreen(
|
||||
fun handleBack() {
|
||||
if (isLoading) return
|
||||
errorText = null
|
||||
if (step == GroupSetupStep.DESCRIPTION) {
|
||||
step = GroupSetupStep.DETAILS
|
||||
} else {
|
||||
when (step) {
|
||||
GroupSetupStep.DESCRIPTION -> step = GroupSetupStep.DETAILS
|
||||
GroupSetupStep.DETAILS -> step = GroupSetupStep.MEMBERS
|
||||
GroupSetupStep.MEMBERS -> {
|
||||
dismissInputUi()
|
||||
onBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler(onBack = ::handleBack)
|
||||
|
||||
val canGoNext = title.trim().isNotEmpty()
|
||||
val canCreate = canGoNext && !isLoading
|
||||
val actionEnabled = if (step == GroupSetupStep.DETAILS) canGoNext else canCreate
|
||||
val actionEnabled = when (step) {
|
||||
GroupSetupStep.MEMBERS -> true
|
||||
GroupSetupStep.DETAILS -> canGoNext
|
||||
GroupSetupStep.DESCRIPTION -> canCreate
|
||||
}
|
||||
val density = LocalDensity.current
|
||||
val imeBottomPx = WindowInsets.ime.getBottom(density)
|
||||
val imeBottomDp = with(density) { imeBottomPx.toDp() }
|
||||
@@ -256,6 +316,16 @@ fun GroupSetupScreen(
|
||||
coordinator.closeEmoji(hideEmoji = { showEmojiKeyboard = false })
|
||||
}
|
||||
}
|
||||
if (step == GroupSetupStep.MEMBERS) {
|
||||
delay(120)
|
||||
memberSearchFocusRequester.requestFocus()
|
||||
keyboardController?.show()
|
||||
}
|
||||
if (step == GroupSetupStep.DETAILS) {
|
||||
delay(120)
|
||||
nameFocusRequester.requestFocus()
|
||||
keyboardController?.show()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
@@ -339,7 +409,11 @@ fun GroupSetupScreen(
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (step == GroupSetupStep.DETAILS) {
|
||||
if (step == GroupSetupStep.MEMBERS) {
|
||||
delay(120)
|
||||
memberSearchFocusRequester.requestFocus()
|
||||
keyboardController?.show()
|
||||
} else if (step == GroupSetupStep.DETAILS) {
|
||||
delay(120)
|
||||
nameFocusRequester.requestFocus()
|
||||
keyboardController?.show()
|
||||
@@ -351,12 +425,27 @@ fun GroupSetupScreen(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Column {
|
||||
Text(
|
||||
text = if (step == GroupSetupStep.DETAILS) "New Group" else "Group Description",
|
||||
text = when (step) {
|
||||
GroupSetupStep.MEMBERS -> "New Group"
|
||||
GroupSetupStep.DETAILS -> "New Group"
|
||||
GroupSetupStep.DESCRIPTION -> "Group Description"
|
||||
},
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.White,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
if (step == GroupSetupStep.MEMBERS) {
|
||||
val countText = if (selectedMembers.isEmpty()) "up to 200 members"
|
||||
else "${selectedMembers.size} of 200 selected"
|
||||
Text(
|
||||
text = countText,
|
||||
color = Color.White.copy(alpha = 0.7f),
|
||||
fontSize = 13.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = ::handleBack) {
|
||||
@@ -404,10 +493,196 @@ fun GroupSetupScreen(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.padding(horizontal = 16.dp)
|
||||
.navigationBarsPadding()
|
||||
) {
|
||||
if (step == GroupSetupStep.DETAILS) {
|
||||
if (step == GroupSetupStep.MEMBERS) {
|
||||
// Search bar
|
||||
TextField(
|
||||
value = memberSearchQuery,
|
||||
onValueChange = { memberSearchQuery = it },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(memberSearchFocusRequester),
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "Who would you like to add?",
|
||||
color = secondaryTextColor,
|
||||
fontSize = 15.sp
|
||||
)
|
||||
},
|
||||
textStyle = MaterialTheme.typography.bodyLarge.copy(
|
||||
color = primaryTextColor,
|
||||
fontSize = 15.sp
|
||||
),
|
||||
singleLine = true,
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedTextColor = primaryTextColor,
|
||||
unfocusedTextColor = primaryTextColor,
|
||||
focusedContainerColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color.White,
|
||||
unfocusedContainerColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color.White,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
cursorColor = accentColor
|
||||
)
|
||||
)
|
||||
|
||||
// Selected members chips
|
||||
if (selectedMembers.isNotEmpty()) {
|
||||
LazyRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
items(selectedMembers, key = { it.publicKey }) { user ->
|
||||
val chipName = user.title.ifEmpty { user.username.ifEmpty { user.publicKey.take(8) } }
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.width(60.dp)
|
||||
.clickable {
|
||||
selectedMembers = selectedMembers.filter { it.publicKey != user.publicKey }
|
||||
}
|
||||
) {
|
||||
Box(contentAlignment = Alignment.BottomEnd) {
|
||||
AvatarImage(
|
||||
publicKey = user.publicKey,
|
||||
avatarRepository = avatarRepository,
|
||||
size = 46.dp,
|
||||
isDarkTheme = isDarkTheme,
|
||||
displayName = user.title.ifEmpty { user.username }
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(18.dp)
|
||||
.clip(CircleShape)
|
||||
.background(backgroundColor)
|
||||
.padding(1.dp)
|
||||
.clip(CircleShape)
|
||||
.background(secondaryTextColor.copy(alpha = 0.6f)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
painter = TelegramIcons.Close,
|
||||
contentDescription = "Remove",
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = chipName,
|
||||
fontSize = 11.sp,
|
||||
color = primaryTextColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(0.5.dp)
|
||||
.background(secondaryTextColor.copy(alpha = 0.2f))
|
||||
)
|
||||
|
||||
// Contact list
|
||||
val listState = androidx.compose.foundation.lazy.rememberLazyListState()
|
||||
LaunchedEffect(listState.isScrollInProgress) {
|
||||
if (listState.isScrollInProgress) {
|
||||
focusManager.clearFocus()
|
||||
keyboardController?.hide()
|
||||
}
|
||||
}
|
||||
LazyColumn(state = listState, modifier = Modifier.fillMaxSize()) {
|
||||
items(filteredContacts, key = { it.publicKey }) { user ->
|
||||
val isSelected = selectedMembers.any { it.publicKey == user.publicKey }
|
||||
val displayName = user.title.ifEmpty {
|
||||
user.username.ifEmpty { user.publicKey.take(8) + "..." }
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
selectedMembers = if (isSelected) {
|
||||
selectedMembers.filter { it.publicKey != user.publicKey }
|
||||
} else {
|
||||
selectedMembers + user
|
||||
}
|
||||
}
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
AvatarImage(
|
||||
publicKey = user.publicKey,
|
||||
avatarRepository = avatarRepository,
|
||||
size = 42.dp,
|
||||
isDarkTheme = isDarkTheme,
|
||||
showOnlineIndicator = false,
|
||||
isOnline = false,
|
||||
displayName = user.title.ifEmpty { user.username }
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(14.dp))
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = displayName,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = primaryTextColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
if (user.verified != 0) {
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
VerifiedBadge(
|
||||
verified = user.verified,
|
||||
size = 16
|
||||
)
|
||||
}
|
||||
}
|
||||
if (user.username.isNotEmpty()) {
|
||||
Text(
|
||||
text = "@${user.username}",
|
||||
fontSize = 14.sp,
|
||||
color = secondaryTextColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Checkmark
|
||||
if (isSelected) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.clip(CircleShape)
|
||||
.background(accentColor),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
painter = TelegramIcons.Done,
|
||||
contentDescription = "Selected",
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(14.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (step == GroupSetupStep.DETAILS) {
|
||||
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Row(
|
||||
@@ -520,34 +795,33 @@ fun GroupSetupScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(22.dp))
|
||||
|
||||
val totalMembers = 1 + selectedMembers.size
|
||||
Text(
|
||||
text = "1 member",
|
||||
text = "$totalMembers member${if (totalMembers > 1) "s" else ""}",
|
||||
color = accentColor,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 15.sp,
|
||||
modifier = Modifier.padding(bottom = 0.dp)
|
||||
modifier = Modifier.padding(start = 0.dp, bottom = 0.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
// Self (admin)
|
||||
Row(
|
||||
modifier =
|
||||
Modifier
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.background(sectionColor)
|
||||
.padding(horizontal = 14.dp, vertical = 12.dp),
|
||||
.padding(horizontal = 0.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
AvatarImage(
|
||||
publicKey = accountPublicKey,
|
||||
avatarRepository = avatarRepository,
|
||||
size = 50.dp,
|
||||
size = 42.dp,
|
||||
isDarkTheme = isDarkTheme,
|
||||
displayName = selfTitle
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.size(12.dp))
|
||||
Spacer(modifier = Modifier.size(14.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
@@ -581,14 +855,61 @@ fun GroupSetupScreen(
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Text(
|
||||
text = "After creating the group, you can invite as many users as you need.",
|
||||
color = secondaryTextColor,
|
||||
fontSize = 13.sp,
|
||||
lineHeight = 18.sp
|
||||
// Selected members
|
||||
selectedMembers.forEach { member ->
|
||||
val memberName = member.title.ifEmpty {
|
||||
member.username.ifEmpty { member.publicKey.take(8) + "..." }
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 0.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
AvatarImage(
|
||||
publicKey = member.publicKey,
|
||||
avatarRepository = avatarRepository,
|
||||
size = 42.dp,
|
||||
isDarkTheme = isDarkTheme,
|
||||
displayName = member.title.ifEmpty { member.username }
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.size(14.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = memberName,
|
||||
color = primaryTextColor,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
if (member.verified != 0) {
|
||||
VerifiedBadge(verified = member.verified, size = 16)
|
||||
}
|
||||
}
|
||||
if (member.username.isNotEmpty()) {
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Text(
|
||||
text = "@${member.username}",
|
||||
color = secondaryTextColor,
|
||||
fontSize = 13.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end DETAILS Column
|
||||
} else {
|
||||
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Row(
|
||||
@@ -697,6 +1018,7 @@ fun GroupSetupScreen(
|
||||
fontSize = 13.sp,
|
||||
textAlign = TextAlign.Start
|
||||
)
|
||||
} // end DESCRIPTION Column
|
||||
}
|
||||
|
||||
if (!errorText.isNullOrBlank()) {
|
||||
@@ -712,6 +1034,13 @@ fun GroupSetupScreen(
|
||||
FloatingActionButton(
|
||||
elevation = androidx.compose.material3.FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp),
|
||||
onClick = {
|
||||
if (step == GroupSetupStep.MEMBERS) {
|
||||
dismissInputUi()
|
||||
memberSearchQuery = ""
|
||||
step = GroupSetupStep.DETAILS
|
||||
return@FloatingActionButton
|
||||
}
|
||||
|
||||
if (step == GroupSetupStep.DETAILS) {
|
||||
if (canGoNext) {
|
||||
errorText = null
|
||||
@@ -736,6 +1065,37 @@ fun GroupSetupScreen(
|
||||
avatarUriString = selectedAvatarUri
|
||||
)
|
||||
}
|
||||
|
||||
// Send invite to all selected members
|
||||
if (selectedMembers.isNotEmpty()) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val groupRepo = GroupRepository.getInstance(context)
|
||||
val groupKey = groupRepo.getGroupKey(
|
||||
accountPublicKey, accountPrivateKey,
|
||||
result.dialogPublicKey
|
||||
)
|
||||
if (!groupKey.isNullOrBlank()) {
|
||||
val invite = groupRepo.constructInviteString(
|
||||
groupId = result.dialogPublicKey,
|
||||
title = result.title.ifBlank { title.trim() },
|
||||
encryptKey = groupKey,
|
||||
description = description.trim()
|
||||
)
|
||||
if (invite.isNotBlank()) {
|
||||
val msgRepo = MessageRepository.getInstance(context)
|
||||
selectedMembers.forEach { member ->
|
||||
runCatching {
|
||||
msgRepo.sendMessage(
|
||||
toPublicKey = member.publicKey,
|
||||
text = invite
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dismissInputUi()
|
||||
openGroup(
|
||||
dialogPublicKey = result.dialogPublicKey,
|
||||
|
||||
Reference in New Issue
Block a user