diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/AddForumDialog.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/AddForumDialog.kt new file mode 100644 index 0000000000000000000000000000000000000000..26511da0f91cd29d2cd0fde54df6fb1dcec73ec3 --- /dev/null +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/AddForumDialog.kt @@ -0,0 +1,165 @@ +/* + * 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.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Scaffold +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.material.rememberScaffoldState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Alignment.Companion.CenterEnd +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.WindowPosition +import androidx.compose.ui.window.rememberDialogState +import org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH +import org.briarproject.briar.desktop.utils.AccessibilityUtils.description +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n +import org.briarproject.briar.desktop.utils.PreviewUtils +import java.awt.Dimension + +fun main() = PreviewUtils.preview { + val visible = mutableStateOf(true) + AddForumDialog(visible, {}, { visible.value = false }) +} + +@Composable +fun AddForumDialog( + visible: State<Boolean>, + onCreate: (String) -> Unit, + onCancelButtonClicked: () -> Unit, +) { + Dialog( + title = i18n("forum.add.title"), + onCloseRequest = onCancelButtonClicked, + state = rememberDialogState( + position = WindowPosition(Alignment.Center), + ), + visible = visible.value, + ) { + window.minimumSize = Dimension(360, 180) + val scaffoldState = rememberScaffoldState() + val name = rememberSaveable { mutableStateOf("") } + Surface { + Scaffold( + modifier = Modifier + .padding(horizontal = 24.dp) + .padding(top = 24.dp, bottom = 12.dp), + scaffoldState = scaffoldState, + topBar = { + Box(Modifier.fillMaxWidth()) { + Text( + text = i18n("forum.add.title"), + style = MaterialTheme.typography.h6, + modifier = Modifier.padding(bottom = 12.dp) + ) + } + }, + content = { + AddForumContent(name, onCreate) + }, + bottomBar = { + OkCancelBottomBar( + okButtonLabel = i18n("forum.add.button"), + okButtonEnabled = isValidForumName(name.value), + onOkButtonClicked = { + onCreate(name.value) + name.value = "" + }, + onCancelButtonClicked = onCancelButtonClicked, + ) + }, + ) + } + } +} + +private fun isValidForumName(name: String): Boolean { + return name.isNotBlank() && name.length <= MAX_FORUM_NAME_LENGTH +} + +@Composable +fun AddForumContent(name: MutableState<String>, onCreate: (String) -> Unit) { + val focusRequester = remember { FocusRequester() } + OutlinedTextField( + value = name.value, + onValueChange = { if (it.length <= MAX_FORUM_NAME_LENGTH) name.value = it }, + label = { Text(i18n("forum.add.hint")) }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + singleLine = true, + onEnter = { + onCreate(name.value) + name.value = "" + }, + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester) + .description(i18n("forum.add.hint")), + ) + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } +} + +@Composable +fun OkCancelBottomBar( + okButtonLabel: String, + okButtonEnabled: Boolean = true, + onOkButtonClicked: () -> Unit, + onCancelButtonClicked: () -> Unit, +) { + Box(Modifier.fillMaxWidth()) { + Row(Modifier.align(CenterEnd)) { + TextButton( + onClick = onCancelButtonClicked, + colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colors.error) + ) { + Text(i18n("cancel")) + } + Button( + onClick = onOkButtonClicked, + enabled = okButtonEnabled, + modifier = Modifier.padding(start = 8.dp) + ) { + Text(okButtonLabel) + } + } + } +} 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 2bb44a4f1b975aeb63abc50f965c1501585a1298..b65d5ae6ddfba25475e4fcb9ae7d69e8d413af6e 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 @@ -25,6 +25,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AddComment import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import org.briarproject.briar.desktop.conversation.Explainer @@ -38,8 +40,18 @@ import org.briarproject.briar.desktop.viewmodel.viewModel fun ForumsScreen( viewModel: ForumsViewModel = viewModel(), ) { + val addDialogVisible = remember { mutableStateOf(false) } + AddForumDialog( + visible = addDialogVisible, + onCreate = { name -> + viewModel.createForum(name) + addDialogVisible.value = false + }, + onCancelButtonClicked = { addDialogVisible.value = false } + ) + if (viewModel.groupList.value.isEmpty()) { - NoForumsYet {} + NoForumsYet { addDialogVisible.value = true } } else { Row(modifier = Modifier.fillMaxWidth()) { ForumsList( @@ -48,7 +60,7 @@ fun ForumsScreen( filterBy = viewModel.filterBy, onFilterSet = viewModel::setFilterBy, onGroupIdSelected = viewModel::selectGroup, - onAddButtonClicked = {}, + onAddButtonClicked = { addDialogVisible.value = true }, ) VerticalDivider() Column(modifier = Modifier.weight(1f).fillMaxHeight()) { 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 b92f33acdbf46732c515a1a01e6af62cacd17b8b..245b4481ac81f2c55e1e9c9cb4fd198dcc37a625 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 @@ -27,6 +27,7 @@ import org.briarproject.bramble.api.event.Event 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.briar.api.forum.ForumManager import org.briarproject.briar.desktop.threading.BriarExecutors import org.briarproject.briar.desktop.utils.clearAndAddAll @@ -64,7 +65,13 @@ constructor( } override fun eventOccurred(e: Event) { - // TODO + if (e is GroupAddedEvent) { + if (e.group.clientId == ForumManager.CLIENT_ID) loadGroups() + } + } + + fun createForum(name: String) { + forumManager.addForum(name) } private fun loadGroups() { 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 67cc50172827d0d336bb5bf966dcd44ae10898fe..b78786075c7abced04706fbc55517461be46750e 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 @@ -107,13 +107,19 @@ fun GroupsCard( modifier = Modifier.fillMaxWidth() ) { Text( - text = i18nP("group.card.posts", item.msgCount), - style = MaterialTheme.typography.caption - ) - Text( - text = getFormattedTimestamp(item.timestamp), + text = if (item.msgCount > 0) { + i18nP("group.card.posts", item.msgCount) + } else { + i18nP("group.card.no_posts", item.msgCount) + }, style = MaterialTheme.typography.caption ) + if (item.msgCount > 0) { + Text( + text = getFormattedTimestamp(item.timestamp), + style = MaterialTheme.typography.caption + ) + } } } } diff --git a/briar-desktop/src/main/resources/strings/BriarDesktop.properties b/briar-desktop/src/main/resources/strings/BriarDesktop.properties index 1ffc70901b0f2defbc2419319acb3b2c744b2290..eb07b780637d95ada98ffe8aa121dfffdabb893e 100644 --- a/briar-desktop/src/main/resources/strings/BriarDesktop.properties +++ b/briar-desktop/src/main/resources/strings/BriarDesktop.properties @@ -106,6 +106,10 @@ forum.search.title=Forums forum.empty_state.text=You don't have any forums yet. Tap the + icon to add a forum: forum.none_selected.title=No forum selected 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 +group.card.no_posts=No posts group.card.posts={0, plural, one {{0} post} other {{0} posts}} # Private Groups