Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve identity error message. #2623

Merged
merged 4 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/components/IdentityErrorBoundary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { Component } from "react";
import { Button } from "pi-ui";
import { withRouter } from "react-router-dom";
import SingleContentPage from "./layout/SingleContentPage";
import IdentityMessageError from "./IdentityMessageError";
import { isIdentityError } from "src/utils";
import { Row } from "./layout";

const identityErrorRenderer = (history) => (
<SingleContentPage noCardWrap>
<IdentityMessageError />
<Row justify="center">
<Button onClick={() => history.push("/")}>Return home</Button>
</Row>
</SingleContentPage>
);

class IdentityErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
error: false
};
}

static getDerivedStateFromError(error) {
return {
error
};
}

render() {
const { history } = this.props;
// If user navigates away reset error.
history.listen(() => {
if (this.state.error) {
this.setState({
error: false
});
}
});
const { error } = this.state;
return isIdentityError(error)
? identityErrorRenderer(history)
: this.props.children;
}
}

export default withRouter(IdentityErrorBoundary);
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Message, P } from "pi-ui";
import Link from "./Link";
import { useLoaderContext } from "src/containers/Loader";

export const IdentityMessageError = () => {
const IdentityMessageError = () => {
const { currentUser } = useLoaderContext();
return currentUser ? (
<Message className="margin-bottom-m" kind="error">
Expand All @@ -16,3 +16,5 @@ export const IdentityMessageError = () => {
</Message>
) : null;
};

export default IdentityMessageError;
12 changes: 10 additions & 2 deletions src/components/ModalConfirm.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { useEffect, useState } from "react";
import { Button, Icon, Modal, Text, useTheme, getThemeProperty } from "pi-ui";
import PropTypes from "prop-types";
import React, { useEffect, useState } from "react";
import FormWrapper from "src/components/FormWrapper";
import { useCrash, useModalContext } from "src/hooks";
import { isIdentityError } from "src/utils";

const ModalConfirm = ({
show,
Expand All @@ -13,6 +15,8 @@ const ModalConfirm = ({
successMessage,
onCloseSuccess
}) => {
const [, handleCloseModal] = useModalContext();
const crash = useCrash();
const [success, setSuccess] = useState(false);

const { theme } = useTheme();
Expand All @@ -34,8 +38,12 @@ const ModalConfirm = ({
resetForm();
setSuccess(true);
} catch (e) {
setFieldError("global", e);
setSubmitting(false);
setFieldError("global", e);
if (isIdentityError(e)) {
crash(e);
handleCloseModal();
}
}
};
useEffect(
Expand Down
14 changes: 11 additions & 3 deletions src/components/ModalConfirmWithReason.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, useEffect } from "react";
import * as Yup from "yup";
import {
P,
Button,
Expand All @@ -9,8 +10,9 @@ import {
useTheme
} from "pi-ui";
import PropTypes from "prop-types";
import { useCrash, useModalContext } from "src/hooks";
import FormWrapper from "src/components/FormWrapper";
import * as Yup from "yup";
import { isIdentityError } from "src/utils";

const ModalConfirmWithReason = ({
show,
Expand All @@ -23,6 +25,8 @@ const ModalConfirmWithReason = ({
successTitle,
onCloseSuccess
}) => {
const [, handleCloseModal] = useModalContext();
const crash = useCrash();
const [success, setSuccess] = useState(false);

const onSubmitReason = async (
Expand All @@ -36,6 +40,10 @@ const ModalConfirmWithReason = ({
} catch (e) {
setSubmitting(false);
setFieldError("global", e);
if (isIdentityError(e)) {
crash(e);
handleCloseModal();
}
}
};
useEffect(() => {
Expand All @@ -62,10 +70,10 @@ const ModalConfirmWithReason = ({
onClose={success && onCloseSuccess ? onCloseSuccess : onClose}
iconComponent={
!success ? (
<Icon type={"info"} size={26} />
<Icon type="info" size={26} />
) : (
<Icon
type={"checkmark"}
type="checkmark"
size={26}
iconColor={iconCheckmarkColor}
backgroundColor={successIconBgColor}
Expand Down
11 changes: 7 additions & 4 deletions src/components/ModalProvider.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, createContext } from "react";
import IdentityErrorBoundary from "src/components/IdentityErrorBoundary";
import isEmpty from "lodash/fp/isEmpty";

export const modalContext = createContext({ component: () => null, props: {} });
Expand Down Expand Up @@ -38,10 +39,12 @@ const ModalProvider = ({ children }) => {
...currentModal.props
};
return (
<modalContext.Provider value={[handleOpenModal, handleCloseModal]}>
{children}
<currentModal.component {...props} />
</modalContext.Provider>
<IdentityErrorBoundary>
<modalContext.Provider value={[handleOpenModal, handleCloseModal]}>
{children}
<currentModal.component {...props} />
</modalContext.Provider>
</IdentityErrorBoundary>
);
};

Expand Down
9 changes: 8 additions & 1 deletion src/components/ModalStartVote/ModalStartVote.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import {
import PropTypes from "prop-types";
import { useLoaderContext } from "src/containers/Loader";
import { validationSchema } from "./validation";
import usePolicy from "src/hooks/api/usePolicy";
import { useCrash, useModalContext, usePolicy } from "src/hooks";
import { isRfpReadyToVote } from "src/containers/Proposal/helpers";
import { VOTE_TYPE_STANDARD, VOTE_TYPE_RUNOFF } from "src/constants";
import { isIdentityError } from "src/utils";

const getRoundedAverage = (a, b) => Math.round((a + b) / 2);

Expand Down Expand Up @@ -47,6 +48,8 @@ const ModalStartVote = ({
proposal,
voteType
}) => {
const [, handleCloseModal] = useModalContext();
const crash = useCrash();
const [success, setSuccess] = useState(false);
const { apiInfo } = useLoaderContext();
const { theme } = useTheme();
Expand Down Expand Up @@ -84,6 +87,10 @@ const ModalStartVote = ({
} catch (e) {
setSubmitting(false);
setFieldError("global", e);
if (isIdentityError(e)) {
crash(e);
handleCloseModal();
}
}
};
useEffect(
Expand Down
4 changes: 4 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ export const NOTIFICATION_EMAIL_ADMIN_PROPOSAL_NEW = 1 << 5;
export const NOTIFICATION_EMAIL_ADMIN_PROPOSAL_VOTE_AUTHORIZED = 1 << 6;
export const NOTIFICATION_EMAIL_COMMENT_ON_MY_PROPOSAL = 1 << 7;
export const NOTIFICATION_EMAIL_COMMENT_ON_MY_COMMENT = 1 << 8;

// Identity
export const STORAGE_PREFIX = "ed255191~";
export const IDENTITY_ERROR = "IDENTITY_ERROR";
// Import key errors
export const PUBLIC_KEY_MISMATCH =
"The provided public key doesn't match the key stored in the server.";
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Proposal/Detail/Detail.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { useConfig } from "src/containers/Config";
import Or from "src/components/Or";
import LoggedInContent from "src/components/LoggedInContent";
import CommentForm from "src/components/CommentForm/CommentFormLazy";
import { IdentityMessageError } from "src/components/IdentityErrorIndicators";
import IdentityMessageError from "src/components/IdentityMessageError";
import WhatAreYourThoughts from "src/components/WhatAreYourThoughts";
import { PROPOSAL_UPDATE_HINT, PROPOSAL_STATE_UNVETTED } from "src/constants";
import { shortRecordToken } from "src/helpers";
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Proposal/Edit/Edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useEditProposal } from "./hooks";
import usePaywall from "src/hooks/api/usePaywall";
import useIdentity from "src/hooks/api/useIdentity";
import Or from "src/components/Or";
import { IdentityMessageError } from "src/components/IdentityErrorIndicators";
import IdentityMessageError from "src/components/IdentityMessageError";
import ProposalForm from "src/components/ProposalForm/ProposalFormLazy";
import Link from "src/components/Link";
import ProposalFormLoader from "src/components/ProposalForm/ProposalFormLoader";
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Proposal/New/New.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Message, Card, P } from "pi-ui";
import React from "react";
import ProposalForm from "src/components/ProposalForm/ProposalFormLazy";
import { IdentityMessageError } from "src/components/IdentityErrorIndicators";
import IdentityMessageError from "src/components/IdentityMessageError";
import Or from "src/components/Or";
import Link from "src/components/Link";
import usePaywall from "src/hooks/api/usePaywall";
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/api/useProposalsBatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ export default function useProposalsBatch({
}

return {
// If added no tokens of the new status return old index as the returned
// tokens are old tokens which means corresponding to the new status.
// If no tokens of the new status added return old index as the returned
// tokens correspond to the old status.
index: newTokens?.length ? index : index - 1,
tokens
};
Expand Down
1 change: 1 addition & 0 deletions src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ export { default as useScrollToTop } from "./utils/useScrollToTop";
export { default as useSessionStorage } from "./utils/useSessionStorage";
export { default as useThrowError } from "./utils/useThrowError";
export { default as useWhyDidYouUpdate } from "./utils/useWhyDidYouUpdate";
export { default as useCrash } from "./utils/useCrash";
12 changes: 12 additions & 0 deletions src/hooks/utils/useCrash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useCallback, useState } from "react";

export default function useCrash() {
const [, setState] = useState();
return useCallback(
(err) =>
setState(() => {
throw err;
}),
[]
);
}
7 changes: 1 addition & 6 deletions src/lib/pki.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ import localforage from "localforage";
import nacl from "tweetnacl";
import util from "tweetnacl-util";
import get from "lodash/fp/get";

export const STORAGE_PREFIX = "ed255191~";
export const IDENTITY_ERROR = `
Your identity is invalid. You cannot currently submit proposals or comments,
please visit your account page to correct this problem.
`;
import { STORAGE_PREFIX, IDENTITY_ERROR } from "src/constants";

export const toHex = (x) => Buffer.from(toUint8Array(x)).toString("hex");

Expand Down
7 changes: 6 additions & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MONTHS_LABELS } from "./constants";
import { MONTHS_LABELS, IDENTITY_ERROR } from "./constants";

/**
* Converts atoms to DCR
Expand Down Expand Up @@ -62,3 +62,8 @@ export const formatCentsToUSD = (centsInput) => {
const dollars = centsInput / 100;
return dollars.toFixed(2) + " USD";
};

/*
* Verifies whether given error is the global identity error.
*/
export const isIdentityError = (error) => error?.message === IDENTITY_ERROR;