interface: working sidebar and new page

This commit is contained in:
Logan Allen 2020-09-18 15:11:06 -05:00
parent 0f2c4d069e
commit 7ef30187bb
10 changed files with 195 additions and 108 deletions

View File

@ -9,7 +9,6 @@ export default class BaseApi<S extends object = {}> {
unsubscribe(id: number) {
this.channel.unsubscribe(id);
}
subscribe(path: Path, method, ship = this.ship, app: string, success, fail, quit) {
@ -37,19 +36,20 @@ export default class BaseApi<S extends object = {}> {
);
}
action(appl: string, mark: string, data: any): Promise<any> {
action(
appl: string,
mark: string,
data: any,
ship = (window as any).ship
): Promise<any> {
return new Promise((resolve, reject) => {
this.channel.poke(
(window as any).ship,
ship,
appl,
mark,
data,
(json) => {
resolve(json);
},
(err) => {
reject(err);
}
(json) => { resolve(json); },
(err) => { reject(err); }
);
});
}
@ -58,9 +58,7 @@ export default class BaseApi<S extends object = {}> {
return fetch(`/~/scry/${app}${path}.json`).then(r => r.json() as Promise<T>);
}
async spider<T>(inputMark: string, outputMark: string, threadName: string, body: any): Promise<T> {
const res = await fetch(`/spider/${inputMark}/${threadName}/${outputMark}.json`, {
method: 'POST',
body: JSON.stringify(body)

View File

@ -4,6 +4,7 @@ import { Patp, Path, PatpNoSig } from '~/types/noun';
import _ from 'lodash';
import {makeResource, resourceFromPath} from '../lib/group';
import {GroupPolicy, Enc, Post} from '~/types';
import { deSig } from '~/logic/lib/util';
export const createPost = (contents: Object[], parentIndex: string = '') => {
return {
@ -26,10 +27,19 @@ export default class GraphApi extends BaseApi<StoreState> {
return this.spider('graph-view-action', 'json', threadName, action);
}
createManagedGraph(name: string, title: string, description: string, group: Path) {
const associated = { group: resourceFromPath(group) };
private hookAction(ship: Patp, action: any): Promise<any> {
return this.action('graph-push-hook', 'graph-update', action, deSig(ship));
}
createManagedGraph(
name: string,
title: string,
description: string,
group: Path
) {
const associated = { group: resourceFromPath(group) };
const resource = makeResource(`~${window.ship}`, name);
return this.viewAction('graph-create', {
"create": {
resource,
@ -40,9 +50,14 @@ export default class GraphApi extends BaseApi<StoreState> {
});
}
createUnmanagedGraph(name: string, title: string, description: string, policy: Enc<GroupPolicy>) {
createUnmanagedGraph(
name: string,
title: string,
description: string,
policy: Enc<GroupPolicy>
) {
const resource = makeResource(`~${window.ship}`, name);
return this.viewAction('graph-create', {
"create": {
resource,
@ -85,7 +100,7 @@ export default class GraphApi extends BaseApi<StoreState> {
}
addGraph(ship: Patp, name: string, graph: any, mark: any) {
this.storeAction({
return this.storeAction({
'add-graph': {
resource: { ship, name },
graph,
@ -95,7 +110,7 @@ export default class GraphApi extends BaseApi<StoreState> {
}
removeGraph(ship: Patp, name: string) {
this.storeAction({
return this.storeAction({
'remove-graph': {
resource: { ship, name }
}
@ -110,7 +125,7 @@ export default class GraphApi extends BaseApi<StoreState> {
children: { empty: null }
};
return this.storeAction({
return this.hookAction(ship, {
'add-nodes': {
resource,
nodes
@ -119,7 +134,7 @@ export default class GraphApi extends BaseApi<StoreState> {
}
addNodes(ship: Patp, name: string, nodes: Object) {
this.storeAction({
this.hookAction(ship, {
'add-nodes': {
resource: { ship, name },
nodes
@ -128,7 +143,7 @@ export default class GraphApi extends BaseApi<StoreState> {
}
removeNodes(ship: Patp, name: string, indices: string[]) {
return this.storeAction({
return this.hookAction(ship, {
'remove-nodes': {
resource: { ship, name },
indices

View File

@ -83,6 +83,9 @@ export class LinksApp extends Component {
graphKeys={graphKeys}>
<NewScreen
api={api}
graphKeys={graphKeys}
associations={associations}
groups={groups}
{...props}
/>
</Skeleton>
@ -200,8 +203,6 @@ export class LinksApp extends Component {
const popout = props.match.url.includes('/popout/');
const contactDetails = contacts[resource['group-path']] || {};
console.log(props.match.params.index.split('-'));
const indexArr = props.match.params.index.split('-');
const graph = graphs[resourcePath] || null;

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { makeRoutePath } from '~/logic/lib/util';
export class ChannelItem extends Component {
render() {
@ -15,7 +15,7 @@ export class ChannelItem extends Component {
: null;
return (
<Link to={makeRoutePath(props.link)}>
<Link to={`/~link/${props.link}`}>
<div className={'w-100 v-mid f9 ph5 z1 pv1 relative ' + selectedClass}>
<p className="f9 dib">{props.name}</p>
<p className="f9 dib fr">

View File

@ -22,12 +22,16 @@ export const ChannelSidebar = (props) => {
const associations = props.associations.contacts ?
alphabetiseAssociations(props.associations.contacts) : {};
const graphAssoc = props.associations.graph || {};
const groupedChannels = {};
[...props.graphKeys].map((path) => {
const groupPath = props.associations.link[path] ?
props.associations.link[path]['group-path'] : '';
[...props.graphKeys].map((gKey) => {
const path = `/ship/~${gKey.split('/')[0]}/${gKey.split('/')[1]}`;
const groupPath = graphAssoc[path] ? graphAssoc[path]['group-path'] : '';
if (groupPath in associations) {
// managed
if (groupedChannels[groupPath]) {
const array = groupedChannels[groupPath];
array.push(path);
@ -35,51 +39,51 @@ export const ChannelSidebar = (props) => {
} else {
groupedChannels[groupPath] = [path];
}
} else {
// unmanaged
if (groupedChannels['/~/']) {
const array = groupedChannels['/~/'];
array.push(path);
groupedChannels['/~/'] = array;
} else {
groupedChannels['/~/'] = [path];
};
}
}
});
let i = -1;
//TODO handle unmanaged links
const groupedItems = Object.keys(associations).map((each, i) => {
const channels = groupedChannels[each];
if (!channels || channels.length === 0) { return; }
const groupedItems = Object.keys(associations)
.map((each) => {
const channels = groupedChannels[each];
if (!channels || channels.length === 0)
return;
i++;
if (groupedChannels['/~/'] && groupedChannels['/~/'].length !== 0) {
i++;
}
return (
<GroupItem
key={i + 1}
unmanaged={false}
association={associations[each]}
metadata={graphAssoc}
channels={channels}
selected={props.selected}
/>
);
});
return (
<GroupItem
key={i}
index={i}
association={associations[each]}
linkMetadata={props.associations['link']}
channels={channels}
selected={props.selected}
/>
);
});
if (groupedChannels['/~/'] && groupedChannels['/~/'].length !== 0) {
groupedItems.push(
<GroupItem
key={0}
unmanaged={true}
association={'/~/'}
metadata={graphAssoc}
channels={groupedChannels['/~/']}
selected={props.selected}
/>
);
}
const activeClasses = (props.active === 'collections') ? ' ' : 'dn-s ';
let hiddenClasses = true;
if (props.popout) {
hiddenClasses = false;
} else {
hiddenClasses = props.sidebarShown;
}
const hiddenClasses = !!props.popout ? false : props.sidebarShown;
return (
<div className={

View File

@ -36,8 +36,8 @@ export const CommentItem = (props) => {
{content}
</RichText>
</Text>
</Box>
);
}
</Row>
</Box>
);
}

View File

@ -1,5 +1,7 @@
import React, { Component } from 'react';
import { ChannelItem } from './channel-item';
import { deSig } from '~/logic/lib/util';
export const GroupItem = (props) => {
const association = props.association ? props.association : {};
@ -13,16 +15,18 @@ export const GroupItem = (props) => {
}
const channels = props.channels ? props.channels : [];
const first = (props.index === 0) ? 'pt1' : 'pt6';
const unmanaged = props.unmanaged ? 'pt6' : 'pt1';
const channelItems = channels.map((each, i) => {
const meta = props.linkMetadata[each];
const meta = props.metadata[each];
if (!meta) { return null; }
const link = `${deSig(each.split('/')[2])}/${each.split('/')[3]}`;
const selected = (props.selected === each);
return (
<ChannelsItem
<ChannelItem
key={each}
link={each}
link={link}
selected={selected}
name={meta.metadata.title}
/>
@ -30,7 +34,7 @@ export const GroupItem = (props) => {
});
return (
<div className={first}>
<div className={unmanaged}>
<p className="f9 ph4 pb2 gray3">{title}</p>
{channelItems}
</div>

View File

@ -10,7 +10,6 @@ import { getContactDetails } from '~/logic/lib/util';
export const LinkDetail = (props) => {
console.log(props);
if (!props.node) {
// TODO: something
return (

View File

@ -1,41 +0,0 @@
import React, { Component } from 'react';
import urbitOb from 'urbit-ob';
import { Link } from 'react-router-dom';
import { InviteSearch } from '~/views/components/InviteSearch';
import { Spinner } from '~/views/components/Spinner';
import { makeRoutePath, deSig } from '~/logic/lib/util';
export const NewScreen = (props) => {
const onClickCreate = () => {
let name = 'a' + String(Math.floor(Math.random() * 100));
let res = `/~tacryt-socryp/` + name;
props.api.metadata.metadataAdd(
'link',
res,
'/~tacryt-socryp/aaa',
name,
'',
'~2000.1.1',
'000000'
);
props.api.graph.addGraph(
`~${window.ship}`,
name,
{},
null
);
};
return (
<div>
<button
onClick={onClickCreate}>
Start Chat
</button>
</div>
);
};

View File

@ -0,0 +1,107 @@
import React, { useCallback } from "react";
import { RouteComponentProps } from "react-router-dom";
import { Box, Input, Col } from "@tlon/indigo-react";
import { Formik, Form } from "formik";
import * as Yup from "yup";
import { AsyncButton } from "~/views/components/AsyncButton";
import { FormError } from "~/views/components/FormError";
import GroupSearch from "~/views/components/GroupSearch";
import GlobalApi from "~/logic/api/global";
import { stringToSymbol } from "~/logic/lib/util";
import { useWaitForProps } from "~/logic/lib/useWaitForProps";
import { Associations } from "~/types/metadata-update";
import { Notebooks } from "~/types/publish-update";
import { Groups, GroupPolicy } from "~/types/group-update";
const formSchema = Yup.object({
name: Yup.string().required("Collection must have a name"),
description: Yup.string(),
group: Yup.string(),
});
export function NewScreen(props: object) {
const { history, api } = props;
const waiter = useWaitForProps(props, 5000);
const onSubmit = async (values: object, actions) => {
const resourceId = stringToSymbol(values.name);
try {
const { name, description, group } = values;
if (!!group) {
await props.api.graph.createManagedGraph(
resourceId,
name,
description,
group
);
} else {
await props.api.graph.createUnmanagedGraph(
resourceId,
name,
description,
{ open: {
banRanks: [],
banned: [],
}
}
);
}
await waiter((p) => p?.graphKeys?.has(`${window.ship}/${resourceId}`));
actions.setStatus({ success: null });
history.push(`/~link/${window.ship}/${resourceId}`);
} catch (e) {
console.error(e);
actions.setStatus({ error: "Collection creation failed" });
}
};
return (
<Col p={3}>
<Box mb={4} color="black">New Collection</Box>
<Formik
validationSchema={formSchema}
initialValues={{ name: "", description: "", group: "" }}
onSubmit={onSubmit}>
<Form>
<Box
display="grid"
gridTemplateRows="auto"
gridRowGap={2}
gridTemplateColumns="300px">
<Input
id="name"
label="Name"
caption="Provide a name for your collection"
placeholder="eg. My Links"
/>
<Input
id="description"
label="Description"
caption="What's your collection about?"
placeholder="Collection description"
/>
<GroupSearch
id="group"
label="Group"
caption="What group is the collection for?"
associations={props.associations}
/>
<Box justifySelf="start">
<AsyncButton loadingText="Creating..." type="submit" border>
Create Collection
</AsyncButton>
</Box>
<FormError message="Collection creation failed" />
</Box>
</Form>
</Formik>
</Col>
);
}
export default NewScreen;