Фикс: Новый флоу создания групп
This commit is contained in:
@@ -1628,6 +1628,7 @@ fun MainScreen(
|
|||||||
accountName = accountName,
|
accountName = accountName,
|
||||||
accountUsername = accountUsername,
|
accountUsername = accountUsername,
|
||||||
avatarRepository = avatarRepository,
|
avatarRepository = avatarRepository,
|
||||||
|
dialogDao = RosettaDatabase.getDatabase(context).dialogDao(),
|
||||||
onBack = { navStack = navStack.filterNot { it is Screen.GroupSetup } },
|
onBack = { navStack = navStack.filterNot { it is Screen.GroupSetup } },
|
||||||
onGroupOpened = { groupUser ->
|
onGroupOpened = { groupUser ->
|
||||||
navStack =
|
navStack =
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ import androidx.compose.foundation.layout.ime
|
|||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
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.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.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
@@ -73,9 +77,13 @@ import coil.compose.AsyncImage
|
|||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.rosetta.messenger.R
|
import com.rosetta.messenger.R
|
||||||
import com.rosetta.messenger.data.GroupRepository
|
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.GroupStatus
|
||||||
import com.rosetta.messenger.network.SearchUser
|
import com.rosetta.messenger.network.SearchUser
|
||||||
import com.rosetta.messenger.repository.AvatarRepository
|
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.AppleEmojiTextField
|
||||||
import com.rosetta.messenger.ui.components.KeyboardHeightProvider
|
import com.rosetta.messenger.ui.components.KeyboardHeightProvider
|
||||||
import com.rosetta.messenger.ui.components.OptimizedEmojiPicker
|
import com.rosetta.messenger.ui.components.OptimizedEmojiPicker
|
||||||
@@ -93,6 +101,7 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
private enum class GroupSetupStep {
|
private enum class GroupSetupStep {
|
||||||
|
MEMBERS,
|
||||||
DETAILS,
|
DETAILS,
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
}
|
}
|
||||||
@@ -106,6 +115,7 @@ fun GroupSetupScreen(
|
|||||||
accountName: String,
|
accountName: String,
|
||||||
accountUsername: String,
|
accountUsername: String,
|
||||||
avatarRepository: AvatarRepository? = null,
|
avatarRepository: AvatarRepository? = null,
|
||||||
|
dialogDao: DialogDao? = null,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onGroupOpened: (SearchUser) -> Unit
|
onGroupOpened: (SearchUser) -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -116,9 +126,53 @@ fun GroupSetupScreen(
|
|||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
val nameFocusRequester = remember { FocusRequester() }
|
val nameFocusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
var step by rememberSaveable { mutableStateOf(GroupSetupStep.DETAILS) }
|
var step by rememberSaveable { mutableStateOf(GroupSetupStep.MEMBERS) }
|
||||||
var title by rememberSaveable { mutableStateOf("") }
|
var title by rememberSaveable { mutableStateOf("") }
|
||||||
var description 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 selectedAvatarUri by rememberSaveable { mutableStateOf<String?>(null) }
|
||||||
var isLoading by rememberSaveable { mutableStateOf(false) }
|
var isLoading by rememberSaveable { mutableStateOf(false) }
|
||||||
var errorText by rememberSaveable { mutableStateOf<String?>(null) }
|
var errorText by rememberSaveable { mutableStateOf<String?>(null) }
|
||||||
@@ -153,7 +207,7 @@ fun GroupSetupScreen(
|
|||||||
val disabledActionColor = if (isDarkTheme) Color(0xFF5A5A5E) else Color(0xFFC7C7CC)
|
val disabledActionColor = if (isDarkTheme) Color(0xFF5A5A5E) else Color(0xFFC7C7CC)
|
||||||
val disabledActionContentColor = if (isDarkTheme) Color(0xFFAAAAAF) else Color(0xFF8E8E93)
|
val disabledActionContentColor = if (isDarkTheme) Color(0xFFAAAAAF) else Color(0xFF8E8E93)
|
||||||
val groupAvatarCameraButtonColor =
|
val groupAvatarCameraButtonColor =
|
||||||
if (isDarkTheme) sectionColor else Color(0xFF8CC9F6)
|
if (isDarkTheme) sectionColor else accentColor
|
||||||
val groupAvatarCameraIconColor =
|
val groupAvatarCameraIconColor =
|
||||||
if (isDarkTheme) accentColor else Color.White
|
if (isDarkTheme) accentColor else Color.White
|
||||||
|
|
||||||
@@ -233,19 +287,25 @@ fun GroupSetupScreen(
|
|||||||
fun handleBack() {
|
fun handleBack() {
|
||||||
if (isLoading) return
|
if (isLoading) return
|
||||||
errorText = null
|
errorText = null
|
||||||
if (step == GroupSetupStep.DESCRIPTION) {
|
when (step) {
|
||||||
step = GroupSetupStep.DETAILS
|
GroupSetupStep.DESCRIPTION -> step = GroupSetupStep.DETAILS
|
||||||
} else {
|
GroupSetupStep.DETAILS -> step = GroupSetupStep.MEMBERS
|
||||||
|
GroupSetupStep.MEMBERS -> {
|
||||||
dismissInputUi()
|
dismissInputUi()
|
||||||
onBack()
|
onBack()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BackHandler(onBack = ::handleBack)
|
BackHandler(onBack = ::handleBack)
|
||||||
|
|
||||||
val canGoNext = title.trim().isNotEmpty()
|
val canGoNext = title.trim().isNotEmpty()
|
||||||
val canCreate = canGoNext && !isLoading
|
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 density = LocalDensity.current
|
||||||
val imeBottomPx = WindowInsets.ime.getBottom(density)
|
val imeBottomPx = WindowInsets.ime.getBottom(density)
|
||||||
val imeBottomDp = with(density) { imeBottomPx.toDp() }
|
val imeBottomDp = with(density) { imeBottomPx.toDp() }
|
||||||
@@ -256,6 +316,16 @@ fun GroupSetupScreen(
|
|||||||
coordinator.closeEmoji(hideEmoji = { showEmojiKeyboard = false })
|
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) {
|
LaunchedEffect(Unit) {
|
||||||
@@ -339,7 +409,11 @@ fun GroupSetupScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (step == GroupSetupStep.DETAILS) {
|
if (step == GroupSetupStep.MEMBERS) {
|
||||||
|
delay(120)
|
||||||
|
memberSearchFocusRequester.requestFocus()
|
||||||
|
keyboardController?.show()
|
||||||
|
} else if (step == GroupSetupStep.DETAILS) {
|
||||||
delay(120)
|
delay(120)
|
||||||
nameFocusRequester.requestFocus()
|
nameFocusRequester.requestFocus()
|
||||||
keyboardController?.show()
|
keyboardController?.show()
|
||||||
@@ -351,12 +425,27 @@ fun GroupSetupScreen(
|
|||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
|
Column {
|
||||||
Text(
|
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,
|
fontWeight = FontWeight.SemiBold,
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
fontSize = 20.sp
|
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 = {
|
navigationIcon = {
|
||||||
IconButton(onClick = ::handleBack) {
|
IconButton(onClick = ::handleBack) {
|
||||||
@@ -404,10 +493,196 @@ fun GroupSetupScreen(
|
|||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
.navigationBarsPadding()
|
.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))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
@@ -520,34 +795,33 @@ fun GroupSetupScreen(
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(22.dp))
|
Spacer(modifier = Modifier.height(22.dp))
|
||||||
|
|
||||||
|
val totalMembers = 1 + selectedMembers.size
|
||||||
Text(
|
Text(
|
||||||
text = "1 member",
|
text = "$totalMembers member${if (totalMembers > 1) "s" else ""}",
|
||||||
color = accentColor,
|
color = accentColor,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
fontSize = 15.sp,
|
fontSize = 15.sp,
|
||||||
modifier = Modifier.padding(bottom = 0.dp)
|
modifier = Modifier.padding(start = 0.dp, bottom = 0.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
// Self (admin)
|
||||||
Row(
|
Row(
|
||||||
modifier =
|
modifier = Modifier
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clip(RoundedCornerShape(14.dp))
|
.padding(horizontal = 0.dp, vertical = 8.dp),
|
||||||
.background(sectionColor)
|
|
||||||
.padding(horizontal = 14.dp, vertical = 12.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
AvatarImage(
|
AvatarImage(
|
||||||
publicKey = accountPublicKey,
|
publicKey = accountPublicKey,
|
||||||
avatarRepository = avatarRepository,
|
avatarRepository = avatarRepository,
|
||||||
size = 50.dp,
|
size = 42.dp,
|
||||||
isDarkTheme = isDarkTheme,
|
isDarkTheme = isDarkTheme,
|
||||||
displayName = selfTitle
|
displayName = selfTitle
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.size(12.dp))
|
Spacer(modifier = Modifier.size(14.dp))
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
@@ -581,14 +855,61 @@ fun GroupSetupScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
// Selected members
|
||||||
Text(
|
selectedMembers.forEach { member ->
|
||||||
text = "After creating the group, you can invite as many users as you need.",
|
val memberName = member.title.ifEmpty {
|
||||||
color = secondaryTextColor,
|
member.username.ifEmpty { member.publicKey.take(8) + "..." }
|
||||||
fontSize = 13.sp,
|
}
|
||||||
lineHeight = 18.sp
|
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 {
|
} else {
|
||||||
|
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
@@ -697,6 +1018,7 @@ fun GroupSetupScreen(
|
|||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
textAlign = TextAlign.Start
|
textAlign = TextAlign.Start
|
||||||
)
|
)
|
||||||
|
} // end DESCRIPTION Column
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!errorText.isNullOrBlank()) {
|
if (!errorText.isNullOrBlank()) {
|
||||||
@@ -712,6 +1034,13 @@ fun GroupSetupScreen(
|
|||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
elevation = androidx.compose.material3.FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp),
|
elevation = androidx.compose.material3.FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
|
if (step == GroupSetupStep.MEMBERS) {
|
||||||
|
dismissInputUi()
|
||||||
|
memberSearchQuery = ""
|
||||||
|
step = GroupSetupStep.DETAILS
|
||||||
|
return@FloatingActionButton
|
||||||
|
}
|
||||||
|
|
||||||
if (step == GroupSetupStep.DETAILS) {
|
if (step == GroupSetupStep.DETAILS) {
|
||||||
if (canGoNext) {
|
if (canGoNext) {
|
||||||
errorText = null
|
errorText = null
|
||||||
@@ -736,6 +1065,37 @@ fun GroupSetupScreen(
|
|||||||
avatarUriString = selectedAvatarUri
|
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()
|
dismissInputUi()
|
||||||
openGroup(
|
openGroup(
|
||||||
dialogPublicKey = result.dialogPublicKey,
|
dialogPublicKey = result.dialogPublicKey,
|
||||||
|
|||||||
Reference in New Issue
Block a user