Skip to content
Permalink
Browse files

Don't render all bookmarks at once

This change considerably improves rendering performance when one has a
lot of bookmarks stored.

The app now only renders the most recent 30 bookmarks by default. It
will then detect when the last item comes into view and load 30 more. It
will do this as long as there are more bookmarks to load. Keeping it
simple.

closes #68
  • Loading branch information...
skddc committed Jan 30, 2019
1 parent dd499ac commit 7bf0867164b76434aaa3d9d1e2ed2893254672a5
@@ -3,6 +3,16 @@ import Component from '@ember/component';
export default Component.extend({

tagName: 'li',
classNames: ['bookmarks']
classNames: ['bookmarks'],

didInsertElement() {
this._super(...arguments);

if (this.item.isObservingItem) {
let domElement = this.$('')[0];
this.paginationObserver.observe(domElement);
this.set('paginationItemObserved', domElement);
}
},

});
@@ -3,6 +3,8 @@ import { alias, sort } from '@ember/object/computed';
import { inject as service } from '@ember/service';
import Controller, { inject as controller } from '@ember/controller';
import { computed } from '@ember/object';
import { scheduleOnce } from '@ember/runloop';
import { isPresent } from '@ember/utils';

export default Controller.extend({

@@ -13,23 +15,74 @@ export default Controller.extend({
showSearchOnSmallScreen: alias('application.showSearchOnSmallScreen'),
isLargeScreen: alias('application.isLargeScreen'),

paginationItemsPerPage: 30,
paginationItemsToLoad: 0,
paginationItemObserved: null,

sortProperties: Object.freeze(['createdAt:desc']),
sortedBookmarks: sort('model', 'sortProperties'),

init() {
this._super(...arguments);
scheduleOnce('afterRender', this, 'createIntersectionObserver');
},

filteredContent: computed('filterText', 'sortedBookmarks', function() {
var filterText = this.filterText.toLowerCase();
this.setInitialPaginationItemCount();
let filterText = this.filterText.toLowerCase();

if (isEmpty(filterText) || filterText.length < 3) {
return this.sortedBookmarks;
} else {
return this.sortedBookmarks.filter(function(item) {
var match = ( (!isEmpty(item.description) &&
let match = ( (!isEmpty(item.description) &&
item.description.toLowerCase().indexOf(filterText) !== -1) ||
item.title.toLowerCase().indexOf(filterText) !== -1 ||
item.url.toLowerCase().indexOf(filterText) !== -1 ||
(!isEmpty(item.tags) && item.tags.indexOf(filterText) !== -1) );
return match;
});
}
})
}),

paginationActive: computed('paginationItemsPerPage.[]', 'filteredContent', function() {
return this.filteredContent.length > this.paginationItemsPerPage;
}),

paginatedContent: computed('filteredContent', 'paginationItemsToLoad', function() {
let items = this.filteredContent.slice(0, this.paginationItemsToLoad);

if (isPresent(this.paginationItemObserved)) {
this.paginationObserver.unobserve(this.paginationItemObserved);
}
items.lastObject.set('isObservingItem', true);

return items;
}),

setInitialPaginationItemCount () {
this.set('paginationItemsToLoad', this.paginationItemsPerPage);
},

createIntersectionObserver () {
const config = {
root: null,
rootMargin: '0px',
threshold: 0
};

let observer = new IntersectionObserver((entries, self) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// render more items
let newItemCount = this.paginatedContent.length + this.paginationItemsPerPage;
this.set('paginationItemsToLoad', newItemCount);
self.unobserve(entry.target);
}
});
}, config);

this.set('paginationObserver', observer);
}

});
@@ -12,6 +12,11 @@ export default Route.extend(RequireRSConnection, {
return this.storage.getBookmarks();
},

setupController (controller, model) {
controller.setInitialPaginationItemCount();
this._super(controller, model)
},

resetController: function(controller) {
if (isEmpty(controller.filterText)) {
controller.set('showSearchOnSmallScreen', false);
@@ -6,31 +6,17 @@
<div id="content">
{{#if model}}
<ul class="bookmark-list">
{{#each filteredContent as |item|}}
<li class="bookmark">
<h4 class="heading">
<a href={{item.url}} target="_blank" rel="noopener">{{item.title}}</a>
</h4>
<p class="meta">
<span class="domain">{{item.domain}}</span>
&middot;
<span class="date">{{item.createdAtTimeAgo}}</span>
{{#if item.updatedAt}}
({{t "bookmark.index.updated"}} <span class="date">{{item.updatedAtTimeAgo}})</span>
{{/if}}
{{#if item.tags}}
&middot;
{{#each item.tags as |tag|}}
<span class="tag">{{tag}}</span>
{{/each}}
{{/if}}
</p>
<div class="actions">
{{#link-to "archive.edit" item title=(t "links.edit")}}{{icon-edit}}{{/link-to}}
</div>
{{#if item.description}}<p class="description">{{item.description}}</p>{{/if}}
</li>
{{/each}}
{{#if paginationActive}}
{{#each paginatedContent as |bookmark|}}
{{bookmark-item item=bookmark
paginationObserver=paginationObserver
paginationItemObserved=paginationItemObserved }}
{{/each}}
{{else}}
{{#each filteredContent as |bookmark|}}
{{bookmark-item item=bookmark}}
{{/each}}
{{/if}}
</ul>
{{else}}
<div class="no-bookmarks">

0 comments on commit 7bf0867

Please sign in to comment.
You can’t perform that action at this time.