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');
+ }
+}