Fix: #795 通知のグルーピング時、リストの新着投稿通知が表示されない (#800)

This commit is contained in:
KMY(雪あすか) 2024-08-17 09:57:16 +09:00 committed by GitHub
parent f0acd992c5
commit c37aba43f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 110 additions and 6 deletions

View 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
}

View file

@ -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 {

View file

@ -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} />

View file

@ -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
/>
);

View file

@ -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':

View file

@ -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

View file

@ -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