Skip to content
Extraits de code Groupes Projets
Valider 9de6a26a rédigé par Steffen van Bergerem's avatar Steffen van Bergerem
Parcourir les fichiers

Port contacts page to backbonejs

parent c9ef1f29
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
Affichage de
avec 319 ajouts et 133 suppressions
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.collections.AspectMemberships = Backbone.Collection.extend({
model: app.models.AspectMembership
})
// @license-end
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.collections.Contacts = Backbone.Collection.extend({
model: app.models.Contact,
comparator : function(con1, con2) {
if( !con1.person || !con2.person ) return 1;
if(app.aspect) {
var inAspect1 = con1.inAspect(app.aspect.get('id'));
var inAspect2 = con2.inAspect(app.aspect.get('id'));
if( inAspect1 && !inAspect2 ) return -1;
if( !inAspect1 && inAspect2 ) return 1;
}
var n1 = con1.person.get('name');
var n2 = con2.person.get('name');
return n1.localeCompare(n2);
}
});
// @license-end
......@@ -21,7 +21,7 @@ Handlebars.registerHelper('urlTo', function(path_helper, id, data){
return Routes[path_helper+'_path'](id, data.hash);
});
Handlebars.registerHelper('linkToPerson', function(context, block) {
Handlebars.registerHelper('linkToAuthor', function(context, block) {
if( !context ) context = this;
var html = "<a href=\"/people/" + context.guid + "\" class=\"author-name ";
html += Handlebars.helpers.hovercardable(context);
......@@ -32,6 +32,15 @@ Handlebars.registerHelper('linkToPerson', function(context, block) {
return html
});
Handlebars.registerHelper('linkToPerson', function(context, block) {
if( !context ) context = this;
var html = "<a href=\"/people/" + context.guid + "\" class=\"name\">";
html += block.fn(context);
html += "</a>";
return html
});
// relationship indicator for profile page
Handlebars.registerHelper('sharingMessage', function(person) {
var i18n_scope = 'people.helper.is_not_sharing';
......@@ -107,5 +116,20 @@ Handlebars.registerHelper('isCurrentProfilePage', function(id, diaspora_handle,
return Handlebars.helpers.isCurrentPage('person', id, options) ||
Handlebars.helpers.isCurrentPage('user_profile', username, options);
});
Handlebars.registerHelper('aspectMembershipIndicator', function(contact,in_aspect) {
if(!app.aspect || !app.aspect.get('id')) return '<div class="aspect_membership_dropdown placeholder"></div>';
var html = '<i class="entypo ';
if( in_aspect == 'in_aspect' ) {
html += 'circled-cross contact_remove-from-aspect" ';
html += 'title="' + Diaspora.I18n.t('contacts.remove_contact') + '" ';
} else {
html += 'circled-plus contact_add-to-aspect" ';
html += 'title="' + Diaspora.I18n.t('contacts.add_contact') + '" ';
}
html += '></i>';
return html;
});
// @license-end
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.models.Contact = Backbone.Model.extend({
initialize : function() {
this.aspect_memberships = new app.collections.AspectMemberships(this.get('aspect_memberships'));
if( this.get('person') ) this.person = new app.models.Person(this.get('person'));
},
inAspect : function(id) {
return this.aspect_memberships.any(function(membership){ return membership.get('aspect').id == id; });
}
});
// @license-end
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Contacts = Backbone.View.extend({
app.pages.Contacts = Backbone.View.extend({
el: "#contacts_container",
......@@ -8,17 +8,15 @@ app.views.Contacts = Backbone.View.extend({
"click #contacts_visibility_toggle" : "toggleContactVisibility",
"click #chat_privilege_toggle" : "toggleChatPrivilege",
"click #change_aspect_name" : "showAspectNameForm",
"click .contact_remove-from-aspect" : "removeContactFromAspect",
"click .contact_add-to-aspect" : "addContactToAspect",
"keyup #contact_list_search" : "searchContactList"
},
initialize: function() {
initialize: function(opts) {
this.visibility_toggle = $("#contacts_visibility_toggle .entypo");
this.chat_toggle = $("#chat_privilege_toggle .entypo");
this.stream = opts.stream;
this.stream.render();
$("#people_stream.contacts .header .entypo").tooltip({ 'placement': 'bottom'});
$(".contact_remove-from-aspect").tooltip();
$(".contact_add-to-aspect").tooltip();
$(document).on('ajax:success', 'form.edit_aspect', this.updateAspectName);
},
......@@ -69,65 +67,8 @@ app.views.Contacts = Backbone.View.extend({
$(".header > h3").show();
},
addContactToAspect: function(e){
var contact = $(e.currentTarget);
var aspect_membership = new app.models.AspectMembership({
'person_id': contact.attr('data-person_id'),
'aspect_id': contact.attr('data-aspect_id')
});
aspect_membership.save({},{
success: function(model,response){
contact.attr('data-membership_id',model.id)
.tooltip('destroy')
.removeAttr('data-original-title')
.removeClass("contact_add-to-aspect").removeClass("circled-plus")
.addClass("contact_remove-from-aspect").addClass("circled-cross")
.attr('title', Diaspora.I18n.t('contacts.remove_contact'))
.tooltip()
.closest('.stream_element').addClass('in_aspect');
},
error: function(model,response){
var msg = Diaspora.I18n.t('contacts.error_add', { 'name':contact.closest('.stream_element').find('.name').text() });
Diaspora.page.flashMessages.render({ 'success':false, 'notice':msg });
}
});
},
removeContactFromAspect: function(e){
var contact = $(e.currentTarget);
var aspect_membership = new app.models.AspectMembership({
'id': contact.attr('data-membership_id')
});
aspect_membership.destroy({
success: function(model,response){
contact.removeAttr('data-membership_id')
.tooltip('destroy')
.removeAttr('data-original-title')
.removeClass("contact_remove-from-aspect").removeClass("circled-cross")
.addClass("contact_add-to-aspect").addClass("circled-plus")
.attr('title', Diaspora.I18n.t('contacts.add_contact'))
.tooltip()
.closest('.stream_element').removeClass('in_aspect');
},
error: function(model,response){
var msg = Diaspora.I18n.t('contacts.error_remove', { 'name':contact.closest('.stream_element').find('.name').text() });
Diaspora.page.flashMessages.render({ 'success':false, 'notice':msg });
}
});
},
searchContactList: function(e) {
var query = new RegExp($(e.target).val(),'i');
$("#people_stream.stream.contacts .stream_element").each(function(){
if($(this).find(".name").text().match(query)){
$(this).show();
} else {
$(this).hide();
}
});
this.stream.search($(e.target).val());
}
});
// @license-end
......
......@@ -43,7 +43,15 @@ app.Router = Backbone.Router.extend({
},
contacts: function() {
app.contacts = new app.views.Contacts();
app.aspect = new app.models.Aspect(gon.preloads.aspect);
app.contacts = new app.collections.Contacts(app.parsePreload('contacts'));
var stream = new app.views.ContactStream({
collection: app.contacts,
el: $('.stream.contacts #contact_stream'),
});
app.page = new app.pages.Contacts({stream: stream});
},
conversations: function() {
......
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.ContactStream = Backbone.View.extend({
initialize: function() {
this.itemCount = 0;
this.perPage = 25;
this.query = '';
this.resultList = this.collection.toArray();
var throttledScroll = _.throttle(_.bind(this.infScroll, this), 200);
$(window).scroll(throttledScroll);
this.on('renderContacts', this.renderContacts, this);
},
render: function() {
if( _.isEmpty(this.resultList) ) {
var content = document.createDocumentFragment();
content = '<div id="no_contacts" class="well">' +
' <h4>' +
Diaspora.I18n.t('contacts.search_no_results') +
' </h4>' +
'</div>';
this.$el.html(content);
} else {
this.$el.html('');
this.renderContacts();
}
},
renderContacts: function() {
this.$el.addClass("loading");
var content = document.createDocumentFragment();
_.rest(_.first(this.resultList , this.itemCount + this.perPage), this.itemCount).forEach( function(item) {
var view = new app.views.Contact({model: item});
content.appendChild(view.render().el);
});
var size = _.size(this.resultList);
if( this.itemCount + this.perPage >= size ){
this.itemCount = size;
this.off('renderContacts');
} else {
this.itemCount += this.perPage;
}
this.$el.append(content);
this.$el.removeClass("loading");
},
search: function(query) {
query = query.trim();
if( query || this.query ) {
this.off('renderContacts');
this.on('renderContacts', this.renderContacts, this);
this.itemCount = 0;
if( query ) {
this.query = query;
var regex = new RegExp(query,'i');
this.resultList = this.collection.filter(function(contact) {
return regex.test(contact.get('person').name) ||
regex.test(contact.get('person').diaspora_id);
});
} else {
this.resultList = this.collection.toArray();
this.query = '';
}
this.render();
}
},
infScroll: function() {
if( this.$el.hasClass('loading') ) return;
var distanceTop = $(window).height() + $(window).scrollTop(),
distanceBottom = $(document).height() - distanceTop;
if(distanceBottom < 300) this.trigger('renderContacts');
}
});
// @license-end
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.Contact = app.views.Base.extend({
templateName: 'contact',
events: {
"click .contact_add-to-aspect" : "addContactToAspect",
"click .contact_remove-from-aspect" : "removeContactFromAspect"
},
tooltipSelector: '.contact_add-to-aspect, .contact_remove-from-aspect',
presenter: function() {
return _.extend(this.defaultPresenter(), {
person_id : this.model.get('person_id'),
person : this.model.get('person'),
in_aspect: (app.aspect && this.model.inAspect(app.aspect.get('id'))) ? 'in_aspect' : '',
});
},
postRenderTemplate: function() {
var self = this;
var dropdownEl = this.$('.aspect_membership_dropdown.placeholder');
if( dropdownEl.length == 0 ) {
return;
}
// TODO render me client side!!!
var href = this.model.person.url() + '/aspect_membership_button?size=small';
if( gon.bootstrap ) href += '&bootstrap=true';
$.get(href, function(resp) {
dropdownEl.html(resp);
new app.views.AspectMembership({el: $('.aspect_dropdown',dropdownEl)});
// UGLY (re-)attach the facebox
self.$('a[rel*=facebox]').facebox();
});
},
addContactToAspect: function(){
var self = this;
this.model.aspect_memberships.create({
'person_id': this.model.get('person_id'),
'aspect_id': app.aspect.get('id')
},{
success: function(model,response){
self.render();
},
error: function(model,response){
var msg = Diaspora.I18n.t('contacts.error_add', { 'name': self.model.get('person').name });
Diaspora.page.flashMessages.render({ 'success':false, 'notice':msg });
}
});
},
removeContactFromAspect: function(){
var self = this;
this.model.aspect_memberships
.find(function(membership){ return membership.get('aspect').id == app.aspect.id; })
.destroy({
success: function(model,response){
self.render();
},
error: function(model,response){
var msg = Diaspora.I18n.t('contacts.error_remove', { 'name': self.model.get('person').name });
Diaspora.page.flashMessages.render({ 'success':false, 'notice':msg });
}
});
}
});
// @license-end
......@@ -66,8 +66,6 @@
}
#no_contacts {
margin-top: 20px;
text-align: center;
padding: 10px;
background-color: #eee;
color: $text-dark-grey;
}
<div id="{{guid}}">
<div class="img">
{{#linkToPerson author}}
{{#linkToAuthor author}}
{{{personImage this "small" "small"}}}
{{/linkToPerson}}
{{/linkToAuthor}}
</div>
<div class="bd">
......
<div class="stream_element media contact {{in_aspect}}" id={{person_id}}>
<div class="pull-right">
{{{aspectMembershipIndicator this in_aspect}}}
</div>
<div class="media-object pull-left">
{{{personImage person 'small'}}}
</div>
<div class="media-body">
{{#linkToPerson person}}
{{name}}
{{/linkToPerson}}
<div class="info diaspora_handle">
{{person.diaspora_id}}
</div>
<div class="info tags">
{{{fmtTags person.profile.tags}}}
</div>
</div>
</div>
......@@ -3,26 +3,26 @@
<div id='post-info' class='span8'>
<div class="img pull-left">
{{#if root}}
{{#linkToPerson root.author}}
{{#linkToAuthor root.author}}
{{{personImage this 'medium'}}}
{{/linkToPerson}}
{{/linkToAuthor}}
{{else}}
{{#linkToPerson author}}
{{#linkToAuthor author}}
{{{personImage this 'medium'}}}
{{/linkToPerson}}
{{/linkToAuthor}}
{{/if}}
</div>
<div class="bd">
<span class='author'>
{{#if root}}
{{#linkToPerson root.author}}
{{#linkToAuthor root.author}}
{{name}}
{{/linkToPerson}}
{{/linkToAuthor}}
{{else}}
{{#linkToPerson author}}
{{#linkToAuthor author}}
{{name}}
{{/linkToPerson}}
{{/linkToAuthor}}
{{/if}}
</span>
......@@ -69,14 +69,14 @@
<div class='span8' id='reshare-info'>
<i class='entypo retweet small pull-left'></i>
<div class="img pull-left">
{{#linkToPerson author}}
{{#linkToAuthor author}}
{{{personImage this 'small'}}}
{{/linkToPerson}}
{{/linkToAuthor}}
</div>
<span class="author">
{{#linkToPerson author}}
{{#linkToAuthor author}}
{{name}}
{{/linkToPerson}}
{{/linkToAuthor}}
</span>
<span class="post-time">
<a href="/posts/{{id}}">
......
......@@ -6,9 +6,9 @@
</span>
<span>
{{#each reshares}}
{{#linkToPerson author}}
{{#linkToAuthor author}}
{{{personImage this 'small' 'micro'}}}
{{/linkToPerson}}
{{/linkToAuthor}}
{{/each}}
</span>
</div>
......@@ -21,9 +21,9 @@
</span>
<span>
{{#each likes}}
{{#linkToPerson author}}
{{#linkToAuthor author}}
{{{personImage this 'small' 'micro'}}}
{{/linkToPerson}}
{{/linkToAuthor}}
{{/each}}
</span>
</div>
......
{{#people}}
{{#linkToPerson this}}
{{#linkToAuthor this}}
{{{personImage this "small"}}}
{{/linkToPerson}}
{{/linkToAuthor}}
{{/people}}
......@@ -14,16 +14,16 @@
<div class='stream-frame-feedback'></div>
<div class="media author">
{{#linkToPerson author}}
{{#linkToAuthor author}}
<div class="img">
<div class="profile-image-container smaller" style="background-image : url('{{avatar.large}}')"></div>
</div>
{{/linkToPerson}}
{{/linkToAuthor}}
<div class="bd">
{{#linkToPerson author}}
{{#linkToAuthor author}}
{{name}}
{{/linkToPerson}}
{{/linkToAuthor}}
</div>
</div>
......
......@@ -34,10 +34,7 @@ class AspectMembershipsController < ApplicationController
respond_to do |format|
format.json do
if success
render :json => {
:person_id => contact.person_id,
:aspect_ids => contact.aspects.map{|a| a.id}
}
render :json => AspectMembershipPresenter.new(membership).base_hash
else
render :text => membership.errors.full_messages, :status => 403
end
......@@ -57,7 +54,9 @@ class AspectMembershipsController < ApplicationController
flash.now[:notice] = I18n.t('aspects.add_to_aspect.success')
respond_with do |format|
format.json do
render :json => AspectMembership.where(:contact_id => @contact.id, :aspect_id => @aspect.id).first.to_json
render :json => AspectMembershipPresenter.new(
AspectMembership.where(:contact_id => @contact.id, :aspect_id => @aspect.id).first)
.base_hash
end
format.all { redirect_to :back }
......
......@@ -40,32 +40,24 @@ class ContactsController < ApplicationController
@contacts = contacts_by_type(type)
@contacts_size = @contacts.length
gon.preloads[:contacts] = @contacts.map{ |c| ContactPresenter.new(c, current_user).full_hash_with_person }
end
def contacts_by_type(type)
contacts = case type
case type
when "all"
[current_user.contacts]
current_user.contacts
when "only_sharing"
[current_user.contacts.only_sharing]
current_user.contacts.only_sharing
when "receiving"
[current_user.contacts.receiving]
current_user.contacts.receiving
when "by_aspect"
@aspect = current_user.aspects.find(params[:a_id])
@contacts_in_aspect = @aspect.contacts
@contacts_not_in_aspect = current_user.contacts.where.not(contacts: {id: @contacts_in_aspect.pluck(:id) })
[@contacts_in_aspect, @contacts_not_in_aspect].map {|relation|
relation.includes(:aspect_memberships)
}
gon.preloads[:aspect] = AspectPresenter.new(@aspect).as_json
current_user.contacts
else
raise ArgumentError, "unknown type #{type}"
end
contacts.map {|relation|
relation.includes(:person => :profile).to_a.tap {|contacts|
contacts.sort_by! {|contact| contact.person.name }
}
}.inject(:+).paginate(:page => params[:page], :per_page => 25)
end
def set_up_contacts_mobile
......
module ContactsHelper
def contact_aspect_dropdown(contact)
membership = contact.aspect_memberships.where(:aspect_id => @aspect.id).first unless @aspect.nil?
if membership
content_tag(:i, nil, :class => 'entypo circled-cross contact_remove-from-aspect',
:title => t('contacts.index.remove_contact'),
'data-aspect_id' => @aspect.id,
'data-person_id' => contact.person_id,
'data-membership_id' => membership.id )
elsif @aspect.nil?
render :partial => 'people/relationship_action',
:locals => { :person => contact.person,
:contact => contact,
:current_user => current_user }
else
content_tag(:i, nil, :class => 'entypo circled-plus contact_add-to-aspect',
:title => t('contacts.index.add_contact'),
'data-aspect_id' => @aspect.id,
'data-person_id' => contact.person_id )
end
render :partial => 'people/relationship_action',
:locals => { :person => contact.person,
:contact => contact,
:current_user => current_user }
end
def start_a_conversation_link(aspect, contacts_size)
......
class AspectMembershipPresenter < BasePresenter
def initialize(membership)
@membership = membership
end
def base_hash
{ id: @membership.id,
aspect: AspectPresenter.new(@membership.aspect).as_json,
}
end
end
class ContactPresenter < BasePresenter
def base_hash
{ id: id,
person_id: person_id
}
end
def full_hash
base_hash.merge({
aspect_memberships: aspect_memberships.map{ |membership| AspectMembershipPresenter.new(membership).base_hash }
})
end
def full_hash_with_person
full_hash.merge({person: PersonPresenter.new(person).full_hash_with_profile})
end
end
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter