diff options
Diffstat (limited to 'app/javascript')
7 files changed, 180 insertions, 3 deletions
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js index f0d2d481f..b2399ae9b 100644 --- a/app/javascript/mastodon/features/account/components/header.js +++ b/app/javascript/mastodon/features/account/components/header.js @@ -7,6 +7,7 @@ import Motion from '../../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { autoPlayGif, me } from '../../../initial_state'; +import classNames from 'classnames'; const messages = defineMessages({ unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, @@ -102,6 +103,10 @@ export default class Header extends ImmutablePureComponent { } } + if (account.get('moved')) { + actionBtn = ''; + } + if (account.get('locked')) { lockedIcon = <i className='fa fa-lock' />; } @@ -110,7 +115,7 @@ export default class Header extends ImmutablePureComponent { const displayNameHtml = { __html: account.get('display_name_html') }; return ( - <div className='account__header' style={{ backgroundImage: `url(${account.get('header')})` }}> + <div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${account.get('header')})` }}> <div> <Avatar account={account} /> diff --git a/app/javascript/mastodon/features/account_timeline/components/header.js b/app/javascript/mastodon/features/account_timeline/components/header.js index 8cf7b92ca..5e251c0e5 100644 --- a/app/javascript/mastodon/features/account_timeline/components/header.js +++ b/app/javascript/mastodon/features/account_timeline/components/header.js @@ -5,6 +5,7 @@ import InnerHeader from '../../account/components/header'; import ActionBar from '../../account/components/action_bar'; import MissingIndicator from '../../../components/missing_indicator'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import MovedNote from './moved_note'; export default class Header extends ImmutablePureComponent { @@ -68,6 +69,8 @@ export default class Header extends ImmutablePureComponent { return ( <div className='account-timeline__header'> + {account.get('moved') && <MovedNote from={account} to={account.get('moved')} />} + <InnerHeader account={account} onFollow={this.handleFollow} diff --git a/app/javascript/mastodon/features/account_timeline/components/moved_note.js b/app/javascript/mastodon/features/account_timeline/components/moved_note.js new file mode 100644 index 000000000..1c0e081cc --- /dev/null +++ b/app/javascript/mastodon/features/account_timeline/components/moved_note.js @@ -0,0 +1,48 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { FormattedMessage } from 'react-intl'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import AvatarOverlay from '../../../components/avatar_overlay'; +import DisplayName from '../../../components/display_name'; + +export default class MovedNote extends ImmutablePureComponent { + + static contextTypes = { + router: PropTypes.object, + }; + + static propTypes = { + from: ImmutablePropTypes.map.isRequired, + to: ImmutablePropTypes.map.isRequired, + }; + + handleAccountClick = e => { + if (e.button === 0) { + e.preventDefault(); + this.context.router.history.push(`/accounts/${this.props.to.get('id')}`); + } + + e.stopPropagation(); + } + + render () { + const { from, to } = this.props; + const displayNameHtml = { __html: from.get('display_name_html') }; + + return ( + <div className='account__moved-note'> + <div className='account__moved-note__message'> + <div className='account__moved-note__icon-wrapper'><i className='fa fa-fw fa-suitcase account__moved-note__icon' /></div> + <FormattedMessage id='account.moved_to' defaultMessage='{name} has moved to:' values={{ name: <strong dangerouslySetInnerHTML={displayNameHtml} /> }} /> + </div> + + <a href={to.get('url')} onClick={this.handleAccountClick} className='detailed-status__display-name'> + <div className='detailed-status__display-avatar'><AvatarOverlay account={to} friend={from} /></div> + <DisplayName account={to} /> + </a> + </div> + ); + } + +} diff --git a/app/javascript/mastodon/reducers/accounts.js b/app/javascript/mastodon/reducers/accounts.js index 8a4d69f26..5891d0fdd 100644 --- a/app/javascript/mastodon/reducers/accounts.js +++ b/app/javascript/mastodon/reducers/accounts.js @@ -59,6 +59,11 @@ const normalizeAccount = (state, account) => { account.display_name_html = emojify(escapeTextContentForBrowser(displayName)); account.note_emojified = emojify(account.note); + if (account.moved) { + state = normalizeAccount(state, account.moved); + account.moved = account.moved.id; + } + return state.set(account.id, fromJS(account)); }; diff --git a/app/javascript/mastodon/selectors/index.js b/app/javascript/mastodon/selectors/index.js index d26d1b727..e47ec5183 100644 --- a/app/javascript/mastodon/selectors/index.js +++ b/app/javascript/mastodon/selectors/index.js @@ -4,14 +4,18 @@ import { List as ImmutableList } from 'immutable'; const getAccountBase = (state, id) => state.getIn(['accounts', id], null); const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null); const getAccountRelationship = (state, id) => state.getIn(['relationships', id], null); +const getAccountMoved = (state, id) => state.getIn(['accounts', state.getIn(['accounts', id, 'moved'])]); export const makeGetAccount = () => { - return createSelector([getAccountBase, getAccountCounters, getAccountRelationship], (base, counters, relationship) => { + return createSelector([getAccountBase, getAccountCounters, getAccountRelationship, getAccountMoved], (base, counters, relationship, moved) => { if (base === null) { return null; } - return base.merge(counters).set('relationship', relationship); + return base.merge(counters).withMutations(map => { + map.set('relationship', relationship); + map.set('moved', moved); + }); }); }; diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 0ded6f159..b70769e18 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -917,6 +917,18 @@ background-position: center; position: relative; + &.inactive { + opacity: 0.5; + + .account__header__avatar { + filter: grayscale(100%); + } + + .account__header__username { + color: $ui-primary-color; + } + } + & > div { background: rgba(lighten($ui-base-color, 4%), 0.9); padding: 20px 10px; @@ -4375,3 +4387,40 @@ noscript { } } } + +.account__moved-note { + padding: 14px 10px; + padding-bottom: 16px; + background: lighten($ui-base-color, 4%); + border-top: 1px solid lighten($ui-base-color, 8%); + border-bottom: 1px solid lighten($ui-base-color, 8%); + + &__message { + position: relative; + margin-left: 58px; + color: $ui-base-lighter-color; + padding: 8px 0; + padding-top: 0; + padding-bottom: 4px; + font-size: 14px; + + > span { + display: block; + overflow: hidden; + text-overflow: ellipsis; + } + } + + &__icon-wrapper { + left: -26px; + position: absolute; + } + + .detailed-status__display-avatar { + position: relative; + } + + .detailed-status__display-name { + margin-bottom: 0; + } +} diff --git a/app/javascript/styles/mastodon/landing_strip.scss b/app/javascript/styles/mastodon/landing_strip.scss index 0bf9daafd..2f7c98d54 100644 --- a/app/javascript/styles/mastodon/landing_strip.scss +++ b/app/javascript/styles/mastodon/landing_strip.scss @@ -34,3 +34,66 @@ .memoriam-strip { background: rgba($base-shadow-color, 0.7); } + +.moved-strip { + padding: 14px; + border-radius: 4px; + background: rgba(darken($ui-base-color, 7%), 0.8); + color: $ui-secondary-color; + font-weight: 400; + margin-bottom: 20px; + + strong, + a { + font-weight: 500; + } + + a { + color: inherit; + text-decoration: underline; + + &.mention { + text-decoration: none; + + span { + text-decoration: none; + } + + &:focus, + &:hover, + &:active { + text-decoration: none; + + span { + text-decoration: underline; + } + } + } + } + + &__message { + margin-bottom: 15px; + + .fa { + margin-right: 5px; + color: $ui-primary-color; + } + } + + &__card { + .detailed-status__display-avatar { + position: relative; + cursor: pointer; + } + + .detailed-status__display-name { + margin-bottom: 0; + text-decoration: none; + + span { + color: $ui-highlight-color; + font-weight: 400; + } + } + } +} |