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:
		| @ -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}:") | ||||
|  | ||||
| @ -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 = <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 ( | ||||
|       <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}> | ||||
| @ -425,6 +441,7 @@ class Status extends ImmutablePureComponent { | ||||
|             <div className='status__expand' onClick={this.handleExpandClick} role='presentation' /> | ||||
|             <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> | ||||
|               <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'> | ||||
|                 <div className='status__avatar'> | ||||
|  | ||||
| @ -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 = <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') { | ||||
|       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 = <React.Fragment> · <Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></React.Fragment>; | ||||
|  | ||||
|     if (['private', 'direct'].includes(status.get('visibility'))) { | ||||
|       reblogLink = <Icon id={reblogIcon} />; | ||||
|       reblogLink = ''; | ||||
|     } else if (this.context.router) { | ||||
|       reblogLink = ( | ||||
|         <Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'> | ||||
|           <Icon id={reblogIcon} /> | ||||
|           <span className='detailed-status__reblogs'> | ||||
|             <AnimatedNumber value={status.get('reblogs_count')} /> | ||||
|           </span> | ||||
|         </Link> | ||||
|         <React.Fragment> | ||||
|           <React.Fragment> · </React.Fragment> | ||||
|           <Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'> | ||||
|             <Icon id={reblogIcon} /> | ||||
|             <span className='detailed-status__reblogs'> | ||||
|               <AnimatedNumber value={status.get('reblogs_count')} /> | ||||
|             </span> | ||||
|           </Link> | ||||
|         </React.Fragment> | ||||
|       ); | ||||
|     } else { | ||||
|       reblogLink = ( | ||||
|         <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}> | ||||
|           <Icon id={reblogIcon} /> | ||||
|           <span className='detailed-status__reblogs'> | ||||
|             <AnimatedNumber value={status.get('reblogs_count')} /> | ||||
|           </span> | ||||
|         </a> | ||||
|         <React.Fragment> | ||||
|           <React.Fragment> · </React.Fragment> | ||||
|           <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}> | ||||
|             <Icon id={reblogIcon} /> | ||||
|             <span className='detailed-status__reblogs'> | ||||
|               <AnimatedNumber value={status.get('reblogs_count')} /> | ||||
|             </span> | ||||
|           </a> | ||||
|         </React.Fragment> | ||||
|       ); | ||||
|     } | ||||
|  | ||||
| @ -210,7 +228,7 @@ export default class DetailedStatus extends ImmutablePureComponent { | ||||
|  | ||||
|     return ( | ||||
|       <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'> | ||||
|             <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div> | ||||
|             <DisplayName account={status.get('account')} localDomain={this.props.domain} /> | ||||
| @ -223,7 +241,7 @@ export default class DetailedStatus extends ImmutablePureComponent { | ||||
|           <div className='detailed-status__meta'> | ||||
|             <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' /> | ||||
|             </a>{applicationLink} · {reblogLink} · {favouriteLink} | ||||
|             </a>{visibilityLink}{applicationLink}{reblogLink} · {favouriteLink} | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -158,6 +158,7 @@ body.rtl { | ||||
|   } | ||||
|  | ||||
|   .status__relative-time, | ||||
|   .status__visibility-icon, | ||||
|   .activity-stream .status.light .status__header .status__meta { | ||||
|     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 | ||||
|     = 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 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
		Reference in New Issue
	
	Block a user