Loading app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Notification.java +1 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ public class Notification { public Filter filteredByApp; public PositionFetchMore positionFetchMore = PositionFetchMore.BOTTOM; public List<Notification> relatedNotifications; public String group_key; public transient boolean isFetchMore; /** Loading app/src/main/java/app/fedilab/android/mastodon/client/entities/api/NotificationGroup.java +1 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ public class NotificationGroup { notification.id = group.most_recent_notification_id; notification.type = group.type; notification.created_at = group.latest_page_notification_at; notification.group_key = group.group_key; // Resolve status from the deduplicated list if (group.status_id != null && results.statuses != null) { Loading app/src/main/java/app/fedilab/android/mastodon/ui/drawer/NotificationAdapter.java +27 −0 Original line number Diff line number Diff line Loading @@ -281,6 +281,33 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH holderFollow.binding.title.setText(String.format(Locale.getDefault(), "%s %s", notification.account.display_name, context.getString(R.string.notif_signed_up))); default -> holderFollow.binding.title.setText(R.string.follow); } if (notification.relatedNotifications != null && !notification.relatedNotifications.isEmpty() && (itemViewType == TYPE_FOLLOW || itemViewType == TYPE_FOLLOW_REQUEST)) { holderFollow.binding.typeOfConcat.setText(R.string.also_followed_by); holderFollow.binding.relatedAccounts.removeAllViews(); for (Notification relativeNotif : notification.relatedNotifications) { if (relativeNotif.account != null) { NotificationsRelatedAccountsBinding notificationsRelatedAccountsBinding = NotificationsRelatedAccountsBinding.inflate(LayoutInflater.from(context)); MastodonHelper.loadPPMastodon(notificationsRelatedAccountsBinding.profilePicture, relativeNotif.account); notificationsRelatedAccountsBinding.acc.setText(relativeNotif.account.username); notificationsRelatedAccountsBinding.relatedAccountContainer.setOnClickListener(v -> { Intent intent = new Intent(context, ProfileActivity.class); Bundle args = new Bundle(); args.putSerializable(Helper.ARG_ACCOUNT, relativeNotif.account); new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { Bundle bundle = new Bundle(); bundle.putLong(Helper.ARG_INTENT_ID, bundleId); intent.putExtras(bundle); context.startActivity(intent); }); }); holderFollow.binding.relatedAccounts.addView(notificationsRelatedAccountsBinding.getRoot()); } } holderFollow.binding.otherAccounts.setVisibility(View.VISIBLE); } else { holderFollow.binding.otherAccounts.setVisibility(View.GONE); } app.fedilab.android.mastodon.client.entities.app.Account.API notifApi = BaseMainActivity.api; if (notifApi == null && Helper.getCurrentAccount(context) != null) { notifApi = Helper.getCurrentAccount(context).api; Loading app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonNotification.java +30 −19 Original line number Diff line number Diff line Loading @@ -720,9 +720,21 @@ public class FragmentMastodonNotification extends Fragment implements Notificati int insertedNotifications = 0; if (notificationsReceived != null && !notificationsReceived.isEmpty()) { for (Notification notificationReceived : notificationsReceived) { if (notificationList == null) { continue; } if (notificationReceived.group_key != null) { for (int i = 0; i < notificationList.size(); i++) { if (notificationReceived.group_key.equals(notificationList.get(i).group_key)) { notificationList.remove(i); notificationAdapter.notifyItemRemoved(i); break; } } } int position = 0; //We loop through messages already in the timeline if (notificationList != null) { notificationAdapter.notifyItemRangeChanged(0, notificationList.size()); for (Notification notificationsAlreadyPresent : notificationList) { //We compare the date of each status and we only add status having a date greater than the another, it is inserted at this position Loading @@ -746,7 +758,6 @@ public class FragmentMastodonNotification extends Fragment implements Notificati } } } } return insertedNotifications; } Loading app/src/main/java/app/fedilab/android/mastodon/viewmodel/mastodon/NotificationsVM.java +25 −0 Original line number Diff line number Diff line Loading @@ -213,6 +213,13 @@ public class NotificationsVM extends AndroidViewModel { try { notificationsDb = statusCacheDAO.getNotifications(timelineParams.excludeType, timelineParams.instance, timelineParams.userId, timelineParams.maxId, timelineParams.minId, timelineParams.sinceId); if (notificationsDb != null && notificationsDb.size() > 0) { notificationsDb = deduplicateByGroupKey(notificationsDb); for (Notification notification : notificationsDb) { if (notification.group_key != null) { notifications.groupedByServer = true; break; } } if (timelineNotification != null) { List<Notification> notPresentNotifications = new ArrayList<>(); for (Notification notification : notificationsDb) { Loading Loading @@ -242,6 +249,24 @@ public class NotificationsVM extends AndroidViewModel { return notificationsMutableLiveData; } private List<Notification> deduplicateByGroupKey(List<Notification> notifications) { Map<String, Notification> groupKeyMap = new HashMap<>(); List<Notification> result = new ArrayList<>(); for (Notification notification : notifications) { if (notification.group_key != null) { Notification existing = groupKeyMap.get(notification.group_key); if (existing == null || Helper.compareTo(notification.id, existing.id) > 0) { groupKeyMap.put(notification.group_key, notification); } } else { result.add(notification); } } result.addAll(groupKeyMap.values()); sortDesc(result); return result; } /** * Get a notification for the authenticated account by its id * Loading Loading
app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Notification.java +1 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ public class Notification { public Filter filteredByApp; public PositionFetchMore positionFetchMore = PositionFetchMore.BOTTOM; public List<Notification> relatedNotifications; public String group_key; public transient boolean isFetchMore; /** Loading
app/src/main/java/app/fedilab/android/mastodon/client/entities/api/NotificationGroup.java +1 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ public class NotificationGroup { notification.id = group.most_recent_notification_id; notification.type = group.type; notification.created_at = group.latest_page_notification_at; notification.group_key = group.group_key; // Resolve status from the deduplicated list if (group.status_id != null && results.statuses != null) { Loading
app/src/main/java/app/fedilab/android/mastodon/ui/drawer/NotificationAdapter.java +27 −0 Original line number Diff line number Diff line Loading @@ -281,6 +281,33 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH holderFollow.binding.title.setText(String.format(Locale.getDefault(), "%s %s", notification.account.display_name, context.getString(R.string.notif_signed_up))); default -> holderFollow.binding.title.setText(R.string.follow); } if (notification.relatedNotifications != null && !notification.relatedNotifications.isEmpty() && (itemViewType == TYPE_FOLLOW || itemViewType == TYPE_FOLLOW_REQUEST)) { holderFollow.binding.typeOfConcat.setText(R.string.also_followed_by); holderFollow.binding.relatedAccounts.removeAllViews(); for (Notification relativeNotif : notification.relatedNotifications) { if (relativeNotif.account != null) { NotificationsRelatedAccountsBinding notificationsRelatedAccountsBinding = NotificationsRelatedAccountsBinding.inflate(LayoutInflater.from(context)); MastodonHelper.loadPPMastodon(notificationsRelatedAccountsBinding.profilePicture, relativeNotif.account); notificationsRelatedAccountsBinding.acc.setText(relativeNotif.account.username); notificationsRelatedAccountsBinding.relatedAccountContainer.setOnClickListener(v -> { Intent intent = new Intent(context, ProfileActivity.class); Bundle args = new Bundle(); args.putSerializable(Helper.ARG_ACCOUNT, relativeNotif.account); new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { Bundle bundle = new Bundle(); bundle.putLong(Helper.ARG_INTENT_ID, bundleId); intent.putExtras(bundle); context.startActivity(intent); }); }); holderFollow.binding.relatedAccounts.addView(notificationsRelatedAccountsBinding.getRoot()); } } holderFollow.binding.otherAccounts.setVisibility(View.VISIBLE); } else { holderFollow.binding.otherAccounts.setVisibility(View.GONE); } app.fedilab.android.mastodon.client.entities.app.Account.API notifApi = BaseMainActivity.api; if (notifApi == null && Helper.getCurrentAccount(context) != null) { notifApi = Helper.getCurrentAccount(context).api; Loading
app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonNotification.java +30 −19 Original line number Diff line number Diff line Loading @@ -720,9 +720,21 @@ public class FragmentMastodonNotification extends Fragment implements Notificati int insertedNotifications = 0; if (notificationsReceived != null && !notificationsReceived.isEmpty()) { for (Notification notificationReceived : notificationsReceived) { if (notificationList == null) { continue; } if (notificationReceived.group_key != null) { for (int i = 0; i < notificationList.size(); i++) { if (notificationReceived.group_key.equals(notificationList.get(i).group_key)) { notificationList.remove(i); notificationAdapter.notifyItemRemoved(i); break; } } } int position = 0; //We loop through messages already in the timeline if (notificationList != null) { notificationAdapter.notifyItemRangeChanged(0, notificationList.size()); for (Notification notificationsAlreadyPresent : notificationList) { //We compare the date of each status and we only add status having a date greater than the another, it is inserted at this position Loading @@ -746,7 +758,6 @@ public class FragmentMastodonNotification extends Fragment implements Notificati } } } } return insertedNotifications; } Loading
app/src/main/java/app/fedilab/android/mastodon/viewmodel/mastodon/NotificationsVM.java +25 −0 Original line number Diff line number Diff line Loading @@ -213,6 +213,13 @@ public class NotificationsVM extends AndroidViewModel { try { notificationsDb = statusCacheDAO.getNotifications(timelineParams.excludeType, timelineParams.instance, timelineParams.userId, timelineParams.maxId, timelineParams.minId, timelineParams.sinceId); if (notificationsDb != null && notificationsDb.size() > 0) { notificationsDb = deduplicateByGroupKey(notificationsDb); for (Notification notification : notificationsDb) { if (notification.group_key != null) { notifications.groupedByServer = true; break; } } if (timelineNotification != null) { List<Notification> notPresentNotifications = new ArrayList<>(); for (Notification notification : notificationsDb) { Loading Loading @@ -242,6 +249,24 @@ public class NotificationsVM extends AndroidViewModel { return notificationsMutableLiveData; } private List<Notification> deduplicateByGroupKey(List<Notification> notifications) { Map<String, Notification> groupKeyMap = new HashMap<>(); List<Notification> result = new ArrayList<>(); for (Notification notification : notifications) { if (notification.group_key != null) { Notification existing = groupKeyMap.get(notification.group_key); if (existing == null || Helper.compareTo(notification.id, existing.id) > 0) { groupKeyMap.put(notification.group_key, notification); } } else { result.add(notification); } } result.addAll(groupKeyMap.values()); sortDesc(result); return result; } /** * Get a notification for the authenticated account by its id * Loading