From 5731b9b1c73fa6b2414248c98a0c42714e964455 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 19 Apr 2022 14:37:48 -0500 Subject: [PATCH 01/16] Redirect all /auth routes --- app/soapbox/actions/auth.js | 4 +- app/soapbox/actions/external_auth.js | 2 +- app/soapbox/components/sidebar_menu.tsx | 2 +- app/soapbox/containers/soapbox.js | 4 -- .../auth_login/components/login_page.js | 2 +- .../features/auth_login/components/logout.tsx | 28 +++++++++++ app/soapbox/features/public_layout/index.js | 2 +- app/soapbox/features/security/mfa_form.js | 2 +- app/soapbox/features/settings/index.js | 2 +- .../features/ui/components/link_footer.tsx | 2 +- .../ui/components/profile-dropdown.tsx | 2 +- app/soapbox/features/ui/index.js | 50 ++++++++++++++----- .../features/ui/util/async-components.js | 4 ++ .../features/verification/waitlist_page.js | 2 +- 14 files changed, 82 insertions(+), 26 deletions(-) create mode 100644 app/soapbox/features/auth_login/components/logout.tsx diff --git a/app/soapbox/actions/auth.js b/app/soapbox/actions/auth.js index 5c0fc467a..00f9c606d 100644 --- a/app/soapbox/actions/auth.js +++ b/app/soapbox/actions/auth.js @@ -249,10 +249,12 @@ export function logOut(intl) { const account = getLoggedInAccount(state); const standalone = isStandalone(state); + if (!account) return dispatch(noOp); + const params = { client_id: state.getIn(['auth', 'app', 'client_id']), client_secret: state.getIn(['auth', 'app', 'client_secret']), - token: state.getIn(['auth', 'users', account.get('url'), 'access_token']), + token: state.getIn(['auth', 'users', account.url, 'access_token']), }; return Promise.all([ diff --git a/app/soapbox/actions/external_auth.js b/app/soapbox/actions/external_auth.js index 8bf0bba3f..4f389667f 100644 --- a/app/soapbox/actions/external_auth.js +++ b/app/soapbox/actions/external_auth.js @@ -42,7 +42,7 @@ function createExternalApp(instance, baseURL) { const params = { client_name: sourceCode.displayName, - redirect_uris: `${window.location.origin}/auth/external`, + redirect_uris: `${window.location.origin}/login/external`, website: sourceCode.homepage, scopes, }; diff --git a/app/soapbox/components/sidebar_menu.tsx b/app/soapbox/components/sidebar_menu.tsx index c31ec29db..e7cec5d95 100644 --- a/app/soapbox/components/sidebar_menu.tsx +++ b/app/soapbox/components/sidebar_menu.tsx @@ -285,7 +285,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
- - - - diff --git a/app/soapbox/features/auth_login/components/login_page.js b/app/soapbox/features/auth_login/components/login_page.js index fbdd210e4..867a80863 100644 --- a/app/soapbox/features/auth_login/components/login_page.js +++ b/app/soapbox/features/auth_login/components/login_page.js @@ -76,7 +76,7 @@ class LoginPage extends ImmutablePureComponent { const { standalone } = this.props; const { isLoading, mfa_auth_needed, mfa_token, shouldRedirect } = this.state; - if (standalone) return ; + if (standalone) return ; if (shouldRedirect) return ; diff --git a/app/soapbox/features/auth_login/components/logout.tsx b/app/soapbox/features/auth_login/components/logout.tsx new file mode 100644 index 000000000..1a44f292f --- /dev/null +++ b/app/soapbox/features/auth_login/components/logout.tsx @@ -0,0 +1,28 @@ +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch } from 'react-redux'; +import { Redirect } from 'react-router-dom'; + +import { logOut } from 'soapbox/actions/auth'; +import { Spinner } from 'soapbox/components/ui'; + +/** Component that logs the user out when rendered */ +const Logout: React.FC = () => { + const intl = useIntl(); + const dispatch = useDispatch(); + const [done, setDone] = useState(false); + + useEffect(() => { + (dispatch(logOut(intl)) as any) + .then(() => setDone(true)) + .catch(console.warn); + }); + + if (done) { + return ; + } else { + return ; + } +}; + +export default Logout; diff --git a/app/soapbox/features/public_layout/index.js b/app/soapbox/features/public_layout/index.js index 735ccdb2f..d7ff35155 100644 --- a/app/soapbox/features/public_layout/index.js +++ b/app/soapbox/features/public_layout/index.js @@ -30,7 +30,7 @@ class PublicLayout extends ImmutablePureComponent { const { standalone } = this.props; if (standalone) { - return ; + return ; } return ( diff --git a/app/soapbox/features/security/mfa_form.js b/app/soapbox/features/security/mfa_form.js index dd15bfb3d..f09e13cd8 100644 --- a/app/soapbox/features/security/mfa_form.js +++ b/app/soapbox/features/security/mfa_form.js @@ -21,7 +21,7 @@ import { Button, Card, CardBody, CardHeader, CardTitle, Column, Form, FormAction /* Security settings page for user account -Routed to /auth/mfa +Routed to /settings/mfa Includes following features: - Set up Multi-factor Auth */ diff --git a/app/soapbox/features/settings/index.js b/app/soapbox/features/settings/index.js index f78cddad7..92a10f90a 100644 --- a/app/soapbox/features/settings/index.js +++ b/app/soapbox/features/settings/index.js @@ -33,7 +33,7 @@ const Settings = () => { const navigateToChangeEmail = React.useCallback(() => history.push('/settings/email'), [history]); const navigateToChangePassword = React.useCallback(() => history.push('/settings/password'), [history]); - const navigateToMfa = React.useCallback(() => history.push('/auth/mfa'), [history]); + const navigateToMfa = React.useCallback(() => history.push('/settings/mfa'), [history]); const navigateToEditProfile = React.useCallback(() => history.push('/settings/profile'), [history]); const isMfaEnabled = mfa.getIn(['settings', 'totp']); diff --git a/app/soapbox/features/ui/components/link_footer.tsx b/app/soapbox/features/ui/components/link_footer.tsx index 84a7c5fc5..da0fc48d6 100644 --- a/app/soapbox/features/ui/components/link_footer.tsx +++ b/app/soapbox/features/ui/components/link_footer.tsx @@ -59,7 +59,7 @@ const LinkFooter: React.FC = (): JSX.Element => { {(features.federating && features.accountMoving) && ( )} - + } diff --git a/app/soapbox/features/ui/components/profile-dropdown.tsx b/app/soapbox/features/ui/components/profile-dropdown.tsx index 850f54611..71d5c1101 100644 --- a/app/soapbox/features/ui/components/profile-dropdown.tsx +++ b/app/soapbox/features/ui/components/profile-dropdown.tsx @@ -87,7 +87,7 @@ const ProfileDropdown: React.FC = ({ account, children }) => { menu.push({ text: intl.formatMessage(messages.logout, { acct: account.acct }), - to: '/auth/sign_out', + to: '/logout', action: handleLogOut, icon: require('@tabler/icons/icons/logout.svg'), }); diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index af27d453a..9af173cb3 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -120,6 +120,7 @@ import { CreateApp, SettingsStore, TestTimeline, + LogoutPage, } from './util/async-components'; import { WrappedRoute } from './util/react_router_helpers'; @@ -221,11 +222,16 @@ class SwitchingColumnsArea extends React.PureComponent { const authenticatedProfile = soapbox.get('authenticatedProfile'); const hasCrypto = soapbox.get('cryptoAddresses').size > 0; + // NOTE: Mastodon and Pleroma route some basenames to the backend. + // When adding new routes, use a basename that does NOT conflict + // with a known backend route, but DO redirect the backend route + // to the corresponding component as a fallback. + // Ex: use /login instead of /auth, but redirect /auth to /login return ( - - - + + + @@ -240,6 +246,7 @@ class SwitchingColumnsArea extends React.PureComponent { + {/* Gab groups */} {/* @@ -251,7 +258,7 @@ class SwitchingColumnsArea extends React.PureComponent { */} - {/* Redirects from Mastodon, Pleroma FE, etc. to fix old bookmarks */} + {/* Mastodon web routes */} @@ -259,6 +266,8 @@ class SwitchingColumnsArea extends React.PureComponent { + + {/* Pleroma FE web routes */} @@ -268,12 +277,35 @@ class SwitchingColumnsArea extends React.PureComponent { - + + + {/* Gab */} + {/* Mastodon rendered pages */} + + + + + + + + + + + + + + {/* Pleroma hard-coded email URLs */} + + {/* Soapbox Legacy redirects */} + + + + @@ -310,14 +342,8 @@ class SwitchingColumnsArea extends React.PureComponent { {features.scheduledStatuses && } - - - - - - @@ -327,11 +353,11 @@ class SwitchingColumnsArea extends React.PureComponent { + {/* */} - diff --git a/app/soapbox/features/ui/util/async-components.js b/app/soapbox/features/ui/util/async-components.js index 17f54d3f4..cd202afb5 100644 --- a/app/soapbox/features/ui/util/async-components.js +++ b/app/soapbox/features/ui/util/async-components.js @@ -250,6 +250,10 @@ export function ExternalLogin() { return import(/* webpackChunkName: "features/external_login" */'../../external_login'); } +export function LogoutPage() { + return import(/* webpackChunkName: "features/auth_login" */'../../auth_login/components/logout'); +} + export function Settings() { return import(/* webpackChunkName: "features/settings" */'../../settings'); } diff --git a/app/soapbox/features/verification/waitlist_page.js b/app/soapbox/features/verification/waitlist_page.js index 819e6af85..01b71bcae 100644 --- a/app/soapbox/features/verification/waitlist_page.js +++ b/app/soapbox/features/verification/waitlist_page.js @@ -34,7 +34,7 @@ const WaitlistPage = ({ account }) => {
-
From 7630c64dddee620d3246dd305e67ee0b8bf39d17 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 19 Apr 2022 15:04:34 -0500 Subject: [PATCH 02/16] ExternalLogin: convert to tsx --- app/soapbox/components/ui/input/input.tsx | 4 +- .../features/auth_login/components/logout.tsx | 2 +- .../components/external-login-form.tsx | 70 ++++++++++++++++ .../components/external_login_form.js | 82 ------------------- app/soapbox/features/external_login/index.js | 12 --- app/soapbox/features/external_login/index.tsx | 10 +++ 6 files changed, 83 insertions(+), 97 deletions(-) create mode 100644 app/soapbox/features/external_login/components/external-login-form.tsx delete mode 100644 app/soapbox/features/external_login/components/external_login_form.js delete mode 100644 app/soapbox/features/external_login/index.js create mode 100644 app/soapbox/features/external_login/index.tsx diff --git a/app/soapbox/components/ui/input/input.tsx b/app/soapbox/components/ui/input/input.tsx index 5314ee583..f30ae5d09 100644 --- a/app/soapbox/components/ui/input/input.tsx +++ b/app/soapbox/components/ui/input/input.tsx @@ -11,7 +11,7 @@ const messages = defineMessages({ hidePassword: { id: 'input.password.hide_password', defaultMessage: 'Hide password' }, }); -interface IInput extends Pick, 'onChange' | 'type'> { +interface IInput extends Pick, 'onChange' | 'type' | 'autoComplete' | 'autoCorrect' | 'autoCapitalize' | 'required'> { autoFocus?: boolean, defaultValue?: string, className?: string, @@ -20,7 +20,7 @@ interface IInput extends Pick, 'onCh placeholder?: string, value?: string, onChange?: (event: React.ChangeEvent) => void, - type: 'text' | 'email' | 'tel' | 'password' + type: 'text' | 'email' | 'tel' | 'password', } const Input = React.forwardRef( diff --git a/app/soapbox/features/auth_login/components/logout.tsx b/app/soapbox/features/auth_login/components/logout.tsx index 1a44f292f..e702e146b 100644 --- a/app/soapbox/features/auth_login/components/logout.tsx +++ b/app/soapbox/features/auth_login/components/logout.tsx @@ -13,7 +13,7 @@ const Logout: React.FC = () => { const [done, setDone] = useState(false); useEffect(() => { - (dispatch(logOut(intl)) as any) + dispatch(logOut(intl) as any) .then(() => setDone(true)) .catch(console.warn); }); diff --git a/app/soapbox/features/external_login/components/external-login-form.tsx b/app/soapbox/features/external_login/components/external-login-form.tsx new file mode 100644 index 000000000..2d6c7d2fa --- /dev/null +++ b/app/soapbox/features/external_login/components/external-login-form.tsx @@ -0,0 +1,70 @@ +import React, { useState, useEffect } from 'react'; +import { useIntl, FormattedMessage, defineMessages } from 'react-intl'; +import { useDispatch } from 'react-redux'; + +import { externalLogin, loginWithCode } from 'soapbox/actions/external_auth'; +import { Button, Form, FormActions, FormGroup, Input, Spinner } from 'soapbox/components/ui'; + +const messages = defineMessages({ + instanceLabel: { id: 'login.fields.instance_label', defaultMessage: 'Instance' }, + instancePlaceholder: { id: 'login.fields.instance_placeholder', defaultMessage: 'example.com' }, +}); + +/** Form for logging into a remote instance */ +const ExternalLoginForm: React.FC = () => { + const code = new URLSearchParams(window.location.search).get('code'); + + const intl = useIntl(); + const dispatch = useDispatch(); + + const [host, setHost] = useState(''); + const [isLoading, setLoading] = useState(false); + + const handleHostChange: React.ChangeEventHandler = ({ currentTarget }) => { + setHost(currentTarget.value); + }; + + const handleSubmit = () => { + setLoading(true); + + dispatch(externalLogin(host) as any) + .then(() => setLoading(false)) + .catch(() => setLoading(false)); + }; + + useEffect(() => { + if (code) { + dispatch(loginWithCode(code)); + } + }); + + if (code) { + return ; + } + + return ( +
+ + + + + + + +
+ ); +}; + +export default ExternalLoginForm; diff --git a/app/soapbox/features/external_login/components/external_login_form.js b/app/soapbox/features/external_login/components/external_login_form.js deleted file mode 100644 index 8cbebc70c..000000000 --- a/app/soapbox/features/external_login/components/external_login_form.js +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { injectIntl, FormattedMessage, defineMessages } from 'react-intl'; -import { connect } from 'react-redux'; - -import { externalLogin, loginWithCode } from 'soapbox/actions/external_auth'; -import { Spinner } from 'soapbox/components/ui'; -import { SimpleForm, FieldsGroup, TextInput } from 'soapbox/features/forms'; - -const messages = defineMessages({ - instanceLabel: { id: 'login.fields.instance_label', defaultMessage: 'Instance' }, - instancePlaceholder: { id: 'login.fields.instance_placeholder', defaultMessage: 'example.com' }, -}); - -export default @connect() -@injectIntl -class ExternalLoginForm extends ImmutablePureComponent { - - state = { - host: '', - isLoading: false, - } - - handleHostChange = ({ target }) => { - this.setState({ host: target.value }); - } - - handleSubmit = e => { - const { dispatch } = this.props; - const { host } = this.state; - - this.setState({ isLoading: true }); - - dispatch(externalLogin(host)) - .then(() => this.setState({ isLoading: false })) - .catch(() => this.setState({ isLoading: false })); - } - - componentDidMount() { - const code = new URLSearchParams(window.location.search).get('code'); - - if (code) { - this.setState({ code }); - this.props.dispatch(loginWithCode(code)); - } - } - - render() { - const { intl } = this.props; - const { isLoading, code } = this.state; - - if (code) { - return ; - } - - return ( - -
- - - -
-
- -
-
- ); - } - -} diff --git a/app/soapbox/features/external_login/index.js b/app/soapbox/features/external_login/index.js deleted file mode 100644 index 881c2e08f..000000000 --- a/app/soapbox/features/external_login/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import ImmutablePureComponent from 'react-immutable-pure-component'; - -import ExternalLoginForm from './components/external_login_form'; - -export default class ExternalLoginPage extends ImmutablePureComponent { - - render() { - return ; - } - -} diff --git a/app/soapbox/features/external_login/index.tsx b/app/soapbox/features/external_login/index.tsx new file mode 100644 index 000000000..510014dc2 --- /dev/null +++ b/app/soapbox/features/external_login/index.tsx @@ -0,0 +1,10 @@ +import React from 'react'; + +import ExternalLoginForm from './components/external-login-form'; + +/** Page for logging into a remote instance */ +const ExternalLoginPage: React.FC = () => { + return ; +}; + +export default ExternalLoginPage; From 79c04713e1d0bc885d9a6144a6b613d63d59a512 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 19 Apr 2022 15:06:35 -0500 Subject: [PATCH 03/16] LoginForm: convert to tsx --- .../components/{login_form.js => login_form.tsx} | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) rename app/soapbox/features/auth_login/components/{login_form.js => login_form.tsx} (89%) diff --git a/app/soapbox/features/auth_login/components/login_form.js b/app/soapbox/features/auth_login/components/login_form.tsx similarity index 89% rename from app/soapbox/features/auth_login/components/login_form.js rename to app/soapbox/features/auth_login/components/login_form.tsx index 965473c25..2b94e843c 100644 --- a/app/soapbox/features/auth_login/components/login_form.js +++ b/app/soapbox/features/auth_login/components/login_form.tsx @@ -1,9 +1,8 @@ -import PropTypes from 'prop-types'; import React from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { Link } from 'react-router-dom'; -import { Button, Form, FormActions, FormGroup, Input } from '../../../components/ui'; +import { Button, Form, FormActions, FormGroup, Input } from 'soapbox/components/ui'; const messages = defineMessages({ username: { @@ -20,7 +19,12 @@ const messages = defineMessages({ }, }); -const LoginForm = ({ isLoading, handleSubmit }) => { +interface ILoginForm { + isLoading: boolean, + handleSubmit: React.FormEventHandler, +} + +const LoginForm: React.FC = ({ isLoading, handleSubmit }) => { const intl = useIntl(); return ( @@ -82,9 +86,4 @@ const LoginForm = ({ isLoading, handleSubmit }) => { ); }; -LoginForm.propTypes = { - isLoading: PropTypes.bool.isRequired, - handleSubmit: PropTypes.func.isRequired, -}; - export default LoginForm; From 33c2fcdea78f3999ddc586653b542d9c5d18949f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 19 Apr 2022 15:10:03 -0500 Subject: [PATCH 04/16] LoginForm: "Email or username", fixes #895 --- .../features/auth_login/components/login_form.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/app/soapbox/features/auth_login/components/login_form.tsx b/app/soapbox/features/auth_login/components/login_form.tsx index 2b94e843c..dd125b568 100644 --- a/app/soapbox/features/auth_login/components/login_form.tsx +++ b/app/soapbox/features/auth_login/components/login_form.tsx @@ -6,12 +6,8 @@ import { Button, Form, FormActions, FormGroup, Input } from 'soapbox/components/ const messages = defineMessages({ username: { - id: 'login.fields.username_placeholder', - defaultMessage: 'Username', - }, - email: { - id: 'login.fields.email_placeholder', - defaultMessage: 'Email address', + id: 'login.fields.username_label', + defaultMessage: 'Email or username', }, password: { id: 'login.fields.password_placeholder', @@ -35,10 +31,10 @@ const LoginForm: React.FC = ({ isLoading, handleSubmit }) => {
- + Date: Tue, 19 Apr 2022 15:13:48 -0500 Subject: [PATCH 05/16] AuthLayout: convert to tsx --- app/soapbox/features/auth_layout/{index.js => index.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/soapbox/features/auth_layout/{index.js => index.tsx} (100%) diff --git a/app/soapbox/features/auth_layout/index.js b/app/soapbox/features/auth_layout/index.tsx similarity index 100% rename from app/soapbox/features/auth_layout/index.js rename to app/soapbox/features/auth_layout/index.tsx From 564524bfcc87dab18431bd9448e29790569b478a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 19 Apr 2022 15:20:50 -0500 Subject: [PATCH 06/16] AuthLayout: use logo from soapboxConfig --- app/soapbox/features/auth_layout/index.tsx | 79 +++++++++++-------- app/soapbox/features/ui/components/navbar.tsx | 2 +- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/app/soapbox/features/auth_layout/index.tsx b/app/soapbox/features/auth_layout/index.tsx index eca951abd..81035b534 100644 --- a/app/soapbox/features/auth_layout/index.tsx +++ b/app/soapbox/features/auth_layout/index.tsx @@ -1,8 +1,10 @@ import React from 'react'; import { Link, Redirect, Route, Switch } from 'react-router-dom'; +import SvgIcon from 'soapbox/components/ui/icon/svg-icon'; import BundleContainer from 'soapbox/features/ui/containers/bundle_container'; import { NotificationsContainer } from 'soapbox/features/ui/util/async-components'; +import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks'; import { Card, CardBody } from '../../components/ui'; import LoginPage from '../auth_login/components/login_page'; @@ -12,43 +14,56 @@ import PasswordResetConfirm from '../auth_login/components/password_reset_confir import Verification from '../verification'; import EmailPassthru from '../verification/email_passthru'; -const AuthLayout = () => ( -
-
+const AuthLayout = () => { + const { logo } = useSoapboxConfig(); + const siteTitle = useAppSelector(state => state.instance.title); -
-
- - Logo - -
+ return ( +
+
-
-
- - - - - - - - - {/* */} +
+
+ + {logo ? ( + {siteTitle} + ) : ( + + )} + +
- - - - - +
+
+ + + + + + + + + {/* */} + + + + + + +
-
-
+ - - {(Component) => } - -
-); + + {(Component) => } + +
+ ); +}; export default AuthLayout; diff --git a/app/soapbox/features/ui/components/navbar.tsx b/app/soapbox/features/ui/components/navbar.tsx index 8b7f9286b..5a075eead 100644 --- a/app/soapbox/features/ui/components/navbar.tsx +++ b/app/soapbox/features/ui/components/navbar.tsx @@ -24,7 +24,7 @@ const Navbar = () => { const singleUserMode = soapboxConfig.get('singleUserMode'); // In demo mode, use the Soapbox logo - const logo = settings.get('demo') ? require('images/soapbox-logo.svg') : soapboxConfig.get('logo'); + const logo = settings.get('demo') ? require('images/soapbox-logo.svg') : soapboxConfig.logo; const onOpenSidebar = () => dispatch(openSidebar()); From 0c98157fb3553f2ff053b0f50d0649ba79fbbe0c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 19 Apr 2022 15:24:12 -0500 Subject: [PATCH 07/16] Delete WelcomeButton, remove TruthSocial remnants --- .../compose/components/compose_form.js | 2 +- app/soapbox/features/preferences/index.tsx | 2 +- .../features/ui/components/boost_modal.js | 2 +- .../features/ui/components/welcome_button.js | 62 ------------------- 4 files changed, 3 insertions(+), 65 deletions(-) delete mode 100644 app/soapbox/features/ui/components/welcome_button.js diff --git a/app/soapbox/features/compose/components/compose_form.js b/app/soapbox/features/compose/components/compose_form.js index 2f054836f..4fe153bbe 100644 --- a/app/soapbox/features/compose/components/compose_form.js +++ b/app/soapbox/features/compose/components/compose_form.js @@ -39,7 +39,7 @@ const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u20 const messages = defineMessages({ placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What\'s on your mind?' }, spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' }, - publish: { id: 'compose_form.publish', defaultMessage: 'Truth' }, + publish: { id: 'compose_form.publish', defaultMessage: 'Post' }, publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' }, message: { id: 'compose_form.message', defaultMessage: 'Message' }, schedule: { id: 'compose_form.schedule', defaultMessage: 'Schedule' }, diff --git a/app/soapbox/features/preferences/index.tsx b/app/soapbox/features/preferences/index.tsx index df0d20639..54d8e4171 100644 --- a/app/soapbox/features/preferences/index.tsx +++ b/app/soapbox/features/preferences/index.tsx @@ -112,7 +112,7 @@ const Preferences = () => { } + label={} hint={} > diff --git a/app/soapbox/features/ui/components/boost_modal.js b/app/soapbox/features/ui/components/boost_modal.js index 8a3862b35..faf648b12 100644 --- a/app/soapbox/features/ui/components/boost_modal.js +++ b/app/soapbox/features/ui/components/boost_modal.js @@ -52,7 +52,7 @@ class BoostModal extends ImmutablePureComponent { return ( diff --git a/app/soapbox/features/ui/components/welcome_button.js b/app/soapbox/features/ui/components/welcome_button.js deleted file mode 100644 index c9c8fe386..000000000 --- a/app/soapbox/features/ui/components/welcome_button.js +++ /dev/null @@ -1,62 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { defineMessages, injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; - -import { openComposeWithText } from 'soapbox/actions/compose'; -import { Button } from 'soapbox/components/ui'; -import emojify from 'soapbox/features/emoji/emoji'; - -const buildWelcomeMessage = account => ( - `Yo @${account.get('acct')} nice to have you on TRUTH! - -Here's the lay of the land... - -Got suggestions? Post a TRUTH (<- this is what we call a post) & tag the @suggestions account. - -Come across a bug? Feel free to let us know by tagging the @Bug account in a TRUTH! Screenshots encouraged! - -Also, if you want to just chat about the product... feel free to drop some 💥 TRUTH 💣 on me! Tag @Billy! - -Finally, make sure to invite only your favorite peeps by hitting Invites in the sidebar.` -); - -const messages = defineMessages({ - welcome: { id: 'account.welcome', defaultMessage: 'Welcome' }, -}); - -const mapDispatchToProps = (dispatch) => ({ - onClick(account) { - const text = buildWelcomeMessage(account); - dispatch(openComposeWithText(text)); - }, -}); - -export default @connect(undefined, mapDispatchToProps) -@injectIntl -class WelcomeButton extends ImmutablePureComponent { - - static propTypes = { - intl: PropTypes.object.isRequired, - account: ImmutablePropTypes.record.isRequired, - onClick: PropTypes.func.isRequired, - }; - - handleClick = () => { - this.props.onClick(this.props.account); - } - - render() { - const { intl } = this.props; - - return ( - - ); - } - -} From 45018a6fb0bbd79c72447d5db6850e21b1dc765d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 19 Apr 2022 16:30:10 -0500 Subject: [PATCH 08/16] Refactor ErrorBoundary, let it destroy the ServiceWorker --- .../{error_boundary.js => error_boundary.tsx} | 118 +++++++++++------- .../normalizers/soapbox/soapbox_config.ts | 1 + 2 files changed, 74 insertions(+), 45 deletions(-) rename app/soapbox/components/{error_boundary.js => error_boundary.tsx} (53%) diff --git a/app/soapbox/components/error_boundary.js b/app/soapbox/components/error_boundary.tsx similarity index 53% rename from app/soapbox/components/error_boundary.js rename to app/soapbox/components/error_boundary.tsx index 9489b0e75..b07bb2733 100644 --- a/app/soapbox/components/error_boundary.js +++ b/app/soapbox/components/error_boundary.tsx @@ -1,43 +1,58 @@ -import PropTypes from 'prop-types'; import React from 'react'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; -import { NODE_ENV } from 'soapbox/build_config'; +import { getSoapboxConfig } from 'soapbox/actions/soapbox'; +import BuildConfig from 'soapbox/build_config'; import { Text, Stack } from 'soapbox/components/ui'; +import SvgIcon from 'soapbox/components/ui/icon/svg-icon'; import { captureException } from 'soapbox/monitoring'; import sourceCode from 'soapbox/utils/code'; -import { getSoapboxConfig } from '../actions/soapbox'; +import type { RootState } from 'soapbox/store'; -const mapStateToProps = (state) => { - const soapboxConfig = getSoapboxConfig(state); +const goHome = () => location.href = '/'; + +/** Unregister the ServiceWorker */ +// https://stackoverflow.com/a/49771828/8811886 +const unregisterSw = async() => { + if (!navigator.serviceWorker) return; + const registrations = await navigator.serviceWorker.getRegistrations(); + const unregisterAll = registrations.map(r => r.unregister()); + await Promise.all(unregisterAll); +}; + +const mapStateToProps = (state: RootState) => { + const { links, logo } = getSoapboxConfig(state); return { siteTitle: state.instance.title, - helpLink: soapboxConfig.getIn(['links', 'help']), - supportLink: soapboxConfig.getIn(['links', 'support']), - statusLink: soapboxConfig.getIn(['links', 'status']), + logo, + links, }; }; -@connect(mapStateToProps) -class ErrorBoundary extends React.PureComponent { +type Props = ReturnType; - static propTypes = { - children: PropTypes.node, - siteTitle: PropTypes.string, - supportLink: PropTypes.string, - helpLink: PropTypes.string, - statusLink: PropTypes.string, - }; +type State = { + hasError: boolean, + error: any, + componentStack: any, + browser?: Bowser.Parser.Parser, +} - state = { +class ErrorBoundary extends React.PureComponent { + + state: State = { hasError: false, + error: undefined, componentStack: undefined, + browser: undefined, } - componentDidCatch(error, info) { + textarea: HTMLTextAreaElement | null = null; + + componentDidCatch(error: any, info: any): void { captureException(error); this.setState({ @@ -55,11 +70,11 @@ class ErrorBoundary extends React.PureComponent { .catch(() => {}); } - setTextareaRef = c => { + setTextareaRef: React.RefCallback = c => { this.textarea = c; } - handleCopy = e => { + handleCopy: React.MouseEventHandler = () => { if (!this.textarea) return; this.textarea.select(); @@ -68,25 +83,30 @@ class ErrorBoundary extends React.PureComponent { document.execCommand('copy'); } - getErrorText = () => { + getErrorText = (): string => { const { error, componentStack } = this.state; return error + componentStack; } - clearCookies = e => { + clearCookies: React.MouseEventHandler = (e) => { localStorage.clear(); sessionStorage.clear(); + + if ('serviceWorker' in navigator) { + e.preventDefault(); + unregisterSw().then(goHome).catch(goHome); + } } render() { const { browser, hasError } = this.state; - const { children, siteTitle, helpLink, statusLink, supportLink } = this.props; + const { children, siteTitle, logo, links } = this.props; if (!hasError) { return children; } - const isProduction = NODE_ENV === 'production'; + const isProduction = BuildConfig.NODE_ENV === 'production'; const errorText = this.getErrorText(); @@ -95,7 +115,11 @@ class ErrorBoundary extends React.PureComponent {
@@ -105,14 +129,18 @@ class ErrorBoundary extends React.PureComponent {

- We're sorry for the interruption. If the problem persists, please reach out to our support team. You - may also try to - - - {' ' }(this will log you out). + + + + ) }} + />

@@ -144,7 +172,7 @@ class ErrorBoundary extends React.PureComponent { {browser && ( - Browser + {browser.getBrowserName()} {browser.getBrowserVersion()} )} @@ -155,28 +183,28 @@ class ErrorBoundary extends React.PureComponent {