parent
f0acd992c5
commit
c37aba43f9
7 changed files with 110 additions and 6 deletions
11
app/javascript/mastodon/api_types/dummy_types.ts
Normal file
11
app/javascript/mastodon/api_types/dummy_types.ts
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
import type { AccountWarningAction } from 'mastodon/models/notification_group';
|
import type { AccountWarningAction } from 'mastodon/models/notification_group';
|
||||||
|
|
||||||
import type { ApiAccountJSON } from './accounts';
|
import type { ApiAccountJSON } from './accounts';
|
||||||
|
import type { ApiListJSON_KmyDummy } from './dummy_types';
|
||||||
import type { ApiReportJSON } from './reports';
|
import type { ApiReportJSON } from './reports';
|
||||||
import type { ApiStatusJSON } from './statuses';
|
import type { ApiStatusJSON } from './statuses';
|
||||||
|
|
||||||
|
@ -17,6 +18,7 @@ export const allNotificationTypes = [
|
||||||
'status_reference',
|
'status_reference',
|
||||||
'poll',
|
'poll',
|
||||||
'status',
|
'status',
|
||||||
|
'list_status',
|
||||||
'update',
|
'update',
|
||||||
'admin.sign_up',
|
'admin.sign_up',
|
||||||
'admin.report',
|
'admin.report',
|
||||||
|
@ -29,6 +31,7 @@ export type NotificationWithStatusType =
|
||||||
| 'emoji_reaction'
|
| 'emoji_reaction'
|
||||||
| 'reblog'
|
| 'reblog'
|
||||||
| 'status'
|
| 'status'
|
||||||
|
| 'list_status'
|
||||||
| 'mention'
|
| 'mention'
|
||||||
| 'status_reference'
|
| 'status_reference'
|
||||||
| 'poll'
|
| 'poll'
|
||||||
|
@ -78,6 +81,7 @@ export interface BaseNotificationGroupJSON {
|
||||||
page_min_id?: string;
|
page_min_id?: string;
|
||||||
page_max_id?: string;
|
page_max_id?: string;
|
||||||
emoji_reaction_groups?: NotificationEmojiReactionGroupJSON[];
|
emoji_reaction_groups?: NotificationEmojiReactionGroupJSON[];
|
||||||
|
list?: ApiListJSON_KmyDummy;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON {
|
interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { NotificationEmojiReaction } from './notification_emoji_reaction';
|
||||||
import { NotificationFavourite } from './notification_favourite';
|
import { NotificationFavourite } from './notification_favourite';
|
||||||
import { NotificationFollow } from './notification_follow';
|
import { NotificationFollow } from './notification_follow';
|
||||||
import { NotificationFollowRequest } from './notification_follow_request';
|
import { NotificationFollowRequest } from './notification_follow_request';
|
||||||
|
import { NotificationListStatus } from './notification_list_status';
|
||||||
import { NotificationMention } from './notification_mention';
|
import { NotificationMention } from './notification_mention';
|
||||||
import { NotificationModerationWarning } from './notification_moderation_warning';
|
import { NotificationModerationWarning } from './notification_moderation_warning';
|
||||||
import { NotificationPoll } from './notification_poll';
|
import { NotificationPoll } from './notification_poll';
|
||||||
|
@ -132,6 +133,14 @@ export const NotificationGroup: React.FC<{
|
||||||
<NotificationStatus unread={unread} notification={notificationGroup} />
|
<NotificationStatus unread={unread} notification={notificationGroup} />
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case 'list_status':
|
||||||
|
content = (
|
||||||
|
<NotificationListStatus
|
||||||
|
unread={unread}
|
||||||
|
notification={notificationGroup}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
break;
|
||||||
case 'update':
|
case 'update':
|
||||||
content = (
|
content = (
|
||||||
<NotificationUpdate unread={unread} notification={notificationGroup} />
|
<NotificationUpdate unread={unread} notification={notificationGroup} />
|
||||||
|
|
|
@ -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 = (
|
||||||
|
<bdi>
|
||||||
|
<Link
|
||||||
|
className='notification__display-name'
|
||||||
|
href={`/lists/${list.id}`}
|
||||||
|
title={list.title}
|
||||||
|
to={`/lists/${list.id}`}
|
||||||
|
>
|
||||||
|
{list.title}
|
||||||
|
</Link>
|
||||||
|
</bdi>
|
||||||
|
);
|
||||||
|
values.listName = listLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormattedMessage
|
||||||
|
id='notification.list_status'
|
||||||
|
defaultMessage='{name} post is added to {listName}'
|
||||||
|
values={values}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return renderer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NotificationListStatus: React.FC<{
|
||||||
|
notification: NotificationGroupListStatus;
|
||||||
|
unread: boolean;
|
||||||
|
}> = ({ notification, unread }) => (
|
||||||
|
<NotificationWithStatus
|
||||||
|
type='list_status'
|
||||||
|
icon={NotificationsActiveIcon}
|
||||||
|
iconId='notifications-active'
|
||||||
|
accountIds={notification.sampleAccountIds}
|
||||||
|
count={notification.notifications_count}
|
||||||
|
statusId={notification.statusId}
|
||||||
|
labelRenderer={createLabelRenderer(notification)}
|
||||||
|
unread={unread}
|
||||||
|
muted
|
||||||
|
/>
|
||||||
|
);
|
|
@ -54,6 +54,8 @@ export type NotificationGroupEmojiReaction =
|
||||||
BaseNotificationWithStatus<'emoji_reaction'>;
|
BaseNotificationWithStatus<'emoji_reaction'>;
|
||||||
export type NotificationGroupReblog = BaseNotificationWithStatus<'reblog'>;
|
export type NotificationGroupReblog = BaseNotificationWithStatus<'reblog'>;
|
||||||
export type NotificationGroupStatus = BaseNotificationWithStatus<'status'>;
|
export type NotificationGroupStatus = BaseNotificationWithStatus<'status'>;
|
||||||
|
export type NotificationGroupListStatus =
|
||||||
|
BaseNotificationWithStatus<'list_status'>;
|
||||||
export type NotificationGroupMention = BaseNotificationWithStatus<'mention'>;
|
export type NotificationGroupMention = BaseNotificationWithStatus<'mention'>;
|
||||||
export type NotificationGroupStatusReference =
|
export type NotificationGroupStatusReference =
|
||||||
BaseNotificationWithStatus<'status_reference'>;
|
BaseNotificationWithStatus<'status_reference'>;
|
||||||
|
@ -103,6 +105,7 @@ export type NotificationGroup =
|
||||||
| NotificationGroupEmojiReaction
|
| NotificationGroupEmojiReaction
|
||||||
| NotificationGroupReblog
|
| NotificationGroupReblog
|
||||||
| NotificationGroupStatus
|
| NotificationGroupStatus
|
||||||
|
| NotificationGroupListStatus
|
||||||
| NotificationGroupMention
|
| NotificationGroupMention
|
||||||
| NotificationGroupStatusReference
|
| NotificationGroupStatusReference
|
||||||
| NotificationGroupPoll
|
| NotificationGroupPoll
|
||||||
|
@ -161,6 +164,7 @@ export function createNotificationGroupFromJSON(
|
||||||
case 'favourite':
|
case 'favourite':
|
||||||
case 'reblog':
|
case 'reblog':
|
||||||
case 'status':
|
case 'status':
|
||||||
|
case 'list_status':
|
||||||
case 'mention':
|
case 'mention':
|
||||||
case 'status_reference':
|
case 'status_reference':
|
||||||
case 'poll':
|
case 'poll':
|
||||||
|
@ -244,6 +248,7 @@ export function createNotificationGroupFromNotificationJSON(
|
||||||
case 'favourite':
|
case 'favourite':
|
||||||
case 'reblog':
|
case 'reblog':
|
||||||
case 'status':
|
case 'status':
|
||||||
|
case 'list_status':
|
||||||
case 'mention':
|
case 'mention':
|
||||||
case 'status_reference':
|
case 'status_reference':
|
||||||
case 'poll':
|
case 'poll':
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class NotificationGroup < ActiveModelSerializers::Model
|
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`
|
# Try to keep this consistent with `app/javascript/mastodon/models/notification_group.ts`
|
||||||
SAMPLE_ACCOUNTS_SIZE = 8
|
SAMPLE_ACCOUNTS_SIZE = 8
|
||||||
|
@ -18,17 +18,19 @@ class NotificationGroup < ActiveModelSerializers::Model
|
||||||
scope = scope.where(id: ..max_id) if max_id.present?
|
scope = scope.where(id: ..max_id) if max_id.present?
|
||||||
|
|
||||||
# Ideally, we would not load accounts for each notification group
|
# 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
|
most_recent_id = most_recent_notifications.first.id
|
||||||
sample_accounts = most_recent_notifications.map(&:from_account)
|
sample_accounts = most_recent_notifications.map(&:from_account)
|
||||||
emoji_reaction_groups = extract_emoji_reaction_pair(
|
emoji_reaction_groups = extract_emoji_reaction_pair(
|
||||||
scope.order(id: :desc).includes(emoji_reaction: :account).take(SAMPLE_ACCOUNTS_SIZE_FOR_EMOJI_REACTION)
|
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
|
notifications_count = scope.count
|
||||||
else
|
else
|
||||||
most_recent_id = notification.id
|
most_recent_id = notification.id
|
||||||
sample_accounts = [notification.from_account]
|
sample_accounts = [notification.from_account]
|
||||||
emoji_reaction_groups = extract_emoji_reaction_pair([notification])
|
emoji_reaction_groups = extract_emoji_reaction_pair([notification])
|
||||||
|
list = pick_list([notification])
|
||||||
notifications_count = 1
|
notifications_count = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -37,6 +39,7 @@ class NotificationGroup < ActiveModelSerializers::Model
|
||||||
group_key: notification.group_key || "ungrouped-#{notification.id}",
|
group_key: notification.group_key || "ungrouped-#{notification.id}",
|
||||||
sample_accounts: sample_accounts,
|
sample_accounts: sample_accounts,
|
||||||
emoji_reaction_groups: emoji_reaction_groups,
|
emoji_reaction_groups: emoji_reaction_groups,
|
||||||
|
list: list,
|
||||||
notifications_count: notifications_count,
|
notifications_count: notifications_count,
|
||||||
most_recent_notification_id: most_recent_id
|
most_recent_notification_id: most_recent_id
|
||||||
)
|
)
|
||||||
|
@ -50,14 +53,23 @@ class NotificationGroup < ActiveModelSerializers::Model
|
||||||
to: :notification, prefix: false
|
to: :notification, prefix: false
|
||||||
|
|
||||||
def self.extract_emoji_reaction_pair(scope)
|
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
|
return [] unless scope.first.type == :emoji_reaction
|
||||||
|
|
||||||
|
scope = scope.filter { |g| g.emoji_reaction.present? }
|
||||||
|
return [] if scope.empty?
|
||||||
|
|
||||||
scope
|
scope
|
||||||
.each_with_object({}) { |e, h| h[e.emoji_reaction.name] = (h[e.emoji_reaction.name] || []).push(e.emoji_reaction) }
|
.each_with_object({}) { |e, h| h[e.emoji_reaction.name] = (h[e.emoji_reaction.name] || []).push(e.emoji_reaction) }
|
||||||
.to_a
|
.to_a
|
||||||
.map { |pair| NotificationEmojiReactionGroup.new(emoji_reaction: pair[1].first, sample_accounts: pair[1].take(SAMPLE_ACCOUNTS_SIZE).map(&:account)) }
|
.map { |pair| NotificationEmojiReactionGroup.new(emoji_reaction: pair[1].first, sample_accounts: pair[1].take(SAMPLE_ACCOUNTS_SIZE).map(&:account)) }
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -13,6 +13,7 @@ class REST::NotificationGroupSerializer < ActiveModel::Serializer
|
||||||
belongs_to :report, if: :report_type?, serializer: REST::ReportSerializer
|
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_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
|
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
|
def sample_account_ids
|
||||||
object.sample_accounts.pluck(:id).map(&:to_s)
|
object.sample_accounts.pluck(:id).map(&:to_s)
|
||||||
|
@ -23,7 +24,7 @@ class REST::NotificationGroupSerializer < ActiveModel::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def status_type?
|
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
|
end
|
||||||
|
|
||||||
def report_type?
|
def report_type?
|
||||||
|
@ -34,6 +35,10 @@ class REST::NotificationGroupSerializer < ActiveModel::Serializer
|
||||||
object.type == :emoji_reaction
|
object.type == :emoji_reaction
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def list_status_type?
|
||||||
|
object.type == :list_status
|
||||||
|
end
|
||||||
|
|
||||||
def relationship_severance_event?
|
def relationship_severance_event?
|
||||||
object.type == :severed_relationships
|
object.type == :severed_relationships
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue