diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsList.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsList.kt index 437b5bdc06c3883e69aea292099aebe2ec3a558d..e90dd40aea326ce21b11bf710d866a714c7a167c 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsList.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsList.kt @@ -54,7 +54,7 @@ fun ForumsList( isSelected: (GroupId) -> Boolean, filterBy: State<String>, onFilterSet: (String) -> Unit, - onGroupIdSelected: (GroupId) -> Unit, + onGroupItemSelected: (GroupItem) -> Unit, onAddButtonClicked: () -> Unit, ) { val scrollState = rememberLazyListState() @@ -82,7 +82,7 @@ fun ForumsList( items(list.value) { item -> GroupsCard( item = item, - onGroupIdSelected = onGroupIdSelected, + onGroupItemSelected = onGroupItemSelected, selected = isSelected(item.id) ) HorizontalDivider() diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsScreen.kt index b65d5ae6ddfba25475e4fcb9ae7d69e8d413af6e..1b4cca977de2b246e187208e271bec30116d4ec7 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsScreen.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsScreen.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import org.briarproject.briar.desktop.conversation.Explainer import org.briarproject.briar.desktop.ui.ColoredIconButton -import org.briarproject.briar.desktop.ui.UiPlaceholder import org.briarproject.briar.desktop.ui.VerticalDivider import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.viewmodel.viewModel @@ -59,16 +58,16 @@ fun ForumsScreen( isSelected = viewModel::isSelected, filterBy = viewModel.filterBy, onFilterSet = viewModel::setFilterBy, - onGroupIdSelected = viewModel::selectGroup, + onGroupItemSelected = viewModel::selectGroup, onAddButtonClicked = { addDialogVisible.value = true }, ) VerticalDivider() Column(modifier = Modifier.weight(1f).fillMaxHeight()) { - val id = viewModel.selectedGroupId.value - if (id == null) { + val item = viewModel.selectedGroupItem.value + if (item == null) { NoForumSelected() } else { - UiPlaceholder() + GroupConversationScreen(item) } } } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsViewModel.kt index 245b4481ac81f2c55e1e9c9cb4fd198dcc37a625..3b8b043b71e0ddcd8b42ebd4e2a5aca9a64aa2fa 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsViewModel.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsViewModel.kt @@ -28,6 +28,7 @@ import org.briarproject.bramble.api.event.EventBus import org.briarproject.bramble.api.lifecycle.LifecycleManager import org.briarproject.bramble.api.sync.GroupId import org.briarproject.bramble.api.sync.event.GroupAddedEvent +import org.briarproject.bramble.api.sync.event.GroupRemovedEvent import org.briarproject.briar.api.forum.ForumManager import org.briarproject.briar.desktop.threading.BriarExecutors import org.briarproject.briar.desktop.utils.clearAndAddAll @@ -53,8 +54,8 @@ constructor( }.sortedByDescending { it.timestamp } } - private val _selectedGroupId = mutableStateOf<GroupId?>(null) - val selectedGroupId: State<GroupId?> = _selectedGroupId + private val _selectedGroupItem = mutableStateOf<GroupItem?>(null) + val selectedGroupItem: State<GroupItem?> = _selectedGroupItem private val _filterBy = mutableStateOf("") val filterBy = _filterBy.asState() @@ -67,6 +68,11 @@ constructor( override fun eventOccurred(e: Event) { if (e is GroupAddedEvent) { if (e.group.clientId == ForumManager.CLIENT_ID) loadGroups() + } else if (e is GroupRemovedEvent) { + if (e.group.clientId == ForumManager.CLIENT_ID) { + loadGroups() + if (selectedGroupItem.value?.id == e.group.id) _selectedGroupItem.value = null + } } } @@ -88,13 +94,17 @@ constructor( } } - fun selectGroup(privateGroupId: GroupId) { - _selectedGroupId.value = privateGroupId + fun selectGroup(groupItem: GroupItem) { + _selectedGroupItem.value = groupItem } - fun isSelected(privateGroupId: GroupId) = _selectedGroupId.value == privateGroupId + fun isSelected(groupId: GroupId) = _selectedGroupItem.value?.id == groupId fun setFilterBy(filter: String) { _filterBy.value = filter } + + fun deleteGroup(groupItem: GroupItem) { + forumManager.removeForum((groupItem as ForumsItem).forum) + } } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/GroupCircle.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/GroupCircle.kt index 18b424084384c14aef7db3dcc8e593b0360cab62..76874e0a7b603285e487dd3682bb1b30f3f2e1ea 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/GroupCircle.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/GroupCircle.kt @@ -41,7 +41,7 @@ import org.briarproject.briar.desktop.theme.outline import org.briarproject.briar.desktop.ui.NumberBadge @Composable -fun GroupCircle(item: GroupItem, modifier: Modifier = Modifier) { +fun GroupCircle(item: GroupItem, showMessageCount: Boolean = true, modifier: Modifier = Modifier) { Box( modifier = modifier ) { @@ -66,7 +66,7 @@ fun GroupCircle(item: GroupItem, modifier: Modifier = Modifier) { ) ) } - NumberBadge( + if (showMessageCount) NumberBadge( num = item.unread, modifier = Modifier.align(TopEnd).offset(8.dp, (-6).dp) ) diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/GroupConversationScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/GroupConversationScreen.kt new file mode 100644 index 0000000000000000000000000000000000000000..1be3918079a1548a2f1e8d03329a27ed60d0f759 --- /dev/null +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/GroupConversationScreen.kt @@ -0,0 +1,181 @@ +/* + * Briar Desktop + * Copyright (C) 2021-2022 The Briar Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package org.briarproject.briar.desktop.forums + +import androidx.compose.foundation.layout.Arrangement.SpaceBetween +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.IntrinsicSize.Max +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +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.material.AlertDialog +import androidx.compose.material.ButtonType.DESTRUCTIVE +import androidx.compose.material.ButtonType.NEUTRAL +import androidx.compose.material.DialogButton +import androidx.compose.material.DropdownMenu +import androidx.compose.material.DropdownMenuItem +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment.Companion.BottomCenter +import androidx.compose.ui.Alignment.Companion.Center +import androidx.compose.ui.Alignment.Companion.CenterVertically +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow.Companion.Ellipsis +import androidx.compose.ui.unit.dp +import org.briarproject.briar.desktop.contact.ContactDropDown.State.CLOSED +import org.briarproject.briar.desktop.contact.ContactDropDown.State.MAIN +import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE +import org.briarproject.briar.desktop.ui.HorizontalDivider +import org.briarproject.briar.desktop.ui.UiPlaceholder +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n +import org.briarproject.briar.desktop.viewmodel.viewModel + +@Composable +fun GroupConversationScreen( + groupItem: GroupItem, + viewModel: ForumsViewModel = viewModel(), +) { + Scaffold( + topBar = { + GroupConversationHeader(groupItem) { viewModel.deleteGroup(groupItem) } + }, + content = { padding -> + // Loader() + UiPlaceholder() + }, + ) +} + +@Composable +private fun GroupConversationHeader( + groupItem: GroupItem, + onGroupDelete: () -> Unit, +) { + val deleteGroupDialogState = remember { mutableStateOf(false) } + val menuState = remember { mutableStateOf(CLOSED) } + val close = { menuState.value = CLOSED } + Box(modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp)) { + Row( + horizontalArrangement = SpaceBetween, + modifier = Modifier.fillMaxWidth().align(Center), + ) { + Row( + modifier = Modifier + .fillMaxHeight() + .padding(start = 8.dp) + .weight(1f, fill = false), + ) { + GroupCircle( + item = groupItem, + showMessageCount = false, + modifier = Modifier.align(CenterVertically), + ) + Text( + modifier = Modifier + .align(CenterVertically) + .padding(start = 12.dp) + .weight(1f, fill = false), + text = groupItem.name, + maxLines = 2, + overflow = Ellipsis, + style = MaterialTheme.typography.h2, + ) + } + IconButton( + icon = Icons.Filled.MoreVert, + contentDescription = i18n("access.menu"), + onClick = { menuState.value = MAIN }, + modifier = Modifier.align(CenterVertically).padding(end = 16.dp), + ) { + DropdownMenu( + expanded = menuState.value == MAIN, + onDismissRequest = close, + ) { + DropdownMenuItem( + onClick = { + close() + deleteGroupDialogState.value = true + } + ) { + Text( + i18n("forum.leave.title"), + style = MaterialTheme.typography.body2, + ) + } + } + } + } + HorizontalDivider(modifier = Modifier.align(BottomCenter)) + } + if (deleteGroupDialogState.value) { + DeleteForumDialog( + close = { deleteGroupDialogState.value = false }, + onDelete = onGroupDelete, + ) + } +} + +@Composable +@OptIn(ExperimentalMaterialApi::class) +fun DeleteForumDialog( + close: () -> Unit, + onDelete: () -> Unit = {}, +) { + AlertDialog( + onDismissRequest = close, + title = { + Text( + text = i18n("forum.delete.dialog.title"), + modifier = Modifier.width(Max), + style = MaterialTheme.typography.h6, + ) + }, + text = { + Text(i18n("forum.delete.dialog.message")) + }, + dismissButton = { + DialogButton( + onClick = { close() }, + text = i18n("cancel"), + type = NEUTRAL, + ) + }, + confirmButton = { + DialogButton( + onClick = { + close() + onDelete() + }, + text = i18n("forum.delete.dialog.button"), + type = DESTRUCTIVE, + ) + }, + ) +} diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/GroupsCard.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/GroupsCard.kt index b78786075c7abced04706fbc55517461be46750e..c5f1fac5828be4489be6113b7dfcd8a07984c7f1 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/GroupsCard.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/GroupsCard.kt @@ -59,7 +59,7 @@ fun main() = PreviewUtils.preview { override val unread: Int = 23 override val timestamp: Long = System.currentTimeMillis() }, - onGroupIdSelected = {}, + onGroupItemSelected = {}, selected = false, ) } @@ -68,14 +68,14 @@ fun main() = PreviewUtils.preview { @Composable fun GroupsCard( item: GroupItem, - onGroupIdSelected: (GroupId) -> Unit, + onGroupItemSelected: (GroupItem) -> Unit, selected: Boolean, ) { Card( modifier = Modifier .fillMaxWidth() .defaultMinSize(minHeight = HEADER_SIZE) - .clickable(onClick = { onGroupIdSelected(item.id) }) + .clickable(onClick = { onGroupItemSelected(item) }) .semantics { contentDescription = if (selected) InternationalizationUtils.i18n("access.list.selected.yes") diff --git a/briar-desktop/src/main/resources/strings/BriarDesktop.properties b/briar-desktop/src/main/resources/strings/BriarDesktop.properties index eb07b780637d95ada98ffe8aa121dfffdabb893e..78c9583eb9c43c53c2864f81fd5d2715ffc87f67 100644 --- a/briar-desktop/src/main/resources/strings/BriarDesktop.properties +++ b/briar-desktop/src/main/resources/strings/BriarDesktop.properties @@ -58,6 +58,7 @@ access.settings.currently_disabled=Currently disabled access.settings.click_to_toggle_notifications=Click to toggle notifications access.return_to_previous_screen=Return to previous screen +access.menu=Show menu access.forums.add=Add forum # Contacts @@ -109,6 +110,10 @@ forum.none_selected.hint=Select a forum to start chatting forum.add.title=Create Forum forum.add.hint=Choose a name for your forum forum.add.button=Create forum +forum.leave.title=Leave Forum +forum.delete.dialog.title=Confirm Leaving Forum +forum.delete.dialog.message=Are you sure that you want to leave this forum?\n\nAny contacts you\'ve shared this forum with might stop receiving updates. +forum.delete.dialog.button=Leave group.card.no_posts=No posts group.card.posts={0, plural, one {{0} post} other {{0} posts}}