mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-14 08:34:25 +03:00
Merge pull request #3869 from urbit/lf/hark-redux-mentions
hark, graph-store: mentions support.
This commit is contained in:
commit
5618567e43
@ -211,11 +211,9 @@
|
||||
^- ?
|
||||
?. mentions %.n
|
||||
?~ contents %.n
|
||||
?. ?=(%text -.i.contents)
|
||||
?. ?=(%mention -.i.contents)
|
||||
$(contents t.contents)
|
||||
=/ res
|
||||
(find (scow %p our.bowl) (trip text.i.contents))
|
||||
?^ res
|
||||
?: =(our.bowl ship.i.contents)
|
||||
%.y
|
||||
$(contents t.contents)
|
||||
::
|
||||
|
@ -74,6 +74,7 @@
|
||||
|= c=^content
|
||||
^- json
|
||||
?- -.c
|
||||
%mention (frond %mention (ship ship.c))
|
||||
%text (frond %text s+text.c)
|
||||
%url (frond %url s+url.c)
|
||||
%reference (frond %reference (uid uid.c))
|
||||
@ -324,7 +325,8 @@
|
||||
::
|
||||
++ content
|
||||
%- of
|
||||
:~ [%text so]
|
||||
:~ [%mention (su ;~(pfix sig fed:ag))]
|
||||
[%text so]
|
||||
[%url so]
|
||||
[%reference uid]
|
||||
[%code eval]
|
||||
|
@ -24,7 +24,7 @@
|
||||
:: comment on link post; comment text
|
||||
::
|
||||
[@ @ ~]
|
||||
?> ?=([[%text @] ~] contents.p.ip)
|
||||
?> ?=(^ contents.p.ip)
|
||||
ip
|
||||
==
|
||||
--
|
||||
|
@ -28,6 +28,7 @@
|
||||
::
|
||||
+$ content
|
||||
$% [%text text=cord]
|
||||
[%mention =ship]
|
||||
[%url url=cord]
|
||||
[%code expression=cord output=(list tank)]
|
||||
[%reference =uid]
|
||||
|
@ -3,10 +3,10 @@ import { StoreState } from '../store/type';
|
||||
import { Patp, Path, PatpNoSig } from '~/types/noun';
|
||||
import _ from 'lodash';
|
||||
import {makeResource, resourceFromPath} from '../lib/group';
|
||||
import {GroupPolicy, Enc, Post, NodeMap} from '~/types';
|
||||
import {GroupPolicy, Enc, Post, NodeMap, Content} from '~/types';
|
||||
import { numToUd, unixToDa } from '~/logic/lib/util';
|
||||
|
||||
export const createPost = (contents: Object[], parentIndex: string = '') => {
|
||||
export const createPost = (contents: Content[], parentIndex: string = '') => {
|
||||
return {
|
||||
author: `~${window.ship}`,
|
||||
index: parentIndex + '/' + unixToDa(Date.now()).toString(),
|
||||
|
24
pkg/interface/src/logic/lib/graph.ts
Normal file
24
pkg/interface/src/logic/lib/graph.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Content } from "~/types";
|
||||
import urbitOb from "urbit-ob";
|
||||
|
||||
export function scanForMentions(text: string) {
|
||||
const regex = /~([a-z]|-)+/g;
|
||||
let result: Content[] = [];
|
||||
let match: RegExpExecArray | null;
|
||||
let lastPos = 0;
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
const newPos = match.index + match[0].length;
|
||||
if (urbitOb.isValidPatp(match[0])) {
|
||||
if (match.index !== lastPos) {
|
||||
result.push({ text: text.slice(lastPos, match.index) });
|
||||
}
|
||||
result.push({ mention: match[0] });
|
||||
}
|
||||
lastPos = newPos;
|
||||
}
|
||||
const remainder = text.slice(lastPos, text.length);
|
||||
if (remainder) {
|
||||
result.push({ text: remainder });
|
||||
}
|
||||
return result;
|
||||
}
|
@ -1,12 +1,28 @@
|
||||
import { Patp } from "./noun";
|
||||
import { BigIntOrderedMap } from "~/logic/lib/BigIntOrderedMap";
|
||||
|
||||
|
||||
export interface TextContent { text: string; };
|
||||
export interface UrlContent { url: string; }
|
||||
export interface CodeContent { expresssion: string; output: string; };
|
||||
export interface ReferenceContent { uid: string; }
|
||||
export type Content = TextContent | UrlContent | CodeContent | ReferenceContent;
|
||||
export interface TextContent {
|
||||
text: string;
|
||||
}
|
||||
export interface UrlContent {
|
||||
url: string;
|
||||
}
|
||||
export interface CodeContent {
|
||||
expresssion: string;
|
||||
output: string;
|
||||
}
|
||||
export interface ReferenceContent {
|
||||
uid: string;
|
||||
}
|
||||
export interface MentionContent {
|
||||
mention: string;
|
||||
}
|
||||
export type Content =
|
||||
| TextContent
|
||||
| UrlContent
|
||||
| CodeContent
|
||||
| ReferenceContent
|
||||
| MentionContent;
|
||||
|
||||
export interface Post {
|
||||
author: Patp;
|
||||
@ -15,10 +31,9 @@ export interface Post {
|
||||
index: string;
|
||||
pending?: boolean;
|
||||
signatures: string[];
|
||||
'time-sent': number;
|
||||
"time-sent": number;
|
||||
}
|
||||
|
||||
|
||||
export interface GraphNode {
|
||||
children: Graph;
|
||||
post: Post;
|
||||
@ -27,5 +42,3 @@ export interface GraphNode {
|
||||
export type Graph = BigIntOrderedMap<GraphNode>;
|
||||
|
||||
export type Graphs = { [rid: string]: Graph };
|
||||
|
||||
|
||||
|
@ -4,9 +4,10 @@ import { cite } from '~/logic/lib/util';
|
||||
import moment from 'moment';
|
||||
import { Box, Text, Row } from '@tlon/indigo-react';
|
||||
import RichText from '~/views/components/RichText';
|
||||
import { MentionText } from "~/views/components/MentionText";
|
||||
|
||||
export const CommentItem = (props) => {
|
||||
const content = props.post.contents[0].text;
|
||||
const content = props.post.contents;
|
||||
const timeSent =
|
||||
moment.unix(props.post['time-sent'] / 1000).format('hh:mm a');
|
||||
|
||||
@ -33,10 +34,12 @@ export const CommentItem = (props) => {
|
||||
</Row>
|
||||
</Row>
|
||||
<Row>
|
||||
<Text display="block" py={3} fontSize={1}>
|
||||
<RichText remoteContentPolicy={props.remoteContentPolicy}>
|
||||
{content}
|
||||
</RichText>
|
||||
<Text py={3} fontSize={1}>
|
||||
<MentionText
|
||||
remoteContentPolicy={props.remoteContentPolicy}
|
||||
contacts={props.contacts}
|
||||
content={content}
|
||||
/>
|
||||
</Text>
|
||||
</Row>
|
||||
</Box>
|
||||
|
@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
||||
import { Spinner } from '~/views/components/Spinner';
|
||||
import { createPost } from '~/logic/api/graph';
|
||||
import { deSig } from "~/logic/lib/util";
|
||||
import { scanForMentions } from "~/logic/lib/graph";
|
||||
|
||||
|
||||
export class CommentSubmit extends Component {
|
||||
@ -17,9 +18,8 @@ export class CommentSubmit extends Component {
|
||||
|
||||
onClickPost() {
|
||||
const parentIndex = this.props.parentIndex || '';
|
||||
let post = createPost([
|
||||
{ text: this.state.comment },
|
||||
], parentIndex);
|
||||
const content = scanForMentions(this.state.comment);
|
||||
let post = createPost(content, parentIndex);
|
||||
|
||||
this.setState({ disabled: true }, () => {
|
||||
this.props.api.graph.addPost(
|
||||
|
@ -23,6 +23,7 @@ export const Comments = (props) => {
|
||||
return (
|
||||
<CommentItem
|
||||
key={comment.post.index}
|
||||
contacts={contacts}
|
||||
post={comment.post}
|
||||
nickname={nickname}
|
||||
hasNickname={Boolean(nickname)}
|
||||
|
@ -6,14 +6,15 @@ import GlobalApi from "~/logic/api/global";
|
||||
import { Box, Row } from "@tlon/indigo-react";
|
||||
import styled from "styled-components";
|
||||
import { Author } from "./Author";
|
||||
import {GraphNode, TextContent} from "~/types/graph-update";
|
||||
import tokenizeMessage from '~/logic/lib/tokenizeMessage';
|
||||
import RichText from '~/views/components/RichText';
|
||||
import {LocalUpdateRemoteContentPolicy} from "~/types";
|
||||
import { GraphNode, TextContent } from "~/types/graph-update";
|
||||
import tokenizeMessage from "~/logic/lib/tokenizeMessage";
|
||||
import RichText from "~/views/components/RichText";
|
||||
import { LocalUpdateRemoteContentPolicy } from "~/types";
|
||||
import { MentionText } from "~/views/components/MentionText";
|
||||
|
||||
const ClickBox = styled(Box)`
|
||||
cursor: pointer;
|
||||
padding-left: ${p => p.theme.space[2]}px;
|
||||
padding-left: ${(p) => p.theme.space[2]}px;
|
||||
`;
|
||||
|
||||
interface CommentItemProps {
|
||||
@ -31,9 +32,7 @@ interface CommentItemProps {
|
||||
export function CommentItem(props: CommentItemProps) {
|
||||
const { ship, contacts, book, api, remoteContentPolicy } = props;
|
||||
const commentData = props.comment?.post;
|
||||
const comment = commentData.contents[0] as TextContent;
|
||||
|
||||
const content = tokenizeMessage(comment.text).flat().join(' ');
|
||||
const comment = commentData.contents;
|
||||
|
||||
const disabled = props.pending || window.ship !== commentData.author;
|
||||
|
||||
@ -62,7 +61,11 @@ export function CommentItem(props: CommentItemProps) {
|
||||
</Author>
|
||||
</Row>
|
||||
<Box mb={2}>
|
||||
<RichText className="f9 white-d" remoteContentPolicy={remoteContentPolicy}>{content}</RichText>
|
||||
<MentionText
|
||||
contacts={contacts}
|
||||
content={comment}
|
||||
remoteContentPolicy={remoteContentPolicy}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
@ -11,6 +11,7 @@ import { FormikHelpers } from "formik";
|
||||
import {GraphNode, Graph} from "~/types/graph-update";
|
||||
import {createPost} from "~/logic/api/graph";
|
||||
import { LocalUpdateRemoteContentPolicy } from "~/types";
|
||||
import {scanForMentions} from "~/logic/lib/graph";
|
||||
|
||||
interface CommentsProps {
|
||||
comments: GraphNode;
|
||||
@ -32,7 +33,8 @@ export function Comments(props: CommentsProps) {
|
||||
actions: FormikHelpers<{ comment: string }>
|
||||
) => {
|
||||
try {
|
||||
const post = createPost([{ text: comment }], comments?.post?.index);
|
||||
const content = scanForMentions(comment)
|
||||
const post = createPost(content, comments?.post?.index);
|
||||
await api.graph.addPost(ship, book, post)
|
||||
actions.resetForm();
|
||||
actions.setStatus({ success: null });
|
||||
@ -48,7 +50,7 @@ export function Comments(props: CommentsProps) {
|
||||
{Array.from(comments.children).reverse().map(([idx, comment]) => (
|
||||
<CommentItem
|
||||
comment={comment}
|
||||
key={idx}
|
||||
key={idx.toString()}
|
||||
contacts={props.contacts}
|
||||
api={api}
|
||||
book={book}
|
||||
|
51
pkg/interface/src/views/components/MentionText.tsx
Normal file
51
pkg/interface/src/views/components/MentionText.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import { Text } from "@tlon/indigo-react";
|
||||
import { Contacts, Content, LocalUpdateRemoteContentPolicy } from "~/types";
|
||||
import RichText from "~/views/components/RichText";
|
||||
import { cite } from "~/logic/lib/util";
|
||||
|
||||
interface MentionTextProps {
|
||||
contacts: Contacts;
|
||||
content: Content[];
|
||||
remoteContentPolicy: LocalUpdateRemoteContentPolicy;
|
||||
}
|
||||
export function MentionText(props: MentionTextProps) {
|
||||
const { content, contacts } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
{_.map(content, (c, idx) => {
|
||||
if ("text" in c) {
|
||||
return (
|
||||
<RichText
|
||||
inline
|
||||
key={idx}
|
||||
remoteContentPolicy={props.remoteContentPolicy}
|
||||
>
|
||||
{c.text}
|
||||
</RichText>
|
||||
);
|
||||
} else if ("mention" in c) {
|
||||
return (
|
||||
<Mention key={idx} contacts={contacts || {}} ship={c.mention} />
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Mention(props: { ship: string; contacts: Contacts }) {
|
||||
const { contacts, ship } = props;
|
||||
const contact = contacts[ship];
|
||||
const showNickname = !!contact?.nickname;
|
||||
const name = showNickname ? contact?.nickname : cite(ship);
|
||||
|
||||
return (
|
||||
<Text mx="2px" px="2px" bg="washedBlue" color="blue" mono={!showNickname}>
|
||||
{name}
|
||||
</Text>
|
||||
);
|
||||
}
|
@ -30,7 +30,7 @@ const RichText = React.memo(({ remoteContentPolicy, ...props }) => (
|
||||
return <BaseAnchor target='_blank' rel='noreferrer noopener' borderBottom='1px solid' {...props}>{props.children}</BaseAnchor>;
|
||||
},
|
||||
paragraph: (paraProps) => {
|
||||
return <Text display='block' mb='2' {...props}>{paraProps.children}</Text>;
|
||||
return <Text display={props.inline ? 'inline' : 'block'} mb='2' {...props}>{paraProps.children}</Text>;
|
||||
}
|
||||
}}
|
||||
plugins={[[
|
||||
|
Loading…
Reference in New Issue
Block a user