diff --git a/app/javascript/mastodon/api_types/dummy_types.ts b/app/javascript/mastodon/api_types/dummy_types.ts
new file mode 100644
index 0000000000..1f8c4db6f2
--- /dev/null
+++ b/app/javascript/mastodon/api_types/dummy_types.ts
@@ -0,0 +1,11 @@
+// A similar definition will eventually be added in the main house. These definitions will replace it.
+
+export interface ApiListJSON_KmyDummy {
+ id: string;
+ title: string;
+ exclusive: boolean;
+ notify: boolean;
+
+ // replies_policy
+ // antennas
+}
diff --git a/app/javascript/mastodon/api_types/notifications.ts b/app/javascript/mastodon/api_types/notifications.ts
index 114f20b48c..f384817b4b 100644
--- a/app/javascript/mastodon/api_types/notifications.ts
+++ b/app/javascript/mastodon/api_types/notifications.ts
@@ -3,6 +3,7 @@
import type { AccountWarningAction } from 'mastodon/models/notification_group';
import type { ApiAccountJSON } from './accounts';
+import type { ApiListJSON_KmyDummy } from './dummy_types';
import type { ApiReportJSON } from './reports';
import type { ApiStatusJSON } from './statuses';
@@ -17,6 +18,7 @@ export const allNotificationTypes = [
'status_reference',
'poll',
'status',
+ 'list_status',
'update',
'admin.sign_up',
'admin.report',
@@ -29,6 +31,7 @@ export type NotificationWithStatusType =
| 'emoji_reaction'
| 'reblog'
| 'status'
+ | 'list_status'
| 'mention'
| 'status_reference'
| 'poll'
@@ -78,6 +81,7 @@ export interface BaseNotificationGroupJSON {
page_min_id?: string;
page_max_id?: string;
emoji_reaction_groups?: NotificationEmojiReactionGroupJSON[];
+ list?: ApiListJSON_KmyDummy;
}
interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON {
diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_group.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_group.tsx
index a21c73e961..317ed56f42 100644
--- a/app/javascript/mastodon/features/notifications_v2/components/notification_group.tsx
+++ b/app/javascript/mastodon/features/notifications_v2/components/notification_group.tsx
@@ -13,6 +13,7 @@ import { NotificationEmojiReaction } from './notification_emoji_reaction';
import { NotificationFavourite } from './notification_favourite';
import { NotificationFollow } from './notification_follow';
import { NotificationFollowRequest } from './notification_follow_request';
+import { NotificationListStatus } from './notification_list_status';
import { NotificationMention } from './notification_mention';
import { NotificationModerationWarning } from './notification_moderation_warning';
import { NotificationPoll } from './notification_poll';
@@ -132,6 +133,14 @@ export const NotificationGroup: React.FC<{
);
break;
+ case 'list_status':
+ content = (
+
+ );
+ break;
case 'update':
content = (
diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_list_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_list_status.tsx
new file mode 100644
index 0000000000..60230874f6
--- /dev/null
+++ b/app/javascript/mastodon/features/notifications_v2/components/notification_list_status.tsx
@@ -0,0 +1,58 @@
+import { FormattedMessage } from 'react-intl';
+
+import { Link } from 'react-router-dom';
+
+import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react';
+import type { NotificationGroupListStatus } from 'mastodon/models/notification_group';
+
+import type { LabelRenderer } from './notification_group_with_status';
+import { NotificationWithStatus } from './notification_with_status';
+
+const createLabelRenderer = (
+ notification: NotificationGroupListStatus,
+): LabelRenderer => {
+ const renderer: LabelRenderer = (values) => {
+ const list = notification.list;
+ if (list) {
+ const listLink = (
+
+
+ {list.title}
+
+
+ );
+ values.listName = listLink;
+ }
+
+ return (
+
+ );
+ };
+ return renderer;
+};
+
+export const NotificationListStatus: React.FC<{
+ notification: NotificationGroupListStatus;
+ unread: boolean;
+}> = ({ notification, unread }) => (
+
+);
diff --git a/app/javascript/mastodon/models/notification_group.ts b/app/javascript/mastodon/models/notification_group.ts
index 402b1f3729..8ed43db685 100644
--- a/app/javascript/mastodon/models/notification_group.ts
+++ b/app/javascript/mastodon/models/notification_group.ts
@@ -54,6 +54,8 @@ export type NotificationGroupEmojiReaction =
BaseNotificationWithStatus<'emoji_reaction'>;
export type NotificationGroupReblog = BaseNotificationWithStatus<'reblog'>;
export type NotificationGroupStatus = BaseNotificationWithStatus<'status'>;
+export type NotificationGroupListStatus =
+ BaseNotificationWithStatus<'list_status'>;
export type NotificationGroupMention = BaseNotificationWithStatus<'mention'>;
export type NotificationGroupStatusReference =
BaseNotificationWithStatus<'status_reference'>;
@@ -103,6 +105,7 @@ export type NotificationGroup =
| NotificationGroupEmojiReaction
| NotificationGroupReblog
| NotificationGroupStatus
+ | NotificationGroupListStatus
| NotificationGroupMention
| NotificationGroupStatusReference
| NotificationGroupPoll
@@ -161,6 +164,7 @@ export function createNotificationGroupFromJSON(
case 'favourite':
case 'reblog':
case 'status':
+ case 'list_status':
case 'mention':
case 'status_reference':
case 'poll':
@@ -244,6 +248,7 @@ export function createNotificationGroupFromNotificationJSON(
case 'favourite':
case 'reblog':
case 'status':
+ case 'list_status':
case 'mention':
case 'status_reference':
case 'poll':
diff --git a/app/models/notification_group.rb b/app/models/notification_group.rb
index a66f352fd3..e6634fe3ce 100644
--- a/app/models/notification_group.rb
+++ b/app/models/notification_group.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class NotificationGroup < ActiveModelSerializers::Model
- attributes :group_key, :sample_accounts, :notifications_count, :notification, :most_recent_notification_id, :emoji_reaction_groups
+ attributes :group_key, :sample_accounts, :notifications_count, :notification, :most_recent_notification_id, :emoji_reaction_groups, :list
# Try to keep this consistent with `app/javascript/mastodon/models/notification_group.ts`
SAMPLE_ACCOUNTS_SIZE = 8
@@ -18,17 +18,19 @@ class NotificationGroup < ActiveModelSerializers::Model
scope = scope.where(id: ..max_id) if max_id.present?
# Ideally, we would not load accounts for each notification group
- most_recent_notifications = scope.order(id: :desc).includes(:from_account).take(SAMPLE_ACCOUNTS_SIZE)
+ most_recent_notifications = scope.order(id: :desc).includes(:from_account, :list_status).take(SAMPLE_ACCOUNTS_SIZE)
most_recent_id = most_recent_notifications.first.id
sample_accounts = most_recent_notifications.map(&:from_account)
emoji_reaction_groups = extract_emoji_reaction_pair(
scope.order(id: :desc).includes(emoji_reaction: :account).take(SAMPLE_ACCOUNTS_SIZE_FOR_EMOJI_REACTION)
)
+ list = pick_list(most_recent_notifications)
notifications_count = scope.count
else
most_recent_id = notification.id
sample_accounts = [notification.from_account]
emoji_reaction_groups = extract_emoji_reaction_pair([notification])
+ list = pick_list([notification])
notifications_count = 1
end
@@ -37,6 +39,7 @@ class NotificationGroup < ActiveModelSerializers::Model
group_key: notification.group_key || "ungrouped-#{notification.id}",
sample_accounts: sample_accounts,
emoji_reaction_groups: emoji_reaction_groups,
+ list: list,
notifications_count: notifications_count,
most_recent_notification_id: most_recent_id
)
@@ -50,14 +53,23 @@ class NotificationGroup < ActiveModelSerializers::Model
to: :notification, prefix: false
def self.extract_emoji_reaction_pair(scope)
- scope = scope.filter { |g| g.emoji_reaction.present? }
-
- return [] if scope.empty?
return [] unless scope.first.type == :emoji_reaction
+ scope = scope.filter { |g| g.emoji_reaction.present? }
+ return [] if scope.empty?
+
scope
.each_with_object({}) { |e, h| h[e.emoji_reaction.name] = (h[e.emoji_reaction.name] || []).push(e.emoji_reaction) }
.to_a
.map { |pair| NotificationEmojiReactionGroup.new(emoji_reaction: pair[1].first, sample_accounts: pair[1].take(SAMPLE_ACCOUNTS_SIZE).map(&:account)) }
end
+
+ def self.pick_list(scope)
+ return [] unless scope.first.type == :list_status
+
+ scope = scope.filter { |g| g.list_status.present? }
+ return [] if scope.empty?
+
+ scope.first.list_status.list
+ end
end
diff --git a/app/serializers/rest/notification_group_serializer.rb b/app/serializers/rest/notification_group_serializer.rb
index e10dfded88..4733d73e20 100644
--- a/app/serializers/rest/notification_group_serializer.rb
+++ b/app/serializers/rest/notification_group_serializer.rb
@@ -13,6 +13,7 @@ class REST::NotificationGroupSerializer < ActiveModel::Serializer
belongs_to :report, if: :report_type?, serializer: REST::ReportSerializer
belongs_to :account_relationship_severance_event, key: :event, if: :relationship_severance_event?, serializer: REST::AccountRelationshipSeveranceEventSerializer
belongs_to :account_warning, key: :moderation_warning, if: :moderation_warning_event?, serializer: REST::AccountWarningSerializer
+ has_one :list, if: :list_status_type?, serializer: REST::ListSerializer
def sample_account_ids
object.sample_accounts.pluck(:id).map(&:to_s)
@@ -23,7 +24,7 @@ class REST::NotificationGroupSerializer < ActiveModel::Serializer
end
def status_type?
- [:favourite, :emoji_reaction, :reblog, :status, :mention, :status_reference, :poll, :update].include?(object.type)
+ [:favourite, :emoji_reaction, :reblog, :status, :mention, :status_reference, :poll, :update, :list_status].include?(object.type)
end
def report_type?
@@ -34,6 +35,10 @@ class REST::NotificationGroupSerializer < ActiveModel::Serializer
object.type == :emoji_reaction
end
+ def list_status_type?
+ object.type == :list_status
+ end
+
def relationship_severance_event?
object.type == :severed_relationships
end