basic experimental stream
This commit is contained in:
parent
5620412fbd
commit
a90968ea2d
16 changed files with 206 additions and 121 deletions
|
|
@ -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(){
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
|
|
|
|||
23
app/assets/javascripts/app/pages/stream.js
Normal file
23
app/assets/javascripts/app/pages/stream.js
Normal 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 })
|
||||
}
|
||||
});
|
||||
|
|
@ -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)
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
33
app/assets/javascripts/app/views/canvas_frame.js
Normal file
33
app/assets/javascripts/app/views/canvas_frame.js
Normal 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()
|
||||
})
|
||||
}
|
||||
});
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}}
|
||||
|
|
|
|||
9
app/assets/templates/stream.jst.hbs
Normal file
9
app/assets/templates/stream.jst.hbs
Normal 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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
31
spec/javascripts/app/views/canvas_frame_spec.js
Normal file
31
spec/javascripts/app/views/canvas_frame_spec.js
Normal 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)
|
||||
})
|
||||
})
|
||||
});
|
||||
|
|
@ -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 }
|
||||
|
|
|
|||
Loading…
Reference in a new issue