feat: fix the invite order properly
This commit is contained in:
parent
cc24542af3
commit
d162522ac2
|
@ -19,6 +19,7 @@ import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||||
import network.loki.messenger.libsession_util.util.GroupDisplayInfo
|
import network.loki.messenger.libsession_util.util.GroupDisplayInfo
|
||||||
import network.loki.messenger.libsession_util.util.GroupInfo
|
import network.loki.messenger.libsession_util.util.GroupInfo
|
||||||
import network.loki.messenger.libsession_util.util.UserPic
|
import network.loki.messenger.libsession_util.util.UserPic
|
||||||
|
import nl.komponents.kovenant.functional.bind
|
||||||
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
|
||||||
|
@ -1277,7 +1278,78 @@ open class Storage(
|
||||||
override fun inviteClosedGroupMembers(groupSessionId: String, invitees: List<String>) {
|
override fun inviteClosedGroupMembers(groupSessionId: String, invitees: List<String>) {
|
||||||
// don't try to process invitee acceptance if we aren't admin
|
// don't try to process invitee acceptance if we aren't admin
|
||||||
if (configFactory.userGroups?.getClosedGroup(groupSessionId)?.hasAdminKey() != true) return
|
if (configFactory.userGroups?.getClosedGroup(groupSessionId)?.hasAdminKey() != true) return
|
||||||
val infoConfig = configFactory
|
val adminKey = configFactory.userGroups?.getClosedGroup(groupSessionId)?.adminKey ?: return
|
||||||
|
val sessionId = SessionId.from(groupSessionId)
|
||||||
|
val membersConfig = configFactory.getGroupMemberConfig(sessionId) ?: return
|
||||||
|
val infoConfig = configFactory.getGroupInfoConfig(sessionId) ?: return
|
||||||
|
|
||||||
|
val filteredMembers = invitees.filter {
|
||||||
|
membersConfig.get(it) == null
|
||||||
|
}
|
||||||
|
filteredMembers.forEach { memberSessionId ->
|
||||||
|
val member = membersConfig.getOrConstruct(memberSessionId).copy(
|
||||||
|
invitePending = true,
|
||||||
|
)
|
||||||
|
membersConfig.set(member)
|
||||||
|
}
|
||||||
|
|
||||||
|
val keysConfig = configFactory.getGroupKeysConfig(
|
||||||
|
sessionId,
|
||||||
|
info = infoConfig,
|
||||||
|
members = membersConfig,
|
||||||
|
free = false
|
||||||
|
) ?: return
|
||||||
|
|
||||||
|
keysConfig.rekey(infoConfig, membersConfig)
|
||||||
|
|
||||||
|
val sentTimestamp = SnodeAPI.nowWithOffset
|
||||||
|
|
||||||
|
val message = SnodeMessage(
|
||||||
|
groupSessionId,
|
||||||
|
Base64.encodeBytes(keysConfig.pendingConfig()!!), // should not be null from checking has pending
|
||||||
|
SnodeMessage.CONFIG_TTL,
|
||||||
|
sentTimestamp
|
||||||
|
)
|
||||||
|
val authenticatedBatch = SnodeAPI.buildAuthenticatedStoreBatchInfo(
|
||||||
|
keysConfig.namespace(),
|
||||||
|
message,
|
||||||
|
adminKey
|
||||||
|
)
|
||||||
|
|
||||||
|
val response = SnodeAPI.getSingleTargetSnode(groupSessionId).bind { snode ->
|
||||||
|
SnodeAPI.getRawBatchResponse(
|
||||||
|
snode,
|
||||||
|
groupSessionId,
|
||||||
|
listOf(authenticatedBatch),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val destination = Destination.ClosedGroup(groupSessionId)
|
||||||
|
|
||||||
|
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(destination)
|
||||||
|
|
||||||
|
try {
|
||||||
|
response.get()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("ClosedGroup", "Failed to store new key", e)
|
||||||
|
infoConfig.free()
|
||||||
|
membersConfig.free()
|
||||||
|
keysConfig.free()
|
||||||
|
// toaster toast here
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
configFactory.saveGroupConfigs(keysConfig, infoConfig, membersConfig)
|
||||||
|
|
||||||
|
infoConfig.free()
|
||||||
|
membersConfig.free()
|
||||||
|
keysConfig.free()
|
||||||
|
|
||||||
|
val newConfigSync = ConfigurationSyncJob(destination)
|
||||||
|
JobQueue.shared.add(newConfigSync)
|
||||||
|
|
||||||
|
val job = InviteContactsJob(groupSessionId, filteredMembers.toTypedArray())
|
||||||
|
JobQueue.shared.add(job)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setServerCapabilities(server: String, capabilities: List<String>) {
|
override fun setServerCapabilities(server: String, capabilities: List<String>) {
|
||||||
|
|
|
@ -221,6 +221,8 @@ class ConfigFactory(
|
||||||
info?.free()
|
info?.free()
|
||||||
members?.free()
|
members?.free()
|
||||||
}
|
}
|
||||||
|
if (usedInfo !== info) usedInfo.free()
|
||||||
|
if (usedMembers !== members) usedMembers.free()
|
||||||
keys
|
keys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,8 +48,6 @@ import network.loki.messenger.R
|
||||||
import network.loki.messenger.libsession_util.util.GroupMember
|
import network.loki.messenger.libsession_util.util.GroupMember
|
||||||
import org.session.libsession.database.StorageProtocol
|
import org.session.libsession.database.StorageProtocol
|
||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
import org.session.libsession.messaging.jobs.InviteContactsJob
|
|
||||||
import org.session.libsession.messaging.jobs.JobQueue
|
|
||||||
import org.thoughtcrime.securesms.groups.ContactList
|
import org.thoughtcrime.securesms.groups.ContactList
|
||||||
import org.thoughtcrime.securesms.groups.destinations.EditClosedGroupInviteScreenDestination
|
import org.thoughtcrime.securesms.groups.destinations.EditClosedGroupInviteScreenDestination
|
||||||
import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
|
import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
|
||||||
|
@ -98,7 +96,6 @@ fun EditClosedGroupInviteScreen(
|
||||||
val state by viewModel.viewState.collectAsState()
|
val state by viewModel.viewState.collectAsState()
|
||||||
val viewState = state.viewState
|
val viewState = state.viewState
|
||||||
val currentMemberSessionIds = viewState.currentMembers.map { it.memberSessionId }
|
val currentMemberSessionIds = viewState.currentMembers.map { it.memberSessionId }
|
||||||
val eventSink = state.eventSink
|
|
||||||
|
|
||||||
SelectContacts(
|
SelectContacts(
|
||||||
viewState.allContacts
|
viewState.allContacts
|
||||||
|
@ -156,15 +153,10 @@ class EditGroupViewModel @AssistedInject constructor(
|
||||||
when (event) {
|
when (event) {
|
||||||
is EditGroupEvent.InviteContacts -> {
|
is EditGroupEvent.InviteContacts -> {
|
||||||
val sessionIds = event.contacts
|
val sessionIds = event.contacts
|
||||||
val invite = InviteContactsJob(
|
|
||||||
groupSessionId,
|
|
||||||
sessionIds.contacts.map(Contact::sessionID).toTypedArray()
|
|
||||||
)
|
|
||||||
storage.inviteClosedGroupMembers(
|
storage.inviteClosedGroupMembers(
|
||||||
groupSessionId,
|
groupSessionId,
|
||||||
sessionIds.contacts.map(Contact::sessionID)
|
sessionIds.contacts.map(Contact::sessionID)
|
||||||
)
|
)
|
||||||
JobQueue.shared.add(invite)
|
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
event.context,
|
event.context,
|
||||||
"Inviting ${event.contacts.contacts.size}",
|
"Inviting ${event.contacts.contacts.size}",
|
||||||
|
@ -210,9 +202,7 @@ class EditGroupInviteViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
EditGroupInviteState(
|
EditGroupInviteState(
|
||||||
EditGroupInviteViewState(closedGroupMembers, contacts)
|
EditGroupInviteViewState(closedGroupMembers, contacts)
|
||||||
) { event ->
|
)
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
@ -321,7 +311,6 @@ data class EditGroupState(
|
||||||
|
|
||||||
data class EditGroupInviteState(
|
data class EditGroupInviteState(
|
||||||
val viewState: EditGroupInviteViewState,
|
val viewState: EditGroupInviteViewState,
|
||||||
val eventSink: (Unit) -> Unit
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class MemberViewModel(
|
data class MemberViewModel(
|
||||||
|
|
|
@ -104,6 +104,8 @@ Java_network_loki_messenger_libsession_1util_GroupKeysConfig_needsDump(JNIEnv *e
|
||||||
return keys->needs_dump();
|
return keys->needs_dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT jbyteArray JNICALL
|
JNIEXPORT jbyteArray JNICALL
|
||||||
Java_network_loki_messenger_libsession_1util_GroupKeysConfig_pendingKey(JNIEnv *env, jobject thiz) {
|
Java_network_loki_messenger_libsession_1util_GroupKeysConfig_pendingKey(JNIEnv *env, jobject thiz) {
|
||||||
|
@ -258,3 +260,17 @@ Java_network_loki_messenger_libsession_1util_GroupKeysConfig_subAccountSign(JNIE
|
||||||
auto swarm_auth = ptr->swarm_subaccount_sign(message_ustring, signing_value_ustring, false);
|
auto swarm_auth = ptr->swarm_subaccount_sign(message_ustring, signing_value_ustring, false);
|
||||||
return util::deserialize_swarm_auth(env, swarm_auth);
|
return util::deserialize_swarm_auth(env, swarm_auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT jbyteArray JNICALL
|
||||||
|
Java_network_loki_messenger_libsession_1util_GroupKeysConfig_supplementFor(JNIEnv *env,
|
||||||
|
jobject thiz,
|
||||||
|
jstring user_session_id) {
|
||||||
|
std::lock_guard lock{util::util_mutex_};
|
||||||
|
auto ptr = ptrToKeys(env, thiz);
|
||||||
|
auto string = env->GetStringUTFChars(user_session_id, nullptr);
|
||||||
|
auto supplement = ptr->key_supplement(string);
|
||||||
|
auto supplement_jbytearray = util::bytes_from_ustring(env, supplement);
|
||||||
|
env->ReleaseStringUTFChars(user_session_id, string);
|
||||||
|
return supplement_jbytearray;
|
||||||
|
}
|
|
@ -321,6 +321,7 @@ class GroupKeysConfig(pointer: Long): ConfigSig(pointer) {
|
||||||
members: GroupMembersConfig)
|
members: GroupMembersConfig)
|
||||||
external fun needsRekey(): Boolean
|
external fun needsRekey(): Boolean
|
||||||
external fun pendingKey(): ByteArray?
|
external fun pendingKey(): ByteArray?
|
||||||
|
external fun supplementFor(userSessionId: String): ByteArray
|
||||||
external fun pendingConfig(): ByteArray?
|
external fun pendingConfig(): ByteArray?
|
||||||
external fun currentHashes(): List<String>
|
external fun currentHashes(): List<String>
|
||||||
external fun rekey(info: GroupInfoConfig, members: GroupMembersConfig): ByteArray
|
external fun rekey(info: GroupInfoConfig, members: GroupMembersConfig): ByteArray
|
||||||
|
|
|
@ -24,10 +24,13 @@ import kotlin.math.roundToLong
|
||||||
class JobQueue : JobDelegate {
|
class JobQueue : JobDelegate {
|
||||||
private var hasResumedPendingJobs = false // Just for debugging
|
private var hasResumedPendingJobs = false // Just for debugging
|
||||||
private val jobTimestampMap = ConcurrentHashMap<Long, AtomicInteger>()
|
private val jobTimestampMap = ConcurrentHashMap<Long, AtomicInteger>()
|
||||||
|
|
||||||
private val rxDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
private val rxDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
private val rxMediaDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
|
private val rxMediaDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
|
||||||
private val openGroupDispatcher = Executors.newFixedThreadPool(8).asCoroutineDispatcher()
|
private val openGroupDispatcher = Executors.newFixedThreadPool(8).asCoroutineDispatcher()
|
||||||
private val txDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
private val txDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
|
private val configDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
|
|
||||||
private val scope = CoroutineScope(Dispatchers.Default) + SupervisorJob()
|
private val scope = CoroutineScope(Dispatchers.Default) + SupervisorJob()
|
||||||
private val queue = Channel<Job>(UNLIMITED)
|
private val queue = Channel<Job>(UNLIMITED)
|
||||||
private val pendingJobIds = mutableSetOf<String>()
|
private val pendingJobIds = mutableSetOf<String>()
|
||||||
|
@ -114,19 +117,23 @@ class JobQueue : JobDelegate {
|
||||||
val txQueue = Channel<Job>(capacity = UNLIMITED)
|
val txQueue = Channel<Job>(capacity = UNLIMITED)
|
||||||
val mediaQueue = Channel<Job>(capacity = UNLIMITED)
|
val mediaQueue = Channel<Job>(capacity = UNLIMITED)
|
||||||
val openGroupQueue = Channel<Job>(capacity = UNLIMITED)
|
val openGroupQueue = Channel<Job>(capacity = UNLIMITED)
|
||||||
|
val configQueue = Channel<Job>(capacity = UNLIMITED)
|
||||||
|
|
||||||
val receiveJob = processWithDispatcher(rxQueue, rxDispatcher, "rx", asynchronous = false)
|
val receiveJob = processWithDispatcher(rxQueue, rxDispatcher, "rx", asynchronous = false)
|
||||||
val txJob = processWithDispatcher(txQueue, txDispatcher, "tx")
|
val txJob = processWithDispatcher(txQueue, txDispatcher, "tx")
|
||||||
val mediaJob = processWithDispatcher(mediaQueue, rxMediaDispatcher, "media")
|
val mediaJob = processWithDispatcher(mediaQueue, rxMediaDispatcher, "media")
|
||||||
val openGroupJob = processWithOpenGroupDispatcher(openGroupQueue, openGroupDispatcher, "openGroup")
|
val openGroupJob = processWithOpenGroupDispatcher(openGroupQueue, openGroupDispatcher, "openGroup")
|
||||||
|
val configJob = processWithDispatcher(configQueue, configDispatcher, "configDispatcher")
|
||||||
|
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
when (val job = queue.receive()) {
|
when (val job = queue.receive()) {
|
||||||
|
is InviteContactsJob,
|
||||||
|
is ConfigurationSyncJob -> {
|
||||||
|
configQueue.send(job)
|
||||||
|
}
|
||||||
is NotifyPNServerJob,
|
is NotifyPNServerJob,
|
||||||
is AttachmentUploadJob,
|
is AttachmentUploadJob,
|
||||||
is MessageSendJob,
|
is MessageSendJob -> {
|
||||||
is ConfigurationSyncJob,
|
|
||||||
is InviteContactsJob -> {
|
|
||||||
txQueue.send(job)
|
txQueue.send(job)
|
||||||
}
|
}
|
||||||
is RetrieveProfileAvatarJob,
|
is RetrieveProfileAvatarJob,
|
||||||
|
@ -158,6 +165,7 @@ class JobQueue : JobDelegate {
|
||||||
txJob.cancel()
|
txJob.cancel()
|
||||||
mediaJob.cancel()
|
mediaJob.cancel()
|
||||||
openGroupJob.cancel()
|
openGroupJob.cancel()
|
||||||
|
configJob.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue