Skip to content

Commit

Permalink
Add attestation to claims (#2734)
Browse files Browse the repository at this point in the history
* init

* Attest component

* Add top notification bar

* better english

* back to 3 steps

* clean up

* in the middle of the refactoring

* Make TS pass

* Clea up some comments

* isPreclaimed

* isOldClaimProcess

* Attest vs Claim (#2737)

* Choose between Claim and Attest

* fix lint

* fix flow between claim and attest (#2738)

* Jaco's comments + Fetch StatementKind (#2741)

* Fix small bug in flow

* Add condition for the warning banner (#2742)

* prevent crash for older chains

* with useEffect

* should work

* back to something simple

* remove hoc

* fix CI

* Add ETH address field for non preclaimed addresses (#2746)

* Eth field

* fix

* remove stray logs

* Update packages/page-claims/src/index.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* Update packages/page-claims/src/index.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* Update packages/apps/src/overlays/Attest.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* Show the amount of token that will be claimed (#2747)

* show amount in attest

* fix isApiReady

* fix lint

* Use counter instead of overlay (#2750)

* Use counter instead of overlay

* Add warning to claim (#2752)

* add warning

* css cleanup

* fix unused const

Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>

* Update packages/page-claims/src/Warning.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* Update packages/page-claims/src/index.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* Update packages/page-claims/src/index.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* Update packages/page-claims/src/index.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* Update packages/page-claims/src/index.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* address comment shortcut

* set Ethereum address outside transfor and fix typos

* lint

* extract claims.preclaims to a hook

* address comments EthereumAddress type and avoid casting

* Missing isUnsigned

* use AddressMini in warning

* use a proper step for eth address field

* Add loading state

* remove log

* unwrapOrNull

* Update packages/page-claims/src/Warning.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* fix for account plural translation

* lint

* Add getStatement function

* nits

* Fix lint

* Update packages/page-claims/src/Warning.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* add statement

* statement component

* address comment unWrapOr

* test styling with long md

* add md to jest

* Update packages/page-claims/src/Statement.tsx

Co-authored-by: Amaury Martiny <amaury.martiny@protonmail.com>

* copy text before

* lint

* Remove EthreumAddress type to avoid unexpected behaviors (#2796)

* remove EthreumAddress type

* createType when needed

* revert

* typo

* Enhance UX for claim process (#2797)

* update content

* init

* comment

* Add HTML components for statements (#2798)

* Add HTML components for statements

* Update packages/page-claims/src/statements/saft.tsx

* Update to final statements

* More nits

* Nits

* OnSuccess

* prevent display of empty statement when attested

* Small fixmes

* Update packages/page-claims/src/index.tsx

* fix isError not being red as expected

* remove word-break

* statement text in black

* attestation for -> accounc balance

* Update packages/page-claims/src/Statement.tsx

Co-authored-by: Logan Saether <x@logansaether.com>

* margin left instead of space

* I agree

* Use iframe

* update saft to QmXEkMahfhHJPzT3RjkXiZVFi77ZeVeuxtAjhojGRNYckz

* remove hash and duplications (#2814)

Co-authored-by: Amaury Martiny <amaury.martiny@protonmail.com>
Co-authored-by: Jaco Greeff <jacogr@gmail.com>
Co-authored-by: Logan Saether <x@logansaether.com>
  • Loading branch information
4 people authored May 26, 2020
1 parent 8e00005 commit 0091fc6
Show file tree
Hide file tree
Showing 10 changed files with 631 additions and 238 deletions.
5 changes: 3 additions & 2 deletions packages/apps-routing/src/claims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { Route } from './types';

import Claims from '@polkadot/app-claims';
import Claims, { useCounter } from '@polkadot/app-claims';

export default function create (t: <T = string> (key: string, text: string, options: { ns: string }) => T): Route {
return {
Expand All @@ -17,6 +17,7 @@ export default function create (t: <T = string> (key: string, text: string, opti
},
icon: 'star',
name: 'claims',
text: t<string>('nav.claims', 'Claim Tokens', { ns: 'apps-routing' })
text: t<string>('nav.claims', 'Claim Tokens', { ns: 'apps-routing' }),
useCounter
};
}
87 changes: 87 additions & 0 deletions packages/page-claims/src/Attest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2017-2020 @polkadot/app-claims authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.

import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { Button, Card, TxButton } from '@polkadot/react-components';
import { TxCallback } from '@polkadot/react-components/Status/types';
import { useApi } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';
import { Option } from '@polkadot/types';
import { BalanceOf, EthereumAddress, StatementKind } from '@polkadot/types/interfaces';

import { ClaimStyles } from './Claim';
import Statement from './Statement';
import { useTranslation } from './translate';
import { getStatement } from './util';

interface Props {
accountId: string;
className?: string;
ethereumAddress: EthereumAddress | null;
onSuccess?: TxCallback;
statementKind?: StatementKind;
systemChain: string;
}

function Attest ({ accountId, className, ethereumAddress, onSuccess, statementKind, systemChain }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { api } = useApi();
const [claimValue, setClaimValue] = useState<BalanceOf | null>(null);
const [isBusy, setIsBusy] = useState(false);

useEffect((): void => {
if (!ethereumAddress) {
return;
}

setIsBusy(true);

api.query.claims
.claims<Option<BalanceOf>>(ethereumAddress)
.then((claim): void => {
setClaimValue(claim.unwrapOr(null));
setIsBusy(false);
})
.catch((): void => setIsBusy(false));
}, [api, ethereumAddress]);

if (isBusy) {
return null;
}

const hasClaim = claimValue && claimValue.gten(0);
const statementSentence = getStatement(systemChain, statementKind)?.sentence;

if (!hasClaim || !statementSentence) {
return null;
}

return (
<Card isSuccess>
<div className={className}>
<Statement
kind={statementKind}
systemChain={systemChain}
/>
<h3><FormatBalance label={t<string>('Account balance:')}
value={claimValue} /></h3>
<Button.Group>
<TxButton
accountId={accountId}
icon='send'
isDisabled={!statementSentence}
isPrimary
label={t<string>('I agree')}
onSuccess={onSuccess}
params={[statementSentence]}
tx='claims.attest'
/>
</Button.Group>
</div>
</Card>
);
}

export default React.memo(styled(Attest)`${ClaimStyles}`);
141 changes: 88 additions & 53 deletions packages/page-claims/src/Claim.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,75 @@
// of the Apache-2.0 license. See the LICENSE file for details.

import { Option } from '@polkadot/types';
import { BalanceOf, EthereumAddress } from '@polkadot/types/interfaces';
import { BalanceOf, EthereumAddress, StatementKind } from '@polkadot/types/interfaces';

import React, { useCallback, useEffect, useState } from 'react';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { Button, Card } from '@polkadot/react-components';
import { Button, Card, TxButton } from '@polkadot/react-components';
import { TxCallback } from '@polkadot/react-components/Status/types';
import { useApi } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';

import { useTranslation } from './translate';
import { addrToChecksum } from './util';
import { addrToChecksum, getStatement } from './util';

interface Props {
button: React.ReactNode;
accountId: string;
className?: string;
ethereumAddress: EthereumAddress | null;
ethereumSignature: string | null;
// Do we sign with `claims.claimAttest` (new) instead of `claims.claim` (old)?
isOldClaimProcess: boolean;
onSuccess?: TxCallback;
statementKind?: StatementKind;
systemChain: string;
}

function Claim ({ button, className = '', ethereumAddress }: Props): React.ReactElement<Props> | null {
interface ConstructTx {
params?: any[];
tx?: string;
}

// Depending on isOldClaimProcess, construct the correct tx.
function constructTx (
systemChain: string,
accountId: string,
ethereumSignature: string | null,
kind: StatementKind | undefined,
isOldClaimProcess: boolean
): ConstructTx {
if (!ethereumSignature || !kind) {
return {};
}

return isOldClaimProcess
? { params: [accountId, ethereumSignature], tx: 'claims.claim' }
: { params: [accountId, ethereumSignature, getStatement(systemChain, kind)?.sentence], tx: 'claims.claimAttest' };
}

function Claim ({ accountId, className = '', ethereumAddress, ethereumSignature, isOldClaimProcess, onSuccess, statementKind, systemChain }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { api } = useApi();
const [claimValue, setClaimValue] = useState<BalanceOf | null>(null);
const [claimAddress, setClaimAddress] = useState<EthereumAddress | null>(null);
const [isBusy, setIsBusy] = useState(false);

const _fetchClaim = useCallback(
(address: EthereumAddress): void => {
setIsBusy(true);

api.query.claims
.claims<Option<BalanceOf>>(address)
.then((claim): void => {
setClaimValue(claim.unwrapOr(null));
setIsBusy(false);
})
.catch((): void => setIsBusy(false));
},
[api]
);

useEffect((): void => {
setClaimAddress(ethereumAddress);
if (!ethereumAddress) {
return;
}

ethereumAddress && _fetchClaim(ethereumAddress);
}, [_fetchClaim, ethereumAddress]);
setIsBusy(true);

if (isBusy || !claimAddress) {
api.query.claims
.claims<Option<BalanceOf>>(ethereumAddress)
.then((claim): void => {
setClaimValue(claim.unwrapOr(null));
setIsBusy(false);
})
.catch((): void => setIsBusy(false));
}, [api, ethereumAddress]);

if (!ethereumAddress || isBusy) {
return null;
}

Expand All @@ -61,13 +84,23 @@ function Claim ({ button, className = '', ethereumAddress }: Props): React.React
>
<div className={className}>
{t<string>('Your Ethereum account')}
<h3>{addrToChecksum(claimAddress.toString())}</h3>
<h3>{addrToChecksum(ethereumAddress.toString())}</h3>
{hasClaim && claimValue
? (
<>
{t<string>('has a valid claim for')}
<h2><FormatBalance value={claimValue} /></h2>
<Button.Group>{button}</Button.Group>
<Button.Group>
<TxButton
accountId={accountId}
icon='send'
isPrimary
isUnsigned
label={t('Claim')}
onSuccess={onSuccess}
{...constructTx(systemChain, accountId, ethereumSignature, statementKind, isOldClaimProcess)}
/>
</Button.Group>
</>
)
: (
Expand All @@ -80,29 +113,31 @@ function Claim ({ button, className = '', ethereumAddress }: Props): React.React
);
}

export default React.memo(styled(Claim)`
font-size: 1.15rem;
display: flex;
flex-direction: column;
justify-content: center;
min-height: 12rem;
align-items: center;
margin: 0 1rem;
h3 {
font-family: monospace;
font-size: 1.5rem;
max-width: 100%;
margin: 0.5rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
export const ClaimStyles = `
font-size: 1.15rem;
display: flex;
flex-direction: column;
justify-content: center;
min-height: 12rem;
align-items: center;
margin: 0 1rem;
h2 {
margin: 0.5rem 0 2rem;
font-family: monospace;
font-size: 2.5rem;
font-weight: 200;
}
`);
h3 {
font-family: monospace;
font-size: 1.5rem;
max-width: 100%;
margin: 0.5rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
h2 {
margin: 0.5rem 0 2rem;
font-family: monospace;
font-size: 2.5rem;
font-weight: 200;
}
`;

export default React.memo(styled(Claim)`${ClaimStyles}`);
82 changes: 82 additions & 0 deletions packages/page-claims/src/Statement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2017-2020 @polkadot/app-claims authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.

import { StatementKind } from '@polkadot/types/interfaces';

import React from 'react';
import styled from 'styled-components';

import { useTranslation } from './translate';
import { getStatement } from './util';

export interface Props {
className?: string;
kind?: StatementKind;
systemChain: string;
}

// Get the full hardcoded text for a statement
function StatementFullText ({ statementUrl, systemChain }: { statementUrl?: string; systemChain: string }): React.ReactElement | null {
const { t } = useTranslation();

switch (systemChain) {
case 'Polkadot CC1': {
if (!statementUrl) {
return null;
}

return <iframe src={statementUrl} />;
}

default:
return <p>{t('Warning: we did not find any attest statement for {{chain}}', { replace: { chain: systemChain } })}</p>;
}
}

function Statement ({ className, kind, systemChain }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const statementUrl = getStatement(systemChain, kind)?.url;

return (
<div className={className}>
{t('Please read these terms and conditions carefully. By submitting this statement, you are deemed to have accepted these Terms and Conditions. If you do not agree to these terms, please refrain from accessing or proceeding. You can also find them at:')}
<a className='statementUrl'
href={statementUrl}
rel='noopener noreferrer'
target='_blank'>{statementUrl}</a>
<div className='statement'>
<StatementFullText
statementUrl={statementUrl}
systemChain={systemChain}
/>
</div>
</div>
);
}

export default React.memo(styled(Statement)`
.statement{
border: 1px solid #c2c2c2;
background: #f2f2f2;
height: 15rem;
padding: 1rem;
width: 100%;
margin: 1rem 0;
white-space: normal;
p {
color: #4e4e4e !important;
}
iframe {
border: 0;
height: 100%;
width: 100%;
}
}
.statementUrl{
margin-left: 0.3rem
}
`);
Loading

0 comments on commit 0091fc6

Please sign in to comment.