on master help sign in
star-release-ui: Star Release refactor

- converted to TS - use new layout components - update `pilot-release` dev script - added new Star Release Button - deprecate Active view - update form styles - add Use my address button - refactor to form components

commit4a210ca6e353afaab3e53b4a22d3b0e63b62708f authortomholford <tomholford@users.noreply.github.com> dateMon 13 Dec 2021, 00:46 (5 years ago) parent42e34eb
15 files changed+298−329
package.json+1−1 src/views/Point/AddressButton.tsx → src/components/AddressButton.tsx+12−5 src/lib/router.js+1−1 src/views/Point/IssueChild.tsx+4−2 src/views/Points.tsx+5−13 src/views/Points/StarReleaseButton.scss+27 src/views/Points/StarReleaseButton.tsx+31 src/views/Release.js−89 src/views/Release/Active.js−101 src/views/Release/StarRelease.scss+65 src/views/Release/StarRelease.tsx+61 src/views/Release/Transfer.js → src/views/Release/TransferForm.tsx+18−38 src/views/Release/Locked.js → src/views/Release/WithdrawForm.tsx+32−79 src/views/Release/useTransferLockup.ts+24 src/views/Release/useWithdrawStars.ts+17
package.json+1−1
@@ -120,7 +120,7 @@
120120 "pilot": "HTTPS=true npm-run-all pilot:setup start pilot:cleanup --continue-on-error",
121121 "pilot-invites": "WITH_TEST_STATE=INVITES npm run pilot",
122122 "pilot-mainnet": "REACT_APP_MAINNET=true HTTPS=true npm-run-all start --continue-on-error",
123− "pilot-release": "BROWSER=NONE WITH_TEST_STATE=STAR_RELEASE NODE_ENV=development npm run pilot",
123+ "pilot-release": "HTTPS=true BROWSER=NONE NODE_ENV=development WITH_TEST_STATE=STAR_RELEASE npm-run-all pilot:setup start pilot:cleanup --continue-on-error",
124124 "pilot-residents": "WITH_TEST_STATE=RESIDENTS npm run pilot",
125125 "pilot-l2": "BROWSER=NONE WITH_TEST_STATE=L2 HTTPS=false NODE_ENV=development npm-run-all pilot:setup start pilot:cleanup --continue-on-error",
126126 "pilot-l2-local": "BROWSER=NONE REACT_APP_L2=true HTTPS=false NODE_ENV=development npm run start --continue-on-error",
src/views/Point/AddressButton.tsx → src/components/AddressButton.tsxrenamed+12−5
@@ -5,20 +5,27 @@ import { useForm } from 'react-final-form';
55import { useWallet } from 'store/wallet';
66
77interface AddressButtonProps {
8+ inputName: string,
89 children?: React.ReactNode;
910 rest?: ButtonProps;
1011}
1112
12−export const AddressButton = ({ children, ...rest }: AddressButtonProps) => {
13− const { change } = useForm();
13+export const AddressButton = ({
14+ inputName,
15+ children,
16+ ...rest
17+}: AddressButtonProps) => {
18+ const { change, focus } = useForm();
1419 const { wallet }: any = useWallet();
1520 const address = need.addressFromWallet(wallet);
1621
1722 const onClick = useCallback(
18− (_event: React.MouseEvent<HTMLSpanElement>) => {
19− change('owner', address);
23+ (event: React.MouseEvent<HTMLSpanElement>) => {
24+ event.stopPropagation();
25+ change(inputName, address);
26+ focus(inputName);
2027 },
21− [address, change]
28+ [address, change, focus, inputName]
2229 );
2330
2431 return (
src/lib/router.js+1−1
@@ -5,11 +5,11 @@ import Senate from 'views/Point/Senate';
55import Documents from 'views/Senate/Documents';
66import Upgrades from 'views/Senate/Upgrades';
77import CreateGalaxy from 'views/CreateGalaxy';
8−import StarRelease from 'views/Release';
98import AcceptLockup from 'views/AcceptLockup';
109
1110import Points from 'views/Points';
1211import Activate from 'views/Activate';
12+import StarRelease from 'views/Release/StarRelease';
1313
1414import Point from 'views/Point/Point';
1515import UrbitOS from 'views/Point/UrbitOS';
src/views/Point/IssueChild.tsx+4−2
@@ -42,7 +42,7 @@ import BridgeForm from 'form/BridgeForm';
4242import FormError from 'form/FormError';
4343
4444import { PatpBadge } from './PatpBadge';
45−import { AddressButton } from './AddressButton';
45+import { AddressButton } from '../../components/AddressButton';
4646import './IssueChild.scss';
4747
4848export function useIssueChild() {
@@ -309,7 +309,9 @@ export default function IssueChild() {
309309 />
310310 {isDefaultState && (
311311 <Box>
312− <AddressButton>Use my address</AddressButton>
312+ <AddressButton inputName="owner">
313+ Use my address
314+ </AddressButton>
313315 </Box>
314316 )}
315317 <Grid.Item full as={FormError} />
src/views/Points.tsx+5−13
@@ -32,6 +32,7 @@ import PointList from 'components/L2/PointList';
3232import { Box, Text } from '@tlon/indigo-react';
3333
3434import './Points.scss';
35+import { StarReleaseButton } from './Points/StarReleaseButton';
3536
3637export const maybeGetResult = (obj, key, defaultValue) =>
3738 obj.matchWith({
@@ -166,10 +167,6 @@ export default function Points() {
166167 .getOrElse([])
167168 .map((value: number) => ({ value }));
168169
169− const processingPoints = pointList.filter(
170− ({ shouldDisplay }) => !shouldDisplay
171− );
172−
173170 const displayEmptyState =
174171 !loading && incomingPoints.length === 0 && allPoints.length === 0;
175172
@@ -341,7 +338,6 @@ export default function Points() {
341338
342339 <Footer>
343340 <Grid>
344− <Grid.Divider />
345341 {isEclipticOwner && (
346342 <>
347343 <Grid.Item
@@ -356,14 +352,10 @@ export default function Points() {
356352 )}
357353 {starReleasing && (
358354 <>
359− <Grid.Item
360− full
361− as={ForwardButton}
362− detail="You have points being released"
363− onClick={goStarRelease}>
364− View Star Release
365− </Grid.Item>
366− <Grid.Divider />
355+ <StarReleaseButton
356+ title={'Locked Stars'}
357+ subtitle={`${starReleaseDetails.value.withdrawn} of ${starReleaseDetails.value.total} available`}
358+ onClick={goStarRelease}></StarReleaseButton>
367359 </>
368360 )}
369361 </Grid>
src/views/Points/StarReleaseButton.scssnew file+27
@@ -0,0 +1,27 @@
1+.star-release-button {
2+ display: flex;
3+ flex-flow: row nowrap;
4+ justify-content: space-between;
5+ align-items: center;
6+ height: 35px;
7+ border-radius: 5px;
8+ padding: 10px;
9+ background-color: rgba(0,0,0,0.04);
10+ cursor: pointer;
11+
12+ .title-container {
13+ display: flex;
14+ flex-flow: column nowrap;
15+ }
16+
17+ .button-title {
18+ font-family: 'Inter';
19+ font-weight: bold;
20+ font-size: 14px;
21+ }
22+
23+ .button-subtitle {
24+ font-family: 'Inter';
25+ font-size: 14px;
26+ }
27+}
src/views/Points/StarReleaseButton.tsxnew file+31
@@ -0,0 +1,31 @@
1+import { Box, Icon, Text } from '@tlon/indigo-react';
2+import './StarReleaseButton.scss';
3+
4+interface StarReleaseButtonProps {
5+ onClick: VoidFunction;
6+ title: string;
7+ subtitle: string;
8+}
9+
10+export const StarReleaseButton = ({
11+ onClick,
12+ title,
13+ subtitle,
14+}: StarReleaseButtonProps) => {
15+ return (
16+ <Box onClick={onClick} className={'star-release-button full'}>
17+ <Box className={'title-container'}>
18+ <Text className={'button-title'}>{title}</Text>
19+ <Text className={'button-subtitle'}>{subtitle}</Text>
20+ </Box>
21+ <Box>
22+ <Icon
23+ display="inline-block"
24+ icon="ChevronEast"
25+ size="14px"
26+ color={'black'}
27+ />
28+ </Box>
29+ </Box>
30+ );
31+};
src/views/Release.jsdeleted−89
@@ -1,89 +0,0 @@
1−import React, { useState, useCallback } from 'react';
2−import { H1, Grid } from 'indigo-react';
3−import { Just } from 'folktale/maybe';
4−
5−import * as need from 'lib/need';
6−
7−import { useHistory } from 'store/history';
8−import { useWallet } from 'store/wallet';
9−import { usePointCursor } from 'store/pointCursor';
10−
11−import View from 'components/View';
12−import Tabs from 'components/Tabs';
13−import CopiableAddress from 'components/copiable/CopiableAddress';
14−
15−import Active from 'views/Release/Active';
16−import Locked from 'views/Release/Locked';
17−import Transfer from 'views/Release/Transfer';
18−import NavHeader from 'components/NavHeader';
19−
20−const NAMES = {
21− ACTIVE: 'ACTIVE',
22− LOCKED: 'LOCKED',
23− TRANSFER: 'TRANSFER',
24−};
25−
26−const VIEWS = {
27− [NAMES.ACTIVE]: Active,
28− [NAMES.LOCKED]: Locked,
29− [NAMES.TRANSFER]: Transfer,
30−};
31−const OPTIONS = [
32− { text: 'Active', value: NAMES.ACTIVE },
33− { text: 'Locked', value: NAMES.LOCKED },
34− { text: 'Transfer', value: NAMES.TRANSFER },
35−];
36−export default function ReleaseView() {
37− const { pop, push, names } = useHistory();
38− const { wallet } = useWallet();
39−
40− const { setPointCursor } = usePointCursor();
41−
42− const goPoint = useCallback(
43− point => {
44− setPointCursor(Just(point));
45−
46− push(names.POINT);
47− },
48− [push, names, setPointCursor]
49− );
50− // inputs
51− const [currentTab, setCurrentTab] = useState(NAMES.ACTIVE);
52−
53− const goActive = useCallback(() => {
54− setCurrentTab(NAMES.ACTIVE);
55− }, [setCurrentTab]);
56−
57− const address = need.addressFromWallet(wallet);
58−
59− return (
60− <View pop={pop} inset>
61− <NavHeader>
62− <CopiableAddress
63− className="f6 mono gray4 us-none pointer"
64− text={address}>
65− {address}
66− </CopiableAddress>
67− </NavHeader>
68− <Grid>
69− <Grid.Item full as={H1}></Grid.Item>
70−
71− <Grid.Item
72− full
73− as={Tabs}
74− className="mt1"
75− // Tabs
76− views={VIEWS}
77− options={OPTIONS}
78− currentTab={currentTab}
79− onTabChange={setCurrentTab}
80− // Tab extra
81− goPoint={goPoint}
82− goActive={goActive}
83− //
84− //
85− />
86− </Grid>
87− </View>
88− );
89−}
src/views/Release/Active.jsdeleted−101
@@ -1,101 +0,0 @@
1−import React, { useCallback } from 'react';
2−import { Grid } from 'indigo-react';
3−import cn from 'classnames';
4−import * as ob from 'urbit-ob';
5−
6−import Sigil from 'components/Sigil';
7−
8−import { usePointCache } from 'store/pointCache';
9−import { useWallet } from 'store/wallet';
10−
11−import * as need from 'lib/need';
12−import usePermissionsForPoint from 'lib/usePermissionsForPoint';
13−import { useSyncDetails } from 'lib/useSyncPoints';
14−import { buildKeyType } from 'lib/point';
15−
16−function ActiveViewRow({ point, goPoint }) {
17− const { wallet } = useWallet();
18− const address = need.addressFromWallet(wallet);
19− useSyncDetails([point]);
20− const permissions = usePermissionsForPoint(address, point);
21−
22− const keyType = buildKeyType(permissions);
23− const patp = ob.patp(point);
24− const sigilSize = 50;
25−
26− const onClick = useCallback(() => goPoint(point), [point, goPoint]);
27− return (
28− <>
29− <Grid.Item className="flex-row align-center" cols={[1, 3]}>
30− {patp}{' '}
31− </Grid.Item>
32− <Grid.Item className="flex-row align-center" cols={[3, 6]}>
33− <div
34− style={{
35− display: 'inline-block',
36− height: `${sigilSize}px`,
37− width: `${sigilSize}px`,
38− }}>
39− <Sigil patp={patp} size={25} colors={['#FFFFFF', '#000000']} />
40− </div>
41− </Grid.Item>
42− <Grid.Item className="flex-row align-center" cols={[6, 10]}>
43− {keyType}
44− </Grid.Item>
45− <Grid.Item
46− onClick={onClick}
47− cols={[10, 13]}
48− className="flex-row-r align-center mr2">
49− ->
50− </Grid.Item>
51− <Grid.Divider />
52− </>
53− );
54−}
55−
56−export default function ActiveView({ className, goPoint }) {
57− // const points = [2560, 2561, 2562];
58− const { controlledPoints } = usePointCache();
59− console.log(controlledPoints);
60− const points = controlledPoints
61− .matchWith({
62− Nothing: () => [],
63− Just: ps =>
64− ps.value.matchWith({
65− Error: () => null,
66− Ok: ({ value }) => {
67− return [
68− ...value.ownedPoints,
69− ...value.votingPoints,
70− ...value.managingPoints,
71− ...value.spawningPoints,
72− ];
73− },
74− }),
75− })
76− .filter(Boolean)
77− .filter(p => 255 < p && p < 65536);
78−
79− return (
80− <Grid className={cn('f5', className)}>
81− <Grid.Item className="pv3 " cols={[1, 6]} third={1}>
82− Point
83− </Grid.Item>
84− <Grid.Item className="pv3 " cols={[6, 10]}>
85− Key Type
86− </Grid.Item>
87− <Grid.Divider />
88− {points.length === 0 && (
89− <Grid.Item full className="flex-center pv8 f5 gray4">
90− You have no active stars
91− </Grid.Item>
92− )}
93− {points.map(point => (
94− <ActiveViewRow
95− key={point}
96− goPoint={goPoint}
97− point={point}></ActiveViewRow>
98− ))}
99− </Grid>
100− );
101−}
src/views/Release/StarRelease.scssnew file+65
@@ -0,0 +1,65 @@
1+.info-row {
2+ padding: 10px;
3+
4+ background-color: rgba(0,0,0,0.02);
5+
6+ &.available {
7+ background-color: #b3e2d0;
8+ }
9+
10+ span {
11+ font-size: 14px;
12+ font-weight: 600;
13+ font-family: Inter;
14+ }
15+
16+ &:nth-child(1) {
17+ border-top-left-radius: 5px;
18+ border-top-right-radius: 5px;
19+ }
20+
21+ &:nth-child(2) {
22+ border-bottom-left-radius: 5px;
23+ border-bottom-right-radius: 5px;
24+ }
25+}
26+
27+.star-release-dropdown {
28+ .selector {
29+ font-weight: 600;
30+ width: min-content;
31+ background-color: white;
32+ svg {
33+ margin-left: 10px;
34+ }
35+ }
36+
37+ .content-border {
38+ z-index: 3;
39+
40+ .dropdown-content {
41+ width: 120px;
42+
43+ .options {
44+ .option {
45+ display: flex;
46+ flex-direction: row;
47+ justify-content: space-between;
48+ align-items: center;
49+ cursor: pointer;
50+ line-height: 20px;
51+
52+ padding: 8px 12px 8px 16px;
53+
54+ &:hover {
55+ opacity: 0.8;
56+ }
57+
58+ .label {
59+ margin-right: 8px;
60+ }
61+ }
62+ }
63+ }
64+ }
65+}
src/views/Release/StarRelease.tsxnew file+61
@@ -0,0 +1,61 @@
1+import View from 'components/View';
2+import Window from 'components/L2/Window/Window';
3+import { useLocalRouter } from 'lib/LocalRouter';
4+import L2BackHeader from 'components/L2/Headers/L2BackHeader';
5+import HeaderPane from 'components/L2/Window/HeaderPane';
6+import { Box, Row, Text } from '@tlon/indigo-react';
7+import BodyPane from 'components/L2/Window/BodyPane';
8+import { WithdrawForm } from './WithdrawForm';
9+import { useCallback, useState } from 'react';
10+import Dropdown from 'components/L2/Dropdowns/Dropdown';
11+import { TransferForm } from './TransferForm';
12+
13+import './StarRelease.scss';
14+
15+export default function StarRelease() {
16+ const { pop }: any = useLocalRouter();
17+ const [dropdownOpen, setDropdownOpen] = useState(false);
18+ const [isWithdrawing, setIsWithdrawing] = useState(true);
19+
20+ const handleDropdownClick = useCallback(() => {
21+ setIsWithdrawing(!isWithdrawing);
22+ setDropdownOpen(false);
23+ }, [isWithdrawing]);
24+
25+ return (
26+ <View
27+ pop={pop}
28+ hideBack
29+ inset
30+ className="issue-child"
31+ header={<L2BackHeader hideBalance={false} back={pop} />}>
32+ <Window className="id-issue-child">
33+ <HeaderPane>
34+ <Row>
35+ <h5>Locked Stars</h5>
36+ </Row>
37+ </HeaderPane>
38+ <BodyPane>
39+ <Box className="inner-container">
40+ <Dropdown
41+ className="star-release-dropdown"
42+ open={dropdownOpen}
43+ value={isWithdrawing ? 'Withdraw' : 'Transfer'}
44+ toggleOpen={() => setDropdownOpen(!dropdownOpen)}>
45+ <Box className="options">
46+ <Row className="option" onClick={handleDropdownClick}>
47+ <Text>{isWithdrawing ? 'Transfer' : 'Withdraw'}</Text>
48+ </Row>
49+ </Box>
50+ </Dropdown>
51+ {isWithdrawing ? (
52+ <WithdrawForm afterSubmit={() => console.log('after submit')} />
53+ ) : (
54+ <TransferForm afterSubmit={() => console.log('after submit')} />
55+ )}
56+ </Box>
57+ </BodyPane>
58+ </Window>
59+ </View>
60+ );
61+}
src/views/Release/Transfer.js → src/views/Release/TransferForm.tsxrenamed+18−38
@@ -1,48 +1,28 @@
1−import React, { useCallback, useEffect, useMemo } from 'react';
2−import cn from 'classnames';
3−import { Grid, Flex } from 'indigo-react';
4−import { conditionalSR, linearSR } from 'azimuth-js';
5−
61import * as need from 'lib/need';
7−import useEthereumTransaction from 'lib/useEthereumTransaction';
8−import { getLockupKind } from 'lib/starRelease';
9−import { eqAddr, isZeroAddress } from 'lib/utils/address';
10−
11−import { useNetwork } from 'store/network';
12−import { useWallet } from 'store/wallet';
13−import { useHistory } from 'store/history';
14−import { useStarReleaseCache } from 'store/starRelease';
15−
16−import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
172import CopiableAddress from 'components/copiable/CopiableAddress';
3+import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
184import NoticeBox from 'components/NoticeBox';
19−
20−import { AddressInput } from 'form/Inputs';
215import BridgeForm from 'form/BridgeForm';
226import FormError from 'form/FormError';
7+import { AddressInput } from 'form/Inputs';
238import { composeValidator } from 'form/validators';
9+import { Grid, Flex } from 'indigo-react';
10+import { useCallback, useEffect, useMemo } from 'react';
11+import { useHistory } from 'store/history';
12+import { useNetwork } from 'store/network';
13+import { useStarReleaseCache } from 'store/starRelease';
14+import { useWallet } from 'store/wallet';
15+import { eqAddr, isZeroAddress } from 'lib/utils/address';
16+import { useTransferLockup } from './useTransferLockup';
2417import { isValidAddress } from 'ethereumjs-util';
18+import { getLockupKind } from 'lib/starRelease';
2519
26−function useTransferLockup(kind) {
27− const { contracts } = useNetwork();
2820
29− return useEthereumTransaction(
30− useCallback(
31− to => {
32− const _contracts = need.contracts(contracts);
33− if (kind === 'conditional') {
34− return conditionalSR.approveCommitmentTransfer(_contracts, to);
35− } else {
36− return linearSR.approveBatchTransfer(_contracts, to);
37− }
38− },
39− [kind, contracts]
40− ),
41− () => {}
42− );
21+interface TransferFormProps {
22+ afterSubmit: VoidFunction;
4323}
4424
45−export default function Transfer({ className, goActive }) {
25+export const TransferForm = ({ afterSubmit }: TransferFormProps) => {
4626 const { wallet } = useWallet();
4727 const { contracts } = useNetwork();
4828 const { pop } = useHistory();
@@ -111,9 +91,9 @@ export default function Transfer({ className, goActive }) {
11191 );
11292
11393 return (
114− <Grid gap={completed ? 0 : 6} className={cn('mt4', className)} full>
94+ <>
11595 <BridgeForm onValues={onValues} validate={validate}>
116− {({ values, form }) => (
96+ {({ values }) => (
11797 <>
11898 {!completed && (
11999 <>
@@ -164,6 +144,6 @@ export default function Transfer({ className, goActive }) {
164144 </>
165145 )}
166146 </BridgeForm>
167− </Grid>
147+ </>
168148 );
169−}
149+};
src/views/Release/Locked.js → src/views/Release/WithdrawForm.tsxrenamed+32−79
@@ -1,66 +1,23 @@
1−import React, { useCallback, useEffect, useMemo } from 'react';
21import cn from 'classnames';
3−import { Grid, Flex } from 'indigo-react';
4−import { Just } from 'folktale/maybe';
5−
6−import * as need from 'lib/need';
7−import useEthereumTransaction from 'lib/useEthereumTransaction';
8−import { pluralize } from 'lib/pluralize';
9−
10−import { useWallet } from 'store/wallet';
11−import { usePointCache } from 'store/pointCache';
12−import { useStarReleaseCache } from 'store/starRelease';
13−
2+import { Box, Text } from '@tlon/indigo-react';
3+import { AddressButton } from 'components/AddressButton';
144import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
15−import { matchBlinky } from 'components/Blinky';
16−
17−import { NumberInput, AddressInput } from 'form/Inputs';
185import BridgeForm from 'form/BridgeForm';
19−import FormError from 'form/FormError';
20−import { composeValidator, buildNumberValidator } from 'form/validators';
216import { convertToNumber } from 'form/formatters';
7+import FormError from 'form/FormError';
8+import { AddressInput, NumberInput } from 'form/Inputs';
9+import { buildNumberValidator, composeValidator } from 'form/validators';
10+import { Grid, Flex } from 'indigo-react';
11+import { pluralize } from 'lib/pluralize';
12+import { useCallback, useEffect, useMemo } from 'react';
13+import { useStarReleaseCache } from 'store/starRelease';
14+import { useWithdrawStars } from './useWithdrawStars';
2215
23−function MatchPluralizeStars({ quantity }) {
24− const _quantity = quantity.getOrElse(0);
25− return (
26− <>
27− {' '}
28− <span
29− className={cn({
30− green2: _quantity > 0,
31− red4: Just.hasInstance(quantity) && _quantity === 0,
32− })}>
33− {matchBlinky(quantity)}
34− </span>
35− {pluralize(_quantity, ' star ', ' stars ')}
36− </>
37− );
38−}
39−
40−function AddressIndicator({ isOwn, className }) {
41− return isOwn ? (
42− <div className={cn('green2', className)}>✓ Current Address</div>
43− ) : (
44− <div className={cn('red4', className)}>! Different address</div>
45− );
46−}
47−
48−function useWithdrawStars() {
49− const { syncControlledPoints } = usePointCache();
50−
51− const { syncStarReleaseDetails, withdraw } = useStarReleaseCache();
52− return useEthereumTransaction(
53− useCallback((to, amount) => withdraw(amount, to), [withdraw]),
54− useCallback(
55− () => Promise.all([syncControlledPoints(), syncStarReleaseDetails()]),
56− [syncStarReleaseDetails, syncControlledPoints]
57− )
58− );
16+interface WithdrawFormProps {
17+ afterSubmit: VoidFunction;
5918}
6019
61−export default function Locked({ className, goActive }) {
62− const { wallet } = useWallet();
63− const address = need.addressFromWallet(wallet);
20+export const WithdrawForm = ({ afterSubmit }: WithdrawFormProps) => {
6421 const {
6522 bind,
6623 construct,
@@ -101,38 +58,33 @@ export default function Locked({ className, goActive }) {
10158 [canWithdraw, inputsLocked]
10259 );
10360
104− const initialValues = { address };
61+ const initialValues = {};
10562
10663 return (
107− <Grid gap={completed ? 0 : 6} className={cn('mt4', className)} full>
64+ <>
10865 <BridgeForm
10966 initialValues={initialValues}
11067 onValues={onValues}
111− validate={validate}>
68+ validate={validate}
69+ afterSubmit={afterSubmit}>
11270 {({ values, form }) => (
11371 <>
11472 {!completed && (
11573 <>
116− <Grid.Item full className="f5">
117− You have <MatchPluralizeStars quantity={canWithdraw} />
118− available to withdraw
119− <br />
120− You have
121− <MatchPluralizeStars quantity={total} />
122− total
123− </Grid.Item>
74+ <Box className={cn("info-row", { available: canWithdraw.value > 0})}>
75+ <Text>{`${canWithdraw.value} of ${total.value} available`}</Text>
76+ </Box>
77+ <Box className="info-row">
78+ <Text>{`${total.value -
79+ canWithdraw.value} are in lockup`}</Text>
80+ </Box>
12481 <Grid.Item
12582 cols={[1, 4]}
12683 label="Stars"
12784 as={NumberInput}
128− placeholder="#"
85+ placeholder="0"
12986 name="numStars"
13087 disabled={inputsLocked}
131− accessory={
132− <div className="flex-center h-full">
133− / &nbsp;{matchBlinky(canWithdraw)}
134− </div>
135− }
13688 />
13789 <Grid.Item full className="f5" cols={[1, 13]} as={Flex} col>
13890 <Flex.Item
@@ -141,10 +93,11 @@ export default function Locked({ className, goActive }) {
14193 label="Withdraw To"
14294 disabled={inputsLocked}
14395 />
144− <Flex.Item
145− as={AddressIndicator}
146− isOwn={address === values.address}
147− />
96+ <Box>
97+ <AddressButton inputName="address">
98+ Use my address
99+ </AddressButton>
100+ </Box>
148101 </Grid.Item>
149102 </>
150103 )}
@@ -173,6 +126,6 @@ export default function Locked({ className, goActive }) {
173126 </>
174127 )}
175128 </BridgeForm>
176− </Grid>
129+ </>
177130 );
178−}
131+};
src/views/Release/useTransferLockup.tsnew file+24
@@ -0,0 +1,24 @@
1+import useEthereumTransaction from 'lib/useEthereumTransaction';
2+import { useCallback } from 'react';
3+import { useNetwork } from 'store/network';
4+import * as need from 'lib/need';
5+import { conditionalSR, linearSR } from 'azimuth-js';
6+
7+export function useTransferLockup(kind) {
8+ const { contracts } = useNetwork();
9+
10+ return useEthereumTransaction(
11+ useCallback(
12+ to => {
13+ const _contracts = need.contracts(contracts);
14+ if (kind === 'conditional') {
15+ return conditionalSR.approveCommitmentTransfer(_contracts, to);
16+ } else {
17+ return linearSR.approveBatchTransfer(_contracts, to);
18+ }
19+ },
20+ [kind, contracts]
21+ ),
22+ () => {}
23+ );
24+}
src/views/Release/useWithdrawStars.tsnew file+17
@@ -0,0 +1,17 @@
1+import useEthereumTransaction from 'lib/useEthereumTransaction';
2+import { useCallback } from 'react';
3+import { usePointCache } from 'store/pointCache';
4+import { useStarReleaseCache } from 'store/starRelease';
5+
6+export function useWithdrawStars() {
7+ const { syncControlledPoints } = usePointCache();
8+
9+ const { syncStarReleaseDetails, withdraw } = useStarReleaseCache();
10+ return useEthereumTransaction(
11+ useCallback((to, amount) => withdraw(amount, to), [withdraw]),
12+ useCallback(
13+ () => Promise.all([syncControlledPoints(), syncStarReleaseDetails()]),
14+ [syncStarReleaseDetails, syncControlledPoints]
15+ )
16+ );
17+}