on ebac710 help sign in
bridge/src/views/Residents.js
233 lines 6.0 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';
20import Footer from 'components/Footer';
22const NAMES = {
23 REQUESTS: 'REQUESTS',
24 ALL: 'ALL',
25};
27const OPTIONS = [
28 { text: 'Requests', value: NAMES.REQUESTS },
29 { text: 'All', value: NAMES.ALL },
30];
32const VIEWS = {
33 [NAMES.REQUESTS]: Tab,
34 [NAMES.ALL]: Tab,
35};
37const PAGE_SIZE = 10;
39function Tab({ className, points, onAccept, onDecline, page, setPage }) {
40 const start = page * PAGE_SIZE;
41 const end = page * PAGE_SIZE + PAGE_SIZE;
42 const pointsCount = points.map(ps => ps.length).getOrElse(0);
43 const maxPage = Math.ceil(pointsCount / PAGE_SIZE) - 1;
44 const hasNext = page < maxPage;
45 const hasPrev = page > 0;
46 const _points = points.map(ps => ps.slice(start, end)).getOrElse([]);
48 const onNext = useCallback(() => {
49 setPage(p => p + 1);
50 }, [setPage]);
52 const onPrev = useCallback(() => {
53 setPage(p => p - 1);
54 }, [setPage]);
56 if (Nothing.hasInstance(points)) {
57 return <Grid className={className}></Grid>;
58 }
60 return (
61 <Grid className={cn('mt2', className)}>
62 {_points.length === 0 && (
63 <Grid.Item full as={HelpText} className="mt8 t-center">
64 {Just.hasInstance(points) ? (
65 'No points to display'
66 ) : (
67 <>
68 <Blinky /> Loading...
69 </>
70 )}
71 </Grid.Item>
72 )}
74 {_points.map(point => (
75 <Resident
76 key={point}
77 point={point}
78 onAccept={onAccept}
79 onDecline={onDecline}
80 />
81 ))}
82 <Grid.Item as={Footer}>
83 <Grid className="pb9">
84 {hasPrev && (
85 <Grid.Item
86 className="pointer underline mt4"
87 fourth={1}
88 onClick={onPrev}>
89 {'<-'}
90 </Grid.Item>
91 )}
92 {maxPage > 0 && (
93 <Grid.Item className="mt4 t-center gray3" cols={[4, 10]}>
94 <span className="black">Page {page + 1}</span> of {maxPage + 1}
95 </Grid.Item>
96 )}
97 {hasNext && (
98 <Grid.Item
99 className="pointer underline t-right mt4"
100 fourth={4}
101 onClick={onNext}>
102 {'->'}
103 </Grid.Item>
104 )}
105 </Grid>
106 </Grid.Item>
107 </Grid>
108 );
111function Resident({ point, onAccept, onDecline }) {
112 const patp = ob.patp(point);
113 const sigilSize = 50;
115 const isRequest = onAccept && onDecline;
117 return (
118 <>
119 <Grid.Item className="flex-row justify-center align-center" cols={[1, 3]}>
120 <div
121 style={{
122 display: 'inline-block',
123 height: `${sigilSize}px`,
124 width: `${sigilSize}px`,
125 }}>
126 <Sigil patp={patp} size={25} colors={['#FFFFFF', '#000000']} />
127 </div>
128 </Grid.Item>
129 <Grid.Item className="flex-row align-center mono" cols={[3, 7]}>
130 {patp}{' '}
131 </Grid.Item>
132 {isRequest && (
133 <>
134 <Grid.Item
135 className="flex-row-r align-center underline green3 pointer"
136 cols={[7, 10]}
137 onClick={() => onAccept(point)}>
138 Accept
139 </Grid.Item>
140 <Grid.Item
141 className="flex-row-r align-center underline red4 pointer"
142 cols={[10, 13]}
143 onClick={() => onDecline(point)}>
144 Decline
145 </Grid.Item>
146 </>
147 )}
148 <Grid.Divider />
149 </>
150 );
153export default function Residents() {
154 const { pop, push, names } = useLocalRouter();
155 const name = useCurrentPointName();
157 const { pointCursor } = usePointCursor();
158 const point = need.point(pointCursor);
159 const { getResidents, syncResidents } = usePointCache();
161 const goBack = useCallback(() => pop(), [pop]);
162 useEffect(() => {
163 syncResidents(point);
164 }, [syncResidents, point]);
166 const { residents, requests } = getResidents(point);
168 const [currentTab, _setCurrentTab] = useState(NAMES.ALL);
169 const [page, setPage] = useState(0);
171 const setCurrentTab = useCallback(
172 tab => {
173 setPage(0);
174 _setCurrentTab(tab);
175 },
176 [setPage, _setCurrentTab]
177 );
179 const isRequests = useMemo(() => currentTab === NAMES.REQUESTS, [currentTab]);
181 const _onAccept = useCallback(
182 adoptee =>
183 push(names.ADOPT, {
184 adoptee,
185 denied: false,
186 }),
187 [push, names]
188 );
189 const onAccept = isRequests && _onAccept;
191 const _onDecline = useCallback(
192 adoptee =>
193 push(names.ADOPT, {
194 adoptee,
195 denied: true,
196 }),
197 [push, names]
198 );
199 const onDecline = isRequests && _onDecline;
201 const points = isRequests ? requests : residents;
203 return (
204 <View pop={pop} inset>
205 <NavHeader>
206 <Crumbs
207 routes={[{ text: name, action: goBack }, { text: 'Residents' }]}
208 />
209 </NavHeader>
210 <Grid>
211 <Grid.Item full as={H4} className="mt4">
212 Residents
213 </Grid.Item>
214 <Grid.Item
215 full
216 className="mt1"
217 as={Tabs}
218 // Tabs
219 views={VIEWS}
220 options={OPTIONS}
221 currentTab={currentTab}
222 onTabChange={setCurrentTab}
223 // Props for tab
224 points={points}
225 onAccept={onAccept}
226 onDecline={onDecline}
227 page={page}
228 setPage={setPage}
229 />
230 </Grid>
231 </View>
232 );