Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change status content markup to match upstream #2923

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions app/javascript/flavours/glitch/components/content_warning.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import type { IconName } from './media_icon';
import { MediaIcon } from './media_icon';
import { StatusBanner, BannerVariant } from './status_banner';

export const ContentWarning: React.FC<{
text: string;
expanded?: boolean;
onClick?: () => void;
icons?: React.ReactNode[];
icons?: IconName[];
}> = ({ text, expanded, onClick, icons }) => (
<StatusBanner
expanded={expanded}
onClick={onClick}
variant={BannerVariant.Warning}
>
{icons}
{icons?.map((icon) => (
<MediaIcon
className='status__content__spoiler-icon'
icon={icon}
key={`icon-${icon}`}
/>
))}
<p dangerouslySetInnerHTML={{ __html: text }} />
</StatusBanner>
);
55 changes: 55 additions & 0 deletions app/javascript/flavours/glitch/components/media_icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { defineMessages, useIntl } from 'react-intl';

import ImageIcon from '@/material-icons/400-24px/image.svg?react';
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
import LinkIcon from '@/material-icons/400-24px/link.svg?react';
import MovieIcon from '@/material-icons/400-24px/movie.svg?react';
import MusicNoteIcon from '@/material-icons/400-24px/music_note.svg?react';
import { Icon } from 'flavours/glitch/components/icon';

const messages = defineMessages({
link: {
id: 'status.has_preview_card',
defaultMessage: 'Features an attached preview card',
},
'picture-o': {
id: 'status.has_pictures',
defaultMessage: 'Features attached pictures',
},
tasks: { id: 'status.is_poll', defaultMessage: 'This toot is a poll' },
'video-camera': {
id: 'status.has_video',
defaultMessage: 'Features attached videos',
},
music: {
id: 'status.has_audio',
defaultMessage: 'Features attached audio files',
},
});

const iconComponents = {
link: LinkIcon,
'picture-o': ImageIcon,
tasks: InsertChartIcon,
'video-camera': MovieIcon,
music: MusicNoteIcon,
};

export type IconName = keyof typeof iconComponents;

export const MediaIcon: React.FC<{
className?: string;
icon: IconName;
}> = ({ className, icon }) => {
const intl = useIntl();

return (
<Icon
className={className}
id={icon}
icon={iconComponents[icon]}
title={intl.formatMessage(messages[icon])}
aria-hidden='true'
/>
);
};
29 changes: 29 additions & 0 deletions app/javascript/flavours/glitch/components/mentions_placeholder.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import ImmutablePropTypes from 'react-immutable-proptypes';

import { Permalink } from 'flavours/glitch/components/permalink';

export const MentionsPlaceholder = ({ status }) => {
if (status.get('spoiler_text').length === 0 || !status.get('mentions')) {
return null;
}

return (
<div className='status__content'>
{status.get('mentions').map(item => (
<Permalink
to={`/@${item.get('acct')}`}
href={item.get('url')}
key={item.get('id')}
className='mention'
>
@<span>{item.get('username')}</span>
</Permalink>
)).reduce((aggregate, item) => [...aggregate, item, ' '], [])}
</div>
);
};

MentionsPlaceholder.propTypes = {
status: ImmutablePropTypes.map.isRequired,
};

84 changes: 40 additions & 44 deletions app/javascript/flavours/glitch/components/status.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';

import { HotKeys } from 'react-hotkeys';

import { ContentWarning } from 'flavours/glitch/components/content_warning';
import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
import PollContainer from 'flavours/glitch/containers/poll_container';
import NotificationOverlayContainer from 'flavours/glitch/features/notifications/containers/overlay_container';
import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning';
import { withOptionalRouter, WithOptionalRouterPropTypes } from 'flavours/glitch/utils/react_router';
Expand All @@ -28,6 +28,7 @@ import { Avatar } from './avatar';
import { AvatarOverlay } from './avatar_overlay';
import { DisplayName } from './display_name';
import { getHashtagBarForStatus } from './hashtag_bar';
import { MentionsPlaceholder } from './mentions_placeholder';
import { Permalink } from './permalink';
import StatusActionBar from './status_action_bar';
import StatusContent from './status_content';
Expand Down Expand Up @@ -134,7 +135,7 @@ class Status extends ImmutablePureComponent {
showMedia: defaultMediaVisibility(this.props.status, this.props.settings) && !(this.context?.hideMediaByDefault),
revealBehindCW: undefined,
showCard: false,
forceFilter: undefined,
showDespiteFilter: undefined,
};

// Avoid checking props that are functions (and whose equality will always
Expand All @@ -158,7 +159,7 @@ class Status extends ImmutablePureComponent {
updateOnStates = [
'isExpanded',
'showMedia',
'forceFilter',
'showDespiteFilter',
];

static getDerivedStateFromProps(nextProps, prevState) {
Expand Down Expand Up @@ -242,7 +243,7 @@ class Status extends ImmutablePureComponent {
if (this.props.status?.get('id') !== prevProps.status?.get('id')) {
this.setState({
showMedia: defaultMediaVisibility(this.props.status, this.props.settings) && !(this.context?.hideMediaByDefault),
forceFilter: undefined,
showDespiteFilter: undefined,
});
}
}
Expand Down Expand Up @@ -399,12 +400,12 @@ class Status extends ImmutablePureComponent {
};

handleUnfilterClick = e => {
this.setState({ forceFilter: false });
this.setState({ showDespiteFilter: false });
e.preventDefault();
};

handleFilterClick = () => {
this.setState({ forceFilter: true });
this.setState({ showDespiteFilter: true });
};

handleRef = c => {
Expand Down Expand Up @@ -448,27 +449,16 @@ class Status extends ImmutablePureComponent {
} = this.props;
let attachments = null;

// Depending on user settings, some media are considered as parts of the
// contents (affected by CW) while other will be displayed outside of the
// CW.
let contentMedia = [];
let contentMediaIcons = [];
let extraMedia = [];
let extraMediaIcons = [];
let media = contentMedia;
let mediaIcons = contentMediaIcons;
let media = [];
let mediaIcons = [];
let statusAvatar;

if (settings.getIn(['content_warnings', 'media_outside'])) {
media = extraMedia;
mediaIcons = extraMediaIcons;
}

if (status === null) {
return null;
}

const isExpanded = settings.getIn(['content_warnings', 'shared_state']) ? !status.get('hidden') : this.state.isExpanded;
const expanded = isExpanded || status.get('spoiler_text').length === 0;

const handlers = {
reply: this.handleHotkeyReply,
Expand Down Expand Up @@ -498,13 +488,13 @@ class Status extends ImmutablePureComponent {
<div ref={this.handleRef} className='status focusable' tabIndex={unfocusable ? null : 0}>
<span>{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}</span>
{status.get('spoiler_text').length > 0 && (<span>{status.get('spoiler_text')}</span>)}
{isExpanded && <span>{status.get('content')}</span>}
{expanded && <span>{status.get('content')}</span>}
</div>
</HotKeys>
);
}

if (this.state.forceFilter === undefined ? matchedFilters : this.state.forceFilter) {
if (this.state.showDespiteFilter === undefined ? matchedFilters : this.state.showDespiteFilter) {
const minHandlers = this.props.muted ? {} : {
moveUp: this.handleHotkeyMoveUp,
moveDown: this.handleHotkeyMoveDown,
Expand Down Expand Up @@ -552,7 +542,7 @@ class Status extends ImmutablePureComponent {
sensitive={status.get('sensitive')}
letterbox={settings.getIn(['media', 'letterbox'])}
fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
hidden={!isExpanded}
hidden={!expanded}
onOpenMedia={this.handleOpenMedia}
cacheWidth={this.props.cacheMediaWidth}
defaultWidth={this.props.cachedMediaWidth}
Expand Down Expand Up @@ -609,7 +599,7 @@ class Status extends ImmutablePureComponent {
sensitive={status.get('sensitive')}
letterbox={settings.getIn(['media', 'letterbox'])}
fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
preventPlayback={!isExpanded}
preventPlayback={!expanded}
onOpenVideo={this.handleOpenVideo}
deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
visible={this.state.showMedia}
Expand All @@ -631,9 +621,7 @@ class Status extends ImmutablePureComponent {
}

if (status.get('poll')) {
const language = status.getIn(['translation', 'language']) || status.get('language');
contentMedia.push(<PollContainer pollId={status.get('poll')} status={status} lang={language} />);
contentMediaIcons.push('tasks');
mediaIcons.push('tasks');
}

// Here we prepare extra data-* attributes for CSS selectors.
Expand Down Expand Up @@ -672,7 +660,6 @@ class Status extends ImmutablePureComponent {
}

const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
contentMedia.push(hashtagBar);

return (
<HotKeys handlers={handlers} tabIndex={unfocusable ? null : -1}>
Expand Down Expand Up @@ -704,26 +691,35 @@ class Status extends ImmutablePureComponent {
</Permalink>
<StatusIcons
status={status}
mediaIcons={contentMediaIcons.concat(extraMediaIcons)}
mediaIcons={mediaIcons}
settings={settings.get('status_icons')}
/>
</header>
)}
<StatusContent
status={status}
onClick={this.handleClick}
onTranslate={this.handleTranslate}
collapsible
media={contentMedia}
extraMedia={extraMedia}
mediaIcons={contentMediaIcons}
expanded={isExpanded}
onExpandedToggle={this.handleExpandedToggle}
onCollapsedToggle={this.handleCollapsedToggle}
tagLinks={settings.get('tag_misleading_links')}
rewriteMentions={settings.get('rewrite_mentions')}
{...statusContentProps}
/>

{status.get('spoiler_text').length > 0 && <ContentWarning text={status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml')} expanded={expanded} onClick={this.handleExpandedToggle} icons={mediaIcons} />}

{expanded && (
<>
<StatusContent
status={status}
onClick={this.handleClick}
onTranslate={this.handleTranslate}
collapsible
media={media}
onCollapsedToggle={this.handleCollapsedToggle}
tagLinks={settings.get('tag_misleading_links')}
rewriteMentions={settings.get('rewrite_mentions')}
{...statusContentProps}
/>

{media}
{hashtagBar}
</>
)}

{/* This is a glitch-soc addition to have a placeholder */}
{!expanded && <MentionsPlaceholder status={status} />}

<StatusActionBar
status={status}
Expand Down
Loading
Loading