@@ -1,11 +1,12 @@
1import React, { useMemo, useEffect, useCallback, useState } from 'react';
1import React, { useMemo, useEffect, useCallback, useState } from 'react';
2import { Grid, Flex, Button } from 'indigo-react';
2import { Grid, Flex, Button } from 'indigo-react';
3import * as ob from 'urbit-ob';
3import * as ob from 'urbit-ob';
4−import { Just } from 'folktale/maybe';
4+import { Just, Nothing } from 'folktale/maybe';
5import cn from 'classnames';
5import cn from 'classnames';
7import { usePointCursor } from 'store/pointCursor';
7import { usePointCursor } from 'store/pointCursor';
8import { usePointCache } from 'store/pointCache';
8import { usePointCache } from 'store/pointCache';
9+import { useWallet } from 'store/wallet';
10import View from 'components/View';
11import View from 'components/View';
11import Tabs from 'components/Tabs';
12import Tabs from 'components/Tabs';
@@ -19,10 +20,13 @@ import Crumbs from 'components/Crumbs';
19import { useLocalRouter } from 'lib/LocalRouter';
20import { useLocalRouter } from 'lib/LocalRouter';
20import useInvites from 'lib/useInvites';
21import useInvites from 'lib/useInvites';
21import * as need from 'lib/need';
22import * as need from 'lib/need';
23+import * as wg from 'lib/walletgen';
23import Inviter from 'views/Invite/Inviter';
25import Inviter from 'views/Invite/Inviter';
25import { ReactComponent as SearchIcon } from 'assets/search.svg';
27import { ReactComponent as SearchIcon } from 'assets/search.svg';
28+import CopyButton from 'components/CopyButton';
29+import { useSyncForeignPoints } from 'lib/useSyncPoints';
27function SearchInput({ className, value, onChange }) {
31function SearchInput({ className, value, onChange }) {
@@ -42,7 +46,7 @@ function SearchInput({ className, value, onChange }) {
45−function CohortMember({ point, pending = false, className }) {
49+function CohortMember({ point, pending = false, className, ...rest }) {
46 const { sentInvites } = useInvites(point);
50 const { sentInvites } = useInvites(point);
47 const patp = useMemo(() => ob.patp(point), [point]);
51 const patp = useMemo(() => ob.patp(point), [point]);
48 const colors = pending ? ['#ee892b', '#FFFFFF'] : ['#000000', '#FFFFFF'];
52 const colors = pending ? ['#ee892b', '#FFFFFF'] : ['#000000', '#FFFFFF'];
@@ -54,7 +58,7 @@ function CohortMember({ point, pending = false, className }) {
57− <Flex justify="between" className={cn('b1 b-gray2', className)}>
61+ <Flex justify="between" className={cn('b1 b-gray2', className)} {...rest}>
58 <Flex.Item className="w9 h9">
62 <Flex.Item className="w9 h9">
59 <MaybeSigil patp={Just(patp)} size={64} colors={colors} />
63 <MaybeSigil patp={Just(patp)} size={64} colors={colors} />
60 </Flex.Item>
64 </Flex.Item>
@@ -73,7 +77,13 @@ function CohortMember({ point, pending = false, className }) {
76−function CohortList({ acceptedPoints, pendingPoints, className, onlyPending }) {
80+function CohortList({
77 const { syncInvites } = usePointCache();
87 const { syncInvites } = usePointCache();
79 const [query, setQuery] = useState('');
89 const [query, setQuery] = useState('');
@@ -98,7 +108,12 @@ function CohortList({ acceptedPoints, pendingPoints, className, onlyPending }) {
101−
111+ const selectInvite = useCallback(
113+ onSelectInvite(point);
102 const _pendingPoints = filterPoints(pendingPoints);
117 const _pendingPoints = filterPoints(pendingPoints);
103 const _acceptedPoints = filterPoints(acceptedPoints);
118 const _acceptedPoints = filterPoints(acceptedPoints);
@@ -115,6 +130,7 @@ function CohortList({ acceptedPoints, pendingPoints, className, onlyPending }) {
115 half={(idx % 2) + 1}
130 half={(idx % 2) + 1}
116 as={CohortMember}
131 as={CohortMember}
117 point={p}
132 point={p}
133+ onClick={selectInvite(p)}
120 {_pendingPoints.map((p, idx) => (
136 {_pendingPoints.map((p, idx) => (
@@ -124,6 +140,7 @@ function CohortList({ acceptedPoints, pendingPoints, className, onlyPending }) {
124 as={CohortMember}
140 as={CohortMember}
125 point={p}
141 point={p}
143+ onClick={selectInvite(p)}
@@ -139,12 +156,122 @@ function CohortList({ acceptedPoints, pendingPoints, className, onlyPending }) {
159+function hideIf(hider, hidden) {
160+ return hider ? '●●●●●●●' : hidden;
163+function CohortMemberExpanded({ point, className, ...rest }) {
164+ const { sentInvites, acceptedInvites } = useInvites(point);
165+ const { getDetails } = usePointCache();
166+ const { authToken } = useWallet();
167+ const details = getDetails(point).getOrElse({});
168+ const { active } = details;
169+ const patp = useMemo(() => ob.patp(point), [point]);
170+ const colors = !active ? ['#ee892b', '#FFFFFF'] : ['#000000', '#FFFFFF'];
172+ const [code, setCode] = useState(Nothing());
173+ const [codeVisible, setCodeVisible] = useState(false);
176+ const fetchWallet = async () => {
177+ const _authToken = authToken.getOrElse(null);
178+ if (!_authToken || active) {
181+ const ticket = await wg.makeDeterministicTicket(point, _authToken);
182+ setCode(Just(ticket));
185+ }, [authToken, point, active]);
187+ const DetailText = useCallback(
189+ !active ? 'Pending' : <> {matchBlinky(sentInvites)} points invited </>,
190+ [active, sentInvites]
194+ <Grid justify="between" className={cn('b1 b-gray2', className)} {...rest}>
195+ <Grid.Item rows={[1, 4]} cols={[1, 4]} className="h10 w10">
196+ <MaybeSigil patp={Just(patp)} size={64} colors={colors} />
199+ <Grid.Item cols={[4, 13]} className="mono mt-auto ">
207+ className="mt-auto mb1 f6 gray4"
214+ className="mb-auto mt1 f6 gray4"
217+ onClick={() => setCodeVisible(r => !r)}>
218+ {hideIf(!codeVisible, matchBlinky(code))}
221+ className="mh-auto f6"
224+ text={code.getOrElse('')}
233+ className="mt-auto mb1 f6 gray4"
236+ {matchBlinky(sentInvites)} invites sent
240+ className="mb-auto mt1 f6 gray4"
243+ {matchBlinky(acceptedInvites)} invites accepted
248+ {/* <Flex.Item>Invite Code</Flex.Item> */}
249+ {/* <Flex.Item>{matchBlinky(code)}</Flex.Item> */}
250+ {/* <span className="pointer underline">Get Invite code </span> */}
252+ {/* {resend && <span>{matchBlinky(code)}</span>} */}
253+ {/* </Grid.Item> */}
254+ {/* {resend && ( */}
255+ {/* <Grid.Item cols={[5, 13]} rows={[2, 3]} className="mv-auto"></Grid.Item> */}
257+ {/* <Grid.Item justify="evenly" as={Flex} col className="mono f6 ph4"> */}
258+ {/* <Flex.Item>{patp}</Flex.Item> */}
259+ {/* <Flex.Item className="gray4"> */}
260+ {/* <DetailText /> */}
261+ {/* </Flex.Item> */}
262+ {/* </Grid.Item> */}
142export default function InviteCohort() {
267export default function InviteCohort() {
143 const { pop } = useLocalRouter();
268 const { pop } = useLocalRouter();
145 const { pointCursor } = usePointCursor();
270 const { pointCursor } = usePointCursor();
146 const point = need.point(pointCursor);
271 const point = need.point(pointCursor);
273+ const { syncDetails } = usePointCache();
149 acceptedInvites,
276 acceptedInvites,
150 acceptedPoints,
277 acceptedPoints,
@@ -164,6 +291,17 @@ export default function InviteCohort() {
164 .map(a => sentInvites.getOrElse(0) + a)
291 .map(a => sentInvites.getOrElse(0) + a)
165 .getOrElse(0);
292 .getOrElse(0);
294+ const [selectedInvite, _setSelectedInvite] = useState();
297+ Promise.all(_acceptedPoints.map(i => syncDetails(i)));
298+ }, [_acceptedPoints, syncDetails]);
300+ const setSelectedInvite = useCallback(
301+ p => _setSelectedInvite(old => (old === p ? null : p)),
168 <View pop={pop} inset>
306 <View pop={pop} inset>
169 <Grid gap={3}>
307 <Grid gap={3}>
@@ -209,18 +347,31 @@ export default function InviteCohort() {
210 {showInviteForm && <Inviter />}
348 {showInviteForm && <Inviter />}
212− <Grid.Item
351+ <Grid.Item
215− as={Tabs}
354+ as={Tabs}
216− views={VIEWS}
355+ views={VIEWS}
217− options={OPTIONS}
356+ options={OPTIONS}
218− currentTab={tab}
357+ currentTab={tab}
219− onTabChange={setTab}
358+ onTabChange={setTab}
220− acceptedPoints={_acceptedPoints}
359+ acceptedPoints={_acceptedPoints}
221− pendingPoints={_pendingPoints}
360+ pendingPoints={_pendingPoints}
222− onlyPending={tab === NAMES.PENDING}
361+ onlyPending={tab === NAMES.PENDING}
362+ onSelectInvite={setSelectedInvite}
365+ {selectedInvite && (
369+ as={CohortMemberExpanded}
371+ point={selectedInvite}
372+ onBack={() => setSelectedInvite(null)}