on 02ebd11 help sign in
bridge/src/views/Points.js
237 lines 7.1 kB history raw
1import React from 'react';
2import { Just, Nothing } from 'folktale/maybe';
3import { Grid, H5, H1, HelpText, LinkButton, Flex } from 'indigo-react';
5import { useHistory } from 'store/history';
6import { useWallet } from 'store/wallet';
7import { usePointCache } from 'store/pointCache';
8import { usePointCursor } from 'store/pointCursor';
10import * as need from 'lib/need';
11import { ROUTE_NAMES } from 'lib/routeNames';
12import { isZeroAddress, abbreviateAddress } from 'lib/wallet';
13import useIsEclipticOwner from 'lib/useIsEclipticOwner';
14import { useSyncKnownPoints } from 'lib/useSyncPoints';
16import View from 'components/View';
17import FooterButton from 'components/FooterButton';
18import Blinky from 'components/Blinky';
19import Passport from 'components/Passport';
20import useRejectedIncomingPointTransfers from 'lib/useRejectedIncomingPointTransfers';
21import pluralize from 'lib/pluralize';
23const getFromMaybe = (obj, key, defaultValue) =>
24 obj.matchWith({
25 Nothing: () => defaultValue,
26 Just: p =>
27 p.value.matchWith({
28 Ok: r => r.value[key],
29 Error: e => defaultValue,
30 }),
31 });
33const hasTransferProxy = (details, point) =>
34 !isZeroAddress(details.transferProxy);
36const PointList = function({ points, className, actions, ...rest }) {
37 const { setPointCursor } = usePointCursor();
38 const history = useHistory();
40 return (
41 <Grid gap={3} className={className}>
42 {points.map((point, i) => (
43 <Grid.Item half={(i % 2) + 1} key={point}>
44 <Flex col>
45 <Passport.Mini
46 point={point}
47 className="clickable"
48 onClick={() => {
49 setPointCursor(Just(point));
50 history.push(history.names.POINT);
51 }}
52 {...rest}
53 />
54 {actions && (
55 <Flex.Item className="mt2">{actions(point, i)}</Flex.Item>
56 )}
57 </Flex>
58 </Grid.Item>
59 ))}
60 </Grid>
61 );
62};
64function ActionButtons({ actions = [] }) {
65 return (
66 <Flex row>
67 {actions.map(action => (
68 <Flex.Item
69 key={action.text}
70 as={LinkButton}
71 className="mr3"
72 onClick={action.onClick}>
73 {action.text}
74 </Flex.Item>
75 ))}
76 </Flex>
77 );
80export default function Points() {
81 const { wallet } = useWallet();
82 const history = useHistory();
83 const { setPointCursor } = usePointCursor();
84 const { controlledPoints, getDetails } = usePointCache();
85 const isEclipticOwner = useIsEclipticOwner();
86 const [
87 rejectedPoints,
88 addRejectedPoint,
89 ] = useRejectedIncomingPointTransfers();
91 const address = need.addressFromWallet(wallet);
93 const loading = Nothing.hasInstance(controlledPoints);
95 const ownedPoints = getFromMaybe(controlledPoints, 'ownedPoints', []);
96 const incomingPoints = getFromMaybe(
97 controlledPoints,
98 'incomingPoints',
99 []
100 ).filter(point => !rejectedPoints.includes(point));
101 const managingPoints = getFromMaybe(controlledPoints, 'managingPoints', []);
102 const votingPoints = getFromMaybe(controlledPoints, 'votingPoints', []);
103 const spawningPoints = getFromMaybe(controlledPoints, 'spawningPoints', []);
104 const outgoingPoints = ownedPoints.filter(point =>
105 getDetails(point).matchWith({
106 Nothing: () => false,
107 Just: p => hasTransferProxy(p.value),
108 })
109 );
111 const multipassPoints = [
112 ...ownedPoints.filter(p => !outgoingPoints.includes(p)),
113 ...managingPoints,
114 ...votingPoints,
115 ...spawningPoints,
116 ];
118 const isEmpty = incomingPoints.length === 0 && multipassPoints.length === 0;
120 // sync display details for known points
121 useSyncKnownPoints([
122 ...ownedPoints,
123 ...incomingPoints,
124 ...managingPoints,
125 ...votingPoints,
126 ...spawningPoints,
127 ]);
129 return (
130 <View>
131 <Grid className="mt6">
132 <Grid.Item full as={H1} className="f6 mono gray4 mb4">
133 {abbreviateAddress(address)}
134 </Grid.Item>
136 {loading && (
137 <Grid.Item full as={HelpText} className="mt8 t-center">
138 <Blinky /> Loading...
139 </Grid.Item>
140 )}
142 {isEmpty && (
143 <Grid.Item full as={HelpText} className="mt8 t-center">
144 No points to display. This wallet is not the owner or proxy for any
145 points.
146 </Grid.Item>
147 )}
149 {incomingPoints.length > 0 && (
150 <Grid.Item full as={Grid} gap={1} className="mb6">
151 <Grid.Item full as={H5}>
152 {pluralize(
153 incomingPoints.length,
154 'Incoming Transfer',
155 'Incoming Transfers'
156 )}
157 </Grid.Item>
158 <Grid.Item
159 full
160 as={PointList}
161 points={incomingPoints}
162 actions={(point, i) => (
163 <ActionButtons
164 actions={[
165 {
166 text: 'Accept',
167 onClick: () => {
168 setPointCursor(Just(point));
169 history.push(history.names.ACCEPT_TRANSFER);
170 },
171 },
172 {
173 text: 'Reject',
174 onClick: () => {
175 addRejectedPoint(point);
176 },
177 },
178 ]}
179 />
180 )}
181 inverted
182 />
183 </Grid.Item>
184 )}
186 {outgoingPoints.length > 0 && (
187 <Grid.Item full as={Grid} gap={1} className="mb6">
188 <Grid.Item full as={H5}>
189 {pluralize(
190 outgoingPoints.length,
191 'Outgoing Transfer',
192 'Outgoing Transfers'
193 )}
194 </Grid.Item>
195 <Grid.Item
196 full
197 as={PointList}
198 points={outgoingPoints}
199 actions={(point, i) => (
200 <ActionButtons
201 actions={[
202 {
203 text: 'Cancel',
204 onClick: () => {
205 setPointCursor(Just(point));
206 // TODO: deep linking to fix this duplicate route
207 history.push(history.names.CANCEL_TRANSFER);
208 },
209 },
210 ]}
211 />
212 )}
213 inverted
214 />
215 </Grid.Item>
216 )}
218 {multipassPoints.length > 0 && (
219 <Grid.Item full as={Grid} gap={1}>
220 <Grid.Item full as={H5}>
221 {pluralize(multipassPoints.length, 'Multipass', 'Multipasses')}
222 </Grid.Item>
223 <Grid.Item full as={PointList} points={multipassPoints} />
224 </Grid.Item>
225 )}
227 {isEclipticOwner && (
228 <FooterButton
229 detail="You have the authority to create a new Galaxy."
230 onClick={() => history.push(ROUTE_NAMES.CREATE_GALAXY)}>
231 Create a galaxy
232 </FooterButton>
233 )}
234 </Grid>
235 </View>
236 );