diff --git a/app/soapbox/__fixtures__/intlMessages.json b/app/soapbox/__fixtures__/intlMessages.json index 45a69a903..7760742ff 100644 --- a/app/soapbox/__fixtures__/intlMessages.json +++ b/app/soapbox/__fixtures__/intlMessages.json @@ -397,7 +397,7 @@ "security.update_email.success": "Email successfully updated.", "security.update_password.fail": "Update password failed.", "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}?", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this post in the moderation interface", @@ -878,7 +878,7 @@ "security.update_email.success": "Email successfully updated.", "security.update_password.fail": "Update password failed.", "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}?", "status.admin_account": "Open moderation interface for @{name}", "status.admin_status": "Open this post in the moderation interface", diff --git a/app/soapbox/actions/alerts.ts b/app/soapbox/actions/alerts.ts index 8f200563a..3e5aed4b3 100644 --- a/app/soapbox/actions/alerts.ts +++ b/app/soapbox/actions/alerts.ts @@ -5,7 +5,7 @@ import { httpErrorMessages } from 'soapbox/utils/errors'; import type { SnackbarActionSeverity } from './snackbar'; import type { AnyAction } from '@reduxjs/toolkit'; import type { AxiosError } from 'axios'; -import type { NotificationObject } from 'react-notification'; +import type { NotificationObject } from 'soapbox/react-notification'; const messages = defineMessages({ unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' }, diff --git a/app/soapbox/components/gdpr-banner.tsx b/app/soapbox/components/gdpr-banner.tsx new file mode 100644 index 000000000..60d5d3098 --- /dev/null +++ b/app/soapbox/components/gdpr-banner.tsx @@ -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(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 ( + +
+ + + + + + + + + + + + {soapbox.gdprUrl && ( + + + + )} + + + +
+
+ ); +}; + +export default GdprBanner; diff --git a/app/soapbox/components/site-logo.tsx b/app/soapbox/components/site-logo.tsx index 01bfc54ed..1e8751ac5 100644 --- a/app/soapbox/components/site-logo.tsx +++ b/app/soapbox/components/site-logo.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames'; import React from 'react'; -import { useSoapboxConfig, useSettings, useSystemTheme } from 'soapbox/hooks'; +import { useSoapboxConfig, useSettings, useTheme } from 'soapbox/hooks'; interface ISiteLogo extends React.ComponentProps<'img'> { /** Extra class names for the element. */ @@ -14,12 +14,7 @@ interface ISiteLogo extends React.ComponentProps<'img'> { const SiteLogo: React.FC = ({ className, theme, ...rest }) => { const { logo, logoDarkMode } = useSoapboxConfig(); const settings = useSettings(); - - const systemTheme = useSystemTheme(); - const userTheme = settings.get('themeMode'); - const darkMode = theme - ? theme === 'dark' - : (userTheme === 'dark' || (userTheme === 'system' && systemTheme === 'dark')); + const darkMode = useTheme() === 'dark'; /** Soapbox logo. */ const soapboxLogo = darkMode diff --git a/app/soapbox/components/ui/banner/banner.tsx b/app/soapbox/components/ui/banner/banner.tsx new file mode 100644 index 000000000..3acbbcf09 --- /dev/null +++ b/app/soapbox/components/ui/banner/banner.tsx @@ -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 = ({ theme, children, className }) => { + return ( +
+
+ {children} +
+
+ ); +}; + +export default Banner; diff --git a/app/soapbox/components/ui/index.ts b/app/soapbox/components/ui/index.ts index 042b26838..5eff0a78f 100644 --- a/app/soapbox/components/ui/index.ts +++ b/app/soapbox/components/ui/index.ts @@ -1,4 +1,5 @@ export { default as Avatar } from './avatar/avatar'; +export { default as Banner } from './banner/banner'; export { default as Button } from './button/button'; export { Card, CardBody, CardHeader, CardTitle } from './card/card'; export { default as Checkbox } from './checkbox/checkbox'; diff --git a/app/soapbox/containers/soapbox.tsx b/app/soapbox/containers/soapbox.tsx index 29cfd74fb..603663aa7 100644 --- a/app/soapbox/containers/soapbox.tsx +++ b/app/soapbox/containers/soapbox.tsx @@ -13,6 +13,7 @@ import { fetchMe } from 'soapbox/actions/me'; import { loadSoapboxConfig, getSoapboxConfig } from 'soapbox/actions/soapbox'; import { fetchVerificationConfig } from 'soapbox/actions/verification'; import * as BuildConfig from 'soapbox/build_config'; +import GdprBanner from 'soapbox/components/gdpr-banner'; import Helmet from 'soapbox/components/helmet'; import LoadingScreen from 'soapbox/components/loading-screen'; import AuthLayout from 'soapbox/features/auth_layout'; @@ -25,7 +26,16 @@ import { WaitlistPage, } from 'soapbox/features/ui/util/async-components'; 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 { useCachedLocationHandler } from 'soapbox/utils/redirect'; import { generateThemeCss } from 'soapbox/utils/theme'; @@ -36,9 +46,6 @@ import ErrorBoundary from '../components/error_boundary'; import UI from '../features/ui'; 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 createGlobals(store); @@ -72,82 +79,24 @@ const loadInitial = () => { /** Highest level node with the Redux store. */ const SoapboxMount = () => { useCachedLocationHandler(); - const dispatch = useAppDispatch(); - const me = useAppSelector(state => state.me); const instance = useAppSelector(state => state.instance); const account = useOwnAccount(); - const settings = useSettings(); const soapboxConfig = useSoapboxConfig(); 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 needsOnboarding = useAppSelector(state => state.onboarding.needsOnboarding); const showOnboarding = account && !waitlisted && needsOnboarding; const singleUserMode = soapboxConfig.singleUserMode && soapboxConfig.singleUserModeProfile; - const [messages, setMessages] = useState>({}); - 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 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 const shouldUpdateScroll = (prevRouterProps, { location }) => { 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 = ( - - - - {themeCss && } - {darkMode && } - - - ); - /** Render the onboarding flow. */ const renderOnboarding = () => ( @@ -214,46 +163,129 @@ const SoapboxMount = () => { } }; + return ( + + + + <> + {renderBody()} + + + {(Component) => } + + + + {Component => } + + + + + + + + ); +}; + +interface ISoapboxLoad { + children: React.ReactNode, +} + +/** Initial data loader. */ +const SoapboxLoad: React.FC = ({ 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>({}); + 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 ( - <> - {helmet} - - - ); + return ; } return ( - {helmet} - - - - <> - {renderBody()} - - - {(Component) => } - - - - {Component => } - - - - - + {children} ); }; +interface ISoapboxHead { + children: React.ReactNode, +} + +/** Injects metadata into site head with Helmet. */ +const SoapboxHead: React.FC = ({ 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 ( + <> + + + + {themeCss && } + {darkMode && } + + + + {children} + + ); +}; + /** The root React node of the application. */ -const Soapbox = () => { +const Soapbox: React.FC = () => { return ( - + + + + + ); }; diff --git a/app/soapbox/features/ui/components/cta-banner.tsx b/app/soapbox/features/ui/components/cta-banner.tsx index 9ecc9b6dc..9d83c6702 100644 --- a/app/soapbox/features/ui/components/cta-banner.tsx +++ b/app/soapbox/features/ui/components/cta-banner.tsx @@ -1,7 +1,7 @@ import React from 'react'; 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'; const CtaBanner = () => { @@ -12,32 +12,30 @@ const CtaBanner = () => { if (me || singleUserMode) return null; return ( -
-
-
- - - - - +
+ + + + + + - - - - + + + + - - + + - - + -
-
+ +
); }; diff --git a/app/soapbox/features/ui/containers/notifications_container.tsx b/app/soapbox/features/ui/containers/notifications_container.tsx index eafd8efe4..b6d1748e5 100644 --- a/app/soapbox/features/ui/containers/notifications_container.tsx +++ b/app/soapbox/features/ui/containers/notifications_container.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { useIntl, MessageDescriptor } from 'react-intl'; -import { NotificationStack, NotificationObject, StyleFactoryFn } from 'react-notification'; import { useHistory } from 'react-router-dom'; import { dismissAlert } from 'soapbox/actions/alerts'; import { Button } from 'soapbox/components/ui'; import { useAppSelector, useAppDispatch } from 'soapbox/hooks'; +import { NotificationStack, NotificationObject, StyleFactoryFn } from 'soapbox/react-notification'; import type { Alert } from 'soapbox/reducers/alerts'; diff --git a/app/soapbox/hooks/index.ts b/app/soapbox/hooks/index.ts index 2cd767c2d..aad62b1f1 100644 --- a/app/soapbox/hooks/index.ts +++ b/app/soapbox/hooks/index.ts @@ -3,9 +3,11 @@ export { useAppDispatch } from './useAppDispatch'; export { useAppSelector } from './useAppSelector'; export { useDimensions } from './useDimensions'; export { useFeatures } from './useFeatures'; +export { useLocale } from './useLocale'; export { useOnScreen } from './useOnScreen'; export { useOwnAccount } from './useOwnAccount'; export { useRefEventHandler } from './useRefEventHandler'; export { useSettings } from './useSettings'; export { useSoapboxConfig } from './useSoapboxConfig'; export { useSystemTheme } from './useSystemTheme'; +export { useTheme } from './useTheme'; diff --git a/app/soapbox/hooks/useLocale.ts b/app/soapbox/hooks/useLocale.ts new file mode 100644 index 000000000..1be278002 --- /dev/null +++ b/app/soapbox/hooks/useLocale.ts @@ -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 }; diff --git a/app/soapbox/hooks/useTheme.ts b/app/soapbox/hooks/useTheme.ts new file mode 100644 index 000000000..22ececd7a --- /dev/null +++ b/app/soapbox/hooks/useTheme.ts @@ -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 }; diff --git a/app/soapbox/locales/ar.json b/app/soapbox/locales/ar.json index a7099c5f2..772c9ff01 100644 --- a/app/soapbox/locales/ar.json +++ b/app/soapbox/locales/ar.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/ast.json b/app/soapbox/locales/ast.json index 7f58ba2f0..7ec22b72b 100644 --- a/app/soapbox/locales/ast.json +++ b/app/soapbox/locales/ast.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/bg.json b/app/soapbox/locales/bg.json index 9b416afb9..d8166b0e2 100644 --- a/app/soapbox/locales/bg.json +++ b/app/soapbox/locales/bg.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/bn.json b/app/soapbox/locales/bn.json index 0e149ef51..6df9b7764 100644 --- a/app/soapbox/locales/bn.json +++ b/app/soapbox/locales/bn.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/br.json b/app/soapbox/locales/br.json index 7e3112806..33eeb6bf9 100644 --- a/app/soapbox/locales/br.json +++ b/app/soapbox/locales/br.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/co.json b/app/soapbox/locales/co.json index f5f93d172..2a36fb5b8 100644 --- a/app/soapbox/locales/co.json +++ b/app/soapbox/locales/co.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/cy.json b/app/soapbox/locales/cy.json index a206dcda2..fc63fe49b 100644 --- a/app/soapbox/locales/cy.json +++ b/app/soapbox/locales/cy.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/da.json b/app/soapbox/locales/da.json index b3de6bcf8..1ebb2ad5d 100644 --- a/app/soapbox/locales/da.json +++ b/app/soapbox/locales/da.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/el.json b/app/soapbox/locales/el.json index e2feab33c..c756fd72c 100644 --- a/app/soapbox/locales/el.json +++ b/app/soapbox/locales/el.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/en.json b/app/soapbox/locales/en.json index 99bae0101..b7d3d60cb 100644 --- a/app/soapbox/locales/en.json +++ b/app/soapbox/locales/en.json @@ -930,7 +930,7 @@ "settings.security": "Security", "settings.settings": "Settings", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/eo.json b/app/soapbox/locales/eo.json index 3c9a621db..7f7016c7e 100644 --- a/app/soapbox/locales/eo.json +++ b/app/soapbox/locales/eo.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/es-AR.json b/app/soapbox/locales/es-AR.json index 99ae5dcbc..676254cde 100644 --- a/app/soapbox/locales/es-AR.json +++ b/app/soapbox/locales/es-AR.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/es.json b/app/soapbox/locales/es.json index 1f81dc00d..e7b64d1db 100644 --- a/app/soapbox/locales/es.json +++ b/app/soapbox/locales/es.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/et.json b/app/soapbox/locales/et.json index 04467c48a..cf751a640 100644 --- a/app/soapbox/locales/et.json +++ b/app/soapbox/locales/et.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/eu.json b/app/soapbox/locales/eu.json index 6ba0bba59..ab4d91000 100644 --- a/app/soapbox/locales/eu.json +++ b/app/soapbox/locales/eu.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/fa.json b/app/soapbox/locales/fa.json index e48da6fc7..485b68f9b 100644 --- a/app/soapbox/locales/fa.json +++ b/app/soapbox/locales/fa.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/fi.json b/app/soapbox/locales/fi.json index aed3c45cf..31910f1c9 100644 --- a/app/soapbox/locales/fi.json +++ b/app/soapbox/locales/fi.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/fr.json b/app/soapbox/locales/fr.json index 8703c3b33..a6be99deb 100644 --- a/app/soapbox/locales/fr.json +++ b/app/soapbox/locales/fr.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/ga.json b/app/soapbox/locales/ga.json index 6f4c00fdc..6401356b7 100644 --- a/app/soapbox/locales/ga.json +++ b/app/soapbox/locales/ga.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/gl.json b/app/soapbox/locales/gl.json index d5b2edcba..380aa48fc 100644 --- a/app/soapbox/locales/gl.json +++ b/app/soapbox/locales/gl.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/hi.json b/app/soapbox/locales/hi.json index 2d4949780..25286e744 100644 --- a/app/soapbox/locales/hi.json +++ b/app/soapbox/locales/hi.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/hr.json b/app/soapbox/locales/hr.json index 01b9ab437..5fa9d2207 100644 --- a/app/soapbox/locales/hr.json +++ b/app/soapbox/locales/hr.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/hu.json b/app/soapbox/locales/hu.json index 7cada2236..1c9a1a5e4 100644 --- a/app/soapbox/locales/hu.json +++ b/app/soapbox/locales/hu.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/hy.json b/app/soapbox/locales/hy.json index 86e5b9d12..f5854229a 100644 --- a/app/soapbox/locales/hy.json +++ b/app/soapbox/locales/hy.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/id.json b/app/soapbox/locales/id.json index f845e3fac..7e5a2b1dc 100644 --- a/app/soapbox/locales/id.json +++ b/app/soapbox/locales/id.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/io.json b/app/soapbox/locales/io.json index a7bc96838..5f6cb6333 100644 --- a/app/soapbox/locales/io.json +++ b/app/soapbox/locales/io.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/it.json b/app/soapbox/locales/it.json index 0804cef89..8b75a4530 100644 --- a/app/soapbox/locales/it.json +++ b/app/soapbox/locales/it.json @@ -920,7 +920,7 @@ "settings.save.success": "Preferenze salvate!", "settings.security": "Sicurezza", "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}?", "snackbar.view": "Mostra", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/ka.json b/app/soapbox/locales/ka.json index d2b44ffca..05f9a3f9f 100644 --- a/app/soapbox/locales/ka.json +++ b/app/soapbox/locales/ka.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/kk.json b/app/soapbox/locales/kk.json index cd63d1a97..e4f77f3ca 100644 --- a/app/soapbox/locales/kk.json +++ b/app/soapbox/locales/kk.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/ko.json b/app/soapbox/locales/ko.json index da5bf6f0c..4e0ac92cd 100644 --- a/app/soapbox/locales/ko.json +++ b/app/soapbox/locales/ko.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/lt.json b/app/soapbox/locales/lt.json index 66b6e9e20..0254f3e80 100644 --- a/app/soapbox/locales/lt.json +++ b/app/soapbox/locales/lt.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/lv.json b/app/soapbox/locales/lv.json index 8df8269f2..00408f1e1 100644 --- a/app/soapbox/locales/lv.json +++ b/app/soapbox/locales/lv.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/mk.json b/app/soapbox/locales/mk.json index 25eaddefe..c25e28d88 100644 --- a/app/soapbox/locales/mk.json +++ b/app/soapbox/locales/mk.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/ms.json b/app/soapbox/locales/ms.json index 3e916f69f..723b073b3 100644 --- a/app/soapbox/locales/ms.json +++ b/app/soapbox/locales/ms.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/nl.json b/app/soapbox/locales/nl.json index 4521bca22..8a6c569af 100644 --- a/app/soapbox/locales/nl.json +++ b/app/soapbox/locales/nl.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/nn.json b/app/soapbox/locales/nn.json index 691a4f1bb..3b534cb0a 100644 --- a/app/soapbox/locales/nn.json +++ b/app/soapbox/locales/nn.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/no.json b/app/soapbox/locales/no.json index 754f4121c..e66c34163 100644 --- a/app/soapbox/locales/no.json +++ b/app/soapbox/locales/no.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/oc.json b/app/soapbox/locales/oc.json index b639064ef..d92fae8fa 100644 --- a/app/soapbox/locales/oc.json +++ b/app/soapbox/locales/oc.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/pt-BR.json b/app/soapbox/locales/pt-BR.json index 2f5970347..8b959d70d 100644 --- a/app/soapbox/locales/pt-BR.json +++ b/app/soapbox/locales/pt-BR.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/ro.json b/app/soapbox/locales/ro.json index 2fe3c5f69..701d4746c 100644 --- a/app/soapbox/locales/ro.json +++ b/app/soapbox/locales/ro.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/ru.json b/app/soapbox/locales/ru.json index 6b720f30a..6f3f01305 100644 --- a/app/soapbox/locales/ru.json +++ b/app/soapbox/locales/ru.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/sk.json b/app/soapbox/locales/sk.json index ce178ba88..efe98b3d7 100644 --- a/app/soapbox/locales/sk.json +++ b/app/soapbox/locales/sk.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/sl.json b/app/soapbox/locales/sl.json index d8dedbc72..e1c370c43 100644 --- a/app/soapbox/locales/sl.json +++ b/app/soapbox/locales/sl.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/sq.json b/app/soapbox/locales/sq.json index 3ecb8068d..d9dd0fae6 100644 --- a/app/soapbox/locales/sq.json +++ b/app/soapbox/locales/sq.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/sr-Latn.json b/app/soapbox/locales/sr-Latn.json index 549d0916e..629f3552f 100644 --- a/app/soapbox/locales/sr-Latn.json +++ b/app/soapbox/locales/sr-Latn.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/sr.json b/app/soapbox/locales/sr.json index b5f8e4987..61e8f8441 100644 --- a/app/soapbox/locales/sr.json +++ b/app/soapbox/locales/sr.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/sv.json b/app/soapbox/locales/sv.json index 915e97be3..474922047 100644 --- a/app/soapbox/locales/sv.json +++ b/app/soapbox/locales/sv.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/ta.json b/app/soapbox/locales/ta.json index 19177bc18..04dac445b 100644 --- a/app/soapbox/locales/ta.json +++ b/app/soapbox/locales/ta.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/te.json b/app/soapbox/locales/te.json index 991f57356..8ebfe5839 100644 --- a/app/soapbox/locales/te.json +++ b/app/soapbox/locales/te.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/th.json b/app/soapbox/locales/th.json index e689a61c8..5d21b188c 100644 --- a/app/soapbox/locales/th.json +++ b/app/soapbox/locales/th.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/tr.json b/app/soapbox/locales/tr.json index e96a4cc2e..d70a53f1a 100644 --- a/app/soapbox/locales/tr.json +++ b/app/soapbox/locales/tr.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/uk.json b/app/soapbox/locales/uk.json index a9dcc588e..4026edbb6 100644 --- a/app/soapbox/locales/uk.json +++ b/app/soapbox/locales/uk.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/zh-HK.json b/app/soapbox/locales/zh-HK.json index 5f9e4eea9..6939545ec 100644 --- a/app/soapbox/locales/zh-HK.json +++ b/app/soapbox/locales/zh-HK.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/locales/zh-TW.json b/app/soapbox/locales/zh-TW.json index ca393ee36..5fb238f09 100644 --- a/app/soapbox/locales/zh-TW.json +++ b/app/soapbox/locales/zh-TW.json @@ -920,7 +920,7 @@ "settings.save.success": "Your preferences have been saved!", "settings.security": "Security", "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}?", "snackbar.view": "View", "soapbox_config.authenticated_profile_hint": "Users must be logged-in to view replies and media on user profiles.", diff --git a/app/soapbox/normalizers/soapbox/soapbox_config.ts b/app/soapbox/normalizers/soapbox/soapbox_config.ts index efb3b0044..d90112810 100644 --- a/app/soapbox/normalizers/soapbox/soapbox_config.ts +++ b/app/soapbox/normalizers/soapbox/soapbox_config.ts @@ -89,6 +89,8 @@ export const SoapboxConfigRecord = ImmutableRecord({ customCss: ImmutableList(), defaultSettings: ImmutableMap(), extensions: ImmutableMap(), + gdpr: false, + gdprUrl: '', greentext: false, promoPanel: PromoPanelRecord(), navlinks: ImmutableMap({ diff --git a/app/soapbox/react-notification/defaultPropTypes.js b/app/soapbox/react-notification/defaultPropTypes.js new file mode 100644 index 000000000..1a3dd9d4e --- /dev/null +++ b/app/soapbox/react-notification/defaultPropTypes.js @@ -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, + ]), +}; diff --git a/app/soapbox/react-notification/index.d.ts b/app/soapbox/react-notification/index.d.ts new file mode 100644 index 000000000..7a1acd780 --- /dev/null +++ b/app/soapbox/react-notification/index.d.ts @@ -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; + /** Setting this prop to `false` will disable all inline styles. */ + style?: boolean; + /** The title for the notification. */ + title?: string | ReactElement; + /** 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 {} + + export class NotificationStack extends Component {} +} diff --git a/app/soapbox/react-notification/index.js b/app/soapbox/react-notification/index.js new file mode 100644 index 000000000..3d7da7cee --- /dev/null +++ b/app/soapbox/react-notification/index.js @@ -0,0 +1,2 @@ +export { default as Notification } from './notification'; +export { default as NotificationStack } from './notificationStack'; diff --git a/app/soapbox/react-notification/notification.js b/app/soapbox/react-notification/notification.js new file mode 100644 index 000000000..ab1cddf9b --- /dev/null +++ b/app/soapbox/react-notification/notification.js @@ -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 ( +
+
+ {this.props.title ? ( + + {this.props.title} + + ) : null} + + {/* eslint-disable */} + + {this.props.message} + + + {this.props.action ? ( + + {this.props.action} + + ) : null} +
+
+ ); + } +} + +Notification.propTypes = defaultPropTypes; + +Notification.defaultProps = { + isActive: false, + dismissAfter: 2000, + activeClassName: 'notification-bar-active' +}; + +export default Notification; diff --git a/app/soapbox/react-notification/notificationStack.js b/app/soapbox/react-notification/notificationStack.js new file mode 100644 index 000000000..dc9c2459b --- /dev/null +++ b/app/soapbox/react-notification/notificationStack.js @@ -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 => ( +
+ {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 ( + + ); + })} +
+); + +/* 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; diff --git a/app/soapbox/react-notification/stackedNotification.js b/app/soapbox/react-notification/stackedNotification.js new file mode 100644 index 000000000..c8d7200d4 --- /dev/null +++ b/app/soapbox/react-notification/stackedNotification.js @@ -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 ( + setTimeout(this.props.onDismiss, 300)} + isActive={this.state.isActive} + /> + ); + } +} + +StackedNotification.propTypes = defaultPropTypes; + +export default StackedNotification; diff --git a/package.json b/package.json index 0918b337b..a34f1b539 100644 --- a/package.json +++ b/package.json @@ -162,7 +162,6 @@ "react-inlinesvg": "^3.0.0", "react-intl": "^5.0.0", "react-motion": "^0.5.2", - "react-notification": "^6.8.5", "react-otp-input": "^2.4.0", "react-overlays": "^0.9.0", "react-popper": "^2.3.0", diff --git a/yarn.lock b/yarn.lock index b8d57fbff..4469a177a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9749,13 +9749,6 @@ react-motion@^0.5.2: prop-types "^15.5.8" 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: version "6.12.1" resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz#92dddd28f55e483a1838c5c2930e051168c1e96b"