mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-15 10:02:47 +03:00
publish: rewrite notebook components for graph-store
This commit is contained in:
parent
22b6277abe
commit
393f9fd53c
@ -7,11 +7,12 @@ import ReactMarkdown from "react-markdown";
|
||||
import moment from "moment";
|
||||
import { Link } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import { GraphNode } from "~/types/graph-update";
|
||||
|
||||
interface NotePreviewProps {
|
||||
host: string;
|
||||
book: string;
|
||||
note: Note;
|
||||
node: GraphNode;
|
||||
contact?: Contact;
|
||||
hideNicknames?: boolean;
|
||||
}
|
||||
@ -21,47 +22,60 @@ const WrappedBox = styled(Box)`
|
||||
`;
|
||||
|
||||
export function NotePreview(props: NotePreviewProps) {
|
||||
const { note, contact } = props;
|
||||
const { node, contact } = props;
|
||||
const { post } = node;
|
||||
if (!post) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let name = note.author;
|
||||
let name = post?.author;
|
||||
if (contact && !props.hideNicknames) {
|
||||
name = contact.nickname.length > 0 ? contact.nickname : note.author;
|
||||
name = contact.nickname.length > 0 ? contact.nickname : post?.author;
|
||||
}
|
||||
if (name === note.author) {
|
||||
name = cite(note.author);
|
||||
if (name === post?.author) {
|
||||
name = cite(post?.author);
|
||||
}
|
||||
let comment = "No Comments";
|
||||
if (note["num-comments"] == 1) {
|
||||
comment = "1 Comment";
|
||||
} else if (note["num-comments"] > 1) {
|
||||
comment = `${note["num-comments"]} Comments`;
|
||||
}
|
||||
const date = moment(note["date-created"]).fromNow();
|
||||
|
||||
const numComments = node.children.size;
|
||||
const commentDesc =
|
||||
numComments === 0
|
||||
? "No Comments"
|
||||
: numComments === 1
|
||||
? "1 Comment"
|
||||
: `${numComments} Comments`;
|
||||
const date = moment(post["time-sent"]).fromNow();
|
||||
//const popout = props.popout ? "popout/" : "";
|
||||
const url = `/~publish/notebook/${props.host}/${props.book}/note/${note["note-id"]}`;
|
||||
const url = `/~publish/notebook/ship/${props.host}/${props.book}/note/${
|
||||
post.index.split("/")[1]
|
||||
}`;
|
||||
|
||||
// stubbing pending notification-store
|
||||
const isRead = true;
|
||||
|
||||
return (
|
||||
<Link to={url}>
|
||||
<Col mb={4}>
|
||||
<WrappedBox mb={1}>{note.title}</WrappedBox>
|
||||
<WrappedBox mb={1}>{post.contents[0]?.text}</WrappedBox>
|
||||
<WrappedBox mb={1}>
|
||||
<ReactMarkdown
|
||||
unwrapDisallowed
|
||||
allowedTypes={["text", "root", "break", "paragraph"]}
|
||||
source={note.snippet}
|
||||
source={post.contents[1]?.text}
|
||||
/>
|
||||
</WrappedBox>
|
||||
<Box color="gray" display="flex">
|
||||
<Box
|
||||
mr={3}
|
||||
fontFamily={contact?.nickname && !props.hideNicknames ? "sans" : "mono"}
|
||||
fontFamily={
|
||||
contact?.nickname && !props.hideNicknames ? "sans" : "mono"
|
||||
}
|
||||
>
|
||||
{name}
|
||||
</Box>
|
||||
<Box color={note.read ? "gray" : "green"} mr={3}>
|
||||
<Box color={isRead ? "gray" : "green"} mr={3}>
|
||||
{date}
|
||||
</Box>
|
||||
<Box>{comment}</Box>
|
||||
<Box>{commentDesc}</Box>
|
||||
</Box>
|
||||
</Col>
|
||||
</Link>
|
||||
|
@ -20,6 +20,8 @@ import { Groups } from "~/types/group-update";
|
||||
import { Contacts, Rolodex } from "~/types/contact-update";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import styled from "styled-components";
|
||||
import {Association, Associations} from "~/types";
|
||||
import {Graph} from "~/types/graph-update";
|
||||
|
||||
const TabList = styled(_TabList)`
|
||||
margin-bottom: ${(p) => p.theme.space[4]}px;
|
||||
@ -33,18 +35,20 @@ interface NotebookProps {
|
||||
api: GlobalApi;
|
||||
ship: string;
|
||||
book: string;
|
||||
notebook: INotebook;
|
||||
graph: Graph;
|
||||
notebookContacts: Contacts;
|
||||
association: Association;
|
||||
associations: Associations;
|
||||
contacts: Rolodex;
|
||||
groups: Groups;
|
||||
hideNicknames: boolean;
|
||||
}
|
||||
|
||||
export function Notebook(props: NotebookProps & RouteComponentProps) {
|
||||
const { api, ship, book, notebook, notebookContacts, groups } = props;
|
||||
const { api, ship, book, association, notebookContacts, groups } = props;
|
||||
|
||||
const contact = notebookContacts[ship];
|
||||
const group = groups[notebook?.["writers-group-path"]];
|
||||
const group = groups[association['group-path']];
|
||||
const role = group ? roleForShip(group, window.ship) : undefined;
|
||||
const isOwn = `~${window.ship}` === ship;
|
||||
const isAdmin = role === "admin" || isOwn;
|
||||
@ -52,10 +56,10 @@ export function Notebook(props: NotebookProps & RouteComponentProps) {
|
||||
const isWriter =
|
||||
isOwn || group.tags?.publish?.[`writers-${book}`]?.has(window.ship);
|
||||
|
||||
const notesList = notebook?.["notes-by-date"] || [];
|
||||
const notes = notebook?.notes || {};
|
||||
const showNickname = contact?.nickname && !props.hideNicknames;
|
||||
|
||||
const { metadata } = props.association || {};
|
||||
|
||||
return (
|
||||
<Box
|
||||
pt={4}
|
||||
@ -71,7 +75,7 @@ export function Notebook(props: NotebookProps & RouteComponentProps) {
|
||||
<Link to="/~publish">{"<- All Notebooks"}</Link>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text> {notebook?.title}</Text>
|
||||
<Text> {metadata?.title}</Text>
|
||||
<br />
|
||||
<Text color="lightGray">by </Text>
|
||||
<Text fontFamily={showNickname ? "sans" : "mono"}>
|
||||
@ -80,7 +84,7 @@ export function Notebook(props: NotebookProps & RouteComponentProps) {
|
||||
</Box>
|
||||
<Row justifyContent={["flex-start", "flex-end"]}>
|
||||
{isWriter && (
|
||||
<Link to={`/~publish/notebook/${ship}/${book}/new`}>
|
||||
<Link to={`/~publish/notebook/ship/${ship}/${book}/new`}>
|
||||
<Button primary border>
|
||||
New Post
|
||||
</Button>
|
||||
@ -103,8 +107,7 @@ export function Notebook(props: NotebookProps & RouteComponentProps) {
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<NotebookPosts
|
||||
notes={notes}
|
||||
list={notesList}
|
||||
graph={props.graph}
|
||||
host={ship}
|
||||
book={book}
|
||||
contacts={notebookContacts}
|
||||
@ -112,15 +115,16 @@ export function Notebook(props: NotebookProps & RouteComponentProps) {
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Box color="black">{notebook?.about}</Box>
|
||||
<Box color="black">{metadata?.description}</Box>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Subscribers
|
||||
host={ship}
|
||||
book={book}
|
||||
notebook={notebook}
|
||||
association={association}
|
||||
associations={props.associations}
|
||||
api={api}
|
||||
groups={groups}
|
||||
contacts={props.contacts}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
@ -128,7 +132,7 @@ export function Notebook(props: NotebookProps & RouteComponentProps) {
|
||||
host={ship}
|
||||
book={book}
|
||||
api={api}
|
||||
notebook={notebook}
|
||||
association={props.association}
|
||||
contacts={notebookContacts}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
@ -29,7 +29,7 @@ function UnreadCount(props: { unread: number }) {
|
||||
|
||||
export function NotebookItem(props: NotebookItemProps) {
|
||||
return (
|
||||
<Link to={"/~publish/notebook/" + props.path}>
|
||||
<Link to={"/~publish/notebook" + props.path}>
|
||||
<HoverBox
|
||||
bg="white"
|
||||
bgActive="washedGray"
|
||||
|
@ -1,13 +1,11 @@
|
||||
import React, { Component } from "react";
|
||||
import { Col } from "@tlon/indigo-react";
|
||||
import { Notes, NoteId } from "../../../../types/publish-update";
|
||||
import { NotePreview } from "./NotePreview";
|
||||
import { Contacts } from "../../../../types/contact-update";
|
||||
import { Contacts, Graph } from "~/types";
|
||||
|
||||
interface NotebookPostsProps {
|
||||
list: NoteId[];
|
||||
contacts: Contacts;
|
||||
notes: Notes;
|
||||
graph: Graph;
|
||||
host: string;
|
||||
book: string;
|
||||
hideNicknames?: boolean;
|
||||
@ -16,23 +14,19 @@ interface NotebookPostsProps {
|
||||
export function NotebookPosts(props: NotebookPostsProps) {
|
||||
return (
|
||||
<Col>
|
||||
{props.list.map((noteId: NoteId) => {
|
||||
const note = props.notes[noteId];
|
||||
if (!note) {
|
||||
console.log(noteId);
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<NotePreview
|
||||
key={noteId}
|
||||
host={props.host}
|
||||
book={props.book}
|
||||
note={note}
|
||||
contact={props.contacts[note.author.substr(1)]}
|
||||
hideNicknames={props.hideNicknames}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{Array.from(props.graph || []).map(
|
||||
([date, node]) =>
|
||||
node && (
|
||||
<NotePreview
|
||||
key={date}
|
||||
host={props.host}
|
||||
book={props.book}
|
||||
contact={props.contacts[node.post.author]}
|
||||
node={node}
|
||||
hideNicknames={props.hideNicknames}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
@ -1,40 +1,37 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { RouteComponentProps, Link, Route, Switch } from "react-router-dom";
|
||||
import { Box, Text } from "@tlon/indigo-react";
|
||||
import GlobalApi from "../../../../api/global";
|
||||
import { PublishContent } from "./PublishContent";
|
||||
import { Notebook as INotebook } from "../../../../types/publish-update";
|
||||
import { Groups } from "../../../../types/group-update";
|
||||
import { Contacts, Rolodex } from "../../../../types/contact-update";
|
||||
import { RouteComponentProps, Route, Switch } from "react-router-dom";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import Notebook from "./Notebook";
|
||||
import NewPost from "./new-post";
|
||||
import { NoteRoutes } from './NoteRoutes';
|
||||
import { Association, Associations, Graphs, Groups, Contacts, Rolodex } from "~/types";
|
||||
|
||||
interface NotebookRoutesProps {
|
||||
api: GlobalApi;
|
||||
ship: string;
|
||||
book: string;
|
||||
notes: any;
|
||||
notebook: INotebook;
|
||||
graphs: Graphs;
|
||||
notebookContacts: Contacts;
|
||||
contacts: Rolodex;
|
||||
groups: Groups;
|
||||
hideAvatars: boolean;
|
||||
hideNicknames: boolean;
|
||||
association: Association;
|
||||
associations: Associations;
|
||||
}
|
||||
|
||||
export function NotebookRoutes(
|
||||
props: NotebookRoutesProps & RouteComponentProps
|
||||
) {
|
||||
const { ship, book, api, notebook, notebookContacts } = props;
|
||||
const { ship, book, api, notebookContacts } = props;
|
||||
|
||||
useEffect(() => {
|
||||
api.publish.fetchNotesPage(ship, book, 1, 50);
|
||||
api.publish.fetchNotebook(ship, book);
|
||||
ship && book && api.graph.getGraph(ship, book);
|
||||
}, [ship, book]);
|
||||
|
||||
const graph = props.graphs[`${ship.slice(1)}/${book}`];
|
||||
|
||||
const baseUrl = `/~publish/notebook/${ship}/${book}`;
|
||||
const baseUrl = `/~publish/notebook/ship/${ship}/${book}`;
|
||||
|
||||
const relativePath = (path: string) => `${baseUrl}${path}`;
|
||||
return (
|
||||
@ -43,7 +40,7 @@ export function NotebookRoutes(
|
||||
path={baseUrl}
|
||||
exact
|
||||
render={(routeProps) => {
|
||||
return <Notebook {...props} />;
|
||||
return <Notebook {...props} graph={graph} />;
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
@ -54,7 +51,8 @@ export function NotebookRoutes(
|
||||
api={api}
|
||||
book={book}
|
||||
ship={ship}
|
||||
notebook={notebook}
|
||||
association={props.association}
|
||||
graph={graph}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -62,15 +60,23 @@ export function NotebookRoutes(
|
||||
path={relativePath("/note/:noteId")}
|
||||
render={(routeProps) => {
|
||||
const { noteId } = routeProps.match.params;
|
||||
const note = notebook?.notes[noteId];
|
||||
const noteIdNum = parseInt(noteId, 10);
|
||||
|
||||
if(!graph) {
|
||||
return null;
|
||||
}
|
||||
const note = graph.get(noteIdNum);
|
||||
if(!note) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<NoteRoutes
|
||||
api={api}
|
||||
book={book}
|
||||
ship={ship}
|
||||
noteId={noteId}
|
||||
notebook={notebook}
|
||||
note={note}
|
||||
notebook={graph}
|
||||
noteId={noteIdNum}
|
||||
contacts={notebookContacts}
|
||||
hideAvatars={props.hideAvatars}
|
||||
hideNicknames={props.hideNicknames}
|
||||
|
@ -4,24 +4,23 @@ import * as Yup from "yup";
|
||||
import {
|
||||
Box,
|
||||
Input,
|
||||
Checkbox,
|
||||
Col,
|
||||
InputLabel,
|
||||
InputCaption,
|
||||
Button,
|
||||
Center,
|
||||
} from "@tlon/indigo-react";
|
||||
import { Formik, Form, useFormikContext, FormikHelpers } from "formik";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import { Notebook } from "~/types/publish-update";
|
||||
import { Contacts } from "~/types/contact-update";
|
||||
import { FormError } from "~/views/components/FormError";
|
||||
import { RouteComponentProps, useHistory } from "react-router-dom";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { Association } from "~/types";
|
||||
import { uxToHex } from "~/logic/lib/util";
|
||||
|
||||
interface SettingsProps {
|
||||
host: string;
|
||||
book: string;
|
||||
notebook: Notebook;
|
||||
association: Association;
|
||||
contacts: Contacts;
|
||||
api: GlobalApi;
|
||||
}
|
||||
@ -29,13 +28,11 @@ interface SettingsProps {
|
||||
interface FormSchema {
|
||||
name: string;
|
||||
description: string;
|
||||
comments: boolean;
|
||||
}
|
||||
|
||||
const formSchema = Yup.object({
|
||||
name: Yup.string().required("Notebook must have a name"),
|
||||
description: Yup.string(),
|
||||
comments: Yup.boolean(),
|
||||
});
|
||||
|
||||
const ResetOnPropsChange = (props: { init: FormSchema; book: string }) => {
|
||||
@ -48,12 +45,12 @@ const ResetOnPropsChange = (props: { init: FormSchema; book: string }) => {
|
||||
};
|
||||
|
||||
export function Settings(props: SettingsProps) {
|
||||
const { host, notebook, api, book } = props;
|
||||
const { api, book } = props;
|
||||
const history = useHistory();
|
||||
const { metadata } = props.association || {};
|
||||
const initialValues: FormSchema = {
|
||||
name: notebook?.title,
|
||||
description: notebook?.about,
|
||||
comments: notebook?.comments,
|
||||
name: metadata?.title,
|
||||
description: metadata?.description,
|
||||
};
|
||||
|
||||
const onSubmit = async (
|
||||
@ -61,9 +58,16 @@ export function Settings(props: SettingsProps) {
|
||||
actions: FormikHelpers<FormSchema>
|
||||
) => {
|
||||
try {
|
||||
const { name, description, comments } = values;
|
||||
await api.publish.editBook(book, name, description, comments);
|
||||
api.publish.fetchNotebook(host, book);
|
||||
const { name, description } = values;
|
||||
await api.metadata.metadataAdd(
|
||||
"publish",
|
||||
props.association["app-path"],
|
||||
props.association["group-path"],
|
||||
name,
|
||||
description,
|
||||
props.association.metadata["date-created"],
|
||||
uxToHex(props.association.metadata.color)
|
||||
);
|
||||
actions.setStatus({ success: null });
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
@ -72,7 +76,7 @@ export function Settings(props: SettingsProps) {
|
||||
};
|
||||
|
||||
const onDelete = async () => {
|
||||
await api.publish.delBook(book);
|
||||
await api.graph.deleteGraph(book);
|
||||
history.push("/~publish");
|
||||
};
|
||||
|
||||
@ -96,7 +100,7 @@ export function Settings(props: SettingsProps) {
|
||||
Permanently delete this notebook. (All current members will no
|
||||
longer see this notebook.)
|
||||
</InputCaption>
|
||||
<Button onClick={onDelete} mt={1} border error>
|
||||
<Button type="button" onClick={onDelete} mt={1} border error>
|
||||
Delete this notebook
|
||||
</Button>
|
||||
</Col>
|
||||
@ -110,11 +114,6 @@ export function Settings(props: SettingsProps) {
|
||||
label="Change description"
|
||||
caption="Change the description of this notebook"
|
||||
/>
|
||||
<Checkbox
|
||||
id="comments"
|
||||
label="Comments"
|
||||
caption="Subscribers may comment when enabled"
|
||||
/>
|
||||
<ResetOnPropsChange init={initialValues} book={book} />
|
||||
<AsyncButton loadingText="Updating.." border>
|
||||
Save
|
||||
|
@ -4,22 +4,22 @@ import { resourceFromPath } from '~/logic/lib/group';
|
||||
import {Notebook} from '~/types/publish-update';
|
||||
import GlobalApi from '~/logic/api/global';
|
||||
import {Groups} from '~/types/group-update';
|
||||
import {Associations} from '~/types/metadata-update';
|
||||
import {Associations, Association} from '~/types/metadata-update';
|
||||
import {Rolodex} from '~/types/contact-update';
|
||||
import {GraphNode} from '~/types/graph-update';
|
||||
|
||||
interface SubscribersProps {
|
||||
notebook: Notebook;
|
||||
api: GlobalApi;
|
||||
groups: Groups;
|
||||
book: string;
|
||||
associations: Associations;
|
||||
association: Association;
|
||||
contacts: Rolodex;
|
||||
}
|
||||
|
||||
export class Subscribers extends Component<SubscribersProps> {
|
||||
constructor(props) {
|
||||
constructor(props: SubscribersProps) {
|
||||
super(props);
|
||||
this.redirect = this.redirect.bind(this);
|
||||
this.addUser = this.addUser.bind(this);
|
||||
this.removeUser = this.removeUser.bind(this);
|
||||
this.addAll = this.addAll.bind(this);
|
||||
@ -33,13 +33,12 @@ export class Subscribers extends Component<SubscribersProps> {
|
||||
this.props.api.groups.remove(path, [who]);
|
||||
}
|
||||
|
||||
redirect(url) {
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
addAll() {
|
||||
const path = this.props.notebook['writers-group-path'];
|
||||
const path = this.props.association['group-path'];
|
||||
const group = path ? this.props.groups[path] : null;
|
||||
if(!group) {
|
||||
return;
|
||||
}
|
||||
const resource = resourceFromPath(path);
|
||||
this.props.api.groups.addTag(
|
||||
resource,
|
||||
@ -50,7 +49,7 @@ export class Subscribers extends Component<SubscribersProps> {
|
||||
|
||||
|
||||
render() {
|
||||
const path = this.props.notebook['writers-group-path'];
|
||||
const path = this.props.association['group-path'];
|
||||
const group = path ? this.props.groups[path] : null;
|
||||
|
||||
|
||||
@ -71,7 +70,10 @@ export class Subscribers extends Component<SubscribersProps> {
|
||||
addDesc: 'Allow user to write to this notebook'
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
if(!group) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
Loading…
Reference in New Issue
Block a user