mirror of
https://github.com/ilyakooo0/urbit.git
synced 2025-01-07 07:30:23 +03:00
interface: refactored chat message component into smaller pieces
This commit is contained in:
parent
4d288f2e4e
commit
f57ad92701
28
pkg/interface/src/apps/chat/components/lib/content/code.js
Normal file
28
pkg/interface/src/apps/chat/components/lib/content/code.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
export default class CodeContent extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { props } = this;
|
||||||
|
const content = props.content;
|
||||||
|
|
||||||
|
const outputElement =
|
||||||
|
(Boolean(content.code.output) &&
|
||||||
|
content.code.output.length && content.code.output.length > 0) ?
|
||||||
|
(
|
||||||
|
<pre className={`f7 clamp-attachment pa1 mt0 mb0 b--gray4 b--gray1-d bl br bb`}>
|
||||||
|
{content.code.output[0].join('\n')}
|
||||||
|
</pre>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mv2">
|
||||||
|
<pre className={`f7 clamp-attachment pa1 mt0 mb0 bg-light-gray b--gray4 b--gray1-d ba`}>
|
||||||
|
{content.code.expression}
|
||||||
|
</pre>
|
||||||
|
{outputElement}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
64
pkg/interface/src/apps/chat/components/lib/content/text.js
Normal file
64
pkg/interface/src/apps/chat/components/lib/content/text.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import RemarkDisableTokenizers from 'remark-disable-tokenizers';
|
||||||
|
import urbitOb from 'urbit-ob';
|
||||||
|
|
||||||
|
const DISABLED_BLOCK_TOKENS = [
|
||||||
|
'indentedCode',
|
||||||
|
'blockquote',
|
||||||
|
'atxHeading',
|
||||||
|
'thematicBreak',
|
||||||
|
'list',
|
||||||
|
'setextHeading',
|
||||||
|
'html',
|
||||||
|
'definition',
|
||||||
|
'table'
|
||||||
|
];
|
||||||
|
|
||||||
|
const DISABLED_INLINE_TOKENS = [
|
||||||
|
'autoLink',
|
||||||
|
'url',
|
||||||
|
'email',
|
||||||
|
'link',
|
||||||
|
'reference'
|
||||||
|
];
|
||||||
|
|
||||||
|
const MessageMarkdown = React.memo(props => (
|
||||||
|
<ReactMarkdown
|
||||||
|
{...props}
|
||||||
|
plugins={[[
|
||||||
|
RemarkDisableTokenizers,
|
||||||
|
{ block: DISABLED_BLOCK_TOKENS, inline: DISABLED_INLINE_TOKENS }
|
||||||
|
]]} />
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
export default class TextContent extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { props } = this;
|
||||||
|
const content = props.content;
|
||||||
|
|
||||||
|
const group = content.text.match(
|
||||||
|
/([~][/])?(~[a-z]{3,6})(-[a-z]{6})?([/])(([a-z])+([/-])?)+/
|
||||||
|
);
|
||||||
|
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 (
|
||||||
|
<Link
|
||||||
|
className="bb b--black b--white-d f7 mono lh-copy v-top"
|
||||||
|
to={'/~groups/join/' + group.input}>
|
||||||
|
{content.text}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<section className="chat-md-message">
|
||||||
|
<MessageMarkdown source={content.text} />
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
pkg/interface/src/apps/chat/components/lib/content/url.js
Normal file
101
pkg/interface/src/apps/chat/components/lib/content/url.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
const IMAGE_REGEX =
|
||||||
|
/(jpg|img|png|gif|tiff|jpeg|JPG|IMG|PNG|TIFF|GIF|webp|WEBP|webm|WEBM|svg|SVG)$/;
|
||||||
|
|
||||||
|
const YOUTUBE_REGEX =
|
||||||
|
new RegExp(
|
||||||
|
String(/(?:https?:\/\/(?:[a-z]+.)?)/.source) // protocol
|
||||||
|
+ /(?:youtu\.?be(?:\.com)?\/)(?:embed\/)?/.source // short and long-links
|
||||||
|
+ /(?:(?:(?:(?:watch\?)?(?:time_continue=(?:[0-9]+))?.+v=)?([a-zA-Z0-9_-]+))(?:\?t\=(?:[0-9a-zA-Z]+))?)/.source // id
|
||||||
|
);
|
||||||
|
|
||||||
|
export default class UrlContent extends Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.state = {
|
||||||
|
unfold: false,
|
||||||
|
copied: false
|
||||||
|
};
|
||||||
|
this.unfoldEmbed = this.unfoldEmbed.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
unfoldEmbed(id) {
|
||||||
|
let unfoldState = this.state.unfold;
|
||||||
|
unfoldState = !unfoldState;
|
||||||
|
this.setState({ unfold: unfoldState });
|
||||||
|
const iframe = this.refs.iframe;
|
||||||
|
iframe.setAttribute('src', iframe.getAttribute('data-src'));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { props } = this;
|
||||||
|
const content = props.content;
|
||||||
|
const imgMatch = IMAGE_REGEX.exec(props.content.url);
|
||||||
|
const ytMatch = YOUTUBE_REGEX.exec(props.content.url);
|
||||||
|
|
||||||
|
let contents = content.url;
|
||||||
|
if (imgMatch) {
|
||||||
|
contents = (
|
||||||
|
<img
|
||||||
|
className="o-80-d"
|
||||||
|
src={content.url}
|
||||||
|
style={{
|
||||||
|
width: '50%',
|
||||||
|
maxWidth: '250px'
|
||||||
|
}}
|
||||||
|
></img>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<a className={`f7 lh-copy v-top word-break-all`}
|
||||||
|
href={content.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{contents}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
} else if (ytMatch) {
|
||||||
|
contents = (
|
||||||
|
<div className={'embed-container mb2 w-100 w-75-l w-50-xl ' +
|
||||||
|
((this.state.unfold === true)
|
||||||
|
? 'db' : 'dn')}
|
||||||
|
>
|
||||||
|
<iframe
|
||||||
|
ref="iframe"
|
||||||
|
width="560"
|
||||||
|
height="315"
|
||||||
|
data-src={`https://www.youtube.com/embed/${ytMatch[1]}`}
|
||||||
|
frameBorder="0" allow="picture-in-picture, fullscreen"
|
||||||
|
>
|
||||||
|
</iframe>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<a href={content.url}
|
||||||
|
className={`f7 lh-copy v-top bb b--white-d word-break-all`}
|
||||||
|
href={content.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer">{content.url}</a>
|
||||||
|
<a className="ml2 f7 pointer lh-copy v-top"
|
||||||
|
onClick={e => this.unfoldEmbed()}>
|
||||||
|
[embed]
|
||||||
|
</a>
|
||||||
|
{contents}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<a className={`f7 lh-copy v-top bb b--white-d b--black word-break-all`}
|
||||||
|
href={content.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{contents}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import TextContent from './content/text';
|
||||||
|
import CodeContent from './content/code';
|
||||||
|
import UrlContent from './content/url';
|
||||||
|
|
||||||
|
|
||||||
|
export default class MessageContent extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { props } = this;
|
||||||
|
|
||||||
|
const content = props.letter;
|
||||||
|
|
||||||
|
if ('code' in content) {
|
||||||
|
return <CodeContent content={content} />;
|
||||||
|
} else if ('url' in content) {
|
||||||
|
return <UrlContent content={content} />;
|
||||||
|
} else if ('me' in content) {
|
||||||
|
return (
|
||||||
|
<p className='f7 i lh-copy v-top'>
|
||||||
|
{content.me}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if ('text' in content) {
|
||||||
|
return <TextContent content={content} />;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,275 +1,127 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { OverlaySigil } from './overlay-sigil';
|
import { OverlaySigil } from './overlay-sigil';
|
||||||
|
import MessageContent from './message-content';
|
||||||
import { uxToHex, cite, writeText } from '../../../../lib/util';
|
import { uxToHex, cite, writeText } from '../../../../lib/util';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import ReactMarkdown from 'react-markdown';
|
|
||||||
import RemarkDisableTokenizers from 'remark-disable-tokenizers';
|
|
||||||
import urbitOb from 'urbit-ob';
|
|
||||||
|
|
||||||
const DISABLED_BLOCK_TOKENS = [
|
|
||||||
'indentedCode',
|
|
||||||
'blockquote',
|
|
||||||
'atxHeading',
|
|
||||||
'thematicBreak',
|
|
||||||
'list',
|
|
||||||
'setextHeading',
|
|
||||||
'html',
|
|
||||||
'definition',
|
|
||||||
'table'
|
|
||||||
];
|
|
||||||
|
|
||||||
const DISABLED_INLINE_TOKENS = [
|
|
||||||
'autoLink',
|
|
||||||
'url',
|
|
||||||
'email',
|
|
||||||
'link',
|
|
||||||
'reference'
|
|
||||||
];
|
|
||||||
|
|
||||||
const MessageMarkdown = React.memo(
|
|
||||||
props => (<ReactMarkdown
|
|
||||||
{...props}
|
|
||||||
plugins={[[RemarkDisableTokenizers, { block: DISABLED_BLOCK_TOKENS, inline: DISABLED_INLINE_TOKENS }]]}
|
|
||||||
/>));
|
|
||||||
|
|
||||||
export class Message extends Component {
|
export class Message extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.state = {
|
this.state = {
|
||||||
unfold: false,
|
|
||||||
copied: false
|
copied: false
|
||||||
};
|
};
|
||||||
this.unFoldEmbed = this.unFoldEmbed.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unFoldEmbed(id) {
|
|
||||||
let unfoldState = this.state.unfold;
|
|
||||||
unfoldState = !unfoldState;
|
|
||||||
this.setState({ unfold: unfoldState });
|
|
||||||
const iframe = this.refs.iframe;
|
|
||||||
iframe.setAttribute('src', iframe.getAttribute('data-src'));
|
|
||||||
}
|
|
||||||
|
|
||||||
renderContent() {
|
|
||||||
const { props } = this;
|
|
||||||
const letter = props.msg.letter;
|
|
||||||
|
|
||||||
if ('code' in letter) {
|
|
||||||
const outputElement =
|
|
||||||
(Boolean(letter.code.output) &&
|
|
||||||
letter.code.output.length && letter.code.output.length > 0) ?
|
|
||||||
(
|
|
||||||
<pre className="f7 clamp-attachment pa1 mt0 mb0 b--gray4 b--gray1-d bl br bb">
|
|
||||||
{letter.code.output[0].join('\n')}
|
|
||||||
</pre>
|
|
||||||
) : null;
|
|
||||||
return (
|
|
||||||
<div className="mv2">
|
|
||||||
<pre className="f7 clamp-attachment pa1 mt0 mb0 bg-light-gray b--gray4 b--gray1-d ba">
|
|
||||||
{letter.code.expression}
|
|
||||||
</pre>
|
|
||||||
{outputElement}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if ('url' in letter) {
|
|
||||||
const imgMatch =
|
|
||||||
/(jpg|img|png|gif|tiff|jpeg|JPG|IMG|PNG|TIFF|GIF|webp|WEBP|svg|SVG)$/
|
|
||||||
.exec(letter.url);
|
|
||||||
const youTubeRegex = new RegExp(String(/(?:https?:\/\/(?:[a-z]+.)?)/.source) // protocol
|
|
||||||
+ /(?:youtu\.?be(?:\.com)?\/)(?:embed\/)?/.source // short and long-links
|
|
||||||
+ /(?:(?:(?:(?:watch\?)?(?:time_continue=(?:[0-9]+))?.+v=)?([a-zA-Z0-9_-]+))(?:\?t\=(?:[0-9a-zA-Z]+))?)/.source // id
|
|
||||||
);
|
|
||||||
const ytMatch =
|
|
||||||
youTubeRegex.exec(letter.url);
|
|
||||||
let contents = letter.url;
|
|
||||||
if (imgMatch) {
|
|
||||||
contents = (
|
|
||||||
<img
|
|
||||||
className="o-80-d"
|
|
||||||
src={letter.url}
|
|
||||||
style={{
|
|
||||||
height: 'min(250px, 20vh)',
|
|
||||||
maxWidth: 'calc(100% - 36px - 1.5rem)',
|
|
||||||
objectFit: 'contain'
|
|
||||||
}}
|
|
||||||
></img>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<a className="f7 lh-copy v-top word-break-all"
|
|
||||||
href={letter.url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{contents}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
} else if (ytMatch) {
|
|
||||||
contents = (
|
|
||||||
<div className={'embed-container mb2 w-100 w-75-l w-50-xl ' +
|
|
||||||
((this.state.unfold === true)
|
|
||||||
? 'db' : 'dn')}
|
|
||||||
>
|
|
||||||
<iframe
|
|
||||||
ref="iframe"
|
|
||||||
width="560"
|
|
||||||
height="315"
|
|
||||||
data-src={`https://www.youtube.com/embed/${ytMatch[1]}`}
|
|
||||||
frameBorder="0" allow="picture-in-picture, fullscreen"
|
|
||||||
>
|
|
||||||
</iframe>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<a href={letter.url}
|
|
||||||
className="f7 lh-copy v-top bb b--white-d word-break-all"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{letter.url}
|
|
||||||
</a>
|
|
||||||
<a className="ml2 f7 pointer lh-copy v-top"
|
|
||||||
onClick={e => this.unFoldEmbed()}
|
|
||||||
>
|
|
||||||
[embed]
|
|
||||||
</a>
|
|
||||||
{contents}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<a className="f7 lh-copy v-top bb b--white-d b--black word-break-all"
|
|
||||||
href={letter.url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{contents}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if ('me' in letter) {
|
|
||||||
return (
|
|
||||||
<p className='f7 i lh-copy v-top'>
|
|
||||||
{letter.me}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const group = letter.text.match(
|
|
||||||
/([~][/])?(~[a-z]{3,6})(-[a-z]{6})?([/])(([a-z])+([/-])?)+/
|
|
||||||
);
|
|
||||||
if ((group !== null) // matched possible chatroom
|
|
||||||
&& (group[2].length > 2) // possible ship?
|
|
||||||
&& (urbitOb.isValidPatp(group[2]) // valid patp?
|
|
||||||
&& (group[0] === letter.text))) { // entire message is room name?
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
className="bb b--black b--white-d f7 mono lh-copy v-top"
|
|
||||||
to={'/~groups/join/' + group.input}
|
|
||||||
>
|
|
||||||
{letter.text}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<section className="chat-md-message">
|
|
||||||
<MessageMarkdown
|
|
||||||
source={letter.text}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { props, state } = this;
|
const { props, state } = this;
|
||||||
|
|
||||||
const pending = props.msg.pending ? ' o-40' : '';
|
const pending = props.msg.pending ? ' o-40' : '';
|
||||||
const datestamp = '~' + moment.unix(props.msg.when / 1000).format('YYYY.M.D');
|
const containerClass =
|
||||||
|
props.renderSigil ?
|
||||||
|
`w-100 f7 pl3 pt4 pr3 cf flex lh-copy ` + pending :
|
||||||
|
'w-100 pr3 cf hide-child flex' + pending;
|
||||||
|
|
||||||
|
const timestamp =
|
||||||
|
moment.unix(props.msg.when / 1000).format(
|
||||||
|
props.renderSigil ? 'hh:mm a' : 'hh:mm'
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={this.containerRef}
|
||||||
|
className={containerClass}
|
||||||
|
style={{
|
||||||
|
minHeight: 'min-content'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
props.renderSigil ? (
|
||||||
|
this.renderWithSigil(timestamp)
|
||||||
|
) : (
|
||||||
|
this.renderWithoutSigil(timestamp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderWithSigil(timestamp) {
|
||||||
|
const { props, state } = this;
|
||||||
const paddingTop = props.paddingTop ? { 'paddingTop': '6px' } : '';
|
const paddingTop = props.paddingTop ? { 'paddingTop': '6px' } : '';
|
||||||
|
const datestamp =
|
||||||
|
'~' + moment.unix(props.msg.when / 1000).format('YYYY.M.D');
|
||||||
|
|
||||||
if (props.renderSigil) {
|
const contact = props.msg.author in props.contacts
|
||||||
const timestamp = moment.unix(props.msg.when / 1000).format('hh:mm a');
|
? props.contacts[props.msg.author] : false;
|
||||||
|
let name = `~${props.msg.author}`;
|
||||||
const contact = props.msg.author in props.contacts
|
let color = '#000000';
|
||||||
? props.contacts[props.msg.author] : false;
|
let sigilClass = 'mix-blend-diff';
|
||||||
let name = `~${props.msg.author}`;
|
if (contact) {
|
||||||
let color = '#000000';
|
name = (contact.nickname.length > 0)
|
||||||
let sigilClass = 'mix-blend-diff';
|
? contact.nickname : `~${props.msg.author}`;
|
||||||
if (contact) {
|
color = `#${uxToHex(contact.color)}`;
|
||||||
name = (contact.nickname.length > 0)
|
sigilClass = '';
|
||||||
? contact.nickname : `~${props.msg.author}`;
|
|
||||||
color = `#${uxToHex(contact.color)}`;
|
|
||||||
sigilClass = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (`~${props.msg.author}` === name) {
|
|
||||||
name = cite(props.msg.author);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={this.containerRef}
|
|
||||||
className={
|
|
||||||
'w-100 f7 pl3 pt4 pr3 cf flex lh-copy ' + ' ' + pending
|
|
||||||
}
|
|
||||||
style={{
|
|
||||||
minHeight: 'min-content'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<OverlaySigil
|
|
||||||
ship={props.msg.author}
|
|
||||||
contact={contact}
|
|
||||||
color={color}
|
|
||||||
sigilClass={sigilClass}
|
|
||||||
association={props.association}
|
|
||||||
group={props.group}
|
|
||||||
className="fl pr3 v-top bg-white bg-gray0-d"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="fr clamp-message white-d"
|
|
||||||
style={{ flexGrow: 1, marginTop: -8 }}
|
|
||||||
>
|
|
||||||
<div className="hide-child" style={paddingTop}>
|
|
||||||
<p className="v-mid f9 gray2 dib mr3 c-default">
|
|
||||||
<span
|
|
||||||
className={'pointer ' + (contact.nickname || state.copied ? null : 'mono')}
|
|
||||||
onClick={() => {
|
|
||||||
writeText(props.msg.author);
|
|
||||||
this.setState({ copied: true });
|
|
||||||
setTimeout(() => {
|
|
||||||
this.setState({ copied: false });
|
|
||||||
}, 800);
|
|
||||||
}}
|
|
||||||
title={`~${props.msg.author}`}
|
|
||||||
>
|
|
||||||
{state.copied && 'Copied' || name}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<p className="v-mid mono f9 gray2 dib">{timestamp}</p>
|
|
||||||
<p className="v-mid mono f9 ml2 gray2 dib child dn-s">{datestamp}</p>
|
|
||||||
</div>
|
|
||||||
{this.renderContent()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const timestamp = moment.unix(props.msg.when / 1000).format('hh:mm');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={'w-100 pr3 cf hide-child flex' + pending}
|
|
||||||
style={{
|
|
||||||
minHeight: 'min-content'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p className="child pt2 pl2 pr1 mono f9 gray2 dib">{timestamp}</p>
|
|
||||||
<div className="fr f7 clamp-message white-d pr3 lh-copy" style={{ flexGrow: 1 }}>
|
|
||||||
{this.renderContent()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (`~${props.msg.author}` === name) {
|
||||||
|
name = cite(props.msg.author);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex w-100">
|
||||||
|
<OverlaySigil
|
||||||
|
ship={props.msg.author}
|
||||||
|
contact={contact}
|
||||||
|
color={color}
|
||||||
|
sigilClass={sigilClass}
|
||||||
|
association={props.association}
|
||||||
|
group={props.group}
|
||||||
|
className="fl pr3 v-top bg-white bg-gray0-d"
|
||||||
|
/>
|
||||||
|
<div className="fr clamp-message white-d"
|
||||||
|
style={{ flexGrow: 1, marginTop: -8 }}>
|
||||||
|
<div className="hide-child" style={paddingTop}>
|
||||||
|
<p className={`v-mid f9 gray2 dib mr3 c-default`}>
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
'pointer ' +
|
||||||
|
(contact.nickname || state.copied ? null : 'mono')
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
writeText(props.msg.author);
|
||||||
|
this.setState({ copied: true });
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setState({ copied: false });
|
||||||
|
}, 800);
|
||||||
|
}}
|
||||||
|
title={`~${props.msg.author}`}
|
||||||
|
>
|
||||||
|
{state.copied && 'Copied' || name}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className={`v-mid mono f9 gray2 dib`}>{timestamp}</p>
|
||||||
|
<p className={`v-mid mono f9 ml2 gray2 dib child dn-s`}>
|
||||||
|
{datestamp}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<MessageContent letter={props.msg.letter} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderWithoutSigil(timestamp) {
|
||||||
|
const { props } = this;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex w-100">
|
||||||
|
<p className="child pt2 pl2 pr1 mono f9 gray2 dib">{timestamp}</p>
|
||||||
|
<div className="fr f7 clamp-message white-d pr3 lh-copy"
|
||||||
|
style={{ flexGrow: 1 }}>
|
||||||
|
<MessageContent letter={props.msg.letter} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ function decodeGroup(group: Enc<Group>): Group {
|
|||||||
tags: decodeTags(group.tags),
|
tags: decodeTags(group.tags),
|
||||||
policy: decodePolicy(group.policy),
|
policy: decodePolicy(group.policy),
|
||||||
};
|
};
|
||||||
console.log(res);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user