basic experimental stream

This commit is contained in:
Dennis Collinson 2012-05-14 17:56:17 -07:00
parent 5620412fbd
commit a90968ea2d
16 changed files with 206 additions and 121 deletions

View file

@ -2,16 +2,13 @@
//= require ../collections/photos
app.models.Stream = Backbone.Collection.extend({
initialize : function(models, options){
var collection = app.collections.Posts;
if( options && options.collection ) collection = options.collection;
this.items = new collection([], this.collectionOptions());
var collectionClass = options && options.collection || app.collections.Posts;
this.items = new collectionClass([], this.collectionOptions());
},
collectionOptions :function(){
var order = this.sortOrder();
return {
comparator : function(item) { return -item[order](); }
}
return { comparator : function(item) { return -item[order](); } }
},
url : function(){
@ -61,13 +58,8 @@ app.models.Stream = Backbone.Collection.extend({
this.items.add(models)
},
preloadOrFetch : function(){ //hai, plz test me THNX
if(app.hasPreload("stream")){
this.preload()
} else {
this.fetch()
}
return $.when(app.hasPreload("stream") ? this.preload() : this.fetch())
},
preload : function(){

View file

@ -24,22 +24,18 @@ app.pages.Profile = app.views.Base.extend({
initialize : function(options) {
this.personGUID = options.personId
this.model = this.model || app.models.Profile.preloadOrFetch(this.personGUID)
this.model = this.model || app.models.Profile.preloadOrFetch(this.personGUID)
this.stream = options && options.stream || new app.models.Stream()
this.stream.preloadOrFetch().done(_.bind(this.pulsateNewPostControl, this));
this.stream.items.bind("remove", this.pulsateNewPostControl, this)
/* this needs to be fixed... used to be bound by this.model change event.
* will most likely result in spontaneous results :(
*
* note: defer to make sure the call stack is emptied before calling this, buying us a little more time */
_.defer(_.bind(this.setPageTitleAndBackground, this))
/* binds for getting started pulsation */
this.stream.bind("fetched", this.pulsateNewPostControl, this)
this.stream.items.bind("remove", this.pulsateNewPostControl, this)
this.stream.preloadOrFetch();
this.canvasView = new app.views.Canvas({ model : this.stream })
this.wallpaperForm = new app.forms.Wallpaper()
this.profileInfo = new app.views.ProfileInfo({ model : this.model })

View file

@ -0,0 +1,23 @@
app.views.NewStream = app.views.Base.extend(_.extend({}, app.views.infiniteScrollMixin, {
initialize: function(){
this.stream = this.model
this.collection = this.stream.items
this.postClass = app.views.SmallFrame
this.setupInfiniteScroll()
}
}));
app.pages.Stream = app.views.Base.extend({
templateName : "stream",
subviews : {
"#stream-content" : "streamView"
},
initialize : function(){
this.stream = this.model = new app.models.Stream()
this.stream.preloadOrFetch();
this.streamView = new app.views.NewStream({ model : this.stream })
}
});

View file

@ -1,44 +1,35 @@
app.Router = Backbone.Router.extend({
routes: {
"activity": "stream",
"stream": "stream",
"participate": "stream",
"explore": "stream",
"aspects": "stream",
"aspects:query": "stream",
"commented": "stream",
"liked": "stream",
"mentions": "stream",
//new hotness
"stream?ex=true": 'newStream',
"people/:id?ex=true": "newProfile",
"people/:id": "profile",
"u/:name": "profile",
"people/:id/photos": "photos",
"followed_tags": "stream",
"tags/:name": "stream",
"posts/new" : "composer",
"posts/:id": "singlePost",
"posts/:id/next": "siblingPost",
"posts/:id/previous": "siblingPost",
"p/:id": "singlePost",
"framer": "framer"
"framer": "framer",
//oldness
"activity": "stream",
"stream": "stream",
"participate": "stream",
"explore": "stream",
"aspects": "stream",
"aspects:query": "stream",
"commented": "stream",
"liked": "stream",
"mentions": "stream",
"followed_tags": "stream",
"tags/:name": "stream",
"people/:id/photos": "photos",
"people/:id": "profile",
"u/:name": "profile"
},
siblingPost : function(){ //next or previous
var post = new app.models.Post();
post.bind("change", setPreloadAttributesAndNavigate)
post.fetch({url : window.location})
function setPreloadAttributesAndNavigate(){
window.preloads.post = post.attributes
app.router.navigate(post.url(), {trigger:true, replace: true})
}
newStream : function() {
this.renderPage(function(){ return new app.pages.Stream()});
},
newProfile : function(personId) {
@ -57,10 +48,25 @@ app.Router = Backbone.Router.extend({
this.renderPage(function(){ return new app.pages.PostViewer({ id: id })});
},
profile : function(page) {
this.stream()
siblingPost : function(){ //next or previous
var post = new app.models.Post();
post.bind("change", setPreloadAttributesAndNavigate)
post.fetch({url : window.location})
function setPreloadAttributesAndNavigate(){
window.preloads.post = post.attributes
app.router.navigate(post.url(), {trigger:true, replace: true})
}
},
renderPage : function(pageConstructor){
app.page && app.page.unbind && app.page.unbind() //old page might mutate global events $(document).keypress, so unbind before creating
app.page = pageConstructor() //create new page after the world is clean (like that will ever happen)
$("#container").html(app.page.render().el)
},
//below here is oldness
stream : function(page) {
app.stream = new app.models.Stream();
app.stream.fetch();
@ -73,22 +79,14 @@ app.Router = Backbone.Router.extend({
$('#selected_aspect_contacts .content').html(streamFacesView.render().el);
},
profile : function(page) {
this.stream()
},
photos : function() {
app.photos = new app.models.Stream([], {collection: app.collections.Photos});
app.page = new app.views.Photos({model : app.photos});
$("#main_stream").html(app.page.render().el);
},
isExperimental : function(query) {
return query.search("ex=true") != -1
},
renderPage : function(pageConstructor){
app.page && app.page.unbind && app.page.unbind() //old page might mutate global events $(document).keypress, so unbind before creating
app.page = pageConstructor() //create new page after the world is clean (like that will ever happen)
$("#container").html(app.page.render().el)
}
});

View file

@ -76,8 +76,8 @@ app.views.Base = Backbone.View.extend({
// Mixin to render a collection that fetches more via infinite scroll, for a view that has no template.
// Requires:
// a stream model, bound as this.stream
// a stream's posts, bound as this.collection
// a stream model, assigned to this.stream
// a stream's posts, assigned to this.collection
// a postClass to be declared
// a #paginate div in the layout
// a call to setupInfiniteScroll
@ -115,6 +115,17 @@ app.views.infiniteScrollMixin = {
$(window).unbind("scroll");
},
renderTemplate : function(){
this.renderInitialPosts()
},
renderInitialPosts : function(){
this.$el.empty()
this.stream.items.each(_.bind(function(post){
this.$el.append(this.createPostView(post).render().el);
}, this))
},
fetchAndshowLoader : function(){
if(this.stream.isFetching()) { return false }
this.stream.fetch()

View file

@ -0,0 +1,33 @@
//= require ./small_frame
app.views.CanvasFrame = app.views.SmallFrame.extend({
SINGLE_COLUMN_WIDTH : 265,
DOUBLE_COLUMN_WIDTH : 560,
adjustedImageHeight : function() {
if(!(this.model.get("photos") || [])[0]) { return }
var modifiers = [this.dimensionsClass(), this.colorClass()].join(' ')
, width;
/* mobile width
*
* currently does not re-calculate on orientation change */
if($(window).width() <= 767) {
width = $(window).width();
}
var firstPhoto = this.model.get("photos")[0]
, width = width || (modifiers.search("x2") != -1 ? this.DOUBLE_COLUMN_WIDTH : this.SINGLE_COLUMN_WIDTH)
, ratio = width / firstPhoto.dimensions.width;
return(ratio * firstPhoto.dimensions.height)
},
presenter : function(){
return _.extend(this.smallFramePresenter(), {
adjustedImageHeight : this.adjustedImageHeight()
})
}
});

View file

@ -2,15 +2,13 @@ app.views.Canvas = app.views.Base.extend(_.extend({}, app.views.infiniteScrollMi
initialize: function(){
this.stream = this.model
this.collection = this.stream.items
this.postClass = app.views.SmallFrame
this.postClass = app.views.CanvasFrame
this.setupInfiniteScroll()
this.stream.bind("reLayout", this.reLayout, this)
this.stream.bind("fetched", this.triggerRelayoutAfterImagesLoaded, this)
},
renderTemplate : function() {
this.$el.empty()
if(this.stream.items.isEmpty()){
var message
, person = app.page.model
@ -23,9 +21,7 @@ app.views.Canvas = app.views.Base.extend(_.extend({}, app.views.infiniteScrollMi
this.$el.html("<p class='no-post-message'>" + message + "</p>")
} else {
this.stream.items.each(_.bind(function(post){
this.$el.append(this.createPostView(post).render().el);
}, this))
this.renderInitialPosts()
}
//needs to be deferred so it happens after html rendering finishes

View file

@ -1,10 +1,6 @@
//= require "./post_view"
app.views.SmallFrame = app.views.Post.extend({
SINGLE_COLUMN_WIDTH : 265,
DOUBLE_COLUMN_WIDTH : 560,
className : "canvas-frame",
templateName : "small-frame",
@ -24,11 +20,14 @@ app.views.SmallFrame = app.views.Post.extend({
},
presenter : function(){
return this.smallFramePresenter()
},
smallFramePresenter : function(){
//todo : we need to have something better for small frame text, probably using the headline() scenario.
return _.extend(this.defaultPresenter(),
{
text : this.model && app.helpers.textFormatter(this.model.get("text"), this.model),
adjustedImageHeight : this.adjustedImageHeight(),
likesCount : this.model.interactions.likesCount(),
resharesCount : this.model.interactions.resharesCount(),
commentsCount : this.model.interactions.commentsCount()
@ -75,26 +74,6 @@ app.views.SmallFrame = app.views.Post.extend({
return (this.model.get("favorite")) ? "x2 width height" : ""
},
adjustedImageHeight : function() {
if(!(this.model.get("photos") || [])[0]) { return }
var modifiers = [this.dimensionsClass(), this.colorClass()].join(' ')
, width;
/* mobile width
*
* currently does not re-calculate on orientation change */
if($(window).width() <= 767) {
width = $(window).width();
}
var firstPhoto = this.model.get("photos")[0]
, width = width || (modifiers.search("x2") != -1 ? this.DOUBLE_COLUMN_WIDTH : this.SINGLE_COLUMN_WIDTH)
, ratio = width / firstPhoto.dimensions.width;
return(ratio * firstPhoto.dimensions.height)
},
favoritePost : function(evt) {
if(evt) {
/* follow links instead of faving the targeted post */
@ -125,4 +104,4 @@ app.views.SmallFrame = app.views.Post.extend({
window.preloads.post = this.model.attributes
app.router.navigate(this.model.url(), true)
}
});
});

View file

@ -315,8 +315,6 @@
background-color: black;
}
img { max-height: 100% }
&.has-text{
img {
//image container background is black, lowering opacity darkens image
@ -338,6 +336,21 @@
}
}
/* new stream overrides */
#stream {
color : #fff;
background-color : #333;
}
#stream-content {
.canvas-frame {
width: 100%;
.content {
width: 100%;
}
}
}
/* responsive */
@media (max-width: 767px) {
body {
@ -357,4 +370,4 @@
width : auto !important;
}
}
}
}

View file

@ -6,7 +6,7 @@
{{#if photos}}
<div class="image-container">
{{#each photos}}
<img src="{{sizes.large}}" style="height:{{../adjustedImageHeight}}px;" />
<img src="{{sizes.large}}" {{#if ../adjustedImageHeight}}style="height:{{../adjustedImageHeight}}px;"{{/if}} />
{{/each}}
</div>
{{/if}}

View file

@ -0,0 +1,9 @@
<div id="stream">
<div class="container">
<div class="row">
<div class="span5">
<section id="stream-content"/>
</div>
</div>
</div>
</div>

View file

@ -113,6 +113,10 @@ class ApplicationController < ActionController::Base
params[:max_time] ? Time.at(params[:max_time].to_i) : Time.now + 1
end
def flag
@flag ||= FeatureFlagger.new(current_user)
end
private
def current_user_redirect_path

View file

@ -36,7 +36,13 @@ class StreamsController < ApplicationController
end
def multi
stream_responder(Stream::Multi)
if params[:ex] && flag.new_stream?
@stream = Stream::Multi.new(current_user, :max_time => max_time)
gon.stream = PostPresenter.collection_json(@stream.stream_posts, current_user)
render :nothing => true, :layout => "post"
else
stream_responder(Stream::Multi)
end
end
def commented

View file

@ -1,17 +1,21 @@
class FeatureFlagger
def initialize(current_user, person=nil)
def initialize(current_user, person_being_viewed=nil)
@current_user = current_user
@person = person
@person = person_being_viewed
end
def new_publisher?
person_is_beta? || admin? || developer?
beta? || admin? || developer?
end
def new_profile?
person_is_beta?
end
def new_stream?
admin? && beta?
end
def new_hotness?
ENV["NEW_HOTNESS"]
end
@ -30,9 +34,12 @@ class FeatureFlagger
@current_user.try(:admin?)
end
def beta?
Role.is_beta?(@current_user.person)
end
def person_is_beta?
return unless @person.present?
Role.is_beta?(@person) || Role.is_admin?(@person)
end
end

View file

@ -0,0 +1,31 @@
describe("app.views.CanvasFrame", function(){
beforeEach(function(){
this.model = factory.post({
photos : [
factory.photoAttrs({sizes : {
large : "http://tieguy.org/me.jpg"
},
dimensions : {
width : 100,
height : 200 }
}),
factory.photoAttrs({sizes : {large : "http://whatthefuckiselizabethstarkupto.com/none_knows.gif"}}) //SIC
]
})
this.view = new app.views.CanvasFrame({model : this.model})
})
context("images", function() {
it("appends the correct dimensions to an image, given a model with an image", function(){
var firstPhoto = this.model.get("photos")[0]
this.view.SINGLE_COLUMN_WIDTH = 100
expect(this.view.adjustedImageHeight(firstPhoto)).toBe(200)
this.view.SINGLE_COLUMN_WIDTH = 200
expect(this.view.adjustedImageHeight(firstPhoto)).toBe(400)
this.view.SINGLE_COLUMN_WIDTH = 50
expect(this.view.adjustedImageHeight(firstPhoto)).toBe(100)
})
})
});

View file

@ -36,19 +36,6 @@ describe("app.views.SmallFrame", function(){
})
})
context("images", function() {
it("appends the correct dimensions to an image, given a model with an image", function(){
var firstPhoto = this.model.get("photos")[0]
this.view.SINGLE_COLUMN_WIDTH = 100
expect(this.view.adjustedImageHeight(firstPhoto)).toBe(200)
this.view.SINGLE_COLUMN_WIDTH = 200
expect(this.view.adjustedImageHeight(firstPhoto)).toBe(400)
this.view.SINGLE_COLUMN_WIDTH = 50
expect(this.view.adjustedImageHeight(firstPhoto)).toBe(100)
})
})
describe("redirecting to a post", function(){
beforeEach(function(){
app.page = { editMode : false }