diff --git a/client/src/components/landing/landing.css b/client/src/components/landing/landing.css
index ac5e4b0e41..6a4e9c32b7 100644
--- a/client/src/components/landing/landing.css
+++ b/client/src/components/landing/landing.css
@@ -9,6 +9,10 @@
white-space: pre-line;
}
+.cert-btn {
+ margin-top: 40px;
+}
+
.landing-page ul {
list-style: none;
padding-left: 0px;
diff --git a/client/src/templates/Introduction/SuperBlockIntro.js b/client/src/templates/Introduction/SuperBlockIntro.js
index 32225f6fdd..178ba7433d 100644
--- a/client/src/templates/Introduction/SuperBlockIntro.js
+++ b/client/src/templates/Introduction/SuperBlockIntro.js
@@ -17,6 +17,7 @@ import { Spacer } from '../../components/helpers';
import {
currentChallengeIdSelector,
userFetchStateSelector,
+ signInLoadingSelector,
isSignedInSelector,
tryToShowDonationModal,
userSelector
@@ -50,6 +51,7 @@ const propTypes = {
})
}),
resetExpansion: PropTypes.func,
+ signInLoading: PropTypes.bool,
t: PropTypes.func,
toggleBlock: PropTypes.func,
tryToShowDonationModal: PropTypes.func.isRequired,
@@ -62,11 +64,13 @@ const mapStateToProps = state => {
return createSelector(
currentChallengeIdSelector,
isSignedInSelector,
+ signInLoadingSelector,
userFetchStateSelector,
userSelector,
- (currentChallengeId, isSignedIn, fetchState, user) => ({
+ (currentChallengeId, isSignedIn, signInLoading, fetchState, user) => ({
currentChallengeId,
isSignedIn,
+ signInLoading,
fetchState,
user
})
@@ -150,6 +154,7 @@ const SuperBlockIntroductionPage = props => {
allChallengeNode: { edges }
},
isSignedIn,
+ signInLoading,
t,
user
} = props;
@@ -202,7 +207,7 @@ const SuperBlockIntroductionPage = props => {
)}
- {!isSignedIn && (
+ {!isSignedIn && !signInLoading && (
{t('buttons.logged-out-cta-btn')}
diff --git a/client/src/templates/Introduction/components/CertChallenge.js b/client/src/templates/Introduction/components/CertChallenge.js
index e1c5df7c4e..8e9ffb06f3 100644
--- a/client/src/templates/Introduction/components/CertChallenge.js
+++ b/client/src/templates/Introduction/components/CertChallenge.js
@@ -5,13 +5,16 @@ import React, { useState, useEffect } from 'react';
import { withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
-
import {
certSlugTypeMap,
superBlockCertTypeMap
} from '../../../../../config/certification-settings';
import { createFlashMessage } from '../../../components/Flash/redux';
-import { stepsToClaimSelector } from '../../../redux';
+import {
+ userFetchStateSelector,
+ stepsToClaimSelector,
+ isSignedInSelector
+} from '../../../redux';
import { StepsType, User } from '../../../redux/prop-types';
import { verifyCert } from '../../../redux/settings';
@@ -22,6 +25,12 @@ import CertificationCard from './CertificationCard';
const propTypes = {
createFlashMessage: PropTypes.func.isRequired,
+ fetchState: PropTypes.shape({
+ pending: PropTypes.bool,
+ complete: PropTypes.bool,
+ errored: PropTypes.bool
+ }),
+ isSignedIn: PropTypes.bool,
steps: StepsType,
superBlock: PropTypes.string,
t: PropTypes.func,
@@ -36,9 +45,16 @@ const honestyInfoMessage = {
};
const mapStateToProps = state => {
- return createSelector(stepsToClaimSelector, steps => ({
- steps
- }))(state);
+ return createSelector(
+ stepsToClaimSelector,
+ userFetchStateSelector,
+ isSignedInSelector,
+ (steps, fetchState, isSignedIn) => ({
+ steps,
+ fetchState,
+ isSignedIn
+ })
+ )(state);
};
const mapDispatchToProps = {
@@ -53,15 +69,21 @@ const CertChallenge = ({
t,
verifyCert,
title,
+ fetchState,
+ isSignedIn,
user: { isHonest, username }
}) => {
- const [canClaim, setCanClaim] = useState({ status: false, result: '' });
+ const [canClaimCert, setCanClaimCert] = useState(false);
+ const [certVerificationMessage, setCertVerificationMessage] = useState('');
const [isCertified, setIsCertified] = useState(false);
+ const [userLoaded, setUserLoaded] = useState(false);
+ const [verificationComplete, setVerificationComplete] = useState(false);
const [stepState, setStepState] = useState({
numberOfSteps: 0,
completedCount: 0
});
- const [canViewCert, setCanViewCert] = useState(false);
+ const [hasCompletedRequiredSteps, setHasCompletedRequiredSteps] =
+ useState(false);
const [isProjectsCompleted, setIsProjectsCompleted] = useState(false);
useEffect(() => {
@@ -70,7 +92,9 @@ const CertChallenge = ({
try {
const data = await getVerifyCanClaimCert(username, superBlock);
const { status, result } = data?.response?.message;
- setCanClaim({ status, result });
+ setCanClaimCert(status);
+ setCertVerificationMessage(result);
+ setVerificationComplete(true);
} catch (e) {
// TODO: How do we handle errors...?
}
@@ -81,6 +105,14 @@ const CertChallenge = ({
const { certSlug } = certMap.find(x => x.title === title);
+ useEffect(() => {
+ const { pending, complete } = fetchState;
+
+ if (complete && !pending) {
+ setUserLoaded(true);
+ }
+ }, [fetchState]);
+
useEffect(() => {
setIsCertified(
steps?.currentCerts?.find(
@@ -90,18 +122,17 @@ const CertChallenge = ({
);
const projectsCompleted =
- canClaim.status || canClaim.result === 'projects-completed';
+ canClaimCert || certVerificationMessage === 'projects-completed';
const completedCount =
Object.values(steps).filter(
stepVal => typeof stepVal === 'boolean' && stepVal
).length + projectsCompleted;
const numberOfSteps = Object.keys(steps).length;
-
- setCanViewCert(completedCount === numberOfSteps);
+ setHasCompletedRequiredSteps(completedCount === numberOfSteps);
setStepState({ numberOfSteps, completedCount });
setIsProjectsCompleted(projectsCompleted);
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [steps, canClaim]);
+ }, [steps, canClaimCert, certVerificationMessage]);
const certLocation = `/certification/${username}/${certSlug}`;
const i18nSuperBlock = t(`intro:${superBlock}.title`);
@@ -109,6 +140,11 @@ const CertChallenge = ({
cert: i18nSuperBlock
});
+ const showCertificationCard =
+ userLoaded &&
+ isSignedIn &&
+ (!isCertified || (!hasCompletedRequiredSteps && verificationComplete));
+
const createClickHandler = certSlug => e => {
e.preventDefault();
if (isCertified) {
@@ -118,10 +154,9 @@ const CertChallenge = ({
? verifyCert(certSlug)
: createFlashMessage(honestyInfoMessage);
};
-
return (
- {(!isCertified || !canViewCert) && (
+ {showCertificationCard && (
)}
-
- {isCertified ? t('buttons.show-cert') : t('buttons.claim-cert')}
-
+ <>
+ {isSignedIn && (
+
+ {isCertified && userLoaded
+ ? t('buttons.show-cert')
+ : t('buttons.claim-cert')}
+
+ )}
+ >
);
};
diff --git a/client/src/templates/Introduction/components/ClaimCertSteps.js b/client/src/templates/Introduction/components/ClaimCertSteps.js
index f02a09f9f8..1f49384674 100644
--- a/client/src/templates/Introduction/components/ClaimCertSteps.js
+++ b/client/src/templates/Introduction/components/ClaimCertSteps.js
@@ -2,7 +2,6 @@ import { Link } from 'gatsby';
import PropTypes from 'prop-types';
import React from 'react';
import { withTranslation, useTranslation } from 'react-i18next';
-
import GreenNotCompleted from '../../../assets/icons/green-not-completed';
import GreenPass from '../../../assets/icons/green-pass';
import { StepsType } from '../../../redux/prop-types';
@@ -39,6 +38,7 @@ const ClaimCertSteps = ({
isShowCerts = false,
isShowProfile = false
} = steps;
+
return (
diff --git a/cypress/integration/learn/responsive-web-design/claim-cert-from-learn.js b/cypress/integration/learn/responsive-web-design/claim-cert-from-learn.js
index fad120e588..f98d84d3db 100644
--- a/cypress/integration/learn/responsive-web-design/claim-cert-from-learn.js
+++ b/cypress/integration/learn/responsive-web-design/claim-cert-from-learn.js
@@ -49,6 +49,7 @@ describe('Responsive Web Design Superblock', () => {
});
describe('After submitting all 5 projects', () => {
before(() => {
+ cy.exec('npm run seed');
cy.login();
cy.toggleAll();
const { superBlock, block, challenges } = projects;
@@ -82,6 +83,10 @@ describe('Responsive Web Design Superblock', () => {
cy.get('.donation-modal').should('not.exist');
// directed to claim-cert-block section
cy.url().should('include', '#claim-cert-block');
+ // make sure that the window has not snapped to the top (a weird bug that
+ // we never figured out and so could randomly reappear)
+ cy.window().its('scrollY').should('not.equal', 0);
+ cy.contains('Claim Your Certification');
cy.contains('Claim Certification').should('not.be.disabled').click();
cy.contains('Show Certification').click();
cy.location().should(loc => {