Skip to content

Commit

Permalink
fix: open login modal on proposalform if session is expired
Browse files Browse the repository at this point in the history
  • Loading branch information
victorgcramos committed Aug 17, 2021
1 parent eef6622 commit 1fd712a
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 32 deletions.
4 changes: 4 additions & 0 deletions src/actions/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ export const onLogout = (isCMS, isPermanent) =>
});
});

export const onRequireCSRF = () => (dispatch) => {
dispatch(act.CSRF_NEEDED(true));
};

export const onChangeUsername = (password, newUsername) =>
withCsrf((dispatch, _, csrf) => {
dispatch(act.REQUEST_CHANGE_USERNAME());
Expand Down
53 changes: 48 additions & 5 deletions src/components/ProposalForm/ProposalForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import SelectField from "src/components/Select/SelectField";
import styles from "./ProposalForm.module.css";
import MarkdownEditor from "src/components/MarkdownEditor";
import ModalMDGuide from "src/components/ModalMDGuide";
import ModalLogin from "src/components/ModalLogin";
import ThumbnailGrid from "src/components/Files";
import AttachFileInput from "src/components/AttachFileInput";
import DraftSaver from "./DraftSaver";
Expand All @@ -35,7 +36,9 @@ import {
PROPOSAL_TYPE_RFP,
PROPOSAL_TYPE_RFP_SUBMISSION,
PROPOSAL_STATE_VETTED,
PROPOSAL_AMOUNT_UNIT
PROPOSAL_AMOUNT_UNIT,
ERROR_CODE_FORBIDDEN,
ERROR_CODE_NEEDS_LOGIN
} from "src/constants";
import {
getProposalTypeOptionsForSelect,
Expand Down Expand Up @@ -405,10 +408,33 @@ const ProposalFormWrapper = ({
});
}, [handleCloseModal, handleOpenModal]);
const [submitSuccess, setSubmitSuccess] = useState(false);
const { proposalFormValidation, onFetchProposalsBatchWithoutState } =
useProposalForm();
const {
proposalFormValidation,
onFetchProposalsBatchWithoutState,
onRequireCSRF
} = useProposalForm();
const openLoginModal = useCallback(
(onClose) => {
handleOpenModal(ModalLogin, {
onLoggedIn: () => {
onClose();
handleCloseModal();
},
onClose: () => {
onClose();
handleCloseModal();
},
disableClose: true,
title: "Your session has expired. Please log in again"
});
},
[handleOpenModal, handleCloseModal]
);
const handleSubmit = useCallback(
async (values, { resetForm, setSubmitting, setFieldError }) => {
async (
values,
{ resetForm, setSubmitting, setFieldError, setFieldTouched }
) => {
try {
const {
type,
Expand Down Expand Up @@ -465,11 +491,28 @@ const ProposalFormWrapper = ({
history.push(`/record/${shortRecordToken(proposalToken)}`);
resetForm();
} catch (e) {
if (
e.statusCode === ERROR_CODE_FORBIDDEN ||
e.errorcode === ERROR_CODE_NEEDS_LOGIN
) {
onRequireCSRF();
openLoginModal(() => {
setFieldError("global", null);
setFieldTouched("description", true);
});
}
setSubmitting(false);
setFieldError("global", e);
}
},
[history, onSubmit, onFetchProposalsBatchWithoutState, isPublic]
[
history,
onSubmit,
onFetchProposalsBatchWithoutState,
isPublic,
openLoginModal,
onRequireCSRF
]
);

const newInitialValues = initialValues
Expand Down
4 changes: 3 additions & 1 deletion src/components/ProposalForm/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ export function useProposalForm() {
const onFetchProposalsBatchWithoutState = useAction(
act.onFetchProposalsBatchWithoutState
);
const onRequireCSRF = useAction(act.onRequireCSRF);
return {
proposalFormValidation: policyPi && proposalValidation(policyPi),
onFetchProposalsBatchWithoutState,
policy: policyPi
policy: policyPi,
onRequireCSRF
};
}
4 changes: 4 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,7 @@ export const TOTP_MISSING_LOGIN_ERROR = 79;

// TODO: remove legacy
export const ARCHIVE_URL = "https://proposals-archive.decred.org/";

// ERRORS
export const ERROR_CODE_FORBIDDEN = 403;
export const ERROR_CODE_NEEDS_LOGIN = 29;
32 changes: 22 additions & 10 deletions src/containers/User/SessionChecker/hooks.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
import { useState, useEffect, useCallback } from "react";
import { useState, useEffect, useCallback, useMemo } from "react";
import * as sel from "src/selectors";
import { useSelector } from "src/redux";

export function useSessionChecker() {
const currentUser = useSelector(sel.currentUser);
const [sessionExpired, setSessionExpired] = useState(false);

const [sessionExpired, setSessionExpired] = useState();
const timeout = useMemo(() => {
if (!currentUser) return;
const { sessionmaxage, lastlogintime } = currentUser;
return sessionmaxage * 1000 - (Date.now() / 1000 - lastlogintime);
}, [currentUser]);

const checkUserSession = useCallback(() => {
if (currentUser && !sessionExpired) {
const { sessionmaxage, lastlogintime } = currentUser;
const expired = sessionmaxage < Date.now() / 1000 - lastlogintime;
setSessionExpired(expired);
}
}, [currentUser, sessionExpired]);
const { sessionmaxage, lastlogintime } = currentUser;
const expired = sessionmaxage < Date.now() / 1000 - lastlogintime;
setSessionExpired(expired);
}, [currentUser]);

useEffect(() => {
checkUserSession();
});
let interval;
if (timeout) {
interval = setInterval(() => {
checkUserSession();
}, timeout);
}
return () => {
interval && clearInterval(interval);
};
}, [timeout, checkUserSession]);

return { sessionExpired, setSessionExpired };
}
11 changes: 3 additions & 8 deletions src/lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
PROPOSAL_METADATA_FILENAME,
VOTE_METADATA_FILENAME
} from "../constants";
import { APIUserError, APIPluginError, isAPIWww } from "./errors.js";
import { APIUserError, APIPluginError } from "./errors.js";
import {
digestPayload,
digest,
Expand Down Expand Up @@ -346,13 +346,8 @@ export const parseResponse = (response) =>
response: {},
csrfToken: response.headers.get("X-Csrf-Token")
});
let errorcode = json.errorcode;
let errorcontext = json.errorcontext;

if (isAPIWww(response.url)) {
errorcode = json.ErrorCode;
errorcontext = json.ErrorContext;
}
const errorcode = json.errorcode || json.ErrorCode;
const errorcontext = json.errorcontext || json.ErrorContext;

if (json.pluginid) {
reject(new APIPluginError(json.pluginid, errorcode, errorcontext));
Expand Down
10 changes: 2 additions & 8 deletions src/lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ function RecordsUserError(code, context) {
17: "The record status is invalid",
18: `The status transition is invalid: ${context}`,
19: "The status reason was not found",
20: "The number of requested record tokens exceeds the page size policy."
20: "The number of requested record tokens exceeds the page size policy.",
29: "You must be logged in to perform this action"
};

this.message = errorMap[code] || defaultErrorMessage(code, APIRecords);
Expand Down Expand Up @@ -357,10 +358,3 @@ function TicketvotePluginError(code, context) {
}

TicketvotePluginError.prototype = new Error();

// Helpers
export function isAPIWww(url) {
if (url.startsWith("/api")) return true;
const apiType = new URL(url).pathname.split("/")[2];
return apiType === APIWww;
}

0 comments on commit 1fd712a

Please sign in to comment.