interface: pending graph updates

This commit is contained in:
Liam Fitzgerald 2021-06-14 11:45:18 +10:00
parent 2d40d0655b
commit 276e2c2fa4
No known key found for this signature in database
GPG Key ID: D390E12C61D1CFFB
12 changed files with 83 additions and 47 deletions

View File

@ -24,7 +24,7 @@ export const bootstrapApi = async () => {
})();
};
airlock.onRetry = (e) => {
airlock.onRetry = () => {
useLocalState.setState({ subscription: 'reconnecting' });
};

View File

@ -152,6 +152,9 @@ function unreads(json: any, state: HarkState): HarkState {
data.forEach(({ index, stats }) => {
const { unreads, notifications, last } = stats;
updateNotificationStats(state, index, 'last', () => last);
if(index.graph.graph === '/ship/~hastuc-dibtux/test-book-7531') {
console.log(index, stats);
}
_.each(notifications, ({ time, index }) => {
if(!time) {
addNotificationToUnread(state, index);
@ -197,6 +200,9 @@ function updateUnreadCount(state: HarkState, index: NotifIndex, count: (c: numbe
}
const property = [index.graph.graph, index.graph.index, 'unreads'];
const curr = _.get(state.unreads.graph, property, 0);
if(typeof curr !== 'number') {
return state;
}
const newCount = count(curr);
_.set(state.unreads.graph, property, newCount);
return state;

View File

@ -5,7 +5,7 @@ import { Association, deSig, GraphNode, Graphs, FlatGraphs, resourceFromPath, Th
import { useCallback } from 'react';
import { createState, createSubscription, reduceStateN } from './base';
import airlock from '~/logic/api';
import { getDeepOlderThan, getFirstborn, getNewest, getNode, getOlderSiblings, getYoungerSiblings } from '@urbit/api/graph';
import { addDmMessage, addPost, Content, getDeepOlderThan, getFirstborn, getNewest, getNode, getOlderSiblings, getYoungerSiblings, markPending, Post, addNode, GraphNodePoke } from '@urbit/api/graph';
import { GraphReducer, reduceDm } from '../reducers/graph-update';
import _ from 'lodash';
@ -24,17 +24,16 @@ export interface GraphState {
screening: boolean;
graphTimesentMap: Record<number, string>;
getDeepOlderThan: (ship: string, name: string, count: number, start?: string) => Promise<void>;
// getKeys: () => Promise<void>;
// getTags: () => Promise<void>;
// getTagQueries: () => Promise<void>;
// getGraph: (ship: string, resource: string) => Promise<void>;
getNewest: (ship: string, resource: string, count: number, index?: string) => Promise<void>;
getOlderSiblings: (ship: string, resource: string, count: number, index?: string) => Promise<void>;
getYoungerSiblings: (ship: string, resource: string, count: number, index?: string) => Promise<void>;
// getGraphSubset: (ship: string, resource: string, start: string, end: string) => Promise<void>;
getNode: (ship: string, resource: string, index: string) => Promise<void>;
getFirstborn: (ship: string, resource: string, index: string) => Promise<void>;
getGraph: (ship: string, name: string) => Promise<void>;
addDmMessage: (ship: string, contents: Content[]) => Promise<void>;
addPost: (ship: string, name: string, post: Post) => Promise<void>;
addNode: (ship: string, name: string, post: GraphNodePoke) => Promise<void>;
}
// @ts-ignore investigate zustand types
const useGraphState = createState<GraphState>('Graph', (set, get) => ({
@ -47,6 +46,37 @@ const useGraphState = createState<GraphState>('Graph', (set, get) => ({
graphTimesentMap: {},
pendingDms: new Set(),
screening: false,
addDmMessage: async (ship: string, contents: Content[]) => {
const promise = airlock.poke(addDmMessage(window.ship, ship, contents));
const { json } = addDmMessage(window.ship, ship, contents);
markPending(json['add-nodes'].nodes);
json['add-nodes'].resource.ship = json['add-nodes'].resource.ship.slice(1);
GraphReducer({
'graph-update': json
});
await promise;
},
addPost: async (ship, name, post) => {
const promise = airlock.thread(addPost(ship, name, post));
const { body } = addPost(ship, name, post);
markPending(body['add-nodes'].nodes);
body['add-nodes'].resource.ship = body['add-nodes'].resource.ship.slice(1);
GraphReducer({
'graph-update': body,
'graph-update-flat': body
});
await promise;
},
addNode: async (ship, name, node) => {
const promise = airlock.thread(addNode(ship, name, node));
const { body } = addNode(ship, name, node);
markPending(body['add-nodes'].nodes);
body['add-nodes'].resource.ship = body['add-nodes'].resource.ship.slice(1);
GraphReducer({
'graph-update': body
});
await promise;
},
getDeepOlderThan: async (ship, name, count, start) => {
const data = await airlock.scry(getDeepOlderThan(ship, name, count, start));

View File

@ -31,7 +31,7 @@ const useGroupState = createState<GroupState>(
reduceStateN(get(), e.groupUpdate, reduce);
}
}),
(set, get) => createSubscription('group-view', '/groups', (e) => {
(set, get) => createSubscription('group-view', '/all', (e) => {
const data = _.get(e, 'group-view-update', false);
if (data) {
reduceStateN(get(), data, reduceView);

View File

@ -1,4 +1,4 @@
import { addPost, Content, createPost, fetchIsAllowed, markCountAsRead, Post, removePosts } from '@urbit/api';
import { Content, createPost, fetchIsAllowed, markCountAsRead, Post, removePosts } from '@urbit/api';
import { Association } from '@urbit/api/metadata';
import { BigInteger } from 'big-integer';
import React, {
@ -43,9 +43,10 @@ const ChatResource = (props: ChatResourceProps): ReactElement => {
const [
getNewest,
getOlderSiblings,
getYoungerSiblings
getYoungerSiblings,
addPost
] = useGraphState(
s => [s.getNewest, s.getOlderSiblings, s.getYoungerSiblings],
s => [s.getNewest, s.getOlderSiblings, s.getYoungerSiblings, s.addPost],
shallow
);
@ -129,8 +130,8 @@ const ChatResource = (props: ChatResourceProps): ReactElement => {
const onSubmit = useCallback((contents: Content[]) => {
const { ship, name } = resourceFromPath(resource);
airlock.thread(addPost(ship, name, createPost(window.ship, contents)));
}, [resource]);
addPost(ship, name, createPost(window.ship, contents));
}, [resource, addPost]);
const onDelete = useCallback((msg: Post) => {
const { ship, name } = resourceFromPath(resource);

View File

@ -1,4 +1,4 @@
import { addDmMessage, cite, Content, markCountAsRead, Post } from '@urbit/api';
import { cite, Content, markCountAsRead, Post } from '@urbit/api';
import React, { useCallback, useEffect } from 'react';
import _ from 'lodash';
import bigInt from 'big-integer';
@ -10,7 +10,6 @@ import useGraphState, { useDM } from '~/logic/state/graph';
import { useHarkDm } from '~/logic/state/hark';
import useSettingsState, { selectCalmState } from '~/logic/state/settings';
import { ChatPane } from './components/ChatPane';
import { patpToUd } from '~/logic/lib/util';
import airlock from '~/logic/api';
import shallow from 'zustand/shallow';
@ -60,11 +59,12 @@ export function DmResource(props: DmResourceProps) {
const nickname = showNickname ? contact!.nickname : cite(ship) ?? ship;
const [
getNewest,
getYoungerSiblings,
getOlderSiblings,
getYoungerSiblings
getNewest,
addDmMessage
] = useGraphState(
s => [s.getYoungerSiblings, s.getOlderSiblings, s.getNewest],
s => [s.getYoungerSiblings, s.getOlderSiblings, s.getNewest, s.addDmMessage],
shallow
);
@ -73,7 +73,7 @@ export function DmResource(props: DmResourceProps) {
`~${window.ship}`,
'dm-inbox',
100,
`/${patpToUd(ship)}`
`/${patp2dec(ship)}`
);
}, [ship]);
@ -90,7 +90,7 @@ export function DmResource(props: DmResourceProps) {
`~${window.ship}`,
'dm-inbox',
pageSize,
`/${patpToUd(ship)}/${index.toString()}`
`/${patp2dec(ship)}/${index.toString()}`
);
return expectedSize !== getCurrDmSize(ship);
} else {
@ -102,7 +102,7 @@ export function DmResource(props: DmResourceProps) {
`~${window.ship}`,
'dm-inbox',
pageSize,
`/${patpToUd(ship)}/${index.toString()}`
`/${patp2dec(ship)}/${index.toString()}`
);
return expectedSize !== getCurrDmSize(ship);
}
@ -116,10 +116,9 @@ export function DmResource(props: DmResourceProps) {
const onSubmit = useCallback(
(contents: Content[]) => {
// XX optimistic
airlock.poke(addDmMessage(`~${window.ship}`, ship, contents));
addDmMessage(ship, contents);
},
[ship]
[ship, addDmMessage]
);
return (

View File

@ -98,10 +98,10 @@ export const LinkItem = React.forwardRef((props: LinkItemProps, ref: RefObject<H
};
const appPath = `/ship/~${resource}`;
const unreads = useHarkState(state => state.unreads);
const commColor = (unreads.graph?.[appPath]?.[`/${index}`]?.unreads ?? 0) > 0 ? 'blue' : 'gray';
const unreads = useHarkState(state => state.unreads?.[appPath]);
const commColor = (unreads?.[`/${index}`]?.unreads ?? 0) > 0 ? 'blue' : 'gray';
// @ts-ignore hark will have to choose between sets and numbers
const isUnread = unreads.graph?.[appPath]?.['/']?.unreads?.has(node.post.index);
const isUnread = (unreads?.['/']?.unreads ?? new Set()).has(node.post.index);
return (
<Box

View File

@ -1,5 +1,4 @@
import { BaseInput, Box, Button, LoadingSpinner, Text } from '@tlon/indigo-react';
import { addPost } from '@urbit/api/graph';
import { hasProvider } from 'oembed-parser';
import React, { useCallback, useEffect, useState } from 'react';
import { createPost } from '~/logic/api/graph';
@ -7,7 +6,7 @@ import { parsePermalink, permalinkToReference } from '~/logic/lib/permalinks';
import { useFileDrag } from '~/logic/lib/useDrag';
import useStorage from '~/logic/lib/useStorage';
import SubmitDragger from '~/views/components/SubmitDragger';
import airlock from '~/logic/api';
import useGraphState from '~/logic/state/graph';
interface LinkSubmitProps {
name: string;
@ -18,6 +17,7 @@ interface LinkSubmitProps {
const LinkSubmit = (props: LinkSubmitProps) => {
const { canUpload, uploadDefault, uploading, promptUpload } =
useStorage();
const addPost = useGraphState(s => s.addPost);
const [submitFocused, setSubmitFocused] = useState(false);
const [urlFocused, setUrlFocused] = useState(false);
@ -37,16 +37,15 @@ const LinkSubmit = (props: LinkSubmitProps) => {
const parentIndex = props.parentIndex || '';
const post = createPost(contents, parentIndex);
airlock.thread(addPost(
addPost(
`~${props.ship}`,
props.name,
post
)).then(() => {
setDisabled(false);
setLinkValue('');
setLinkTitle('');
setLinkValid(false);
});
);
setDisabled(false);
setLinkValue('');
setLinkTitle('');
setLinkValid(false);
};
const validateLink = (link) => {

View File

@ -13,7 +13,6 @@ import { StatelessAsyncAction } from '~/views/components/StatelessAsyncAction';
import { SwipeMenu } from '~/views/components/SwipeMenu';
import { GraphNotification } from './graph';
import { GroupNotification } from './group';
import airlock from '~/logic/api';
import useHarkState from '~/logic/state/hark';
import shallow from 'zustand/shallow';
@ -47,7 +46,7 @@ export function NotificationWrapper(props: {
if (!notification || read) {
return;
}
return airlock.poke(readNote(notification.index));
return readNote(notification.index);
};
const { hovering, bind } = useHovering();

View File

@ -66,15 +66,15 @@ export function NotePreview(props: NotePreviewProps) {
const noteId = post.index.split('/')[1];
const url = `${props.baseUrl}/note/${noteId}`;
const [rev, title, body, content] = getLatestRevision(node);
const [, title, body] = getLatestRevision(node);
const appPath = `/ship/${props.host}/${props.book}`;
const unreads = useHarkState(state => state.unreads);
const unreads = useHarkState(state => state.unreads.graph?.[appPath]);
// @ts-ignore hark will have to choose between sets and numbers
const isUnread = unreads.graph?.[appPath]?.['/']?.unreads?.has(`/${noteId}/1/1`);
const isUnread = (unreads?.['/'].unreads ?? new Set()).has(`/${noteId}/1/1`);
const snippet = getSnippet(body);
const commColor = (unreads.graph?.[appPath]?.[`/${noteId}`]?.unreads ?? 0) > 0 ? 'blue' : 'gray';
const commColor = (unreads?.[`/${noteId}`]?.unreads ?? 0) > 0 ? 'blue' : 'gray';
const cursorStyle = post.pending ? 'default' : 'pointer';

View File

@ -1,5 +1,5 @@
import { Col } from '@tlon/indigo-react';
import { createPost, createBlankNodeWithChildPost, addNode, Association, GraphNode, Group, markCountAsRead, addPost } from '@urbit/api';
import { createPost, createBlankNodeWithChildPost, Association, GraphNode, Group, markCountAsRead, addPost } from '@urbit/api';
import bigInt from 'big-integer';
import { FormikHelpers } from 'formik';
import React, { useEffect, useMemo } from 'react';
@ -14,6 +14,7 @@ import { PropFunc } from '~/types/util';
import CommentInput from './CommentInput';
import { CommentItem } from './CommentItem';
import airlock from '~/logic/api';
import useGraphState from '~/logic/state/graph';
interface CommentsProps {
comments: GraphNode;
@ -35,6 +36,7 @@ export function Comments(props: CommentsProps & PropFunc<typeof Col>) {
group,
...rest
} = props;
const addNode = useGraphState(s => s.addNode);
const { query } = useQuery();
const selectedComment = useMemo(() => {
@ -54,12 +56,12 @@ export function Comments(props: CommentsProps & PropFunc<typeof Col>) {
try {
const content = tokenizeMessage(comment);
const node = createBlankNodeWithChildPost(
`~${window.ship}`,
window.ship,
comments?.post?.index,
'1',
content
);
await airlock.thread(addNode(ship, name, node));
addNode(ship, name, node);
actions.resetForm();
actions.setStatus({ success: null });
} catch (e) {

View File

@ -345,7 +345,7 @@ export const removePosts = (
* @param ship recipient
* @param contents contents of message
*/
export const addDmMessage = (our: Patp, ship: Patp, contents: Content[]): Poke<any> => {
export const addDmMessage = (our: PatpNoSig, ship: Patp, contents: Content[]): Poke<any> => {
const post = createPost(our, contents, `/${patp2dec(ship)}`);
const node: GraphNode = {
post,
@ -356,7 +356,7 @@ export const addDmMessage = (our: Patp, ship: Patp, contents: Content[]): Poke<a
mark: `graph-update-${GRAPH_UPDATE_VERSION}`,
json: {
'add-nodes': {
resource: { ship: our, name: 'dm-inbox' },
resource: { ship: `~${our}`, name: 'dm-inbox' },
nodes: {
[post.index]: node
}