Adding the ability to customise the text for the deleted control messages

pull/1647/head
ThomasSession 9 months ago
parent 584b013cc4
commit 751016617c

@ -216,33 +216,34 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
threadId?.let{ MessagingModuleConfiguration.shared.lastSentTimestampCache.delete(it, messages.map { it.timestamp }) } threadId?.let{ MessagingModuleConfiguration.shared.lastSentTimestampCache.delete(it, messages.map { it.timestamp }) }
} }
override fun updateMessageAsDeleted(timestamp: Long, author: String): Long? { override fun markMessageAsDeleted(timestamp: Long, author: String, displayedMessage: String): Long? {
val database = DatabaseComponent.get(context).mmsSmsDatabase() val database = DatabaseComponent.get(context).mmsSmsDatabase()
val address = Address.fromSerialized(author) val address = Address.fromSerialized(author)
val message = database.getMessageFor(timestamp, address) ?: return null val message = database.getMessageFor(timestamp, address) ?: return null
val messagingDatabase: MessagingDatabase = if (message.isMms) DatabaseComponent.get(context).mmsDatabase()
else DatabaseComponent.get(context).smsDatabase()
messagingDatabase.markAsDeleted(message.id, message.isOutgoing) markMessagesAsDeleted(
if (message.isOutgoing) { messages = listOf(MarkAsDeletedMessage(
messagingDatabase.deleteMessage(message.id) messageId = message.id,
} isOutgoing = message.isOutgoing
//todo DELETION might need to change this with new logic )),
isSms = !message.isMms,
displayedMessage = displayedMessage
)
return message.id return message.id
} }
override fun markMessagesAsDeleted( override fun markMessagesAsDeleted(
messages: List<MarkAsDeletedMessage>, messages: List<MarkAsDeletedMessage>,
threadId: Long, isSms: Boolean,
isSms: Boolean displayedMessage: String
) { ) {
val messagingDatabase: MessagingDatabase = if (isSms) DatabaseComponent.get(context).smsDatabase() val messagingDatabase: MessagingDatabase = if (isSms) DatabaseComponent.get(context).smsDatabase()
else DatabaseComponent.get(context).mmsDatabase() else DatabaseComponent.get(context).mmsDatabase()
//todo DELETION can this be batched? //todo DELETION can this be batched?
//todo DELETION we need to have customisable text for "marked as deleted" message
messages.forEach { message -> messages.forEach { message ->
messagingDatabase.markAsDeleted(message.messageId, message.isOutgoing) messagingDatabase.markAsDeleted(message.messageId, message.isOutgoing, displayedMessage)
} }
} }

@ -1968,7 +1968,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// creating a reusable callback // creating a reusable callback
val deleteDeviceOnly = { val deleteDeviceOnly = {
viewModel.markAsDeletedLocally(messages = messages, threadId = viewModel.threadId) viewModel.markAsDeletedLocally(
messages = messages,
displayedMessage = "[UPDATE THIS!] This message was deleted on this device" //todo DELETION update once we have strings
)
endActionMode() endActionMode()
//todo DELETION show confirmation toast //todo DELETION show confirmation toast
} }

@ -38,8 +38,7 @@ class ConversationViewModel(
val edKeyPair: KeyPair?, val edKeyPair: KeyPair?,
private val repository: ConversationRepository, private val repository: ConversationRepository,
private val storage: Storage, private val storage: Storage,
private val messageDataProvider: MessageDataProvider, private val messageDataProvider: MessageDataProvider
database: MmsDatabase,
) : ViewModel() { ) : ViewModel() {
val showSendAfterApprovalText: Boolean val showSendAfterApprovalText: Boolean
@ -167,14 +166,17 @@ class ConversationViewModel(
* but the messages themselves won't be removed from the db. * but the messages themselves won't be removed from the db.
* Instead they will appear as a special type of message * Instead they will appear as a special type of message
* that says something like "This message was deleted" * that says something like "This message was deleted"
*
* @displayedMessage is the message that will be displayed in place of the deleted message.
*/ */
fun markAsDeletedLocally(messages: Set<MessageRecord>, threadId: Long) { fun markAsDeletedLocally(messages: Set<MessageRecord>, displayedMessage: String) {
// make sure to stop audio messages, if any // make sure to stop audio messages, if any
messages.filterIsInstance<MmsMessageRecord>() messages.filterIsInstance<MmsMessageRecord>()
.mapNotNull { it.slideDeck.audioSlide } .mapNotNull { it.slideDeck.audioSlide }
.forEach(::stopMessageAudio) .forEach(::stopMessageAudio)
repository.markAsDeletedLocally(messages, threadId)
repository.markAsDeletedLocally(messages, displayedMessage)
} }
/** /**
@ -304,7 +306,6 @@ class ConversationViewModel(
@Assisted private val edKeyPair: KeyPair?, @Assisted private val edKeyPair: KeyPair?,
private val repository: ConversationRepository, private val repository: ConversationRepository,
private val storage: Storage, private val storage: Storage,
private val mmsDatabase: MmsDatabase,
private val messageDataProvider: MessageDataProvider, private val messageDataProvider: MessageDataProvider,
) : ViewModelProvider.Factory { ) : ViewModelProvider.Factory {
@ -314,8 +315,7 @@ class ConversationViewModel(
edKeyPair = edKeyPair, edKeyPair = edKeyPair,
repository = repository, repository = repository,
storage = storage, storage = storage,
messageDataProvider = messageDataProvider, messageDataProvider = messageDataProvider
database = mmsDatabase
) as T ) as T
} }
} }

@ -21,7 +21,8 @@ class DeletedMessageView : LinearLayout {
// region Updating // region Updating
fun bind(message: MessageRecord, @ColorInt textColor: Int) { fun bind(message: MessageRecord, @ColorInt textColor: Int) {
assert(message.isDeleted) assert(message.isDeleted)
binding.deleteTitleTextView.text = context.getString(R.string.deleted_message) // set the text to the message's body if it is set, else use a fallback
binding.deleteTitleTextView.text = message.body.ifEmpty { context.getString(R.string.deleted_message) }
binding.deleteTitleTextView.setTextColor(textColor) binding.deleteTitleTextView.setTextColor(textColor)
binding.deletedMessageViewIconImageView.imageTintList = ColorStateList.valueOf(textColor) binding.deletedMessageViewIconImageView.imageTintList = ColorStateList.valueOf(textColor)
} }

@ -46,7 +46,7 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
public abstract void markUnidentified(long messageId, boolean unidentified); public abstract void markUnidentified(long messageId, boolean unidentified);
public abstract void markAsDeleted(long messageId, boolean isOutgoing); public abstract void markAsDeleted(long messageId, boolean isOutgoing, String displayedMessage);
public abstract boolean deleteMessage(long messageId); public abstract boolean deleteMessage(long messageId);
public abstract boolean deleteMessages(long[] messageId, long threadId); public abstract boolean deleteMessages(long[] messageId, long threadId);

@ -305,11 +305,11 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
db.update(TABLE_NAME, contentValues, ID_WHERE, arrayOf(messageId.toString())) db.update(TABLE_NAME, contentValues, ID_WHERE, arrayOf(messageId.toString()))
} }
override fun markAsDeleted(messageId: Long, isOutgoing: Boolean) { override fun markAsDeleted(messageId: Long, isOutgoing: Boolean, displayedMessage: String) {
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
val contentValues = ContentValues() val contentValues = ContentValues()
contentValues.put(READ, 1) contentValues.put(READ, 1)
contentValues.put(BODY, "") contentValues.put(BODY, displayedMessage)
contentValues.put(HAS_MENTION, 0) contentValues.put(HAS_MENTION, 0)
database.update(TABLE_NAME, contentValues, ID_WHERE, arrayOf(messageId.toString())) database.update(TABLE_NAME, contentValues, ID_WHERE, arrayOf(messageId.toString()))
val attachmentDatabase = get(context).attachmentDatabase() val attachmentDatabase = get(context).attachmentDatabase()

@ -236,11 +236,11 @@ public class SmsDatabase extends MessagingDatabase {
} }
@Override @Override
public void markAsDeleted(long messageId, boolean isOutgoing) { public void markAsDeleted(long messageId, boolean isOutgoing, String displayedMessage) {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(READ, 1); contentValues.put(READ, 1);
contentValues.put(BODY, ""); contentValues.put(BODY, displayedMessage);
contentValues.put(HAS_MENTION, 0); contentValues.put(HAS_MENTION, 0);
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)}); database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)});

@ -57,7 +57,7 @@ interface ConversationRepository {
fun clearDrafts(threadId: Long) fun clearDrafts(threadId: Long)
fun inviteContacts(threadId: Long, contacts: List<Recipient>) fun inviteContacts(threadId: Long, contacts: List<Recipient>)
fun setBlocked(recipient: Recipient, blocked: Boolean) fun setBlocked(recipient: Recipient, blocked: Boolean)
fun markAsDeletedLocally(messages: Set<MessageRecord>, threadId: Long) fun markAsDeletedLocally(messages: Set<MessageRecord>, displayedMessage: String)
fun deleteMessages(messages: Set<MessageRecord>, threadId: Long) fun deleteMessages(messages: Set<MessageRecord>, threadId: Long)
fun deleteAllLocalMessagesInThreadFromSenderOfMessage(messageRecord: MessageRecord) fun deleteAllLocalMessagesInThreadFromSenderOfMessage(messageRecord: MessageRecord)
fun setApproved(recipient: Recipient, isApproved: Boolean) fun setApproved(recipient: Recipient, isApproved: Boolean)
@ -182,7 +182,7 @@ class DefaultConversationRepository @Inject constructor(
* They won't be removed from the db but instead will appear as a special type * They won't be removed from the db but instead will appear as a special type
* of message that says something like "This message was deleted" * of message that says something like "This message was deleted"
*/ */
override fun markAsDeletedLocally(messages: Set<MessageRecord>, threadId: Long) { override fun markAsDeletedLocally(messages: Set<MessageRecord>, displayedMessage: String) {
// split the messages into mms and sms // split the messages into mms and sms
val (mms, sms) = messages.partition { it.isMms } val (mms, sms) = messages.partition { it.isMms }
@ -190,14 +190,20 @@ class DefaultConversationRepository @Inject constructor(
messageDataProvider.markMessagesAsDeleted(mms.map { MarkAsDeletedMessage( messageDataProvider.markMessagesAsDeleted(mms.map { MarkAsDeletedMessage(
messageId = it.id, messageId = it.id,
isOutgoing = it.isOutgoing isOutgoing = it.isOutgoing
) }, threadId, isSms = false) ) },
isSms = false,
displayedMessage = displayedMessage
)
} }
if(sms.isNotEmpty()){ if(sms.isNotEmpty()){
messageDataProvider.markMessagesAsDeleted(sms.map { MarkAsDeletedMessage( messageDataProvider.markMessagesAsDeleted(sms.map { MarkAsDeletedMessage(
messageId = it.id, messageId = it.id,
isOutgoing = it.isOutgoing isOutgoing = it.isOutgoing
) }, threadId, isSms = true) ) },
isSms = true,
displayedMessage = displayedMessage
)
} }
//todo DELETION delete attachments and links //todo DELETION delete attachments and links

@ -28,7 +28,6 @@ class ConversationViewModelTest: BaseViewModelTest() {
private val repository = mock<ConversationRepository>() private val repository = mock<ConversationRepository>()
private val storage = mock<Storage>() private val storage = mock<Storage>()
private val mmsDatabase = mock<MmsDatabase>()
private val threadId = 123L private val threadId = 123L
private val edKeyPair = mock<KeyPair>() private val edKeyPair = mock<KeyPair>()
@ -36,7 +35,7 @@ class ConversationViewModelTest: BaseViewModelTest() {
private lateinit var messageRecord: MessageRecord private lateinit var messageRecord: MessageRecord
private val viewModel: ConversationViewModel by lazy { private val viewModel: ConversationViewModel by lazy {
ConversationViewModel(threadId, edKeyPair, repository, storage, mock(), mmsDatabase) ConversationViewModel(threadId, edKeyPair, repository, storage, mock())
} }
@Before @Before

@ -24,8 +24,8 @@ interface MessageDataProvider {
fun getMessageIDs(serverIDs: List<Long>, threadID: Long): Pair<List<Long>, List<Long>> fun getMessageIDs(serverIDs: List<Long>, threadID: Long): Pair<List<Long>, List<Long>>
fun deleteMessage(messageID: Long, isSms: Boolean) fun deleteMessage(messageID: Long, isSms: Boolean)
fun deleteMessages(messageIDs: List<Long>, threadId: Long, isSms: Boolean) fun deleteMessages(messageIDs: List<Long>, threadId: Long, isSms: Boolean)
fun updateMessageAsDeleted(timestamp: Long, author: String): Long? fun markMessageAsDeleted(timestamp: Long, author: String, displayedMessage: String): Long?
fun markMessagesAsDeleted(messages: List<MarkAsDeletedMessage>, threadId: Long, isSms: Boolean) fun markMessagesAsDeleted(messages: List<MarkAsDeletedMessage>, isSms: Boolean, displayedMessage: String)
fun getServerHashForMessage(messageID: Long, mms: Boolean): String? fun getServerHashForMessage(messageID: Long, mms: Boolean): String?
fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment? fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment?
fun getAttachmentStream(attachmentId: Long): SessionServiceAttachmentStream? fun getAttachmentStream(attachmentId: Long): SessionServiceAttachmentStream?

@ -258,7 +258,11 @@ fun MessageReceiver.handleUnsendRequest(message: UnsendRequest): Long? {
messageDataProvider.getServerHashForMessage(messageIdToDelete, mms)?.let { serverHash -> messageDataProvider.getServerHashForMessage(messageIdToDelete, mms)?.let { serverHash ->
SnodeAPI.deleteMessage(author, listOf(serverHash)) SnodeAPI.deleteMessage(author, listOf(serverHash))
} }
val deletedMessageId = messageDataProvider.updateMessageAsDeleted(timestamp, author) val deletedMessageId = messageDataProvider.markMessageAsDeleted(
timestamp = timestamp,
author = author,
displayedMessage = "[UPDATE THIS!] This message was deleted on this device" //todo DELETION update once we have strings
)
if (!messageDataProvider.isOutgoingMessage(timestamp)) { if (!messageDataProvider.isOutgoingMessage(timestamp)) {
SSKEnvironment.shared.notificationManager.updateNotification(context) SSKEnvironment.shared.notificationManager.updateNotification(context)
} }

Loading…
Cancel
Save