mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-25 02:58:13 +00:00
feat: add background group creation and placeholder spinny progress
This commit is contained in:
@@ -7,15 +7,19 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.gestures.Orientation
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
import androidx.compose.foundation.gestures.scrollable
|
import androidx.compose.foundation.gestures.scrollable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.defaultMinSize
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.CircularProgressIndicator
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.OutlinedButton
|
import androidx.compose.material.OutlinedButton
|
||||||
import androidx.compose.material.OutlinedTextField
|
import androidx.compose.material.OutlinedTextField
|
||||||
@@ -28,6 +32,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
@@ -36,7 +41,10 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.databinding.FragmentCreateGroupBinding
|
import network.loki.messenger.databinding.FragmentCreateGroupBinding
|
||||||
import org.session.libsession.utilities.Device
|
import org.session.libsession.utilities.Device
|
||||||
@@ -93,9 +101,13 @@ class CreateGroupFragment : Fragment() {
|
|||||||
createGroupState,
|
createGroupState,
|
||||||
onCreate = { newGroup ->
|
onCreate = { newGroup ->
|
||||||
// launch something to create here
|
// launch something to create here
|
||||||
val groupRecipient = viewModel.tryCreateGroup(newGroup)
|
// dunno if we want to key this here as a launched effect on some property :thinking:
|
||||||
groupRecipient?.let { recipient ->
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
openConversationActivity(requireContext(), recipient)
|
val groupRecipient = viewModel.tryCreateGroup(newGroup)
|
||||||
|
groupRecipient?.let { recipient ->
|
||||||
|
openConversationActivity(requireContext(), recipient)
|
||||||
|
delegate.onDialogClosePressed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClose = {
|
onClose = {
|
||||||
@@ -110,11 +122,10 @@ class CreateGroupFragment : Fragment() {
|
|||||||
|
|
||||||
data class ViewState(
|
data class ViewState(
|
||||||
val isLoading: Boolean,
|
val isLoading: Boolean,
|
||||||
@StringRes val error: Int?,
|
@StringRes val error: Int?
|
||||||
val createdThreadId: Long?
|
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
val DEFAULT = ViewState(false, null, null)
|
val DEFAULT = ViewState(false, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,59 +152,68 @@ fun CreateGroup(
|
|||||||
|
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
Column(
|
Box {
|
||||||
modifier
|
Column(
|
||||||
.fillMaxWidth()) {
|
modifier
|
||||||
Column(modifier.scrollable(scrollState, orientation = Orientation.Vertical)) {
|
.fillMaxWidth()) {
|
||||||
// Top bar
|
Column(modifier.scrollable(scrollState, orientation = Orientation.Vertical)) {
|
||||||
NavigationBar(
|
// Top bar
|
||||||
title = stringResource(id = R.string.activity_create_group_title),
|
NavigationBar(
|
||||||
onBack = onBack,
|
title = stringResource(id = R.string.activity_create_group_title),
|
||||||
onClose = onClose
|
onBack = onBack,
|
||||||
)
|
onClose = onClose
|
||||||
// Editable avatar (future chunk)
|
)
|
||||||
EditableAvatar(
|
// Editable avatar (future chunk)
|
||||||
|
EditableAvatar(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.CenterHorizontally)
|
||||||
|
.padding(top = 16.dp)
|
||||||
|
)
|
||||||
|
// Title
|
||||||
|
OutlinedTextField(
|
||||||
|
value = name,
|
||||||
|
onValueChange = { name = it },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.align(Alignment.CenterHorizontally)
|
||||||
|
.padding(vertical = 8.dp, horizontal = 24.dp),
|
||||||
|
)
|
||||||
|
// Description
|
||||||
|
OutlinedTextField(
|
||||||
|
value = description,
|
||||||
|
onValueChange = { description = it },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.align(Alignment.CenterHorizontally)
|
||||||
|
.padding(vertical = 8.dp, horizontal = 24.dp),
|
||||||
|
)
|
||||||
|
// Group list
|
||||||
|
MemberList(contacts = members, modifier = Modifier.padding(vertical = 8.dp, horizontal = 24.dp))
|
||||||
|
}
|
||||||
|
// Create button
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = { onCreate(CreateGroupState(name, description, members)) },
|
||||||
|
enabled = name.isNotBlank() && !viewState.isLoading,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.CenterHorizontally)
|
.align(Alignment.CenterHorizontally)
|
||||||
.padding(top = 16.dp)
|
.padding(16.dp),
|
||||||
)
|
shape = RoundedCornerShape(32.dp)
|
||||||
// Title
|
) {
|
||||||
OutlinedTextField(
|
Text(
|
||||||
value = name,
|
text = stringResource(id = R.string.activity_create_group_create_button_title),
|
||||||
onValueChange = { name = it },
|
// TODO: colours of everything here probably needs to be redone
|
||||||
modifier = Modifier
|
color = MaterialTheme.colors.onBackground,
|
||||||
.fillMaxWidth()
|
modifier = Modifier.width(160.dp),
|
||||||
.align(Alignment.CenterHorizontally)
|
textAlign = TextAlign.Center
|
||||||
.padding(vertical = 8.dp, horizontal = 24.dp),
|
)
|
||||||
)
|
}
|
||||||
// Description
|
|
||||||
OutlinedTextField(
|
|
||||||
value = description,
|
|
||||||
onValueChange = { description = it },
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.align(Alignment.CenterHorizontally)
|
|
||||||
.padding(vertical = 8.dp, horizontal = 24.dp),
|
|
||||||
)
|
|
||||||
// Group list
|
|
||||||
MemberList(contacts = members, modifier = Modifier.padding(vertical = 8.dp, horizontal = 24.dp))
|
|
||||||
}
|
}
|
||||||
// Create button
|
if (viewState.isLoading) {
|
||||||
OutlinedButton(
|
Box(modifier = modifier.fillMaxSize().background(Color.Gray.copy(alpha = 0.5f))) {
|
||||||
onClick = { onCreate(CreateGroupState(name, description, members)) },
|
CircularProgressIndicator(
|
||||||
enabled = name.isNotBlank() && !viewState.isLoading,
|
modifier = Modifier.align(Alignment.Center)
|
||||||
modifier = Modifier
|
)
|
||||||
.align(Alignment.CenterHorizontally)
|
}
|
||||||
.padding(16.dp),
|
|
||||||
shape = RoundedCornerShape(32.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(id = R.string.activity_create_group_create_button_title),
|
|
||||||
// TODO: colours of everything here probably needs to be redone
|
|
||||||
color = MaterialTheme.colors.onBackground,
|
|
||||||
modifier = Modifier.width(160.dp),
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,7 +246,7 @@ fun ClosedGroupPreview(
|
|||||||
) {
|
) {
|
||||||
PreviewTheme(themeResId) {
|
PreviewTheme(themeResId) {
|
||||||
CreateGroup(
|
CreateGroup(
|
||||||
viewState = CreateGroupFragment.ViewState(false, null, null),
|
viewState = CreateGroupFragment.ViewState(false, null),
|
||||||
createGroupState = CreateGroupState("Group Name", "Test Group Description", emptySet()),
|
createGroupState = CreateGroupState("Group Name", "Test Group Description", emptySet()),
|
||||||
onCreate = {},
|
onCreate = {},
|
||||||
onClose = {},
|
onClose = {},
|
||||||
|
@@ -41,23 +41,26 @@ class CreateGroupViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun tryCreateGroup(createGroupState: CreateGroupState): Recipient? {
|
fun tryCreateGroup(createGroupState: CreateGroupState): Recipient? {
|
||||||
_viewState.postValue(CreateGroupFragment.ViewState(true, null, null))
|
_viewState.postValue(CreateGroupFragment.ViewState(true, null))
|
||||||
|
|
||||||
val name = createGroupState.groupName
|
val name = createGroupState.groupName
|
||||||
val description = createGroupState.groupDescription
|
val description = createGroupState.groupDescription
|
||||||
val members = createGroupState.members
|
val members = createGroupState.members
|
||||||
|
|
||||||
// do some validations
|
// do some validation
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
_viewState.postValue(
|
_viewState.postValue(
|
||||||
CreateGroupFragment.ViewState(false, R.string.error, null)
|
CreateGroupFragment.ViewState(false, R.string.error)
|
||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
// 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
|
||||||
val newGroup = storage.createNewGroup(name, description, members) // TODO: handle optional
|
val newGroup = storage.createNewGroup(name, description, members) // TODO: handle optional
|
||||||
|
if (!newGroup.isPresent) {
|
||||||
|
_viewState.postValue(CreateGroupFragment.ViewState(isLoading = false, null))
|
||||||
|
}
|
||||||
return newGroup.orNull()
|
return newGroup.orNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -198,3 +198,20 @@ Java_network_loki_messenger_libsession_1util_GroupKeysConfig_keys(JNIEnv *env, j
|
|||||||
}
|
}
|
||||||
return our_stack;
|
return our_stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jobject JNICALL
|
||||||
|
Java_network_loki_messenger_libsession_1util_GroupKeysConfig_currentHashes(JNIEnv *env,
|
||||||
|
jobject thiz) {
|
||||||
|
auto ptr = ptrToKeys(env, thiz);
|
||||||
|
auto existing = ptr->current_hashes();
|
||||||
|
jclass stack = env->FindClass("java/util/Stack");
|
||||||
|
jmethodID init = env->GetMethodID(stack, "<init>", "()V");
|
||||||
|
jobject our_list = env->NewObject(stack, init);
|
||||||
|
jmethodID push = env->GetMethodID(stack, "push", "(Ljava/lang/Object;)Ljava/lang/Object;");
|
||||||
|
for (auto& hash : existing) {
|
||||||
|
auto hash_bytes = env->NewStringUTF(hash.data());
|
||||||
|
env->CallObjectMethod(our_list, push, hash_bytes);
|
||||||
|
}
|
||||||
|
return our_list;
|
||||||
|
}
|
@@ -292,7 +292,8 @@ class GroupMembersConfig(pointer: Long): ConfigBase(pointer), Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ConfigSig(pointer: Long) : Config(pointer)
|
sealed class ConfigSig(pointer: Long) : Config(pointer) {
|
||||||
|
}
|
||||||
|
|
||||||
class GroupKeysConfig(pointer: Long): ConfigSig(pointer) {
|
class GroupKeysConfig(pointer: Long): ConfigSig(pointer) {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -322,6 +323,7 @@ class GroupKeysConfig(pointer: Long): ConfigSig(pointer) {
|
|||||||
external fun needsRekey(): Boolean
|
external fun needsRekey(): Boolean
|
||||||
external fun pendingKey(): ByteArray?
|
external fun pendingKey(): ByteArray?
|
||||||
external fun pendingConfig(): ByteArray?
|
external fun pendingConfig(): ByteArray?
|
||||||
|
external fun currentHashes(): List<String>
|
||||||
external fun rekey(info: GroupInfoConfig, members: GroupMembersConfig): ByteArray
|
external fun rekey(info: GroupInfoConfig, members: GroupMembersConfig): ByteArray
|
||||||
override fun close() {
|
override fun close() {
|
||||||
free()
|
free()
|
||||||
|
@@ -23,6 +23,7 @@ import org.session.libsignal.utilities.Log
|
|||||||
import org.session.libsignal.utilities.Namespace
|
import org.session.libsignal.utilities.Namespace
|
||||||
import org.session.libsignal.utilities.SessionId
|
import org.session.libsignal.utilities.SessionId
|
||||||
import org.session.libsignal.utilities.Snode
|
import org.session.libsignal.utilities.Snode
|
||||||
|
import kotlin.time.Duration.Companion.days
|
||||||
|
|
||||||
class ClosedGroupPoller(private val executor: CoroutineScope,
|
class ClosedGroupPoller(private val executor: CoroutineScope,
|
||||||
private val closedGroupSessionId: SessionId,
|
private val closedGroupSessionId: SessionId,
|
||||||
@@ -97,6 +98,12 @@ class ClosedGroupPoller(private val executor: CoroutineScope,
|
|||||||
val members = configFactoryProtocol.getGroupMemberConfig(closedGroupSessionId) ?: return null
|
val members = configFactoryProtocol.getGroupMemberConfig(closedGroupSessionId) ?: return null
|
||||||
val keys = configFactoryProtocol.getGroupKeysConfig(closedGroupSessionId) ?: return null
|
val keys = configFactoryProtocol.getGroupKeysConfig(closedGroupSessionId) ?: return null
|
||||||
|
|
||||||
|
val hashesToExtend = mutableSetOf<String>()
|
||||||
|
|
||||||
|
hashesToExtend += info.currentHashes()
|
||||||
|
hashesToExtend += members.currentHashes()
|
||||||
|
hashesToExtend += keys.currentHashes()
|
||||||
|
|
||||||
val keysIndex = 0
|
val keysIndex = 0
|
||||||
val infoIndex = 1
|
val infoIndex = 1
|
||||||
val membersIndex = 2
|
val membersIndex = 2
|
||||||
@@ -133,14 +140,26 @@ class ClosedGroupPoller(private val executor: CoroutineScope,
|
|||||||
group.signingKey()
|
group.signingKey()
|
||||||
) ?: return null
|
) ?: return null
|
||||||
|
|
||||||
|
val requests = mutableListOf(keysPoll, infoPoll, membersPoll, messagePoll)
|
||||||
|
|
||||||
|
if (hashesToExtend.isNotEmpty()) {
|
||||||
|
SnodeAPI.buildAuthenticatedAlterTtlBatchRequest(
|
||||||
|
messageHashes = hashesToExtend.toList(),
|
||||||
|
publicKey = closedGroupSessionId.hexString(),
|
||||||
|
signingKey = group.signingKey(),
|
||||||
|
newExpiry = SnodeAPI.nowWithOffset + 14.days.inWholeMilliseconds,
|
||||||
|
extend = true
|
||||||
|
)?.let { extensionRequest ->
|
||||||
|
requests += extensionRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val pollResult = SnodeAPI.getRawBatchResponse(
|
val pollResult = SnodeAPI.getRawBatchResponse(
|
||||||
snode,
|
snode,
|
||||||
closedGroupSessionId.hexString(),
|
closedGroupSessionId.hexString(),
|
||||||
listOf(keysPoll, infoPoll, membersPoll, messagePoll)
|
requests
|
||||||
).get()
|
).get()
|
||||||
|
|
||||||
// TODO: add the extend duration TTLs for known hashes here
|
|
||||||
|
|
||||||
// if poll result body is null here we don't have any things ig
|
// if poll result body is null here we don't have any things ig
|
||||||
if (ENABLE_LOGGING) Log.d("ClosedGroupPoller", "Poll results @${SnodeAPI.nowWithOffset}:")
|
if (ENABLE_LOGGING) Log.d("ClosedGroupPoller", "Poll results @${SnodeAPI.nowWithOffset}:")
|
||||||
(pollResult["results"] as List<RawResponse>).forEachIndexed { index, response ->
|
(pollResult["results"] as List<RawResponse>).forEachIndexed { index, response ->
|
||||||
|
@@ -525,9 +525,11 @@ object SnodeAPI {
|
|||||||
messageHashes: List<String>,
|
messageHashes: List<String>,
|
||||||
newExpiry: Long,
|
newExpiry: Long,
|
||||||
publicKey: String,
|
publicKey: String,
|
||||||
|
signingKey: ByteArray,
|
||||||
|
pubKeyEd25519: String? = null,
|
||||||
shorten: Boolean = false,
|
shorten: Boolean = false,
|
||||||
extend: Boolean = false): SnodeBatchRequestInfo? {
|
extend: Boolean = false): SnodeBatchRequestInfo? {
|
||||||
val params = buildAlterTtlParams(messageHashes, newExpiry, publicKey, extend, shorten) ?: return null
|
val params = buildAlterTtlParams(messageHashes, newExpiry, publicKey, signingKey, pubKeyEd25519, extend, shorten) ?: return null
|
||||||
return SnodeBatchRequestInfo(
|
return SnodeBatchRequestInfo(
|
||||||
Snode.Method.Expire.rawValue,
|
Snode.Method.Expire.rawValue,
|
||||||
params,
|
params,
|
||||||
@@ -535,6 +537,28 @@ object SnodeAPI {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun buildAuthenticatedAlterTtlBatchRequest(
|
||||||
|
messageHashes: List<String>,
|
||||||
|
newExpiry: Long,
|
||||||
|
publicKey: String,
|
||||||
|
shorten: Boolean = false,
|
||||||
|
extend: Boolean = false): SnodeBatchRequestInfo? {
|
||||||
|
val userEd25519KeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null
|
||||||
|
val signingKey = userEd25519KeyPair.secretKey.asBytes
|
||||||
|
val pubKeyEd25519 = userEd25519KeyPair.publicKey.asHexString
|
||||||
|
return buildAuthenticatedAlterTtlBatchRequest(
|
||||||
|
messageHashes,
|
||||||
|
newExpiry,
|
||||||
|
publicKey,
|
||||||
|
signingKey,
|
||||||
|
pubKeyEd25519,
|
||||||
|
shorten,
|
||||||
|
extend
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun getRawBatchResponse(snode: Snode, publicKey: String, requests: List<SnodeBatchRequestInfo>, sequence: Boolean = false): RawResponsePromise {
|
fun getRawBatchResponse(snode: Snode, publicKey: String, requests: List<SnodeBatchRequestInfo>, sequence: Boolean = false): RawResponsePromise {
|
||||||
val parameters = mutableMapOf<String, Any>(
|
val parameters = mutableMapOf<String, Any>(
|
||||||
"requests" to requests
|
"requests" to requests
|
||||||
@@ -587,9 +611,18 @@ object SnodeAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun alterTtl(messageHashes: List<String>, newExpiry: Long, publicKey: String, extend: Boolean = false, shorten: Boolean = false): RawResponsePromise {
|
fun alterTtl(messageHashes: List<String>,
|
||||||
|
newExpiry: Long,
|
||||||
|
publicKey: String,
|
||||||
|
extend: Boolean = false,
|
||||||
|
shorten: Boolean = false): RawResponsePromise {
|
||||||
return retryIfNeeded(maxRetryCount) {
|
return retryIfNeeded(maxRetryCount) {
|
||||||
val params = buildAlterTtlParams(messageHashes, newExpiry, publicKey, extend, shorten)
|
val userEd25519KeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return@retryIfNeeded Promise.ofFail(
|
||||||
|
Exception("No user key pair to sign alter ttl message")
|
||||||
|
)
|
||||||
|
val signingKey = userEd25519KeyPair.secretKey.asBytes
|
||||||
|
val pubKeyEd25519 = userEd25519KeyPair.publicKey.asHexString
|
||||||
|
val params = buildAlterTtlParams(messageHashes, newExpiry, publicKey, signingKey, pubKeyEd25519, extend, shorten)
|
||||||
?: return@retryIfNeeded Promise.ofFail(
|
?: return@retryIfNeeded Promise.ofFail(
|
||||||
Exception("Couldn't build signed params for alterTtl request for newExpiry=$newExpiry, extend=$extend, shorten=$shorten")
|
Exception("Couldn't build signed params for alterTtl request for newExpiry=$newExpiry, extend=$extend, shorten=$shorten")
|
||||||
)
|
)
|
||||||
@@ -599,13 +632,15 @@ object SnodeAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildAlterTtlParams( // TODO: in future this will probably need to use the closed group subkeys / admin keys for group swarms
|
private fun buildAlterTtlParams(
|
||||||
messageHashes: List<String>,
|
messageHashes: List<String>,
|
||||||
newExpiry: Long,
|
newExpiry: Long,
|
||||||
publicKey: String,
|
publicKey: String,
|
||||||
|
signingKey: ByteArray,
|
||||||
|
pubKeyEd25519: String? = null,
|
||||||
extend: Boolean = false,
|
extend: Boolean = false,
|
||||||
shorten: Boolean = false): Map<String, Any>? {
|
shorten: Boolean = false): Map<String, Any>? {
|
||||||
val userEd25519KeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null
|
|
||||||
val params = mutableMapOf(
|
val params = mutableMapOf(
|
||||||
"expiry" to newExpiry,
|
"expiry" to newExpiry,
|
||||||
"messages" to messageHashes,
|
"messages" to messageHashes,
|
||||||
@@ -619,21 +654,23 @@ object SnodeAPI {
|
|||||||
|
|
||||||
val signData = "${Snode.Method.Expire.rawValue}$shortenOrExtend$newExpiry${messageHashes.joinToString(separator = "")}".toByteArray()
|
val signData = "${Snode.Method.Expire.rawValue}$shortenOrExtend$newExpiry${messageHashes.joinToString(separator = "")}".toByteArray()
|
||||||
|
|
||||||
val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString
|
|
||||||
val signature = ByteArray(Sign.BYTES)
|
val signature = ByteArray(Sign.BYTES)
|
||||||
try {
|
try {
|
||||||
sodium.cryptoSignDetached(
|
sodium.cryptoSignDetached(
|
||||||
signature,
|
signature,
|
||||||
signData,
|
signData,
|
||||||
signData.size.toLong(),
|
signData.size.toLong(),
|
||||||
userEd25519KeyPair.secretKey.asBytes
|
signingKey
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("Loki", "Signing data failed with user secret key", e)
|
Log.e("Loki", "Signing data failed with user secret key", e)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
params["pubkey"] = publicKey
|
params["pubkey"] = publicKey
|
||||||
params["pubkey_ed25519"] = ed25519PublicKey
|
if (pubKeyEd25519 != null) {
|
||||||
|
params["pubkey_ed25519"] = pubKeyEd25519
|
||||||
|
}
|
||||||
|
|
||||||
params["signature"] = Base64.encodeBytes(signature)
|
params["signature"] = Base64.encodeBytes(signature)
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
Reference in New Issue
Block a user