diff --git a/webApp/src/main/scala/views/ChatView.scala b/webApp/src/main/scala/views/ChatView.scala index 31fc317fa..8407a8531 100644 --- a/webApp/src/main/scala/views/ChatView.scala +++ b/webApp/src/main/scala/views/ChatView.scala @@ -74,25 +74,33 @@ object ChatView { width := "100%" ), div( + Styles.flex, + flexDirection.column, + flexDirection.columnReverse, // on resize, align scrolling to bottom (https://stackoverflow.com/questions/34779723/how-to-change-a-scrolling-element-behavior-on-resize-html-css-javascript/34790448#34790448) cls := "chat-history", - InfiniteScroll.onInfiniteScrollUp(shouldLoadInfinite) --> pageCounter, - backgroundColor <-- state.pageStyle.map(_.bgLightColor), - Rx { - state.page().parentId map { pageParentId => - VDomModifier( - chatHistory(state, pageParentId, currentReply, selectedNodes, inputFieldFocusTrigger, pageCounter, shouldLoadInfinite), - outerDragOptions(pageParentId) - ) + flexGrow := 1, + div( + flexGrow := 1, + backgroundColor <-- state.pageStyle.map(_.bgLightColor), + Rx { + state.page().parentId map { pageParentId => + VDomModifier( + chatHistory(state, pageParentId, currentReply, selectedNodes, inputFieldFocusTrigger, pageCounter, shouldLoadInfinite), + outerDragOptions(pageParentId) + ) + } + }, + + // clicking on background deselects + onClick foreach { e => if(e.currentTarget == e.target) selectedNodes() = Set.empty[SelectedNode] }, + scrollHandler.modifier, + // on page change, always scroll down + emitterRx(state.page).foreach { + scrollHandler.scrollToBottomInAnimationFrame() } - }, - - // clicking on background deselects - onClick foreach { e => if(e.currentTarget == e.target) selectedNodes() = Set.empty[SelectedNode] }, - scrollHandler.modifier, - // on page change, always scroll down - emitterRx(state.page).foreach { - scrollHandler.scrollToBottomInAnimationFrame() - } + ), + // since flexDirection is reversed, InfiniteScroll is written at the bottom instead of top + InfiniteScroll.onInfiniteScrollUp(shouldLoadInfinite) --> pageCounter, ), emitterRx(state.page).foreach { currentReply() = Set.empty[NodeId] }, onGlobalEscape(Set.empty[NodeId]) --> currentReply, diff --git a/webApp/src/main/scala/views/Elements.scala b/webApp/src/main/scala/views/Elements.scala index 63d7142d8..a48d0a93a 100644 --- a/webApp/src/main/scala/views/Elements.scala +++ b/webApp/src/main/scala/views/Elements.scala @@ -33,7 +33,7 @@ object Elements { def scrollToBottom(elem: dom.Element): Unit = { //TODO: scrollHeight is not yet available in jsdom tests: https://github.com/tmpvar/jsdom/issues/1013 try { - elem.scrollTop = elem.scrollHeight - elem.clientHeight + elem.scrollTop = elem.scrollHeight } catch { case _: Throwable => } // with NonFatal(_) it fails in the tests } @@ -52,6 +52,12 @@ object Elements { } // at bottom + 10 px tolerance def modifier(implicit ctx: Ctx.Owner) = VDomModifier( + managed(IO( + outwatch.dom.dsl.events.window.onResize.foreach { _ => + if(isScrolledToBottom.now) + scrollToBottomInAnimationFrame() + } + )), onDomPreUpdate foreach { isScrolledToBottom() = isScrolledToBottomNow }, diff --git a/webApp/src/main/scala/views/InfiniteScroll.scala b/webApp/src/main/scala/views/InfiniteScroll.scala index 7b21992a4..d04f7c35e 100644 --- a/webApp/src/main/scala/views/InfiniteScroll.scala +++ b/webApp/src/main/scala/views/InfiniteScroll.scala @@ -53,7 +53,7 @@ object InfiniteScroll { var numSteps = 0 VDomModifier( - overflow.auto, + overflowY.auto, shouldLoad.map { case true => VDomModifier( div( diff --git a/webApp/src/main/scala/views/ThreadView.scala b/webApp/src/main/scala/views/ThreadView.scala index 830c397e9..d53f677f2 100644 --- a/webApp/src/main/scala/views/ThreadView.scala +++ b/webApp/src/main/scala/views/ThreadView.scala @@ -63,19 +63,27 @@ object ThreadView { width := "100%" ), div( + Styles.flex, + flexDirection.column, + flexDirection.columnReverse, // on resize, align scrolling to bottom (https://stackoverflow.com/questions/34779723/how-to-change-a-scrolling-element-behavior-on-resize-html-css-javascript/34790448#34790448) cls := "chat-history", + flexGrow := 1, + div( + flexGrow := 1, + backgroundColor <-- state.pageStyle.map(_.bgLightColor), + chatHistory(state, selectedNodes, pageCounter, shouldLoadInfinite), + outerDragOptions, + + // clicking on background deselects + onClick foreach { e => if(e.currentTarget == e.target) selectedNodes() = Set.empty[SelectedNode] }, + scrollHandler.modifier, + // on page change, always scroll down + emitterRx(state.page).foreach { + scrollHandler.scrollToBottomInAnimationFrame() + } + ), + // since flexDirection is reversed, InfiniteScroll is written at the bottom instead of top InfiniteScroll.onInfiniteScrollUp(shouldLoadInfinite) --> pageCounter, - backgroundColor <-- state.pageStyle.map(_.bgLightColor), - chatHistory(state, selectedNodes, pageCounter, shouldLoadInfinite), - outerDragOptions, - - // clicking on background deselects - onClick foreach { e => if(e.currentTarget == e.target) selectedNodes() = Set.empty[SelectedNode] }, - scrollHandler.modifier, - // on page change, always scroll down - emitterRx(state.page).foreach { - scrollHandler.scrollToBottomInAnimationFrame() - } ), { def submitAction(str:String) = {