1
0

Merge branch 'pr2462'

This commit is contained in:
Noa Himesaka 2024-07-27 15:29:08 +09:00
commit 4778c61349
9 changed files with 142 additions and 3 deletions

View File

@ -504,3 +504,75 @@ export function toggleFavourite(statusId, skipModal = false) {
} }
}; };
} }
export const addReaction = (statusId, name, url) => (dispatch, getState) => {
const status = getState().get('statuses').get(statusId);
let alreadyAdded = false;
if (status) {
const reaction = status.get('reactions').find(x => x.get('name') === name);
if (reaction && reaction.get('me')) {
alreadyAdded = true;
}
}
if (!alreadyAdded) {
dispatch(addReactionRequest(statusId, name, url));
}
// encodeURIComponent is required for the Keycap Number Sign emoji, see:
// <https://github.com/glitch-soc/mastodon/pull/1980#issuecomment-1345538932>
api(getState).post(`/api/v1/statuses/${statusId}/react/${encodeURIComponent(name)}`).then(() => {
dispatch(addReactionSuccess(statusId, name));
}).catch(err => {
if (!alreadyAdded) {
dispatch(addReactionFail(statusId, name, err));
}
});
};
export const addReactionRequest = (statusId, name, url) => ({
type: REACTION_ADD_REQUEST,
id: statusId,
name,
url,
});
export const addReactionSuccess = (statusId, name) => ({
type: REACTION_ADD_SUCCESS,
id: statusId,
name,
});
export const addReactionFail = (statusId, name, error) => ({
type: REACTION_ADD_FAIL,
id: statusId,
name,
error,
});
export const removeReaction = (statusId, name) => (dispatch, getState) => {
dispatch(removeReactionRequest(statusId, name));
api(getState).post(`/api/v1/statuses/${statusId}/unreact/${encodeURIComponent(name)}`).then(() => {
dispatch(removeReactionSuccess(statusId, name));
}).catch(err => {
dispatch(removeReactionFail(statusId, name, err));
});
};
export const removeReactionRequest = (statusId, name) => ({
type: REACTION_REMOVE_REQUEST,
id: statusId,
name,
});
export const removeReactionSuccess = (statusId, name) => ({
type: REACTION_REMOVE_SUCCESS,
id: statusId,
name,
});
export const removeReactionFail = (statusId, name) => ({
type: REACTION_REMOVE_FAIL,
id: statusId,
name,
});

View File

@ -11,6 +11,7 @@ export const allNotificationTypes = [
'follow', 'follow',
'follow_request', 'follow_request',
'favourite', 'favourite',
'reaction',
'reblog', 'reblog',
'mention', 'mention',
'poll', 'poll',
@ -24,6 +25,7 @@ export const allNotificationTypes = [
export type NotificationWithStatusType = export type NotificationWithStatusType =
| 'favourite' | 'favourite'
| 'reaction'
| 'reblog' | 'reblog'
| 'status' | 'status'
| 'mention' | 'mention'

View File

@ -15,6 +15,7 @@ import { NotificationFollowRequest } from './notification_follow_request';
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';
import { NotificationReaction } from './notification_reaction';
import { NotificationReblog } from './notification_reblog'; import { NotificationReblog } from './notification_reblog';
import { NotificationSeveredRelationships } from './notification_severed_relationships'; import { NotificationSeveredRelationships } from './notification_severed_relationships';
import { NotificationStatus } from './notification_status'; import { NotificationStatus } from './notification_status';
@ -78,6 +79,14 @@ export const NotificationGroup: React.FC<{
/> />
); );
break; break;
case 'reaction':
content = (
<NotificationReaction
unread={unread}
notification={notificationGroup}
/>
);
break;
case 'severed_relationships': case 'severed_relationships':
content = ( content = (
<NotificationSeveredRelationships <NotificationSeveredRelationships

View File

@ -0,0 +1,34 @@
import { FormattedMessage } from 'react-intl';
import MoodIcon from '@/material-icons/400-24px/mood.svg?react';
import type { NotificationGroupReaction } from 'flavours/glitch/models/notification_group';
import type { LabelRenderer } from './notification_group_with_status';
import { NotificationGroupWithStatus } from './notification_group_with_status';
const labelRenderer: LabelRenderer = (values) => (
<FormattedMessage
id='notification.reaction'
defaultMessage='{name} reacted to your status'
values={values}
/>
);
export const NotificationReaction: React.FC<{
notification: NotificationGroupReaction;
unread: boolean;
}> = ({ notification, unread }) => {
return (
<NotificationGroupWithStatus
type='reaction'
icon={MoodIcon}
iconId='react'
accountIds={notification.sampleAccountIds}
statusId={notification.statusId}
timestamp={notification.latest_page_notification_at}
count={notification.notifications_count}
labelRenderer={labelRenderer}
unread={unread}
/>
);
};

View File

@ -5,6 +5,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react'; import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react'; import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
import MoodIcon from '@/material-icons/400-24px/mood.svg?react';
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react'; import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react'; import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react'; import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
@ -23,6 +24,10 @@ const tooltips = defineMessages({
id: 'notifications.filter.favourites', id: 'notifications.filter.favourites',
defaultMessage: 'Favorites', defaultMessage: 'Favorites',
}, },
reactions: {
id: 'notifications.filter.reactions',
defaultMessage: 'Reactions',
},
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' }, boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' },
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' }, polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' }, follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
@ -91,6 +96,14 @@ export const FilterBar: React.FC = () => {
> >
<Icon id='star' icon={StarIcon} /> <Icon id='star' icon={StarIcon} />
</BarButton> </BarButton>
<BarButton
selectedFilter={selectedFilter}
type='reaction'
key='reaction'
title={intl.formatMessage(tooltips.reactions)}
>
<Icon id='react' icon={MoodIcon} />
</BarButton>
<BarButton <BarButton
selectedFilter={selectedFilter} selectedFilter={selectedFilter}
type='reblog' type='reblog'

View File

@ -31,6 +31,7 @@ interface BaseNotification<Type extends NotificationType>
export type NotificationGroupFavourite = export type NotificationGroupFavourite =
BaseNotificationWithStatus<'favourite'>; BaseNotificationWithStatus<'favourite'>;
export type NotificationGroupReaction = BaseNotificationWithStatus<'reaction'>;
export type NotificationGroupReblog = BaseNotificationWithStatus<'reblog'>; export type NotificationGroupReblog = BaseNotificationWithStatus<'reblog'>;
export type NotificationGroupStatus = BaseNotificationWithStatus<'status'>; export type NotificationGroupStatus = BaseNotificationWithStatus<'status'>;
export type NotificationGroupMention = BaseNotificationWithStatus<'mention'>; export type NotificationGroupMention = BaseNotificationWithStatus<'mention'>;
@ -76,6 +77,7 @@ export interface NotificationGroupAdminReport
export type NotificationGroup = export type NotificationGroup =
| NotificationGroupFavourite | NotificationGroupFavourite
| NotificationGroupReaction
| NotificationGroupReblog | NotificationGroupReblog
| NotificationGroupStatus | NotificationGroupStatus
| NotificationGroupMention | NotificationGroupMention
@ -120,6 +122,7 @@ export function createNotificationGroupFromJSON(
switch (group.type) { switch (group.type) {
case 'favourite': case 'favourite':
case 'reaction':
case 'reblog': case 'reblog':
case 'status': case 'status':
case 'mention': case 'mention':
@ -179,6 +182,7 @@ export function createNotificationGroupFromNotificationJSON(
switch (notification.type) { switch (notification.type) {
case 'favourite': case 'favourite':
case 'reaction':
case 'reblog': case 'reblog':
case 'status': case 'status':
case 'mention': case 'mention':

View File

@ -10946,6 +10946,10 @@ noscript {
color: $gold-star; color: $gold-star;
} }
&--reaction &__icon {
color: $blurple-300;
}
&--reblog &__icon { &--reblog &__icon {
color: $valid-value-color; color: $valid-value-color;
} }
@ -11119,7 +11123,8 @@ noscript {
$icon-margin: 48px; // 40px avatar + 8px gap $icon-margin: 48px; // 40px avatar + 8px gap
.status__content, .status__content,
.status__action-bar { .status__action-bar,
.reactions-bar {
margin-inline-start: $icon-margin; margin-inline-start: $icon-margin;
width: calc(100% - $icon-margin); width: calc(100% - $icon-margin);
} }

View File

@ -15,7 +15,7 @@ class REST::NotificationGroupSerializer < ActiveModel::Serializer
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
def status_type? def status_type?
[:favourite, :reblog, :status, :mention, :poll, :update].include?(object.type) [:favourite, :reaction, :reblog, :status, :mention, :poll, :update].include?(object.type)
end end
def report_type? def report_type?

View File

@ -206,7 +206,7 @@ class NotifyService < BaseService
private private
def notification_group_key def notification_group_key
return nil if @notification.filtered || %i(favourite reblog).exclude?(@notification.type) return nil if @notification.filtered || %i(favourite reaction reblog).exclude?(@notification.type)
type_prefix = "#{@notification.type}-#{@notification.target_status.id}" type_prefix = "#{@notification.type}-#{@notification.target_status.id}"
redis_key = "notif-group/#{@recipient.id}/#{type_prefix}" redis_key = "notif-group/#{@recipient.id}/#{type_prefix}"