From 3dc6a0bc9aff34ec1fed494686f57a5c8c83e70a Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 10 Apr 2020 16:32:25 -0400 Subject: [PATCH 1/6] chat: add firefox-specific chat window --- pkg/interface/chat/src/js/components/chat.js | 157 ++++++++++++------- 1 file changed, 100 insertions(+), 57 deletions(-) diff --git a/pkg/interface/chat/src/js/components/chat.js b/pkg/interface/chat/src/js/components/chat.js index 9b98e1612..b3837d562 100644 --- a/pkg/interface/chat/src/js/components/chat.js +++ b/pkg/interface/chat/src/js/components/chat.js @@ -16,48 +16,48 @@ import { deSig } from '/lib/util'; export class ChatScreen extends Component { constructor(props) { super(props); - + this.state = { numPages: 1, scrollLocked: false }; - + this.hasAskedForMessages = false; this.onScroll = this.onScroll.bind(this); - + this.updateReadInterval = setInterval( this.updateReadNumber.bind(this), 1000 ); } - + componentDidMount() { this.updateReadNumber(); this.askForMessages(); } - + componentWillUnmount() { if (this.updateReadInterval) { clearInterval(this.updateReadInterval); this.updateReadInterval = null; } } - + componentDidUpdate(prevProps, prevState) { const { props, state } = this; - + if ( prevProps.match.params.station !== props.match.params.station || prevProps.match.params.ship !== props.match.params.ship ) { this.hasAskedForMessages = false; - + if (props.envelopes.length < 100) { this.askForMessages(); } - + clearInterval(this.updateReadInterval); - + this.setState( { scrollLocked: false }, () => { @@ -76,19 +76,22 @@ export class ChatScreen extends Component { props.history.push("/~chat"); } else if ( props.envelopes.length - prevProps.envelopes.length >= - 200 + 40 ) { this.hasAskedForMessages = false; + this.setState({ scrollLocked: false }, () => { + this.scrollToBottom(); + }) } } - + updateReadNumber() { const { props, state } = this; if (props.read < props.length) { props.api.chat.read(props.station); } } - + askForMessages() { const { props, state } = this; @@ -120,19 +123,20 @@ export class ChatScreen extends Component { props.subscription.fetchMessages(start, end - 1, props.station); } } - + scrollToBottom() { if (!this.state.scrollLocked && this.scrollElement) { - this.scrollElement.scrollIntoView({ behavior: "smooth" }); + this.scrollElement.scrollIntoView(); } } - + onScroll(e) { if ( - navigator.userAgent.includes("Safari") && - navigator.userAgent.includes("Chrome") + (navigator.userAgent.includes("Safari") && + navigator.userAgent.includes("Chrome")) || + navigator.userAgent.includes("Firefox") ) { - // Google Chrome + // Google Chrome and Firefox if (e.target.scrollTop === 0) { this.setState( { @@ -177,32 +181,33 @@ export class ChatScreen extends Component { console.log("Your browser is not supported."); } } - - render() { + + chatWindow() { + + // Replace with just the "not Firefox" implementation + // when Firefox #1042151 is patched. + const { props, state } = this; - + let messages = props.envelopes.slice(0); - - let lastMsgNum = messages.length > 0 ? messages.length : 0; - if (messages.length > 100 * state.numPages) { messages = messages.slice( messages.length - 100 * state.numPages, messages.length ); } - + let pendingMessages = props.pendingMessages.has(props.station) ? props.pendingMessages.get(props.station) : []; - - pendingMessages.map(function(value) { + + pendingMessages.map(function (value) { return (value.pending = true); }); - + let reversedMessages = messages.concat(pendingMessages); reversedMessages = reversedMessages.reverse(); - + reversedMessages = reversedMessages.map((msg, i) => { // Render sigil if previous message is not by the same sender let aut = ["author"]; @@ -213,7 +218,7 @@ export class ChatScreen extends Component { let paddingBot = _.get(reversedMessages[i - 1], aut) !== _.get(msg, aut, msg.author); - + return ( ); }); - + + if (navigator.userAgent.includes("Firefox")) { + return ( +
+
+
{ + this.scrollElement = el; + }}>
+ {( + !(props.station in props.chatSynced) && + (reversedMessages.length > 0) + ) ? ( + + ) : (
) + } + {reversedMessages} +
+
+ )} + else { + return ( +
+
{ + this.scrollElement = el; + }}>
+ {( + !(props.station in props.chatSynced) && + (reversedMessages.length > 0) + ) ? ( + + ) : (
) + } + {reversedMessages} +
+ )} + } + + render() { + const { props, state } = this; + + let messages = props.envelopes.slice(0); + + let lastMsgNum = messages.length > 0 ? messages.length : 0; + let group = Array.from(props.permission.who.values()); - + const isinPopout = props.popout ? "popout/" : ""; - + let ownerContact = (window.ship in props.contacts) ? props.contacts[window.ship] : false; - + let title = props.station.substr(1); - + if (props.association && "metadata" in props.association) { title = props.association.metadata.title !== "" ? props.association.metadata.title : props.station.substr(1); } - + return (
-
-
{ - this.scrollElement = el; - }}>
- { ( - !(props.station in props.chatSynced) && - (reversedMessages.length > 0) - ) ? ( - - ) : (
) - } - {reversedMessages} -
+ {this.chatWindow()} Date: Fri, 10 Apr 2020 16:42:05 -0400 Subject: [PATCH 2/6] chat: only scrolltobottom if first backlog grab --- pkg/interface/chat/src/js/components/chat.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/interface/chat/src/js/components/chat.js b/pkg/interface/chat/src/js/components/chat.js index b3837d562..8d626fded 100644 --- a/pkg/interface/chat/src/js/components/chat.js +++ b/pkg/interface/chat/src/js/components/chat.js @@ -79,9 +79,11 @@ export class ChatScreen extends Component { 40 ) { this.hasAskedForMessages = false; - this.setState({ scrollLocked: false }, () => { - this.scrollToBottom(); - }) + if (prevProps.envelopes.length <= 20) { + this.setState({ scrollLocked: false }, () => { + this.scrollToBottom(); + }) + } } } From 948dbe85e7fb344fa8c95ae993d723438ad73d00 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 10 Apr 2020 16:55:23 -0400 Subject: [PATCH 3/6] chat: scrollToBottom on mount for FF --- pkg/interface/chat/src/js/components/chat.js | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/interface/chat/src/js/components/chat.js b/pkg/interface/chat/src/js/components/chat.js index 8d626fded..2d74702cd 100644 --- a/pkg/interface/chat/src/js/components/chat.js +++ b/pkg/interface/chat/src/js/components/chat.js @@ -32,6 +32,7 @@ export class ChatScreen extends Component { } componentDidMount() { + this.scrollToBottom(); this.updateReadNumber(); this.askForMessages(); } From 9ddf06d2a1b3aa2a12494a446f675530fb46e5e9 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 14 Apr 2020 16:34:10 +1000 Subject: [PATCH 4/6] chat-fe: correct FF scroll behaviour Firefox treats the reflow in the scroll container weirdly so for the scrollback case, we save the position to restore later, when the backlog messages come in. For the 'locked at bottom' case we add a new state variable and rescroll based on that. --- pkg/interface/chat/src/js/components/chat.js | 53 ++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/pkg/interface/chat/src/js/components/chat.js b/pkg/interface/chat/src/js/components/chat.js index 0ef1aac10..1194618c8 100644 --- a/pkg/interface/chat/src/js/components/chat.js +++ b/pkg/interface/chat/src/js/components/chat.js @@ -19,10 +19,15 @@ export class ChatScreen extends Component { this.state = { numPages: 1, - scrollLocked: false + scrollLocked: false, + // only for FF + lastScrollHeight: null, + scrollBottom: false }; this.hasAskedForMessages = false; + + this.lastScrollEvent = null; this.onScroll = this.onScroll.bind(this); this.updateReadInterval = setInterval( @@ -78,12 +83,22 @@ export class ChatScreen extends Component { } else if ( props.envelopes.length >= prevProps.envelopes.length + 10 ) { + if(navigator.userAgent.includes('Firefox')) { + // new messages came in, restore FF scroll pos + this.recalculateScrollTop(); + } this.hasAskedForMessages = false; if (prevProps.envelopes.length <= 20) { this.setState({ scrollLocked: false }, () => { this.scrollToBottom(); }) } + } else if ( + navigator.userAgent.includes("Firefox") && + props.length !== prevProps.length && + state.scrollBottom + ) { + this.scrollToBottom(); } } @@ -123,10 +138,29 @@ export class ChatScreen extends Component { scrollToBottom() { if (!this.state.scrollLocked && this.scrollElement) { - this.scrollElement.scrollIntoView(); + if(navigator.userAgent.includes('Firefox')) { + this.scrollElement.scrollIntoView(false); + } else { + this.scrollElement.scrollIntoView(true); + } } } + // Restore chat position on FF when new messages come in + recalculateScrollTop() { + if(!this.lastScrollEvent) { + return; + } + + const { lastScrollHeight } = this.state; + let { target } = this.lastScrollEvent; + if(target.scrollTop !== 0) { + return; + } + target.scrollTop = target.scrollHeight - lastScrollHeight; + + } + onScroll(e) { if ( (navigator.userAgent.includes("Safari") && @@ -135,6 +169,16 @@ export class ChatScreen extends Component { ) { // Google Chrome and Firefox if (e.target.scrollTop === 0) { + + // Save scroll position for FF + if (navigator.userAgent.includes('Firefox')) { + + e.persist(); + this.lastScrollEvent = e; + this.setState({ + lastScrollHeight: e.target.scrollHeight + }) + } this.setState( { numPages: this.state.numPages + 1, @@ -150,8 +194,11 @@ export class ChatScreen extends Component { ) { this.setState({ numPages: 1, - scrollLocked: false + scrollLocked: false, + scrollBottom: true }); + } else if (navigator.userAgent.includes('Firefox')) { + this.setState({ scrollBottom: false }); } } else if (navigator.userAgent.includes("Safari")) { // Safari From 1b90328f3950a07882de4141fd274c87cd8b6d58 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 17 Apr 2020 14:20:28 +1000 Subject: [PATCH 5/6] chat-fe: fix index issue with pending messages To produce message elements we iterate over pendingMessage ++ messages, but to check whether they're adjacent to a message by the same author, we index back into messages, which produces incorrect results when pendingMessages is nonEmpty --- pkg/interface/chat/src/js/components/chat.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/interface/chat/src/js/components/chat.js b/pkg/interface/chat/src/js/components/chat.js index 502ffcec3..416e18919 100644 --- a/pkg/interface/chat/src/js/components/chat.js +++ b/pkg/interface/chat/src/js/components/chat.js @@ -244,11 +244,14 @@ export class ChatScreen extends Component { ? props.pendingMessages.get(props.station) : []; + pendingMessages.map(function (value) { return (value.pending = true); }); - let messageElements = pendingMessages.concat(messages).map((msg, i) => { + messages = pendingMessages.concat(messages); + + let messageElements = messages.map((msg, i) => { // Render sigil if previous message is not by the same sender let aut = ["author"]; let renderSigil = @@ -284,6 +287,7 @@ export class ChatScreen extends Component { this.scrollElement = el; }}>
{( + props.chatSynced && !(props.station in props.chatSynced) && (messages.length > 0) ) ? ( From 5fbe955874efa18b185c8594ff79e35d1295d98b Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 17 Apr 2020 15:15:29 +1000 Subject: [PATCH 6/6] chat-fe: more FF scrollback fixes --- pkg/interface/chat/src/js/components/chat.js | 58 +++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/pkg/interface/chat/src/js/components/chat.js b/pkg/interface/chat/src/js/components/chat.js index 416e18919..28d425e36 100644 --- a/pkg/interface/chat/src/js/components/chat.js +++ b/pkg/interface/chat/src/js/components/chat.js @@ -12,6 +12,12 @@ import { ChatTabBar } from '/components/lib/chat-tabbar'; import { ChatInput } from '/components/lib/chat-input'; import { deSig } from '/lib/util'; +function getNumPending(props) { + const result = props.pendingMessages.has(props.station) + ? props.pendingMessages.get(props.station).length + : 0; + return result; +} export class ChatScreen extends Component { constructor(props) { @@ -22,12 +28,13 @@ export class ChatScreen extends Component { scrollLocked: false, // only for FF lastScrollHeight: null, - scrollBottom: false + scrollBottom: true }; this.hasAskedForMessages = false; + this.lastNumPending = 0; - this.lastScrollEvent = null; + this.scrollContainer = null; this.onScroll = this.onScroll.bind(this); this.updateReadInterval = setInterval( @@ -83,22 +90,26 @@ export class ChatScreen extends Component { } else if ( props.envelopes.length >= prevProps.envelopes.length + 10 ) { - if(navigator.userAgent.includes('Firefox')) { - // new messages came in, restore FF scroll pos - this.recalculateScrollTop(); - } this.hasAskedForMessages = false; - if (prevProps.envelopes.length <= 20) { - this.setState({ scrollLocked: false }, () => { + } + + // FF logic + if ( + navigator.userAgent.includes("Firefox") && + (props.length !== prevProps.length || + props.envelopes.length !== prevProps.envelopes.length || + getNumPending(props) !== this.lastNumPending || + state.numPages !== prevState.numPages) + ) { + if(state.scrollBottom) { + setTimeout(() => { this.scrollToBottom(); }) + } else { + this.recalculateScrollTop(); } - } else if ( - navigator.userAgent.includes("Firefox") && - props.length !== prevProps.length && - state.scrollBottom - ) { - this.scrollToBottom(); + + this.lastNumPending = getNumPending(props); } } @@ -138,23 +149,20 @@ export class ChatScreen extends Component { scrollToBottom() { if (!this.state.scrollLocked && this.scrollElement) { - if(navigator.userAgent.includes('Firefox')) { - this.scrollElement.scrollIntoView(false); - } else { - this.scrollElement.scrollIntoView(true); - } + this.scrollElement.scrollIntoView(); } } // Restore chat position on FF when new messages come in recalculateScrollTop() { - if(!this.lastScrollEvent) { + if(!this.scrollContainer) { return; } const { lastScrollHeight } = this.state; - let { target } = this.lastScrollEvent; - if(target.scrollTop !== 0) { + let target = this.scrollContainer; + let newScrollTop = this.scrollContainer.scrollHeight - lastScrollHeight; + if(target.scrollTop !== 0 || newScrollTop === target.scrollTop) { return; } target.scrollTop = target.scrollHeight - lastScrollHeight; @@ -173,8 +181,6 @@ export class ChatScreen extends Component { // Save scroll position for FF if (navigator.userAgent.includes('Firefox')) { - e.persist(); - this.lastScrollEvent = e; this.setState({ lastScrollHeight: e.target.scrollHeight }) @@ -277,9 +283,9 @@ export class ChatScreen extends Component { if (navigator.userAgent.includes("Firefox")) { return ( -
+
{ this.scrollContainer = e; }}>