Merge branch 'gdpr' into 'develop'
GDPR banner See merge request soapbox-pub/soapbox-fe!1678
This commit is contained in:
commit
50d60b7bb4
|
@ -397,7 +397,7 @@
|
||||||
"security.update_email.success": "Email successfully updated.",
|
"security.update_email.success": "Email successfully updated.",
|
||||||
"security.update_password.fail": "Update password failed.",
|
"security.update_password.fail": "Update password failed.",
|
||||||
"security.update_password.success": "Password successfully updated.",
|
"security.update_password.success": "Password successfully updated.",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"status.admin_account": "Open moderation interface for @{name}",
|
"status.admin_account": "Open moderation interface for @{name}",
|
||||||
"status.admin_status": "Open this post in the moderation interface",
|
"status.admin_status": "Open this post in the moderation interface",
|
||||||
|
@ -878,7 +878,7 @@
|
||||||
"security.update_email.success": "Email successfully updated.",
|
"security.update_email.success": "Email successfully updated.",
|
||||||
"security.update_password.fail": "Update password failed.",
|
"security.update_password.fail": "Update password failed.",
|
||||||
"security.update_password.success": "Password successfully updated.",
|
"security.update_password.success": "Password successfully updated.",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"status.admin_account": "Open moderation interface for @{name}",
|
"status.admin_account": "Open moderation interface for @{name}",
|
||||||
"status.admin_status": "Open this post in the moderation interface",
|
"status.admin_status": "Open this post in the moderation interface",
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { httpErrorMessages } from 'soapbox/utils/errors';
|
||||||
import type { SnackbarActionSeverity } from './snackbar';
|
import type { SnackbarActionSeverity } from './snackbar';
|
||||||
import type { AnyAction } from '@reduxjs/toolkit';
|
import type { AnyAction } from '@reduxjs/toolkit';
|
||||||
import type { AxiosError } from 'axios';
|
import type { AxiosError } from 'axios';
|
||||||
import type { NotificationObject } from 'react-notification';
|
import type { NotificationObject } from 'soapbox/react-notification';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' },
|
unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' },
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { Banner, Button, HStack, Stack, Text } from 'soapbox/components/ui';
|
||||||
|
import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
const acceptedGdpr = !!localStorage.getItem('soapbox:gdpr');
|
||||||
|
|
||||||
|
/** Displays a cookie consent banner. */
|
||||||
|
const GdprBanner: React.FC = () => {
|
||||||
|
/** Track whether the banner has already been displayed once. */
|
||||||
|
const [shown, setShown] = useState<boolean>(acceptedGdpr);
|
||||||
|
const [slideout, setSlideout] = useState(false);
|
||||||
|
|
||||||
|
const soapbox = useSoapboxConfig();
|
||||||
|
const isLoggedIn = useAppSelector(state => !!state.me);
|
||||||
|
const siteTitle = useAppSelector(state => state.instance.title);
|
||||||
|
|
||||||
|
const handleAccept = () => {
|
||||||
|
localStorage.setItem('soapbox:gdpr', 'true');
|
||||||
|
setSlideout(true);
|
||||||
|
setTimeout(() => setShown(true), 200);
|
||||||
|
};
|
||||||
|
|
||||||
|
const showBanner = soapbox.gdpr && !isLoggedIn && !shown;
|
||||||
|
|
||||||
|
if (!showBanner) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Banner theme='opaque' className={classNames('transition-transform', { 'translate-y-full': slideout })}>
|
||||||
|
<div className='flex flex-col space-y-4 lg:space-y-0 lg:space-x-4 lg:flex-row lg:items-center lg:justify-between'>
|
||||||
|
<Stack space={2}>
|
||||||
|
<Text size='xl' weight='bold'>
|
||||||
|
<FormattedMessage id='gdpr.title' defaultMessage='{siteTitle} uses cookies' values={{ siteTitle }} />
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text weight='medium' className='opacity-60'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='gdpr.message'
|
||||||
|
defaultMessage="{siteTitle} uses session cookies, which are essential to the website's functioning."
|
||||||
|
values={{ siteTitle }}
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<HStack space={2} alignItems='center'>
|
||||||
|
{soapbox.gdprUrl && (
|
||||||
|
<a href={soapbox.gdprUrl} tabIndex={-1} className='inline-flex'>
|
||||||
|
<Button theme='secondary'>
|
||||||
|
<FormattedMessage id='gdpr.learn_more' defaultMessage='Learn more' />
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button theme='accent' onClick={handleAccept}>
|
||||||
|
<FormattedMessage id='gdpr.accept' defaultMessage='Accept' />
|
||||||
|
</Button>
|
||||||
|
</HStack>
|
||||||
|
</div>
|
||||||
|
</Banner>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GdprBanner;
|
|
@ -1,7 +1,7 @@
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useSoapboxConfig, useSettings, useSystemTheme } from 'soapbox/hooks';
|
import { useSoapboxConfig, useSettings, useTheme } from 'soapbox/hooks';
|
||||||
|
|
||||||
interface ISiteLogo extends React.ComponentProps<'img'> {
|
interface ISiteLogo extends React.ComponentProps<'img'> {
|
||||||
/** Extra class names for the <img> element. */
|
/** Extra class names for the <img> element. */
|
||||||
|
@ -14,12 +14,7 @@ interface ISiteLogo extends React.ComponentProps<'img'> {
|
||||||
const SiteLogo: React.FC<ISiteLogo> = ({ className, theme, ...rest }) => {
|
const SiteLogo: React.FC<ISiteLogo> = ({ className, theme, ...rest }) => {
|
||||||
const { logo, logoDarkMode } = useSoapboxConfig();
|
const { logo, logoDarkMode } = useSoapboxConfig();
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
|
const darkMode = useTheme() === 'dark';
|
||||||
const systemTheme = useSystemTheme();
|
|
||||||
const userTheme = settings.get('themeMode');
|
|
||||||
const darkMode = theme
|
|
||||||
? theme === 'dark'
|
|
||||||
: (userTheme === 'dark' || (userTheme === 'system' && systemTheme === 'dark'));
|
|
||||||
|
|
||||||
/** Soapbox logo. */
|
/** Soapbox logo. */
|
||||||
const soapboxLogo = darkMode
|
const soapboxLogo = darkMode
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface IBanner {
|
||||||
|
theme: 'frosted' | 'opaque',
|
||||||
|
children: React.ReactNode,
|
||||||
|
className?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Displays a sticky full-width banner at the bottom of the screen. */
|
||||||
|
const Banner: React.FC<IBanner> = ({ theme, children, className }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-testid='banner'
|
||||||
|
className={classNames('fixed bottom-0 left-0 right-0 py-8 z-50', {
|
||||||
|
'backdrop-blur bg-primary-900/80': theme === 'frosted',
|
||||||
|
'bg-white dark:bg-slate-800 text-black dark:text-white shadow-2xl dark:shadow-inset': theme === 'opaque',
|
||||||
|
}, className)}
|
||||||
|
>
|
||||||
|
<div className='max-w-4xl mx-auto px-4'>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Banner;
|
|
@ -1,4 +1,5 @@
|
||||||
export { default as Avatar } from './avatar/avatar';
|
export { default as Avatar } from './avatar/avatar';
|
||||||
|
export { default as Banner } from './banner/banner';
|
||||||
export { default as Button } from './button/button';
|
export { default as Button } from './button/button';
|
||||||
export { Card, CardBody, CardHeader, CardTitle } from './card/card';
|
export { Card, CardBody, CardHeader, CardTitle } from './card/card';
|
||||||
export { default as Checkbox } from './checkbox/checkbox';
|
export { default as Checkbox } from './checkbox/checkbox';
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { fetchMe } from 'soapbox/actions/me';
|
||||||
import { loadSoapboxConfig, getSoapboxConfig } from 'soapbox/actions/soapbox';
|
import { loadSoapboxConfig, getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||||
import { fetchVerificationConfig } from 'soapbox/actions/verification';
|
import { fetchVerificationConfig } from 'soapbox/actions/verification';
|
||||||
import * as BuildConfig from 'soapbox/build_config';
|
import * as BuildConfig from 'soapbox/build_config';
|
||||||
|
import GdprBanner from 'soapbox/components/gdpr-banner';
|
||||||
import Helmet from 'soapbox/components/helmet';
|
import Helmet from 'soapbox/components/helmet';
|
||||||
import LoadingScreen from 'soapbox/components/loading-screen';
|
import LoadingScreen from 'soapbox/components/loading-screen';
|
||||||
import AuthLayout from 'soapbox/features/auth_layout';
|
import AuthLayout from 'soapbox/features/auth_layout';
|
||||||
|
@ -25,7 +26,16 @@ import {
|
||||||
WaitlistPage,
|
WaitlistPage,
|
||||||
} from 'soapbox/features/ui/util/async-components';
|
} from 'soapbox/features/ui/util/async-components';
|
||||||
import { createGlobals } from 'soapbox/globals';
|
import { createGlobals } from 'soapbox/globals';
|
||||||
import { useAppSelector, useAppDispatch, useOwnAccount, useFeatures, useSoapboxConfig, useSettings, useSystemTheme } from 'soapbox/hooks';
|
import {
|
||||||
|
useAppSelector,
|
||||||
|
useAppDispatch,
|
||||||
|
useOwnAccount,
|
||||||
|
useFeatures,
|
||||||
|
useSoapboxConfig,
|
||||||
|
useSettings,
|
||||||
|
useTheme,
|
||||||
|
useLocale,
|
||||||
|
} from 'soapbox/hooks';
|
||||||
import MESSAGES from 'soapbox/locales/messages';
|
import MESSAGES from 'soapbox/locales/messages';
|
||||||
import { useCachedLocationHandler } from 'soapbox/utils/redirect';
|
import { useCachedLocationHandler } from 'soapbox/utils/redirect';
|
||||||
import { generateThemeCss } from 'soapbox/utils/theme';
|
import { generateThemeCss } from 'soapbox/utils/theme';
|
||||||
|
@ -36,9 +46,6 @@ import ErrorBoundary from '../components/error_boundary';
|
||||||
import UI from '../features/ui';
|
import UI from '../features/ui';
|
||||||
import { store } from '../store';
|
import { store } from '../store';
|
||||||
|
|
||||||
/** Ensure the given locale exists in our codebase */
|
|
||||||
const validLocale = (locale: string): boolean => Object.keys(MESSAGES).includes(locale);
|
|
||||||
|
|
||||||
// Configure global functions for developers
|
// Configure global functions for developers
|
||||||
createGlobals(store);
|
createGlobals(store);
|
||||||
|
|
||||||
|
@ -72,82 +79,24 @@ const loadInitial = () => {
|
||||||
/** Highest level node with the Redux store. */
|
/** Highest level node with the Redux store. */
|
||||||
const SoapboxMount = () => {
|
const SoapboxMount = () => {
|
||||||
useCachedLocationHandler();
|
useCachedLocationHandler();
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const me = useAppSelector(state => state.me);
|
const me = useAppSelector(state => state.me);
|
||||||
const instance = useAppSelector(state => state.instance);
|
const instance = useAppSelector(state => state.instance);
|
||||||
const account = useOwnAccount();
|
const account = useOwnAccount();
|
||||||
const settings = useSettings();
|
|
||||||
const soapboxConfig = useSoapboxConfig();
|
const soapboxConfig = useSoapboxConfig();
|
||||||
const features = useFeatures();
|
const features = useFeatures();
|
||||||
const swUpdating = useAppSelector(state => state.meta.swUpdating);
|
|
||||||
|
|
||||||
const locale = validLocale(settings.get('locale')) ? settings.get('locale') : 'en';
|
|
||||||
|
|
||||||
const waitlisted = account && !account.source.get('approved', true);
|
const waitlisted = account && !account.source.get('approved', true);
|
||||||
const needsOnboarding = useAppSelector(state => state.onboarding.needsOnboarding);
|
const needsOnboarding = useAppSelector(state => state.onboarding.needsOnboarding);
|
||||||
const showOnboarding = account && !waitlisted && needsOnboarding;
|
const showOnboarding = account && !waitlisted && needsOnboarding;
|
||||||
const singleUserMode = soapboxConfig.singleUserMode && soapboxConfig.singleUserModeProfile;
|
const singleUserMode = soapboxConfig.singleUserMode && soapboxConfig.singleUserModeProfile;
|
||||||
|
|
||||||
const [messages, setMessages] = useState<Record<string, string>>({});
|
|
||||||
const [localeLoading, setLocaleLoading] = useState(true);
|
|
||||||
const [isLoaded, setIsLoaded] = useState(false);
|
|
||||||
|
|
||||||
const systemTheme = useSystemTheme();
|
|
||||||
const userTheme = settings.get('themeMode');
|
|
||||||
const darkMode = userTheme === 'dark' || (userTheme === 'system' && systemTheme === 'dark');
|
|
||||||
const pepeEnabled = soapboxConfig.getIn(['extensions', 'pepe', 'enabled']) === true;
|
const pepeEnabled = soapboxConfig.getIn(['extensions', 'pepe', 'enabled']) === true;
|
||||||
|
|
||||||
const themeCss = generateThemeCss(soapboxConfig);
|
|
||||||
|
|
||||||
// Load the user's locale
|
|
||||||
useEffect(() => {
|
|
||||||
MESSAGES[locale]().then(messages => {
|
|
||||||
setMessages(messages);
|
|
||||||
setLocaleLoading(false);
|
|
||||||
}).catch(() => { });
|
|
||||||
}, [locale]);
|
|
||||||
|
|
||||||
// Load initial data from the API
|
|
||||||
useEffect(() => {
|
|
||||||
dispatch(loadInitial()).then(() => {
|
|
||||||
setIsLoaded(true);
|
|
||||||
}).catch(() => {
|
|
||||||
setIsLoaded(true);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// @ts-ignore: I don't actually know what these should be, lol
|
// @ts-ignore: I don't actually know what these should be, lol
|
||||||
const shouldUpdateScroll = (prevRouterProps, { location }) => {
|
const shouldUpdateScroll = (prevRouterProps, { location }) => {
|
||||||
return !(location.state?.soapboxModalKey && location.state?.soapboxModalKey !== prevRouterProps?.location?.state?.soapboxModalKey);
|
return !(location.state?.soapboxModalKey && location.state?.soapboxModalKey !== prevRouterProps?.location?.state?.soapboxModalKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Whether to display a loading indicator. */
|
|
||||||
const showLoading = [
|
|
||||||
me === null,
|
|
||||||
me && !account,
|
|
||||||
!isLoaded,
|
|
||||||
localeLoading,
|
|
||||||
swUpdating,
|
|
||||||
].some(Boolean);
|
|
||||||
|
|
||||||
const bodyClass = classNames('bg-white dark:bg-slate-900 text-base h-full', {
|
|
||||||
'no-reduce-motion': !settings.get('reduceMotion'),
|
|
||||||
'underline-links': settings.get('underlineLinks'),
|
|
||||||
'dyslexic': settings.get('dyslexicFont'),
|
|
||||||
'demetricator': settings.get('demetricator'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const helmet = (
|
|
||||||
<Helmet>
|
|
||||||
<html lang={locale} className={classNames('h-full', { dark: darkMode })} />
|
|
||||||
<body className={bodyClass} />
|
|
||||||
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
|
||||||
{darkMode && <style type='text/css'>{':root { color-scheme: dark; }'}</style>}
|
|
||||||
<meta name='theme-color' content={soapboxConfig.brandColor} />
|
|
||||||
</Helmet>
|
|
||||||
);
|
|
||||||
|
|
||||||
/** Render the onboarding flow. */
|
/** Render the onboarding flow. */
|
||||||
const renderOnboarding = () => (
|
const renderOnboarding = () => (
|
||||||
<BundleContainer fetchComponent={OnboardingWizard} loading={LoadingScreen}>
|
<BundleContainer fetchComponent={OnboardingWizard} loading={LoadingScreen}>
|
||||||
|
@ -214,20 +163,7 @@ const SoapboxMount = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// intl is part of loading.
|
|
||||||
// It's important nothing in here depends on intl.
|
|
||||||
if (showLoading) {
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
{helmet}
|
|
||||||
<LoadingScreen />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IntlProvider locale={locale} messages={messages}>
|
|
||||||
{helmet}
|
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<BrowserRouter basename={BuildConfig.FE_SUBDIRECTORY}>
|
<BrowserRouter basename={BuildConfig.FE_SUBDIRECTORY}>
|
||||||
<ScrollContext shouldUpdateScroll={shouldUpdateScroll}>
|
<ScrollContext shouldUpdateScroll={shouldUpdateScroll}>
|
||||||
|
@ -241,19 +177,115 @@ const SoapboxMount = () => {
|
||||||
<BundleContainer fetchComponent={ModalContainer}>
|
<BundleContainer fetchComponent={ModalContainer}>
|
||||||
{Component => <Component />}
|
{Component => <Component />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
|
|
||||||
|
<GdprBanner />
|
||||||
</>
|
</>
|
||||||
</ScrollContext>
|
</ScrollContext>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ISoapboxLoad {
|
||||||
|
children: React.ReactNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Initial data loader. */
|
||||||
|
const SoapboxLoad: React.FC<ISoapboxLoad> = ({ children }) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const me = useAppSelector(state => state.me);
|
||||||
|
const account = useOwnAccount();
|
||||||
|
const swUpdating = useAppSelector(state => state.meta.swUpdating);
|
||||||
|
const locale = useLocale();
|
||||||
|
|
||||||
|
const [messages, setMessages] = useState<Record<string, string>>({});
|
||||||
|
const [localeLoading, setLocaleLoading] = useState(true);
|
||||||
|
const [isLoaded, setIsLoaded] = useState(false);
|
||||||
|
|
||||||
|
/** Whether to display a loading indicator. */
|
||||||
|
const showLoading = [
|
||||||
|
me === null,
|
||||||
|
me && !account,
|
||||||
|
!isLoaded,
|
||||||
|
localeLoading,
|
||||||
|
swUpdating,
|
||||||
|
].some(Boolean);
|
||||||
|
|
||||||
|
// Load the user's locale
|
||||||
|
useEffect(() => {
|
||||||
|
MESSAGES[locale]().then(messages => {
|
||||||
|
setMessages(messages);
|
||||||
|
setLocaleLoading(false);
|
||||||
|
}).catch(() => { });
|
||||||
|
}, [locale]);
|
||||||
|
|
||||||
|
// Load initial data from the API
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(loadInitial()).then(() => {
|
||||||
|
setIsLoaded(true);
|
||||||
|
}).catch(() => {
|
||||||
|
setIsLoaded(true);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// intl is part of loading.
|
||||||
|
// It's important nothing in here depends on intl.
|
||||||
|
if (showLoading) {
|
||||||
|
return <LoadingScreen />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IntlProvider locale={locale} messages={messages}>
|
||||||
|
{children}
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface ISoapboxHead {
|
||||||
|
children: React.ReactNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Injects metadata into site head with Helmet. */
|
||||||
|
const SoapboxHead: React.FC<ISoapboxHead> = ({ children }) => {
|
||||||
|
const locale = useLocale();
|
||||||
|
const settings = useSettings();
|
||||||
|
const soapboxConfig = useSoapboxConfig();
|
||||||
|
|
||||||
|
const darkMode = useTheme() === 'dark';
|
||||||
|
const themeCss = generateThemeCss(soapboxConfig);
|
||||||
|
|
||||||
|
const bodyClass = classNames('bg-white dark:bg-slate-900 text-base h-full', {
|
||||||
|
'no-reduce-motion': !settings.get('reduceMotion'),
|
||||||
|
'underline-links': settings.get('underlineLinks'),
|
||||||
|
'dyslexic': settings.get('dyslexicFont'),
|
||||||
|
'demetricator': settings.get('demetricator'),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Helmet>
|
||||||
|
<html lang={locale} className={classNames('h-full', { dark: darkMode })} />
|
||||||
|
<body className={bodyClass} />
|
||||||
|
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
||||||
|
{darkMode && <style type='text/css'>{':root { color-scheme: dark; }'}</style>}
|
||||||
|
<meta name='theme-color' content={soapboxConfig.brandColor} />
|
||||||
|
</Helmet>
|
||||||
|
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/** The root React node of the application. */
|
/** The root React node of the application. */
|
||||||
const Soapbox = () => {
|
const Soapbox: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
<SoapboxHead>
|
||||||
|
<SoapboxLoad>
|
||||||
<SoapboxMount />
|
<SoapboxMount />
|
||||||
|
</SoapboxLoad>
|
||||||
|
</SoapboxHead>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Button, HStack, Stack, Text } from 'soapbox/components/ui';
|
import { Banner, Button, HStack, Stack, Text } from 'soapbox/components/ui';
|
||||||
import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks';
|
import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks';
|
||||||
|
|
||||||
const CtaBanner = () => {
|
const CtaBanner = () => {
|
||||||
|
@ -12,9 +12,8 @@ const CtaBanner = () => {
|
||||||
if (me || singleUserMode) return null;
|
if (me || singleUserMode) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid='cta-banner' className='hidden lg:block fixed bottom-0 left-0 right-0 py-4 backdrop-blur bg-primary-900/80 z-50'>
|
<div data-testid='cta-banner' className='hidden lg:block'>
|
||||||
<div className='max-w-3xl md:max-w-7xl mx-auto lg:grid lg:grid-cols-12'>
|
<Banner theme='frosted'>
|
||||||
<div className='col-span-9 col-start-4 xl:col-start-4 xl:col-span-6 md:px-8'>
|
|
||||||
<HStack alignItems='center' justifyContent='between'>
|
<HStack alignItems='center' justifyContent='between'>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Text theme='white' size='xl' weight='bold'>
|
<Text theme='white' size='xl' weight='bold'>
|
||||||
|
@ -22,7 +21,7 @@ const CtaBanner = () => {
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text theme='white' weight='medium' className='opacity-90'>
|
<Text theme='white' weight='medium' className='opacity-90'>
|
||||||
<FormattedMessage id='signup_panel.subtitle' defaultMessage='Sign up now to discuss.' />
|
<FormattedMessage id='signup_panel.subtitle' defaultMessage="Sign up now to discuss what's happening." />
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
@ -36,8 +35,7 @@ const CtaBanner = () => {
|
||||||
</Button>
|
</Button>
|
||||||
</HStack>
|
</HStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
</div>
|
</Banner>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useIntl, MessageDescriptor } from 'react-intl';
|
import { useIntl, MessageDescriptor } from 'react-intl';
|
||||||
import { NotificationStack, NotificationObject, StyleFactoryFn } from 'react-notification';
|
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { dismissAlert } from 'soapbox/actions/alerts';
|
import { dismissAlert } from 'soapbox/actions/alerts';
|
||||||
import { Button } from 'soapbox/components/ui';
|
import { Button } from 'soapbox/components/ui';
|
||||||
import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
|
import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
|
||||||
|
import { NotificationStack, NotificationObject, StyleFactoryFn } from 'soapbox/react-notification';
|
||||||
|
|
||||||
import type { Alert } from 'soapbox/reducers/alerts';
|
import type { Alert } from 'soapbox/reducers/alerts';
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,11 @@ export { useAppDispatch } from './useAppDispatch';
|
||||||
export { useAppSelector } from './useAppSelector';
|
export { useAppSelector } from './useAppSelector';
|
||||||
export { useDimensions } from './useDimensions';
|
export { useDimensions } from './useDimensions';
|
||||||
export { useFeatures } from './useFeatures';
|
export { useFeatures } from './useFeatures';
|
||||||
|
export { useLocale } from './useLocale';
|
||||||
export { useOnScreen } from './useOnScreen';
|
export { useOnScreen } from './useOnScreen';
|
||||||
export { useOwnAccount } from './useOwnAccount';
|
export { useOwnAccount } from './useOwnAccount';
|
||||||
export { useRefEventHandler } from './useRefEventHandler';
|
export { useRefEventHandler } from './useRefEventHandler';
|
||||||
export { useSettings } from './useSettings';
|
export { useSettings } from './useSettings';
|
||||||
export { useSoapboxConfig } from './useSoapboxConfig';
|
export { useSoapboxConfig } from './useSoapboxConfig';
|
||||||
export { useSystemTheme } from './useSystemTheme';
|
export { useSystemTheme } from './useSystemTheme';
|
||||||
|
export { useTheme } from './useTheme';
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import MESSAGES from 'soapbox/locales/messages';
|
||||||
|
|
||||||
|
import { useSettings } from './useSettings';
|
||||||
|
|
||||||
|
/** Ensure the given locale exists in our codebase */
|
||||||
|
const validLocale = (locale: string): boolean => Object.keys(MESSAGES).includes(locale);
|
||||||
|
|
||||||
|
/** Get valid locale from settings. */
|
||||||
|
const useLocale = (fallback = 'en') => {
|
||||||
|
const settings = useSettings();
|
||||||
|
const locale = settings.get('locale');
|
||||||
|
|
||||||
|
return validLocale(locale) ? locale : fallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { useLocale };
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { useSettings } from './useSettings';
|
||||||
|
import { useSystemTheme } from './useSystemTheme';
|
||||||
|
|
||||||
|
type Theme = 'light' | 'dark';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the actual theme being displayed (eg "light" or "dark")
|
||||||
|
* regardless of whether that's by system theme or direct setting.
|
||||||
|
*/
|
||||||
|
const useTheme = (): Theme => {
|
||||||
|
const settings = useSettings();
|
||||||
|
const systemTheme = useSystemTheme();
|
||||||
|
|
||||||
|
const userTheme = settings.get('themeMode');
|
||||||
|
const darkMode = userTheme === 'dark' || (userTheme === 'system' && systemTheme === 'dark');
|
||||||
|
|
||||||
|
return darkMode ? 'dark' : 'light';
|
||||||
|
};
|
||||||
|
|
||||||
|
export { useTheme };
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -930,7 +930,7 @@
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"shared.tos": "Terms of Service",
|
"shared.tos": "Terms of Service",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Preferenze salvate!",
|
"settings.save.success": "Preferenze salvate!",
|
||||||
"settings.security": "Sicurezza",
|
"settings.security": "Sicurezza",
|
||||||
"settings.settings": "Impostazioni",
|
"settings.settings": "Impostazioni",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "Mostra",
|
"snackbar.view": "Mostra",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -920,7 +920,7 @@
|
||||||
"settings.save.success": "Your preferences have been saved!",
|
"settings.save.success": "Your preferences have been saved!",
|
||||||
"settings.security": "Security",
|
"settings.security": "Security",
|
||||||
"settings.settings": "Settings",
|
"settings.settings": "Settings",
|
||||||
"signup_panel.subtitle": "Sign up now to discuss.",
|
"signup_panel.subtitle": "Sign up now to discuss what's happening.",
|
||||||
"signup_panel.title": "New to {site_title}?",
|
"signup_panel.title": "New to {site_title}?",
|
||||||
"snackbar.view": "View",
|
"snackbar.view": "View",
|
||||||
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
"soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.",
|
||||||
|
|
|
@ -89,6 +89,8 @@ export const SoapboxConfigRecord = ImmutableRecord({
|
||||||
customCss: ImmutableList<string>(),
|
customCss: ImmutableList<string>(),
|
||||||
defaultSettings: ImmutableMap<string, any>(),
|
defaultSettings: ImmutableMap<string, any>(),
|
||||||
extensions: ImmutableMap(),
|
extensions: ImmutableMap(),
|
||||||
|
gdpr: false,
|
||||||
|
gdprUrl: '',
|
||||||
greentext: false,
|
greentext: false,
|
||||||
promoPanel: PromoPanelRecord(),
|
promoPanel: PromoPanelRecord(),
|
||||||
navlinks: ImmutableMap({
|
navlinks: ImmutableMap({
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
message: PropTypes.oneOfType([
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.element,
|
||||||
|
]).isRequired,
|
||||||
|
action: PropTypes.oneOfType([
|
||||||
|
PropTypes.bool,
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.node,
|
||||||
|
]),
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
style: PropTypes.bool,
|
||||||
|
actionStyle: PropTypes.object,
|
||||||
|
titleStyle: PropTypes.object,
|
||||||
|
barStyle: PropTypes.object,
|
||||||
|
activeBarStyle: PropTypes.object,
|
||||||
|
dismissAfter: PropTypes.oneOfType([
|
||||||
|
PropTypes.bool,
|
||||||
|
PropTypes.number,
|
||||||
|
]),
|
||||||
|
onDismiss: PropTypes.func,
|
||||||
|
className: PropTypes.string,
|
||||||
|
activeClassName: PropTypes.string,
|
||||||
|
isActive: PropTypes.bool,
|
||||||
|
title: PropTypes.oneOfType([
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.node,
|
||||||
|
]),
|
||||||
|
};
|
|
@ -0,0 +1,88 @@
|
||||||
|
declare module 'soapbox/react-notification' {
|
||||||
|
import { Component, ReactElement } from 'react';
|
||||||
|
|
||||||
|
interface StyleFactoryFn {
|
||||||
|
(index: number, style: object | void, notification: NotificationProps): object;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OnClickNotificationProps {
|
||||||
|
/**
|
||||||
|
* Callback function to run when the action is clicked.
|
||||||
|
* @param notification Notification currently being clicked
|
||||||
|
* @param deactivate Function that can be called to set the notification to inactive.
|
||||||
|
* Used to activate notification exit animation on click.
|
||||||
|
*/
|
||||||
|
onClick?(notification: NotificationProps, deactivate: () => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NotificationProps extends OnClickNotificationProps {
|
||||||
|
/** The name of the action, e.g., "close" or "undo". */
|
||||||
|
action?: string;
|
||||||
|
/** Custom action styles. */
|
||||||
|
actionStyle?: object;
|
||||||
|
/** Custom snackbar styles when the bar is active. */
|
||||||
|
activeBarStyle?: object;
|
||||||
|
/**
|
||||||
|
* Custom class to apply to the top-level component when active.
|
||||||
|
* @default 'notification-bar-active'
|
||||||
|
*/
|
||||||
|
activeClassName?: string;
|
||||||
|
/** Custom snackbar styles. */
|
||||||
|
barStyle?: object;
|
||||||
|
/** Custom class to apply to the top-level component. */
|
||||||
|
className?: string;
|
||||||
|
/**
|
||||||
|
* Timeout for onDismiss event.
|
||||||
|
* @default 2000
|
||||||
|
*/
|
||||||
|
dismissAfter?: boolean | number;
|
||||||
|
/**
|
||||||
|
* If true, the notification is visible.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
isActive?: boolean;
|
||||||
|
/** The message or component for the notification. */
|
||||||
|
message: string | ReactElement<NotificationProps>;
|
||||||
|
/** Setting this prop to `false` will disable all inline styles. */
|
||||||
|
style?: boolean;
|
||||||
|
/** The title for the notification. */
|
||||||
|
title?: string | ReactElement<any>;
|
||||||
|
/** Custom title styles. */
|
||||||
|
titleStyle?: object;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function to run when dismissAfter timer runs out
|
||||||
|
* @param notification Notification currently being dismissed.
|
||||||
|
*/
|
||||||
|
onDismiss?(notification: NotificationProps): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NotificationStackProps extends OnClickNotificationProps {
|
||||||
|
/** Create the style of the actions. */
|
||||||
|
actionStyleFactory?: StyleFactoryFn;
|
||||||
|
/** Create the style of the active notification. */
|
||||||
|
activeBarStyleFactory?: StyleFactoryFn;
|
||||||
|
/** Create the style of the notification. */
|
||||||
|
barStyleFactory?: StyleFactoryFn;
|
||||||
|
/**
|
||||||
|
* If false, notification dismiss timers start immediately.
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
dismissInOrder?: boolean;
|
||||||
|
/** Array of notifications to render. */
|
||||||
|
notifications: NotificationObject[];
|
||||||
|
/**
|
||||||
|
* Callback function to run when dismissAfter timer runs out
|
||||||
|
* @param notification Notification currently being dismissed.
|
||||||
|
*/
|
||||||
|
onDismiss?(notification: NotificationObject): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NotificationObject extends NotificationProps {
|
||||||
|
key: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Notification extends Component<NotificationProps, {}> {}
|
||||||
|
|
||||||
|
export class NotificationStack extends Component<NotificationStackProps, {}> {}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as Notification } from './notification';
|
||||||
|
export { default as NotificationStack } from './notificationStack';
|
|
@ -0,0 +1,175 @@
|
||||||
|
/* linting temp disabled while working on updates */
|
||||||
|
/* eslint-disable */
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import defaultPropTypes from './defaultPropTypes';
|
||||||
|
|
||||||
|
class Notification extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.getBarStyle = this.getBarStyle.bind(this);
|
||||||
|
this.getActionStyle = this.getActionStyle.bind(this);
|
||||||
|
this.getTitleStyle = this.getTitleStyle.bind(this);
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
|
||||||
|
if (props.onDismiss && props.isActive) {
|
||||||
|
this.dismissTimeout = setTimeout(
|
||||||
|
props.onDismiss,
|
||||||
|
props.dismissAfter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps.dismissAfter === false) return;
|
||||||
|
|
||||||
|
// See http://eslint.org/docs/rules/no-prototype-builtins
|
||||||
|
if (!{}.hasOwnProperty.call(nextProps, 'isLast')) {
|
||||||
|
clearTimeout(this.dismissTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextProps.onDismiss) {
|
||||||
|
if (
|
||||||
|
(nextProps.isActive && !this.props.isActive) ||
|
||||||
|
(nextProps.dismissAfter && this.props.dismissAfter === false)
|
||||||
|
) {
|
||||||
|
this.dismissTimeout = setTimeout(
|
||||||
|
nextProps.onDismiss,
|
||||||
|
nextProps.dismissAfter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.props.dismissAfter) clearTimeout(this.dismissTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @description Dynamically get the styles for the bar.
|
||||||
|
* @returns {object} result The style.
|
||||||
|
*/
|
||||||
|
getBarStyle() {
|
||||||
|
if (this.props.style === false) return {};
|
||||||
|
|
||||||
|
const { isActive, barStyle, activeBarStyle } = this.props;
|
||||||
|
|
||||||
|
const baseStyle = {
|
||||||
|
position: 'fixed',
|
||||||
|
bottom: '2rem',
|
||||||
|
left: '-100%',
|
||||||
|
width: 'auto',
|
||||||
|
padding: '1rem',
|
||||||
|
margin: 0,
|
||||||
|
color: '#fafafa',
|
||||||
|
font: '1rem normal Roboto, sans-serif',
|
||||||
|
borderRadius: '5px',
|
||||||
|
background: '#212121',
|
||||||
|
borderSizing: 'border-box',
|
||||||
|
boxShadow: '0 0 1px 1px rgba(10, 10, 11, .125)',
|
||||||
|
cursor: 'default',
|
||||||
|
WebKitTransition: '.5s cubic-bezier(0.89, 0.01, 0.5, 1.1)',
|
||||||
|
MozTransition: '.5s cubic-bezier(0.89, 0.01, 0.5, 1.1)',
|
||||||
|
msTransition: '.5s cubic-bezier(0.89, 0.01, 0.5, 1.1)',
|
||||||
|
OTransition: '.5s cubic-bezier(0.89, 0.01, 0.5, 1.1)',
|
||||||
|
transition: '.5s cubic-bezier(0.89, 0.01, 0.5, 1.1)',
|
||||||
|
WebkitTransform: 'translatez(0)',
|
||||||
|
MozTransform: 'translatez(0)',
|
||||||
|
msTransform: 'translatez(0)',
|
||||||
|
OTransform: 'translatez(0)',
|
||||||
|
transform: 'translatez(0)'
|
||||||
|
};
|
||||||
|
|
||||||
|
return isActive ?
|
||||||
|
Object.assign({}, baseStyle, { left: '1rem' }, barStyle, activeBarStyle) :
|
||||||
|
Object.assign({}, baseStyle, barStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @function getActionStyle
|
||||||
|
* @description Dynamically get the styles for the action text.
|
||||||
|
* @returns {object} result The style.
|
||||||
|
*/
|
||||||
|
getActionStyle() {
|
||||||
|
return this.props.style !== false ? Object.assign({}, {
|
||||||
|
padding: '0.125rem',
|
||||||
|
marginLeft: '1rem',
|
||||||
|
color: '#f44336',
|
||||||
|
font: '.75rem normal Roboto, sans-serif',
|
||||||
|
lineHeight: '1rem',
|
||||||
|
letterSpacing: '.125ex',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
borderRadius: '5px',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}, this.props.actionStyle) : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @function getTitleStyle
|
||||||
|
* @description Dynamically get the styles for the title.
|
||||||
|
* @returns {object} result The style.
|
||||||
|
*/
|
||||||
|
getTitleStyle() {
|
||||||
|
return this.props.style !== false ? Object.assign({}, {
|
||||||
|
fontWeight: '700',
|
||||||
|
marginRight: '.5rem'
|
||||||
|
}, this.props.titleStyle) : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @function handleClick
|
||||||
|
* @description Handle click events on the action button.
|
||||||
|
*/
|
||||||
|
handleClick() {
|
||||||
|
if (this.props.onClick && typeof this.props.onClick === 'function') {
|
||||||
|
return this.props.onClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let className = 'notification-bar';
|
||||||
|
|
||||||
|
if (this.props.isActive) className += ` ${this.props.activeClassName}`;
|
||||||
|
if (this.props.className) className += ` ${this.props.className}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className} style={this.getBarStyle()}>
|
||||||
|
<div className="notification-bar-wrapper">
|
||||||
|
{this.props.title ? (
|
||||||
|
<span
|
||||||
|
className="notification-bar-title"
|
||||||
|
style={this.getTitleStyle()}
|
||||||
|
>
|
||||||
|
{this.props.title}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{/* eslint-disable */}
|
||||||
|
<span className="notification-bar-message">
|
||||||
|
{this.props.message}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{this.props.action ? (
|
||||||
|
<span
|
||||||
|
className="notification-bar-action"
|
||||||
|
onClick={this.handleClick}
|
||||||
|
style={this.getActionStyle()}
|
||||||
|
>
|
||||||
|
{this.props.action}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification.propTypes = defaultPropTypes;
|
||||||
|
|
||||||
|
Notification.defaultProps = {
|
||||||
|
isActive: false,
|
||||||
|
dismissAfter: 2000,
|
||||||
|
activeClassName: 'notification-bar-active'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Notification;
|
|
@ -0,0 +1,95 @@
|
||||||
|
/* linting temp disabled while working on updates */
|
||||||
|
/* eslint-disable */
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import StackedNotification from './stackedNotification';
|
||||||
|
import defaultPropTypes from './defaultPropTypes';
|
||||||
|
|
||||||
|
function defaultBarStyleFactory(index, style) {
|
||||||
|
return Object.assign(
|
||||||
|
{},
|
||||||
|
style,
|
||||||
|
{ bottom: `${2 + (index * 4)}rem` }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultActionStyleFactory(index, style) {
|
||||||
|
return Object.assign(
|
||||||
|
{},
|
||||||
|
style,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The notification list does not have any state, so use a
|
||||||
|
* pure function here. It just needs to return the stacked array
|
||||||
|
* of notification components.
|
||||||
|
*/
|
||||||
|
const NotificationStack = props => (
|
||||||
|
<div className="notification-list">
|
||||||
|
{props.notifications.map((notification, index) => {
|
||||||
|
const isLast = index === 0 && props.notifications.length === 1;
|
||||||
|
const dismissNow = isLast || !props.dismissInOrder;
|
||||||
|
|
||||||
|
// Handle styles
|
||||||
|
const barStyle = props.barStyleFactory(index, notification.barStyle, notification);
|
||||||
|
const actionStyle = props.actionStyleFactory(index, notification.actionStyle, notification);
|
||||||
|
const activeBarStyle = props.activeBarStyleFactory(
|
||||||
|
index,
|
||||||
|
notification.activeBarStyle,
|
||||||
|
notification
|
||||||
|
);
|
||||||
|
|
||||||
|
// Allow onClick from notification stack or individual notifications
|
||||||
|
const onClick = notification.onClick || props.onClick;
|
||||||
|
const onDismiss = props.onDismiss;
|
||||||
|
|
||||||
|
let { dismissAfter } = notification;
|
||||||
|
|
||||||
|
if (dismissAfter !== false) {
|
||||||
|
if (dismissAfter == null) dismissAfter = props.dismissAfter;
|
||||||
|
if (!dismissNow) dismissAfter += index * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StackedNotification
|
||||||
|
{...notification}
|
||||||
|
key={notification.key}
|
||||||
|
isLast={isLast}
|
||||||
|
action={notification.action || props.action}
|
||||||
|
dismissAfter={dismissAfter}
|
||||||
|
onDismiss={onDismiss.bind(this, notification)}
|
||||||
|
onClick={onClick.bind(this, notification)}
|
||||||
|
activeBarStyle={activeBarStyle}
|
||||||
|
barStyle={barStyle}
|
||||||
|
actionStyle={actionStyle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
/* eslint-disable react/no-unused-prop-types, react/forbid-prop-types */
|
||||||
|
NotificationStack.propTypes = {
|
||||||
|
activeBarStyleFactory: PropTypes.func,
|
||||||
|
barStyleFactory: PropTypes.func,
|
||||||
|
actionStyleFactory: PropTypes.func,
|
||||||
|
dismissInOrder: PropTypes.bool,
|
||||||
|
notifications: PropTypes.array.isRequired,
|
||||||
|
onDismiss: PropTypes.func.isRequired,
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
action: defaultPropTypes.action
|
||||||
|
};
|
||||||
|
|
||||||
|
NotificationStack.defaultProps = {
|
||||||
|
activeBarStyleFactory: defaultBarStyleFactory,
|
||||||
|
barStyleFactory: defaultBarStyleFactory,
|
||||||
|
actionStyleFactory: defaultActionStyleFactory,
|
||||||
|
dismissInOrder: true,
|
||||||
|
dismissAfter: 1000,
|
||||||
|
onClick: () => {}
|
||||||
|
};
|
||||||
|
/* eslint-enable no-alert, no-console */
|
||||||
|
|
||||||
|
export default NotificationStack;
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* linting temp disabled while working on updates */
|
||||||
|
/* eslint-disable */
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import defaultPropTypes from './defaultPropTypes';
|
||||||
|
import Notification from './notification';
|
||||||
|
|
||||||
|
class StackedNotification extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isActive: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.activeTimeout = setTimeout(this.setState.bind(this, {
|
||||||
|
isActive: true
|
||||||
|
}), 1);
|
||||||
|
|
||||||
|
this.dismiss(this.props.dismissAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps.dismissAfter !== this.props.dismissAfter) {
|
||||||
|
this.dismiss(nextProps.dismissAfter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
clearTimeout(this.activeTimeout);
|
||||||
|
clearTimeout(this.dismissTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
dismiss(dismissAfter) {
|
||||||
|
if (dismissAfter === false) return;
|
||||||
|
|
||||||
|
this.dismissTimeout = setTimeout(this.setState.bind(this, {
|
||||||
|
isActive: false
|
||||||
|
}), dismissAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @function handleClick
|
||||||
|
* @description Bind deactivate Notification function to Notification click handler
|
||||||
|
*/
|
||||||
|
handleClick() {
|
||||||
|
if (this.props.onClick && typeof this.props.onClick === 'function') {
|
||||||
|
return this.props.onClick(this.setState.bind(this, { isActive: false }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Notification
|
||||||
|
{...this.props}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
onDismiss={() => setTimeout(this.props.onDismiss, 300)}
|
||||||
|
isActive={this.state.isActive}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StackedNotification.propTypes = defaultPropTypes;
|
||||||
|
|
||||||
|
export default StackedNotification;
|
|
@ -162,7 +162,6 @@
|
||||||
"react-inlinesvg": "^3.0.0",
|
"react-inlinesvg": "^3.0.0",
|
||||||
"react-intl": "^5.0.0",
|
"react-intl": "^5.0.0",
|
||||||
"react-motion": "^0.5.2",
|
"react-motion": "^0.5.2",
|
||||||
"react-notification": "^6.8.5",
|
|
||||||
"react-otp-input": "^2.4.0",
|
"react-otp-input": "^2.4.0",
|
||||||
"react-overlays": "^0.9.0",
|
"react-overlays": "^0.9.0",
|
||||||
"react-popper": "^2.3.0",
|
"react-popper": "^2.3.0",
|
||||||
|
|
|
@ -9749,13 +9749,6 @@ react-motion@^0.5.2:
|
||||||
prop-types "^15.5.8"
|
prop-types "^15.5.8"
|
||||||
raf "^3.1.0"
|
raf "^3.1.0"
|
||||||
|
|
||||||
react-notification@^6.8.5:
|
|
||||||
version "6.8.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-notification/-/react-notification-6.8.5.tgz#7ea90a633bb2a280d899e30c93cf372265cce4f0"
|
|
||||||
integrity sha512-3pJPhSsWNYizpyeMeWuC+jVthqE9WKqQ6rHq2naiiP4fLGN4irwL2Xp2Q8Qn7agW/e4BIDxarab6fJOUp1cKUw==
|
|
||||||
dependencies:
|
|
||||||
prop-types "^15.6.2"
|
|
||||||
|
|
||||||
react-onclickoutside@^6.12.0:
|
react-onclickoutside@^6.12.0:
|
||||||
version "6.12.1"
|
version "6.12.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz#92dddd28f55e483a1838c5c2930e051168c1e96b"
|
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz#92dddd28f55e483a1838c5c2930e051168c1e96b"
|
||||||
|
|
Loading…
Reference in New Issue