Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1771,11 +1771,23 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
return
} else {
// Put the message in the database
val messageId = originalMessage.messageId
val count = if (recipient.isCommunityRecipient) {
// ReactionRecord count is total number of reactions for this emoji/messageId in community,
// regardless of the author of each ReactionRecord.
// We set to the existing number for now and we'll increase all of them
// by 1 below.
originalMessage.reactions.firstOrNull { it.messageId == messageId && it.emoji == emoji }
?.count ?: 0
} else {
1
}

val reaction = ReactionRecord(
messageId = originalMessage.messageId,
messageId = messageId,
author = author,
emoji = emoji,
count = 1,
count = count,
dateSent = emojiTimestamp,
dateReceived = emojiTimestamp
)
Expand All @@ -1789,7 +1801,11 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
reactionMessage.reaction = Reaction.from(originalMessage.timestamp, originalAuthor.toString(), emoji, true)
if (recipient.address is Address.Community) {

val messageServerId = lokiMessageDb.getServerID(originalMessage.messageId) ?:
// Increment the reaction count locally immediately. This
// has to apply on all the ReactionRecords with the same messageId/emoji per design.
reactionDb.updateAllCountFor(messageId, emoji, 1)

val messageServerId = lokiMessageDb.getServerID(messageId) ?:
return Log.w(TAG, "Failed to find message server ID when adding emoji reaction")

scope.launch {
Expand Down Expand Up @@ -1825,7 +1841,7 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
} else {
reactionDb.deleteReaction(
emoji,
MessageId(originalMessage.id, originalMessage.isMms),
originalMessage.messageId,
author
)

Expand All @@ -1841,6 +1857,10 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
)

if (recipient.address is Address.Community) {
// Decrement the reaction count locally immediately (they will
// get overwritten when we get the server update back)
reactionDb.updateAllCountFor(originalMessage.messageId, emoji, -1)


val messageServerId = lokiMessageDb.getServerID(originalMessage.messageId) ?:
return Log.w(TAG, "Failed to find message server ID when removing emoji reaction")
Expand Down Expand Up @@ -2025,7 +2045,13 @@ class ConversationActivityV2 : ScreenLockActionBarActivity(), InputBarDelegate,
override fun onReactionLongClicked(messageId: MessageId, emoji: String?) {
if (viewModel.recipient.isGroupOrCommunityRecipient) {
val isUserCommunityModerator = viewModel.recipient.takeIf { it.isCommunityRecipient }?.currentUserRole?.canModerate == true
val fragment = ReactionsDialogFragment.create(messageId, isUserCommunityModerator, emoji, viewModel.canRemoveReaction)
val fragment = ReactionsDialogFragment.create(
messageId,
isUserCommunityModerator,
emoji,
viewModel.canRemoveReaction,
viewModel.recipient.isCommunityRecipient
)
fragment.show(supportFragmentManager, TAG_REACTION_FRAGMENT)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,30 @@ class ReactionDatabase(context: Context, helper: Provider<SQLCipherOpenHelper>)
)
}

/**
* Update the count for all reactions with the given emoji on the specified message.
* Note this should ONLY be used on community reactions as each reaction record contains the
* same count for that particular emoji/messageId, so it makes sense to update them all at once.
*
* For other type of messages, this will likely result to errors in the reaction counts!
*/
fun updateAllCountFor(messageId: MessageId, emoji: String, countDiff: Int) {
val changed = writableDatabase.compileStatement("""
UPDATE $TABLE_NAME SET $COUNT = $COUNT + ?
WHERE $MESSAGE_ID = ? AND $IS_MMS = ? AND $EMOJI = ?
""").use { statement ->
statement.bindLong(1, countDiff.toLong())
statement.bindLong(2, messageId.id)
statement.bindLong(3, if (messageId.mms) 1 else 0)
statement.bindString(4, emoji)
statement.executeUpdateDelete() > 0
}

if (changed) {
mutableChangeNotification.tryEmit(messageId)
}
}

private fun deleteReactions(query: String, args: Array<String>) {
val updatedMessageIDs = writableDatabase.rawQuery("DELETE FROM $TABLE_NAME WHERE $query RETURNING $MESSAGE_ID, $IS_MMS", *args).use { cursor ->
cursor.asSequence()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,24 @@

import java.util.List;

final class EmojiCount {
import kotlin.collections.CollectionsKt;

static EmojiCount all(@NonNull List<ReactionDetails> reactions) {
return new EmojiCount("", "", reactions);
}
public final class EmojiCount {

private final String baseEmoji;
private final String displayEmoji;
private final List<ReactionDetails> reactions;
private final boolean shouldAccumulateReactionCount;

EmojiCount(@NonNull String baseEmoji,
@NonNull String emoji,
@NonNull List<ReactionDetails> reactions)
@NonNull List<ReactionDetails> reactions,
boolean shouldAccumulateReactionCount)
{
this.baseEmoji = baseEmoji;
this.displayEmoji = emoji;
this.reactions = reactions;
this.shouldAccumulateReactionCount = shouldAccumulateReactionCount;
}

public @NonNull String getBaseEmoji() {
Expand All @@ -34,7 +35,12 @@ static EmojiCount all(@NonNull List<ReactionDetails> reactions) {
}

public int getCount() {
return Stream.of(reactions).reduce(0, (count, reaction) -> count + reaction.getCount());
if (shouldAccumulateReactionCount) {
return CollectionsKt.fold(reactions, 0, (count, reaction) -> count + reaction.getCount());
}

ReactionDetails first = CollectionsKt.getOrNull(reactions, 0);
return first == null ? 0 : first.getCount();
}

public @NonNull List<ReactionDetails> getReactions() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public final class ReactionsDialogFragment extends BottomSheetDialogFragment imp
private static final String ARGS_IS_MODERATOR = "reactions.args.is.moderator";
private static final String ARGS_EMOJI = "reactions.args.emoji";
private static final String ARGS_CAN_REMOVE = "reactions.args.can.remove";
private static final String ARGS_FROM_COMMUNITY_THREAD = "reactions.args.from.community.thread";

private ViewPager2 recipientPagerView;
private ReactionViewPagerAdapter recipientsAdapter;
Expand All @@ -53,7 +54,11 @@ public final class ReactionsDialogFragment extends BottomSheetDialogFragment imp

private final LifecycleDisposable disposables = new LifecycleDisposable();

public static DialogFragment create(MessageId messageId, boolean isUserModerator, @Nullable String emoji, boolean canRemove) {
public static DialogFragment create(MessageId messageId,
boolean isUserModerator,
@Nullable String emoji,
boolean canRemove,
boolean fromCommunityThread) {
Bundle args = new Bundle();
DialogFragment fragment = new ReactionsDialogFragment();

Expand All @@ -62,6 +67,7 @@ public static DialogFragment create(MessageId messageId, boolean isUserModerator
args.putBoolean(ARGS_IS_MODERATOR, isUserModerator);
args.putString(ARGS_EMOJI, emoji);
args.putBoolean(ARGS_CAN_REMOVE, canRemove);
args.putBoolean(ARGS_FROM_COMMUNITY_THREAD, fromCommunityThread);

fragment.setArguments(args);

Expand Down Expand Up @@ -158,7 +164,8 @@ private void setUpViewModel(@NonNull MessageId messageId) {
getDefaultViewModelProviderFactory(),
HiltViewModelExtensions.withCreationCallback(
getDefaultViewModelCreationExtras(),
(ReactionsViewModel.Factory factory) -> factory.create(messageId)
(ReactionsViewModel.Factory factory) -> factory.create(
messageId, requireArguments().getBoolean(ARGS_FROM_COMMUNITY_THREAD))
)
).get(ReactionsViewModel.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@ public class ReactionsViewModel extends ViewModel {

private final MessageId messageId;
private final ReactionsRepository repository;
private final boolean fromCommunityThread;

@AssistedInject
public ReactionsViewModel(@Assisted @NonNull MessageId messageId, final ReactionsRepository repository) {
public ReactionsViewModel(@Assisted @NonNull MessageId messageId,
@Assisted boolean fromCommunityThread,
final ReactionsRepository repository) {
this.messageId = messageId;
this.repository = repository;
this.fromCommunityThread = fromCommunityThread;
}

public @NonNull
Expand All @@ -38,9 +42,11 @@ Observable<List<EmojiCount>> getEmojiCounts() {
.map(reactionList -> Stream.of(reactionList)
.groupBy(ReactionDetails::getBaseEmoji)
.sorted(this::compareReactions)
.map(entry -> new EmojiCount(entry.getKey(),
getCountDisplayEmoji(entry.getValue()),
entry.getValue()))
.map(entry -> new EmojiCount(
entry.getKey(),
getCountDisplayEmoji(entry.getValue()),
entry.getValue(),
!fromCommunityThread))
.toList())
.observeOn(AndroidSchedulers.mainThread());
}
Expand Down Expand Up @@ -75,6 +81,6 @@ private long getLatestTimestamp(List<ReactionDetails> reactions) {
@AssistedFactory
public interface Factory {

ReactionsViewModel create(@NonNull MessageId messageId);
ReactionsViewModel create(@NonNull MessageId messageId, boolean fromCommunityThread);
}
}