diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb
index 339848d33..efff2f0cd 100644
--- a/app/controllers/photos_controller.rb
+++ b/app/controllers/photos_controller.rb
@@ -29,9 +29,12 @@ class PhotosController < ApplicationController
@contacts_of_contact_count = 0
end
- @posts = current_user.photos_from(@person).paginate(:page => params[:page])
-
- render 'people/show'
+ @posts = current_user.photos_from(@person)
+
+ respond_to do |format|
+ format.all { render 'people/show' }
+ format.json{ render_for_api :backbone, :json => @posts, :root => :photos }
+ end
else
flash[:error] = I18n.t 'people.show.does_not_exist'
diff --git a/public/javascripts/app/collections/photos.js b/public/javascripts/app/collections/photos.js
new file mode 100644
index 000000000..40c960b23
--- /dev/null
+++ b/public/javascripts/app/collections/photos.js
@@ -0,0 +1,12 @@
+app.collections.Photos = Backbone.Collection.extend({
+ url : "/photos",
+
+ model: function(attrs, options) {
+ var modelClass = app.models.Photo
+ return new modelClass(attrs, options);
+ },
+
+ parse: function(resp){
+ return resp.photos;
+ }
+});
diff --git a/public/javascripts/app/models/photo.js b/public/javascripts/app/models/photo.js
new file mode 100644
index 000000000..05b4a3c1e
--- /dev/null
+++ b/public/javascripts/app/models/photo.js
@@ -0,0 +1,14 @@
+app.models.Photo = Backbone.Model.extend({
+ urlRoot : "/photos",
+
+ initialize : function() {},
+
+ createdAt : function() {
+ return this.timeOf("created_at");
+ },
+
+ timeOf: function(field) {
+ return new Date(this.get(field)) /1000;
+ },
+
+});
\ No newline at end of file
diff --git a/public/javascripts/app/models/photos.js b/public/javascripts/app/models/photos.js
new file mode 100644
index 000000000..cbffb3de2
--- /dev/null
+++ b/public/javascripts/app/models/photos.js
@@ -0,0 +1,69 @@
+app.models.Photos = Backbone.Model.extend({
+ initialize : function(){
+ this.photos = new app.collections.Photos([], this.photoOptions());
+ },
+
+ photoOptions :function(){
+ var order = this.sortOrder();
+ return {
+ comparator : function(photo) { return -photo[order](); }
+ }
+ },
+
+ url : function() {
+ return _.any(this.photos.models) ? this.timeFilteredPath() : this.basePath()
+ },
+
+ _fetching : false,
+
+ fetch : function(){
+ if(this._fetching) { return false; }
+ var self = this;
+
+ // we're fetching the collection... there is probably a better way to do this
+ self._fetching = true;
+
+ this.photos
+ .fetch({
+ add : true,
+ url : self.url()
+ })
+ .done(
+ function(resp){
+ // we're done fetching... there is probably a better way to handle this
+ self._fetching = false;
+
+ self.trigger("fetched", self);
+
+ // all loaded?
+ if(resp.photos && resp.photos.length == 0) {
+ self.trigger("allPostsLoaded", self);
+ }
+ }
+ );
+
+ return this;
+ },
+
+ basePath : function(){
+ return document.location.pathname;
+ },
+
+ timeFilteredPath : function(){
+ return this.basePath() + "?max_time=" + this.maxTime();
+ },
+
+ maxTime: function(){
+ var lastPost = _.last(this.photos.models);
+ return lastPost[this.sortOrder()]()
+ },
+
+ sortOrder : function() {
+ return "createdAt";
+ },
+
+ add : function(models){
+ this.photos.add(models)
+ }
+
+});
\ No newline at end of file
diff --git a/public/javascripts/app/router.js b/public/javascripts/app/router.js
index 736128730..2f6e6dc23 100644
--- a/public/javascripts/app/router.js
+++ b/public/javascripts/app/router.js
@@ -11,7 +11,8 @@ app.Router = Backbone.Router.extend({
"liked": "stream",
"mentions": "stream",
"people/:id": "stream",
- "u/:name": "stream",
+ "people/:id/photos": "photos",
+ "u/:name": "stream",
"followed_tags": "stream",
"tags/:name": "stream",
"posts/:id": "singlePost",
@@ -19,7 +20,7 @@ app.Router = Backbone.Router.extend({
},
stream : function() {
- app.stream = new app.models.Stream()
+ app.stream = new app.models.Stream();
app.page = new app.views.Stream({model : app.stream}).render();
app.publisher = app.publisher || new app.views.Publisher({collection : app.stream.posts});
@@ -29,6 +30,13 @@ app.Router = Backbone.Router.extend({
$('#selected_aspect_contacts .content').html(streamFacesView.el);
},
+ photos : function() {
+ app.photos = new app.models.Photos();
+ app.page = new app.views.Photos({model : app.photos}).render();
+
+ $("#main_stream").html(app.page.el);
+ },
+
singlePost : function(id) {
new app.models.Post({id : id}).fetch({success : function(resp){
var postAttrs = resp.get("posts");
diff --git a/public/javascripts/app/templates/photo.handlebars b/public/javascripts/app/templates/photo.handlebars
new file mode 100644
index 000000000..375d6869e
--- /dev/null
+++ b/public/javascripts/app/templates/photo.handlebars
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/public/javascripts/app/views/photo_view.js b/public/javascripts/app/views/photo_view.js
new file mode 100644
index 000000000..c672863d4
--- /dev/null
+++ b/public/javascripts/app/views/photo_view.js
@@ -0,0 +1,13 @@
+app.views.Photo = app.views.StreamObject.extend({
+
+ templateName: "photo",
+
+ className : "photo loaded",
+
+ initialize : function() {
+ $(this.el).attr("id", this.model.get("guid"));
+ this.model.bind('remove', this.remove, this);
+ return this;
+ }
+
+});
\ No newline at end of file
diff --git a/public/javascripts/app/views/photos_view.js b/public/javascripts/app/views/photos_view.js
new file mode 100644
index 000000000..d9161356e
--- /dev/null
+++ b/public/javascripts/app/views/photos_view.js
@@ -0,0 +1,56 @@
+app.views.Photos = Backbone.View.extend({
+
+ events : {},
+
+ initialize : function(options) {
+ this.photos = this.model;
+ this.collection = this.model.photos;
+
+ this.setupEvents();
+ //this.setupLightbox(); ERROR: "imageThumb is undefined" ...
+ },
+
+ setupEvents : function(){
+ this.photos.bind("fetched", this.removeLoader, this)
+ this.collection.bind("add", this.addPhoto, this);
+ },
+
+ addPhoto : function(photo) {
+ var photoView = new app.views.Photo({ model: photo });
+
+ $(this.el)[
+ (this.collection.at(0).id == photo.id)
+ ? "prepend"
+ : "append"
+ ](photoView.render().el);
+
+ return this;
+ },
+
+ render : function(evt) {
+ if(evt) {evt.preventDefault(); }
+
+ if(this.model.fetch()) {
+ this.appendLoader();
+ };
+
+ return this;
+ },
+
+ appendLoader: function(){
+ $("#paginate").html($("
", {
+ src : "/images/static-loader.png",
+ "class" : "loader"
+ }));
+ },
+
+ removeLoader: function() {
+ $("#paginate").empty();
+ },
+
+ setupLightbox : function(){
+ this.lightbox = Diaspora.BaseWidget.instantiate("Lightbox");
+ $(this.el).delegate("a.photo-link", "click", this.lightbox.lightboxImageClicked);
+ },
+
+});
\ No newline at end of file
diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass
index f0583a407..cee46c3c5 100644
--- a/public/stylesheets/sass/application.sass
+++ b/public/stylesheets/sass/application.sass
@@ -2821,3 +2821,4 @@ a.toggle_selector
:color #999
a
:color #666
+ :color #666
\ No newline at end of file
diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb
index 1d9e5d491..6279723af 100644
--- a/spec/controllers/photos_controller_spec.rb
+++ b/spec/controllers/photos_controller_spec.rb
@@ -80,6 +80,14 @@ describe PhotosController do
assigns[:person].should == bob.person
assigns[:posts].should == [@bobs_photo]
end
+
+ it "returns json when requested" do
+ request.env['HTTP_ACCEPT'] = 'application/json'
+ get :index, :person_id => alice.person.guid.to_s
+
+ response.headers['Content-Type'].should match 'application/json.*'
+ save_fixture(response.body, "photos_json")
+ end
end
describe '#show' do
diff --git a/spec/javascripts/app/models/photo_spec.js b/spec/javascripts/app/models/photo_spec.js
new file mode 100644
index 000000000..d1ccd8703
--- /dev/null
+++ b/spec/javascripts/app/models/photo_spec.js
@@ -0,0 +1,27 @@
+describe("app.models.Photo", function() {
+
+ beforeEach(function(){
+ this.photo = new app.models.Photo();
+ });
+
+ describe("url", function(){
+ it("should be /photos when it doesn't have an id", function(){
+ expect(new app.models.Photo().url()).toBe("/photos");
+ });
+
+ it("should be /photos/id when it has an id", function(){
+ expect(new app.models.Photo({id: 5}).url()).toBe("/photos/5");
+ });
+ });
+
+ describe("createdAt", function() {
+ it("returns the photo's created_at as an integer", function() {
+ var date = new Date;
+ this.photo.set({ created_at: +date * 1000 });
+
+ expect(typeof this.photo.createdAt()).toEqual("number");
+ expect(this.photo.createdAt()).toEqual(+date);
+ });
+ });
+
+});
diff --git a/spec/javascripts/app/views/photos_view_spec.js b/spec/javascripts/app/views/photos_view_spec.js
new file mode 100644
index 000000000..c96a39df8
--- /dev/null
+++ b/spec/javascripts/app/views/photos_view_spec.js
@@ -0,0 +1,45 @@
+describe("app.views.Photos", function() {
+ beforeEach(function() {
+ loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
+
+ this._photos = $.parseJSON(spec.readFixture("photos_json"))["photos"];
+
+ this.photos = new app.models.Photos();
+ this.photos.add(this._photos);
+
+ this.view = new app.views.Photos({model : this.photos});
+
+ // do this manually because we've moved loadMore into render??
+ this.view.render();
+ _.each(this.view.collection.models, function(photo) {
+ this.view.addPhoto(photo);
+ }, this);
+ });
+
+ describe("initialize", function() {
+ // nothing there yet
+ });
+
+ describe("#render", function() {
+ beforeEach(function() {
+ this.photo = this.photos.photos.models[0];
+ this.photoElement = $(this.view.$("#" + this.photo.get("guid")));
+ });
+
+ context("when rendering a photo message", function() {
+ it("shows the photo in the content area", function() {
+ expect(this.photoElement.length).toBeGreaterThan(0); //markdown'ed
+ });
+ });
+ });
+
+ describe("removeLoader", function() {
+ it("emptys the pagination div when the stream is fetched", function() {
+ $("#jasmine_content").append($('