Single user mode
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
ec5e498068
commit
06d33de47f
|
@ -60,6 +60,8 @@ export const makeDefaultConfig = features => {
|
||||||
}),
|
}),
|
||||||
aboutPages: ImmutableMap(),
|
aboutPages: ImmutableMap(),
|
||||||
authenticatedProfile: true,
|
authenticatedProfile: true,
|
||||||
|
singleUserMode: false,
|
||||||
|
singleUserModeProfile: '',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import React from 'react';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { IntlProvider } from 'react-intl';
|
import { IntlProvider } from 'react-intl';
|
||||||
import { Provider, connect } from 'react-redux';
|
import { Provider, connect } from 'react-redux';
|
||||||
import { Switch, BrowserRouter, Route } from 'react-router-dom';
|
import { BrowserRouter, Switch, Redirect, Route } from 'react-router-dom';
|
||||||
import { ScrollContext } from 'react-router-scroll-4';
|
import { ScrollContext } from 'react-router-scroll-4';
|
||||||
|
|
||||||
// import Introduction from '../features/introduction';
|
// import Introduction from '../features/introduction';
|
||||||
|
@ -66,6 +66,8 @@ const mapStateToProps = (state) => {
|
||||||
const brandColor = settings.get('demo') ? '#0482d8' : soapboxConfig.get('brandColor');
|
const brandColor = settings.get('demo') ? '#0482d8' : soapboxConfig.get('brandColor');
|
||||||
const accentColor = settings.get('demo') ? null : soapboxConfig.get('accentColor');
|
const accentColor = settings.get('demo') ? null : soapboxConfig.get('accentColor');
|
||||||
|
|
||||||
|
const singleUserMode = soapboxConfig.get('singleUserMode') && soapboxConfig.get('singleUserModeProfile');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
showIntroduction,
|
showIntroduction,
|
||||||
me,
|
me,
|
||||||
|
@ -81,6 +83,7 @@ const mapStateToProps = (state) => {
|
||||||
themeMode: settings.get('themeMode'),
|
themeMode: settings.get('themeMode'),
|
||||||
halloween: settings.get('halloween'),
|
halloween: settings.get('halloween'),
|
||||||
customCss: settings.get('demo') ? null : soapboxConfig.get('customCss'),
|
customCss: settings.get('demo') ? null : soapboxConfig.get('customCss'),
|
||||||
|
singleUserMode,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -103,6 +106,7 @@ class SoapboxMount extends React.PureComponent {
|
||||||
customCss: ImmutablePropTypes.list,
|
customCss: ImmutablePropTypes.list,
|
||||||
halloween: PropTypes.bool,
|
halloween: PropTypes.bool,
|
||||||
dispatch: PropTypes.func,
|
dispatch: PropTypes.func,
|
||||||
|
singleUserMode: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -135,7 +139,7 @@ class SoapboxMount extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { me, instanceLoaded, themeCss, locale, customCss } = this.props;
|
const { me, instanceLoaded, themeCss, locale, customCss, singleUserMode } = this.props;
|
||||||
if (me === null) return null;
|
if (me === null) return null;
|
||||||
if (!instanceLoaded) return null;
|
if (!instanceLoaded) return null;
|
||||||
if (this.state.localeLoading) return null;
|
if (this.state.localeLoading) return null;
|
||||||
|
@ -171,7 +175,9 @@ class SoapboxMount extends React.PureComponent {
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
||||||
<Switch>
|
<Switch>
|
||||||
{!me && <Route exact path='/' component={PublicLayout} />}
|
{!me && (singleUserMode
|
||||||
|
? <Redirect exact from='/' to={`/${singleUserMode}`} />
|
||||||
|
: <Route exact path='/' component={PublicLayout} />)}
|
||||||
<Route exact path='/about/:slug?' component={PublicLayout} />
|
<Route exact path='/about/:slug?' component={PublicLayout} />
|
||||||
<Route path='/' component={UI} />
|
<Route path='/' component={UI} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
|
@ -56,6 +56,10 @@ const messages = defineMessages({
|
||||||
promoPanelIconsLink: { id: 'soapbox_config.hints.promo_panel_icons.link', defaultMessage: 'Soapbox Icons List' },
|
promoPanelIconsLink: { id: 'soapbox_config.hints.promo_panel_icons.link', defaultMessage: 'Soapbox Icons List' },
|
||||||
authenticatedProfileLabel: { id: 'soapbox_config.authenticated_profile_label', defaultMessage: 'Profiles require authentication' },
|
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.' },
|
authenticatedProfileHint: { id: 'soapbox_config.authenticated_profile_hint', defaultMessage: 'Users must be logged-in to view replies and media on user profiles.' },
|
||||||
|
singleUserModeLabel: { id: 'soapbox_config.single_user_mode_label', defaultMessage: 'Single user mode' },
|
||||||
|
singleUserModeHint: { id: 'soapbox_config.single_user_mode_hint', defaultMessage: 'Front page will redirect to a given user profile.' },
|
||||||
|
singleUserModeProfileLabel: { id: 'soapbox_config.single_user_mode_profile_label', defaultMessage: 'Main user handle' },
|
||||||
|
singleUserModeProfileHint: { id: 'soapbox_config.single_user_mode_profile_hint', defaultMessage: '@handle' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||||
|
@ -297,6 +301,22 @@ class SoapboxConfig extends ImmutablePureComponent {
|
||||||
checked={soapbox.get('authenticatedProfile') === true}
|
checked={soapbox.get('authenticatedProfile') === true}
|
||||||
onChange={this.handleChange(['authenticatedProfile'], (e) => e.target.checked)}
|
onChange={this.handleChange(['authenticatedProfile'], (e) => e.target.checked)}
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
name='singleUserMode'
|
||||||
|
label={intl.formatMessage(messages.singleUserModeLabel)}
|
||||||
|
hint={intl.formatMessage(messages.singleUserModeHint)}
|
||||||
|
checked={soapbox.get('singleUserMode') === true}
|
||||||
|
onChange={this.handleChange(['singleUserMode'], (e) => e.target.checked)}
|
||||||
|
/>
|
||||||
|
{soapbox.get('singleUserMode') && (
|
||||||
|
<TextInput
|
||||||
|
name='singleUserModeProfile'
|
||||||
|
label={intl.formatMessage(messages.singleUserModeProfileLabel)}
|
||||||
|
placeholder={intl.formatMessage(messages.singleUserModeProfileHint)}
|
||||||
|
value={soapbox.get('singleUserModeProfile')}
|
||||||
|
onChange={this.handleChange(['singleUserModeProfile'], (e) => e.target.value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</FieldsGroup>
|
</FieldsGroup>
|
||||||
<FieldsGroup>
|
<FieldsGroup>
|
||||||
<div className='input with_block_label popup'>
|
<div className='input with_block_label popup'>
|
||||||
|
|
|
@ -3,17 +3,21 @@ import React from 'react';
|
||||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||||
import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types';
|
import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types';
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
|
const soapboxConfig = getSoapboxConfig(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
siteTitle: state.getIn(['instance', 'title']),
|
siteTitle: state.getIn(['instance', 'title']),
|
||||||
me: state.get('me'),
|
me: state.get('me'),
|
||||||
|
singleUserMode: soapboxConfig.get('singleUserMode'),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const SignUpPanel = ({ siteTitle, me }) => {
|
const SignUpPanel = ({ siteTitle, me, singleUserMode }) => {
|
||||||
if (me) return null;
|
if (me || singleUserMode) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='wtf-panel'>
|
<div className='wtf-panel'>
|
||||||
|
@ -39,6 +43,7 @@ const SignUpPanel = ({ siteTitle, me }) => {
|
||||||
SignUpPanel.propTypes = {
|
SignUpPanel.propTypes = {
|
||||||
siteTitle: PropTypes.string,
|
siteTitle: PropTypes.string,
|
||||||
me: SoapboxPropTypes.me,
|
me: SoapboxPropTypes.me,
|
||||||
|
singleUserMode: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default injectIntl(connect(mapStateToProps)(SignUpPanel));
|
export default injectIntl(connect(mapStateToProps)(SignUpPanel));
|
||||||
|
|
|
@ -38,6 +38,7 @@ class TabsBar extends React.PureComponent {
|
||||||
dashboardCount: PropTypes.number,
|
dashboardCount: PropTypes.number,
|
||||||
notificationCount: PropTypes.number,
|
notificationCount: PropTypes.number,
|
||||||
chatsCount: PropTypes.number,
|
chatsCount: PropTypes.number,
|
||||||
|
singleUserMode: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -67,7 +68,7 @@ class TabsBar extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { intl, account, logo, onOpenCompose, onOpenSidebar, features, dashboardCount, notificationCount, chatsCount } = this.props;
|
const { intl, account, logo, onOpenCompose, onOpenSidebar, features, dashboardCount, notificationCount, chatsCount, singleUserMode } = this.props;
|
||||||
const { collapsed } = this.state;
|
const { collapsed } = this.state;
|
||||||
const showLinks = this.shouldShowLinks();
|
const showLinks = this.shouldShowLinks();
|
||||||
|
|
||||||
|
@ -151,9 +152,11 @@ class TabsBar extends React.PureComponent {
|
||||||
<Link className='tabs-bar__button button' to='/auth/sign_in'>
|
<Link className='tabs-bar__button button' to='/auth/sign_in'>
|
||||||
<FormattedMessage id='account.login' defaultMessage='Log In' />
|
<FormattedMessage id='account.login' defaultMessage='Log In' />
|
||||||
</Link>
|
</Link>
|
||||||
<Link className='tabs-bar__button button button-alternative-2' to='/'>
|
{!singleUserMode && (
|
||||||
<FormattedMessage id='account.register' defaultMessage='Sign up' />
|
<Link className='tabs-bar__button button button-alternative-2' to='/'>
|
||||||
</Link>
|
<FormattedMessage id='account.register' defaultMessage='Sign up' />
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -170,6 +173,7 @@ const mapStateToProps = state => {
|
||||||
const approvalCount = state.getIn(['admin', 'awaitingApproval']).count();
|
const approvalCount = state.getIn(['admin', 'awaitingApproval']).count();
|
||||||
const instance = state.get('instance');
|
const instance = state.get('instance');
|
||||||
const settings = getSettings(state);
|
const settings = getSettings(state);
|
||||||
|
const soapboxConfig = getSoapboxConfig(state);
|
||||||
|
|
||||||
// In demo mode, use the Soapbox logo
|
// In demo mode, use the Soapbox logo
|
||||||
const logo = settings.get('demo') ? require('images/soapbox-logo.svg') : getSoapboxConfig(state).get('logo');
|
const logo = settings.get('demo') ? require('images/soapbox-logo.svg') : getSoapboxConfig(state).get('logo');
|
||||||
|
@ -181,6 +185,7 @@ const mapStateToProps = state => {
|
||||||
notificationCount: state.getIn(['notifications', 'unread']),
|
notificationCount: state.getIn(['notifications', 'unread']),
|
||||||
chatsCount: state.getIn(['chats', 'items']).reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0),
|
chatsCount: state.getIn(['chats', 'items']).reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0),
|
||||||
dashboardCount: reportsCount + approvalCount,
|
dashboardCount: reportsCount + approvalCount,
|
||||||
|
singleUserMode: soapboxConfig.get('singleUserMode'),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { remoteInteraction } from 'soapbox/actions/interactions';
|
import { remoteInteraction } from 'soapbox/actions/interactions';
|
||||||
import snackbar from 'soapbox/actions/snackbar';
|
import snackbar from 'soapbox/actions/snackbar';
|
||||||
|
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||||
import IconButton from 'soapbox/components/icon_button';
|
import IconButton from 'soapbox/components/icon_button';
|
||||||
import { getFeatures } from 'soapbox/utils/features';
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
|
|
||||||
|
@ -19,12 +20,14 @@ const messages = defineMessages({
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
const instance = state.get('instance');
|
const instance = state.get('instance');
|
||||||
const features = getFeatures(instance);
|
const features = getFeatures(instance);
|
||||||
|
const soapboxConfig = getSoapboxConfig(state);
|
||||||
|
|
||||||
if (props.action !== 'FOLLOW') {
|
if (props.action !== 'FOLLOW') {
|
||||||
return {
|
return {
|
||||||
features,
|
features,
|
||||||
siteTitle: state.getIn(['instance', 'title']),
|
siteTitle: state.getIn(['instance', 'title']),
|
||||||
remoteInteractionsAPI: features.remoteInteractionsAPI,
|
remoteInteractionsAPI: features.remoteInteractionsAPI,
|
||||||
|
singleUserMode: soapboxConfig.get('singleUserMode'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +38,7 @@ const mapStateToProps = (state, props) => {
|
||||||
siteTitle: state.getIn(['instance', 'title']),
|
siteTitle: state.getIn(['instance', 'title']),
|
||||||
userName,
|
userName,
|
||||||
remoteInteractionsAPI: features.remoteInteractionsAPI,
|
remoteInteractionsAPI: features.remoteInteractionsAPI,
|
||||||
|
singleUserMode: soapboxConfig.get('singleUserMode'),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,6 +57,7 @@ class UnauthorizedModal extends ImmutablePureComponent {
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
onRemoteInteraction: PropTypes.func.isRequired,
|
onRemoteInteraction: PropTypes.func.isRequired,
|
||||||
userName: PropTypes.string,
|
userName: PropTypes.string,
|
||||||
|
singleUserMode: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -86,7 +91,7 @@ class UnauthorizedModal extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderRemoteInteractions() {
|
renderRemoteInteractions() {
|
||||||
const { intl, siteTitle, userName, action } = this.props;
|
const { intl, siteTitle, userName, action, singleUserMode } = this.props;
|
||||||
const { account } = this.state;
|
const { account } = this.state;
|
||||||
|
|
||||||
let header;
|
let header;
|
||||||
|
@ -134,10 +139,14 @@ class UnauthorizedModal extends ImmutablePureComponent {
|
||||||
<FormattedMessage id='remote_interaction.divider' defaultMessage='or' />
|
<FormattedMessage id='remote_interaction.divider' defaultMessage='or' />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<h3 className='compose-modal__header__title'><FormattedMessage id='unauthorized_modal.title' defaultMessage='Sign up for {site_title}' values={{ site_title: siteTitle }} /></h3>
|
{!singleUserMode && (
|
||||||
<Link to='/' className='unauthorized-modal-content__button button' onClick={this.onClickClose}>
|
<>
|
||||||
<FormattedMessage id='account.register' defaultMessage='Sign up' />
|
<h3 className='compose-modal__header__title'><FormattedMessage id='unauthorized_modal.title' defaultMessage='Sign up for {site_title}' values={{ site_title: siteTitle }} /></h3>
|
||||||
</Link>
|
<Link to='/' className='unauthorized-modal-content__button button' onClick={this.onClickClose}>
|
||||||
|
<FormattedMessage id='account.register' defaultMessage='Sign up' />
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Link to='/auth/sign_in' className='unauthorized-modal-content__button button button-secondary' onClick={this.onClickClose}>
|
<Link to='/auth/sign_in' className='unauthorized-modal-content__button button button-secondary' onClick={this.onClickClose}>
|
||||||
<FormattedMessage id='account.login' defaultMessage='Log in' />
|
<FormattedMessage id='account.login' defaultMessage='Log in' />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
Loading…
Reference in New Issue