250 lines
8.6 KiB
JavaScript
250 lines
8.6 KiB
JavaScript
/************************************************************************
|
|
Copyright (C) Ayushya Amitabh - All Rights Reserved
|
|
Unauthorized copying of this file, via any medium is strictly prohibited
|
|
Proprietary and confidential
|
|
Written by Ayushya Amitabh <admin@chromewatchparty.com>, 2020
|
|
https://www.chromewatchparty.com/copyright
|
|
=========================================================================
|
|
ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, UNLESS OTHERWISE
|
|
CITED ARE THE SOLE INTELLECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE
|
|
COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION.
|
|
************************************************************************/
|
|
import React, { Component, createRef } from 'react';
|
|
import {
|
|
Typography,
|
|
IconButton,
|
|
Paper,
|
|
TextField,
|
|
InputAdornment,
|
|
Button,
|
|
} from '@material-ui/core';
|
|
import { Reply, Send } from '@material-ui/icons';
|
|
import './Thread.css';
|
|
import { LocalData, webhookFetcher } from './Api';
|
|
import { CellMeasurer, CellMeasurerCache, AutoSizer, List } from 'react-virtualized';
|
|
|
|
var cache = new CellMeasurerCache({
|
|
fixedWidth: true,
|
|
});
|
|
|
|
export default class Thread extends Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
autoSizeWidth: 0,
|
|
};
|
|
this.listRef = createRef();
|
|
this.getMessage = this.getMessage.bind(this);
|
|
this.getTimeString = this.getTimeString.bind(this);
|
|
this.getReplyMessage = this.getReplyMessage.bind(this);
|
|
this.getMessageTextOrImage = this.getMessageTextOrImage.bind(this);
|
|
}
|
|
scrollToMessage(msgIdx, messages) {
|
|
this.listRef.current.scrollToRow(msgIdx);
|
|
setTimeout(() => {
|
|
const id = messages[msgIdx].id || `sending-${msgIdx}`;
|
|
const msg = document.getElementById(id);
|
|
msg.classList.add('highlight');
|
|
setTimeout(() => msg.classList.remove('highlight'), 2000);
|
|
}, 500);
|
|
}
|
|
handleInteractiveMessageCall(msg) {
|
|
const thanksElement = document.createElement('div');
|
|
thanksElement.innerText = 'THANK YOU FOR THE FEEDBACK';
|
|
thanksElement.className = 'thread-interactive-thankyou';
|
|
if (msg.uid === '<system>') {
|
|
if (msg.endpoint) {
|
|
var bodyData = {
|
|
question: msg.text,
|
|
...LocalData(),
|
|
};
|
|
if (msg.type === 'interactive/text') {
|
|
const answerElement = document.getElementById(`reply-${msg.id}`);
|
|
bodyData.answer = answerElement ? answerElement.value : 'ANSWER_ELEM_NOT_FOUND';
|
|
answerElement.disabled = true;
|
|
answerElement.parentElement.parentElement.parentElement.firstElementChild.remove();
|
|
answerElement.parentElement.replaceWith(thanksElement);
|
|
}
|
|
webhookFetcher({
|
|
endpoint: msg.endpoint,
|
|
body: JSON.stringify(bodyData),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
getItemClassNames(uid, userUid, msgIdx, messages, isSending = false) {
|
|
const lastUid = msgIdx >= 1 ? messages[msgIdx - 1].uid : '';
|
|
return `thread-item ${uid === userUid ? 'my-message ' : ''}${
|
|
isSending ? 'sending-message ' : ''
|
|
}${uid === '<system>' ? 'system-message ' : ''}${lastUid === uid ? 'continued-message' : ''}`;
|
|
}
|
|
getMessageTextOrImage(msg, measure) {
|
|
if (msg.type === 'giphy') {
|
|
return <img onLoad={measure} alt="gif" src={msg.text} />;
|
|
}
|
|
if (msg.type === 'interactive/text') {
|
|
return (
|
|
<Paper onLoad={measure} elevation={0} className="thread-interactive-text">
|
|
<Typography className="thread-interactive-question" variant="overline">
|
|
{msg.text}
|
|
</Typography>
|
|
<TextField
|
|
className="thread-interactive-answer addFocus"
|
|
size="small"
|
|
id={`reply-${msg.id}`}
|
|
multiline
|
|
variant="outlined"
|
|
color="secondary"
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
<IconButton onClick={() => this.handleInteractiveMessageCall(msg)}>
|
|
<Send />
|
|
</IconButton>
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
rowsMax={2}
|
|
/>
|
|
</Paper>
|
|
);
|
|
}
|
|
if (msg.type === 'interactive/link') {
|
|
return (
|
|
<Paper onLoad={measure} elevation={0} className="thread-interactive-text">
|
|
<Typography className="thread-interactive-question" variant="overline">
|
|
{msg.text}
|
|
</Typography>
|
|
<Button
|
|
className="thread-interactive-link"
|
|
fullWidth
|
|
target="_blank"
|
|
href={msg.endpoint}
|
|
color="secondary"
|
|
>
|
|
{msg.name}
|
|
</Button>
|
|
</Paper>
|
|
);
|
|
}
|
|
return <p onLoad={measure}>{msg.text}</p>;
|
|
}
|
|
getTimeString(time) {
|
|
return new Date(time.seconds * 1000).toLocaleTimeString();
|
|
}
|
|
getReplyMessage(replyTo, messages) {
|
|
if (replyTo !== undefined && replyTo !== null) {
|
|
const replyMessage = messages.filter((v) => v.id === replyTo)[0];
|
|
if (replyMessage) {
|
|
const msgIdx = messages.indexOf(replyMessage);
|
|
return (
|
|
<div className="thread-reply" onClick={() => this.scrollToMessage(msgIdx, messages)}>
|
|
<p className="thread-reply-name">Reply to {replyMessage.name}:</p>
|
|
{this.getMessageTextOrImage(replyMessage)}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
getMessage(params) {
|
|
const { autoSizeWidth } = this.state;
|
|
const { sentMessages, setReplyTo } = this.props;
|
|
const { userUid } = LocalData();
|
|
const messages = sentMessages;
|
|
const msg = messages[params.index];
|
|
const msgIdx = params.index;
|
|
return (
|
|
<CellMeasurer
|
|
cache={cache}
|
|
columnIndex={0}
|
|
key={`msg-${msgIdx}`}
|
|
parent={params.parent}
|
|
rowIndex={msgIdx}
|
|
width={autoSizeWidth}
|
|
>
|
|
{({ measure, registerChild }) => (
|
|
<div
|
|
ref={registerChild}
|
|
style={params.style}
|
|
id={msg.id || `sending-${msgIdx}`}
|
|
key={`msg-${msgIdx}-item`}
|
|
className={this.getItemClassNames(msg.uid, userUid, msgIdx, messages, !!!msg.id)}
|
|
>
|
|
<div className="thread-name">{msg.name}</div>
|
|
{this.getReplyMessage(msg.replyTo, messages)}
|
|
<div className="thread-message">
|
|
<div className="thread-overline">
|
|
<span>{this.getTimeString(msg.time)}</span>
|
|
<div className="thread-reply-icon">
|
|
<IconButton onClick={() => setReplyTo(msg)}>
|
|
<Reply />
|
|
</IconButton>
|
|
</div>
|
|
</div>
|
|
{this.getMessageTextOrImage(msg, measure)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CellMeasurer>
|
|
);
|
|
}
|
|
render() {
|
|
const { userUid } = LocalData();
|
|
const { autoSizeWidth } = this.state;
|
|
const { sentMessages, isSending, sendingMessages, setReplyTo } = this.props;
|
|
return !isSending ? (
|
|
<AutoSizer style={{ flex: '1 auto', minWidth: '100%' }}>
|
|
{({ height, width }) => {
|
|
if (autoSizeWidth !== width) {
|
|
cache.clearAll();
|
|
this.listRef.current.recomputeRowHeights();
|
|
this.setState({ autoSizeWidth: width });
|
|
}
|
|
return (
|
|
<List
|
|
className="thread-wrapper"
|
|
deferredMeasurementCache={cache}
|
|
height={height}
|
|
width={width}
|
|
ref={this.listRef}
|
|
estimatedRowSize={40}
|
|
rowCount={sentMessages.length}
|
|
rowHeight={({ index }) => cache.rowHeight({ index }) + 10}
|
|
rowRenderer={this.getMessage}
|
|
/>
|
|
);
|
|
}}
|
|
</AutoSizer>
|
|
) : (
|
|
<div className="thread-wrapper sending-wrapper">
|
|
{sendingMessages.map((msg, msgIdx) => (
|
|
<div
|
|
id={msg.id || `sending-${msgIdx}`}
|
|
key={`sending-msg-${msgIdx}-item`}
|
|
className={this.getItemClassNames(msg.uid, userUid, msgIdx, sendingMessages, true)}
|
|
>
|
|
<div className="thread-name">{msg.name}</div>
|
|
{this.getReplyMessage(msg.replyTo, sentMessages)}
|
|
<div className="thread-message">
|
|
<div className="thread-overline">
|
|
<span>{this.getTimeString(msg.time)}</span>
|
|
<div className="thread-reply-icon">
|
|
<IconButton onClick={() => setReplyTo(msg)}>
|
|
<Reply />
|
|
</IconButton>
|
|
</div>
|
|
</div>
|
|
{this.getMessageTextOrImage(msg, null)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
Thread.defaultProps = {
|
|
setReplyTo: () => {},
|
|
};
|