mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-13 20:04:35 +03:00
parent
0934c52e83
commit
e9c129b1e3
@ -1,17 +1,16 @@
|
||||
import React, { useRef, useCallback } from "react";
|
||||
import { RouteComponentProps } from "react-router-dom";
|
||||
import { Col } from "@tlon/indigo-react";
|
||||
import React, { useRef, useCallback } from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
import { Col } from '@tlon/indigo-react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { Association } from "~/types/metadata-update";
|
||||
import { StoreState } from "~/logic/store/type";
|
||||
import { useFileDrag } from "~/logic/lib/useDrag";
|
||||
import ChatWindow from "./components/ChatWindow";
|
||||
import ChatInput from "./components/ChatInput";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import { deSig } from "~/logic/lib/util";
|
||||
import { SubmitDragger } from "~/views/components/s3-upload";
|
||||
import { useLocalStorageState } from "~/logic/lib/useLocalStorageState";
|
||||
import { Association } from '~/types/metadata-update';
|
||||
import { StoreState } from '~/logic/store/type';
|
||||
import { useFileDrag } from '~/logic/lib/useDrag';
|
||||
import ChatWindow from './components/ChatWindow';
|
||||
import ChatInput from './components/ChatInput';
|
||||
import GlobalApi from '~/logic/api/global';
|
||||
import { SubmitDragger } from '~/views/components/s3-upload';
|
||||
import { useLocalStorageState } from '~/logic/lib/useLocalStorageState';
|
||||
|
||||
type ChatResourceProps = StoreState & {
|
||||
association: Association;
|
||||
@ -20,22 +19,22 @@ type ChatResourceProps = StoreState & {
|
||||
} & RouteComponentProps;
|
||||
|
||||
export function ChatResource(props: ChatResourceProps) {
|
||||
const station = props.association["app-path"];
|
||||
const station = props.association['app-path'];
|
||||
if (!props.chatInitialized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { envelopes, config } = (props.inbox?.[station]) ? props.inbox[station] : {envelopes: [], config: {}};
|
||||
const { envelopes, config } = (props.inbox?.[station]) ? props.inbox[station] : { envelopes: [], config: {} };
|
||||
const { read, length } = (config) ? config : undefined;
|
||||
|
||||
const groupPath = props.association["group-path"];
|
||||
const groupPath = props.association['group-path'];
|
||||
const group = props.groups[groupPath];
|
||||
const contacts = props.contacts[groupPath] || {};
|
||||
|
||||
const pendingMessages = (props.pendingMessages.get(station) || []).map(
|
||||
(value) => ({
|
||||
value => ({
|
||||
...value,
|
||||
pending: true,
|
||||
pending: true
|
||||
})
|
||||
);
|
||||
|
||||
@ -62,7 +61,7 @@ export function ChatResource(props: ChatResourceProps) {
|
||||
const unreadCount = length - read;
|
||||
const unreadMsg = unreadCount > 0 && envelopes[unreadCount - 1];
|
||||
|
||||
const [, owner, name] = station.split("/");
|
||||
const [, owner, name] = station.split('/');
|
||||
const ourContact = contacts?.[window.ship];
|
||||
const lastMsgNum = envelopes.length || 0;
|
||||
|
||||
@ -81,17 +80,17 @@ export function ChatResource(props: ChatResourceProps) {
|
||||
const { bind, dragging } = useFileDrag(onFileDrag);
|
||||
|
||||
const [unsent, setUnsent] = useLocalStorageState<Record<string, string>>(
|
||||
"chat-unsent",
|
||||
'chat-unsent',
|
||||
{}
|
||||
);
|
||||
|
||||
const appendUnsent = useCallback(
|
||||
(u: string) => setUnsent((s) => ({ ...s, [station]: u })),
|
||||
(u: string) => setUnsent(s => ({ ...s, [station]: u })),
|
||||
[station]
|
||||
);
|
||||
|
||||
const clearUnsent = useCallback(() => setUnsent((s) => _.omit(s, station)), [
|
||||
station,
|
||||
const clearUnsent = useCallback(() => setUnsent(s => _.omit(s, station)), [
|
||||
station
|
||||
]);
|
||||
|
||||
return (
|
||||
@ -131,7 +130,7 @@ export function ChatResource(props: ChatResourceProps) {
|
||||
s3={props.s3}
|
||||
hideAvatars={props.hideAvatars}
|
||||
placeholder="Message..."
|
||||
message={unsent[station] || ""}
|
||||
message={unsent[station] || ''}
|
||||
deleteMessage={clearUnsent}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -1,13 +1,13 @@
|
||||
import React, { Component } from 'react';
|
||||
import ChatEditor from './chat-editor';
|
||||
import { S3Upload, SubmitDragger } from '~/views/components/s3-upload' ;
|
||||
import { S3Upload } from '~/views/components/s3-upload' ;
|
||||
import { uxToHex } from '~/logic/lib/util';
|
||||
import { Sigil } from '~/logic/lib/sigil';
|
||||
import tokenizeMessage, { isUrl } from '~/logic/lib/tokenizeMessage';
|
||||
import GlobalApi from '~/logic/api/global';
|
||||
import { Envelope } from '~/types/chat-update';
|
||||
import { Contacts, S3Configuration } from '~/types';
|
||||
import { Row } from '@tlon/indigo-react';
|
||||
import { Contacts } from '~/types';
|
||||
import { Row, BaseImage, Box, Icon } from '@tlon/indigo-react';
|
||||
|
||||
interface ChatInputProps {
|
||||
api: GlobalApi;
|
||||
@ -31,7 +31,6 @@ interface ChatInputState {
|
||||
uploadingPaste: boolean;
|
||||
}
|
||||
|
||||
|
||||
export default class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
||||
public s3Uploader: React.RefObject<S3Upload>;
|
||||
private chatEditor: React.RefObject<ChatEditor>;
|
||||
@ -42,7 +41,7 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
this.state = {
|
||||
inCodeMode: false,
|
||||
submitFocus: false,
|
||||
uploadingPaste: false,
|
||||
uploadingPaste: false
|
||||
};
|
||||
|
||||
this.s3Uploader = React.createRef();
|
||||
@ -50,7 +49,6 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
|
||||
this.submit = this.submit.bind(this);
|
||||
this.toggleCode = this.toggleCode.bind(this);
|
||||
|
||||
}
|
||||
|
||||
toggleCode() {
|
||||
@ -82,8 +80,6 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
submit(text) {
|
||||
const { props, state } = this;
|
||||
if (state.inCodeMode) {
|
||||
@ -134,7 +130,6 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
{ url }
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uploadError(error) {
|
||||
@ -159,10 +154,11 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
if (!this.readyToUpload()) {
|
||||
return;
|
||||
}
|
||||
if (!this.s3Uploader.current || !this.s3Uploader.current.inputRef.current) return;
|
||||
if (!this.s3Uploader.current || !this.s3Uploader.current.inputRef.current)
|
||||
return;
|
||||
this.s3Uploader.current.inputRef.current.files = files;
|
||||
const fire = document.createEvent("HTMLEvents");
|
||||
fire.initEvent("change", true, true);
|
||||
const fire = document.createEvent('HTMLEvents');
|
||||
fire.initEvent('change', true, true);
|
||||
this.s3Uploader.current?.inputRef.current?.dispatchEvent(fire);
|
||||
}
|
||||
|
||||
@ -179,7 +175,7 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
props.ourContact &&
|
||||
((props.ourContact.avatar !== null) && !props.hideAvatars)
|
||||
)
|
||||
? <img src={props.ourContact.avatar} height={16} width={16} className="dib" />
|
||||
? <BaseImage src={props.ourContact.avatar} height={16} width={16} className="dib" />
|
||||
: <Sigil
|
||||
ship={window.ship}
|
||||
size={16}
|
||||
@ -201,9 +197,9 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
className='cf'
|
||||
zIndex='0'
|
||||
>
|
||||
<div className="pa2 flex items-center">
|
||||
<Row p='2' alignItems='center'>
|
||||
{avatar}
|
||||
</div>
|
||||
</Row>
|
||||
<ChatEditor
|
||||
ref={this.chatEditor}
|
||||
inCodeMode={state.inCodeMode}
|
||||
@ -213,12 +209,13 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
onPaste={this.onPaste.bind(this)}
|
||||
placeholder='Message...'
|
||||
/>
|
||||
<div className="ml2 mr2 flex-shrink-0"
|
||||
style={{
|
||||
height: '16px',
|
||||
width: '16px',
|
||||
flexBasis: 16,
|
||||
}}>
|
||||
<Box
|
||||
mx='2'
|
||||
flexShrink='0'
|
||||
height='16px'
|
||||
width='16px'
|
||||
flexBasis='16px'
|
||||
>
|
||||
<S3Upload
|
||||
ref={this.s3Uploader}
|
||||
configuration={props.s3.configuration}
|
||||
@ -227,28 +224,25 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
uploadError={this.uploadError.bind(this)}
|
||||
accept="*"
|
||||
>
|
||||
<img
|
||||
className="invert-d"
|
||||
src="/~landscape/img/ImageUpload.png"
|
||||
<Icon icon='Links'
|
||||
width="16"
|
||||
height="16"
|
||||
/>
|
||||
</S3Upload>
|
||||
</div>
|
||||
<div className="mr2 flex-shrink-0" style={{
|
||||
height: '16px',
|
||||
width: '16px',
|
||||
flexBasis: 16,
|
||||
}}>
|
||||
<img style={{
|
||||
filter: state.inCodeMode ? 'invert(100%)' : '',
|
||||
height: '14px',
|
||||
width: '14px',
|
||||
}}
|
||||
</Box>
|
||||
<Box
|
||||
mr='2'
|
||||
flexShrink='0'
|
||||
height='16px'
|
||||
width='16px'
|
||||
flexBasis='16px'
|
||||
>
|
||||
<Icon
|
||||
icon='Dojo'
|
||||
onClick={this.toggleCode}
|
||||
src="/~landscape/img/CodeEval.png"
|
||||
className="contrast-10-d bg-white bg-none-d ba b--gray1-d br1" />
|
||||
</div>
|
||||
color={state.inCodeMode ? 'blue' : 'black'}
|
||||
/>
|
||||
</Box>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
@ -289,27 +289,89 @@ export const MessageContent = ({ content, remoteContentPolicy, measure }) => {
|
||||
};
|
||||
|
||||
export const MessagePlaceholder = ({ height, index, className = '', style = {}, ...props }) => (
|
||||
<div className={`w-100 f7 pl3 pt4 pr3 cf flex lh-copy ${className}`} style={{ height, ...style }} {...props}>
|
||||
<div className="fl pr3 v-top bg-white bg-gray0-d">
|
||||
<span
|
||||
className="db bg-gray2 bg-white-d"
|
||||
<Box
|
||||
width='100%'
|
||||
fontSize='2'
|
||||
pl='3' pt='4'
|
||||
pr='3'
|
||||
display='flex'
|
||||
lineHeight='tall'
|
||||
className={className}
|
||||
style={{ height, ...style }}
|
||||
{...props}
|
||||
>
|
||||
<Box pr='3' verticalAlign='top' backgroundColor='white' style={{ float: 'left' }}>
|
||||
<Text
|
||||
display='block'
|
||||
background='gray'
|
||||
width='24px'
|
||||
height='24px'
|
||||
borderRadius='50%'
|
||||
style={{
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
borderRadius: "50%",
|
||||
visibility: (index % 5 == 0) ? "initial" : "hidden",
|
||||
}}
|
||||
></span>
|
||||
</div>
|
||||
<div className="fr clamp-message white-d" style={{ flexGrow: 1, marginTop: -8 }}>
|
||||
<div className="hide-child" style={{paddingTop: "6px", visibility: (index % 5 == 0) ? "initial" : "hidden" }}>
|
||||
<p className={`v-mid f9 gray2 dib mr3 c-default`}>
|
||||
<span className="mw5 db"><span className="bg-gray5 bg-gray1-d db w-100 h-100"></span></span>
|
||||
</p>
|
||||
<p className="v-mid mono f9 gray2 dib"><span className="bg-gray5 bg-gray1-d db w-100 h-100" style={{height: "1em", width: `${(index % 3 + 1) * 3}em`}}></span></p>
|
||||
<p className="v-mid mono f9 ml2 gray2 dib child dn-s"><span className="bg-gray5 bg-gray1-d db w-100 h-100"></span></p>
|
||||
</div>
|
||||
<span className="bg-gray5 bg-gray1-d db w-100 h-100 db" style={{height: `1em`, width: `${(index % 5) * 20}%`}}></span>
|
||||
</div>
|
||||
</div>
|
||||
></Text>
|
||||
</Box>
|
||||
<Box
|
||||
style={{ float: 'right', flexGrow: 1 }}
|
||||
color='black'
|
||||
className="clamp-message"
|
||||
>
|
||||
<Box
|
||||
className="hide-child"
|
||||
paddingTop='4'
|
||||
style={{visibility: (index % 5 == 0) ? "initial" : "hidden" }}
|
||||
>
|
||||
<Text
|
||||
display='inline-block'
|
||||
verticalAlign='middle'
|
||||
fontSize='0'
|
||||
gray
|
||||
cursor='default'
|
||||
>
|
||||
<Text maxWidth='32rem' display='block'>
|
||||
<Text
|
||||
backgroundColor='gray'
|
||||
display='block'
|
||||
width='100%'
|
||||
height='100%'></Text>
|
||||
</Text>
|
||||
</Text>
|
||||
<Text
|
||||
display='inline-block'
|
||||
mono
|
||||
verticalAlign='middle'
|
||||
fontSize='0'
|
||||
gray
|
||||
>
|
||||
<Text
|
||||
background='gray'
|
||||
display='block'
|
||||
height='1em'
|
||||
style={{ width: `${(index % 3 + 1) * 3}em` }}
|
||||
></Text>
|
||||
</Text>
|
||||
<Text
|
||||
mono
|
||||
verticalAlign='middle'
|
||||
fontSize='0'
|
||||
ml='2'
|
||||
gray
|
||||
display={['none', 'inline-block']}
|
||||
className="child">
|
||||
<Text
|
||||
backgroundColor='gray'
|
||||
display='block'
|
||||
width='100%'
|
||||
height='100%'
|
||||
></Text>
|
||||
</Text>
|
||||
</Box>
|
||||
<Text
|
||||
display='block'
|
||||
backgroundColor='gray'
|
||||
height='1em'
|
||||
style={{ width: `${(index % 5) * 20}%` }}></Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
@ -1,22 +1,34 @@
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { Box, LoadingSpinner, Text } from '@tlon/indigo-react';
|
||||
|
||||
export const BacklogElement = (props) => {
|
||||
if (!props.isChatLoading) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="center mw6 absolute z-9999" style={{ left: 0, right: 0, top: 48}}>
|
||||
<div className={
|
||||
"db pa3 ma3 ba b--gray4 bg-gray5 b--gray2-d bg-gray1-d " +
|
||||
"white-d flex items-center"
|
||||
}>
|
||||
<img className="invert-d spin-active v-mid"
|
||||
src="/~landscape/img/Spinner.png"
|
||||
width={16}
|
||||
height={16}
|
||||
<Box
|
||||
marginLeft='auto'
|
||||
marginRight='auto'
|
||||
maxWidth='32rem'
|
||||
position='absolute'
|
||||
zIndex='9999'
|
||||
style={{ left: 0, right: 0, top: 0 }}
|
||||
>
|
||||
<Box
|
||||
display='flex'
|
||||
justifyContent='center'
|
||||
p='3'
|
||||
m='3'
|
||||
border='1px solid'
|
||||
borderColor='washedGray'
|
||||
backgroundColor='white'
|
||||
>
|
||||
<LoadingSpinner
|
||||
foreground='black'
|
||||
background='gray'
|
||||
/>
|
||||
<p className="lh-copy db ml3">Past messages are being restored</p>
|
||||
</div>
|
||||
</div>
|
||||
<Text display='block' ml='3' lineHeight='tall'>Past messages are being restored</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
ProfileOverlay,
|
||||
OVERLAY_HEIGHT
|
||||
} from './profile-overlay';
|
||||
import { Box, BaseImage } from '@tlon/indigo-react';
|
||||
|
||||
export class OverlaySigil extends PureComponent {
|
||||
constructor() {
|
||||
@ -58,7 +59,7 @@ export class OverlaySigil extends PureComponent {
|
||||
const { hideAvatars } = props;
|
||||
|
||||
const img = (props.contact && (props.contact.avatar !== null) && !hideAvatars)
|
||||
? <img src={props.contact.avatar} height={16} width={16} className="dib" />
|
||||
? <BaseImage display='inline-block' src={props.contact.avatar} height={16} width={16} />
|
||||
: <Sigil
|
||||
ship={props.ship}
|
||||
size={16}
|
||||
@ -69,9 +70,11 @@ export class OverlaySigil extends PureComponent {
|
||||
/>;
|
||||
|
||||
return (
|
||||
<div
|
||||
<Box
|
||||
cursor='pointer'
|
||||
position='relative'
|
||||
onClick={this.profileShow}
|
||||
className={props.className + ' pointer relative'}
|
||||
className={props.className}
|
||||
ref={this.containerRef}
|
||||
>
|
||||
{state.profileClicked && (
|
||||
@ -91,7 +94,7 @@ export class OverlaySigil extends PureComponent {
|
||||
/>
|
||||
)}
|
||||
{img}
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
|
||||
import { cite } from '~/logic/lib/util';
|
||||
import { Sigil } from '~/logic/lib/sigil';
|
||||
|
||||
import { Box, Col, Button, Text } from "@tlon/indigo-react";
|
||||
import { Box, Col, Button, Text, BaseImage } from '@tlon/indigo-react';
|
||||
|
||||
export const OVERLAY_HEIGHT = 250;
|
||||
|
||||
@ -51,8 +51,8 @@ export class ProfileOverlay extends PureComponent {
|
||||
|
||||
const isOwn = window.ship === ship;
|
||||
|
||||
let img = contact?.avatar && !hideAvatars
|
||||
? <img src={contact.avatar} height={160} width={160} className="brt2 dib" />
|
||||
const img = contact?.avatar && !hideAvatars
|
||||
? <BaseImage display='inline-block' src={contact.avatar} height={160} width={160} className="brt2" />
|
||||
: <Sigil
|
||||
ship={ship}
|
||||
size={160}
|
||||
@ -63,7 +63,7 @@ export class ProfileOverlay extends PureComponent {
|
||||
const showNickname = contact?.nickname && !hideNicknames;
|
||||
|
||||
// TODO: we need to rethink this "top-level profile view" of other ships
|
||||
/*if (!group.hidden) {
|
||||
/* if (!group.hidden) {
|
||||
}*/
|
||||
|
||||
const isHidden = group.hidden;
|
||||
@ -103,7 +103,7 @@ export class ProfileOverlay extends PureComponent {
|
||||
<Button
|
||||
mt='2'
|
||||
width='100%'
|
||||
style={{ cursor: 'pointer '}}
|
||||
style={{ cursor: 'pointer ' }}
|
||||
onClick={() => (isHidden) ? history.push('/~profile/identity') : history.push(`${history.location.pathname}/popover/profile`)}
|
||||
>
|
||||
Edit Identity
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Box, Text, Button } from '@tlon/indigo-react';
|
||||
|
||||
export class ResubscribeElement extends Component {
|
||||
onClickResubscribe() {
|
||||
@ -9,21 +10,23 @@ export class ResubscribeElement extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { props } = this;
|
||||
const { props } = this;
|
||||
if (props.isChatUnsynced) {
|
||||
return (
|
||||
<div className="db pa3 ma3 ba b--yellow2 bg-yellow0">
|
||||
<p className="lh-copy db">
|
||||
<Box p='3' m='3' border='1px solid' borderColor='yellow' backgroundColor='lightYellow'>
|
||||
<Text lineHeight='tall' display='block'>
|
||||
Your ship has been disconnected from the chat's host.
|
||||
This may be due to a bad connection, going offline, lack of permission,
|
||||
or an over-the-air update.
|
||||
</p>
|
||||
<a onClick={this.onClickResubscribe.bind(this)}
|
||||
className="db underline black pointer mt3"
|
||||
</Text>
|
||||
<Button
|
||||
primary
|
||||
mt='3'
|
||||
onClick={this.onClickResubscribe.bind(this)}
|
||||
>
|
||||
Reconnect to this chat
|
||||
</a>
|
||||
</div>
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
|
@ -91,20 +91,6 @@ h2 {
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
|
||||
/* spinner */
|
||||
|
||||
.spin-active {
|
||||
animation: spin 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {transform: rotate(0deg);}
|
||||
25% {transform: rotate(90deg);}
|
||||
50% {transform: rotate(180deg);}
|
||||
75% {transform: rotate(270deg);}
|
||||
100% {transform: rotate(360deg);}
|
||||
}
|
||||
|
||||
.embed-container iframe {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user