mirror of
https://github.com/urbit/shrub.git
synced 2024-12-20 09:21:42 +03:00
chat-fe: MVP graphification
This commit is contained in:
parent
a1cf88faba
commit
403ef0a275
@ -67,6 +67,9 @@ function moduleToMark(mod: string): string | undefined {
|
||||
if(mod === 'publish') {
|
||||
return 'graph-validator-publish';
|
||||
}
|
||||
if(mod === 'chat') {
|
||||
return 'graph-validator-chat';
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import urbitOb from 'urbit-ob';
|
||||
|
||||
const URL_REGEX = new RegExp(String(/^((\w+:\/\/)[-a-zA-Z0-9:@;?&=\/%\+\.\*!'\(\),\$_\{\}\^~\[\]`#|]+)/.source));
|
||||
|
||||
const isUrl = (string) => {
|
||||
@ -45,11 +47,20 @@ const tokenizeMessage = (text) => {
|
||||
if (isUrl(str) && !isInCodeBlock) {
|
||||
if (message.length > 0) {
|
||||
// If we're in the middle of a message, add it to the stack and reset
|
||||
messages.push(message);
|
||||
messages.push({ text: message.join('') });
|
||||
message = [];
|
||||
}
|
||||
messages.push([str]);
|
||||
messages.push({ url: str });
|
||||
message = [];
|
||||
} else if(urbitOb.isValidPatp(str) && !isInCodeBlock) {
|
||||
if (message.length > 0) {
|
||||
// If we're in the middle of a message, add it to the stack and reset
|
||||
messages.push({ text: message.join('') });
|
||||
message = [];
|
||||
}
|
||||
messages.push({ mention: str });
|
||||
message = [];
|
||||
|
||||
} else {
|
||||
message.push(str);
|
||||
}
|
||||
@ -59,9 +70,9 @@ const tokenizeMessage = (text) => {
|
||||
|
||||
if (message.length) {
|
||||
// Add any remaining message
|
||||
messages.push(message);
|
||||
messages.push({ text: message.join('') });
|
||||
}
|
||||
return messages;
|
||||
};
|
||||
|
||||
export { tokenizeMessage as default, isUrl, URL_REGEX };
|
||||
export { tokenizeMessage as default, isUrl, URL_REGEX };
|
||||
|
@ -8,9 +8,12 @@ export interface UrlContent {
|
||||
url: string;
|
||||
}
|
||||
export interface CodeContent {
|
||||
expresssion: string;
|
||||
output: string;
|
||||
code: {
|
||||
expresssion: string;
|
||||
output: string | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ReferenceContent {
|
||||
uid: string;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import ChatInput from './components/ChatInput';
|
||||
import GlobalApi from '~/logic/api/global';
|
||||
import { SubmitDragger } from '~/views/components/s3-upload';
|
||||
import { useLocalStorageState } from '~/logic/lib/useLocalStorageState';
|
||||
import {Loading} from '~/views/components/Loading';
|
||||
|
||||
type ChatResourceProps = StoreState & {
|
||||
association: Association;
|
||||
@ -31,6 +32,8 @@ export function ChatResource(props: ChatResourceProps) {
|
||||
const group = props.groups[groupPath];
|
||||
const contacts = props.contacts[groupPath] || {};
|
||||
|
||||
const graph = props.graphs[station.slice(7)];
|
||||
|
||||
const pendingMessages = (props.pendingMessages.get(station) || []).map(
|
||||
value => ({
|
||||
...value,
|
||||
@ -38,12 +41,7 @@ export function ChatResource(props: ChatResourceProps) {
|
||||
})
|
||||
);
|
||||
|
||||
const isChatMissing =
|
||||
(props.chatInitialized &&
|
||||
!(station in props.inbox) &&
|
||||
props.chatSynced &&
|
||||
!(station in props.chatSynced)) ||
|
||||
false;
|
||||
const isChatMissing = !props.graphKeys.has(station.slice(7));
|
||||
|
||||
const isChatLoading =
|
||||
(props.chatInitialized &&
|
||||
@ -61,12 +59,16 @@ 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;
|
||||
|
||||
const chatInput = useRef<ChatInput>();
|
||||
|
||||
useEffect(() => {
|
||||
props.api.graph.getGraph(owner,name);
|
||||
}, [station]);
|
||||
|
||||
const onFileDrag = useCallback(
|
||||
(files: FileList) => {
|
||||
if (!chatInput.current) {
|
||||
@ -102,21 +104,26 @@ export function ChatResource(props: ChatResourceProps) {
|
||||
return clear;
|
||||
}, [station]);
|
||||
|
||||
if(!graph) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Col {...bind} height="100%" overflow="hidden" position="relative">
|
||||
{dragging && <SubmitDragger />}
|
||||
<ChatWindow
|
||||
remoteContentPolicy={props.remoteContentPolicy}
|
||||
mailboxSize={length}
|
||||
mailboxSize={5}
|
||||
match={props.match as any}
|
||||
stationPendingMessages={pendingMessages}
|
||||
stationPendingMessages={[]}
|
||||
history={props.history}
|
||||
isChatMissing={isChatMissing}
|
||||
isChatLoading={isChatLoading}
|
||||
isChatUnsynced={isChatUnsynced}
|
||||
unreadCount={unreadCount}
|
||||
unreadMsg={unreadMsg}
|
||||
envelopes={envelopes || []}
|
||||
graph={graph}
|
||||
unreadCount={0}
|
||||
unreadMsg={false}
|
||||
envelopes={[]}
|
||||
contacts={contacts}
|
||||
association={props.association}
|
||||
group={group}
|
||||
|
@ -3,10 +3,11 @@ import ChatEditor from './chat-editor';
|
||||
import { S3Upload } from '~/views/components/s3-upload' ;
|
||||
import { uxToHex } from '~/logic/lib/util';
|
||||
import { Sigil } from '~/logic/lib/sigil';
|
||||
import { createPost } from '~/logic/api/graph';
|
||||
import tokenizeMessage, { isUrl } from '~/logic/lib/tokenizeMessage';
|
||||
import GlobalApi from '~/logic/api/global';
|
||||
import { Envelope } from '~/types/chat-update';
|
||||
import { Contacts } from '~/types';
|
||||
import { Contacts, Content } from '~/types';
|
||||
import { Row, BaseImage, Box, Icon } from '@tlon/indigo-react';
|
||||
|
||||
interface ChatInputProps {
|
||||
@ -82,39 +83,23 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
|
||||
submit(text) {
|
||||
const { props, state } = this;
|
||||
const [,,ship,name] = props.station.split('/');
|
||||
if (state.inCodeMode) {
|
||||
this.setState({
|
||||
inCodeMode: false
|
||||
}, () => {
|
||||
props.api.chat.message(
|
||||
props.station,
|
||||
`~${window.ship}`,
|
||||
Date.now(), {
|
||||
code: {
|
||||
expression: text,
|
||||
output: undefined
|
||||
}
|
||||
}
|
||||
);
|
||||
const contents: Content[] = [{ code: { expression: text, output: undefined }}];
|
||||
const post = createPost(contents);
|
||||
props.api.graph.addPost(ship, name, post);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const messages = tokenizeMessage(text);
|
||||
const post = createPost(tokenizeMessage((text)))
|
||||
|
||||
props.deleteMessage();
|
||||
|
||||
messages.forEach((message) => {
|
||||
if (message.length > 0) {
|
||||
message = this.getLetterType(message.join(' '));
|
||||
props.api.chat.message(
|
||||
props.station,
|
||||
`~${window.ship}`,
|
||||
Date.now(),
|
||||
message
|
||||
);
|
||||
}
|
||||
});
|
||||
props.api.graph.addPost(ship,name, post);
|
||||
}
|
||||
|
||||
uploadSuccess(url) {
|
||||
@ -123,12 +108,8 @@ export default class ChatInput extends Component<ChatInputProps, ChatInputState>
|
||||
this.chatEditor.current.editor.setValue(url);
|
||||
this.setState({ uploadingPaste: false });
|
||||
} else {
|
||||
props.api.chat.message(
|
||||
props.station,
|
||||
`~${window.ship}`,
|
||||
Date.now(),
|
||||
{ url }
|
||||
);
|
||||
const [,,ship,name] = props.station.split('/');
|
||||
props.api.graph.addPost(ship,name, createPost([{ url }]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,11 @@ import { Box, Row, Text, Rule } from "@tlon/indigo-react";
|
||||
import { OverlaySigil } from './overlay-sigil';
|
||||
import { uxToHex, cite, writeText } from '~/logic/lib/util';
|
||||
import { Envelope, IMessage } from "~/types/chat-update";
|
||||
import { Group, Association, Contacts, LocalUpdateRemoteContentPolicy } from "~/types";
|
||||
import { Group, Association, Contacts, LocalUpdateRemoteContentPolicy, Post } from "~/types";
|
||||
import TextContent from './content/text';
|
||||
import CodeContent from './content/code';
|
||||
import RemoteContent from '~/views/components/RemoteContent';
|
||||
import { Mention } from "~/views/components/MentionText";
|
||||
|
||||
export const DATESTAMP_FORMAT = '[~]YYYY.M.D';
|
||||
|
||||
@ -30,9 +31,9 @@ export const DayBreak = ({ when }) => (
|
||||
|
||||
interface ChatMessageProps {
|
||||
measure(element): void;
|
||||
msg: Envelope | IMessage;
|
||||
previousMsg?: Envelope | IMessage;
|
||||
nextMsg?: Envelope | IMessage;
|
||||
msg: Post;
|
||||
previousMsg?: Post;
|
||||
nextMsg?: Post;
|
||||
isLastRead: boolean;
|
||||
group: Group;
|
||||
association: Association;
|
||||
@ -91,13 +92,13 @@ export default class ChatMessage extends Component<ChatMessageProps> {
|
||||
} = this.props;
|
||||
|
||||
const renderSigil = Boolean((nextMsg && msg.author !== nextMsg.author) || !nextMsg || msg.number === 1);
|
||||
const dayBreak = nextMsg && new Date(msg.when).getDate() !== new Date(nextMsg.when).getDate();
|
||||
const dayBreak = nextMsg && new Date(msg['time-sent']).getDate() !== new Date(nextMsg['time-sent']).getDate();
|
||||
|
||||
const containerClass = `${renderSigil
|
||||
? `cf pl2 lh-copy`
|
||||
: `items-top cf hide-child`} ${isPending ? 'o-40' : ''} ${className}`
|
||||
|
||||
const timestamp = moment.unix(msg.when / 1000).format(renderSigil ? 'hh:mm a' : 'hh:mm');
|
||||
const timestamp = moment.unix(msg['time-sent'] / 1000).format(renderSigil ? 'hh:mm a' : 'hh:mm');
|
||||
|
||||
const reboundMeasure = (event) => {
|
||||
return measure(this.divRef.current);
|
||||
@ -140,7 +141,6 @@ export default class ChatMessage extends Component<ChatMessageProps> {
|
||||
ref={this.divRef}
|
||||
className={containerClass}
|
||||
style={style}
|
||||
data-number={msg.number}
|
||||
mb={1}
|
||||
>
|
||||
{dayBreak && !isLastRead ? <DayBreak when={msg.when} /> : null}
|
||||
@ -156,7 +156,7 @@ export default class ChatMessage extends Component<ChatMessageProps> {
|
||||
}
|
||||
|
||||
interface MessageProps {
|
||||
msg: Envelope | IMessage;
|
||||
msg: Post;
|
||||
timestamp: string;
|
||||
group: Group;
|
||||
association: Association;
|
||||
@ -191,10 +191,10 @@ export class MessageWithSigil extends PureComponent<MessageProps> {
|
||||
fontSize
|
||||
} = this.props;
|
||||
|
||||
const datestamp = moment.unix(msg.when / 1000).format(DATESTAMP_FORMAT);
|
||||
const datestamp = moment.unix(msg['time-sent']).format(DATESTAMP_FORMAT);
|
||||
const contact = msg.author in contacts ? contacts[msg.author] : false;
|
||||
const showNickname = !hideNicknames && contact && contact.nickname;
|
||||
const name = showNickname ? contact.nickname : cite(msg.author);
|
||||
const name = showNickname ? contact!.nickname : cite(msg.author);
|
||||
const color = contact ? `#${uxToHex(contact.color)}` : this.isDark ? '#000000' :'#FFFFFF'
|
||||
const sigilClass = contact ? '' : this.isDark ? 'mix-blend-diff' : 'mix-blend-darken';
|
||||
|
||||
@ -251,23 +251,32 @@ export class MessageWithSigil extends PureComponent<MessageProps> {
|
||||
<Text flexShrink='0' gray mono className="v-mid">{timestamp}</Text>
|
||||
<Text flexShrink={0} gray mono ml={2} className="v-mid child dn-s">{datestamp}</Text>
|
||||
</Box>
|
||||
<Box flexShrink={0} fontSize={fontSize ? fontSize : '14px'}><MessageContent content={msg.letter} remoteContentPolicy={remoteContentPolicy} measure={measure} fontSize={fontSize} /></Box>
|
||||
<Box flexShrink={0} fontSize={fontSize ? fontSize : '14px'}>
|
||||
{msg.contents.map(c =>
|
||||
<MessageContent
|
||||
contacts={contacts}
|
||||
content={c}
|
||||
remoteContentPolicy={remoteContentPolicy}
|
||||
measure={measure}
|
||||
fontSize={fontSize}
|
||||
/>)}
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const MessageWithoutSigil = ({ timestamp, msg, remoteContentPolicy, measure }) => (
|
||||
export const MessageWithoutSigil = ({ timestamp, contacts, msg, remoteContentPolicy, measure }) => (
|
||||
<>
|
||||
<Text flexShrink={0} mono gray display='inline-block' pt='2px' lineHeight='tall' className="child">{timestamp}</Text>
|
||||
<Box flexShrink={0} fontSize='14px' className="clamp-message" style={{ flexGrow: 1 }}>
|
||||
<MessageContent content={msg.letter} remoteContentPolicy={remoteContentPolicy} measure={measure}/>
|
||||
{msg.contents.map(c => (<MessageContent contacts={contacts} content={c} remoteContentPolicy={remoteContentPolicy} measure={measure}/>))}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
||||
export const MessageContent = ({ content, remoteContentPolicy, measure, fontSize }) => {
|
||||
export const MessageContent = ({ content, contacts, remoteContentPolicy, measure, fontSize }) => {
|
||||
if ('code' in content) {
|
||||
return <CodeContent content={content} />;
|
||||
} else if ('url' in content) {
|
||||
@ -286,15 +295,10 @@ export const MessageContent = ({ content, remoteContentPolicy, measure, fontSize
|
||||
/>
|
||||
</Text>
|
||||
);
|
||||
} else if ('me' in content) {
|
||||
return (
|
||||
<Text flexShrink={0} fontStyle='italic' fontSize={fontSize ? fontSize : '14px'} lineHeight='tall' color='black'>
|
||||
{content.me}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
else if ('text' in content) {
|
||||
} else if ('text' in content) {
|
||||
return <TextContent fontSize={fontSize} content={content} />;
|
||||
} else if ('mention' in content) {
|
||||
return <Mention ship={content.mention} contacts={contacts} />
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { Contacts } from "~/types/contact-update";
|
||||
import { Association } from "~/types/metadata-update";
|
||||
import { Group } from "~/types/group-update";
|
||||
import { Envelope, IMessage } from "~/types/chat-update";
|
||||
import { LocalUpdateRemoteContentPolicy } from "~/types";
|
||||
import { LocalUpdateRemoteContentPolicy, Graph } from "~/types";
|
||||
import { BigIntOrderedMap } from "~/logic/lib/BigIntOrderedMap";
|
||||
|
||||
import VirtualScroller from "~/views/components/VirtualScroller";
|
||||
@ -29,13 +29,12 @@ type ChatWindowProps = RouteComponentProps<{
|
||||
station: string;
|
||||
}> & {
|
||||
unreadCount: number;
|
||||
envelopes: Envelope[];
|
||||
isChatMissing: boolean;
|
||||
isChatLoading: boolean;
|
||||
isChatUnsynced: boolean;
|
||||
unreadMsg: Envelope | false;
|
||||
stationPendingMessages: IMessage[];
|
||||
mailboxSize: number;
|
||||
graph: Graph;
|
||||
contacts: Contacts;
|
||||
association: Association;
|
||||
group: Group;
|
||||
@ -58,6 +57,7 @@ interface ChatWindowState {
|
||||
export default class ChatWindow extends Component<ChatWindowProps, ChatWindowState> {
|
||||
private virtualList: VirtualScroller | null;
|
||||
private unreadMarkerRef: React.RefObject<HTMLDivElement>;
|
||||
private prevSize = 0;
|
||||
|
||||
INITIALIZATION_MAX_TIME = 1500;
|
||||
|
||||
@ -112,6 +112,7 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
||||
}
|
||||
|
||||
initialFetch() {
|
||||
/*
|
||||
const { envelopes, mailboxSize, unreadCount } = this.props;
|
||||
if (envelopes.length > 0) {
|
||||
const start = Math.min(mailboxSize - unreadCount, mailboxSize - DEFAULT_BACKLOG_SIZE);
|
||||
@ -127,28 +128,37 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
||||
this.initialFetch();
|
||||
}, 2000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: ChatWindowProps, prevState) {
|
||||
const { isChatMissing, history, envelopes, mailboxSize, stationPendingMessages, unreadCount, station } = this.props;
|
||||
const { isChatMissing, history, graph, unreadCount, station } = this.props;
|
||||
|
||||
if (isChatMissing) {
|
||||
history.push("/~404");
|
||||
} else if (envelopes.length !== prevProps.envelopes.length && this.state.fetchPending) {
|
||||
} else if (graph.size !== prevProps.graph.size && this.state.fetchPending) {
|
||||
this.setState({ fetchPending: false });
|
||||
}
|
||||
|
||||
if ((mailboxSize !== prevProps.mailboxSize) || (envelopes.length !== prevProps.envelopes.length)) {
|
||||
/*if ((mailboxSize !== prevProps.mailboxSize) || (envelopes.length !== prevProps.envelopes.length)) {
|
||||
this.virtualList?.calculateVisibleItems();
|
||||
this.stayLockedIfActive();
|
||||
}
|
||||
}*/
|
||||
|
||||
if (unreadCount > prevProps.unreadCount && this.state.idle) {
|
||||
/*if (unreadCount > prevProps.unreadCount && this.state.idle) {
|
||||
this.setState({
|
||||
lastRead: unreadCount ? mailboxSize - unreadCount : -1,
|
||||
});
|
||||
}
|
||||
}*/
|
||||
|
||||
console.log(graph.size);
|
||||
console.log(prevProps.graph.size);
|
||||
|
||||
if(this.prevSize !== graph.size) {
|
||||
this.prevSize = graph.size;
|
||||
this.virtualList?.calculateVisibleItems();
|
||||
}
|
||||
/*
|
||||
if (stationPendingMessages.length !== prevProps.stationPendingMessages.length) {
|
||||
this.virtualList?.calculateVisibleItems();
|
||||
}
|
||||
@ -164,6 +174,7 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
||||
lastRead: unreadCount ? mailboxSize - unreadCount : -1,
|
||||
});
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
stayLockedIfActive() {
|
||||
@ -174,16 +185,18 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
||||
}
|
||||
|
||||
scrollToUnread() {
|
||||
/*
|
||||
const { mailboxSize, unreadCount, scrollTo } = this.props;
|
||||
const target = scrollTo || (mailboxSize - unreadCount);
|
||||
this.virtualList?.scrollToData(target);
|
||||
*/
|
||||
}
|
||||
|
||||
dismissUnread() {
|
||||
if (this.state.fetchPending) return;
|
||||
if (this.props.unreadCount === 0) return;
|
||||
this.props.api.chat.read(this.props.station);
|
||||
this.props.api.hark.readIndex({ chat: { chat: this.props.station, mention: false }});
|
||||
//this.props.api.chat.read(this.props.station);
|
||||
//this.props.api.hark.readIndex({ chat: { chat: this.props.station, mention: false }});
|
||||
}
|
||||
|
||||
fetchMessages(start, end, force = false): Promise<void> {
|
||||
@ -235,7 +248,6 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
||||
|
||||
render() {
|
||||
const {
|
||||
envelopes,
|
||||
stationPendingMessages,
|
||||
unreadCount,
|
||||
unreadMsg,
|
||||
@ -248,6 +260,7 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
||||
group,
|
||||
contacts,
|
||||
mailboxSize,
|
||||
graph,
|
||||
hideAvatars,
|
||||
hideNicknames,
|
||||
remoteContentPolicy,
|
||||
@ -256,27 +269,12 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
||||
|
||||
const unreadMarkerRef = this.unreadMarkerRef;
|
||||
|
||||
const messages = new BigIntOrderedMap();
|
||||
let lastMessage = 0;
|
||||
|
||||
[...envelopes]
|
||||
.sort((a, b) => a.number - b.number)
|
||||
.forEach(message => {
|
||||
const num = bigInt(message.number);
|
||||
messages.set(num, message);
|
||||
lastMessage = message.number;
|
||||
});
|
||||
|
||||
stationPendingMessages
|
||||
.sort((a, b) => a.when - b.when)
|
||||
.forEach((message, index) => {
|
||||
const idx = bigInt(index + 1); // To 1-index it
|
||||
messages.set(bigInt(mailboxSize).add(idx), message);
|
||||
lastMessage = mailboxSize + index;
|
||||
});
|
||||
|
||||
const messageProps = { association, group, contacts, hideAvatars, hideNicknames, remoteContentPolicy, unreadMarkerRef, history, api };
|
||||
|
||||
const keys = graph.keys().reverse();
|
||||
|
||||
return (
|
||||
<>
|
||||
<UnreadNotice
|
||||
@ -296,10 +294,10 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
||||
this.dismissUnread();
|
||||
}}
|
||||
onScroll={this.onScroll.bind(this)}
|
||||
data={messages}
|
||||
size={mailboxSize + stationPendingMessages.length}
|
||||
data={graph}
|
||||
size={graph.size}
|
||||
renderer={({ index, measure, scrollWindow }) => {
|
||||
const msg: Envelope | IMessage = messages.get(index);
|
||||
const msg = graph.get(index)!.post;
|
||||
if (!msg) return null;
|
||||
if (!this.state.initialized) {
|
||||
return <MessagePlaceholder key={index.toString()} height="64px" index={index} />;
|
||||
@ -309,11 +307,14 @@ export default class ChatWindow extends Component<ChatWindowProps, ChatWindowSta
|
||||
const isLastRead: boolean = Boolean(!isLastMessage && index.eq(bigInt(this.state.lastRead)));
|
||||
const highlighted = bigInt(this.props.scrollTo || -1).eq(index);
|
||||
const props = { measure, highlighted, scrollWindow, isPending, isLastRead, isLastMessage, msg, ...messageProps };
|
||||
const graphIdx = keys.findIndex(idx => idx.eq(index));
|
||||
const prevIdx = keys[graphIdx+1];
|
||||
const nextIdx = keys[graphIdx-1];
|
||||
return (
|
||||
<ChatMessage
|
||||
key={index.toString()}
|
||||
previousMsg={messages.get(index.add(bigInt.one))}
|
||||
nextMsg={messages.get(index.subtract(bigInt.one))}
|
||||
previousMsg={prevIdx && graph.get(prevIdx)?.post}
|
||||
nextMsg={nextIdx && graph.get(nextIdx)?.post}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
@ -37,7 +37,7 @@ export function MentionText(props: MentionTextProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function Mention(props: { ship: string; contacts: Contacts }) {
|
||||
export function Mention(props: { ship: string; contacts: Contacts }) {
|
||||
const { contacts, ship } = props;
|
||||
const contact = contacts[ship];
|
||||
const showNickname = !!contact?.nickname;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import React, { PureComponent, Component } from 'react';
|
||||
import _ from 'lodash';
|
||||
import { BigIntOrderedMap } from "~/logic/lib/BigIntOrderedMap";
|
||||
import normalizeWheel from 'normalize-wheel';
|
||||
@ -34,10 +34,10 @@ interface VirtualScrollerState {
|
||||
scrollTop: number;
|
||||
}
|
||||
|
||||
export default class VirtualScroller extends PureComponent<VirtualScrollerProps, VirtualScrollerState> {
|
||||
export default class VirtualScroller extends Component<VirtualScrollerProps, VirtualScrollerState> {
|
||||
private scrollContainer: React.RefObject<HTMLDivElement>;
|
||||
public window: HTMLDivElement | null;
|
||||
private cache: BigIntOrderedMap<BigInteger, any>;
|
||||
private cache: BigIntOrderedMap<any>;
|
||||
private pendingLoad: {
|
||||
start: BigInteger;
|
||||
end: BigInteger
|
||||
@ -144,8 +144,8 @@ export default class VirtualScroller extends PureComponent<VirtualScrollerProps,
|
||||
|
||||
|
||||
//console.log([...items].map(([index]) => this.heightOf(index)));
|
||||
const list = [...data];
|
||||
console.log(list[0][0].toString());
|
||||
//const list = [...data];
|
||||
//console.log(list[0][0].toString());
|
||||
// console.log(list[list.length - 1][0].toString());
|
||||
[...data].forEach(([index, datum]) => {
|
||||
const height = this.heightOf(index);
|
||||
|
@ -95,14 +95,11 @@ export function GroupsPane(props: GroupsPaneProps) {
|
||||
string,
|
||||
string
|
||||
>;
|
||||
const appName = app as AppName;
|
||||
const isGraph = appIsGraph(app);
|
||||
|
||||
const resource = `${isGraph ? "/ship" : ""}/${host}/${name}`;
|
||||
const association =
|
||||
isGraph
|
||||
? associations.graph[resource]
|
||||
: associations[appName][resource];
|
||||
const appName = app as AppName;
|
||||
|
||||
const resource = `/ship/${host}/${name}`;
|
||||
const association = associations.graph[resource]
|
||||
const resourceUrl = `${baseUrl}/resource/${app}${resource}`;
|
||||
|
||||
if (!association) {
|
||||
@ -133,10 +130,8 @@ export function GroupsPane(props: GroupsPaneProps) {
|
||||
path={relativePath("/join/:app/(ship)?/:host/:name")}
|
||||
render={(routeProps) => {
|
||||
const { app, host, name } = routeProps.match.params;
|
||||
const appName = app as AppName;
|
||||
const isGraph = appIsGraph(app);
|
||||
const appPath = `${isGraph ? '/ship/' : '/'}${host}/${name}`;
|
||||
const association = isGraph ? associations.graph[appPath] : associations[appName][appPath];
|
||||
const appPath = `/ship/${host}/${name}`;
|
||||
const association = associations.graph[appPath];
|
||||
const resourceUrl = `${baseUrl}/join/${app}${appPath}`;
|
||||
|
||||
if (!association) {
|
||||
|
@ -52,44 +52,22 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) {
|
||||
const resId: string = stringToSymbol(values.name);
|
||||
try {
|
||||
const { name, description, moduleType, ships } = values;
|
||||
switch (moduleType) {
|
||||
case 'chat':
|
||||
const appPath = `/~${window.ship}/${resId}`;
|
||||
const groupPath = group || `/ship${appPath}`;
|
||||
|
||||
await api.chat.create(
|
||||
name,
|
||||
description,
|
||||
appPath,
|
||||
groupPath,
|
||||
{ invite: { pending: ships.map(s => `~${s}`) } },
|
||||
ships.map(s => `~${s}`),
|
||||
true,
|
||||
false
|
||||
);
|
||||
break;
|
||||
case "publish":
|
||||
case "link":
|
||||
if (group) {
|
||||
await api.graph.createManagedGraph(
|
||||
resId,
|
||||
name,
|
||||
description,
|
||||
group,
|
||||
moduleType
|
||||
);
|
||||
} else {
|
||||
await api.graph.createUnmanagedGraph(
|
||||
resId,
|
||||
name,
|
||||
description,
|
||||
{ invite: { pending: ships.map((s) => `~${s}`) } },
|
||||
moduleType
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.log('fallthrough');
|
||||
if (group) {
|
||||
await api.graph.createManagedGraph(
|
||||
resId,
|
||||
name,
|
||||
description,
|
||||
group,
|
||||
moduleType
|
||||
);
|
||||
} else {
|
||||
await api.graph.createUnmanagedGraph(
|
||||
resId,
|
||||
name,
|
||||
description,
|
||||
{ invite: { pending: ships.map((s) => `~${s}`) } },
|
||||
moduleType
|
||||
);
|
||||
}
|
||||
|
||||
if (!group) {
|
||||
@ -98,8 +76,7 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) {
|
||||
actions.setStatus({ success: null });
|
||||
const resourceUrl = parentPath(location.pathname);
|
||||
history.push(
|
||||
`${resourceUrl}/resource/${moduleType}` +
|
||||
`${moduleType !== 'chat' ? '/ship' : ''}/~${window.ship}/${resId}`
|
||||
`${resourceUrl}/resource/${moduleType}/ship/~${window.ship}/${resId}`
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
Loading…
Reference in New Issue
Block a user