diff --git a/app/assets/javascripts/discourse/app/components/topic-navigation.js b/app/assets/javascripts/discourse/app/components/topic-navigation.js index aad6ca44209..8e5c07ad9cf 100644 --- a/app/assets/javascripts/discourse/app/components/topic-navigation.js +++ b/app/assets/javascripts/discourse/app/components/topic-navigation.js @@ -13,10 +13,7 @@ const MIN_WIDTH_TIMELINE = 924, MIN_HEIGHT_TIMELINE = 325; export default Component.extend(PanEvents, { - classNameBindings: [ - "info.topicProgressExpanded:topic-progress-expanded", - "info.renderTimeline:render-timeline", - ], + classNameBindings: ["info.topicProgressExpanded:topic-progress-expanded"], composerOpen: null, info: null, isPanning: false, diff --git a/app/assets/javascripts/discourse/app/components/topic-progress.js b/app/assets/javascripts/discourse/app/components/topic-progress.js index 22661d04ca7..bf772e19324 100644 --- a/app/assets/javascripts/discourse/app/components/topic-progress.js +++ b/app/assets/javascripts/discourse/app/components/topic-progress.js @@ -2,12 +2,13 @@ import discourseComputed, { bind } from "discourse-common/utils/decorators"; import Component from "@ember/component"; import I18n from "I18n"; import { alias } from "@ember/object/computed"; -import { scheduleOnce } from "@ember/runloop"; +import { later, scheduleOnce } from "@ember/runloop"; export default Component.extend({ elementId: "topic-progress-wrapper", - classNameBindings: ["docked"], + classNameBindings: ["docked", "withTransitions"], docked: false, + withTransitions: null, progressPosition: null, postStream: alias("topic.postStream"), _streamPercentage: null, @@ -98,6 +99,10 @@ export default Component.extend({ scheduleOnce("afterRender", this, this._topicScrolled, this.prevEvent); } scheduleOnce("afterRender", this, this._startObserver); + + // start CSS transitions a tiny bit later + // to avoid jumpiness on initial topic load + later(this._addCssTransitions, 500); }, willDestroyElement() { @@ -108,6 +113,14 @@ export default Component.extend({ .off("topic:current-post-scrolled", this, this._topicScrolled); }, + @bind + _addCssTransitions() { + if (this.isDestroying || this.isDestroyed) { + return; + } + this.set("withTransitions", true); + }, + _startObserver() { if ("IntersectionObserver" in window) { this._topicBottomObserver = this._setupObserver(); @@ -118,12 +131,15 @@ export default Component.extend({ }, _setupObserver() { - const composerH = - document.querySelector("#reply-control")?.clientHeight || 0; + // minimum 50px here ensures element is not docked when + // scrolling down quickly, it causes post stream refresh loop + // on Android + const bottomIntersectionMargin = + document.querySelector("#reply-control")?.clientHeight || 50; return new IntersectionObserver(this._intersectionHandler, { - threshold: 0.1, - rootMargin: `0px 0px -${composerH}px 0px`, + threshold: 1, + rootMargin: `0px 0px -${bottomIntersectionMargin}px 0px`, }); }, diff --git a/app/assets/stylesheets/common/base/topic.scss b/app/assets/stylesheets/common/base/topic.scss index 8161b62189e..58e47fcbb80 100644 --- a/app/assets/stylesheets/common/base/topic.scss +++ b/app/assets/stylesheets/common/base/topic.scss @@ -42,28 +42,31 @@ $topic-progress-height: 42px; } // timeline - .topic-navigation { - &.render-timeline { + @media screen and (min-width: 925px) { + // at 925px viewport width and above the timeline is visible (see topic-navigation.js) + .topic-navigation { grid-area: timeline; align-self: start; @include sticky; top: calc(var(--header-offset, 60px) + 2em); margin-left: 1em; z-index: z("timeline"); - } - &:not(.render-timeline) { - // span all columns of grid layout so RTL can go as far left as possible - grid-column: 1/-1; - // save the space to avoid jumping when child gets fixed-positioned - min-height: $topic-progress-height; - } - &.topic-progress-expanded { - z-index: z("fullscreen"); + + &.topic-progress-expanded { + z-index: z("fullscreen"); + } } } - // progress bar @media screen and (max-width: 924px) { + grid-template-areas: "posts"; + grid-template-columns: auto; + .topic-navigation { + grid-area: posts; + grid-row: 2; + width: auto; + } + .timeline-container:not(.timeline-fullscreen) { display: none; // hiding this because sometimes the JS switch lags and causes layout issues } @@ -92,7 +95,6 @@ $topic-progress-height: 42px; #topic-progress-wrapper { position: fixed; bottom: 0px; - transition: bottom 0.1s, margin-bottom 0.1s; right: 10px; margin: 0 auto; display: flex; @@ -133,6 +135,14 @@ $topic-progress-height: 42px; margin-right: calc(#{$reply-area-max-width} / 2 * -1); } } + + &.with-transitions { + transition: bottom 0.2s, margin-bottom 0.2s; + + #topic-progress .bg { + transition: width 0.5s; + } + } } #topic-progress { @@ -170,7 +180,6 @@ $topic-progress-height: 42px; bottom: 0; width: var(--progress-bg-width, 0); background-color: var(--tertiary-low); - transition: width 0.75s; } } diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index 07e132b6939..ab3737de0f8 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -613,8 +613,7 @@ blockquote { box-sizing: border-box; } -.topic-area > .loading-container, -.topic-navigation:not(.render-timeline) { +.topic-area > .loading-container { // loader needs to be same width as posts width: calc( #{$topic-avatar-width} + #{$topic-body-width} + diff --git a/app/assets/stylesheets/mobile/topic.scss b/app/assets/stylesheets/mobile/topic.scss index 9eb3c5b49a3..ec5b6202ddc 100644 --- a/app/assets/stylesheets/mobile/topic.scss +++ b/app/assets/stylesheets/mobile/topic.scss @@ -1,8 +1,3 @@ -.container.posts { - grid-template-areas: "posts"; - grid-template-columns: auto; -} - .post-info a { color: var(--primary-medium); }