sforkowany z mirror/soapbox
Mastodon: conditionally display Markdown, emojiReact column settings, sidebar features, and account aliases
rodzic
dbb13eabc3
commit
be0f252351
|
@ -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',
|
||||
};
|
||||
});
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue