UI adjustment

This commit is contained in:
SessionHero01 2024-10-24 11:02:34 +11:00
parent 74f7bbb6d5
commit 6e1fa1b257
No known key found for this signature in database
10 changed files with 245 additions and 238 deletions

View File

@ -30,6 +30,7 @@ import org.session.libsession.database.StorageProtocol
import org.session.libsession.messaging.groups.GroupManagerV2
import org.session.libsession.utilities.ConfigUpdateNotification
import org.session.libsignal.utilities.AccountId
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.dependencies.ConfigFactory
const val MAX_GROUP_NAME_LENGTH = 100
@ -45,6 +46,11 @@ class EditGroupViewModel @AssistedInject constructor(
// Input/Output state
private val mutableEditingName = MutableStateFlow<String?>(null)
// Input/Output: the name that has been written and submitted for change to push to the server,
// but not yet confirmed by the server. When this state is present, it takes precedence over
// the group name in the group info.
private val mutablePendingEditedName = MutableStateFlow<String?>(null)
// Input: invite/promote member's intermediate states. This is needed because we don't have
// a state that we can map into in the config system. The config system only provides "sent", "failed", etc.
// The intermediate states are needed to show the user that the operation is in progress, and the
@ -94,8 +100,8 @@ class EditGroupViewModel @AssistedInject constructor(
.stateIn(viewModelScope, SharingStarted.Eagerly, false)
// Output: The name of the group. This is the current name of the group, not the name being edited.
val groupName: StateFlow<String> = groupInfo
.map { it?.first?.name.orEmpty() }
val groupName: StateFlow<String> = combine(groupInfo
.map { it?.first?.name.orEmpty() }, mutablePendingEditedName) { name, pendingName -> pendingName ?: name }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "")
// Output: the list of the members and their state in the group.
@ -260,11 +266,22 @@ class EditGroupViewModel @AssistedInject constructor(
fun onEditNameConfirmClicked() {
val newName = mutableEditingName.value
if (newName.isNullOrBlank()) {
return
}
// Move the edited name into the pending state
mutableEditingName.value = null
mutablePendingEditedName.value = newName
performGroupOperation {
if (!newName.isNullOrBlank()) {
try {
groupManager.setName(groupId, newName)
mutableEditingName.value = null
} finally {
// As soon as the operation is done, clear the pending state,
// no matter if it's successful or not. So that we update the UI to reflect the
// real state.
mutablePendingEditedName.value = null
}
}
}

View File

@ -11,7 +11,6 @@ import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_VISIBLE
import network.loki.messenger.libsession_util.util.Conversation
@ -323,12 +322,39 @@ class GroupManagerV2Impl @Inject constructor(
removedMembers: List<AccountId>,
removeMessages: Boolean
) {
val adminKey = requireAdminAccess(groupAccountId)
// Update the config to mark this member as "removed"
flagMembersForRemoval(
group = groupAccountId,
groupAdminKey = adminKey,
members = removedMembers,
alsoRemoveMembersMessage = removeMessages,
sendMemberChangeMessage = true
)
val timestamp = clock.currentTimeMills()
val signature = SodiumUtilities.sign(
buildMemberChangeSignature(
GroupUpdateMemberChangeMessage.Type.REMOVED,
timestamp
),
adminKey
)
val updateMessage = GroupUpdateMessage.newBuilder()
.setMemberChangeMessage(
GroupUpdateMemberChangeMessage.newBuilder()
.addAllMemberSessionIds(removedMembers.map { it.hexString })
.setType(GroupUpdateMemberChangeMessage.Type.REMOVED)
.setAdminSignature(ByteString.copyFrom(signature))
)
.build()
val message = GroupUpdated(
updateMessage
).apply { sentTimestamp = timestamp }
MessageSender.send(message, Destination.ClosedGroup(groupAccountId.hexString), false).await()
storage.insertGroupInfoChange(message, groupAccountId)
}
override suspend fun removeMemberMessages(
@ -362,32 +388,17 @@ class GroupManagerV2Impl @Inject constructor(
SnodeAPI.deleteMessage(groupAccountId.hexString, groupAdminAuth, messagesToDelete)
}
override suspend fun handleMemberLeft(message: GroupUpdated, group: AccountId) {
override suspend fun handleMemberLeftMessage(memberId: AccountId, group: AccountId) {
val closedGroup = configFactory.getClosedGroup(group) ?: return
val groupAdminKey = closedGroup.adminKey
if (closedGroup.hasAdminKey()) {
if (groupAdminKey != null) {
flagMembersForRemoval(
group = group,
members = listOf(AccountId(message.sender!!)),
groupAdminKey = groupAdminKey,
members = listOf(memberId),
alsoRemoveMembersMessage = false,
sendMemberChangeMessage = false
)
} else {
val hasAnyAdminRemaining = configFactory.withGroupConfigs(group) { configs ->
configs.groupMembers.all()
.asSequence()
.filterNot { it.sessionId == message.sender }
.any { it.admin && !it.removed }
}
// if the leaving member is last admin, disable the group and remove it
// This is just to emulate the "existing" group behaviour, this will probably be removed in future
if (!hasAnyAdminRemaining) {
pollerFactory.pollerFor(group)?.stop()
storage.getThreadId(Address.fromSerialized(group.hexString))
?.let(storage::deleteConversation)
configFactory.removeGroup(group)
}
}
}
@ -530,15 +541,17 @@ class GroupManagerV2Impl @Inject constructor(
storage.insertGroupInfoChange(message, group)
}
private suspend fun flagMembersForRemoval(
/**
* Mark this member as "removed" in the group config.
*
* [RemoveGroupMemberHandler] should be able to pick up the config changes and remove the member from the group.
*/
private fun flagMembersForRemoval(
group: AccountId,
groupAdminKey: ByteArray, // Not used ATM required here for verification purpose
members: List<AccountId>,
alsoRemoveMembersMessage: Boolean,
sendMemberChangeMessage: Boolean
) {
val adminKey = requireAdminAccess(group)
// 1. Mark the members as removed in the group configs
configFactory.withMutableGroupConfigs(group) { configs ->
for (member in members) {
val memberConfig = configs.groupMembers.get(member.hexString)
@ -547,33 +560,6 @@ class GroupManagerV2Impl @Inject constructor(
}
}
}
// 2. Send a member change message
if (sendMemberChangeMessage) {
val timestamp = clock.currentTimeMills()
val signature = SodiumUtilities.sign(
buildMemberChangeSignature(
GroupUpdateMemberChangeMessage.Type.REMOVED,
timestamp
),
adminKey
)
val updateMessage = GroupUpdateMessage.newBuilder()
.setMemberChangeMessage(
GroupUpdateMemberChangeMessage.newBuilder()
.addAllMemberSessionIds(members.map { it.hexString })
.setType(GroupUpdateMemberChangeMessage.Type.REMOVED)
.setAdminSignature(ByteString.copyFrom(signature))
)
.build()
val message = GroupUpdated(
updateMessage
).apply { sentTimestamp = timestamp }
MessageSender.send(message, Destination.ClosedGroup(group.hexString), false).await()
storage.insertGroupInfoChange(message, group)
}
}
override suspend fun respondToInvitation(groupId: AccountId, approved: Boolean) =

View File

@ -6,8 +6,10 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@ -44,7 +46,9 @@ import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.AccountId
import org.thoughtcrime.securesms.groups.ContactItem
import org.thoughtcrime.securesms.ui.Avatar
import org.thoughtcrime.securesms.ui.components.RadioButton
import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
@ -73,31 +77,26 @@ fun LazyListScope.multiSelectMemberList(
onContactItemClicked: (accountId: AccountId) -> Unit,
enabled: Boolean = true,
) {
items(contacts) { contact ->
Column {
Row(
modifier = modifier
.fillMaxWidth()
.toggleable(
enabled = enabled,
value = contact.selected,
onValueChange = { onContactItemClicked(contact.accountID) },
role = Role.Checkbox
)
.padding(vertical = 8.dp, horizontal = 24.dp),
verticalAlignment = CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
items(contacts.size) { index ->
val contact = contacts[index]
Column(modifier = modifier) {
if (index == 0) {
// Show top divider for the first item only
HorizontalDivider(color = LocalColors.current.borders)
}
RadioButton(
onClick = { onContactItemClicked(contact.accountID) },
selected = contact.selected,
enabled = enabled,
contentPadding = PaddingValues(
vertical = LocalDimensions.current.xxsSpacing,
horizontal = LocalDimensions.current.smallSpacing
)
) {
ContactPhoto(
contact.accountID,
)
ContactPhoto(contact.accountID)
Spacer(modifier = Modifier.size(LocalDimensions.current.smallSpacing))
MemberName(name = contact.name)
Checkbox(
checked = contact.selected,
onCheckedChange = null,
colors = CheckboxDefaults.colors(checkedColor = LocalColors.current.primary),
enabled = enabled,
)
}
HorizontalDivider(color = LocalColors.current.borders)
@ -105,15 +104,14 @@ fun LazyListScope.multiSelectMemberList(
}
}
val MemberNameStyle = TextStyle(fontWeight = FontWeight.Bold)
@Composable
fun RowScope.MemberName(
name: String,
modifier: Modifier = Modifier
) = Text(
text = name,
style = MemberNameStyle,
style = LocalType.current.h8,
color = LocalColors.current.text,
modifier = modifier
.weight(1f)
.align(CenterVertically)
@ -121,7 +119,7 @@ fun RowScope.MemberName(
@Composable
fun RowScope.ContactPhoto(sessionId: AccountId) {
fun ContactPhoto(sessionId: AccountId) {
return if (LocalInspectionMode.current) {
Image(
painterResource(id = R.drawable.ic_profile_default),

View File

@ -3,11 +3,14 @@ package org.thoughtcrime.securesms.groups.compose
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@ -71,7 +74,6 @@ fun CreateGroupScreen(
items = viewModel.selectContactsViewModel.contacts.collectAsState().value,
onCreateClicked = viewModel::onCreateClicked,
onBack = onBack,
onClose = onClose,
)
}
@ -88,54 +90,67 @@ fun CreateGroup(
items: List<ContactItem>,
onCreateClicked: () -> Unit,
onBack: () -> Unit,
onClose: () -> Unit,
modifier: Modifier = Modifier
) {
val focusManager = LocalFocusManager.current
Column(
modifier = modifier.padding(bottom = LocalDimensions.current.mediumSpacing),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
BackAppBar(
title = stringResource(id = R.string.groupCreate),
onBack = onBack,
)
SessionOutlinedTextField(
text = groupName,
onChange = onGroupNameChanged,
placeholder = stringResource(R.string.groupNameEnter),
textStyle = LocalType.current.base,
modifier = Modifier.padding(horizontal = 16.dp),
error = groupNameError.takeIf { it.isNotBlank() },
enabled = !showLoading,
onContinue = focusManager::clearFocus
)
SearchBar(
query = contactSearchQuery,
onValueChanged = onContactSearchQueryChanged,
placeholder = stringResource(R.string.searchContacts),
modifier = Modifier.padding(horizontal = 16.dp),
enabled = !showLoading
)
LazyColumn(modifier = Modifier.weight(1f)) {
multiSelectMemberList(
contacts = items,
onContactItemClicked = onContactItemClicked,
enabled = !showLoading
Scaffold(
containerColor = LocalColors.current.backgroundSecondary,
topBar = {
BackAppBar(
title = stringResource(id = R.string.groupCreate),
backgroundColor = LocalColors.current.backgroundSecondary,
onBack = onBack,
)
}
) { paddings ->
Box(modifier = modifier.padding(paddings),) {
Column(
modifier = modifier.padding(vertical = LocalDimensions.current.spacing),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.spacing)
) {
SessionOutlinedTextField(
text = groupName,
onChange = onGroupNameChanged,
placeholder = stringResource(R.string.groupNameEnter),
textStyle = LocalType.current.base,
modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing),
error = groupNameError.takeIf { it.isNotBlank() },
enabled = !showLoading,
innerPadding = PaddingValues(LocalDimensions.current.smallSpacing),
onContinue = focusManager::clearFocus
)
PrimaryOutlineButton(onClick = onCreateClicked, modifier = Modifier.widthIn(min = 120.dp)) {
LoadingArcOr(loading = showLoading) {
Text(stringResource(R.string.create))
SearchBar(
query = contactSearchQuery,
onValueChanged = onContactSearchQueryChanged,
placeholder = stringResource(R.string.searchContacts),
modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing),
enabled = !showLoading
)
LazyColumn(modifier = Modifier.weight(1f)) {
multiSelectMemberList(
contacts = items,
onContactItemClicked = onContactItemClicked,
enabled = !showLoading
)
}
PrimaryOutlineButton(
onClick = onCreateClicked,
modifier = Modifier.widthIn(min = 120.dp)
) {
LoadingArcOr(loading = showLoading) {
Text(stringResource(R.string.create))
}
}
}
}
}
}
@Preview
@ -150,18 +165,17 @@ private fun CreateGroupPreview(
PreviewTheme {
CreateGroup(
modifier = Modifier.background(LocalColors.current.backgroundSecondary),
groupName = "Group Name",
groupName = "",
onGroupNameChanged = {},
groupNameError = "",
contactSearchQuery = "",
onContactSearchQueryChanged = {},
onContactItemClicked = {},
items = previewMembers,
onBack = {},
onClose = {},
onCreateClicked = {},
showLoading = false,
groupNameError = "",
items = previewMembers,
onCreateClicked = {},
onBack = {},
modifier = Modifier.background(LocalColors.current.backgroundSecondary),
)
}

View File

@ -1,13 +1,13 @@
package org.thoughtcrime.securesms.groups.compose
import android.widget.Toast
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
@ -15,14 +15,10 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SheetState
import androidx.compose.material3.Snackbar
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -50,14 +46,15 @@ import org.thoughtcrime.securesms.groups.GroupMemberState
import org.thoughtcrime.securesms.ui.AlertDialog
import org.thoughtcrime.securesms.ui.DialogButtonModel
import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.components.ActionAppBar
import org.thoughtcrime.securesms.ui.components.AppBarBackIcon
import org.thoughtcrime.securesms.ui.components.BackAppBar
import org.thoughtcrime.securesms.ui.components.BottomOptionsDialog
import org.thoughtcrime.securesms.ui.components.BottomOptionsDialogItem
import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton
import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField
import org.thoughtcrime.securesms.ui.theme.LocalColors
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
import org.thoughtcrime.securesms.ui.theme.LocalType
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.theme.bold
@Composable
fun EditGroupScreen(
@ -130,9 +127,7 @@ fun EditGroup(
showingError: String?,
onErrorDismissed: () -> Unit,
) {
val sheetState = rememberModalBottomSheetState()
val (showingBottomModelForMember, setShowingBottomModelForMember) = remember {
val (showingOptionsDialogForMember, setShowingBottomModelForMember) = remember {
mutableStateOf<GroupMemberState?>(null)
}
@ -142,20 +137,9 @@ fun EditGroup(
Scaffold(
topBar = {
ActionAppBar(
BackAppBar(
title = stringResource(id = R.string.groupEdit),
navigationIcon = {
AppBarBackIcon(onBack = onBackClick)
},
actions = {
TextButton(onClick = onBackClick) {
Text(
text = stringResource(id = R.string.done),
color = LocalColors.current.text,
style = LocalType.current.large.bold()
)
}
},
onBack = onBackClick,
)
}
) { paddingValues ->
@ -168,8 +152,11 @@ fun EditGroup(
modifier = Modifier
.animateContentSize()
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterHorizontally),
.padding(LocalDimensions.current.smallSpacing),
horizontalArrangement = Arrangement.spacedBy(
LocalDimensions.current.xxxsSpacing,
Alignment.CenterHorizontally
),
verticalAlignment = CenterVertically,
) {
if (editingName != null) {
@ -185,7 +172,11 @@ fun EditGroup(
modifier = Modifier.width(180.dp),
text = editingName,
onChange = onEditingNameValueChanged,
textStyle = LocalType.current.large
textStyle = LocalType.current.h8,
innerPadding = PaddingValues(
horizontal = LocalDimensions.current.spacing,
vertical = LocalDimensions.current.smallSpacing
)
)
IconButton(onClick = onEditNameConfirmed) {
@ -198,7 +189,7 @@ fun EditGroup(
} else {
Text(
text = groupName,
style = LocalType.current.h3,
style = LocalType.current.h4,
textAlign = TextAlign.Center,
)
@ -242,6 +233,7 @@ fun EditGroup(
MemberItem(
modifier = Modifier.fillMaxWidth(),
member = member,
clickable = member.canEdit,
onClick = { setShowingBottomModelForMember(member) }
)
}
@ -249,27 +241,26 @@ fun EditGroup(
}
}
if (showingBottomModelForMember != null) {
MemberModalBottomSheetOptions(
if (showingOptionsDialogForMember != null) {
MemberOptionsDialog(
onDismissRequest = { setShowingBottomModelForMember(null) },
sheetState = sheetState,
onRemove = {
setShowingConfirmRemovingMember(showingBottomModelForMember)
setShowingConfirmRemovingMember(showingOptionsDialogForMember)
setShowingBottomModelForMember(null)
},
onPromote = {
setShowingBottomModelForMember(null)
onPromoteClick(showingBottomModelForMember.accountId)
onPromoteClick(showingOptionsDialogForMember.accountId)
},
onResendInvite = {
setShowingBottomModelForMember(null)
onResendInviteClick(showingBottomModelForMember.accountId)
onResendInviteClick(showingOptionsDialogForMember.accountId)
},
onResendPromotion = {
setShowingBottomModelForMember(null)
onResendPromotionClick(showingBottomModelForMember.accountId)
onResendPromotionClick(showingOptionsDialogForMember.accountId)
},
member = showingBottomModelForMember,
member = showingOptionsDialogForMember,
)
}
@ -284,17 +275,13 @@ fun EditGroup(
)
}
if (!showingError.isNullOrEmpty()) {
Snackbar(
dismissAction = {
TextButton(onClick = onErrorDismissed) {
Text(text = stringResource(id = R.string.dismiss))
}
},
content = {
Text(text = showingError)
}
)
val context = LocalContext.current
LaunchedEffect(showingError) {
if (showingError != null) {
Toast.makeText(context, showingError, Toast.LENGTH_SHORT).show()
onErrorDismissed()
}
}
}
@ -328,80 +315,81 @@ private fun ConfirmRemovingMemberDialog(
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun MemberModalBottomSheetOptions(
private fun MemberOptionsDialog(
member: GroupMemberState,
onRemove: () -> Unit,
onPromote: () -> Unit,
onResendInvite: () -> Unit,
onResendPromotion: () -> Unit,
onDismissRequest: () -> Unit,
sheetState: SheetState,
) {
ModalBottomSheet(
onDismissRequest = onDismissRequest,
sheetState = sheetState,
) {
if (member.canRemove) {
val context = LocalContext.current
MemberModalBottomSheetOptionItem(
onClick = onRemove,
text = context.resources.getQuantityString(R.plurals.groupRemoveUserOnly, 1)
)
}
val context = LocalContext.current
if (member.canPromote) {
MemberModalBottomSheetOptionItem(
onClick = onPromote,
text = stringResource(R.string.adminPromoteToAdmin)
)
}
val options = remember(member) {
buildList {
if (member.canRemove) {
this += BottomOptionsDialogItem(
title = context.resources.getQuantityString(R.plurals.groupRemoveUserOnly, 1),
iconRes = R.drawable.ic_delete,
onClick = onRemove
)
}
if (member.canResendInvite) {
MemberModalBottomSheetOptionItem(onClick = onResendInvite, text = "Resend invite")
}
if (member.canPromote) {
this += BottomOptionsDialogItem(
title = context.getString(R.string.adminPromoteToAdmin),
iconRes = R.drawable.ic_profile_default,
onClick = onPromote
)
}
if (member.canResendPromotion) {
MemberModalBottomSheetOptionItem(onClick = onResendPromotion, text = "Resend promotion")
}
if (member.canResendInvite) {
this += BottomOptionsDialogItem(
title = "Resend invite",
iconRes = R.drawable.ic_arrow_left,
onClick = onResendInvite
)
}
Spacer(modifier = Modifier.height(32.dp))
if (member.canResendPromotion) {
this += BottomOptionsDialogItem(
title = "Resend promotion",
iconRes = R.drawable.ic_arrow_left,
onClick = onResendPromotion
)
}
}
}
}
@Composable
private fun MemberModalBottomSheetOptionItem(
text: String,
onClick: () -> Unit
) {
Text(
modifier = Modifier
.clickable(onClick = onClick)
.padding(16.dp)
.fillMaxWidth(),
style = LocalType.current.base,
text = text,
color = LocalColors.current.text,
BottomOptionsDialog(
items = options,
onDismissRequest = onDismissRequest
)
}
@Composable
private fun MemberItem(
clickable: Boolean,
onClick: (accountId: AccountId) -> Unit,
member: GroupMemberState,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier.padding(horizontal = 16.dp, vertical = 8.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier
.clickable(enabled = clickable, onClick = { onClick(member.accountId) })
.padding(
horizontal = LocalDimensions.current.smallSpacing,
vertical = LocalDimensions.current.xsSpacing
),
horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing),
verticalAlignment = CenterVertically,
) {
ContactPhoto(member.accountId)
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(4.dp)
verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.xxxsSpacing)
) {
Text(
@ -424,12 +412,10 @@ private fun MemberItem(
}
if (member.canEdit) {
IconButton(onClick = { onClick(member.accountId) }) {
Icon(
painter = painterResource(R.drawable.ic_circle_dot_dot_dot),
contentDescription = stringResource(R.string.AccessibilityId_sessionSettings)
)
}
Icon(
painter = painterResource(R.drawable.ic_circle_dot_dot_dot),
contentDescription = stringResource(R.string.AccessibilityId_sessionSettings)
)
}
}
}

View File

@ -544,7 +544,7 @@ fun SearchBar(
painterResource(id = R.drawable.ic_search_24),
contentDescription = null,
colorFilter = ColorFilter.tint(
LocalColors.current.text
LocalColors.current.textSecondary
),
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
@ -557,7 +557,7 @@ fun SearchBar(
Text(
text = placeholder,
color = LocalColors.current.textSecondary,
style = LocalType.current.base
style = LocalType.current.xl
)
}
}

View File

@ -8,6 +8,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@ -91,6 +92,7 @@ fun SessionOutlinedTextField(
modifier: Modifier = Modifier,
onChange: (String) -> Unit = {},
textStyle: TextStyle = LocalType.current.base,
innerPadding: PaddingValues = PaddingValues(LocalDimensions.current.spacing),
placeholder: String = "",
onContinue: () -> Unit = {},
error: String? = null,
@ -122,7 +124,7 @@ fun SessionOutlinedTextField(
)
.fillMaxWidth()
.wrapContentHeight()
.padding(LocalDimensions.current.spacing)
.padding(innerPadding)
) {
innerTextField()

View File

@ -42,7 +42,7 @@ interface GroupManagerV2 {
members: List<AccountId>
)
suspend fun handleMemberLeft(message: GroupUpdated, group: AccountId)
suspend fun handleMemberLeftMessage(memberId: AccountId, group: AccountId)
suspend fun leaveGroup(groupId:
AccountId, deleteOnLeave: Boolean)

View File

@ -57,7 +57,6 @@ import org.session.libsignal.crypto.ecc.DjbECPublicKey
import org.session.libsignal.crypto.ecc.ECKeyPair
import org.session.libsignal.messages.SignalServiceGroup
import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.protos.SignalServiceProtos.DataMessage.GroupUpdateMemberChangeMessage
import org.session.libsignal.protos.SignalServiceProtos.SharedConfigMessage
import org.session.libsignal.utilities.AccountId
import org.session.libsignal.utilities.Base64
@ -675,8 +674,12 @@ private fun handleMemberChange(message: GroupUpdated, closedGroup: AccountId) {
private fun handleMemberLeft(message: GroupUpdated, closedGroup: AccountId) {
GlobalScope.launch(Dispatchers.Default) {
runCatching {
MessagingModuleConfiguration.shared.groupManagerV2.handleMemberLeft(message, closedGroup)
try {
MessagingModuleConfiguration.shared.groupManagerV2.handleMemberLeftMessage(
AccountId(message.sender!!), closedGroup
)
} catch (e: Exception) {
Log.e("GroupUpdated", "Failed to handle member left message", e)
}
}
}

View File

@ -209,11 +209,12 @@ object UpdateMessageBuilder {
if (historyShared) R.string.groupMemberNewYouHistoryMultiple else R.string.groupInviteYouAndMoreNew)
.put(COUNT_KEY, updateData.sessionIds.size - 1)
.format()
else -> Phrase.from(context,
number > 0 -> Phrase.from(context,
if (historyShared) R.string.groupMemberNewHistoryMultiple else R.string.groupMemberNewMultiple)
.put(NAME_KEY, context.youOrSender(updateData.sessionIds.first()))
.put(COUNT_KEY, updateData.sessionIds.size - 1)
.format()
else -> ""
}
}