links: update for new groups layout

This commit is contained in:
Liam Fitzgerald 2020-09-25 10:44:10 +10:00
parent cf38904b81
commit 24c9067921
13 changed files with 275 additions and 83 deletions

View File

@ -5,7 +5,6 @@
|% |%
++ noun upd ++ noun upd
++ json (update:enjs upd) ++ json (update:enjs upd)
++ noun upd
-- --
:: ::
++ grab ++ grab

View File

@ -5,7 +5,7 @@ import { Path, Patp } from '~/types/noun';
export default class MetadataApi extends BaseApi<StoreState> { export default class MetadataApi extends BaseApi<StoreState> {
metadataAdd(appName: string, appPath: Path, groupPath: Path, title: string, description: string, dateCreated: string, color: string) { metadataAdd(appName: string, appPath: Path, groupPath: Path, title: string, description: string, dateCreated: string, color: string, module: string = '') {
const creator = `~${this.ship}`; const creator = `~${this.ship}`;
return this.metadataAction({ return this.metadataAction({
add: { add: {
@ -19,7 +19,8 @@ export default class MetadataApi extends BaseApi<StoreState> {
description, description,
color, color,
'date-created': dateCreated, 'date-created': dateCreated,
creator creator,
module
} }
} }
}); });

View File

@ -18,6 +18,19 @@ import LaunchReducer from '../reducers/launch-update';
import LinkListenReducer from '../reducers/listen-update'; import LinkListenReducer from '../reducers/listen-update';
import ConnectionReducer from '../reducers/connection'; import ConnectionReducer from '../reducers/connection';
export const homeAssociation = {
"app-path": "/home",
"app-name": "contact",
"group-path": "/home",
metadata: {
color: "0x0",
title: "Home",
description: "",
"date-created": "",
module: "",
},
};
export default class GlobalStore extends BaseStore<StoreState> { export default class GlobalStore extends BaseStore<StoreState> {
inviteReducer = new InviteReducer(); inviteReducer = new InviteReducer();

View File

@ -38,7 +38,7 @@ export interface StoreState {
permissions: Permissions; permissions: Permissions;
s3: S3State; s3: S3State;
graphs: Graphs; graphs: Graphs;
graphKeys: Set<String>; graphKeys: Set<string>;
// App specific states // App specific states

View File

@ -15,12 +15,12 @@ const chatSubscriptions: AppSubscription[] = [
]; ];
const publishSubscriptions: AppSubscription[] = [ const publishSubscriptions: AppSubscription[] = [
['/primary', 'publish'], // ['/primary', 'publish'],
]; ];
const linkSubscriptions: AppSubscription[] = [ const linkSubscriptions: AppSubscription[] = [
['/json/seen', 'link-view'], // ['/json/seen', 'link-view'],
['/listening', 'link-listen-hook'] // ['/listening', 'link-listen-hook']
] ]
const groupSubscriptions: AppSubscription[] = [ const groupSubscriptions: AppSubscription[] = [

View File

@ -17,3 +17,4 @@ export * from './permission-update';
export * from './publish-response'; export * from './publish-response';
export * from './publish-update'; export * from './publish-update';
export * from './s3-update'; export * from './s3-update';
export * from './workspace';

View File

@ -51,4 +51,5 @@ interface Metadata {
'date-created': string; 'date-created': string;
description: string; description: string;
title: string; title: string;
module: string;
} }

View File

@ -18,7 +18,7 @@ export type Serial = string;
export type Jug<K,V> = Map<K,Set<V>>; export type Jug<K,V> = Map<K,Set<V>>;
// name of app // name of app
export type AppName = 'chat' | 'link' | 'contacts' | 'publish'; export type AppName = 'chat' | 'link' | 'contacts' | 'publish' | 'graph';
export function getTagFromFrond<O>(frond: O): keyof O { export function getTagFromFrond<O>(frond: O): keyof O {
const tags = Object.keys(frond) as Array<keyof O>; const tags = Object.keys(frond) as Array<keyof O>;

View File

@ -1,12 +1,14 @@
import React from 'react'; import React from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import { Link } from 'react-router-dom';
import { Box } from '@tlon/indigo-react'; import { Box, Row, Icon, Text } from '@tlon/indigo-react';
import './css/custom.css'; import './css/custom.css';
import Tiles from './components/tiles'; import Tiles from './components/tiles';
import Welcome from './components/welcome'; import Welcome from './components/welcome';
import Groups from './components/Groups';
export default class LaunchApp extends React.Component { export default class LaunchApp extends React.Component {
@ -25,8 +27,30 @@ export default class LaunchApp extends React.Component {
<title>OS1 - Home</title> <title>OS1 - Home</title>
</Helmet> </Helmet>
<div className="h-100 flex flex-column h-100"> <div className="h-100 flex flex-column h-100">
<div className='v-mid ph2 dtc-m dtc-l dtc-xl flex justify-between flex-wrap' style={{ maxWidth: '40rem' }}> <Welcome firstTime={props.launch.firstTime} api={props.api} />
<Welcome firstTime={props.launch.firstTime} api={props.api} /> <Row flexWrap="wrap" mb={4} pitch={4}>
<Box
border={1}
borderRadius={1}
borderColor="lightGray"
m={2}
bg="white"
width="126px"
height="126px"
>
<Link to='/~groups/home'>
<Box p={2} bg="washedGreen" width="100%" height="100%">
<Row alignItems="center">
<Icon
stroke="green"
fill="rgba(0,0,0,0)"
icon="CircleDot"
/>
<Text ml="1" color="green">Home</Text>
</Row>
</Box>
</Link>
</Box>
<Tiles <Tiles
tiles={props.launch.tiles} tiles={props.launch.tiles}
tileOrdering={props.launch.tileOrdering} tileOrdering={props.launch.tileOrdering}
@ -34,7 +58,8 @@ export default class LaunchApp extends React.Component {
location={props.userLocation} location={props.userLocation}
weather={props.weather} weather={props.weather}
/> />
</div> </Row>
<Groups associations={props.associations} />
<Box <Box
position="absolute" position="absolute"
fontFamily="mono" fontFamily="mono"

View File

@ -10,7 +10,9 @@ export default class Tiles extends React.PureComponent {
const { props } = this; const { props } = this;
const tiles = props.tileOrdering.filter((key) => { const tiles = props.tileOrdering.filter((key) => {
return props.tiles[key].isShown; const tile = props.tiles[key];
return tile.isShown;
}).map((key) => { }).map((key) => {
const tile = props.tiles[key]; const tile = props.tiles[key];
if ('basic' in tile.type) { if ('basic' in tile.type) {

View File

@ -0,0 +1,144 @@
import React, { useEffect } from "react";
import { Box, Row, Col, Center } from "@tlon/indigo-react";
import { Switch, Route, Link } from "react-router-dom";
import GlobalApi from "~/logic/api/global";
import { StoreState } from "~/logic/store/type";
import { Association, GraphNode } from "~/types";
import { RouteComponentProps } from "react-router-dom";
import { LinkList } from "./components/link-list";
import { LinkDetail } from "./components/link-detail";
import { LinkItem } from "./components/lib/link-item";
import { LinkSubmit } from "./components/lib/link-submit";
import { LinkPreview } from "./components/lib/link-preview";
import { CommentSubmit } from "./components/lib/comment-submit";
import { Comments } from "./components/lib/comments";
import "./css/custom.css";
type LinkResourceProps = StoreState & {
association: Association;
api: GlobalApi;
baseUrl: string;
} & RouteComponentProps;
export function LinkResource(props: LinkResourceProps) {
const {
association,
api,
baseUrl,
graphs,
contacts,
groups,
associations,
graphKeys,
s3,
hideAvatars,
hideNicknames,
remoteContentPolicy,
} = props;
const appPath = association["app-path"];
const relativePath = (p: string) => `${baseUrl}/resource/link${appPath}${p}`;
const [, , ship, name] = appPath.split("/");
const resourcePath = `${ship.slice(1)}/${name}`;
const resource = associations.graph[resourcePath]
? associations.graph[resourcePath]
: { metadata: {} };
const contactDetails = contacts[resource["group-path"]] || {};
const popout = props.match.url.includes("/popout/");
const graph = graphs[resourcePath] || null;
useEffect(() => {
api.graph.getGraph(ship, name);
}, [association]);
const resourceUrl = `${baseUrl}/resource/link${appPath}`;
if (!graph) {
return <Center>Loading...</Center>;
}
return (
<Col alignItems="center" height="100%" width="100%" overflowY="auto">
<Switch>
<Route
exact
path={relativePath("")}
render={(props) => {
return (
<Col width="100%" p={3} alignItems="center" maxWidth="640px">
<Row>
<LinkSubmit name={name} ship={ship.slice(1)} api={api} />
</Row>
{Array.from(graph.values()).map((node: GraphNode) => {
const contact = contactDetails[node.post.author];
return (
<LinkItem
resource={resourcePath}
node={node}
nickname={contact?.nickname}
hideAvatars={hideAvatars}
hideNicknames={hideNicknames}
baseUrl={resourceUrl}
/>
);
})}
</Col>
);
}}
/>
<Route
path={relativePath("/:index")}
render={(props) => {
const indexArr = props.match.params.index.split("-");
if (indexArr.length <= 1) {
return <div>Malformed URL</div>;
}
const index = parseInt(indexArr[1], 10);
const node = !!graph ? graph.get(index) : null;
if (!node) {
return <Box>Not found</Box>;
}
const contact = contactDetails[node.post.author];
return (
<Col width="100%" p={3} maxWidth="640px">
<Link to={resourceUrl}>{"<- Back"}</Link>
<LinkPreview
resourcePath={resourcePath}
post={node.post}
nickname={contact?.nickname}
hideNicknames={hideNicknames}
commentNumber={node.children.size}
/>
<Row>
<CommentSubmit
name={name}
ship={ship}
api={api}
parentIndex={node.post.index}
/>
</Row>
<Comments
comments={node.children}
resourcePath={resourcePath}
contacts={contactDetails}
popout={false}
api={api}
hideAvatars={hideAvatars}
hideNicknames={hideNicknames}
/>
</Col>
);
}}
/>
</Switch>
</Col>
);
}

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Row, Col, Anchor, Box, Text } from '@tlon/indigo-react';
import { Sigil } from '~/logic/lib/sigil'; import { Sigil } from '~/logic/lib/sigil';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
@ -24,32 +25,43 @@ export const LinkItem = (props) => {
const mono = showNickname ? 'inter white-d' : 'mono white-d'; const mono = showNickname ? 'inter white-d' : 'mono white-d';
const img = showAvatar const img = showAvatar
? <img src={props.avatar} height={38} width={38} className="dib" /> ? <img src={props.avatar} height={36} width={36} className="dib" />
: <Sigil ship={`~${author}`} size={38} color={'#' + props.color} />; : <Sigil ship={`~${author}`} size={36} color={'#' + props.color} />;
const baseUrl = props.baseUrl || `/~link/${resource}`;
let hostname = '';
try {
const url = new URL(contents[1].url);
hostname = url.hostname;
} catch (e) {}
return ( return (
<div className="w-100 pv3 flex bg-white bg-gray0-d lh-solid"> <Row alignItems="center" py={3} bg="white">
{img} {img}
<div className="flex flex-column ml2 flex-auto"> <Col height="100%" justifyContent="space-between" ml={2}>
<a href={contents[1].url} <Anchor
className="w-100 flex" lineHeight="tall"
target="_blank" textDecoration="none"
rel="noopener noreferrer"> href={contents[1].url}
<p className="f8 truncate">{props.title}</p> width="100%"
<span className="gray2 dib v-btm ml2 f8 flex-shrink-0"> target="_blank"
{contents[0].text} rel="noopener noreferrer">
</span> <Text> {contents[0].text}</Text>
</a> <Text ml="2" color="gray">{hostname} </Text>
<div className="w-100"> </Anchor>
<span className={'f9 pr2 pl2 dib ' + mono} title={author}> <Box width="100%">
{ showNickname ? nickname : cite(author) } <Text
</span> fontFamily={showNickname ? 'sans' : 'mono'} pr={2}>
<Link to={`/~link/${resource}/${index}`}> {showNickname ? nickname : cite(author) }
<span className="f9 inter gray2 dib">{size} comments</span> </Text>
<Link to={`${baseUrl}/${index}`}>
<Text color="gray">{size} comments</Text>
</Link> </Link>
</div> </Box>
</div> </Col>
</div> </Row>
); );
} }

View File

@ -1,54 +1,53 @@
import React, { Component, useEffect } from 'react'; import React, { Component, useEffect } from "react";
import { TabBar } from '~/views/components/chat-link-tabbar'; import { TabBar } from "~/views/components/chat-link-tabbar";
import { SidebarSwitcher } from '~/views/components/SidebarSwitch'; import { SidebarSwitcher } from "~/views/components/SidebarSwitch";
import { Link } from 'react-router-dom'; import { Link } from "react-router-dom";
import { LinkItem } from './lib/link-item'; import { LinkItem } from "./lib/link-item";
import { LinkSubmit } from './lib/link-submit'; import { LinkSubmit } from "./lib/link-submit";
import { getContactDetails } from '~/logic/lib/util'; import { getContactDetails } from "~/logic/lib/util";
export const LinkList = (props) => { export const LinkList = (props) => {
const resource = `${props.ship}/${props.name}`; const resource = `${props.ship}/${props.name}`;
const title = props.metadata.title || resource; const title = props.metadata.title || resource;
useEffect(() => {
props.api.graph.getGraph(
`~${props.match.params.ship}`,
props.match.params.name
);
}, [props.match.params.ship, props.match.params.name]);
if (!props.graph && props.graphResource) { if (!props.graph && props.graphResource) {
useEffect(() => { return <div>Loading...</div>;
props.api.graph.getGraph(
`~${props.match.params.ship}`,
props.match.params.name
);
});
return (
<div>Loading...</div>
);
} }
if (!props.graph) { if (!props.graph) {
return ( return <div>Not found</div>;
<div>Not found</div>
);
} }
return ( return (
<div className="h-100 w-100 overflow-hidden flex flex-column"> <div className="h-100 w-100 overflow-hidden flex flex-column">
<div <div
className="w-100 dn-m dn-l dn-xl inter pt4 pb6 pl3 f8" className="w-100 dn-m dn-l dn-xl inter pt4 pb6 pl3 f8"
style={{ height: '1rem' }}> style={{ height: "1rem" }}
<Link to="/~link">{'⟵ All Channels'}</Link> >
<Link to="/~link">{"⟵ All Channels"}</Link>
</div> </div>
<div className={ <div
'pl4 pt2 flex relative overflow-x-scroll' + className={
'overflow-x-auto-l overflow-x-auto-xl flex-shrink-0' + "pl4 pt2 flex relative overflow-x-scroll" +
'bb b--gray4 b--gray1-d bg-gray0-d' "overflow-x-auto-l overflow-x-auto-xl flex-shrink-0" +
} "bb b--gray4 b--gray1-d bg-gray0-d"
style={{ height: 48 }}> }
style={{ height: 48 }}
>
<SidebarSwitcher <SidebarSwitcher
sidebarShown={props.sidebarShown} sidebarShown={props.sidebarShown}
popout={props.popout} popout={props.popout}
api={props.api} /> api={props.api}
<h2 className='white-d dib f9 fw4 lh-solid v-top pt2'>{title}</h2> />
<h2 className="white-d dib f9 fw4 lh-solid v-top pt2">{title}</h2>
<TabBar <TabBar
location={props.location} location={props.location}
popout={props.popout} popout={props.popout}
@ -59,26 +58,21 @@ export const LinkList = (props) => {
<div className="w-100 mt6 flex justify-center overflow-y-scroll ph4 pb4"> <div className="w-100 mt6 flex justify-center overflow-y-scroll ph4 pb4">
<div className="w-100 mw7"> <div className="w-100 mw7">
<div className="flex"> <div className="flex">
<LinkSubmit <LinkSubmit name={props.name} ship={props.ship} api={props.api} />
name={props.name}
ship={props.ship}
api={props.api} />
</div> </div>
{ Array.from(props.graph.values()).map((node) => { {Array.from(props.graph.values()).map((node) => {
return ( return (
<LinkItem <LinkItem
resource={resource} resource={resource}
node={node} node={node}
nickname={props.metadata.nickname} nickname={props.metadata.nickname}
hideAvatars={props.hideAvatars} hideAvatars={props.hideAvatars}
hideNicknames={props.hideNicknames} hideNicknames={props.hideNicknames}
/> />
); );
}) })}
}
</div> </div>
</div> </div>
</div> </div>
); );
} };