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:
committed by
Mrugesh Mohapatra
parent
4f179ec8e1
commit
40323aef6a
@@ -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);
|
@@ -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 requestedUserSelector = createRequestedUserSelector();
|
||||
const isSessionUserSelector = createIsSessionUserSelector();
|
||||
const fetchState = userProfileFetchStateSelector(state, props);
|
||||
return {
|
||||
requestedUser: requestedUserSelector(state, props),
|
||||
isSessionUser: isSessionUserSelector(state, props),
|
||||
showLoading: fetchState.pending,
|
||||
fetchState
|
||||
const makeMapStateToProps =
|
||||
() => (state: unknown, props: IShowProfileOrFourOhFourProps) => {
|
||||
const requestedUserSelector = createRequestedUserSelector();
|
||||
const isSessionUserSelector = createIsSessionUserSelector();
|
||||
const fetchState = userProfileFetchStateSelector(
|
||||
state
|
||||
) as IShowProfileOrFourOhFourProps['fetchState'];
|
||||
return {
|
||||
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
|
@@ -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;
|
@@ -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 */
|
@@ -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);
|
@@ -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;
|
@@ -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)
|
||||
);
|
@@ -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() {
|
||||
|
@@ -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';
|
||||
|
||||
|
@@ -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 (
|
||||
|
@@ -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 (
|
||||
|
@@ -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 (
|
||||
|
@@ -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] : {};
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user