feat(client): improve SuperBlock cert claiming UX (#41147)

* feat(client): improve SuperBlock cert claiming UX

* broken: add certCard foundation

* broken: add TODO comments for scatter-brain

* restructure stepsToClaimSelector

* add api-server verifyCanClaimCert logic

* temp: correct verifyCanClaim URL

* move GET logic to CertificationCard, remove console.logs

* add error handling, and navigation logic

* correct verification logical flow

* fix completion-epic updates, fix cert verify

* update widget to button, disable button unless verified

* working: refactor CertChallenge with hook state

* add StepsType

* update Honesty snapshot

* add DonationModal to SuperBlockIntro

* disable Claim Cert button unless also isHonest

* prevent warning when viewing cert

* test: use navigate in Modal to return to hash

* test: replace gatsby.navigate with reach/router.navigate

* add propTypes

* fix: rename propTypes -> prop-types

* use react-scrollable-anchor to squash modal bug

* update location parser type

* open-source Oliver's suggestion

* fix superblock title

* add claim-cert-from-learn tests

* use larger tests

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>

* fix some cypress stuff

* fix ShowCertification cypress test

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Shaun Hamilton
2021-07-15 15:51:27 +01:00
committed by GitHub
parent 5a52c229f5
commit 6ca6d9950c
17 changed files with 634 additions and 253 deletions

View File

@@ -5,7 +5,7 @@ import {
catchError,
concat,
filter,
tap
finalize
} from 'rxjs/operators';
import { ofType } from 'redux-observable';
import { navigate } from 'gatsby';
@@ -24,11 +24,13 @@ import {
isSignedInSelector,
submitComplete,
updateComplete,
updateFailed
updateFailed,
usernameSelector
} from '../../../redux';
import postUpdate$ from '../utils/postUpdate$';
import { challengeTypes, submitTypes } from '../../../../utils/challengeTypes';
import { getVerifyCanClaimCert } from '../../../utils/ajax';
function postChallenge(update, username) {
const saveChallenge = postUpdate$(update).pipe(
@@ -133,7 +135,7 @@ export default function completionEpic(action$, state$) {
switchMap(({ type }) => {
const state = state$.value;
const meta = challengeMetaSelector(state);
const { nextChallengePath, challengeType } = meta;
const { nextChallengePath, challengeType, superBlock } = meta;
const closeChallengeModal = of(closeModal('completion'));
let submitter = () => of({ type: 'no-user-signed-in' });
@@ -150,11 +152,55 @@ export default function completionEpic(action$, state$) {
submitter = submitters[submitTypes[challengeType]];
}
const pathToNavigateTo = async () => {
return await findPathToNavigateTo(
nextChallengePath,
superBlock,
state,
challengeType
);
};
return submitter(type, state).pipe(
tap(() => navigate(nextChallengePath)),
concat(closeChallengeModal),
filter(Boolean)
filter(Boolean),
finalize(async () => navigate(await pathToNavigateTo()))
);
})
);
}
async function findPathToNavigateTo(
nextChallengePath,
superBlock,
state,
challengeType
) {
let canClaimCert = false;
const isProjectSubmission = [
challengeTypes.frontEndProject,
challengeTypes.backEndProject,
challengeTypes.pythonProject
].includes(challengeType);
if (isProjectSubmission) {
const username = usernameSelector(state);
try {
const response = await getVerifyCanClaimCert(username, superBlock);
if (response.status === 200) {
canClaimCert = response.data?.response?.message === 'can-claim-cert';
}
} catch (err) {
console.error('failed to verify if user can claim certificate', err);
}
}
let pathToNavigateTo;
if (nextChallengePath.includes(superBlock) && !canClaimCert) {
pathToNavigateTo = nextChallengePath;
} else if (canClaimCert) {
pathToNavigateTo = `/learn/${superBlock}/#claim-cert-block`;
} else {
pathToNavigateTo = `/learn/${superBlock}/#${superBlock}-projects`;
}
return pathToNavigateTo;
}