diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 115ed401a2..5f228f2ae5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -409,11 +409,11 @@ public class ConversationFragment extends Fragment } menu.findItem(R.id.menu_context_copy_public_key).setVisible(selectedMessageCount == 1 && !areAllSentByUser); menu.findItem(R.id.menu_context_reply).setVisible(selectedMessageCount == 1); - String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(getContext()); + String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext()); boolean userCanModerate = (isPublicChat && - (OpenGroupAPI.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer()) - || OpenGroupAPIV2.isUserModerator(userHexEncodedPublicKey, openGroupChat.getRoom(), openGroupChat.getServer())) + ((publicChat != null && OpenGroupAPI.isUserModerator(userHexEncodedPublicKey, publicChat.getChannel(), publicChat.getServer())) + || (openGroupChat != null && OpenGroupAPIV2.isUserModerator(userHexEncodedPublicKey, openGroupChat.getRoom(), openGroupChat.getServer()))) ); boolean isDeleteOptionVisible = !isPublicChat || (areAllSentByUser || userCanModerate); // allow banning if moderating a public chat and only one user's messages are selected @@ -515,6 +515,7 @@ public class ConversationFragment extends Fragment builder.setCancelable(true); PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); + OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId); builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() { @Override @@ -525,7 +526,7 @@ public class ConversationFragment extends Fragment { @Override protected Void doInBackground(MessageRecord... messageRecords) { - if (publicChat != null) { + if (publicChat != null || openGroupChat != null) { ArrayList serverIDs = new ArrayList<>(); ArrayList ignoredMessages = new ArrayList<>(); ArrayList failedMessages = new ArrayList<>(); @@ -561,7 +562,29 @@ public class ConversationFragment extends Fragment Log.w("Loki", "Couldn't delete message due to error: " + e.toString() + "."); return null; }); - } + } else if (openGroupChat != null) { + for (Long serverId : serverIDs) { + OpenGroupAPIV2 + .deleteMessage(serverId, openGroupChat.getRoom(), openGroupChat.getServer()) + .success(l -> { + for (MessageRecord messageRecord : messageRecords) { + Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); + if (serverID != null && serverID.equals(serverId)) { + if (messageRecord.isMms()) { + DatabaseFactory.getMmsDatabase(getContext()).delete(messageRecord.getId()); + } else { + DatabaseFactory.getSmsDatabase(getContext()).deleteMessage(messageRecord.getId()); + } + break; + } + } + return null; + }).fail(e->{ + Log.e("Loki", "Couldn't delete message due to error",e); + return null; + }); + } + } } else { for (MessageRecord messageRecord : messageRecords) { if (messageRecord.isMms()) { @@ -597,7 +620,8 @@ public class ConversationFragment extends Fragment builder.setTitle(R.string.ConversationFragment_ban_selected_user); builder.setCancelable(true); - PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); + final PublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getPublicChat(threadId); + final OpenGroupV2 openGroupChat = DatabaseFactory.getLokiThreadDatabase(getContext()).getOpenGroupChat(threadId); builder.setPositiveButton(R.string.ban, (dialog, which) -> { ConversationAdapter chatAdapter = getListAdapter(); @@ -616,9 +640,19 @@ public class ConversationFragment extends Fragment Log.d("Loki", "User banned"); return Unit.INSTANCE; }).fail(e -> { - Log.d("Loki", "Couldn't ban user due to error: " + e.toString() + "."); + Log.e("Loki", "Couldn't ban user due to error",e); return null; }); + } else if (openGroupChat != null) { + OpenGroupAPIV2 + .ban(userPublicKey, openGroupChat.getRoom(), openGroupChat.getServer()) + .success(l -> { + Log.d("Loki", "User banned"); + return Unit.INSTANCE; + }).fail(e -> { + Log.e("Loki", "Failed to ban user",e); + return null; + }); } else { Log.d("Loki", "Tried to ban user from a non-public chat"); } 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 c1c3479c2a..c716c564a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -580,7 +580,9 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, val mmsDb = DatabaseFactory.getMmsDatabase(context) val cursor = mmsDb.getMessage(mmsId) val reader = mmsDb.readerFor(cursor) - return reader.next.threadId + val threadId = reader.next.threadId + cursor.close() + return threadId } override fun getSessionRequestSentTimestamp(publicKey: String): Long? { diff --git a/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupAPIV2.kt b/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupAPIV2.kt index 1f0f3af799..c29066509f 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupAPIV2.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/opengroups/OpenGroupAPIV2.kt @@ -1,5 +1,7 @@ package org.session.libsession.messaging.opengroups +import com.fasterxml.jackson.databind.PropertyNamingStrategy +import com.fasterxml.jackson.databind.annotation.JsonNaming import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -36,8 +38,6 @@ object OpenGroupAPIV2 { const val DEFAULT_SERVER = "https://sog.ibolpap.finance" private const val DEFAULT_SERVER_PUBLIC_KEY = "b464aa186530c97d6bcf663a3a3b7465a5f782beaa67c83bee99468824b4aa10" - // https://sog.ibolpap.finance/main?public_key=b464aa186530c97d6bcf663a3a3b7465a5f782beaa67c83bee99468824b4aa10 - val defaultRooms = MutableSharedFlow>(replay = 1) private val sharedContext = Kovenant.createContext() @@ -64,6 +64,13 @@ object OpenGroupAPIV2 { val imageID: String? ) + @JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy::class) + data class CompactPollRequest(val roomId: String, + val authToken: String, + val fromDeletionServerId: Long?, + val fromMessageServerId: Long? + ) + data class CompactPollResult(val messages: List, val deletions: List, val moderators: List @@ -83,7 +90,9 @@ object OpenGroupAPIV2 { val useOnionRouting: Boolean = true ) - private fun createBody(parameters: Any): RequestBody { + private fun createBody(parameters: Any?): RequestBody? { + if (parameters == null) return null + val parametersAsJSON = JsonUtil.toJson(parameters) return RequestBody.create(MediaType.get("application/json"), parametersAsJSON) } @@ -111,9 +120,9 @@ object OpenGroupAPIV2 { } when (request.verb) { GET -> requestBuilder.get() - PUT -> requestBuilder.put(createBody(request.parameters!!)) - POST -> requestBuilder.post(createBody(request.parameters!!)) - DELETE -> requestBuilder.delete(createBody(request.parameters!!)) + PUT -> requestBuilder.put(createBody(request.parameters)) + POST -> requestBuilder.post(createBody(request.parameters)) + DELETE -> requestBuilder.delete(createBody(request.parameters)) } if (!request.room.isNullOrEmpty()) { @@ -145,21 +154,6 @@ object OpenGroupAPIV2 { } } - fun downloadOpenGroupProfilePicture(imageUrl: String): ByteArray? { - Log.d("Loki", "Downloading open group profile picture from \"$imageUrl\".") - val outputStream = ByteArrayOutputStream() - try { - DownloadUtilities.downloadFile(outputStream, imageUrl, FileServerAPI.maxFileSize, null) - Log.d("Loki", "Open group profile picture was successfully loaded from \"$imageUrl\"") - return outputStream.toByteArray() - } catch (e: Exception) { - Log.d("Loki", "Failed to download open group profile picture from \"$imageUrl\" due to error: $e.") - return null - } finally { - outputStream.close() - } - } - fun downloadOpenGroupProfilePicture(roomID: String, server: String): Promise { val request = Request(verb = GET, room = roomID, server = server, endpoint = "rooms/$roomID/image", isAuthRequired = false) return send(request).map(sharedContext) { json -> @@ -291,8 +285,9 @@ object OpenGroupAPIV2 { // endregion // region Message Deletion + @JvmStatic fun deleteMessage(serverID: Long, room: String, server: String): Promise { - val request = Request(verb = DELETE, room = room, server = server, endpoint = "message/$serverID") + val request = Request(verb = DELETE, room = room, server = server, endpoint = "messages/$serverID") return send(request).map(sharedContext) { Log.d("Loki", "Deleted server message") } @@ -306,9 +301,9 @@ object OpenGroupAPIV2 { } val request = Request(verb = GET, room = room, server = server, endpoint = "deleted_messages", queryParameters = queryParameters) return send(request).map(sharedContext) { json -> - @Suppress("UNCHECKED_CAST") val serverIDs = json["ids"] as? List + @Suppress("UNCHECKED_CAST") val serverIDs = (json["ids"] as? List)?.map { it.toLong() } ?: throw Error.PARSING_FAILED - val lastMessageServerId = storage.getLastMessageServerId(room, server) ?: 0 + val lastMessageServerId = storage.getLastDeletionServerId(room, server) ?: 0 val serverID = serverIDs.maxOrNull() ?: 0 if (serverID > lastMessageServerId) { storage.setLastDeletionServerId(room, server, serverID) @@ -330,6 +325,7 @@ object OpenGroupAPIV2 { } } + @JvmStatic fun ban(publicKey: String, room: String, server: String): Promise { val parameters = mapOf("public_key" to publicKey) val request = Request(verb = POST, room = room, server = server, endpoint = "block_list", parameters = parameters) @@ -351,8 +347,11 @@ object OpenGroupAPIV2 { // endregion // region General -// fun getCompactPoll(): Promise { -// val request = Request() +// fun getCompactPoll(rooms: List, server: String): Promise, Exception> { +// val request = Request(verb = POST, room = null, server = server, endpoint = "compact_poll", isAuthRequired = false) +// +// // build a request for all rooms +// // } fun getDefaultRoomsIfNeeded(): Promise, Exception> { @@ -362,8 +361,9 @@ object OpenGroupAPIV2 { val earlyGroups = groups.map { group -> DefaultGroup(group.id, group.name, null) } - defaultRooms.replayCache.firstOrNull()?.let { groups -> - if (groups.none { it.image?.isNotEmpty() == true}) { + // see if we have any cached rooms, and if they already have images, don't overwrite with early non-image results + defaultRooms.replayCache.firstOrNull()?.let { replayed -> + if (replayed.none { it.image?.isNotEmpty() == true}) { defaultRooms.tryEmit(earlyGroups) } }