on master help sign in
chore: various refactors, cleanups, comments for review
da9b631Matt Condon (shrugs)
src/components/LoadableButton.js+1
@@ -1,6 +1,7 @@
1import React from 'react';
1import React from 'react';
2import { Button } from 'indigo-react';
2import { Button } from 'indigo-react';
3
3
4+// TODO: implement loading bar behind text
4export default function LoadableButton({ ...rest }) {
5export default function LoadableButton({ ...rest }) {
5 return <Button {...rest}></Button>;
6 return <Button {...rest}></Button>;
6}
7}
src/components/MiniBackButton.js+1−1
@@ -1,8 +1,8 @@
1import React from 'react';
1import React from 'react';
2import cn from 'classnames';
2import cn from 'classnames';
3+import { IconButton } from 'indigo-react';
3
4
4import { ReactComponent as Back } from 'assets/back.svg';
5import { ReactComponent as Back } from 'assets/back.svg';
5−import { IconButton } from 'indigo-react';
6
6
7export default ({ className, ...rest }) => (
7export default ({ className, ...rest }) => (
8 <IconButton {...rest}>
8 <IconButton {...rest}>
src/indigo-react/components/Button.js+1−1
@@ -45,7 +45,7 @@ export default function Button({
45 cursor: 'not-allowed',
45 cursor: 'not-allowed',
46 }),
46 }),
47 }}
47 }}
48− onClick={onClick ? () => !disabled && onClick() : undefined}
48+ onClick={!disabled && onClick ? onClick : undefined}
49 {...rest}>
49 {...rest}>
50 <Grid.Item as={Flex} justify="between" full>
50 <Grid.Item as={Flex} justify="between" full>
51 <span className={cn(textColor)}>{children}</span>
51 <span className={cn(textColor)}>{children}</span>
src/indigo-react/lib/useForm.js+21−19
@@ -3,6 +3,7 @@ import { keyBy, get, every, some } from 'lodash';
3
3
4import { compose } from 'lib/lib';
4import { compose } from 'lib/lib';
5import { kDefaultValidator } from 'lib/validators';
5import { kDefaultValidator } from 'lib/validators';
6+import useSetState from 'lib/useSetState';
6
7
7// interface InputConfig {
8// interface InputConfig {
8// name: string;
9// name: string;
@@ -21,7 +22,7 @@ const defaultsFor = (configs, mapper) =>
21 );
22 );
22
23
23/**
24/**
24− * useForm managest a set of inputs for rendering them in a loop
25+ * useForm manages a set of inputs for rendering them in a loop
25 */
26 */
26export default function useForm(configs = []) {
27export default function useForm(configs = []) {
27 const byName = useMemo(() => keyBy(configs, 'name'), [configs]);
28 const byName = useMemo(() => keyBy(configs, 'name'), [configs]);
@@ -42,7 +43,7 @@ export default function useForm(configs = []) {
42 // until there's a traditional state update.
43 // until there's a traditional state update.
43
44
44 // track focused states
45 // track focused states
45− const [focused, _setFocused] = useState(() =>
46+ const [focused, setFocused] = useSetState(() =>
46 defaultsFor(configs, config => config.autoFocus && !config.disabled)
47 defaultsFor(configs, config => config.autoFocus && !config.disabled)
47 );
48 );
48 const focuses = useMemo(
49 const focuses = useMemo(
@@ -50,23 +51,16 @@ export default function useForm(configs = []) {
50 [configs, focused]
51 [configs, focused]
51 );
52 );
52
53
53− const setFocused = useCallback(
54− (name, value) => _setFocused(values => ({ ...values, [name]: value })),
55− [_setFocused]
56− );
57−
58 // track whether or not input has been focused
54 // track whether or not input has been focused
59− const [hasBeenFocused, _setHasBeenFocused] = useState({});
60− const setHasBeenFocused = useCallback(
55+ const [hasBeenFocused, setHasBeenFocused] = useSetState({});
61− (name, value) =>
62− _setHasBeenFocused(values => ({ ...values, [name]: value })),
63− [_setHasBeenFocused]
64− );
65
56
57+ // build fn that transforms a value by input name
66 const transform = useCallback(
58 const transform = useCallback(
67 (name, value) => compose(...get(byName, [name, 'transformers'], []))(value),
59 (name, value) => compose(...get(byName, [name, 'transformers'], []))(value),
68 [byName]
60 [byName]
69 );
61 );
62+
63+ // build fn that validates a value by input name
70 const validate = useCallback(
64 const validate = useCallback(
71 (name, value) =>
65 (name, value) =>
72 compose(
66 compose(
@@ -76,28 +70,33 @@ export default function useForm(configs = []) {
76 [byName]
70 [byName]
77 );
71 );
78
72
73+ // on change, transform and set value
79 const onChange = useCallback(
74 const onChange = useCallback(
80 name => e => setValue(name, transform(name, e.target.value)), //
75 name => e => setValue(name, transform(name, e.target.value)), //
81 [setValue, transform]
76 [setValue, transform]
82 );
77 );
78+ // on focus, update focus
83 const onFocus = useCallback(
79 const onFocus = useCallback(
84− name => e => setFocused(name, true), //
80+ name => e => setFocused({ [name]: true }), //
85 [setFocused]
81 [setFocused]
86 );
82 );
83+
84+ // on blur, defocus and set has been focused
87 const onBlur = useCallback(
85 const onBlur = useCallback(
88 name => e => {
86 name => e => {
89− setFocused(name, false);
87+ setFocused({ [name]: false });
90− setHasBeenFocused(name, true);
88+ setHasBeenFocused({ [name]: false });
91 },
89 },
92 [setFocused, setHasBeenFocused]
90 [setFocused, setHasBeenFocused]
93 );
91 );
94
92
95− // memo-compute whether or not the current value is valid
93+ // memo-compute validations of curent values
96 const validations = useMemo(
94 const validations = useMemo(
97 () => configs.map(config => validate(config.name, values[config.name])), //
95 () => configs.map(config => validate(config.name, values[config.name])), //
98 [configs, validate, values]
96 [configs, validate, values]
99 );
97 );
100
98
99+ // memo-compute the set of (perhaps changed by validation) data
101 const datas = useMemo(() => validations.map(v => v.data), [validations]);
100 const datas = useMemo(() => validations.map(v => v.data), [validations]);
102
101
103 // the input has errored if it
102 // the input has errored if it
@@ -137,6 +136,7 @@ export default function useForm(configs = []) {
137 [configs, hasBeenFocused, focused, passes]
136 [configs, hasBeenFocused, focused, passes]
138 );
137 );
139
138
139+ // generate the list of input states
140 const inputs = useMemo(
140 const inputs = useMemo(
141 () =>
141 () =>
142 configs.map(
142 configs.map(
@@ -175,8 +175,10 @@ export default function useForm(configs = []) {
175 ]
175 ]
176 );
176 );
177
177
178+ // did all of the inputs pass validation?
178− const pass = every(passes);
179+ const pass = useMemo(() => every(passes), [passes]);
180+ // did any of the inputs error?
179− const error = some(errors);
181+ const error = useMemo(() => some(errors), [errors]);
180
182
181 return {
183 return {
182 inputs,
184 inputs,
src/lib/useSyncPoints.js+17−2
@@ -4,9 +4,24 @@ import { isEqual } from 'lodash';
4import { usePointCache } from 'store/pointCache';
4import { usePointCache } from 'store/pointCache';
5import usePreviousValue from 'lib/usePreviousValue';
5import usePreviousValue from 'lib/usePreviousValue';
6
6
7−// sync a known point to the cache (i.e display values)
7+// sync a known (i.e. mine) point to the cache (i.e display values)
8export function useSyncKnownPoints(points = []) {
8export function useSyncKnownPoints(points = []) {
9− //
9+ // TBD: right now we don't need anything more than the point itself
10+ // but soon we'll need to know information around "key type"
11+}
12+
13+// sync a foreign (not the user's) point to the cache
14+export function useSyncForeignPoints(points = []) {
15+ const { syncForeignPoint } = usePointCache();
16+ const prevPoints = usePreviousValue(points);
17+
18+ useEffect(() => {
19+ if (!isEqual(prevPoints, points)) {
20+ // if the actual set of points changes, sync the whole array
21+ // TODO: we should get clever about caching
22+ points.forEach(point => syncForeignPoint(point));
23+ }
24+ }, [syncForeignPoint, prevPoints, points]);
10}
25}
11
26
12// sync an owned point to the cache
27// sync an owned point to the cache
src/store/lib/useBirthdaysStore.js+15−13
@@ -30,19 +30,21 @@ export default function useBirthdayStore() {
30 return;
30 return;
31 }
31 }
32
32
33− // fetch birthday if not already known—will not change after being set
33+ // birthday will not change after being set, so bail if we already know
34− if (Maybe.Nothing.hasInstance(getBirthday(point))) {
34+ if (Maybe.Just.hasInstance(getBirthday(point))) {
35+ return;
36+ }
37+
35− const birthBlock = await azimuth.azimuth.getActivationBlock(
38+ const birthBlock = await azimuth.azimuth.getActivationBlock(
36− _contracts,
39+ _contracts,
37− point
40+ point
38− );
41+ );
39−
42+
40− if (birthBlock > 0) {
43+ if (birthBlock > 0) {
41− const block = await _web3.eth.getBlock(birthBlock);
44+ const block = await _web3.eth.getBlock(birthBlock);
42− addToBirthdayCache({
45+ addToBirthdayCache({
43− [point]: Maybe.Just(new Date(block.timestamp * 1000)),
46+ [point]: Maybe.Just(new Date(block.timestamp * 1000)),
44− });
47+ });
45− }
46 }
48 }
47 },
49 },
48 [contracts, web3, addToBirthdayCache, getBirthday]
50 [contracts, web3, addToBirthdayCache, getBirthday]
src/store/lib/useControlledPointsStore.js+13−20
@@ -34,26 +34,19 @@ export default function useControlledPointsStore() {
34
34
35 const address = _wallet.address;
35 const address = _wallet.address;
36
36
37+ const [
38+ ownedPoints,
39+ incomingPoints,
40+ managingPoints,
41+ votingPoints,
42+ spawningPoints,
43+ ] = await Promise.all([
37− const ownedPoints = await azimuth.azimuth.getOwnedPoints(
44+ azimuth.azimuth.getOwnedPoints(_contracts, address),
38− _contracts,
39− address
40− );
41− const incomingPoints = await azimuth.azimuth.getTransferringFor(
45+ azimuth.azimuth.getTransferringFor(_contracts, address),
42− _contracts,
43− address
44− );
45− const managingPoints = await azimuth.azimuth.getManagerFor(
46+ azimuth.azimuth.getManagerFor(_contracts, address),
46− _contracts,
47− address
48− );
49− const votingPoints = await azimuth.azimuth.getVotingFor(
47+ azimuth.azimuth.getVotingFor(_contracts, address),
50− _contracts,
51− address
52− );
53− const spawningPoints = await azimuth.azimuth.getSpawningFor(
48+ azimuth.azimuth.getSpawningFor(_contracts, address),
54− _contracts,
55− address
56− );
49+ ]);
57
50
58 _setControlledPointsCache({
51 _setControlledPointsCache({
59 ownedPoints: Maybe.Just(ownedPoints),
52 ownedPoints: Maybe.Just(ownedPoints),
src/store/lib/useInvitesStore.js+11−21
@@ -36,29 +36,19 @@ export default function useInvitesStore() {
36 return;
36 return;
37 }
37 }
38
38
39+ const [availableInvites, invitedPoints] = await Promise.all([
39− const availableInvites = await azimuth.delegatedSending.getTotalUsableInvites(
40+ azimuth.delegatedSending.getTotalUsableInvites(_contracts, point),
40− _contracts,
41+ azimuth.delegatedSending.getInvited(_contracts, point),
41− point
42− );
42+ ]);
43
43
44− const invitedPoints = await azimuth.delegatedSending.getInvited(
45− _contracts,
46− point
47− );
48− const invitedPointDetails = await Promise.all(
44+ const activity = await Promise.all(
49− invitedPoints.map(async invitedPoint => {
45+ invitedPoints.map(invitedPoint =>
50− const active = await azimuth.azimuth.isActive(
46+ azimuth.azimuth.isActive(_contracts, invitedPoint)
51− _contracts,
52− invitedPoint
53− );
54− return {
55− point: invitedPoint,
56− active,
57− };
58− })
47+ )
59 );
48 );
49+
60− const sentInvites = invitedPointDetails.length;
50+ const sentInvites = invitedPoints.length;
61− const acceptedInvites = invitedPointDetails.filter(i => i.active).length;
51+ const acceptedInvites = activity.filter(i => i.active).length;
62
52
63 addToInvitesCache({
53 addToInvitesCache({
64 [point]: {
54 [point]: {
src/store/lib/usePointStore.js+8−6
@@ -15,15 +15,17 @@ export default function usePointStore() {
15 } = useControlledPointsStore();
15 } = useControlledPointsStore();
16
16
17 // sync all of the on-chain info required to display a foreign point
17 // sync all of the on-chain info required to display a foreign point
18− const syncForeignPoint = useCallback(
18+ const syncForeignPoint = useCallback(async point => Promise.all([]), []);
19− async point => Promise.all([syncDetails(point), syncBirthday(point)]),
20− [syncDetails, syncBirthday]
21− );
22
19
23 // sync all of the on-chain info required for a point that the user owns
20 // sync all of the on-chain info required for a point that the user owns
24 const syncOwnedPoint = useCallback(
21 const syncOwnedPoint = useCallback(
22+ async point =>
23+ Promise.all([
24+ syncDetails(point),
25+ syncBirthday(point),
25− async point => Promise.all([syncForeignPoint(point), syncInvites(point)]),
26+ syncInvites(point),
26− [syncForeignPoint, syncInvites]
27+ ]),
28+ [syncDetails, syncBirthday, syncInvites]
27 );
29 );
28
30
29 return {
31 return {
src/views/Login.js+4−5
@@ -57,8 +57,8 @@ export default function Login() {
57 const _contracts = need.contracts(contracts);
57 const _contracts = need.contracts(contracts);
58
58
59 // if no point cursor set by login logic, try to deduce it
59 // if no point cursor set by login logic, try to deduce it
60− let deduced = Nothing();
60+ let deduced = pointCursor;
61− if (Nothing.hasInstance(pointCursor)) {
61+ if (Nothing.hasInstance(deduced)) {
62 const owned = await azimuth.azimuth.getOwnedPoints(
62 const owned = await azimuth.azimuth.getOwnedPoints(
63 _contracts,
63 _contracts,
64 _wallet.address
64 _wallet.address
@@ -80,9 +80,8 @@ export default function Login() {
80 // navigate to that specific point, otherwise navigate to list of points
80 // navigate to that specific point, otherwise navigate to list of points
81 if (Just.hasInstance(deduced)) {
81 if (Just.hasInstance(deduced)) {
82 setPointCursor(deduced);
82 setPointCursor(deduced);
83− history.popAndPush(ROUTE_NAMES.POINT);
83+ history.popAndPush(ROUTE_NAMES.POINTS);
84− } else if (Just.hasInstance(pointCursor)) {
85− history.popAndPush(ROUTE_NAMES.POINT);
84+ history.push(ROUTE_NAMES.POINT);
86 } else {
85 } else {
87 history.popAndPush(ROUTE_NAMES.POINTS);
86 history.popAndPush(ROUTE_NAMES.POINTS);
88 }
87 }
src/views/ViewPoint.js+1−1
@@ -1,4 +1,4 @@
1−import React, { useCallback, useState } from 'react';
1+import React, { useCallback } from 'react';
2import Maybe from 'folktale/maybe';
2import Maybe from 'folktale/maybe';
3import * as ob from 'urbit-ob';
3import * as ob from 'urbit-ob';
4import { Input } from 'indigo-react';
4import { Input } from 'indigo-react';