mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-02 15:13:25 +03:00
chat-fe: pretty group joining link
This commit is contained in:
parent
afb0424efd
commit
303dc6b3bd
@ -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]
|
||||
);
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
75
pkg/interface/src/views/components/GroupLink.tsx
Normal file
75
pkg/interface/src/views/components/GroupLink.tsx
Normal 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>
|
||||
);
|
||||
}
|
@ -45,6 +45,7 @@ interface JoinGroupProps {
|
||||
groups: Groups;
|
||||
associations: Associations;
|
||||
api: GlobalApi;
|
||||
autojoin?: string;
|
||||
}
|
||||
|
||||
function Autojoin(props: { autojoin: string | null }) {
|
||||
|
Loading…
Reference in New Issue
Block a user