Merge pull request #4975 from urbit/lf/more-buses

interface: post-deploy fix omnibus
This commit is contained in:
matildepark 2021-06-03 01:51:32 -04:00 committed by GitHub
commit 6a23722448
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 140 additions and 53 deletions

View File

@ -62,8 +62,8 @@ export function getNotificationKey(
): string { ): string {
const base = time.toString(); const base = time.toString();
if ('graph' in notification.index) { if ('graph' in notification.index) {
const { graph, index } = notification.index.graph; const { graph, index, description } = notification.index.graph;
return `${base}-${graph}-${index}`; return `${base}-${graph}-${index}-${description}`;
} else if ('group' in notification.index) { } else if ('group' in notification.index) {
const { group } = notification.index.group; const { group } = notification.index.group;
return `${base}-${group}`; return `${base}-${group}`;

View File

@ -4,7 +4,6 @@ import {
Timebox Timebox
} from '@urbit/api'; } from '@urbit/api';
import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap'; import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap';
import { BigInteger } from 'big-integer';
import _ from 'lodash'; import _ from 'lodash';
import { compose } from 'lodash/fp'; import { compose } from 'lodash/fp';
import { makePatDa } from '~/logic/lib/util'; import { makePatDa } from '~/logic/lib/util';
@ -152,7 +151,9 @@ function unreads(json: any, state: HarkState): HarkState {
const { unreads, notifications, last } = stats; const { unreads, notifications, last } = stats;
updateNotificationStats(state, index, 'last', () => last); updateNotificationStats(state, index, 'last', () => last);
_.each(notifications, ({ time, index }) => { _.each(notifications, ({ time, index }) => {
addNotificationToUnread(state, index, makePatDa(time)); if(!time) {
addNotificationToUnread(state, index);
}
}); });
if('count' in unreads) { if('count' in unreads) {
updateUnreadCount(state, index, (u = 0) => u + unreads.count); updateUnreadCount(state, index, (u = 0) => u + unreads.count);
@ -210,14 +211,16 @@ function updateUnreads(state: HarkState, index: NotifIndex, f: (us: Set<string>)
return state; return state;
} }
function addNotificationToUnread(state: HarkState, index: NotifIndex, time: BigInteger) { function addNotificationToUnread(state: HarkState, index: NotifIndex) {
if('graph' in index) { if('graph' in index) {
const path = [index.graph.graph, index.graph.index, 'notifications']; const path = [index.graph.graph, index.graph.index, 'notifications'];
const curr = _.get(state.unreads.graph, path, []); const curr = _.get(state.unreads.graph, path, []);
_.set(state.unreads.graph, path, _.set(state.unreads.graph, path,
[ [
...curr.filter(c => !(c.time.eq(time) && notifIdxEqual(c.index, index))), ...curr.filter((c) => {
{ time, index } return !(notifIdxEqual(c.index, index));
}),
{ index }
] ]
); );
} else if ('group' in index) { } else if ('group' in index) {
@ -225,12 +228,23 @@ function addNotificationToUnread(state: HarkState, index: NotifIndex, time: BigI
const curr = _.get(state.unreads.group, path, []); const curr = _.get(state.unreads.group, path, []);
_.set(state.unreads.group, path, _.set(state.unreads.group, path,
[ [
...curr.filter(c => !(c.time.eq(time) && notifIdxEqual(c.index, index))), ...curr.filter(c => !notifIdxEqual(c.index, index)),
{ time, index } { index }
] ]
); );
} }
} }
function removeNotificationFromUnread(state: HarkState, index: NotifIndex) {
if('graph' in index) {
const path = [index.graph.graph, index.graph.index, 'notifications'];
const curr = _.get(state.unreads.graph, path, []);
_.set(state.unreads.graph, path, curr.filter(c => !notifIdxEqual(c.index, index)));
} else if ('group' in index) {
const path = [index.group.group, 'notifications'];
const curr = _.get(state.unreads.group, path, []);
_.set(state.unreads.group, path, curr.filter(c => !notifIdxEqual(c.index, index)));
}
}
function updateNotificationStats(state: HarkState, index: NotifIndex, statField: 'unreads' | 'last', f: (x: number) => number, notify = false) { function updateNotificationStats(state: HarkState, index: NotifIndex, statField: 'unreads' | 'last', f: (x: number) => number, notify = false) {
if('graph' in index) { if('graph' in index) {
@ -280,6 +294,9 @@ const timebox = (json: any, state: HarkState): HarkState => {
state.notifications = state.notifications.set(time, data.notifications); state.notifications = state.notifications.set(time, data.notifications);
} else { } else {
state.unreadNotes = data.notifications; state.unreadNotes = data.notifications;
_.each(data.notifications, ({ index }) => {
addNotificationToUnread(state, index);
});
} }
} }
return state; return state;
@ -338,6 +355,7 @@ function read(json: any, state: HarkState): HarkState {
read[0].notification.contents = mergeNotifs(read[0].notification.contents, toMerge[0].notification.contents); read[0].notification.contents = mergeNotifs(read[0].notification.contents, toMerge[0].notification.contents);
} }
state.notifications = state.notifications.set(time, [...read, ...rest]); state.notifications = state.notifications.set(time, [...read, ...rest]);
removeNotificationFromUnread(state, index);
} }
return state; return state;
} }
@ -364,6 +382,7 @@ function archive(json: any, state: HarkState): HarkState {
} }
} else { } else {
state.unreadNotes = state.unreadNotes.filter(({ index: idx }) => !notifIdxEqual(idx, index)); state.unreadNotes = state.unreadNotes.filter(({ index: idx }) => !notifIdxEqual(idx, index));
removeNotificationFromUnread(state, index);
} }
} }
return state; return state;

View File

@ -54,7 +54,12 @@ Omnibus.args = {
{ mention: 'sipfyn-pidmex' }, { mention: 'sipfyn-pidmex' },
{ text: `: you can always use a pattern like this { text: `: you can always use a pattern like this
> blockquote demo > blockquote demo
should not be quoted` } should not be quoted
> blockquote, then newline, then mention
` },
{ mention: '~sampel-palnet' },
{ text: '> mention inside blockquote' },
{ mention: '~sampel-palnet' }
] ]
}; };

View File

@ -0,0 +1,17 @@
import React from 'react';
import { Meta } from '@storybook/react';
import { Box } from '@tlon/indigo-react';
import { PendingDm } from '~/views/apps/notifications/PendingDm';
export default {
title: 'Notifications/PendingDm',
component: PendingDm
} as Meta;
const fakeApi = {} as any;
export const Default = () => (
<Box width="95%" p="1" backgroundColor="white">
<PendingDm api={fakeApi} ship="~hastuc-dibtux" />
</Box>
);

View File

@ -1,5 +1,6 @@
import { cite, Content } from '@urbit/api'; import { cite, Content, Post } from '@urbit/api';
import React, { useCallback, useEffect } from 'react'; import React, { useCallback, useEffect } from 'react';
import _ from 'lodash';
import bigInt from 'big-integer'; import bigInt from 'big-integer';
import { Box, Row, Col, Text } from '@tlon/indigo-react'; import { Box, Row, Col, Text } from '@tlon/indigo-react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
@ -27,6 +28,27 @@ const getCurrDmSize = (ship: string) => {
return shipGraph?.children?.size ?? 0; return shipGraph?.children?.size ?? 0;
}; };
function quoteReply(post: Post) {
const reply = _.reduce(
post.contents,
(acc, content) => {
if ('text' in content) {
return `${acc}${content.text}`;
} else if ('url' in content) {
return `${acc}${content.url}`;
} else if ('mention' in content) {
return `${acc}${content.mention}`;
}
return acc;
},
''
)
.split('\n')
.map(l => `> ${l}`)
.join('\n');
return `${reply}\n\n~${post.author}:`;
}
export function DmResource(props: DmResourceProps) { export function DmResource(props: DmResourceProps) {
const { ship, api } = props; const { ship, api } = props;
const dm = useDM(ship); const dm = useDM(ship);
@ -38,7 +60,12 @@ export function DmResource(props: DmResourceProps) {
const nickname = showNickname ? contact!.nickname : cite(ship) ?? ship; const nickname = showNickname ? contact!.nickname : cite(ship) ?? ship;
useEffect(() => { useEffect(() => {
api.graph.getNewest(`~${window.ship}`, 'dm-inbox', 100, `/${patpToUd(ship)}`); api.graph.getNewest(
`~${window.ship}`,
'dm-inbox',
100,
`/${patpToUd(ship)}`
);
}, [ship]); }, [ship]);
const fetchMessages = useCallback( const fetchMessages = useCallback(
@ -75,7 +102,10 @@ export function DmResource(props: DmResourceProps) {
); );
const dismissUnread = useCallback(() => { const dismissUnread = useCallback(() => {
api.hark.dismissReadCount(`/ship/~${window.ship}/dm-inbox`, `/${patp2dec(ship)}`); api.hark.dismissReadCount(
`/ship/~${window.ship}/dm-inbox`,
`/${patp2dec(ship)}`
);
}, [ship]); }, [ship]);
const onSubmit = useCallback( const onSubmit = useCallback(
@ -99,13 +129,13 @@ export function DmResource(props: DmResourceProps) {
<Row alignItems="baseline"> <Row alignItems="baseline">
<Box <Box
borderRight={1} borderRight={1}
borderRightColor='gray' borderRightColor="gray"
pr={3} pr={3}
fontSize={1} fontSize={1}
mr={3} mr={3}
my={1} my={1}
flexShrink={0} flexShrink={0}
display={['block','none']} display={['block', 'none']}
> >
<Link to={'/~landscape/messages'}> <Link to={'/~landscape/messages'}>
<Text>{'<- Back'}</Text> <Text>{'<- Back'}</Text>
@ -131,10 +161,10 @@ export function DmResource(props: DmResourceProps) {
id={ship} id={ship}
graph={dm} graph={dm}
unreadCount={unreadCount} unreadCount={unreadCount}
onReply={() => ''} onReply={quoteReply}
fetchMessages={fetchMessages} fetchMessages={fetchMessages}
dismissUnread={dismissUnread} dismissUnread={dismissUnread}
getPermalink={() => ''} getPermalink={() => undefined}
isAdmin isAdmin
onSubmit={onSubmit} onSubmit={onSubmit}
/> />

View File

@ -329,9 +329,11 @@ const MessageActions = ({ onReply, onDelete, msg, isAdmin, permalink }) => {
<MessageActionItem onClick={() => onReply(msg)}> <MessageActionItem onClick={() => onReply(msg)}>
Reply Reply
</MessageActionItem> </MessageActionItem>
<MessageActionItem onClick={doCopy}> {permalink ? (
{copyDisplay} <MessageActionItem onClick={doCopy}>
</MessageActionItem> {copyDisplay}
</MessageActionItem>
) : null }
{(isAdmin || isOwn()) ? ( {(isAdmin || isOwn()) ? (
<MessageActionItem onClick={e => onDelete(msg)} color='red'> <MessageActionItem onClick={e => onDelete(msg)} color='red'>
Delete Message Delete Message

View File

@ -25,7 +25,7 @@ type ChatWindowProps = {
dismissUnread: () => void; dismissUnread: () => void;
pendingSize?: number; pendingSize?: number;
showOurContact: boolean; showOurContact: boolean;
getPermalink: (index: BigInteger) => string; getPermalink: (index: BigInteger) => string | undefined;
isAdmin: boolean; isAdmin: boolean;
}; };
@ -208,7 +208,7 @@ class ChatWindow extends Component<
This message has been deleted. This message has been deleted.
</Text> </Text>
); );
}; }
if (!this.state.initialized) { if (!this.state.initialized) {
return ( return (
<MessagePlaceholder <MessagePlaceholder
@ -239,7 +239,7 @@ class ChatWindow extends Component<
}; };
return ( return (
// @ts-ignore // @ts-ignore virt typings
<ChatMessage <ChatMessage
key={index.toString()} key={index.toString()}
ref={ref} ref={ref}

View File

@ -1,5 +1,5 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { Box, Col, Row, Text } from '@tlon/indigo-react'; import { Box, Row, Text } from '@tlon/indigo-react';
import { StatelessAsyncAction } from '~/views/components/StatelessAsyncAction'; import { StatelessAsyncAction } from '~/views/components/StatelessAsyncAction';
import Author from '~/views/components/Author'; import Author from '~/views/components/Author';
import GlobalApi from '~/logic/api/global'; import GlobalApi from '~/logic/api/global';
@ -23,8 +23,12 @@ export function PendingDm(props: { ship: string; api: GlobalApi }) {
bg="washedGray" bg="washedGray"
borderRadius="2" borderRadius="2"
gridTemplateColumns={['1fr 24px', '1fr 200px']} gridTemplateColumns={['1fr 24px', '1fr 200px']}
gridRowGap={2}
gridTemplateRows="auto" gridTemplateRows="auto"
gridTemplateAreas="'header actions' 'main main'" gridTemplateAreas={[
'\'header header\' \'actions actions\'',
'\'header actions\' \'main main\''
]}
p={2} p={2}
m={2} m={2}
> >
@ -40,10 +44,17 @@ export function PendingDm(props: { ship: string; api: GlobalApi }) {
gridArea="actions" gridArea="actions"
justifyContent="flex-end" justifyContent="flex-end"
> >
<StatelessAsyncAction height="auto" primary p="1" onClick={onAccept}> <StatelessAsyncAction
backgroundColor="white"
height="auto"
primary
p="1"
onClick={onAccept}
>
Accept Accept
</StatelessAsyncAction> </StatelessAsyncAction>
<StatelessAsyncAction <StatelessAsyncAction
backgroundColor="white"
height="auto" height="auto"
destructive destructive
p="1" p="1"

View File

@ -97,13 +97,13 @@ export const GraphNodeContent = ({ post, mod, index, hidden, association }) => {
const { contents } = post; const { contents } = post;
const idx = index.slice(1).split('/'); const idx = index.slice(1).split('/');
const url = getNodeUrl(mod, hidden, association?.group, association?.resource, index); const url = getNodeUrl(mod, hidden, association?.group, association?.resource, index);
if (mod === 'link' && idx.length === 1) { if (mod === 'graph-validator-link' && idx.length === 1) {
const [{ text: title }] = contents; const [{ text: title }] = contents;
return ( return (
<ContentSummary to={url} icon="Links" name={title} author={post.author} /> <ContentSummary to={url} icon="Links" name={title} author={post.author} />
); );
} }
if (mod === 'publish' && idx[1] === '1') { if (mod === 'graph-validator-publish' && idx[1] === '1') {
const [{ text: title }] = contents; const [{ text: title }] = contents;
return ( return (
<ContentSummary to={url} icon="Note" name={title} author={post.author} /> <ContentSummary to={url} icon="Note" name={title} author={post.author} />
@ -251,23 +251,16 @@ export function GraphNotification(props: {
history.push(`/~landscape/messages/dm/~${authors[0]}`); history.push(`/~landscape/messages/dm/~${authors[0]}`);
return; return;
} }
if ( const first = contents[0];
!( history.push(
(index.description === 'note' || index.description === 'link') && getNodeUrl(
index.index === '/' index.mark,
groups[association?.group]?.hidden,
association?.group,
association?.resource,
first.index
) )
) { );
const first = contents[0];
history.push(
getNodeUrl(
index.mark,
groups[association?.group]?.hidden,
association?.group,
association?.resource,
first.index
)
);
}
}, [api, timebox, index, read, history.push, authors, dm]); }, [api, timebox, index, read, history.push, authors, dm]);
const authorsInHeader = const authorsInHeader =

View File

@ -156,7 +156,7 @@ const ProfileOverlay = (props: ProfileOverlayProps) => {
icon='Chat' icon='Chat'
size={16} size={16}
cursor='pointer' cursor='pointer'
onClick={() => history.push(`/~landscape/messages/dm/${ship}`)} onClick={() => history.push(`/~landscape/messages/dm/~${ship}`)}
/> />
)} )}
</Row> </Row>

View File

@ -100,9 +100,9 @@ export function Omnibox(props: OmniboxProps): ReactElement {
} }
Mousetrap.bind('escape', props.toggle); Mousetrap.bind('escape', props.toggle);
const touchstart = new Event('touchstart'); const touchstart = new Event('touchstart');
// @ts-ignore // @ts-ignore ref typings
inputRef?.current?.input?.dispatchEvent(touchstart); inputRef?.current?.input?.dispatchEvent(touchstart);
// @ts-ignore // @ts-ignore ref typings
inputRef?.current?.input?.focus(); inputRef?.current?.input?.focus();
return () => { return () => {
Mousetrap.unbind('escape'); Mousetrap.unbind('escape');
@ -150,7 +150,7 @@ export function Omnibox(props: OmniboxProps): ReactElement {
}, [query, index]); }, [query, index]);
const navigate = useCallback( const navigate = useCallback(
(app: string, link: string) => { (app: string, link: string, shift: boolean) => {
props.toggle(); props.toggle();
if ( if (
defaultApps.includes(app.toLowerCase()) || defaultApps.includes(app.toLowerCase()) ||
@ -162,6 +162,10 @@ export function Omnibox(props: OmniboxProps): ReactElement {
app === 'home' || app === 'home' ||
app === 'inbox' app === 'inbox'
) { ) {
if(shift && app === 'profile') {
// TODO: hacky, fix
link = link.replace('~profile', '~landscape/messages/dm');
}
history.push(link); history.push(link);
} else { } else {
window.location.href = link; window.location.href = link;
@ -246,13 +250,14 @@ export function Omnibox(props: OmniboxProps): ReactElement {
if (evt.key === 'Enter') { if (evt.key === 'Enter') {
evt.preventDefault(); evt.preventDefault();
if (selected.length) { if (selected.length) {
navigate(selected[0], selected[1]); navigate(selected[0], selected[1], evt.shiftKey);
} else if (Array.from(results.values()).flat().length === 0) { } else if (Array.from(results.values()).flat().length === 0) {
return; return;
} else { } else {
navigate( navigate(
Array.from(results.values()).flat()[0].app, Array.from(results.values()).flat()[0].app,
Array.from(results.values()).flat()[0].link Array.from(results.values()).flat()[0].link,
evt.shiftKey
); );
} }
} }
@ -335,7 +340,7 @@ export function Omnibox(props: OmniboxProps): ReactElement {
subtext={result.host} subtext={result.host}
link={result.link} link={result.link}
cursor={leapCursor} cursor={leapCursor}
navigate={() => navigate(result.app, result.link)} navigate={() => navigate(result.app, result.link, false)}
setSelection={() => setSelection(result.app, result.link)} setSelection={() => setSelection(result.app, result.link)}
selected={sel} selected={sel}
/> />

View File

@ -122,6 +122,7 @@ function stitchInline(a: any, b: any) {
} }
const lastParaIdx = a.children.length - 1; const lastParaIdx = a.children.length - 1;
const last = a.children[lastParaIdx]; const last = a.children[lastParaIdx];
console.log(last);
if (last?.children) { if (last?.children) {
const ros = { const ros = {
...a, ...a,

View File

@ -1,3 +1,4 @@
/* eslint-disable */
/** pulled from remark-parse /** pulled from remark-parse
* *
* critical change is that blockquotes require a newline to be continued, see * critical change is that blockquotes require a newline to be continued, see
@ -106,6 +107,7 @@ function blockquote(eat, value, silent) {
index = nextIndex + 1 index = nextIndex + 1
} }
const trailingNewline = value.charAt(nextIndex) === '\n';
index = -1 index = -1
length = indents.length length = indents.length
@ -118,7 +120,9 @@ function blockquote(eat, value, silent) {
exit = self.enterBlock() exit = self.enterBlock()
contents = self.tokenizeBlock(contents.join(lineFeed), now) contents = self.tokenizeBlock(contents.join(lineFeed), now)
console.log(values);
exit() exit()
return add({type: 'blockquote', children: contents}) const added = add({type: 'blockquote', children: contents})
return trailingNewline ? add({ type: 'paragraph', children: [] }) : added;
} }

View File

@ -6,7 +6,7 @@ function lineBreak(eat, value: string, silent) {
while(++index < length ) { while(++index < length ) {
character = value.charAt(index); character = value.charAt(index);
if(character === '\n') { if(character === '\n') {
eat(character)({ type : 'break' }); eat(character)({ type: 'paragraph', contents: [{ type: 'break' }] });
} else { } else {
return; return;
} }