Add advanced mode to the account column
This commit is contained in:
parent
4b3f4be472
commit
2bee3e3fdb
24 changed files with 1296 additions and 165 deletions
34
app/javascript/mastodon/actions/featured_tags.js
Normal file
34
app/javascript/mastodon/actions/featured_tags.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import api from '../api';
|
||||||
|
|
||||||
|
export const FEATURED_TAGS_FETCH_REQUEST = 'FEATURED_TAGS_FETCH_REQUEST';
|
||||||
|
export const FEATURED_TAGS_FETCH_SUCCESS = 'FEATURED_TAGS_FETCH_SUCCESS';
|
||||||
|
export const FEATURED_TAGS_FETCH_FAIL = 'FEATURED_TAGS_FETCH_FAIL';
|
||||||
|
|
||||||
|
export const fetchFeaturedTags = (id) => (dispatch, getState) => {
|
||||||
|
if (getState().getIn(['user_lists', 'featured_tags', id, 'items'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(fetchFeaturedTagsRequest(id));
|
||||||
|
|
||||||
|
api(getState).get(`/api/v1/accounts/${id}/featured_tags`)
|
||||||
|
.then(({ data }) => dispatch(fetchFeaturedTagsSuccess(id, data)))
|
||||||
|
.catch(err => dispatch(fetchFeaturedTagsFail(id, err)));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchFeaturedTagsRequest = (id) => ({
|
||||||
|
type: FEATURED_TAGS_FETCH_REQUEST,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fetchFeaturedTagsSuccess = (id, tags) => ({
|
||||||
|
type: FEATURED_TAGS_FETCH_SUCCESS,
|
||||||
|
id,
|
||||||
|
tags,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fetchFeaturedTagsFail = (id, error) => ({
|
||||||
|
type: FEATURED_TAGS_FETCH_FAIL,
|
||||||
|
id,
|
||||||
|
error,
|
||||||
|
});
|
|
@ -182,9 +182,9 @@ export const expandLimitedTimeline = ({ maxId, visibilities } = {}, done
|
||||||
export const expandPublicTimeline = ({ maxId, onlyMedia, withoutMedia, withoutBot, onlyRemote } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : ''}${withoutBot ? ':nobot' : ':bot'}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, without_bot: !!withoutBot }, done);
|
export const expandPublicTimeline = ({ maxId, onlyMedia, withoutMedia, withoutBot, onlyRemote } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : ''}${withoutBot ? ':nobot' : ':bot'}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, without_bot: !!withoutBot }, done);
|
||||||
export const expandDomainTimeline = (domain, { maxId, onlyMedia, withoutMedia, withoutBot } = {}, done = noOp) => expandTimeline(`domain${withoutBot ? ':nobot' : ':bot'}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}:${domain}`, '/api/v1/timelines/public', { local: false, domain: domain, max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, without_bot: !!withoutBot }, done);
|
export const expandDomainTimeline = (domain, { maxId, onlyMedia, withoutMedia, withoutBot } = {}, done = noOp) => expandTimeline(`domain${withoutBot ? ':nobot' : ':bot'}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}:${domain}`, '/api/v1/timelines/public', { local: false, domain: domain, max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, without_bot: !!withoutBot }, done);
|
||||||
export const expandGroupTimeline = (id, { maxId, onlyMedia, withoutMedia, tagged } = {}, done = noOp) => expandTimeline(`group:${id}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}${tagged ? `:${tagged}` : ''}`, `/api/v1/timelines/group/${id}`, { max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, tagged: tagged }, done);
|
export const expandGroupTimeline = (id, { maxId, onlyMedia, withoutMedia, tagged } = {}, done = noOp) => expandTimeline(`group:${id}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}${tagged ? `:${tagged}` : ''}`, `/api/v1/timelines/group/${id}`, { max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, tagged: tagged }, done);
|
||||||
export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
|
export const expandAccountTimeline = (accountId, { maxId, withReplies, withoutReblogs, tagged } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}${withoutReblogs ? ':without_reblogs' : ''}${tagged ? `:${tagged}` : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, exclude_reblogs: withoutReblogs, tagged, max_id: maxId });
|
||||||
export const expandAccountCoversations = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:conversations`, `/api/v1/accounts/${accountId}/conversations`, { max_id: maxId });
|
export const expandAccountCoversations = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:conversations`, `/api/v1/accounts/${accountId}/conversations`, { max_id: maxId });
|
||||||
export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
|
export const expandAccountFeaturedTimeline = (accountId, { tagged } = {}) => expandTimeline(`account:${accountId}:pinned${tagged ? `:${tagged}` : ''}`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true, tagged });
|
||||||
export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: 40 });
|
export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: 40 });
|
||||||
export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
|
export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
|
||||||
export const expandHashtagTimeline = (hashtag, { maxId, tags } = {}, done = noOp) => {
|
export const expandHashtagTimeline = (hashtag, { maxId, tags } = {}, done = noOp) => {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import ScrollableList from './scrollable_list';
|
||||||
import RegenerationIndicator from 'mastodon/components/regeneration_indicator';
|
import RegenerationIndicator from 'mastodon/components/regeneration_indicator';
|
||||||
import { isIOS } from 'mastodon/is_mobile';
|
import { isIOS } from 'mastodon/is_mobile';
|
||||||
import { showReloadButton } from '../initial_state';
|
import { showReloadButton } from '../initial_state';
|
||||||
|
import { List as ImmutableList } from 'immutable';
|
||||||
|
|
||||||
export default class StatusList extends ImmutablePureComponent {
|
export default class StatusList extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
@ -112,7 +113,7 @@ export default class StatusList extends ImmutablePureComponent {
|
||||||
showCard={showCard}
|
showCard={showCard}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
) : null;
|
) : ImmutableList();
|
||||||
|
|
||||||
if (scrollableContent && featuredStatusIds) {
|
if (scrollableContent && featuredStatusIds) {
|
||||||
scrollableContent = featuredStatusIds.map(statusId => (
|
scrollableContent = featuredStatusIds.map(statusId => (
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import Permalink from 'mastodon/components/permalink';
|
||||||
|
import ShortNumber from 'mastodon/components/short_number';
|
||||||
|
import { List as ImmutableList } from 'immutable';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
hashtag_all: { id: 'account.hashtag_all', defaultMessage: 'All' },
|
||||||
|
hashtag_all_description: { id: 'account.hashtag_all_description', defaultMessage: 'All posts (deselect hashtags)' },
|
||||||
|
hashtag_select_description: { id: 'account.hashtag_select_description', defaultMessage: 'Select hashtag #{name}' },
|
||||||
|
statuses_counter: { id: 'account.statuses_counter', defaultMessage: '{count, plural, one {{counter} Post} other {{counter} Posts}}' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapStateToProps = (state, { account }) => ({
|
||||||
|
featuredTags: state.getIn(['user_lists', 'featured_tags', account.get('id'), 'items'], ImmutableList()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
|
class FeaturedTags extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static contextTypes = {
|
||||||
|
router: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
account: ImmutablePropTypes.map,
|
||||||
|
featuredTags: ImmutablePropTypes.list,
|
||||||
|
tagged: PropTypes.string,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { account, featuredTags, tagged, intl } = this.props;
|
||||||
|
|
||||||
|
if (!account || featuredTags.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const suspended = account.get('suspended');
|
||||||
|
const accountId = account.get('id');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('account__header', 'advanced', { inactive: !!account.get('moved') })}>
|
||||||
|
<div className='account__header__extra'>
|
||||||
|
<div className='account__header__extra__hashtag-links'>
|
||||||
|
<Permalink key='all' className={classNames('account__hashtag-link', { active: !tagged })} title={intl.formatMessage(messages.hashtag_all_description)} href={account.get('url')} to={`/accounts/${accountId}/posts`}>{intl.formatMessage(messages.hashtag_all)}</Permalink>
|
||||||
|
{!suspended && featuredTags.map(featuredTag => {
|
||||||
|
const name = featuredTag.get('name');
|
||||||
|
const url = featuredTag.get('url');
|
||||||
|
const to = `/accounts/${accountId}/posts/${name}`;
|
||||||
|
const desc = intl.formatMessage(messages.hashtag_select_description, { name });
|
||||||
|
const count = featuredTag.get('statuses_count');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Permalink key={`#${name}`} className={classNames('account__hashtag-link', { active: this.context.router.history.location.pathname === to })} title={desc} href={url} to={to}>
|
||||||
|
#{name} <span title={intl.formatMessage(messages.statuses_counter, { count: count, counter: intl.formatNumber(count) })}>({<ShortNumber value={count} />})</span>
|
||||||
|
</Permalink>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -93,6 +93,7 @@ class Header extends ImmutablePureComponent {
|
||||||
onEditAccountNote: PropTypes.func.isRequired,
|
onEditAccountNote: PropTypes.func.isRequired,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
domain: PropTypes.string.isRequired,
|
domain: PropTypes.string.isRequired,
|
||||||
|
hideProfile: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
openEditProfile = () => {
|
openEditProfile = () => {
|
||||||
|
@ -154,7 +155,7 @@ class Header extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, intl, domain, identity_proofs } = this.props;
|
const { account, intl, domain, identity_proofs, hideProfile } = this.props;
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -390,6 +391,7 @@ class Header extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='account__header__extra'>
|
<div className='account__header__extra'>
|
||||||
|
{!hideProfile && (
|
||||||
<div className='account__header__bio'>
|
<div className='account__header__bio'>
|
||||||
{(fields.size > 0 || identity_proofs.size > 0) && (
|
{(fields.size > 0 || identity_proofs.size > 0) && (
|
||||||
<div className='account__header__fields'>
|
<div className='account__header__fields'>
|
||||||
|
@ -440,10 +442,11 @@ class Header extends ImmutablePureComponent {
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{!suspended && (
|
{!hideProfile && !suspended && (
|
||||||
<div className='account__header__extra__links'>
|
<div className='account__header__extra__links'>
|
||||||
<NavLink isActive={this.isStatusesPageActive} activeClassName='active' to={`/accounts/${account.get('id')}`} title={hide_statuses_count ? intl.formatMessage(messages.secret) : intl.formatNumber(account.get('statuses_count'))}>
|
<NavLink isActive={this.isStatusesPageActive} activeClassName='active' to={`/accounts/${account.get('id')}/posts`} title={hide_statuses_count ? intl.formatMessage(messages.secret) : intl.formatNumber(account.get('statuses_count'))}>
|
||||||
<ShortNumber
|
<ShortNumber
|
||||||
hide={hide_statuses_count}
|
hide={hide_statuses_count}
|
||||||
value={account.get('statuses_count')}
|
value={account.get('statuses_count')}
|
||||||
|
|
|
@ -0,0 +1,366 @@
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import Button from 'mastodon/components/button';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import { autoPlayGif, me, isStaff, show_followed_by, follow_button_to_list_adder } from 'mastodon/initial_state';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import Icon from 'mastodon/components/icon';
|
||||||
|
import IconButton from 'mastodon/components/icon_button';
|
||||||
|
import Avatar from 'mastodon/components/avatar';
|
||||||
|
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||||
|
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
||||||
|
unsubscribe: { id: 'account.unsubscribe', defaultMessage: 'Unsubscribe' },
|
||||||
|
subscribe: { id: 'account.subscribe', defaultMessage: 'Subscribe' },
|
||||||
|
cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Cancel follow request' },
|
||||||
|
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
|
||||||
|
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||||
|
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
|
||||||
|
account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
|
||||||
|
conversations: { id: 'account.conversations', defaultMessage: 'Show conversations with @{name}' },
|
||||||
|
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
|
||||||
|
direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' },
|
||||||
|
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
||||||
|
block: { id: 'account.block', defaultMessage: 'Block @{name}' },
|
||||||
|
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
|
||||||
|
report: { id: 'account.report', defaultMessage: 'Report @{name}' },
|
||||||
|
share: { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' },
|
||||||
|
media: { id: 'account.media', defaultMessage: 'Media' },
|
||||||
|
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
|
||||||
|
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
||||||
|
hideReblogs: { id: 'account.hide_reblogs', defaultMessage: 'Hide boosts from @{name}' },
|
||||||
|
showReblogs: { id: 'account.show_reblogs', defaultMessage: 'Show boosts from @{name}' },
|
||||||
|
enableNotifications: { id: 'account.enable_notifications', defaultMessage: 'Notify me when @{name} posts' },
|
||||||
|
disableNotifications: { id: 'account.disable_notifications', defaultMessage: 'Stop notifying me when @{name} posts' },
|
||||||
|
pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' },
|
||||||
|
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||||
|
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||||
|
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
||||||
|
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||||
|
circles: { id: 'navigation_bar.circles', defaultMessage: 'Circles' },
|
||||||
|
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
||||||
|
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
|
||||||
|
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||||
|
endorse: { id: 'account.endorse', defaultMessage: 'Feature on profile' },
|
||||||
|
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
|
||||||
|
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
|
||||||
|
add_or_remove_from_circle: { id: 'account.add_or_remove_from_circle', defaultMessage: 'Add or Remove from circles' },
|
||||||
|
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
class HeaderCommon extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
account: ImmutablePropTypes.map,
|
||||||
|
onFollow: PropTypes.func.isRequired,
|
||||||
|
onSubscribe: PropTypes.func.isRequired,
|
||||||
|
onAddToList: PropTypes.func.isRequired,
|
||||||
|
onBlock: PropTypes.func.isRequired,
|
||||||
|
onMention: PropTypes.func.isRequired,
|
||||||
|
onDirect: PropTypes.func.isRequired,
|
||||||
|
onConversations: PropTypes.func.isRequired,
|
||||||
|
onReblogToggle: PropTypes.func.isRequired,
|
||||||
|
onNotifyToggle: PropTypes.func.isRequired,
|
||||||
|
onReport: PropTypes.func.isRequired,
|
||||||
|
onMute: PropTypes.func.isRequired,
|
||||||
|
onBlockDomain: PropTypes.func.isRequired,
|
||||||
|
onUnblockDomain: PropTypes.func.isRequired,
|
||||||
|
onEndorseToggle: PropTypes.func.isRequired,
|
||||||
|
onAddToList: PropTypes.func.isRequired,
|
||||||
|
onEditAccountNote: PropTypes.func.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
domain: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
openEditProfile = () => {
|
||||||
|
window.open('/settings/profile', '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
isStatusesPageActive = (match, location) => {
|
||||||
|
if (!match) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !location.pathname.match(/\/(followers|following)\/?$/);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseEnter = ({ currentTarget }) => {
|
||||||
|
if (autoPlayGif) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emojis = currentTarget.querySelectorAll('.custom-emoji');
|
||||||
|
|
||||||
|
for (var i = 0; i < emojis.length; i++) {
|
||||||
|
let emoji = emojis[i];
|
||||||
|
emoji.src = emoji.getAttribute('data-original');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseLeave = ({ currentTarget }) => {
|
||||||
|
if (autoPlayGif) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emojis = currentTarget.querySelectorAll('.custom-emoji');
|
||||||
|
|
||||||
|
for (var i = 0; i < emojis.length; i++) {
|
||||||
|
let emoji = emojis[i];
|
||||||
|
emoji.src = emoji.getAttribute('data-static');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFollow = (e) => {
|
||||||
|
if ((e && e.shiftKey) ^ !follow_button_to_list_adder) {
|
||||||
|
this.props.onFollow(this.props.account);
|
||||||
|
} else {
|
||||||
|
this.props.onAddToList(this.props.account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubscribe = (e) => {
|
||||||
|
if ((e && e.shiftKey) ^ !follow_button_to_list_adder) {
|
||||||
|
this.props.onSubscribe(this.props.account);
|
||||||
|
} else {
|
||||||
|
this.props.onAddToList(this.props.account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setRef = (c) => {
|
||||||
|
this.node = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { account, intl, domain } = this.props;
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const suspended = account.get('suspended');
|
||||||
|
|
||||||
|
let info = [];
|
||||||
|
let actionBtn = '';
|
||||||
|
let bellBtn = '';
|
||||||
|
let lockedIcon = '';
|
||||||
|
let menu = [];
|
||||||
|
|
||||||
|
if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
|
||||||
|
info.push(<span key='followed_by' className='relationship-tag'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>);
|
||||||
|
} else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
|
||||||
|
info.push(<span key='blocked' className='relationship-tag'><FormattedMessage id='account.blocked' defaultMessage='Blocked' /></span>);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (me !== account.get('id') && account.getIn(['relationship', 'muting'])) {
|
||||||
|
info.push(<span key='muted' className='relationship-tag'><FormattedMessage id='account.muted' defaultMessage='Muted' /></span>);
|
||||||
|
} else if (me !== account.get('id') && account.getIn(['relationship', 'domain_blocking'])) {
|
||||||
|
info.push(<span key='domain_blocked' className='relationship-tag'><FormattedMessage id='account.domain_blocked' defaultMessage='Domain blocked' /></span>);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) {
|
||||||
|
bellBtn = <IconButton icon='bell-o' size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (me !== account.get('id')) {
|
||||||
|
if (!account.get('relationship')) { // Wait until the relationship is loaded
|
||||||
|
actionBtn = '';
|
||||||
|
} else if (account.getIn(['relationship', 'requested'])) {
|
||||||
|
actionBtn = <Button className={classNames('logo-button', { 'button--with-bell': bellBtn !== '' })} text={intl.formatMessage(messages.cancel_follow_request)} title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />;
|
||||||
|
} else if (!account.getIn(['relationship', 'blocking'])) {
|
||||||
|
actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']), 'button--with-bell': bellBtn !== '' })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />;
|
||||||
|
} else if (account.getIn(['relationship', 'blocking'])) {
|
||||||
|
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.edit_profile)} onClick={this.openEditProfile} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.get('moved') && !account.getIn(['relationship', 'following'])) {
|
||||||
|
actionBtn = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.get('locked')) {
|
||||||
|
lockedIcon = <Icon id='lock' title={intl.formatMessage(messages.account_locked)} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.get('id') !== me) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.props.onDirect });
|
||||||
|
menu.push(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.push({ text: intl.formatMessage(messages.conversations, { name: account.get('username') }), action: this.props.onConversations });
|
||||||
|
menu.push(null);
|
||||||
|
|
||||||
|
if ('share' in navigator) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
|
||||||
|
menu.push(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.get('id') === me) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.pins), to: '/pinned' });
|
||||||
|
menu.push(null);
|
||||||
|
menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.circles), to: '/circles' });
|
||||||
|
menu.push(null);
|
||||||
|
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
|
||||||
|
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
|
||||||
|
} else {
|
||||||
|
if (account.getIn(['relationship', 'following'])) {
|
||||||
|
if (!account.getIn(['relationship', 'muting'])) {
|
||||||
|
if (account.getIn(['relationship', 'showing_reblogs'])) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
|
||||||
|
} else {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.showReblogs, { name: account.get('username') }), action: this.props.onReblogToggle });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });
|
||||||
|
menu.push(null);
|
||||||
|
}
|
||||||
|
menu.push({ text: intl.formatMessage(messages.add_or_remove_from_list), action: this.props.onAddToList });
|
||||||
|
menu.push(null);
|
||||||
|
|
||||||
|
if (account.getIn(['relationship', 'followed_by'])) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.add_or_remove_from_circle), action: this.props.onAddToCircle });
|
||||||
|
menu.push(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.getIn(['relationship', 'muting'])) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.props.onMute });
|
||||||
|
} else {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.props.onMute });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.getIn(['relationship', 'blocking'])) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.props.onBlock });
|
||||||
|
} else {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock });
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.get('acct') !== account.get('username')) {
|
||||||
|
const domain = account.get('acct').split('@')[1];
|
||||||
|
|
||||||
|
menu.push(null);
|
||||||
|
|
||||||
|
if (account.getIn(['relationship', 'domain_blocking'])) {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.props.onUnblockDomain });
|
||||||
|
} else {
|
||||||
|
menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.props.onBlockDomain });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.get('id') !== me && isStaff) {
|
||||||
|
menu.push(null);
|
||||||
|
menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${account.get('id')}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayNameHtml = { __html: account.get('display_name_html') };
|
||||||
|
const acct = account.get('acct').indexOf('@') === -1 && domain ? `${account.get('acct')}@${domain}` : account.get('acct');
|
||||||
|
|
||||||
|
let badge;
|
||||||
|
|
||||||
|
if (account.get('bot')) {
|
||||||
|
badge = (<div className='account-role bot'><FormattedMessage id='account.badges.bot' defaultMessage='Bot' /></div>);
|
||||||
|
} else if (account.get('group')) {
|
||||||
|
badge = (<div className='account-role group'><FormattedMessage id='account.badges.group' defaultMessage='Group' /></div>);
|
||||||
|
} else {
|
||||||
|
badge = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const following = account.getIn(['relationship', 'following']);
|
||||||
|
const delivery = account.getIn(['relationship', 'delivery_following']);
|
||||||
|
const followed_by = account.getIn(['relationship', 'followed_by']) && show_followed_by;
|
||||||
|
const subscribing = account.getIn(['relationship', 'subscribing'], new Map).size > 0;
|
||||||
|
const subscribing_home = account.getIn(['relationship', 'subscribing', '-1'], new Map).size > 0;
|
||||||
|
const blockd_by = account.getIn(['relationship', 'blocked_by']);
|
||||||
|
let buttons;
|
||||||
|
|
||||||
|
if(me !== account.get('id') && !blockd_by) {
|
||||||
|
let following_buttons, subscribing_buttons;
|
||||||
|
if(!account.get('moved') || subscribing) {
|
||||||
|
subscribing_buttons = (
|
||||||
|
<IconButton
|
||||||
|
icon='rss-square'
|
||||||
|
title={intl.formatMessage(
|
||||||
|
subscribing ? messages.unsubscribe : messages.subscribe
|
||||||
|
)}
|
||||||
|
onClick={this.handleSubscribe}
|
||||||
|
active={subscribing}
|
||||||
|
no_delivery={subscribing && !subscribing_home}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if(!account.get('moved') || following) {
|
||||||
|
following_buttons = (
|
||||||
|
<IconButton
|
||||||
|
icon={following ? 'user-times' : 'user-plus'}
|
||||||
|
title={intl.formatMessage(
|
||||||
|
following ? messages.unfollow : messages.follow
|
||||||
|
)}
|
||||||
|
onClick={this.handleFollow}
|
||||||
|
active={following}
|
||||||
|
passive={followed_by}
|
||||||
|
no_delivery={following && !delivery}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
buttons = <Fragment>{subscribing_buttons}{following_buttons}</Fragment>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('account__header', 'advanced', { inactive: !!account.get('moved') })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||||
|
<div className='account__header__image'>
|
||||||
|
<div className='account__header__info'>
|
||||||
|
{!suspended && info}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img src={autoPlayGif ? account.get('header') : account.get('header_static')} alt='' className='parallax' />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='account__header__bar'>
|
||||||
|
<div className='account__header__tabs'>
|
||||||
|
<a className='avatar' href={account.get('url')} rel='noopener noreferrer' target='_blank'>
|
||||||
|
<Avatar account={account} size={90} />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div className='spacer' />
|
||||||
|
|
||||||
|
{!suspended && (
|
||||||
|
<div className='account__header__tabs__buttons'>
|
||||||
|
{actionBtn}
|
||||||
|
{bellBtn}
|
||||||
|
|
||||||
|
<DropdownMenuContainer items={menu} icon='ellipsis-v' size={24} direction='right' />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='account__header__tabs__name'>
|
||||||
|
<h1>
|
||||||
|
<span dangerouslySetInnerHTML={displayNameHtml} /> {badge}
|
||||||
|
<small>@{acct} {lockedIcon}</small>
|
||||||
|
</h1>
|
||||||
|
<div className='account__header__tabs__name__relationship account__relationship'>
|
||||||
|
{buttons}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { defineMessages, injectIntl, FormattedMessage, FormattedDate } from 'react-intl';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import { me } from 'mastodon/initial_state';
|
||||||
|
import Icon from 'mastodon/components/icon';
|
||||||
|
import AccountNoteContainer from '../containers/account_note_container';
|
||||||
|
import age from 's-age';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const dateFormatOptions = {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
hour12: false,
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
class HeaderExtra extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
account: ImmutablePropTypes.map,
|
||||||
|
identity_proofs: ImmutablePropTypes.list,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
isStatusesPageActive = (match, location) => {
|
||||||
|
if (!match) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !location.pathname.match(/\/(followers|following)\/?$/);
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { account, intl, identity_proofs } = this.props;
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const suspended = account.get('suspended');
|
||||||
|
|
||||||
|
const content = { __html: account.get('note_emojified') };
|
||||||
|
const fields = account.get('fields');
|
||||||
|
|
||||||
|
const location = account.getIn(['other_settings', 'location']);
|
||||||
|
const birthday = account.getIn(['other_settings', 'birthday']);
|
||||||
|
const joined = account.get('created_at');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('account__header', 'advanced', { inactive: !!account.get('moved') })}>
|
||||||
|
<div className='account__header__extra'>
|
||||||
|
<div className='account__header__bio'>
|
||||||
|
{(fields.size > 0 || identity_proofs.size > 0) && (
|
||||||
|
<div className='account__header__fields'>
|
||||||
|
{identity_proofs.map((proof, i) => (
|
||||||
|
<dl key={i}>
|
||||||
|
<dt dangerouslySetInnerHTML={{ __html: proof.get('provider') }} />
|
||||||
|
|
||||||
|
<dd className='verified'>
|
||||||
|
<a href={proof.get('proof_url')} target='_blank' rel='noopener noreferrer'><span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
|
||||||
|
<Icon id='check' className='verified__mark' />
|
||||||
|
</span></a>
|
||||||
|
<a href={proof.get('profile_url')} target='_blank' rel='noopener noreferrer'><span dangerouslySetInnerHTML={{ __html: ' '+proof.get('provider_username') }} /></a>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
))}
|
||||||
|
{fields.map((pair, i) => (
|
||||||
|
<dl key={i}>
|
||||||
|
<dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} className='translate' />
|
||||||
|
|
||||||
|
<dd className={`${pair.get('verified_at') ? 'verified' : ''} translate`} title={pair.get('value_plain')}>
|
||||||
|
{pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><Icon id='check' className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{account.get('id') !== me && !suspended && <AccountNoteContainer account={account} />}
|
||||||
|
|
||||||
|
{account.get('note').length > 0 && account.get('note') !== '<p></p>' && <div className='account__header__content translate' dangerouslySetInnerHTML={content} />}
|
||||||
|
|
||||||
|
<div className='account__header__personal--wrapper'>
|
||||||
|
<table className='account__header__personal'>
|
||||||
|
<tbody>
|
||||||
|
{location && <tr>
|
||||||
|
<th><Icon id='map-marker' fixedWidth aria-hidden='true' /> <FormattedMessage id='account.location' defaultMessage='Location' /></th>
|
||||||
|
<td>{location}</td>
|
||||||
|
</tr>}
|
||||||
|
{birthday && <tr>
|
||||||
|
<th><Icon id='birthday-cake' fixedWidth aria-hidden='true' /> <FormattedMessage id='account.birthday' defaultMessage='Birthday' /></th>
|
||||||
|
<td><FormattedDate value={birthday} hour12={false} year='numeric' month='short' day='2-digit' />(<FormattedMessage id='account.age' defaultMessage='{age} years old}' values={{ age: age(birthday) }} />)</td>
|
||||||
|
</tr>}
|
||||||
|
<tr>
|
||||||
|
<th><Icon id='calendar' fixedWidth aria-hidden='true' /> <FormattedMessage id='account.joined' defaultMessage='Joined' /></th>
|
||||||
|
<td><FormattedDate value={joined} hour12={false} year='numeric' month='short' day='2-digit' /></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import { me } from 'mastodon/initial_state';
|
||||||
|
import { counterRenderer } from 'mastodon/components/common_counter';
|
||||||
|
import ShortNumber from 'mastodon/components/short_number';
|
||||||
|
import { NavLink } from 'react-router-dom';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
secret: { id: 'account.secret', defaultMessage: 'Secret' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
class HeaderExtraLinks extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
account: ImmutablePropTypes.map,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
isStatusesPageActive = (match, location) => {
|
||||||
|
if (!match) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !location.pathname.match(/\/(followers|following|subscribing)\/?$/);
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { account, intl } = this.props;
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const suspended = account.get('suspended');
|
||||||
|
|
||||||
|
const hide_statuses_count = account.getIn(['other_settings', 'hide_statuses_count'], false);
|
||||||
|
const hide_following_count = account.getIn(['other_settings', 'hide_following_count'], false);
|
||||||
|
const hide_followers_count = account.getIn(['other_settings', 'hide_followers_count'], false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('account__header', 'advanced', { inactive: !!account.get('moved') })}>
|
||||||
|
<div className='account__header__extra'>
|
||||||
|
{!suspended && (
|
||||||
|
<div className='account__header__extra__links'>
|
||||||
|
<NavLink isActive={this.isStatusesPageActive} activeClassName='active' to={`/accounts/${account.get('id')}/posts`} title={hide_statuses_count ? intl.formatMessage(messages.secret) : intl.formatNumber(account.get('statuses_count'))}>
|
||||||
|
<ShortNumber
|
||||||
|
hide={hide_statuses_count}
|
||||||
|
value={account.get('statuses_count')}
|
||||||
|
renderer={counterRenderer('statuses')}
|
||||||
|
/>
|
||||||
|
</NavLink>
|
||||||
|
|
||||||
|
<NavLink exact activeClassName='active' to={`/accounts/${account.get('id')}/following`} title={hide_following_count ? intl.formatMessage(messages.secret) : intl.formatNumber(account.get('following_count'))}>
|
||||||
|
<ShortNumber
|
||||||
|
hide={hide_following_count}
|
||||||
|
value={account.get('following_count')}
|
||||||
|
renderer={counterRenderer('following')}
|
||||||
|
/>
|
||||||
|
</NavLink>
|
||||||
|
|
||||||
|
<NavLink exact activeClassName='active' to={`/accounts/${account.get('id')}/followers`} title={hide_followers_count ? intl.formatMessage(messages.secret) : intl.formatNumber(account.get('followers_count'))}>
|
||||||
|
<ShortNumber
|
||||||
|
hide={hide_followers_count}
|
||||||
|
value={account.get('followers_count')}
|
||||||
|
renderer={counterRenderer('followers')}
|
||||||
|
/>
|
||||||
|
</NavLink>
|
||||||
|
|
||||||
|
{ (me === account.get('id')) && (
|
||||||
|
<NavLink exact activeClassName='active' to={`/accounts/${account.get('id')}/subscribing`} title={intl.formatNumber(account.get('subscribing_count'))}>
|
||||||
|
<ShortNumber
|
||||||
|
value={account.get('subscribing_count')}
|
||||||
|
renderer={counterRenderer('subscribers')}
|
||||||
|
/>
|
||||||
|
</NavLink>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,20 +6,26 @@ import { fetchAccount } from '../../actions/accounts';
|
||||||
import { expandAccountCoversations } from '../../actions/timelines';
|
import { expandAccountCoversations } from '../../actions/timelines';
|
||||||
import StatusList from '../../components/status_list';
|
import StatusList from '../../components/status_list';
|
||||||
import LoadingIndicator from '../../components/loading_indicator';
|
import LoadingIndicator from '../../components/loading_indicator';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../../components/column';
|
||||||
|
import ColumnHeader from '../../components/column_header';
|
||||||
|
import ColumnSettingsContainer from '../account_timeline/containers/column_settings_container';
|
||||||
import HeaderContainer from '../account_timeline/containers/header_container';
|
import HeaderContainer from '../account_timeline/containers/header_container';
|
||||||
import ColumnBackButton from '../../components/column_back_button';
|
import ColumnBackButton from '../../components/column_back_button';
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
|
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
|
||||||
import MissingIndicator from 'mastodon/components/missing_indicator';
|
import MissingIndicator from 'mastodon/components/missing_indicator';
|
||||||
import TimelineHint from 'mastodon/components/timeline_hint';
|
import TimelineHint from 'mastodon/components/timeline_hint';
|
||||||
|
import { new_features_policy } from 'mastodon/initial_state';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'column.account', defaultMessage: 'Account' },
|
||||||
|
});
|
||||||
|
|
||||||
const emptyList = ImmutableList();
|
const emptyList = ImmutableList();
|
||||||
|
|
||||||
const mapStateToProps = (state, { params: { accountId } }) => {
|
const mapStateToProps = (state, { params: { accountId } }) => ({
|
||||||
return {
|
|
||||||
remote: !!(state.getIn(['accounts', accountId, 'acct']) !== state.getIn(['accounts', accountId, 'username'])),
|
remote: !!(state.getIn(['accounts', accountId, 'acct']) !== state.getIn(['accounts', accountId, 'username'])),
|
||||||
remoteUrl: state.getIn(['accounts', accountId, 'url']),
|
remoteUrl: state.getIn(['accounts', accountId, 'url']),
|
||||||
isAccount: !!state.getIn(['accounts', accountId]),
|
isAccount: !!state.getIn(['accounts', accountId]),
|
||||||
|
@ -28,8 +34,9 @@ const mapStateToProps = (state, { params: { accountId } }) => {
|
||||||
hasMore: state.getIn(['timelines', `account:${accountId}:conversations`, 'hasMore']),
|
hasMore: state.getIn(['timelines', `account:${accountId}:conversations`, 'hasMore']),
|
||||||
suspended: state.getIn(['accounts', accountId, 'suspended'], false),
|
suspended: state.getIn(['accounts', accountId, 'suspended'], false),
|
||||||
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
|
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
|
||||||
};
|
advancedMode: state.getIn(['settings', 'account', 'other', 'advancedMode'], new_features_policy === 'conservative' ? false : true),
|
||||||
};
|
hideRelation: state.getIn(['settings', 'account', 'other', 'hideRelation'], false),
|
||||||
|
});
|
||||||
|
|
||||||
const RemoteHint = ({ url }) => (
|
const RemoteHint = ({ url }) => (
|
||||||
<TimelineHint url={url} resource={<FormattedMessage id='timeline_hint.resources.statuses' defaultMessage='Older toots' />} />
|
<TimelineHint url={url} resource={<FormattedMessage id='timeline_hint.resources.statuses' defaultMessage='Older toots' />} />
|
||||||
|
@ -40,6 +47,7 @@ RemoteHint.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default @connect(mapStateToProps)
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
class AccountConversations extends ImmutablePureComponent {
|
class AccountConversations extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -49,11 +57,11 @@ class AccountConversations extends ImmutablePureComponent {
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
withReplies: PropTypes.bool,
|
withReplies: PropTypes.bool,
|
||||||
|
advancedMode: PropTypes.bool,
|
||||||
|
hideRelation: PropTypes.bool,
|
||||||
blockedBy: PropTypes.bool,
|
blockedBy: PropTypes.bool,
|
||||||
isAccount: PropTypes.bool,
|
isAccount: PropTypes.bool,
|
||||||
suspended: PropTypes.bool,
|
suspended: PropTypes.bool,
|
||||||
remote: PropTypes.bool,
|
|
||||||
remoteUrl: PropTypes.string,
|
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -81,8 +89,16 @@ class AccountConversations extends ImmutablePureComponent {
|
||||||
this.props.dispatch(expandAccountCoversations(this.props.params.accountId, { maxId }));
|
this.props.dispatch(expandAccountCoversations(this.props.params.accountId, { maxId }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleHeaderClick = () => {
|
||||||
|
this.column.scrollTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
setRef = c => {
|
||||||
|
this.column = c;
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { statusIds, isLoading, hasMore, blockedBy, suspended, isAccount, multiColumn, remote, remoteUrl } = this.props;
|
const { statusIds, isLoading, hasMore, blockedBy, suspended, isAccount, multiColumn, hideRelation, intl } = this.props;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
return (
|
return (
|
||||||
|
@ -107,22 +123,26 @@ class AccountConversations extends ImmutablePureComponent {
|
||||||
emptyMessage = <FormattedMessage id='empty_column.account_suspended' defaultMessage='Account suspended' />;
|
emptyMessage = <FormattedMessage id='empty_column.account_suspended' defaultMessage='Account suspended' />;
|
||||||
} else if (blockedBy) {
|
} else if (blockedBy) {
|
||||||
emptyMessage = <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />;
|
emptyMessage = <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />;
|
||||||
} else if (remote && statusIds.isEmpty()) {
|
|
||||||
emptyMessage = <RemoteHint url={remoteUrl} />;
|
|
||||||
} else {
|
} else {
|
||||||
emptyMessage = <FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />;
|
emptyMessage = <FormattedMessage id='empty_column.conversation_unavailable' defaultMessage='No conversation with this user yet' />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const remoteMessage = remote ? <RemoteHint url={remoteUrl} /> : null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||||
<ColumnBackButton multiColumn={multiColumn} />
|
<ColumnHeader
|
||||||
|
icon='user'
|
||||||
|
active={false}
|
||||||
|
title={intl.formatMessage(messages.title)}
|
||||||
|
onClick={this.handleHeaderClick}
|
||||||
|
pinned={false}
|
||||||
|
multiColumn={multiColumn}
|
||||||
|
>
|
||||||
|
<ColumnSettingsContainer />
|
||||||
|
</ColumnHeader>
|
||||||
|
|
||||||
<StatusList
|
<StatusList
|
||||||
prepend={<HeaderContainer accountId={this.props.params.accountId} />}
|
prepend={<HeaderContainer accountId={this.props.params.accountId} hideProfile hideRelation={hideRelation} hideFeaturedTags />}
|
||||||
alwaysPrepend
|
alwaysPrepend
|
||||||
append={remoteMessage}
|
|
||||||
scrollKey='account_conversations'
|
scrollKey='account_conversations'
|
||||||
statusIds={(suspended || blockedBy) ? emptyList : statusIds}
|
statusIds={(suspended || blockedBy) ? emptyList : statusIds}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
|
|
@ -5,8 +5,9 @@ import PropTypes from 'prop-types';
|
||||||
import { fetchAccount } from 'mastodon/actions/accounts';
|
import { fetchAccount } from 'mastodon/actions/accounts';
|
||||||
import { expandAccountMediaTimeline } from '../../actions/timelines';
|
import { expandAccountMediaTimeline } from '../../actions/timelines';
|
||||||
import LoadingIndicator from 'mastodon/components/loading_indicator';
|
import LoadingIndicator from 'mastodon/components/loading_indicator';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../../components/column';
|
||||||
import ColumnBackButton from 'mastodon/components/column_back_button';
|
import ColumnHeader from '../../components/column_header';
|
||||||
|
import ColumnSettingsContainer from '../account_timeline/containers/column_settings_container';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { getAccountGallery } from 'mastodon/selectors';
|
import { getAccountGallery } from 'mastodon/selectors';
|
||||||
import MediaItem from './components/media_item';
|
import MediaItem from './components/media_item';
|
||||||
|
@ -15,7 +16,12 @@ import ScrollContainer from 'mastodon/containers/scroll_container';
|
||||||
import LoadMore from 'mastodon/components/load_more';
|
import LoadMore from 'mastodon/components/load_more';
|
||||||
import MissingIndicator from 'mastodon/components/missing_indicator';
|
import MissingIndicator from 'mastodon/components/missing_indicator';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { new_features_policy } from 'mastodon/initial_state';
|
||||||
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'column.account', defaultMessage: 'Account' },
|
||||||
|
});
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => ({
|
||||||
isAccount: !!state.getIn(['accounts', props.params.accountId]),
|
isAccount: !!state.getIn(['accounts', props.params.accountId]),
|
||||||
|
@ -24,6 +30,8 @@ const mapStateToProps = (state, props) => ({
|
||||||
hasMore: state.getIn(['timelines', `account:${props.params.accountId}:media`, 'hasMore']),
|
hasMore: state.getIn(['timelines', `account:${props.params.accountId}:media`, 'hasMore']),
|
||||||
suspended: state.getIn(['accounts', props.params.accountId, 'suspended'], false),
|
suspended: state.getIn(['accounts', props.params.accountId, 'suspended'], false),
|
||||||
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
||||||
|
advancedMode: state.getIn(['settings', 'account', 'other', 'advancedMode'], new_features_policy === 'conservative' ? false : true),
|
||||||
|
hideRelation: state.getIn(['settings', 'account', 'other', 'hideRelation'], false),
|
||||||
});
|
});
|
||||||
|
|
||||||
class LoadMoreMedia extends ImmutablePureComponent {
|
class LoadMoreMedia extends ImmutablePureComponent {
|
||||||
|
@ -49,6 +57,7 @@ class LoadMoreMedia extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default @connect(mapStateToProps)
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
class AccountGallery extends ImmutablePureComponent {
|
class AccountGallery extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -58,6 +67,8 @@ class AccountGallery extends ImmutablePureComponent {
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isAccount: PropTypes.bool,
|
isAccount: PropTypes.bool,
|
||||||
|
advancedMode: PropTypes.bool,
|
||||||
|
hideRelation: PropTypes.bool,
|
||||||
blockedBy: PropTypes.bool,
|
blockedBy: PropTypes.bool,
|
||||||
suspended: PropTypes.bool,
|
suspended: PropTypes.bool,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
|
@ -125,8 +136,16 @@ class AccountGallery extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleHeaderClick = () => {
|
||||||
|
this.column.scrollTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
setRef = c => {
|
||||||
|
this.column = c;
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { attachments, isLoading, hasMore, isAccount, multiColumn, blockedBy, suspended } = this.props;
|
const { attachments, isLoading, hasMore, isAccount, multiColumn, blockedBy, suspended, hideRelation, intl } = this.props;
|
||||||
const { width } = this.state;
|
const { width } = this.state;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
|
@ -160,12 +179,21 @@ class AccountGallery extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||||
<ColumnBackButton multiColumn={multiColumn} />
|
<ColumnHeader
|
||||||
|
icon='user'
|
||||||
|
active={false}
|
||||||
|
title={intl.formatMessage(messages.title)}
|
||||||
|
onClick={this.handleHeaderClick}
|
||||||
|
pinned={false}
|
||||||
|
multiColumn={multiColumn}
|
||||||
|
>
|
||||||
|
<ColumnSettingsContainer />
|
||||||
|
</ColumnHeader>
|
||||||
|
|
||||||
<ScrollContainer scrollKey='account_gallery'>
|
<ScrollContainer scrollKey='account_gallery'>
|
||||||
<div className='scrollable scrollable--flex' onScroll={this.handleScroll}>
|
<div className='scrollable scrollable--flex' onScroll={this.handleScroll}>
|
||||||
<HeaderContainer accountId={this.props.params.accountId} />
|
<HeaderContainer accountId={this.props.params.accountId} hideProfile hideRelation={hideRelation} hideFeaturedTags />
|
||||||
|
|
||||||
{(suspended || blockedBy) ? (
|
{(suspended || blockedBy) ? (
|
||||||
<div className='empty-column-indicator'>
|
<div className='empty-column-indicator'>
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import SettingToggle from '../../notifications/components/setting_toggle';
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
class ColumnSettings extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
settings: ImmutablePropTypes.map.isRequired,
|
||||||
|
advancedMode: PropTypes.bool.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { settings, advancedMode, onChange } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='column-settings__row'>
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'advancedMode']} onChange={onChange} label={<FormattedMessage id='account.column_settings.advanced_mode' defaultMessage='Advanced mode' />} />
|
||||||
|
{advancedMode && (
|
||||||
|
<Fragment>
|
||||||
|
<span className='column-settings__section'><FormattedMessage id='account.column_settings.advanced_settings' defaultMessage='Advanced settings' /></span>
|
||||||
|
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'openPostsFirst']} onChange={onChange} label={<FormattedMessage id='account.column_settings.open_posts_first' defaultMessage='Open posts first' />} />
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'withoutReblogs']} onChange={onChange} label={<FormattedMessage id='account.column_settings.without_reblogs' defaultMessage='Without boosts' />} />
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'showPostsInAbout']} onChange={onChange} label={<FormattedMessage id='account.column_settings.show_posts_in_about' defaultMessage='Show posts in about' />} />
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'hideFeaturedTags']} onChange={onChange} label={<FormattedMessage id='account.column_settings.hide_featured_tags' defaultMessage='Hide featuread tags selection' />} />
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'hideRelation']} onChange={onChange} label={<FormattedMessage id='account.column_settings.hide_relation' defaultMessage='Hide post and follow counters (except about)' />} />
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,12 +1,17 @@
|
||||||
import React from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import InnerHeader from '../../account/components/header';
|
import InnerHeader from '../../account/components/header';
|
||||||
|
import InnerHeaderCommon from '../../account/components/header_common';
|
||||||
|
import InnerHeaderExtra from '../../account/components/header_extra';
|
||||||
|
import InnerHeaderExtraLinks from '../../account/components/header_extra_links';
|
||||||
|
import FeaturedTags from '../../account/components/featured_tags';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { follow_button_to_list_adder } from 'mastodon/initial_state';
|
import { follow_button_to_list_adder } from 'mastodon/initial_state';
|
||||||
import MovedNote from './moved_note';
|
import MovedNote from './moved_note';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
|
import Icon from 'mastodon/components/icon';
|
||||||
|
|
||||||
export default class Header extends ImmutablePureComponent {
|
export default class Header extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
@ -28,10 +33,18 @@ export default class Header extends ImmutablePureComponent {
|
||||||
onEndorseToggle: PropTypes.func.isRequired,
|
onEndorseToggle: PropTypes.func.isRequired,
|
||||||
onAddToList: PropTypes.func.isRequired,
|
onAddToList: PropTypes.func.isRequired,
|
||||||
onAddToCircle: PropTypes.func.isRequired,
|
onAddToCircle: PropTypes.func.isRequired,
|
||||||
hideTabs: PropTypes.bool,
|
hideRelation: PropTypes.bool,
|
||||||
|
hideProfile: PropTypes.bool,
|
||||||
|
advancedMode: PropTypes.bool,
|
||||||
|
tagged: PropTypes.string,
|
||||||
|
hideFeaturedTags: PropTypes.bool,
|
||||||
domain: PropTypes.string.isRequired,
|
domain: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
hideProfile: false,
|
||||||
|
};
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
router: PropTypes.object,
|
router: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
@ -117,7 +130,7 @@ export default class Header extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, hideTabs, identity_proofs } = this.props;
|
const { account, hideRelation, identity_proofs, hideProfile, advancedMode, tagged, hideFeaturedTags } = this.props;
|
||||||
|
|
||||||
if (account === null) {
|
if (account === null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -127,6 +140,43 @@ export default class Header extends ImmutablePureComponent {
|
||||||
<div className='account-timeline__header'>
|
<div className='account-timeline__header'>
|
||||||
{account.get('moved') && <MovedNote from={account} to={account.get('moved')} />}
|
{account.get('moved') && <MovedNote from={account} to={account.get('moved')} />}
|
||||||
|
|
||||||
|
{advancedMode ? (
|
||||||
|
<Fragment>
|
||||||
|
<InnerHeaderCommon
|
||||||
|
account={account}
|
||||||
|
onFollow={this.handleFollow}
|
||||||
|
onSubscribe={this.handleSubscribe}
|
||||||
|
onBlock={this.handleBlock}
|
||||||
|
onMention={this.handleMention}
|
||||||
|
onDirect={this.handleDirect}
|
||||||
|
onConversations={this.handleConversations}
|
||||||
|
onReblogToggle={this.handleReblogToggle}
|
||||||
|
onNotifyToggle={this.handleNotifyToggle}
|
||||||
|
onReport={this.handleReport}
|
||||||
|
onMute={this.handleMute}
|
||||||
|
onBlockDomain={this.handleBlockDomain}
|
||||||
|
onUnblockDomain={this.handleUnblockDomain}
|
||||||
|
onEndorseToggle={this.handleEndorseToggle}
|
||||||
|
onAddToList={this.handleAddToList}
|
||||||
|
onAddToCircle={this.handleAddToCircle}
|
||||||
|
onEditAccountNote={this.handleEditAccountNote}
|
||||||
|
domain={this.props.domain}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='account__section-headline with-short-label'>
|
||||||
|
<NavLink exact to={`/accounts/${account.get('id')}/posts`}><Icon id='home' fixedWidth /><span className='account__section-headline__short-label'><FormattedMessage id='account.short.posts' defaultMessage='Posts' children={msg=> <>{msg}</>} /></span></NavLink>
|
||||||
|
<NavLink exact to={`/accounts/${account.get('id')}/with_replies`}><Icon id='reply-all' fixedWidth /><span className='account__section-headline__short-label'><FormattedMessage id='account.short.with_replies' defaultMessage='Posts & Replies' children={msg=> <>{msg}</>} /></span></NavLink>
|
||||||
|
<NavLink exact to={`/accounts/${account.get('id')}/media`}><Icon id='picture-o' fixedWidth /><span className='account__section-headline__short-label'><FormattedMessage id='account.short.media' defaultMessage='Media' children={msg=> <>{msg}</>} /></span></NavLink>
|
||||||
|
<NavLink exact to={`/accounts/${account.get('id')}/conversations`}><Icon id='at' fixedWidth /><span className='account__section-headline__short-label'><FormattedMessage id='account.short.conversations' defaultMessage='Conversations' children={msg=> <>{msg}</>} /></span></NavLink>
|
||||||
|
<NavLink exact to={`/accounts/${account.get('id')}/about`}><Icon id='address-card-o' fixedWidth /><span className='account__section-headline__short-label'><FormattedMessage id='account.short.about' defaultMessage='About' children={msg=> <>{msg}</>} /></span></NavLink>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{hideProfile && !hideFeaturedTags && <FeaturedTags account={account} tagged={tagged} />}
|
||||||
|
{!hideRelation && <InnerHeaderExtraLinks account={account} />}
|
||||||
|
{!hideProfile && <InnerHeaderExtra account={account} identity_proofs={identity_proofs} />}
|
||||||
|
</Fragment>
|
||||||
|
) : (
|
||||||
|
<Fragment>
|
||||||
<InnerHeader
|
<InnerHeader
|
||||||
account={account}
|
account={account}
|
||||||
identity_proofs={identity_proofs}
|
identity_proofs={identity_proofs}
|
||||||
|
@ -147,14 +197,15 @@ export default class Header extends ImmutablePureComponent {
|
||||||
onAddToCircle={this.handleAddToCircle}
|
onAddToCircle={this.handleAddToCircle}
|
||||||
onEditAccountNote={this.handleEditAccountNote}
|
onEditAccountNote={this.handleEditAccountNote}
|
||||||
domain={this.props.domain}
|
domain={this.props.domain}
|
||||||
|
hideProfile={hideProfile}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!hideTabs && (
|
|
||||||
<div className='account__section-headline'>
|
<div className='account__section-headline'>
|
||||||
<NavLink exact to={`/accounts/${account.get('id')}`}><FormattedMessage id='account.posts' defaultMessage='Toots' /></NavLink>
|
<NavLink exact to={`/accounts/${account.get('id')}`}><FormattedMessage id='account.posts' defaultMessage='Toots' /></NavLink>
|
||||||
<NavLink exact to={`/accounts/${account.get('id')}/with_replies`}><FormattedMessage id='account.posts_with_replies' defaultMessage='Toots and replies' /></NavLink>
|
<NavLink exact to={`/accounts/${account.get('id')}/with_replies`}><FormattedMessage id='account.posts_with_replies' defaultMessage='Toots and replies' /></NavLink>
|
||||||
<NavLink exact to={`/accounts/${account.get('id')}/media`}><FormattedMessage id='account.media' defaultMessage='Media' /></NavLink>
|
<NavLink exact to={`/accounts/${account.get('id')}/media`}><FormattedMessage id='account.media' defaultMessage='Media' /></NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ColumnSettings from '../components/column_settings';
|
||||||
|
import { changeSetting } from '../../../actions/settings';
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
settings: state.getIn(['settings', 'account']),
|
||||||
|
advancedMode: state.getIn(['settings', 'account', 'other', 'advancedMode'], false),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => {
|
||||||
|
return {
|
||||||
|
onChange (key, checked) {
|
||||||
|
dispatch(changeSetting(['account', ...key], checked));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
|
|
@ -34,10 +34,12 @@ const messages = defineMessages({
|
||||||
const makeMapStateToProps = () => {
|
const makeMapStateToProps = () => {
|
||||||
const getAccount = makeGetAccount();
|
const getAccount = makeGetAccount();
|
||||||
|
|
||||||
const mapStateToProps = (state, { accountId }) => ({
|
const mapStateToProps = (state, { accountId, hideTabs, hideProfile }) => ({
|
||||||
account: getAccount(state, accountId),
|
account: getAccount(state, accountId),
|
||||||
domain: state.getIn(['meta', 'domain']),
|
domain: state.getIn(['meta', 'domain']),
|
||||||
identity_proofs: state.getIn(['identity_proofs', accountId], ImmutableList()),
|
identity_proofs: state.getIn(['identity_proofs', accountId], ImmutableList()),
|
||||||
|
hideProfile: hideTabs || hideProfile,
|
||||||
|
advancedMode: state.getIn(['settings', 'account', 'other', 'advancedMode'], false),
|
||||||
});
|
});
|
||||||
|
|
||||||
return mapStateToProps;
|
return mapStateToProps;
|
||||||
|
|
|
@ -6,33 +6,54 @@ import { fetchAccount } from '../../actions/accounts';
|
||||||
import { expandAccountFeaturedTimeline, expandAccountTimeline } from '../../actions/timelines';
|
import { expandAccountFeaturedTimeline, expandAccountTimeline } from '../../actions/timelines';
|
||||||
import StatusList from '../../components/status_list';
|
import StatusList from '../../components/status_list';
|
||||||
import LoadingIndicator from '../../components/loading_indicator';
|
import LoadingIndicator from '../../components/loading_indicator';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../../components/column';
|
||||||
|
import ColumnHeader from '../../components/column_header';
|
||||||
|
import ColumnSettingsContainer from './containers/column_settings_container';
|
||||||
import HeaderContainer from './containers/header_container';
|
import HeaderContainer from './containers/header_container';
|
||||||
import ColumnBackButton from '../../components/column_back_button';
|
import ColumnBackButton from '../../components/column_back_button';
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
|
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
|
||||||
import MissingIndicator from 'mastodon/components/missing_indicator';
|
import MissingIndicator from 'mastodon/components/missing_indicator';
|
||||||
import TimelineHint from 'mastodon/components/timeline_hint';
|
import TimelineHint from 'mastodon/components/timeline_hint';
|
||||||
import { me } from 'mastodon/initial_state';
|
import { me, new_features_policy } from 'mastodon/initial_state';
|
||||||
import { connectTimeline, disconnectTimeline } from 'mastodon/actions/timelines';
|
import { connectTimeline, disconnectTimeline } from 'mastodon/actions/timelines';
|
||||||
|
import { fetchFeaturedTags } from '../../actions/featured_tags';
|
||||||
|
|
||||||
const emptyList = ImmutableList();
|
const emptyList = ImmutableList();
|
||||||
|
|
||||||
const mapStateToProps = (state, { params: { accountId }, withReplies = false }) => {
|
const messages = defineMessages({
|
||||||
const path = withReplies ? `${accountId}:with_replies` : accountId;
|
title: { id: 'column.account', defaultMessage: 'Account' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapStateToProps = (state, { params: { accountId, tagged }, about, withReplies, posts }) => {
|
||||||
|
posts = tagged ? false : posts;
|
||||||
|
withReplies = tagged ? true : withReplies;
|
||||||
|
const advancedMode = state.getIn(['settings', 'account', 'other', 'advancedMode'], new_features_policy === 'conservative' ? false : true);
|
||||||
|
const hideFeaturedTags = state.getIn(['settings', 'account', 'other', 'hideFeaturedTags'], false);
|
||||||
|
const withoutReblogs = advancedMode && state.getIn(['settings', 'account', 'other', 'withoutReblogs'], false);
|
||||||
|
const showPostsInAbout = state.getIn(['settings', 'account', 'other', 'showPostsInAbout'], true);
|
||||||
|
const hideRelation = state.getIn(['settings', 'account', 'other', 'hideRelation'], false);
|
||||||
|
const path = `${accountId}${withReplies ? ':with_replies' : ''}${withoutReblogs ? ':without_reblogs' : ''}${tagged ? `:${tagged}` : ''}`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
remote: !!(state.getIn(['accounts', accountId, 'acct']) !== state.getIn(['accounts', accountId, 'username'])),
|
remote: !!(state.getIn(['accounts', accountId, 'acct']) !== state.getIn(['accounts', accountId, 'username'])),
|
||||||
remoteUrl: state.getIn(['accounts', accountId, 'url']),
|
remoteUrl: state.getIn(['accounts', accountId, 'url']),
|
||||||
isAccount: !!state.getIn(['accounts', accountId]),
|
isAccount: !!state.getIn(['accounts', accountId]),
|
||||||
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
|
statusIds: advancedMode && about && !showPostsInAbout ? emptyList : state.getIn(['timelines', `account:${path}`, 'items'], emptyList),
|
||||||
featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], emptyList),
|
featuredStatusIds: (withReplies || posts) ? emptyList : state.getIn(['timelines', `account:${accountId}:pinned${tagged ? `:${tagged}` : ''}`, 'items'], emptyList),
|
||||||
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
|
isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
|
||||||
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
|
hasMore: state.getIn(['timelines', `account:${path}`, 'hasMore']),
|
||||||
suspended: state.getIn(['accounts', accountId, 'suspended'], false),
|
suspended: state.getIn(['accounts', accountId, 'suspended'], false),
|
||||||
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
|
blockedBy: state.getIn(['relationships', accountId, 'blocked_by'], false),
|
||||||
|
advancedMode,
|
||||||
|
hideFeaturedTags,
|
||||||
|
posts,
|
||||||
|
withReplies,
|
||||||
|
withoutReblogs,
|
||||||
|
showPostsInAbout,
|
||||||
|
hideRelation,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,16 +66,24 @@ RemoteHint.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default @connect(mapStateToProps)
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
class AccountTimeline extends ImmutablePureComponent {
|
class AccountTimeline extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
statusIds: ImmutablePropTypes.list,
|
statusIds: ImmutablePropTypes.list,
|
||||||
featuredStatusIds: ImmutablePropTypes.list,
|
featuredStatusIds: ImmutablePropTypes.list,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
|
about: PropTypes.bool,
|
||||||
withReplies: PropTypes.bool,
|
withReplies: PropTypes.bool,
|
||||||
|
withoutReblogs: PropTypes.bool,
|
||||||
|
posts: PropTypes.bool,
|
||||||
|
advancedMode: PropTypes.bool,
|
||||||
|
hideFeaturedTags: PropTypes.bool,
|
||||||
|
hideRelation: PropTypes.bool,
|
||||||
blockedBy: PropTypes.bool,
|
blockedBy: PropTypes.bool,
|
||||||
isAccount: PropTypes.bool,
|
isAccount: PropTypes.bool,
|
||||||
suspended: PropTypes.bool,
|
suspended: PropTypes.bool,
|
||||||
|
@ -63,17 +92,29 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
about: false,
|
||||||
|
withReplies: false,
|
||||||
|
posts: false,
|
||||||
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
const { params: { accountId }, withReplies, dispatch } = this.props;
|
const { params: { accountId, tagged }, about, withReplies, posts, advancedMode, hideFeaturedTags, withoutReblogs, showPostsInAbout, dispatch } = this.props;
|
||||||
|
|
||||||
dispatch(fetchAccount(accountId));
|
dispatch(fetchAccount(accountId));
|
||||||
dispatch(fetchAccountIdentityProofs(accountId));
|
dispatch(fetchAccountIdentityProofs(accountId));
|
||||||
|
|
||||||
if (!withReplies) {
|
if (!withReplies && !posts) {
|
||||||
dispatch(expandAccountFeaturedTimeline(accountId));
|
dispatch(expandAccountFeaturedTimeline(accountId, { tagged }));
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(expandAccountTimeline(accountId, { withReplies }));
|
if (!about || !advancedMode || showPostsInAbout) {
|
||||||
|
dispatch(expandAccountTimeline(accountId, { withReplies, tagged, withoutReblogs }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagged || !hideFeaturedTags) {
|
||||||
|
dispatch(fetchFeaturedTags(accountId));
|
||||||
|
}
|
||||||
|
|
||||||
if (accountId === me) {
|
if (accountId === me) {
|
||||||
dispatch(connectTimeline(`account:${me}`));
|
dispatch(connectTimeline(`account:${me}`));
|
||||||
|
@ -83,18 +124,29 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
|
|
||||||
if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) {
|
if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId)
|
||||||
|
|| (nextProps.params.tagged !== this.props.params.tagged)
|
||||||
|
|| nextProps.withReplies !== this.props.withReplies
|
||||||
|
|| nextProps.withoutReblogs !== this.props.withoutReblogs
|
||||||
|
|| nextProps.showPostsInAbout !== this.props.showPostsInAbout
|
||||||
|
) {
|
||||||
dispatch(fetchAccount(nextProps.params.accountId));
|
dispatch(fetchAccount(nextProps.params.accountId));
|
||||||
dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));
|
dispatch(fetchAccountIdentityProofs(nextProps.params.accountId));
|
||||||
|
|
||||||
if (!nextProps.withReplies) {
|
if (!nextProps.withReplies && !nextProps.posts) {
|
||||||
dispatch(expandAccountFeaturedTimeline(nextProps.params.accountId));
|
dispatch(expandAccountFeaturedTimeline(nextProps.params.accountId, { tagged: nextProps.params.tagged }));
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(expandAccountTimeline(nextProps.params.accountId, { withReplies: nextProps.params.withReplies }));
|
if (!nextProps.about || nextProps.showPostsInAbout) {
|
||||||
|
dispatch(expandAccountTimeline(nextProps.params.accountId, { withReplies: nextProps.withReplies, tagged: nextProps.params.tagged, withoutReblogs: nextProps.withoutReblogs }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextProps.params.accountId === me && this.props.params.accountId !== me) {
|
if (nextProps.params.tagged || !nextProps.hideFeaturedTags) {
|
||||||
|
dispatch(fetchFeaturedTags(nextProps.params.accountId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!nextProps.about || !this.props.advancedMode) && nextProps.params.accountId === me && this.props.params.accountId !== me) {
|
||||||
dispatch(connectTimeline(`account:${me}`));
|
dispatch(connectTimeline(`account:${me}`));
|
||||||
} else if (this.props.params.accountId === me && nextProps.params.accountId !== me) {
|
} else if (this.props.params.accountId === me && nextProps.params.accountId !== me) {
|
||||||
dispatch(disconnectTimeline(`account:${me}`));
|
dispatch(disconnectTimeline(`account:${me}`));
|
||||||
|
@ -110,11 +162,19 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoadMore = maxId => {
|
handleLoadMore = maxId => {
|
||||||
this.props.dispatch(expandAccountTimeline(this.props.params.accountId, { maxId, withReplies: this.props.withReplies }));
|
this.props.dispatch(expandAccountTimeline(this.props.params.accountId, { maxId, withReplies: this.props.withReplies, tagged: this.props.params.tagged, withoutReblogs: this.props.withoutReblogs }));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHeaderClick = () => {
|
||||||
|
this.column.scrollTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
setRef = c => {
|
||||||
|
this.column = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, suspended, isAccount, multiColumn, remote, remoteUrl } = this.props;
|
const { intl, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, suspended, isAccount, multiColumn, remote, remoteUrl, about, withReplies, posts, advancedMode, hideFeaturedTags, showPostsInAbout, hideRelation } = this.props;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
return (
|
return (
|
||||||
|
@ -139,28 +199,39 @@ class AccountTimeline extends ImmutablePureComponent {
|
||||||
emptyMessage = <FormattedMessage id='empty_column.account_suspended' defaultMessage='Account suspended' />;
|
emptyMessage = <FormattedMessage id='empty_column.account_suspended' defaultMessage='Account suspended' />;
|
||||||
} else if (blockedBy) {
|
} else if (blockedBy) {
|
||||||
emptyMessage = <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />;
|
emptyMessage = <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />;
|
||||||
|
} else if (about && advancedMode && featuredStatusIds.isEmpty()) {
|
||||||
|
emptyMessage = <FormattedMessage id='empty_column.pinned_unavailable' defaultMessage='Pinned posts unavailable' />;
|
||||||
} else if (remote && statusIds.isEmpty()) {
|
} else if (remote && statusIds.isEmpty()) {
|
||||||
emptyMessage = <RemoteHint url={remoteUrl} />;
|
emptyMessage = <RemoteHint url={remoteUrl} />;
|
||||||
} else {
|
} else {
|
||||||
emptyMessage = <FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />;
|
emptyMessage = <FormattedMessage id='empty_column.account_timeline' defaultMessage='No toots here!' />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const remoteMessage = remote ? <RemoteHint url={remoteUrl} /> : null;
|
const remoteMessage = (!about && remote) ? <RemoteHint url={remoteUrl} /> : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||||
<ColumnBackButton multiColumn={multiColumn} />
|
<ColumnHeader
|
||||||
|
icon='user'
|
||||||
|
active={false}
|
||||||
|
title={intl.formatMessage(messages.title)}
|
||||||
|
onClick={this.handleHeaderClick}
|
||||||
|
pinned={false}
|
||||||
|
multiColumn={multiColumn}
|
||||||
|
>
|
||||||
|
<ColumnSettingsContainer />
|
||||||
|
</ColumnHeader>
|
||||||
|
|
||||||
<StatusList
|
<StatusList
|
||||||
prepend={<HeaderContainer accountId={this.props.params.accountId} />}
|
prepend={<HeaderContainer accountId={this.props.params.accountId} tagged={this.props.params.tagged} hideProfile={withReplies || posts || !!this.props.params.tagged} hideRelation={!about && hideRelation} hideFeaturedTags={hideFeaturedTags} />}
|
||||||
alwaysPrepend
|
alwaysPrepend
|
||||||
append={remoteMessage}
|
append={remoteMessage}
|
||||||
scrollKey='account_timeline'
|
scrollKey='account_timeline'
|
||||||
statusIds={(suspended || blockedBy) ? emptyList : statusIds}
|
statusIds={(suspended || blockedBy) ? emptyList : statusIds}
|
||||||
featuredStatusIds={featuredStatusIds}
|
featuredStatusIds={featuredStatusIds}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
hasMore={hasMore}
|
hasMore={about && advancedMode && !showPostsInAbout ? null : hasMore}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={about && advancedMode && !showPostsInAbout ? null : this.handleLoadMore}
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
timelineId='account'
|
timelineId='account'
|
||||||
|
|
|
@ -10,14 +10,20 @@ import {
|
||||||
fetchFollowers,
|
fetchFollowers,
|
||||||
expandFollowers,
|
expandFollowers,
|
||||||
} from '../../actions/accounts';
|
} from '../../actions/accounts';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import AccountContainer from '../../containers/account_container';
|
import AccountContainer from '../../containers/account_container';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../../components/column';
|
||||||
|
import ColumnHeader from '../../components/column_header';
|
||||||
|
import ColumnSettingsContainer from '../account_timeline/containers/column_settings_container';
|
||||||
import HeaderContainer from '../account_timeline/containers/header_container';
|
import HeaderContainer from '../account_timeline/containers/header_container';
|
||||||
import ColumnBackButton from '../../components/column_back_button';
|
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
import MissingIndicator from 'mastodon/components/missing_indicator';
|
import MissingIndicator from 'mastodon/components/missing_indicator';
|
||||||
import TimelineHint from 'mastodon/components/timeline_hint';
|
import TimelineHint from 'mastodon/components/timeline_hint';
|
||||||
|
import { new_features_policy } from 'mastodon/initial_state';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'column.account', defaultMessage: 'Account' },
|
||||||
|
});
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => ({
|
||||||
remote: !!(state.getIn(['accounts', props.params.accountId, 'acct']) !== state.getIn(['accounts', props.params.accountId, 'username'])),
|
remote: !!(state.getIn(['accounts', props.params.accountId, 'acct']) !== state.getIn(['accounts', props.params.accountId, 'username'])),
|
||||||
|
@ -27,6 +33,7 @@ const mapStateToProps = (state, props) => ({
|
||||||
hasMore: !!state.getIn(['user_lists', 'followers', props.params.accountId, 'next']),
|
hasMore: !!state.getIn(['user_lists', 'followers', props.params.accountId, 'next']),
|
||||||
isLoading: state.getIn(['user_lists', 'followers', props.params.accountId, 'isLoading'], true),
|
isLoading: state.getIn(['user_lists', 'followers', props.params.accountId, 'isLoading'], true),
|
||||||
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
||||||
|
advancedMode: state.getIn(['settings', 'account', 'other', 'advancedMode'], new_features_policy === 'conservative' ? false : true),
|
||||||
});
|
});
|
||||||
|
|
||||||
const RemoteHint = ({ url }) => (
|
const RemoteHint = ({ url }) => (
|
||||||
|
@ -38,12 +45,14 @@ RemoteHint.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default @connect(mapStateToProps)
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
class Followers extends ImmutablePureComponent {
|
class Followers extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
|
advancedMode: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
blockedBy: PropTypes.bool,
|
blockedBy: PropTypes.bool,
|
||||||
|
@ -71,8 +80,16 @@ class Followers extends ImmutablePureComponent {
|
||||||
this.props.dispatch(expandFollowers(this.props.params.accountId));
|
this.props.dispatch(expandFollowers(this.props.params.accountId));
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
|
handleHeaderClick = () => {
|
||||||
|
this.column.scrollTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
setRef = c => {
|
||||||
|
this.column = c;
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props;
|
const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl, intl } = this.props;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
return (
|
return (
|
||||||
|
@ -103,15 +120,24 @@ class Followers extends ImmutablePureComponent {
|
||||||
const remoteMessage = remote ? <RemoteHint url={remoteUrl} /> : null;
|
const remoteMessage = remote ? <RemoteHint url={remoteUrl} /> : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||||
<ColumnBackButton multiColumn={multiColumn} />
|
<ColumnHeader
|
||||||
|
icon='user'
|
||||||
|
active={false}
|
||||||
|
title={intl.formatMessage(messages.title)}
|
||||||
|
onClick={this.handleHeaderClick}
|
||||||
|
pinned={false}
|
||||||
|
multiColumn={multiColumn}
|
||||||
|
>
|
||||||
|
<ColumnSettingsContainer />
|
||||||
|
</ColumnHeader>
|
||||||
|
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='followers'
|
scrollKey='followers'
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
|
prepend={<HeaderContainer accountId={this.props.params.accountId} hideProfile hideFeaturedTags />}
|
||||||
alwaysPrepend
|
alwaysPrepend
|
||||||
append={remoteMessage}
|
append={remoteMessage}
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
|
|
|
@ -10,14 +10,20 @@ import {
|
||||||
fetchFollowing,
|
fetchFollowing,
|
||||||
expandFollowing,
|
expandFollowing,
|
||||||
} from '../../actions/accounts';
|
} from '../../actions/accounts';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import AccountContainer from '../../containers/account_container';
|
import AccountContainer from '../../containers/account_container';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../../components/column';
|
||||||
|
import ColumnHeader from '../../components/column_header';
|
||||||
|
import ColumnSettingsContainer from '../account_timeline/containers/column_settings_container';
|
||||||
import HeaderContainer from '../account_timeline/containers/header_container';
|
import HeaderContainer from '../account_timeline/containers/header_container';
|
||||||
import ColumnBackButton from '../../components/column_back_button';
|
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
import MissingIndicator from 'mastodon/components/missing_indicator';
|
import MissingIndicator from 'mastodon/components/missing_indicator';
|
||||||
import TimelineHint from 'mastodon/components/timeline_hint';
|
import TimelineHint from 'mastodon/components/timeline_hint';
|
||||||
|
import { new_features_policy } from 'mastodon/initial_state';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'column.account', defaultMessage: 'Account' },
|
||||||
|
});
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => ({
|
||||||
remote: !!(state.getIn(['accounts', props.params.accountId, 'acct']) !== state.getIn(['accounts', props.params.accountId, 'username'])),
|
remote: !!(state.getIn(['accounts', props.params.accountId, 'acct']) !== state.getIn(['accounts', props.params.accountId, 'username'])),
|
||||||
|
@ -27,6 +33,7 @@ const mapStateToProps = (state, props) => ({
|
||||||
hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
|
hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
|
||||||
isLoading: state.getIn(['user_lists', 'following', props.params.accountId, 'isLoading'], true),
|
isLoading: state.getIn(['user_lists', 'following', props.params.accountId, 'isLoading'], true),
|
||||||
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
||||||
|
advancedMode: state.getIn(['settings', 'account', 'other', 'advancedMode'], new_features_policy === 'conservative' ? false : true),
|
||||||
});
|
});
|
||||||
|
|
||||||
const RemoteHint = ({ url }) => (
|
const RemoteHint = ({ url }) => (
|
||||||
|
@ -38,12 +45,14 @@ RemoteHint.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default @connect(mapStateToProps)
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
class Following extends ImmutablePureComponent {
|
class Following extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
|
advancedMode: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
blockedBy: PropTypes.bool,
|
blockedBy: PropTypes.bool,
|
||||||
|
@ -71,8 +80,16 @@ class Following extends ImmutablePureComponent {
|
||||||
this.props.dispatch(expandFollowing(this.props.params.accountId));
|
this.props.dispatch(expandFollowing(this.props.params.accountId));
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
|
handleHeaderClick = () => {
|
||||||
|
this.column.scrollTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
setRef = c => {
|
||||||
|
this.column = c;
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl } = this.props;
|
const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, remote, remoteUrl, intl } = this.props;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
return (
|
return (
|
||||||
|
@ -103,15 +120,24 @@ class Following extends ImmutablePureComponent {
|
||||||
const remoteMessage = remote ? <RemoteHint url={remoteUrl} /> : null;
|
const remoteMessage = remote ? <RemoteHint url={remoteUrl} /> : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||||
<ColumnBackButton multiColumn={multiColumn} />
|
<ColumnHeader
|
||||||
|
icon='user'
|
||||||
|
active={false}
|
||||||
|
title={intl.formatMessage(messages.title)}
|
||||||
|
onClick={this.handleHeaderClick}
|
||||||
|
pinned={false}
|
||||||
|
multiColumn={multiColumn}
|
||||||
|
>
|
||||||
|
<ColumnSettingsContainer />
|
||||||
|
</ColumnHeader>
|
||||||
|
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='following'
|
scrollKey='following'
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
|
prepend={<HeaderContainer accountId={this.props.params.accountId} hideProfile hideFeaturedTags />}
|
||||||
alwaysPrepend
|
alwaysPrepend
|
||||||
append={remoteMessage}
|
append={remoteMessage}
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
|
|
|
@ -10,13 +10,19 @@ import {
|
||||||
fetchSubscribing,
|
fetchSubscribing,
|
||||||
expandSubscribing,
|
expandSubscribing,
|
||||||
} from '../../actions/accounts';
|
} from '../../actions/accounts';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import AccountContainer from '../../containers/account_container';
|
import AccountContainer from '../../containers/account_container';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../../components/column';
|
||||||
|
import ColumnHeader from '../../components/column_header';
|
||||||
|
import ColumnSettingsContainer from '../account_timeline/containers/column_settings_container';
|
||||||
import HeaderContainer from '../account_timeline/containers/header_container';
|
import HeaderContainer from '../account_timeline/containers/header_container';
|
||||||
import ColumnBackButton from '../../components/column_back_button';
|
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
import MissingIndicator from 'mastodon/components/missing_indicator';
|
import MissingIndicator from 'mastodon/components/missing_indicator';
|
||||||
|
import { new_features_policy } from 'mastodon/initial_state';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'column.account', defaultMessage: 'Account' },
|
||||||
|
});
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => ({
|
||||||
isAccount: !!state.getIn(['accounts', props.params.accountId]),
|
isAccount: !!state.getIn(['accounts', props.params.accountId]),
|
||||||
|
@ -24,15 +30,18 @@ const mapStateToProps = (state, props) => ({
|
||||||
hasMore: !!state.getIn(['user_lists', 'subscribing', props.params.accountId, 'next']),
|
hasMore: !!state.getIn(['user_lists', 'subscribing', props.params.accountId, 'next']),
|
||||||
isLoading: state.getIn(['user_lists', 'subscribing', props.params.accountId, 'isLoading'], true),
|
isLoading: state.getIn(['user_lists', 'subscribing', props.params.accountId, 'isLoading'], true),
|
||||||
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
blockedBy: state.getIn(['relationships', props.params.accountId, 'blocked_by'], false),
|
||||||
|
advancedMode: state.getIn(['settings', 'account', 'other', 'advancedMode'], new_features_policy === 'conservative' ? false : true),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default @connect(mapStateToProps)
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
class Subscribing extends ImmutablePureComponent {
|
class Subscribing extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
params: PropTypes.object.isRequired,
|
params: PropTypes.object.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
|
advancedMode: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
blockedBy: PropTypes.bool,
|
blockedBy: PropTypes.bool,
|
||||||
isAccount: PropTypes.bool,
|
isAccount: PropTypes.bool,
|
||||||
|
@ -57,8 +66,16 @@ class Subscribing extends ImmutablePureComponent {
|
||||||
this.props.dispatch(expandSubscribing(this.props.params.accountId));
|
this.props.dispatch(expandSubscribing(this.props.params.accountId));
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
|
handleHeaderClick = () => {
|
||||||
|
this.column.scrollTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
setRef = c => {
|
||||||
|
this.column = c;
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading } = this.props;
|
const { accountIds, hasMore, blockedBy, isAccount, multiColumn, isLoading, intl } = this.props;
|
||||||
|
|
||||||
if (!isAccount) {
|
if (!isAccount) {
|
||||||
return (
|
return (
|
||||||
|
@ -79,15 +96,24 @@ class Subscribing extends ImmutablePureComponent {
|
||||||
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' /> : <FormattedMessage id='account.subscribes.empty' defaultMessage="This user doesn't subscribe anyone yet." />;
|
const emptyMessage = blockedBy ? <FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' /> : <FormattedMessage id='account.subscribes.empty' defaultMessage="This user doesn't subscribe anyone yet." />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
|
||||||
<ColumnBackButton multiColumn={multiColumn} />
|
<ColumnHeader
|
||||||
|
icon='user'
|
||||||
|
active={false}
|
||||||
|
title={intl.formatMessage(messages.title)}
|
||||||
|
onClick={this.handleHeaderClick}
|
||||||
|
pinned={false}
|
||||||
|
multiColumn={multiColumn}
|
||||||
|
>
|
||||||
|
<ColumnSettingsContainer />
|
||||||
|
</ColumnHeader>
|
||||||
|
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='subscribing'
|
scrollKey='subscribing'
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
|
prepend={<HeaderContainer accountId={this.props.params.accountId} hideProfile hideFeaturedTags />}
|
||||||
alwaysPrepend
|
alwaysPrepend
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
|
|
|
@ -87,6 +87,8 @@ const mapStateToProps = state => ({
|
||||||
canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < 4,
|
canUploadMore: !state.getIn(['compose', 'media_attachments']).some(x => ['audio', 'video'].includes(x.get('type'))) && state.getIn(['compose', 'media_attachments']).size < 4,
|
||||||
dropdownMenuIsOpen: state.getIn(['dropdown_menu', 'openId']) !== null,
|
dropdownMenuIsOpen: state.getIn(['dropdown_menu', 'openId']) !== null,
|
||||||
firstLaunch: state.getIn(['settings', 'introductionVersion'], 0) < INTRODUCTION_VERSION,
|
firstLaunch: state.getIn(['settings', 'introductionVersion'], 0) < INTRODUCTION_VERSION,
|
||||||
|
advancedMode: state.getIn(['settings', 'account', 'other', 'advancedMode'], false),
|
||||||
|
openPostsFirst: state.getIn(['settings', 'account', 'other', 'openPostsFirst'], false),
|
||||||
visibilities: getHomeVisibilities(state),
|
visibilities: getHomeVisibilities(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -129,6 +131,8 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
location: PropTypes.object,
|
location: PropTypes.object,
|
||||||
mobile: PropTypes.bool,
|
mobile: PropTypes.bool,
|
||||||
|
advancedMode: PropTypes.bool,
|
||||||
|
openPostsFirst: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
|
@ -159,13 +163,15 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { children, mobile } = this.props;
|
const { children, mobile, advancedMode, openPostsFirst } = this.props;
|
||||||
const redirect = mobile ? <Redirect from='/' to='/timelines/home' exact /> : enableEmptyColumn ? <Redirect from='/' to='/empty' exact /> : <Redirect from='/' to='/getting-started' exact />;
|
const redirect = mobile ? <Redirect from='/' to='/timelines/home' exact /> : enableEmptyColumn ? <Redirect from='/' to='/empty' exact /> : <Redirect from='/' to='/getting-started' exact />;
|
||||||
|
const account_redirect = advancedMode && openPostsFirst ? <Redirect from='/accounts/:accountId' to='/accounts/:accountId/posts' exact /> : <Redirect from='/accounts/:accountId' to='/accounts/:accountId/about' exact />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ColumnsAreaContainer ref={this.setRef} singleColumn={mobile}>
|
<ColumnsAreaContainer ref={this.setRef} singleColumn={mobile}>
|
||||||
<WrappedSwitch>
|
<WrappedSwitch>
|
||||||
{redirect}
|
{redirect}
|
||||||
|
{account_redirect}
|
||||||
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
||||||
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
||||||
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
|
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
|
||||||
|
@ -200,7 +206,9 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||||
<WrappedRoute path='/statuses/:statusId/mentions' component={Mentions} content={children} />
|
<WrappedRoute path='/statuses/:statusId/mentions' component={Mentions} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
|
<WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
|
||||||
|
<WrappedRoute path='/accounts/:accountId/about' exact component={AccountTimeline} content={children} componentParams={{ about: true }} />
|
||||||
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
|
<WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
|
||||||
|
<WrappedRoute path='/accounts/:accountId/posts/:tagged?' component={AccountTimeline} content={children} componentParams={{ posts: true }} />
|
||||||
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
|
<WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
|
||||||
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
|
<WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
|
||||||
<WrappedRoute path='/accounts/:accountId/subscribing' component={Subscribing} content={children} />
|
<WrappedRoute path='/accounts/:accountId/subscribing' component={Subscribing} content={children} />
|
||||||
|
@ -246,6 +254,8 @@ class UI extends React.PureComponent {
|
||||||
layout: PropTypes.string.isRequired,
|
layout: PropTypes.string.isRequired,
|
||||||
firstLaunch: PropTypes.bool,
|
firstLaunch: PropTypes.bool,
|
||||||
visibilities: PropTypes.arrayOf(PropTypes.string),
|
visibilities: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
advancedMode: PropTypes.bool,
|
||||||
|
openPostsFirst: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -534,7 +544,7 @@ class UI extends React.PureComponent {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { draggingOver } = this.state;
|
const { draggingOver } = this.state;
|
||||||
const { children, isComposing, location, dropdownMenuIsOpen, layout } = this.props;
|
const { children, isComposing, location, dropdownMenuIsOpen, layout, advancedMode, openPostsFirst } = this.props;
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
help: this.handleHotkeyToggleHelp,
|
help: this.handleHotkeyToggleHelp,
|
||||||
|
@ -561,7 +571,7 @@ class UI extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
|
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
|
||||||
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef} style={{ pointerEvents: dropdownMenuIsOpen ? 'none' : null }}>
|
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef} style={{ pointerEvents: dropdownMenuIsOpen ? 'none' : null }}>
|
||||||
<SwitchingColumnsArea location={location} mobile={layout === 'mobile' || layout === 'single-column'}>
|
<SwitchingColumnsArea location={location} mobile={layout === 'mobile' || layout === 'single-column'} advancedMode={advancedMode} openPostsFirst={openPostsFirst}>
|
||||||
{children}
|
{children}
|
||||||
</SwitchingColumnsArea>
|
</SwitchingColumnsArea>
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,13 @@
|
||||||
"account.blocked": "Blocked",
|
"account.blocked": "Blocked",
|
||||||
"account.browse_more_on_origin_server": "Browse more on the original profile",
|
"account.browse_more_on_origin_server": "Browse more on the original profile",
|
||||||
"account.cancel_follow_request": "Cancel follow request",
|
"account.cancel_follow_request": "Cancel follow request",
|
||||||
|
"account.column_settings.advanced_settings": "Advanced settings",
|
||||||
|
"account.column_settings.advanced_mode": "Advanced mode",
|
||||||
|
"account.column_settings.hide_featured_tags": "Hide featuread tags selection",
|
||||||
|
"account.column_settings.hide_relation": "Hide post and follow counters",
|
||||||
|
"account.column_settings.open_posts_first": "Open first post when selecting an account",
|
||||||
|
"account.column_settings.show_posts_in_about": "Show posts in about",
|
||||||
|
"account.column_settings.without_reblogs": "Without boosts",
|
||||||
"account.conversations": "Show conversations with @{name}",
|
"account.conversations": "Show conversations with @{name}",
|
||||||
"account.conversations_all": "Show all conversations",
|
"account.conversations_all": "Show all conversations",
|
||||||
"account.direct": "Direct message @{name}",
|
"account.direct": "Direct message @{name}",
|
||||||
|
@ -27,6 +34,9 @@
|
||||||
"account.follows": "Follows",
|
"account.follows": "Follows",
|
||||||
"account.follows.empty": "This user doesn't follow anyone yet.",
|
"account.follows.empty": "This user doesn't follow anyone yet.",
|
||||||
"account.follows_you": "Follows you",
|
"account.follows_you": "Follows you",
|
||||||
|
"account.hashtag_all": "All",
|
||||||
|
"account.hashtag_all_description": "All posts (deselect hashtags)",
|
||||||
|
"account.hashtag_select_description": "Select hashtag #{name}",
|
||||||
"account.hide_reblogs": "Hide boosts from @{name}",
|
"account.hide_reblogs": "Hide boosts from @{name}",
|
||||||
"account.joined": "Joined",
|
"account.joined": "Joined",
|
||||||
"account.last_status": "Last active",
|
"account.last_status": "Last active",
|
||||||
|
@ -49,6 +59,11 @@
|
||||||
"account.requested": "Awaiting approval. Click to cancel follow request",
|
"account.requested": "Awaiting approval. Click to cancel follow request",
|
||||||
"account.secret": "Secret",
|
"account.secret": "Secret",
|
||||||
"account.share": "Share @{name}'s profile",
|
"account.share": "Share @{name}'s profile",
|
||||||
|
"account.short.about": "About",
|
||||||
|
"account.short.conversations": "Conversations",
|
||||||
|
"account.short.media": "Media",
|
||||||
|
"account.short.posts": "Posts",
|
||||||
|
"account.short.with_replies": "Posts & Replies",
|
||||||
"account.show_reblogs": "Show boosts from @{name}",
|
"account.show_reblogs": "Show boosts from @{name}",
|
||||||
"account.statuses_counter": "{count, plural, one {{counter} Post} other {{counter} Posts}}",
|
"account.statuses_counter": "{count, plural, one {{counter} Post} other {{counter} Posts}}",
|
||||||
"account.subscribe": "Subscribe",
|
"account.subscribe": "Subscribe",
|
||||||
|
@ -64,6 +79,7 @@
|
||||||
"account.unmute_notifications": "Unmute notifications from @{name}",
|
"account.unmute_notifications": "Unmute notifications from @{name}",
|
||||||
"account_note.placeholder": "Click to add note",
|
"account_note.placeholder": "Click to add note",
|
||||||
"account_popup.more_users": "({number, plural, one {# other user} other {# other users}})",
|
"account_popup.more_users": "({number, plural, one {# other user} other {# other users}})",
|
||||||
|
"account.view_about": "View about",
|
||||||
"alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
|
"alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
|
||||||
"alert.rate_limited.title": "Rate limited",
|
"alert.rate_limited.title": "Rate limited",
|
||||||
"alert.unexpected.message": "An unexpected error occurred.",
|
"alert.unexpected.message": "An unexpected error occurred.",
|
||||||
|
@ -91,6 +107,7 @@
|
||||||
"circle.reply": "(Reply to circle context)",
|
"circle.reply": "(Reply to circle context)",
|
||||||
"circle.select": "Select circle",
|
"circle.select": "Select circle",
|
||||||
"circle.unselect": "(Select circle)",
|
"circle.unselect": "(Select circle)",
|
||||||
|
"column.account": "Account",
|
||||||
"column.blocks": "Blocked users",
|
"column.blocks": "Blocked users",
|
||||||
"column.bookmarks": "Bookmarks",
|
"column.bookmarks": "Bookmarks",
|
||||||
"column.circles": "Circles",
|
"column.circles": "Circles",
|
||||||
|
@ -223,6 +240,7 @@
|
||||||
"empty_column.blocks": "You haven't blocked any users yet.",
|
"empty_column.blocks": "You haven't blocked any users yet.",
|
||||||
"empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
|
"empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
|
||||||
"empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
|
"empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
|
||||||
|
"empty_column.conversation_unavailable": "No conversation with this user yet",
|
||||||
"empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
|
"empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
|
||||||
"empty_column.domain_blocks": "There are no blocked domains yet.",
|
"empty_column.domain_blocks": "There are no blocked domains yet.",
|
||||||
"empty_column.emoji_reactioned_statuses": "You don't have any reaction posts yet. When you reaction one, it will show up here.",
|
"empty_column.emoji_reactioned_statuses": "You don't have any reaction posts yet. When you reaction one, it will show up here.",
|
||||||
|
@ -240,6 +258,7 @@
|
||||||
"empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.",
|
"empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.",
|
||||||
"empty_column.mutes": "You haven't muted any users yet.",
|
"empty_column.mutes": "You haven't muted any users yet.",
|
||||||
"empty_column.notifications": "You don't have any notifications yet. When other people interact with you, you will see it here.",
|
"empty_column.notifications": "You don't have any notifications yet. When other people interact with you, you will see it here.",
|
||||||
|
"empty_column.pinned_unavailable": "Pinned posts unavailable",
|
||||||
"empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up",
|
"empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up",
|
||||||
"empty_column.referred_by_statuses": "There are no referred by posts yet. When someone refers a post, it will appear here.",
|
"empty_column.referred_by_statuses": "There are no referred by posts yet. When someone refers a post, it will appear here.",
|
||||||
"empty_column.suggestions": "No one has suggestions yet.",
|
"empty_column.suggestions": "No one has suggestions yet.",
|
||||||
|
|
|
@ -11,6 +11,13 @@
|
||||||
"account.blocked": "ブロック済み",
|
"account.blocked": "ブロック済み",
|
||||||
"account.browse_more_on_origin_server": "リモートで表示",
|
"account.browse_more_on_origin_server": "リモートで表示",
|
||||||
"account.cancel_follow_request": "フォローリクエストを取り消す",
|
"account.cancel_follow_request": "フォローリクエストを取り消す",
|
||||||
|
"account.column_settings.advanced_settings": "詳細設定",
|
||||||
|
"account.column_settings.advanced_mode": "詳細モード",
|
||||||
|
"account.column_settings.hide_featured_tags": "注目のタグの選択を隠す",
|
||||||
|
"account.column_settings.hide_relation": "投稿とフォローのカウンターを隠す",
|
||||||
|
"account.column_settings.open_posts_first": "アカウント選択時、最初に投稿を開く",
|
||||||
|
"account.column_settings.show_posts_in_about": "概要に投稿を表示",
|
||||||
|
"account.column_settings.without_reblogs": "ブーストを除外",
|
||||||
"account.conversations": "@{name}さんとの会話を表示",
|
"account.conversations": "@{name}さんとの会話を表示",
|
||||||
"account.conversations_all": "すべての会話を表示",
|
"account.conversations_all": "すべての会話を表示",
|
||||||
"account.direct": "@{name}さんにダイレクトメッセージ",
|
"account.direct": "@{name}さんにダイレクトメッセージ",
|
||||||
|
@ -27,6 +34,9 @@
|
||||||
"account.follows": "フォロー",
|
"account.follows": "フォロー",
|
||||||
"account.follows.empty": "まだ誰もフォローしていません。",
|
"account.follows.empty": "まだ誰もフォローしていません。",
|
||||||
"account.follows_you": "フォローされています",
|
"account.follows_you": "フォローされています",
|
||||||
|
"account.hashtag_all": "すべて",
|
||||||
|
"account.hashtag_all_description": "すべての投稿(ハッシュタグの選択解除)",
|
||||||
|
"account.hashtag_select_description": "ハッシュタグ #{name} を選択",
|
||||||
"account.hide_reblogs": "@{name}さんからのブーストを非表示",
|
"account.hide_reblogs": "@{name}さんからのブーストを非表示",
|
||||||
"account.joined": "登録日",
|
"account.joined": "登録日",
|
||||||
"account.last_status": "最後の活動",
|
"account.last_status": "最後の活動",
|
||||||
|
@ -49,6 +59,11 @@
|
||||||
"account.report": "@{name}さんを通報",
|
"account.report": "@{name}さんを通報",
|
||||||
"account.requested": "フォロー承認待ちです。クリックしてキャンセル",
|
"account.requested": "フォロー承認待ちです。クリックしてキャンセル",
|
||||||
"account.share": "@{name}さんのプロフィールを共有する",
|
"account.share": "@{name}さんのプロフィールを共有する",
|
||||||
|
"account.short.about": "概要",
|
||||||
|
"account.short.conversations": "会話",
|
||||||
|
"account.short.media": "メディア",
|
||||||
|
"account.short.posts": "投稿",
|
||||||
|
"account.short.with_replies": "投稿と返信",
|
||||||
"account.show_reblogs": "@{name}さんからのブーストを表示",
|
"account.show_reblogs": "@{name}さんからのブーストを表示",
|
||||||
"account.statuses_counter": "{counter} 投稿",
|
"account.statuses_counter": "{counter} 投稿",
|
||||||
"account.subscribe": "購読",
|
"account.subscribe": "購読",
|
||||||
|
@ -64,6 +79,7 @@
|
||||||
"account.unmute_notifications": "@{name}さんからの通知を受け取るようにする",
|
"account.unmute_notifications": "@{name}さんからの通知を受け取るようにする",
|
||||||
"account_note.placeholder": "クリックしてメモを追加",
|
"account_note.placeholder": "クリックしてメモを追加",
|
||||||
"account_popup.more_users": "(他、{number}人のユーザー)",
|
"account_popup.more_users": "(他、{number}人のユーザー)",
|
||||||
|
"account.view_about": "概要を見る",
|
||||||
"alert.rate_limited.message": "{retry_time, time, medium} 以降に再度実行してください。",
|
"alert.rate_limited.message": "{retry_time, time, medium} 以降に再度実行してください。",
|
||||||
"alert.rate_limited.title": "制限に達しました",
|
"alert.rate_limited.title": "制限に達しました",
|
||||||
"alert.unexpected.message": "不明なエラーが発生しました。",
|
"alert.unexpected.message": "不明なエラーが発生しました。",
|
||||||
|
@ -91,6 +107,7 @@
|
||||||
"circles.new.title_placeholder": "新規サークル名",
|
"circles.new.title_placeholder": "新規サークル名",
|
||||||
"circles.search": "フォローされている人の中から検索",
|
"circles.search": "フォローされている人の中から検索",
|
||||||
"circles.subheading": "あなたのサークル",
|
"circles.subheading": "あなたのサークル",
|
||||||
|
"column.account": "アカウント",
|
||||||
"column.blocks": "ブロックしたユーザー",
|
"column.blocks": "ブロックしたユーザー",
|
||||||
"column.bookmarks": "ブックマーク",
|
"column.bookmarks": "ブックマーク",
|
||||||
"column.circles": "サークル",
|
"column.circles": "サークル",
|
||||||
|
@ -223,6 +240,7 @@
|
||||||
"empty_column.blocks": "まだ誰もブロックしていません。",
|
"empty_column.blocks": "まだ誰もブロックしていません。",
|
||||||
"empty_column.bookmarked_statuses": "まだ何もブックマーク登録していません。ブックマーク登録するとここに表示されます。",
|
"empty_column.bookmarked_statuses": "まだ何もブックマーク登録していません。ブックマーク登録するとここに表示されます。",
|
||||||
"empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!",
|
"empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!",
|
||||||
|
"empty_column.conversation_unavailable": "このユーザーとの会話はまだありません。",
|
||||||
"empty_column.direct": "ダイレクトメッセージはまだありません。ダイレクトメッセージをやりとりすると、ここに表示されます。",
|
"empty_column.direct": "ダイレクトメッセージはまだありません。ダイレクトメッセージをやりとりすると、ここに表示されます。",
|
||||||
"empty_column.domain_blocks": "ブロックしているドメインはありません。",
|
"empty_column.domain_blocks": "ブロックしているドメインはありません。",
|
||||||
"empty_column.emoji_reactioned_statuses": "まだ何も絵文字リアクションしていません。絵文字リアクションするとここに表示されます。",
|
"empty_column.emoji_reactioned_statuses": "まだ何も絵文字リアクションしていません。絵文字リアクションするとここに表示されます。",
|
||||||
|
@ -240,6 +258,7 @@
|
||||||
"empty_column.lists": "まだリストがありません。リストを作るとここに表示されます。",
|
"empty_column.lists": "まだリストがありません。リストを作るとここに表示されます。",
|
||||||
"empty_column.mutes": "まだ誰もミュートしていません。",
|
"empty_column.mutes": "まだ誰もミュートしていません。",
|
||||||
"empty_column.notifications": "まだ通知がありません。他の人とふれ合って会話を始めましょう。",
|
"empty_column.notifications": "まだ通知がありません。他の人とふれ合って会話を始めましょう。",
|
||||||
|
"empty_column.pinned_unavailable": "固定された投稿はありません。",
|
||||||
"empty_column.referred_by_statuses": "まだ、参照している投稿はありません。誰かが投稿を参照すると、ここに表示されます。",
|
"empty_column.referred_by_statuses": "まだ、参照している投稿はありません。誰かが投稿を参照すると、ここに表示されます。",
|
||||||
"empty_column.suggestions": "まだおすすめできるユーザーがいません。",
|
"empty_column.suggestions": "まだおすすめできるユーザーがいません。",
|
||||||
"empty_column.trends": "まだ何もトレンドがありません。",
|
"empty_column.trends": "まだ何もトレンドがありません。",
|
||||||
|
|
|
@ -16,6 +16,17 @@ const initialState = ImmutableMap({
|
||||||
show: true,
|
show: true,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
account: ImmutableMap({
|
||||||
|
other: ImmutableMap({
|
||||||
|
advancedMode: true,
|
||||||
|
openPostsFirst: false,
|
||||||
|
withoutReblogs: false,
|
||||||
|
showPostsInAbout: true,
|
||||||
|
hideFeaturedTags: false,
|
||||||
|
hideRelation: false,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
home: ImmutableMap({
|
home: ImmutableMap({
|
||||||
shows: ImmutableMap({
|
shows: ImmutableMap({
|
||||||
reblog: true,
|
reblog: true,
|
||||||
|
|
|
@ -87,6 +87,11 @@ import {
|
||||||
DIRECTORY_EXPAND_SUCCESS,
|
DIRECTORY_EXPAND_SUCCESS,
|
||||||
DIRECTORY_EXPAND_FAIL,
|
DIRECTORY_EXPAND_FAIL,
|
||||||
} from 'mastodon/actions/directory';
|
} from 'mastodon/actions/directory';
|
||||||
|
import {
|
||||||
|
FEATURED_TAGS_FETCH_REQUEST,
|
||||||
|
FEATURED_TAGS_FETCH_SUCCESS,
|
||||||
|
FEATURED_TAGS_FETCH_FAIL,
|
||||||
|
} from 'mastodon/actions/featured_tags';
|
||||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||||
|
|
||||||
const initialListState = ImmutableMap({
|
const initialListState = ImmutableMap({
|
||||||
|
@ -106,6 +111,7 @@ const initialState = ImmutableMap({
|
||||||
follow_requests: initialListState,
|
follow_requests: initialListState,
|
||||||
blocks: initialListState,
|
blocks: initialListState,
|
||||||
mutes: initialListState,
|
mutes: initialListState,
|
||||||
|
featured_tags: initialListState,
|
||||||
});
|
});
|
||||||
|
|
||||||
const normalizeList = (state, path, accounts, next) => {
|
const normalizeList = (state, path, accounts, next) => {
|
||||||
|
@ -147,6 +153,18 @@ const appendToEmojiReactions = (state, path, emojiReactions, next) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const normalizeFeaturedTag = (featuredTags, accountId) => {
|
||||||
|
const normalizeFeaturedTag = { ...featuredTags, accountId: accountId };
|
||||||
|
return fromJS(normalizeFeaturedTag);
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeFeaturedTags = (state, path, featuredTags, accountId) => {
|
||||||
|
return state.setIn(path, ImmutableMap({
|
||||||
|
items: ImmutableList(featuredTags.map(featuredTag => normalizeFeaturedTag(featuredTag, accountId)).sort((a, b) => b.get('statuses_count') - a.get('statuses_count'))),
|
||||||
|
isLoading: false,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
export default function userLists(state = initialState, action) {
|
export default function userLists(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case FOLLOWERS_FETCH_SUCCESS:
|
case FOLLOWERS_FETCH_SUCCESS:
|
||||||
|
@ -274,6 +292,12 @@ export default function userLists(state = initialState, action) {
|
||||||
case DIRECTORY_FETCH_FAIL:
|
case DIRECTORY_FETCH_FAIL:
|
||||||
case DIRECTORY_EXPAND_FAIL:
|
case DIRECTORY_EXPAND_FAIL:
|
||||||
return state.setIn(['directory', 'isLoading'], false);
|
return state.setIn(['directory', 'isLoading'], false);
|
||||||
|
case FEATURED_TAGS_FETCH_SUCCESS:
|
||||||
|
return normalizeFeaturedTags(state, ['featured_tags', action.id], action.tags, action.id);
|
||||||
|
case FEATURED_TAGS_FETCH_REQUEST:
|
||||||
|
return state.setIn(['featured_tags', action.id, 'isLoading'], true);
|
||||||
|
case FEATURED_TAGS_FETCH_FAIL:
|
||||||
|
return state.setIn(['featured_tags', action.id, 'isLoading'], false);
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6663,6 +6663,13 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.with-short-label {
|
||||||
|
button,
|
||||||
|
a {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.directory__section-headline {
|
&.directory__section-headline {
|
||||||
background: darken($ui-base-color, 2%);
|
background: darken($ui-base-color, 2%);
|
||||||
border-bottom-color: transparent;
|
border-bottom-color: transparent;
|
||||||
|
@ -6680,6 +6687,13 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__short-label {
|
||||||
|
font-size: 10px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-form {
|
.filter-form {
|
||||||
|
@ -7569,6 +7583,51 @@ noscript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__hashtag-links {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 10px 5px;
|
||||||
|
margin: 0 -5px;
|
||||||
|
color: $darker-text-color;
|
||||||
|
border-bottom: 1px solid lighten($ui-base-color, 12%);
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
color: $darker-text-color;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
color: $primary-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.active {
|
||||||
|
color: darken($ui-base-color, 4%);
|
||||||
|
background: $darker-text-color;
|
||||||
|
border-radius: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.advanced {
|
||||||
|
.account__header__bio {
|
||||||
|
.account__header__personal--wrapper {
|
||||||
|
border-bottom: 1px solid lighten($ui-base-color, 12%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.account__header__extra {
|
||||||
|
margin-top: 0;
|
||||||
|
padding: 0 5px;
|
||||||
|
.account__header__extra__links {
|
||||||
|
border-top: none;
|
||||||
|
border-bottom: 1px solid lighten($ui-base-color, 12%);
|
||||||
|
margin: 0 -5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__account-note {
|
&__account-note {
|
||||||
|
|
Loading…
Reference in a new issue