diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumListViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumListViewModel.kt index 6fbb5d74cea942fb5c591fe3589aadb4246419d1..5efeec62135b6c776247089183b0f3cc15217fdf 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumListViewModel.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumListViewModel.kt @@ -72,7 +72,7 @@ class ForumListViewModel groupCount = forumManager.getGroupCount(txn, id), ) - fun createForum(name: String) = runOnDbThread { + override fun createGroup(name: String) = runOnDbThread { forumManager.addForum(name) } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumScreen.kt index 0305332497297e30fefec9ee3d143b11119f9c74..db4b9ebe2af099d6d9e068eaa8bc59e7366b8144 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumScreen.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumScreen.kt @@ -28,15 +28,5 @@ fun ForumScreen( ) = GroupScreen( strings = ForumStrings, viewModel = viewModel, - addGroupDialog = { visible -> - AddForumDialog( - visible = visible.value, - onCreate = { name -> - viewModel.createForum(name) - visible.value = false - }, - onCancelButtonClicked = { visible.value = false } - ) - }, conversationScreen = { GroupConversationScreen(viewModel.threadViewModel) } ) diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumStrings.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumStrings.kt index ceee63746d7066c186866c2a34601fe7e253c45e..aa8f947f0e7a6473d8672e25b691782316774672 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumStrings.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumStrings.kt @@ -18,17 +18,18 @@ package org.briarproject.briar.desktop.forums +import org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH import org.briarproject.briar.desktop.group.GroupStrings -import org.briarproject.briar.desktop.utils.InternationalizationUtils import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nF import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nP -import org.briarproject.briar.desktop.utils.TimeUtils object ForumStrings : GroupStrings( listTitle = i18n("forum.search.title"), listDescription = i18n("access.forums.list"), - addButtonDescription = i18n("forum.add.title"), + addGroupTitle = i18n("forum.add.title"), + addGroupHint = i18n("forum.add.hint"), + addGroupButton = i18n("forum.add.button"), noGroupsYet = i18n("forum.empty_state.text"), noGroupSelectedTitle = i18n("forum.none_selected.title"), noGroupSelectedText = i18n("forum.none_selected.hint"), @@ -42,4 +43,5 @@ object ForumStrings : GroupStrings( lastMessage = { timestamp -> i18nF("access.forums.last_post_timestamp", timestamp) }, + groupNameMaxLength = MAX_FORUM_NAME_LENGTH, ) 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/group/AddGroupDialog.kt similarity index 83% rename from briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/AddForumDialog.kt rename to briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/AddGroupDialog.kt index bd5e742b1ed8a0c1e42fc29b7569b9c37cbb8296..a33754503b52e1c2942b2978182aeca08657c8c2 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/AddForumDialog.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/AddGroupDialog.kt @@ -1,6 +1,6 @@ /* * Briar Desktop - * Copyright (C) 2021-2022 The Briar Project + * Copyright (C) 2021-2023 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 @@ -16,7 +16,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package org.briarproject.briar.desktop.forums +package org.briarproject.briar.desktop.group import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row @@ -49,7 +49,8 @@ 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.bramble.util.StringUtils.utf8IsTooLong +import org.briarproject.briar.desktop.forums.ForumStrings import org.briarproject.briar.desktop.utils.AccessibilityUtils.description import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.PreviewUtils.preview @@ -57,11 +58,12 @@ import org.briarproject.briar.desktop.utils.UiUtils.DensityDimension fun main() = preview { val visible = mutableStateOf(true) - AddForumDialog(visible.value, {}, { visible.value = false }) + AddGroupDialog(ForumStrings, visible.value, {}, { visible.value = false }) } @Composable -fun AddForumDialog( +fun AddGroupDialog( + strings: GroupStrings, visible: Boolean, onCreate: (String) -> Unit, onCancelButtonClicked: () -> Unit, @@ -69,7 +71,7 @@ fun AddForumDialog( if (!visible) return val density = LocalDensity.current Dialog( - title = i18n("forum.add.title"), + title = strings.addGroupTitle, onCloseRequest = onCancelButtonClicked, state = rememberDialogState( position = WindowPosition(Alignment.Center), @@ -82,7 +84,7 @@ fun AddForumDialog( val name = rememberSaveable { mutableStateOf("") } val onNameChanged = { changedName: String -> // not checking for blank here, so user can still remove all characters - if (changedName.length <= MAX_FORUM_NAME_LENGTH) name.value = changedName + if (!utf8IsTooLong(changedName, strings.groupNameMaxLength)) name.value = changedName } Surface { Scaffold( @@ -93,19 +95,19 @@ fun AddForumDialog( topBar = { Box(Modifier.fillMaxWidth()) { Text( - text = i18n("forum.add.title"), + text = strings.addGroupTitle, style = MaterialTheme.typography.h6, modifier = Modifier.padding(bottom = 12.dp) ) } }, content = { - AddForumContent(name.value, onNameChanged, onCreate) + AddGroupContent(name.value, onNameChanged, strings.addGroupHint, onCreate) }, bottomBar = { OkCancelBottomBar( - okButtonLabel = i18n("forum.add.button"), - okButtonEnabled = isValidForumName(name.value), + okButtonLabel = strings.addGroupButton, + okButtonEnabled = isValidGroupName(name.value, strings.groupNameMaxLength), onOkButtonClicked = { onCreate(name.value) onNameChanged("") @@ -119,17 +121,16 @@ fun AddForumDialog( } } -private fun isValidForumName(name: String): Boolean { - return name.isNotBlank() && name.length <= MAX_FORUM_NAME_LENGTH -} +private fun isValidGroupName(name: String, maxLength: Int) = + name.isNotBlank() && !utf8IsTooLong(name, maxLength) @Composable -fun AddForumContent(name: String, onNameChanged: (String) -> Unit, onCreate: (String) -> Unit) { +fun AddGroupContent(name: String, onNameChanged: (String) -> Unit, description: String, onCreate: (String) -> Unit) { val focusRequester = remember { FocusRequester() } OutlinedTextField( value = name, onValueChange = onNameChanged, - label = { Text(i18n("forum.add.hint")) }, + label = { Text(description) }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), singleLine = true, onEnter = { @@ -139,7 +140,7 @@ fun AddForumContent(name: String, onNameChanged: (String) -> Unit, onCreate: (St modifier = Modifier .fillMaxWidth() .focusRequester(focusRequester) - .description(i18n("forum.add.hint")), + .description(description), ) LaunchedEffect(Unit) { focusRequester.requestFocus() diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupList.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupList.kt index 30ab73ee5a83692acab86095c6da74062c3663d9..bd766693819caae16eb7d6bba671e69d1b530a56 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupList.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupList.kt @@ -120,7 +120,7 @@ fun GroupList( placeholder = strings.listTitle, icon = Icons.Filled.AddComment, searchValue = filterBy, - addButtonDescription = strings.addButtonDescription, + addButtonDescription = strings.addGroupTitle, onValueChange = onFilterSet, onAddButtonClicked = onAddButtonClicked, ) diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupListViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupListViewModel.kt index 225da54ce6044c4a68d8f400e5843f4579a1447a..4c01f085ab222e11040637fae0c2a7a797bdab90 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupListViewModel.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupListViewModel.kt @@ -52,7 +52,7 @@ abstract class GroupListViewModel<T : GroupItem>( protected abstract val clientId: ClientId - protected abstract val _groupList: MutableList<T> //todo: check internal + protected abstract val _groupList: MutableList<T> // todo: check internal val list = derivedStateOf { val filter = _filterBy.value _groupList.filter { item -> @@ -123,6 +123,8 @@ abstract class GroupListViewModel<T : GroupItem>( } } + abstract fun createGroup(name: String) + protected abstract fun addOwnMessage(header: PostHeader) fun selectGroup(groupItem: GroupItem) { diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupScreen.kt index 1eb9101a70a20399a3bb7cfb82b4735c3e2c2fb8..3c1e7bfd6b21c79598848b250e4efc7cbb53cab2 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupScreen.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupScreen.kt @@ -25,9 +25,10 @@ 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.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import org.briarproject.briar.desktop.conversation.Explainer @@ -39,14 +40,21 @@ import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n fun <T : GroupItem> GroupScreen( strings: GroupStrings, viewModel: GroupListViewModel<T>, - addGroupDialog: @Composable (MutableState<Boolean>) -> Unit, conversationScreen: @Composable () -> Unit, ) { - val addDialogVisible = remember { mutableStateOf(false) } - addGroupDialog(addDialogVisible) + var addDialogVisible by remember { mutableStateOf(false) } + AddGroupDialog( + strings = strings, + visible = addDialogVisible, + onCreate = { name -> + viewModel.createGroup(name) + addDialogVisible = false + }, + onCancelButtonClicked = { addDialogVisible = false } + ) if (viewModel.noGroupsYet.value) { - NoGroupsYet(strings) { addDialogVisible.value = true } + NoGroupsYet(strings) { addDialogVisible = true } } else { Row(modifier = Modifier.fillMaxWidth()) { GroupList( @@ -56,7 +64,7 @@ fun <T : GroupItem> GroupScreen( filterBy = viewModel.filterBy.value, onFilterSet = viewModel::setFilterBy, onGroupItemSelected = viewModel::selectGroup, - onAddButtonClicked = { addDialogVisible.value = true }, + onAddButtonClicked = { addDialogVisible = true }, ) VerticalDivider() Column(modifier = Modifier.weight(1f).fillMaxHeight()) { @@ -78,7 +86,7 @@ fun NoGroupsYet(strings: GroupStrings, onAdd: () -> Unit) = Explainer( ColoredIconButton( icon = Icons.Filled.AddComment, iconSize = 20.dp, - contentDescription = strings.addButtonDescription, + contentDescription = strings.addGroupTitle, onClick = onAdd, ) } diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupStrings.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupStrings.kt index 5ae2f103736e28790ab4bf0fa31b4c88ffdae874..505d325ffd4021925f1c7afceda2614f9c8bc116 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupStrings.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/group/GroupStrings.kt @@ -21,11 +21,14 @@ package org.briarproject.briar.desktop.group abstract class GroupStrings( val listTitle: String, val listDescription: String, - val addButtonDescription: String, + val addGroupTitle: String, + val addGroupHint: String, + val addGroupButton: String, val noGroupsYet: String, val noGroupSelectedTitle: String, val noGroupSelectedText: String, val messageCount: (Int) -> String, val unreadCount: (Int) -> String, val lastMessage: (String) -> String, + val groupNameMaxLength: Int, ) diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupListViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupListViewModel.kt index c273f1c594ec05a3238fb8374dba70c0b081402f..c6310296e2a4e54c0069876cb7f6e56a39f4bcbf 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupListViewModel.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupListViewModel.kt @@ -23,11 +23,15 @@ import org.briarproject.bramble.api.db.Transaction import org.briarproject.bramble.api.db.TransactionManager import org.briarproject.bramble.api.event.Event import org.briarproject.bramble.api.event.EventBus +import org.briarproject.bramble.api.identity.IdentityManager +import org.briarproject.bramble.api.identity.LocalAuthor import org.briarproject.bramble.api.lifecycle.LifecycleManager import org.briarproject.bramble.api.sync.ClientId import org.briarproject.bramble.api.sync.GroupId +import org.briarproject.bramble.api.system.Clock import org.briarproject.briar.api.client.PostHeader -import org.briarproject.briar.api.forum.ForumManager +import org.briarproject.briar.api.privategroup.GroupMessageFactory +import org.briarproject.briar.api.privategroup.PrivateGroupFactory import org.briarproject.briar.api.privategroup.PrivateGroupManager import org.briarproject.briar.api.privategroup.event.GroupMessageAddedEvent import org.briarproject.briar.desktop.forums.ThreadedConversationViewModel @@ -39,7 +43,11 @@ import javax.inject.Inject class PrivateGroupListViewModel @Inject constructor( + private val clock: Clock, + private val identityManager: IdentityManager, private val privateGroupManager: PrivateGroupManager, + private val privateGroupFactory: PrivateGroupFactory, + private val privateGroupMessageFactory: GroupMessageFactory, threadViewModel: ThreadedConversationViewModel, // todo: subclass briarExecutors: BriarExecutors, lifecycleManager: LifecycleManager, @@ -47,7 +55,7 @@ class PrivateGroupListViewModel eventBus: EventBus, ) : GroupListViewModel<PrivateGroupItem>(threadViewModel, briarExecutors, lifecycleManager, db, eventBus) { - override val clientId: ClientId = ForumManager.CLIENT_ID + override val clientId: ClientId = PrivateGroupManager.CLIENT_ID override val _groupList = mutableStateListOf<PrivateGroupItem>() @@ -70,9 +78,14 @@ class PrivateGroupListViewModel groupCount = privateGroupManager.getGroupCount(txn, id), ) - fun createPrivateGroup(name: String) = runOnDbThread { - TODO() - //privateGroupManager.addForum(name) + override fun createGroup(name: String) = runOnDbThread { + val author: LocalAuthor = identityManager.localAuthor + // in Android, the following two actions are done on the cryptoExecutor + val group = privateGroupFactory.createPrivateGroup(name, author) + val joinMsg = privateGroupMessageFactory.createJoinMessage( + group.id, clock.currentTimeMillis(), author + ) + privateGroupManager.addPrivateGroup(group, joinMsg, true) } override fun loadGroups(txn: Transaction) = diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupScreen.kt index 0f3d5a83875ccf1716ec70d900923686a1e46f8a..eb8091a1186eec312d90ab37b9770cfcbe4c36c9 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupScreen.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupScreen.kt @@ -29,8 +29,5 @@ fun PrivateGroupScreen( ) = GroupScreen( strings = PrivateGroupStrings, viewModel = viewModel, - addGroupDialog = { visible -> - // TODO - }, conversationScreen = { UiPlaceholder() } ) diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupStrings.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupStrings.kt index e3994aee389ba072bd99b48ed6c7e2cb978040a3..d0ca2055c8eb3f88b1025105337a2ed6257f38b7 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupStrings.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/PrivateGroupStrings.kt @@ -18,6 +18,7 @@ package org.briarproject.briar.desktop.privategroup +import org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH import org.briarproject.briar.desktop.group.GroupStrings import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nF @@ -27,7 +28,9 @@ import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nP object PrivateGroupStrings : GroupStrings( listTitle = i18n("forum.search.title"), listDescription = i18n("access.forums.list"), - addButtonDescription = i18n("forum.add.title"), + addGroupTitle = i18n("forum.add.title"), + addGroupHint = i18n("forum.add.hint"), + addGroupButton = i18n("forum.add.button"), noGroupsYet = i18n("forum.empty_state.text"), noGroupSelectedTitle = i18n("forum.none_selected.title"), noGroupSelectedText = i18n("forum.none_selected.hint"), @@ -41,4 +44,5 @@ object PrivateGroupStrings : GroupStrings( lastMessage = { timestamp -> i18nF("access.forums.last_post_timestamp", timestamp) }, + groupNameMaxLength = MAX_GROUP_NAME_LENGTH, )