Skip to content

Commit

Permalink
Implement infinite scroll for events
Browse files Browse the repository at this point in the history
This introduces a new view LoadMoreView which triggers a call to `loadMore` on the controller when the view is visible in the current viewport. The callback then can trigger a loading of more items...
  • Loading branch information
pangratz committed Aug 12, 2012
1 parent b263a56 commit 68d1728
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 32 deletions.
38 changes: 36 additions & 2 deletions app/lib/controller.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,51 @@
require('dashboard/core');

Dashboard.LoadMoreMixin = Ember.Mixin.create(Ember.Evented, {
canLoadMore: true,
autoFetch: true,
currentPage: 1,
resetLoadMore: function() {
this.set('currentPage', 1);
},
loadMore: Ember.K
});

Dashboard.ApplicationController = Ember.Controller.extend();

Dashboard.UserController = Ember.ObjectController.extend();

Dashboard.RepositoryController = Ember.ObjectController.extend();

Dashboard.EventsController = Ember.ArrayController.extend();
Dashboard.EventsController = Ember.ArrayController.extend(Dashboard.LoadMoreMixin, {
canLoadMore: function() {
return this.get('currentPage') < 10;
}.property('currentPage'),

loadMore: function() {
if (this.get('canLoadMore')) {
var page = this.incrementProperty('currentPage');
this.get('target').send('loadMoreEvents', page);
}
}
});

Dashboard.RepositoriesController = Ember.ArrayController.extend({
Dashboard.RepositoriesController = Ember.ArrayController.extend(Dashboard.LoadMoreMixin, {
sortProperties: 'updated_at'.w(),
sortAscending: false,
loadWatchedRepositories: function(username) {
this.get('dataSource').watchedRepositories(username, this, 'addObjects');
},

canLoadMore: function() {
var reposCount = this.get('owner.publicRepos');
var length = this.get('length');
return true || (length < reposCount);
}.property('owner.publicRepos', 'length'),

loadMore: function() {
if (this.get('canLoadMore')) {
var page = this.incrementProperty('currentPage');
this.get('target').send('loadMoreRepos', page);
}
}
});
32 changes: 18 additions & 14 deletions app/lib/github_adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,17 @@ Dashboard.GitHubAdpater = DS.Adapter.extend({
}
},

_invoke: function(target, callback) {
_invoke: function(target, callback, query) {
return function(data) {
Ember.tryInvoke(target, callback, [data]);
}
Ember.tryInvoke(query, 'isLoadedCallback');
};
},

_storeLoad: function(store, type, id) {
return function(data) {
store.load(type, id, data);
}
};
},

find: function(store, type, id) {
Expand All @@ -58,25 +59,28 @@ Dashboard.GitHubAdpater = DS.Adapter.extend({
this.ajax('/users/%@/repos'.fmt(query.username), this._invoke(modelArray, 'load'));

// events for a repository
} else if (Dashboard.Event.detect(type) && query.username && query.repository) {
this.ajax('/repos/%@/%@/events'.fmt(query.username, query.repository), this._invoke(modelArray, 'load'));
} else if (Dashboard.Event.detect(type) && query.repoName) {
this.ajax('/repos/%@/events?page=%@'.fmt(query.repoName, query.page || 1), this._invoke(modelArray, 'load', query));

// events for a user
} else if (Dashboard.Event.detect(type) && query.username && !query.repository) {
this.ajax('/users/%@/events'.fmt(query.username), this._invoke(modelArray, 'load'));
} else if (Dashboard.Event.detect(type) && query.username) {
this.ajax('/users/%@/events?page=%@'.fmt(query.username, query.page || 1), this._invoke(modelArray, 'load', query));
}
}
});

Dashboard.GitHubFixtureAdpater = Dashboard.GitHubAdpater.extend({
PREFIX: 'http://localhost:9292/app/tests/mock_response_data',
_ajax: function(url, callback) {
Ember.$.ajax({
url: this.PREFIX + url + '.json',
context: this,
success: function(data) {
callback.call(this, {meta: {}, data: data});
}
});
var encodedUrl = url.replace(/\?/, '%3F').replace(/\=/, '%3D');
Ember.run.later(this, function() {
Ember.$.ajax({
url: this.PREFIX + encodedUrl + '.json',
context: this,
success: function(data) {
callback.call(this, {meta: {}, data: data});
}
});
}, 500);
}
});
62 changes: 50 additions & 12 deletions app/lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,30 @@ Dashboard.Router = Ember.Router.extend({
var username = router.get('userController.id');
var store = router.get('store');

router.get('eventsController').resetLoadMore();
router.get('repositoriesController').resetLoadMore();

// set current query
var query = { username: username, isLoadedCallback: function() {
router.set('eventsController.isLoading', false);
}};
router.set('eventsController.query', query);
router.set('eventsController.isLoading', true);

// get repositories for user
var repos = store.findQuery(Dashboard.Repository, { username: username });
router.set('repositoriesController.content', repos);
var userRepositories = store.findQuery(Dashboard.Repository, { username: username });

// get events performed by user
var userEvents = store.findQuery(Dashboard.Event, { username: username });
router.set('eventsController.content', userEvents);
var filter = function(data) {
if (Ember.get(data, 'savedData.org.login') === username) { return true; }
return Ember.get(data, 'savedData.actor.login') === username;
};
var userEvents = store.filter(Dashboard.Event, query, filter);

// connect user with events and watched repositories
router.get('applicationController').connectOutlet('user');
router.get('userController').connectOutlet('repositories', 'repositories');
router.get('userController').connectOutlet('events', 'events');
router.get('userController').connectOutlet('repositories', 'repositories', userRepositories);
router.get('userController').connectOutlet('events', 'events', userEvents);
}
}),

Expand All @@ -48,22 +60,48 @@ Dashboard.Router = Ember.Router.extend({
connectOutlets: function(router, context) {
var username = router.get('userController.id');
var repoName = context.repository;
var name = '%@/%@'.fmt(username, repoName);

router.get('eventsController').resetLoadMore();

// set current query
var query = {
repoName: name,
isLoadedCallback: function() {
router.set('eventsController.isLoading', false);
}
};
router.set('eventsController.query', query);
router.set('eventsController.isLoading', true);

// fetch repo for current user
var repo = router.get('store').find(Dashboard.Repository, '%@/%@'.fmt(username, repoName));
router.set('repositoryController.content', repo);
var repository = router.get('store').find(Dashboard.Repository, name);

var events = router.get('store').findQuery(Dashboard.Event, {
username: username,
repository: repoName
});
// get all events for this repository
var filter = function(data) {
return Ember.get(data, 'savedData.repo.name') === name;
};
var events = router.get('store').filter(Dashboard.Event, query, filter);

// connect repository and events
router.set('repositoryController.content', repository);
router.get('applicationController').connectOutlet('repository');
router.get('repositoryController').connectOutlet('events', 'events', events);
}
}),

loadMoreEvents: function(router, page) {
var query = router.get('eventsController.query');
query.page = page;
router.get('store').findQuery(Dashboard.Event, query);
router.set('eventsController.isLoading', true);
},
loadMoreRepos: function(router, page) {
var username = router.get('userController.id');
var query = { username: username };
var store = router.get('store').findQuery(Dashboard.Repository, query);
},

showUserOfEvent: function(router, evt) {
var e = evt.context;
var username = e.get('actor.login');
Expand Down
13 changes: 13 additions & 0 deletions app/lib/view.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
require('dashboard/core');
require('dashboard/handlebars_helpers');
require('jquery.inview');

Dashboard.ApplicationView = Ember.View.extend({
templateName: 'application'
});

Dashboard.LoadMoreView = Ember.View.extend({
templateName: 'loadMore',
didInsertElement: function() {
if (this.get('controller.autoFetch')) {
var view = this;
this.$().bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
if (isInView) Ember.tryInvoke(view.get('controller'), 'loadMore');
});
}
}
});

Dashboard.EventsView = Ember.View.extend({
templateName: 'events',
EventView: Ember.View.extend({
Expand Down
Binary file added app/static/img/ajax-loader.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions app/templates/events.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
<ul class="unstyled">
{{#each event in controller}}
{{view view.EventView eventBinding="event"}}
{{else}}
loading ...
{{/each}}
{{view Dashboard.LoadMoreView controllerBinding="view.controller" }}
</ul>
9 changes: 9 additions & 0 deletions app/templates/loadMore.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{#if isLoading}}
fetching some more stuff <img width="10" src="img/ajax-loader.gif" >
{{else}}
{{#if canLoadMore}}
<a {{action "loadMore" target="controller" }} >click to load more</a>
{{else}}
<strong><em>no more items</em></strong>
{{/if}}
{{/if}}
2 changes: 0 additions & 2 deletions app/templates/repositories.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,5 @@
<div>{{repository.description}}</div>
<dl class="dl-horizontal">
</li>
{{else}}
loading ...
{{/each}}
</ul>
118 changes: 118 additions & 0 deletions app/vendor/jquery.inview.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 68d1728

Please sign in to comment.