mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-15 01:52:42 +03:00
Merge branch 'release/next-js' into la/chat-delete
This commit is contained in:
commit
147fae2dd2
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2773b91958c18c7537cb568e01bcf83056447bfef981c5ed4b6688cc3ab69936
|
||||
size 10739521
|
||||
oid sha256:e60e340322fb6cc14507f29d7a25cec1a37a5890ed931ece96e4d5fc45d19466
|
||||
size 10869373
|
||||
|
@ -5,7 +5,7 @@
|
||||
/- glob
|
||||
/+ default-agent, verb, dbug
|
||||
|%
|
||||
++ hash 0v2.9br5i.hl1e5.cda79.75gdi.jj5hc :: DO NOT MOVE FROM LINE 8
|
||||
++ hash 0vp51an.2c81t.2dc6j.oibjo.0mbfd
|
||||
+$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))]
|
||||
+$ all-states
|
||||
$% state-0
|
||||
|
@ -24,6 +24,6 @@
|
||||
<div id="portal-root"></div>
|
||||
<script src="/~landscape/js/channel.js"></script>
|
||||
<script src="/~landscape/js/session.js"></script>
|
||||
<script src="/~landscape/js/bundle/index.2eee573a41d00825e88a.js"></script>
|
||||
<script src="/~landscape/js/bundle/index.b8ba33f37b3f5dd608d6.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -3,15 +3,14 @@
|
||||
:::: /hoon/code/hood/gen
|
||||
::
|
||||
/? 310
|
||||
::
|
||||
::::
|
||||
::
|
||||
:- %say
|
||||
/- *sole
|
||||
/+ *generators
|
||||
:- %ask
|
||||
|= $: [now=@da eny=@uvJ bec=beak]
|
||||
[arg=?(~ [%reset ~]) ~]
|
||||
==
|
||||
=* our p.bec
|
||||
:- %helm-code
|
||||
^- (sole-result [%helm-code ?(~ %reset)])
|
||||
?~ arg
|
||||
=/ code=tape
|
||||
%+ slag 1
|
||||
@ -20,11 +19,23 @@
|
||||
=/ step=tape
|
||||
%+ scow %ud
|
||||
.^(@ud %j /(scot %p our)/step/(scot %da now)/(scot %p our))
|
||||
%- %- slog
|
||||
:~ [%leaf code]
|
||||
[%leaf (weld "current step=" step)]
|
||||
[%leaf "use |code %reset to invalidate this and generate a new code"]
|
||||
==
|
||||
~
|
||||
::
|
||||
%+ print 'use |code %reset to invalidate this and generate a new code'
|
||||
%+ print leaf+(weld "current step=" step)
|
||||
%+ print leaf+code
|
||||
(produce [%helm-code ~])
|
||||
::
|
||||
?> =(%reset -.arg)
|
||||
%reset
|
||||
%+ print 'continue?'
|
||||
%+ print 'warning: resetting your code closes all web sessions'
|
||||
%+ prompt
|
||||
[%& %project "y/n: "]
|
||||
%+ parse
|
||||
;~ pose
|
||||
(cold %.y (mask "yY"))
|
||||
(cold %.n (mask "nN"))
|
||||
==
|
||||
|= reset=?
|
||||
?. reset
|
||||
no-product
|
||||
(produce [%helm-code %reset])
|
||||
|
@ -114,6 +114,15 @@
|
||||
state-3
|
||||
state-4
|
||||
==
|
||||
:: +diplomatic: only renegotiate if versions changed
|
||||
::
|
||||
:: If %.n please leave note as to why renegotiation necessary
|
||||
::
|
||||
:: - Fixing incorrectly held unversioned subscriptions
|
||||
::
|
||||
++ diplomatic
|
||||
^- ?
|
||||
%.n
|
||||
::
|
||||
++ default
|
||||
|* [pull-hook=* =config]
|
||||
@ -239,6 +248,7 @@
|
||||
=/ kick=(list card)
|
||||
?: ?& =(min-version.config prev-min-version.old)
|
||||
=(version.config prev-version.old)
|
||||
diplomatic
|
||||
==
|
||||
~
|
||||
(poke-self:pass kick+!>(%kick))^~
|
||||
@ -439,6 +449,7 @@
|
||||
?~ tan tr-core
|
||||
?. versioned
|
||||
(tr-ap-og:tr-cleanup |.((on-pull-nack:og rid u.tan)))
|
||||
%- (slog leaf+"versioned nack for {<rid>} in {<dap.bowl>}" u.tan)
|
||||
=/ pax
|
||||
(kick-mule:virt rid |.((on-pull-kick:og rid)))
|
||||
?~ pax tr-failed-kick
|
||||
@ -463,18 +474,18 @@
|
||||
:: subscription
|
||||
tr-core
|
||||
(tr-suspend-pub-ver min-version.config)
|
||||
=/ =vase
|
||||
=/ =^cage
|
||||
(convert-to:ver cage)
|
||||
=/ =wire
|
||||
(make-wire /store)
|
||||
=+ resources=(~(gas in *(set resource)) (resource-for-update:og vase))
|
||||
=+ resources=(~(gas in *(set resource)) (resource-for-update:og q.cage))
|
||||
?> ?| no-validate.config
|
||||
?& (check-src resources)
|
||||
(~(has in resources) rid)
|
||||
== ==
|
||||
=/ =mark
|
||||
(append-version:ver version.config)
|
||||
(tr-emit (~(poke-our pass wire) store-name.config mark vase))
|
||||
(tr-emit (~(poke-our pass wire) store-name.config cage))
|
||||
--
|
||||
::
|
||||
++ tr-kick
|
||||
|
@ -73,6 +73,16 @@
|
||||
state-1
|
||||
state-2
|
||||
==
|
||||
:: +diplomatic: only renegotiate if versions changed
|
||||
::
|
||||
:: If %.n please leave note as to why renegotiation necessary
|
||||
::
|
||||
:: - Fixing incorrectly held unversioned subscriptions
|
||||
::
|
||||
++ diplomatic
|
||||
^- ?
|
||||
%.n
|
||||
::
|
||||
++ push-hook
|
||||
|* =config
|
||||
$_ ^|
|
||||
@ -221,6 +231,7 @@
|
||||
|= [prev-min-version=@ud prev-version=@ud]
|
||||
?: ?& =(min-version.config prev-min-version)
|
||||
=(prev-version version.config)
|
||||
diplomatic
|
||||
==
|
||||
:: bail on kick if we didn't change versions
|
||||
~
|
||||
@ -291,23 +302,21 @@
|
||||
?. (supported:ver mark)
|
||||
:_ this
|
||||
(fact-init-kick:io version+!>(min-version.config))
|
||||
=/ =vase
|
||||
(convert-to:ver mark (initial-watch:og t.t.t.t.t.t.path resource))
|
||||
:_ this
|
||||
[%give %fact ~ mark vase]~
|
||||
=- [%give %fact ~ -]~
|
||||
(convert-to:ver mark (initial-watch:og t.t.t.t.t.t.path resource))
|
||||
::
|
||||
++ unversioned
|
||||
?> ?=([%ship @ @ *] t.path)
|
||||
?. =(min-version.config 0)
|
||||
~& >>> "unversioned req from: {<src.bowl>}, nooping"
|
||||
`this
|
||||
=/ =resource
|
||||
(de-path:resource t.path)
|
||||
=/ =vase
|
||||
%+ convert-to:ver update-mark.config
|
||||
=/ =vase
|
||||
(initial-watch:og t.t.t.t.path resource)
|
||||
:_ this
|
||||
[%give %fact ~ update-mark.config vase]~
|
||||
?. =(min-version.config 0)
|
||||
~& >>> "unversioned req from: {<src.bowl>}, nooping"
|
||||
~
|
||||
[%give %fact ~ (convert-to:ver update-mark.config vase)]~
|
||||
--
|
||||
::
|
||||
++ on-agent
|
||||
@ -461,10 +470,7 @@
|
||||
|= [fact-ver=@ud paths=(set path)]
|
||||
=/ =mark
|
||||
(append-version:ver fact-ver)
|
||||
=/ =^cage
|
||||
:- mark
|
||||
(convert-from:ver mark q.cage)
|
||||
(fact:io cage ~(tap in paths))
|
||||
(fact:io (convert-from:ver mark q.cage) ~(tap in paths))
|
||||
:: TODO: deprecate
|
||||
++ unversioned
|
||||
?. =(min-version.config 0) ~
|
||||
@ -474,18 +480,15 @@
|
||||
%- ~(gas in *(set path))
|
||||
(turn (incoming-subscriptions prefix) tail)
|
||||
?: =(0 ~(wyt in unversioned)) ~
|
||||
=/ =^cage
|
||||
:- update-mark.config
|
||||
(convert-from:ver update-mark.config q.cage)
|
||||
(fact:io cage ~(tap in unversioned))^~
|
||||
(fact:io (convert-from:ver update-mark.config q.cage) ~(tap in unversioned))^~
|
||||
--
|
||||
::
|
||||
++ forward-update
|
||||
|= =cage
|
||||
^- (list card:agent:gall)
|
||||
=- lis
|
||||
=/ vas
|
||||
(convert-to:ver cage)
|
||||
=/ vas=vase
|
||||
q:(convert-to:ver cage)
|
||||
%+ roll (resource-for-update q.cage)
|
||||
|= [rid=resource [lis=(list card:agent:gall) tf-vas=(unit vase)]]
|
||||
^- [(list card:agent:gall) (unit vase)]
|
||||
|
@ -29,11 +29,12 @@
|
||||
&((gte ver min) (lte ver version))
|
||||
::
|
||||
++ convert-to
|
||||
|= =cage
|
||||
^- vase
|
||||
?: =(p.cage current-version)
|
||||
q.cage
|
||||
((tube-to p.cage) q.cage)
|
||||
|= [=mark =vase]
|
||||
^- cage
|
||||
:- current-version
|
||||
?: =(mark current-version)
|
||||
vase
|
||||
((tube-to mark) vase)
|
||||
::
|
||||
++ tube-to
|
||||
|= =mark
|
||||
@ -44,10 +45,11 @@
|
||||
.^(tube:clay %cc (scry:io %home /[current-version]/[mark]))
|
||||
::
|
||||
++ convert-from
|
||||
|= =cage
|
||||
^- vase
|
||||
?: =(p.cage current-version)
|
||||
q.cage
|
||||
((tube-from p.cage) q.cage)
|
||||
|= [=mark =vase]
|
||||
^- cage
|
||||
:- mark
|
||||
?: =(mark current-version)
|
||||
vase
|
||||
((tube-from mark) vase)
|
||||
--
|
||||
|
||||
|
@ -3,7 +3,7 @@ import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap';
|
||||
import bigInt, { BigInteger } from 'big-integer';
|
||||
import { buntPost } from '~/logic/lib/post';
|
||||
import { unixToDa } from '~/logic/lib/util';
|
||||
//import tokenizeMessage from './tokenizeMessage';
|
||||
import tokenizeMessage from './tokenizeMessage';
|
||||
|
||||
export function newPost(
|
||||
title: string,
|
||||
@ -20,8 +20,7 @@ export function newPost(
|
||||
signatures: []
|
||||
};
|
||||
|
||||
// re-enable on mainnet deploy
|
||||
//const tokenisedBody = tokenizeMessage(body);
|
||||
const tokenisedBody = tokenizeMessage(body);
|
||||
|
||||
const revContainer: Post = { ...root, index: root.index + '/1' };
|
||||
const commentsContainer = { ...root, index: root.index + '/2' };
|
||||
@ -29,8 +28,7 @@ export function newPost(
|
||||
const firstRevision: Post = {
|
||||
...revContainer,
|
||||
index: revContainer.index + '/1',
|
||||
contents: [{ text: title }, { text: body }]
|
||||
//contents: [{ text: title }, { text: body } ...tokenisedBody]
|
||||
contents: [{ text: title }, ...tokenisedBody]
|
||||
};
|
||||
|
||||
const nodes = {
|
||||
@ -59,14 +57,12 @@ export function newPost(
|
||||
|
||||
export function editPost(rev: number, noteId: BigInteger, title: string, body: string) {
|
||||
const now = Date.now();
|
||||
// reenable
|
||||
//const tokenisedBody = tokenizeMessage(body);
|
||||
const tokenisedBody = tokenizeMessage(body);
|
||||
const newRev: Post = {
|
||||
author: `~${window.ship}`,
|
||||
index: `/${noteId.toString()}/1/${rev}`,
|
||||
'time-sent': now,
|
||||
//contents: [{ text: title }, ...tokenisedBody],
|
||||
contents: [{ text: title }, { text: body }],
|
||||
contents: [{ text: title }, ...tokenisedBody],
|
||||
hash: null,
|
||||
signatures: []
|
||||
};
|
||||
|
@ -98,7 +98,7 @@ class WeatherTile extends React.Component {
|
||||
<Text color='black' cursor='pointer'
|
||||
onClick={() => this.locationSubmit()}
|
||||
>
|
||||
Detect ->
|
||||
Detect {'->'}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
@ -177,7 +177,7 @@ class WeatherTile extends React.Component {
|
||||
<Text>Weather</Text>
|
||||
</Box>
|
||||
<Text style={{ cursor: 'pointer' }}>
|
||||
-> Set location
|
||||
{'->'} Set location
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
@ -226,7 +226,7 @@ class WeatherTile extends React.Component {
|
||||
this.setState({ manualEntry: !this.state.manualEntry })
|
||||
}
|
||||
>
|
||||
Weather ->
|
||||
Weather {'->'}
|
||||
</Text>
|
||||
</Text>
|
||||
|
||||
@ -281,7 +281,7 @@ class WeatherTile extends React.Component {
|
||||
this.setState({ manualEntry: !this.state.manualEntry })
|
||||
}
|
||||
>
|
||||
->
|
||||
{'->'}
|
||||
</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
|
@ -20,7 +20,7 @@ function TranscludedLinkNode(props: {
|
||||
api: GlobalApi;
|
||||
}) {
|
||||
const { node, api, assoc, transcluded } = props;
|
||||
const idx = node.post.index.slice(1).split('/');
|
||||
const idx = node?.post?.index?.slice(1)?.split('/') ?? [];
|
||||
|
||||
switch (idx.length) {
|
||||
case 1:
|
||||
@ -91,7 +91,7 @@ function TranscludedPublishNode(props: {
|
||||
}) {
|
||||
const { node, assoc, transcluded, api } = props;
|
||||
const group = useGroupForAssoc(assoc)!;
|
||||
const idx = node.post.index.slice(1).split('/');
|
||||
const idx = node?.post?.index?.slice(1)?.split('/') ?? [];
|
||||
switch (idx.length) {
|
||||
case 1:
|
||||
const post = node.children
|
||||
|
@ -27,18 +27,10 @@ interface NoteProps {
|
||||
group: Group;
|
||||
}
|
||||
|
||||
const renderers = {
|
||||
link: ({ href, children }) => {
|
||||
return (
|
||||
<Anchor display="inline" target="_blank" href={href}>{children}</Anchor>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export function NoteContent({ post, api }) {
|
||||
return (
|
||||
<Box color="black" className="md" style={{ overflowWrap: 'break-word', overflow: 'hidden' }}>
|
||||
<GraphContent tall contents={post.contents.slice(1)} showOurContact api={api} />
|
||||
<GraphContent tall={true} contents={post.contents.slice(1)} showOurContact api={api} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -56,28 +56,26 @@ const codeToMdAst = (content: CodeContent) => {
|
||||
children: [
|
||||
{
|
||||
type: 'code',
|
||||
value: content.code.expression
|
||||
value: content.code.expression,
|
||||
},
|
||||
{
|
||||
type: 'code',
|
||||
value: (content.code.output || []).join('\n')
|
||||
}
|
||||
]
|
||||
value: (content.code.output || []).join('\n'),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const contentToMdAst = (tall: boolean) => (
|
||||
content: Content
|
||||
): [StitchMode, any] => {
|
||||
if ('text' in content) {
|
||||
return ['merge', tall ? parseTall(content.text) : parseWide(content.text)] as [StitchMode, any];
|
||||
} else if ('code' in content) {
|
||||
return [
|
||||
'block',
|
||||
codeToMdAst(content)
|
||||
];
|
||||
'merge',
|
||||
tall ? parseTall(content.text) : parseWide(content.text),
|
||||
] as [StitchMode, any];
|
||||
} else if ('code' in content) {
|
||||
return ['block', codeToMdAst(content)];
|
||||
} else if ('reference' in content) {
|
||||
return [
|
||||
'block',
|
||||
@ -171,7 +169,9 @@ export function asParent<T extends BlockContent>(node: T): Parent | undefined {
|
||||
function stitchMerge(a: Root, b: Root) {
|
||||
const aChildren = a.children;
|
||||
const bChildren = b.children;
|
||||
if (last(aChildren)?.type === bChildren[0]?.type) {
|
||||
const lastType = last(aChildren)?.type;
|
||||
|
||||
if (lastType === bChildren[0]?.type) {
|
||||
const aGrandchild = getChildren(last(aChildren));
|
||||
const bGrandchild = getChildren(bChildren[0]);
|
||||
const mergedPara = {
|
||||
@ -230,16 +230,16 @@ const header = ({ children, depth, ...rest }) => {
|
||||
const level = depth;
|
||||
const inner =
|
||||
level === 1 ? (
|
||||
<H1>{children}</H1>
|
||||
<H1 display='block'>{children}</H1>
|
||||
) : level === 2 ? (
|
||||
<H2>{children}</H2>
|
||||
<H2 display='block'>{children}</H2>
|
||||
) : level === 3 ? (
|
||||
<H3>{children}</H3>
|
||||
<H3 display='block'>{children}</H3>
|
||||
) : (
|
||||
<H4>{children}</H4>
|
||||
<H4 display='block'>{children}</H4>
|
||||
);
|
||||
return (
|
||||
<Box {...rest} mt="2" mb="4">
|
||||
<Box {...rest}>
|
||||
{inner}
|
||||
</Box>
|
||||
);
|
||||
@ -247,6 +247,12 @@ const header = ({ children, depth, ...rest }) => {
|
||||
|
||||
const renderers = {
|
||||
heading: header,
|
||||
break: () => {
|
||||
return <Box display='block' width='100%' height={2}></Box>
|
||||
},
|
||||
thematicBreak: () => {
|
||||
return <Box display='block' width='100%' height={2}></Box>
|
||||
},
|
||||
inlineCode: ({ language, value }) => {
|
||||
return (
|
||||
<Text
|
||||
@ -260,7 +266,20 @@ const renderers = {
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
|
||||
strong: ({ children }) => {
|
||||
return (
|
||||
<Text fontWeight="bold" lineHeight='1'>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
emphasis: ({ children }) => {
|
||||
return (
|
||||
<Text fontStyle="italic" fontSize="1" lineHeight={'20px'}>
|
||||
{children}
|
||||
</Text>
|
||||
)
|
||||
},
|
||||
blockquote: ({ children, tall, ...rest }) => {
|
||||
return (
|
||||
<Text
|
||||
@ -270,6 +289,7 @@ const renderers = {
|
||||
color="black"
|
||||
paddingLeft={2}
|
||||
py="1"
|
||||
mb="1"
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
@ -319,7 +339,7 @@ const renderers = {
|
||||
},
|
||||
link: (props) => {
|
||||
return (
|
||||
<Anchor href={props.href} borderBottom="1" color="black">
|
||||
<Anchor href={props.url} borderBottom="1" color="black" target="_blank">
|
||||
{props.children}
|
||||
</Anchor>
|
||||
);
|
||||
@ -332,36 +352,55 @@ const renderers = {
|
||||
);
|
||||
},
|
||||
'graph-mention': ({ ship }) => <Mention api={{} as any} ship={ship} />,
|
||||
'graph-url': ({ url }) => (
|
||||
<Box my="2" flexShrink={0}>
|
||||
'image': ({ url }) => (
|
||||
<Box mt="1" mb="2" flexShrink={0}>
|
||||
<RemoteContent key={url} url={url} />
|
||||
</Box>
|
||||
),
|
||||
'graph-reference': ({ api, reference }) => {
|
||||
'graph-url': ({ url }) => (
|
||||
<Box mt="1" mb="2" flexShrink={0}>
|
||||
<RemoteContent key={url} url={url} />
|
||||
</Box>
|
||||
),
|
||||
'graph-reference': ({ api, reference, transcluded }) => {
|
||||
const { link } = referenceToPermalink({ reference });
|
||||
return (
|
||||
<PermalinkEmbed api={api} link={link} transcluded={0} showOurContact />
|
||||
<Box mb="2" flexShrink={0}>
|
||||
<PermalinkEmbed
|
||||
api={api}
|
||||
link={link}
|
||||
transcluded={transcluded}
|
||||
showOurContact
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
},
|
||||
root: ({ children }) => <Col gapY="2">{children}</Col>,
|
||||
root: ({ tall, children }) =>
|
||||
tall ? <Col display='grid' style={{ 'row-gap': '1rem' }}>{children}</Col> : <Box>{children}</Box>,
|
||||
text: ({ value }) => value,
|
||||
};
|
||||
|
||||
export function Graphdown<T extends {} = {}>(
|
||||
props: {
|
||||
ast: GraphAstNode;
|
||||
transcluded: number;
|
||||
tall?: boolean;
|
||||
depth?: number;
|
||||
} & T
|
||||
) {
|
||||
const { ast, depth = 0, ...rest } = props;
|
||||
const { ast, transcluded, tall, depth = 0, ...rest } = props;
|
||||
const { type, children = [], ...nodeRest } = ast;
|
||||
const Renderer = renderers[ast.type] ?? (() => `unknown element: ${type}`);
|
||||
|
||||
return (
|
||||
<Renderer depth={depth} {...rest} {...nodeRest}>
|
||||
<Renderer transcluded={transcluded} depth={depth} {...rest} {...nodeRest} tall={tall}>
|
||||
{children.map((c) => (
|
||||
<Graphdown depth={depth+1} {...rest} ast={c} />
|
||||
<Graphdown
|
||||
transcluded={transcluded}
|
||||
depth={depth + 1}
|
||||
{...rest}
|
||||
ast={c}
|
||||
/>
|
||||
))}
|
||||
</Renderer>
|
||||
);
|
||||
@ -385,11 +424,10 @@ export const GraphContent = React.memo(function GraphContent(
|
||||
api,
|
||||
...rest
|
||||
} = props;
|
||||
const [,ast] = stitchAsts(contents.map(contentToMdAst(tall)));
|
||||
const [, ast] = stitchAsts(contents.map(contentToMdAst(tall)));
|
||||
return (
|
||||
<Box {...rest}>
|
||||
<Graphdown api={api} ast={ast} />
|
||||
<Graphdown transcluded={transcluded} api={api} ast={ast} tall={tall} />
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
import { Formik, Form } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import { Col, Text, Box } from '@tlon/indigo-react';
|
||||
import { ShipSearch } from '~/views/components/ShipSearch';
|
||||
import { resourceFromPath } from '~/logic/lib/group';
|
||||
import { deSig } from '~/logic/lib/util';
|
||||
import { AsyncButton } from '~/views/components/AsyncButton';
|
||||
|
||||
const formSchema = Yup.object({
|
||||
ships: Yup.array(Yup.string()).min(1, 'Must invite at least one ship')
|
||||
});
|
||||
|
||||
export const MessageInvite = (props) => {
|
||||
const { association, api } = { ...props };
|
||||
const initialValues: FormSchema = { ships: [] };
|
||||
const onSubmit = async ({ ships, description }: FormSchema, actions) => {
|
||||
try {
|
||||
const { ship, name } = resourceFromPath(association.group);
|
||||
await api.groups.invite(
|
||||
ship,
|
||||
name,
|
||||
_.compact(ships).map(s => `~${deSig(s)}`),
|
||||
`Inviting you to a DM with ~${ship}`
|
||||
);
|
||||
actions.setStatus({ success: null });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
actions.setStatus({ error: e.message });
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Col p={3}>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
onSubmit={onSubmit}
|
||||
validationSchema={formSchema}
|
||||
validateOnBlur
|
||||
>
|
||||
<Form>
|
||||
<Box mb={3}>
|
||||
<Text fontSize={2} bold>Add to Group Message</Text>
|
||||
</Box>
|
||||
<ShipSearch id='ships' label='Invitees' autoFocus />
|
||||
<AsyncButton border={0} primary loadingText='Inviting...'>
|
||||
Invite
|
||||
</AsyncButton>
|
||||
</Form>
|
||||
</Formik>
|
||||
</Col>
|
||||
);
|
||||
};
|
||||
|
||||
export default MessageInvite;
|
@ -29,34 +29,37 @@ type FormSchema = {
|
||||
moduleType: 'chat' | 'publish' | 'link';
|
||||
} & ChannelWriteFieldSchema;
|
||||
|
||||
const formSchema = (members?: string[]) => Yup.object({
|
||||
name: Yup.string(),
|
||||
description: Yup.string(),
|
||||
ships: Yup.array(Yup.string()),
|
||||
moduleType: Yup.string().required('Must choose channel type'),
|
||||
writers: members ? shipSearchSchemaInGroup(members) : shipSearchSchema,
|
||||
writePerms: Yup.string()
|
||||
});
|
||||
const formSchema = (members?: string[]) =>
|
||||
Yup.object({
|
||||
name: Yup.string(),
|
||||
description: Yup.string(),
|
||||
ships: Yup.array(Yup.string()),
|
||||
moduleType: Yup.string().required('Must choose channel type'),
|
||||
writers: members ? shipSearchSchemaInGroup(members) : shipSearchSchema,
|
||||
writePerms: Yup.string()
|
||||
});
|
||||
|
||||
interface NewChannelProps {
|
||||
type NewChannelProps = {
|
||||
api: GlobalApi;
|
||||
group?: string;
|
||||
workspace: Workspace;
|
||||
baseUrl?: string;
|
||||
}
|
||||
existingMembers: string[];
|
||||
} & PropFunc<typeof Col>;
|
||||
|
||||
export function NewChannel(props: NewChannelProps): ReactElement {
|
||||
const history = useHistory();
|
||||
const { api, group, workspace } = props;
|
||||
|
||||
const { api, group, workspace, existingMembers, ...rest } = props;
|
||||
const groups = useGroupState(state => state.groups);
|
||||
const waiter = useWaitForProps({ groups }, 5000);
|
||||
|
||||
const onSubmit = async (values: FormSchema, actions) => {
|
||||
const name = (values.name) ? values.name : values.moduleType;
|
||||
const resId: string = stringToSymbol(values.name)
|
||||
+ ((workspace?.type !== 'messages') ? `-${Math.floor(Math.random() * 10000)}`
|
||||
: '');
|
||||
const name = values.name ? values.name : values.moduleType;
|
||||
const resId: string =
|
||||
stringToSymbol(values.name) +
|
||||
(workspace?.type !== 'messages'
|
||||
? `-${Math.floor(Math.random() * 10000)}`
|
||||
: '');
|
||||
try {
|
||||
let { description, moduleType, ships, writers } = values;
|
||||
ships = ships.filter(e => e !== '');
|
||||
@ -80,9 +83,9 @@ export function NewChannel(props: NewChannelProps): ReactElement {
|
||||
const resource = resourceFromPath(group);
|
||||
writers = _.compact(writers).map(s => `~${s}`);
|
||||
const us = `~${window.ship}`;
|
||||
if(values.writePerms === 'self') {
|
||||
if (values.writePerms === 'self') {
|
||||
await api.groups.addTag(resource, tag, [us]);
|
||||
} else if(values.writePerms === 'subset') {
|
||||
} else if (values.writePerms === 'subset') {
|
||||
writers.push(us);
|
||||
await api.groups.addTag(resource, tag, writers);
|
||||
}
|
||||
@ -97,10 +100,14 @@ export function NewChannel(props: NewChannelProps): ReactElement {
|
||||
}
|
||||
|
||||
if (!group) {
|
||||
await waiter(p => Boolean(p.groups?.[`/ship/~${window.ship}/${resId}`]));
|
||||
await waiter(p =>
|
||||
Boolean(p.groups?.[`/ship/~${window.ship}/${resId}`])
|
||||
);
|
||||
}
|
||||
actions.setStatus({ success: null });
|
||||
const resourceUrl = (location.pathname.includes('/messages')) ? '/~landscape/messages' : parentPath(location.pathname);
|
||||
const resourceUrl = location.pathname.includes('/messages')
|
||||
? '/~landscape/messages'
|
||||
: parentPath(location.pathname);
|
||||
history.push(
|
||||
`${resourceUrl}/resource/${moduleType}/ship/~${window.ship}/${resId}`
|
||||
);
|
||||
@ -113,7 +120,12 @@ export function NewChannel(props: NewChannelProps): ReactElement {
|
||||
const members = group ? Array.from(groups[group]?.members) : undefined;
|
||||
|
||||
return (
|
||||
<Col overflowY="auto" p={3} backgroundColor="white">
|
||||
<Col
|
||||
overflowY='auto'
|
||||
p={3}
|
||||
backgroundColor='white'
|
||||
{...rest}
|
||||
>
|
||||
<Box
|
||||
pb='3'
|
||||
display={workspace?.type === 'messages' ? 'none' : ['block', 'none']}
|
||||
@ -122,80 +134,84 @@ export function NewChannel(props: NewChannelProps): ReactElement {
|
||||
<Text>{'<- Back'}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fontSize={2} bold>{workspace?.type === 'messages' ? 'Direct Message' : 'New Channel'}</Text>
|
||||
<Text fontSize={2} bold>
|
||||
{workspace?.type === 'messages' && existingMembers ? 'New Group ' : null}
|
||||
{workspace?.type === 'messages' ? 'Direct Message' : 'New Channel'}
|
||||
</Text>
|
||||
</Box>
|
||||
<Formik
|
||||
validationSchema={formSchema(members)}
|
||||
initialValues={{
|
||||
moduleType: (workspace?.type === 'home') ? 'publish' : 'chat',
|
||||
moduleType: workspace?.type === 'home' ? 'publish' : 'chat',
|
||||
name: '',
|
||||
description: '',
|
||||
group: '',
|
||||
ships: [],
|
||||
ships: existingMembers ? [...existingMembers] : [],
|
||||
writePerms: 'everyone',
|
||||
writers: []
|
||||
}}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<Form>
|
||||
<Col
|
||||
maxWidth="348px"
|
||||
gapY="4"
|
||||
>
|
||||
<Col pt={4} gapY="2" display={(workspace?.type === 'messages') ? 'none' : 'flex'}>
|
||||
<Box fontSize="1" color="black" mb={2}>Channel Type</Box>
|
||||
<Form>
|
||||
<Col maxWidth='348px' gapY='4'>
|
||||
<Col
|
||||
pt={4}
|
||||
gapY='2'
|
||||
display={workspace?.type === 'messages' ? 'none' : 'flex'}
|
||||
>
|
||||
<Box fontSize='1' color='black' mb={2}>
|
||||
Channel Type
|
||||
</Box>
|
||||
<IconRadio
|
||||
display={!(workspace?.type === 'home') ? 'flex' : 'none'}
|
||||
icon="Chat"
|
||||
label="Chat"
|
||||
id="chat"
|
||||
name="moduleType"
|
||||
icon='Chat'
|
||||
label='Chat'
|
||||
id='chat'
|
||||
name='moduleType'
|
||||
/>
|
||||
<IconRadio
|
||||
icon="Notebook"
|
||||
label="Notebook"
|
||||
id="publish"
|
||||
name="moduleType"
|
||||
icon='Notebook'
|
||||
label='Notebook'
|
||||
id='publish'
|
||||
name='moduleType'
|
||||
/>
|
||||
<IconRadio
|
||||
icon="Collection"
|
||||
label="Collection"
|
||||
id="link"
|
||||
name="moduleType"
|
||||
icon='Collection'
|
||||
label='Collection'
|
||||
id='link'
|
||||
name='moduleType'
|
||||
/>
|
||||
</Col>
|
||||
<Input
|
||||
display={workspace?.type === 'messages' ? 'none' : 'flex'}
|
||||
id="name"
|
||||
label="Name"
|
||||
caption="Provide a name for your channel"
|
||||
placeholder="eg. My Channel"
|
||||
display={workspace?.type === 'messages' ? 'none' : 'flex'}
|
||||
id='name'
|
||||
label='Name'
|
||||
caption='Provide a name for your channel'
|
||||
placeholder='eg. My Channel'
|
||||
/>
|
||||
<Input
|
||||
display={workspace?.type === 'messages' ? 'none' : 'flex'}
|
||||
id="description"
|
||||
label="Description"
|
||||
caption="What's your channel about?"
|
||||
placeholder="Channel description"
|
||||
display={workspace?.type === 'messages' ? 'none' : 'flex'}
|
||||
id='description'
|
||||
label='Description'
|
||||
caption="What's your channel about?"
|
||||
placeholder='Channel description'
|
||||
/>
|
||||
{(workspace?.type === 'home' || workspace?.type === 'messages') ? (
|
||||
<ShipSearch
|
||||
id="ships"
|
||||
label="Invitees"
|
||||
/>) : (
|
||||
<ChannelWritePerms />
|
||||
)}
|
||||
<Box justifySelf="start">
|
||||
{workspace?.type === 'home' || workspace?.type === 'messages' ? (
|
||||
<ShipSearch id='ships' label='Invitees' />
|
||||
) : (
|
||||
<ChannelWritePerms />
|
||||
)}
|
||||
<Box justifySelf='start'>
|
||||
<AsyncButton
|
||||
primary
|
||||
loadingText="Creating..."
|
||||
type="submit"
|
||||
border
|
||||
primary
|
||||
loadingText='Creating...'
|
||||
type='submit'
|
||||
border
|
||||
>
|
||||
Create
|
||||
</AsyncButton>
|
||||
</Box>
|
||||
<FormError message="Channel creation failed" />
|
||||
<FormError message='Channel creation failed' />
|
||||
</Col>
|
||||
</Form>
|
||||
</Formik>
|
||||
|
@ -1,14 +1,16 @@
|
||||
import { Box, Col, Icon, Text } from '@tlon/indigo-react';
|
||||
import { Association } from '@urbit/api/metadata';
|
||||
import React, { ReactElement, ReactNode, useRef } from 'react';
|
||||
import React, { ReactElement, ReactNode, useRef, useState, useCallback } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import urbitOb from 'urbit-ob';
|
||||
import { Dropdown } from '~/views/components/Dropdown';
|
||||
import GlobalApi from '~/logic/api/global';
|
||||
import { isWriter } from '~/logic/lib/group';
|
||||
import { getItemTitle } from '~/logic/lib/util';
|
||||
import useContactState from '~/logic/state/contact';
|
||||
import useGroupState from '~/logic/state/group';
|
||||
import { MessageInvite } from '~/views/landscape/components/MessageInvite';
|
||||
import RichText from '~/views/components/RichText';
|
||||
|
||||
const TruncatedText = styled(RichText)`
|
||||
@ -27,7 +29,7 @@ type ResourceSkeletonProps = {
|
||||
};
|
||||
|
||||
export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
const { association, baseUrl, children } = props;
|
||||
const { association, baseUrl, children, api } = props;
|
||||
let app = association['app-name'];
|
||||
if (association?.metadata?.config && 'graph' in association.metadata.config) {
|
||||
app = association.metadata.config.graph;
|
||||
@ -36,7 +38,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
const groups = useGroupState(state => state.groups);
|
||||
const group = groups[association.group];
|
||||
let workspace = association.group;
|
||||
const actionsRef = useRef(null);
|
||||
const [actionsWidth, setActionsWidth] = useState(0);
|
||||
|
||||
if (group?.hidden && app === 'chat') {
|
||||
workspace = '/messages';
|
||||
@ -99,7 +101,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
maxWidth={association?.metadata?.description ? ['100%', '50%'] : 'none'}
|
||||
mr='2'
|
||||
ml='1'
|
||||
flexShrink={[1, 0]}
|
||||
flexShrink={1}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
@ -122,13 +124,45 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
</TruncatedText>
|
||||
);
|
||||
|
||||
const WriterControls = () => (
|
||||
<Link to={resourcePath('/new')}>
|
||||
<Text bold pr='3' color='blue'>
|
||||
+ New Post
|
||||
</Text>
|
||||
</Link>
|
||||
);
|
||||
const ExtraControls = () => {
|
||||
if (workspace === '/messages' && isOwn && !resource.startsWith('dm-')) {
|
||||
return (
|
||||
<Dropdown
|
||||
flexShrink={0}
|
||||
dropWidth='300px'
|
||||
width='auto'
|
||||
alignY='top'
|
||||
alignX='right'
|
||||
options={
|
||||
<Col
|
||||
backgroundColor='white'
|
||||
border={1}
|
||||
borderRadius={2}
|
||||
borderColor='lightGray'
|
||||
color='washedGray'
|
||||
boxShadow='0px 0px 0px 3px'
|
||||
>
|
||||
<MessageInvite association={association} api={api} />
|
||||
</Col>
|
||||
}
|
||||
>
|
||||
<Text bold pr='3' color='blue'>
|
||||
+ Add Ship
|
||||
</Text>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
if (canWrite) {
|
||||
return (
|
||||
<Link to={resourcePath('/new')}>
|
||||
<Text bold pr='3' color='blue'>
|
||||
+ New Post
|
||||
</Text>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const MenuControl = () => (
|
||||
<Link to={`${baseUrl}/settings`}>
|
||||
@ -136,12 +170,9 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
</Link>
|
||||
);
|
||||
|
||||
const actRef = actionsRef.current;
|
||||
let actionsWidth = 0;
|
||||
|
||||
if (actRef) {
|
||||
actionsWidth = actRef.clientWidth;
|
||||
}
|
||||
const actionsRef = useCallback((actionsRef) => {
|
||||
setActionsWidth(actionsRef?.getBoundingClientRect().width);
|
||||
}, [rid]);
|
||||
|
||||
return (
|
||||
<Col width='100%' height='100%' overflow='hidden'>
|
||||
@ -173,7 +204,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
flexShrink={0}
|
||||
ref={actionsRef}
|
||||
>
|
||||
{canWrite && <WriterControls />}
|
||||
{ExtraControls()}
|
||||
<MenuControl />
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -52,9 +52,9 @@ export function SidebarListHeader(props: {
|
||||
|
||||
const noun = (props.workspace?.type === 'messages') ? 'Messages' : 'Channels';
|
||||
|
||||
let feedPath: any = '';
|
||||
if (metadata?.config && 'group' in metadata.config) {
|
||||
feedPath = metadata.config.group ?? '';
|
||||
let feedPath: any = null;
|
||||
if (metadata?.config && 'group' in metadata?.config && metadata.config?.group && 'resource' in metadata.config.group) {
|
||||
feedPath = metadata.config.group.resource;
|
||||
}
|
||||
|
||||
const unreadCount = useHarkState(
|
||||
@ -63,7 +63,7 @@ export function SidebarListHeader(props: {
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{( feedPath) ? (
|
||||
{( !!feedPath) ? (
|
||||
<Row
|
||||
flexShrink={0}
|
||||
alignItems="center"
|
||||
|
@ -16,7 +16,7 @@ function sortBigInt(a: BigInteger, b: BigInteger) {
|
||||
}
|
||||
export default class BigIntOrderedMap<V> implements Iterable<[BigInteger, V]> {
|
||||
root: Record<string, V> = {}
|
||||
cachedIter: [BigInteger, V][] = [];
|
||||
cachedIter: [BigInteger, V][] = null;
|
||||
[immerable] = true;
|
||||
|
||||
constructor(items: [BigInteger, V][] = []) {
|
||||
|
Loading…
Reference in New Issue
Block a user