Merge pull request #4612 from diaspora/drop_spv_old_code

Drop spv old code
This commit is contained in:
Jonne Haß 2013-12-11 07:41:51 -08:00
commit c660c90091
46 changed files with 13 additions and 1362 deletions

View file

@ -4,6 +4,7 @@
A new feature [has been added](https://github.com/diaspora/diaspora/pull/4602) to allow pods to report extra statistics. Automatically after this code change, the route /statistics.json contains some basic data that was also available before via page headers (pod name, version, status of signups). But also, optionally podmins can enable user and post counts in the diaspora.yml configuration file. The counts are by default switched off, so if you want to report the total user, active user and local post counts, please edit your diaspora.yml configuration with the example values in diaspora.yml.example and uncomment the required lines as indicated.
## Refactor
* Remove old SPV code [#4612](https://github.com/diaspora/diaspora/pull/4612)
* Move non-model federation stuff into lib/ [#4363](https://github.com/diaspora/diaspora/pull/4363)
* Build a color palette to uniform color usage [#4437](https://github.com/diaspora/diaspora/pull/4437) [#4469](https://github.com/diaspora/diaspora/pull/4469) [#4479](https://github.com/diaspora/diaspora/pull/4479)
* Rename bitcoin_wallet_id setting to bitcoin_address [#4485](https://github.com/diaspora/diaspora/pull/4485)

View file

@ -15,14 +15,6 @@ app.models.Post = Backbone.Model.extend(_.extend({}, app.models.formatDateMixin,
}
},
setFrameName : function(){
this.set({frame_name : new app.models.Post.TemplatePicker(this).getFrameName()})
},
applicableTemplates: function() {
return new app.models.Post.TemplatePicker(this).applicableTemplates()
},
interactedAt : function() {
return this.timeOf("interacted_at");
},
@ -67,13 +59,4 @@ app.models.Post = Backbone.Model.extend(_.extend({}, app.models.formatDateMixin,
hasText : function(){
return $.trim(this.get("text")) !== ""
}
}), {
headlineLimit : 118,
frameMoods : [
"Wallpaper",
"Vanilla",
"Typist",
"Fridge"
]
});
}));

View file

@ -1,35 +0,0 @@
//require ../post
app.models.Post.TemplatePicker = function(model){
this.model = model
}
_.extend(app.models.Post.TemplatePicker.prototype, {
getFrameName : function getFrameName() {
var frameName
if(this.isNewspaper()){
frameName = "Typist"
} else if(this.isWallpaper()) {
frameName = "Wallpaper"
} else {
frameName = "Vanilla"
}
return frameName
},
isNewspaper : function(){
return this.model.get("text").length > 300
},
isWallpaper : function(){
return this.model.get("photos").length == 1
},
applicableTemplates : function(){
/* don't show the wallpaper option if there is no image */
var moods = app.models.Post.frameMoods;
return (!this.isWallpaper() ? _.without(moods, "Wallpaper") : moods)
}
});

View file

@ -1,107 +0,0 @@
app.pages.PostViewer = app.views.Base.extend({
templateName: "post-viewer",
subviews : {
"#post-content" : "postView",
"#post-nav" : "navView",
"#post-interactions" : "interactionsView",
"#author-info" : "authorView"
},
initialize : function(options) {
this.model = new app.models.Post({ id : options.id });
this.model.preloadOrFetch().done(_.bind(this.initViews, this));
this.model.interactions.fetch() //async, yo, might want to throttle this later.
this.bindEvents()
},
initViews : function() {
/* init view */
this.authorView = new app.views.PostViewerAuthor({ model : this.model });
this.interactionsView = new app.views.PostViewerInteractions({ model : this.model });
this.navView = new app.views.PostViewerNav({ model : this.model });
this.postView = app.views.Post.showFactory(this.model)
this.render();
},
bindEvents : function(){
this.prepIdleHooks();
this.bindNavHooks();
$(document).bind("keypress", _.bind(this.commentAnywhere, this))
$(document).bind("keypress", _.bind(this.invokePane, this))
$(document).bind("keyup", _.bind(this.closePane, this))
},
unbind : function(){
$(document).unbind("idle.idleTimer")
$(document).unbind("active.idleTimer")
$(document).unbind('keydown')
$(document).unbind('keypress')
$(document).unbind('keyup')
},
prepIdleHooks : function () {
$.idleTimer(3000);
$(document).bind("idle.idleTimer", function(){
$("body").addClass('idle');
});
$(document).bind("active.idleTimer", function(){
$("body").removeClass('idle');
});
},
bindNavHooks : function() {
var model = this.model;
$(document).keydown(function(evt){
// prevent nav from happening if the user is using the arrow keys to navigate through their comment text
if($(evt.target).is("textarea")) { return }
switch(evt.keyCode) {
case KEYCODES.LEFT:
app.router.navigate(model.get("next_post"), true); break;
case KEYCODES.RIGHT:
app.router.navigate(model.get("previous_post"), true); break;
default:
break;
}
})
},
postRenderTemplate : function() {
if(this.model.get("title")){
// formats title to html...
var html_title = app.helpers.textFormatter(this.model.get("title"), this.model);
//... and converts html to plain text
document.title = $('<div>').html(html_title).text();
}
},
commentAnywhere : function(evt) {
/* ignore enter, space bar, arrow keys */
if(_.include([KEYCODES.RETURN, KEYCODES.SPACE, KEYCODES.LEFT,
KEYCODES.UP, KEYCODES.RIGHT, KEYCODES.DOWN], evt.keyCode) ||
this.modifierPressed(evt) ) { return }
this.interactionsView.invokePane();
$('#new-post-comment textarea').focus();
},
invokePane : function(evt) {
if(evt.keyCode != KEYCODES.SPACE) { return }
this.interactionsView.invokePane();
},
closePane : function(evt) {
if(evt.keyCode != KEYCODES.ESC) { return }
this.interactionsView.hidePane();
},
modifierPressed: function(evt) {
return (evt.altKey || evt.ctrlKey || evt.shiftKey);
}
});

View file

@ -2,8 +2,6 @@ app.Router = Backbone.Router.extend({
routes: {
//new hotness
"posts/:id": "singlePost",
"posts/:id/next": "siblingPost",
"posts/:id/previous": "siblingPost",
"p/:id": "singlePost",
//oldness
@ -28,17 +26,6 @@ app.Router = Backbone.Router.extend({
this.renderPage(function(){ return new app.pages.SinglePostViewer({ id: id })});
},
siblingPost : function(){ //next or previous
var post = new app.models.Post();
post.bind("change", setPreloadAttributesAndNavigate)
post.fetch({url : window.location})
function setPreloadAttributesAndNavigate(){
window.gon.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)

View file

@ -1,15 +0,0 @@
app.views.PostViewerAuthor = app.views.Base.extend({
id : "post-author",
className : "media",
tooltipSelector : ".profile-image-container",
templateName: "post-viewer/author",
initialize : function() {
/* add a class so we know how to color the text for the author name */
this.$el.addClass(this.model.get("frame_name"))
}
});

View file

@ -1,34 +0,0 @@
//= require ../feedback_view
app.views.PostViewerFeedback = app.views.Feedback.extend({
id : "user-controls",
className : "",
templateName: "post-viewer/feedback",
subviews : {
".feedback-actions" : "feedbackActions"
},
events :_.extend({}, app.views.Feedback.prototype.events, {
"click *[rel='invoke-interaction-pane']" : "invokePane",
"click *[rel='hide-interaction-pane']" : "hidePane"
}),
initViews : function(){
this.feedbackActions = new app.views.FeedbackActions({model : this.model})
},
postRenderTemplate : function() {
this.sneakyVisiblity()
},
sneakyVisiblity : function() {
if(!$("#post-info").is(":visible")) {
this.$("#post-info-sneaky").removeClass('passive')
}
},
invokePane : function(evt){ this.trigger("invokePane") },
hidePane : function(evt){ this.trigger("hidePane") },
});

View file

@ -1,56 +0,0 @@
app.views.PostViewerInteractions = app.views.Base.extend({
className : "",
subviews : {
"#post-feedback" : "feedbackView",
"#post-reactions" : "reactionsView",
"#new-post-comment" : "newCommentView"
},
templateName: "post-viewer/interactions",
initialize : function() {
this.initViews();
this.feedbackView && this.feedbackView.bind("invokePane", this.invokePane, this)
this.feedbackView && this.feedbackView.bind("hidePane", this.hidePane, this)
},
initViews : function() {
this.reactionsView = new app.views.PostViewerReactions({ model : this.model.interactions })
/* subviews that require user */
this.feedbackView = new app.views.PostViewerFeedback({ model : this.model })
if(app.currentUser.authenticated()) {
this.newCommentView = new app.views.PostViewerNewComment({ model : this.model })
}
},
togglePane : function(evt) {
if(evt) { evt.preventDefault() }
$("#post-interactions").toggleClass("active")
this.$("#post-info").slideToggle(300)
this.removeTooltips()
},
invokePane : function() {
if(!this.$("#post-info").is(":visible")) {
this.$("#post-info-sneaky").addClass("passive")
this.togglePane()
}
},
hidePane : function() {
if(this.$("#post-info").is(":visible")) {
/* it takes about 400ms for the pane to hide. we need to keep
* the sneaky hidden until the slide is complete */
setTimeout(function(){
this.$("#post-info-sneaky").removeClass("passive")
}, 400)
this.togglePane()
}
}
});

View file

@ -1,3 +0,0 @@
app.views.PostViewerNav = app.views.Base.extend({
templateName: "post-viewer/nav"
});

View file

@ -1,66 +0,0 @@
//= require ../post_view
app.views.Post.Mood = app.views.Post.extend({
templateName : "mood",
className : "post loaded",
tagName : "article",
subviews : { "section.photo_viewer" : "photoViewer" },
initialize : function(){
$(this.el).addClass(this.mood)
},
presenter : function(){
var model = this.model
return _.extend(this.defaultPresenter(), {
headline : $(app.helpers.textFormatter(model.headline(), model)).html(),
body : app.helpers.textFormatter(model.body(), model)
})
},
photoViewer : function(){
return new app.views.PhotoViewer({ model : this.model })
},
postRenderTemplate : function(){
if(this.model.body().length < 200){
this.$('section.body').addClass('short_body');
}
}
});
app.views.Post.Day = app.views.Post.Mood.extend({
mood : "day"
})
app.views.Post.Night = app.views.Post.Mood.extend({
mood : "night"
})
app.views.Post.Newspaper = app.views.Post.Mood.extend({
mood : "newspaper"
})
app.views.Post.Wallpaper = app.views.Post.Mood.extend({
mood : "wallpaper",
templateName : "wallpaper-mood",
presenter : function(){
var backgroundPhoto = _.first(this.model.get("photos") || [])
return _.extend(app.views.Post.Mood.prototype.presenter.call(this), {
backgroundUrl : backgroundPhoto && backgroundPhoto.sizes.large
})
}
})
app.views.Post.Typist = app.views.Post.Mood.extend({
mood : "typist"
})
app.views.Post.Vanilla = app.views.Post.Mood.extend({
mood : "vanilla"
})
app.views.Post.Fridge = app.views.Post.Mood.extend({
mood : "fridge"
})

View file

@ -14,38 +14,4 @@ app.views.Post = app.views.Base.extend({
showPost : function() {
return (app.currentUser.get("showNsfw")) || !this.model.get("nsfw")
}
}, { //static methods below
showFactory : function(model) {
var frameName = model.get("frame_name");
//translate obsolete template names to the new Moods, should be removed when template picker comes client side.
var map = {
'status-with-photo-backdrop' : 'Wallpaper', //equivalent
'status' : 'Day', //equivalent
'note' : 'Newspaper', //equivalent
'photo-backdrop' : 'Day' //that theme was bad
}
frameName = map[frameName] || frameName
return new app.views.Post[frameName]({
model : model
})
function legacyShow(model) {
return new app.views.Post.Legacy({
model : model,
className : frameName + " post loaded",
templateName : "post-viewer/content/" + frameName
});
}
}
});
app.views.Post.Legacy = app.views.Post.extend({
tagName : "article",
initialize : function(options) {
this.templateName = options.templateName || this.templateName
}
})

View file

@ -1,6 +1,6 @@
app.views.PostViewerNewComment = app.views.Base.extend({
templateName: "post-viewer/new-comment",
templateName: "single-post-viewer/new-comment",
events : {
"click button" : "createComment",
@ -39,4 +39,4 @@ app.views.PostViewerNewComment = app.views.Base.extend({
$(this.scrollableArea).scrollTop($(this.scrollableArea).prop("scrollHeight"))
}
});
});

View file

@ -2,7 +2,7 @@ app.views.PostViewerReactions = app.views.Base.extend({
className : "",
templateName: "post-viewer/reactions",
templateName: "single-post-viewer/reactions",
tooltipSelector : ".avatar",

View file

@ -42,218 +42,6 @@ a { color : $link-blue }
color: inherit;
}
#author-info {
position : absolute;
z-index : 300;
.author-name {
color: #555;
}
.bd {
margin-top : 5px;
display: inline-block;
}
.post-time,
.icon-retweet {
color: #555;
@include opacity(0.5);
}
}
#post-author {
margin : 0;
padding : 10px;
&.status-with-photo-backdrop,
&.Wallpaper {
.author-name {
color : #fff;
}
.post-time,
.icon-retweet {
color: #fff;
@include opacity(0.5);
}
}
}
.post-view {
display: table;
height: 100%;
width: 100%;
}
#post-content {
display: table;
position: absolute;
height: 93%;
width: 100%;
img,
iframe {
@include photo-shadow();
}
iframe {
width: 857px;
height: 480px;
max-width: 100%;
}
.opengraph {
a {
text-decoration: none;
color: #000;
}
h2 {
margin-bottom: 20px;
}
img {
margin-bottom: 20px;
}
}
}
article { //mood posts
//default frame show styles
$big-text-size : 2.5em;
$medium-text-size : 1.5em;
$small-text-size: 1em;
width: 960px;
margin: 0 auto;
img {
max-height: 70%;
border : 10px solid #fff;
}
.photo_viewer {
margin-bottom : 20px;
}
@include centered-frame();
.container {
padding: 70px 0;
max-width: 85%;
}
header {
padding : 0 100px;
}
header, header p{
//big text
@include media-text();
font-size: $big-text-size;
line-height: 1.5em;
}
section.body{
p { font-size: $small-text-size; }
&.short_body{
p{
font-size: $medium-text-size;
margin-top: .5em;
}
}
}
&.night{
background-color : $night-background-color;
color : $night-text-color;
#author-info {
color : $night-text-color;
}
}
&.newspaper {
@include newspaper-type();
text-align: left;
.container {
max-width: 600px;
}
.photo_viewer {
float: right;
margin-left: 20px;
max-width: 320px;
}
header {
margin-bottom: 1em;
line-height: 1.2em;
}
.body p {
@include newspaper-type();
font-size: 1.2em;
line-height: 1.7em;
margin-bottom: 1.2em;
}
}
&.wallpaper{
color : #fff;
}
.img-bounding-box {
margin : 10px;
display : inline-block;
text-align : left;
}
img {
max-height: 400px;
max-width: 400px;
}
}
.status-with-photo-backdrop {
p {
color: #fff;
a {
@include transition(background-color, 0.3s);
color: #000;
background-color: #fff;
background-color: rgba(255,255,255,0.7);
padding: 0 5px;
&:hover {
text-decoration: none;
background-color: #fff;
background-color: rgba(255,255,255,1);
}
}
}
}
.darken {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background-color: rgba(0,0,0,0.3);
display: table;
.darken-content {
@include centered-frame();
height: 100%;
width: 100%;
}
}
.photo-fill {
@include background-cover();
position: absolute;
@ -263,21 +51,6 @@ article { //mood posts
width: 100%;
}
.photo-backdrop {
p {
color: #fff;
z-index: 2;
}
img {
position: relative;
}
.photo-fill {
@include opacity(0.2)
}
}
$bring-dark-accent-forward-color: #DDD;
#top-right-nav {

View file

@ -1,22 +1,6 @@
@import '../mixins';
#post-interactions {
position: fixed;
width: 100%;
bottom: 0;
left: 0;
text-align: center;
}
#post-reactions {
height: 80%;
max-height: 350px;
overflow: auto;
margin-top: 5px;
}
#post-info,
#post-info-sneaky {
#post-info {
text-align: center;
z-index: 10;
@ -69,16 +53,6 @@
padding-top: 0;
}
#new-post-comment {
-webkit-box-shadow: 0 -3px 6px -5px rgba(0,0,0,0.8);
-moz-box-shadow: 0 -3px 6px -5px rgba(0,0,0,0.8);
box-shadow: 0 -3px 6px -5px rgba(0,0,0,0.8);
border-top: 1px solid #444;
text-align: left;
background-image: image-url("texture/hatched-dark.png");
}
#new-post-comment-container {
position: relative;
@ -105,58 +79,6 @@
}
}
#post-info-sneaky {
@include transition(all, 0.2s);
z-index: 1;
position: fixed;
width: 100%;
margin: 0;
padding: 0;
bottom: 0;
margin-bottom: -60px;
min-height: 20px;
#post-info-container-sneaky {
@include info-container();
padding: 10px 0;
min-height: 20px;
}
}
.home-button {
@include transition(opacity, 0.3s);
@include opacity(0.6);
position: absolute;
left: 2px;
top: 4px;
padding: 4px 6px;
&:hover {
@include opacity(1);
}
}
.invoker {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#post-feedback:hover {
#post-info-sneaky:not(.passive) {
@include opacity(1);
margin-bottom: -13px;
}
}
.comment-content h1, .comment-content h2, .comment-content h3, .comment-content h4, .comment-content h5, .comment-content h6 {
color: white;
text-shadow: 1px 1px black;
@ -277,44 +199,6 @@
}
}
#post-feedback {
/* fixes flicker on hover... no idea as to why */
position: relative;
z-index: 30;
}
#close-reactions-pane {
display: none;
text-align: center;
position: absolute;
width: 100%;
left: 0;
top: -3px;
#close-reactions-pane-container {
@include pane-width();
top: 0;
min-height: 30px;
display: inline-block;
position: relative;
}
}
#post-interactions.active #close-reactions-pane {
display: block;
}
.info-tick {
@include opacity(0.8);
position: absolute;
right: 8px;
top: 8px;
}
/* stream specific wrapper */
#stream-interactions {
position : fixed;

View file

@ -1,10 +1,5 @@
@import '../mixins';
#post-nav {
@include transition(opacity, 0.5s);
@include opacity(1);
}
body.idle {
#post-nav {
@include opacity(0);
@ -21,51 +16,3 @@ body.idle {
}
}
.nav-arrow {
@include opacity(0.8);
@include transition(all, 0.3s);
display: table;
position: fixed;
height: 100%;
z-index: 3;
top: 0;
padding: 0 1.2%;
background-color: rgba(0,0,0,0);
.nav-arrow-inner {
padding: 0; margin: 0;
display: table-cell;
vertical-align: middle;
}
img {
@include opacity(0.5);
height: 30px;
width: 30px;
/* half the size of the nav arrows on mobile phones */
@media (max-width: 480px) {
height: 15px;
width: 15px;
}
}
&.left {
left: 0;
padding-right: 19px;
}
&.right {
right: 0;
padding-left: 19px;
}
&:hover {
background-color: rgba(0,0,0,0.1)
}
}

View file

@ -1,25 +0,0 @@
<div class="img">
{{#linkToPerson author}}
<div class="profile-image-container small" style="background-image : url('{{avatar.small}}');" data-placement="right"></div>
{{/linkToPerson}}
</div>
<div class="bd">
{{#linkToPerson author}}
{{name}}
{{/linkToPerson}}
{{#if root}}
<i class="icon-retweet"></i>
{{#linkToPerson root.author}}
{{name}}
{{/linkToPerson}}
{{/if}}
<div class="post-time">
<time datetime="{{created_at}}" />
{{#unless public}}
<i class="icon-lock"> </i>
{{/unless}}
</div>
</div>

View file

@ -1,27 +0,0 @@
{{#if canRemove}}
<div class="controls">
<a href="#" class="delete comment_delete" title="{{t "delete"}}">
<div alt="Deletelabel" class="icons-deletelabel" />
<a/>
</div>
{{/if}}
<div class="img">
{{#linkToPerson author}}
<div class="profile-image-container smaller" style="background-image : url('{{avatar.large}}')"></div>
{{/linkToPerson}}
</div>
<div class="bd">
<a href="/people/{{author.guid}}" class="author author-name">
{{author.name}}
</a>
<div class="collapsible comment-content">
{{{text}}}
</div>
<div class="info">
<time class="timeago" data-original-title="{{{localTime created_at}}}" datetime="{{created_at}}"/>
</div>
</div>

View file

@ -1,7 +0,0 @@
<div class="photoset">
<div class="img-bounding-box">
<a href="{{object_url}}" class="stream-photo-link">
<img src="{{image_url}}" data-small-photo="{{image_url}}" data-full-photo="{{image_url}}" class="stream-photo" />
</a>
</div>
</div>

View file

@ -1,3 +0,0 @@
<div class="note-content">
{{{text}}}
</div>

View file

@ -1,4 +0,0 @@
{{#each photos}}
<div class="photo-fill" style="background-image: url({{sizes.large}})"> </div>
<img src="{{sizes.large}}" />
{{/each}}

View file

@ -1,11 +0,0 @@
<div class="rich-media-container">
<div class="rich-media-container2">
{{{o_embed_cache.data.html}}}
<br>
<br>
{{{text}}}
</div>
</div>

View file

@ -1,18 +0,0 @@
<div class="feedback-actions"/>
<!-- this acts as a dock underlay -->
<div id="post-info-sneaky" class="passive">
<div id="post-info-container-sneaky">
<a href="#" rel="invoke-interaction-pane" class="invoker">
<img src="{{imageUrl "up-tick-inset.png"}}" class="info-tick"/>
</a>
</div>
</div>
<!-- this closes an open interaction pane -->
<div id="close-reactions-pane">
<div id="close-reactions-pane-container">
<a href="#" rel="hide-interaction-pane" class="invoker"></a>
</div>
</div>

View file

@ -1,11 +0,0 @@
<div id="post-feedback"> </div>
<div id="post-info" style="display:none;">
<div id="post-info-container">
<img src="{{imageUrl "down-tick-inset.png"}}" class="info-tick"/>
<div id="post-reactions"> </div>
<div id="new-post-comment"> </div>
</div>
</div>

View file

@ -1,11 +0,0 @@
<a href="{{next_post}}" class="nav-arrow left" id="forward" rel="backbone">
<div class="nav-arrow-inner">
<img src="{{imageUrl "arrow-left.png"}}" />
</div>
</a>
<a href="{{previous_post}}" class="nav-arrow right" id="back" rel="backbone">
<div class="nav-arrow-inner">
<img src="{{imageUrl "arrow-right.png"}}" />
</div>
</a>

View file

@ -1,4 +0,0 @@
<div id="author-info"></div>
<div id="post-content"></div>
<div id="post-nav"></div>
<div id="post-interactions"></div>

View file

@ -1,8 +0,0 @@
<div class="photo-fill" data-img-src="{{backgroundUrl}}" style="background-image: url({{backgroundUrl}})">
<div class="darken">
<div class="darken-content">
<header>{{{headline}}}</header>
<section class="body">{{{body}}}</section>
</div>
</div>
</div>

View file

@ -7,7 +7,7 @@ class PostsController < ApplicationController
before_filter :authenticate_user!, :except => [:show, :iframe, :oembed, :interactions]
before_filter :set_format_if_malformed_from_status_net, :only => :show
before_filter :find_post, :only => [:show, :next, :previous, :interactions]
before_filter :find_post, :only => [:show, :interactions]
before_filter -> { @css_framework = :bootstrap }
@ -48,24 +48,6 @@ class PostsController < ApplicationController
end
end
def next
next_post = Post.visible_from_author(@post.author, current_user).newer(@post)
respond_to do |format|
format.html{ redirect_to post_path(next_post) }
format.json{ render :json => PostPresenter.new(next_post, current_user)}
end
end
def previous
previous_post = Post.visible_from_author(@post.author, current_user).older(@post)
respond_to do |format|
format.html{ redirect_to post_path(previous_post) }
format.json{ render :json => PostPresenter.new(previous_post, current_user)}
end
end
def interactions
respond_with(PostInteractionPresenter.new(@post, current_user))
end

View file

@ -49,10 +49,6 @@ class Reshare < Post
self.root ? root.photos : []
end
def frame_name
self.root ? root.frame_name : nil
end
def receive(recipient, sender)
local_reshare = Reshare.where(:guid => self.guid).first
if local_reshare && local_reshare.root.author_id == recipient.person.id

View file

@ -32,11 +32,8 @@ class PostPresenter
:open_graph_cache => @post.open_graph_cache.try(:as_api_response, :backbone),
:mentioned_people => @post.mentioned_people.as_api_response(:backbone),
:photos => @post.photos.map {|p| p.as_api_response(:backbone)},
:frame_name => @post.frame_name || template_name,
:root => root,
:title => title,
:next_post => next_post_path,
:previous_post => previous_post_path,
:address => @post.address,
:interactions => {
@ -49,22 +46,10 @@ class PostPresenter
}
end
def next_post_path
Rails.application.routes.url_helpers.next_post_path(@post)
end
def previous_post_path
Rails.application.routes.url_helpers.previous_post_path(@post)
end
def title
@post.text.present? ? post_page_title(@post) : I18n.translate('posts.presenter.title', :name => @post.author_name)
end
def template_name #kill me, lol, I should be client side
@template_name ||= TemplatePicker.new(@post).template_name
end
def root
PostPresenter.new(@post.absolute_root, current_user).as_json if @post.respond_to?(:absolute_root) && @post.absolute_root.present?
end

View file

@ -31,6 +31,5 @@ require 'pubsubhubbub'
require 'salmon'
require 'statistics'
require 'stream'
require 'template_picker'
require 'webfinger'
require 'webfinger_profile'

View file

@ -1,33 +0,0 @@
module PostGenerationHelpers
def generate_post_of_each_template(user)
time = Time.now
TemplatePicker::TEMPLATES.each do |template|
Timecop.travel time += 1.minute
FactoryGirl.create(template, :author => user.person)
end
Timecop.return
end
def visit_posts_and_collect_template_names(user)
visit(post_path(user.posts.last))
user.posts.map do |post|
sleep 0.25
post = find('.post')
template_name = post['data-template']
click_next_button
template_name
end
end
def click_next_button
next_arrow = '.nav-arrow.right'
if page.has_selector?(next_arrow)
find(next_arrow).click()
end
end
end
World(PostGenerationHelpers)

View file

@ -1,45 +0,0 @@
class TemplatePicker
attr_accessor :post
TEMPLATES = %w{ status_with_photo_backdrop
note
photo_backdrop
status
}
def initialize(post)
self.post = post
end
def template_name
TEMPLATES.each do |template|
return TemplatePicker.jsonify_name(template) if self.send("#{template}?".to_sym)
end
'status' #default
end
def status_with_photo_backdrop?
status? && photo_backdrop?
end
def note?
self.status? && post.text(:plain_text => true).length > 200
end
def photo_backdrop?
false # No backdrop, ever.
end
def status?
post.text?
end
def self.jsonified_templates
TEMPLATES.map{|x| jsonify_name(x)}
end
def self.jsonify_name(name)
name.gsub('_', '-')
end
end

View file

@ -167,58 +167,4 @@ describe PostsController do
StatusMessage.exists?(message.id).should be_true
end
end
describe "#next" do
before do
sign_in alice
Post.stub(:find_by_guid_or_id_with_user).and_return(mock_model(Post, :author => 4))
Post.stub_chain(:visible_from_author, :newer).and_return(next_post)
end
let(:next_post){ mock_model(StatusMessage, :id => 34)}
context "GET .json" do
let(:mock_presenter) { mock(:as_json => {:title => "the unbearable lightness of being"}) }
it "should return a show presenter the next post" do
PostPresenter.should_receive(:new).with(next_post, alice).and_return(mock_presenter)
get :next, :id => 14, :format => :json
response.body.should == {:title => "the unbearable lightness of being"}.to_json
end
end
context "GET .html" do
it "should redirect to the next post" do
get :next, :id => 14
response.should redirect_to(post_path(next_post))
end
end
end
describe "previous" do
before do
sign_in alice
Post.stub(:find_by_guid_or_id_with_user).and_return(mock_model(Post, :author => 4))
Post.stub_chain(:visible_from_author, :older).and_return(previous_post)
end
let(:previous_post){ mock_model(StatusMessage, :id => 11)}
context "GET .json" do
let(:mock_presenter) { mock(:as_json => {:title => "existential crises"})}
it "should return a show presenter the next post" do
PostPresenter.should_receive(:new).with(previous_post, alice).and_return(mock_presenter)
get :previous, :id => 14, :format => :json
response.body.should == {:title => "existential crises"}.to_json
end
end
context "GET .html" do
it "should redirect to the next post" do
get :previous, :id => 14
response.should redirect_to(post_path(previous_post))
end
end
end
end

View file

@ -1,56 +0,0 @@
describe("app.models.Post.TemplatePicker", function(){
beforeEach(function(){
this.post = factory.statusMessage({frame_name: undefined, text : "Lol this is a post"})
this.templatePicker = new app.models.Post.TemplatePicker(this.post)
})
describe("getFrameName", function(){
context("when the model has hella text", function(){
beforeEach(function(){
this.post.set({text : window.hipsterIpsumFourParagraphs })
})
it("returns Typist", function(){
expect(this.templatePicker.getFrameName()).toBe("Typist")
})
})
context("when the model has photos:", function(){
context("one photo", function(){
beforeEach(function(){
this.post.set({photos : [factory.photoAttrs()]})
})
it("returns Wallpaper", function(){
expect(this.templatePicker.getFrameName()).toBe("Wallpaper")
})
})
context("two photos", function(){
beforeEach(function(){
this.post.set({photos : [factory.photoAttrs(), factory.photoAttrs()]})
})
it("returns Vanilla", function(){
expect(this.templatePicker.getFrameName()).toBe("Vanilla")
})
})
it("returns 'Vanilla' by default", function(){
expect(this.templatePicker.getFrameName()).toBe("Vanilla")
})
})
})
describe("applicableTemplates", function(){
it("includes wallpaper if isWallpaper is true", function(){
spyOn(this.templatePicker, "isWallpaper").andReturn(true)
expect(_.include(this.templatePicker.applicableTemplates(), "Wallpaper")).toBeTruthy()
})
it("does not include wallpaper if isWallpaper is false", function(){
spyOn(this.templatePicker, "isWallpaper").andReturn(false)
expect(_.include(this.templatePicker.applicableTemplates(), "Wallpaper")).toBeFalsy()
})
})
})

View file

@ -1,14 +0,0 @@
describe("app.Pages.PostViewer", function(){
describe("postRenderTemplate", function(){
beforeEach(function(){
app.setPreload('post', factory.post({frame_name : "note"}).attributes);
this.page = new app.pages.PostViewer({id : 2});
})
it('translates post title from Markdown to plain text and pushes it in document.title', function () {
this.page.model.set({title : "### My [Markdown](url) *title*" });
this.page.postRenderTemplate();
expect(document.title).toEqual("My Markdown title");
})
})
});

View file

@ -1,55 +0,0 @@
describe("app.views.FeedbackActions", function(){
beforeEach(function(){
loginAs({id : -1, name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
this.post = new app.models.Post({
"author": {
"diaspora_id": "alice@localhost:3000"
},
"post_type": "Reshare",
"public": true,
"root": {
"author":{"diaspora_id": null}
}
})
this.view = new app.views.PostViewerFeedback({model: this.post})
});
describe("FeedbackActions", function(){
it("reshares a post", function(){
spyOn(window, "confirm").andReturn(true)
spyOn(this.view.model.interactions, "reshare")
this.view.render()
this.view.$('.reshare').click()
expect(this.view.model.interactions.reshare.callCount).toBe(1)
expect(window.confirm.callCount).toBe(1)
});
it('cancels a reshare confirmation ', function(){
spyOn(window, "confirm").andReturn(false)
spyOn(this.view.model.interactions, "reshare")
this.view.render()
this.view.$('.reshare').click()
expect(this.view.model.interactions.reshare).not.toHaveBeenCalled();
});
it("likes a post", function(){
spyOn(this.view.model.interactions, "toggleLike")
this.view.render()
this.view.$('.like').click()
expect(this.view.model.interactions.toggleLike.callCount).toBe(1)
})
})
})

View file

@ -1,34 +0,0 @@
describe("app.views.Post.Day", function(){
beforeEach(function(){
this.post = factory.post()
this.view = new app.views.Post.Day({model : this.post})
})
describe("rendering", function(){
it("is happy", function(){
this.view.render()
})
describe("when the body is under 200 characters", function(){
it("has class shortBody", function(){
this.post.set({text : "Headline\nLol this is a short body"})
this.view.render()
expect(this.view.$("section.body")).toHaveClass("short_body")
})
})
describe("when the body is over 200 characters", function(){
it("has doesn't have headline", function(){
this.post.set({text :"HEADLINE\nVegan bushwick tempor labore. Nulla seitan anim, aesthetic ex gluten-free viral" +
"thundercats street art. Occaecat carles deserunt lomo messenger bag wes anderson. Narwhal cray selvage " +
"dolor. Mixtape wes anderson american apparel, mustache readymade cred nulla squid veniam small batch id " +
"cupidatat. Pork belly high life consequat, raw denim sint terry richardson seitan single-origin coffee " +
"butcher. Sint yr fugiat cillum."
})
this.view.render()
expect(this.view.$("section.body")).not.toHaveClass("short_body")
})
})
})
})

View file

@ -1,12 +0,0 @@
describe("app.views.Post.Newspaper", function(){
beforeEach(function(){
this.post = factory.post()
this.view = new app.views.Post.Newspaper({model : this.post})
})
describe("rendering", function(){
it("is happy", function(){
this.view.render()
})
})
})

View file

@ -1,12 +0,0 @@
describe("app.views.Post.Night", function(){
beforeEach(function(){
this.post = factory.post()
this.view = new app.views.Post.Night({model : this.post})
})
describe("rendering", function(){
it("is happy", function(){
this.view.render()
})
})
})

View file

@ -1,14 +0,0 @@
describe("app.views.Post.Wallpaper", function(){
beforeEach(function(){
this.post = factory.post({photos : [factory.photoAttrs({sizes :{large : "http://omgimabackground.com/wow.gif"}})]})
this.view = new app.views.Post.Wallpaper({model : this.post})
})
describe("rendering", function(){
it("has the image as the photo-fill", function(){
this.view.render()
expect(this.view.$(".photo-fill").data("img-src")).toBe("http://omgimabackground.com/wow.gif") //for the cuke
expect(this.view.$(".photo-fill").css("background-image")).toMatch('http://omgimabackground.com/wow.gif')
})
})
})

View file

@ -1,66 +0,0 @@
require 'spec_helper'
describe TemplatePicker do
before do
@post_stubs = {:type => 'StatusMessage', :photos => stub(:size => 2),
:o_embed_cache => stub(:present? => true),
:text? => true, :text => stub(:length => 400)
}
end
let(:post) {
stub(@post_stubs)
}
it 'has a post' do
t = TemplatePicker.new(post)
t.post.should_not be_nil
end
describe '#template_name' do
it 'returns the coolest template if the post has lots of cool stuff' do
TemplatePicker.new(post).template_name.should_not be_nil
end
end
describe '#status_with_photo_backdrop?' do
it 'is false even if the post contains a single photo and text' do
@post_stubs.merge!(:photos => stub(:size => 1))
TemplatePicker.new(post).should_not be_status_with_photo_backdrop
end
end
describe '#note?' do
it 'is true if the post contains text more than 300 characters long' do
TemplatePicker.new(post).should be_note
end
end
describe '#photo_backdrop?' do
it 'is false even if the post contains only one photo' do
@post_stubs.merge!(:photos => stub(:size => 1))
TemplatePicker.new(post).should_not be_photo_backdrop
end
end
describe '#status?' do
it 'is true if the post contains text' do
TemplatePicker.new(post).should be_status
end
end
describe 'factories' do
# No photo_backdrop for now.
(TemplatePicker::TEMPLATES - ['status_with_photo_backdrop', 'photo_backdrop']).each do |template|
describe "#{template} factory" do
it 'works' do
post = FactoryGirl.build(template.to_sym, :author => alice.person)
template_name = TemplatePicker.new(post).template_name.gsub('-', '_')
template_name.should == template
end
end
end
end
end

View file

@ -42,18 +42,18 @@ describe PostPresenter do
@unauthenticated_presenter.user_reshare.should be_nil
end
end
describe '#root' do
it 'does not raise if the absolute_root does not exists' do
first_reshare = FactoryGirl.create :reshare
first_reshare.root = nil
reshare = FactoryGirl.create :reshare, :root => first_reshare
expect {
PostPresenter.new(reshare).root
}.to_not raise_error
end
it 'does not raise if the root does not exists' do
reshare = FactoryGirl.create:reshare
reshare.root = nil
@ -62,20 +62,8 @@ describe PostPresenter do
}.to_not raise_error
end
end
describe '#next_post_path' do
it 'returns a string of the users next post' do
@presenter.next_post_path.should == "#{Rails.application.routes.url_helpers.post_path(@sm)}/next"
end
end
describe '#previous_post_path' do
it 'returns a string of the users next post' do
@presenter.previous_post_path.should == "#{Rails.application.routes.url_helpers.post_path(@sm)}/previous"
end
end
describe '#title' do
describe '#title' do
context 'with posts with text' do
context 'with a Markdown header of less than 200 characters on first line'do
it 'returns atx style header' do
@ -90,6 +78,7 @@ describe PostPresenter do
@presenter.title.should == "My title \n======"
end
end
context 'without a Markdown header of less than 200 characters on first line 'do
it 'truncates post to the 20 first characters' do
@sm = stub(:text => "Very, very, very long post")
@ -98,9 +87,9 @@ describe PostPresenter do
end
end
end
context 'with posts without text' do
it ' displays a messaage with the post class' do
@sm = stub(:text => "", :author => bob.person, :author_name => bob.person.name)
@presenter.post = @sm
@presenter.title.should == "A post from #{@sm.author.name}"