Fix/empty conversations (#1698)

* Not deleting a thread when it's empty - showing an empty snippet when thread is empty or only made up of 'marked as deleted' messages

* SES-2814 - show empty state when conversations are empty
This commit is contained in:
ThomasSession 2024-10-22 15:38:46 +11:00 committed by GitHub
parent 4b01fcec5e
commit 4917548faf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 42 additions and 44 deletions

View File

@ -1123,8 +1123,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
val openGroup = viewModel.openGroup val openGroup = viewModel.openGroup
// Get the correct placeholder text for this type of empty conversation // Get the correct placeholder text for this type of empty conversation
val isNoteToSelf = recipient.isLocalNumber
val txtCS: CharSequence = when { val txtCS: CharSequence = when {
// note to self
recipient.isLocalNumber -> getString(R.string.noteToSelfEmpty) recipient.isLocalNumber -> getString(R.string.noteToSelfEmpty)
// If this is a community which we cannot write to // If this is a community which we cannot write to
@ -1141,8 +1141,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
.format() .format()
} }
recipient.isGroupRecipient -> { // 10n1 and groups
// If this is a group or community that we CAN send messages to recipient.is1on1 || recipient.isGroupRecipient -> {
Phrase.from(applicationContext, R.string.groupNoMessages) Phrase.from(applicationContext, R.string.groupNoMessages)
.put(GROUP_NAME_KEY, recipient.toShortString()) .put(GROUP_NAME_KEY, recipient.toShortString())
.format() .format()

View File

@ -175,7 +175,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
) )
get(context).groupReceiptDatabase() get(context).groupReceiptDatabase()
.update(ourAddress, id, status, timestamp) .update(ourAddress, id, status, timestamp)
get(context).threadDatabase().update(threadId, false, true) get(context).threadDatabase().update(threadId, false)
notifyConversationListeners(threadId) notifyConversationListeners(threadId)
} }
} }
@ -274,7 +274,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
" WHERE " + ID + " = ?", arrayOf(id.toString() + "") " WHERE " + ID + " = ?", arrayOf(id.toString() + "")
) )
if (threadId.isPresent) { if (threadId.isPresent) {
get(context).threadDatabase().update(threadId.get(), false, true) get(context).threadDatabase().update(threadId.get(), false)
} }
} }
@ -646,7 +646,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
) )
if (!MmsSmsColumns.Types.isExpirationTimerUpdate(mailbox)) { if (!MmsSmsColumns.Types.isExpirationTimerUpdate(mailbox)) {
if (runThreadUpdate) { if (runThreadUpdate) {
get(context).threadDatabase().update(threadId, true, true) get(context).threadDatabase().update(threadId, true)
} }
} }
notifyConversationListeners(threadId) notifyConversationListeners(threadId)
@ -791,7 +791,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
} }
setHasSent(threadId, true) setHasSent(threadId, true)
if (runThreadUpdate) { if (runThreadUpdate) {
update(threadId, true, true) update(threadId, true)
} }
} }
return messageId return messageId
@ -930,7 +930,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
groupReceiptDatabase.deleteRowsForMessage(messageId) groupReceiptDatabase.deleteRowsForMessage(messageId)
val database = databaseHelper.writableDatabase val database = databaseHelper.writableDatabase
database!!.delete(TABLE_NAME, ID_WHERE, arrayOf(messageId.toString())) database!!.delete(TABLE_NAME, ID_WHERE, arrayOf(messageId.toString()))
val threadDeleted = get(context).threadDatabase().update(threadId, false, true) val threadDeleted = get(context).threadDatabase().update(threadId, false)
notifyConversationListeners(threadId) notifyConversationListeners(threadId)
notifyStickerListeners() notifyStickerListeners()
notifyStickerPackListeners() notifyStickerPackListeners()
@ -948,7 +948,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
argValues argValues
) )
val threadDeleted = get(context).threadDatabase().update(threadId, false, true) val threadDeleted = get(context).threadDatabase().update(threadId, false)
notifyConversationListeners(threadId) notifyConversationListeners(threadId)
notifyStickerListeners() notifyStickerListeners()
notifyStickerPackListeners() notifyStickerPackListeners()
@ -1145,7 +1145,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
} }
val threadDb = get(context).threadDatabase() val threadDb = get(context).threadDatabase()
for (threadId in threadIds) { for (threadId in threadIds) {
val threadDeleted = threadDb.update(threadId, false, true) val threadDeleted = threadDb.update(threadId, false)
notifyConversationListeners(threadId) notifyConversationListeners(threadId)
} }
notifyStickerListeners() notifyStickerListeners()

View File

@ -158,7 +158,7 @@ public class SmsDatabase extends MessagingDatabase {
long threadId = getThreadIdForMessage(id); long threadId = getThreadIdForMessage(id);
DatabaseComponent.get(context).threadDatabase().update(threadId, false, true); DatabaseComponent.get(context).threadDatabase().update(threadId, false);
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
} }
@ -261,7 +261,7 @@ public class SmsDatabase extends MessagingDatabase {
long threadId = getThreadIdForMessage(id); long threadId = getThreadIdForMessage(id);
DatabaseComponent.get(context).threadDatabase().update(threadId, false, true); DatabaseComponent.get(context).threadDatabase().update(threadId, false);
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
} }
@ -346,7 +346,7 @@ public class SmsDatabase extends MessagingDatabase {
ID + " = ?", ID + " = ?",
new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))}); new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))});
DatabaseComponent.get(context).threadDatabase().update(threadId, false, true); DatabaseComponent.get(context).threadDatabase().update(threadId, false);
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
foundMessage = true; foundMessage = true;
} }
@ -429,7 +429,7 @@ public class SmsDatabase extends MessagingDatabase {
long threadId = getThreadIdForMessage(messageId); long threadId = getThreadIdForMessage(messageId);
DatabaseComponent.get(context).threadDatabase().update(threadId, true, true); DatabaseComponent.get(context).threadDatabase().update(threadId, true);
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
notifyConversationListListeners(); notifyConversationListListeners();
@ -504,7 +504,7 @@ public class SmsDatabase extends MessagingDatabase {
long messageId = db.insert(TABLE_NAME, null, values); long messageId = db.insert(TABLE_NAME, null, values);
if (runThreadUpdate) { if (runThreadUpdate) {
DatabaseComponent.get(context).threadDatabase().update(threadId, true, true); DatabaseComponent.get(context).threadDatabase().update(threadId, true);
} }
if (message.getSubscriptionId() != -1) { if (message.getSubscriptionId() != -1) {
@ -596,7 +596,7 @@ public class SmsDatabase extends MessagingDatabase {
} }
if (runThreadUpdate) { if (runThreadUpdate) {
DatabaseComponent.get(context).threadDatabase().update(threadId, true, true); DatabaseComponent.get(context).threadDatabase().update(threadId, true);
} }
long lastSeen = DatabaseComponent.get(context).threadDatabase().getLastSeenAndHasSent(threadId).first(); long lastSeen = DatabaseComponent.get(context).threadDatabase().getLastSeenAndHasSent(threadId).first();
if (lastSeen < message.getSentTimestampMillis()) { if (lastSeen < message.getSentTimestampMillis()) {
@ -656,7 +656,7 @@ public class SmsDatabase extends MessagingDatabase {
long threadId = getThreadIdForMessage(messageId); long threadId = getThreadIdForMessage(messageId);
db.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""}); db.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""});
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false, false); boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false);
return threadDeleted; return threadDeleted;
} }
@ -676,7 +676,7 @@ public class SmsDatabase extends MessagingDatabase {
ID + " IN (" + StringUtils.join(argsArray, ',') + ")", ID + " IN (" + StringUtils.join(argsArray, ',') + ")",
argValues argValues
); );
boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false, true); boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false);
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
return threadDeleted; return threadDeleted;
} }

View File

@ -287,7 +287,7 @@ open class Storage(
override fun updateThread(threadId: Long, unarchive: Boolean) { override fun updateThread(threadId: Long, unarchive: Boolean) {
val threadDb = DatabaseComponent.get(context).threadDatabase() val threadDb = DatabaseComponent.get(context).threadDatabase()
threadDb.update(threadId, unarchive, false) threadDb.update(threadId, unarchive)
} }
override fun persist(message: VisibleMessage, override fun persist(message: VisibleMessage,

View File

@ -188,6 +188,16 @@ public class ThreadDatabase extends Database {
notifyConversationListListeners(); notifyConversationListListeners();
} }
public void clearSnippet(long threadId){
ContentValues contentValues = new ContentValues(1);
contentValues.put(SNIPPET, "");
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""});
notifyConversationListListeners();
}
public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) { public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) {
ContentValues contentValues = new ContentValues(4); ContentValues contentValues = new ContentValues(4);
@ -281,7 +291,7 @@ public class ThreadDatabase extends Database {
DatabaseComponent.get(context).smsDatabase().deleteMessagesInThreadBeforeDate(threadId, lastTweetDate); DatabaseComponent.get(context).smsDatabase().deleteMessagesInThreadBeforeDate(threadId, lastTweetDate);
DatabaseComponent.get(context).mmsDatabase().deleteMessagesInThreadBeforeDate(threadId, lastTweetDate); DatabaseComponent.get(context).mmsDatabase().deleteMessagesInThreadBeforeDate(threadId, lastTweetDate);
update(threadId, false, true); update(threadId, false);
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
} }
} finally { } finally {
@ -294,7 +304,7 @@ public class ThreadDatabase extends Database {
Log.i("ThreadDatabase", "Trimming thread: " + threadId + " before :"+timestamp); Log.i("ThreadDatabase", "Trimming thread: " + threadId + " before :"+timestamp);
DatabaseComponent.get(context).smsDatabase().deleteMessagesInThreadBeforeDate(threadId, timestamp); DatabaseComponent.get(context).smsDatabase().deleteMessagesInThreadBeforeDate(threadId, timestamp);
DatabaseComponent.get(context).mmsDatabase().deleteMessagesInThreadBeforeDate(threadId, timestamp); DatabaseComponent.get(context).mmsDatabase().deleteMessagesInThreadBeforeDate(threadId, timestamp);
update(threadId, false, true); update(threadId, false);
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
} }
@ -722,18 +732,10 @@ public class ThreadDatabase extends Database {
notifyConversationListListeners(); notifyConversationListListeners();
} }
public boolean update(long threadId, boolean unarchive, boolean shouldDeleteOnEmpty) { public boolean update(long threadId, boolean unarchive) {
MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase(); MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase();
long count = mmsSmsDatabase.getConversationCount(threadId); long count = mmsSmsDatabase.getConversationCount(threadId);
boolean shouldDeleteEmptyThread = shouldDeleteOnEmpty && possibleToDeleteThreadOnEmpty(threadId);
if (count == 0 && shouldDeleteEmptyThread) {
deleteThread(threadId);
notifyConversationListListeners();
return true;
}
try (MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(mmsSmsDatabase.getConversationSnippet(threadId))) { try (MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(mmsSmsDatabase.getConversationSnippet(threadId))) {
MessageRecord record = null; MessageRecord record = null;
if (reader != null) { if (reader != null) {
@ -748,11 +750,8 @@ public class ThreadDatabase extends Database {
record.getType(), unarchive, record.getExpiresIn(), record.getReadReceiptCount()); record.getType(), unarchive, record.getExpiresIn(), record.getReadReceiptCount());
return false; return false;
} else { } else {
if (shouldDeleteEmptyThread) { // for empty threads or if there is only deleted messages, show an empty snippet
deleteThread(threadId); clearSnippet(threadId);
return true;
}
// todo: add empty snippet that clears existing data
return false; return false;
} }
} finally { } finally {
@ -800,11 +799,6 @@ public class ThreadDatabase extends Database {
return setLastSeen(threadId, lastSeenTime); return setLastSeen(threadId, lastSeenTime);
} }
private boolean possibleToDeleteThreadOnEmpty(long threadId) {
Recipient threadRecipient = getRecipientForThreadId(threadId);
return threadRecipient != null && !threadRecipient.isCommunityRecipient();
}
private @NonNull String getFormattedBodyFor(@NonNull MessageRecord messageRecord) { private @NonNull String getFormattedBodyFor(@NonNull MessageRecord messageRecord) {
if (messageRecord.isMms()) { if (messageRecord.isMms()) {
MmsMessageRecord record = (MmsMessageRecord) messageRecord; MmsMessageRecord record = (MmsMessageRecord) messageRecord;

View File

@ -102,7 +102,11 @@ public class ThreadRecord extends DisplayRecord {
@Override @Override
public CharSequence getDisplayBody(@NonNull Context context) { public CharSequence getDisplayBody(@NonNull Context context) {
if (isGroupUpdateMessage()) { // no need to display anything if there are no messages
if(lastMessage == null){
return "";
}
else if (isGroupUpdateMessage()) {
return context.getString(R.string.groupUpdated); return context.getString(R.string.groupUpdated);
} else if (isOpenGroupInvitation()) { } else if (isOpenGroupInvitation()) {
return context.getString(R.string.communityInvitation); return context.getString(R.string.communityInvitation);

View File

@ -271,14 +271,14 @@
<TextView <TextView
android:padding="@dimen/medium_spacing" android:padding="@dimen/medium_spacing"
android:textSize="@dimen/small_font_size" style="@style/Signal.Text.Preview"
android:textColor="?android:textColorSecondary" android:textColor="?android:textColorTertiary"
android:textAlignment="center" android:textAlignment="center"
android:id="@+id/placeholderText" android:id="@+id/placeholderText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/large_spacing"
app:layout_constraintTop_toBottomOf="@+id/outdatedBanner" app:layout_constraintTop_toBottomOf="@+id/outdatedBanner"
android:elevation="8dp"
android:contentDescription="@string/AccessibilityId_control_message" android:contentDescription="@string/AccessibilityId_control_message"
tools:text="Some Control Message Text" tools:text="Some Control Message Text"
/> />