mirror of
https://github.com/irislib/iris-messenger.git
synced 2024-09-19 17:46:33 +00:00
render only visible ChatListItems
This commit is contained in:
parent
0f9363d779
commit
db3bd8cfbe
@ -10,7 +10,7 @@
|
||||
<meta property="og:description" content="Social Networking Freedom" />
|
||||
<meta property="og:url" content="https://iris.to" />
|
||||
<meta property="og:image" content="https://iris.to/img/cover.jpg" />
|
||||
|
||||
|
||||
<meta name="twitter:card" content="summary"></meta>
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
@ -6,6 +6,7 @@ import Helpers from '../Helpers.js';
|
||||
import Session from '../Session.js';
|
||||
import { route } from '../lib/preact-router.es.js';
|
||||
import Notifications from '../Notifications.js';
|
||||
import ScrollViewport from '../lib/preact-scroll-viewport.js';
|
||||
|
||||
class ChatList extends Component {
|
||||
constructor() {
|
||||
@ -56,14 +57,16 @@ class ChatList extends Component {
|
||||
</svg>
|
||||
${t('new_chat')}
|
||||
</div>
|
||||
${this.state.chats.map(chat =>
|
||||
html`<${ChatListItem}
|
||||
photo=${chat.photo}
|
||||
active=${chat.id === activeChat}
|
||||
key=${chat.id}
|
||||
chat=${chat}/>`
|
||||
)
|
||||
}
|
||||
<${ScrollViewport}>
|
||||
${this.state.chats.map(chat =>
|
||||
html`<${ChatListItem}
|
||||
photo=${chat.photo}
|
||||
active=${chat.id === activeChat}
|
||||
key=${chat.id}
|
||||
chat=${chat}/>`
|
||||
)
|
||||
}
|
||||
</${ScrollViewport}>
|
||||
</div>
|
||||
</section>`
|
||||
}
|
||||
|
101
src/js/lib/preact-scroll-viewport.js
Normal file
101
src/js/lib/preact-scroll-viewport.js
Normal file
@ -0,0 +1,101 @@
|
||||
import { h, Component } from './preact.js';
|
||||
import {html} from '../Helpers.js';
|
||||
|
||||
const EVENT_OPTS = {
|
||||
passive: true,
|
||||
capture: true
|
||||
};
|
||||
|
||||
/** Virtual list, renders only visible items.
|
||||
* @param {Number} rowHeight Use a static height value for each row. Prevents relayout.
|
||||
* @param {Number} defaultRowHeight Initial row height, used prior to height being calculated from first item
|
||||
* @param {Number} [overscan=10] Amount of rows to render above and below visible area of the list
|
||||
* @param {Boolean} [sync=false] true forces synchronous rendering
|
||||
* @example
|
||||
* <ScrollViewport
|
||||
* rowHeight={22}
|
||||
* defaultRowHeight={22}
|
||||
* sync
|
||||
* />
|
||||
*/
|
||||
export default class ScrollViewport extends Component {
|
||||
resized = () => {
|
||||
let height = window.innerHeight || document.documentElement.offsetHeight;
|
||||
if (height!==this.state.height) {
|
||||
this.setState({ height });
|
||||
}
|
||||
};
|
||||
|
||||
scrolled = () => {
|
||||
let offset = Math.max(0, this.base && -this.base.getBoundingClientRect().top || 0);
|
||||
this.setState({ offset });
|
||||
if (this.props.sync) this.forceUpdate();
|
||||
};
|
||||
|
||||
computeRowHeight() {
|
||||
if (this._height) return this._height;
|
||||
let first = this.base && this.base.firstElementChild && this.base.firstElementChild.firstElementChild;
|
||||
return this._height = (first && first.offsetHeight || 0);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.resized();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.resized();
|
||||
this.scrolled();
|
||||
addEventListener('resize', this.resized, EVENT_OPTS);
|
||||
addEventListener('scroll', this.scrolled, EVENT_OPTS);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
removeEventListener('resize', this.resized, EVENT_OPTS);
|
||||
removeEventListener('scroll', this.scrolled, EVENT_OPTS);
|
||||
}
|
||||
|
||||
render({ overscan=10, rowHeight, defaultRowHeight, children, ...props }, { offset=0, height=0 }) {
|
||||
rowHeight = rowHeight || this.computeRowHeight() || defaultRowHeight || 100;
|
||||
|
||||
// compute estimated height based on first item height and number of items:
|
||||
let estimatedHeight = rowHeight * children.length;
|
||||
if (typeof props.style==='string') {
|
||||
props.style += ' height:'+estimatedHeight+'px;';
|
||||
}
|
||||
else {
|
||||
(props.style || (props.style={})).height = estimatedHeight.toExponential() + 'px';
|
||||
}
|
||||
|
||||
let start = 0,
|
||||
visibleRowCount = 1;
|
||||
|
||||
if (rowHeight) {
|
||||
// first visible row index
|
||||
start = (offset / rowHeight)|0;
|
||||
|
||||
// actual number of visible rows (without overscan)
|
||||
visibleRowCount = (height / rowHeight)|0;
|
||||
|
||||
// Overscan: render blocks of rows modulo an overscan row count
|
||||
// This dramatically reduces DOM writes during scrolling
|
||||
if (overscan) {
|
||||
start = Math.max(0, start - (start % overscan));
|
||||
visibleRowCount += overscan;
|
||||
}
|
||||
}
|
||||
|
||||
// last visible + overscan row index
|
||||
let end = start + 1 + visibleRowCount;
|
||||
|
||||
// children currently in viewport plus overscan items
|
||||
let visible = children.slice(start, end);
|
||||
|
||||
return html`
|
||||
<div ${{...props}}>
|
||||
<div style=${{ position: 'relative', top: start*rowHeight }}>
|
||||
${visible}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
2
src/js/lib/preact-scroll-viewport.min.js
vendored
Normal file
2
src/js/lib/preact-scroll-viewport.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("preact")):"function"==typeof define&&define.amd?define(["preact"],t):e.ScrollViewport=t(e.preact)}(this,function(e){function t(e,t){var o={};for(var i in e)t.indexOf(i)>=0||Object.prototype.hasOwnProperty.call(e,i)&&(o[i]=e[i]);return o}function o(e,t){if(e)return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){"function"!=typeof t&&null!==t||(e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t))}var n={passive:!0,capture:!0};return function(r){function s(){for(var e,t,i,n=arguments.length,s=Array(n),c=0;c<n;c++)s[c]=arguments[c];return e=t=o(this,r.call.apply(r,[this].concat(s))),t.resized=function(){var e=window.innerHeight||document.documentElement.offsetHeight;e!==t.state.height&&t.setState({height:e})},t.scrolled=function(){var e=Math.max(0,t.base&&-t.base.getBoundingClientRect().top||0);t.setState({offset:e}),t.props.sync&&t.forceUpdate()},i=e,o(t,i)}return i(s,r),s.prototype.computeRowHeight=function(){if(this._height)return this._height;var e=this.base&&this.base.firstElementChild&&this.base.firstElementChild.firstElementChild;return this._height=e&&e.offsetHeight||0},s.prototype.componentDidUpdate=function(){this.resized()},s.prototype.componentDidMount=function(){this.resized(),this.scrolled(),addEventListener("resize",this.resized,n),addEventListener("scroll",this.scrolled,n)},s.prototype.componentWillUnmount=function(){removeEventListener("resize",this.resized,n),removeEventListener("scroll",this.scrolled,n)},s.prototype.render=function(o,i){var n=i.offset,r=void 0===n?0:n,s=i.height,c=void 0===s?0:s,h=o.overscan,p=void 0===h?10:h,l=o.rowHeight,a=o.defaultRowHeight,f=o.children,u=t(o,["overscan","rowHeight","defaultRowHeight","children"]);l=l||this.computeRowHeight()||a||100;var d=l*f.length;"string"==typeof u.style?u.style+=" height:"+d+"px;":(u.style||(u.style={})).height=d.toExponential()+"px";var v=0,y=1;l&&(v=r/l|0,y=c/l|0,p&&(v=Math.max(0,v-v%p),y+=p));var g=v+1+y,m=f.slice(v,g);return e.h("div",u,e.h("div",{style:{position:"relative",top:v*l}},m))},s}(e.Component)});
|
||||
//# sourceMappingURL=preact-scroll-viewport.min.js.map
|
Loading…
Reference in New Issue
Block a user