on ded2e62 help sign in
bridge/src/views/Residents.js
226 lines 5.8 kB history raw
1import React, { useState, useEffect, useCallback, useMemo } from 'react';
2import cn from 'classnames';
3import { Grid, H4, HelpText, Flex } from 'indigo-react';
4import { Just, Nothing } from 'folktale/maybe';
5import * as ob from 'urbit-ob';
7import View from 'components/View';
8import Tabs from 'components/Tabs';
9import Crumbs from 'components/Crumbs';
10import Sigil from 'components/Sigil';
11import Blinky from 'components/Blinky';
12import NavHeader from 'components/NavHeader';
14import { useLocalRouter } from 'lib/LocalRouter';
15import useCurrentPointName from 'lib/useCurrentPointName';
16import * as need from 'lib/need';
18import { usePointCursor } from 'store/pointCursor';
19import { usePointCache } from 'store/pointCache';
21const NAMES = {
22 REQUESTS: 'REQUESTS',
23 ALL: 'ALL',
24};
26const OPTIONS = [
27 { text: 'Requests', value: NAMES.REQUESTS },
28 { text: 'All', value: NAMES.ALL },
29];
31const VIEWS = {
32 [NAMES.REQUESTS]: Tab,
33 [NAMES.ALL]: Tab,
34};
36const PAGE_SIZE = 10;
38function Tab({ className, points, onAccept, onDecline, page, setPage }) {
39 const start = page * PAGE_SIZE;
40 const end = page * PAGE_SIZE + PAGE_SIZE;
41 const pointsCount = points.map(ps => ps.length).getOrElse(0);
42 const maxPage = Math.ceil(pointsCount / PAGE_SIZE) - 1;
43 const hasNext = page < maxPage;
44 const hasPrev = page > 0;
45 const _points = points.map(ps => ps.slice(start, end)).getOrElse([]);
47 const onNext = useCallback(() => {
48 setPage(p => p + 1);
49 }, [setPage]);
51 const onPrev = useCallback(() => {
52 setPage(p => p - 1);
53 }, [setPage]);
55 if (Nothing.hasInstance(points)) {
56 return <Grid className={className}></Grid>;
57 }
59 return (
60 <Grid className={cn('mt2', className)}>
61 {_points.length === 0 && (
62 <Grid.Item full as={HelpText} className="mt8 t-center">
63 {Just.hasInstance(points) ? (
64 'No points to display'
65 ) : (
66 <>
67 <Blinky /> Loading...
68 </>
69 )}
70 </Grid.Item>
71 )}
73 {_points.map(point => (
74 <Resident
75 key={point}
76 point={point}
77 onAccept={onAccept}
78 onDecline={onDecline}
79 />
80 ))}
82 {hasPrev && (
83 <Grid.Item
84 className="pointer underline mt4"
85 fourth={1}
86 onClick={onPrev}>
87 {'<-'} Previous
88 </Grid.Item>
89 )}
90 {maxPage !== 0 && (
91 <Grid.Item className="mt6 ml7 t-center gray3" cols={[4, 10]}>
92 <span className="black">Page {page + 1}</span> of {maxPage + 1}
93 </Grid.Item>
94 )}
95 {hasNext && (
96 <Grid.Item
97 className="pointer underline t-right mt4"
98 fourth={4}
99 onClick={onNext}>
100 Next {'->'}
101 </Grid.Item>
102 )}
103 </Grid>
104 );
107function Resident({ point, onAccept, onDecline }) {
108 const patp = ob.patp(point);
109 const sigilSize = 50;
111 const isRequest = onAccept && onDecline;
113 return (
114 <>
115 <Grid.Item className="flex-row justify-center align-center" cols={[1, 3]}>
116 <div
117 style={{
118 display: 'inline-block',
119 height: `${sigilSize}px`,
120 width: `${sigilSize}px`,
121 }}>
122 <Sigil patp={patp} size={25} colors={['#FFFFFF', '#000000']} />
123 </div>
124 </Grid.Item>
125 <Grid.Item className="flex-row align-center" cols={[3, 7]}>
126 {patp}{' '}
127 </Grid.Item>
128 {isRequest && (
129 <>
130 <Grid.Item
131 className="flex-row-r align-center underline green3 pointer"
132 cols={[7, 10]}
133 onClick={() => onAccept(point)}>
134 Accept
135 </Grid.Item>
136 <Grid.Item
137 className="flex-row-r align-center underline red4 pointer"
138 cols={[10, 13]}
139 onClick={() => onDecline(point)}>
140 Decline
141 </Grid.Item>
142 </>
143 )}
144 <Grid.Divider />
145 </>
146 );
149export default function Residents() {
150 const { pop, push, names } = useLocalRouter();
151 const name = useCurrentPointName();
153 const { pointCursor } = usePointCursor();
154 const point = need.point(pointCursor);
155 const { getResidents, syncResidents } = usePointCache();
157 useEffect(() => {
158 syncResidents(point);
159 }, [syncResidents, point]);
161 const { residents, requests } = getResidents(point);
163 const [currentTab, _setCurrentTab] = useState(NAMES.ALL);
164 const [page, setPage] = useState(0);
166 const setCurrentTab = useCallback(
167 tab => {
168 setPage(0);
169 _setCurrentTab(tab);
170 },
171 [setPage, _setCurrentTab]
172 );
174 const isRequests = useMemo(() => currentTab === NAMES.REQUESTS, [currentTab]);
176 const _onAccept = useCallback(
177 adoptee =>
178 push(names.ADOPT, {
179 adoptee,
180 denied: false,
181 }),
182 [push, names]
183 );
184 const onAccept = isRequests && _onAccept;
186 const _onDecline = useCallback(
187 adoptee =>
188 push(names.ADOPT, {
189 adoptee,
190 denied: true,
191 }),
192 [push, names]
193 );
194 const onDecline = isRequests && _onDecline;
196 const points = isRequests ? requests : residents;
198 return (
199 <View pop={pop} inset>
200 <NavHeader>
201 <Crumbs routes={[{ text: name, action: pop }, { text: 'Residents' }]} />
202 </NavHeader>
203 <Grid>
204 <Grid.Item full as={H4} className="mt4">
205 Residents
206 </Grid.Item>
207 <Grid.Item
208 full
209 className="mt1"
210 as={Tabs}
211 // Tabs
212 views={VIEWS}
213 options={OPTIONS}
214 currentTab={currentTab}
215 onTabChange={setCurrentTab}
216 // Props for tab
217 points={points}
218 onAccept={onAccept}
219 onDecline={onDecline}
220 page={page}
221 setPage={setPage}
222 />
223 </Grid>
224 </View>
225 );