fix: remove user notifications for leaving group to prevent synced device issues, don't create thread in messages for new closed groups, includei nactive groups in the deletion queries for merging group configs

This commit is contained in:
0x330a 2023-05-05 16:05:43 +10:00
parent a1ee4ccde6
commit d4d4f81c36
12 changed files with 17 additions and 24 deletions

View File

@ -321,7 +321,7 @@ object ConversationMenuHelper {
} }
try { try {
if (isClosedGroup) { if (isClosedGroup) {
MessageSender.leave(groupPublicKey!!, true) MessageSender.leave(groupPublicKey!!, notifyUser = false)
} else { } else {
Toast.makeText(context, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show() Toast.makeText(context, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show()
} }

View File

@ -133,12 +133,12 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
return new Reader(cursor); return new Reader(cursor);
} }
public List<GroupRecord> getAllGroups() { public List<GroupRecord> getAllGroups(boolean includeInactive) {
Reader reader = getGroups(); Reader reader = getGroups();
GroupRecord record; GroupRecord record;
List<GroupRecord> groups = new LinkedList<>(); List<GroupRecord> groups = new LinkedList<>();
while ((record = reader.getNext()) != null) { while ((record = reader.getNext()) != null) {
if (record.isActive()) { groups.add(record); } if (record.isActive() || includeInactive) { groups.add(record); }
} }
reader.close(); reader.close();
return groups; return groups;

View File

@ -497,7 +497,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
val toAddCommunities = communities.filter { it.community.fullUrl() !in existingCommunities.map { it.value.joinURL } } val toAddCommunities = communities.filter { it.community.fullUrl() !in existingCommunities.map { it.value.joinURL } }
val existingJoinUrls = existingCommunities.values.map { it.joinURL } val existingJoinUrls = existingCommunities.values.map { it.joinURL }
val existingClosedGroups = getAllGroups().filter { it.isClosedGroup } val existingClosedGroups = getAllGroups(includeInactive = true).filter { it.isClosedGroup }
val lgcIds = lgc.map { it.sessionId } val lgcIds = lgc.map { it.sessionId }
val toDeleteClosedGroups = existingClosedGroups.filter { group -> val toDeleteClosedGroups = existingClosedGroups.filter { group ->
GroupUtil.doubleDecodeGroupId(group.encodedId) !in lgcIds GroupUtil.doubleDecodeGroupId(group.encodedId) !in lgcIds
@ -513,6 +513,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
if (threadId == null) { if (threadId == null) {
Log.w("Loki-DBG", "Existing group had no thread to delete") Log.w("Loki-DBG", "Existing group had no thread to delete")
} else { } else {
Log.d("Loki-DBG", "Deleting group for thread $threadId")
ClosedGroupManager.silentlyRemoveGroup(context,threadId,GroupUtil.doubleDecodeGroupId(deleteGroup.encodedId), deleteGroup.encodedId, localUserPublicKey, delete = true) ClosedGroupManager.silentlyRemoveGroup(context,threadId,GroupUtil.doubleDecodeGroupId(deleteGroup.encodedId), deleteGroup.encodedId, localUserPublicKey, delete = true)
} }
} }
@ -954,8 +955,8 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
OpenGroupManager.updateOpenGroup(openGroup, context) OpenGroupManager.updateOpenGroup(openGroup, context)
} }
override fun getAllGroups(): List<GroupRecord> { override fun getAllGroups(includeInactive: Boolean): List<GroupRecord> {
return DatabaseComponent.get(context).groupDatabase().allGroups return DatabaseComponent.get(context).groupDatabase().getAllGroups(includeInactive)
} }
override fun addOpenGroup(urlAsString: String): OpenGroupApi.RoomInfo? { override fun addOpenGroup(urlAsString: String): OpenGroupApi.RoomInfo? {

View File

@ -535,7 +535,6 @@ public class ThreadDatabase extends Database {
// edge case where we set the last seen time for a conversation before it loads messages (joining community for example) // edge case where we set the last seen time for a conversation before it loads messages (joining community for example)
MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase(); MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase();
if (mmsSmsDatabase.getConversationCount(threadId) <= 0) return false; if (mmsSmsDatabase.getConversationCount(threadId) <= 0) return false;
Log.d("Loki-DBG", "setLastSeen "+threadId+" @ "+timestamp);
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
@ -559,7 +558,6 @@ public class ThreadDatabase extends Database {
db.execSQL(reflectUpdates, new Object[]{threadId}); db.execSQL(reflectUpdates, new Object[]{threadId});
db.setTransactionSuccessful(); db.setTransactionSuccessful();
db.endTransaction(); db.endTransaction();
Log.d("Loki-DBG", "Updated last seen to "+timestamp);
notifyConversationListListeners(); notifyConversationListListeners();
return true; return true;
} }
@ -800,7 +798,6 @@ public class ThreadDatabase extends Database {
public boolean markAllAsRead(long threadId, boolean isGroupRecipient, long lastSeenTime) { public boolean markAllAsRead(long threadId, boolean isGroupRecipient, long lastSeenTime) {
MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase(); MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase();
if (mmsSmsDatabase.getConversationCount(threadId) <= 0) return false; if (mmsSmsDatabase.getConversationCount(threadId) <= 0) return false;
Log.d("Loki-DBG", "markAllAsRead "+threadId+" @ "+lastSeenTime);
List<MarkedMessageInfo> messages = setRead(threadId, lastSeenTime); List<MarkedMessageInfo> messages = setRead(threadId, lastSeenTime);
if (isGroupRecipient) { if (isGroupRecipient) {
for (MarkedMessageInfo message: messages) { for (MarkedMessageInfo message: messages) {

View File

@ -16,11 +16,11 @@ object ClosedGroupManager {
fun silentlyRemoveGroup(context: Context, threadId: Long, groupPublicKey: String, groupID: String, userPublicKey: String, delete: Boolean = true) { fun silentlyRemoveGroup(context: Context, threadId: Long, groupPublicKey: String, groupID: String, userPublicKey: String, delete: Boolean = true) {
val storage = MessagingModuleConfiguration.shared.storage val storage = MessagingModuleConfiguration.shared.storage
// Mark the group as inactive
storage.setActive(groupID, false)
storage.removeClosedGroupPublicKey(groupPublicKey) storage.removeClosedGroupPublicKey(groupPublicKey)
// Remove the key pairs // Remove the key pairs
storage.removeAllClosedGroupEncryptionKeyPairs(groupPublicKey) storage.removeAllClosedGroupEncryptionKeyPairs(groupPublicKey)
// Mark the group as inactive
storage.setActive(groupID, false)
storage.removeMember(groupID, Address.fromSerialized(userPublicKey)) storage.removeMember(groupID, Address.fromSerialized(userPublicKey))
// Notify the PN server // Notify the PN server
PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey) PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey)

View File

@ -302,7 +302,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
isLoading = true isLoading = true
loaderContainer.fadeIn() loaderContainer.fadeIn()
val promise: Promise<Any, Exception> = if (!members.contains(Recipient.from(this, Address.fromSerialized(userPublicKey), false))) { val promise: Promise<Any, Exception> = if (!members.contains(Recipient.from(this, Address.fromSerialized(userPublicKey), false))) {
MessageSender.explicitLeave(groupPublicKey!!, true) MessageSender.explicitLeave(groupPublicKey!!, false)
} else { } else {
task { task {
if (hasNameChanged) { if (hasNameChanged) {

View File

@ -236,7 +236,7 @@ object ConfigurationMessageUtilities {
GroupInfo.CommunityGroupInfo(baseInfo, if (isPinned) 1 else 0) GroupInfo.CommunityGroupInfo(baseInfo, if (isPinned) 1 else 0)
} }
val allLgc = storage.getAllGroups().filter { it.isClosedGroup && it.isActive }.mapNotNull { group -> val allLgc = storage.getAllGroups(includeInactive = false).filter { it.isClosedGroup }.mapNotNull { group ->
val groupAddress = Address.fromSerialized(group.encodedId) val groupAddress = Address.fromSerialized(group.encodedId)
val groupPublicKey = GroupUtil.doubleDecodeGroupID(groupAddress.serialize()).toHexString() val groupPublicKey = GroupUtil.doubleDecodeGroupID(groupAddress.serialize()).toHexString()
val recipient = storage.getRecipientSettings(groupAddress) ?: return@mapNotNull null val recipient = storage.getRecipientSettings(groupAddress) ?: return@mapNotNull null

View File

@ -148,7 +148,7 @@ interface StorageProtocol {
fun setExpirationTimer(address: String, duration: Int) fun setExpirationTimer(address: String, duration: Int)
// Groups // Groups
fun getAllGroups(): List<GroupRecord> fun getAllGroups(includeInactive: Boolean): List<GroupRecord>
// Settings // Settings
fun setProfileSharing(address: Address, value: Boolean) fun setProfileSharing(address: Address, value: Boolean)

View File

@ -78,7 +78,7 @@ class BatchMessageReceiveJob(
else { // message is control message otherwise else { // message is control message otherwise
return when(message) { return when(message) {
is SharedConfigurationMessage -> false is SharedConfigurationMessage -> false
is ClosedGroupControlMessage -> message.kind is ClosedGroupControlMessage.Kind.New is ClosedGroupControlMessage -> false // message.kind is ClosedGroupControlMessage.Kind.New && !message.isSenderSelf
is DataExtractionNotification -> false is DataExtractionNotification -> false
is MessageRequestResponse -> false is MessageRequestResponse -> false
is ExpirationTimerUpdate -> false is ExpirationTimerUpdate -> false

View File

@ -122,7 +122,7 @@ class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups:
val displayName = TextSecurePreferences.getProfileName(context) ?: return null val displayName = TextSecurePreferences.getProfileName(context) ?: return null
val profilePicture = TextSecurePreferences.getProfilePictureURL(context) val profilePicture = TextSecurePreferences.getProfilePictureURL(context)
val profileKey = ProfileKeyUtil.getProfileKey(context) val profileKey = ProfileKeyUtil.getProfileKey(context)
val groups = storage.getAllGroups() val groups = storage.getAllGroups(includeInactive = false)
for (group in groups) { for (group in groups) {
if (group.isClosedGroup) { if (group.isClosedGroup) {
if (!group.members.contains(Address.fromSerialized(storage.getUserPublicKey()!!))) continue if (!group.members.contains(Address.fromSerialized(storage.getUserPublicKey()!!))) continue

View File

@ -426,7 +426,7 @@ object MessageSender {
@JvmStatic @JvmStatic
fun send(message: Message, address: Address) { fun send(message: Message, address: Address) {
val threadID = MessagingModuleConfiguration.shared.storage.getOrCreateThreadIdFor(address) val threadID = MessagingModuleConfiguration.shared.storage.getThreadId(address)
message.threadID = threadID message.threadID = threadID
val destination = Destination.from(address) val destination = Destination.from(address)
val job = MessageSendJob(message, destination) val job = MessageSendJob(message, destination)

View File

@ -459,7 +459,7 @@ private fun ClosedGroupControlMessage.getPublicKey(): String = kind!!.let { when
private fun MessageReceiver.handleNewClosedGroup(message: ClosedGroupControlMessage) { private fun MessageReceiver.handleNewClosedGroup(message: ClosedGroupControlMessage) {
val kind = message.kind!! as? ClosedGroupControlMessage.Kind.New ?: return val kind = message.kind!! as? ClosedGroupControlMessage.Kind.New ?: return
val recipient = Recipient.from(MessagingModuleConfiguration.shared.context, Address.fromSerialized(message.sender!!), false) val recipient = Recipient.from(MessagingModuleConfiguration.shared.context, Address.fromSerialized(message.sender!!), false)
if (!recipient.isApproved && !recipient.isLocalNumber) return if (!recipient.isApproved && !recipient.isLocalNumber) return Log.e("Loki", "not accepting new closed group from unapproved recipient")
val groupPublicKey = kind.publicKey.toByteArray().toHexString() val groupPublicKey = kind.publicKey.toByteArray().toHexString()
val members = kind.members.map { it.toByteArray().toHexString() } val members = kind.members.map { it.toByteArray().toHexString() }
val admins = kind.admins.map { it.toByteArray().toHexString() } val admins = kind.admins.map { it.toByteArray().toHexString() }
@ -758,12 +758,7 @@ private fun MessageReceiver.handleClosedGroupMemberLeft(message: ClosedGroupCont
storage.setZombieMembers(groupID, zombies.plus(senderPublicKey).map { Address.fromSerialized(it) }) storage.setZombieMembers(groupID, zombies.plus(senderPublicKey).map { Address.fromSerialized(it) })
} }
// Notify the user // Notify the user
if (userLeft) { if (!userLeft) {
val threadID = storage.getThreadId(Address.fromSerialized(groupID))
if (threadID != null) {
storage.insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.QUIT, name, members, admins, threadID, message.sentTimestamp!!)
}
} else {
storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.QUIT, name, members, admins, message.sentTimestamp!!) storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.QUIT, name, members, admins, message.sentTimestamp!!)
} }
} }