Mastodon: conditionally display Markdown, emojiReact column settings, sidebar features, and account aliases

merge-requests/690/head
Alex Gleason 3 years ago
parent dbb13eabc3
commit be0f252351
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7

@ -9,6 +9,7 @@ export default class TextIconButton extends React.PureComponent {
active: PropTypes.bool,
onClick: PropTypes.func.isRequired,
ariaControls: PropTypes.string,
unavailable: PropTypes.bool,
};
handleClick = (e) => {
@ -17,7 +18,11 @@ export default class TextIconButton extends React.PureComponent {
}
render() {
const { label, title, active, ariaControls } = this.props;
const { label, title, active, ariaControls, unavailable } = this.props;
if (unavailable) {
return null;
}
return (
<button title={title} aria-label={title} className={`text-icon-button ${active ? 'active' : ''}`} aria-expanded={active} onClick={this.handleClick} aria-controls={ariaControls}>

@ -2,18 +2,25 @@ import { connect } from 'react-redux';
import TextIconButton from '../components/text_icon_button';
import { changeComposeContentType } from '../../../actions/compose';
import { injectIntl, defineMessages } from 'react-intl';
import { getFeatures } from 'soapbox/utils/features';
const messages = defineMessages({
marked: { id: 'compose_form.markdown.marked', defaultMessage: 'Post markdown enabled' },
unmarked: { id: 'compose_form.markdown.unmarked', defaultMessage: 'Post markdown disabled' },
});
const mapStateToProps = (state, { intl }) => ({
label: 'MD',
title: intl.formatMessage(state.getIn(['compose', 'content_type']) === 'text/markdown' ? messages.marked : messages.unmarked),
active: state.getIn(['compose', 'content_type']) === 'text/markdown',
ariaControls: 'markdown-input',
});
const mapStateToProps = (state, { intl }) => {
const instance = state.get('instance');
const features = getFeatures(instance);
return {
label: 'MD',
title: intl.formatMessage(state.getIn(['compose', 'content_type']) === 'text/markdown' ? messages.marked : messages.unmarked),
active: state.getIn(['compose', 'content_type']) === 'text/markdown',
ariaControls: 'markdown-input',
unavailable: !features.richText,
};
};
const mapDispatchToProps = dispatch => ({

@ -13,6 +13,7 @@ export default class ColumnSettings extends React.PureComponent {
pushSettings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired,
onClear: PropTypes.func.isRequired,
supportsEmojiReacts: PropTypes.bool,
};
onPushChange = (path, checked) => {
@ -28,7 +29,7 @@ export default class ColumnSettings extends React.PureComponent {
}
render() {
const { settings, pushSettings, onChange, onClear } = this.props;
const { settings, pushSettings, onChange, onClear, supportsEmojiReacts } = this.props;
const filterShowStr = <FormattedMessage id='notifications.column_settings.filter_bar.show' defaultMessage='Show' />;
const filterAdvancedStr = <FormattedMessage id='notifications.column_settings.filter_bar.advanced' defaultMessage='Display all categories' />;
@ -96,7 +97,7 @@ export default class ColumnSettings extends React.PureComponent {
</div>
</div>
<div role='group' aria-labelledby='notifications-emoji-react'>
{supportsEmojiReacts && <div role='group' aria-labelledby='notifications-emoji-react'>
<span id='notifications-favourite' className='column-settings__section'><FormattedMessage id='notifications.column_settings.emoji_react' defaultMessage='Emoji reacts:' /></span>
<div className='column-settings__row'>
@ -105,7 +106,7 @@ export default class ColumnSettings extends React.PureComponent {
<SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'pleroma:emoji_reaction']} onChange={onChange} label={showStr} />
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'pleroma:emoji_reaction']} onChange={onChange} label={soundStr} />
</div>
</div>
</div>}
<div role='group' aria-labelledby='notifications-mention'>
<span id='notifications-mention' className='column-settings__section'><FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' /></span>

@ -20,6 +20,7 @@ class FilterBar extends React.PureComponent {
selectFilter: PropTypes.func.isRequired,
selectedFilter: PropTypes.string.isRequired,
advancedMode: PropTypes.bool.isRequired,
supportsEmojiReacts: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
@ -28,7 +29,7 @@ class FilterBar extends React.PureComponent {
}
render() {
const { selectedFilter, advancedMode, intl } = this.props;
const { selectedFilter, advancedMode, supportsEmojiReacts, intl } = this.props;
const renderedElement = !advancedMode ? (
<div className='notification__filter-bar'>
<button
@ -75,13 +76,13 @@ class FilterBar extends React.PureComponent {
>
<Icon id='thumbs-up' fixedWidth />
</button>
<button
{supportsEmojiReacts && <button
className={selectedFilter === 'pleroma:emoji_reaction' ? 'active' : ''}
onClick={this.onClick('pleroma:emoji_reaction')}
title={intl.formatMessage(tooltips.emoji_reacts)}
>
<Icon id='smile-o' fixedWidth />
</button>
</button>}
<button
className={selectedFilter === 'reblog' ? 'active' : ''}
onClick={this.onClick('reblog')}

@ -6,16 +6,23 @@ import { setFilter } from '../../../actions/notifications';
import { clearNotifications } from '../../../actions/notifications';
import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications';
import { openModal } from '../../../actions/modal';
import { getFeatures } from 'soapbox/utils/features';
const messages = defineMessages({
clearMessage: { id: 'notifications.clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all your notifications?' },
clearConfirm: { id: 'notifications.clear', defaultMessage: 'Clear notifications' },
});
const mapStateToProps = state => ({
settings: getSettings(state).get('notifications'),
pushSettings: state.get('push_notifications'),
});
const mapStateToProps = state => {
const instance = state.get('instance');
const features = getFeatures(instance);
return {
settings: getSettings(state).get('notifications'),
pushSettings: state.get('push_notifications'),
supportsEmojiReacts: features.emojiReacts,
};
};
const mapDispatchToProps = (dispatch, { intl }) => ({

@ -2,12 +2,17 @@ import { connect } from 'react-redux';
import FilterBar from '../components/filter_bar';
import { setFilter } from '../../../actions/notifications';
import { getSettings } from 'soapbox/actions/settings';
import { getFeatures } from 'soapbox/utils/features';
const makeMapStateToProps = state => {
const settings = getSettings(state);
const instance = state.get('instance');
const features = getFeatures(instance);
return {
selectedFilter: settings.getIn(['notifications', 'quickFilter', 'active']),
advancedMode: settings.getIn(['notifications', 'quickFilter', 'advanced']),
supportsEmojiReacts: features.emojiReacts,
};
};

@ -6,6 +6,8 @@ import IconWithCounter from 'soapbox/components/icon_with_counter';
import { NavLink } from 'react-router-dom';
import { injectIntl, defineMessages } from 'react-intl';
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
import { getFeatures } from 'soapbox/utils/features';
import { getBaseURL } from 'soapbox/utils/accounts';
const messages = defineMessages({
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit Profile' },
@ -18,9 +20,16 @@ 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 {
isLocked: state.getIn(['accounts', me, 'locked']),
followRequestsCount: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableOrderedSet()).count(),
baseURL: getBaseURL(account),
features,
};
};
@ -32,10 +41,12 @@ class FeaturesPanel extends React.PureComponent {
intl: PropTypes.object.isRequired,
isLocked: PropTypes.bool,
followRequestsCount: PropTypes.number,
baseURL: PropTypes.string,
features: PropTypes.object.isRequired,
};
render() {
const { intl, isLocked, followRequestsCount } = this.props;
const { intl, isLocked, followRequestsCount, baseURL, features } = this.props;
return (
<div className='wtf-panel promo-panel'>
@ -61,15 +72,29 @@ class FeaturesPanel extends React.PureComponent {
{intl.formatMessage(messages.lists)}
</NavLink>
<NavLink className='promo-panel-item' to='/auth/edit'>
<Icon id='lock' className='promo-panel-item__icon' fixedWidth />
{intl.formatMessage(messages.security)}
</NavLink>
{features.securityAPI ? (
<NavLink className='promo-panel-item' to='/auth/edit'>
<Icon id='lock' className='promo-panel-item__icon' fixedWidth />
{intl.formatMessage(messages.security)}
</NavLink>
) : (
<a className='promo-panel-item' href={`${baseURL}/auth/edit`}>
<Icon id='lock' className='promo-panel-item__icon' fixedWidth />
{intl.formatMessage(messages.security)}
</a>
)}
<NavLink className='promo-panel-item' to='/settings/preferences'>
<Icon id='cog' className='promo-panel-item__icon' fixedWidth />
{intl.formatMessage(messages.preferences)}
</NavLink>
{features.settingsStore ? (
<NavLink className='promo-panel-item' to='/settings/preferences'>
<Icon id='cog' className='promo-panel-item__icon' fixedWidth />
{intl.formatMessage(messages.preferences)}
</NavLink>
) : (
<a className='promo-panel-item' href={`${baseURL}/settings/preferences`}>
<Icon id='cog' className='promo-panel-item__icon' fixedWidth />
{intl.formatMessage(messages.preferences)}
</a>
)}
</div>
</div>

@ -18,6 +18,7 @@ const mapStateToProps = state => {
return {
account: state.getIn(['accounts', me]),
federating: features.federating,
showAliases: features.accountAliasesAPI,
};
};
@ -31,7 +32,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
},
});
const LinkFooter = ({ onOpenHotkeys, account, federating, onClickLogOut }) => (
const LinkFooter = ({ onOpenHotkeys, account, federating, showAliases, onClickLogOut }) => (
<div className='getting-started__footer'>
<ul>
{account && <>
@ -43,7 +44,7 @@ const LinkFooter = ({ onOpenHotkeys, account, federating, onClickLogOut }) => (
{isAdmin(account) && <li><a href='/pleroma/admin'><FormattedMessage id='navigation_bar.admin_settings' defaultMessage='AdminFE' /></a></li>}
{isAdmin(account) && <li><Link to='/soapbox/config'><FormattedMessage id='navigation_bar.soapbox_config' defaultMessage='Soapbox config' /></Link></li>}
<li><Link to='/settings/import'><FormattedMessage id='navigation_bar.import_data' defaultMessage='Import data' /></Link></li>
{federating && <li><Link to='/settings/aliases'><FormattedMessage id='navigation_bar.account_aliases' defaultMessage='Account aliases' /></Link></li>}
{(federating && showAliases) && <li><Link to='/settings/aliases'><FormattedMessage id='navigation_bar.account_aliases' defaultMessage='Account aliases' /></Link></li>}
<li><a href='#' onClick={onOpenHotkeys}><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></a></li>
</>}
<li><Link to='/about'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></Link></li>
@ -67,6 +68,7 @@ const LinkFooter = ({ onOpenHotkeys, account, federating, onClickLogOut }) => (
LinkFooter.propTypes = {
account: ImmutablePropTypes.map,
federating: PropTypes.bool,
showAliases: PropTypes.bool,
onOpenHotkeys: PropTypes.func.isRequired,
onClickLogOut: PropTypes.func.isRequired,
};

@ -21,6 +21,15 @@ export const guessFqn = account => {
return account.get('acct');
};
export const getBaseURL = account => {
try {
const url = account.get('url');
return new URL(url).origin;
} catch {
return '';
}
};
// user@domain even for local users
export const acctFull = account => (
account.get('fqn') || guessFqn(account)

@ -20,6 +20,10 @@ export const getFeatures = createSelector([
chats: v.software === 'Pleroma' && gte(v.version, '2.1.0'),
scopes: v.software === 'Pleroma' ? 'read write follow push admin' : 'read write follow push',
federating: federation.get('enabled', true), // Assume true unless explicitly false
richText: v.software === 'Pleroma',
securityAPI: v.software === 'Pleroma',
settingsStore: v.software === 'Pleroma',
accountAliasesAPI: v.software === 'Pleroma',
};
});

Loading…
Cancel
Save