That fuck shit the fascists are using
at master 192 lines 6.4 kB view raw
1package org.tm.archive.database 2 3import android.content.ContentValues 4import android.content.Context 5import android.database.Cursor 6import org.signal.core.util.CursorUtil 7import org.signal.core.util.SqlUtil 8import org.signal.core.util.delete 9import org.signal.core.util.update 10import org.tm.archive.database.model.MessageId 11import org.tm.archive.database.model.ReactionRecord 12import org.tm.archive.dependencies.ApplicationDependencies 13import org.tm.archive.recipients.RecipientId 14 15/** 16 * Store reactions on messages. 17 */ 18class ReactionTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper), RecipientIdDatabaseReference { 19 20 companion object { 21 const val TABLE_NAME = "reaction" 22 23 private const val ID = "_id" 24 const val MESSAGE_ID = "message_id" 25 const val AUTHOR_ID = "author_id" 26 const val EMOJI = "emoji" 27 const val DATE_SENT = "date_sent" 28 const val DATE_RECEIVED = "date_received" 29 30 @JvmField 31 val CREATE_TABLE = """ 32 CREATE TABLE $TABLE_NAME ( 33 $ID INTEGER PRIMARY KEY, 34 $MESSAGE_ID INTEGER NOT NULL REFERENCES ${MessageTable.TABLE_NAME} (${MessageTable.ID}) ON DELETE CASCADE, 35 $AUTHOR_ID INTEGER NOT NULL REFERENCES ${RecipientTable.TABLE_NAME} (${RecipientTable.ID}) ON DELETE CASCADE, 36 $EMOJI TEXT NOT NULL, 37 $DATE_SENT INTEGER NOT NULL, 38 $DATE_RECEIVED INTEGER NOT NULL, 39 UNIQUE($MESSAGE_ID, $AUTHOR_ID) ON CONFLICT REPLACE 40 ) 41 """ 42 43 @JvmField 44 val CREATE_INDEXES = arrayOf( 45 "CREATE INDEX IF NOT EXISTS reaction_author_id_index ON $TABLE_NAME ($AUTHOR_ID)" 46 ) 47 48 private fun readReaction(cursor: Cursor): ReactionRecord { 49 return ReactionRecord( 50 emoji = CursorUtil.requireString(cursor, EMOJI), 51 author = RecipientId.from(CursorUtil.requireLong(cursor, AUTHOR_ID)), 52 dateSent = CursorUtil.requireLong(cursor, DATE_SENT), 53 dateReceived = CursorUtil.requireLong(cursor, DATE_RECEIVED) 54 ) 55 } 56 } 57 58 fun getReactions(messageId: MessageId): List<ReactionRecord> { 59 val query = "$MESSAGE_ID = ?" 60 val args = SqlUtil.buildArgs(messageId.id) 61 62 val reactions: MutableList<ReactionRecord> = mutableListOf() 63 64 readableDatabase.query(TABLE_NAME, null, query, args, null, null, null).use { cursor -> 65 while (cursor.moveToNext()) { 66 reactions += readReaction(cursor) 67 } 68 } 69 70 return reactions 71 } 72 73 fun getReactionsForMessages(messageIds: Collection<Long>): Map<Long, List<ReactionRecord>> { 74 if (messageIds.isEmpty()) { 75 return emptyMap() 76 } 77 78 val messageIdToReactions: MutableMap<Long, MutableList<ReactionRecord>> = mutableMapOf() 79 80 val args: List<Array<String>> = messageIds.map { SqlUtil.buildArgs(it) } 81 82 for (query: SqlUtil.Query in SqlUtil.buildCustomCollectionQuery("$MESSAGE_ID = ?", args)) { 83 readableDatabase.query(TABLE_NAME, null, query.where, query.whereArgs, null, null, null).use { cursor -> 84 while (cursor.moveToNext()) { 85 val reaction: ReactionRecord = readReaction(cursor) 86 val messageId = CursorUtil.requireLong(cursor, MESSAGE_ID) 87 88 var reactionsList: MutableList<ReactionRecord>? = messageIdToReactions[messageId] 89 90 if (reactionsList == null) { 91 reactionsList = mutableListOf() 92 messageIdToReactions[messageId] = reactionsList 93 } 94 95 reactionsList.add(reaction) 96 } 97 } 98 } 99 100 return messageIdToReactions 101 } 102 103 fun addReaction(messageId: MessageId, reaction: ReactionRecord) { 104 writableDatabase.beginTransaction() 105 try { 106 val values = ContentValues().apply { 107 put(MESSAGE_ID, messageId.id) 108 put(EMOJI, reaction.emoji) 109 put(AUTHOR_ID, reaction.author.serialize()) 110 put(DATE_SENT, reaction.dateSent) 111 put(DATE_RECEIVED, reaction.dateReceived) 112 } 113 114 writableDatabase.insert(TABLE_NAME, null, values) 115 SignalDatabase.messages.updateReactionsUnread(writableDatabase, messageId.id, hasReactions(messageId), false) 116 117 writableDatabase.setTransactionSuccessful() 118 } finally { 119 writableDatabase.endTransaction() 120 } 121 122 ApplicationDependencies.getDatabaseObserver().notifyMessageUpdateObservers(messageId) 123 } 124 125 fun deleteReaction(messageId: MessageId, recipientId: RecipientId) { 126 writableDatabase.beginTransaction() 127 try { 128 writableDatabase 129 .delete(TABLE_NAME) 130 .where("$MESSAGE_ID = ? AND $AUTHOR_ID = ?", messageId.id, recipientId) 131 .run() 132 133 SignalDatabase.messages.updateReactionsUnread(writableDatabase, messageId.id, hasReactions(messageId), true) 134 135 writableDatabase.setTransactionSuccessful() 136 } finally { 137 writableDatabase.endTransaction() 138 } 139 140 ApplicationDependencies.getDatabaseObserver().notifyMessageUpdateObservers(messageId) 141 } 142 143 fun deleteReactions(messageId: MessageId) { 144 writableDatabase 145 .delete(TABLE_NAME) 146 .where("$MESSAGE_ID = ?", messageId.id) 147 .run() 148 } 149 150 fun hasReaction(messageId: MessageId, reaction: ReactionRecord): Boolean { 151 val query = "$MESSAGE_ID = ? AND $AUTHOR_ID = ? AND $EMOJI = ?" 152 val args = SqlUtil.buildArgs(messageId.id, reaction.author, reaction.emoji) 153 154 readableDatabase.query(TABLE_NAME, arrayOf(MESSAGE_ID), query, args, null, null, null).use { cursor -> 155 return cursor.moveToFirst() 156 } 157 } 158 159 private fun hasReactions(messageId: MessageId): Boolean { 160 val query = "$MESSAGE_ID = ?" 161 val args = SqlUtil.buildArgs(messageId.id) 162 163 readableDatabase.query(TABLE_NAME, arrayOf(MESSAGE_ID), query, args, null, null, null).use { cursor -> 164 return cursor.moveToFirst() 165 } 166 } 167 168 override fun remapRecipient(oldAuthorId: RecipientId, newAuthorId: RecipientId) { 169 val query = "$AUTHOR_ID = ?" 170 val args = SqlUtil.buildArgs(oldAuthorId) 171 val values = ContentValues().apply { 172 put(AUTHOR_ID, newAuthorId.serialize()) 173 } 174 175 readableDatabase.update(TABLE_NAME, values, query, args) 176 } 177 178 fun deleteAbandonedReactions() { 179 writableDatabase 180 .delete(TABLE_NAME) 181 .where("$MESSAGE_ID NOT IN (SELECT ${MessageTable.ID} FROM ${MessageTable.TABLE_NAME})") 182 .run() 183 } 184 185 fun moveReactionsToNewMessage(newMessageId: Long, previousId: Long) { 186 writableDatabase 187 .update(TABLE_NAME) 188 .values(MESSAGE_ID to newMessageId) 189 .where("$MESSAGE_ID = ?", previousId) 190 .run() 191 } 192}