From 9977d491cfab52287b5053289abadd48b82fa91f Mon Sep 17 00:00:00 2001 From: flarum-bot Date: Fri, 14 Feb 2025 08:29:43 +0000 Subject: [PATCH] Bundled output for commit 9758592daae3710c44aa0d754e2d4d2d666cc5d3 Includes transpiled JS/TS, and Typescript declaration files (typings). [skip ci] --- .../core/js/dist-typings/forum/components/PostStream.d.ts | 4 ++++ framework/core/js/dist/forum/components/PostStream.js | 2 +- framework/core/js/dist/forum/components/PostStream.js.map | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/framework/core/js/dist-typings/forum/components/PostStream.d.ts b/framework/core/js/dist-typings/forum/components/PostStream.d.ts index 16d3dfcb1..ba52db501 100644 --- a/framework/core/js/dist-typings/forum/components/PostStream.d.ts +++ b/framework/core/js/dist-typings/forum/components/PostStream.d.ts @@ -16,6 +16,10 @@ export default class PostStream extends Component} + */ + afterFirstPostItems(): ItemList; /** * @returns {ItemList} */ diff --git a/framework/core/js/dist/forum/components/PostStream.js b/framework/core/js/dist/forum/components/PostStream.js index 8a0cf649b..fccf1cd87 100644 --- a/framework/core/js/dist/forum/components/PostStream.js +++ b/framework/core/js/dist/forum/components/PostStream.js @@ -1,2 +1,2 @@ -"use strict";(self.webpackChunkflarum_core=self.webpackChunkflarum_core||[]).push([[158],{3038:(t,s,e)=>{e.r(s),e.d(s,{default:()=>P});var o=e(3554),i=e(5710),a=e(2361),r=e(348),n=e(6445),l=e(5952),d=e(1883),c=e(5673),h=e(7891);class u extends i.A{view(){if(this.attrs.composingReply?this.attrs.composingReply():o.A.composer.composingReplyTo(this.attrs.discussion))return m("article",{className:"Post CommentPost editing","aria-busy":"true"},m("div",{className:"Post-container"},m("div",{className:"Post-side"},m(h.A,{user:o.A.session.user,className:"Post-avatar"})),m("div",{className:"Post-main"},m("header",{className:"Post-header"},m("div",{className:"PostUser"},m("h3",{className:"PostUser-name"},(0,n.A)(o.A.session.user)),m("ul",{className:"PostUser-badges badges badges--packed"},(0,c.A)(o.A.session.user.badges().toArray())))),m("div",{className:"Post-body"},m(d.A,{className:"Post-body",composer:o.A.composer,surround:this.anchorPreview.bind(this)})))));const t=this.attrs.onclick||(()=>{l.A.replyAction.call(this.attrs.discussion,!0,!1).catch((()=>{}))});return m("button",{className:"Post ReplyPlaceholder",onclick:t},m("div",{className:"Post-container"},m("div",{className:"Post-side"},m(h.A,{user:o.A.session.user,className:"Post-avatar"})),m("div",{className:"Post-main"},m("span",{className:"Post-header"},o.A.translator.trans("core.forum.post_stream.reply_placeholder")))))}anchorPreview(t){const s=$(window).scrollTop()+$(window).height()>=$(document).height();t(),s&&$(window).scrollTop($(document).height())}}flarum.reg.add("core","forum/components/ReplyPlaceholder",u);var p=e(3092),f=e(6064);class g extends i.A{view(){const t=this.attrs.post,s=o.A.postComponents[t.contentType()];return!!s&&m(s,{post:t})}}flarum.reg.add("core","forum/components/PostType",g);class P extends i.A{oninit(t){super.oninit(t),this.discussion=this.attrs.discussion,this.stream=this.attrs.stream,this.scrollListener=new a.A(this.onscroll.bind(this))}view(){let t;const s=this.stream.viewingEnd(),e=this.stream.posts(),i=this.discussion.postIds(),a=t=>{$(t.dom).addClass("fadeIn"),setTimeout((()=>$(t.dom).removeClass("fadeIn")),500)},n=e.map(((s,e)=>{let n;const l={"data-index":this.stream.visibleStart+e};if(s){const e=s.createdAt();n=m(g,{post:s}),l.key="post"+s.id(),l.oncreate=a,l["data-time"]=e.toISOString(),l["data-number"]=s.number(),l["data-id"]=s.id(),l["data-type"]=s.contentType();const i=e-t;i>3456e5&&(n=[m("div",{className:"PostStream-timeGap"},m("span",null,o.A.translator.trans("core.forum.post_stream.time_lapsed_text",{period:dayjs().add(i,"ms").fromNow(!0)}))),n]),t=e}else l.key="post"+i[this.stream.visibleStart+e],n=m(r.A,null);return m("div",Object.assign({className:"PostStream-item"},l),n)}));return!s&&e[this.stream.visibleEnd-this.stream.visibleStart-1]&&n.push(m("div",{className:"PostStream-loadMore",key:"loadMore"},m(p.A,{className:"Button",onclick:this.stream.loadNext.bind(this.stream)},o.A.translator.trans("core.forum.post_stream.load_more_button")))),s&&n.push(...this.endItems().toArray()),!s||o.A.session.user&&!this.discussion.canReply()||n.push(m("div",{className:"PostStream-item",key:"reply","data-index":this.stream.count(),oncreate:a},m(u,{discussion:this.discussion}))),m("div",{className:"PostStream",role:"feed","aria-live":"off","aria-busy":this.stream.pagesLoading?"true":"false"},n)}endItems(){return new f.A}onupdate(t){super.onupdate(t),this.triggerScroll()}oncreate(t){super.oncreate(t),this.triggerScroll(),setTimeout((()=>this.scrollListener.start()))}onremove(t){super.onremove(t),this.scrollListener.stop(),clearTimeout(this.calculatePositionTimeout)}triggerScroll(){if(!this.stream.needsScroll)return;const t=this.stream.targetPost;this.stream.needsScroll=!1,"number"in t?this.scrollToNumber(t.number,this.stream.animateScroll):"index"in t&&this.scrollToIndex(t.index,this.stream.animateScroll,t.reply)}onscroll(t){void 0===t&&(t=window.pageYOffset),this.stream.paused||this.stream.pagesLoading||(this.updateScrubber(t),this.loadPostsIfNeeded(t),clearTimeout(this.calculatePositionTimeout),this.calculatePositionTimeout=setTimeout(this.calculatePosition.bind(this,t),100))}loadPostsIfNeeded(t){void 0===t&&(t=window.pageYOffset);const s=this.getMarginTop(),e=$(window).height()-s,o=t+s;if(this.stream.visibleStart>0){const t=this.$(".PostStream-item[data-index="+this.stream.visibleStart+"]");t.length&&t.offset().top>o-300&&this.stream.loadPrevious()}if(this.stream.visibleEndi+e)return!1;const a=Math.max(0,i-s),l=Math.min(o,i+e-s)-a;null===m&&(m=parseFloat(t.data("index"))+a/o),l>0&&(r+=l/o);const d=t.data("time");d&&(n=d)})),this.stream.index=null!==m?m+1:this.stream.count(),this.stream.visible=r,n&&(this.stream.description=o.A.translator.formatDateTime(dayjs(n),"core.lib.datetime_formats.scrubber"))}calculatePosition(t){void 0===t&&(t=window.pageYOffset);const s=this.getMarginTop(),e=$(window),o=e.height()-s,i=e.scrollTop()+s,a=t+s;let r,n;this.$(".PostStream-item").each((function(){const t=$(this),s=t.offset().top,e=t.outerHeight(!0),m=Math.max(0,a-s);if(void 0===r&&(m/e<.75||(e-m)/o>.25)&&(r=t.data("number")),s+e>i){if(!(s+el){const e=i?n-$(window).height()+o.A.composer.computedHeight():t.is(":first-child")?0:r;s?e!==m&&a.animate({scrollTop:e},"fast"):a.scrollTop(e)}}const n=()=>{this.updateScrubber(),void 0!==r&&(this.stream.index=r+1)};return n(),this.stream.forceUpdateScrubber=!0,Promise.all([a.promise(),this.stream.loadPromise]).then((()=>{let t;if(m.redraw.sync(),i){const t=$(".PostStream-item:last-child");$(window).scrollTop(t.offset().top+t.height()-$(window).height()+o.A.composer.computedHeight())}else 0===r?$(window).scrollTop(0):(t=$(`.PostStream-item[data-index=${r}]`).offset())&&$(window).scrollTop(t.top-this.getMarginTop());n(),this.calculatePosition(),this.stream.paused=!1,this.loadPostsIfNeeded()}))}flashItem(t){t.removeClass("fadeIn"),t.addClass("flash").on("animationend webkitAnimationEnd",(s=>{t.removeClass("flash")}))}}flarum.reg.add("core","forum/components/PostStream",P)}}]); +"use strict";(self.webpackChunkflarum_core=self.webpackChunkflarum_core||[]).push([[158],{3038:(t,s,e)=>{e.r(s),e.d(s,{default:()=>P});var o=e(3554),i=e(5710),a=e(2361),r=e(348),n=e(6445),l=e(5952),d=e(1883),c=e(5673),h=e(7891);class u extends i.A{view(){if(this.attrs.composingReply?this.attrs.composingReply():o.A.composer.composingReplyTo(this.attrs.discussion))return m("article",{className:"Post CommentPost editing","aria-busy":"true"},m("div",{className:"Post-container"},m("div",{className:"Post-side"},m(h.A,{user:o.A.session.user,className:"Post-avatar"})),m("div",{className:"Post-main"},m("header",{className:"Post-header"},m("div",{className:"PostUser"},m("h3",{className:"PostUser-name"},(0,n.A)(o.A.session.user)),m("ul",{className:"PostUser-badges badges badges--packed"},(0,c.A)(o.A.session.user.badges().toArray())))),m("div",{className:"Post-body"},m(d.A,{className:"Post-body",composer:o.A.composer,surround:this.anchorPreview.bind(this)})))));const t=this.attrs.onclick||(()=>{l.A.replyAction.call(this.attrs.discussion,!0,!1).catch((()=>{}))});return m("button",{className:"Post ReplyPlaceholder",onclick:t},m("div",{className:"Post-container"},m("div",{className:"Post-side"},m(h.A,{user:o.A.session.user,className:"Post-avatar"})),m("div",{className:"Post-main"},m("span",{className:"Post-header"},o.A.translator.trans("core.forum.post_stream.reply_placeholder")))))}anchorPreview(t){const s=$(window).scrollTop()+$(window).height()>=$(document).height();t(),s&&$(window).scrollTop($(document).height())}}flarum.reg.add("core","forum/components/ReplyPlaceholder",u);var p=e(3092),f=e(6064);class g extends i.A{view(){const t=this.attrs.post,s=o.A.postComponents[t.contentType()];return!!s&&m(s,{post:t})}}flarum.reg.add("core","forum/components/PostType",g);class P extends i.A{oninit(t){super.oninit(t),this.discussion=this.attrs.discussion,this.stream=this.attrs.stream,this.scrollListener=new a.A(this.onscroll.bind(this))}view(){let t;const s=this.stream.viewingEnd(),e=this.stream.posts(),i=this.discussion.postIds(),a=t=>{$(t.dom).addClass("fadeIn"),setTimeout((()=>$(t.dom).removeClass("fadeIn")),500)},n=e.map(((s,e)=>{let n;const l={"data-index":this.stream.visibleStart+e};if(s){const e=s.createdAt();n=m(g,{post:s}),l.key="post"+s.id(),l.oncreate=a,l["data-time"]=e.toISOString(),l["data-number"]=s.number(),l["data-id"]=s.id(),l["data-type"]=s.contentType();const i=e-t;i>3456e5&&(n=[m("div",{className:"PostStream-timeGap"},m("span",null,o.A.translator.trans("core.forum.post_stream.time_lapsed_text",{period:dayjs().add(i,"ms").fromNow(!0)}))),n]),t=e}else l.key="post"+i[this.stream.visibleStart+e],n=m(r.A,null);const d=m("div",Object.assign({className:"PostStream-item"},l),n);return s&&s.id()===this.discussion.data.relationships.firstPost?.data.id&&this.afterFirstPostItems().toArray().length>0?m.fragment({...l},[d,m("div",{className:"PostStream-item PostStream-afterFirstPost",key:"afterFirstPost"},this.afterFirstPostItems().toArray())]):d}));return!s&&e[this.stream.visibleEnd-this.stream.visibleStart-1]&&n.push(m("div",{className:"PostStream-loadMore",key:"loadMore"},m(p.A,{className:"Button",onclick:this.stream.loadNext.bind(this.stream)},o.A.translator.trans("core.forum.post_stream.load_more_button")))),s&&n.push(...this.endItems().toArray()),!s||o.A.session.user&&!this.discussion.canReply()||n.push(m("div",{className:"PostStream-item",key:"reply","data-index":this.stream.count(),oncreate:a},m(u,{discussion:this.discussion}))),m("div",{className:"PostStream",role:"feed","aria-live":"off","aria-busy":this.stream.pagesLoading?"true":"false"},n)}afterFirstPostItems(){return new f.A}endItems(){return new f.A}onupdate(t){super.onupdate(t),this.triggerScroll()}oncreate(t){super.oncreate(t),this.triggerScroll(),setTimeout((()=>this.scrollListener.start()))}onremove(t){super.onremove(t),this.scrollListener.stop(),clearTimeout(this.calculatePositionTimeout)}triggerScroll(){if(!this.stream.needsScroll)return;const t=this.stream.targetPost;this.stream.needsScroll=!1,"number"in t?this.scrollToNumber(t.number,this.stream.animateScroll):"index"in t&&this.scrollToIndex(t.index,this.stream.animateScroll,t.reply)}onscroll(t){void 0===t&&(t=window.pageYOffset),this.stream.paused||this.stream.pagesLoading||(this.updateScrubber(t),this.loadPostsIfNeeded(t),clearTimeout(this.calculatePositionTimeout),this.calculatePositionTimeout=setTimeout(this.calculatePosition.bind(this,t),100))}loadPostsIfNeeded(t){void 0===t&&(t=window.pageYOffset);const s=this.getMarginTop(),e=$(window).height()-s,o=t+s;if(this.stream.visibleStart>0){const t=this.$(".PostStream-item[data-index="+this.stream.visibleStart+"]");t.length&&t.offset().top>o-300&&this.stream.loadPrevious()}if(this.stream.visibleEndi+e)return!1;const a=Math.max(0,i-s),l=Math.min(o,i+e-s)-a;null===m&&(m=parseFloat(t.data("index"))+a/o),l>0&&(r+=l/o);const d=t.data("time");d&&(n=d)})),this.stream.index=null!==m?m+1:this.stream.count(),this.stream.visible=r,n&&(this.stream.description=o.A.translator.formatDateTime(dayjs(n),"core.lib.datetime_formats.scrubber"))}calculatePosition(t){void 0===t&&(t=window.pageYOffset);const s=this.getMarginTop(),e=$(window),o=e.height()-s,i=e.scrollTop()+s,a=t+s;let r,n;this.$(".PostStream-item").each((function(){const t=$(this),s=t.offset().top,e=t.outerHeight(!0),m=Math.max(0,a-s);if(void 0===r&&(m/e<.75||(e-m)/o>.25)&&(r=t.data("number")),s+e>i){if(!(s+el){const e=i?n-$(window).height()+o.A.composer.computedHeight():t.is(":first-child")?0:r;s?e!==m&&a.animate({scrollTop:e},"fast"):a.scrollTop(e)}}const n=()=>{this.updateScrubber(),void 0!==r&&(this.stream.index=r+1)};return n(),this.stream.forceUpdateScrubber=!0,Promise.all([a.promise(),this.stream.loadPromise]).then((()=>{let t;if(m.redraw.sync(),i){const t=$(".PostStream-item:last-child");$(window).scrollTop(t.offset().top+t.height()-$(window).height()+o.A.composer.computedHeight())}else 0===r?$(window).scrollTop(0):(t=$(`.PostStream-item[data-index=${r}]`).offset())&&$(window).scrollTop(t.top-this.getMarginTop());n(),this.calculatePosition(),this.stream.paused=!1,this.loadPostsIfNeeded()}))}flashItem(t){t.removeClass("fadeIn"),t.addClass("flash").on("animationend webkitAnimationEnd",(s=>{t.removeClass("flash")}))}}flarum.reg.add("core","forum/components/PostStream",P)}}]); //# sourceMappingURL=PostStream.js.map \ No newline at end of file diff --git a/framework/core/js/dist/forum/components/PostStream.js.map b/framework/core/js/dist/forum/components/PostStream.js.map index 2b2c64b1f..15c3b238a 100644 --- a/framework/core/js/dist/forum/components/PostStream.js.map +++ b/framework/core/js/dist/forum/components/PostStream.js.map @@ -1 +1 @@ -{"version":3,"file":"forum/components/PostStream.js","mappings":"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","sources":["webpack://@flarum/core/./src/forum/components/ReplyPlaceholder.tsx","webpack://@flarum/core/./src/forum/components/PostType.tsx","webpack://@flarum/core/./src/forum/components/PostStream.js"],"sourcesContent":["import app from '../../forum/app';\nimport Component from '../../common/Component';\nimport username from '../../common/helpers/username';\nimport DiscussionControls from '../utils/DiscussionControls';\nimport ComposerPostPreview from './ComposerPostPreview';\nimport listItems from '../../common/helpers/listItems';\nimport Avatar from '../../common/components/Avatar';\n/**\n * The `ReplyPlaceholder` component displays a placeholder for a reply, which,\n * when clicked, opens the reply composer.\n */\nexport default class ReplyPlaceholder extends Component {\n view() {\n const composingReply = this.attrs.composingReply ? this.attrs.composingReply() : app.composer.composingReplyTo(this.attrs.discussion);\n if (composingReply) {\n return m(\"article\", {\n className: \"Post CommentPost editing\",\n \"aria-busy\": \"true\"\n }, m(\"div\", {\n className: \"Post-container\"\n }, m(\"div\", {\n className: \"Post-side\"\n }, m(Avatar, {\n user: app.session.user,\n className: \"Post-avatar\"\n })), m(\"div\", {\n className: \"Post-main\"\n }, m(\"header\", {\n className: \"Post-header\"\n }, m(\"div\", {\n className: \"PostUser\"\n }, m(\"h3\", {\n className: \"PostUser-name\"\n }, username(app.session.user)), m(\"ul\", {\n className: \"PostUser-badges badges badges--packed\"\n }, listItems(app.session.user.badges().toArray())))), m(\"div\", {\n className: \"Post-body\"\n }, m(ComposerPostPreview, {\n className: \"Post-body\",\n composer: app.composer,\n surround: this.anchorPreview.bind(this)\n })))));\n }\n const reply = this.attrs.onclick || (() => {\n DiscussionControls.replyAction.call(this.attrs.discussion, true, false).catch(() => {});\n });\n return m(\"button\", {\n className: \"Post ReplyPlaceholder\",\n onclick: reply\n }, m(\"div\", {\n className: \"Post-container\"\n }, m(\"div\", {\n className: \"Post-side\"\n }, m(Avatar, {\n user: app.session.user,\n className: \"Post-avatar\"\n })), m(\"div\", {\n className: \"Post-main\"\n }, m(\"span\", {\n className: \"Post-header\"\n }, app.translator.trans('core.forum.post_stream.reply_placeholder')))));\n }\n anchorPreview(preview) {\n const anchorToBottom = $(window).scrollTop() + $(window).height() >= $(document).height();\n preview();\n if (anchorToBottom) {\n $(window).scrollTop($(document).height());\n }\n }\n}\nflarum.reg.add('core', 'forum/components/ReplyPlaceholder', ReplyPlaceholder);","import Component from '../../common/Component';\nimport app from '../app';\nexport default class PostType extends Component {\n view() {\n const post = this.attrs.post;\n const PostComponent = app.postComponents[post.contentType()];\n return !!PostComponent && m(PostComponent, {\n post: post\n });\n }\n}\nflarum.reg.add('core', 'forum/components/PostType', PostType);","import app from '../../forum/app';\nimport Component from '../../common/Component';\nimport ScrollListener from '../../common/utils/ScrollListener';\nimport LoadingPost from './LoadingPost';\nimport ReplyPlaceholder from './ReplyPlaceholder';\nimport Button from '../../common/components/Button';\nimport ItemList from '../../common/utils/ItemList';\nimport PostType from './PostType';\n\n/**\n * The `PostStream` component displays an infinitely-scrollable wall of posts in\n * a discussion. Posts that have not loaded will be displayed as placeholders.\n *\n * ### Attrs\n *\n * - `discussion`\n * - `stream`\n * - `targetPost`\n * - `onPositionChange`\n */\nexport default class PostStream extends Component {\n oninit(vnode) {\n super.oninit(vnode);\n this.discussion = this.attrs.discussion;\n this.stream = this.attrs.stream;\n this.scrollListener = new ScrollListener(this.onscroll.bind(this));\n }\n view() {\n let lastTime;\n const viewingEnd = this.stream.viewingEnd();\n const posts = this.stream.posts();\n const postIds = this.discussion.postIds();\n const postFadeIn = vnode => {\n $(vnode.dom).addClass('fadeIn');\n // 500 is the duration of the fadeIn CSS animation + 100ms,\n // so the animation has time to complete\n setTimeout(() => $(vnode.dom).removeClass('fadeIn'), 500);\n };\n const items = posts.map((post, i) => {\n let content;\n const attrs = {\n 'data-index': this.stream.visibleStart + i\n };\n if (post) {\n const time = post.createdAt();\n content = m(PostType, {\n post: post\n });\n attrs.key = 'post' + post.id();\n attrs.oncreate = postFadeIn;\n attrs['data-time'] = time.toISOString();\n attrs['data-number'] = post.number();\n attrs['data-id'] = post.id();\n attrs['data-type'] = post.contentType();\n\n // If the post before this one was more than 4 days ago, we will\n // display a 'time gap' indicating how long it has been in between\n // the posts.\n const dt = time - lastTime;\n if (dt > 1000 * 60 * 60 * 24 * 4) {\n content = [m(\"div\", {\n className: \"PostStream-timeGap\"\n }, m(\"span\", null, app.translator.trans('core.forum.post_stream.time_lapsed_text', {\n period: dayjs().add(dt, 'ms').fromNow(true)\n }))), content];\n }\n lastTime = time;\n } else {\n attrs.key = 'post' + postIds[this.stream.visibleStart + i];\n content = m(LoadingPost, null);\n }\n return m(\"div\", Object.assign({\n className: \"PostStream-item\"\n }, attrs), content);\n });\n if (!viewingEnd && posts[this.stream.visibleEnd - this.stream.visibleStart - 1]) {\n items.push(m(\"div\", {\n className: \"PostStream-loadMore\",\n key: \"loadMore\"\n }, m(Button, {\n className: \"Button\",\n onclick: this.stream.loadNext.bind(this.stream)\n }, app.translator.trans('core.forum.post_stream.load_more_button'))));\n }\n\n // Allow extensions to add items to the end of the post stream.\n if (viewingEnd) {\n items.push(...this.endItems().toArray());\n }\n\n // If we're viewing the end of the discussion, the user can reply, and\n // is not already doing so, then show a 'write a reply' placeholder.\n if (viewingEnd && (!app.session.user || this.discussion.canReply())) {\n items.push(m(\"div\", {\n className: \"PostStream-item\",\n key: \"reply\",\n \"data-index\": this.stream.count(),\n oncreate: postFadeIn\n }, m(ReplyPlaceholder, {\n discussion: this.discussion\n })));\n }\n return m(\"div\", {\n className: \"PostStream\",\n role: \"feed\",\n \"aria-live\": \"off\",\n \"aria-busy\": this.stream.pagesLoading ? 'true' : 'false'\n }, items);\n }\n\n /**\n * @returns {ItemList}\n */\n endItems() {\n const items = new ItemList();\n return items;\n }\n onupdate(vnode) {\n super.onupdate(vnode);\n this.triggerScroll();\n }\n oncreate(vnode) {\n super.oncreate(vnode);\n this.triggerScroll();\n\n // This is wrapped in setTimeout due to the following Mithril issue:\n // https://github.com/lhorie/mithril.js/issues/637\n setTimeout(() => this.scrollListener.start());\n }\n onremove(vnode) {\n super.onremove(vnode);\n this.scrollListener.stop();\n clearTimeout(this.calculatePositionTimeout);\n }\n\n /**\n * Start scrolling, if appropriate, to a newly-targeted post.\n */\n triggerScroll() {\n if (!this.stream.needsScroll) return;\n const target = this.stream.targetPost;\n this.stream.needsScroll = false;\n if ('number' in target) {\n this.scrollToNumber(target.number, this.stream.animateScroll);\n } else if ('index' in target) {\n this.scrollToIndex(target.index, this.stream.animateScroll, target.reply);\n }\n }\n\n /**\n *\n * @param {number} top\n */\n onscroll(top) {\n if (top === void 0) {\n top = window.pageYOffset;\n }\n if (this.stream.paused || this.stream.pagesLoading) return;\n this.updateScrubber(top);\n this.loadPostsIfNeeded(top);\n\n // Throttle calculation of our position (start/end numbers of posts in the\n // viewport) to 100ms.\n clearTimeout(this.calculatePositionTimeout);\n this.calculatePositionTimeout = setTimeout(this.calculatePosition.bind(this, top), 100);\n }\n\n /**\n * Check if either extreme of the post stream is in the viewport,\n * and if so, trigger loading the next/previous page.\n *\n * @param {number} top\n */\n loadPostsIfNeeded(top) {\n if (top === void 0) {\n top = window.pageYOffset;\n }\n const marginTop = this.getMarginTop();\n const viewportHeight = $(window).height() - marginTop;\n const viewportTop = top + marginTop;\n const loadAheadDistance = 300;\n if (this.stream.visibleStart > 0) {\n const $item = this.$('.PostStream-item[data-index=' + this.stream.visibleStart + ']');\n if ($item.length && $item.offset().top > viewportTop - loadAheadDistance) {\n this.stream.loadPrevious();\n }\n }\n if (this.stream.visibleEnd < this.stream.count()) {\n const $item = this.$('.PostStream-item[data-index=' + (this.stream.visibleEnd - 1) + ']');\n if ($item.length && $item.offset().top + $item.outerHeight(true) < viewportTop + viewportHeight + loadAheadDistance) {\n this.stream.loadNext();\n }\n }\n }\n updateScrubber(top) {\n if (top === void 0) {\n top = window.pageYOffset;\n }\n const marginTop = this.getMarginTop();\n const viewportHeight = $(window).height() - marginTop;\n const viewportTop = top + marginTop;\n\n // Before looping through all of the posts, we reset the scrollbar\n // properties to a 'default' state. These values reflect what would be\n // seen if the browser were scrolled right up to the top of the page,\n // and the viewport had a height of 0.\n const $items = this.$('.PostStream-item[data-index]');\n let visible = 0;\n let period = '';\n let indexFromViewPort = null;\n\n // Now loop through each of the items in the discussion. An 'item' is\n // either a single post or a 'gap' of one or more posts that haven't\n // been loaded yet.\n $items.each(function () {\n const $this = $(this);\n const top = $this.offset().top;\n const height = $this.outerHeight(true);\n\n // If this item is above the top of the viewport, skip to the next\n // one. If it's below the bottom of the viewport, break out of the\n // loop.\n if (top + height < viewportTop) {\n return true;\n }\n if (top > viewportTop + viewportHeight) {\n return false;\n }\n\n // Work out how many pixels of this item are visible inside the viewport.\n // Then add the proportion of this item's total height to the index.\n const visibleTop = Math.max(0, viewportTop - top);\n const visibleBottom = Math.min(height, viewportTop + viewportHeight - top);\n const visiblePost = visibleBottom - visibleTop;\n\n // We take the index of the first item that passed the previous checks.\n // It is the item that is first visible in the viewport.\n if (indexFromViewPort === null) {\n indexFromViewPort = parseFloat($this.data('index')) + visibleTop / height;\n }\n if (visiblePost > 0) {\n visible += visiblePost / height;\n }\n\n // If this item has a time associated with it, then set the\n // scrollbar's current period to a formatted version of this time.\n const time = $this.data('time');\n if (time) period = time;\n });\n\n // If indexFromViewPort is null, it means no posts are visible in the\n // viewport. This can happen, when drafting a long reply post. In that case\n // set the index to the last post.\n this.stream.index = indexFromViewPort !== null ? indexFromViewPort + 1 : this.stream.count();\n this.stream.visible = visible;\n if (period) this.stream.description = app.translator.formatDateTime(dayjs(period), 'core.lib.datetime_formats.scrubber');\n }\n\n /**\n * Work out which posts (by number) are currently visible in the viewport, and\n * fire an event with the information.\n */\n calculatePosition(top) {\n if (top === void 0) {\n top = window.pageYOffset;\n }\n const marginTop = this.getMarginTop();\n const $window = $(window);\n const viewportHeight = $window.height() - marginTop;\n const scrollTop = $window.scrollTop() + marginTop;\n const viewportTop = top + marginTop;\n let startNumber;\n let endNumber;\n this.$('.PostStream-item').each(function () {\n const $item = $(this);\n const top = $item.offset().top;\n const height = $item.outerHeight(true);\n const visibleTop = Math.max(0, viewportTop - top);\n const threeQuartersVisible = visibleTop / height < 0.75;\n const coversQuarterOfViewport = (height - visibleTop) / viewportHeight > 0.25;\n if (startNumber === undefined && (threeQuartersVisible || coversQuarterOfViewport)) {\n startNumber = $item.data('number');\n }\n if (top + height > scrollTop) {\n if (top + height < scrollTop + viewportHeight) {\n if ($item.data('number')) {\n endNumber = $item.data('number');\n }\n } else return false;\n }\n });\n if (startNumber) {\n this.attrs.onPositionChange(startNumber || 1, endNumber, startNumber);\n }\n }\n\n /**\n * Get the distance from the top of the viewport to the point at which we\n * would consider a post to be the first one visible.\n *\n * @return {number}\n */\n getMarginTop() {\n const headerId = app.screen() === 'phone' ? '#app-navigation' : '#header';\n return this.$() && $(headerId).outerHeight() + parseInt(this.$().css('margin-top'), 10);\n }\n\n /**\n * Scroll down to a certain post by number and 'flash' it.\n *\n * @param {number} number\n * @param {boolean} animate\n * @return {JQueryDeferred}\n */\n scrollToNumber(number, animate) {\n const $item = this.$(`.PostStream-item[data-number=${number}]`);\n return this.scrollToItem($item, animate).then(this.flashItem.bind(this, $item));\n }\n\n /**\n * Scroll down to a certain post by index.\n *\n * @param {number} index\n * @param {boolean} animate\n * @param {boolean} reply Whether or not to scroll to the reply placeholder.\n * @return {JQueryDeferred}\n */\n scrollToIndex(index, animate, reply) {\n const $item = reply ? $('.PostStream-item:last-child') : this.$(`.PostStream-item[data-index=${index}]`);\n this.scrollToItem($item, animate, true, reply);\n if (reply) {\n this.flashItem($item);\n }\n }\n\n /**\n * Scroll down to the given post.\n *\n * @param {JQuery} $item\n * @param {boolean} animate\n * @param {boolean} force Whether or not to force scrolling to the item, even\n * if it is already in the viewport.\n * @param {boolean} reply Whether or not to scroll to the reply placeholder.\n * @return {JQueryDeferred}\n */\n scrollToItem($item, animate, force, reply) {\n const $container = $('html, body').stop(true);\n const index = $item.data('index');\n if ($item.length) {\n const itemTop = $item.offset().top - this.getMarginTop();\n const itemBottom = $item.offset().top + $item.height();\n const scrollTop = $(document).scrollTop();\n const scrollBottom = scrollTop + $(window).height();\n\n // If the item is already in the viewport, we may not need to scroll.\n // If we're scrolling to the reply placeholder, we'll make sure its\n // bottom will line up with the top of the composer.\n if (force || itemTop < scrollTop || itemBottom > scrollBottom) {\n const top = reply ? itemBottom - $(window).height() + app.composer.computedHeight() : $item.is(':first-child') ? 0 : itemTop;\n if (!animate) {\n $container.scrollTop(top);\n } else if (top !== scrollTop) {\n $container.animate({\n scrollTop: top\n }, 'fast');\n }\n }\n }\n const updateScrubberHeight = () => {\n // We manually set the index because we want to display the index of the\n // exact post we've scrolled to, not just that of the first post within viewport.\n this.updateScrubber();\n if (index !== undefined) this.stream.index = index + 1;\n };\n\n // If we don't update this before the scroll, the scrubber will start\n // at the top, and animate down, which can be confusing\n updateScrubberHeight();\n this.stream.forceUpdateScrubber = true;\n return Promise.all([$container.promise(), this.stream.loadPromise]).then(() => {\n m.redraw.sync();\n\n // Rendering post contents will probably throw off our position.\n // To counter this, we'll scroll either:\n // - To the reply placeholder (aligned with composer top)\n // - To the top of the page if we're on the first post\n // - To the top of a post (if that post exists)\n // If the post does not currently exist, it's probably\n // outside of the range we loaded in, so we won't adjust anything,\n // as it will soon be rendered by the \"load more\" system.\n let itemOffset;\n if (reply) {\n const $placeholder = $('.PostStream-item:last-child');\n $(window).scrollTop($placeholder.offset().top + $placeholder.height() - $(window).height() + app.composer.computedHeight());\n } else if (index === 0) {\n $(window).scrollTop(0);\n } else if (itemOffset = $(`.PostStream-item[data-index=${index}]`).offset()) {\n $(window).scrollTop(itemOffset.top - this.getMarginTop());\n }\n\n // We want to adjust this again after posts have been loaded in\n // and position adjusted so that the scrubber's height is accurate.\n updateScrubberHeight();\n this.calculatePosition();\n this.stream.paused = false;\n // Check if we need to load more posts after scrolling.\n this.loadPostsIfNeeded();\n });\n }\n\n /**\n * 'Flash' the given post, drawing the user's attention to it.\n *\n * @param {JQuery} $item\n */\n flashItem($item) {\n // This might execute before the fadeIn class has been removed in PostStreamItem's\n // oncreate, so we remove it just to be safe and avoid a double animation.\n $item.removeClass('fadeIn');\n $item.addClass('flash').on('animationend webkitAnimationEnd', e => {\n $item.removeClass('flash');\n });\n }\n}\nflarum.reg.add('core', 'forum/components/PostStream', PostStream);"],"names":["ReplyPlaceholder","Component","view","this","attrs","composingReply","app","composer","composingReplyTo","discussion","m","className","Avatar","A","user","session","username","listItems","badges","toArray","ComposerPostPreview","surround","anchorPreview","bind","reply","onclick","DiscussionControls","replyAction","call","catch","translator","trans","preview","anchorToBottom","$","window","scrollTop","height","document","flarum","reg","add","PostType","post","PostComponent","postComponents","contentType","PostStream","oninit","vnode","super","stream","scrollListener","ScrollListener","onscroll","lastTime","viewingEnd","posts","postIds","postFadeIn","dom","addClass","setTimeout","removeClass","items","map","i","content","visibleStart","time","createdAt","key","id","oncreate","toISOString","number","dt","period","dayjs","fromNow","LoadingPost","Object","assign","visibleEnd","push","Button","loadNext","endItems","canReply","count","role","pagesLoading","ItemList","onupdate","triggerScroll","start","onremove","stop","clearTimeout","calculatePositionTimeout","needsScroll","target","targetPost","scrollToNumber","animateScroll","scrollToIndex","index","top","pageYOffset","paused","updateScrubber","loadPostsIfNeeded","calculatePosition","marginTop","getMarginTop","viewportHeight","viewportTop","$item","length","offset","loadPrevious","outerHeight","$items","visible","indexFromViewPort","each","$this","visibleTop","Math","max","visiblePost","min","parseFloat","data","description","formatDateTime","$window","startNumber","endNumber","undefined","onPositionChange","headerId","screen","parseInt","css","animate","scrollToItem","then","flashItem","force","$container","itemTop","itemBottom","scrollBottom","computedHeight","is","updateScrubberHeight","forceUpdateScrubber","Promise","all","promise","loadPromise","itemOffset","redraw","sync","$placeholder","on","e"],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"forum/components/PostStream.js","mappings":"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","sources":["webpack://@flarum/core/./src/forum/components/ReplyPlaceholder.tsx","webpack://@flarum/core/./src/forum/components/PostType.tsx","webpack://@flarum/core/./src/forum/components/PostStream.js"],"sourcesContent":["import app from '../../forum/app';\nimport Component from '../../common/Component';\nimport username from '../../common/helpers/username';\nimport DiscussionControls from '../utils/DiscussionControls';\nimport ComposerPostPreview from './ComposerPostPreview';\nimport listItems from '../../common/helpers/listItems';\nimport Avatar from '../../common/components/Avatar';\n/**\n * The `ReplyPlaceholder` component displays a placeholder for a reply, which,\n * when clicked, opens the reply composer.\n */\nexport default class ReplyPlaceholder extends Component {\n view() {\n const composingReply = this.attrs.composingReply ? this.attrs.composingReply() : app.composer.composingReplyTo(this.attrs.discussion);\n if (composingReply) {\n return m(\"article\", {\n className: \"Post CommentPost editing\",\n \"aria-busy\": \"true\"\n }, m(\"div\", {\n className: \"Post-container\"\n }, m(\"div\", {\n className: \"Post-side\"\n }, m(Avatar, {\n user: app.session.user,\n className: \"Post-avatar\"\n })), m(\"div\", {\n className: \"Post-main\"\n }, m(\"header\", {\n className: \"Post-header\"\n }, m(\"div\", {\n className: \"PostUser\"\n }, m(\"h3\", {\n className: \"PostUser-name\"\n }, username(app.session.user)), m(\"ul\", {\n className: \"PostUser-badges badges badges--packed\"\n }, listItems(app.session.user.badges().toArray())))), m(\"div\", {\n className: \"Post-body\"\n }, m(ComposerPostPreview, {\n className: \"Post-body\",\n composer: app.composer,\n surround: this.anchorPreview.bind(this)\n })))));\n }\n const reply = this.attrs.onclick || (() => {\n DiscussionControls.replyAction.call(this.attrs.discussion, true, false).catch(() => {});\n });\n return m(\"button\", {\n className: \"Post ReplyPlaceholder\",\n onclick: reply\n }, m(\"div\", {\n className: \"Post-container\"\n }, m(\"div\", {\n className: \"Post-side\"\n }, m(Avatar, {\n user: app.session.user,\n className: \"Post-avatar\"\n })), m(\"div\", {\n className: \"Post-main\"\n }, m(\"span\", {\n className: \"Post-header\"\n }, app.translator.trans('core.forum.post_stream.reply_placeholder')))));\n }\n anchorPreview(preview) {\n const anchorToBottom = $(window).scrollTop() + $(window).height() >= $(document).height();\n preview();\n if (anchorToBottom) {\n $(window).scrollTop($(document).height());\n }\n }\n}\nflarum.reg.add('core', 'forum/components/ReplyPlaceholder', ReplyPlaceholder);","import Component from '../../common/Component';\nimport app from '../app';\nexport default class PostType extends Component {\n view() {\n const post = this.attrs.post;\n const PostComponent = app.postComponents[post.contentType()];\n return !!PostComponent && m(PostComponent, {\n post: post\n });\n }\n}\nflarum.reg.add('core', 'forum/components/PostType', PostType);","import app from '../../forum/app';\nimport Component from '../../common/Component';\nimport ScrollListener from '../../common/utils/ScrollListener';\nimport LoadingPost from './LoadingPost';\nimport ReplyPlaceholder from './ReplyPlaceholder';\nimport Button from '../../common/components/Button';\nimport ItemList from '../../common/utils/ItemList';\nimport PostType from './PostType';\n\n/**\n * The `PostStream` component displays an infinitely-scrollable wall of posts in\n * a discussion. Posts that have not loaded will be displayed as placeholders.\n *\n * ### Attrs\n *\n * - `discussion`\n * - `stream`\n * - `targetPost`\n * - `onPositionChange`\n */\nexport default class PostStream extends Component {\n oninit(vnode) {\n super.oninit(vnode);\n this.discussion = this.attrs.discussion;\n this.stream = this.attrs.stream;\n this.scrollListener = new ScrollListener(this.onscroll.bind(this));\n }\n view() {\n let lastTime;\n const viewingEnd = this.stream.viewingEnd();\n const posts = this.stream.posts();\n const postIds = this.discussion.postIds();\n const postFadeIn = vnode => {\n $(vnode.dom).addClass('fadeIn');\n // 500 is the duration of the fadeIn CSS animation + 100ms,\n // so the animation has time to complete\n setTimeout(() => $(vnode.dom).removeClass('fadeIn'), 500);\n };\n const items = posts.map((post, i) => {\n let content;\n const attrs = {\n 'data-index': this.stream.visibleStart + i\n };\n if (post) {\n const time = post.createdAt();\n content = m(PostType, {\n post: post\n });\n attrs.key = 'post' + post.id();\n attrs.oncreate = postFadeIn;\n attrs['data-time'] = time.toISOString();\n attrs['data-number'] = post.number();\n attrs['data-id'] = post.id();\n attrs['data-type'] = post.contentType();\n\n // If the post before this one was more than 4 days ago, we will\n // display a 'time gap' indicating how long it has been in between\n // the posts.\n const dt = time - lastTime;\n if (dt > 1000 * 60 * 60 * 24 * 4) {\n content = [m(\"div\", {\n className: \"PostStream-timeGap\"\n }, m(\"span\", null, app.translator.trans('core.forum.post_stream.time_lapsed_text', {\n period: dayjs().add(dt, 'ms').fromNow(true)\n }))), content];\n }\n lastTime = time;\n } else {\n attrs.key = 'post' + postIds[this.stream.visibleStart + i];\n content = m(LoadingPost, null);\n }\n const postStreamElement = m(\"div\", Object.assign({\n className: \"PostStream-item\"\n }, attrs), content);\n if (post && post.id() === this.discussion.data.relationships.firstPost?.data.id && this.afterFirstPostItems().toArray().length > 0) {\n return m.fragment({\n ...attrs\n }, [postStreamElement, m(\"div\", {\n className: \"PostStream-item PostStream-afterFirstPost\",\n key: \"afterFirstPost\"\n }, this.afterFirstPostItems().toArray())]);\n }\n return postStreamElement;\n });\n if (!viewingEnd && posts[this.stream.visibleEnd - this.stream.visibleStart - 1]) {\n items.push(m(\"div\", {\n className: \"PostStream-loadMore\",\n key: \"loadMore\"\n }, m(Button, {\n className: \"Button\",\n onclick: this.stream.loadNext.bind(this.stream)\n }, app.translator.trans('core.forum.post_stream.load_more_button'))));\n }\n\n // Allow extensions to add items to the end of the post stream.\n if (viewingEnd) {\n items.push(...this.endItems().toArray());\n }\n\n // If we're viewing the end of the discussion, the user can reply, and\n // is not already doing so, then show a 'write a reply' placeholder.\n if (viewingEnd && (!app.session.user || this.discussion.canReply())) {\n items.push(m(\"div\", {\n className: \"PostStream-item\",\n key: \"reply\",\n \"data-index\": this.stream.count(),\n oncreate: postFadeIn\n }, m(ReplyPlaceholder, {\n discussion: this.discussion\n })));\n }\n return m(\"div\", {\n className: \"PostStream\",\n role: \"feed\",\n \"aria-live\": \"off\",\n \"aria-busy\": this.stream.pagesLoading ? 'true' : 'false'\n }, items);\n }\n\n /**\n * @returns {ItemList}\n */\n afterFirstPostItems() {\n const items = new ItemList();\n return items;\n }\n\n /**\n * @returns {ItemList}\n */\n endItems() {\n const items = new ItemList();\n return items;\n }\n onupdate(vnode) {\n super.onupdate(vnode);\n this.triggerScroll();\n }\n oncreate(vnode) {\n super.oncreate(vnode);\n this.triggerScroll();\n\n // This is wrapped in setTimeout due to the following Mithril issue:\n // https://github.com/lhorie/mithril.js/issues/637\n setTimeout(() => this.scrollListener.start());\n }\n onremove(vnode) {\n super.onremove(vnode);\n this.scrollListener.stop();\n clearTimeout(this.calculatePositionTimeout);\n }\n\n /**\n * Start scrolling, if appropriate, to a newly-targeted post.\n */\n triggerScroll() {\n if (!this.stream.needsScroll) return;\n const target = this.stream.targetPost;\n this.stream.needsScroll = false;\n if ('number' in target) {\n this.scrollToNumber(target.number, this.stream.animateScroll);\n } else if ('index' in target) {\n this.scrollToIndex(target.index, this.stream.animateScroll, target.reply);\n }\n }\n\n /**\n *\n * @param {number} top\n */\n onscroll(top) {\n if (top === void 0) {\n top = window.pageYOffset;\n }\n if (this.stream.paused || this.stream.pagesLoading) return;\n this.updateScrubber(top);\n this.loadPostsIfNeeded(top);\n\n // Throttle calculation of our position (start/end numbers of posts in the\n // viewport) to 100ms.\n clearTimeout(this.calculatePositionTimeout);\n this.calculatePositionTimeout = setTimeout(this.calculatePosition.bind(this, top), 100);\n }\n\n /**\n * Check if either extreme of the post stream is in the viewport,\n * and if so, trigger loading the next/previous page.\n *\n * @param {number} top\n */\n loadPostsIfNeeded(top) {\n if (top === void 0) {\n top = window.pageYOffset;\n }\n const marginTop = this.getMarginTop();\n const viewportHeight = $(window).height() - marginTop;\n const viewportTop = top + marginTop;\n const loadAheadDistance = 300;\n if (this.stream.visibleStart > 0) {\n const $item = this.$('.PostStream-item[data-index=' + this.stream.visibleStart + ']');\n if ($item.length && $item.offset().top > viewportTop - loadAheadDistance) {\n this.stream.loadPrevious();\n }\n }\n if (this.stream.visibleEnd < this.stream.count()) {\n const $item = this.$('.PostStream-item[data-index=' + (this.stream.visibleEnd - 1) + ']');\n if ($item.length && $item.offset().top + $item.outerHeight(true) < viewportTop + viewportHeight + loadAheadDistance) {\n this.stream.loadNext();\n }\n }\n }\n updateScrubber(top) {\n if (top === void 0) {\n top = window.pageYOffset;\n }\n const marginTop = this.getMarginTop();\n const viewportHeight = $(window).height() - marginTop;\n const viewportTop = top + marginTop;\n\n // Before looping through all of the posts, we reset the scrollbar\n // properties to a 'default' state. These values reflect what would be\n // seen if the browser were scrolled right up to the top of the page,\n // and the viewport had a height of 0.\n const $items = this.$('.PostStream-item[data-index]');\n let visible = 0;\n let period = '';\n let indexFromViewPort = null;\n\n // Now loop through each of the items in the discussion. An 'item' is\n // either a single post or a 'gap' of one or more posts that haven't\n // been loaded yet.\n $items.each(function () {\n const $this = $(this);\n const top = $this.offset().top;\n const height = $this.outerHeight(true);\n\n // If this item is above the top of the viewport, skip to the next\n // one. If it's below the bottom of the viewport, break out of the\n // loop.\n if (top + height < viewportTop) {\n return true;\n }\n if (top > viewportTop + viewportHeight) {\n return false;\n }\n\n // Work out how many pixels of this item are visible inside the viewport.\n // Then add the proportion of this item's total height to the index.\n const visibleTop = Math.max(0, viewportTop - top);\n const visibleBottom = Math.min(height, viewportTop + viewportHeight - top);\n const visiblePost = visibleBottom - visibleTop;\n\n // We take the index of the first item that passed the previous checks.\n // It is the item that is first visible in the viewport.\n if (indexFromViewPort === null) {\n indexFromViewPort = parseFloat($this.data('index')) + visibleTop / height;\n }\n if (visiblePost > 0) {\n visible += visiblePost / height;\n }\n\n // If this item has a time associated with it, then set the\n // scrollbar's current period to a formatted version of this time.\n const time = $this.data('time');\n if (time) period = time;\n });\n\n // If indexFromViewPort is null, it means no posts are visible in the\n // viewport. This can happen, when drafting a long reply post. In that case\n // set the index to the last post.\n this.stream.index = indexFromViewPort !== null ? indexFromViewPort + 1 : this.stream.count();\n this.stream.visible = visible;\n if (period) this.stream.description = app.translator.formatDateTime(dayjs(period), 'core.lib.datetime_formats.scrubber');\n }\n\n /**\n * Work out which posts (by number) are currently visible in the viewport, and\n * fire an event with the information.\n */\n calculatePosition(top) {\n if (top === void 0) {\n top = window.pageYOffset;\n }\n const marginTop = this.getMarginTop();\n const $window = $(window);\n const viewportHeight = $window.height() - marginTop;\n const scrollTop = $window.scrollTop() + marginTop;\n const viewportTop = top + marginTop;\n let startNumber;\n let endNumber;\n this.$('.PostStream-item').each(function () {\n const $item = $(this);\n const top = $item.offset().top;\n const height = $item.outerHeight(true);\n const visibleTop = Math.max(0, viewportTop - top);\n const threeQuartersVisible = visibleTop / height < 0.75;\n const coversQuarterOfViewport = (height - visibleTop) / viewportHeight > 0.25;\n if (startNumber === undefined && (threeQuartersVisible || coversQuarterOfViewport)) {\n startNumber = $item.data('number');\n }\n if (top + height > scrollTop) {\n if (top + height < scrollTop + viewportHeight) {\n if ($item.data('number')) {\n endNumber = $item.data('number');\n }\n } else return false;\n }\n });\n if (startNumber) {\n this.attrs.onPositionChange(startNumber || 1, endNumber, startNumber);\n }\n }\n\n /**\n * Get the distance from the top of the viewport to the point at which we\n * would consider a post to be the first one visible.\n *\n * @return {number}\n */\n getMarginTop() {\n const headerId = app.screen() === 'phone' ? '#app-navigation' : '#header';\n return this.$() && $(headerId).outerHeight() + parseInt(this.$().css('margin-top'), 10);\n }\n\n /**\n * Scroll down to a certain post by number and 'flash' it.\n *\n * @param {number} number\n * @param {boolean} animate\n * @return {JQueryDeferred}\n */\n scrollToNumber(number, animate) {\n const $item = this.$(`.PostStream-item[data-number=${number}]`);\n return this.scrollToItem($item, animate).then(this.flashItem.bind(this, $item));\n }\n\n /**\n * Scroll down to a certain post by index.\n *\n * @param {number} index\n * @param {boolean} animate\n * @param {boolean} reply Whether or not to scroll to the reply placeholder.\n * @return {JQueryDeferred}\n */\n scrollToIndex(index, animate, reply) {\n const $item = reply ? $('.PostStream-item:last-child') : this.$(`.PostStream-item[data-index=${index}]`);\n this.scrollToItem($item, animate, true, reply);\n if (reply) {\n this.flashItem($item);\n }\n }\n\n /**\n * Scroll down to the given post.\n *\n * @param {JQuery} $item\n * @param {boolean} animate\n * @param {boolean} force Whether or not to force scrolling to the item, even\n * if it is already in the viewport.\n * @param {boolean} reply Whether or not to scroll to the reply placeholder.\n * @return {JQueryDeferred}\n */\n scrollToItem($item, animate, force, reply) {\n const $container = $('html, body').stop(true);\n const index = $item.data('index');\n if ($item.length) {\n const itemTop = $item.offset().top - this.getMarginTop();\n const itemBottom = $item.offset().top + $item.height();\n const scrollTop = $(document).scrollTop();\n const scrollBottom = scrollTop + $(window).height();\n\n // If the item is already in the viewport, we may not need to scroll.\n // If we're scrolling to the reply placeholder, we'll make sure its\n // bottom will line up with the top of the composer.\n if (force || itemTop < scrollTop || itemBottom > scrollBottom) {\n const top = reply ? itemBottom - $(window).height() + app.composer.computedHeight() : $item.is(':first-child') ? 0 : itemTop;\n if (!animate) {\n $container.scrollTop(top);\n } else if (top !== scrollTop) {\n $container.animate({\n scrollTop: top\n }, 'fast');\n }\n }\n }\n const updateScrubberHeight = () => {\n // We manually set the index because we want to display the index of the\n // exact post we've scrolled to, not just that of the first post within viewport.\n this.updateScrubber();\n if (index !== undefined) this.stream.index = index + 1;\n };\n\n // If we don't update this before the scroll, the scrubber will start\n // at the top, and animate down, which can be confusing\n updateScrubberHeight();\n this.stream.forceUpdateScrubber = true;\n return Promise.all([$container.promise(), this.stream.loadPromise]).then(() => {\n m.redraw.sync();\n\n // Rendering post contents will probably throw off our position.\n // To counter this, we'll scroll either:\n // - To the reply placeholder (aligned with composer top)\n // - To the top of the page if we're on the first post\n // - To the top of a post (if that post exists)\n // If the post does not currently exist, it's probably\n // outside of the range we loaded in, so we won't adjust anything,\n // as it will soon be rendered by the \"load more\" system.\n let itemOffset;\n if (reply) {\n const $placeholder = $('.PostStream-item:last-child');\n $(window).scrollTop($placeholder.offset().top + $placeholder.height() - $(window).height() + app.composer.computedHeight());\n } else if (index === 0) {\n $(window).scrollTop(0);\n } else if (itemOffset = $(`.PostStream-item[data-index=${index}]`).offset()) {\n $(window).scrollTop(itemOffset.top - this.getMarginTop());\n }\n\n // We want to adjust this again after posts have been loaded in\n // and position adjusted so that the scrubber's height is accurate.\n updateScrubberHeight();\n this.calculatePosition();\n this.stream.paused = false;\n // Check if we need to load more posts after scrolling.\n this.loadPostsIfNeeded();\n });\n }\n\n /**\n * 'Flash' the given post, drawing the user's attention to it.\n *\n * @param {JQuery} $item\n */\n flashItem($item) {\n // This might execute before the fadeIn class has been removed in PostStreamItem's\n // oncreate, so we remove it just to be safe and avoid a double animation.\n $item.removeClass('fadeIn');\n $item.addClass('flash').on('animationend webkitAnimationEnd', e => {\n $item.removeClass('flash');\n });\n }\n}\nflarum.reg.add('core', 'forum/components/PostStream', PostStream);"],"names":["ReplyPlaceholder","Component","view","this","attrs","composingReply","app","composer","composingReplyTo","discussion","m","className","Avatar","A","user","session","username","listItems","badges","toArray","ComposerPostPreview","surround","anchorPreview","bind","reply","onclick","DiscussionControls","replyAction","call","catch","translator","trans","preview","anchorToBottom","$","window","scrollTop","height","document","flarum","reg","add","PostType","post","PostComponent","postComponents","contentType","PostStream","oninit","vnode","super","stream","scrollListener","ScrollListener","onscroll","lastTime","viewingEnd","posts","postIds","postFadeIn","dom","addClass","setTimeout","removeClass","items","map","i","content","visibleStart","time","createdAt","key","id","oncreate","toISOString","number","dt","period","dayjs","fromNow","LoadingPost","postStreamElement","Object","assign","data","relationships","firstPost","afterFirstPostItems","length","fragment","visibleEnd","push","Button","loadNext","endItems","canReply","count","role","pagesLoading","ItemList","onupdate","triggerScroll","start","onremove","stop","clearTimeout","calculatePositionTimeout","needsScroll","target","targetPost","scrollToNumber","animateScroll","scrollToIndex","index","top","pageYOffset","paused","updateScrubber","loadPostsIfNeeded","calculatePosition","marginTop","getMarginTop","viewportHeight","viewportTop","$item","offset","loadPrevious","outerHeight","$items","visible","indexFromViewPort","each","$this","visibleTop","Math","max","visiblePost","min","parseFloat","description","formatDateTime","$window","startNumber","endNumber","undefined","onPositionChange","headerId","screen","parseInt","css","animate","scrollToItem","then","flashItem","force","$container","itemTop","itemBottom","scrollBottom","computedHeight","is","updateScrubberHeight","forceUpdateScrubber","Promise","all","promise","loadPromise","itemOffset","redraw","sync","$placeholder","on","e"],"sourceRoot":""} \ No newline at end of file