Merge pull request #5228 from urbit/hm/landscape-app-link

interface: app permalink embeds
This commit is contained in:
Liam Fitzgerald 2021-09-21 13:43:41 +10:00 committed by GitHub
commit 37b1e9569b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 207 additions and 27 deletions

View File

@ -330,7 +330,7 @@
=/ body=json (need (de-json:html q.u.body.request.inbound-request))
=/ input=vase (slop !>(~) (tube !>(body)))
=/ boc bec
=/ =start-args [~ `tid boc(q desk) thread input]
=/ =start-args [~ `tid boc(q desk, r da+now.bowl) thread input]
(handle-start-thread start-args)
::
++ on-poke-input

View File

@ -48,7 +48,7 @@
=+ !<(=diff:hood q.cage.sign)
?+ -.diff `this
::
%merge
%commit
=/ =action:hark ~(updated de:cc desk.diff)
:_ this
~[(poke:ha:cc action)]

View File

@ -2,6 +2,8 @@
import {
ReferenceContent, resourceFromPath
} from '@urbit/api';
import { isValidPatp } from 'urbit-ob';
import _ from 'lodash';
export function getPermalinkForGraph(
group: string,
@ -18,7 +20,15 @@ function getPermalinkForAssociatedGroup(group: string) {
return `web+urbitgraph://group/${ship}/${name}`;
}
type Permalink = GraphPermalink | GroupPermalink;
type Permalink = AppPermalink | GraphPermalink | GroupPermalink;
export interface AppPermalink {
type: 'app';
link: string;
ship: string;
desk: string;
path: string;
}
export interface GroupPermalink {
type: 'group';
@ -54,20 +64,13 @@ function parseGraphPermalink(
}
export function permalinkToReference(link: Permalink): ReferenceContent {
if(link.type === 'graph') {
const reference = {
graph: {
graph: link.graph,
group: link.group,
index: link.index
}
};
return { reference };
} else {
const reference = {
group: link.group
};
return { reference };
switch (link.type) {
case 'graph':
return { reference: { graph: _.omit(link, ['type', 'link']) } };
case 'group':
return { reference: { group: link.group } };
case 'app':
return { reference: { app: _.omit(link, ['type', 'link']) } };
}
}
@ -80,6 +83,15 @@ export function referenceToPermalink({ reference }: ReferenceContent): Permalink
link,
...reference.graph
};
} else if ('app' in reference) {
const { ship, desk, path } = reference.app;
return {
type: 'app',
link: `web+urbitgraph://${ship}/${desk}${path}`,
ship,
desk,
path
};
} else {
const link = `web+urbitgraph://group${reference.group.slice(5)}`;
return {
@ -92,6 +104,7 @@ export function referenceToPermalink({ reference }: ReferenceContent): Permalink
export function parsePermalink(url: string): Permalink | null {
const [kind, ...rest] = url.slice(17).split('/');
if (kind === 'group') {
const [ship, name, ...graph] = rest;
const group = `/ship/${ship}/${name}`;
@ -104,5 +117,18 @@ export function parsePermalink(url: string): Permalink | null {
link: url.slice(11)
};
}
if (isValidPatp(kind)) {
const [desk, ...parts] = rest;
const path = '/' + parts.join('/');
return {
type: 'app',
link: url,
ship: kind,
desk,
path
};
}
return null;
}

View File

@ -0,0 +1,54 @@
import { Docket, Treaties, Treaty } from '@urbit/api';
import { useCallback } from 'react';
import create from 'zustand';
import airlock from '~/logic/api';
interface DocketState {
treaties: Treaties;
requestTreaty: (ship: string, desk: string) => Promise<Treaty>;
}
const useDocketState = create<DocketState>((set, get) => ({
requestTreaty: async (ship: string, desk: string) => {
const { treaties } = get();
const key = `${ship}/${desk}`;
if (key in treaties) {
return treaties[key];
}
const result = await airlock.subscribeOnce('treaty', `/treaty/${key}`, 20000);
const treaty = { ...normalizeDocket(result, desk), ship };
set(state => ({
treaties: { ...state.treaties, [key]: treaty }
}));
return treaty;
},
treaties: {},
set
}));
function normalizeDocket<T extends Docket>(docket: T, desk: string): T {
const color = docket?.color?.startsWith('#')
? docket.color
: `#${docket.color.slice(2).replace('.', '')}`.toUpperCase();
return {
...docket,
desk,
color
};
}
export function useTreaty(host: string, desk: string) {
return useDocketState(
useCallback(
(s) => {
const ref = `${host}/${desk}`;
return s.treaties[ref];
},
[host, desk]
)
);
}
export default useDocketState;

View File

@ -1,10 +1,11 @@
import { BaseAnchor, Box, Center, Col, Icon, Row, Text } from '@tlon/indigo-react';
import { BaseAnchor, Box, Button, Center, Col, H3, Icon, Image, Row, Text } from '@tlon/indigo-react';
import { Association, GraphNode, resourceFromPath, GraphConfig } from '@urbit/api';
import React, { useCallback, useEffect, useState } from 'react';
import _ from 'lodash';
import { Link, useLocation } from 'react-router-dom';
import {
getPermalinkForGraph, GraphPermalink as IGraphPermalink, parsePermalink
getPermalinkForGraph, GraphPermalink as IGraphPermalink, parsePermalink,
AppPermalink as IAppPermalink
} from '~/logic/lib/permalinks';
import { getModuleIcon, GraphModule } from '~/logic/lib/util';
import { useVirtualResizeProp } from '~/logic/lib/virtualContext';
@ -12,6 +13,9 @@ import useGraphState from '~/logic/state/graph';
import useMetadataState from '~/logic/state/metadata';
import { GroupLink } from '~/views/components/GroupLink';
import { TranscludedNode } from './TranscludedNode';
import styled from 'styled-components';
import Author from '~/views/components/Author';
import useDocketState, { useTreaty } from '~/logic/state/docket';
function Placeholder(type) {
const lines = (type) => {
@ -184,6 +188,61 @@ function GraphPermalink(
);
}
const ClampedText = styled(Text)`
display: -webkit-box;
line-clamp: 2;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
`;
function AppPermalink({ link, ship, desk }: Omit<IAppPermalink, 'type'>) {
const treaty = useTreaty(ship, desk);
useEffect(() => {
if (!treaty) {
useDocketState.getState().requestTreaty(ship, desk);
}
}, [treaty, ship, desk]);
return (
<Row
display="inline-flex"
width="500px"
padding={3}
bg="washedGray"
borderRadius={3}
>
<Box
position="relative"
flex="none"
height="132px"
width="132px"
marginRight={3}
borderRadius={3}
bg={treaty?.color || 'gray'}
>
{treaty?.image && (
<Image
src={treaty.image}
position="absolute"
top="0"
left="0"
width="100%"
height="100%"
/>
)}
</Box>
<Col>
<H3 color="black">{treaty?.title}</H3>
{treaty?.ship && <Author ship={treaty?.ship} showImage dontShowTime={true} marginBottom={2} />}
<ClampedText marginBottom={2} color="gray">{treaty?.info}</ClampedText>
<Button as="a" href={link} primary alignSelf="start" display="inline-flex" marginTop="auto">Open App</Button>
</Col>
</Row>
);
}
function PermalinkDetails(props: {
title: string;
icon: any;
@ -243,5 +302,9 @@ export function PermalinkEmbed(props: {
showOurContact={props.showOurContact}
/>
);
case 'app':
return (
<AppPermalink {...permalink} />
);
}
}

View File

@ -20,6 +20,7 @@ export interface AuthorProps {
size?: number;
lineHeight?: string | number;
isRelativeTime?: boolean;
dontShowTime: boolean;
}
// eslint-disable-next-line max-lines-per-function

View File

@ -1200,6 +1200,10 @@
::
%group
txt+"[reference to msg in {~(phat tr group.reference.content)}]"
::
%app
=, reference.content
txt+"[reference to app: {(scow %p ship)}/{(trip desk)}{(spud path)}]"
==
::
%mention

View File

@ -115,6 +115,7 @@
?- -.ref
%graph (graph +.ref)
%group (group +.ref)
%app (app +.ref)
==
::
++ graph
@ -128,6 +129,14 @@
++ group
|= grp=res
s+(enjs-path:res grp)
::
++ app
|= [=^ship =desk p=^path]
%- pairs
:~ ship+s+(scot %p ship)
desk+s+desk
path+(path p)
==
--
::
++ maybe-post
@ -391,6 +400,7 @@
%- of
:~ graph+graph
group+dejs-path:res
app+app
==
::
++ graph
@ -399,6 +409,13 @@
graph+dejs-path:res
index+index
==
::
++ app
%- ot
:~ ship+(su ;~(pfix sig fed:ag))
desk+so
path+pa
==
--
::
++ tang
@ -486,6 +503,13 @@
^- [resource update-log]
[*resource *update-log]
--
++ pa
|= j=json
^- path
?> ?=(%s -.j)
?: =('/' p.j) /
(stab p.j)
::
--
::
++ create

View File

@ -29,6 +29,7 @@
+$ reference
$% [%graph group=resource =uid]
[%group group=resource]
[%app =ship =desk =path]
==
::
+$ content

View File

@ -1,7 +1,6 @@
import { Patp } from "../lib";
import BigIntOrderedMap from "../lib/BigIntOrderedMap";
import BigIntArrayOrderedMap from "../lib/BigIntArrayOrderedMap";
import { Patp } from '../lib';
import BigIntOrderedMap from '../lib/BigIntOrderedMap';
import BigIntArrayOrderedMap from '../lib/BigIntArrayOrderedMap';
export interface TextContent {
text: string;
@ -17,7 +16,7 @@ export interface CodeContent {
}
export interface ReferenceContent {
reference: GraphReference | GroupReference;
reference: AppReference | GraphReference | GroupReference;
}
export interface GraphReference {
@ -32,6 +31,14 @@ export interface GroupReference {
group: string;
}
export interface AppReference {
app: {
ship: string;
desk: string;
path: string;
}
}
export interface MentionContent {
mention: string;
}
@ -49,7 +56,7 @@ export interface Post {
index: string;
pending?: boolean;
signatures: string[];
"time-sent": number;
'time-sent': number;
}
export interface GraphNodePoke {