Marking messages as read completed

pull/1102/head
Vincent 5 years ago
parent 9a6aafe99c
commit c7d96f2565

@ -224,6 +224,7 @@ $composition-container-height: 60px;
background-color: $session-shade-4;
border: 1px solid $session-shade-6-alt;
border-radius: 8px;
padding-bottom: $session-margin-sm;
.emoji-mart-category-label {
@ -259,7 +260,6 @@ $composition-container-height: 60px;
border: 0.7px solid $session-shade-6-alt;
clip-path: polygon(100% 100%, 7.2px 100%, 100% 7.2px);
}
}
}

@ -994,7 +994,7 @@ export class Message extends React.PureComponent<Props, State> {
const dimensions = getGridDimensions(attachments);
if (dimensions) {
return dimensions.width;
}
}
}
if (previews && previews.length) {

@ -208,7 +208,8 @@ export class SessionCompositionBox extends React.Component<Props, State> {
private onChooseAttachment() {
this.fileInput.current?.click();
const fileInput = this.fileInput.current;
if(fileInput) fileInput.click();
}
private onChoseAttachment() {
@ -250,12 +251,13 @@ export class SessionCompositionBox extends React.Component<Props, State> {
private onSendMessage(){
// FIXME VINCE: Get emoiji, attachments, etc
const messagePlaintext = this.textarea.current?.value;
const {attachments} = this.state;
const messageInput = this.textarea.current;
if (!messageInput) return;
const messagePlaintext = messageInput.value;
const {attachments} = this.state;
// FIXME VINCE: Get emoiji, attachments, etc
console.log(`[vince][msg] Message:`, messagePlaintext);
console.log(`[vince][msg] fileAttachments:`, attachments);

@ -28,6 +28,7 @@ interface State {
export class SessionConversation extends React.Component<any, State> {
private messagesEndRef: React.RefObject<HTMLDivElement>;
private messageContainerRef: React.RefObject<HTMLDivElement>;
constructor(props: any) {
super(props);
@ -67,20 +68,21 @@ export class SessionConversation extends React.Component<any, State> {
this.resetSelection = this.resetSelection.bind(this);
this.messagesEndRef = React.createRef();
this.messageContainerRef = React.createRef();
}
public async componentWillMount() {
await this.getMessages();
// Pause thread to wait for rendering to complete
setTimeout(() => {
this.scrollToUnread();
}, 0);
setTimeout(() => {
this.setState({
doneInitialScroll: true,
});
}, 100);
public componentDidMount() {
this.getMessages().then(() => {
// Pause thread to wait for rendering to complete
setTimeout(() => {
this.scrollToUnread();
}, 0);
setTimeout(() => {
this.setState({
doneInitialScroll: true,
});
}, 100);
});
}
public componentDidUpdate(){
@ -134,7 +136,11 @@ export class SessionConversation extends React.Component<any, State> {
<div className="messages-container__loading"></div>
)}
<div className="messages-container" onScroll={this.handleScroll}>
<div
className="messages-container"
onScroll={this.handleScroll}
ref={this.messageContainerRef}
>
{this.renderMessages()}
<div ref={this.messagesEndRef} />
</div>
@ -213,6 +219,7 @@ export class SessionConversation extends React.Component<any, State> {
isFriendRequestPending={headerProps.isFriendRequestPending}
isOnline={headerProps.isOnline}
selectedMessages={headerProps.selectedMessages}
onUpdateGroupName={headerProps.onUpdateGroupName}
onSetDisappearingMessages={headerProps.onSetDisappearingMessages}
onDeleteMessages={headerProps.onDeleteMessages}
onDeleteContact={headerProps.onDeleteContact}
@ -238,7 +245,6 @@ export class SessionConversation extends React.Component<any, State> {
);
}
public renderMessage(messageProps: any, firstMessageOfSeries: boolean, quoteProps?: any) {
const selected = !! messageProps?.id
&& this.state.selectedMessages.includes(messageProps.id);
@ -357,7 +363,7 @@ export class SessionConversation extends React.Component<any, State> {
// Set first member of series here.
const messageModels = messageSet.models;
let messages = [];
const messages = [];
let previousSender;
for (let i = 0; i < messageModels.length; i++){
// Handle firstMessageOfSeries for conditional avatar rendering
@ -373,24 +379,109 @@ export class SessionConversation extends React.Component<any, State> {
const previousTopMessage = this.state.messages[0]?.id;
const newTopMessage = messages[0]?.id;
await this.setState({ messages, messageFetchTimestamp: timestamp });
this.setState({ messages, messageFetchTimestamp: timestamp }, () => {
if (this.state.isScrolledToBottom) {
this.updateReadMessages();
}
});
return { newTopMessage, previousTopMessage };
}
public getTimestamp() {
return Math.floor(Date.now() / 1000);
public updateReadMessages() {
const { isScrolledToBottom, messages, conversationKey } = this.state;
let unread;
if (!messages || messages.length === 0) {
return;
}
if (isScrolledToBottom) {
unread = messages[messages.length - 1];
} else {
unread = this.findNewestVisibleUnread();
}
if (unread) {
const model = window.ConversationController.get(conversationKey);
model.markRead.bind(model)(unread.attributes.received_at);
console.log(`[read] Unread:`, unread);
console.log(`[read] Model:`, model);
}
}
public findNewestVisibleUnread() {
const messageContainer = this.messageContainerRef.current;
if (!messageContainer) return null;
const { messages, unreadCount } = this.state;
const { length } = messages;
const viewportBottom = (messageContainer?.clientHeight + messageContainer?.scrollTop) || 0;
// Start with the most recent message, search backwards in time
let foundUnread = 0;
for (let i = length - 1; i >= 0; i -= 1) {
// Search the latest 30, then stop if we believe we've covered all known
// unread messages. The unread should be relatively recent.
// Why? local notifications can be unread but won't be reflected the
// conversation's unread count.
if (i > 30 && foundUnread >= unreadCount) {
return null;
}
const message = messages[i];
if (!message.attributes.unread) {
// eslint-disable-next-line no-continue
continue;
}
foundUnread += 1;
const el = document.getElementById(`${message.id}`);
if (!el) {
// eslint-disable-next-line no-continue
continue;
}
const top = el.offsetTop;
// If the bottom fits on screen, we'll call it visible. Even if the
// message is really tall.
const height = el.offsetHeight;
const bottom = top + height;
// We're fully below the viewport, continue searching up.
if (top > viewportBottom) {
// eslint-disable-next-line no-continue
continue;
}
if (bottom <= viewportBottom) {
return message;
}
// Continue searching up.
}
return null;
}
public async handleScroll() {
const { messages } = this.state;
const messageContainer = document.getElementsByClassName('messages-container')[0];
const messageContainer = this.messageContainerRef.current;
if (!messageContainer) return;
const isScrolledToBottom = messageContainer.scrollHeight - messageContainer.clientHeight <= messageContainer.scrollTop + 1;
// FIXME VINCE: Update unread count
// In models/conversations
// Update unread count by geting all divs of .session-message
// which are currently in view.
this.updateReadMessages();
// Pin scroll to bottom on new message, unless user has scrolled up
if (this.state.isScrolledToBottom !== isScrolledToBottom){
@ -412,9 +503,9 @@ export class SessionConversation extends React.Component<any, State> {
public scrollToUnread() {
const { messages, unreadCount } = this.state;
const message = messages[(messages.length - 1) - unreadCount];
message && this.scrollToMessage(message.id);
if(message) this.scrollToMessage(message.id);
}
public scrollToMessage(messageId: string) {
@ -428,7 +519,8 @@ export class SessionConversation extends React.Component<any, State> {
// { behavior: firstLoad ? 'auto' : 'smooth' }
// );
const messageContainer = document.getElementsByClassName('messages-container')[0];
const messageContainer = this.messageContainerRef.current;
if (!messageContainer) return;
messageContainer.scrollTop = messageContainer.scrollHeight - messageContainer.clientHeight;
}
@ -491,6 +583,9 @@ export class SessionConversation extends React.Component<any, State> {
onShowAllMedia: async () => {
conversation.updateHeader();
},
onUpdateGroupName: () => {
conversation.onUpdateGroupName();
},
onShowGroupMembers: async () => {
await conversation.showMembers();
conversation.updateHeader();
@ -633,10 +728,12 @@ export class SessionConversation extends React.Component<any, State> {
}
private onKeyDown(event: any) {
const messageContainer = this.messageContainerRef.current;
if (!messageContainer) return;
const selectionMode = !!this.state.selectedMessages.length;
const recordingMode = this.state.showRecordingView;
const messageContainer = document.getElementsByClassName('messages-container')[0];
const pageHeight = messageContainer.clientHeight;
const arrowScrollPx = 50;
const pageScrollPx = 0.80 * pageHeight;
@ -646,7 +743,7 @@ export class SessionConversation extends React.Component<any, State> {
console.log(`[vince][key] key: `, event.key);
console.log(`[vince][key] key: `, event.keyCode);
if (event.key === 'Escape') {
//
}
switch(event.key){
@ -674,6 +771,10 @@ export class SessionConversation extends React.Component<any, State> {
}
public getTimestamp() {
return Math.floor(Date.now() / 1000);
}
}

@ -198,6 +198,13 @@
dependencies:
electron-is-dev "*"
"@types/emoji-mart@^2.11.3":
version "2.11.3"
resolved "https://registry.yarnpkg.com/@types/emoji-mart/-/emoji-mart-2.11.3.tgz#9949f6a8a231aea47aac1b2d4212597b41140b07"
integrity sha512-pRlU6+CFIB+9+FwjGGCVtDQq78u7N0iUijrO0Qh1j9RJ6T23DSNNfe0X6kf81N4ubVhF9jVckCI1M3kHpkwjqA==
dependencies:
"@types/react" "*"
"@types/events@*":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"

Loading…
Cancel
Save