mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-03-31 03:16:05 +00:00
android, desktop: add gallery tabs to group info view (#6559)
* Added GroupSettings button and view and moved related logic * Added tabs in group settings and done UI skeleton for those * Fixed merge issues * Reverted back some classes to original places * Reverted back some classes to original places --------- Co-authored-by: hayk-space <hayk.nahapetian@space.ge> Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
@@ -0,0 +1,396 @@
|
||||
package chat.simplex.common.views.chat.group
|
||||
|
||||
import SectionDividerSpaced
|
||||
import SectionItemView
|
||||
import SectionItemViewLongClickable
|
||||
import SectionSpacer
|
||||
import SectionView
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.chatModel
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.SelectedListItem
|
||||
import chat.simplex.common.views.chat.SendReceipts
|
||||
import chat.simplex.common.views.chat.item.ItemAction
|
||||
import chat.simplex.common.views.chat.item.SelectItemAction
|
||||
import chat.simplex.common.views.chat.group.showGroupReportsView
|
||||
import chat.simplex.common.views.chatlist.cantInviteIncognitoAlert
|
||||
import chat.simplex.common.views.chatlist.openChat
|
||||
import chat.simplex.common.views.chatlist.UnreadBadge
|
||||
import chat.simplex.common.views.chatlist.unreadCountStr
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItem
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
enum class GroupInfoTab {
|
||||
Members,
|
||||
Images,
|
||||
Videos,
|
||||
Links,
|
||||
Files,
|
||||
Voices
|
||||
}
|
||||
|
||||
fun LazyListScope.GroupChatInfoTabs(
|
||||
groupInfo: GroupInfo,
|
||||
activeSortedMembers: List<GroupMember>,
|
||||
filteredMembers: State<List<GroupMember>>,
|
||||
searchText: MutableState<TextFieldValue>,
|
||||
selectedItems: MutableState<Set<Long>?>,
|
||||
scrollToItemId: MutableState<Long?>,
|
||||
addMembers: () -> Unit,
|
||||
showMemberInfo: (GroupMember) -> Unit,
|
||||
selectedTab: MutableState<GroupInfoTab>,
|
||||
filteredChatItems: State<List<ChatItem>>,
|
||||
chat: Chat,
|
||||
groupLink: GroupLink?,
|
||||
manageGroupLink: () -> Unit,
|
||||
openMemberSupport: () -> Unit
|
||||
) {
|
||||
|
||||
item {
|
||||
SectionSpacer()
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
Column {
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(scrollState),
|
||||
horizontalArrangement = Arrangement.Start
|
||||
) {
|
||||
GroupInfoTab.values().forEach { tab ->
|
||||
Tab(
|
||||
selected = selectedTab.value == tab,
|
||||
onClick = { selectedTab.value = tab },
|
||||
text = { Text(tabTitle(tab), fontSize = 13.sp) },
|
||||
selectedContentColor = MaterialTheme.colors.primary,
|
||||
unselectedContentColor = MaterialTheme.colors.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
// Simple indicator line
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(2.dp)
|
||||
.background(MaterialTheme.colors.surface)
|
||||
)
|
||||
}
|
||||
Divider()
|
||||
|
||||
SectionSpacer()
|
||||
}
|
||||
|
||||
when (selectedTab.value) {
|
||||
GroupInfoTab.Members -> {
|
||||
MembersTabContent(
|
||||
groupInfo = groupInfo,
|
||||
activeSortedMembers = activeSortedMembers,
|
||||
filteredMembers = filteredMembers,
|
||||
searchText = searchText,
|
||||
selectedItems = selectedItems,
|
||||
showMemberInfo = showMemberInfo,
|
||||
addMembers = addMembers,
|
||||
chat = chat,
|
||||
groupLink = groupLink,
|
||||
manageGroupLink = manageGroupLink,
|
||||
openMemberSupport = openMemberSupport,
|
||||
scrollToItemId = scrollToItemId
|
||||
)
|
||||
}
|
||||
GroupInfoTab.Images,
|
||||
GroupInfoTab.Videos,
|
||||
GroupInfoTab.Links,
|
||||
GroupInfoTab.Files,
|
||||
GroupInfoTab.Voices -> {
|
||||
ContentItemsTab(
|
||||
filteredChatItems = filteredChatItems,
|
||||
scrollToItemId = scrollToItemId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.MembersTabContent(
|
||||
groupInfo: GroupInfo,
|
||||
activeSortedMembers: List<GroupMember>,
|
||||
filteredMembers: State<List<GroupMember>>,
|
||||
searchText: MutableState<TextFieldValue>,
|
||||
selectedItems: MutableState<Set<Long>?>,
|
||||
showMemberInfo: (GroupMember) -> Unit,
|
||||
addMembers: () -> Unit,
|
||||
chat: Chat,
|
||||
groupLink: GroupLink?,
|
||||
manageGroupLink: () -> Unit,
|
||||
openMemberSupport: () -> Unit,
|
||||
scrollToItemId: MutableState<Long?>
|
||||
) {
|
||||
|
||||
item {
|
||||
val scope = rememberCoroutineScope()
|
||||
var anyTopSectionRowShow = false
|
||||
SectionView {
|
||||
if (groupInfo.canAddMembers && groupInfo.businessChat == null) {
|
||||
anyTopSectionRowShow = true
|
||||
if (groupLink == null) {
|
||||
CreateGroupLinkButton(manageGroupLink)
|
||||
} else {
|
||||
GroupLinkButton(manageGroupLink)
|
||||
}
|
||||
}
|
||||
if (groupInfo.businessChat == null && groupInfo.membership.memberRole >= GroupMemberRole.Moderator) {
|
||||
anyTopSectionRowShow = true
|
||||
MemberSupportButton(chat, openMemberSupport)
|
||||
}
|
||||
if (groupInfo.canModerate) {
|
||||
anyTopSectionRowShow = true
|
||||
GroupReportsButton(chat) {
|
||||
scope.launch {
|
||||
showGroupReportsView(chatModel.chatId, scrollToItemId, chat.chatInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
groupInfo.membership.memberActive &&
|
||||
(groupInfo.membership.memberRole < GroupMemberRole.Moderator || groupInfo.membership.supportChat != null)
|
||||
) {
|
||||
anyTopSectionRowShow = true
|
||||
UserSupportChatButton(chat, groupInfo, scrollToItemId)
|
||||
}
|
||||
}
|
||||
if (anyTopSectionRowShow) {
|
||||
SectionDividerSpaced(maxBottomPadding = false)
|
||||
}
|
||||
}
|
||||
|
||||
if (!groupInfo.nextConnectPrepared) {
|
||||
item {
|
||||
SectionView(title = String.format(generalGetString(MR.strings.group_info_section_title_num_members), activeSortedMembers.count() + 1)) {
|
||||
if (groupInfo.canAddMembers) {
|
||||
val onAddMembersClick = if (chat.chatInfo.incognito) ::cantInviteIncognitoAlert else addMembers
|
||||
val tint = if (chat.chatInfo.incognito) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
|
||||
val addMembersTitleId = when (groupInfo.businessChat?.chatType) {
|
||||
BusinessChatType.Customer -> MR.strings.button_add_team_members
|
||||
BusinessChatType.Business -> MR.strings.button_add_friends
|
||||
null -> MR.strings.button_add_members
|
||||
}
|
||||
AddMembersButton(addMembersTitleId, tint, onAddMembersClick)
|
||||
}
|
||||
if (activeSortedMembers.size > 8) {
|
||||
SectionItemView(padding = PaddingValues(start = 14.dp, end = DEFAULT_PADDING_HALF)) {
|
||||
MemberListSearchRowView(searchText)
|
||||
}
|
||||
}
|
||||
SectionItemView(minHeight = 54.dp, padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
|
||||
MemberRow(groupInfo.membership, user = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!groupInfo.nextConnectPrepared) {
|
||||
items(filteredMembers.value, key = { it.groupMemberId }) { member ->
|
||||
Divider()
|
||||
val showMenu = remember { mutableStateOf(false) }
|
||||
val canBeSelected = groupInfo.membership.memberRole >= member.memberRole && member.memberRole < GroupMemberRole.Moderator
|
||||
SectionItemViewLongClickable(
|
||||
click = {
|
||||
if (selectedItems.value != null) {
|
||||
if (canBeSelected) {
|
||||
toggleItemSelection(member.groupMemberId, selectedItems)
|
||||
}
|
||||
} else {
|
||||
showMemberInfo(member)
|
||||
}
|
||||
},
|
||||
longClick = { showMenu.value = true },
|
||||
minHeight = 54.dp,
|
||||
padding = PaddingValues(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
Box(contentAlignment = Alignment.CenterStart) {
|
||||
this@SectionItemViewLongClickable.AnimatedVisibility(selectedItems.value != null, enter = fadeIn(), exit = fadeOut()) {
|
||||
SelectedListItem(Modifier.alpha(if (canBeSelected) 1f else 0f).padding(start = 2.dp), member.groupMemberId, selectedItems)
|
||||
}
|
||||
val selectionOffset by animateDpAsState(if (selectedItems.value != null) 20.dp + 22.dp * fontSizeMultiplier else 0.dp)
|
||||
DropDownMenuForMember(chat.remoteHostId, member, groupInfo, selectedItems, showMenu)
|
||||
Box(Modifier.padding(start = selectionOffset)) {
|
||||
MemberRow(member)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.ContentItemsTab(
|
||||
filteredChatItems: State<List<ChatItem>>,
|
||||
scrollToItemId: MutableState<Long?>
|
||||
) {
|
||||
if (filteredChatItems.value.isEmpty()) {
|
||||
item {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = DEFAULT_PADDING * 2),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
//TODO: this is just a temporary UI, if no item, tab will not be shown
|
||||
Text(
|
||||
"No items found",
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
items(filteredChatItems.value, key = { it.id }) { chatItem ->
|
||||
Divider()
|
||||
SectionItemView(
|
||||
click = { scrollToItemId.value = chatItem.id },
|
||||
minHeight = 54.dp,
|
||||
padding = PaddingValues(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
GroupChatItemRow(chatItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun tabTitle(tab: GroupInfoTab): String {
|
||||
return when (tab) {
|
||||
GroupInfoTab.Members -> stringResource(MR.strings.group_info_tab_members)
|
||||
GroupInfoTab.Images -> stringResource(MR.strings.group_info_tab_images)
|
||||
GroupInfoTab.Videos -> stringResource(MR.strings.group_info_tab_videos)
|
||||
GroupInfoTab.Links -> stringResource(MR.strings.group_info_tab_links)
|
||||
GroupInfoTab.Files -> stringResource(MR.strings.group_info_tab_files)
|
||||
GroupInfoTab.Voices -> stringResource(MR.strings.group_info_tab_voices)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun GroupChatItemRow(chatItem: ChatItem) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
when (val content = chatItem.content.msgContent) {
|
||||
is MsgContent.MCImage -> {
|
||||
Icon(painterResource(MR.images.ic_image), null, Modifier.size(24.dp), tint = MaterialTheme.colors.secondary)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING))
|
||||
Column(Modifier.weight(1f)) {
|
||||
//TODO, This is just a temporary UI, needs to be adjusted
|
||||
Text(
|
||||
content.text.ifEmpty { "Image" },
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
getTimestampText(chatItem.meta.itemTs),
|
||||
fontSize = 12.sp,
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
is MsgContent.MCVideo -> {
|
||||
Icon(painterResource(MR.images.ic_videocam), null, Modifier.size(24.dp), tint = MaterialTheme.colors.secondary)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING))
|
||||
Column(Modifier.weight(1f)) {
|
||||
//TODO, This is just a temporary UI, needs to be adjusted
|
||||
Text(
|
||||
content.text.ifEmpty { "Video" },
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
getTimestampText(chatItem.meta.itemTs),
|
||||
fontSize = 12.sp,
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
is MsgContent.MCLink -> {
|
||||
Icon(painterResource(MR.images.ic_link), null, Modifier.size(24.dp), tint = MaterialTheme.colors.secondary)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING))
|
||||
Column(Modifier.weight(1f)) {
|
||||
//TODO, This is just a temporary UI, needs to be adjusted
|
||||
Text(
|
||||
content.preview.uri.ifEmpty { content.text },
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
getTimestampText(chatItem.meta.itemTs),
|
||||
fontSize = 12.sp,
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
is MsgContent.MCFile -> {
|
||||
Icon(painterResource(MR.images.ic_draft_filled), null, Modifier.size(24.dp), tint = MaterialTheme.colors.secondary)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING))
|
||||
Column(Modifier.weight(1f)) {
|
||||
//TODO, This is just a temporary UI, needs to be adjusted
|
||||
Text(
|
||||
chatItem.file?.fileName ?: content.text.ifEmpty { "File" },
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
getTimestampText(chatItem.meta.itemTs),
|
||||
fontSize = 12.sp,
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
is MsgContent.MCVoice -> {
|
||||
Icon(painterResource(MR.images.ic_mic_filled), null, Modifier.size(24.dp), tint = MaterialTheme.colors.secondary)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING))
|
||||
Column(Modifier.weight(1f)) {
|
||||
//TODO, This is just a temporary UI, needs to be adjusted
|
||||
Text(
|
||||
content.text.ifEmpty { generalGetString(MR.strings.voice_message) },
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
getTimestampText(chatItem.meta.itemTs),
|
||||
fontSize = 12.sp,
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Text(
|
||||
generalGetString(MR.strings.unknown_message_format),
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import SectionSpacer
|
||||
import SectionTextFooter
|
||||
import SectionView
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
@@ -161,11 +162,21 @@ fun ModalData.GroupChatInfoView(
|
||||
)
|
||||
}
|
||||
},
|
||||
openSettings = {
|
||||
ModalManager.end.showCustomModal { close ->
|
||||
GroupSettingsView(
|
||||
chatModel,
|
||||
rhId,
|
||||
chat.id,
|
||||
close
|
||||
)
|
||||
}
|
||||
},
|
||||
deleteGroup = { deleteGroupDialog(chat, groupInfo, chatModel, close) },
|
||||
clearChat = { clearChatDialog(chat, close) },
|
||||
leaveGroup = { leaveGroupDialog(rhId, groupInfo, chatModel, close) },
|
||||
manageGroupLink = {
|
||||
ModalManager.end.showModal { GroupLinkView(chatModel, rhId, groupInfo, groupLink, onGroupLinkUpdated) }
|
||||
ModalManager.end.showModal { GroupLinkView(chatModel, rhId, groupInfo, groupLink, onGroupLinkUpdated) }
|
||||
},
|
||||
onSearchClicked = onSearchClicked,
|
||||
deletingItems = deletingItems
|
||||
@@ -228,7 +239,7 @@ fun leaveGroupDialog(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, cl
|
||||
)
|
||||
}
|
||||
|
||||
private fun removeMemberAlert(rhId: Long?, groupInfo: GroupInfo, mem: GroupMember) {
|
||||
fun removeMemberAlert(rhId: Long?, groupInfo: GroupInfo, mem: GroupMember) {
|
||||
val messageId = if (groupInfo.businessChat == null)
|
||||
MR.strings.member_will_be_removed_from_group_cannot_be_undone
|
||||
else
|
||||
@@ -326,7 +337,7 @@ fun MuteButton(
|
||||
|
||||
InfoViewActionButton(
|
||||
modifier = modifier,
|
||||
icon = painterResource(nextNotificationMode.icon),
|
||||
icon = painterResource(nextNotificationMode.icon),
|
||||
title = generalGetString(nextNotificationMode.text(true)),
|
||||
disabled = !groupInfo.ready,
|
||||
disabledLook = !groupInfo.ready,
|
||||
@@ -336,6 +347,22 @@ fun MuteButton(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsButton(
|
||||
modifier: Modifier,
|
||||
groupInfo: GroupInfo,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
InfoViewActionButton(
|
||||
modifier = modifier,
|
||||
icon = painterResource(MR.images.ic_settings),
|
||||
title = generalGetString(MR.strings.icon_descr_settings),
|
||||
disabled = !groupInfo.ready,
|
||||
disabledLook = !groupInfo.ready,
|
||||
onClick = onClick
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AddGroupMembersButton(
|
||||
modifier: Modifier,
|
||||
@@ -344,7 +371,7 @@ fun AddGroupMembersButton(
|
||||
) {
|
||||
InfoViewActionButton(
|
||||
modifier = modifier,
|
||||
icon = if (groupInfo.incognito) painterResource(MR.images.ic_add_link) else painterResource(MR.images.ic_person_add_500),
|
||||
icon = if (groupInfo.incognito) painterResource(MR.images.ic_add_link) else painterResource(MR.images.ic_person_add_500),
|
||||
title = stringResource(MR.strings.action_button_add_members),
|
||||
disabled = !groupInfo.ready,
|
||||
disabledLook = !groupInfo.ready,
|
||||
@@ -414,11 +441,12 @@ fun ModalData.GroupChatInfoLayout(
|
||||
addOrEditWelcomeMessage: () -> Unit,
|
||||
openMemberSupport: () -> Unit,
|
||||
openPreferences: () -> Unit,
|
||||
openSettings: () -> Unit,
|
||||
deleteGroup: () -> Unit,
|
||||
clearChat: () -> Unit,
|
||||
leaveGroup: () -> Unit,
|
||||
manageGroupLink: () -> Unit,
|
||||
close: () -> Unit = { ModalManager.closeAllModalsEverywhere()},
|
||||
close: () -> Unit = { ModalManager.closeAllModalsEverywhere() },
|
||||
onSearchClicked: () -> Unit,
|
||||
deletingItems: State<Boolean>
|
||||
) {
|
||||
@@ -434,219 +462,124 @@ fun ModalData.GroupChatInfoLayout(
|
||||
if (s.isEmpty()) activeSortedMembers else activeSortedMembers.filter { m -> m.anyNameContains(s) }
|
||||
}
|
||||
}
|
||||
val selectedTab = rememberSaveable { mutableStateOf(GroupInfoTab.Members) }
|
||||
val filteredChatItems = remember(chat.chatItems, selectedTab.value) {
|
||||
derivedStateOf {
|
||||
when (selectedTab.value) {
|
||||
GroupInfoTab.Members -> emptyList()
|
||||
GroupInfoTab.Images -> chat.chatItems.filter {
|
||||
it.content.msgContent is MsgContent.MCImage && it.meta.itemDeleted == null
|
||||
}
|
||||
GroupInfoTab.Videos -> chat.chatItems.filter {
|
||||
it.content.msgContent is MsgContent.MCVideo && it.meta.itemDeleted == null
|
||||
}
|
||||
GroupInfoTab.Links -> chat.chatItems.filter {
|
||||
it.content.msgContent is MsgContent.MCLink && it.meta.itemDeleted == null
|
||||
}
|
||||
GroupInfoTab.Files -> chat.chatItems.filter {
|
||||
it.content.msgContent is MsgContent.MCFile && it.meta.itemDeleted == null
|
||||
}
|
||||
GroupInfoTab.Voices -> chat.chatItems.filter {
|
||||
it.content.msgContent is MsgContent.MCVoice && it.meta.itemDeleted == null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Box {
|
||||
val oneHandUI = remember { appPrefs.oneHandUI.state }
|
||||
val selectedItemsBarHeight = if (selectedItems.value != null) AppBarHeight * fontSizeSqrtMultiplier else 0.dp
|
||||
val navBarPadding = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
|
||||
val imePadding = WindowInsets.ime.asPaddingValues().calculateBottomPadding()
|
||||
LazyColumnWithScrollBar(
|
||||
state = listState,
|
||||
contentPadding = if (oneHandUI.value) {
|
||||
PaddingValues(
|
||||
top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + DEFAULT_PADDING + 5.dp,
|
||||
bottom = navBarPadding +
|
||||
imePadding +
|
||||
selectedItemsBarHeight +
|
||||
// TODO: that's workaround but works. Actually, something in the codebase doesn't consume padding for AppBar and it produce
|
||||
// different padding when the user has NavigationBar and doesn't have it with ime shown (developer options helps to test it nav bars)
|
||||
(if (navBarPadding > 0.dp && imePadding > 0.dp) 0.dp else AppBarHeight * fontSizeSqrtMultiplier)
|
||||
)
|
||||
} else {
|
||||
PaddingValues(
|
||||
top = topPaddingToContent(false),
|
||||
bottom = if (imePadding > 0.dp) {
|
||||
imePadding + selectedItemsBarHeight
|
||||
} else {
|
||||
navBarPadding + selectedItemsBarHeight
|
||||
}
|
||||
)
|
||||
}
|
||||
) {
|
||||
item {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
GroupChatInfoHeader(chat.chatInfo, groupInfo)
|
||||
LazyColumnWithScrollBar(
|
||||
state = listState,
|
||||
contentPadding = if (oneHandUI.value) {
|
||||
PaddingValues(
|
||||
top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + DEFAULT_PADDING + 5.dp,
|
||||
bottom = navBarPadding +
|
||||
imePadding +
|
||||
selectedItemsBarHeight +
|
||||
// TODO: that's workaround but works. Actually, something in the codebase doesn't consume padding for AppBar and it produce
|
||||
// different padding when the user has NavigationBar and doesn't have it with ime shown (developer options helps to test it nav bars)
|
||||
(if (navBarPadding > 0.dp && imePadding > 0.dp) 0.dp else AppBarHeight * fontSizeSqrtMultiplier)
|
||||
)
|
||||
} else {
|
||||
PaddingValues(
|
||||
top = topPaddingToContent(false),
|
||||
bottom = if (imePadding > 0.dp) {
|
||||
imePadding + selectedItemsBarHeight
|
||||
} else {
|
||||
navBarPadding + selectedItemsBarHeight
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
LocalAliasEditor(chat.id, groupInfo.localAlias, isContact = false, updateValue = onLocalAliasChanged)
|
||||
|
||||
SectionSpacer()
|
||||
|
||||
Box(
|
||||
Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
) {
|
||||
item {
|
||||
Row(
|
||||
Modifier
|
||||
.widthIn(max = if (groupInfo.canAddMembers) 320.dp else 230.dp)
|
||||
.padding(horizontal = DEFAULT_PADDING),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
if (groupInfo.canAddMembers) {
|
||||
SearchButton(modifier = Modifier.fillMaxWidth(0.33f), chat, groupInfo, close, onSearchClicked)
|
||||
AddGroupMembersButton(modifier = Modifier.fillMaxWidth(0.5f), chat, groupInfo)
|
||||
MuteButton(modifier = Modifier.fillMaxWidth(1f), chat, groupInfo)
|
||||
} else {
|
||||
SearchButton(modifier = Modifier.fillMaxWidth(0.5f), chat, groupInfo, close, onSearchClicked)
|
||||
MuteButton(modifier = Modifier.fillMaxWidth(1f), chat, groupInfo)
|
||||
}
|
||||
GroupChatInfoHeader(chat.chatInfo, groupInfo)
|
||||
}
|
||||
}
|
||||
|
||||
SectionSpacer()
|
||||
LocalAliasEditor(chat.id, groupInfo.localAlias, isContact = false, updateValue = onLocalAliasChanged)
|
||||
|
||||
var anyTopSectionRowShow = false
|
||||
SectionView {
|
||||
if (groupInfo.canAddMembers && groupInfo.businessChat == null) {
|
||||
anyTopSectionRowShow = true
|
||||
if (groupLink == null) {
|
||||
CreateGroupLinkButton(manageGroupLink)
|
||||
} else {
|
||||
GroupLinkButton(manageGroupLink)
|
||||
}
|
||||
}
|
||||
if (groupInfo.businessChat == null && groupInfo.membership.memberRole >= GroupMemberRole.Moderator) {
|
||||
anyTopSectionRowShow = true
|
||||
MemberSupportButton(chat, openMemberSupport)
|
||||
}
|
||||
if (groupInfo.canModerate) {
|
||||
anyTopSectionRowShow = true
|
||||
GroupReportsButton(chat) {
|
||||
scope.launch {
|
||||
showGroupReportsView(chatModel.chatId, scrollToItemId, chat.chatInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
groupInfo.membership.memberActive &&
|
||||
(groupInfo.membership.memberRole < GroupMemberRole.Moderator || groupInfo.membership.supportChat != null)
|
||||
SectionSpacer()
|
||||
|
||||
Box(
|
||||
Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
anyTopSectionRowShow = true
|
||||
UserSupportChatButton(chat, groupInfo, scrollToItemId)
|
||||
}
|
||||
}
|
||||
if (anyTopSectionRowShow) {
|
||||
SectionDividerSpaced(maxBottomPadding = false)
|
||||
}
|
||||
|
||||
SectionView {
|
||||
if (groupInfo.isOwner && groupInfo.businessChat?.chatType == null) {
|
||||
EditGroupProfileButton(editGroupProfile)
|
||||
}
|
||||
if (groupInfo.groupProfile.description != null || (groupInfo.isOwner && groupInfo.businessChat?.chatType == null)) {
|
||||
AddOrEditWelcomeMessage(groupInfo.groupProfile.description, addOrEditWelcomeMessage)
|
||||
}
|
||||
val prefsTitleId = if (groupInfo.businessChat == null) MR.strings.group_preferences else MR.strings.chat_preferences
|
||||
GroupPreferencesButton(prefsTitleId, openPreferences)
|
||||
}
|
||||
val footerId = if (groupInfo.businessChat == null) MR.strings.only_group_owners_can_change_prefs else MR.strings.only_chat_owners_can_change_prefs
|
||||
SectionTextFooter(stringResource(footerId))
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
|
||||
SectionView {
|
||||
if (activeSortedMembers.filter { it.memberCurrent }.size <= SMALL_GROUPS_RCPS_MEM_LIMIT) {
|
||||
SendReceiptsOption(currentUser, sendReceipts, setSendReceipts)
|
||||
} else {
|
||||
SendReceiptsOptionDisabled()
|
||||
}
|
||||
WallpaperButton {
|
||||
ModalManager.end.showModal {
|
||||
val chat = remember { derivedStateOf { chatModel.chats.value.firstOrNull { it.id == chat.id } } }
|
||||
val c = chat.value
|
||||
if (c != null) {
|
||||
ChatWallpaperEditorModal(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
ChatTTLOption(chatItemTTL, setChatItemTTL, deletingItems)
|
||||
SectionTextFooter(stringResource(MR.strings.chat_ttl_options_footer))
|
||||
}
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = true)
|
||||
|
||||
if (!groupInfo.nextConnectPrepared) {
|
||||
SectionView(title = String.format(generalGetString(MR.strings.group_info_section_title_num_members), activeSortedMembers.count() + 1)) {
|
||||
if (groupInfo.canAddMembers) {
|
||||
val onAddMembersClick = if (chat.chatInfo.incognito) ::cantInviteIncognitoAlert else addMembers
|
||||
val tint = if (chat.chatInfo.incognito) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
|
||||
val addMembersTitleId = when (groupInfo.businessChat?.chatType) {
|
||||
BusinessChatType.Customer -> MR.strings.button_add_team_members
|
||||
BusinessChatType.Business -> MR.strings.button_add_friends
|
||||
null -> MR.strings.button_add_members
|
||||
}
|
||||
AddMembersButton(addMembersTitleId, tint, onAddMembersClick)
|
||||
}
|
||||
if (activeSortedMembers.size > 8) {
|
||||
SectionItemView(padding = PaddingValues(start = 14.dp, end = DEFAULT_PADDING_HALF)) {
|
||||
MemberListSearchRowView(searchText)
|
||||
}
|
||||
}
|
||||
SectionItemView(minHeight = 54.dp, padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
|
||||
MemberRow(groupInfo.membership, user = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!groupInfo.nextConnectPrepared) {
|
||||
items(filteredMembers.value, key = { it.groupMemberId }) { member ->
|
||||
Divider()
|
||||
val showMenu = remember { mutableStateOf(false) }
|
||||
val canBeSelected = groupInfo.membership.memberRole >= member.memberRole && member.memberRole < GroupMemberRole.Moderator
|
||||
SectionItemViewLongClickable(
|
||||
click = {
|
||||
if (selectedItems.value != null) {
|
||||
if (canBeSelected) {
|
||||
toggleItemSelection(member.groupMemberId, selectedItems)
|
||||
}
|
||||
Row(
|
||||
Modifier
|
||||
.widthIn(max = if (groupInfo.canAddMembers) 480.dp else 320.dp)
|
||||
.padding(horizontal = DEFAULT_PADDING),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (groupInfo.canAddMembers) {
|
||||
SearchButton(modifier = Modifier.weight(1f), chat, groupInfo, close, onSearchClicked)
|
||||
AddGroupMembersButton(modifier = Modifier.weight(1f), chat, groupInfo)
|
||||
MuteButton(modifier = Modifier.weight(1f), chat, groupInfo)
|
||||
SettingsButton(modifier = Modifier.weight(1f), groupInfo, openSettings)
|
||||
} else {
|
||||
showMemberInfo(member)
|
||||
}
|
||||
},
|
||||
longClick = { showMenu.value = true },
|
||||
minHeight = 54.dp,
|
||||
padding = PaddingValues(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
Box(contentAlignment = Alignment.CenterStart) {
|
||||
androidx.compose.animation.AnimatedVisibility(selectedItems.value != null, enter = fadeIn(), exit = fadeOut()) {
|
||||
SelectedListItem(Modifier.alpha(if (canBeSelected) 1f else 0f).padding(start = 2.dp), member.groupMemberId, selectedItems)
|
||||
}
|
||||
val selectionOffset by animateDpAsState(if (selectedItems.value != null) 20.dp + 22.dp * fontSizeMultiplier else 0.dp)
|
||||
DropDownMenuForMember(chat.remoteHostId, member, groupInfo, selectedItems, showMenu)
|
||||
Box(Modifier.padding(start = selectionOffset)) {
|
||||
MemberRow(member)
|
||||
SearchButton(modifier = Modifier.weight(1f), chat, groupInfo, close, onSearchClicked)
|
||||
MuteButton(modifier = Modifier.weight(1f), chat, groupInfo)
|
||||
SettingsButton(modifier = Modifier.weight(1f), groupInfo, openSettings)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
if (!groupInfo.nextConnectPrepared) {
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
}
|
||||
SectionView {
|
||||
ClearChatButton(clearChat)
|
||||
if (groupInfo.canDelete) {
|
||||
val titleId = if (groupInfo.businessChat == null) MR.strings.button_delete_group else MR.strings.button_delete_chat
|
||||
DeleteGroupButton(titleId, deleteGroup)
|
||||
}
|
||||
if (groupInfo.membership.memberCurrentOrPending) {
|
||||
val titleId = if (groupInfo.businessChat == null) MR.strings.button_leave_group else MR.strings.button_leave_chat
|
||||
LeaveGroupButton(titleId, leaveGroup)
|
||||
}
|
||||
|
||||
SectionSpacer()
|
||||
}
|
||||
|
||||
if (developerTools) {
|
||||
SectionDividerSpaced()
|
||||
SectionView(title = stringResource(MR.strings.section_title_for_console)) {
|
||||
InfoRow(stringResource(MR.strings.info_row_local_name), groupInfo.localDisplayName)
|
||||
InfoRow(stringResource(MR.strings.info_row_database_id), groupInfo.apiId.toString())
|
||||
GroupChatInfoTabs(
|
||||
groupInfo = groupInfo,
|
||||
activeSortedMembers = activeSortedMembers,
|
||||
filteredMembers = filteredMembers,
|
||||
searchText = searchText,
|
||||
selectedItems = selectedItems,
|
||||
scrollToItemId = scrollToItemId,
|
||||
addMembers = addMembers,
|
||||
showMemberInfo = showMemberInfo,
|
||||
selectedTab = selectedTab,
|
||||
filteredChatItems = filteredChatItems,
|
||||
chat = chat,
|
||||
groupLink = groupLink,
|
||||
manageGroupLink = manageGroupLink,
|
||||
openMemberSupport = openMemberSupport
|
||||
)
|
||||
|
||||
item {
|
||||
if (developerTools) {
|
||||
SectionDividerSpaced()
|
||||
SectionView(title = stringResource(MR.strings.section_title_for_console)) {
|
||||
InfoRow(stringResource(MR.strings.info_row_local_name), groupInfo.localDisplayName)
|
||||
InfoRow(stringResource(MR.strings.info_row_database_id), groupInfo.apiId.toString())
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
if (!oneHandUI.value) {
|
||||
NavigationBarBackground(oneHandUI.value, oneHandUI.value)
|
||||
}
|
||||
@@ -779,7 +712,7 @@ private fun GroupChatInfoHeader(cInfo: ChatInfo, groupInfo: GroupInfo) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MemberSupportButton(chat: Chat, onClick: () -> Unit) {
|
||||
fun MemberSupportButton(chat: Chat, onClick: () -> Unit) {
|
||||
SettingsActionItemWithContent(
|
||||
painterResource(if (chat.supportUnreadCount > 0) MR.images.ic_flag_filled else MR.images.ic_flag),
|
||||
stringResource(MR.strings.member_support),
|
||||
@@ -796,7 +729,7 @@ private fun MemberSupportButton(chat: Chat, onClick: () -> Unit) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GroupPreferencesButton(titleId: StringResource, onClick: () -> Unit) {
|
||||
fun GroupPreferencesButton(titleId: StringResource, onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_toggle_on),
|
||||
stringResource(titleId),
|
||||
@@ -805,7 +738,7 @@ private fun GroupPreferencesButton(titleId: StringResource, onClick: () -> Unit)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GroupReportsButton(chat: Chat, onClick: () -> Unit) {
|
||||
fun GroupReportsButton(chat: Chat, onClick: () -> Unit) {
|
||||
SettingsActionItemWithContent(
|
||||
painterResource(if (chat.chatStats.reportsCount > 0) MR.images.ic_flag_filled else MR.images.ic_flag),
|
||||
stringResource(MR.strings.group_reports_member_reports),
|
||||
@@ -822,7 +755,7 @@ private fun GroupReportsButton(chat: Chat, onClick: () -> Unit) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SendReceiptsOption(currentUser: User, state: State<SendReceipts>, onSelected: (SendReceipts) -> Unit) {
|
||||
fun SendReceiptsOption(currentUser: User, state: State<SendReceipts>, onSelected: (SendReceipts) -> Unit) {
|
||||
val values = remember {
|
||||
mutableListOf(SendReceipts.Yes, SendReceipts.No, SendReceipts.UserDefault(currentUser.sendRcptsSmallGroups)).map { it to it.text }
|
||||
}
|
||||
@@ -853,7 +786,7 @@ fun SendReceiptsOptionDisabled() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddMembersButton(titleId: StringResource, tint: Color = MaterialTheme.colors.primary, onClick: () -> Unit) {
|
||||
fun AddMembersButton(titleId: StringResource, tint: Color = MaterialTheme.colors.primary, onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_add),
|
||||
stringResource(titleId),
|
||||
@@ -945,7 +878,7 @@ fun MemberVerifiedShield() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DropDownMenuForMember(rhId: Long?, member: GroupMember, groupInfo: GroupInfo, selectedItems: MutableState<Set<Long>?>, showMenu: MutableState<Boolean>) {
|
||||
fun DropDownMenuForMember(rhId: Long?, member: GroupMember, groupInfo: GroupInfo, selectedItems: MutableState<Set<Long>?>, showMenu: MutableState<Boolean>) {
|
||||
if (groupInfo.membership.memberRole >= GroupMemberRole.Moderator) {
|
||||
val canBlockForAll = member.canBlockForAll(groupInfo)
|
||||
val canRemove = member.canBeRemoved(groupInfo)
|
||||
@@ -994,7 +927,7 @@ private fun DropDownMenuForMember(rhId: Long?, member: GroupMember, groupInfo: G
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GroupLinkButton(onClick: () -> Unit) {
|
||||
fun GroupLinkButton(onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_link),
|
||||
stringResource(MR.strings.group_link),
|
||||
@@ -1004,7 +937,7 @@ private fun GroupLinkButton(onClick: () -> Unit) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreateGroupLinkButton(onClick: () -> Unit) {
|
||||
fun CreateGroupLinkButton(onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_add_link),
|
||||
stringResource(MR.strings.create_group_link),
|
||||
@@ -1024,7 +957,7 @@ fun EditGroupProfileButton(onClick: () -> Unit) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddOrEditWelcomeMessage(welcomeMessage: String?, onClick: () -> Unit) {
|
||||
fun AddOrEditWelcomeMessage(welcomeMessage: String?, onClick: () -> Unit) {
|
||||
val text = if (welcomeMessage == null) {
|
||||
stringResource(MR.strings.button_add_welcome_message)
|
||||
} else {
|
||||
@@ -1039,7 +972,7 @@ private fun AddOrEditWelcomeMessage(welcomeMessage: String?, onClick: () -> Unit
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LeaveGroupButton(titleId: StringResource, onClick: () -> Unit) {
|
||||
fun LeaveGroupButton(titleId: StringResource, onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_logout),
|
||||
stringResource(titleId),
|
||||
@@ -1050,7 +983,7 @@ private fun LeaveGroupButton(titleId: StringResource, onClick: () -> Unit) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DeleteGroupButton(titleId: StringResource, onClick: () -> Unit) {
|
||||
fun DeleteGroupButton(titleId: StringResource, onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_delete),
|
||||
stringResource(titleId),
|
||||
@@ -1060,6 +993,7 @@ private fun DeleteGroupButton(titleId: StringResource, onClick: () -> Unit) {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun MemberListSearchRowView(
|
||||
searchText: MutableState<TextFieldValue> = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) }
|
||||
@@ -1155,7 +1089,9 @@ fun PreviewGroupChatInfoLayout() {
|
||||
leaveGroup = {},
|
||||
manageGroupLink = {},
|
||||
onSearchClicked = {},
|
||||
deletingItems = remember { mutableStateOf(true) }
|
||||
deletingItems = remember { mutableStateOf(true) },
|
||||
openSettings = { },
|
||||
close = { }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
package chat.simplex.common.views.chat.group
|
||||
|
||||
import SectionBottomSpacer
|
||||
import SectionDividerSpaced
|
||||
import SectionTextFooter
|
||||
import SectionView
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
import chat.simplex.common.views.chatlist.updateChatSettings
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItem
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun GroupSettingsView(
|
||||
m: ChatModel,
|
||||
rhId: Long?,
|
||||
chatId: String,
|
||||
close: () -> Unit
|
||||
) {
|
||||
val chat = remember { derivedStateOf { m.getChat(chatId) } }
|
||||
val c = chat.value
|
||||
if (c == null || c.chatInfo !is ChatInfo.Group) return
|
||||
|
||||
val groupInfo = c.chatInfo.groupInfo
|
||||
val currentUser = m.currentUser.value ?: return
|
||||
|
||||
val sendReceipts = remember { mutableStateOf(SendReceipts.fromBool(groupInfo.chatSettings.sendRcpts, currentUser.sendRcptsSmallGroups)) }
|
||||
val chatItemTTL = rememberSaveable(groupInfo.id) { mutableStateOf(if (groupInfo.chatItemTTL != null) ChatItemTTL.fromSeconds(groupInfo.chatItemTTL) else null) }
|
||||
val deletingItems = rememberSaveable(groupInfo.id) { mutableStateOf(false) }
|
||||
|
||||
fun setSendReceipts(sendRcpts: SendReceipts) {
|
||||
val chatSettings = (c.chatInfo.chatSettings ?: ChatSettings.defaults).copy(sendRcpts = sendRcpts.bool)
|
||||
updateChatSettings(c.remoteHostId, c.chatInfo, chatSettings, m)
|
||||
sendReceipts.value = sendRcpts
|
||||
}
|
||||
|
||||
fun setChatItemTTL(ttl: ChatItemTTL?) {
|
||||
if (ttl == chatItemTTL.value) return
|
||||
val previousChatTTL = chatItemTTL.value
|
||||
chatItemTTL.value = ttl
|
||||
setChatTTLAlert(m.chatsContext, c.remoteHostId, c.chatInfo, chatItemTTL, previousChatTTL, deletingItems)
|
||||
}
|
||||
|
||||
ModalView(close = close) {
|
||||
GroupSettingsLayout(
|
||||
chat = c,
|
||||
groupInfo = groupInfo,
|
||||
currentUser = currentUser,
|
||||
sendReceipts = sendReceipts,
|
||||
setSendReceipts = ::setSendReceipts,
|
||||
chatItemTTL = chatItemTTL,
|
||||
setChatItemTTL = ::setChatItemTTL,
|
||||
deletingItems = deletingItems,
|
||||
editGroupProfile = {
|
||||
ModalManager.end.showCustomModal { closeModal ->
|
||||
GroupProfileView(rhId, groupInfo, m, closeModal)
|
||||
}
|
||||
},
|
||||
addOrEditWelcomeMessage = {
|
||||
ModalManager.end.showCustomModal { closeModal ->
|
||||
GroupWelcomeView(m, rhId, groupInfo, closeModal)
|
||||
}
|
||||
},
|
||||
openPreferences = {
|
||||
ModalManager.end.showCustomModal { closeModal ->
|
||||
GroupPreferencesView(m, rhId, chatId, closeModal)
|
||||
}
|
||||
},
|
||||
clearChat = { clearChatDialog(c, close) },
|
||||
deleteGroup = { deleteGroupDialog(c, groupInfo, m, close) },
|
||||
leaveGroup = { leaveGroupDialog(rhId, groupInfo, m, close) },
|
||||
close = close
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GroupSettingsLayout(
|
||||
chat: Chat,
|
||||
groupInfo: GroupInfo,
|
||||
currentUser: User,
|
||||
sendReceipts: MutableState<SendReceipts>,
|
||||
setSendReceipts: (SendReceipts) -> Unit,
|
||||
chatItemTTL: MutableState<ChatItemTTL?>,
|
||||
setChatItemTTL: (ChatItemTTL?) -> Unit,
|
||||
deletingItems: MutableState<Boolean>,
|
||||
editGroupProfile: () -> Unit,
|
||||
addOrEditWelcomeMessage: () -> Unit,
|
||||
openPreferences: () -> Unit,
|
||||
clearChat: () -> Unit,
|
||||
deleteGroup: () -> Unit,
|
||||
leaveGroup: () -> Unit,
|
||||
close: () -> Unit
|
||||
) {
|
||||
ColumnWithScrollBar {
|
||||
AppBarTitle(stringResource(MR.strings.icon_descr_settings))
|
||||
|
||||
SectionView {
|
||||
if (groupInfo.isOwner && groupInfo.businessChat?.chatType == null) {
|
||||
EditGroupProfileButton(editGroupProfile)
|
||||
}
|
||||
if (groupInfo.groupProfile.description != null || (groupInfo.isOwner && groupInfo.businessChat?.chatType == null)) {
|
||||
AddOrEditWelcomeMessage(groupInfo.groupProfile.description, addOrEditWelcomeMessage)
|
||||
}
|
||||
val prefsTitleId = if (groupInfo.businessChat == null) MR.strings.group_preferences else MR.strings.chat_preferences
|
||||
GroupPreferencesButton(prefsTitleId, openPreferences)
|
||||
}
|
||||
val footerId = if (groupInfo.businessChat == null) MR.strings.only_group_owners_can_change_prefs else MR.strings.only_chat_owners_can_change_prefs
|
||||
SectionTextFooter(stringResource(footerId))
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
|
||||
SectionView {
|
||||
val activeSortedMembers = remember { chatModel.groupMembers }.value
|
||||
.filter { it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved }
|
||||
|
||||
if (activeSortedMembers.filter { it.memberCurrent }.size <= SMALL_GROUPS_RCPS_MEM_LIMIT) {
|
||||
SendReceiptsOption(currentUser, sendReceipts, setSendReceipts)
|
||||
} else {
|
||||
SendReceiptsOptionDisabled()
|
||||
}
|
||||
WallpaperButton {
|
||||
ModalManager.end.showModal {
|
||||
val chatState = remember { derivedStateOf { chatModel.chats.value.firstOrNull { it.id == chat.id } } }
|
||||
val currentChat = chatState.value
|
||||
if (currentChat != null) {
|
||||
ChatWallpaperEditorModal(currentChat)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
|
||||
SectionView {
|
||||
ChatTTLOption(chatItemTTL, setChatItemTTL, deletingItems)
|
||||
SectionTextFooter(stringResource(MR.strings.chat_ttl_options_footer))
|
||||
}
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
|
||||
SectionView {
|
||||
ClearChatButton(clearChat)
|
||||
if (groupInfo.canDelete) {
|
||||
val titleId = if (groupInfo.businessChat == null) MR.strings.button_delete_group else MR.strings.button_delete_chat
|
||||
DeleteGroupButton(titleId, deleteGroup)
|
||||
}
|
||||
if (groupInfo.membership.memberCurrentOrPending) {
|
||||
val titleId = if (groupInfo.businessChat == null) MR.strings.button_leave_group else MR.strings.button_leave_chat
|
||||
LeaveGroupButton(titleId, leaveGroup)
|
||||
}
|
||||
}
|
||||
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WallpaperButton(onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_image),
|
||||
stringResource(MR.strings.settings_section_title_chat_theme),
|
||||
click = onClick
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ClearChatButton(onClick: () -> Unit) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_settings_backup_restore),
|
||||
stringResource(MR.strings.clear_chat_button),
|
||||
click = onClick,
|
||||
textColor = WarningOrange,
|
||||
iconColor = WarningOrange,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1789,6 +1789,12 @@
|
||||
<string name="button_add_team_members">Add team members</string>
|
||||
<string name="button_add_friends">Add friends</string>
|
||||
<string name="group_info_section_title_num_members">%1$s MEMBERS</string>
|
||||
<string name="group_info_tab_members">MEMBERS</string>
|
||||
<string name="group_info_tab_images">IMAGES</string>
|
||||
<string name="group_info_tab_videos">VIDEOS</string>
|
||||
<string name="group_info_tab_links">LINKS</string>
|
||||
<string name="group_info_tab_files">FILES</string>
|
||||
<string name="group_info_tab_voices">VOICES</string>
|
||||
<string name="group_info_member_you">you: %1$s</string>
|
||||
<string name="button_delete_group">Delete group</string>
|
||||
<string name="button_delete_chat">Delete chat</string>
|
||||
|
||||
Reference in New Issue
Block a user