Remove singleUserMode, upgrade to redirectRootNoLogin
This commit is contained in:
parent
1e07c03479
commit
6f0e398a78
9 changed files with 70 additions and 47 deletions
|
@ -96,7 +96,7 @@ const SoapboxMount = () => {
|
|||
const waitlisted = account && !account.source.get('approved', true);
|
||||
const needsOnboarding = useAppSelector(state => state.onboarding.needsOnboarding);
|
||||
const showOnboarding = account && !waitlisted && needsOnboarding;
|
||||
const singleUserMode = soapboxConfig.singleUserMode && soapboxConfig.singleUserModeProfile;
|
||||
const { redirectRootNoLogin } = soapboxConfig;
|
||||
|
||||
const pepeEnabled = soapboxConfig.getIn(['extensions', 'pepe', 'enabled']) === true;
|
||||
|
||||
|
@ -134,8 +134,8 @@ const SoapboxMount = () => {
|
|||
/>
|
||||
)}
|
||||
|
||||
{!me && (singleUserMode
|
||||
? <Redirect exact from='/' to={`/${singleUserMode}`} />
|
||||
{!me && (redirectRootNoLogin
|
||||
? <Redirect exact from='/' to={redirectRootNoLogin} />
|
||||
: <Route exact path='/' component={PublicLayout} />)}
|
||||
|
||||
{!me && (
|
||||
|
|
|
@ -47,10 +47,6 @@ const messages = defineMessages({
|
|||
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.' },
|
||||
displayCtaLabel: { id: 'soapbox_config.cta_label', defaultMessage: 'Display call to action panels if not authenticated' },
|
||||
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' },
|
||||
mediaPreviewLabel: { id: 'soapbox_config.media_preview_label', defaultMessage: 'Prefer preview media for thumbnails' },
|
||||
mediaPreviewHint: { id: 'soapbox_config.media_preview_hint', defaultMessage: 'Some backends provide an optimized version of media for display in timelines. However, these preview images may be too small without additional configuration.' },
|
||||
feedInjectionLabel: { id: 'soapbox_config.feed_injection_label', defaultMessage: 'Feed injection' },
|
||||
|
@ -283,27 +279,6 @@ const SoapboxConfig: React.FC = () => {
|
|||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
label={intl.formatMessage(messages.singleUserModeLabel)}
|
||||
hint={intl.formatMessage(messages.singleUserModeHint)}
|
||||
>
|
||||
<Toggle
|
||||
checked={soapbox.singleUserMode === true}
|
||||
onChange={handleChange(['singleUserMode'], (e) => e.target.checked)}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
{soapbox.get('singleUserMode') && (
|
||||
<ListItem label={intl.formatMessage(messages.singleUserModeProfileLabel)}>
|
||||
<Input
|
||||
type='text'
|
||||
placeholder={intl.formatMessage(messages.singleUserModeProfileHint)}
|
||||
value={soapbox.singleUserModeProfile}
|
||||
onChange={handleChange(['singleUserModeProfile'], (e) => e.target.value)}
|
||||
/>
|
||||
</ListItem>
|
||||
)}
|
||||
|
||||
<ListItem
|
||||
label={intl.formatMessage(messages.redirectRootNoLoginLabel)}
|
||||
hint={intl.formatMessage(messages.redirectRootNoLoginHint)}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Map as ImmutableMap } from 'immutable';
|
||||
import React from 'react';
|
||||
|
||||
import { normalizeInstance } from 'soapbox/normalizers';
|
||||
|
||||
import { render, screen } from '../../../../jest/test-helpers';
|
||||
import CtaBanner from '../cta-banner';
|
||||
|
||||
|
@ -19,9 +20,9 @@ describe('<CtaBanner />', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('with singleUserMode enabled', () => {
|
||||
describe('with registrations closed', () => {
|
||||
it('renders empty', () => {
|
||||
const store = { soapbox: ImmutableMap({ singleUserMode: true }) };
|
||||
const store = { instance: normalizeInstance({ registrations: false }) };
|
||||
|
||||
render(<CtaBanner />, undefined, store);
|
||||
expect(screen.queryAllByTestId('cta-banner')).toHaveLength(0);
|
||||
|
|
|
@ -6,10 +6,10 @@ import { useAppSelector, useInstance, useSoapboxConfig } from 'soapbox/hooks';
|
|||
|
||||
const CtaBanner = () => {
|
||||
const instance = useInstance();
|
||||
const { displayCta, singleUserMode } = useSoapboxConfig();
|
||||
const { displayCta } = useSoapboxConfig();
|
||||
const me = useAppSelector((state) => state.me);
|
||||
|
||||
if (me || !displayCta || singleUserMode) return null;
|
||||
if (me || !displayCta || !instance.registrations) return null;
|
||||
|
||||
return (
|
||||
<div data-testid='cta-banner' className='hidden lg:block'>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useHistory } from 'react-router-dom';
|
|||
|
||||
import { remoteInteraction } from 'soapbox/actions/interactions';
|
||||
import { Button, Modal, Stack, Text } from 'soapbox/components/ui';
|
||||
import { useAppSelector, useAppDispatch, useFeatures, useSoapboxConfig, useInstance } from 'soapbox/hooks';
|
||||
import { useAppSelector, useAppDispatch, useFeatures, useInstance } from 'soapbox/hooks';
|
||||
import toast from 'soapbox/toast';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -31,7 +31,6 @@ const UnauthorizedModal: React.FC<IUnauthorizedModal> = ({ action, onClose, acco
|
|||
const dispatch = useAppDispatch();
|
||||
const instance = useInstance();
|
||||
|
||||
const { singleUserMode } = useSoapboxConfig();
|
||||
const username = useAppSelector(state => state.accounts.get(accountId)?.display_name);
|
||||
const features = useFeatures();
|
||||
|
||||
|
@ -98,7 +97,7 @@ const UnauthorizedModal: React.FC<IUnauthorizedModal> = ({ action, onClose, acco
|
|||
<Modal
|
||||
title={header}
|
||||
onClose={onClickClose}
|
||||
confirmationAction={!singleUserMode ? onLogin : undefined}
|
||||
confirmationAction={instance.registrations ? onLogin : undefined}
|
||||
confirmationText={<FormattedMessage id='account.login' defaultMessage='Log in' />}
|
||||
secondaryAction={onRegister}
|
||||
secondaryText={<FormattedMessage id='account.register' defaultMessage='Sign up' />}
|
||||
|
@ -122,7 +121,7 @@ const UnauthorizedModal: React.FC<IUnauthorizedModal> = ({ action, onClose, acco
|
|||
<FormattedMessage id='remote_interaction.divider' defaultMessage='or' />
|
||||
</Text>
|
||||
</div>
|
||||
{!singleUserMode && (
|
||||
{instance.registrations && (
|
||||
<Text size='lg' weight='medium'>
|
||||
<FormattedMessage id='unauthorized_modal.title' defaultMessage='Sign up for {site_title}' values={{ site_title: instance.title }} />
|
||||
</Text>
|
||||
|
|
|
@ -9,7 +9,7 @@ import { openSidebar } from 'soapbox/actions/sidebar';
|
|||
import SiteLogo from 'soapbox/components/site-logo';
|
||||
import { Avatar, Button, Form, HStack, IconButton, Input, Tooltip } from 'soapbox/components/ui';
|
||||
import Search from 'soapbox/features/compose/components/search';
|
||||
import { useAppDispatch, useOwnAccount, useSoapboxConfig } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useInstance, useOwnAccount } from 'soapbox/hooks';
|
||||
|
||||
import ProfileDropdown from './profile-dropdown';
|
||||
|
||||
|
@ -29,8 +29,7 @@ const Navbar = () => {
|
|||
const node = useRef(null);
|
||||
|
||||
const account = useOwnAccount();
|
||||
const soapboxConfig = useSoapboxConfig();
|
||||
const singleUserMode = soapboxConfig.get('singleUserMode');
|
||||
const instance = useInstance();
|
||||
|
||||
const [isLoading, setLoading] = useState<boolean>(false);
|
||||
const [username, setUsername] = useState<string>('');
|
||||
|
@ -151,7 +150,7 @@ const Navbar = () => {
|
|||
<FormattedMessage id='account.login' defaultMessage='Log In' />
|
||||
</Button>
|
||||
|
||||
{!singleUserMode && (
|
||||
{!instance.registrations && (
|
||||
<Button theme='primary' to='/signup' size='sm'>
|
||||
<FormattedMessage id='account.register' defaultMessage='Sign up' />
|
||||
</Button>
|
||||
|
|
|
@ -2,14 +2,13 @@ import React from 'react';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Button, Stack, Text } from 'soapbox/components/ui';
|
||||
import { useAppSelector, useInstance, useSoapboxConfig } from 'soapbox/hooks';
|
||||
import { useAppSelector, useInstance } from 'soapbox/hooks';
|
||||
|
||||
const SignUpPanel = () => {
|
||||
const instance = useInstance();
|
||||
const { singleUserMode } = useSoapboxConfig();
|
||||
const me = useAppSelector((state) => state.me);
|
||||
|
||||
if (me || singleUserMode) return null;
|
||||
if (me || !instance.registrations) return null;
|
||||
|
||||
return (
|
||||
<Stack space={2}>
|
||||
|
|
|
@ -34,4 +34,15 @@ describe('normalizeSoapboxConfig()', () => {
|
|||
expect(ImmutableRecord.isRecord(result.promoPanel.items.get(0))).toBe(true);
|
||||
expect(result.promoPanel.items.get(2)?.icon).toBe('question-circle');
|
||||
});
|
||||
|
||||
it('upgrades singleUserModeProfile to redirectRootNoLogin', () => {
|
||||
expect(normalizeSoapboxConfig({ singleUserMode: true, singleUserModeProfile: 'alex' }).redirectRootNoLogin).toBe('/@alex');
|
||||
expect(normalizeSoapboxConfig({ singleUserMode: false, singleUserModeProfile: 'alex' }).redirectRootNoLogin).toBe('');
|
||||
});
|
||||
|
||||
it('normalizes redirectRootNoLogin', () => {
|
||||
expect(normalizeSoapboxConfig({ redirectRootNoLogin: 'benis' }).redirectRootNoLogin).toBe('/benis');
|
||||
expect(normalizeSoapboxConfig({ redirectRootNoLogin: '/benis' }).redirectRootNoLogin).toBe('/benis');
|
||||
expect(normalizeSoapboxConfig({ redirectRootNoLogin: '/' }).redirectRootNoLogin).toBe('');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -106,8 +106,6 @@ export const SoapboxConfigRecord = ImmutableRecord({
|
|||
}),
|
||||
aboutPages: ImmutableMap<string, ImmutableMap<string, unknown>>(),
|
||||
authenticatedProfile: true,
|
||||
singleUserMode: false,
|
||||
singleUserModeProfile: '',
|
||||
linkFooterMessage: '',
|
||||
links: ImmutableMap<string, string>(),
|
||||
displayCta: true,
|
||||
|
@ -115,7 +113,7 @@ export const SoapboxConfigRecord = ImmutableRecord({
|
|||
feedInjection: true,
|
||||
tileServer: '',
|
||||
tileServerAttribution: '',
|
||||
redirectRootNoLogin: '/',
|
||||
redirectRootNoLogin: '',
|
||||
/**
|
||||
* Whether to use the preview URL for media thumbnails.
|
||||
* On some platforms this can be too blurry without additional configuration.
|
||||
|
@ -198,6 +196,45 @@ const normalizeAdsAlgorithm = (soapboxConfig: SoapboxConfigMap): SoapboxConfigMa
|
|||
}
|
||||
};
|
||||
|
||||
/** Single user mode is now managed by `redirectRootNoLogin`. */
|
||||
const upgradeSingleUserMode = (soapboxConfig: SoapboxConfigMap): SoapboxConfigMap => {
|
||||
const singleUserMode = soapboxConfig.get('singleUserMode');
|
||||
const singleUserModeProfile = soapboxConfig.get('singleUserModeProfile');
|
||||
const redirectRootNoLogin = soapboxConfig.get('redirectRootNoLogin');
|
||||
|
||||
if (!redirectRootNoLogin && singleUserMode && singleUserModeProfile) {
|
||||
return soapboxConfig
|
||||
.set('redirectRootNoLogin', `/@${singleUserModeProfile}`)
|
||||
.deleteAll(['singleUserMode', 'singleUserModeProfile']);
|
||||
} else {
|
||||
return soapboxConfig
|
||||
.deleteAll(['singleUserMode', 'singleUserModeProfile']);
|
||||
}
|
||||
};
|
||||
|
||||
/** Ensure a valid path is used. */
|
||||
const normalizeRedirectRootNoLogin = (soapboxConfig: SoapboxConfigMap): SoapboxConfigMap => {
|
||||
const redirectRootNoLogin = soapboxConfig.get('redirectRootNoLogin');
|
||||
|
||||
if (!redirectRootNoLogin) return soapboxConfig;
|
||||
|
||||
try {
|
||||
// Basically just get the pathname with a leading slash.
|
||||
const normalized = new URL(redirectRootNoLogin, 'a://').pathname;
|
||||
|
||||
if (normalized !== '/') {
|
||||
return soapboxConfig.set('redirectRootNoLogin', normalized);
|
||||
} else {
|
||||
// Prevent infinite redirect(?)
|
||||
return soapboxConfig.delete('redirectRootNoLogin');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('You have configured an invalid redirect in Soapbox Config.');
|
||||
console.error(e);
|
||||
return soapboxConfig.delete('redirectRootNoLogin');
|
||||
}
|
||||
};
|
||||
|
||||
export const normalizeSoapboxConfig = (soapboxConfig: Record<string, any>) => {
|
||||
return SoapboxConfigRecord(
|
||||
ImmutableMap(fromJS(soapboxConfig)).withMutations(soapboxConfig => {
|
||||
|
@ -210,6 +247,8 @@ export const normalizeSoapboxConfig = (soapboxConfig: Record<string, any>) => {
|
|||
normalizeCryptoAddresses(soapboxConfig);
|
||||
normalizeAds(soapboxConfig);
|
||||
normalizeAdsAlgorithm(soapboxConfig);
|
||||
upgradeSingleUserMode(soapboxConfig);
|
||||
normalizeRedirectRootNoLogin(soapboxConfig);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue