mirror of https://github.com/mastodon/mastodon
Add a visibility icon to status (#14123)
* Add a visibility icon to status * Change to using the icon element * Fix RTL * Add a public globe
This commit is contained in:
parent
1d2b0d2121
commit
418f0a33e9
|
@ -77,6 +77,18 @@ module ApplicationHelper
|
||||||
content_tag(:i, nil, attributes.merge(class: class_names.join(' ')))
|
content_tag(:i, nil, attributes.merge(class: class_names.join(' ')))
|
||||||
end
|
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)
|
def custom_emoji_tag(custom_emoji, animate = true)
|
||||||
if animate
|
if animate
|
||||||
image_tag(custom_emoji.image.url, class: 'emojione', alt: ":#{custom_emoji.shortcode}:")
|
image_tag(custom_emoji.image.url, class: 'emojione', alt: ":#{custom_emoji.shortcode}:")
|
||||||
|
|
|
@ -10,7 +10,7 @@ import StatusContent from './status_content';
|
||||||
import StatusActionBar from './status_action_bar';
|
import StatusActionBar from './status_action_bar';
|
||||||
import AttachmentList from './attachment_list';
|
import AttachmentList from './attachment_list';
|
||||||
import Card from '../features/status/components/card';
|
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 ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { MediaGallery, Video, Audio } from '../features/ui/util/async-components';
|
import { MediaGallery, Video, Audio } from '../features/ui/util/async-components';
|
||||||
import { HotKeys } from 'react-hotkeys';
|
import { HotKeys } from 'react-hotkeys';
|
||||||
|
@ -51,6 +51,13 @@ export const defaultMediaVisibility = (status) => {
|
||||||
return (displayMedia !== 'hide_all' && !status.get('sensitive') || displayMedia === 'show_all');
|
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
|
export default @injectIntl
|
||||||
class Status extends ImmutablePureComponent {
|
class Status extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
@ -416,6 +423,15 @@ class Status extends ImmutablePureComponent {
|
||||||
statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />;
|
statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<HotKeys handlers={handlers}>
|
<HotKeys handlers={handlers}>
|
||||||
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
|
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
|
||||||
|
@ -425,6 +441,7 @@ class Status extends ImmutablePureComponent {
|
||||||
<div className='status__expand' onClick={this.handleExpandClick} role='presentation' />
|
<div className='status__expand' onClick={this.handleExpandClick} role='presentation' />
|
||||||
<div className='status__info'>
|
<div className='status__info'>
|
||||||
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
|
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
|
||||||
|
<span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span>
|
||||||
|
|
||||||
<a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} title={status.getIn(['account', 'acct'])} className='status__display-name' target='_blank' rel='noopener noreferrer'>
|
<a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} title={status.getIn(['account', 'acct'])} className='status__display-name' target='_blank' rel='noopener noreferrer'>
|
||||||
<div className='status__avatar'>
|
<div className='status__avatar'>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import DisplayName from '../../../components/display_name';
|
||||||
import StatusContent from '../../../components/status_content';
|
import StatusContent from '../../../components/status_content';
|
||||||
import MediaGallery from '../../../components/media_gallery';
|
import MediaGallery from '../../../components/media_gallery';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { FormattedDate } from 'react-intl';
|
import { injectIntl, defineMessages, FormattedDate } from 'react-intl';
|
||||||
import Card from './card';
|
import Card from './card';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import Video from '../../video';
|
import Video from '../../video';
|
||||||
|
@ -16,7 +16,15 @@ import classNames from 'classnames';
|
||||||
import Icon from 'mastodon/components/icon';
|
import Icon from 'mastodon/components/icon';
|
||||||
import AnimatedNumber from 'mastodon/components/animated_number';
|
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 = {
|
static contextTypes = {
|
||||||
router: PropTypes.object,
|
router: PropTypes.object,
|
||||||
|
@ -92,7 +100,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||||
render () {
|
render () {
|
||||||
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
||||||
const outerStyle = { boxSizing: 'border-box' };
|
const outerStyle = { boxSizing: 'border-box' };
|
||||||
const { compact } = this.props;
|
const { intl, compact } = this.props;
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -157,34 +165,44 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.get('application')) {
|
if (status.get('application')) {
|
||||||
applicationLink = <span> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener noreferrer'>{status.getIn(['application', 'name'])}</a></span>;
|
applicationLink = <React.Fragment> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener noreferrer'>{status.getIn(['application', 'name'])}</a></React.Fragment>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.get('visibility') === 'direct') {
|
const visibilityIconInfo = {
|
||||||
reblogIcon = 'envelope';
|
'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) },
|
||||||
} else if (status.get('visibility') === 'private') {
|
'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) },
|
||||||
reblogIcon = 'lock';
|
'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 = <React.Fragment> · <Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></React.Fragment>;
|
||||||
|
|
||||||
if (['private', 'direct'].includes(status.get('visibility'))) {
|
if (['private', 'direct'].includes(status.get('visibility'))) {
|
||||||
reblogLink = <Icon id={reblogIcon} />;
|
reblogLink = '';
|
||||||
} else if (this.context.router) {
|
} else if (this.context.router) {
|
||||||
reblogLink = (
|
reblogLink = (
|
||||||
<Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'>
|
<React.Fragment>
|
||||||
<Icon id={reblogIcon} />
|
<React.Fragment> · </React.Fragment>
|
||||||
<span className='detailed-status__reblogs'>
|
<Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'>
|
||||||
<AnimatedNumber value={status.get('reblogs_count')} />
|
<Icon id={reblogIcon} />
|
||||||
</span>
|
<span className='detailed-status__reblogs'>
|
||||||
</Link>
|
<AnimatedNumber value={status.get('reblogs_count')} />
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
reblogLink = (
|
reblogLink = (
|
||||||
<a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}>
|
<React.Fragment>
|
||||||
<Icon id={reblogIcon} />
|
<React.Fragment> · </React.Fragment>
|
||||||
<span className='detailed-status__reblogs'>
|
<a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}>
|
||||||
<AnimatedNumber value={status.get('reblogs_count')} />
|
<Icon id={reblogIcon} />
|
||||||
</span>
|
<span className='detailed-status__reblogs'>
|
||||||
</a>
|
<AnimatedNumber value={status.get('reblogs_count')} />
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +228,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={outerStyle}>
|
<div style={outerStyle}>
|
||||||
<div ref={this.setRef} className={classNames('detailed-status', { compact })}>
|
<div ref={this.setRef} className={classNames('detailed-status', `detailed-status-${status.get('visibility')}`, { compact })}>
|
||||||
<a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
|
<a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
|
||||||
<div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div>
|
<div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div>
|
||||||
<DisplayName account={status.get('account')} localDomain={this.props.domain} />
|
<DisplayName account={status.get('account')} localDomain={this.props.domain} />
|
||||||
|
@ -223,7 +241,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||||
<div className='detailed-status__meta'>
|
<div className='detailed-status__meta'>
|
||||||
<a className='detailed-status__datetime' href={status.get('url')} target='_blank' rel='noopener noreferrer'>
|
<a className='detailed-status__datetime' href={status.get('url')} target='_blank' rel='noopener noreferrer'>
|
||||||
<FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />
|
<FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />
|
||||||
</a>{applicationLink} · {reblogLink} · {favouriteLink}
|
</a>{visibilityLink}{applicationLink}{reblogLink} · {favouriteLink}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1019,7 +1019,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.light {
|
&.light {
|
||||||
.status__relative-time {
|
.status__relative-time,
|
||||||
|
.status__visibility-icon {
|
||||||
color: $light-text-color;
|
color: $light-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1065,12 +1066,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.status__relative-time,
|
.status__relative-time,
|
||||||
|
.status__visibility-icon,
|
||||||
.notification__relative_time {
|
.notification__relative_time {
|
||||||
color: $dark-text-color;
|
color: $dark-text-color;
|
||||||
float: right;
|
float: right;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status__visibility-icon {
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.status__display-name {
|
.status__display-name {
|
||||||
color: $dark-text-color;
|
color: $dark-text-color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,6 +158,7 @@ body.rtl {
|
||||||
}
|
}
|
||||||
|
|
||||||
.status__relative-time,
|
.status__relative-time,
|
||||||
|
.status__visibility-icon,
|
||||||
.activity-stream .status.light .status__header .status__meta {
|
.activity-stream .status.light .status__header .status__meta {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.detailed-status.detailed-status--flex
|
.detailed-status.detailed-status--flex{ class: "detailed-status-#{status.visibility}" }
|
||||||
.p-author.h-card
|
.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
|
= 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
|
.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
|
= 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)
|
%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 && @account.user&.setting_show_application
|
||||||
- if status.application.website.blank?
|
- if status.application.website.blank?
|
||||||
%strong.detailed-status__application= status.application.name
|
%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
|
%span.detailed-status__reblogs>= number_to_human status.replies_count, strip_insignificant_zeros: true
|
||||||
= " "
|
= " "
|
||||||
·
|
·
|
||||||
- if status.direct_visibility?
|
- if status.public_visibility? || status.unlisted_visibility?
|
||||||
%span.detailed-status__link<
|
|
||||||
= fa_icon('envelope')
|
|
||||||
- elsif status.private_visibility? || status.limited_visibility?
|
|
||||||
%span.detailed-status__link<
|
|
||||||
= fa_icon('lock')
|
|
||||||
- else
|
|
||||||
= link_to remote_interaction_path(status, type: :reblog), class: 'modal-button detailed-status__link' do
|
= link_to remote_interaction_path(status, type: :reblog), class: 'modal-button detailed-status__link' do
|
||||||
= fa_icon('retweet')
|
= fa_icon('retweet')
|
||||||
%span.detailed-status__reblogs>= number_to_human status.reblogs_count, strip_insignificant_zeros: true
|
%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
|
= link_to remote_interaction_path(status, type: :favourite), class: 'modal-button detailed-status__link' do
|
||||||
= fa_icon('star')
|
= fa_icon('star')
|
||||||
%span.detailed-status__favorites>= number_to_human status.favourites_count, strip_insignificant_zeros: true
|
%span.detailed-status__favorites>= number_to_human status.favourites_count, strip_insignificant_zeros: true
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
.status
|
.status{ class: "status-#{status.visibility}" }
|
||||||
.status__info
|
.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
|
= 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)
|
%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 }
|
%data.dt-published{ value: status.created_at.to_time.iso8601 }
|
||||||
|
%span.status__visibility-icon
|
||||||
|
= visibility_icon status
|
||||||
|
|
||||||
.p-author.h-card
|
.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
|
= link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'status__display-name u-url', target: stream_link_target, rel: 'noopener noreferrer' do
|
||||||
|
|
Loading…
Reference in New Issue