From 147b0936df93137c8f517a81e933600cdfa6cd53 Mon Sep 17 00:00:00 2001 From: Sarthak Sharma <7lovesharma7@gmail.com> Date: Wed, 4 Dec 2019 01:49:04 +0530 Subject: [PATCH] =?UTF-8?q?Feature=F0=9F=9A=80:=20Ability=20to=20see=20cha?= =?UTF-8?q?t=20history=20by=20scrolling=20up=20(#4903)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feature๐Ÿš€: Ability to see chat history by scrolling up * Test samples updated * Feature Enhancement ๐Ÿ›  : Jump back to recent messages If you scroll far away just click this button to go back ![img](https://i.imgur.com/HlhB8Up.gif) * Test case added * Bug Fix ๐Ÿž: Scrolling problem with large screen and re-rendering issue --- app/assets/stylesheets/chat.scss | 22 +++++ app/controllers/chat_channels_controller.rb | 2 +- .../__snapshots__/chat.test.jsx.snap | 16 ++++ app/javascript/chat/actions.js | 4 +- app/javascript/chat/chat.jsx | 94 ++++++++++++++++++- 5 files changed, 130 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/chat.scss b/app/assets/stylesheets/chat.scss index ed45919c0..c969534ec 100644 --- a/app/assets/stylesheets/chat.scss +++ b/app/assets/stylesheets/chat.scss @@ -1204,3 +1204,25 @@ width:96%; } } + +.jumpback{ + position: relative; +} + +.jumpback__messages{ + position: absolute; + bottom: 10px; + right: 20px; + background: var(--theme-background, #f5f6f7); + border: 2px solid #111; + padding: 10px; + border-radius: 20px; + cursor: pointer; + font-size: 14px; + text-transform: uppercase; + font-weight: bold; +} + +.jumpback__hide{ + display: none; +} \ No newline at end of file diff --git a/app/controllers/chat_channels_controller.rb b/app/controllers/chat_channels_controller.rb index 28aa55489..34bd33596 100644 --- a/app/controllers/chat_channels_controller.rb +++ b/app/controllers/chat_channels_controller.rb @@ -17,7 +17,7 @@ class ChatChannelsController < ApplicationController end def show - @chat_messages = @chat_channel.messages.includes(:user).order("created_at DESC").limit(50) + @chat_messages = @chat_channel.messages.includes(:user).order("created_at DESC").offset(params[:message_offset]).limit(150) end def create diff --git a/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap index 1935ce80f..be5723c27 100644 --- a/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap +++ b/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap @@ -176,12 +176,28 @@ exports[` should load chat 1`] = `
+
+
+ Jump Back +
+
diff --git a/app/javascript/chat/actions.js b/app/javascript/chat/actions.js index 53f54b424..7e516d8ff 100644 --- a/app/javascript/chat/actions.js +++ b/app/javascript/chat/actions.js @@ -1,5 +1,5 @@ -export function getAllMessages(channelId, successCb, failureCb) { - fetch(`/chat_channels/${channelId}`, { +export function getAllMessages(channelId, messageOffset, successCb, failureCb) { + fetch(`/chat_channels/${channelId}?message_offset=${messageOffset}`, { Accept: 'application/json', 'Content-Type': 'application/json', credentials: 'same-origin', diff --git a/app/javascript/chat/chat.jsx b/app/javascript/chat/chat.jsx index ac62d423e..ea3bb2bd4 100644 --- a/app/javascript/chat/chat.jsx +++ b/app/javascript/chat/chat.jsx @@ -60,6 +60,9 @@ export default class Chat extends Component { inviteChannels: [], soundOn: true, videoOn: true, + messageOffset: 0, + allMessagesLoaded: false, + currentMessageLocation: 0, }; } @@ -72,6 +75,7 @@ export default class Chat extends Component { isMobileDevice, channelPaginationNum, } = this.state; + this.setupChannels(chatChannels); const channelsForPusherSub = chatChannels.filter( this.channelTypeFilter('open'), @@ -113,12 +117,18 @@ export default class Chat extends Component { } componentDidUpdate() { - const { scrolled } = this.state; - if (document.getElementById('messagelist')) { + const { scrolled, currentMessageLocation } = this.state; + const messageList = document.getElementById('messagelist'); + if (messageList) { if (!scrolled) { scrollToBottom(); } } + + if (currentMessageLocation && messageList.scrollTop === 0) { + messageList.scrollTop = + messageList.scrollHeight - (currentMessageLocation + 30); + } } liveCoding = e => { @@ -257,13 +267,13 @@ export default class Chat extends Component { }; setupChannel = channelId => { - const { messages } = this.state; + const { messages, messageOffset } = this.state; if ( !messages[channelId] || messages[channelId].length === 0 || messages[channelId][0].reception_method === 'pushed' ) { - getAllMessages(channelId, this.receiveAllMessages); + getAllMessages(channelId, messageOffset, this.receiveAllMessages); } this.subscribePusher(`presence-channel-${channelId}`); }; @@ -853,17 +863,91 @@ export default class Chat extends Component { return ''; }; + handleMessageScroll = () => { + const { + allMessagesLoaded, + messages, + activeChannelId, + messageOffset, + } = this.state; + + const jumpbackButton = document.getElementById('jumpback_button'); + + if (this.scroller) { + const scrolledRatio = + (this.scroller.scrollTop + this.scroller.clientHeight) / + this.scroller.scrollHeight; + + if (scrolledRatio < 0.7) { + jumpbackButton.classList.remove('jumpback__hide'); + } else if (scrolledRatio > 0.8) { + jumpbackButton.classList.add('jumpback__hide'); + } + + if (this.scroller.scrollTop === 0 && !allMessagesLoaded) { + getAllMessages( + activeChannelId, + messageOffset + messages[activeChannelId].length, + this.addMoreMessages, + ); + const curretPosition = this.scroller.scrollHeight; + this.setState({ currentMessageLocation: curretPosition }); + } + } + }; + + addMoreMessages = res => { + const { chatChannelId, messages } = res; + + if (messages.length > 0) { + this.setState(prevState => ({ + messages: { + [chatChannelId]: [...messages, ...prevState.messages[chatChannelId]], + }, + })); + } else { + this.setState({ allMessagesLoaded: true }); + } + }; + + jumpBacktoBottom = () => { + scrollToBottom(); + document + .getElementById('jumpback_button') + .classList.remove('jumpback__hide'); + }; + renderActiveChatChannel = (channelHeader, incomingCall) => { const { state, props } = this; return (
{channelHeader} -
+
{ + this.scroller = scroller; + }} + id="messagelist" + > {this.renderMessages()} {incomingCall}
+
+
{ + if (e.keyCode === 13) this.jumpBacktoBottom(); + }} + > + Jump Back +
+