Skip to content

Commit 2430286

Browse files
committed
fix: keep the page shell rendered while a discussion loads
Navigating between discussions remounts DiscussionPage, and its view bailed out to a bare loading indicator until the discussion arrived — blanking the entire layout, including the pinned discussion list pane, whose contents are already cached and need no network. With the pane pinned, every discussion-to-discussion navigation flashed the whole page white. PageStructure was already built for this: given loading, it renders the spinner inside the main area, keeps the pane, and skips the hero, sidebar and content closures so none of them run without a discussion. The early return was defeating that shell. The pane also already restores its scroll position when coming from another discussion page, so the rebuilt pane is visually stable. The view now always renders PageStructure, passing loading while the discussion or the post stream components are still pending.
1 parent 2553eb5 commit 2430286

2 files changed

Lines changed: 34 additions & 6 deletions

File tree

framework/core/js/src/forum/components/DiscussionPage.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import Page, { IPageAttrs } from '../../common/components/Page';
55
import ItemList from '../../common/utils/ItemList';
66
import DiscussionHero from './DiscussionHero';
77
import DiscussionListPane from './DiscussionListPane';
8-
import LoadingIndicator from '../../common/components/LoadingIndicator';
98
import SplitDropdown from '../../common/components/SplitDropdown';
109
import listItems from '../../common/helpers/listItems';
1110
import DiscussionControls from '../utils/DiscussionControls';
@@ -94,19 +93,24 @@ export default class DiscussionPage<CustomAttrs extends IDiscussionPageAttrs = I
9493
}
9594

9695
view() {
97-
if (this.loading || !this.discussion) {
98-
return <LoadingIndicator />;
99-
}
96+
// Keep the page shell rendered while the discussion loads. Bailing out to
97+
// a bare loading indicator here blanks the whole layout on every
98+
// discussion-to-discussion navigation — including the pinned discussion
99+
// list pane, whose contents are already cached and need no network.
100+
// PageStructure renders the spinner inside the main area and skips the
101+
// hero/sidebar/content closures while `loading` is set, so none of them
102+
// run without a discussion.
103+
const loading = this.loading || !this.discussion;
100104

101105
return (
102106
<PageStructure
103107
className="DiscussionPage"
104-
loading={this.loading}
108+
loading={loading}
105109
hero={this.hero.bind(this)}
106110
sidebar={this.sidebar.bind(this)}
107111
pane={() => <DiscussionListPane state={app.discussions} />}
108112
>
109-
{this.loading || (
113+
{loading || (
110114
<div className="DiscussionPage-stream">
111115
<this.PostStream discussion={this.discussion} stream={this.stream} onPositionChange={this.positionChanged.bind(this)} />
112116
</div>

framework/core/js/tests/integration/forum/components/DiscussionPage.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,19 @@ describe('DiscussionPage', () => {
5757
beforeAll(() => {
5858
bootstrapForum();
5959
app.boot();
60+
6061
});
6162

6263
beforeEach(() => {
6364
routeParams = {};
6465
findCalls = [];
6566
pendingFinds = [];
6667

68+
// The pane helper is created by ForumApplication when mounting the real
69+
// app; the pane component's lifecycle hooks expect it to exist.
70+
// @ts-ignore
71+
app.pane = app.pane || { enable() {}, disable() {}, hide() {}, show() {}, onmouseleave() {}, togglePinned() {} };
72+
6773
// @ts-ignore
6874
m.route.param = (key?: string) => (key ? routeParams[key] : routeParams);
6975

@@ -115,6 +121,24 @@ describe('DiscussionPage', () => {
115121
pending!.resolve(models);
116122
};
117123

124+
test('keeps the page structure while the discussion loads instead of blanking', () => {
125+
// Navigating between discussions remounts the page. Rendering a bare
126+
// loading indicator during the fetch blanks the whole layout — including
127+
// the pinned discussion list pane, whose contents are already cached and
128+
// need no network. The shell must stay; only the main area may spin.
129+
seedDiscussionFromList();
130+
routeParams = { id: '476-test-discussion' };
131+
132+
const page = mountPage();
133+
134+
// Nothing resolved yet: the page is loading. The pane slot must exist
135+
// (its contents render whenever the list state has items), and the
136+
// spinner must be inside the main area, not the whole page.
137+
expect(page).toHaveElement('.DiscussionPage');
138+
expect(page).toHaveElement('.Page-pane');
139+
expect(page).toHaveElement('#page-main .LoadingIndicator');
140+
});
141+
118142
test('requests the discussion and the post window in parallel when the discussion is known', () => {
119143
seedDiscussionFromList();
120144
routeParams = { id: '476-test-discussion' };

0 commit comments

Comments
 (0)