WIP
Some checks failed
Check formatting / lint (push) Has been cancelled
CSS Linting / lint (push) Has been cancelled
JavaScript Linting / lint (push) Has been cancelled
JavaScript Testing / test (push) Has been cancelled
Test one step migrations / pre_job (push) Has been cancelled
Test two step migrations / pre_job (push) Has been cancelled
Ruby Testing / build (production) (push) Has been cancelled
Ruby Testing / build (test) (push) Has been cancelled
Test one step migrations / test (14-alpine) (push) Has been cancelled
Test one step migrations / test (15-alpine) (push) Has been cancelled
Test two step migrations / test (14-alpine) (push) Has been cancelled
Test two step migrations / test (15-alpine) (push) Has been cancelled
Ruby Testing / test (.ruby-version) (push) Has been cancelled
Ruby Testing / test (3.1) (push) Has been cancelled
Ruby Testing / test (3.2) (push) Has been cancelled
Ruby Testing / End to End testing (.ruby-version) (push) Has been cancelled
Ruby Testing / End to End testing (3.1) (push) Has been cancelled
Ruby Testing / End to End testing (3.2) (push) Has been cancelled
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Has been cancelled
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.10.2) (push) Has been cancelled
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Has been cancelled
Ruby Testing / Elastic Search integration testing (3.1, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Has been cancelled
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Has been cancelled

This commit is contained in:
Eugen Rochko 2024-06-05 12:29:43 +02:00
parent 62088f51bd
commit b7d822fc59
22 changed files with 687 additions and 56 deletions

View File

@ -56,23 +56,18 @@ export const ModerationWarning: React.FC<Props> = ({ action, id, hidden }) => {
}
return (
<a
href={`/disputes/strikes/${id}`}
target='_blank'
rel='noopener noreferrer'
className='notification__moderation-warning'
>
<Icon id='warning' icon={GavelIcon} />
<div className='notification-group notification-group--link notification-group--moderation-warning focusable' tabIndex='0'>
<div className='notification-group__icon'><Icon id='warning' icon={GavelIcon} /></div>
<div className='notification__moderation-warning__content'>
<div className='notification-group__main'>
<p>{intl.formatMessage(messages[action])}</p>
<span className='link-button'>
<a href={`/disputes/strikes/${id}`} target='_blank' rel='noopener noreferrer' className='link-button'>
<FormattedMessage
id='notification.moderation-warning.learn_more'
defaultMessage='Learn more'
/>
</span>
</div>
</a>
</div>
</div>
);
};

View File

@ -21,14 +21,14 @@ export const RelationshipsSeveranceEvent = ({ type, target, followingCount, foll
}
return (
<a href='/severed_relationships' target='_blank' rel='noopener noreferrer' className='notification__relationships-severance-event'>
<Icon id='heart_broken' icon={HeartBrokenIcon} />
<div className='notification-group notification-group--link notification-group--relationships-severance-event focusable' tabIndex='0'>
<div className='notification-group__icon'><Icon id='heart_broken' icon={HeartBrokenIcon} /></div>
<div className='notification__relationships-severance-event__content'>
<div className='notification-group__main'>
<p>{intl.formatMessage(messages[type], { from: <strong>{domain}</strong>, target: <strong>{target}</strong>, followingCount, followersCount })}</p>
<span className='link-button'><FormattedMessage id='notification.relationships_severance_event.learn_more' defaultMessage='Learn more' /></span>
<a href='/severed_relationships' target='_blank' rel='noopener noreferrer' className='link-button'><FormattedMessage id='notification.relationships_severance_event.learn_more' defaultMessage='Learn more' /></a>
</div>
</div>
</a>
);
};

View File

@ -0,0 +1,19 @@
import { Avatar } from 'mastodon/components/avatar';
import { useAppSelector } from 'mastodon/store';
import { Link } from 'react-router-dom';
const AvatarWrapper = ({ accountId }) => {
const account = useAppSelector(state => state.getIn(['accounts', accountId]));
return (
<Link to={`/@${account.get('acct')}`} title={`@${account.get('acct')}`}>
<Avatar account={account} size={28} />
</Link>
);
};
export const AvatarGroup = ({ accountIds }) => (
<div className='notification-group__avatar-group'>
{accountIds.map(accountId => <AvatarWrapper key={accountId} accountId={accountId} />)}
</div>
);

View File

@ -0,0 +1,36 @@
import { useAppSelector } from 'mastodon/store';
import BarChart4BarsIcon from '@/material-icons/400-24px/bar_chart_4_bars.svg?react';
import PhotoLibraryIcon from '@/material-icons/400-24px/photo_library.svg?react';
import { Icon } from 'mastodon/components/icon';
import { FormattedMessage } from 'react-intl';
import { Avatar } from 'mastodon/components/avatar';
import { DisplayName } from 'mastodon/components/display_name';
export const EmbeddedStatus = ({ statusId }) => {
const status = useAppSelector(state => state.getIn(['statuses', statusId]));
const account = useAppSelector(state => state.getIn(['accounts', status?.get('account')]));
if (!status) {
return null;
}
const content = { __html: status.get('contentHtml') };
return (
<div className='notification-group__embedded-status'>
<div className='notification-group__embedded-status__account'>
<Avatar account={account} size={16} />
<DisplayName account={account} />
</div>
<div className='notification-group__embedded-status__content reply-indicator__content translate' dangerouslySetInnerHTML={content} />
{(status.get('poll') || status.get('media_attachments').size > 0) && (
<div className='notification-group__embedded-status__attachments reply-indicator__attachments'>
{status.get('poll') && <><Icon icon={BarChart4BarsIcon} /><FormattedMessage id='reply_indicator.poll' defaultMessage='Poll' /></>}
{status.get('media_attachments').size > 0 && <><Icon icon={PhotoLibraryIcon} /><FormattedMessage id='reply_indicator.attachments' defaultMessage='{count, plural, one {# attachment} other {# attachments}}' values={{ count: status.get('media_attachments').size }} /></>}
</div>
)}
</div>
);
}

View File

@ -0,0 +1,21 @@
import { useAppSelector } from 'mastodon/store';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
export const NamesList = ({ accountIds, total }) => {
const lastAccountId = accountIds[0];
const account = useAppSelector(state => state.getIn(['accounts', lastAccountId]));
const displayedName = <Link to={`/@${account.get('acct')}`} title={`@${account.get('acct')}`}><bdi dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></Link>;
if (total === 1) {
return displayedName;
}
return (
<FormattedMessage
id=''
defaultMessage='{name} and {count, plural, one {# other} other {# others}}'
values={{ name: displayedName, count: total - 1 }}
/>
);
};

View File

@ -0,0 +1,57 @@
import type { NotificationGroupAdminReport } from 'mastodon/models/notification_group';
import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react';
import { NamesList } from './names_list';
import { Icon } from 'mastodon/components/icon';
import { FormattedMessage, useIntl, defineMessages } from 'react-intl';
import { useAppSelector } from 'mastodon/store';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
// This needs to be kept in sync with app/models/report.rb
const messages = defineMessages({
other: { id: 'report_notification.categories.other', defaultMessage: 'Other' },
spam: { id: 'report_notification.categories.spam', defaultMessage: 'Spam' },
legal: { id: 'report_notification.categories.legal', defaultMessage: 'Legal' },
violation: { id: 'report_notification.categories.violation', defaultMessage: 'Rule violation' },
});
export const NotificationAdminReport: React.FC<{
notification: NotificationGroupAdminReport;
}> = ({ notification, notification: { report } }) => {
const intl = useIntl();
const targetAccount = useAppSelector(state => state.getIn(['accounts', report.target_account.id]));
const account = useAppSelector(state => state.getIn(['accounts', notification.sampleAccountsIds[0]]));
const values = { name: <bdi dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} />, target: <bdi dangerouslySetInnerHTML={{ __html: targetAccount.get('display_name_html') }} />, category: intl.formatMessage(messages[report.category]), count: report.status_ids.length };
let message;
if (report.status_ids.length > 0) {
if (report.category === 'other') {
message = <FormattedMessage id='notification.admin.report_account_other' defaultMessage='{name} reported {count, plural, one {one post} other {# posts}} from {target}' values={values} />;
} else {
message = <FormattedMessage id='notification.admin.report_account' defaultMessage='{name} reported {count, plural, one {one post} other {# posts}} from {target} for {category}' values={values} />;
}
} else {
if (report.category === 'other') {
message = <FormattedMessage id='notification.admin.report_statuses_other' defaultMessage='{name} reported {target}' values={values} />;
} else {
message = <FormattedMessage id='notification.admin.report_statuses' defaultMessage='{name} reported {target} for {category}' values={values} />;
}
}
return (
<a href={`/admin/reports/${report.id}`} target='_blank' rel='noopener noreferrer' className='notification-group notification-group--link notification-group--admin-report focusable' tabIndex='0'>
<div className='notification-group__icon'><Icon id='flag' icon={FlagIcon} /></div>
<div className='notification-group__main'>
<div className='notification-group__main__header'>
<div className='notification-group__main__header__label'>
{message}
<RelativeTimestamp timestamp={report.created_at} />
</div>
</div>
{report.comment.length > 0 && <div className='notification-group__embedded-status__content'>{report.comment}</div>}
</div>
</a>
);
};

View File

@ -0,0 +1,20 @@
import type { NotificationGroupAdminSignUp } from 'mastodon/models/notification_group';
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
import { FormattedMessage } from 'react-intl';
import { NotificationGroupWithStatus } from './notification_group_with_status';
const labelRenderer = values =>
<FormattedMessage id='notification.admin.sign_up' defaultMessage='{name} signed up' values={values} />;
export const NotificationAdminSignUp: React.FC<{
notification: NotificationGroupAdminSignUp;
}> = ({ notification }) => (
<NotificationGroupWithStatus
type='admin-sign-up'
icon={PersonAddIcon}
accountIds={notification.sampleAccountsIds}
timestamp={notification.latest_page_notification_at}
count={notification.notifications_count}
labelRenderer={labelRenderer}
/>
);

View File

@ -0,0 +1,21 @@
import type { NotificationGroupFavourite } from 'mastodon/models/notification_group';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import { FormattedMessage } from 'react-intl';
import { NotificationGroupWithStatus } from './notification_group_with_status';
const labelRenderer = values =>
<FormattedMessage id='notification.favourite' defaultMessage='{name} favorited your status' values={values} />;
export const NotificationFavourite: React.FC<{
notification: NotificationGroupFavourite;
}> = ({ notification }) => (
<NotificationGroupWithStatus
type='favourite'
icon={StarIcon}
accountIds={notification.sampleAccountsIds}
statusId={notification.statusId}
timestamp={notification.latest_page_notification_at}
count={notification.notifications_count}
labelRenderer={labelRenderer}
/>
);

View File

@ -0,0 +1,20 @@
import type { NotificationGroupFollow } from 'mastodon/models/notification_group';
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
import { FormattedMessage } from 'react-intl';
import { NotificationGroupWithStatus } from './notification_group_with_status';
const labelRenderer = values =>
<FormattedMessage id='notification.follow' defaultMessage='{name} followed you' values={values} />;
export const NotificationFollow: React.FC<{
notification: NotificationGroupFollow;
}> = ({ notification }) => (
<NotificationGroupWithStatus
type='follow'
icon={PersonAddIcon}
accountIds={notification.sampleAccountsIds}
timestamp={notification.latest_page_notification_at}
count={notification.notifications_count}
labelRenderer={labelRenderer}
/>
);

View File

@ -0,0 +1,20 @@
import type { NotificationGroupFollowRequest } from 'mastodon/models/notification_group';
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
import { FormattedMessage } from 'react-intl';
import { NotificationGroupWithStatus } from './notification_group_with_status';
const labelRenderer = values =>
<FormattedMessage id='notification.follow_request' defaultMessage='{name} has requested to follow you' values={values} />;
export const NotificationFollowRequest: React.FC<{
notification: NotificationGroupFollowRequest;
}> = ({ notification }) => (
<NotificationGroupWithStatus
type='follow-request'
icon={PersonAddIcon}
accountIds={notification.sampleAccountsIds}
timestamp={notification.latest_page_notification_at}
count={notification.notifications_count}
labelRenderer={labelRenderer}
/>
);

View File

@ -1,14 +1,28 @@
import type { NotificationGroup as NotificationGroupModel } from 'mastodon/models/notification_group';
import { useMemo } from 'react';
import { useAppSelector } from 'mastodon/store';
import { HotKeys } from 'react-hotkeys';
import { NotificationReblog } from './notification_reblog';
import { NotificationFavourite } from './notification_favourite';
import { NotificationSeveredRelationships } from './notification_severed_relationships';
import { NotificationMention } from './notification_mention';
import { NotificationFollow } from './notification_follow';
import { NotificationFollowRequest } from './notification_follow_request';
import { NotificationPoll } from './notification_poll';
import { NotificationStatus } from './notification_status';
import { NotificationUpdate } from './notification_update';
import { NotificationAdminSignUp } from './notification_admin_sign_up';
import { NotificationAdminReport } from './notification_admin_report';
import { NotificationModerationWarning } from './notification_moderation_warning';
export const NotificationGroup: React.FC<{
notificationGroupId: NotificationGroupModel['group_key'];
unread: boolean;
onMoveUp: unknown;
onMoveDown: unknown;
}> = ({ notificationGroupId }) => {
}> = ({ notificationGroupId, onMoveUp, onMoveDown }) => {
const notificationGroup = useAppSelector((state) =>
state.notificationsGroups.groups.find(
(item) => item.type !== 'gap' && item.group_key === notificationGroupId,
@ -17,26 +31,76 @@ export const NotificationGroup: React.FC<{
if (!notificationGroup || notificationGroup.type === 'gap') return null;
let content;
switch (notificationGroup.type) {
case 'reblog':
return <NotificationReblog notification={notificationGroup} />;
case 'follow':
case 'follow_request':
content = <NotificationReblog notification={notificationGroup} />;
break;
case 'favourite':
case 'mention':
case 'poll':
case 'status':
case 'update':
case 'admin.sign_up':
case 'admin.report':
case 'moderation_warning':
content = <NotificationFavourite notification={notificationGroup} />;
break;
case 'severed_relationships':
content = <NotificationSeveredRelationships notification={notificationGroup} />;
break;
case 'mention':
content = <NotificationMention notification={notificationGroup} />;;
break;
case 'follow':
content = <NotificationFollow notification={notificationGroup} />;
break;
case 'follow_request':
content = <NotificationFollowRequest notification={notificationGroup} />;
break;
case 'poll':
content = <NotificationPoll notification={notificationGroup} />;
break;
case 'status':
content = <NotificationStatus notification={notificationGroup} />;
break;
case 'update':
content = <NotificationUpdate notification={notificationGroup} />;
break;
case 'admin.sign_up':
content = <NotificationAdminSignUp notification={notificationGroup} />;
break;
case 'admin.report':
content = <NotificationAdminReport notification={notificationGroup} />;
break;
case 'moderation_warning':
content = <NotificationModerationWarning notification={notificationGroup} />;
break;
default:
return (
<div>
<pre>{JSON.stringify(notificationGroup, undefined, 2)}</pre>
<hr />
</div>
);
return null;
}
const handlers = useMemo(() => ({
moveUp: () => {
onMoveUp(notificationGroupId)
},
moveDown: () => {
onMoveDown(notificationGroupId)
},
reply: () => {},
favourite: () => {},
boost: () => {},
mention: () => {},
open: () => {},
openProfile: () => {},
toggleHidden: () => {},
}), [notificationGroupId, onMoveUp, onMoveDown]);
return (
<HotKeys handlers={handlers}>
{content}
</HotKeys>
);
};

View File

@ -0,0 +1,43 @@
import { useMemo } from 'react';
import { Icon } from 'mastodon/components/icon';
import { AvatarGroup } from './avatar_group';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import { NamesList } from './names_list';
import { FormattedMessage } from 'react-intl';
import { EmbeddedStatus } from './embedded_status';
export const NotificationGroupWithStatus = ({
icon,
timestamp,
accountIds,
count,
statusId,
labelRenderer,
type,
}) => {
const label = useMemo(() =>
labelRenderer({ name: <NamesList accountIds={accountIds} total={count} /> }), [labelRenderer, accountIds, count]);
return (
<div className={`notification-group focusable notification-group--${type}`} tabIndex='0'>
<div className='notification-group__icon'><Icon icon={icon} /></div>
<div className='notification-group__main'>
<div className='notification-group__main__header'>
<AvatarGroup accountIds={accountIds} />
<div className='notification-group__main__header__label'>
{label}
<RelativeTimestamp timestamp={timestamp} />
</div>
</div>
{statusId && (
<div className='notification-group__main__status'>
<EmbeddedStatus statusId={statusId} />
</div>
)}
</div>
</div>
);
};

View File

@ -0,0 +1,20 @@
import type { NotificationGroupMention } from 'mastodon/models/notification_group';
import ReplyIcon from '@/material-icons/400-24px/reply-fill.svg?react';
import { FormattedMessage } from 'react-intl';
import { NotificationWithStatus } from './notification_with_status';
const labelRenderer = values =>
<FormattedMessage id='notification.mention' defaultMessage='{name} mentioned you' values={values} />;
export const NotificationMention: React.FC<{
notification: NotificationGroupMention;
}> = ({ notification }) => (
<NotificationWithStatus
type='mention'
icon={ReplyIcon}
accountIds={notification.sampleAccountsIds}
count={notification.notifications_count}
statusId={notification.statusId}
labelRenderer={labelRenderer}
/>
);

View File

@ -0,0 +1,11 @@
import type { NotificationGroupModerationWarning } from 'mastodon/models/notification_group';
import { ModerationWarning } from 'mastodon/features/notifications/components/moderation_warning';
export const NotificationModerationWarning: React.FC<{
notification: NotificationGroupModerationWarning;
}> = ({ notification: { event } }) => (
<ModerationWarning
action={event.action}
id={event.id}
/>
);

View File

@ -0,0 +1,20 @@
import type { NotificationGroupPoll } from 'mastodon/models/notification_group';
import BarChart4BarsIcon from '@/material-icons/400-20px/bar_chart_4_bars.svg?react';
import { FormattedMessage } from 'react-intl';
import { NotificationWithStatus } from './notification_with_status';
const labelRenderer = values =>
<FormattedMessage id='notification.poll' defaultMessage='A poll you have voted in has ended' />;
export const NotificationPoll: React.FC<{
notification: NotificationGroupPoll;
}> = ({ notification }) => (
<NotificationWithStatus
type='poll'
icon={BarChart4BarsIcon}
accountIds={notification.sampleAccountsIds}
count={notification.notifications_count}
statusId={notification.statusId}
labelRenderer={labelRenderer}
/>
);

View File

@ -1,7 +1,21 @@
import type { NotificationGroupReblog } from 'mastodon/models/notification_group';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import { FormattedMessage } from 'react-intl';
import { NotificationGroupWithStatus } from './notification_group_with_status';
const labelRenderer = values =>
<FormattedMessage id='notification.reblog' defaultMessage='{name} boosted your status' values={values} />;
export const NotificationReblog: React.FC<{
notification: NotificationGroupReblog;
}> = ({ notification }) => {
return <div>reblog {notification.group_key}</div>;
};
}> = ({ notification }) => (
<NotificationGroupWithStatus
type='reblog'
icon={RepeatIcon}
accountIds={notification.sampleAccountsIds}
statusId={notification.statusId}
timestamp={notification.latest_page_notification_at}
count={notification.notifications_count}
labelRenderer={labelRenderer}
/>
);

View File

@ -0,0 +1,13 @@
import type { NotificationGroupSeveredRelationships } from 'mastodon/models/notification_group';
import { RelationshipsSeveranceEvent } from 'mastodon/features/notifications/components/relationships_severance_event';
export const NotificationSeveredRelationships: React.FC<{
notification: NotificationGroupSeveredRelationships;
}> = ({ notification: { event } }) => (
<RelationshipsSeveranceEvent
type={event.type}
target={event.target_name}
followersCount={event.followers_count}
followingCount={event.following_count}
/>
);

View File

@ -0,0 +1,20 @@
import type { NotificationGroupStatus } from 'mastodon/models/notification_group';
import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react';
import { FormattedMessage } from 'react-intl';
import { NotificationWithStatus } from './notification_with_status';
const labelRenderer = values =>
<FormattedMessage id='notification.status' defaultMessage='{name} just posted' values={values} />;
export const NotificationStatus: React.FC<{
notification: NotificationGroupStatus;
}> = ({ notification }) => (
<NotificationWithStatus
type='status'
icon={NotificationsActiveIcon}
accountIds={notification.sampleAccountsIds}
count={notification.notifications_count}
statusId={notification.statusId}
labelRenderer={labelRenderer}
/>
);

View File

@ -0,0 +1,20 @@
import type { NotificationGroupUpdate } from 'mastodon/models/notification_group';
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import { FormattedMessage } from 'react-intl';
import { NotificationWithStatus } from './notification_with_status';
const labelRenderer = values =>
<FormattedMessage id='notification.update' defaultMessage='{name} edited a post' values={values} />;
export const NotificationUpdate: React.FC<{
notification: NotificationGroupUpdate;
}> = ({ notification }) => (
<NotificationWithStatus
type='update'
icon={EditIcon}
accountIds={notification.sampleAccountsIds}
count={notification.notifications_count}
statusId={notification.statusId}
labelRenderer={labelRenderer}
/>
);

View File

@ -0,0 +1,31 @@
import { useMemo } from 'react';
import Status from 'mastodon/containers/status_container';
import { Icon } from 'mastodon/components/icon';
import { NamesList } from './names_list';
import { FormattedMessage } from 'react-intl';
export const NotificationWithStatus = ({
icon,
accountIds,
statusId,
count,
labelRenderer,
type,
}) => {
const label = useMemo(() => labelRenderer({ name: <NamesList accountIds={accountIds} total={count} /> }), [labelRenderer, accountIds, count]);
return (
<div className={`notification-ungrouped focusable notification-ungrouped--${type}`} tabIndex='0'>
<div className='notification-ungrouped__header'>
<div className='notification-ungrouped__header__icon'><Icon icon={icon} /></div>
{label}
</div>
<Status
id={statusId}
contextType='notifications'
withDismiss
/>
</div>
);
};

View File

@ -1,4 +1,5 @@
{
"a9ng3b": "{name} and {count, plural, one {# other} other {# others}}",
"about.blocks": "Moderated servers",
"about.contact": "Contact:",
"about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
@ -469,6 +470,10 @@
"navigation_bar.security": "Security",
"not_signed_in_indicator.not_signed_in": "You need to login to access this resource.",
"notification.admin.report": "{name} reported {target}",
"notification.admin.report_account": "{name} reported {count, plural, one {one post} other {# posts}} from {target} for {category}",
"notification.admin.report_account_other": "{name} reported {count, plural, one {one post} other {# posts}} from {target}",
"notification.admin.report_statuses": "{name} reported {target} for {category}",
"notification.admin.report_statuses_other": "{name} reported {target}",
"notification.admin.sign_up": "{name} signed up",
"notification.favourite": "{name} favorited your post",
"notification.follow": "{name} followed you",

View File

@ -2180,41 +2180,28 @@ a.account__display-name {
}
}
.notification__relationships-severance-event,
.notification__moderation-warning {
display: flex;
gap: 16px;
.notification-group--link {
color: $secondary-text-color;
text-decoration: none;
align-items: flex-start;
padding: 16px 32px;
border-bottom: 1px solid var(--background-border-color);
&:hover {
color: $primary-text-color;
}
.icon {
padding: 2px;
color: $highlight-text-color;
}
&__content {
.notification-group__main {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
flex-grow: 1;
font-size: 16px;
line-height: 24px;
font-size: 15px;
line-height: 22px;
strong {
strong,
bdi {
font-weight: 700;
}
.link-button {
font-size: inherit;
line-height: inherit;
font-weight: inherit;
}
}
}
@ -10266,3 +10253,177 @@ noscript {
}
}
}
.notification-group {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 16px;
border-bottom: 1px solid var(--background-border-color);
&__icon {
width: 40px;
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
color: $highlight-text-color;
.icon {
width: 28px;
height: 28px;
}
}
&--favourite &__icon {
color: $gold-star;;
}
&--reblog &__icon {
color: $valid-value-color;
}
&--relationships-severance-event &__icon,
&--admin-report &__icon,
&--admin-sign-up &__icon {
color: $dark-text-color;
}
&--moderation-warning &__icon {
color: $red-bookmark;
}
&__main {
display: flex;
flex-direction: column;
gap: 8px;
flex: 1 1 auto;
&__header {
display: flex;
flex-direction: column;
gap: 8px;
&__label {
display: flex;
gap: 8px;
font-size: 15px;
line-height: 22px;
color: $darker-text-color;
a {
color: inherit;
text-decoration: none;
}
bdi {
font-weight: 700;
color: $primary-text-color;
}
time {
color: $dark-text-color;
}
}
}
&__status {
border: 1px solid var(--background-border-color);
border-radius: 8px;
padding: 8px;
}
}
&__avatar-group {
display: flex;
gap: 8px;
}
.status {
padding: 0;
border: 0;
}
&__embedded-status {
&__account {
display: flex;
align-items: center;
gap: 4px;
margin-bottom: 8px;
color: $dark-text-color;
bdi {
color: inherit;
}
}
.account__avatar {
opacity: 0.5;
}
&__content {
font-size: 15px;
line-height: 22px;
color: $dark-text-color;
p,
a {
color: inherit;
}
}
}
}
.notification-ungrouped {
padding: 16px;
border-bottom: 1px solid var(--background-border-color);
&__header {
display: flex;
align-items: center;
gap: 8px;
color: $dark-text-color;
font-size: 15px;
line-height: 22px;
font-weight: 500;
padding-inline-start: 24px;
margin-bottom: 16px;
&__icon {
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
.icon {
width: 16px;
height: 16px;
}
}
a {
color: inherit;
text-decoration: none;
}
}
.status {
border: 0;
padding: 0;
&__avatar {
width: 40px;
height: 40px;
.account__avatar {
width: 40px !important;
height: 40px !important;
}
}
}
.notification__report {
border: 0;
padding: 0;
}
}