@@ -0,0 +1,143 @@
1+import React, { useCallback, useEffect, useMemo } from 'react';
2+import cn from 'classnames';
3+import { Grid, Flex } from 'indigo-react';
4+
5+import * as need from 'lib/need';
6+import useEthereumTransaction from 'lib/useEthereumTransaction';
7+import pluralize from 'lib/pluralize';
8+
9+import { useWallet } from 'store/wallet';
10+import { usePointCache } from 'store/pointCache';
11+import { useStarReleaseCache } from 'store/starRelease';
12+
13+import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
14+
15+import { NumberInput, AddressInput } from 'form/Inputs';
16+import BridgeForm from 'form/BridgeForm';
17+import FormError from 'form/FormError';
18+import { composeValidator, buildNumberValidator } from 'form/validators';
19+import { convertToNumber } from 'form/formatters';
20+
21+function AddressIndicator({ isOwn, className }) {
22+ return isOwn ? (
23+ <div className={cn('green2', className)}>✓ Current Address</div>
24+ ) : (
25+ <div className={cn('red4', className)}>! Different address</div>
26+ );
27+}
28+
29+function useWithdrawStars() {
30+ const { syncControlledPoints } = usePointCache();
31+
32+ const { syncStarReleaseDetails, withdraw } = useStarReleaseCache();
33+ return useEthereumTransaction(
34+ useCallback((to, amount) => withdraw(amount, to)),
35+ useCallback(
36+ () => Promise.all([syncControlledPoints(), syncStarReleaseDetails()]),
37+ [syncStarReleaseDetails, syncControlledPoints]
38+ )
39+ );
40+}
41+
42+export default function LockedView({ className }) {
43+ const { wallet } = useWallet();
44+ const address = need.addressFromWallet(wallet);
45+ const { bind, construct, unconstruct, inputsLocked } = useWithdrawStars();
46+
47+ const { syncStarReleaseDetails, starReleaseDetails } = useStarReleaseCache();
48+
49+ useEffect(() => {
50+ syncStarReleaseDetails();
51+ }, [syncStarReleaseDetails]);
52+
53+ const onValues = useCallback(
54+ ({ valid, values }) => {
55+ if (valid) {
56+ construct(values.address, convertToNumber(values.numStars));
57+ } else {
58+ unconstruct();
59+ }
60+ },
61+ [construct, unconstruct]
62+ );
63+
64+ const available = starReleaseDetails
65+ .map(b => b.available - b.withdrawn)
66+ .getOrElse(0);
67+ const total = starReleaseDetails.map(b => b.total).getOrElse(0);
68+ const withdrawn = starReleaseDetails.map(b => b.withdrawn).getOrElse(0);
69+
70+ const validate = useMemo(
71+ () =>
72+ composeValidator({
73+ numStars: buildNumberValidator(0, available + 1),
74+ }),
75+ [available]
76+ );
77+
78+ const initialValues = { address };
79+
80+ return (
81+ <Grid gap={6} className={cn('mt4', className)} full>
82+ <BridgeForm
83+ initialValues={initialValues}
84+ onValues={onValues}
85+ validate={validate}>
86+ {({ values, reset }) => (
87+ <>
88+ <Grid.Item full className="f5">
89+ You have{' '}
90+ <span
91+ className={cn({
92+ green2: available > 0,
93+ red4: available === 0,
94+ })}>
95+ {available}
96+ </span>
97+ {pluralize(available, ' star ', ' stars ')}
98+ available to withdraw
99+ <br />
100+ You have <span className="green2">{total - withdrawn}</span>
101+ {pluralize(available, ' star ', ' stars ')}
102+ total
103+ </Grid.Item>
104+ <Grid.Item full className="f5" cols={[1, 10]} as={Flex} col>
105+ <Flex.Item
106+ name="address"
107+ as={AddressInput}
108+ label="Withdraw To"
109+ disabled={inputsLocked}
110+ />
111+ <Flex.Item
112+ as={AddressIndicator}
113+ isOwn={address === values.address}
114+ />
115+ </Grid.Item>
116+
117+ <Grid.Item
118+ cols={[1, 5]}
119+ label="Stars"
120+ style={{ minWidth: '0' }}
121+ as={NumberInput}
122+ placeholder="#"
123+ name="numStars"
124+ disabled={inputsLocked}
125+ />
126+ <Grid.Item as={Flex} col cols={[5, 8]}>
127+ <Flex.Item className="f6 lh-tall"> </Flex.Item>
128+ <Flex.Item className="f3">/ {available}</Flex.Item>
129+ </Grid.Item>
130+ <Grid.Item full as={FormError} />
131+ <Grid.Item
132+ full
133+ as={InlineEthereumTransaction}
134+ {...bind}
135+ onReturn={() => reset()}
136+ label="Withdraw Stars"
137+ />
138+ </>
139+ )}
140+ </BridgeForm>
141+ </Grid>
142+ );
143+}