feat(client): ts-migrate /client-only-routes/ (#42587)

* feat(client): refactor /client-only-routes/ to ts

* ts refactor with no-verify for Spacer

* kebaberise filenames

* fix imports
This commit is contained in:
Shaun Hamilton
2021-06-25 15:58:07 +01:00
committed by Mrugesh Mohapatra
parent 4f179ec8e1
commit 40323aef6a
13 changed files with 224 additions and 176 deletions

View File

@@ -1,12 +1,11 @@
/* eslint-disable react/jsx-sort-props */
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { bindActionCreators, Dispatch } from 'redux';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { Grid, Row, Col, Image, Button } from '@freecodecamp/react-bootstrap';
import ShowProjectLinks from './ShowProjectLinks';
import ShowProjectLinks from './show-project-links';
import FreeCodeCampLogo from '../assets/icons/FreeCodeCamp-logo';
// eslint-disable-next-line max-len
import DonateForm from '../components/Donation/DonateForm';
@@ -23,62 +22,70 @@ import {
userByNameSelector,
fetchProfileForUser
} from '../redux';
import { certMap } from '../../src/resources/certAndProjectMap';
import { certMap } from '../resources/certAndProjectMap';
import { createFlashMessage } from '../components/Flash/redux';
import standardErrorMessage from '../utils/standardErrorMessage';
import reallyWeirdErrorMessage from '../utils/reallyWeirdErrorMessage';
import { langCodes } from '../../../config/i18n/all-langs';
// eslint-disable-next-line
// @ts-ignore
import envData from '../../../config/env.json';
import RedirectHome from '../components/RedirectHome';
import { Loader, Spacer } from '../components/helpers';
import { isEmpty } from 'lodash-es';
import { User } from '../redux/prop-types';
import { UserType } from '../redux/prop-types';
const { clientLocale } = envData;
const { clientLocale } = envData as { clientLocale: keyof typeof langCodes };
const localeCode = langCodes[clientLocale];
const propTypes = {
cert: PropTypes.shape({
username: PropTypes.string,
name: PropTypes.string,
certName: PropTypes.string,
certTitle: PropTypes.string,
completionTime: PropTypes.number,
date: PropTypes.number
}),
certDashedName: PropTypes.string,
certSlug: PropTypes.string,
createFlashMessage: PropTypes.func.isRequired,
executeGA: PropTypes.func,
fetchProfileForUser: PropTypes.func,
fetchState: PropTypes.shape({
pending: PropTypes.bool,
complete: PropTypes.bool,
errored: PropTypes.bool
}),
isDonating: PropTypes.bool,
isValidCert: PropTypes.bool,
location: PropTypes.shape({
pathname: PropTypes.string
}),
showCert: PropTypes.func.isRequired,
signedInUserName: PropTypes.string,
user: User,
userFetchState: PropTypes.shape({
complete: PropTypes.bool
}),
userFullName: PropTypes.string,
username: PropTypes.string
type CertType = {
username: string;
name: string;
certName: string;
certTitle: string;
completionTime: number;
date: number;
};
interface IShowCertificationProps {
cert: CertType;
certDashedName: string;
certSlug: string;
createFlashMessage: (payload: typeof standardErrorMessage) => void;
executeGA: (payload: Record<string, unknown>) => void;
fetchProfileForUser: (username: string) => void;
fetchState: {
pending: boolean;
complete: boolean;
errored: boolean;
};
isDonating: boolean;
isValidCert: boolean;
location: {
pathname: string;
};
showCert: ({
username,
certSlug
}: {
username: string;
certSlug: string;
}) => void;
signedInUserName: string;
user: UserType;
userFetchState: {
complete: boolean;
};
userFullName: string;
username: string;
}
const requestedUserSelector = (state, { username = '' }) =>
userByNameSelector(username.toLowerCase())(state);
const requestedUserSelector = (state: unknown, { username = '' }) =>
userByNameSelector(username.toLowerCase())(state) as UserType;
const validCertSlugs = certMap.map(cert => cert.certSlug);
const mapStateToProps = (state, props) => {
const mapStateToProps = (state: unknown, props: IShowCertificationProps) => {
const isValidCert = validCertSlugs.some(slug => slug === props.certSlug);
return createSelector(
showCertSelector,
@@ -87,7 +94,14 @@ const mapStateToProps = (state, props) => {
userFetchStateSelector,
isDonatingSelector,
requestedUserSelector,
(cert, fetchState, signedInUserName, userFetchState, isDonating, user) => ({
(
cert: CertType,
fetchState: IShowCertificationProps['fetchState'],
signedInUserName: string,
userFetchState: IShowCertificationProps['userFetchState'],
isDonating: boolean,
user
) => ({
cert,
fetchState,
isValidCert,
@@ -99,13 +113,13 @@ const mapStateToProps = (state, props) => {
);
};
const mapDispatchToProps = dispatch =>
const mapDispatchToProps = (dispatch: Dispatch) =>
bindActionCreators(
{ createFlashMessage, showCert, fetchProfileForUser, executeGA },
dispatch
);
const ShowCertification = props => {
const ShowCertification = (props: IShowCertificationProps): JSX.Element => {
const { t } = useTranslation();
const [isDonationSubmitted, setIsDonationSubmitted] = useState(false);
const [isDonationDisplayed, setIsDonationDisplayed] = useState(false);
@@ -131,6 +145,7 @@ const ShowCertification = props => {
} = props;
if (!signedInUserName || signedInUserName !== username) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
if (isEmpty(user) && username) {
fetchProfileForUser(username);
}
@@ -168,7 +183,11 @@ const ShowCertification = props => {
setIsDonationClosed(true);
};
const handleProcessing = (duration, amount, action) => {
const handleProcessing = (
duration: string,
amount: number,
action: string
) => {
props.executeGA({
type: 'event',
data: {
@@ -241,7 +260,7 @@ const ShowCertification = props => {
</div>
);
let donationSection = (
const donationSection = (
<div className='donation-section'>
{!isDonationSubmitted && (
<Row>
@@ -327,12 +346,7 @@ const ShowCertification = props => {
<main className='information'>
<div className='information-container'>
<Trans
user={displayName}
title={certTitle}
time={completionTime}
i18nKey='certification.fulltext'
>
<Trans title={certTitle} i18nKey='certification.fulltext'>
<h3>placeholder</h3>
<h1>
<strong>{{ user: displayName }}</strong>
@@ -376,6 +390,5 @@ const ShowCertification = props => {
};
ShowCertification.displayName = 'ShowCertification';
ShowCertification.propTypes = propTypes;
export default connect(mapStateToProps, mapDispatchToProps)(ShowCertification);

View File

@@ -1,5 +1,4 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { isEmpty } from 'lodash-es';
@@ -13,46 +12,58 @@ import {
import FourOhFour from '../components/FourOhFour';
import Profile from '../components/profile/Profile';
import { isBrowser } from '../../utils/index';
import { UserType } from '../redux/prop-types';
const propTypes = {
fetchProfileForUser: PropTypes.func.isRequired,
isSessionUser: PropTypes.bool,
maybeUser: PropTypes.string,
requestedUser: PropTypes.shape({
username: PropTypes.string,
profileUI: PropTypes.object
}),
showLoading: PropTypes.bool
};
interface IShowProfileOrFourOhFourProps {
fetchProfileForUser: (username: string) => void;
fetchState: {
pending: boolean;
complete: boolean;
errored: boolean;
};
isSessionUser: boolean;
maybeUser: string;
requestedUser: UserType;
showLoading: boolean;
}
const createRequestedUserSelector =
() =>
(state, { maybeUser = '' }) =>
userByNameSelector(maybeUser.toLowerCase())(state);
(state: unknown, { maybeUser = '' }) =>
userByNameSelector(maybeUser.toLowerCase())(state) as string;
const createIsSessionUserSelector =
() =>
(state, { maybeUser = '' }) =>
(state: unknown, { maybeUser = '' }) =>
maybeUser.toLowerCase() === usernameSelector(state);
const makeMapStateToProps = () => (state, props) => {
const makeMapStateToProps =
() => (state: unknown, props: IShowProfileOrFourOhFourProps) => {
const requestedUserSelector = createRequestedUserSelector();
const isSessionUserSelector = createIsSessionUserSelector();
const fetchState = userProfileFetchStateSelector(state, props);
const fetchState = userProfileFetchStateSelector(
state
) as IShowProfileOrFourOhFourProps['fetchState'];
return {
requestedUser: requestedUserSelector(state, props),
requestedUser: requestedUserSelector(
state,
props
) as IShowProfileOrFourOhFourProps['requestedUser'],
isSessionUser: isSessionUserSelector(state, props),
showLoading: fetchState.pending,
fetchState
};
};
};
const mapDispatchToProps = {
const mapDispatchToProps: {
fetchProfileForUser: IShowProfileOrFourOhFourProps['fetchProfileForUser'];
} = {
fetchProfileForUser
};
class ShowProfileOrFourOhFour extends Component {
class ShowProfileOrFourOhFour extends Component<IShowProfileOrFourOhFourProps> {
componentDidMount() {
const { requestedUser, maybeUser, fetchProfileForUser } = this.props;
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
if (isEmpty(requestedUser)) {
fetchProfileForUser(maybeUser);
}
@@ -64,6 +75,7 @@ class ShowProfileOrFourOhFour extends Component {
}
const { isSessionUser, requestedUser, showLoading } = this.props;
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
if (isEmpty(requestedUser)) {
if (showLoading) {
// We don't know if /:maybeUser is a user or not, we will show
@@ -78,13 +90,17 @@ class ShowProfileOrFourOhFour extends Component {
// We have a response from the API, and we have some state in the
// store for /:maybeUser, we now handover rendering to the Profile component
// eslint-disable-next-line
// @ts-ignore TODO: sort out whether user.portfolio is an array or obj. lit.
return <Profile isSessionUser={isSessionUser} user={requestedUser} />;
}
}
// eslint-disable-next-line
// @ts-ignore
ShowProfileOrFourOhFour.displayName = 'ShowProfileOrFourOhFour';
ShowProfileOrFourOhFour.propTypes = propTypes;
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
export default connect(
makeMapStateToProps,
mapDispatchToProps

View File

@@ -1,6 +1,5 @@
/* eslint-disable react/jsx-sort-props */
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import '../components/layouts/project-links.css';
import { maybeUrlRE } from '../utils';
import { Spacer, Link } from '../components/helpers';
@@ -8,69 +7,63 @@ import { projectMap, legacyProjectMap } from '../resources/certAndProjectMap';
import ProjectModal from '../components/SolutionViewer/ProjectModal';
import { find, first } from 'lodash-es';
import { Trans, useTranslation } from 'react-i18next';
import {
ChallengeFileType,
CompletedChallenge,
UserType
} from '../redux/prop-types';
const propTypes = {
certName: PropTypes.string,
name: PropTypes.string,
user: PropTypes.shape({
completedChallenges: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string,
solution: PropTypes.string,
githubLink: PropTypes.string,
files: PropTypes.arrayOf(
PropTypes.shape({
contents: PropTypes.string,
ext: PropTypes.string,
key: PropTypes.string,
name: PropTypes.string,
path: PropTypes.string
})
)
})
),
username: PropTypes.string
})
interface IShowProjectLinksProps {
certName: string;
name: string;
user: UserType;
}
type SolutionStateType = {
projectTitle: string;
challengeFiles: ChallengeFileType[] | null;
solution: null | string;
isOpen: boolean;
};
const initSolutionState = {
const initSolutionState: SolutionStateType = {
projectTitle: '',
files: null,
challengeFiles: null,
solution: null,
isOpen: false
};
const ShowProjectLinks = props => {
const ShowProjectLinks = (props: IShowProjectLinksProps): JSX.Element => {
const [solutionState, setSolutionState] = useState(initSolutionState);
const handleSolutionModalHide = () => setSolutionState(initSolutionState);
const { t } = useTranslation();
const getProjectSolution = (projectId, projectTitle) => {
const getProjectSolution = (projectId: string, projectTitle: string) => {
const {
user: { completedChallenges }
} = props;
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
const completedProject = find(
completedChallenges,
({ id }) => projectId === id
);
) as CompletedChallenge;
if (!completedProject) {
return null;
}
const { solution, githubLink, files } = completedProject;
const { solution, githubLink, challengeFiles } = completedProject;
const onClickHandler = () =>
setSolutionState({
projectTitle,
files,
challengeFiles,
solution,
isOpen: true
});
if (files && files.length) {
if (challengeFiles) {
return (
<button
onClick={onClickHandler}
@@ -96,7 +89,6 @@ const ShowProjectLinks = props => {
if (maybeUrlRE.test(solution)) {
return (
<a
block={'true'}
className='btn-invert'
href={solution}
rel='noopener noreferrer'
@@ -113,7 +105,7 @@ const ShowProjectLinks = props => {
);
};
const renderProjectsFor = certName => {
const renderProjectsFor = (certName: string) => {
if (certName === 'Legacy Full Stack') {
const legacyCerts = [
{ title: 'Responsive Web Design' },
@@ -124,8 +116,13 @@ const ShowProjectLinks = props => {
{ title: 'Legacy Information Security and Quality Assurance' }
];
return legacyCerts.map((cert, ind) => {
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
// @ts-expect-error Error expected until projectMap is typed
const mapToUse = projectMap[cert.title] || legacyProjectMap[cert.title];
const { certSlug } = first(mapToUse);
const { certSlug } = first(mapToUse) as { certSlug: string };
const certLocation = `/certification/${username}/${certSlug}`;
return (
<li key={ind}>
@@ -141,16 +138,23 @@ const ShowProjectLinks = props => {
);
});
}
// @ts-expect-error Error expected until projectMap is typed
return (projectMap[certName] || legacyProjectMap[certName]).map(
// @ts-expect-error Error expected until projectMap is typed
({ link, title, id }) => (
<li key={id}>
{/* @ts-expect-error Link needs to be typed */}
<Link to={link} className='project-link'>
{t(`certification.project.title.${title}`, title)}
{t(`certification.project.title.${title as string}`, title)}
</Link>
: {getProjectSolution(id, title)}
</li>
)
);
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
};
const {
@@ -158,7 +162,7 @@ const ShowProjectLinks = props => {
name,
user: { username }
} = props;
const { files, isOpen, projectTitle, solution } = solutionState;
const { challengeFiles, isOpen, projectTitle, solution } = solutionState;
return (
<div>
{t(
@@ -172,7 +176,7 @@ const ShowProjectLinks = props => {
<Spacer />
{isOpen ? (
<ProjectModal
files={files}
files={challengeFiles}
handleSolutionModalHide={handleSolutionModalHide}
isOpen={isOpen}
projectTitle={projectTitle}
@@ -202,7 +206,6 @@ const ShowProjectLinks = props => {
);
};
ShowProjectLinks.propTypes = propTypes;
ShowProjectLinks.displayName = 'ShowProjectLinks';
export default ShowProjectLinks;

View File

@@ -1,10 +1,12 @@
/* eslint-disable */
// @ts-nocheck Likely need to not use ShallowRenderer
import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';
import envData from '../../../config/env.json';
import { ShowSettings } from './ShowSettings';
import { ShowSettings } from './show-settings';
const { apiLocation } = envData;
const { apiLocation } = envData as Record<string, string>;
jest.mock('../analytics');
@@ -53,3 +55,4 @@ const loggedInProps = {
};
const loggedOutProps = { ...loggedInProps };
loggedOutProps.isSignedIn = false;
/* eslint-disable */

View File

@@ -1,5 +1,4 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { Grid } from '@freecodecamp/react-bootstrap';
@@ -25,30 +24,31 @@ import Portfolio from '../components/settings/Portfolio';
import Honesty from '../components/settings/Honesty';
import Certification from '../components/settings/Certification';
import DangerZone from '../components/settings/DangerZone';
import { User } from '../redux/prop-types';
import { UserType } from '../redux/prop-types';
const { apiLocation } = envData;
const { apiLocation } = envData as Record<string, string>;
const propTypes = {
createFlashMessage: PropTypes.func.isRequired,
isSignedIn: PropTypes.bool.isRequired,
navigate: PropTypes.func.isRequired,
showLoading: PropTypes.bool.isRequired,
submitNewAbout: PropTypes.func.isRequired,
toggleNightMode: PropTypes.func.isRequired,
updateInternetSettings: PropTypes.func.isRequired,
updateIsHonest: PropTypes.func.isRequired,
updatePortfolio: PropTypes.func.isRequired,
updateQuincyEmail: PropTypes.func.isRequired,
user: User,
verifyCert: PropTypes.func.isRequired
};
// TODO: update types for actions
interface IShowSettingsProps {
createFlashMessage: (paylaod: string[]) => void;
isSignedIn: boolean;
navigate: (location: string) => void;
showLoading: boolean;
submitNewAbout: () => void;
toggleNightMode: (theme: string) => void;
updateInternetSettings: () => void;
updateIsHonest: () => void;
updatePortfolio: () => void;
updateQuincyEmail: (isSendQuincyEmail: boolean) => void;
user: UserType;
verifyCert: () => void;
}
const mapStateToProps = createSelector(
signInLoadingSelector,
userSelector,
isSignedInSelector,
(showLoading, user, isSignedIn) => ({
(showLoading: boolean, user: UserType, isSignedIn) => ({
showLoading,
user,
isSignedIn
@@ -59,15 +59,16 @@ const mapDispatchToProps = {
createFlashMessage,
navigate,
submitNewAbout,
toggleNightMode: theme => updateUserFlag({ theme }),
toggleNightMode: (theme: string) => updateUserFlag({ theme }),
updateInternetSettings: updateUserFlag,
updateIsHonest: updateUserFlag,
updatePortfolio: updateUserFlag,
updateQuincyEmail: sendQuincyEmail => updateUserFlag({ sendQuincyEmail }),
updateQuincyEmail: (sendQuincyEmail: boolean) =>
updateUserFlag({ sendQuincyEmail }),
verifyCert
};
export function ShowSettings(props) {
export function ShowSettings(props: IShowSettingsProps): JSX.Element {
const { t } = useTranslation();
const {
createFlashMessage,
@@ -199,6 +200,5 @@ export function ShowSettings(props) {
}
ShowSettings.displayName = 'ShowSettings';
ShowSettings.propTypes = propTypes;
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
export default connect(mapStateToProps, mapDispatchToProps)(ShowSettings);

View File

@@ -1,5 +1,4 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Grid, Panel, Button } from '@freecodecamp/react-bootstrap';
import Helmet from 'react-helmet';
import { useTranslation } from 'react-i18next';
@@ -8,9 +7,13 @@ import envData from '../../../config/env.json';
import FullWidthRow from '../components/helpers/full-width-row';
import { Spacer } from '../components/helpers';
const { apiLocation } = envData;
const { apiLocation } = envData as Record<string, string>;
function ShowUnsubscribed({ unsubscribeId }) {
function ShowUnsubscribed({
unsubscribeId
}: {
unsubscribeId: string;
}): JSX.Element {
const { t } = useTranslation();
return (
<Fragment>
@@ -47,8 +50,5 @@ function ShowUnsubscribed({ unsubscribeId }) {
}
ShowUnsubscribed.displayName = 'ShowUnsubscribed';
ShowUnsubscribed.propTypes = {
unsubscribeId: PropTypes.string
};
export default ShowUnsubscribed;

View File

@@ -1,5 +1,4 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import {
@@ -24,24 +23,31 @@ import {
} from '../redux';
import { Spacer, Loader, FullWidthRow } from '../components/helpers';
const propTypes = {
email: PropTypes.string,
isSignedIn: PropTypes.bool,
reportUser: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
userFetchState: PropTypes.shape({
pending: PropTypes.bool,
complete: PropTypes.bool,
errored: PropTypes.bool
}),
username: PropTypes.string
};
interface IShowUserProps {
email: string;
isSignedIn: boolean;
reportUser: (payload: {
username: string;
reportDescription: string;
}) => void;
t: (payload: unknown, ops?: Record<string, unknown>) => string;
userFetchState: {
pending: boolean;
complete: boolean;
errored: boolean;
};
username: string;
}
const mapStateToProps = createSelector(
isSignedInSelector,
userFetchStateSelector,
userSelector,
(isSignedIn, userFetchState, { email }) => ({
(
isSignedIn,
userFetchState: IShowUserProps['userFetchState'],
{ email }: { email: string }
) => ({
isSignedIn,
userFetchState,
email
@@ -52,8 +58,11 @@ const mapDispatchToProps = {
reportUser
};
class ShowUser extends Component {
constructor(props) {
class ShowUser extends Component<IShowUserProps> {
state: {
textarea: string;
};
constructor(props: IShowUserProps) {
super(props);
this.state = {
@@ -63,14 +72,14 @@ class ShowUser extends Component {
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
handleChange(e: React.ChangeEvent<HTMLTextAreaElement>) {
const textarea = e.target.value.slice();
return this.setState({
textarea
});
}
handleSubmit(e) {
handleSubmit(e: React.FormEvent) {
e.preventDefault();
const { textarea: reportDescription } = this.state;
const { username, reportUser } = this.props;
@@ -124,11 +133,12 @@ class ShowUser extends Component {
<Row className='overflow-fix'>
<Col sm={6} smOffset={3} xs={12}>
<p>
<Trans email={email} i18nKey='report.notify-1'>
<Trans i18nKey='report.notify-1'>
<strong>{{ email }}</strong>
</Trans>
</p>
<p>{t('report.notify-2')}</p>
{/* eslint-disable @typescript-eslint/unbound-method */}
<form onSubmit={this.handleSubmit}>
<FormGroup controlId='report-user-textarea'>
<ControlLabel>{t('report.what')}</ControlLabel>
@@ -144,6 +154,7 @@ class ShowUser extends Component {
</Button>
<Spacer />
</form>
{/* eslint-disable @typescript-eslint/unbound-method */}
</Col>
</Row>
</Fragment>
@@ -151,9 +162,10 @@ class ShowUser extends Component {
}
}
// @ts-expect-error Config might need to be remedied, or component transformed into F.C.
ShowUser.displayName = 'ShowUser';
ShowUser.propTypes = propTypes;
export default withTranslation()(
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
connect(mapStateToProps, mapDispatchToProps)(ShowUser)
);

View File

@@ -4,7 +4,7 @@ import { withPrefix } from 'gatsby';
import FourOhFour from '../components/FourOhFour';
/* eslint-disable max-len */
import ShowProfileOrFourOhFour from '../client-only-routes/ShowProfileOrFourOhFour';
import ShowProfileOrFourOhFour from '../client-only-routes/show-profile-or-four-oh-four';
/* eslint-enable max-len */
function FourOhFourPage() {

View File

@@ -3,7 +3,7 @@ import { Router } from '@reach/router';
import { withPrefix } from 'gatsby';
import RedirectHome from '../components/RedirectHome';
import ShowCertification from '../client-only-routes/ShowCertification';
import ShowCertification from '../client-only-routes/show-certification';
import './certification.css';

View File

@@ -3,7 +3,7 @@ import { Router } from '@reach/router';
import { withPrefix } from 'gatsby';
import RedirectHome from '../components/RedirectHome';
import ShowSettings from '../client-only-routes/ShowSettings';
import ShowSettings from '../client-only-routes/show-settings';
function Settings() {
return (

View File

@@ -3,7 +3,7 @@ import { Router } from '@reach/router';
import { withPrefix } from 'gatsby';
import RedirectHome from '../components/RedirectHome';
import ShowUnsubscribed from '../client-only-routes/ShowUnsubscribed';
import ShowUnsubscribed from '../client-only-routes/show-unsubscribed';
function Unsubscribed() {
return (

View File

@@ -3,7 +3,7 @@ import { Router } from '@reach/router';
import { withPrefix } from 'gatsby';
import RedirectHome from '../components/RedirectHome';
import ShowUser from '../client-only-routes/ShowUser';
import ShowUser from '../client-only-routes/show-user';
function User() {
return (

View File

@@ -215,6 +215,7 @@ export const shouldRequestDonationSelector = state => {
export const userByNameSelector = username => state => {
const { user } = state[ns];
// TODO: Why return a string or empty objet literal?
return username in user ? user[username] : {};
};