Mastodon: conditionally display Markdown, emojiReact column settings, sidebar features, and account aliases
This commit is contained in:
parent
dbb13eabc3
commit
be0f252351
|
@ -9,6 +9,7 @@ export default class TextIconButton extends React.PureComponent {
|
||||||
active: PropTypes.bool,
|
active: PropTypes.bool,
|
||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
ariaControls: PropTypes.string,
|
ariaControls: PropTypes.string,
|
||||||
|
unavailable: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = (e) => {
|
handleClick = (e) => {
|
||||||
|
@ -17,7 +18,11 @@ export default class TextIconButton extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { label, title, active, ariaControls } = this.props;
|
const { label, title, active, ariaControls, unavailable } = this.props;
|
||||||
|
|
||||||
|
if (unavailable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button title={title} aria-label={title} className={`text-icon-button ${active ? 'active' : ''}`} aria-expanded={active} onClick={this.handleClick} aria-controls={ariaControls}>
|
<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 TextIconButton from '../components/text_icon_button';
|
||||||
import { changeComposeContentType } from '../../../actions/compose';
|
import { changeComposeContentType } from '../../../actions/compose';
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { injectIntl, defineMessages } from 'react-intl';
|
||||||
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
marked: { id: 'compose_form.markdown.marked', defaultMessage: 'Post markdown enabled' },
|
marked: { id: 'compose_form.markdown.marked', defaultMessage: 'Post markdown enabled' },
|
||||||
unmarked: { id: 'compose_form.markdown.unmarked', defaultMessage: 'Post markdown disabled' },
|
unmarked: { id: 'compose_form.markdown.unmarked', defaultMessage: 'Post markdown disabled' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = (state, { intl }) => ({
|
const mapStateToProps = (state, { intl }) => {
|
||||||
label: 'MD',
|
const instance = state.get('instance');
|
||||||
title: intl.formatMessage(state.getIn(['compose', 'content_type']) === 'text/markdown' ? messages.marked : messages.unmarked),
|
const features = getFeatures(instance);
|
||||||
active: state.getIn(['compose', 'content_type']) === 'text/markdown',
|
|
||||||
ariaControls: 'markdown-input',
|
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 => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ export default class ColumnSettings extends React.PureComponent {
|
||||||
pushSettings: ImmutablePropTypes.map.isRequired,
|
pushSettings: ImmutablePropTypes.map.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
onClear: PropTypes.func.isRequired,
|
onClear: PropTypes.func.isRequired,
|
||||||
|
supportsEmojiReacts: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
onPushChange = (path, checked) => {
|
onPushChange = (path, checked) => {
|
||||||
|
@ -28,7 +29,7 @@ export default class ColumnSettings extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
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 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' />;
|
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>
|
</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>
|
<span id='notifications-favourite' className='column-settings__section'><FormattedMessage id='notifications.column_settings.emoji_react' defaultMessage='Emoji reacts:' /></span>
|
||||||
|
|
||||||
<div className='column-settings__row'>
|
<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={['shows', 'pleroma:emoji_reaction']} onChange={onChange} label={showStr} />
|
||||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'pleroma:emoji_reaction']} onChange={onChange} label={soundStr} />
|
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'pleroma:emoji_reaction']} onChange={onChange} label={soundStr} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>}
|
||||||
|
|
||||||
<div role='group' aria-labelledby='notifications-mention'>
|
<div role='group' aria-labelledby='notifications-mention'>
|
||||||
<span id='notifications-mention' className='column-settings__section'><FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' /></span>
|
<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,
|
selectFilter: PropTypes.func.isRequired,
|
||||||
selectedFilter: PropTypes.string.isRequired,
|
selectedFilter: PropTypes.string.isRequired,
|
||||||
advancedMode: PropTypes.bool.isRequired,
|
advancedMode: PropTypes.bool.isRequired,
|
||||||
|
supportsEmojiReacts: PropTypes.bool,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ class FilterBar extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { selectedFilter, advancedMode, intl } = this.props;
|
const { selectedFilter, advancedMode, supportsEmojiReacts, intl } = this.props;
|
||||||
const renderedElement = !advancedMode ? (
|
const renderedElement = !advancedMode ? (
|
||||||
<div className='notification__filter-bar'>
|
<div className='notification__filter-bar'>
|
||||||
<button
|
<button
|
||||||
|
@ -75,13 +76,13 @@ class FilterBar extends React.PureComponent {
|
||||||
>
|
>
|
||||||
<Icon id='thumbs-up' fixedWidth />
|
<Icon id='thumbs-up' fixedWidth />
|
||||||
</button>
|
</button>
|
||||||
<button
|
{supportsEmojiReacts && <button
|
||||||
className={selectedFilter === 'pleroma:emoji_reaction' ? 'active' : ''}
|
className={selectedFilter === 'pleroma:emoji_reaction' ? 'active' : ''}
|
||||||
onClick={this.onClick('pleroma:emoji_reaction')}
|
onClick={this.onClick('pleroma:emoji_reaction')}
|
||||||
title={intl.formatMessage(tooltips.emoji_reacts)}
|
title={intl.formatMessage(tooltips.emoji_reacts)}
|
||||||
>
|
>
|
||||||
<Icon id='smile-o' fixedWidth />
|
<Icon id='smile-o' fixedWidth />
|
||||||
</button>
|
</button>}
|
||||||
<button
|
<button
|
||||||
className={selectedFilter === 'reblog' ? 'active' : ''}
|
className={selectedFilter === 'reblog' ? 'active' : ''}
|
||||||
onClick={this.onClick('reblog')}
|
onClick={this.onClick('reblog')}
|
||||||
|
|
|
@ -6,16 +6,23 @@ import { setFilter } from '../../../actions/notifications';
|
||||||
import { clearNotifications } from '../../../actions/notifications';
|
import { clearNotifications } from '../../../actions/notifications';
|
||||||
import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications';
|
import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications';
|
||||||
import { openModal } from '../../../actions/modal';
|
import { openModal } from '../../../actions/modal';
|
||||||
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
clearMessage: { id: 'notifications.clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all your notifications?' },
|
clearMessage: { id: 'notifications.clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all your notifications?' },
|
||||||
clearConfirm: { id: 'notifications.clear', defaultMessage: 'Clear notifications' },
|
clearConfirm: { id: 'notifications.clear', defaultMessage: 'Clear notifications' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => {
|
||||||
settings: getSettings(state).get('notifications'),
|
const instance = state.get('instance');
|
||||||
pushSettings: state.get('push_notifications'),
|
const features = getFeatures(instance);
|
||||||
});
|
|
||||||
|
return {
|
||||||
|
settings: getSettings(state).get('notifications'),
|
||||||
|
pushSettings: state.get('push_notifications'),
|
||||||
|
supportsEmojiReacts: features.emojiReacts,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,17 @@ import { connect } from 'react-redux';
|
||||||
import FilterBar from '../components/filter_bar';
|
import FilterBar from '../components/filter_bar';
|
||||||
import { setFilter } from '../../../actions/notifications';
|
import { setFilter } from '../../../actions/notifications';
|
||||||
import { getSettings } from 'soapbox/actions/settings';
|
import { getSettings } from 'soapbox/actions/settings';
|
||||||
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
|
||||||
const makeMapStateToProps = state => {
|
const makeMapStateToProps = state => {
|
||||||
const settings = getSettings(state);
|
const settings = getSettings(state);
|
||||||
|
const instance = state.get('instance');
|
||||||
|
const features = getFeatures(instance);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectedFilter: settings.getIn(['notifications', 'quickFilter', 'active']),
|
selectedFilter: settings.getIn(['notifications', 'quickFilter', 'active']),
|
||||||
advancedMode: settings.getIn(['notifications', 'quickFilter', 'advanced']),
|
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 { NavLink } from 'react-router-dom';
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { injectIntl, defineMessages } from 'react-intl';
|
||||||
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||||
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
import { getBaseURL } from 'soapbox/utils/accounts';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit Profile' },
|
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit Profile' },
|
||||||
|
@ -18,9 +20,16 @@ const messages = defineMessages({
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
const me = state.get('me');
|
const me = state.get('me');
|
||||||
|
const account = state.getIn(['accounts', me]);
|
||||||
|
|
||||||
|
const instance = state.get('instance');
|
||||||
|
const features = getFeatures(instance);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isLocked: state.getIn(['accounts', me, 'locked']),
|
isLocked: state.getIn(['accounts', me, 'locked']),
|
||||||
followRequestsCount: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableOrderedSet()).count(),
|
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,
|
intl: PropTypes.object.isRequired,
|
||||||
isLocked: PropTypes.bool,
|
isLocked: PropTypes.bool,
|
||||||
followRequestsCount: PropTypes.number,
|
followRequestsCount: PropTypes.number,
|
||||||
|
baseURL: PropTypes.string,
|
||||||
|
features: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { intl, isLocked, followRequestsCount } = this.props;
|
const { intl, isLocked, followRequestsCount, baseURL, features } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='wtf-panel promo-panel'>
|
<div className='wtf-panel promo-panel'>
|
||||||
|
@ -61,15 +72,29 @@ class FeaturesPanel extends React.PureComponent {
|
||||||
{intl.formatMessage(messages.lists)}
|
{intl.formatMessage(messages.lists)}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<NavLink className='promo-panel-item' to='/auth/edit'>
|
{features.securityAPI ? (
|
||||||
<Icon id='lock' className='promo-panel-item__icon' fixedWidth />
|
<NavLink className='promo-panel-item' to='/auth/edit'>
|
||||||
{intl.formatMessage(messages.security)}
|
<Icon id='lock' className='promo-panel-item__icon' fixedWidth />
|
||||||
</NavLink>
|
{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'>
|
{features.settingsStore ? (
|
||||||
<Icon id='cog' className='promo-panel-item__icon' fixedWidth />
|
<NavLink className='promo-panel-item' to='/settings/preferences'>
|
||||||
{intl.formatMessage(messages.preferences)}
|
<Icon id='cog' className='promo-panel-item__icon' fixedWidth />
|
||||||
</NavLink>
|
{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>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,6 +18,7 @@ const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
account: state.getIn(['accounts', me]),
|
account: state.getIn(['accounts', me]),
|
||||||
federating: features.federating,
|
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'>
|
<div className='getting-started__footer'>
|
||||||
<ul>
|
<ul>
|
||||||
{account && <>
|
{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><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>}
|
{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>
|
<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><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>
|
<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 = {
|
LinkFooter.propTypes = {
|
||||||
account: ImmutablePropTypes.map,
|
account: ImmutablePropTypes.map,
|
||||||
federating: PropTypes.bool,
|
federating: PropTypes.bool,
|
||||||
|
showAliases: PropTypes.bool,
|
||||||
onOpenHotkeys: PropTypes.func.isRequired,
|
onOpenHotkeys: PropTypes.func.isRequired,
|
||||||
onClickLogOut: PropTypes.func.isRequired,
|
onClickLogOut: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,15 @@ export const guessFqn = account => {
|
||||||
return account.get('acct');
|
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
|
// user@domain even for local users
|
||||||
export const acctFull = account => (
|
export const acctFull = account => (
|
||||||
account.get('fqn') || guessFqn(account)
|
account.get('fqn') || guessFqn(account)
|
||||||
|
|
|
@ -20,6 +20,10 @@ export const getFeatures = createSelector([
|
||||||
chats: v.software === 'Pleroma' && gte(v.version, '2.1.0'),
|
chats: v.software === 'Pleroma' && gte(v.version, '2.1.0'),
|
||||||
scopes: v.software === 'Pleroma' ? 'read write follow push admin' : 'read write follow push',
|
scopes: v.software === 'Pleroma' ? 'read write follow push admin' : 'read write follow push',
|
||||||
federating: federation.get('enabled', true), // Assume true unless explicitly false
|
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…
Reference in New Issue