1import React, { useCallback, useEffect, useMemo } from 'react';
2import { Just, Nothing } from 'folktale/maybe';
3import { Grid, H5, H1, HelpText, LinkButton, Flex } from 'indigo-react';
4import { get } from 'lodash';
6import { useHistory } from 'store/history';
7import { useWallet } from 'store/wallet';
8import { usePointCache } from 'store/pointCache';
9import { usePointCursor } from 'store/pointCursor';
11import * as need from 'lib/need';
12import { isZeroAddress, abbreviateAddress } from 'lib/wallet';
13import useIsEclipticOwner from 'lib/useIsEclipticOwner';
14import { useSyncKnownPoints } from 'lib/useSyncPoints';
15import useRejectedIncomingPointTransfers from 'lib/useRejectedIncomingPointTransfers';
16import pluralize from 'lib/pluralize';
17import useCopiable from 'lib/useCopiable';
19import View from 'components/View';
20import Blinky from 'components/Blinky';
21import Passport from 'components/Passport';
22import Footer from 'components/Footer';
23import { ForwardButton } from 'components/Buttons';
25const maybeGetResult = (obj, key, defaultValue) =>
27 Nothing: () => defaultValue,
30 Ok: r => get(r.value, key, defaultValue),
31 Error: e => defaultValue,
35const hasTransferProxy = details => !isZeroAddress(details.transferProxy);
37const PointList = function({ points, className, actions, ...rest }) {
38 const { setPointCursor } = usePointCursor();
39 const { push, names } = useHistory();
42 <Grid gap={3} className={className}>
43 {points.map((point, i) => (
46 className={`full half-${(i % 2) + 1}-md half-${(i % 2) + 1}-lg`}>
52 setPointCursor(Just(point));
58 <Flex.Item className="mt2">{actions(point, i)}</Flex.Item>
67function ActionButtons({ actions = [] }) {
70 {actions.map(action => (
75 onClick={action.onClick}>
83export default function Points() {
84 const { wallet } = useWallet();
85 const { pop, push, popAndPush, names } = useHistory();
86 const { setPointCursor } = usePointCursor();
87 const { controlledPoints, getDetails } = usePointCache();
88 const isEclipticOwner = useIsEclipticOwner();
92 ] = useRejectedIncomingPointTransfers();
94 const maybeOutgoingPoints = useMemo(
96 controlledPoints.chain(points =>
98 Error: () => Nothing(),
100 const points = c.value.ownedPoints.map(point =>
101 getDetails(point).chain(details =>
102 Just({ point: point, has: hasTransferProxy(details) })
105 // if we have details for every point,
106 // return the array of pending transfers.
107 if (points.every(p => Just.hasInstance(p))) {
108 const outgoing = points
109 .filter(p => p.value.has)
110 .map(p => p.value.point);
111 return Just(outgoing);
118 [getDetails, controlledPoints]
121 // if we can only interact with a single point, forget about the existence
122 // of this page and jump to the point page.
123 // if there are any pending transfers, incoming or outgoing, stay on this
124 // page, because those can only be completed/cancelled here.
126 if (Nothing.hasInstance(maybeOutgoingPoints)) {
129 controlledPoints.matchWith({
136 ...c.value.ownedPoints,
137 ...c.value.votingPoints,
138 ...c.value.managingPoints,
139 ...c.value.spawningPoints,
141 const incoming = c.value.incomingPoints.filter(
142 p => !rejectedPoints.includes(p)
146 incoming.length === 0 &&
147 maybeOutgoingPoints.value.length === 0
149 setPointCursor(Just(all[0]));
150 popAndPush(names.POINT);
165 const address = need.addressFromWallet(wallet);
166 const [doCopy, didCopy] = useCopiable(address);
168 const loading = Nothing.hasInstance(controlledPoints);
170 const ownedPoints = maybeGetResult(controlledPoints, 'ownedPoints', []);
171 const incomingPoints = maybeGetResult(
175 ).filter(point => !rejectedPoints.includes(point));
176 const managingPoints = maybeGetResult(controlledPoints, 'managingPoints', []);
177 const votingPoints = maybeGetResult(controlledPoints, 'votingPoints', []);
178 const spawningPoints = maybeGetResult(controlledPoints, 'spawningPoints', []);
179 const outgoingPoints = maybeOutgoingPoints.getOrElse([]);
182 ...ownedPoints.filter(p => !outgoingPoints.includes(p)),
188 const displayEmptyState =
189 !loading && incomingPoints.length === 0 && allPoints.length === 0;
191 // sync display details for known points
200 const goCreateGalaxy = useCallback(() => push(names.CREATE_GALAXY), [
205 const goViewPoint = useCallback(() => push(names.VIEW_POINT), [
211 <View pop={pop} inset>
216 className="f6 mono gray4 mb4 us-none clickable"
218 {`${abbreviateAddress(address)}${didCopy ? ' (copied!)' : ''}`}
222 <Grid.Item full as={HelpText} className="mt8 t-center">
223 <Blinky /> Loading...
227 {displayEmptyState && (
228 <Grid.Item full as={HelpText} className="mt8 t-center">
229 No points to display. This wallet is not the owner or proxy for any
234 {incomingPoints.length > 0 && (
235 <Grid.Item full as={Grid} gap={1} className="mb6">
236 <Grid.Item full as={H5}>
238 incomingPoints.length,
246 points={incomingPoints}
247 actions={(point, i) => (
253 setPointCursor(Just(point));
254 push(names.ACCEPT_TRANSFER);
260 addRejectedPoint(point);
271 {outgoingPoints.length > 0 && (
272 <Grid.Item full as={Grid} gap={1} className="mb6">
273 <Grid.Item full as={H5}>
275 outgoingPoints.length,
283 points={outgoingPoints}
284 actions={(point, i) => (
290 setPointCursor(Just(point));
291 // TODO: deep linking to fix this duplicate route
292 push(names.CANCEL_TRANSFER);
303 {allPoints.length > 0 && (
304 <Grid.Item full as={Grid} gap={1}>
305 <Grid.Item full as={H5}>
306 {pluralize(allPoints.length, 'Point')}
308 <Grid.Item full as={PointList} points={allPoints} />
315 {isEclipticOwner && (
320 detail="You have the authority to create a new Galaxy."
321 onClick={goCreateGalaxy}>
330 detail="View a point"
331 onClick={goViewPoint}>
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list