feat: all the control messages and basic sending of leave group
This commit is contained in:
parent
56e9a42086
commit
d02230cee3
|
@ -1,9 +1,13 @@
|
||||||
package org.thoughtcrime.securesms.conversation.settings
|
package org.thoughtcrime.securesms.conversation.settings
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Typeface
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.SpannableStringBuilder
|
||||||
|
import android.text.style.StyleSpan
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
import androidx.core.text.set
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
@ -68,6 +72,7 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
|
||||||
binding.notificationSettings.setOnClickListener(this)
|
binding.notificationSettings.setOnClickListener(this)
|
||||||
binding.editGroup.setOnClickListener(this)
|
binding.editGroup.setOnClickListener(this)
|
||||||
binding.addAdmins.setOnClickListener(this)
|
binding.addAdmins.setOnClickListener(this)
|
||||||
|
binding.leaveGroup.setOnClickListener(this)
|
||||||
binding.back.setOnClickListener(this)
|
binding.back.setOnClickListener(this)
|
||||||
binding.autoDownloadMediaSwitch.setOnCheckedChangeListener { _, isChecked ->
|
binding.autoDownloadMediaSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
viewModel.setAutoDownloadAttachments(isChecked)
|
viewModel.setAutoDownloadAttachments(isChecked)
|
||||||
|
@ -86,7 +91,7 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
|
||||||
}
|
}
|
||||||
// Setup group description (if group)
|
// Setup group description (if group)
|
||||||
binding.conversationSubtitle.isVisible = recipient.isClosedGroupRecipient.apply {
|
binding.conversationSubtitle.isVisible = recipient.isClosedGroupRecipient.apply {
|
||||||
binding.conversationSubtitle.text = "TODO: This is a test for group descriptions"
|
binding.conversationSubtitle.text = viewModel.closedGroupInfo(recipient.address.serialize())?.description
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle group-specific settings
|
// Toggle group-specific settings
|
||||||
|
@ -99,10 +104,12 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
|
||||||
val isUserGroupAdmin = areGroupOptionsVisible && viewModel.isUserGroupAdmin()
|
val isUserGroupAdmin = areGroupOptionsVisible && viewModel.isUserGroupAdmin()
|
||||||
with (binding) {
|
with (binding) {
|
||||||
groupMembersDivider.root.isVisible = areGroupOptionsVisible && !isUserGroupAdmin
|
groupMembersDivider.root.isVisible = areGroupOptionsVisible && !isUserGroupAdmin
|
||||||
groupMembers.isVisible = areGroupOptionsVisible && !isUserGroupAdmin
|
groupMembers.isVisible = !isUserGroupAdmin
|
||||||
adminControlsGroup.isVisible = isUserGroupAdmin
|
adminControlsGroup.isVisible = isUserGroupAdmin
|
||||||
deleteGroup.isVisible = isUserGroupAdmin
|
deleteGroup.isVisible = isUserGroupAdmin
|
||||||
|
clearMessages.isVisible = isUserGroupAdmin
|
||||||
clearMessagesDivider.root.isVisible = isUserGroupAdmin
|
clearMessagesDivider.root.isVisible = isUserGroupAdmin
|
||||||
|
leaveGroupDivider.root.isVisible = isUserGroupAdmin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set pinned state
|
// Set pinned state
|
||||||
|
@ -161,6 +168,35 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
|
||||||
cancelButton()
|
cancelButton()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
v === binding.leaveGroup -> {
|
||||||
|
showSessionDialog {
|
||||||
|
|
||||||
|
title(R.string.conversation_settings_leave_group)
|
||||||
|
|
||||||
|
val name = viewModel.recipient!!.name!!
|
||||||
|
val text = getString(R.string.conversation_settings_leave_group_name)
|
||||||
|
val textWithArgs = getString(R.string.conversation_settings_leave_group_name, name)
|
||||||
|
|
||||||
|
// Searches for the start index of %1$s
|
||||||
|
val startIndex = """%1${"\\$"}s""".toRegex().find(text)?.range?.start
|
||||||
|
val endIndex = startIndex?.plus(name.length)
|
||||||
|
|
||||||
|
val styledText = if (startIndex == null || endIndex == null) {
|
||||||
|
textWithArgs
|
||||||
|
} else {
|
||||||
|
val boldName = SpannableStringBuilder(textWithArgs)
|
||||||
|
boldName[startIndex .. endIndex] = StyleSpan(Typeface.BOLD)
|
||||||
|
boldName
|
||||||
|
}
|
||||||
|
text(styledText)
|
||||||
|
destructiveButton(
|
||||||
|
R.string.conversation_settings_leave_group,
|
||||||
|
R.string.conversation_settings_leave_group
|
||||||
|
) {
|
||||||
|
viewModel.leaveGroup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
v === binding.editGroup -> {
|
v === binding.editGroup -> {
|
||||||
val recipient = viewModel.recipient ?: return
|
val recipient = viewModel.recipient ?: return
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import network.loki.messenger.libsession_util.util.GroupDisplayInfo
|
||||||
import org.session.libsession.database.StorageProtocol
|
import org.session.libsession.database.StorageProtocol
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
@ -61,6 +62,14 @@ class ConversationSettingsViewModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun closedGroupInfo(address: String): GroupDisplayInfo? = storage.getClosedGroupDisplayInfo(address)
|
||||||
|
|
||||||
|
fun leaveGroup() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
storage.leaveGroup(recipient!!.address.serialize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DI-related
|
// DI-related
|
||||||
@dagger.assisted.AssistedFactory
|
@dagger.assisted.AssistedFactory
|
||||||
interface AssistedFactory {
|
interface AssistedFactory {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import androidx.core.database.getBlobOrNull
|
||||||
import androidx.core.database.getLongOrNull
|
import androidx.core.database.getLongOrNull
|
||||||
import androidx.sqlite.db.transaction
|
import androidx.sqlite.db.transaction
|
||||||
import org.session.libsignal.protos.SignalServiceProtos.SharedConfigMessage
|
import org.session.libsignal.protos.SignalServiceProtos.SharedConfigMessage
|
||||||
|
import org.session.libsignal.utilities.SessionId
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
|
|
||||||
class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(context, helper) {
|
class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(context, helper) {
|
||||||
|
@ -22,6 +23,7 @@ class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(co
|
||||||
"CREATE TABLE $TABLE_NAME ($VARIANT TEXT NOT NULL, $PUBKEY TEXT NOT NULL, $DATA BLOB, $TIMESTAMP INTEGER NOT NULL DEFAULT 0, PRIMARY KEY($VARIANT, $PUBKEY));"
|
"CREATE TABLE $TABLE_NAME ($VARIANT TEXT NOT NULL, $PUBKEY TEXT NOT NULL, $DATA BLOB, $TIMESTAMP INTEGER NOT NULL DEFAULT 0, PRIMARY KEY($VARIANT, $PUBKEY));"
|
||||||
|
|
||||||
private const val VARIANT_AND_PUBKEY_WHERE = "$VARIANT = ? AND $PUBKEY = ?"
|
private const val VARIANT_AND_PUBKEY_WHERE = "$VARIANT = ? AND $PUBKEY = ?"
|
||||||
|
private const val VARIANT_IN_AND_PUBKEY_WHERE = "$VARIANT in (?) AND $PUBKEY = ?"
|
||||||
|
|
||||||
val KEYS_VARIANT = SharedConfigMessage.Kind.ENCRYPTION_KEYS.name
|
val KEYS_VARIANT = SharedConfigMessage.Kind.ENCRYPTION_KEYS.name
|
||||||
val INFO_VARIANT = SharedConfigMessage.Kind.CLOSED_GROUP_INFO.name
|
val INFO_VARIANT = SharedConfigMessage.Kind.CLOSED_GROUP_INFO.name
|
||||||
|
@ -39,6 +41,16 @@ class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(co
|
||||||
db.insertOrUpdate(TABLE_NAME, contentValues, VARIANT_AND_PUBKEY_WHERE, arrayOf(variant, publicKey))
|
db.insertOrUpdate(TABLE_NAME, contentValues, VARIANT_AND_PUBKEY_WHERE, arrayOf(variant, publicKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteGroupConfigs(closedGroupId: SessionId) {
|
||||||
|
val db = writableDatabase
|
||||||
|
db.transaction {
|
||||||
|
val variants = arrayOf(KEYS_VARIANT, INFO_VARIANT, MEMBER_VARIANT)
|
||||||
|
db.delete(TABLE_NAME, VARIANT_IN_AND_PUBKEY_WHERE,
|
||||||
|
arrayOf(variants, closedGroupId.hexString())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun storeGroupConfigs(publicKey: String, keysConfig: ByteArray, infoConfig: ByteArray, memberConfig: ByteArray, timestamp: Long) {
|
fun storeGroupConfigs(publicKey: String, keysConfig: ByteArray, infoConfig: ByteArray, memberConfig: ByteArray, timestamp: Long) {
|
||||||
val db = writableDatabase
|
val db = writableDatabase
|
||||||
db.transaction {
|
db.transaction {
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.session.libsession.messaging.jobs.ConfigurationSyncJob
|
||||||
import org.session.libsession.messaging.jobs.GroupAvatarDownloadJob
|
import org.session.libsession.messaging.jobs.GroupAvatarDownloadJob
|
||||||
import org.session.libsession.messaging.jobs.InviteContactsJob
|
import org.session.libsession.messaging.jobs.InviteContactsJob
|
||||||
import org.session.libsession.messaging.jobs.Job
|
import org.session.libsession.messaging.jobs.Job
|
||||||
|
import org.session.libsession.messaging.jobs.JobDelegate
|
||||||
import org.session.libsession.messaging.jobs.JobQueue
|
import org.session.libsession.messaging.jobs.JobQueue
|
||||||
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
||||||
import org.session.libsession.messaging.jobs.MessageSendJob
|
import org.session.libsession.messaging.jobs.MessageSendJob
|
||||||
|
@ -87,6 +88,7 @@ import org.session.libsignal.messages.SignalServiceGroup
|
||||||
import org.session.libsignal.protos.SignalServiceProtos.DataMessage
|
import org.session.libsignal.protos.SignalServiceProtos.DataMessage
|
||||||
import org.session.libsignal.protos.SignalServiceProtos.DataMessage.GroupUpdateInviteResponseMessage
|
import org.session.libsignal.protos.SignalServiceProtos.DataMessage.GroupUpdateInviteResponseMessage
|
||||||
import org.session.libsignal.protos.SignalServiceProtos.DataMessage.GroupUpdateMemberChangeMessage
|
import org.session.libsignal.protos.SignalServiceProtos.DataMessage.GroupUpdateMemberChangeMessage
|
||||||
|
import org.session.libsignal.protos.SignalServiceProtos.DataMessage.GroupUpdateMessage
|
||||||
import org.session.libsignal.utilities.Base64
|
import org.session.libsignal.utilities.Base64
|
||||||
import org.session.libsignal.utilities.Hex
|
import org.session.libsignal.utilities.Hex
|
||||||
import org.session.libsignal.utilities.IdPrefix
|
import org.session.libsignal.utilities.IdPrefix
|
||||||
|
@ -185,7 +187,7 @@ open class Storage(
|
||||||
// these should be removed in the group leave / handling new configs
|
// these should be removed in the group leave / handling new configs
|
||||||
Log.w("Loki", "Thread delete called for open group address, expecting to be handled elsewhere")
|
Log.w("Loki", "Thread delete called for open group address, expecting to be handled elsewhere")
|
||||||
} else if (address.isClosedGroup) {
|
} else if (address.isClosedGroup) {
|
||||||
TODO("add the thread deleted checks for new closed groups")
|
Log.w("Loki", "Thread delete called for closed group address, expecting to be handled elsewhere")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// non-standard contact prefixes: 15, 00 etc shouldn't be stored in config
|
// non-standard contact prefixes: 15, 00 etc shouldn't be stored in config
|
||||||
|
@ -1345,10 +1347,24 @@ open class Storage(
|
||||||
response.get()
|
response.get()
|
||||||
|
|
||||||
val newConfigSync = ConfigurationSyncJob(destination)
|
val newConfigSync = ConfigurationSyncJob(destination)
|
||||||
|
var exception: Exception? = null
|
||||||
|
val delegate = object: JobDelegate {
|
||||||
|
override fun handleJobSucceeded(job: Job, dispatcherName: String) {}
|
||||||
|
override fun handleJobFailed(job: Job, dispatcherName: String, error: Exception) { exception = error }
|
||||||
|
override fun handleJobFailedPermanently(
|
||||||
|
job: Job,
|
||||||
|
dispatcherName: String,
|
||||||
|
error: Exception
|
||||||
|
) { exception = error }
|
||||||
|
}
|
||||||
|
newConfigSync.delegate = delegate
|
||||||
runBlocking {
|
runBlocking {
|
||||||
newConfigSync.execute("updating-members")
|
newConfigSync.execute("updating-members")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rethrow failure
|
||||||
|
exception?.let { throw it }
|
||||||
|
|
||||||
configFactory.saveGroupConfigs(keysConfig, infoConfig, membersConfig)
|
configFactory.saveGroupConfigs(keysConfig, infoConfig, membersConfig)
|
||||||
|
|
||||||
val job = InviteContactsJob(groupSessionId, filteredMembers.toTypedArray())
|
val job = InviteContactsJob(groupSessionId, filteredMembers.toTypedArray())
|
||||||
|
@ -1366,8 +1382,9 @@ open class Storage(
|
||||||
.setAdminSignature(ByteString.copyFrom(signature))
|
.setAdminSignature(ByteString.copyFrom(signature))
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
).apply { this.sentTimestamp = timestamp }
|
||||||
MessageSender.send(updatedMessage, fromSerialized(groupSessionId))
|
MessageSender.send(updatedMessage, fromSerialized(groupSessionId))
|
||||||
|
insertGroupInfoChange(updatedMessage, sessionId)
|
||||||
infoConfig.free()
|
infoConfig.free()
|
||||||
membersConfig.free()
|
membersConfig.free()
|
||||||
keysConfig.free()
|
keysConfig.free()
|
||||||
|
@ -1383,12 +1400,12 @@ open class Storage(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun insertGroupInfoChange(message: GroupUpdated, closedGroup: SessionId) {
|
override fun insertGroupInfoChange(message: GroupUpdated, closedGroup: SessionId) {
|
||||||
val sentTimestamp = message.sentTimestamp ?: return
|
val sentTimestamp = message.sentTimestamp ?: SnodeAPI.nowWithOffset
|
||||||
val senderPublicKey = message.sender ?: return
|
val senderPublicKey = message.sender
|
||||||
val userPublicKey = getUserPublicKey()!!
|
val userPublicKey = getUserPublicKey()!!
|
||||||
val updateData = UpdateMessageData.buildGroupUpdate(message)?.toJSON() ?: return
|
val updateData = UpdateMessageData.buildGroupUpdate(message)?.toJSON() ?: return
|
||||||
|
|
||||||
if (senderPublicKey == userPublicKey) {
|
if (senderPublicKey == null || senderPublicKey == userPublicKey) {
|
||||||
val recipient = Recipient.from(context, fromSerialized(closedGroup.hexString()), false)
|
val recipient = Recipient.from(context, fromSerialized(closedGroup.hexString()), false)
|
||||||
val infoMessage = OutgoingGroupMediaMessage(recipient, updateData, closedGroup.hexString(), null, sentTimestamp, 0, true, null, listOf(), listOf())
|
val infoMessage = OutgoingGroupMediaMessage(recipient, updateData, closedGroup.hexString(), null, sentTimestamp, 0, true, null, listOf(), listOf())
|
||||||
val mmsDB = DatabaseComponent.get(context).mmsDatabase()
|
val mmsDB = DatabaseComponent.get(context).mmsDatabase()
|
||||||
|
@ -1451,8 +1468,11 @@ open class Storage(
|
||||||
.setAdminSignature(ByteString.copyFrom(signature))
|
.setAdminSignature(ByteString.copyFrom(signature))
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
).apply { sentTimestamp = timestamp }
|
).apply {
|
||||||
|
sentTimestamp = timestamp
|
||||||
|
}
|
||||||
MessageSender.send(message, fromSerialized(groupSessionId))
|
MessageSender.send(message, fromSerialized(groupSessionId))
|
||||||
|
insertGroupInfoChange(message, closedGroupId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handlePromoted(keyPair: KeyPair) {
|
override fun handlePromoted(keyPair: KeyPair) {
|
||||||
|
@ -1480,6 +1500,27 @@ open class Storage(
|
||||||
keys.free()
|
keys.free()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun leaveGroup(groupSessionId: String) {
|
||||||
|
val closedGroupId = SessionId.from(groupSessionId)
|
||||||
|
val message = GroupUpdated(
|
||||||
|
GroupUpdateMessage.newBuilder()
|
||||||
|
.setMemberLeftMessage(DataMessage.GroupUpdateMemberLeftMessage.getDefaultInstance())
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
MessageSender.sendNonDurably(message, fromSerialized(groupSessionId), false).get()
|
||||||
|
pollerFactory.pollerFor(closedGroupId)?.stop()
|
||||||
|
// TODO: unsub from pushes
|
||||||
|
getThreadId(fromSerialized(groupSessionId))?.let { threadId ->
|
||||||
|
deleteConversation(threadId)
|
||||||
|
}
|
||||||
|
configFactory.removeGroup(closedGroupId)
|
||||||
|
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("ClosedGroup", "Failed to send leave group message")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -402,6 +402,12 @@ class ConfigFactory(
|
||||||
configDatabase.storeGroupConfigs(pubKey, groupKeys.dump(), groupInfo.dump(), groupMembers.dump(), timestamp)
|
configDatabase.storeGroupConfigs(pubKey, groupKeys.dump(), groupInfo.dump(), groupMembers.dump(), timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun removeGroup(closedGroupId: SessionId) {
|
||||||
|
val groups = userGroups ?: return
|
||||||
|
groups.eraseClosedGroup(closedGroupId.hexString())
|
||||||
|
configDatabase.deleteGroupConfigs(closedGroupId)
|
||||||
|
}
|
||||||
|
|
||||||
override fun scheduleUpdate(destination: Destination) {
|
override fun scheduleUpdate(destination: Destination) {
|
||||||
// there's probably a better way to do this
|
// there's probably a better way to do this
|
||||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(destination)
|
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(destination)
|
||||||
|
|
|
@ -1079,6 +1079,5 @@
|
||||||
<string name="media_overview_activity__clear_media">Clear All</string>
|
<string name="media_overview_activity__clear_media">Clear All</string>
|
||||||
<string name="activity_create_closed_group_select_contacts">Select Contacts</string>
|
<string name="activity_create_closed_group_select_contacts">Select Contacts</string>
|
||||||
<string name="activity_create_closed_group_add_account_or_ons">Add Account ID or ONS</string>
|
<string name="activity_create_closed_group_add_account_or_ons">Add Account ID or ONS</string>
|
||||||
<string name="closed_group_update_control__single_joined"></string>
|
<string name="conversation_settings_leave_group_name">Are you sure you want to leave %1$s?</string>
|
||||||
<string name="closed_group_update_control__two_joined"></string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -171,6 +171,7 @@ interface StorageProtocol {
|
||||||
fun insertGroupInfoChange(message: GroupUpdated, closedGroup: SessionId)
|
fun insertGroupInfoChange(message: GroupUpdated, closedGroup: SessionId)
|
||||||
fun promoteMember(groupSessionId: String, promotions: Array<String>)
|
fun promoteMember(groupSessionId: String, promotions: Array<String>)
|
||||||
fun handlePromoted(keyPair: KeyPair)
|
fun handlePromoted(keyPair: KeyPair)
|
||||||
|
fun leaveGroup(groupSessionId: String)
|
||||||
|
|
||||||
// Groups
|
// Groups
|
||||||
fun getAllGroups(includeInactive: Boolean): List<GroupRecord>
|
fun getAllGroups(includeInactive: Boolean): List<GroupRecord>
|
||||||
|
|
|
@ -10,6 +10,8 @@ class GroupUpdated(val inner: GroupUpdateMessage): ControlMessage() {
|
||||||
return true // TODO: add the validation here
|
return true // TODO: add the validation here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSelfSendValid: Boolean = true
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromProto(message: Content): GroupUpdated? =
|
fun fromProto(message: Content): GroupUpdated? =
|
||||||
if (message.hasDataMessage() && message.dataMessage.hasGroupUpdateMessage())
|
if (message.hasDataMessage() && message.dataMessage.hasGroupUpdateMessage())
|
||||||
|
|
|
@ -77,52 +77,54 @@ object UpdateMessageBuilder {
|
||||||
val number = updateData.sessionIds.size
|
val number = updateData.sessionIds.size
|
||||||
if (number == 1) context.getString(
|
if (number == 1) context.getString(
|
||||||
R.string.ConversationItem_group_member_added_single,
|
R.string.ConversationItem_group_member_added_single,
|
||||||
getSenderName(updateData.sessionIds.first())
|
context.youOrSender(updateData.sessionIds.first())
|
||||||
)
|
)
|
||||||
else if (number == 2) context.getString(
|
else if (number == 2) context.getString(
|
||||||
R.string.ConversationItem_group_member_added_two,
|
R.string.ConversationItem_group_member_added_two,
|
||||||
getSenderName(updateData.sessionIds.first()),
|
context.youOrSender(updateData.sessionIds.first()),
|
||||||
getSenderName(updateData.sessionIds.last())
|
context.youOrSender(updateData.sessionIds.last())
|
||||||
)
|
)
|
||||||
else context.getString(
|
else context.getString(
|
||||||
R.string.ConversationItem_group_member_added_multiple,
|
R.string.ConversationItem_group_member_added_multiple,
|
||||||
getSenderName(updateData.sessionIds.first()),
|
context.youOrSender(updateData.sessionIds.first()),
|
||||||
updateData.sessionIds.size - 1
|
updateData.sessionIds.size - 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
UpdateMessageData.MemberUpdateType.PROMOTED -> {
|
UpdateMessageData.MemberUpdateType.PROMOTED -> {
|
||||||
val number = updateData.sessionIds.size
|
when (updateData.sessionIds.size) {
|
||||||
if (number == 1) context.getString(
|
1 -> context.getString(
|
||||||
R.string.ConversationItem_group_member_promoted_single,
|
R.string.ConversationItem_group_member_promoted_single,
|
||||||
getSenderName(updateData.sessionIds.first())
|
context.youOrSender(updateData.sessionIds.first())
|
||||||
)
|
)
|
||||||
else if (number == 2) context.getString(
|
2 -> context.getString(
|
||||||
R.string.ConversationItem_group_member_promoted_two,
|
R.string.ConversationItem_group_member_promoted_two,
|
||||||
getSenderName(updateData.sessionIds.first()),
|
context.youOrSender(updateData.sessionIds.first()),
|
||||||
getSenderName(updateData.sessionIds.last())
|
context.youOrSender(updateData.sessionIds.last())
|
||||||
)
|
)
|
||||||
else context.getString(
|
else -> context.getString(
|
||||||
R.string.ConversationItem_group_member_promoted_multiple,
|
R.string.ConversationItem_group_member_promoted_multiple,
|
||||||
getSenderName(updateData.sessionIds.first()),
|
context.youOrSender(updateData.sessionIds.first()),
|
||||||
updateData.sessionIds.size - 1
|
updateData.sessionIds.size - 1
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
UpdateMessageData.MemberUpdateType.REMOVED -> {
|
UpdateMessageData.MemberUpdateType.REMOVED -> {
|
||||||
val number = updateData.sessionIds.size
|
when (updateData.sessionIds.size) {
|
||||||
if (number == 1) context.getString(
|
1 -> context.getString(
|
||||||
R.string.ConversationItem_group_member_removed_single,
|
R.string.ConversationItem_group_member_removed_single,
|
||||||
getSenderName(updateData.sessionIds.first())
|
context.youOrSender(updateData.sessionIds.first())
|
||||||
)
|
)
|
||||||
else if (number == 2) context.getString(
|
2 -> context.getString(
|
||||||
R.string.ConversationItem_group_member_removed_two,
|
R.string.ConversationItem_group_member_removed_two,
|
||||||
getSenderName(updateData.sessionIds.first()),
|
context.youOrSender(updateData.sessionIds.first()),
|
||||||
getSenderName(updateData.sessionIds.last())
|
context.youOrSender(updateData.sessionIds.last())
|
||||||
)
|
)
|
||||||
else context.getString(
|
else -> context.getString(
|
||||||
R.string.ConversationItem_group_member_removed_multiple,
|
R.string.ConversationItem_group_member_removed_multiple,
|
||||||
getSenderName(updateData.sessionIds.first()),
|
context.youOrSender(updateData.sessionIds.first()),
|
||||||
updateData.sessionIds.size - 1
|
updateData.sessionIds.size - 1
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
null -> ""
|
null -> ""
|
||||||
}
|
}
|
||||||
|
@ -131,6 +133,8 @@ object UpdateMessageBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Context.youOrSender(sessionId: String) = if (storage.getUserPublicKey() == sessionId) getString(R.string.MessageRecord_you) else getSenderName(sessionId)
|
||||||
|
|
||||||
fun buildExpirationTimerMessage(context: Context, duration: Long, senderId: String? = null, isOutgoing: Boolean = false): String {
|
fun buildExpirationTimerMessage(context: Context, duration: Long, senderId: String? = null, isOutgoing: Boolean = false): String {
|
||||||
if (!isOutgoing && senderId == null) return ""
|
if (!isOutgoing && senderId == null) return ""
|
||||||
val senderName: String = if (!isOutgoing) {
|
val senderName: String = if (!isOutgoing) {
|
||||||
|
|
|
@ -35,6 +35,7 @@ interface ConfigFactoryProtocol {
|
||||||
groupInfo: GroupInfoConfig,
|
groupInfo: GroupInfoConfig,
|
||||||
groupMembers: GroupMembersConfig
|
groupMembers: GroupMembersConfig
|
||||||
)
|
)
|
||||||
|
fun removeGroup(closedGroupId: SessionId)
|
||||||
|
|
||||||
fun scheduleUpdate(destination: Destination)
|
fun scheduleUpdate(destination: Destination)
|
||||||
fun constructGroupKeysConfig(
|
fun constructGroupKeysConfig(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.session.libsession.utilities
|
package org.session.libsession.utilities
|
||||||
|
|
||||||
fun truncateIdForDisplay(id: String): String =
|
fun truncateIdForDisplay(id: String): String =
|
||||||
id.takeIf { it.length > 8 }?.apply{ "${take(4)}…${takeLast(4)}" } ?: id
|
id.takeIf { it.length > 8 }?.run{ "${take(4)}…${takeLast(4)}" } ?: id
|
||||||
|
|
Loading…
Reference in New Issue