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)) =/ body=json (need (de-json:html q.u.body.request.inbound-request))
=/ input=vase (slop !>(~) (tube !>(body))) =/ input=vase (slop !>(~) (tube !>(body)))
=/ boc bec =/ 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) (handle-start-thread start-args)
:: ::
++ on-poke-input ++ on-poke-input

View File

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

View File

@ -2,6 +2,8 @@
import { import {
ReferenceContent, resourceFromPath ReferenceContent, resourceFromPath
} from '@urbit/api'; } from '@urbit/api';
import { isValidPatp } from 'urbit-ob';
import _ from 'lodash';
export function getPermalinkForGraph( export function getPermalinkForGraph(
group: string, group: string,
@ -18,7 +20,15 @@ function getPermalinkForAssociatedGroup(group: string) {
return `web+urbitgraph://group/${ship}/${name}`; 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 { export interface GroupPermalink {
type: 'group'; type: 'group';
@ -54,20 +64,13 @@ function parseGraphPermalink(
} }
export function permalinkToReference(link: Permalink): ReferenceContent { export function permalinkToReference(link: Permalink): ReferenceContent {
if(link.type === 'graph') { switch (link.type) {
const reference = { case 'graph':
graph: { return { reference: { graph: _.omit(link, ['type', 'link']) } };
graph: link.graph, case 'group':
group: link.group, return { reference: { group: link.group } };
index: link.index case 'app':
} return { reference: { app: _.omit(link, ['type', 'link']) } };
};
return { reference };
} else {
const reference = {
group: link.group
};
return { reference };
} }
} }
@ -80,6 +83,15 @@ export function referenceToPermalink({ reference }: ReferenceContent): Permalink
link, link,
...reference.graph ...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 { } else {
const link = `web+urbitgraph://group${reference.group.slice(5)}`; const link = `web+urbitgraph://group${reference.group.slice(5)}`;
return { return {
@ -92,6 +104,7 @@ export function referenceToPermalink({ reference }: ReferenceContent): Permalink
export function parsePermalink(url: string): Permalink | null { export function parsePermalink(url: string): Permalink | null {
const [kind, ...rest] = url.slice(17).split('/'); const [kind, ...rest] = url.slice(17).split('/');
if (kind === 'group') { if (kind === 'group') {
const [ship, name, ...graph] = rest; const [ship, name, ...graph] = rest;
const group = `/ship/${ship}/${name}`; const group = `/ship/${ship}/${name}`;
@ -104,5 +117,18 @@ export function parsePermalink(url: string): Permalink | null {
link: url.slice(11) 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; 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 { Association, GraphNode, resourceFromPath, GraphConfig } from '@urbit/api';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import _ from 'lodash'; import _ from 'lodash';
import { Link, useLocation } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
import { import {
getPermalinkForGraph, GraphPermalink as IGraphPermalink, parsePermalink getPermalinkForGraph, GraphPermalink as IGraphPermalink, parsePermalink,
AppPermalink as IAppPermalink
} from '~/logic/lib/permalinks'; } from '~/logic/lib/permalinks';
import { getModuleIcon, GraphModule } from '~/logic/lib/util'; import { getModuleIcon, GraphModule } from '~/logic/lib/util';
import { useVirtualResizeProp } from '~/logic/lib/virtualContext'; import { useVirtualResizeProp } from '~/logic/lib/virtualContext';
@ -12,6 +13,9 @@ import useGraphState from '~/logic/state/graph';
import useMetadataState from '~/logic/state/metadata'; import useMetadataState from '~/logic/state/metadata';
import { GroupLink } from '~/views/components/GroupLink'; import { GroupLink } from '~/views/components/GroupLink';
import { TranscludedNode } from './TranscludedNode'; 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) { function Placeholder(type) {
const lines = (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: { function PermalinkDetails(props: {
title: string; title: string;
icon: any; icon: any;
@ -243,5 +302,9 @@ export function PermalinkEmbed(props: {
showOurContact={props.showOurContact} showOurContact={props.showOurContact}
/> />
); );
case 'app':
return (
<AppPermalink {...permalink} />
);
} }
} }

View File

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

View File

@ -1200,6 +1200,10 @@
:: ::
%group %group
txt+"[reference to msg in {~(phat tr group.reference.content)}]" 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 %mention

View File

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

View File

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

View File

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