forum: make Discussion Page properly redraw PostStream - don't store vnode

Issue is that the code now looks like an ugly mess. :/
This commit is contained in:
David Sevilla Martin 2020-03-10 17:56:25 -04:00
parent b9583943c5
commit dcb3cc1701
No known key found for this signature in database
GPG Key ID: F764F1417E16B15F
5 changed files with 45 additions and 51 deletions

4
js/dist/forum.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -127,7 +127,7 @@ export default class CommentPost extends Post {
const post = this.props.post;
const props = { post };
items.add('user', this.postUser, 100);
items.add('user', <PostUser post={this.props.post} />, 100);
// items.add('meta', PostMeta.component(props));
if (post.isEdited() && !post.isHidden()) {

View File

@ -10,8 +10,6 @@ import DiscussionControls from '../utils/DiscussionControls';
import Discussion from '../../common/models/Discussion';
import Post from '../../common/models/Post';
import { Vnode } from 'mithril';
/**
* The `DiscussionPage` component displays a whole discussion page, including
* the discussion list pane, the hero, the posts, and the sidebar.
@ -27,7 +25,10 @@ export default class DiscussionPage extends Page {
*/
near?: number;
stream?: Vnode<{}, PostStream>;
stream!: PostStream;
scrubber!: PostStreamScrubber;
includedPosts: Post[] = [];
oninit(vnode) {
super.oninit(vnode);
@ -63,7 +64,7 @@ export default class DiscussionPage extends Page {
const near = m.route.param('near') || '1';
if (near !== String(this.near)) {
this.stream?.state.goToNumber(near);
this.stream.goToNumber(near);
}
this.near = null;
@ -89,6 +90,23 @@ export default class DiscussionPage extends Page {
view() {
const discussion = this.discussion;
// Set up the post stream for this discussion, along with the first page of
// posts we want to display. Tell the stream to scroll down and highlight
// the specific post that was routed to.
const postStream = (
<PostStream
discussion={discussion}
includedPosts={this.includedPosts}
oninit={vnode => {
this.stream = vnode.state;
this.scrubber.stream = vnode.state;
this.stream.on('positionChanged', this.positionChanged.bind(this));
this.stream.goToNumber(m.route.param('near') || (this.includedPosts[0] && this.includedPosts[0].number()), true);
}}
/>
);
return (
<div className="DiscussionPage">
{app.cache.discussionList ? (
@ -107,7 +125,7 @@ export default class DiscussionPage extends Page {
<nav className="DiscussionPage-nav">
<ul>{listItems(this.sidebarItems().toArray())}</ul>
</nav>
<div className="DiscussionPage-stream">{this.stream}</div>
<div className="DiscussionPage-stream">{postStream}</div>
</div>,
]
: LoadingIndicator.component({ className: 'LoadingIndicator--block' })}
@ -173,11 +191,10 @@ export default class DiscussionPage extends Page {
// extensions. We need to distinguish the two so we don't end up displaying
// the wrong posts. We do so by filtering out the posts that don't have
// the 'discussion' relationship linked, then sorting and splicing.
let includedPosts: Post[] = [];
if (discussion.payload && discussion.payload.included) {
const discussionId = discussion.id();
includedPosts = discussion.payload.included
this.includedPosts = discussion.payload.included
.filter(
record =>
record.type === 'posts' &&
@ -191,22 +208,6 @@ export default class DiscussionPage extends Page {
}
m.redraw();
const positionChanged = this.positionChanged.bind(this);
// Set up the post stream for this discussion, along with the first page of
// posts we want to display. Tell the stream to scroll down and highlight
// the specific post that was routed to.
this.stream = (
<PostStream
discussion={discussion}
includedPosts={includedPosts}
oncreate={function(this: PostStream) {
this.on('positionChanged', positionChanged);
this.goToNumber(m.route.param('near') || (includedPosts[0] && includedPosts[0].number()), true);
}}
/>
);
}
/**
@ -259,7 +260,7 @@ export default class DiscussionPage extends Page {
})
);
items.add('scrubber', <PostStreamScrubber stream={this.stream} className="App-titleControl" />, -100);
items.add('scrubber', <PostStreamScrubber oninit={vnode => (this.scrubber = vnode.state)} className="App-titleControl" />, -100);
return items;
}

View File

@ -1,21 +1,15 @@
import Component, { ComponentProps } from '../../common/Component';
import Component from '../../common/Component';
import icon from '../../common/helpers/icon';
import ScrollListener from '../../common/utils/ScrollListener';
import SubtreeRetainer from '../../common/utils/SubtreeRetainer';
import formatNumber from '../../common/utils/formatNumber';
import PostStream from './PostStream';
import { EventHandler } from '../../common/utils/Evented';
import { Vnode } from 'mithril';
export interface PostStreamScrubberProps extends ComponentProps {
stream: Vnode<{}, PostStream>;
}
/**
* The `PostStreamScrubber` component displays a scrubber which can be used to
* navigate/scrub through a post stream.
*/
export default class PostStreamScrubber<T extends PostStreamScrubberProps = PostStreamScrubberProps> extends Component<T> {
export default class PostStreamScrubber extends Component {
handlers: { [key: string]: EventHandler } = {};
/**
@ -51,13 +45,12 @@ export default class PostStreamScrubber<T extends PostStreamScrubberProps = Post
mouseStart = 0;
indexStart = 0;
oninit(vnode) {
super.oninit(vnode);
}
// Added when Component is initialized through `oninit` prop
stream!: PostStream;
view() {
const count = this.count();
const unreadCount = this.props.stream.state?.discussion.unreadCount() || 0;
const unreadCount = this.stream?.discussion.unreadCount() || 0;
const unreadPercent = count ? Math.min(count - this.index, unreadCount) / count : 0;
const viewing = app.translator.transChoice('core.forum.post_scrubber.viewing_text', count, {
@ -129,7 +122,7 @@ export default class PostStreamScrubber<T extends PostStreamScrubberProps = Post
* Go to the first post in the discussion.
*/
goToFirst() {
this.props.stream.state?.goToFirst();
this.stream.goToFirst();
this.index = 0;
this.renderScrollbar(true);
}
@ -138,7 +131,7 @@ export default class PostStreamScrubber<T extends PostStreamScrubberProps = Post
* Go to the last post in the discussion.
*/
goToLast() {
this.props.stream.state?.goToLast();
this.stream.goToLast();
this.index = this.count();
this.renderScrollbar(true);
}
@ -147,7 +140,7 @@ export default class PostStreamScrubber<T extends PostStreamScrubberProps = Post
* Get the number of posts in the discussion.
*/
count(): number {
return this.props.stream.state?.count() || 0;
return this.stream?.count() || 0;
}
/**
@ -171,7 +164,7 @@ export default class PostStreamScrubber<T extends PostStreamScrubberProps = Post
* posts.
*/
onscroll(top: number) {
const stream = this.props.stream.state;
const stream = this.stream;
if (!stream || stream.paused || !stream.$()) return;
@ -184,7 +177,7 @@ export default class PostStreamScrubber<T extends PostStreamScrubberProps = Post
* current scroll position.
*/
update(scrollTop?: number) {
const stream = this.props.stream.state;
const stream = this.stream;
const marginTop = stream.getMarginTop();
const viewportTop = scrollTop + marginTop;
@ -253,8 +246,8 @@ export default class PostStreamScrubber<T extends PostStreamScrubberProps = Post
// When the post stream begins loading posts at a certain index, we want our
// scrubber scrollbar to jump to that position.
this.props.stream.state.on('unpaused', (this.handlers.streamWasUnpaused = this.streamWasUnpaused.bind(this)));
this.props.stream.state.on('update', () => this.update());
this.stream.on('unpaused', (this.handlers.streamWasUnpaused = this.streamWasUnpaused.bind(this)));
this.stream.on('update', () => this.update());
this.scrollListener.start();
@ -293,7 +286,7 @@ export default class PostStreamScrubber<T extends PostStreamScrubberProps = Post
ondestroy() {
this.scrollListener.stop();
this.props.stream.state.off('unpaused', this.handlers.streamWasUnpaused);
this.stream.off('unpaused', this.handlers.streamWasUnpaused);
$(window).off('resize', this.handlers.onresize);
@ -384,7 +377,7 @@ export default class PostStreamScrubber<T extends PostStreamScrubberProps = Post
this.mouseStart = e.clientY || e.originalEvent.touches[0].clientY;
this.indexStart = this.index;
this.dragging = true;
this.props.stream.state.paused = true;
this.stream.paused = true;
$('body').css('cursor', 'move');
}
@ -417,7 +410,7 @@ export default class PostStreamScrubber<T extends PostStreamScrubberProps = Post
// If the index we've landed on is in a gap, then tell the stream-
// content that we want to load those posts.
const intIndex = Math.floor(this.index);
this.props.stream.state?.goToIndex(intIndex);
this.stream.goToIndex(intIndex);
this.renderScrollbar(true);
}
@ -439,7 +432,7 @@ export default class PostStreamScrubber<T extends PostStreamScrubberProps = Post
// content component to jump to that index.
let offsetIndex = offsetPercent / this.percentPerPost().index;
offsetIndex = Math.max(0, Math.min(this.count() - 1, offsetIndex));
this.props.stream.state?.goToIndex(Math.floor(offsetIndex));
this.stream.goToIndex(Math.floor(offsetIndex));
this.index = offsetIndex;
this.renderScrollbar(true);