NotificationsAdapter: implement custom emoji reacts

If the emoji_url field is specified in the notification data, it will be
used to represent the emoji in the notification text. As such, the
Notification entity data class has been updated accordingly, to match
the current implementation at AkkomaDev/Akkoma, which may later be
integrated by more Pleroma-based backends.

The SpannableBuilder formatting is a bit hacky, but it works. Ideally,
something like Phrase should be used for localization and Spannable
formatting. However, this would be a different undertaking of its own,
and would require more work on refactoring multiple parts of the
application code.

Signed-off-by: Hélène <pleroma-dev@helene.moe>
Signed-off-by: Adolfo Santiago <epoch@nixnetmail.com>
This commit is contained in:
Hélène 2022-06-25 14:07:07 +02:00 committed by Adolfo Santiago
parent e6b402b377
commit b85335e8d4
No known key found for this signature in database
GPG Key ID: 244D6F9A317B4A65
5 changed files with 50 additions and 18 deletions

View File

@ -540,8 +540,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
Notification.Type type = notificationViewData.getType();
Context context = message.getContext();
String wholeMessage;
Drawable icon;
SpannableStringBuilder builder = new SpannableStringBuilder();
switch (type) {
default:
case FAVOURITE: {
@ -552,7 +552,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
}
String format = context.getString(R.string.notification_favourite_format);
wholeMessage = String.format(format, displayName);
builder.append(String.format(format, displayName));
break;
}
case REBLOG: {
@ -563,7 +563,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
}
String format = context.getString(R.string.notification_reblog_format);
wholeMessage = String.format(format, displayName);
builder.append(String.format(format, displayName));
break;
}
case STATUS: {
@ -574,7 +574,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
}
String format = context.getString(R.string.notification_subscription_format);
wholeMessage = String.format(format, displayName);
builder.append(String.format(format, displayName));
break;
}
case EMOJI_REACTION: {
@ -586,15 +586,26 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
String format = context.getString(R.string.notification_emoji_format);
String emojiCode = notificationViewData.getEmoji();
wholeMessage = String.format(format, displayName, emojiCode);
builder.append(String.format(format, displayName, emojiCode));
final String emojiUrl = notificationViewData.getEmojiUrl();
if(emojiUrl != null) {
// terrible hack... ideally, there should be a CharSequence formatter
final int emojiPosition = format.indexOf("%s", 1)
- "%s".length() + displayName.length();
var span = CustomEmojiHelper.createEmojiSpan(emojiUrl, message, true);
builder.setSpan(span, emojiPosition, emojiPosition + emojiCode.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
break;
}
}
message.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
final SpannableString str = new SpannableString(wholeMessage);
str.setSpan(new StyleSpan(Typeface.BOLD), 0, displayName.length(),
builder.setSpan(new StyleSpan(Typeface.BOLD), 0, displayName.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
CharSequence emojifiedText = CustomEmojiHelper.emojify(str, notificationViewData.getAccount().getEmojis(), message, true);
CharSequence emojifiedText = CustomEmojiHelper.emojify(builder, notificationViewData.getAccount().getEmojis(), message, true);
message.setText(emojifiedText);
if (statusViewData != null) {

View File

@ -31,6 +31,7 @@ data class Notification(
val status: Status?,
val pleroma: PleromaNotification? = null,
val emoji: String? = null,
@SerializedName("emoji_url") val emojiUrl: String? = null,
@SerializedName("chat_message") val chatMessage: ChatMessage? = null,
@SerializedName("created_at") val createdAt: Date? = null,
val target: Account? = null) {

View File

@ -505,7 +505,8 @@ public class NotificationsFragment extends SFragment implements
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget());
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(),
viewdata.getEmojiUrl(), viewdata.getTarget());
notifications.setPairedItem(position, newViewData);
updateAdapter();
}
@ -539,7 +540,8 @@ public class NotificationsFragment extends SFragment implements
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget());
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(),
viewdata.getEmojiUrl(), viewdata.getTarget());
notifications.setPairedItem(position, newViewData);
updateAdapter();
@ -574,7 +576,8 @@ public class NotificationsFragment extends SFragment implements
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget());
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(),
viewdata.getEmojiUrl(), viewdata.getTarget());
notifications.setPairedItem(position, newViewData);
updateAdapter();
@ -603,7 +606,8 @@ public class NotificationsFragment extends SFragment implements
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget());
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(),
viewdata.getEmojiUrl(), viewdata.getTarget());
notifications.setPairedItem(position, newViewData);
updateAdapter();
@ -650,7 +654,8 @@ public class NotificationsFragment extends SFragment implements
.setIsExpanded(expanded)
.createStatusViewData();
NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(),
old.getId(), old.getAccount(), statusViewData, old.getEmoji(), old.getTarget());
old.getId(), old.getAccount(), statusViewData, old.getEmoji(),
old.getEmojiUrl(), old.getTarget());
notifications.setPairedItem(position, notificationViewData);
updateAdapter();
}
@ -664,7 +669,8 @@ public class NotificationsFragment extends SFragment implements
.setIsShowingSensitiveContent(isShowing)
.createStatusViewData();
NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(),
old.getId(), old.getAccount(), statusViewData, old.getEmoji(), old.getTarget());
old.getId(), old.getAccount(), statusViewData, old.getEmoji(),
old.getEmojiUrl(), old.getTarget());
notifications.setPairedItem(position, notificationViewData);
updateAdapter();
}
@ -678,7 +684,8 @@ public class NotificationsFragment extends SFragment implements
.setMuted(isMuted)
.createStatusViewData();
NotificationViewData notificationViewData = new NotificationViewData.Concrete(old.getType(),
old.getId(), old.getAccount(), statusViewData, old.getEmoji(), old.getTarget());
old.getId(), old.getAccount(), statusViewData, old.getEmoji(),
old.getEmojiUrl(), old.getTarget());
notifications.setPairedItem(position, notificationViewData);
updateAdapter();
}
@ -694,7 +701,8 @@ public class NotificationsFragment extends SFragment implements
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(), viewdata.getTarget());
viewDataBuilder.createStatusViewData(), viewdata.getEmoji(),
viewdata.getEmojiUrl(), viewdata.getTarget());
notifications.setPairedItem(position, newViewData);
}
@ -751,6 +759,7 @@ public class NotificationsFragment extends SFragment implements
concreteNotification.getAccount(),
updatedStatus,
concreteNotification.getEmoji(),
concreteNotification.getEmojiUrl(),
concreteNotification.getTarget()
);
notifications.setPairedItem(position, updatedNotification);
@ -1445,7 +1454,7 @@ public class NotificationsFragment extends SFragment implements
NotificationViewData.Concrete newViewData = new NotificationViewData.Concrete(
viewdata.getType(), viewdata.getId(), viewdata.getAccount(),
ViewDataUtils.statusToViewData(newStatus, false, false),
viewdata.getEmoji(), viewdata.getTarget());
viewdata.getEmoji(), viewdata.getEmojiUrl(), viewdata.getTarget());
notifications.setPairedItem(position, newViewData);
updateAdapter();

View File

@ -94,6 +94,7 @@ public final class ViewDataUtils {
alwaysOpenSpoiler
),
notification.getEmoji(),
notification.getEmojiUrl(),
notification.getTarget()
);
}

View File

@ -50,16 +50,20 @@ public abstract class NotificationViewData {
@Nullable
private final String emoji;
@Nullable
private final String emojiUrl;
@Nullable
private final Account target; // move notification
public Concrete(Notification.Type type, String id, Account account,
@Nullable StatusViewData.Concrete statusViewData,
@Nullable String emoji, @Nullable Account target) {
@Nullable String emoji, @Nullable String emojiUrl,
@Nullable Account target) {
this.type = type;
this.id = id;
this.account = account;
this.statusViewData = statusViewData;
this.emoji = emoji;
this.emojiUrl = emojiUrl;
this.target = target;
}
@ -85,6 +89,11 @@ public abstract class NotificationViewData {
return emoji;
}
@Nullable
public String getEmojiUrl() {
return emojiUrl;
}
@Nullable
public Account getTarget() {
return target;
@ -104,6 +113,7 @@ public abstract class NotificationViewData {
Objects.equals(id, concrete.id) &&
account.getId().equals(concrete.account.getId()) &&
(emoji != null && concrete.emoji != null && emoji.equals(concrete.emoji)) &&
(emojiUrl != null && concrete.emojiUrl != null && emojiUrl.equals(concrete.emojiUrl)) &&
(target != null && concrete.target != null && target.getId().equals(concrete.target.getId())) &&
(statusViewData == concrete.statusViewData ||
statusViewData != null &&