diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 1ea8019522..26c1fefcf8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -912,7 +912,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe } // show or hide loading indicator - binding.loader.isVisible = uiState.showLoader + binding.loader.isVisible = state.showLoader } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 05f0ac0558..95b37e641d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -303,9 +303,8 @@ open class Storage @Inject constructor( override fun deleteMessagesByHash(threadId: Long, hashes: List) { val info = lokiMessageDatabase.getSendersForHashes(threadId, hashes.toSet()) - // TODO: no idea if we need to server delete this for ((serverHash, sender, messageIdToDelete, isSms) in info) { - messageDataProvider.updateMessageAsDeleted(messageIdToDelete, isSms) + messageDataProvider.deleteMessage(messageIdToDelete, isSms) if (!messageDataProvider.isOutgoingMessage(messageIdToDelete)) { notificationManager.updateNotification(context) } @@ -662,9 +661,8 @@ open class Storage @Inject constructor( } override fun getMessageType(timestamp: Long, author: String): MessageType? { - val database = DatabaseComponent.get(context).mmsSmsDatabase() val address = fromSerialized(author) - return database.getMessageFor(timestamp, address)?.individualRecipient?.getType() + return mmsSmsDatabase.getMessageFor(timestamp, address)?.individualRecipient?.getType() } override fun updateSentTimestamp( @@ -1817,7 +1815,7 @@ open class Storage @Inject constructor( } override fun deleteReactions(messageIds: List, mms: Boolean) { - DatabaseComponent.get(context).reactionDatabase().deleteMessageReactions( + reactionDatabase.deleteMessageReactions( messageIds.map { MessageId(it, mms) } ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/EditGroupViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/EditGroupViewModel.kt index b5c1abba61..7150e2e7f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/EditGroupViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/EditGroupViewModel.kt @@ -59,7 +59,6 @@ class EditGroupViewModel @AssistedInject constructor( val editingName: StateFlow get() = mutableEditingName // Output: the source-of-truth group information. Other states are derived from this. - @OptIn(ExperimentalCoroutinesApi::class) private val groupInfo: StateFlow>?> = combine( configFactory.configUpdateNotifications @@ -77,20 +76,17 @@ class EditGroupViewModel @AssistedInject constructor( ?: return@withContext null val members = storage.getMembers(groupId.hexString) - .asSequence() - .filter { !it.removed } - .mapTo(arrayListOf()) { member -> - createGroupMember( - member = member, - myAccountId = currentUserId, - amIAdmin = displayInfo.isUserAdmin, - pendingState = pending[AccountId(member.sessionId)] - ) - } - + .filterTo(mutableListOf()) { !it.removed } sortMembers(members, currentUserId) - displayInfo to members + displayInfo to members.map { member -> + createGroupMember( + member = member, + myAccountId = currentUserId, + amIAdmin = displayInfo.isUserAdmin, + pendingState = pending[AccountId(member.sessionId)] + ) + } } }.stateIn(viewModelScope, SharingStarted.Eagerly, null) @@ -181,16 +177,16 @@ class EditGroupViewModel @AssistedInject constructor( ) } - private fun sortMembers(members: MutableList, currentUserId: String) { - // Order or members: - // 1. Current user always comes first - // 2. Then sort by name - // 3. Then sort by account ID + private fun sortMembers(members: MutableList, currentUserId: String) { members.sortWith( compareBy( - { it.accountId != currentUserId }, - { it.name }, - { it.accountId } + { !it.inviteFailed }, // Failed invite comes first (as false value is less than true) + { memberPendingState.value[AccountId(it.sessionId)] != MemberPendingState.Inviting }, // "Sending invite" comes first + { !it.invitePending }, // "Invite sent" comes first + { !it.isAdminOrBeingPromoted }, // Admins come first + { it.sessionId != currentUserId }, // Being myself comes first + { it.name }, // Sort by name + { it.sessionId } // Last resort: sort by account ID ) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt index 59aaaceb52..d3edeceb8f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2Impl.kt @@ -326,7 +326,7 @@ class GroupManagerV2Impl @Inject constructor( OwnedSwarmAuth.ofClosedGroup(groupAccountId, it) } ?: return@withContext - SnodeAPI.deleteMessage(groupAccountId.hexString, groupAdminAuth, messagesToDelete).await() + SnodeAPI.deleteMessage(groupAccountId.hexString, groupAdminAuth, messagesToDelete) } override suspend fun handleMemberLeft(message: GroupUpdated, group: AccountId) { @@ -831,7 +831,7 @@ class GroupManagerV2Impl @Inject constructor( publicKey = groupId.hexString, swarmAuth = OwnedSwarmAuth.ofClosedGroup(groupId, adminKey), serverHashes = messageHashes - ).await() + ) } // Construct a message to ask members to delete the messages, sign if we are admin, then send @@ -918,7 +918,7 @@ class GroupManagerV2Impl @Inject constructor( groupId.hexString, OwnedSwarmAuth.ofClosedGroup(groupId, adminKey), hashes - ).await() + ) } // The non-admin user shouldn't be able to delete other user's messages so we will diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt index 3a24b0375c..9cf36cdee8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt @@ -90,7 +90,7 @@ class PushReceiver @Inject constructor( } else -> { - Log.w(TAG, "Received a push notification with an unknown namespace: ${metadata.namespace}") + Log.w(TAG, "Received a push notification with an unknown namespace: ${pushData.metadata.namespace}") return } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt index 92e46e2184..49e8e083cc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt @@ -30,8 +30,6 @@ import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.utilities.AccountId -import org.session.libsignal.utilities.Log -import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.database.DatabaseContentProviders import org.thoughtcrime.securesms.database.DraftDatabase import org.thoughtcrime.securesms.database.LokiMessageDatabase @@ -46,8 +44,6 @@ import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.dependencies.ConfigFactory import javax.inject.Inject -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine interface ConversationRepository { fun maybeGetRecipientForThreadId(threadId: Long): Recipient? @@ -81,7 +77,6 @@ interface ConversationRepository { messages: Set ) - fun buildUnsendRequest(recipient: Recipient, message: MessageRecord): UnsendRequest? suspend fun banUser(threadId: Long, recipient: Recipient): Result suspend fun banAndDeleteAll(threadId: Long, recipient: Recipient): Result suspend fun deleteThread(threadId: Long): Result @@ -280,21 +275,24 @@ class DefaultConversationRepository @Inject constructor( // delete the messages remotely val publicKey = recipient.address.serialize() val userAddress: Address? = textSecurePreferences.getLocalNumber()?.let { Address.fromSerialized(it) } + val userAuth = requireNotNull(storage.userAuth) { + "User auth is required to delete messages remotely" + } messages.forEach { message -> // delete from swarm messageDataProvider.getServerHashForMessage(message.id, message.isMms) ?.let { serverHash -> - SnodeAPI.deleteMessage(publicKey, listOf(serverHash)) + SnodeAPI.deleteMessage(publicKey, userAuth, listOf(serverHash)) } // send an UnsendRequest to user's swarm - buildUnsendRequest(recipient, message)?.let { unsendRequest -> + buildUnsendRequest(message).let { unsendRequest -> userAddress?.let { MessageSender.send(unsendRequest, it) } } // send an UnsendRequest to recipient's swarm - buildUnsendRequest(recipient, message)?.let { unsendRequest -> + buildUnsendRequest(message).let { unsendRequest -> MessageSender.send(unsendRequest, recipient.address) } } @@ -304,12 +302,12 @@ class DefaultConversationRepository @Inject constructor( recipient: Recipient, messages: Set ) { - if (recipient.isClosedGroupRecipient) { + if (recipient.isLegacyClosedGroupRecipient) { val publicKey = recipient.address messages.forEach { message -> // send an UnsendRequest to group's swarm - buildUnsendRequest(recipient, message)?.let { unsendRequest -> + buildUnsendRequest(message).let { unsendRequest -> MessageSender.send(unsendRequest, publicKey) } } @@ -324,16 +322,19 @@ class DefaultConversationRepository @Inject constructor( // delete the messages remotely val publicKey = recipient.address.serialize() val userAddress: Address? = textSecurePreferences.getLocalNumber()?.let { Address.fromSerialized(it) } + val userAuth = requireNotNull(storage.userAuth) { + "User auth is required to delete messages remotely" + } messages.forEach { message -> // delete from swarm messageDataProvider.getServerHashForMessage(message.id, message.isMms) ?.let { serverHash -> - SnodeAPI.deleteMessage(publicKey, listOf(serverHash)) + SnodeAPI.deleteMessage(publicKey, userAuth, listOf(serverHash)) } // send an UnsendRequest to user's swarm - buildUnsendRequest(recipient, message)?.let { unsendRequest -> + buildUnsendRequest(message).let { unsendRequest -> userAddress?.let { MessageSender.send(unsendRequest, it) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColors.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColors.kt index b5b6aa6c8b..233f3472aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColors.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColors.kt @@ -18,7 +18,6 @@ interface ThemeColors { // properties to override for each theme val isLight: Boolean val primary: Color - val warning: Color val textAlert: Color val danger: Color val warning: Color diff --git a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt index d02d9f35d1..59a56a5736 100644 --- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt @@ -3,17 +3,13 @@ package org.session.libsession.database import android.content.Context import android.net.Uri import com.goterl.lazysodium.utils.KeyPair -import network.loki.messenger.libsession_util.Config import network.loki.messenger.libsession_util.util.GroupDisplayInfo -import network.loki.messenger.libsession_util.util.GroupInfo -import nl.komponents.kovenant.Promise import org.session.libsession.messaging.BlindedIdMapping import org.session.libsession.messaging.calls.CallMessageType import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.jobs.AttachmentUploadJob import org.session.libsession.messaging.jobs.Job import org.session.libsession.messaging.jobs.MessageSendJob -import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.ExpirationConfiguration import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.messages.control.ConfigurationMessage @@ -34,14 +30,13 @@ import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.utilities.UpdateMessageData import org.session.libsession.utilities.Address import org.session.libsession.utilities.GroupRecord +import org.session.libsession.utilities.recipients.MessageType import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient.RecipientSettings -import org.session.libsession.utilities.recipients.MessageType import org.session.libsignal.crypto.ecc.ECKeyPair import org.session.libsignal.messages.SignalServiceAttachmentPointer import org.session.libsignal.messages.SignalServiceGroup import org.session.libsignal.utilities.AccountId -import org.session.libsignal.utilities.guava.Optional import network.loki.messenger.libsession_util.util.Contact as LibSessionContact import network.loki.messenger.libsession_util.util.GroupMember as LibSessionGroupMember diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt index 9b3b95cf01..41db0dfa3c 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt @@ -292,8 +292,9 @@ fun MessageReceiver.handleUnsendRequest(message: UnsendRequest): Long? { messageDataProvider.getServerHashForMessage(messageIdToDelete, mms)?.let { serverHash -> GlobalScope.launch(Dispatchers.IO) { // using GlobalScope as we are slowly migrating to coroutines but we can't migrate everything at once try { - SnodeAPI.deleteMessage(author, listOf(serverHash)) + SnodeAPI.deleteMessage(author, userAuth, listOf(serverHash)) } catch (e: Exception) { + Log.e("Loki", "Failed to delete message", e) } } } diff --git a/libsession/src/main/java/org/session/libsession/utilities/recipients/MessageType.kt b/libsession/src/main/java/org/session/libsession/utilities/recipients/MessageType.kt index de9f894e48..be089b8042 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/recipients/MessageType.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/recipients/MessageType.kt @@ -8,7 +8,7 @@ fun Recipient.getType(): MessageType = when{ isCommunityRecipient -> MessageType.COMMUNITY isLocalNumber -> MessageType.NOTE_TO_SELF - isClosedGroupRecipient -> MessageType.LEGACY_GROUP //todo GROUPS V2 this property will change for groups v2. Check for legacyGroup here - //isXXXXX -> RecipientType.GROUPS_V2 //todo GROUPS V2 this property will change for groups v2. Check for legacyGroup here + isLegacyClosedGroupRecipient -> MessageType.LEGACY_GROUP + isClosedGroupV2Recipient -> MessageType.GROUPS_V2 else -> MessageType.ONE_ON_ONE } \ No newline at end of file