That fuck shit the fascists are using
1package org.tm.archive.database
2
3import android.content.Context
4import android.net.Uri
5import androidx.core.content.contentValuesOf
6import org.signal.core.util.SqlUtil
7import org.signal.core.util.delete
8import org.signal.core.util.deleteAll
9import org.signal.core.util.logging.Log
10import org.signal.core.util.readToList
11import org.signal.core.util.requireNonNullString
12import org.signal.core.util.select
13import org.signal.core.util.update
14import org.signal.core.util.withinTransaction
15import org.tm.archive.R
16import java.util.LinkedList
17
18class DraftTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseTable(context, databaseHelper), ThreadIdDatabaseReference {
19 companion object {
20 private val TAG = Log.tag(DraftTable::class.java)
21 const val TABLE_NAME = "drafts"
22 const val ID = "_id"
23 const val THREAD_ID = "thread_id"
24 const val DRAFT_TYPE = "type"
25 const val DRAFT_VALUE = "value"
26 const val CREATE_TABLE = """
27 CREATE TABLE $TABLE_NAME (
28 $ID INTEGER PRIMARY KEY,
29 $THREAD_ID INTEGER,
30 $DRAFT_TYPE TEXT,
31 $DRAFT_VALUE TEXT
32 )
33 """
34
35 @JvmField
36 val CREATE_INDEXS = arrayOf("CREATE INDEX IF NOT EXISTS draft_thread_index ON $TABLE_NAME ($THREAD_ID);")
37 }
38
39 fun replaceDrafts(threadId: Long, drafts: List<Draft>) {
40 writableDatabase.withinTransaction { db ->
41 val deletedRowCount = db
42 .delete(TABLE_NAME)
43 .where("$THREAD_ID = ?", threadId)
44 .run()
45 Log.d(TAG, "[replaceDrafts] Deleted $deletedRowCount rows for thread $threadId")
46
47 for (draft in drafts) {
48 val values = contentValuesOf(
49 THREAD_ID to threadId,
50 DRAFT_TYPE to draft.type,
51 DRAFT_VALUE to draft.value
52 )
53 db.insert(TABLE_NAME, null, values)
54 }
55 }
56 }
57
58 fun clearDrafts(threadId: Long) {
59 val deletedRowCount = writableDatabase
60 .delete(TABLE_NAME)
61 .where("$THREAD_ID = ?", threadId)
62 .run()
63 Log.d(TAG, "[clearDrafts] Deleted $deletedRowCount rows for thread $threadId")
64 }
65
66 fun clearDrafts(threadIds: Set<Long>) {
67 val query = SqlUtil.buildSingleCollectionQuery(THREAD_ID, threadIds)
68 writableDatabase
69 .delete(TABLE_NAME)
70 .where(query.where, *query.whereArgs)
71 .run()
72 }
73
74 fun clearAllDrafts() {
75 writableDatabase.deleteAll(TABLE_NAME)
76 }
77
78 fun getDrafts(threadId: Long): Drafts {
79 return readableDatabase
80 .select()
81 .from(TABLE_NAME)
82 .where("$THREAD_ID = ?", threadId)
83 .run()
84 .readToList { cursor ->
85 Draft(
86 type = cursor.requireNonNullString(DRAFT_TYPE),
87 value = cursor.requireNonNullString(DRAFT_VALUE)
88 )
89 }
90 .asDrafts()
91 }
92
93 fun getAllVoiceNoteDrafts(): Drafts {
94 return readableDatabase
95 .select()
96 .from(TABLE_NAME)
97 .where("$DRAFT_TYPE = ?", Draft.VOICE_NOTE)
98 .run()
99 .readToList { cursor ->
100 Draft(
101 type = cursor.requireNonNullString(DRAFT_TYPE),
102 value = cursor.requireNonNullString(DRAFT_VALUE)
103 )
104 }
105 .asDrafts()
106 }
107
108 override fun remapThread(fromId: Long, toId: Long) {
109 writableDatabase
110 .update(TABLE_NAME)
111 .values(THREAD_ID to toId)
112 .where("$THREAD_ID = ?", fromId)
113 .run()
114 }
115
116 private fun List<Draft>.asDrafts(): Drafts {
117 return Drafts(this)
118 }
119
120 class Draft(val type: String, val value: String) {
121 fun getSnippet(context: Context): String {
122 return when (type) {
123 TEXT -> value
124 IMAGE -> context.getString(R.string.DraftDatabase_Draft_image_snippet)
125 VIDEO -> context.getString(R.string.DraftDatabase_Draft_video_snippet)
126 AUDIO -> context.getString(R.string.DraftDatabase_Draft_audio_snippet)
127 LOCATION -> context.getString(R.string.DraftDatabase_Draft_location_snippet)
128 QUOTE -> context.getString(R.string.DraftDatabase_Draft_quote_snippet)
129 VOICE_NOTE -> context.getString(R.string.DraftDatabase_Draft_voice_note)
130 else -> ""
131 }
132 }
133
134 companion object {
135 const val TEXT = "text"
136 const val IMAGE = "image"
137 const val VIDEO = "video"
138 const val AUDIO = "audio"
139 const val LOCATION = "location"
140 const val QUOTE = "quote"
141 const val BODY_RANGES = "mention"
142 const val VOICE_NOTE = "voice_note"
143 const val MESSAGE_EDIT = "message_edit"
144 }
145 }
146
147 class Drafts(list: List<Draft> = emptyList()) : LinkedList<Draft>() {
148 init {
149 addAll(list)
150 }
151
152 fun addIfNotNull(draft: Draft?) {
153 if (draft != null) {
154 add(draft)
155 }
156 }
157
158 fun getDraftOfType(type: String): Draft? {
159 return firstOrNull { it.type == type }
160 }
161
162 fun shouldUpdateSnippet(): Boolean {
163 return none { it.type == Draft.MESSAGE_EDIT }
164 }
165
166 fun getSnippet(context: Context): String {
167 val textDraft = getDraftOfType(Draft.TEXT)
168 return if (textDraft != null) {
169 textDraft.getSnippet(context)
170 } else if (size > 0) {
171 get(0).getSnippet(context)
172 } else {
173 ""
174 }
175 }
176
177 fun getUriSnippet(): Uri? {
178 val imageDraft = getDraftOfType(Draft.IMAGE)
179
180 return if (imageDraft?.value != null) {
181 Uri.parse(imageDraft.value)
182 } else {
183 null
184 }
185 }
186 }
187}