2020-02-25 01:29:44 +01:00
|
|
|
import React, { useEffect, useRef } from 'react';
|
2020-02-17 01:02:35 +01:00
|
|
|
|
2020-02-17 05:16:24 +01:00
|
|
|
import { ConversationHeader } from '../conversation/ConversationHeader';
|
2020-02-19 02:05:48 +01:00
|
|
|
import { SessionCompositionBox } from './SessionCompositionBox';
|
|
|
|
import { SessionProgress } from './SessionProgress'
|
|
|
|
|
|
|
|
import { Message } from '../conversation/Message';
|
2020-02-25 07:26:45 +01:00
|
|
|
import { FriendRequest } from '../conversation/FriendRequest';
|
2020-02-25 05:20:34 +01:00
|
|
|
import { TimerNotification } from '../conversation/TimerNotification';
|
|
|
|
|
2020-02-25 07:26:45 +01:00
|
|
|
|
2020-02-19 03:54:59 +01:00
|
|
|
import { SessionSpinner } from './SessionSpinner';
|
2020-02-25 01:29:44 +01:00
|
|
|
import { SessionScrollButton } from './SessionScrollButton';
|
|
|
|
|
2020-02-21 00:17:04 +01:00
|
|
|
// interface Props {
|
|
|
|
// getHeaderProps: any;
|
|
|
|
// conversationKey: any;
|
|
|
|
// }
|
|
|
|
|
2020-02-25 05:20:34 +01:00
|
|
|
|
2020-02-21 06:14:36 +01:00
|
|
|
interface State {
|
|
|
|
sendingProgess: number;
|
|
|
|
prevSendingProgess: number;
|
|
|
|
conversationKey: string;
|
2020-02-26 00:33:32 +01:00
|
|
|
unreadCount: number;
|
2020-02-21 06:14:36 +01:00
|
|
|
messages: Array<any>;
|
2020-02-25 01:29:44 +01:00
|
|
|
// Scroll position as percentage of message-list
|
|
|
|
scrollPositionPc: number;
|
|
|
|
// Scroll position in pixels
|
|
|
|
scrollPositionPx: number;
|
|
|
|
doneInitialScroll: boolean;
|
2020-02-26 00:33:32 +01:00
|
|
|
messageFetchTimestamp: number;
|
2020-02-21 06:14:36 +01:00
|
|
|
}
|
2020-02-21 00:17:04 +01:00
|
|
|
|
2020-02-21 06:14:36 +01:00
|
|
|
export class SessionConversation extends React.Component<any, State> {
|
2020-02-25 01:29:44 +01:00
|
|
|
private messagesEndRef: React.RefObject<HTMLDivElement>;
|
|
|
|
|
2020-02-19 02:05:48 +01:00
|
|
|
constructor(props: any) {
|
|
|
|
super(props);
|
2020-02-26 00:33:32 +01:00
|
|
|
|
2020-02-24 04:11:54 +01:00
|
|
|
const conversationKey = this.props.conversations.selectedConversation;
|
2020-02-26 00:33:32 +01:00
|
|
|
|
|
|
|
const conversation = this.props.conversations.conversationLookup[conversationKey];
|
|
|
|
const unreadCount = conversation.unreadCount;
|
|
|
|
|
|
|
|
console.log(`[vince][info] Conversation: `, conversation);
|
2020-02-21 06:14:36 +01:00
|
|
|
|
|
|
|
this.state = {
|
|
|
|
sendingProgess: 0,
|
|
|
|
prevSendingProgess: 0,
|
|
|
|
conversationKey,
|
2020-02-26 00:33:32 +01:00
|
|
|
unreadCount,
|
2020-02-25 05:20:34 +01:00
|
|
|
messages: [],
|
2020-02-25 01:29:44 +01:00
|
|
|
doneInitialScroll: false,
|
|
|
|
scrollPositionPc: 0,
|
|
|
|
scrollPositionPx: 0,
|
2020-02-26 00:33:32 +01:00
|
|
|
messageFetchTimestamp: 0,
|
2020-02-21 06:14:36 +01:00
|
|
|
};
|
2020-02-24 04:11:54 +01:00
|
|
|
|
2020-02-25 01:29:44 +01:00
|
|
|
this.scrollToUnread = this.scrollToUnread.bind(this);
|
|
|
|
this.scrollToBottom = this.scrollToBottom.bind(this);
|
|
|
|
|
2020-02-25 05:20:34 +01:00
|
|
|
this.renderMessage = this.renderMessage.bind(this);
|
|
|
|
this.renderTimerNotification = this.renderTimerNotification.bind(this);
|
2020-02-25 07:26:45 +01:00
|
|
|
this.renderFriendRequest = this.renderFriendRequest.bind(this);
|
2020-02-25 05:20:34 +01:00
|
|
|
|
2020-02-25 01:29:44 +01:00
|
|
|
this.messagesEndRef = React.createRef();
|
2020-02-24 04:11:54 +01:00
|
|
|
}
|
|
|
|
|
2020-02-25 05:20:34 +01:00
|
|
|
public async componentWillMount() {
|
|
|
|
const { conversationKey } = this.state;
|
|
|
|
await this.getMessages(conversationKey);
|
2020-02-26 00:33:32 +01:00
|
|
|
|
|
|
|
// Inside a setTimeout to simultate onready()
|
2020-02-25 01:29:44 +01:00
|
|
|
setTimeout(() => {
|
|
|
|
this.scrollToBottom(true);
|
2020-02-25 05:20:34 +01:00
|
|
|
}, 0);
|
|
|
|
setTimeout(() => {
|
|
|
|
this.setState({
|
|
|
|
doneInitialScroll: true,
|
|
|
|
});
|
|
|
|
}, 100);
|
2020-02-25 01:29:44 +01:00
|
|
|
|
2020-02-19 02:05:48 +01:00
|
|
|
}
|
2020-02-17 05:16:24 +01:00
|
|
|
|
2020-02-25 05:20:34 +01:00
|
|
|
public async componentWillReceiveProps() {
|
|
|
|
const { conversationKey } = this.state;
|
2020-02-26 00:33:32 +01:00
|
|
|
const timestamp = this.getTimestamp();
|
|
|
|
|
|
|
|
// If we have pulled messages in the last second, don't bother rescanning
|
|
|
|
// This avoids getting messages on every re-render.
|
|
|
|
if (timestamp > this.state.messageFetchTimestamp) {
|
|
|
|
await this.getMessages(conversationKey);
|
|
|
|
} else{
|
|
|
|
console.log(`[vince][info] Messages recieved in last second, stream`);
|
|
|
|
}
|
2020-02-21 06:14:36 +01:00
|
|
|
}
|
2020-02-19 02:05:48 +01:00
|
|
|
|
|
|
|
render() {
|
2020-02-24 04:11:54 +01:00
|
|
|
console.log('[vince] SessionConversation was just rerendered!');
|
|
|
|
console.log(`[vince] These are SessionConversation props: `, this.props);
|
|
|
|
|
2020-02-21 06:14:36 +01:00
|
|
|
// const headerProps = this.props.getHeaderProps;
|
2020-02-25 01:29:44 +01:00
|
|
|
const { messages, conversationKey, doneInitialScroll } = this.state;
|
2020-02-25 05:20:34 +01:00
|
|
|
const loading = !doneInitialScroll || messages.length === 0;
|
2020-02-21 06:14:36 +01:00
|
|
|
|
2020-02-25 01:29:44 +01:00
|
|
|
console.log(`[vince] Loading: `, loading);
|
2020-02-24 04:11:54 +01:00
|
|
|
console.log(`[vince] My conversation key is: `, conversationKey);
|
2020-02-25 05:20:34 +01:00
|
|
|
|
2020-02-24 04:11:54 +01:00
|
|
|
|
2020-02-21 06:14:36 +01:00
|
|
|
// TMEPORARY SOLUTION TO GETTING CONVERSATION UNTIL
|
|
|
|
// SessionConversationStack is created
|
|
|
|
|
|
|
|
// Get conversation by Key (NOT cid)
|
2020-02-24 04:11:54 +01:00
|
|
|
const conversation = this.props.conversations.conversationLookup[conversationKey]
|
2020-02-21 06:14:36 +01:00
|
|
|
|
|
|
|
return (
|
2020-02-25 01:29:44 +01:00
|
|
|
<div className="conversation-item">
|
2020-02-21 06:14:36 +01:00
|
|
|
<div className="conversation-header">
|
|
|
|
{this.renderHeader(conversation)}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<SessionProgress
|
|
|
|
visible={true}
|
|
|
|
value={this.state.sendingProgess}
|
|
|
|
prevValue={this.state.prevSendingProgess}
|
|
|
|
/>
|
|
|
|
|
2020-02-25 01:29:44 +01:00
|
|
|
<div className="messages-wrapper">
|
|
|
|
{ loading && (
|
2020-02-21 06:14:36 +01:00
|
|
|
<div className="messages-container__loading">
|
2020-02-25 05:20:34 +01:00
|
|
|
{/* <SessionSpinner/> */}
|
2020-02-21 06:14:36 +01:00
|
|
|
</div>
|
|
|
|
)}
|
2020-02-25 01:29:44 +01:00
|
|
|
|
|
|
|
<div className="messages-container">
|
2020-02-26 00:33:32 +01:00
|
|
|
{this.renderMessages()}
|
2020-02-25 01:29:44 +01:00
|
|
|
<div ref={this.messagesEndRef} />
|
|
|
|
</div>
|
|
|
|
|
2020-02-26 00:33:32 +01:00
|
|
|
<SessionScrollButton display={true} onClick={this.scrollToUnread}/>
|
2020-02-25 01:29:44 +01:00
|
|
|
|
|
|
|
|
2020-02-21 06:14:36 +01:00
|
|
|
</div>
|
2020-02-19 02:05:48 +01:00
|
|
|
|
2020-02-21 06:14:36 +01:00
|
|
|
<SessionCompositionBox
|
|
|
|
onSendMessage={() => null}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-02-25 05:20:34 +01:00
|
|
|
public renderMessages() {
|
2020-02-21 06:14:36 +01:00
|
|
|
const { messages } = this.state;
|
2020-02-25 07:26:45 +01:00
|
|
|
|
2020-02-21 06:14:36 +01:00
|
|
|
return (
|
|
|
|
<>{
|
|
|
|
messages.map((message: any) => {
|
2020-02-25 05:20:34 +01:00
|
|
|
const messageProps = message.propsForMessage;
|
|
|
|
const timerProps = message.propsForTimerNotification;
|
2020-02-25 07:26:45 +01:00
|
|
|
const friendRequestProps = message.propsForFriendRequest;
|
2020-02-25 05:20:34 +01:00
|
|
|
const attachmentProps = message.propsForAttachment;
|
2020-02-25 07:26:45 +01:00
|
|
|
const groupNotificationProps = message.propsForGroupNotification;
|
2020-02-25 05:20:34 +01:00
|
|
|
const quoteProps = message.propsForQuote;
|
|
|
|
|
|
|
|
let item;
|
2020-02-25 07:26:45 +01:00
|
|
|
// firstMessageOfSeries tells us to render the avatar only for the first message
|
|
|
|
// in a series of messages from the same user
|
|
|
|
item = messageProps ? this.renderMessage(messageProps, message.firstMessageOfSeries) : item;
|
2020-02-25 05:20:34 +01:00
|
|
|
item = timerProps ? this.renderTimerNotification(timerProps) : item;
|
2020-02-25 07:26:45 +01:00
|
|
|
item = quoteProps ? this.renderMessage(timerProps, message.firstMessageOfSeries, quoteProps) : item;
|
|
|
|
item = friendRequestProps
|
|
|
|
? this.renderFriendRequest(friendRequestProps): item;
|
|
|
|
// item = attachmentProps ? this.renderMessage(timerProps) : item;
|
2020-02-25 05:20:34 +01:00
|
|
|
|
|
|
|
return item;
|
|
|
|
})
|
2020-02-21 06:14:36 +01:00
|
|
|
}</>
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public renderHeader(conversation: any) {
|
2020-02-19 02:05:48 +01:00
|
|
|
return (
|
2020-02-21 06:14:36 +01:00
|
|
|
<ConversationHeader
|
|
|
|
id={conversation.cid}
|
|
|
|
phoneNumber={conversation.id}
|
|
|
|
isVerified={true}
|
|
|
|
isMe={false}
|
|
|
|
isFriend={true}
|
|
|
|
i18n={window.i18n}
|
|
|
|
isGroup={false}
|
|
|
|
isArchived={false}
|
|
|
|
isPublic={false}
|
|
|
|
isRss={false}
|
|
|
|
amMod={false}
|
|
|
|
members={[]}
|
|
|
|
showBackButton={false}
|
|
|
|
timerOptions={[]}
|
|
|
|
isBlocked={false}
|
|
|
|
hasNickname={false}
|
|
|
|
isFriendRequestPending={false}
|
|
|
|
isOnline={true}
|
|
|
|
selectedMessages={null}
|
|
|
|
onSetDisappearingMessages={() => null}
|
|
|
|
onDeleteMessages={() => null}
|
|
|
|
onDeleteContact={() => null}
|
|
|
|
onResetSession={() => null}
|
|
|
|
onCloseOverlay={() => null}
|
|
|
|
onDeleteSelectedMessages={() => null}
|
|
|
|
onArchive={() => null}
|
|
|
|
onMoveToInbox={() => null}
|
|
|
|
onShowSafetyNumber={() => null}
|
|
|
|
onShowAllMedia={() => null}
|
|
|
|
onShowGroupMembers={() => null}
|
|
|
|
onGoBack={() => null}
|
|
|
|
onBlockUser={() => null}
|
|
|
|
onUnblockUser={() => null}
|
|
|
|
onClearNickname={() => null}
|
|
|
|
onChangeNickname={() => null}
|
|
|
|
onCopyPublicKey={() => null}
|
|
|
|
onLeaveGroup={() => null}
|
|
|
|
onAddModerators={() => null}
|
|
|
|
onRemoveModerators={() => null}
|
|
|
|
onInviteFriends={() => null}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-02-25 05:20:34 +01:00
|
|
|
|
2020-02-26 00:33:32 +01:00
|
|
|
public async getMessages(conversationKey: string){
|
|
|
|
const msgCount = window.CONSTANTS.DEFAULT_MESSAGE_FETCH_COUNT + this.state.unreadCount;
|
|
|
|
|
2020-02-25 05:20:34 +01:00
|
|
|
const messageSet = await window.Signal.Data.getMessagesByConversation(
|
|
|
|
conversationKey,
|
2020-02-26 00:33:32 +01:00
|
|
|
{ msgCount, MessageCollection: window.Whisper.MessageCollection },
|
2020-02-25 05:20:34 +01:00
|
|
|
);
|
|
|
|
|
2020-02-25 07:26:45 +01:00
|
|
|
// Set first member of series here.
|
|
|
|
const messageModels = messageSet.models;
|
|
|
|
let messages = [];
|
|
|
|
let previousSender;
|
|
|
|
for (let i = 0; i < messageModels.length; i++){
|
2020-02-26 00:33:32 +01:00
|
|
|
// Handle firstMessageOfSeries for conditional avatar rendering
|
2020-02-25 07:26:45 +01:00
|
|
|
let firstMessageOfSeries = true;
|
|
|
|
if (i > 0 && previousSender === messageModels[i].authorPhoneNumber){
|
|
|
|
firstMessageOfSeries = false;
|
|
|
|
}
|
2020-02-26 00:33:32 +01:00
|
|
|
|
2020-02-25 07:26:45 +01:00
|
|
|
messages.push({...messageModels[i], firstMessageOfSeries});
|
|
|
|
previousSender = messageModels[i].authorPhoneNumber;
|
|
|
|
}
|
|
|
|
|
2020-02-26 00:33:32 +01:00
|
|
|
const messageFetchTimestamp = this.getTimestamp();
|
|
|
|
|
|
|
|
console.log(`[vince][messages] Messages Set`, messageModels);
|
|
|
|
this.setState({ messages, messageFetchTimestamp });
|
2020-02-25 05:20:34 +01:00
|
|
|
}
|
|
|
|
|
2020-02-25 07:26:45 +01:00
|
|
|
public renderMessage(messageProps: any, firstMessageOfSeries: boolean, quoteProps?: any) {
|
|
|
|
|
2020-02-25 05:20:34 +01:00
|
|
|
return (
|
|
|
|
<Message
|
|
|
|
i18n = {window.i18n}
|
2020-02-25 07:26:45 +01:00
|
|
|
text = {messageProps?.text}
|
|
|
|
direction = {messageProps?.direction}
|
|
|
|
timestamp = {messageProps?.timestamp}
|
|
|
|
attachments = {messageProps?.attachments}
|
|
|
|
authorAvatarPath = {messageProps?.authorAvatarPath}
|
|
|
|
authorColor = {messageProps?.authorColor}
|
|
|
|
authorName = {messageProps?.authorName}
|
|
|
|
authorPhoneNumber = {messageProps?.authorPhoneNumber}
|
|
|
|
firstMessageOfSeries = {firstMessageOfSeries}
|
|
|
|
authorProfileName = {messageProps?.authorProfileName}
|
|
|
|
contact = {messageProps?.contact}
|
|
|
|
conversationType = {messageProps?.conversationType}
|
|
|
|
convoId = {messageProps?.convoId}
|
|
|
|
expirationLength = {messageProps?.expirationLength}
|
|
|
|
expirationTimestamp = {messageProps?.expirationTimestamp}
|
|
|
|
id = {messageProps?.id}
|
|
|
|
isDeletable = {messageProps?.isDeletable}
|
|
|
|
isExpired = {messageProps?.isExpired}
|
|
|
|
isModerator = {messageProps?.isModerator}
|
|
|
|
isPublic = {messageProps?.isPublic}
|
|
|
|
isRss = {messageProps?.isRss}
|
|
|
|
multiSelectMode = {messageProps?.multiSelectMode}
|
|
|
|
onBanUser = {messageProps?.onBanUser}
|
|
|
|
onClickAttachment = {messageProps?.onClickAttachment}
|
|
|
|
onClickLinkPreview = {messageProps?.onClickLinkPreview}
|
|
|
|
onCopyPubKey = {messageProps?.onCopyPubKey}
|
|
|
|
onCopyText = {messageProps?.onCopyText}
|
|
|
|
onDelete = {messageProps?.onDelete}
|
|
|
|
onDownload = {messageProps?.onDownload}
|
|
|
|
onReply = {messageProps?.onReply}
|
|
|
|
onRetrySend = {messageProps?.onRetrySend}
|
|
|
|
onSelectMessage = {messageProps?.onSelectMessage}
|
|
|
|
onSelectMessageUnchecked = {messageProps?.onSelectMessageUnchecked}
|
|
|
|
onShowDetail = {messageProps?.onShowDetail}
|
|
|
|
onShowUserDetails = {messageProps?.onShowUserDetails}
|
|
|
|
previews = {messageProps?.previews}
|
|
|
|
quote = {quoteProps || undefined}
|
|
|
|
selected = {messageProps?.selected}
|
|
|
|
senderIsModerator = {messageProps?.senderIsModerator}
|
|
|
|
status = {messageProps?.status}
|
|
|
|
textPending = {messageProps?.textPending}
|
2020-02-25 05:20:34 +01:00
|
|
|
/>
|
|
|
|
);
|
2020-02-25 07:26:45 +01:00
|
|
|
|
2020-02-25 05:20:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public renderTimerNotification(timerProps: any) {
|
|
|
|
return (
|
|
|
|
<TimerNotification
|
|
|
|
type={timerProps.type}
|
|
|
|
phoneNumber={timerProps.phoneNumber}
|
|
|
|
profileName={timerProps.profileName}
|
|
|
|
name={timerProps.name}
|
|
|
|
disabled={timerProps.disabled}
|
|
|
|
timespan={timerProps.timespan}
|
|
|
|
i18n={window.i18n}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
2020-02-25 07:26:45 +01:00
|
|
|
|
|
|
|
public renderFriendRequest(friendRequestProps: any){
|
|
|
|
return (
|
|
|
|
<FriendRequest
|
|
|
|
text={friendRequestProps.text}
|
|
|
|
direction={friendRequestProps.direction}
|
|
|
|
status={friendRequestProps.status}
|
|
|
|
friendStatus={friendRequestProps.friendStatus}
|
|
|
|
i18n={window.i18n}
|
|
|
|
isBlocked={friendRequestProps.isBlocked}
|
|
|
|
timestamp={friendRequestProps.timestamp}
|
|
|
|
onAccept={friendRequestProps.onAccept}
|
|
|
|
onDecline={friendRequestProps.onDecline}
|
|
|
|
onDeleteConversation={friendRequestProps.onDeleteConversation}
|
|
|
|
onRetrySend={friendRequestProps.onRetrySend}
|
|
|
|
onBlockUser={friendRequestProps.onBlockUser}
|
|
|
|
onUnblockUser={friendRequestProps.onUnblockUser}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
2020-02-26 00:33:32 +01:00
|
|
|
|
|
|
|
public getTimestamp() {
|
|
|
|
return Math.floor(Date.now() / 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
public handleScroll() {
|
|
|
|
// Update unread count
|
|
|
|
|
|
|
|
// Get id of message at bottom of screen in full view. This is scroll position by messageID
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public scrollToUnread() {
|
|
|
|
const topUnreadMessage = document.getElementById('70fd6220-5292-43d8-9e0d-f98bf4792f43');
|
|
|
|
topUnreadMessage?.scrollIntoView(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
public scrollToBottom(firstLoad = false) {
|
|
|
|
this.messagesEndRef.current?.scrollIntoView(
|
|
|
|
{ behavior: firstLoad ? 'auto' : 'smooth' }
|
|
|
|
);
|
|
|
|
}
|
2020-02-21 04:57:21 +01:00
|
|
|
}
|
2020-02-25 05:20:34 +01:00
|
|
|
|