Change: 絵文字リアクションの通知のグループ化で、アカウントを絵文字の種類ごとに表示 (#796)
* Change: 絵文字リアクションの通知のグループ化で、アカウントを絵文字の種類ごとに表示 * Fix lint * アカウントの一括取得数を制限 * ストリーミング対応 * Fix * Fix * Fix * Fix some problems * Fix
This commit is contained in:
parent
5dec110dec
commit
f14c2d3ada
10 changed files with 258 additions and 7 deletions
|
@ -43,12 +43,29 @@ export type NotificationType =
|
|||
| 'admin.sign_up'
|
||||
| 'admin.report';
|
||||
|
||||
export interface NotifyEmojiReactionJSON {
|
||||
name: string;
|
||||
count: number;
|
||||
me: boolean;
|
||||
url?: string;
|
||||
static_url?: string;
|
||||
domain?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export interface NotificationEmojiReactionGroupJSON {
|
||||
emoji_reaction: NotifyEmojiReactionJSON;
|
||||
sample_account_ids: string[];
|
||||
}
|
||||
|
||||
export interface BaseNotificationJSON {
|
||||
id: string;
|
||||
type: NotificationType;
|
||||
created_at: string;
|
||||
group_key: string;
|
||||
account: ApiAccountJSON;
|
||||
emoji_reaction?: NotifyEmojiReactionJSON;
|
||||
}
|
||||
|
||||
export interface BaseNotificationGroupJSON {
|
||||
|
@ -60,6 +77,7 @@ export interface BaseNotificationGroupJSON {
|
|||
most_recent_notification_id: string;
|
||||
page_min_id?: string;
|
||||
page_max_id?: string;
|
||||
emoji_reaction_groups?: NotificationEmojiReactionGroupJSON[];
|
||||
}
|
||||
|
||||
interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON {
|
||||
|
@ -70,6 +88,7 @@ interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON {
|
|||
interface NotificationWithStatusJSON extends BaseNotificationJSON {
|
||||
type: NotificationWithStatusType;
|
||||
status: ApiStatusJSON;
|
||||
emoji_reaction?: NotifyEmojiReactionJSON;
|
||||
}
|
||||
|
||||
interface ReportNotificationGroupJSON extends BaseNotificationGroupJSON {
|
||||
|
|
|
@ -32,6 +32,7 @@ export const NotificationEmojiReaction: React.FC<{
|
|||
icon={EmojiReactionIcon}
|
||||
iconId='star'
|
||||
accountIds={notification.sampleAccountIds}
|
||||
emojiReactionGroups={notification.emojiReactionGroups}
|
||||
statusId={notification.statusId}
|
||||
timestamp={notification.latest_page_notification_at}
|
||||
count={notification.notifications_count}
|
||||
|
|
|
@ -6,9 +6,11 @@ import { HotKeys } from 'react-hotkeys';
|
|||
|
||||
import { replyComposeById } from 'mastodon/actions/compose';
|
||||
import { navigateToStatus } from 'mastodon/actions/statuses';
|
||||
import EmojiView from 'mastodon/components/emoji_view';
|
||||
import type { IconProp } from 'mastodon/components/icon';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||
import type { EmojiReactionGroup } from 'mastodon/models/notification_group';
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import { AvatarGroup } from './avatar_group';
|
||||
|
@ -26,6 +28,7 @@ export const NotificationGroupWithStatus: React.FC<{
|
|||
actions?: JSX.Element;
|
||||
count: number;
|
||||
accountIds: string[];
|
||||
emojiReactionGroups?: EmojiReactionGroup[];
|
||||
timestamp: string;
|
||||
labelRenderer: LabelRenderer;
|
||||
labelSeeMoreHref?: string;
|
||||
|
@ -36,6 +39,7 @@ export const NotificationGroupWithStatus: React.FC<{
|
|||
iconId,
|
||||
timestamp,
|
||||
accountIds,
|
||||
emojiReactionGroups,
|
||||
actions,
|
||||
count,
|
||||
statusId,
|
||||
|
@ -89,11 +93,28 @@ export const NotificationGroupWithStatus: React.FC<{
|
|||
|
||||
<div className='notification-group__main'>
|
||||
<div className='notification-group__main__header'>
|
||||
<div className='notification-group__main__header__wrapper'>
|
||||
<AvatarGroup accountIds={accountIds} />
|
||||
{emojiReactionGroups?.map((group) => (
|
||||
<div key={group.emoji.name}>
|
||||
<div className='notification-group__main__header__wrapper__for_emoji_reaction'>
|
||||
<EmojiView
|
||||
name={group.emoji.name}
|
||||
url={group.emoji.url}
|
||||
staticUrl={group.emoji.static_url}
|
||||
/>
|
||||
<AvatarGroup accountIds={group.sampleAccountIds} />
|
||||
|
||||
{actions}
|
||||
</div>
|
||||
{actions}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{!emojiReactionGroups && (
|
||||
<div className='notification-group__main__header__wrapper'>
|
||||
<AvatarGroup accountIds={accountIds} />
|
||||
|
||||
{actions}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className='notification-group__main__header__label'>
|
||||
{label}
|
||||
|
|
|
@ -6,6 +6,8 @@ import type {
|
|||
ApiNotificationJSON,
|
||||
NotificationType,
|
||||
NotificationWithStatusType,
|
||||
NotificationEmojiReactionGroupJSON,
|
||||
NotifyEmojiReactionJSON,
|
||||
} from 'mastodon/api_types/notifications';
|
||||
import type { ApiReportJSON } from 'mastodon/api_types/reports';
|
||||
|
||||
|
@ -22,6 +24,23 @@ interface BaseNotificationWithStatus<Type extends NotificationWithStatusType>
|
|||
extends BaseNotificationGroup {
|
||||
type: Type;
|
||||
statusId: string;
|
||||
emojiReactionGroups?: EmojiReactionGroup[];
|
||||
}
|
||||
|
||||
interface EmojiInfo {
|
||||
name: string;
|
||||
count: number;
|
||||
me: boolean;
|
||||
url?: string;
|
||||
static_url?: string;
|
||||
domain?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export interface EmojiReactionGroup {
|
||||
emoji: EmojiInfo;
|
||||
sampleAccountIds: string[];
|
||||
}
|
||||
|
||||
interface BaseNotification<Type extends NotificationType>
|
||||
|
@ -119,6 +138,20 @@ function createAccountRelationshipSeveranceEventFromJSON(
|
|||
return eventJson;
|
||||
}
|
||||
|
||||
function createEmojiReactionGroupsFromJSON(
|
||||
json: NotifyEmojiReactionJSON | undefined,
|
||||
sampleAccountIds: string[],
|
||||
): EmojiReactionGroup[] {
|
||||
if (typeof json === 'undefined') return [];
|
||||
|
||||
return [
|
||||
{
|
||||
emoji: json,
|
||||
sampleAccountIds,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function createNotificationGroupFromJSON(
|
||||
groupJson: ApiNotificationGroupJSON,
|
||||
): NotificationGroup {
|
||||
|
@ -126,7 +159,6 @@ export function createNotificationGroupFromJSON(
|
|||
|
||||
switch (group.type) {
|
||||
case 'favourite':
|
||||
case 'emoji_reaction':
|
||||
case 'reblog':
|
||||
case 'status':
|
||||
case 'mention':
|
||||
|
@ -140,6 +172,29 @@ export function createNotificationGroupFromJSON(
|
|||
...groupWithoutStatus,
|
||||
};
|
||||
}
|
||||
case 'emoji_reaction': {
|
||||
const {
|
||||
status_id: statusId,
|
||||
emoji_reaction_groups: emojiReactionGroups,
|
||||
...groupWithoutStatus
|
||||
} = group;
|
||||
const groups = (
|
||||
typeof emojiReactionGroups === 'undefined'
|
||||
? ([] as NotificationEmojiReactionGroupJSON[])
|
||||
: emojiReactionGroups
|
||||
).map((g) => {
|
||||
return {
|
||||
sampleAccountIds: g.sample_account_ids,
|
||||
emoji: g.emoji_reaction,
|
||||
} as EmojiReactionGroup;
|
||||
});
|
||||
return {
|
||||
statusId,
|
||||
sampleAccountIds,
|
||||
emojiReactionGroups: groups,
|
||||
...groupWithoutStatus,
|
||||
};
|
||||
}
|
||||
case 'admin.report': {
|
||||
const { report, ...groupWithoutTargetAccount } = group;
|
||||
return {
|
||||
|
@ -187,7 +242,6 @@ export function createNotificationGroupFromNotificationJSON(
|
|||
|
||||
switch (notification.type) {
|
||||
case 'favourite':
|
||||
case 'emoji_reaction':
|
||||
case 'reblog':
|
||||
case 'status':
|
||||
case 'mention':
|
||||
|
@ -195,6 +249,15 @@ export function createNotificationGroupFromNotificationJSON(
|
|||
case 'poll':
|
||||
case 'update':
|
||||
return { ...group, statusId: notification.status.id };
|
||||
case 'emoji_reaction':
|
||||
return {
|
||||
...group,
|
||||
statusId: notification.status.id,
|
||||
emojiReactionGroups: createEmojiReactionGroupsFromJSON(
|
||||
notification.emoji_reaction,
|
||||
group.sampleAccountIds,
|
||||
),
|
||||
};
|
||||
case 'admin.report':
|
||||
return { ...group, report: createReportFromJSON(notification.report) };
|
||||
case 'severed_relationships':
|
||||
|
|
|
@ -211,6 +211,41 @@ function processNewNotification(
|
|||
if (existingGroupIndex > -1) {
|
||||
const existingGroup = groups[existingGroupIndex];
|
||||
|
||||
if (existingGroup && existingGroup.type !== 'gap') {
|
||||
// Update emoji reaction emoji groups
|
||||
if (existingGroup.type === 'emoji_reaction') {
|
||||
const emojiReactionGroups = existingGroup.emojiReactionGroups;
|
||||
const emojiReactionData = notification.emoji_reaction;
|
||||
|
||||
if (emojiReactionGroups && emojiReactionData) {
|
||||
const sameEmojiIndex = emojiReactionGroups.findIndex(
|
||||
(g) => g.emoji.name === emojiReactionData.name,
|
||||
);
|
||||
|
||||
if (sameEmojiIndex > -1) {
|
||||
const sameEmoji = emojiReactionGroups[sameEmojiIndex];
|
||||
|
||||
if (sameEmoji) {
|
||||
if (
|
||||
!sameEmoji.sampleAccountIds.includes(notification.account.id) &&
|
||||
sameEmoji.sampleAccountIds.unshift(notification.account.id) >
|
||||
NOTIFICATIONS_GROUP_MAX_AVATARS
|
||||
)
|
||||
sameEmoji.sampleAccountIds.pop();
|
||||
|
||||
emojiReactionGroups.splice(sameEmojiIndex, 1);
|
||||
emojiReactionGroups.unshift(sameEmoji);
|
||||
}
|
||||
} else {
|
||||
emojiReactionGroups.unshift({
|
||||
emoji: emojiReactionData,
|
||||
sampleAccountIds: [notification.account.id],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
existingGroup &&
|
||||
existingGroup.type !== 'gap' &&
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue