From de885b4b4a0018a2e9dedc2b3161c5c54bcae028 Mon Sep 17 00:00:00 2001 From: 0x330a <92654767+0x330a@users.noreply.github.com> Date: Fri, 8 Sep 2023 12:42:53 +1000 Subject: [PATCH] feat: wiring together storage and configs for groups in a test way, experimenting with best structure that makes sense for group creation --- .../securesms/database/Storage.kt | 19 +++++++++---- .../securesms/dependencies/ConfigFactory.kt | 10 +++---- .../securesms/groups/CreateGroupFragment.kt | 27 +++++++++++++------ .../securesms/groups/CreateGroupViewModel.kt | 10 ++++--- .../libsession/database/StorageProtocol.kt | 2 +- .../utilities/ConfigFactoryProtocol.kt | 6 ++--- 6 files changed, 49 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index fe0152da74..b9c680ff9c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -21,6 +21,7 @@ import network.loki.messenger.libsession_util.util.UserPic import org.session.libsession.avatars.AvatarHelper import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.BlindedIdMapping +import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.calls.CallMessageType import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.jobs.AttachmentUploadJob @@ -880,15 +881,23 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co DatabaseComponent.get(context).groupDatabase().create(groupId, title, members, avatar, relay, admins, formationTimestamp) } - override fun createNewGroup(groupName: String, groupDescription: String, members: List): Long? { + override fun createNewGroup(groupName: String, groupDescription: String, members: Set): Long? { val userGroups = configFactory.userGroups ?: return null val ourSessionId = getUserPublicKey() ?: return null + val userKp = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null val group = userGroups.createGroup() userGroups.set(group) - val groupInfo = configFactory.groupInfoConfig(group.groupSessionId) ?: return null - val groupMembers = configFactory.groupMemberConfig(group.groupSessionId) ?: return null - val groupKeys = configFactory.groupKeysConfig(group.groupSessionId) ?: return null + val groupInfo = configFactory.getOrConstructGroupInfoConfig(group.groupSessionId) ?: return null + val groupMembers = configFactory.getOrConstructGroupMemberConfig(group.groupSessionId) ?: return null + + val groupKeys = GroupKeysConfig.newInstance( + userKp.secretKey.asBytes, + Hex.fromStringCondensed(group.groupSessionId.publicKey), + group.adminKey, + info = groupInfo, + members = groupMembers + ) with (groupInfo) { setName(groupName) @@ -1070,7 +1079,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co } override fun getMembers(groupPublicKey: String): List = - configFactory.groupMemberConfig(SessionId.from(groupPublicKey))?.all()?.toList() ?: emptyList() + configFactory.getOrConstructGroupMemberConfig(SessionId.from(groupPublicKey))?.all()?.toList() ?: emptyList() override fun setServerCapabilities(server: String, capabilities: List) { return DatabaseComponent.get(context).lokiAPIDatabase().setServerCapabilities(server, capabilities) diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt index 2c44b3d4e8..109727652f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt @@ -176,7 +176,7 @@ class ConfigFactory( it.adminKey to it.authData } - override fun groupInfoConfig(groupSessionId: SessionId): GroupInfoConfig? = getGroupAuthInfo(groupSessionId)?.let { (sk, _) -> + override fun getOrConstructGroupInfoConfig(groupSessionId: SessionId): GroupInfoConfig? = getGroupAuthInfo(groupSessionId)?.let { (sk, _) -> // get any potential initial dumps val dump = configDatabase.retrieveConfigAndHashes( SharedConfigMessage.Kind.CLOSED_GROUP_INFO.name, @@ -186,15 +186,15 @@ class ConfigFactory( GroupInfoConfig.newInstance(Hex.fromStringCondensed(groupSessionId.publicKey), sk, dump) } - override fun groupKeysConfig(groupSessionId: SessionId): GroupKeysConfig? = getGroupAuthInfo(groupSessionId)?.let { (sk, _) -> + override fun getGroupKeysConfig(groupSessionId: SessionId): GroupKeysConfig? = getGroupAuthInfo(groupSessionId)?.let { (sk, _) -> // Get the user info or return early val (userSk, _) = maybeGetUserInfo() ?: return@let null // Get the group info or return early - val info = groupInfoConfig(groupSessionId) ?: return@let null + val info = getOrConstructGroupInfoConfig(groupSessionId) ?: return@let null // Get the group members or return early - val members = groupMemberConfig(groupSessionId) ?: return@let null + val members = getOrConstructGroupMemberConfig(groupSessionId) ?: return@let null // Get the dump or empty val dump = configDatabase.retrieveConfigAndHashes( @@ -213,7 +213,7 @@ class ConfigFactory( ) } - override fun groupMemberConfig(groupSessionId: SessionId): GroupMembersConfig? = getGroupAuthInfo(groupSessionId)?.let { (sk, auth) -> + override fun getOrConstructGroupMemberConfig(groupSessionId: SessionId): GroupMembersConfig? = getGroupAuthInfo(groupSessionId)?.let { (sk, auth) -> // Get initial dump if we have one val dump = configDatabase.retrieveConfigAndHashes( SharedConfigMessage.Kind.CLOSED_GROUP_MEMBERS.name, diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt index acb4db100a..ff2791e350 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupFragment.kt @@ -22,6 +22,7 @@ import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -64,12 +65,13 @@ class CreateGroupFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - - val isLoading = viewModel.viewState. - return ComposeView(requireContext()).apply { setContent { - CreateGroupScreen(createGroupState = CreateGroupState("", "", emptySet())) + // this is kind of annoying to require an initial state in the fragment and the VM + val currentState = viewModel.viewState.observeAsState(initial = ViewState.DEFAULT) + // create group state might be useful in future for adding members and returning + // to the create group state with a copy or something + CreateGroupScreen(currentState.value, createGroupState = CreateGroupState("", "", emptySet())) } } } @@ -82,11 +84,15 @@ class CreateGroupFragment : Fragment() { } @Composable - fun CreateGroupScreen(createGroupState: CreateGroupState, modifier: Modifier = Modifier) { + fun CreateGroupScreen(viewState: ViewState, + createGroupState: CreateGroupState, + modifier: Modifier = Modifier) { CreateGroup( + viewState, createGroupState, - onCreate = { + onCreate = { newGroup -> // launch something to create here + viewModel.tryCreateGroup(newGroup) }, onClose = { delegate.onDialogClosePressed() @@ -101,7 +107,11 @@ class CreateGroupFragment : Fragment() { val isLoading: Boolean, @StringRes val error: Int?, val createdThreadId: Long? - ) + ) { + companion object { + val DEFAULT = ViewState(false, null, null) + } + } } @@ -113,6 +123,7 @@ data class CreateGroupState ( @Composable fun CreateGroup( + viewState: CreateGroupFragment.ViewState, createGroupState: CreateGroupState, onCreate: (CreateGroupState) -> Unit, onBack: () -> Unit, @@ -165,7 +176,7 @@ fun CreateGroup( // Create button OutlinedButton( onClick = { onCreate(CreateGroupState(name, description, members)) }, - enabled = name.isNotBlank(), + enabled = name.isNotBlank() && !viewState.isLoading, modifier = Modifier .align(Alignment.CenterHorizontally) .padding(16.dp), diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupViewModel.kt index c174a526ae..6cc236d44f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/CreateGroupViewModel.kt @@ -21,7 +21,7 @@ class CreateGroupViewModel @Inject constructor( private val _recipients = MutableLiveData>() val recipients: LiveData> = _recipients - private val _viewState = MutableLiveData(CreateGroupFragment.ViewState(false, null, null)) + private val _viewState = MutableLiveData(CreateGroupFragment.ViewState.DEFAULT) val viewState: LiveData = _viewState init { @@ -43,8 +43,12 @@ class CreateGroupViewModel @Inject constructor( fun tryCreateGroup(createGroupState: CreateGroupState) { _viewState.postValue(CreateGroupFragment.ViewState(true, null, null)) + val name = createGroupState.groupName + val description = createGroupState.groupDescription + val members = createGroupState.members + // do some validations - if (createGroupState.groupName.isEmpty()) { + if (name.isEmpty()) { return _viewState.postValue( CreateGroupFragment.ViewState(false, R.string.error, null) ) @@ -52,7 +56,7 @@ class CreateGroupViewModel @Inject constructor( // TODO: add future validation for empty group ? we'll add ourselves anyway ig // make a group - storage.createGroup() + storage.createNewGroup(name, description, members) } fun filter(query: String): List { diff --git a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt index 2494821cff..77c88c3e70 100644 --- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt @@ -155,7 +155,7 @@ interface StorageProtocol { fun setExpirationTimer(address: String, duration: Int) // Closed Groups - fun createNewGroup(groupName: String, groupDescription: String, members: List): Long? + fun createNewGroup(groupName: String, groupDescription: String, members: Set): Long? fun getMembers(groupPublicKey: String): List // Groups diff --git a/libsession/src/main/java/org/session/libsession/utilities/ConfigFactoryProtocol.kt b/libsession/src/main/java/org/session/libsession/utilities/ConfigFactoryProtocol.kt index 90a6200134..93346351a1 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/ConfigFactoryProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/ConfigFactoryProtocol.kt @@ -16,9 +16,9 @@ interface ConfigFactoryProtocol { val convoVolatile: ConversationVolatileConfig? val userGroups: UserGroupsConfig? - fun groupInfoConfig(groupSessionId: SessionId): GroupInfoConfig? - fun groupKeysConfig(groupSessionId: SessionId): GroupKeysConfig? - fun groupMemberConfig(groupSessionId: SessionId): GroupMembersConfig? + fun getOrConstructGroupInfoConfig(groupSessionId: SessionId): GroupInfoConfig? + fun getOrConstructGroupMemberConfig(groupSessionId: SessionId): GroupMembersConfig? + fun getGroupKeysConfig(groupSessionId: SessionId): GroupKeysConfig? fun getUserConfigs(): List fun persist(forConfigObject: ConfigBase, timestamp: Long)