diff --git a/.eslintrc.js b/.eslintrc.js
index 65cbd437..c6843d0d 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -345,6 +345,7 @@ module.exports = {
"(?=.*[A-F])#[0-9a-fA-F]{1,6}", // Don't use upper case letters in hex colors.
"@js[x]", // Don't use a custom JSX pragma.
"Style[d]", // Don't use "styled" components.
+ ",\\s+&", // Don't use spaces between comma separated selectors.
"eslint-disabl[e]", // Don't disable rules.
],
],
diff --git a/_pages/profile/[id]/submission-details-card/deadlines/challenge-button.js b/_pages/profile/[id]/submission-details-card/deadlines/challenge-button.js
new file mode 100644
index 00000000..f91d8afb
--- /dev/null
+++ b/_pages/profile/[id]/submission-details-card/deadlines/challenge-button.js
@@ -0,0 +1,224 @@
+import {
+ Box,
+ Button,
+ Card,
+ Grid,
+ Image,
+ Input,
+ Link,
+ NextLink,
+ Popup,
+ Text,
+ useContract,
+ useWeb3,
+} from "@kleros/components";
+import { useEffect, useMemo, useState } from "react";
+import { graphql, useFragment } from "relay-hooks";
+
+import { challengeReasonEnum, ethereumAddressRegExp, zeroAddress } from "data";
+
+const challengeButtonFragments = {
+ contract: graphql`
+ fragment challengeButtonContract on Contract {
+ submissionChallengeBaseDeposit
+ sharedStakeMultiplier
+ }
+ `,
+ request: graphql`
+ fragment challengeButtonRequest on Request {
+ disputed
+ arbitrator
+ arbitratorExtraData
+ usedReasons
+ currentReason
+ }
+ `,
+};
+function ChallengeTypeCard({ type, setType, currentType, ...rest }) {
+ const { imageSrc, startCase, description } = type;
+ return (
+ setType(type)}
+ active={type === currentType}
+ {...rest}
+ >
+
+ {startCase}
+ {description}
+
+ );
+}
+function DuplicateInput({ submissionID, setDuplicate }) {
+ const [value, setValue] = useState("");
+ const isValidAddress = ethereumAddressRegExp.test(value);
+ const [submission] = useContract(
+ "proofOfHumanity",
+ "getSubmissionInfo",
+ useMemo(
+ () => ({
+ args: [isValidAddress ? value : undefined],
+ }),
+ [isValidAddress, value]
+ )
+ );
+
+ let message;
+ if (submissionID.toLowerCase() === value.toLowerCase())
+ message = "A submission can not be a duplicate of itself.";
+ else if (isValidAddress && submission)
+ if (Number(submission.status) > 0 || submission.registered)
+ message = "Valid duplicate.";
+ else
+ message =
+ "A supposed duplicate should be either registered or pending registration.";
+ useEffect(() => {
+ if (message === "Valid duplicate.") setDuplicate(value);
+ else setDuplicate();
+ }, [message, setDuplicate, value]);
+ return (
+
+ setValue(event.target.value)}
+ />
+ {message}
+ {isValidAddress && (
+
+ See Profile
+
+ )}
+
+ );
+}
+export default function ChallengeButton({ request, contract, submissionID }) {
+ const {
+ currentReason: _currentReason,
+ arbitrator,
+ arbitratorExtraData,
+ disputed,
+ usedReasons: _usedReasons,
+ } = useFragment(challengeButtonFragments.request, request);
+ const currentReason = challengeReasonEnum.parse(_currentReason);
+ const usedReasons = challengeReasonEnum.parse(_usedReasons);
+ const currentReasonIsNotDuplicate =
+ currentReason !== challengeReasonEnum.Duplicate;
+
+ const [arbitrationCost] = useContract(
+ "klerosLiquid",
+ "arbitrationCost",
+ useMemo(
+ () => ({
+ address: arbitrator,
+ args: [arbitratorExtraData],
+ }),
+ [arbitrator, arbitratorExtraData]
+ )
+ );
+ const { web3 } = useWeb3();
+ const { sharedStakeMultiplier, submissionChallengeBaseDeposit } = useFragment(
+ challengeButtonFragments.contract,
+ contract
+ );
+ const totalCost = arbitrationCost
+ ?.add(
+ arbitrationCost
+ .mul(web3.utils.toBN(sharedStakeMultiplier))
+ .div(web3.utils.toBN(10000))
+ )
+ .add(web3.utils.toBN(submissionChallengeBaseDeposit));
+
+ const [type, setType] = useState();
+ const duplicateTypeSelected = type === challengeReasonEnum.Duplicate;
+ const [duplicate, setDuplicate] = useState();
+ const { send, loading } = useContract("proofOfHumanity", "challengeRequest");
+ return (
+
+ Challenge Request
+
+ }
+ modal
+ >
+ {(close) => (
+
+ Deposit:
+
+
+ {totalCost && `${web3.utils.fromWei(totalCost)} ETH Deposit`}
+
+
+ Challenge Type:
+
+
+
+
+
+
+ {duplicateTypeSelected && (
+
+ )}
+
+
+ )}
+
+ );
+}
diff --git a/_pages/profile/[id]/submission-details-card/deadlines.js b/_pages/profile/[id]/submission-details-card/deadlines/index.js
similarity index 74%
rename from _pages/profile/[id]/submission-details-card/deadlines.js
rename to _pages/profile/[id]/submission-details-card/deadlines/index.js
index 665e98f8..550a9d87 100644
--- a/_pages/profile/[id]/submission-details-card/deadlines.js
+++ b/_pages/profile/[id]/submission-details-card/deadlines/index.js
@@ -1,41 +1,47 @@
import { Text, TimeAgo } from "@kleros/components";
import { graphql, useFragment } from "relay-hooks";
+import ChallengeButton from "./challenge-button";
+
import { submissionStatusEnum } from "data";
const deadlinesFragments = {
contract: graphql`
fragment deadlinesContract on Contract {
challengePeriodDuration
+ ...challengeButtonContract
}
`,
submission: graphql`
fragment deadlinesSubmission on Submission {
+ id
submissionTime
renewalTimestamp
request: requests(orderDirection: desc, first: 1) {
lastStatusChange
+ ...challengeButtonRequest
}
}
`,
};
-function Deadline({ label, datetime }) {
+function Deadline({ label, datetime, button }) {
return (
{label}:
+ {Date.now() < datetime && button}
);
}
export default function Deadlines({ submission, contract, status }) {
- const { request, submissionTime, renewalTimestamp } = useFragment(
+ const { request, id, submissionTime, renewalTimestamp } = useFragment(
deadlinesFragments.submission,
submission
);
- const { challengePeriodDuration } = useFragment(
+ const { challengePeriodDuration } = (contract = useFragment(
deadlinesFragments.contract,
contract
- );
+ ));
return (
<>
+ }
/>
) : (
submissionTime && (
diff --git a/_pages/profile/[id]/submission-details-card/index.js b/_pages/profile/[id]/submission-details-card/index.js
index 22175863..3c0d83b6 100644
--- a/_pages/profile/[id]/submission-details-card/index.js
+++ b/_pages/profile/[id]/submission-details-card/index.js
@@ -65,7 +65,7 @@ export default function SubmissionDetailsCard({ submission, contract }) {
const contributions = useMemo(
() =>
request.challenges[0].rounds[0].contributions.map((contribution) =>
- partyEnum.parse(contribution)
+ partyEnum.parse(contribution.values)
),
[request]
);
@@ -104,7 +104,7 @@ export default function SubmissionDetailsCard({ submission, contract }) {
const totalContribution = useMemo(
() =>
contributions.reduce(
- (acc, { values: { Requester } }) => acc.add(web3.utils.toBN(Requester)),
+ (acc, { Requester }) => acc.add(web3.utils.toBN(Requester)),
web3.utils.toBN(0)
),
[contributions, web3.utils]
@@ -140,7 +140,7 @@ export default function SubmissionDetailsCard({ submission, contract }) {
{evidence?.file?.bio}
- {status === submissionStatusEnum.Vouching ? (
+ {status === submissionStatusEnum.Vouching && (
<>
{totalCost?.gt(totalContribution) && (
>
- ) : null}
+ )}
-
+
diff --git a/components/card.js b/components/card.js
index 5576c37d..bfabd4a8 100644
--- a/components/card.js
+++ b/components/card.js
@@ -1,6 +1,7 @@
import { Flex, Card as _Card } from "theme-ui";
export default function Card({
+ active,
header,
headerSx,
mainSx,
@@ -10,7 +11,11 @@ export default function Card({
...rest
}) {
return (
- <_Card {...rest}>
+ <_Card
+ as={rest.disabled || rest.onClick ? "button" : _Card}
+ className={active ? "active" : undefined}
+ {...rest}
+ >
{header && (
{
const _enum = keys.reduce(
(acc, key, index) => {
@@ -23,7 +27,22 @@ const createEnum = (keys, parse) => {
acc[value.startCase] = value;
return acc;
},
- { parse }
+ {
+ parse:
+ parse ||
+ ((arrayOrKey) =>
+ Array.isArray(arrayOrKey)
+ ? arrayOrKey.reduce((acc, key) => {
+ const value = _enum[key];
+ acc[key] = value;
+ acc[value.index] = value;
+ acc[value.camelCase] = value;
+ acc[value.kebabCase] = value;
+ acc[value.startCase] = value;
+ return acc;
+ }, {})
+ : _enum[arrayOrKey]),
+ }
);
_enum.map = (callback) => keys.map((_, index) => callback(_enum[index]));
_enum.find = (callback) =>
@@ -31,19 +50,6 @@ const createEnum = (keys, parse) => {
return _enum;
};
-export const partyEnum = createEnum(["Requester", "Challenger"], (struct) =>
- Object.keys(struct).reduce((acc, key) => {
- acc[key] =
- Array.isArray(struct[key]) && struct[key].length === 2
- ? {
- [partyEnum.Requester.key]: struct[key][0],
- [partyEnum.Challenger.key]: struct[key][1],
- }
- : struct[key];
- return acc;
- }, {})
-);
-
export const submissionStatusEnum = createEnum(
[
["None", { kebabCase: undefined, startCase: "All" }],
@@ -80,4 +86,41 @@ export const submissionStatusEnum = createEnum(
: submissionStatusEnum[status]
);
+export const partyEnum = createEnum(["Requester", "Challenger"], (array) => ({
+ [partyEnum.Requester.key]: array[0],
+ [partyEnum.Challenger.key]: array[1],
+}));
+
+export const challengeReasonEnum = createEnum([
+ "None",
+ [
+ "IncorrectSubmission",
+ {
+ imageSrc: "/images/incorrect-submission.png",
+ description: "Parts of the submission are incorrect.",
+ },
+ ],
+ [
+ "Deceased",
+ {
+ imageSrc: "/images/deceased.png",
+ description: "The submitter has passed away.",
+ },
+ ],
+ [
+ "Duplicate",
+ {
+ imageSrc: "/images/duplicate.png",
+ description: "The submitter is already registered.",
+ },
+ ],
+ [
+ "DoesNotExist",
+ {
+ imageSrc: "/images/does-not-exist.png",
+ description: "The submitter does not exist.",
+ },
+ ],
+]);
+
export const queryEnums = { status: submissionStatusEnum };
diff --git a/public/images/deceased.png b/public/images/deceased.png
new file mode 100644
index 00000000..7eaba9ec
Binary files /dev/null and b/public/images/deceased.png differ
diff --git a/public/images/does-not-exist.png b/public/images/does-not-exist.png
new file mode 100644
index 00000000..8d8c2d1c
Binary files /dev/null and b/public/images/does-not-exist.png differ
diff --git a/public/images/duplicate.png b/public/images/duplicate.png
new file mode 100644
index 00000000..d870d154
Binary files /dev/null and b/public/images/duplicate.png differ
diff --git a/public/images/incorrect-submission.png b/public/images/incorrect-submission.png
new file mode 100644
index 00000000..b8f1ca48
Binary files /dev/null and b/public/images/incorrect-submission.png differ
diff --git a/subgraph/src/mapping.ts b/subgraph/src/mapping.ts
index 8e2dfe3b..f4751a89 100644
--- a/subgraph/src/mapping.ts
+++ b/subgraph/src/mapping.ts
@@ -43,11 +43,11 @@ import {
Submission,
} from "../generated/schema";
-function getStatus(reason: number): string {
- if (reason == 0) return "None";
- if (reason == 1) return "Vouching";
- if (reason == 2) return "PendingRegistration";
- if (reason == 3) return "PendingRemoval";
+function getStatus(status: number): string {
+ if (status == 0) return "None";
+ if (status == 1) return "Vouching";
+ if (status == 2) return "PendingRegistration";
+ if (status == 3) return "PendingRemoval";
return "Error";
}