diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 2c03bd1d51..716df0bacc 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -77,6 +77,18 @@ module ApplicationHelper content_tag(:i, nil, attributes.merge(class: class_names.join(' '))) end + def visibility_icon(status) + if status.public_visibility? + fa_icon('globe', title: I18n.t('statuses.visibilities.public')) + elsif status.unlisted_visibility? + fa_icon('unlock', title: I18n.t('statuses.visibilities.unlisted')) + elsif status.private_visibility? || status.limited_visibility? + fa_icon('lock', title: I18n.t('statuses.visibilities.private')) + elsif status.direct_visibility? + fa_icon('envelope', title: I18n.t('statuses.visibilities.direct')) + end + end + def custom_emoji_tag(custom_emoji, animate = true) if animate image_tag(custom_emoji.image.url, class: 'emojione', alt: ":#{custom_emoji.shortcode}:") diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 5f42534bac..2dc9619366 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -10,7 +10,7 @@ import StatusContent from './status_content'; import StatusActionBar from './status_action_bar'; import AttachmentList from './attachment_list'; import Card from '../features/status/components/card'; -import { injectIntl, FormattedMessage } from 'react-intl'; +import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { MediaGallery, Video, Audio } from '../features/ui/util/async-components'; import { HotKeys } from 'react-hotkeys'; @@ -51,6 +51,13 @@ export const defaultMediaVisibility = (status) => { return (displayMedia !== 'hide_all' && !status.get('sensitive') || displayMedia === 'show_all'); }; +const messages = defineMessages({ + public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, + unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' }, + private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' }, + direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' }, +}); + export default @injectIntl class Status extends ImmutablePureComponent { @@ -416,6 +423,15 @@ class Status extends ImmutablePureComponent { statusAvatar = ; } + const visibilityIconInfo = { + 'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) }, + 'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) }, + 'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) }, + 'direct': { icon: 'envelope', text: intl.formatMessage(messages.direct_short) }, + }; + + const visibilityIcon = visibilityIconInfo[status.get('visibility')]; + return (
@@ -425,6 +441,7 @@ class Status extends ImmutablePureComponent {
+
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.js b/app/javascript/mastodon/features/status/components/detailed_status.js index 72d15ddf71..935e4207e9 100644 --- a/app/javascript/mastodon/features/status/components/detailed_status.js +++ b/app/javascript/mastodon/features/status/components/detailed_status.js @@ -6,7 +6,7 @@ import DisplayName from '../../../components/display_name'; import StatusContent from '../../../components/status_content'; import MediaGallery from '../../../components/media_gallery'; import { Link } from 'react-router-dom'; -import { FormattedDate } from 'react-intl'; +import { injectIntl, defineMessages, FormattedDate } from 'react-intl'; import Card from './card'; import ImmutablePureComponent from 'react-immutable-pure-component'; import Video from '../../video'; @@ -16,7 +16,15 @@ import classNames from 'classnames'; import Icon from 'mastodon/components/icon'; import AnimatedNumber from 'mastodon/components/animated_number'; -export default class DetailedStatus extends ImmutablePureComponent { +const messages = defineMessages({ + public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, + unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' }, + private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' }, + direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' }, +}); + +export default @injectIntl +class DetailedStatus extends ImmutablePureComponent { static contextTypes = { router: PropTypes.object, @@ -92,7 +100,7 @@ export default class DetailedStatus extends ImmutablePureComponent { render () { const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status; const outerStyle = { boxSizing: 'border-box' }; - const { compact } = this.props; + const { intl, compact } = this.props; if (!status) { return null; @@ -157,34 +165,44 @@ export default class DetailedStatus extends ImmutablePureComponent { } if (status.get('application')) { - applicationLink = · {status.getIn(['application', 'name'])}; + applicationLink = · {status.getIn(['application', 'name'])}; } - if (status.get('visibility') === 'direct') { - reblogIcon = 'envelope'; - } else if (status.get('visibility') === 'private') { - reblogIcon = 'lock'; - } + const visibilityIconInfo = { + 'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) }, + 'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) }, + 'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) }, + 'direct': { icon: 'envelope', text: intl.formatMessage(messages.direct_short) }, + }; + + const visibilityIcon = visibilityIconInfo[status.get('visibility')]; + const visibilityLink = · ; if (['private', 'direct'].includes(status.get('visibility'))) { - reblogLink = ; + reblogLink = ''; } else if (this.context.router) { reblogLink = ( - - - - - - + + · + + + + + + + ); } else { reblogLink = ( - - - - - - + + · + + + + + + + ); } @@ -210,7 +228,7 @@ export default class DetailedStatus extends ImmutablePureComponent { return (
-
+
@@ -223,7 +241,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
- {applicationLink} · {reblogLink} · {favouriteLink} + {visibilityLink}{applicationLink}{reblogLink} · {favouriteLink}
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 2ff9073db1..e3da780ef0 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1019,7 +1019,8 @@ } &.light { - .status__relative-time { + .status__relative-time, + .status__visibility-icon { color: $light-text-color; } @@ -1065,12 +1066,18 @@ } .status__relative-time, +.status__visibility-icon, .notification__relative_time { color: $dark-text-color; float: right; font-size: 14px; } +.status__visibility-icon { + margin-left: 4px; + margin-right: 4px; +} + .status__display-name { color: $dark-text-color; } diff --git a/app/javascript/styles/mastodon/rtl.scss b/app/javascript/styles/mastodon/rtl.scss index ecd1662535..fbf26e30bc 100644 --- a/app/javascript/styles/mastodon/rtl.scss +++ b/app/javascript/styles/mastodon/rtl.scss @@ -158,6 +158,7 @@ body.rtl { } .status__relative-time, + .status__visibility-icon, .activity-stream .status.light .status__header .status__meta { float: left; } diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml index c23733053a..684dd08d16 100644 --- a/app/views/statuses/_detailed_status.html.haml +++ b/app/views/statuses/_detailed_status.html.haml @@ -1,4 +1,4 @@ -.detailed-status.detailed-status--flex +.detailed-status.detailed-status--flex{ class: "detailed-status-#{status.visibility}" } .p-author.h-card = link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'detailed-status__display-name u-url', target: stream_link_target, rel: 'noopener' do .detailed-status__display-avatar @@ -47,6 +47,9 @@ = link_to ActivityPub::TagManager.instance.url_for(status), class: 'detailed-status__datetime u-url u-uid', target: stream_link_target, rel: 'noopener noreferrer' do %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) · + %span.detailed-status__visibility-icon + = visibility_icon status + · - if status.application && @account.user&.setting_show_application - if status.application.website.blank? %strong.detailed-status__application= status.application.name @@ -61,18 +64,12 @@ %span.detailed-status__reblogs>= number_to_human status.replies_count, strip_insignificant_zeros: true = " " · - - if status.direct_visibility? - %span.detailed-status__link< - = fa_icon('envelope') - - elsif status.private_visibility? || status.limited_visibility? - %span.detailed-status__link< - = fa_icon('lock') - - else + - if status.public_visibility? || status.unlisted_visibility? = link_to remote_interaction_path(status, type: :reblog), class: 'modal-button detailed-status__link' do = fa_icon('retweet') %span.detailed-status__reblogs>= number_to_human status.reblogs_count, strip_insignificant_zeros: true = " " - · + · = link_to remote_interaction_path(status, type: :favourite), class: 'modal-button detailed-status__link' do = fa_icon('star') %span.detailed-status__favorites>= number_to_human status.favourites_count, strip_insignificant_zeros: true diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml index d5950658a6..06dc5ff937 100644 --- a/app/views/statuses/_simple_status.html.haml +++ b/app/views/statuses/_simple_status.html.haml @@ -1,8 +1,10 @@ -.status +.status{ class: "status-#{status.visibility}" } .status__info = link_to ActivityPub::TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', target: stream_link_target, rel: 'noopener noreferrer' do %time.time-ago{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) %data.dt-published{ value: status.created_at.to_time.iso8601 } + %span.status__visibility-icon + = visibility_icon status .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