mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-08 20:08:35 +00:00
Group together skin tone variations of the same reaction.
This commit is contained in:
parent
6e75d42a92
commit
9a566e5559
@ -3,16 +3,22 @@ package org.thoughtcrime.securesms.reactions;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
final class EmojiCount {
|
final class EmojiCount {
|
||||||
private final String emoji;
|
private final String baseEmoji;
|
||||||
private final int count;
|
private final String displayEmoji;
|
||||||
|
private final int count;
|
||||||
|
|
||||||
EmojiCount(@NonNull String emoji, int count) {
|
EmojiCount(@NonNull String baseEmoji, @NonNull String emoji, int count) {
|
||||||
this.emoji = emoji;
|
this.baseEmoji = baseEmoji;
|
||||||
this.count = count;
|
this.displayEmoji = emoji;
|
||||||
|
this.count = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull String getEmoji() {
|
public @NonNull String getBaseEmoji() {
|
||||||
return emoji;
|
return baseEmoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull String getDisplayEmoji() {
|
||||||
|
return displayEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
|
@ -38,7 +38,7 @@ final class ReactionEmojiCountAdapter extends RecyclerView.Adapter<ReactionEmoji
|
|||||||
int newPosition = -1;
|
int newPosition = -1;
|
||||||
|
|
||||||
for (int i = 0; i < newEmojiCount.size(); i++) {
|
for (int i = 0; i < newEmojiCount.size(); i++) {
|
||||||
if (newEmojiCount.get(i).getEmoji().equals(oldSelection.getEmoji())) {
|
if (newEmojiCount.get(i).getBaseEmoji().equals(oldSelection.getBaseEmoji())) {
|
||||||
newPosition = i;
|
newPosition = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ final class ReactionEmojiCountAdapter extends RecyclerView.Adapter<ReactionEmoji
|
|||||||
public @NonNull ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public @NonNull ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.reactions_bottom_sheet_dialog_fragment_emoji_item, parent, false), position -> {
|
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.reactions_bottom_sheet_dialog_fragment_emoji_item, parent, false), position -> {
|
||||||
if (position != -1 && position != selectedPosition) {
|
if (position != -1 && position != selectedPosition) {
|
||||||
onEmojiCountSelectedListener.onSelected(position == 0 ? null : emojiCountList.get(position - 1).getEmoji());
|
onEmojiCountSelectedListener.onSelected(position == 0 ? null : emojiCountList.get(position - 1).getBaseEmoji());
|
||||||
|
|
||||||
int oldPosition = selectedPosition;
|
int oldPosition = selectedPosition;
|
||||||
selectedPosition = position;
|
selectedPosition = position;
|
||||||
@ -83,7 +83,7 @@ final class ReactionEmojiCountAdapter extends RecyclerView.Adapter<ReactionEmoji
|
|||||||
holder.bind(null, totalCount, selectedPosition == position);
|
holder.bind(null, totalCount, selectedPosition == position);
|
||||||
} else {
|
} else {
|
||||||
EmojiCount item = emojiCountList.get(position - 1);
|
EmojiCount item = emojiCountList.get(position - 1);
|
||||||
holder.bind(item.getEmoji(), item.getCount(), selectedPosition == position);
|
holder.bind(item.getDisplayEmoji(), item.getCount(), selectedPosition == position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ final class ReactionRecipientsAdapter extends RecyclerView.Adapter<ReactionRecip
|
|||||||
}
|
}
|
||||||
|
|
||||||
void bind(@NonNull Reaction reaction) {
|
void bind(@NonNull Reaction reaction) {
|
||||||
this.emoji.setText(reaction.getEmoji());
|
this.emoji.setText(reaction.getDisplayEmoji());
|
||||||
|
|
||||||
if (reaction.getSender().isLocalNumber()) {
|
if (reaction.getSender().isLocalNumber()) {
|
||||||
this.recipient.setText(R.string.ReactionsRecipientAdapter_you);
|
this.recipient.setText(R.string.ReactionsRecipientAdapter_you);
|
||||||
|
@ -15,6 +15,7 @@ import androidx.annotation.Nullable;
|
|||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
|
||||||
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
@ -107,15 +108,16 @@ public class ReactionsConversationView extends LinearLayout {
|
|||||||
RecipientId selfId = Recipient.self().getId();
|
RecipientId selfId = Recipient.self().getId();
|
||||||
|
|
||||||
for (ReactionRecord record : records) {
|
for (ReactionRecord record : records) {
|
||||||
Reaction info = counters.get(record.getEmoji());
|
String baseEmoji = EmojiUtil.getCanonicalRepresentation(record.getEmoji());
|
||||||
|
Reaction info = counters.get(baseEmoji);
|
||||||
|
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
info = new Reaction(record.getEmoji(), 1, record.getDateReceived(), selfId.equals(record.getAuthor()));
|
info = new Reaction(baseEmoji, record.getEmoji(), 1, record.getDateReceived(), selfId.equals(record.getAuthor()));
|
||||||
} else {
|
} else {
|
||||||
info.update(record.getDateReceived(), selfId.equals(record.getAuthor()));
|
info.update(record.getEmoji(), record.getDateReceived(), selfId.equals(record.getAuthor()));
|
||||||
}
|
}
|
||||||
|
|
||||||
counters.put(record.getEmoji(), info);
|
counters.put(baseEmoji, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Reaction> reactions = new ArrayList<>(counters.values());
|
List<Reaction> reactions = new ArrayList<>(counters.values());
|
||||||
@ -126,7 +128,7 @@ public class ReactionsConversationView extends LinearLayout {
|
|||||||
List<Reaction> shortened = new ArrayList<>(3);
|
List<Reaction> shortened = new ArrayList<>(3);
|
||||||
shortened.add(reactions.get(0));
|
shortened.add(reactions.get(0));
|
||||||
shortened.add(reactions.get(1));
|
shortened.add(reactions.get(1));
|
||||||
shortened.add(Stream.of(reactions).skip(2).reduce(new Reaction(null, 0, 0, false), Reaction::merge));
|
shortened.add(Stream.of(reactions).skip(2).reduce(new Reaction(null, null, 0, 0, false), Reaction::merge));
|
||||||
|
|
||||||
return shortened;
|
return shortened;
|
||||||
} else {
|
} else {
|
||||||
@ -140,8 +142,8 @@ public class ReactionsConversationView extends LinearLayout {
|
|||||||
TextView countView = root.findViewById(R.id.reactions_pill_count);
|
TextView countView = root.findViewById(R.id.reactions_pill_count);
|
||||||
View spacer = root.findViewById(R.id.reactions_pill_spacer);
|
View spacer = root.findViewById(R.id.reactions_pill_spacer);
|
||||||
|
|
||||||
if (reaction.emoji != null) {
|
if (reaction.displayEmoji != null) {
|
||||||
emojiView.setText(reaction.emoji);
|
emojiView.setText(reaction.displayEmoji);
|
||||||
|
|
||||||
if (reaction.count > 1) {
|
if (reaction.count > 1) {
|
||||||
countView.setText(String.valueOf(reaction.count));
|
countView.setText(String.valueOf(reaction.count));
|
||||||
@ -166,19 +168,27 @@ public class ReactionsConversationView extends LinearLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class Reaction implements Comparable<Reaction> {
|
private static class Reaction implements Comparable<Reaction> {
|
||||||
private String emoji;
|
private String baseEmoji;
|
||||||
|
private String displayEmoji;
|
||||||
private int count;
|
private int count;
|
||||||
private long lastSeen;
|
private long lastSeen;
|
||||||
private boolean userWasSender;
|
private boolean userWasSender;
|
||||||
|
|
||||||
Reaction(@Nullable String emoji, int count, long lastSeen, boolean userWasSender) {
|
Reaction(@Nullable String baseEmoji, @Nullable String displayEmoji, int count, long lastSeen, boolean userWasSender) {
|
||||||
this.emoji = emoji;
|
this.baseEmoji = baseEmoji;
|
||||||
|
this.displayEmoji = displayEmoji;
|
||||||
this.count = count;
|
this.count = count;
|
||||||
this.lastSeen = lastSeen;
|
this.lastSeen = lastSeen;
|
||||||
this.userWasSender = userWasSender;
|
this.userWasSender = userWasSender;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(long lastSeen, boolean userWasSender) {
|
void update(@NonNull String displayEmoji, long lastSeen, boolean userWasSender) {
|
||||||
|
if (!this.userWasSender) {
|
||||||
|
if (userWasSender || lastSeen > this.lastSeen) {
|
||||||
|
this.displayEmoji = displayEmoji;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.count = this.count + 1;
|
this.count = this.count + 1;
|
||||||
this.lastSeen = Math.max(this.lastSeen, lastSeen);
|
this.lastSeen = Math.max(this.lastSeen, lastSeen);
|
||||||
this.userWasSender = this.userWasSender || userWasSender;
|
this.userWasSender = this.userWasSender || userWasSender;
|
||||||
|
@ -6,18 +6,16 @@ import android.os.Bundle;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.util.Pair;
|
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
import androidx.lifecycle.Transformations;
|
|
||||||
import androidx.loader.app.LoaderManager;
|
import androidx.loader.app.LoaderManager;
|
||||||
import androidx.loader.content.Loader;
|
import androidx.loader.content.Loader;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
|
import org.thoughtcrime.securesms.util.AbstractCursorLoader;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||||
@ -57,6 +55,7 @@ public class ReactionsLoader implements ReactionsViewModel.Repository, LoaderMan
|
|||||||
} else {
|
} else {
|
||||||
internalLiveData.postValue(Stream.of(record.getReactions())
|
internalLiveData.postValue(Stream.of(record.getReactions())
|
||||||
.map(reactionRecord -> new Reaction(Recipient.resolved(reactionRecord.getAuthor()),
|
.map(reactionRecord -> new Reaction(Recipient.resolved(reactionRecord.getAuthor()),
|
||||||
|
EmojiUtil.getCanonicalRepresentation(reactionRecord.getEmoji()),
|
||||||
reactionRecord.getEmoji(),
|
reactionRecord.getEmoji(),
|
||||||
reactionRecord.getDateReceived()))
|
reactionRecord.getDateReceived()))
|
||||||
.toList());
|
.toList());
|
||||||
@ -106,21 +105,27 @@ public class ReactionsLoader implements ReactionsViewModel.Repository, LoaderMan
|
|||||||
|
|
||||||
static class Reaction {
|
static class Reaction {
|
||||||
private final Recipient sender;
|
private final Recipient sender;
|
||||||
private final String emoji;
|
private final String baseEmoji;
|
||||||
|
private final String displayEmoji;
|
||||||
private final long timestamp;
|
private final long timestamp;
|
||||||
|
|
||||||
private Reaction(@NonNull Recipient sender, @NonNull String emoji, long timestamp) {
|
private Reaction(@NonNull Recipient sender, @NonNull String baseEmoji, @NonNull String displayEmoji, long timestamp) {
|
||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
this.emoji = emoji;
|
this.baseEmoji = baseEmoji;
|
||||||
this.timestamp = timestamp;
|
this.displayEmoji = displayEmoji;
|
||||||
|
this.timestamp = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull Recipient getSender() {
|
public @NonNull Recipient getSender() {
|
||||||
return sender;
|
return sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull String getEmoji() {
|
public @NonNull String getBaseEmoji() {
|
||||||
return emoji;
|
return baseEmoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull String getDisplayEmoji() {
|
||||||
|
return displayEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTimestamp() {
|
public long getTimestamp() {
|
||||||
|
@ -27,16 +27,16 @@ public class ReactionsViewModel extends ViewModel {
|
|||||||
return Transformations.switchMap(filterEmoji,
|
return Transformations.switchMap(filterEmoji,
|
||||||
emoji -> Transformations.map(repository.getReactions(),
|
emoji -> Transformations.map(repository.getReactions(),
|
||||||
reactions -> Stream.of(reactions)
|
reactions -> Stream.of(reactions)
|
||||||
.filter(reaction -> emoji == null || reaction.getEmoji().equals(emoji))
|
.filter(reaction -> emoji == null || reaction.getBaseEmoji().equals(emoji))
|
||||||
.toList()));
|
.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull LiveData<List<EmojiCount>> getEmojiCounts() {
|
public @NonNull LiveData<List<EmojiCount>> getEmojiCounts() {
|
||||||
return Transformations.map(repository.getReactions(),
|
return Transformations.map(repository.getReactions(),
|
||||||
reactionList -> Stream.of(reactionList)
|
reactionList -> Stream.of(reactionList)
|
||||||
.groupBy(Reaction::getEmoji)
|
.groupBy(Reaction::getBaseEmoji)
|
||||||
.sorted(this::compareReactions)
|
.sorted(this::compareReactions)
|
||||||
.map(entry -> new EmojiCount(entry.getKey(), entry.getValue().size()))
|
.map(entry -> new EmojiCount(entry.getKey(), getCountDisplayEmoji(entry.getValue()), entry.getValue().size()))
|
||||||
.toList());
|
.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +61,16 @@ public class ReactionsViewModel extends ViewModel {
|
|||||||
.orElse(-1L);
|
.orElse(-1L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @NonNull String getCountDisplayEmoji(@NonNull List<Reaction> reactions) {
|
||||||
|
for (Reaction reaction : reactions) {
|
||||||
|
if (reaction.getSender().isLocalNumber()) {
|
||||||
|
return reaction.getDisplayEmoji();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reactions.get(reactions.size() - 1).getDisplayEmoji();
|
||||||
|
}
|
||||||
|
|
||||||
interface Repository {
|
interface Repository {
|
||||||
LiveData<List<Reaction>> getReactions();
|
LiveData<List<Reaction>> getReactions();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user