1import React, { useCallback, useEffect, useMemo } from 'react';
2import { Just, Nothing, Result } from 'folktale/maybe';
3import { Grid, H5, 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';
10import { useStarReleaseCache } from 'store/starRelease';
12import * as need from 'lib/need';
13import { isZeroAddress, abbreviateAddress } from 'lib/utils/crypto';
14import useIsEclipticOwner from 'lib/useIsEclipticOwner';
15import useRejectedIncomingPointTransfers from 'lib/useRejectedIncomingPointTransfers';
16import pluralize from 'lib/pluralize';
17import newGithubIssueUrl from 'new-github-issue-url';
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';
24import CopiableAddress from 'components/CopiableAddress';
25import NavHeader from 'components/NavHeader';
27const maybeGetResult = (obj, key, defaultValue) =>
29 Nothing: () => defaultValue,
32 Ok: r => get(r.value, key, defaultValue),
33 Error: e => defaultValue,
37const hasTransferProxy = details => !isZeroAddress(details.transferProxy);
39const PointList = function({ points, className, actions, ...rest }) {
40 const { setPointCursor } = usePointCursor();
41 const { push, names } = useHistory();
44 <Grid gap={3} className={className}>
45 {points.map((point, i) => (
48 className={`full half-${(i % 2) + 1}-md half-${(i % 2) + 1}-lg`}>
54 setPointCursor(Just(point));
60 <Flex.Item className="mt2">{actions(point, i)}</Flex.Item>
69function ActionButtons({ actions = [] }) {
72 {actions.map(action => (
77 onClick={action.onClick}>
85export default function Points() {
86 const { wallet } = useWallet();
87 const { pop, push, popAndPush, names } = useHistory();
88 const { setPointCursor } = usePointCursor();
89 const { controlledPoints, getDetails } = usePointCache();
90 const isEclipticOwner = useIsEclipticOwner();
94 ] = useRejectedIncomingPointTransfers();
95 const { syncStarReleaseDetails, starReleaseDetails } = useStarReleaseCache();
97 const maybeOutgoingPoints = useMemo(
99 controlledPoints.chain(points =>
101 Error: () => Nothing(),
103 const points = c.value.ownedPoints.map(point =>
104 getDetails(point).chain(details =>
105 Just({ point: point, has: hasTransferProxy(details) })
108 // if we have details for every point,
109 // return the array of pending transfers.
110 if (points.every(p => Just.hasInstance(p))) {
111 const outgoing = points
112 .filter(p => p.value.has)
113 .map(p => p.value.point);
114 return Just(outgoing);
121 [getDetails, controlledPoints]
124 // if we can only interact with a single point, forget about the existence
125 // of this page and jump to the point page.
126 // if there are any pending transfers, incoming or outgoing, stay on this
127 // page, because those can only be completed/cancelled here.
130 Nothing.hasInstance(maybeOutgoingPoints) ||
131 Nothing.hasInstance(starReleaseDetails)
135 controlledPoints.matchWith({
142 ...c.value.ownedPoints,
143 ...c.value.votingPoints,
144 ...c.value.managingPoints,
145 ...c.value.spawningPoints,
147 const incoming = c.value.incomingPoints.filter(
148 p => !rejectedPoints.includes(p)
152 incoming.length === 0 &&
153 maybeOutgoingPoints.value.length === 0 &&
154 starReleaseDetails.value.total === 0
156 setPointCursor(Just(all[0]));
157 popAndPush(names.POINT);
173 const address = need.addressFromWallet(wallet);
175 const loading = Nothing.hasInstance(controlledPoints);
177 const ownedPoints = maybeGetResult(controlledPoints, 'ownedPoints', []);
178 const incomingPoints = maybeGetResult(
182 ).filter(point => !rejectedPoints.includes(point));
183 const managingPoints = maybeGetResult(controlledPoints, 'managingPoints', []);
184 const votingPoints = maybeGetResult(controlledPoints, 'votingPoints', []);
185 const spawningPoints = maybeGetResult(controlledPoints, 'spawningPoints', []);
186 const outgoingPoints = maybeOutgoingPoints.getOrElse([]);
189 ...ownedPoints.filter(p => !outgoingPoints.includes(p)),
195 const displayEmptyState =
196 !loading && incomingPoints.length === 0 && allPoints.length === 0;
198 const starReleasing = starReleaseDetails
199 .map(s => s.total > 0)
203 syncStarReleaseDetails();
204 }, [syncStarReleaseDetails]);
206 const goCreateGalaxy = useCallback(() => push(names.CREATE_GALAXY), [
211 const goStarRelease = useCallback(() => push(names.STAR_RELEASE), [
216 // if we got an error result, we should display it, instead of showing
217 // a potentially inaccurately empty point list.
220 Just.hasInstance(controlledPoints) &&
221 controlledPoints.value.matchWith({
226 const url = newGithubIssueUrl({
229 title: controlledPoints.value.value,
230 body: `<!-- Please provide some context. Do you have Metamask installed? What login method did you use? -->`,
235 <Grid.Item full as={HelpText} className="mt8 t-center">
236 {controlledPoints.value.value}
238 Try reloading the page. If this problem persists, please{' '}
239 <a href={url}>file an issue on GitHub</a>.
248 (allPoints.length === 1 &&
249 incomingPoints.length === 0 &&
250 outgoingPoints.length === 0 &&
256 <Grid.Item full as={HelpText} className="mt8 t-center">
257 <Blinky /> Loading...
265 <View inset pop={pop} hideBack>
269 className="f6 mono gray4 mb4 us-none pointer">
270 {abbreviateAddress(address)}
274 {displayEmptyState && (
275 <Grid.Item full as={HelpText} className="mt8 t-center">
276 No points to display. This wallet is not the owner or proxy for any
281 {incomingPoints.length > 0 && (
282 <Grid.Item full as={Grid} gap={1} className="mb6">
283 <Grid.Item full as={H5}>
285 incomingPoints.length,
293 points={incomingPoints}
294 actions={(point, i) => (
300 setPointCursor(Just(point));
301 push(names.ACCEPT_TRANSFER);
307 addRejectedPoint(point);
318 {outgoingPoints.length > 0 && (
319 <Grid.Item full as={Grid} gap={1} className="mb6">
320 <Grid.Item full as={H5}>
322 outgoingPoints.length,
330 points={outgoingPoints}
331 actions={(point, i) => (
337 setPointCursor(Just(point));
338 // TODO: deep linking to fix this duplicate route
339 push(names.CANCEL_TRANSFER);
350 {allPoints.length > 0 && (
351 <Grid.Item full as={Grid} gap={1}>
352 <Grid.Item full as={H5}>
353 {pluralize(allPoints.length, 'ID')}
355 <Grid.Item full as={PointList} points={allPoints} />
362 {isEclipticOwner && (
367 detail="You have the authority to create a new Galaxy."
368 onClick={goCreateGalaxy}>
379 detail="You have points being released"
380 onClick={goStarRelease}>
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