mirror of
https://github.com/urbit/shrub.git
synced 2024-12-24 03:14:30 +03:00
graph: permalinks
This commit is contained in:
parent
f81e36317a
commit
5d43031f3d
@ -4,16 +4,38 @@ import { Center, Text } from "@tlon/indigo-react";
|
||||
import { deSig } from '~/logic/lib/util';
|
||||
import useGraphState from '~/logic/state/graph';
|
||||
import useMetadataState from '~/logic/state/metadata';
|
||||
import useGroupState from '~/logic/state/group';
|
||||
import { GraphIndexRoute } from './graphIndex';
|
||||
|
||||
const GraphApp = (props) => {
|
||||
const associations= useMetadataState(state => state.associations);
|
||||
const graphKeys = useGraphState(state => state.graphKeys);
|
||||
const groups = useGroupState(state => state.groups);
|
||||
const history = useHistory();
|
||||
|
||||
const { api } = props;
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route path="/~graph/graph/ship/:ship/:name"
|
||||
render={(props) => {
|
||||
const resource =
|
||||
`${deSig(props.match.params.ship)}/${props.match.params.name}`;
|
||||
const { ship, name } = props.match.params;
|
||||
const path = `/ship/~${deSig(ship)}/${name}`;
|
||||
const association = associations.graph[path];
|
||||
const url = `/~graph/graph/ship/${ship}/${name}`;
|
||||
const group = groups[association.group];
|
||||
if(!(group && association)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<GraphIndexRoute url={url} association={association} index="" group={group} />
|
||||
);
|
||||
|
||||
}}
|
||||
/>
|
||||
<Route exact path="/~graph/join/ship/:ship/:name/:module?"
|
||||
render={(props) => {
|
||||
const resource =
|
||||
@ -52,4 +74,4 @@ const GraphApp = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
export default GraphApp;
|
||||
export default GraphApp;
|
||||
|
122
pkg/interface/src/views/apps/graph/graphIndex.tsx
Normal file
122
pkg/interface/src/views/apps/graph/graphIndex.tsx
Normal file
@ -0,0 +1,122 @@
|
||||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import { Switch, Route, Redirect } from "react-router-dom";
|
||||
import { Association, Group } from "@urbit/api";
|
||||
|
||||
export function getGraphPermalink(
|
||||
assoc: Association,
|
||||
group: Group,
|
||||
index: string
|
||||
) {
|
||||
const mod = assoc.metadata.module;
|
||||
const groupPath = group.hidden
|
||||
? "/~/landscape/home"
|
||||
: `/~landscape${assoc.group}`;
|
||||
if (mod === "chat") {
|
||||
return getChatPermalink(
|
||||
group.hidden ? "/~landscape/messages" : `/~landscape${assoc.group}`,
|
||||
assoc,
|
||||
index
|
||||
);
|
||||
} else if (mod === "publish") {
|
||||
return getPublishPermalink(groupPath, assoc, index);
|
||||
} else if (mod === "link") {
|
||||
return getLinkPermalink(groupPath, assoc, index);
|
||||
}
|
||||
return "/~404";
|
||||
}
|
||||
|
||||
function getPublishPermalink(
|
||||
groupPath: string,
|
||||
assoc: Association,
|
||||
index: string
|
||||
) {
|
||||
const idx = index.split("/").slice(1);
|
||||
const base = `${groupPath}/resource/publish${assoc.resource}`;
|
||||
let isComment = false;
|
||||
const res = _.reduce(
|
||||
idx,
|
||||
(acc, val, i) => {
|
||||
if (i === 0) {
|
||||
return {...acc, pathname: `${acc.pathname}/note/${val}` };
|
||||
} else if (i === 1 && val === '2') {
|
||||
isComment = true;
|
||||
return acc;
|
||||
} else if (i === 2 && isComment) {
|
||||
return { ...acc, search: `?selected=${val}` };
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ pathname: base }
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
function getLinkPermalink(
|
||||
groupPath: string,
|
||||
assoc: Association,
|
||||
index: string
|
||||
) {
|
||||
const idx = index.split("/").slice(1);
|
||||
const base = `${groupPath}/resource/link${assoc.resource}`;
|
||||
const res = _.reduce(
|
||||
idx,
|
||||
(acc, val, i) => {
|
||||
console.log(acc);
|
||||
if (i === 0) {
|
||||
return {...acc, pathname: `${acc.pathname}/${val}` };
|
||||
} else if (i === 1) {
|
||||
return {...acc, search: `?selected=${val}` };
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ pathname: base }
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
function getChatPermalink(
|
||||
groupPath: string,
|
||||
assoc: Association,
|
||||
index: string
|
||||
) {
|
||||
const idx = index.split("/").slice(1);
|
||||
if (idx.length === 0) {
|
||||
return `${groupPath}/resource/chat${assoc.resource}`;
|
||||
}
|
||||
return `${groupPath}/resource/chat${assoc.resource}?msg=${idx[0]}`;
|
||||
}
|
||||
|
||||
export function GraphIndexRoute(props: {
|
||||
association: Association;
|
||||
group: Group;
|
||||
index: string;
|
||||
url: string;
|
||||
}) {
|
||||
const { url, index, association, group } = props;
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
path={`${url}/:id`}
|
||||
render={({ match }) => {
|
||||
const newUrl = `${url}/${match.params.id}`;
|
||||
const newIndex = `${index}/${match.params.id}`;
|
||||
return (
|
||||
<GraphIndexRoute
|
||||
group={group}
|
||||
url={newUrl}
|
||||
association={association}
|
||||
index={newIndex}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Route path="">
|
||||
<Redirect
|
||||
to={getGraphPermalink(association, group, index)}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
}
|
@ -82,7 +82,7 @@ export function LinkResource(props: LinkResourceProps) {
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path={relativePath('/:index(\\d+)/:commentId?')}
|
||||
path={relativePath('/:index')}
|
||||
render={(props) => {
|
||||
const index = bigInt(props.match.params.index);
|
||||
const editCommentId = props.match.params.commentId || null;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, {useEffect, useRef} from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
|
||||
@ -27,10 +27,12 @@ interface CommentItemProps {
|
||||
ship: string;
|
||||
api: GlobalApi;
|
||||
group: Group;
|
||||
highlighted: boolean;
|
||||
}
|
||||
|
||||
export function CommentItem(props: CommentItemProps): ReactElement {
|
||||
const { ship, name, api, comment, group } = props;
|
||||
const ref = useRef<HTMLElement | null>(null);
|
||||
const [, post] = getLatestCommentRevision(comment);
|
||||
const disabled = props.pending;
|
||||
|
||||
@ -40,13 +42,12 @@ export function CommentItem(props: CommentItemProps): ReactElement {
|
||||
|
||||
const commentIndexArray = (comment.post?.index || '/').split('/');
|
||||
const commentIndex = commentIndexArray[commentIndexArray.length - 1];
|
||||
const updateUrl = `${props.baseUrl}/${commentIndex}`;
|
||||
|
||||
const adminLinks: JSX.Element[] = [];
|
||||
const ourRole = roleForShip(group, window.ship);
|
||||
if (window.ship == post?.author && !disabled) {
|
||||
adminLinks.push(
|
||||
<Link to={updateUrl}>
|
||||
<Link to={{ pathname: props.baseUrl, search: `?edit=${commentIndex}`}}>
|
||||
<Text
|
||||
color="blue"
|
||||
ml={2}
|
||||
@ -65,9 +66,16 @@ export function CommentItem(props: CommentItemProps): ReactElement {
|
||||
)
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if(props.highlighted) {
|
||||
ref.current.scrollIntoView();
|
||||
}
|
||||
|
||||
}, [props.highlighted]);
|
||||
|
||||
return (
|
||||
<Box mb={4} opacity={post?.pending ? '60%' : '100%'}>
|
||||
<Row bg="white" my={3}>
|
||||
<Box ref={ref} border={props.highlighted ? 1 : 0} borderRadius={1} borderColor="blue" mb={4} opacity={post?.pending ? '60%' : '100%'}>
|
||||
<Row my={3}>
|
||||
<Author
|
||||
showImage
|
||||
ship={post?.author}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import bigInt from 'big-integer';
|
||||
import { Col } from '@tlon/indigo-react';
|
||||
import { CommentItem } from './CommentItem';
|
||||
@ -14,6 +14,7 @@ import { getUnreadCount } from '~/logic/lib/hark';
|
||||
import { PropFunc } from '~/types/util';
|
||||
import { isWriter } from '~/logic/lib/group';
|
||||
import useHarkState from '~/logic/state/hark';
|
||||
import {useQuery} from '~/logic/lib/useQuery';
|
||||
|
||||
interface CommentsProps {
|
||||
comments: GraphNode;
|
||||
@ -32,7 +33,6 @@ export function Comments(props: CommentsProps & PropFunc<typeof Col>) {
|
||||
comments,
|
||||
ship,
|
||||
name,
|
||||
editCommentId,
|
||||
api,
|
||||
history,
|
||||
baseUrl,
|
||||
@ -40,6 +40,18 @@ export function Comments(props: CommentsProps & PropFunc<typeof Col>) {
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const { query } = useQuery();
|
||||
const selectedComment = useMemo(() => {
|
||||
const id = query.get('selected')
|
||||
return id ? bigInt(id) : null;
|
||||
}, [query]);
|
||||
|
||||
const editCommentId = useMemo(() => {
|
||||
const id = query.get('edit')
|
||||
return id || '';
|
||||
}, [query]);
|
||||
|
||||
|
||||
const onSubmit = async (
|
||||
{ comment },
|
||||
actions: FormikHelpers<{ comment: string }>
|
||||
@ -116,8 +128,8 @@ export function Comments(props: CommentsProps & PropFunc<typeof Col>) {
|
||||
|
||||
return (
|
||||
<Col {...rest}>
|
||||
{( !props.editCommentId && canComment ? <CommentInput onSubmit={onSubmit} /> : null )}
|
||||
{( props.editCommentId ? (
|
||||
{( !editCommentId && canComment ? <CommentInput onSubmit={onSubmit} /> : null )}
|
||||
{( editCommentId ? (
|
||||
<CommentInput
|
||||
onSubmit={onEdit}
|
||||
label='Edit Comment'
|
||||
@ -126,9 +138,11 @@ export function Comments(props: CommentsProps & PropFunc<typeof Col>) {
|
||||
/>
|
||||
) : null )}
|
||||
{children.reverse()
|
||||
.map(([idx, comment], i) => {
|
||||
.map(([idx, comment], i) => {
|
||||
const highlighted = selectedComment?.eq(idx) ?? false;
|
||||
return (
|
||||
<CommentItem
|
||||
highlighted={highlighted}
|
||||
comment={comment}
|
||||
key={idx.toString()}
|
||||
api={api}
|
||||
|
Loading…
Reference in New Issue
Block a user