mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-26 08:25:04 +03:00
Merge pull request #5228 from urbit/hm/landscape-app-link
interface: app permalink embeds
This commit is contained in:
commit
37b1e9569b
@ -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
|
||||||
|
@ -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)]
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
54
pkg/interface/src/logic/state/docket.ts
Normal file
54
pkg/interface/src/logic/state/docket.ts
Normal 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;
|
@ -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} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user