Merge remote-tracking branch 'origin' into a11y--
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
@@ -17,7 +17,6 @@ import {
|
||||
isRemote,
|
||||
getDomain,
|
||||
} from 'soapbox/utils/accounts';
|
||||
import { parseVersion } from 'soapbox/utils/features';
|
||||
import classNames from 'classnames';
|
||||
import Avatar from 'soapbox/components/avatar';
|
||||
import { shortNumberFormat } from 'soapbox/utils/numbers';
|
||||
@@ -30,6 +29,7 @@ import ActionButton from 'soapbox/features/ui/components/action_button';
|
||||
import SubscriptionButton from 'soapbox/features/ui/components/subscription_button';
|
||||
import { openModal } from 'soapbox/actions/modal';
|
||||
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
||||
import { getFeatures } from 'soapbox/utils/features';
|
||||
|
||||
const messages = defineMessages({
|
||||
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
|
||||
@@ -72,11 +72,13 @@ const messages = defineMessages({
|
||||
const mapStateToProps = state => {
|
||||
const me = state.get('me');
|
||||
const account = state.getIn(['accounts', me]);
|
||||
const instance = state.get('instance');
|
||||
const features = getFeatures(instance);
|
||||
|
||||
return {
|
||||
me,
|
||||
meAccount: account,
|
||||
version: parseVersion(state.getIn(['instance', 'version'])),
|
||||
features,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -90,7 +92,7 @@ class Header extends ImmutablePureComponent {
|
||||
identity_props: ImmutablePropTypes.list,
|
||||
intl: PropTypes.object.isRequired,
|
||||
username: PropTypes.string,
|
||||
version: PropTypes.object,
|
||||
features: PropTypes.object,
|
||||
};
|
||||
|
||||
state = {
|
||||
@@ -156,7 +158,7 @@ class Header extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
makeMenu() {
|
||||
const { account, intl, me, meAccount, version } = this.props;
|
||||
const { account, intl, me, meAccount, features } = this.props;
|
||||
|
||||
const menu = [];
|
||||
|
||||
@@ -196,7 +198,7 @@ class Header extends ImmutablePureComponent {
|
||||
menu.push({ text: intl.formatMessage(messages.add_or_remove_from_list), action: this.props.onAddToList });
|
||||
// menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });
|
||||
menu.push(null);
|
||||
} else if (version.software === 'Pleroma') {
|
||||
} else if (features.unrestrictedLists) {
|
||||
menu.push({ text: intl.formatMessage(messages.add_or_remove_from_list), action: this.props.onAddToList });
|
||||
}
|
||||
|
||||
@@ -285,7 +287,7 @@ class Header extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account, intl, username, me } = this.props;
|
||||
const { account, intl, username, me, features } = this.props;
|
||||
const { isSmallScreen } = this.state;
|
||||
|
||||
if (!account) {
|
||||
@@ -327,9 +329,9 @@ class Header extends ImmutablePureComponent {
|
||||
<StillImage src={account.get('header')} alt='' className='parallax' />
|
||||
</a>}
|
||||
|
||||
<div className='account__header__subscribe'>
|
||||
{features.accountSubscriptions && <div className='account__header__subscribe'>
|
||||
<SubscriptionButton account={account} />
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
|
||||
<div className='account__header__bar'>
|
||||
@@ -356,24 +358,20 @@ class Header extends ImmutablePureComponent {
|
||||
<span><FormattedMessage id='account.followers' defaultMessage='Followers' /></span>
|
||||
</NavLink>}
|
||||
|
||||
{
|
||||
ownAccount &&
|
||||
<div>
|
||||
<NavLink
|
||||
exact activeClassName='active' to={`/@${account.get('acct')}/favorites`}
|
||||
>
|
||||
{ /* : TODO : shortNumberFormat(account.get('favourite_count')) */ }
|
||||
<span>•</span>
|
||||
<span><FormattedMessage id='navigation_bar.favourites' defaultMessage='Likes' /></span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
exact activeClassName='active' to={`/@${account.get('acct')}/pins`}
|
||||
>
|
||||
{ /* : TODO : shortNumberFormat(account.get('pinned_count')) */ }
|
||||
<span>•</span>
|
||||
<span><FormattedMessage id='navigation_bar.pins' defaultMessage='Pins' /></span>
|
||||
</NavLink>
|
||||
</div>
|
||||
{(ownAccount || !account.getIn(['pleroma', 'hide_favorites'], true)) && <NavLink exact activeClassName='active' to={`/@${account.get('acct')}/favorites`}>
|
||||
{ /* : TODO : shortNumberFormat(account.get('favourite_count')) */ }
|
||||
<span>•</span>
|
||||
<span><FormattedMessage id='navigation_bar.favourites' defaultMessage='Likes' /></span>
|
||||
</NavLink>}
|
||||
|
||||
{ownAccount &&
|
||||
<NavLink
|
||||
exact activeClassName='active' to={`/@${account.get('acct')}/pins`}
|
||||
>
|
||||
{ /* : TODO : shortNumberFormat(account.get('pinned_count')) */ }
|
||||
<span>•</span>
|
||||
<span><FormattedMessage id='navigation_bar.pins' defaultMessage='Pins' /></span>
|
||||
</NavLink>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ import classNames from 'classnames';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import { buildCustomEmojis } from '../../emoji/emoji';
|
||||
import { join } from 'path';
|
||||
import { FE_SUBDIRECTORY } from 'soapbox/build_config';
|
||||
import { joinPublicPath } from 'soapbox/utils/static';
|
||||
|
||||
const messages = defineMessages({
|
||||
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
|
||||
@@ -29,7 +28,7 @@ const messages = defineMessages({
|
||||
|
||||
let EmojiPicker, Emoji; // load asynchronously
|
||||
|
||||
const backgroundImageFn = () => join(FE_SUBDIRECTORY, 'emoji', 'sheet_13.png');
|
||||
const backgroundImageFn = () => require('emoji-datasource/img/twitter/sheets/32.png');
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||
|
||||
const categoriesSort = [
|
||||
@@ -358,8 +357,8 @@ class EmojiPickerDropdown extends React.PureComponent {
|
||||
<div ref={this.setTargetRef} className='emoji-button' title={title} aria-label={title} aria-expanded={active} role='button' onClick={this.onToggle} onKeyDown={this.onToggle} tabIndex={0}>
|
||||
<img
|
||||
className={classNames('emojione', { 'pulse-loading': active && loading })}
|
||||
alt='🙂'
|
||||
src={join(FE_SUBDIRECTORY, 'emoji', '1f602.svg')}
|
||||
alt='😂'
|
||||
src={joinPublicPath('packs/emoji/1f602.svg')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { setSchedule, removeSchedule } from '../../../actions/compose';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import IconButton from 'soapbox/components/icon_button';
|
||||
import { removeSchedule } from 'soapbox/actions/compose';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -15,11 +15,22 @@ const messages = defineMessages({
|
||||
remove: { id: 'schedule.remove', defaultMessage: 'Remove schedule' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
const mapStateToProps = state => ({
|
||||
active: state.getIn(['compose', 'schedule']) ? true : false,
|
||||
scheduledAt: state.getIn(['compose', 'schedule']),
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onSchedule(date) {
|
||||
dispatch(setSchedule(date));
|
||||
},
|
||||
|
||||
onRemoveSchedule(date) {
|
||||
dispatch(removeSchedule());
|
||||
},
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps, mapDispatchToProps)
|
||||
@injectIntl
|
||||
class ScheduleForm extends React.Component {
|
||||
|
||||
@@ -27,6 +38,7 @@ class ScheduleForm extends React.Component {
|
||||
scheduledAt: PropTypes.instanceOf(Date),
|
||||
intl: PropTypes.object.isRequired,
|
||||
onSchedule: PropTypes.func.isRequired,
|
||||
onRemoveSchedule: PropTypes.func.isRequired,
|
||||
dispatch: PropTypes.func,
|
||||
active: PropTypes.bool,
|
||||
};
|
||||
@@ -60,7 +72,7 @@ class ScheduleForm extends React.Component {
|
||||
}
|
||||
|
||||
handleRemove = e => {
|
||||
this.props.dispatch(removeSchedule());
|
||||
this.props.onRemoveSchedule();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { connect } from 'react-redux';
|
||||
import ScheduleForm from '../components/schedule_form';
|
||||
import { setSchedule } from '../../../actions/compose';
|
||||
import React from 'react';
|
||||
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
||||
import { ScheduleForm } from 'soapbox/features/ui/util/async-components';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
schedule: state.getIn(['compose', 'schedule']),
|
||||
active: state.getIn(['compose', 'schedule']) ? true : false,
|
||||
});
|
||||
export default class ScheduleFormContainer extends React.PureComponent {
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onSchedule(date) {
|
||||
dispatch(setSchedule(date));
|
||||
},
|
||||
});
|
||||
render() {
|
||||
return (
|
||||
<BundleContainer fetchComponent={ScheduleForm}>
|
||||
{Component => <Component {...this.props} />}
|
||||
</BundleContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ScheduleForm);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import CoinDB from '../utils/coin_db';
|
||||
import { getCoinIcon } from '../utils/coin_icons';
|
||||
import CryptoIcon from './crypto_icon';
|
||||
import { openModal } from 'soapbox/actions/modal';
|
||||
import { CopyableInput } from 'soapbox/features/forms';
|
||||
import { getExplorerUrl } from '../utils/block_explorer';
|
||||
@@ -31,9 +31,11 @@ class CryptoAddress extends ImmutablePureComponent {
|
||||
return (
|
||||
<div className='crypto-address'>
|
||||
<div className='crypto-address__head'>
|
||||
<div className='crypto-address__icon'>
|
||||
<img src={getCoinIcon(ticker)} alt={title} />
|
||||
</div>
|
||||
<CryptoIcon
|
||||
className='crypto-address__icon'
|
||||
ticker={ticker}
|
||||
title={title}
|
||||
/>
|
||||
<div className='crypto-address__title'>{title || ticker.toUpperCase()}</div>
|
||||
<div className='crypto-address__actions'>
|
||||
<a href='' onClick={this.handleModalClick}>
|
||||
|
||||
26
app/soapbox/features/crypto_donate/components/crypto_icon.js
Normal file
26
app/soapbox/features/crypto_donate/components/crypto_icon.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export default class CryptoIcon extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
ticker: PropTypes.string.isRequired,
|
||||
title: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { ticker, title, className } = this.props;
|
||||
|
||||
return (
|
||||
<div className={classNames('crypto-icon', className)}>
|
||||
<img
|
||||
src={require(`cryptocurrency-icons/svg/color/${ticker.toLowerCase()}.svg`)}
|
||||
alt={title || ticker}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import QRCode from 'qrcode.react';
|
||||
import CoinDB from '../utils/coin_db';
|
||||
import { getCoinIcon } from '../utils/coin_icons';
|
||||
import CryptoIcon from './crypto_icon';
|
||||
import { CopyableInput } from 'soapbox/features/forms';
|
||||
import { getExplorerUrl } from '../utils/block_explorer';
|
||||
|
||||
export default @connect()
|
||||
class DetailedCryptoAddress extends ImmutablePureComponent {
|
||||
export default class DetailedCryptoAddress extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
address: PropTypes.string.isRequired,
|
||||
@@ -26,9 +24,11 @@ class DetailedCryptoAddress extends ImmutablePureComponent {
|
||||
return (
|
||||
<div className='crypto-address'>
|
||||
<div className='crypto-address__head'>
|
||||
<div className='crypto-address__icon'>
|
||||
<img src={getCoinIcon(ticker)} alt={title} />
|
||||
</div>
|
||||
<CryptoIcon
|
||||
className='crypto-address__icon'
|
||||
ticker={ticker}
|
||||
title={title}
|
||||
/>
|
||||
<div className='crypto-address__title'>{title || ticker.toUpperCase()}</div>
|
||||
<div className='crypto-address__actions'>
|
||||
{explorerUrl && <a href={explorerUrl} target='_blank'>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// Does some trickery to import all the icons into the project
|
||||
// See: https://stackoverflow.com/questions/42118296/dynamically-import-images-from-a-directory-using-webpack
|
||||
|
||||
const icons = {};
|
||||
|
||||
function importAll(r) {
|
||||
const pathRegex = /\.\/(.*)\.svg/i;
|
||||
|
||||
r.keys().forEach((key) => {
|
||||
const ticker = pathRegex.exec(key)[1];
|
||||
return icons[ticker] = r(key).default;
|
||||
});
|
||||
}
|
||||
|
||||
importAll(require.context('cryptocurrency-icons/svg/color/', true, /\.svg$/));
|
||||
|
||||
export default icons;
|
||||
|
||||
// For getting the icon
|
||||
export const getCoinIcon = ticker => icons[ticker] || icons.generic || null;
|
||||
@@ -22,23 +22,23 @@ describe('emoji', () => {
|
||||
|
||||
it('does unicode', () => {
|
||||
expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="👩👩👦👦" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg" />');
|
||||
'<img draggable="false" class="emojione" alt="👩👩👦👦" title=":woman-woman-boy-boy:" src="/packs/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg" />');
|
||||
expect(emojify('👨👩👧👧')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="👨👩👧👧" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg" />');
|
||||
expect(emojify('👩👩👦')).toEqual('<img draggable="false" class="emojione" alt="👩👩👦" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg" />');
|
||||
'<img draggable="false" class="emojione" alt="👨👩👧👧" title=":man-woman-girl-girl:" src="/packs/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg" />');
|
||||
expect(emojify('👩👩👦')).toEqual('<img draggable="false" class="emojione" alt="👩👩👦" title=":woman-woman-boy:" src="/packs/emoji/1f469-200d-1f469-200d-1f466.svg" />');
|
||||
expect(emojify('\u2757')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" />');
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/packs/emoji/2757.svg" />');
|
||||
});
|
||||
|
||||
it('does multiple unicode', () => {
|
||||
expect(emojify('\u2757 #\uFE0F\u20E3')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" />');
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/packs/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/packs/emoji/23-20e3.svg" />');
|
||||
expect(emojify('\u2757#\uFE0F\u20E3')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /><img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" />');
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/packs/emoji/2757.svg" /><img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/packs/emoji/23-20e3.svg" />');
|
||||
expect(emojify('\u2757 #\uFE0F\u20E3 \u2757')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" /> <img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" />');
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/packs/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/packs/emoji/23-20e3.svg" /> <img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/packs/emoji/2757.svg" />');
|
||||
expect(emojify('foo \u2757 #\uFE0F\u20E3 bar')).toEqual(
|
||||
'foo <img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" /> bar');
|
||||
'foo <img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/packs/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/packs/emoji/23-20e3.svg" /> bar');
|
||||
});
|
||||
|
||||
it('ignores unicode inside of tags', () => {
|
||||
@@ -46,16 +46,16 @@ describe('emoji', () => {
|
||||
});
|
||||
|
||||
it('does multiple emoji properly (issue 5188)', () => {
|
||||
expect(emojify('👌🌈💕')).toEqual('<img draggable="false" class="emojione" alt="👌" title=":ok_hand:" src="/emoji/1f44c.svg" /><img draggable="false" class="emojione" alt="🌈" title=":rainbow:" src="/emoji/1f308.svg" /><img draggable="false" class="emojione" alt="💕" title=":two_hearts:" src="/emoji/1f495.svg" />');
|
||||
expect(emojify('👌 🌈 💕')).toEqual('<img draggable="false" class="emojione" alt="👌" title=":ok_hand:" src="/emoji/1f44c.svg" /> <img draggable="false" class="emojione" alt="🌈" title=":rainbow:" src="/emoji/1f308.svg" /> <img draggable="false" class="emojione" alt="💕" title=":two_hearts:" src="/emoji/1f495.svg" />');
|
||||
expect(emojify('👌🌈💕')).toEqual('<img draggable="false" class="emojione" alt="👌" title=":ok_hand:" src="/packs/emoji/1f44c.svg" /><img draggable="false" class="emojione" alt="🌈" title=":rainbow:" src="/packs/emoji/1f308.svg" /><img draggable="false" class="emojione" alt="💕" title=":two_hearts:" src="/packs/emoji/1f495.svg" />');
|
||||
expect(emojify('👌 🌈 💕')).toEqual('<img draggable="false" class="emojione" alt="👌" title=":ok_hand:" src="/packs/emoji/1f44c.svg" /> <img draggable="false" class="emojione" alt="🌈" title=":rainbow:" src="/packs/emoji/1f308.svg" /> <img draggable="false" class="emojione" alt="💕" title=":two_hearts:" src="/packs/emoji/1f495.svg" />');
|
||||
});
|
||||
|
||||
it('does an emoji that has no shortcode', () => {
|
||||
expect(emojify('👁🗨')).toEqual('<img draggable="false" class="emojione" alt="👁🗨" title="" src="/emoji/1f441-200d-1f5e8.svg" />');
|
||||
expect(emojify('👁🗨')).toEqual('<img draggable="false" class="emojione" alt="👁🗨" title="" src="/packs/emoji/1f441-200d-1f5e8.svg" />');
|
||||
});
|
||||
|
||||
it('does an emoji whose filename is irregular', () => {
|
||||
expect(emojify('↙️')).toEqual('<img draggable="false" class="emojione" alt="↙️" title=":arrow_lower_left:" src="/emoji/2199.svg" />');
|
||||
expect(emojify('↙️')).toEqual('<img draggable="false" class="emojione" alt="↙️" title=":arrow_lower_left:" src="/packs/emoji/2199.svg" />');
|
||||
});
|
||||
|
||||
it('avoid emojifying on invisible text', () => {
|
||||
@@ -67,16 +67,16 @@ describe('emoji', () => {
|
||||
|
||||
it('avoid emojifying on invisible text with nested tags', () => {
|
||||
expect(emojify('<span class="invisible">😄<span class="foo">bar</span>😴</span>😇'))
|
||||
.toEqual('<span class="invisible">😄<span class="foo">bar</span>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/emoji/1f607.svg" />');
|
||||
.toEqual('<span class="invisible">😄<span class="foo">bar</span>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/packs/emoji/1f607.svg" />');
|
||||
expect(emojify('<span class="invisible">😄<span class="invisible">😕</span>😴</span>😇'))
|
||||
.toEqual('<span class="invisible">😄<span class="invisible">😕</span>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/emoji/1f607.svg" />');
|
||||
.toEqual('<span class="invisible">😄<span class="invisible">😕</span>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/packs/emoji/1f607.svg" />');
|
||||
expect(emojify('<span class="invisible">😄<br/>😴</span>😇'))
|
||||
.toEqual('<span class="invisible">😄<br/>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/emoji/1f607.svg" />');
|
||||
.toEqual('<span class="invisible">😄<br/>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/packs/emoji/1f607.svg" />');
|
||||
});
|
||||
|
||||
it('skips the textual presentation VS15 character', () => {
|
||||
expect(emojify('✴︎')) // This is U+2734 EIGHT POINTED BLACK STAR then U+FE0E VARIATION SELECTOR-15
|
||||
.toEqual('<img draggable="false" class="emojione" alt="✴" title=":eight_pointed_black_star:" src="/emoji/2734.svg" />');
|
||||
.toEqual('<img draggable="false" class="emojione" alt="✴" title=":eight_pointed_black_star:" src="/packs/emoji/2734.svg" />');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import unicodeMapping from './emoji_unicode_mapping_light';
|
||||
import Trie from 'substring-trie';
|
||||
import { join } from 'path';
|
||||
import { FE_SUBDIRECTORY } from 'soapbox/build_config';
|
||||
import { joinPublicPath } from 'soapbox/utils/static';
|
||||
|
||||
const trie = new Trie(Object.keys(unicodeMapping));
|
||||
|
||||
@@ -62,7 +61,8 @@ const emojify = (str, customEmojis = {}, autoplay = false) => {
|
||||
} else { // matched to unicode emoji
|
||||
const { filename, shortCode } = unicodeMapping[match];
|
||||
const title = shortCode ? `:${shortCode}:` : '';
|
||||
replacement = `<img draggable="false" class="emojione" alt="${match}" title="${title}" src="${join(FE_SUBDIRECTORY, 'emoji', `${filename}.svg`)}" />`;
|
||||
const src = joinPublicPath(`packs/emoji/${filename}.svg`);
|
||||
replacement = `<img draggable="false" class="emojione" alt="${match}" title="${title}" src="${src}" />`;
|
||||
rend = i + match.length;
|
||||
// If the matched character was followed by VS15 (for selecting text presentation), skip it.
|
||||
if (str.codePointAt(rend) === 65038) {
|
||||
|
||||
@@ -2,23 +2,55 @@ import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { fetchFavouritedStatuses, expandFavouritedStatuses } from '../../actions/favourites';
|
||||
import { fetchFavouritedStatuses, expandFavouritedStatuses, fetchAccountFavouritedStatuses, expandAccountFavouritedStatuses } from '../../actions/favourites';
|
||||
import Column from '../ui/components/column';
|
||||
import StatusList from '../../components/status_list';
|
||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { debounce } from 'lodash';
|
||||
import MissingIndicator from 'soapbox/components/missing_indicator';
|
||||
import { fetchAccount, fetchAccountByUsername } from '../../actions/accounts';
|
||||
import LoadingIndicator from '../../components/loading_indicator';
|
||||
|
||||
const mapStateToProps = (state, { params }) => {
|
||||
const username = params.username || '';
|
||||
const me = state.get('me');
|
||||
const meUsername = state.getIn(['accounts', me, 'username']);
|
||||
const meUsername = state.getIn(['accounts', me, 'username'], '');
|
||||
|
||||
const isMyAccount = (username.toLowerCase() === meUsername.toLowerCase());
|
||||
|
||||
if (isMyAccount) {
|
||||
return {
|
||||
isMyAccount,
|
||||
statusIds: state.getIn(['status_lists', 'favourites', 'items']),
|
||||
isLoading: state.getIn(['status_lists', 'favourites', 'isLoading'], true),
|
||||
hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
|
||||
};
|
||||
}
|
||||
|
||||
const accounts = state.getIn(['accounts']);
|
||||
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() === username.toLowerCase());
|
||||
|
||||
let accountId = -1;
|
||||
if (accountFetchError) {
|
||||
accountId = null;
|
||||
} else {
|
||||
const account = accounts.find(acct => username.toLowerCase() === acct.getIn(['acct'], '').toLowerCase());
|
||||
accountId = account ? account.getIn(['id'], null) : -1;
|
||||
}
|
||||
|
||||
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false);
|
||||
const unavailable = (me === accountId) ? false : isBlocked;
|
||||
|
||||
return {
|
||||
isMyAccount: (username.toLowerCase() === meUsername.toLowerCase()),
|
||||
statusIds: state.getIn(['status_lists', 'favourites', 'items']),
|
||||
isLoading: state.getIn(['status_lists', 'favourites', 'isLoading'], true),
|
||||
hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
|
||||
isMyAccount,
|
||||
accountId,
|
||||
unavailable,
|
||||
username,
|
||||
isAccount: !!state.getIn(['accounts', accountId]),
|
||||
statusIds: state.getIn(['status_lists', `favourites:${accountId}`, 'items'], []),
|
||||
isLoading: state.getIn(['status_lists', `favourites:${accountId}`, 'isLoading'], true),
|
||||
hasMore: !!state.getIn(['status_lists', `favourites:${accountId}`, 'next']),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -36,17 +68,43 @@ class Favourites extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.props.dispatch(fetchFavouritedStatuses());
|
||||
const { accountId, isMyAccount, username } = this.props;
|
||||
|
||||
if (isMyAccount)
|
||||
this.props.dispatch(fetchFavouritedStatuses());
|
||||
else {
|
||||
if (accountId && accountId !== -1) {
|
||||
this.props.dispatch(fetchAccount(accountId));
|
||||
this.props.dispatch(fetchAccountFavouritedStatuses(accountId));
|
||||
} else {
|
||||
this.props.dispatch(fetchAccountByUsername(username));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { accountId, isMyAccount } = this.props;
|
||||
|
||||
if (!isMyAccount && accountId && accountId !== -1 && (accountId !== prevProps.accountId && accountId)) {
|
||||
this.props.dispatch(fetchAccount(accountId));
|
||||
this.props.dispatch(fetchAccountFavouritedStatuses(accountId));
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = debounce(() => {
|
||||
this.props.dispatch(expandFavouritedStatuses());
|
||||
const { accountId, isMyAccount } = this.props;
|
||||
|
||||
if (isMyAccount) {
|
||||
this.props.dispatch(expandFavouritedStatuses());
|
||||
} else {
|
||||
this.props.dispatch(expandAccountFavouritedStatuses(accountId));
|
||||
}
|
||||
}, 300, { leading: true })
|
||||
|
||||
render() {
|
||||
const { statusIds, hasMore, isLoading, isMyAccount } = this.props;
|
||||
const { statusIds, isLoading, hasMore, isMyAccount, isAccount, accountId, unavailable } = this.props;
|
||||
|
||||
if (!isMyAccount) {
|
||||
if (!isMyAccount && !isAccount && accountId !== -1) {
|
||||
return (
|
||||
<Column>
|
||||
<MissingIndicator />
|
||||
@@ -54,7 +112,27 @@ class Favourites extends ImmutablePureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any liked posts yet. When you like one, it will show up here." />;
|
||||
if (accountId === -1) {
|
||||
return (
|
||||
<Column>
|
||||
<LoadingIndicator />
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
if (unavailable) {
|
||||
return (
|
||||
<Column>
|
||||
<div className='empty-column-indicator'>
|
||||
<FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
const emptyMessage = isMyAccount
|
||||
? <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any liked posts yet. When you like one, it will show up here." />
|
||||
: <FormattedMessage id='empty_column.account_favourited_statuses' defaultMessage="This user doesn't have any liked posts yet." />;
|
||||
|
||||
return (
|
||||
<Column>
|
||||
|
||||
@@ -12,7 +12,7 @@ import MissingIndicator from 'soapbox/components/missing_indicator';
|
||||
const mapStateToProps = (state, { params }) => {
|
||||
const username = params.username || '';
|
||||
const me = state.get('me');
|
||||
const meUsername = state.getIn(['accounts', me, 'username']);
|
||||
const meUsername = state.getIn(['accounts', me, 'username'], '');
|
||||
return {
|
||||
isMyAccount: (username.toLowerCase() === meUsername.toLowerCase()),
|
||||
statusIds: state.getIn(['status_lists', 'pins', 'items']),
|
||||
|
||||
@@ -73,7 +73,7 @@ class Reactions extends ImmutablePureComponent {
|
||||
|
||||
render() {
|
||||
const { params, reactions, accounts, status } = this.props;
|
||||
const { username, statusId, reaction } = params;
|
||||
const { username, statusId } = params;
|
||||
|
||||
const back = `/@${username}/posts/${statusId}`;
|
||||
|
||||
@@ -95,7 +95,6 @@ class Reactions extends ImmutablePureComponent {
|
||||
|
||||
const emptyMessage = <FormattedMessage id='status.reactions.empty' defaultMessage='No one has reacted to this post yet. When someone does, they will show up here.' />;
|
||||
|
||||
console.log(params.reaction);
|
||||
return (
|
||||
<Column back={back}>
|
||||
{
|
||||
|
||||
@@ -51,6 +51,8 @@ const messages = defineMessages({
|
||||
displayFqnLabel: { id: 'soapbox_config.display_fqn_label', defaultMessage: 'Display domain (eg @user@domain) for local accounts.' },
|
||||
greentextLabel: { id: 'soapbox_config.greentext_label', defaultMessage: 'Enable greentext support' },
|
||||
promoPanelIconsLink: { id: 'soapbox_config.hints.promo_panel_icons.link', defaultMessage: 'Soapbox Icons List' },
|
||||
authenticatedProfileLabel: { id: 'soapbox_config.authenticated_profile_label', defaultMessage: 'Profiles require authentication' },
|
||||
authenticatedProfileHint: { id: 'soapbox_config.authenticated_profile_hint', defaultMessage: 'Users must be logged-in to view replies and media on user profiles.' },
|
||||
});
|
||||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||
@@ -279,6 +281,13 @@ class SoapboxConfig extends ImmutablePureComponent {
|
||||
checked={soapbox.get('greentext') === true}
|
||||
onChange={this.handleChange(['greentext'], (e) => e.target.checked)}
|
||||
/>
|
||||
<Checkbox
|
||||
name='authenticatedProfile'
|
||||
label={intl.formatMessage(messages.authenticatedProfileLabel)}
|
||||
hint={intl.formatMessage(messages.authenticatedProfileHint)}
|
||||
checked={soapbox.get('authenticatedProfile') === true}
|
||||
onChange={this.handleChange(['authenticatedProfile'], (e) => e.target.checked)}
|
||||
/>
|
||||
</FieldsGroup>
|
||||
<FieldsGroup>
|
||||
<div className='input with_block_label popup'>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Immutable from 'immutable';
|
||||
import { is, fromJS } from 'immutable';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import punycode from 'punycode';
|
||||
import classnames from 'classnames';
|
||||
@@ -77,7 +77,7 @@ export default class Card extends React.PureComponent {
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!Immutable.is(prevProps.card, this.props.card)) {
|
||||
if (!is(prevProps.card, this.props.card)) {
|
||||
this.setState({ embedded: false });
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ export default class Card extends React.PureComponent {
|
||||
const { card, onOpenMedia } = this.props;
|
||||
|
||||
onOpenMedia(
|
||||
Immutable.fromJS([
|
||||
fromJS([
|
||||
{
|
||||
type: 'image',
|
||||
url: card.get('embed_url'),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Immutable from 'immutable';
|
||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -71,11 +71,11 @@ const makeMapStateToProps = () => {
|
||||
(_, { id }) => id,
|
||||
state => state.getIn(['contexts', 'inReplyTos']),
|
||||
], (statusId, inReplyTos) => {
|
||||
let ancestorsIds = Immutable.OrderedSet();
|
||||
let ancestorsIds = ImmutableOrderedSet();
|
||||
let id = statusId;
|
||||
|
||||
while (id) {
|
||||
ancestorsIds = Immutable.OrderedSet([id]).union(ancestorsIds);
|
||||
ancestorsIds = ImmutableOrderedSet([id]).union(ancestorsIds);
|
||||
id = inReplyTos.get(id);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ const makeMapStateToProps = () => {
|
||||
(_, { id }) => id,
|
||||
state => state.getIn(['contexts', 'replies']),
|
||||
], (statusId, contextReplies) => {
|
||||
let descendantsIds = Immutable.OrderedSet();
|
||||
let descendantsIds = ImmutableOrderedSet();
|
||||
const ids = [statusId];
|
||||
|
||||
while (ids.length > 0) {
|
||||
@@ -109,8 +109,8 @@ const makeMapStateToProps = () => {
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const status = getStatus(state, { id: props.params.statusId });
|
||||
let ancestorsIds = Immutable.List();
|
||||
let descendantsIds = Immutable.List();
|
||||
let ancestorsIds = ImmutableOrderedSet();
|
||||
let descendantsIds = ImmutableOrderedSet();
|
||||
|
||||
if (status) {
|
||||
ancestorsIds = getAncestorsIds(state, { id: state.getIn(['contexts', 'inReplyTos', status.get('id')]) });
|
||||
@@ -146,8 +146,8 @@ class Status extends ImmutablePureComponent {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
status: ImmutablePropTypes.map,
|
||||
ancestorsIds: ImmutablePropTypes.list,
|
||||
descendantsIds: ImmutablePropTypes.list,
|
||||
ancestorsIds: ImmutablePropTypes.orderedSet,
|
||||
descendantsIds: ImmutablePropTypes.orderedSet,
|
||||
intl: PropTypes.object.isRequired,
|
||||
askReplyConfirmation: PropTypes.bool,
|
||||
domain: PropTypes.string,
|
||||
|
||||
@@ -14,13 +14,13 @@ import FocalPointModal from './focal_point_modal';
|
||||
import HotkeysModal from './hotkeys_modal';
|
||||
import ComposeModal from './compose_modal';
|
||||
import UnauthorizedModal from './unauthorized_modal';
|
||||
import CryptoDonateModal from './crypto_donate_modal';
|
||||
import EditFederationModal from './edit_federation_modal';
|
||||
|
||||
import {
|
||||
MuteModal,
|
||||
ReportModal,
|
||||
EmbedModal,
|
||||
CryptoDonateModal,
|
||||
ListEditor,
|
||||
ListAdder,
|
||||
} from '../../../features/ui/util/async-components';
|
||||
@@ -41,7 +41,7 @@ const MODAL_COMPONENTS = {
|
||||
'HOTKEYS': () => Promise.resolve({ default: HotkeysModal }),
|
||||
'COMPOSE': () => Promise.resolve({ default: ComposeModal }),
|
||||
'UNAUTHORIZED': () => Promise.resolve({ default: UnauthorizedModal }),
|
||||
'CRYPTO_DONATE': () => Promise.resolve({ default: CryptoDonateModal }),
|
||||
'CRYPTO_DONATE': CryptoDonateModal,
|
||||
'EDIT_FEDERATION': () => Promise.resolve({ default: EditFederationModal }),
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import VerificationBadge from 'soapbox/components/verification_badge';
|
||||
@@ -13,7 +14,7 @@ import { List as ImmutableList } from 'immutable';
|
||||
import { getAcct, isAdmin, isModerator, isLocal } from 'soapbox/utils/accounts';
|
||||
import { displayFqn } from 'soapbox/utils/state';
|
||||
import classNames from 'classnames';
|
||||
import CryptoAddress from 'soapbox/features/crypto_donate/components/crypto_address';
|
||||
import { CryptoAddress } from 'soapbox/features/ui/util/async-components';
|
||||
|
||||
const TICKER_REGEX = /\$([a-zA-Z]*)/i;
|
||||
|
||||
@@ -143,7 +144,15 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
||||
|
||||
{fields.map((pair, i) =>
|
||||
isTicker(pair.get('name', '')) ? (
|
||||
<CryptoAddress key={i} ticker={getTicker(pair.get('name')).toLowerCase()} address={pair.get('value_plain')} />
|
||||
<BundleContainer fetchComponent={CryptoAddress}>
|
||||
{Component => (
|
||||
<Component
|
||||
key={i}
|
||||
ticker={getTicker(pair.get('name')).toLowerCase()}
|
||||
address={pair.get('value_plain')}
|
||||
/>
|
||||
)}
|
||||
</BundleContainer>
|
||||
) : (
|
||||
<dl className='profile-info-panel-content__fields__item' key={i}>
|
||||
<dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
|
||||
|
||||
@@ -7,6 +7,7 @@ import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import { Switch, withRouter } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types';
|
||||
import NotificationsContainer from './containers/notifications_container';
|
||||
import LoadingBarContainer from './containers/loading_bar_container';
|
||||
@@ -44,6 +45,7 @@ import ProfileHoverCard from 'soapbox/components/profile_hover_card';
|
||||
import { getAccessToken } from 'soapbox/utils/auth';
|
||||
import { getFeatures } from 'soapbox/utils/features';
|
||||
import { fetchCustomEmojis } from 'soapbox/actions/custom_emojis';
|
||||
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||
|
||||
import {
|
||||
Status,
|
||||
@@ -121,6 +123,7 @@ const mapStateToProps = state => {
|
||||
const me = state.get('me');
|
||||
const account = state.getIn(['accounts', me]);
|
||||
const instance = state.get('instance');
|
||||
const soapbox = getSoapboxConfig(state);
|
||||
|
||||
return {
|
||||
dropdownMenuIsOpen: state.getIn(['dropdown_menu', 'openId']) !== null,
|
||||
@@ -129,6 +132,7 @@ const mapStateToProps = state => {
|
||||
me,
|
||||
account,
|
||||
features: getFeatures(instance),
|
||||
soapbox,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -166,6 +170,7 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||
children: PropTypes.node,
|
||||
location: PropTypes.object,
|
||||
onLayoutChange: PropTypes.func.isRequired,
|
||||
soapbox: ImmutablePropTypes.map.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
@@ -194,7 +199,8 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
const { children, soapbox } = this.props;
|
||||
const authenticatedProfile = soapbox.get('authenticatedProfile');
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
@@ -254,10 +260,10 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||
<WrappedRoute path='/mutes' page={DefaultPage} component={Mutes} content={children} />
|
||||
<WrappedRoute path='/filters' page={DefaultPage} component={Filters} content={children} />
|
||||
<WrappedRoute path='/@:username' publicRoute exact component={AccountTimeline} page={ProfilePage} content={children} />
|
||||
<WrappedRoute path='/@:username/with_replies' component={AccountTimeline} page={ProfilePage} content={children} componentParams={{ withReplies: true }} />
|
||||
<WrappedRoute path='/@:username/followers' component={Followers} page={ProfilePage} content={children} />
|
||||
<WrappedRoute path='/@:username/following' component={Following} page={ProfilePage} content={children} />
|
||||
<WrappedRoute path='/@:username/media' component={AccountGallery} page={ProfilePage} content={children} />
|
||||
<WrappedRoute path='/@:username/with_replies' publicRoute={!authenticatedProfile} component={AccountTimeline} page={ProfilePage} content={children} componentParams={{ withReplies: true }} />
|
||||
<WrappedRoute path='/@:username/followers' publicRoute={!authenticatedProfile} component={Followers} page={ProfilePage} content={children} />
|
||||
<WrappedRoute path='/@:username/following' publicRoute={!authenticatedProfile} component={Following} page={ProfilePage} content={children} />
|
||||
<WrappedRoute path='/@:username/media' publicRoute={!authenticatedProfile} component={AccountGallery} page={ProfilePage} content={children} />
|
||||
<WrappedRoute path='/@:username/tagged/:tag' exact component={AccountTimeline} page={ProfilePage} content={children} />
|
||||
<WrappedRoute path='/@:username/favorites' component={FavouritedStatuses} page={ProfilePage} content={children} />
|
||||
<WrappedRoute path='/@:username/pins' component={PinnedStatuses} page={ProfilePage} content={children} />
|
||||
@@ -314,6 +320,7 @@ class UI extends React.PureComponent {
|
||||
streamingUrl: PropTypes.string,
|
||||
account: PropTypes.object,
|
||||
features: PropTypes.object.isRequired,
|
||||
soapbox: ImmutablePropTypes.map.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
@@ -594,7 +601,7 @@ class UI extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { streamingUrl, features } = this.props;
|
||||
const { streamingUrl, features, soapbox } = this.props;
|
||||
const { draggingOver, mobile } = this.state;
|
||||
const { intl, children, location, dropdownMenuIsOpen, me } = this.props;
|
||||
|
||||
@@ -644,7 +651,7 @@ class UI extends React.PureComponent {
|
||||
<HotKeys keyMap={keyMap} handlers={handlers} ref={this.setHotkeysRef} attach={window} focused>
|
||||
<div className={classnames} ref={this.setRef} style={style}>
|
||||
<TabsBar />
|
||||
<SwitchingColumnsArea location={location} onLayoutChange={this.handleLayoutChange}>
|
||||
<SwitchingColumnsArea location={location} onLayoutChange={this.handleLayoutChange} soapbox={soapbox}>
|
||||
{children}
|
||||
</SwitchingColumnsArea>
|
||||
|
||||
|
||||
@@ -250,6 +250,18 @@ export function CryptoDonate() {
|
||||
return import(/* webpackChunkName: "features/crypto_donate" */'../../crypto_donate');
|
||||
}
|
||||
|
||||
export function CryptoDonatePanel() {
|
||||
return import(/* webpackChunkName: "features/crypto_donate" */'../../crypto_donate/components/crypto_donate_panel');
|
||||
}
|
||||
|
||||
export function CryptoAddress() {
|
||||
return import(/* webpackChunkName: "features/crypto_donate" */'../../crypto_donate/components/crypto_address');
|
||||
}
|
||||
|
||||
export function CryptoDonateModal() {
|
||||
return import(/* webpackChunkName: "features/crypto_donate" */'../components/crypto_donate_modal');
|
||||
}
|
||||
|
||||
export function ScheduledStatuses() {
|
||||
return import(/* webpackChunkName: "features/scheduled_statuses" */'../../scheduled_statuses');
|
||||
}
|
||||
@@ -265,3 +277,7 @@ export function FederationRestrictions() {
|
||||
export function Aliases() {
|
||||
return import(/* webpackChunkName: "features/aliases" */'../../aliases');
|
||||
}
|
||||
|
||||
export function ScheduleForm() {
|
||||
return import(/* webpackChunkName: "features/compose" */'../../compose/components/schedule_form');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user