Merge pull request #3909 from tylershuster/links-sigil-size

Links sigil size
This commit is contained in:
matildepark 2020-11-24 23:30:36 -05:00 committed by GitHub
commit 25a5090f6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 441 additions and 240 deletions

View File

@ -3,7 +3,7 @@ import moment from "moment";
import _ from "lodash";
import { Box, Row, Text, Rule } from "@tlon/indigo-react";
import { OverlaySigil } from './overlay-sigil';
import OverlaySigil from '~/views/components/OverlaySigil';
import { uxToHex, cite, writeText } from '~/logic/lib/util';
import { Envelope, IMessage } from "~/types/chat-update";
import { Group, Association, Contacts, LocalUpdateRemoteContentPolicy } from "~/types";
@ -191,7 +191,7 @@ export class MessageWithSigil extends PureComponent<MessageProps> {
} = this.props;
const datestamp = moment.unix(msg.when / 1000).format(DATESTAMP_FORMAT);
const contact = msg.author in contacts ? contacts[msg.author] : false;
const contact = msg.author in contacts ? contacts[msg.author] : undefined;
const showNickname = !hideNicknames && contact && contact.nickname;
const name = showNickname ? contact.nickname : cite(msg.author);
const color = contact ? `#${uxToHex(contact.color)}` : this.isDark ? '#000000' :'#FFFFFF'
@ -215,16 +215,16 @@ export class MessageWithSigil extends PureComponent<MessageProps> {
contact={contact}
color={color}
sigilClass={sigilClass}
association={association}
group={group}
hideAvatars={hideAvatars}
hideNicknames={hideNicknames}
scrollWindow={scrollWindow}
history={history}
api={api}
bg="white"
className="fl pr3 v-top pt1"
/>
<Box flexGrow='1' display='block' className="clamp-message">
<Box flexGrow={1} display='block' className="clamp-message">
<Box
className="hide-child"
pt={1}
@ -245,7 +245,7 @@ export class MessageWithSigil extends PureComponent<MessageProps> {
}}
title={`~${msg.author}`}
>{name}</Text>
<Text flexShrink='0' gray mono className="v-mid">{timestamp}</Text>
<Text flexShrink={0} gray mono className="v-mid">{timestamp}</Text>
<Text gray mono ml={2} className="v-mid child dn-s">{datestamp}</Text>
</Box>
<Box fontSize={fontSize ? fontSize : '14px'}><MessageContent content={msg.letter} remoteContentPolicy={remoteContentPolicy} measure={measure} fontSize={fontSize} /></Box>
@ -279,6 +279,11 @@ export const MessageContent = ({ content, remoteContentPolicy, measure, fontSize
}}}
videoProps={{style: {
maxWidth: '18rem'
}
}}
textProps={{style: {
fontSize: 'inherit',
textDecoration: 'underline'
}}}
/>
</Text>

View File

@ -1,100 +0,0 @@
import React, { PureComponent } from 'react';
import { Sigil } from '~/logic/lib/sigil';
import {
ProfileOverlay,
OVERLAY_HEIGHT
} from './profile-overlay';
import { Box, BaseImage } from '@tlon/indigo-react';
export class OverlaySigil extends PureComponent {
constructor() {
super();
this.state = {
clicked: false,
captured: false,
topSpace: 0,
bottomSpace: 0
};
this.containerRef = React.createRef();
this.profileShow = this.profileShow.bind(this);
this.profileHide = this.profileHide.bind(this);
this.updateContainerOffset = this.updateContainerOffset.bind(this);
this.updateContainerInterval = null;
}
profileShow() {
this.updateContainerOffset();
this.setState({ profileClicked: true });
this.props.scrollWindow.addEventListener('scroll', this.updateContainerOffset);
}
profileHide() {
this.setState({ profileClicked: false });
this.props.scrollWindow.removeEventListener('scroll', this.updateContainerOffset, true);
}
updateContainerOffset() {
if (this.containerRef && this.containerRef.current) {
const container = this.containerRef.current;
const scrollWindow = this.props.scrollWindow;
const bottomSpace = scrollWindow.scrollHeight - container.offsetTop - scrollWindow.scrollTop;
const topSpace = scrollWindow.offsetHeight - bottomSpace - OVERLAY_HEIGHT;
this.setState({
topSpace,
bottomSpace
});
}
}
componentWillUnmount() {
this.props.scrollWindow?.removeEventListener('scroll', this.updateContainerOffset, true);
}
render() {
const { props, state } = this;
const { hideAvatars } = props;
const img = (props.contact && (props.contact.avatar !== null) && !hideAvatars)
? <BaseImage display='inline-block' src={props.contact.avatar} height={16} width={16} />
: <Sigil
ship={props.ship}
size={16}
color={props.color}
classes={props.sigilClass}
icon
padded
/>;
return (
<Box
cursor='pointer'
position='relative'
onClick={this.profileShow}
className={props.className}
ref={this.containerRef}
>
{state.profileClicked && (
<ProfileOverlay
ship={props.ship}
contact={props.contact}
color={props.color}
topSpace={state.topSpace}
bottomSpace={state.bottomSpace}
association={props.association}
group={props.group}
onDismiss={this.profileHide}
hideAvatars={hideAvatars}
hideNicknames={props.hideNicknames}
history={props.history}
api={props.api}
/>
)}
{img}
</Box>
);
}
}

View File

@ -1,15 +1,14 @@
import React, { useEffect } from "react";
import { Box, Row, Col, Center, LoadingSpinner } from "@tlon/indigo-react";
import { Box, Row, Col, Center, LoadingSpinner, Text } from "@tlon/indigo-react";
import { Switch, Route, Link } from "react-router-dom";
import bigInt from 'big-integer';
import GlobalApi from "~/logic/api/global";
import { StoreState } from "~/logic/store/type";
import { uxToHex } from '~/logic/lib/util';
import { Association, GraphNode } from "~/types";
import { RouteComponentProps } from "react-router-dom";
import { LinkItem } from "./components/link-item";
import { LinkItem } from "./components/LinkItem";
import { LinkSubmit } from "./components/link-submit";
import { LinkPreview } from "./components/link-preview";
import { Comments } from "~/views/components/comments";
@ -77,16 +76,18 @@ export function LinkResource(props: LinkResourceProps) {
const contact = contactDetails[node.post.author];
return (
<LinkItem
contacts={contacts}
key={date.toString()}
resource={resourcePath}
node={node}
nickname={contact?.nickname}
hideAvatars={hideAvatars}
hideNicknames={hideNicknames}
remoteContentPolicy={remoteContentPolicy}
baseUrl={resourceUrl}
color={uxToHex(contact?.color || '0x0')}
group={group}
path={resource["group-path"]}
api={api}
mb={3}
/>
);
})}
@ -113,15 +114,21 @@ export function LinkResource(props: LinkResourceProps) {
const contact = contactDetails[node.post.author];
return (
<Col width="100%" p={3} maxWidth="640px">
<Link to={resourceUrl}>{"<- Back"}</Link>
<LinkPreview
resourcePath={resourcePath}
post={node.post}
nickname={contact?.nickname}
<Col width="100%" p={3} maxWidth="768px">
<Link to={resourceUrl}><Text bold>{"<- Back"}</Text></Link>
<LinkItem
contacts={contacts}
key={node.post.index}
resource={resourcePath}
node={node}
hideAvatars={hideAvatars}
hideNicknames={hideNicknames}
commentNumber={node.children.size}
remoteContentPolicy={remoteContentPolicy}
baseUrl={resourceUrl}
group={group}
path={resource["group-path"]}
api={api}
mt={3}
/>
<Comments
ship={ship}
@ -136,6 +143,8 @@ export function LinkResource(props: LinkResourceProps) {
editCommentId={editCommentId}
history={props.history}
baseUrl={`${resourceUrl}/${props.match.params.index}`}
association={association}
group={group}
/>
</Col>
);

View File

@ -0,0 +1,164 @@
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { Row, Col, Anchor, Box, Text, BaseImage, Icon, Action } from '@tlon/indigo-react';
import { Sigil } from '~/logic/lib/sigil';
import { writeText } from '~/logic/lib/util';
import Author from '~/views/components/Author';
import { roleForShip } from '~/logic/lib/group';
import { Contacts, GraphNode, Group, LocalUpdateRemoteContentPolicy, Rolodex } from '~/types';
import GlobalApi from '~/logic/api/global';
import { Dropdown } from '~/views/components/Dropdown';
import RemoteContent from '~/views/components/RemoteContent';
interface LinkItemProps {
node: GraphNode;
resource: string;
hideAvatars: boolean;
hideNicknames: boolean;
remoteContentPolicy: LocalUpdateRemoteContentPolicy;
api: GlobalApi;
group: Group;
path: string;
contacts: Rolodex[];
}
export const LinkItem = (props: LinkItemProps) => {
const {
node,
resource,
hideAvatars,
hideNicknames,
remoteContentPolicy,
api,
group,
path,
contacts,
...rest
} = props;
const URLparser = new RegExp(
/((?:([\w\d\.-]+)\:\/\/?){1}(?:(www)\.?){0,1}(((?:[\w\d-]+\.)*)([\w\d-]+\.[\w\d]+))){1}(?:\:(\d+)){0,1}((\/(?:(?:[^\/\s\?]+\/)*))(?:([^\?\/\s#]+?(?:.[^\?\s]+){0,1}){0,1}(?:\?([^\s#]+)){0,1})){0,1}(?:#([^#\s]+)){0,1}/
);
const author = node.post.author;
const index = node.post.index.split('/')[1];
const size = node.children ? node.children.size : 0;
const contents = node.post.contents;
const hostname = URLparser.exec(contents[1].url) ? URLparser.exec(contents[1].url)[4] : null;
const baseUrl = props.baseUrl || `/~404/${resource}`;
const ourRole = group ? roleForShip(group, window.ship) : undefined;
const [ship, name] = resource.split('/');
const [locationText, setLocationText] = useState('Copy Link Location');
const copyLocation = () => {
setLocationText('Copied');
writeText(contents[1].url);
setTimeout(() => {
setLocationText('Copy Link Location');
}, 2000);
};
const deleteLink = () => {
if (confirm('Are you sure you want to delete this link?')) {
api.graph.removeNodes(`~${ship}`, name, [node.post.index]);
}
};
return (
<Box width="100%" {...rest}>
<Box
lineHeight="tall"
display='flex'
flexDirection='column'
width="100%"
color='washedGray'
border={1}
borderRadius={2}
alignItems="flex-start"
overflow="hidden"
>
<RemoteContent
url={contents[1].url}
text={contents[0].text}
remoteContentPolicy={remoteContentPolicy}
unfold={true}
style={{ alignSelf: 'center' }}
oembedProps={{
p: 2,
className: 'links embed-container',
}}
imageProps={{
marginLeft: 'auto',
marginRight: 'auto',
display: 'block'
}}
textProps={{
overflow: 'hidden',
color: 'black',
display: 'block',
alignSelf: 'center',
style: { textOverflow: 'ellipsis', whiteSpace: 'pre', width: '100%' },
p: 2
}} />
<Text color="gray" p={2} flexShrink={0}>
<Anchor target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'none' }} href={contents[1].url}>
<Box display='flex'>
<Icon icon='ArrowExternal' mr={1} />{hostname}
</Box>
</Anchor>
</Text>
</Box>
<Row minWidth='0' flexShrink={0} width="100%" justifyContent="space-between" py={3} bg="white">
<Author
showImage
contacts={contacts[path]}
ship={author}
date={node.post['time-sent']}
hideAvatars={hideAvatars}
hideNicknames={hideNicknames}
remoteContentPolicy={remoteContentPolicy}
group={group}
api={api}
></Author>
<Box ml="auto" mr={1}>
<Link to={`${baseUrl}/${index}`}>
<Box display='flex'>
<Icon color='blue' icon='Chat' />
<Text color='blue' ml={1}>{node.children.size}</Text>
</Box>
</Link>
</Box>
<Dropdown
width="200px"
alignX="right"
alignY="top"
options={
<Col backgroundColor="white" border={1} borderRadius={1} borderColor="lightGray">
<Row alignItems="center" p={1}>
<Action bg="white" m={1} color="black" onClick={copyLocation}>{locationText}</Action>
</Row>
{(ourRole === 'admin' || node.post.author === window.ship) &&
<Row alignItems="center" p={1}>
<Action bg="white" m={1} color="red" destructive onClick={deleteLink}>Delete Link</Action>
</Row>
}
</Col>
}
>
<Icon display="block" icon="Ellipsis" color="gray" />
</Dropdown>
</Row>
</Box>);
};

View File

@ -1,76 +0,0 @@
import React from 'react';
import { Row, Col, Anchor, Box, Text, BaseImage } from '@tlon/indigo-react';
import { Sigil } from '~/logic/lib/sigil';
import { Link } from 'react-router-dom';
import { cite } from '~/logic/lib/util';
import { roleForShip } from '~/logic/lib/group';
export const LinkItem = (props) => {
const {
node,
nickname,
avatar,
resource,
hideAvatars,
hideNicknames,
api,
group
} = props;
const URLparser = new RegExp(
/((?:([\w\d\.-]+)\:\/\/?){1}(?:(www)\.?){0,1}(((?:[\w\d-]+\.)*)([\w\d-]+\.[\w\d]+))){1}(?:\:(\d+)){0,1}((\/(?:(?:[^\/\s\?]+\/)*))(?:([^\?\/\s#]+?(?:.[^\?\s]+){0,1}){0,1}(?:\?([^\s#]+)){0,1})){0,1}(?:#([^#\s]+)){0,1}/
);
const author = node.post.author;
const index = node.post.index.split('/')[1];
const size = node.children ? node.children.size : 0;
const contents = node.post.contents;
const hostname = URLparser.exec(contents[1].url) ? URLparser.exec(contents[1].url)[4] : null;
const showAvatar = avatar && !hideAvatars;
const showNickname = nickname && !hideNicknames;
const img = showAvatar
? <BaseImage display='inline-block' src={props.avatar} height={36} width={36} />
: <Sigil ship={`~${author}`} size={36} color={'#' + props.color} />;
const baseUrl = props.baseUrl || `/~404/${resource}`;
const ourRole = group ? roleForShip(group, window.ship) : undefined;
const [ship, name] = resource.split('/');
return (
<Row minWidth='0' flexShrink='0' width="100%" alignItems="center" py={3} bg="white">
{img}
<Col minWidth='0' height="100%" width='100%' justifyContent="space-between" ml={2}>
<Anchor
lineHeight="tall"
display='flex'
style={{ textDecoration: 'none' }}
href={contents[1].url}
width="100%"
target="_blank"
rel="noopener noreferrer"
>
<Text display='inline-block' overflow='hidden' style={{ textOverflow: 'ellipsis', whiteSpace: 'pre' }}>{contents[0].text}</Text>
<Text ml="2" color="gray" display='inline-block' flexShrink='0'>{hostname} </Text>
</Anchor>
<Box width="100%">
<Text
fontFamily={showNickname ? 'sans' : 'mono'} pr={2}
>
{showNickname ? nickname : cite(author) }
</Text>
<Link to={`${baseUrl}/${index}`}>
<Text color="gray">{size} comments</Text>
</Link>
{(ourRole === 'admin' || node.post.author === window.ship)
&& (<Text color='red' ml='2' cursor='pointer' onClick={() => api.graph.removeNodes(`~${ship}`, name, [node.post.index])}>Delete</Text>)}
</Box>
</Col>
</Row>
);
};

View File

@ -9,7 +9,7 @@ import { Comments } from "~/views/components/Comments";
import { NoteNavigation } from "./NoteNavigation";
import GlobalApi from "~/logic/api/global";
import { getLatestRevision, getComments } from '~/logic/lib/publish';
import { Author } from "./Author";
import Author from "~/views/components/Author";
import { Contacts, GraphNode, Graph, LocalUpdateRemoteContentPolicy } from "~/types";
interface NoteProps {

View File

@ -6,7 +6,7 @@ import { RouteComponentProps } from "react-router-dom";
import Note from "./Note";
import { EditPost } from "./EditPost";
import { GraphNode, Graph, Contacts, LocalUpdateRemoteContentPolicy } from "~/types";
import { GraphNode, Graph, Contacts, LocalUpdateRemoteContentPolicy, Group } from "~/types";
interface NoteRoutesProps {
ship: string;
@ -24,8 +24,6 @@ interface NoteRoutesProps {
}
export function NoteRoutes(props: NoteRoutesProps & RouteComponentProps) {
const { ship, book, noteId } = props;
const baseUrl = props.baseUrl || '/~404';
const rootUrl = props.rootUrl || '/~404';

View File

@ -86,7 +86,7 @@ export function NotebookRoutes(
path={relativePath("/note/:noteId")}
render={(routeProps) => {
const { noteId } = routeProps.match.params;
const noteIdNum = bigInt(noteId)
const noteIdNum = bigInt(noteId);
if(!graph) {
return <Center height="100%"><LoadingSpinner /></Center>;

View File

@ -1,28 +1,36 @@
import React, {ReactNode} from "react";
import moment from "moment";
import { Sigil } from "~/logic/lib/sigil"
import { uxToHex, cite } from "~/logic/lib/util";
import { Contacts } from "~/types/contact-update";
import { Row, Box } from "@tlon/indigo-react";
import { Sigil } from "~/logic/lib/sigil"
import { uxToHex, cite } from "~/logic/lib/util";
import { Contacts, Rolodex } from "~/types/contact-update";
import OverlaySigil from "./OverlaySigil";
import { Group, Association, LocalUpdateRemoteContentPolicy } from "~/types";
import GlobalApi from "~/logic/api/global";
import { useHistory } from "react-router-dom";
interface AuthorProps {
contacts: Contacts;
contacts: Rolodex;
ship: string;
date: number;
showImage?: boolean;
hideAvatars: boolean;
hideNicknames: boolean;
children?: ReactNode;
remoteContentPolicy: LocalUpdateRemoteContentPolicy;
group: Group;
api: GlobalApi;
}
export function Author(props: AuthorProps) {
const { contacts, ship = '', date, showImage } = props;
let contact = null;
export default function Author(props: AuthorProps) {
const { contacts, ship = '', date, showImage, hideAvatars, hideNicknames, remoteContentPolicy, group, api } = props;
const history = useHistory();
let contact;
if (contacts) {
contact = ship in contacts ? contacts[ship] : null;
}
const color = contact?.color ? `#${uxToHex(contact?.color)}` : "#000000";
const showAvatar = !props.hideAvatars && contact?.avatar;
const showNickname = !props.hideNicknames && contact?.nickname;
const name = showNickname ? contact?.nickname : cite(ship);
@ -31,18 +39,19 @@ export function Author(props: AuthorProps) {
<Row alignItems="center" width="auto">
{showImage && (
<Box>
{showAvatar ? (
<img src={contact?.avatar} height={16} width={16} className="dib" />
) : (
<Sigil
ship={ship}
size={16}
icon
padded
color={color}
classes={contact?.color ? '' : "mix-blend-diff"}
/>
)}
<OverlaySigil
ship={ship}
contact={contact}
color={color}
sigilClass={''}
group={group}
hideAvatars={hideAvatars}
hideNicknames={hideNicknames}
history={history}
api={api}
bg="white"
className="fl v-top pt1"
/>
</Box>
)}
<Box

View File

@ -1,13 +1,13 @@
import React from 'react';
import React, { useState } from 'react';
import { Link } from "react-router-dom";
import { Contacts } from '~/types/contact-update';
import GlobalApi from '~/logic/api/global';
import { Box, Row, Text } from '@tlon/indigo-react';
import styled from 'styled-components';
import { Author } from '~/views/apps/publish/components/Author';
import Author from '~/views/components/Author';
import { GraphNode, TextContent } from '~/types/graph-update';
import tokenizeMessage from '~/logic/lib/tokenizeMessage';
import { LocalUpdateRemoteContentPolicy } from '~/types';
import { LocalUpdateRemoteContentPolicy, Group } from '~/types';
import { MentionText } from '~/views/components/MentionText';
import { getLatestCommentRevision } from '~/logic/lib/publish';
@ -27,10 +27,11 @@ interface CommentItemProps {
hideNicknames: boolean;
hideAvatars: boolean;
remoteContentPolicy: LocalUpdateRemoteContentPolicy;
group: Group;
}
export function CommentItem(props: CommentItemProps) {
const { ship, contacts, name, api, remoteContentPolicy, comment } = props;
const { ship, contacts, name, api, remoteContentPolicy, comment, group } = props;
const [revNum, post] = getLatestCommentRevision(comment);
const disabled = props.pending || window.ship !== post?.author;
@ -52,6 +53,9 @@ export function CommentItem(props: CommentItemProps) {
date={post?.['time-sent']}
hideAvatars={props.hideAvatars}
hideNicknames={props.hideNicknames}
remoteContentPolicy={remoteContentPolicy}
group={group}
api={api}
>
{!disabled && (
<Box display="inline-block" verticalAlign="middle">

View File

@ -9,7 +9,7 @@ import { FormikHelpers } from 'formik';
import { GraphNode } from '~/types/graph-update';
import { createPost, createBlankNodeWithChildPost } from '~/logic/api/graph';
import { getLatestCommentRevision } from '~/logic/lib/publish';
import { LocalUpdateRemoteContentPolicy } from '~/types';
import { LocalUpdateRemoteContentPolicy, Group } from '~/types';
import { scanForMentions } from '~/logic/lib/graph';
interface CommentsProps {
@ -23,10 +23,11 @@ interface CommentsProps {
hideAvatars: boolean;
hideNicknames: boolean;
remoteContentPolicy: LocalUpdateRemoteContentPolicy;
group: Group;
}
export function Comments(props: CommentsProps) {
const { comments, ship, name, api, baseUrl, history} = props;
const { comments, ship, name, api, baseUrl, history, group } = props;
const onSubmit = async (
{ comment },

View File

@ -0,0 +1,141 @@
import React, { PureComponent } from 'react';
import { Sigil } from '~/logic/lib/sigil';
import { Contact, Group } from '~/types';
import {
ProfileOverlay,
OVERLAY_HEIGHT
} from './ProfileOverlay';
import { Box, BaseImage, ColProps } from '@tlon/indigo-react';
type OverlaySigilProps = ColProps & {
ship: string;
contact?: Contact;
color: string;
sigilClass: string;
group?: Group;
hideAvatars: boolean;
hideNicknames: boolean;
scrollWindow?: HTMLElement;
history: any;
api: any;
className: string;
}
interface OverlaySigilState {
clicked: boolean;
topSpace: number | 'auto';
bottomSpace: number | 'auto';
}
export default class OverlaySigil extends PureComponent<OverlaySigilProps, OverlaySigilState> {
public containerRef: React.Ref<HTMLDivElement>;
constructor(props) {
super(props);
this.state = {
clicked: false,
topSpace: 0,
bottomSpace: 0
};
this.containerRef = React.createRef();
this.profileShow = this.profileShow.bind(this);
this.profileHide = this.profileHide.bind(this);
this.updateContainerOffset = this.updateContainerOffset.bind(this);
}
profileShow() {
this.updateContainerOffset();
this.setState({ clicked: true });
this.props.scrollWindow?.addEventListener('scroll', this.updateContainerOffset);
}
profileHide() {
this.setState({ clicked: false });
this.props.scrollWindow?.removeEventListener('scroll', this.updateContainerOffset, true);
}
updateContainerOffset() {
if (this.containerRef && this.containerRef.current) {
const container = this.containerRef.current;
const scrollWindow = this.props.scrollWindow;
const bottomSpace = scrollWindow
? scrollWindow.scrollHeight - container.offsetTop - scrollWindow.scrollTop
: 'auto';
const topSpace = scrollWindow
? scrollWindow.offsetHeight - bottomSpace - OVERLAY_HEIGHT
: 0;
this.setState({
topSpace,
bottomSpace
});
}
}
componentWillUnmount() {
this.props.scrollWindow?.removeEventListener('scroll', this.updateContainerOffset, true);
}
render() {
const {
className,
ship,
contact,
color,
group,
hideAvatars,
hideNicknames,
history,
api,
sigilClass,
...rest
} = this.props;
const { state } = this;
const img = (contact && (contact.avatar !== null) && !hideAvatars)
? <BaseImage display='inline-block' src={contact.avatar} height={16} width={16} />
: <Sigil
ship={ship}
size={16}
color={color}
classes={sigilClass}
icon
padded
/>;
return (
<Box
cursor='pointer'
position='relative'
onClick={this.profileShow}
ref={this.containerRef}
className={className}
>
{state.clicked && (
<ProfileOverlay
ship={ship}
contact={contact}
color={color}
topSpace={state.topSpace}
bottomSpace={state.bottomSpace}
group={group}
onDismiss={this.profileHide}
hideAvatars={hideAvatars}
hideNicknames={hideNicknames}
history={history}
api={api}
{...rest}
/>
)}
{img}
</Box>
);
}
}

View File

@ -1,14 +1,32 @@
import React, { PureComponent } from 'react';
import { Contact, Group } from '~/types';
import { cite } from '~/logic/lib/util';
import { Sigil } from '~/logic/lib/sigil';
import { Box, Col, Button, Text, BaseImage } from '@tlon/indigo-react';
import { Box, Col, Button, Text, BaseImage, ColProps } from '@tlon/indigo-react';
export const OVERLAY_HEIGHT = 250;
export class ProfileOverlay extends PureComponent {
constructor() {
super();
type ProfileOverlayProps = ColProps & {
ship: string;
contact?: Contact;
color: string;
topSpace: number | 'auto';
bottomSpace: number | 'auto';
group?: Group;
onDismiss(): void;
hideAvatars: boolean;
hideNicknames: boolean;
history: any;
api: any;
}
export class ProfileOverlay extends PureComponent<ProfileOverlayProps, {}> {
public popoverRef: React.Ref<typeof Col>;
constructor(props) {
super(props);
this.popoverRef = React.createRef();
this.onDocumentClick = this.onDocumentClick.bind(this);
@ -35,7 +53,19 @@ export class ProfileOverlay extends PureComponent {
}
render() {
const { contact, ship, color, topSpace, bottomSpace, group, hideNicknames, hideAvatars, history } = this.props;
const {
contact,
ship,
color,
topSpace,
bottomSpace,
group = false,
hideNicknames,
hideAvatars,
history,
onDismiss,
...rest
} = this.props;
let top, bottom;
if (topSpace < OVERLAY_HEIGHT / 2) {
@ -66,7 +96,7 @@ export class ProfileOverlay extends PureComponent {
/* if (!group.hidden) {
}*/
const isHidden = group.hidden;
const isHidden = group ? group.hidden : false;
return (
<Col
@ -77,6 +107,7 @@ export class ProfileOverlay extends PureComponent {
zIndex='3'
fontSize='0'
style={containerStyle}
{...rest}
>
<Box height='160px' width='160px'>
{img}

View File

@ -1,12 +1,13 @@
import React, { PureComponent, Fragment } from 'react';
import { LocalUpdateRemoteContentPolicy } from "~/types/local-update";
import { BaseAnchor, BaseImage, Box, Button } from '@tlon/indigo-react';
import { BaseAnchor, BaseImage, Box, Button, Text } from '@tlon/indigo-react';
import { hasProvider } from 'oembed-parser';
import EmbedContainer from 'react-oembed-container';
import { memoize } from 'lodash';
interface RemoteContentProps {
url: string;
text?: string;
remoteContentPolicy: LocalUpdateRemoteContentPolicy;
unfold?: boolean;
renderUrl?: boolean;
@ -14,6 +15,7 @@ interface RemoteContentProps {
audioProps?: any;
videoProps?: any;
oembedProps?: any;
textProps?: any;
style?: any;
onLoad?(): void;
}
@ -27,8 +29,6 @@ const IMAGE_REGEX = new RegExp(/(jpg|img|png|gif|tiff|jpeg|webp|webm|svg)$/i);
const AUDIO_REGEX = new RegExp(/(mp3|wav|ogg)$/i);
const VIDEO_REGEX = new RegExp(/(mov|mp4|ogv)$/i);
const memoizedFetch = memoize(fetch);
export default class RemoteContent extends PureComponent<RemoteContentProps, RemoteContentState> {
private fetchController: AbortController | undefined;
constructor(props) {
@ -48,7 +48,8 @@ export default class RemoteContent extends PureComponent<RemoteContentProps, Rem
}
}
unfoldEmbed() {
unfoldEmbed(event: Event) {
event.stopPropagation();
let unfoldState = this.state.unfold;
unfoldState = !unfoldState;
this.setState({ unfold: unfoldState });
@ -57,7 +58,7 @@ export default class RemoteContent extends PureComponent<RemoteContentProps, Rem
loadOembed() {
this.fetchController = new AbortController();
memoizedFetch(`https://noembed.com/embed?url=${this.props.url}`, {
fetch(`https://noembed.com/embed?url=${this.props.url}`, {
signal: this.fetchController.signal
})
.then(response => response.clone().json())
@ -70,11 +71,13 @@ export default class RemoteContent extends PureComponent<RemoteContentProps, Rem
}
wrapInLink(contents) {
const { style } = this.props;
return (<BaseAnchor
href={this.props.url}
style={{ color: 'inherit', textDecoration: 'none' }}
style={{ color: 'inherit', textDecoration: 'none', ...style }}
className={`word-break-all ${(typeof contents === 'string') ? 'bb' : ''}`}
target="_blank"
width="100%"
rel="noopener noreferrer"
>
{contents}
@ -85,12 +88,14 @@ export default class RemoteContent extends PureComponent<RemoteContentProps, Rem
const {
remoteContentPolicy,
url,
text,
unfold = false,
renderUrl = true,
imageProps = {},
audioProps = {},
videoProps = {},
oembedProps = {},
textProps = {},
style = {},
onLoad = () => {},
...props
@ -113,7 +118,9 @@ export default class RemoteContent extends PureComponent<RemoteContentProps, Rem
} else if (isAudio && remoteContentPolicy.audioShown) {
return (
<>
{renderUrl ? this.wrapInLink(url) : null}
{renderUrl
? this.wrapInLink(<Text {...textProps}>{text || url}</Text>)
: null}
<audio
controls
className="db"
@ -127,7 +134,9 @@ export default class RemoteContent extends PureComponent<RemoteContentProps, Rem
} else if (isVideo && remoteContentPolicy.videoShown) {
return (
<>
{renderUrl ? this.wrapInLink(url) : null}
{renderUrl
? this.wrapInLink(<Text {...textProps}>{text || url}</Text>)
: null}
<video
controls
className="db"
@ -146,7 +155,11 @@ export default class RemoteContent extends PureComponent<RemoteContentProps, Rem
return (
<Fragment>
{renderUrl ? this.wrapInLink(this.state.embed && this.state.embed.title ? this.state.embed.title : url) : null}
{renderUrl
? this.wrapInLink(<Text {...textProps}>{(this.state.embed && this.state.embed.title)
? this.state.embed.title
: (text || url)}</Text>)
: null}
{this.state.embed !== 'error' && this.state.embed?.html && !unfold ? <Button
display='inline-flex'
border={1}
@ -176,7 +189,9 @@ export default class RemoteContent extends PureComponent<RemoteContentProps, Rem
</Fragment>
);
} else {
return renderUrl ? this.wrapInLink(url) : null;
return renderUrl
? this.wrapInLink(<Text {...textProps}>{text || url}</Text>)
: null;
}
}
}