interface: reworked thread view so that one can navigate to it from any child to see the conversation around that particular child

This commit is contained in:
Logan Allen 2021-05-18 16:55:43 -05:00
parent 43a7c12ef7
commit 6b102450a5
9 changed files with 77 additions and 60 deletions

View File

@ -361,15 +361,18 @@ export default class GraphApi extends BaseApi<StoreState> {
}); });
} }
async getFirstborn(ship: string, resource: string, topAncestor: string) { async getFirstborn(ship: string, resource: string, index = '') {
const top = topAncestor ? decToUd(topAncestor) : null; const idx = index.split('/').map(decToUd).join('/');
const data = await this.scry<any>('graph-store', const data = await this.scry<any>('graph-store',
`/firstborn/${ship}/${resource}/${top}` `/firstborn/${ship}/${resource}/${idx}`
); );
const node = data['graph-update']; const node = data['graph-update'];
this.store.handleEvent({ this.store.handleEvent({
data: { data: {
'graph-update-thread': node, 'graph-update-thread': {
index,
...node
},
'graph-update': node 'graph-update': node
}, },
}); });

View File

@ -67,6 +67,7 @@ const addNodesFlat = (json: any, state: GraphState): GraphState => {
} }
const indices = Array.from(Object.keys(data.nodes)); const indices = Array.from(Object.keys(data.nodes));
console.log(indices);
indices.forEach((index) => { indices.forEach((index) => {
if (index.split('/').length === 0) { return; } if (index.split('/').length === 0) { return; }
@ -85,7 +86,9 @@ const addNodesFlat = (json: any, state: GraphState): GraphState => {
const addNodesThread = (json: any, state: GraphState): GraphState => { const addNodesThread = (json: any, state: GraphState): GraphState => {
const data = _.get(json, 'add-nodes', false); const data = _.get(json, 'add-nodes', false);
if (data) { const parentIndex = _.get(json, 'index', false);
console.log(json);
if (data && parentIndex) {
if (!('threadGraphs' in state)) { if (!('threadGraphs' in state)) {
return state; return state;
} }
@ -96,21 +99,25 @@ const addNodesThread = (json: any, state: GraphState): GraphState => {
} }
const indices = Array.from(Object.keys(data.nodes)); const indices = Array.from(Object.keys(data.nodes));
if (!(parentIndex in state.threadGraphs[resource])) {
state.threadGraphs[resource][parentIndex] = new BigIntArrayOrderedMap([], true);
}
console.log(state.threadGraphs);
indices.forEach((index) => { indices.forEach((index) => {
if (index.split('/').length === 0) { return; } if (index.split('/').length === 0) { return; }
const indexArr = index.split('/').slice(1).map((ind) => bigInt(ind)); const indexArr = index.split('/').slice(1).map((ind) => bigInt(ind));
if (indexArr.length === 0) { return state; } if (indexArr.length === 0) { return state; }
let parentKey = indexArr[0].toString();
if (!(parentKey in state.threadGraphs[resource])) {
state.threadGraphs[resource][parentKey] = new BigIntArrayOrderedMap([], true);
}
let node = data.nodes[index]; let node = data.nodes[index];
node.children = mapifyChildren({}); state.threadGraphs[resource][parentIndex] =
state.threadGraphs[resource][parentKey] = state.threadGraphs[resource][parentIndex].set(indexArr,
state.threadGraphs[resource][parentKey].set(indexArr, node); produce(node, (draft) => {
draft.children = mapifyChildren({});
})
);
}); });
} }

View File

@ -148,6 +148,28 @@ export function useGraph(ship: string, name: string) {
); );
} }
export function useFlatGraph(ship: string, name: string) {
return useGraphState(
useCallback(s => s.flatGraphs[`${deSig(ship)}/${name}`], [ship, name])
);
}
export function useThreadGraph(ship: string, name: string, index: string) {
return useGraphState(
useCallback(s => s.threadGraphs[`${deSig(ship)}/${name}/${index}`], [
ship,
name,
index
])
);
}
export function useGraphTimesentMap(ship: string, name: string) {
return useGraphState(
useCallback(s => s.graphTimesentMap[`${deSig(ship)}/${name}`], [ship, name])
);
}
export function useGraphForAssoc(association: Association) { export function useGraphForAssoc(association: Association) {
const { resource } = association; const { resource } = association;
const { ship, name } = resourceFromPath(resource); const { ship, name } = resourceFromPath(resource);

View File

@ -4,6 +4,7 @@ import React from 'react';
export function GroupFeedHeader(props) { export function GroupFeedHeader(props) {
const { baseUrl, history, graphResource, vip } = props; const { baseUrl, history, graphResource, vip } = props;
let graph = props.graph; let graph = props.graph;
const historyLocation = history.location.pathname; const historyLocation = history.location.pathname;
const graphId = `${graphResource.ship.slice(1)}/${graphResource.name}`; const graphId = `${graphResource.ship.slice(1)}/${graphResource.name}`;

View File

@ -4,9 +4,9 @@ import React, {
} from 'react'; } from 'react';
import { Route, Switch, useHistory } from 'react-router-dom'; import { Route, Switch, useHistory } from 'react-router-dom';
import { resourceFromPath } from '~/logic/lib/group'; import { resourceFromPath } from '~/logic/lib/group';
import useGraphState from '~/logic/state/graph'; import { useFlatGraph, useGraphTimesentMap } from '~/logic/state/graph';
import useGroupState from '~/logic/state/group'; import { useGroup } from '~/logic/state/group';
import useMetadataState from '~/logic/state/metadata'; import { useAssocForGraph } from '~/logic/state/metadata';
import { Loading } from '~/views/components/Loading'; import { Loading } from '~/views/components/Loading';
import { GroupFeedHeader } from './GroupFeedHeader'; import { GroupFeedHeader } from './GroupFeedHeader';
import PostThread from './Post/PostThread'; import PostThread from './Post/PostThread';
@ -23,36 +23,27 @@ function GroupFlatFeed(props) {
vip vip
} = props; } = props;
const groups = useGroupState(state => state.groups); const group = useGroup(groupPath);
const group = groups[groupPath];
const associations = useMetadataState(state => state.associations); const graphRid =
const flatGraphs = useGraphState(state => state.flatGraphs);
const graphResource =
graphPath ? resourceFromPath(graphPath) : resourceFromPath('/ship/~zod/null'); graphPath ? resourceFromPath(graphPath) : resourceFromPath('/ship/~zod/null');
const graphTimesentMap = useGraphState(state => state.graphTimesentMap);
const pendingSize = Object.keys( const flatGraph = useFlatGraph(graphRid.ship, graphRid.name);
graphTimesentMap[`${graphResource.ship.slice(1)}/${graphResource.name}`] || const association = useAssocForGraph(graphPath);
{}
).length; const graphTimesentMap = useGraphTimesentMap(graphRid.ship, graphRid.name);
const pendingSize = Object.keys(graphTimesentMap || {}).length;
const relativePath = path => baseUrl + path; const relativePath = path => baseUrl + path;
const association = associations.graph[graphPath];
const history = useHistory(); const history = useHistory();
const locationUrl = history.location.pathname; const locationUrl = history.location.pathname;
const graphId = `${graphResource.ship.slice(1)}/${graphResource.name}`;
const flatGraph = flatGraphs[graphId];
useEffect(() => { useEffect(() => {
// TODO: VirtualScroller should support lower starting values than 100 // TODO: VirtualScroller should support lower starting values than 100
if (graphResource.ship === '~zod' && graphResource.name === 'null') { if (graphRid.ship === '~zod' && graphRid.name === 'null') {
return; return;
} }
api.graph.getDeepOlderThan(graphResource.ship, graphResource.name, null, 100); api.graph.getDeepOlderThan(graphRid.ship, graphRid.name, null, 100);
api.hark.markCountAsRead(association, '/', 'post'); api.hark.markCountAsRead(association, '/', 'post');
}, [graphPath]); }, [graphPath]);
@ -72,9 +63,8 @@ function GroupFlatFeed(props) {
<GroupFeedHeader <GroupFeedHeader
baseUrl={baseUrl} baseUrl={baseUrl}
history={history} history={history}
graphs={flatGraphs}
vip={vip} vip={vip}
graphResource={graphResource} graphResource={graphRid}
/> />
<Switch> <Switch>
<Route <Route

View File

@ -55,7 +55,6 @@ class PostFlatFeed extends React.Component<PostFeedProps, PostFeedState> {
} = this.props; } = this.props;
const node = flatGraph.get(index); const node = flatGraph.get(index);
console.log(arrToString(index))
const parentNode = index.length > 1 ? const parentNode = index.length > 1 ?
flatGraph.get(index.slice(0, index.length - 1)) : null; flatGraph.get(index.slice(0, index.length - 1)) : null;
@ -63,6 +62,10 @@ class PostFlatFeed extends React.Component<PostFeedProps, PostFeedState> {
return null; return null;
} }
if (arrToString(index) !== node.post.index) {
console.log(node.post.index, arrToString(index), node);
}
const first = flatGraph.peekLargest()?.[0]; const first = flatGraph.peekLargest()?.[0];
const last = flatGraph.peekSmallest()?.[0]; const last = flatGraph.peekSmallest()?.[0];
const post = node?.post; const post = node?.post;

View File

@ -47,6 +47,7 @@ const PostInput = (props: PostInputProps): ReactElement | null => {
const [code, toggleCode] = useToggleState(false); const [code, toggleCode] = useToggleState(false);
const { canUpload, promptUpload, uploading } = useStorage(); const { canUpload, promptUpload, uploading } = useStorage();
const [postContent, setPostContent] = useState(''); const [postContent, setPostContent] = useState('');
const uploadImage = useCallback(async () => { const uploadImage = useCallback(async () => {
try { try {
setDisabled(true); setDisabled(true);

View File

@ -41,8 +41,7 @@ class PostItem extends React.Component<PostItemProps, PostItemState> {
this.state = { inReplyMode: false }; this.state = { inReplyMode: false };
this.toggleReplyMode = this.toggleReplyMode.bind(this); this.toggleReplyMode = this.toggleReplyMode.bind(this);
this.navigateToReplies = this.navigateToReplies.bind(this); this.navigateToChildren = this.navigateToChildren.bind(this);
this.navigateToThread = this.navigateToThread.bind(this);
this.submitCallback = this.submitCallback.bind(this); this.submitCallback = this.submitCallback.bind(this);
} }
@ -69,22 +68,8 @@ class PostItem extends React.Component<PostItemProps, PostItemState> {
this.setState({ inReplyMode: !this.state.inReplyMode }); this.setState({ inReplyMode: !this.state.inReplyMode });
} }
navigateToReplies() { navigateToChildren() {
const { history, baseUrl, index, isParent } = this.props; const { history, baseUrl, index, isParent, isThread } = this.props;
if (isParent) {
return;
}
let indexString = '';
index.forEach((i) => {
indexString = indexString + '/' + i.toString();
});
history.push(`${baseUrl}/feed/replies${indexString}`);
}
navigateToThread() {
const { history, baseUrl, index, isParent } = this.props;
if (isParent) { if (isParent) {
return; return;
} }
@ -94,7 +79,10 @@ class PostItem extends React.Component<PostItemProps, PostItemState> {
indexString = indexString + '/' + i.toString(); indexString = indexString + '/' + i.toString();
}); });
// TODO: ensure that the logic here works properly
history.push(`${baseUrl}/feed/thread${indexString}`); history.push(`${baseUrl}/feed/thread${indexString}`);
//history.push(`${baseUrl}/feed/replies${indexString}`);
} }
submitCallback() { submitCallback() {
@ -148,7 +136,7 @@ class PostItem extends React.Component<PostItemProps, PostItemState> {
width="100%" width="100%"
maxWidth="600px" maxWidth="600px"
backgroundColor={ hovering ? 'washedGray' : 'transparent' } backgroundColor={ hovering ? 'washedGray' : 'transparent' }
onClick={isThread ? this.navigateToReplies : this.navigateToThread} onClick={this.navigateToChildren}
cursor={isParent ? "default": "pointer"} cursor={isParent ? "default": "pointer"}
{...bind}> {...bind}>
{ (postExists) ? ( { (postExists) ? (

View File

@ -5,6 +5,7 @@ import React, {
} from 'react'; } from 'react';
import { resourceFromPath } from '~/logic/lib/group'; import { resourceFromPath } from '~/logic/lib/group';
import { Loading } from '~/views/components/Loading'; import { Loading } from '~/views/components/Loading';
import { arrToString } from '~/views/components/ArrayVirtualScroller';
import useGraphState from '~/logic/state/graph'; import useGraphState from '~/logic/state/graph';
import PostFlatFeed from './PostFlatFeed'; import PostFlatFeed from './PostFlatFeed';
import PostItem from './PostItem/PostItem'; import PostItem from './PostItem/PostItem';
@ -36,12 +37,11 @@ export default function PostThread(props) {
if (index.length < 1) { if (index.length < 1) {
return; return;
} }
console.log(index[0].toString());
api.graph.getFirstborn( api.graph.getFirstborn(
graphResource.ship, graphResource.ship,
graphResource.name, graphResource.name,
index[0].toString() arrToString(index)
); );
}, [graphPath, props.locationUrl]); }, [graphPath, props.locationUrl]);
@ -49,7 +49,7 @@ export default function PostThread(props) {
const graphId = `${graphResource.ship.slice(1)}/${graphResource.name}`; const graphId = `${graphResource.ship.slice(1)}/${graphResource.name}`;
const shouldRenderFeed = const shouldRenderFeed =
graphId in threadGraphs && index[0].toString() in threadGraphs[graphId]; graphId in threadGraphs && arrToString(index) in threadGraphs[graphId];
if (!shouldRenderFeed) { if (!shouldRenderFeed) {
return ( return (
@ -59,7 +59,9 @@ export default function PostThread(props) {
); );
} }
const threadGraph = threadGraphs[graphId][index[0].toString()]; // TODO: improve performance characteristics of the useGraphState required
// to fetch this
const threadGraph = threadGraphs[graphId][arrToString(index)];
const first = threadGraph.peekLargest()?.[0]; const first = threadGraph.peekLargest()?.[0];
if (!first) { if (!first) {