feat: wiring together storage and configs for groups in a test way, experimenting with best structure that makes sense for group creation

This commit is contained in:
0x330a 2023-09-08 12:42:53 +10:00
parent 026f0056b9
commit de885b4b4a
6 changed files with 49 additions and 25 deletions

View file

@ -21,6 +21,7 @@ import network.loki.messenger.libsession_util.util.UserPic
import org.session.libsession.avatars.AvatarHelper import org.session.libsession.avatars.AvatarHelper
import org.session.libsession.database.StorageProtocol import org.session.libsession.database.StorageProtocol
import org.session.libsession.messaging.BlindedIdMapping import org.session.libsession.messaging.BlindedIdMapping
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.calls.CallMessageType import org.session.libsession.messaging.calls.CallMessageType
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.jobs.AttachmentUploadJob 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) DatabaseComponent.get(context).groupDatabase().create(groupId, title, members, avatar, relay, admins, formationTimestamp)
} }
override fun createNewGroup(groupName: String, groupDescription: String, members: List<SessionId>): Long? { override fun createNewGroup(groupName: String, groupDescription: String, members: Set<SessionId>): Long? {
val userGroups = configFactory.userGroups ?: return null val userGroups = configFactory.userGroups ?: return null
val ourSessionId = getUserPublicKey() ?: return null val ourSessionId = getUserPublicKey() ?: return null
val userKp = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null
val group = userGroups.createGroup() val group = userGroups.createGroup()
userGroups.set(group) userGroups.set(group)
val groupInfo = configFactory.groupInfoConfig(group.groupSessionId) ?: return null val groupInfo = configFactory.getOrConstructGroupInfoConfig(group.groupSessionId) ?: return null
val groupMembers = configFactory.groupMemberConfig(group.groupSessionId) ?: return null val groupMembers = configFactory.getOrConstructGroupMemberConfig(group.groupSessionId) ?: return null
val groupKeys = configFactory.groupKeysConfig(group.groupSessionId) ?: return null
val groupKeys = GroupKeysConfig.newInstance(
userKp.secretKey.asBytes,
Hex.fromStringCondensed(group.groupSessionId.publicKey),
group.adminKey,
info = groupInfo,
members = groupMembers
)
with (groupInfo) { with (groupInfo) {
setName(groupName) setName(groupName)
@ -1070,7 +1079,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
} }
override fun getMembers(groupPublicKey: String): List<LibSessionGroupMember> = override fun getMembers(groupPublicKey: String): List<LibSessionGroupMember> =
configFactory.groupMemberConfig(SessionId.from(groupPublicKey))?.all()?.toList() ?: emptyList() configFactory.getOrConstructGroupMemberConfig(SessionId.from(groupPublicKey))?.all()?.toList() ?: emptyList()
override fun setServerCapabilities(server: String, capabilities: List<String>) { override fun setServerCapabilities(server: String, capabilities: List<String>) {
return DatabaseComponent.get(context).lokiAPIDatabase().setServerCapabilities(server, capabilities) return DatabaseComponent.get(context).lokiAPIDatabase().setServerCapabilities(server, capabilities)

View file

@ -176,7 +176,7 @@ class ConfigFactory(
it.adminKey to it.authData 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 // get any potential initial dumps
val dump = configDatabase.retrieveConfigAndHashes( val dump = configDatabase.retrieveConfigAndHashes(
SharedConfigMessage.Kind.CLOSED_GROUP_INFO.name, SharedConfigMessage.Kind.CLOSED_GROUP_INFO.name,
@ -186,15 +186,15 @@ class ConfigFactory(
GroupInfoConfig.newInstance(Hex.fromStringCondensed(groupSessionId.publicKey), sk, dump) 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 // Get the user info or return early
val (userSk, _) = maybeGetUserInfo() ?: return@let null val (userSk, _) = maybeGetUserInfo() ?: return@let null
// Get the group info or return early // 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 // 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 // Get the dump or empty
val dump = configDatabase.retrieveConfigAndHashes( 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 // Get initial dump if we have one
val dump = configDatabase.retrieveConfigAndHashes( val dump = configDatabase.retrieveConfigAndHashes(
SharedConfigMessage.Kind.CLOSED_GROUP_MEMBERS.name, SharedConfigMessage.Kind.CLOSED_GROUP_MEMBERS.name,

View file

@ -22,6 +22,7 @@ import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@ -64,12 +65,13 @@ class CreateGroupFragment : Fragment() {
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
val isLoading = viewModel.viewState.
return ComposeView(requireContext()).apply { return ComposeView(requireContext()).apply {
setContent { 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 @Composable
fun CreateGroupScreen(createGroupState: CreateGroupState, modifier: Modifier = Modifier) { fun CreateGroupScreen(viewState: ViewState,
createGroupState: CreateGroupState,
modifier: Modifier = Modifier) {
CreateGroup( CreateGroup(
viewState,
createGroupState, createGroupState,
onCreate = { onCreate = { newGroup ->
// launch something to create here // launch something to create here
viewModel.tryCreateGroup(newGroup)
}, },
onClose = { onClose = {
delegate.onDialogClosePressed() delegate.onDialogClosePressed()
@ -101,7 +107,11 @@ class CreateGroupFragment : Fragment() {
val isLoading: Boolean, val isLoading: Boolean,
@StringRes val error: Int?, @StringRes val error: Int?,
val createdThreadId: Long? val createdThreadId: Long?
) ) {
companion object {
val DEFAULT = ViewState(false, null, null)
}
}
} }
@ -113,6 +123,7 @@ data class CreateGroupState (
@Composable @Composable
fun CreateGroup( fun CreateGroup(
viewState: CreateGroupFragment.ViewState,
createGroupState: CreateGroupState, createGroupState: CreateGroupState,
onCreate: (CreateGroupState) -> Unit, onCreate: (CreateGroupState) -> Unit,
onBack: () -> Unit, onBack: () -> Unit,
@ -165,7 +176,7 @@ fun CreateGroup(
// Create button // Create button
OutlinedButton( OutlinedButton(
onClick = { onCreate(CreateGroupState(name, description, members)) }, onClick = { onCreate(CreateGroupState(name, description, members)) },
enabled = name.isNotBlank(), enabled = name.isNotBlank() && !viewState.isLoading,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
.padding(16.dp), .padding(16.dp),

View file

@ -21,7 +21,7 @@ class CreateGroupViewModel @Inject constructor(
private val _recipients = MutableLiveData<List<Recipient>>() private val _recipients = MutableLiveData<List<Recipient>>()
val recipients: LiveData<List<Recipient>> = _recipients val recipients: LiveData<List<Recipient>> = _recipients
private val _viewState = MutableLiveData(CreateGroupFragment.ViewState(false, null, null)) private val _viewState = MutableLiveData(CreateGroupFragment.ViewState.DEFAULT)
val viewState: LiveData<CreateGroupFragment.ViewState> = _viewState val viewState: LiveData<CreateGroupFragment.ViewState> = _viewState
init { init {
@ -43,8 +43,12 @@ class CreateGroupViewModel @Inject constructor(
fun tryCreateGroup(createGroupState: CreateGroupState) { fun tryCreateGroup(createGroupState: CreateGroupState) {
_viewState.postValue(CreateGroupFragment.ViewState(true, null, null)) _viewState.postValue(CreateGroupFragment.ViewState(true, null, null))
val name = createGroupState.groupName
val description = createGroupState.groupDescription
val members = createGroupState.members
// do some validations // do some validations
if (createGroupState.groupName.isEmpty()) { if (name.isEmpty()) {
return _viewState.postValue( return _viewState.postValue(
CreateGroupFragment.ViewState(false, R.string.error, null) 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 // TODO: add future validation for empty group ? we'll add ourselves anyway ig
// make a group // make a group
storage.createGroup() storage.createNewGroup(name, description, members)
} }
fun filter(query: String): List<Recipient> { fun filter(query: String): List<Recipient> {

View file

@ -155,7 +155,7 @@ interface StorageProtocol {
fun setExpirationTimer(address: String, duration: Int) fun setExpirationTimer(address: String, duration: Int)
// Closed Groups // Closed Groups
fun createNewGroup(groupName: String, groupDescription: String, members: List<SessionId>): Long? fun createNewGroup(groupName: String, groupDescription: String, members: Set<SessionId>): Long?
fun getMembers(groupPublicKey: String): List<network.loki.messenger.libsession_util.util.GroupMember> fun getMembers(groupPublicKey: String): List<network.loki.messenger.libsession_util.util.GroupMember>
// Groups // Groups

View file

@ -16,9 +16,9 @@ interface ConfigFactoryProtocol {
val convoVolatile: ConversationVolatileConfig? val convoVolatile: ConversationVolatileConfig?
val userGroups: UserGroupsConfig? val userGroups: UserGroupsConfig?
fun groupInfoConfig(groupSessionId: SessionId): GroupInfoConfig? fun getOrConstructGroupInfoConfig(groupSessionId: SessionId): GroupInfoConfig?
fun groupKeysConfig(groupSessionId: SessionId): GroupKeysConfig? fun getOrConstructGroupMemberConfig(groupSessionId: SessionId): GroupMembersConfig?
fun groupMemberConfig(groupSessionId: SessionId): GroupMembersConfig? fun getGroupKeysConfig(groupSessionId: SessionId): GroupKeysConfig?
fun getUserConfigs(): List<ConfigBase> fun getUserConfigs(): List<ConfigBase>
fun persist(forConfigObject: ConfigBase, timestamp: Long) fun persist(forConfigObject: ConfigBase, timestamp: Long)