diff --git a/app/javascript/mastodon/components/edited_timestamp/index.jsx b/app/javascript/mastodon/components/edited_timestamp/index.jsx index 7b70f9d6e1..fbf14ec4bd 100644 --- a/app/javascript/mastodon/components/edited_timestamp/index.jsx +++ b/app/javascript/mastodon/components/edited_timestamp/index.jsx @@ -5,9 +5,7 @@ import { FormattedMessage, injectIntl } from 'react-intl'; import { connect } from 'react-redux'; -import ArrowDropDownIcon from '@/material-icons/400-24px/arrow_drop_down.svg?react'; import { openModal } from 'mastodon/actions/modal'; -import { Icon } from 'mastodon/components/icon'; import InlineAccount from 'mastodon/components/inline_account'; import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; @@ -67,7 +65,7 @@ class EditedTimestamp extends PureComponent { return ( <DropdownMenu statusId={statusId} renderItem={this.renderItem} scrollable renderHeader={this.renderHeader} onItemClick={this.handleItemClick}> <button className='dropdown-menu__text-button'> - <FormattedMessage id='status.edited' defaultMessage='Edited {date}' values={{ date: intl.formatDate(timestamp, { month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) }} /> <Icon id='caret-down' icon={ArrowDropDownIcon} /> + <FormattedMessage id='status.edited' defaultMessage='Edited {date}' values={{ date: <span className='animated-number'>{intl.formatDate(timestamp, { month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' })}</span> }} /> </button> </DropdownMenu> ); diff --git a/app/javascript/mastodon/features/status/components/detailed_status.jsx b/app/javascript/mastodon/features/status/components/detailed_status.jsx index d10c8966e4..45935716ca 100644 --- a/app/javascript/mastodon/features/status/components/detailed_status.jsx +++ b/app/javascript/mastodon/features/status/components/detailed_status.jsx @@ -9,8 +9,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react'; -import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react'; -import StarIcon from '@/material-icons/400-24px/star-fill.svg?react'; import { AnimatedNumber } from 'mastodon/components/animated_number'; import EditedTimestamp from 'mastodon/components/edited_timestamp'; import { getHashtagBarForStatus } from 'mastodon/components/hashtag_bar'; @@ -143,10 +141,7 @@ class DetailedStatus extends ImmutablePureComponent { let media = ''; let applicationLink = ''; let reblogLink = ''; - const reblogIcon = 'retweet'; - const reblogIconComponent = RepeatIcon; let favouriteLink = ''; - let edited = ''; if (this.props.measureHeight) { outerStyle.height = `${this.state.height}px`; @@ -218,68 +213,53 @@ class DetailedStatus extends ImmutablePureComponent { } if (status.get('application')) { - applicationLink = <> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener noreferrer'>{status.getIn(['application', 'name'])}</a></>; + applicationLink = <>·<a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener noreferrer'>{status.getIn(['application', 'name'])}</a></>; } - const visibilityLink = <> · <VisibilityIcon visibility={status.get('visibility')} /></>; + const visibilityLink = <>·<VisibilityIcon visibility={status.get('visibility')} /></>; if (['private', 'direct'].includes(status.get('visibility'))) { reblogLink = ''; } else if (this.props.history) { reblogLink = ( - <> - {' · '} - <Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/reblogs`} className='detailed-status__link'> - <Icon id={reblogIcon} icon={reblogIconComponent} /> - <span className='detailed-status__reblogs'> - <AnimatedNumber value={status.get('reblogs_count')} /> - </span> - </Link> - </> + <Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/reblogs`} className='detailed-status__link'> + <span className='detailed-status__reblogs'> + <AnimatedNumber value={status.get('reblogs_count')} /> + </span> + <FormattedMessage id='status.reblogs' defaultMessage='{count, plural, one {boost} other {boosts}}' values={{ count: status.get('reblogs_count') }} /> + </Link> ); } else { reblogLink = ( - <> - {' · '} - <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}> - <Icon id={reblogIcon} icon={reblogIconComponent} /> - <span className='detailed-status__reblogs'> - <AnimatedNumber value={status.get('reblogs_count')} /> - </span> - </a> - </> + <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}> + <span className='detailed-status__reblogs'> + <AnimatedNumber value={status.get('reblogs_count')} /> + </span> + <FormattedMessage id='status.reblogs' defaultMessage='{count, plural, one {boost} other {boosts}}' values={{ count: status.get('reblogs_count') }} /> + </a> ); } if (this.props.history) { favouriteLink = ( <Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/favourites`} className='detailed-status__link'> - <Icon id='star' icon={StarIcon} /> <span className='detailed-status__favorites'> <AnimatedNumber value={status.get('favourites_count')} /> </span> + <FormattedMessage id='status.favourites' defaultMessage='{count, plural, one {favorite} other {favorites}}' values={{ count: status.get('favourites_count') }} /> </Link> ); } else { favouriteLink = ( <a href={`/interact/${status.get('id')}?type=favourite`} className='detailed-status__link' onClick={this.handleModalLink}> - <Icon id='star' icon={StarIcon} /> <span className='detailed-status__favorites'> <AnimatedNumber value={status.get('favourites_count')} /> </span> + <FormattedMessage id='status.favourites' defaultMessage='{count, plural, one {favorite} other {favorites}}' values={{ count: status.get('favourites_count') }} /> </a> ); } - if (status.get('edited_at')) { - edited = ( - <> - {' · '} - <EditedTimestamp statusId={status.get('id')} timestamp={status.get('edited_at')} /> - </> - ); - } - const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status); const expanded = !status.get('hidden') || status.get('spoiler_text').length === 0; @@ -310,9 +290,23 @@ class DetailedStatus extends ImmutablePureComponent { {expanded && hashtagBar} <div className='detailed-status__meta'> - <a className='detailed-status__datetime' href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} target='_blank' rel='noopener noreferrer'> - <FormattedDate value={new Date(status.get('created_at'))} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' /> - </a>{edited}{visibilityLink}{applicationLink}{reblogLink} · {favouriteLink} + <div className='detailed-status__meta__line'> + <a className='detailed-status__datetime' href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} target='_blank' rel='noopener noreferrer'> + <FormattedDate value={new Date(status.get('created_at'))} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' /> + </a> + + {visibilityLink} + + {applicationLink} + </div> + + {status.get('edited_at') && <div className='detailed-status__meta__line'><EditedTimestamp statusId={status.get('id')} timestamp={status.get('edited_at')} /></div>} + + <div className='detailed-status__meta__line'> + {reblogLink} + · + {favouriteLink} + </div> </div> </div> </div> diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 2f202bfe15..8a66695f32 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -662,10 +662,11 @@ "status.direct": "Privately mention @{name}", "status.direct_indicator": "Private mention", "status.edit": "Edit", - "status.edited": "Edited {date}", + "status.edited": "Last edited {date}", "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}", "status.embed": "Embed", "status.favourite": "Favorite", + "status.favourites": "{count, plural, one {favorite} other {favorites}}", "status.filter": "Filter this post", "status.filtered": "Filtered", "status.hide": "Hide post", @@ -686,6 +687,7 @@ "status.reblog": "Boost", "status.reblog_private": "Boost with original visibility", "status.reblogged_by": "{name} boosted", + "status.reblogs": "{count, plural, one {boost} other {boosts}}", "status.reblogs.empty": "No one has boosted this post yet. When someone does, they will show up here.", "status.redraft": "Delete & re-draft", "status.remove_bookmark": "Remove bookmark", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 6aa3588501..398babb259 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1659,15 +1659,35 @@ body > [data-popper-placement] { } .detailed-status__meta { - margin-top: 16px; + margin-top: 24px; color: $dark-text-color; font-size: 14px; line-height: 18px; + &__line { + border-bottom: 1px solid var(--background-border-color); + padding: 8px 0; + display: flex; + align-items: center; + gap: 8px; + + &:first-child { + padding-top: 0; + } + + &:last-child { + padding-bottom: 0; + border-bottom: 0; + } + } + .icon { - width: 15px; - height: 15px; - vertical-align: middle; + width: 18px; + height: 18px; + } + + .animated-number { + color: $secondary-text-color; } } @@ -1711,19 +1731,6 @@ body > [data-popper-placement] { color: inherit; text-decoration: none; gap: 6px; - position: relative; - top: 0.145em; - - .icon { - top: 0; - } -} - -.detailed-status__favorites, -.detailed-status__reblogs { - font-weight: 500; - font-size: 12px; - line-height: 18px; } .domain { @@ -2292,6 +2299,10 @@ a.account__display-name { outline: 1px dotted; } + &:hover { + text-decoration: underline; + } + .icon { width: 15px; height: 15px;