From 9999fdc9fbd9da9d4aaf505ab9b321eb2618b810 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 5 Jun 2024 12:29:43 +0200 Subject: [PATCH] Implement notification components --- .../components/moderation_warning.tsx | 17 +- .../relationships_severance_event.jsx | 10 +- .../components/avatar_group.tsx | 20 ++ .../components/embedded_status.tsx | 37 ++++ .../components/names_list.tsx | 23 ++ .../components/notification_admin_report.tsx | 62 ++++++ .../components/notification_admin_sign_up.tsx | 22 ++ .../components/notification_favourite.tsx | 23 ++ .../components/notification_follow.tsx | 22 ++ .../notification_follow_request.tsx | 22 ++ .../components/notification_group.tsx | 86 ++++++-- .../notification_group_with_status.tsx | 46 ++++ .../components/notification_mention.tsx | 22 ++ .../notification_moderation_warning.tsx | 11 + .../components/notification_poll.tsx | 22 ++ .../components/notification_reblog.tsx | 22 +- .../notification_severed_relationships.tsx | 13 ++ .../components/notification_status.tsx | 22 ++ .../components/notification_update.tsx | 22 ++ .../components/notification_with_status.tsx | 35 +++ app/javascript/mastodon/locales/en.json | 5 + .../styles/mastodon/components.scss | 201 ++++++++++++++++-- 22 files changed, 709 insertions(+), 56 deletions(-) create mode 100644 app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/names_list.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_admin_report.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_admin_sign_up.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_favourite.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_follow.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_follow_request.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_mention.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_moderation_warning.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_poll.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_severed_relationships.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_status.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_update.tsx create mode 100644 app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx diff --git a/app/javascript/mastodon/features/notifications/components/moderation_warning.tsx b/app/javascript/mastodon/features/notifications/components/moderation_warning.tsx index 2c1683e218..e7ca77f423 100644 --- a/app/javascript/mastodon/features/notifications/components/moderation_warning.tsx +++ b/app/javascript/mastodon/features/notifications/components/moderation_warning.tsx @@ -56,23 +56,18 @@ export const ModerationWarning: React.FC = ({ action, id, hidden }) => { } return ( - - +
+
-
+

{intl.formatMessage(messages[action])}

- +
- +
- +
); }; diff --git a/app/javascript/mastodon/features/notifications/components/relationships_severance_event.jsx b/app/javascript/mastodon/features/notifications/components/relationships_severance_event.jsx index 738159fc5a..310be8eb13 100644 --- a/app/javascript/mastodon/features/notifications/components/relationships_severance_event.jsx +++ b/app/javascript/mastodon/features/notifications/components/relationships_severance_event.jsx @@ -21,14 +21,14 @@ export const RelationshipsSeveranceEvent = ({ type, target, followingCount, foll } return ( - - +
+
-
+ - +
); }; diff --git a/app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx b/app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx new file mode 100644 index 0000000000..a32962c475 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx @@ -0,0 +1,20 @@ +import { Link } from 'react-router-dom'; + +import { Avatar } from 'mastodon/components/avatar'; +import { useAppSelector } from 'mastodon/store'; + +const AvatarWrapper = ({ accountId }) => { + const account = useAppSelector(state => state.getIn(['accounts', accountId])); + + return ( + + + + ); +}; + +export const AvatarGroup = ({ accountIds }) => ( +
+ {accountIds.map(accountId => )} +
+); diff --git a/app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx new file mode 100644 index 0000000000..8f80959de1 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx @@ -0,0 +1,37 @@ +import { FormattedMessage } from 'react-intl'; + +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 { Avatar } from 'mastodon/components/avatar'; +import { DisplayName } from 'mastodon/components/display_name'; +import { Icon } from 'mastodon/components/icon'; +import { useAppSelector } from 'mastodon/store'; + +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 ( +
+
+ + +
+ +
+ + {(status.get('poll') || status.get('media_attachments').size > 0) && ( +
+ {status.get('poll') && <>} + {status.get('media_attachments').size > 0 && <>} +
+ )} +
+ ); +}; diff --git a/app/javascript/mastodon/features/notifications_v2/components/names_list.tsx b/app/javascript/mastodon/features/notifications_v2/components/names_list.tsx new file mode 100644 index 0000000000..14a20dd307 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/names_list.tsx @@ -0,0 +1,23 @@ +import { FormattedMessage } from 'react-intl'; + +import { Link } from 'react-router-dom'; + +import { useAppSelector } from 'mastodon/store'; + +export const NamesList = ({ accountIds, total }) => { + const lastAccountId = accountIds[0]; + const account = useAppSelector(state => state.getIn(['accounts', lastAccountId])); + const displayedName = ; + + if (total === 1) { + return displayedName; + } + + return ( + + ); +}; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_admin_report.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_admin_report.tsx new file mode 100644 index 0000000000..68b6d1768d --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_admin_report.tsx @@ -0,0 +1,62 @@ +import { FormattedMessage, useIntl, defineMessages } from 'react-intl'; + +import FlagIcon from '@/material-icons/400-24px/flag-fill.svg?react'; +import { Icon } from 'mastodon/components/icon'; +import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; +import type { NotificationGroupAdminReport } from 'mastodon/models/notification_group'; +import { useAppSelector } from 'mastodon/store'; + +import { NamesList } from './names_list'; + + + + +// 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: , target: , category: intl.formatMessage(messages[report.category]), count: report.status_ids.length }; + + let message; + + if (report.status_ids.length > 0) { + if (report.category === 'other') { + message = ; + } else { + message = ; + } + } else { + if (report.category === 'other') { + message = ; + } else { + message = ; + } + } + + return ( + +
+ +
+
+
+ {message} + +
+
+ + {report.comment.length > 0 &&
“{report.comment}”
} +
+
+ ); +}; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_admin_sign_up.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_admin_sign_up.tsx new file mode 100644 index 0000000000..7a01c12c22 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_admin_sign_up.tsx @@ -0,0 +1,22 @@ +import { FormattedMessage } from 'react-intl'; + +import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; +import type { NotificationGroupAdminSignUp } from 'mastodon/models/notification_group'; + +import { NotificationGroupWithStatus } from './notification_group_with_status'; + +const labelRenderer = values => + ; + +export const NotificationAdminSignUp: React.FC<{ + notification: NotificationGroupAdminSignUp; +}> = ({ notification }) => ( + +); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_favourite.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_favourite.tsx new file mode 100644 index 0000000000..8341646b30 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_favourite.tsx @@ -0,0 +1,23 @@ +import { FormattedMessage } from 'react-intl'; + +import StarIcon from '@/material-icons/400-24px/star-fill.svg?react'; +import type { NotificationGroupFavourite } from 'mastodon/models/notification_group'; + +import { NotificationGroupWithStatus } from './notification_group_with_status'; + +const labelRenderer = values => + ; + +export const NotificationFavourite: React.FC<{ + notification: NotificationGroupFavourite; +}> = ({ notification }) => ( + +); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_follow.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_follow.tsx new file mode 100644 index 0000000000..b7c74b8ae2 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_follow.tsx @@ -0,0 +1,22 @@ +import { FormattedMessage } from 'react-intl'; + +import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; +import type { NotificationGroupFollow } from 'mastodon/models/notification_group'; + +import { NotificationGroupWithStatus } from './notification_group_with_status'; + +const labelRenderer = values => + ; + +export const NotificationFollow: React.FC<{ + notification: NotificationGroupFollow; +}> = ({ notification }) => ( + +); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_follow_request.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_follow_request.tsx new file mode 100644 index 0000000000..98aa53f198 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_follow_request.tsx @@ -0,0 +1,22 @@ +import { FormattedMessage } from 'react-intl'; + +import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; +import type { NotificationGroupFollowRequest } from 'mastodon/models/notification_group'; + +import { NotificationGroupWithStatus } from './notification_group_with_status'; + +const labelRenderer = values => + ; + +export const NotificationFollowRequest: React.FC<{ + notification: NotificationGroupFollowRequest; +}> = ({ notification }) => ( + +); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_group.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_group.tsx index 48ea2282b0..95889027d5 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_group.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_group.tsx @@ -1,42 +1,94 @@ +import { useMemo } from 'react'; + +import { HotKeys } from 'react-hotkeys'; + import type { NotificationGroup as NotificationGroupModel } from 'mastodon/models/notification_group'; import { useAppSelector } from 'mastodon/store'; + +import { NotificationAdminReport } from './notification_admin_report'; +import { NotificationAdminSignUp } from './notification_admin_sign_up'; +import { NotificationFavourite } from './notification_favourite'; +import { NotificationFollow } from './notification_follow'; +import { NotificationFollowRequest } from './notification_follow_request'; +import { NotificationMention } from './notification_mention'; +import { NotificationModerationWarning } from './notification_moderation_warning'; +import { NotificationPoll } from './notification_poll'; import { NotificationReblog } from './notification_reblog'; +import { NotificationSeveredRelationships } from './notification_severed_relationships'; +import { NotificationStatus } from './notification_status'; +import { NotificationUpdate } from './notification_update'; 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, ), ); + const handlers = useMemo(() => ({ + moveUp: () => { + onMoveUp(notificationGroupId); + }, + + moveDown: () => { + onMoveDown(notificationGroupId); + }, + }), [notificationGroupId, onMoveUp, onMoveDown]); + if (!notificationGroup || notificationGroup.type === 'gap') return null; + let content; + switch (notificationGroup.type) { case 'reblog': - return ; - case 'follow': - case 'follow_request': + content = ; + break; case 'favourite': - case 'mention': - case 'poll': - case 'status': - case 'update': - case 'admin.sign_up': - case 'admin.report': - case 'moderation_warning': + content = ; + break; case 'severed_relationships': + content = ; + break; + case 'mention': + content = ; + break; + case 'follow': + content = ; + break; + case 'follow_request': + content = ; + break; + case 'poll': + content = ; + break; + case 'status': + content = ; + break; + case 'update': + content = ; + break; + case 'admin.sign_up': + content = ; + break; + case 'admin.report': + content = ; + break; + case 'moderation_warning': + content = ; + break; default: - return ( -
-
{JSON.stringify(notificationGroup, undefined, 2)}
-
-
- ); + return null; } + + return ( + + {content} + + ); }; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx new file mode 100644 index 0000000000..fbcae2e920 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx @@ -0,0 +1,46 @@ +import { useMemo } from 'react'; + +import { FormattedMessage } from 'react-intl'; + +import { Icon } from 'mastodon/components/icon'; +import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; + +import { AvatarGroup } from './avatar_group'; +import { EmbeddedStatus } from './embedded_status'; +import { NamesList } from './names_list'; + +export const NotificationGroupWithStatus = ({ + icon, + timestamp, + accountIds, + count, + statusId, + labelRenderer, + type, +}) => { + const label = useMemo(() => + labelRenderer({ name: }), [labelRenderer, accountIds, count]); + + return ( +
+
+ +
+
+ + +
+ {label} + +
+
+ + {statusId && ( +
+ +
+ )} +
+
+ ); +}; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_mention.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_mention.tsx new file mode 100644 index 0000000000..b862a297a4 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_mention.tsx @@ -0,0 +1,22 @@ +import { FormattedMessage } from 'react-intl'; + +import ReplyIcon from '@/material-icons/400-24px/reply-fill.svg?react'; +import type { NotificationGroupMention } from 'mastodon/models/notification_group'; + +import { NotificationWithStatus } from './notification_with_status'; + +const labelRenderer = values => + ; + +export const NotificationMention: React.FC<{ + notification: NotificationGroupMention; +}> = ({ notification }) => ( + +); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_moderation_warning.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_moderation_warning.tsx new file mode 100644 index 0000000000..1235a6c771 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_moderation_warning.tsx @@ -0,0 +1,11 @@ +import { ModerationWarning } from 'mastodon/features/notifications/components/moderation_warning'; +import type { NotificationGroupModerationWarning } from 'mastodon/models/notification_group'; + +export const NotificationModerationWarning: React.FC<{ + notification: NotificationGroupModerationWarning; +}> = ({ notification: { event } }) => ( + +); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_poll.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_poll.tsx new file mode 100644 index 0000000000..708b081a6c --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_poll.tsx @@ -0,0 +1,22 @@ +import { FormattedMessage } from 'react-intl'; + +import BarChart4BarsIcon from '@/material-icons/400-20px/bar_chart_4_bars.svg?react'; +import type { NotificationGroupPoll } from 'mastodon/models/notification_group'; + +import { NotificationWithStatus } from './notification_with_status'; + +const labelRenderer = values => + ; + +export const NotificationPoll: React.FC<{ + notification: NotificationGroupPoll; +}> = ({ notification }) => ( + +); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_reblog.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_reblog.tsx index 0f06511ac1..d0de0590ff 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_reblog.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_reblog.tsx @@ -1,7 +1,23 @@ +import { FormattedMessage } from 'react-intl'; + +import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react'; import type { NotificationGroupReblog } from 'mastodon/models/notification_group'; +import { NotificationGroupWithStatus } from './notification_group_with_status'; + +const labelRenderer = values => + ; + export const NotificationReblog: React.FC<{ notification: NotificationGroupReblog; -}> = ({ notification }) => { - return
reblog {notification.group_key}
; -}; +}> = ({ notification }) => ( + +); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_severed_relationships.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_severed_relationships.tsx new file mode 100644 index 0000000000..a7c662e48d --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_severed_relationships.tsx @@ -0,0 +1,13 @@ +import { RelationshipsSeveranceEvent } from 'mastodon/features/notifications/components/relationships_severance_event'; +import type { NotificationGroupSeveredRelationships } from 'mastodon/models/notification_group'; + +export const NotificationSeveredRelationships: React.FC<{ + notification: NotificationGroupSeveredRelationships; +}> = ({ notification: { event } }) => ( + +); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_status.tsx new file mode 100644 index 0000000000..c0e7c2c1e9 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_status.tsx @@ -0,0 +1,22 @@ +import { FormattedMessage } from 'react-intl'; + +import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react'; +import type { NotificationGroupStatus } from 'mastodon/models/notification_group'; + +import { NotificationWithStatus } from './notification_with_status'; + +const labelRenderer = values => + ; + +export const NotificationStatus: React.FC<{ + notification: NotificationGroupStatus; +}> = ({ notification }) => ( + +); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_update.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_update.tsx new file mode 100644 index 0000000000..cfb05685ce --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_update.tsx @@ -0,0 +1,22 @@ +import { FormattedMessage } from 'react-intl'; + +import EditIcon from '@/material-icons/400-24px/edit.svg?react'; +import type { NotificationGroupUpdate } from 'mastodon/models/notification_group'; + +import { NotificationWithStatus } from './notification_with_status'; + +const labelRenderer = values => + ; + +export const NotificationUpdate: React.FC<{ + notification: NotificationGroupUpdate; +}> = ({ notification }) => ( + +); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx new file mode 100644 index 0000000000..138e436fb3 --- /dev/null +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx @@ -0,0 +1,35 @@ +import { useMemo } from 'react'; + +import { FormattedMessage } from 'react-intl'; + +import { Icon } from 'mastodon/components/icon'; +import Status from 'mastodon/containers/status_container'; + +import { NamesList } from './names_list'; + + +export const NotificationWithStatus = ({ + icon, + accountIds, + statusId, + count, + labelRenderer, + type, +}) => { + const label = useMemo(() => labelRenderer({ name: }), [labelRenderer, accountIds, count]); + + return ( +
+
+
+ {label} +
+ + +
+ ); +}; diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 13296e1d20..3dcb8c95b7 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -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.", @@ -470,6 +471,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", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index cbf9314ff8..7d9769607e 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2210,41 +2210,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; } } } @@ -10345,6 +10332,180 @@ 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; + } +} + .hover-card-controller[data-popper-reference-hidden='true'] { opacity: 0; pointer-events: none;