chat-fe: pretty group joining link

This commit is contained in:
Liam Fitzgerald 2021-02-10 16:27:49 +10:00
parent afb0424efd
commit 303dc6b3bd
No known key found for this signature in database
GPG Key ID: D390E12C61D1CFFB
7 changed files with 150 additions and 36 deletions

View File

@ -11,6 +11,7 @@ import React, {
import { Box } from "@tlon/indigo-react";
import { useOutsideClick } from "./useOutsideClick";
import { ModalOverlay } from "~/views/components/ModalOverlay";
import {Portal} from "~/views/components/Portal";
type ModalFunc = (dismiss: () => void) => JSX.Element;
interface UseModalProps {
@ -48,7 +49,8 @@ export function useModal(props: UseModalProps): UseModalResult {
const modal = useMemo(
() =>
!inner ? null : (
!inner ? null : (
<Portal>
<ModalOverlay
ref={innerRef}
maxWidth="500px"
@ -65,6 +67,7 @@ export function useModal(props: UseModalProps): UseModalResult {
>
{inner}
</ModalOverlay>
</Portal>
),
[inner, dismiss]
);

View File

@ -111,6 +111,8 @@ export function ChatResource(props: ChatResourceProps) {
envelopes={[]}
contacts={contacts}
association={props.association}
associations={props.associations}
groups={props.groups}
group={group}
ship={owner}
station={station}

View File

@ -17,7 +17,7 @@ import {
useShowNickname,
useHovering
} from '~/logic/lib/util';
import { Group, Association, Contacts, Post } from '~/types';
import { Group, Association, Contacts, Post, Groups, Associations } from '~/types';
import TextContent from './content/text';
import CodeContent from './content/code';
import RemoteContent from '~/views/components/RemoteContent';
@ -108,7 +108,9 @@ export default class ChatMessage extends Component<ChatMessageProps> {
history,
api,
highlighted,
fontSize
fontSize,
groups,
associations
} = this.props;
const renderSigil = Boolean(
@ -145,7 +147,9 @@ export default class ChatMessage extends Component<ChatMessageProps> {
api,
scrollWindow,
highlighted,
fontSize
fontSize,
associations,
groups,
};
const unreadContainerStyle = {
@ -206,6 +210,8 @@ interface MessageProps {
style: any;
measure(element): void;
scrollWindow: HTMLDivElement;
associations: Associations;
groups: Groups;
}
export const MessageWithSigil = (props) => {
@ -214,6 +220,8 @@ export const MessageWithSigil = (props) => {
timestamp,
contacts,
association,
associations,
groups,
group,
measure,
api,
@ -355,6 +363,9 @@ export const MessageWithSigil = (props) => {
scrollWindow={scrollWindow}
fontSize={fontSize}
group={group}
api={api}
associations={associations}
groups={groups}
/>
))}
</ContentBox>
@ -375,6 +386,9 @@ export const MessageWithoutSigil = ({
msg,
measure,
group,
api,
associations,
groups,
scrollWindow
}) => {
const { hovering, bind } = useHovering();
@ -409,6 +423,9 @@ export const MessageWithoutSigil = ({
group={group}
measure={measure}
scrollWindow={scrollWindow}
groups={groups}
associations={associations}
api={api}
/>
))}
</ContentBox>
@ -419,6 +436,9 @@ export const MessageWithoutSigil = ({
export const MessageContent = ({
content,
contacts,
api,
associations,
groups,
measure,
scrollWindow,
fontSize,
@ -461,7 +481,15 @@ export const MessageContent = ({
</Box>
);
} else if ('text' in content) {
return <TextContent fontSize={fontSize} content={content} />;
return (
<TextContent
associations={associations}
groups={groups}
measure={measure}
api={api}
fontSize={fontSize}
content={content}
/>);
} else if ('mention' in content) {
return (
<Mention

View File

@ -6,8 +6,8 @@ import bigInt, { BigInteger } from 'big-integer';
import GlobalApi from "~/logic/api/global";
import { Patp, Path } from "~/types/noun";
import { Contacts } from "~/types/contact-update";
import { Association } from "~/types/metadata-update";
import { Group } from "~/types/group-update";
import { Association, Associations } from "~/types/metadata-update";
import { Group, Groups } from "~/types/group-update";
import { Envelope, IMessage } from "~/types/chat-update";
import { Graph } from "~/types";
@ -39,6 +39,8 @@ type ChatWindowProps = RouteComponentProps<{
station: any;
api: GlobalApi;
scrollTo?: number;
associations: Associations;
groups: Groups;
}
interface ChatWindowState {
@ -247,13 +249,15 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
contacts,
mailboxSize,
graph,
history
history,
groups,
associations
} = this.props;
const unreadMarkerRef = this.unreadMarkerRef;
const messageProps = { association, group, contacts, unreadMarkerRef, history, api };
const messageProps = { association, group, contacts, unreadMarkerRef, history, api, groups, associations };
const keys = graph.keys().reverse();
const unreadIndex = graph.keys()[this.props.unreadCount];

View File

@ -1,10 +1,11 @@
import React, { Component } from 'react';
import React, { Component, useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import ReactMarkdown from 'react-markdown';
import RemarkDisableTokenizers from 'remark-disable-tokenizers';
import RemarkBreaks from 'remark-breaks';
import urbitOb from 'urbit-ob';
import { Text } from '@tlon/indigo-react';
import { GroupLink } from "~/views/components/GroupLink";
const DISABLED_BLOCK_TOKENS = [
'indentedCode',
@ -73,35 +74,35 @@ const MessageMarkdown = React.memo(props => (
plugins={[RemarkBreaks]} />
));
export default function TextContent(props) {
const content = props.content;
export default class TextContent extends Component {
const group = content.text.match(
/([~][/])?(~[a-z]{3,6})(-[a-z]{6})?([/])(([a-z0-9-])+([/-])?)+/
);
const isGroupLink = ((group !== null) // matched possible chatroom
&& (group[2].length > 2) // possible ship?
&& (urbitOb.isValidPatp(group[2]) // valid patp?
&& (group[0] === content.text))) // entire message is room name?
render() {
const { props } = this;
const content = props.content;
console.log(isGroupLink);
const group = content.text.match(
/([~][/])?(~[a-z]{3,6})(-[a-z]{6})?([/])(([a-z0-9-])+([/-])?)+/
if(isGroupLink) {
const resource = `/ship/${content.text}`;
return (
<GroupLink
measure={props.measure}
resource={resource}
api={props.api}
associations={props.associations}
groups={props.groups}
/>
);
} else {
return (
<Text mx="2px" flexShrink={0} color='black' fontSize={props.fontSize ? props.fontSize : '14px'} lineHeight="tall" style={{ overflowWrap: 'break-word' }}>
<MessageMarkdown source={content.text} />
</Text>
);
if ((group !== null) // matched possible chatroom
&& (group[2].length > 2) // possible ship?
&& (urbitOb.isValidPatp(group[2]) // valid patp?
&& (group[0] === content.text))) { // entire message is room name?
return (
<Text mx="2px" flexShrink={0} fontSize={props.fontSize ? props.fontSize : '14px'} color='black' lineHeight="tall">
<Link
className="bb b--black b--white-d mono"
to={'/~landscape/join/' + group.input}>
{content.text}
</Link>
</Text>
);
} else {
return (
<Text mx="2px" flexShrink={0} color='black' fontSize={props.fontSize ? props.fontSize : '14px'} lineHeight="tall" style={{ overflowWrap: 'break-word' }}>
<MessageMarkdown source={content.text} />
</Text>
);
}
}
}

View File

@ -0,0 +1,75 @@
import React, { useEffect, useState, useLayoutEffect } from "react";
import { Box, Text, Row, Button, Action } from "@tlon/indigo-react";
import GlobalApi from "~/logic/api/global";
import { Associations, Groups } from "~/types";
import { MetadataIcon } from "../landscape/components/MetadataIcon";
import {JoinGroup} from "../landscape/components/JoinGroup";
import {useModal} from "~/logic/lib/useModal";
export function GroupLink(props: {
api: GlobalApi;
resource: string;
associations: Associations;
groups: Groups;
measure: () => void;
}) {
const { resource, api, measure } = props;
const name = resource.slice(6);
const [preview, setPreview] = useState<MetadataUpdatePreview | null>(null);
const { modal, showModal } = useModal({
modal: (
<JoinGroup
groups={props.groups}
associations={props.associations}
api={api}
autojoin={name}
/>
)
});
const joined = resource in props.associations.groups;
useEffect(() => {
(async () => {
setPreview(await api.metadata.preview(resource));
})();
return () => {
setPreview(null);
};
}, [resource]);
useLayoutEffect(() => {
measure();
}, [preview]);
return (
<Box>
{modal}
<Row
width="fit-content"
flexShrink={1}
alignItems="center"
border="1"
borderColor="lightGray"
borderRadius="2"
py="2"
px="2"
>
{preview ? (
<>
<MetadataIcon height="4" width="4" metadata={preview.metadata} />
<Text ml="2" fontWeight="medium">{preview.metadata.title}</Text>
<Button disabled={joined} onClick={showModal} ml="4" primary>
{joined ? "Joined" : "Join"}
</Button>
</>
) : (
<Text mono>{name}</Text>
)}
</Row>
</Box>
);
}

View File

@ -45,6 +45,7 @@ interface JoinGroupProps {
groups: Groups;
associations: Associations;
api: GlobalApi;
autojoin?: string;
}
function Autojoin(props: { autojoin: string | null }) {