import React, { useCallback, useEffect, useMemo } from 'react';
import { Just, Nothing } from 'folktale/maybe';
import { Grid, H5, H1, HelpText, LinkButton, Flex } from 'indigo-react';
import { get } from 'lodash';
import { useHistory } from 'store/history';
import { useWallet } from 'store/wallet';
import { usePointCache } from 'store/pointCache';
import { usePointCursor } from 'store/pointCursor';
import { useStarReleaseCache } from 'store/starRelease';
import * as need from 'lib/need';
import { isZeroAddress, abbreviateAddress } from 'lib/wallet';
import useIsEclipticOwner from 'lib/useIsEclipticOwner';
import { useSyncKnownPoints, useSyncOwnedPoints } from 'lib/useSyncPoints';
import useRejectedIncomingPointTransfers from 'lib/useRejectedIncomingPointTransfers';
import pluralize from 'lib/pluralize';
import View from 'components/View';
import Blinky from 'components/Blinky';
import Passport from 'components/Passport';
import Footer from 'components/Footer';
import { ForwardButton } from 'components/Buttons';
import CopiableAddress from 'components/CopiableAddress';
const maybeGetResult = (obj, key, defaultValue) =>
obj.matchWith({
Nothing: () => defaultValue,
Just: p =>
p.value.matchWith({
Ok: r => get(r.value, key, defaultValue),
Error: e => defaultValue,
}),
});
const hasTransferProxy = details => !isZeroAddress(details.transferProxy);
const PointList = function({ points, className, actions, ...rest }) {
const { setPointCursor } = usePointCursor();
const { push, names } = useHistory();
return (
{points.map((point, i) => (
{
setPointCursor(Just(point));
push(names.POINT);
}}
{...rest}
/>
{actions && (
{actions(point, i)}
)}
))}
);
};
function ActionButtons({ actions = [] }) {
return (
{actions.map(action => (
{action.text}
))}
);
}
export default function Points() {
const { wallet } = useWallet();
const { pop, push, popAndPush, names } = useHistory();
const { setPointCursor } = usePointCursor();
const { controlledPoints, getDetails } = usePointCache();
const isEclipticOwner = useIsEclipticOwner();
const [
rejectedPoints,
addRejectedPoint,
] = useRejectedIncomingPointTransfers();
const { syncStarReleaseDetails, starReleaseDetails } = useStarReleaseCache();
const maybeOutgoingPoints = useMemo(
() =>
controlledPoints.chain(points =>
points.matchWith({
Error: () => Nothing(),
Ok: c => {
const points = c.value.ownedPoints.map(point =>
getDetails(point).chain(details =>
Just({ point: point, has: hasTransferProxy(details) })
)
);
// if we have details for every point,
// return the array of pending transfers.
if (points.every(p => Just.hasInstance(p))) {
const outgoing = points
.filter(p => p.value.has)
.map(p => p.value.point);
return Just(outgoing);
} else {
return Nothing();
}
},
})
),
[getDetails, controlledPoints]
);
// if we can only interact with a single point, forget about the existence
// of this page and jump to the point page.
// if there are any pending transfers, incoming or outgoing, stay on this
// page, because those can only be completed/cancelled here.
useEffect(() => {
if (Nothing.hasInstance(maybeOutgoingPoints)) {
return;
}
controlledPoints.matchWith({
Nothing: () => null,
Just: r => {
r.value.matchWith({
Error: () => null,
Ok: c => {
let all = [
...c.value.ownedPoints,
...c.value.votingPoints,
...c.value.managingPoints,
...c.value.spawningPoints,
];
const incoming = c.value.incomingPoints.filter(
p => !rejectedPoints.includes(p)
);
if (
all.length === 1 &&
incoming.length === 0 &&
maybeOutgoingPoints.value.length === 0
) {
setPointCursor(Just(all[0]));
popAndPush(names.POINT);
}
},
});
},
});
}, [
controlledPoints,
rejectedPoints,
maybeOutgoingPoints,
setPointCursor,
popAndPush,
names,
]);
const address = need.addressFromWallet(wallet);
const loading = Nothing.hasInstance(controlledPoints);
const ownedPoints = maybeGetResult(controlledPoints, 'ownedPoints', []);
const incomingPoints = maybeGetResult(
controlledPoints,
'incomingPoints',
[]
).filter(point => !rejectedPoints.includes(point));
const managingPoints = maybeGetResult(controlledPoints, 'managingPoints', []);
const votingPoints = maybeGetResult(controlledPoints, 'votingPoints', []);
const spawningPoints = maybeGetResult(controlledPoints, 'spawningPoints', []);
const outgoingPoints = maybeOutgoingPoints.getOrElse([]);
const allPoints = [
...ownedPoints.filter(p => !outgoingPoints.includes(p)),
...managingPoints,
...votingPoints,
...spawningPoints,
];
const displayEmptyState =
!loading && incomingPoints.length === 0 && allPoints.length === 0;
const starReleasing = starReleaseDetails
.map(s => s.total > 0)
.getOrElse(false);
useEffect(() => {
syncStarReleaseDetails();
}, [syncStarReleaseDetails]);
// sync display details for known points
useSyncKnownPoints([
...ownedPoints,
...incomingPoints,
...managingPoints,
...votingPoints,
...spawningPoints,
]);
useSyncOwnedPoints(ownedPoints);
const goCreateGalaxy = useCallback(() => push(names.CREATE_GALAXY), [
names.CREATE_GALAXY,
push,
]);
const goViewPoint = useCallback(() => push(names.VIEW_POINT), [
names.VIEW_POINT,
push,
]);
const goStarRelease = useCallback(() => push(names.STAR_RELEASE), [
names.STAR_RELEASE,
push,
]);
if (
loading ||
(allPoints.length === 1 &&
incomingPoints.length === 0 &&
outgoingPoints.length === 0)
) {
return (
Loading...
);
}
return (
{abbreviateAddress(address)}
{displayEmptyState && (
No points to display. This wallet is not the owner or proxy for any
points.
)}
{incomingPoints.length > 0 && (
{pluralize(
incomingPoints.length,
'Incoming Transfer',
'Incoming Transfers'
)}
(
{
setPointCursor(Just(point));
push(names.ACCEPT_TRANSFER);
},
},
{
text: 'Reject',
onClick: () => {
addRejectedPoint(point);
},
},
]}
/>
)}
inverted
/>
)}
{outgoingPoints.length > 0 && (
{pluralize(
outgoingPoints.length,
'Outgoing Transfer',
'Outgoing Transfers'
)}
(
{
setPointCursor(Just(point));
// TODO: deep linking to fix this duplicate route
push(names.CANCEL_TRANSFER);
},
},
]}
/>
)}
inverted
/>
)}
{allPoints.length > 0 && (
{pluralize(allPoints.length, 'ID')}
)}
);
}