Merge pull request #132 from Y-zu-don-maintenance-org/features/quote

Features/quote
This commit is contained in:
YorimiMochida 2024-02-04 11:40:37 +09:00 committed by GitHub
commit c4712ed22c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 168 additions and 111 deletions

View File

@ -9,8 +9,8 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
def index
cache_if_unauthenticated!
@statuses = load_statuses
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
accounts = Account.where(id: account_ids)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
account_relationships: AccountRelationshipsPresenter.new(accounts, current_user&.account_id)

View File

@ -7,8 +7,8 @@ class Api::V1::BookmarksController < Api::BaseController
def index
@statuses = load_statuses
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
accounts = Account.where(id: account_ids)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
account_relationships: AccountRelationshipsPresenter.new(accounts, current_user&.account_id)

View File

@ -7,8 +7,8 @@ class Api::V1::FavouritesController < Api::BaseController
def index
@statuses = load_statuses
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
accounts = Account.where(id: account_ids)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
account_relationships: AccountRelationshipsPresenter.new(accounts, current_user&.account_id)

View File

@ -49,8 +49,8 @@ class Api::V1::StatusesController < Api::BaseController
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
statuses = [@status] + @context.ancestors + @context.descendants
accounts = statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
accounts = Account.where(id: account_ids)
render json: @context, serializer: REST::ContextSerializer,
relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id),

View File

@ -10,8 +10,8 @@ class Api::V1::Timelines::HomeController < Api::V1::Timelines::BaseController
with_read_replica do
@statuses = load_statuses
@relationships = StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
accounts = Account.where(id: account_ids)
@account_relationships = AccountRelationshipsPresenter.new(accounts, current_user&.account_id)
end

View File

@ -9,8 +9,8 @@ class Api::V1::Timelines::ListController < Api::V1::Timelines::BaseController
PERMITTED_PARAMS = %i(limit).freeze
def show
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
accounts = Account.where(id: account_ids)
render json: @statuses,
each_serializer: REST::StatusSerializer,

View File

@ -8,8 +8,8 @@ class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
def show
cache_if_unauthenticated!
@statuses = load_statuses
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
accounts = Account.where(id: account_ids)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),

View File

@ -9,8 +9,8 @@ class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController
def show
cache_if_unauthenticated!
@statuses = load_statuses
accounts = @statuses.filter_map { |status| status.quote&.account }.uniq
account_ids = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
accounts = Account.where(id: account_ids)
render json: @statuses, each_serializer: REST::StatusSerializer,
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),

View File

@ -25,7 +25,6 @@ export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
export const COMPOSE_REPLY = 'COMPOSE_REPLY';
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
export const COMPOSE_QUOTE = 'COMPOSE_QUOTE';
export const COMPOSE_QUOTE_CANCEL = 'COMPOSE_QUOTE_CANCEL';
export const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
export const COMPOSE_MENTION = 'COMPOSE_MENTION';
export const COMPOSE_RESET = 'COMPOSE_RESET';
@ -137,13 +136,7 @@ export function quoteCompose(status, routerHistory) {
ensureComposeIsVisible(getState, routerHistory);
};
};
export function cancelQuoteCompose() {
return {
type: COMPOSE_QUOTE_CANCEL,
};
};
}
export function resetCompose() {
return {

View File

@ -361,7 +361,7 @@ export function hideQuote(ids) {
type: QUOTE_HIDE,
ids,
};
};
}
export function revealQuote(ids) {
if (!Array.isArray(ids)) {
@ -372,4 +372,4 @@ export function revealQuote(ids) {
type: QUOTE_REVEAL,
ids,
};
};
}

View File

@ -134,7 +134,7 @@ export const quote = (status, muted, quoteMuted, handleQuoteClick, handleExpande
return (
<div>
<div className='status__info'>
{identity(quoteStatus, null, null, true)}
{identity(quoteStatus, null, true)}
</div>
<StatusContent status={quoteStatus} onClick={handleQuoteClick} expanded={!status.get('quote_hidden')} onExpandedToggle={handleExpandedQuoteToggle} quote />
{media(quoteStatus, true)}
@ -146,7 +146,6 @@ export const quote = (status, muted, quoteMuted, handleQuoteClick, handleExpande
<div
className={classNames('quote-status', `status-${quoteStatus.get('visibility')}`, { muted: muted })}
data-id={quoteStatus.get('id')}
dataurl={quoteStatus.get('url')}
>
{quoteInner}
</div>
@ -238,7 +237,7 @@ class Status extends ImmutablePureComponent {
handleToggleQuoteMediaVisibility = () => {
this.setState({ showQuoteMedia: !this.state.showQuoteMedia });
}
};
handleClick = e => {
if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) {
@ -253,10 +252,6 @@ class Status extends ImmutablePureComponent {
};
handlePrependAccountClick = e => {
this.handleAccountClick(e, false);
};
handleAccountClick = (e, proper = true) => {
if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) {
return;
}
@ -266,18 +261,30 @@ class Status extends ImmutablePureComponent {
e.stopPropagation();
}
this._openProfile(proper);
this._openProfile(false);
};
handleQuoteClick = () => {
if (!this.props) {
handleAccountClick = (e) => {
if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) {
return;
}
const { status } = this.props;
this.props.history.push(`/statuses/${status.getIn(['reblog', 'quote', 'id'], status.getIn(['quote', 'id']))}`);
if (e) {
e.preventDefault();
e.stopPropagation();
}
const acct = e.currentTarget.getAttribute('data-acct');
this.props.history.push(`/@${acct}`);
};
handleQuoteClick = () => {
if (this.props.history) {
const status = this._properStatus();
this.props.history.push(`/@${status.getIn(['quote', 'account', 'acct'])}/${status.getIn(['quote', 'id'])}`);
}
};
handleQuoteUserClick = () =>{
if (!this.props) {
return;
@ -301,7 +308,7 @@ class Status extends ImmutablePureComponent {
handleExpandedQuoteToggle = () => {
this.props.onQuoteToggleHidden(this._properStatus());
}
};
getAttachmentAspectRatio () {
const attachments = this._properStatus().get('media_attachments');
@ -554,9 +561,10 @@ class Status extends ImmutablePureComponent {
</div>
);
}
const media = (status, quote = false) => {
if (pictureInPicture.get('inUse')) {
return <PictureInPicturePlaceholder aspectRatio={this.getAttachmentAspectRatio()} />;
return <PictureInPicturePlaceholder aspectRatio={this.getAttachmentAspectRatio()} width={this.props.cachedMediaWidth} />;
} else if (status.get('media_attachments').size > 0) {
const language = status.getIn(['translation', 'language']) || status.get('language');
@ -656,8 +664,8 @@ class Status extends ImmutablePureComponent {
}
};
const identity = (status, account, _0, quote = false) => (
<a onClick={quote ? this.handleQuoteUserClick : this.handleAccountClick} href={`/@${status.getIn(['account', 'acct'])}`} title={status.getIn(['account', 'acct'])} className='status__display-name' rel='noopener noreferrer'>
const identity = (status, account) => (
<a onClick={this.handleAccountClick} data-acct={status.getIn(['account', 'acct'])} href={`/@${status.getIn(['account', 'acct'])}`} title={status.getIn(['account', 'acct'])} className='status__display-name' target='_blank' rel='noopener noreferrer'>
<div className='status__avatar'>
{statusAvatar(status, account)}
</div>
@ -721,4 +729,4 @@ class Status extends ImmutablePureComponent {
}
export default withOptionalRouter(injectIntl(Status));
export default connect(mapStateToProps)(withOptionalRouter(injectIntl(Status)));

View File

@ -11,6 +11,7 @@ import { connect } from 'react-redux';
import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg';
import BookmarkBorderIcon from '@/material-icons/400-24px/bookmark.svg?react';
import FormatQuoteIcon from '@/material-icons/400-24px/format_quote.svg?react';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
@ -160,8 +161,8 @@ class StatusActionBar extends ImmutablePureComponent {
};
handleQuoteClick = () => {
this.props.onQuote(this.props.status, this.props.history);
}
this.props.onQuote(this.props.status);
};
handleBookmarkClick = () => {
this.props.onBookmark(this.props.status);
@ -262,7 +263,7 @@ class StatusActionBar extends ImmutablePureComponent {
} else {
return intl.formatMessage(messages.cannot_quote);
}
}
};
render () {
const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props;
@ -406,8 +407,8 @@ class StatusActionBar extends ImmutablePureComponent {
<div className='status__action-bar'>
<IconButton className='status__action-bar__button' title={replyTitle} icon={isReply ? 'reply' : replyIcon} iconComponent={isReply ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} />
<IconButton className={classNames('status__action-bar__button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
<IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
<IconButton className='status__action-bar__button' disabled={!publicStatus} title={StatusActionBar.quoteTitle(intl, messages, publicStatus)} icon='quote' iconComponent={QuoteIcon} onClick={this.handleQuoteClick} />
<IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
<IconButton className='status__action-bar__button' disabled={!publicStatus} title={StatusActionBar.quoteTitle(intl, messages, publicStatus)} icon='format-quote' iconComponent={FormatQuoteIcon} onClick={this.handleQuoteClick} />
<IconButton className='status__action-bar__button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} />
{filterButton}

View File

@ -241,7 +241,7 @@ class StatusContent extends PureComponent {
};
render () {
const { status, intl, statusContent, quote } = this.props;
const { status, intl, quote, statusContent } = this.props;
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
const renderReadMore = this.props.onClick && status.get('collapsed');

View File

@ -29,6 +29,7 @@ import { CharacterCounter } from './character_counter';
import { EditIndicator } from './edit_indicator';
import { NavigationBar } from './navigation_bar';
import { PollForm } from "./poll_form";
import { QuoteIndicator } from './quote_indicator';
import { ReplyIndicator } from './reply_indicator';
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
@ -296,6 +297,8 @@ class ComposeForm extends ImmutablePureComponent {
<UploadFormContainer />
<PollForm />
<QuoteIndicator />
<div className='compose-form__footer'>
<div className='compose-form__dropdowns'>
<PrivacyDropdownContainer disabled={this.props.isEditing} />

View File

@ -0,0 +1,62 @@
import { useCallback } from "react";
import { FormattedMessage, defineMessages, useIntl } from "react-intl";
import { Link } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import BarChart4BarsIcon from 'mastodon/../material-icons/400-24px/bar_chart_4_bars.svg?react';
import CloseIcon from 'mastodon/../material-icons/400-24px/close.svg?react';
import PhotoLibraryIcon from 'mastodon/../material-icons/400-24px/photo_library.svg?react';
import { cancelReplyCompose } from "mastodon/actions/compose";
import { Avatar } from "mastodon/components/avatar";
import { DisplayName } from "mastodon/components/display_name";
import { Icon } from "mastodon/components/icon";
import { IconButton } from "mastodon/components/icon_button";
const messages = defineMessages({
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
});
export const QuoteIndicator = () => {
const intl = useIntl();
const dispatch = useDispatch();
const id = useSelector(state => state.getIn(['compose', 'quote_from']));
const status = useSelector(state => state.getIn(['statuses', id]));
const account = useSelector(state => state.getIn(['accounts', status?.get('account')]));
const handleCancelClick = useCallback(() => {
dispatch(cancelReplyCompose());
}, [dispatch]);
if (!status) {
return null;
}
const content = { __html: status.get('contentHtml') };
return (
<div className='edit-indicator'>
<div className='edit-indicator__header'>
<Link to={`/@${account.get('acct')}`} className='status__display-name'>
<Avatar account={account} size={46} />
<DisplayName account={account} />
</Link>
<div className='edit-indicator__cancel'>
<IconButton title={intl.formatMessage(messages.cancel)} icon='times' iconComponent={CloseIcon} onClick={handleCancelClick} inverted />
</div>
</div>
<div className='edit-indicator__content translate' dangerouslySetInnerHTML={content} />
{(status.get('poll') || status.get('media_attachments').size > 0) && (
<div className='edit-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

@ -167,7 +167,7 @@ class Footer extends ImmutablePureComponent {
}
dispatch(quoteCompose(status, router.history));
}
};
handleQuoteClick = () => {
const { dispatch, askReplyConfirmation, intl } = this.props;
@ -181,7 +181,7 @@ class Footer extends ImmutablePureComponent {
} else {
this._performQuote();
}
}
};
handleOpenClick = e => {
if (e.button !== 0 || !history) {

View File

@ -11,6 +11,7 @@ import { connect } from 'react-redux';
import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg?react';
import BookmarkBorderIcon from '@/material-icons/400-24px/bookmark.svg?react';
import FormatQuoteIcon from '@/material-icons/400-24px/format_quote.svg?react';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
@ -111,8 +112,8 @@ class ActionBar extends PureComponent {
};
handleQuoteClick = () => {
this.props.onQuote(this.props.status, this.props.history);
}
this.props.onQuote(this.props.status);
};
handleFavouriteClick = () => {
this.props.onFavourite(this.props.status);
@ -324,7 +325,7 @@ class ActionBar extends PureComponent {
<div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} iconComponent={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} /></div>
<div className='detailed-status__button'><IconButton className={classNames({ reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={reblogIconComponent} onClick={this.handleReblogClick} /></div>
<div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} /></div>
<div className='detailed-status__button'><IconButton disabled={!publicStatus} title={StatusActionBar.quoteTitle(intl, messages, publicStatus)} icon='quote' iconComponent={QuoteIcon} onClick={this.handleQuoteClick} /></div>
<div className='detailed-status__button'><IconButton disabled={!publicStatus} title={StatusActionBar.quoteTitle(intl, messages, publicStatus)} icon='format-quote' iconComponent={FormatQuoteIcon} onClick={this.handleQuoteClick} /></div>
<div className='detailed-status__button'><IconButton className='bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} /></div>
<div className='detailed-status__action-bar-dropdown'>
<DropdownMenuContainer icon='ellipsis-h' iconComponent={MoreHorizIcon} status={status} items={menu} direction='left' title={intl.formatMessage(messages.more)} />

View File

@ -152,9 +152,9 @@ export default class Card extends PureComponent {
};
if (largeImage && card.get('type') === 'video') {
thumbnailStyle.aspectRatio = `16 / 9`;
thumbnailStyle.aspectRatio = `${quote ? 8 : 16} / 9`;
} else if (largeImage) {
thumbnailStyle.aspectRatio = '1.91 / 1';
thumbnailStyle.aspectRatio = `1.91 / ${quote ? 2 : 1}`;
} else {
thumbnailStyle.aspectRatio = 1;
}

View File

@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import { FormattedDate, FormattedMessage } from 'react-intl';
import { FormattedDate, FormattedMessage, injectIntl } from 'react-intl';
import classNames from 'classnames';
import { Link, withRouter } from 'react-router-dom';
@ -62,7 +62,8 @@ class DetailedStatus extends ImmutablePureComponent {
handleAccountClick = (e) => {
if (e.button === 0 && !(e.ctrlKey || e.metaKey) && this.props.history) {
e.preventDefault();
this.props.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
const acct = e.currentTarget.getAttribute('data-acct');
this.props.history.push(`/@${acct}`);
}
e.stopPropagation();
@ -78,16 +79,14 @@ class DetailedStatus extends ImmutablePureComponent {
handleExpandedQuoteToggle = () => {
this.props.onQuoteToggleHidden(this.props.status);
}
};
handleQuoteClick = () => {
if (!this.props) {
return;
}
const { status } = this.props;
this.props.history.push(`/statuses/${status.getIn(['quote', 'id'])}`);
if (this.props.history) {
const status = this._properStatus();
this.props.history.push(`/@${status.getIn(['quote', 'account', 'acct'])}/${status.getIn(['quote', 'id'])}`);
}
};
handleQuoteUserClick = () =>{
if (!this.props) {
@ -180,11 +179,19 @@ class DetailedStatus extends ImmutablePureComponent {
const language = status.getIn(['translation', 'language']) || status.get('language');
const identity = (status, _0, _1, quote = false) => (
<a href={`/@${status.getIn(['account', 'acct'])}`} onClick={quote ? this.handleQuoteUserClick : this.handleAccountClick} data-acct={status.getIn(['account', 'acct'])} className='detailed-status__display-name'>
<div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={46} /></div>
const identity = (status, _, quote) => (
<>
{status.get('visibility') === 'direct' && (
<div className='status__prepend'>
<div className='status__prepend-icon-wrapper'><Icon id='at' className='status__prepend-icon' fixedWidth /></div>
<FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' />
</div>
)}
<a href={`/@${status.getIn(['account', 'acct'])}`} data-acct={status.getIn(['account', 'acct'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
<div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={quote ? 20 : 48} /></div>
<DisplayName account={status.get('account')} localDomain={this.props.domain} />
</a>
</>
);
const media = (status, quote = false) => {
@ -251,7 +258,8 @@ class DetailedStatus extends ImmutablePureComponent {
);
}
} else if (status.get('spoiler_text').length === 0) {
return <Card sensitive={status.get('sensitive')} onOpenMedia={this.props.onOpenMedia} card={status.get('card', null)} quote={quote} />;
return (<Card sensitive={status.get('sensitive')} onOpenMedia={this.props.onOpenMedia}
card={status.get('card', null)} quote={quote} />);
}
}
@ -331,10 +339,7 @@ class DetailedStatus extends ImmutablePureComponent {
<FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' />
</div>
)}
<a href={`/@${status.getIn(['account', 'acct'])}`} onClick={this.handleAccountClick} className='detailed-status__display-name'>
<div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={46} /></div>
<DisplayName account={status.get('account')} localDomain={this.props.domain} />
</a>
{identity(status, null, false)}
<StatusContent
status={status}
@ -362,4 +367,4 @@ class DetailedStatus extends ImmutablePureComponent {
}
export default withRouter(DetailedStatus);
export default connect(mapStateToProps)(withRouter(injectIntl(DetailedStatus)));

View File

@ -255,7 +255,7 @@ class Status extends ImmutablePureComponent {
handleToggleQuoteMediaVisibility = () => {
this.setState({ showQuoteMedia: !this.state.showQuoteMedia });
}
};
handleFavouriteClick = (status) => {
const { dispatch } = this.props;
@ -327,7 +327,7 @@ class Status extends ImmutablePureComponent {
} else {
dispatch(quoteCompose(status, this.props.history));
}
}
};
handleModalReblog = (status, privacy) => {
this.props.dispatch(reblog(status, privacy));
@ -450,7 +450,7 @@ class Status extends ImmutablePureComponent {
} else {
this.props.dispatch(hideQuote(status.get('id')));
}
}
};
handleToggleAll = () => {
const { status, ancestorsIds, descendantsIds } = this.props;

View File

@ -683,6 +683,7 @@
"status.translate": "翻訳",
"status.translated_from_with": "{provider}を使って{lang}から翻訳",
"status.uncached_media_warning": "プレビューは使用できません",
"status.unlisted_quote": "未収載の引用",
"status.unmute_conversation": "会話のミュートを解除",
"status.unpin": "プロフィールへの固定を解除",
"subscribed_languages.lead": "選択した言語のトゥートだけがホームとリストのタイムラインに表示されます。全ての言語のトゥートを受け取る場合は全てのチェックを外して下さい。",

View File

@ -7,7 +7,6 @@ import {
COMPOSE_REPLY,
COMPOSE_REPLY_CANCEL,
COMPOSE_QUOTE,
COMPOSE_QUOTE_CANCEL,
COMPOSE_DIRECT,
COMPOSE_MENTION,
COMPOSE_SUBMIT_REQUEST,
@ -397,7 +396,6 @@ export default function compose(state = initialState, action) {
case COMPOSE_UPLOAD_CHANGE_REQUEST:
return state.set('is_changing_upload', true);
case COMPOSE_REPLY_CANCEL:
case COMPOSE_QUOTE_CANCEL:
case COMPOSE_RESET:
case COMPOSE_SUBMIT_SUCCESS:
return clearAll(state);
@ -530,6 +528,8 @@ export default function compose(state = initialState, action) {
map.set('id', action.status.get('id'));
map.set('text', action.text);
map.set('in_reply_to', action.status.get('in_reply_to_id'));
map.set('quote_from', action.status.getIn(['quote', 'id']));
map.set('quote_from_url', action.status.getIn(['quote', 'url']));
map.set('privacy', action.status.get('visibility'));
map.set('media_attachments', action.status.get('media_attachments'));
map.set('focusDate', new Date());

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m228-240 92-160q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 23-5.5 42.5T458-480L320-240h-92Zm360 0 92-160q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 23-5.5 42.5T818-480L680-240h-92Z"/></svg>

After

Width:  |  Height:  |  Size: 322 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m228-240 92-160q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 23-5.5 42.5T458-480L320-240h-92Zm360 0 92-160q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 23-5.5 42.5T818-480L680-240h-92ZM320-500q25 0 42.5-17.5T380-560q0-25-17.5-42.5T320-620q-25 0-42.5 17.5T260-560q0 25 17.5 42.5T320-500Zm360 0q25 0 42.5-17.5T740-560q0-25-17.5-42.5T680-620q-25 0-42.5 17.5T620-560q0 25 17.5 42.5T680-500Zm0-60Zm-360 0Z"/></svg>

After

Width:  |  Height:  |  Size: 538 B

View File

@ -1105,6 +1105,10 @@ body > [data-popper-placement] {
grid-template-rows: 46px max-content;
gap: 0 10px;
&.quote-indicator {
background: $success-green;
}
.detailed-status__display-name {
margin-bottom: 4px;
}
@ -1422,17 +1426,6 @@ body > [data-popper-placement] {
appearance: none;
}
.status__avatar,
.detailed-status__display-avatar {
position: absolute;
top: 5px !important;
left: 5px !important;
}
.display-name {
padding-left: 56px;
}
.detailed-status__display-name {
margin-bottom: 0;
line-height: unset;
@ -1630,7 +1623,8 @@ body > [data-popper-placement] {
color: $dark-text-color;
}
.status__info .status__display-name {
.status__info .status__display-name,
.edit-indicator .status__display-name {
max-width: 100%;
display: flex;
font-size: 15px;

View File

@ -13,7 +13,7 @@
%span.display-name
%bdi
%strong.display-name__html.p-name.emojify= display_name(author, custom_emojify: true, autoplay: prefers_autoplay?)
&nbsp;
%span.display-name__account
= acct(author)
- if !inline && author.locked?

View File

@ -12,21 +12,8 @@
*
%data.dt-published{ value: status.created_at.to_time.iso8601 }
.p-author.h-card
= link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'status__display-name u-url', target: stream_link_target, rel: 'noopener noreferrer' do
.status__avatar
%div
- if prefers_autoplay?
= image_tag status.account.avatar_original_url, alt: '', class: 'u-photo account__avatar'
- else
= image_tag status.account.avatar_static_url, alt: '', class: 'u-photo account__avatar'
%span.display-name
%bdi
%strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true, autoplay: prefers_autoplay?)
&nbsp;
%span.display-name__account
= acct(status.account)
= fa_icon('lock') if status.account.locked?
= render 'statuses/author', author: status.account
.status__content.emojify{ data: ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }<
- if status.spoiler_text?
%p<