diff --git a/app/javascript/flavours/glitch/components/content_warning.tsx b/app/javascript/flavours/glitch/components/content_warning.tsx new file mode 100644 index 0000000000..df8afca74d --- /dev/null +++ b/app/javascript/flavours/glitch/components/content_warning.tsx @@ -0,0 +1,15 @@ +import { StatusBanner, BannerVariant } from './status_banner'; + +export const ContentWarning: React.FC<{ + text: string; + expanded?: boolean; + onClick?: () => void; +}> = ({ text, expanded, onClick }) => ( + +

+ +); diff --git a/app/javascript/flavours/glitch/components/filter_warning.tsx b/app/javascript/flavours/glitch/components/filter_warning.tsx new file mode 100644 index 0000000000..4305e43038 --- /dev/null +++ b/app/javascript/flavours/glitch/components/filter_warning.tsx @@ -0,0 +1,23 @@ +import { FormattedMessage } from 'react-intl'; + +import { StatusBanner, BannerVariant } from './status_banner'; + +export const FilterWarning: React.FC<{ + title: string; + expanded?: boolean; + onClick?: () => void; +}> = ({ title, expanded, onClick }) => ( + +

+ +

+
+); diff --git a/app/javascript/flavours/glitch/components/status_banner.tsx b/app/javascript/flavours/glitch/components/status_banner.tsx new file mode 100644 index 0000000000..8ff17a9b2e --- /dev/null +++ b/app/javascript/flavours/glitch/components/status_banner.tsx @@ -0,0 +1,37 @@ +import { FormattedMessage } from 'react-intl'; + +export enum BannerVariant { + Yellow = 'yellow', + Blue = 'blue', +} + +export const StatusBanner: React.FC<{ + children: React.ReactNode; + variant: BannerVariant; + expanded?: boolean; + onClick?: () => void; +}> = ({ children, variant, expanded, onClick }) => ( +
+ {children} + + +
+); diff --git a/app/javascript/flavours/glitch/features/notifications_v2/components/embedded_status.tsx b/app/javascript/flavours/glitch/features/notifications_v2/components/embedded_status.tsx index 3ff021e574..6a67b5c849 100644 --- a/app/javascript/flavours/glitch/features/notifications_v2/components/embedded_status.tsx +++ b/app/javascript/flavours/glitch/features/notifications_v2/components/embedded_status.tsx @@ -8,11 +8,13 @@ import type { List as ImmutableList, RecordOf } from 'immutable'; 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 { toggleStatusSpoilers } from 'flavours/glitch/actions/statuses'; import { Avatar } from 'flavours/glitch/components/avatar'; +import { ContentWarning } from 'flavours/glitch/components/content_warning'; import { DisplayName } from 'flavours/glitch/components/display_name'; import { Icon } from 'flavours/glitch/components/icon'; import type { Status } from 'flavours/glitch/models/status'; -import { useAppSelector } from 'flavours/glitch/store'; +import { useAppSelector, useAppDispatch } from 'flavours/glitch/store'; import { EmbeddedStatusContent } from './embedded_status_content'; @@ -23,6 +25,7 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({ }) => { const history = useHistory(); const clickCoordinatesRef = useRef<[number, number] | null>(); + const dispatch = useAppDispatch(); const status = useAppSelector( (state) => state.statuses.get(statusId) as Status | undefined, @@ -96,15 +99,21 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({ [], ); + const handleContentWarningClick = useCallback(() => { + dispatch(toggleStatusSpoilers(statusId)); + }, [dispatch, statusId]); + if (!status) { return null; } // Assign status attributes to variables with a forced type, as status is not yet properly typed const contentHtml = status.get('contentHtml') as string; + const contentWarning = status.get('spoilerHtml') as string; const poll = status.get('poll'); const language = status.get('language') as string; const mentions = status.get('mentions') as ImmutableList; + const expanded = !status.get('hidden') || !contentWarning; const mediaAttachmentsSize = ( status.get('media_attachments') as ImmutableList ).size; @@ -124,14 +133,24 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({ - + {contentWarning && ( + + )} - {(poll || mediaAttachmentsSize > 0) && ( + {(!contentWarning || expanded) && ( + + )} + + {expanded && (poll || mediaAttachmentsSize > 0) && (
{!!poll && ( <> diff --git a/app/javascript/flavours/glitch/styles/components.scss b/app/javascript/flavours/glitch/styles/components.scss index f044227c95..44bee55ffc 100644 --- a/app/javascript/flavours/glitch/styles/components.scss +++ b/app/javascript/flavours/glitch/styles/components.scss @@ -620,7 +620,7 @@ body > [data-popper-placement] { .spoiler-input__input { padding: 12px 12px - 5px; - background: mix($ui-base-color, $ui-highlight-color, 85%); + background: rgba($ui-highlight-color, 0.05); color: $highlight-text-color; } @@ -1447,6 +1447,14 @@ body > [data-popper-placement] { } } + .content-warning { + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + } + .media-gallery, .video-player, .audio-player, @@ -1771,6 +1779,14 @@ body > [data-popper-placement] { .media-gallery__item-thumbnail { cursor: default; } + + .content-warning { + margin-bottom: 16px; + + &:last-child { + margin-bottom: 0; + } + } } .status__prepend { @@ -11185,39 +11201,53 @@ noscript { } &__embedded-status { + display: flex; + flex-direction: column; + gap: 8px; cursor: pointer; &__account { display: flex; align-items: center; gap: 4px; - margin-bottom: 8px; color: $dark-text-color; + font-size: 15px; + line-height: 22px; bdi { - color: inherit; + color: $darker-text-color; } } - .account__avatar { - opacity: 0.5; - } - &__content { display: -webkit-box; font-size: 15px; line-height: 22px; - color: $dark-text-color; + color: $darker-text-color; -webkit-line-clamp: 4; -webkit-box-orient: vertical; max-height: 4 * 22px; overflow: hidden; + p { + display: none; + + &:first-child { + display: initial; + } + } + p, a { color: inherit; } } + + .reply-indicator__attachments { + font-size: 15px; + line-height: 22px; + color: $dark-text-color; + } } } @@ -11492,3 +11522,53 @@ noscript { } } } + +.content-warning { + background: rgba($ui-highlight-color, 0.05); + color: $secondary-text-color; + border-top: 1px solid; + border-bottom: 1px solid; + border-color: rgba($ui-highlight-color, 0.15); + padding: 8px (5px + 8px); + position: relative; + font-size: 15px; + line-height: 22px; + + p { + margin-bottom: 8px; + } + + .link-button { + font-size: inherit; + line-height: inherit; + font-weight: 500; + } + + &::before, + &::after { + content: ''; + display: block; + position: absolute; + height: 100%; + background: url('~images/warning-stripes.svg') repeat-y; + width: 5px; + top: 0; + } + + &::before { + border-start-start-radius: 4px; + border-end-start-radius: 4px; + inset-inline-start: 0; + } + + &::after { + border-start-end-radius: 4px; + border-end-end-radius: 4px; + inset-inline-end: 0; + } + + &--filter::before, + &--filter::after { + background-image: url('~images/filter-stripes.svg'); + } +}