441 lines
11 KiB
JavaScript
441 lines
11 KiB
JavaScript
import api from '../api';
|
|
|
|
/**
|
|
* LocalStorage 'soapbox:verification'
|
|
*
|
|
* {
|
|
* token: String,
|
|
* challenges: {
|
|
* email: Number (0 = incomplete, 1 = complete),
|
|
* sms: Number,
|
|
* age: Number
|
|
* }
|
|
* }
|
|
*/
|
|
const LOCAL_STORAGE_VERIFICATION_KEY = 'soapbox:verification';
|
|
|
|
const PEPE_FETCH_INSTANCE_SUCCESS = 'PEPE_FETCH_INSTANCE_SUCCESS';
|
|
const FETCH_CHALLENGES_SUCCESS = 'FETCH_CHALLENGES_SUCCESS';
|
|
const FETCH_TOKEN_SUCCESS = 'FETCH_TOKEN_SUCCESS';
|
|
|
|
const SET_NEXT_CHALLENGE = 'SET_NEXT_CHALLENGE';
|
|
const SET_CHALLENGES_COMPLETE = 'SET_CHALLENGES_COMPLETE';
|
|
const SET_LOADING = 'SET_LOADING';
|
|
|
|
const ChallengeTypes = {
|
|
EMAIL: 'email',
|
|
SMS: 'sms',
|
|
AGE: 'age',
|
|
};
|
|
|
|
/**
|
|
* Fetch the state of the user's verification in local storage.
|
|
*
|
|
* @returns {object}
|
|
* {
|
|
* token: String,
|
|
* challenges: {
|
|
* email: Number (0 = incomplete, 1 = complete),
|
|
* sms: Number,
|
|
* age: Number
|
|
* }
|
|
* }
|
|
*/
|
|
function fetchStoredVerification() {
|
|
try {
|
|
return JSON.parse(localStorage.getItem(LOCAL_STORAGE_VERIFICATION_KEY));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove the state of the user's verification from local storage.
|
|
*/
|
|
function removeStoredVerification() {
|
|
localStorage.removeItem(LOCAL_STORAGE_VERIFICATION_KEY);
|
|
}
|
|
|
|
|
|
/**
|
|
* Fetch and return the Registration token for Pepe.
|
|
* @returns {string}
|
|
*/
|
|
function fetchStoredToken() {
|
|
try {
|
|
const verification = fetchStoredVerification();
|
|
return verification.token;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch and return the state of the verification challenges.
|
|
* @returns {object}
|
|
* {
|
|
* challenges: {
|
|
* email: Number (0 = incomplete, 1 = complete),
|
|
* sms: Number,
|
|
* age: Number
|
|
* }
|
|
* }
|
|
*/
|
|
function fetchStoredChallenges() {
|
|
try {
|
|
const verification = fetchStoredVerification();
|
|
return verification.challenges;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the verification object in local storage.
|
|
*
|
|
* @param {*} verification object
|
|
*/
|
|
function updateStorage({ ...updatedVerification }) {
|
|
const verification = fetchStoredVerification();
|
|
|
|
localStorage.setItem(
|
|
LOCAL_STORAGE_VERIFICATION_KEY,
|
|
JSON.stringify({ ...verification, ...updatedVerification }),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Fetch Pepe challenges and registration token
|
|
* @returns {promise}
|
|
*/
|
|
function fetchVerificationConfig() {
|
|
return async(dispatch) => {
|
|
await dispatch(fetchPepeInstance());
|
|
|
|
dispatch(fetchRegistrationToken());
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Save the challenges in localStorage.
|
|
*
|
|
* - If the API removes a challenge after the client has stored it, remove that
|
|
* challenge from localStorage.
|
|
* - If the API adds a challenge after the client has stored it, add that
|
|
* challenge to localStorage.
|
|
* - Don't overwrite a challenge that has already been completed.
|
|
* - Update localStorage to the new set of challenges.
|
|
*
|
|
* @param {array} challenges - ['age', 'sms', 'email']
|
|
*/
|
|
function saveChallenges(challenges) {
|
|
const currentChallenges = fetchStoredChallenges() || {};
|
|
|
|
const challengesToRemove = Object.keys(currentChallenges).filter((currentChallenge) => !challenges.includes(currentChallenge));
|
|
challengesToRemove.forEach((challengeToRemove) => delete currentChallenges[challengeToRemove]);
|
|
|
|
for (let i = 0; i < challenges.length; i++) {
|
|
const challengeName = challenges[i];
|
|
|
|
if (typeof currentChallenges[challengeName] !== 'number') {
|
|
currentChallenges[challengeName] = 0;
|
|
}
|
|
}
|
|
|
|
updateStorage({ challenges: currentChallenges });
|
|
}
|
|
|
|
/**
|
|
* Finish a challenge.
|
|
* @param {string} challenge - "sms" or "email" or "age"
|
|
*/
|
|
function finishChallenge(challenge) {
|
|
const currentChallenges = fetchStoredChallenges() || {};
|
|
// Set challenge to "complete"
|
|
currentChallenges[challenge] = 1;
|
|
|
|
updateStorage({ challenges: currentChallenges });
|
|
}
|
|
|
|
/**
|
|
* Fetch the next challenge
|
|
* @returns {string} challenge - "sms" or "email" or "age"
|
|
*/
|
|
function fetchNextChallenge() {
|
|
const currentChallenges = fetchStoredChallenges() || {};
|
|
return Object.keys(currentChallenges).find((challenge) => currentChallenges[challenge] === 0);
|
|
}
|
|
|
|
/**
|
|
* Dispatch the next challenge or set to complete if all challenges are completed.
|
|
*/
|
|
function dispatchNextChallenge(dispatch) {
|
|
const nextChallenge = fetchNextChallenge();
|
|
|
|
if (nextChallenge) {
|
|
dispatch({ type: SET_NEXT_CHALLENGE, challenge: nextChallenge });
|
|
} else {
|
|
dispatch({ type: SET_CHALLENGES_COMPLETE });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch the challenges and age mininum from Pepe
|
|
* @returns {promise}
|
|
*/
|
|
function fetchPepeInstance() {
|
|
return (dispatch, getState) => {
|
|
dispatch({ type: SET_LOADING });
|
|
|
|
return api(getState).get('/api/v1/pepe/instance').then(response => {
|
|
const { challenges, age_minimum: ageMinimum } = response.data;
|
|
saveChallenges(challenges);
|
|
const currentChallenge = fetchNextChallenge();
|
|
|
|
dispatch({ type: PEPE_FETCH_INSTANCE_SUCCESS, instance: { isReady: true, ...response.data } });
|
|
|
|
dispatch({
|
|
type: FETCH_CHALLENGES_SUCCESS,
|
|
ageMinimum,
|
|
currentChallenge,
|
|
isComplete: !currentChallenge,
|
|
});
|
|
})
|
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Fetch the regristration token from Pepe unless it's already been stored locally
|
|
* @returns {promise}
|
|
*/
|
|
function fetchRegistrationToken() {
|
|
return (dispatch, getState) => {
|
|
dispatch({ type: SET_LOADING });
|
|
|
|
const token = fetchStoredToken();
|
|
if (token) {
|
|
dispatch({
|
|
type: FETCH_TOKEN_SUCCESS,
|
|
value: token,
|
|
});
|
|
return null;
|
|
}
|
|
|
|
|
|
return api(getState).post('/api/v1/pepe/registrations')
|
|
.then(response => {
|
|
updateStorage({ token: response.data.access_token });
|
|
|
|
return dispatch({
|
|
type: FETCH_TOKEN_SUCCESS,
|
|
value: response.data.access_token,
|
|
});
|
|
})
|
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
|
};
|
|
}
|
|
|
|
function checkEmailAvailability(email) {
|
|
return (dispatch, getState) => {
|
|
dispatch({ type: SET_LOADING });
|
|
|
|
const token = fetchStoredToken();
|
|
|
|
return api(getState).get(`/api/v1/pepe/account/exists?email=${email}`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
.catch(() => {})
|
|
.then(() => dispatch({ type: SET_LOADING, value: false }));
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Send the user's email to Pepe to request confirmation
|
|
* @param {string} email
|
|
* @returns {promise}
|
|
*/
|
|
function requestEmailVerification(email) {
|
|
return (dispatch, getState) => {
|
|
dispatch({ type: SET_LOADING });
|
|
|
|
const token = fetchStoredToken();
|
|
|
|
return api(getState).post('/api/v1/pepe/verify_email/request', { email }, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
|
};
|
|
}
|
|
|
|
function checkEmailVerification() {
|
|
return (dispatch, getState) => {
|
|
const token = fetchStoredToken();
|
|
|
|
return api(getState).get('/api/v1/pepe/verify_email', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Confirm the user's email with Pepe
|
|
* @param {string} emailToken
|
|
* @returns {promise}
|
|
*/
|
|
function confirmEmailVerification(emailToken) {
|
|
return (dispatch, getState) => {
|
|
dispatch({ type: SET_LOADING });
|
|
|
|
const token = fetchStoredToken();
|
|
|
|
return api(getState).post('/api/v1/pepe/verify_email/confirm', { token: emailToken }, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
.then(() => {
|
|
finishChallenge(ChallengeTypes.EMAIL);
|
|
dispatchNextChallenge(dispatch);
|
|
})
|
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
|
};
|
|
}
|
|
|
|
function postEmailVerification() {
|
|
return (dispatch, getState) => {
|
|
finishChallenge(ChallengeTypes.EMAIL);
|
|
dispatchNextChallenge(dispatch);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Send the user's phone number to Pepe to request confirmation
|
|
* @param {string} phone
|
|
* @returns {promise}
|
|
*/
|
|
function requestPhoneVerification(phone) {
|
|
return (dispatch, getState) => {
|
|
dispatch({ type: SET_LOADING });
|
|
|
|
const token = fetchStoredToken();
|
|
|
|
return api(getState).post('/api/v1/pepe/verify_sms/request', { phone }, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Send the user's phone number to Pepe to re-request confirmation
|
|
* @param {string} phone
|
|
* @returns {promise}
|
|
*/
|
|
function reRequestPhoneVerification(phone) {
|
|
return (dispatch, getState) => {
|
|
dispatch({ type: SET_LOADING });
|
|
|
|
return api(getState).post('/api/v1/pepe/reverify_sms/request', { phone })
|
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Confirm the user's phone number with Pepe
|
|
* @param {string} code
|
|
* @returns {promise}
|
|
*/
|
|
function confirmPhoneVerification(code) {
|
|
return (dispatch, getState) => {
|
|
dispatch({ type: SET_LOADING });
|
|
|
|
const token = fetchStoredToken();
|
|
|
|
return api(getState).post('/api/v1/pepe/verify_sms/confirm', { code }, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
.then(() => {
|
|
finishChallenge(ChallengeTypes.SMS);
|
|
dispatchNextChallenge(dispatch);
|
|
})
|
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Re-Confirm the user's phone number with Pepe
|
|
* @param {string} code
|
|
* @returns {promise}
|
|
*/
|
|
function reConfirmPhoneVerification(code) {
|
|
return (dispatch, getState) => {
|
|
dispatch({ type: SET_LOADING });
|
|
|
|
return api(getState).post('/api/v1/pepe/reverify_sms/confirm', { code })
|
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Confirm the user's age with Pepe
|
|
* @param {date} birthday
|
|
* @returns {promise}
|
|
*/
|
|
function verifyAge(birthday) {
|
|
return (dispatch, getState) => {
|
|
dispatch({ type: SET_LOADING });
|
|
|
|
const token = fetchStoredToken();
|
|
|
|
return api(getState).post('/api/v1/pepe/verify_age/confirm', { birthday }, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
.then(() => {
|
|
finishChallenge(ChallengeTypes.AGE);
|
|
dispatchNextChallenge(dispatch);
|
|
})
|
|
.finally(() => dispatch({ type: SET_LOADING, value: false }));
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create the user's account with Pepe
|
|
* @param {string} username
|
|
* @param {string} password
|
|
* @returns {promise}
|
|
*/
|
|
function createAccount(username, password) {
|
|
return (dispatch, getState) => {
|
|
dispatch({ type: SET_LOADING });
|
|
|
|
const token = fetchStoredToken();
|
|
|
|
return api(getState).post('/api/v1/pepe/accounts', { username, password }, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
}).finally(() => dispatch({ type: SET_LOADING, value: false }));
|
|
};
|
|
}
|
|
|
|
export {
|
|
PEPE_FETCH_INSTANCE_SUCCESS,
|
|
FETCH_CHALLENGES_SUCCESS,
|
|
FETCH_TOKEN_SUCCESS,
|
|
LOCAL_STORAGE_VERIFICATION_KEY,
|
|
SET_CHALLENGES_COMPLETE,
|
|
SET_LOADING,
|
|
SET_NEXT_CHALLENGE,
|
|
checkEmailAvailability,
|
|
confirmEmailVerification,
|
|
confirmPhoneVerification,
|
|
createAccount,
|
|
fetchStoredChallenges,
|
|
fetchVerificationConfig,
|
|
fetchRegistrationToken,
|
|
removeStoredVerification,
|
|
requestEmailVerification,
|
|
checkEmailVerification,
|
|
postEmailVerification,
|
|
reConfirmPhoneVerification,
|
|
requestPhoneVerification,
|
|
reRequestPhoneVerification,
|
|
verifyAge,
|
|
};
|