* refactored text direction detector into helper (also for handlebars)

* added handlebars helper for markdown formatting
* finished port of profile sidebar view to handlebars template
* people_controller refactoring
This commit is contained in:
Florian Staudacher 2014-09-08 03:35:40 +02:00
parent 9a16560d8d
commit 2572fb77fc
13 changed files with 232 additions and 70 deletions

View file

@ -0,0 +1,52 @@
(function() {
app.helpers.txtDirection = {
setCssFor: function(str, on_element) {
if( this.isRTL(str) ) {
$(on_element).css('direction', 'rtl');
} else {
$(on_element).css('direction', 'ltr');
}
},
classFor: function(str) {
if( this.isRTL(str) ) return 'rtl';
return 'ltr';
},
isRTL: function(str) {
if(typeof str !== "string" || str.length < 1) {
return false;
}
var charCode = str.charCodeAt(0);
if(charCode >= 1536 && charCode <= 1791) // Sarabic, Persian, ...
return true;
else if(charCode >= 65136 && charCode <= 65279) // Arabic present 1
return true;
else if(charCode >= 64336 && charCode <= 65023) // Arabic present 2
return true;
else if(charCode>=1424 && charCode<=1535) // Hebrew
return true;
else if(charCode>=64256 && charCode<=64335) // Hebrew present
return true;
else if(charCode>=1792 && charCode<=1871) // Syriac
return true;
else if(charCode>=1920 && charCode<=1983) // Thaana
return true;
else if(charCode>=1984 && charCode<=2047) // NKo
return true;
else if(charCode>=11568 && charCode<=11647) // Tifinagh
return true;
return false;
}
};
})();

View file

@ -2,6 +2,10 @@ Handlebars.registerHelper('t', function(scope, values) {
return Diaspora.I18n.t(scope, values.hash) return Diaspora.I18n.t(scope, values.hash)
}); });
Handlebars.registerHelper('txtDirClass', function(str) {
return app.helpers.txtDirection.classFor(str);
});
Handlebars.registerHelper('imageUrl', function(path){ Handlebars.registerHelper('imageUrl', function(path){
return ImagePaths.get(path); return ImagePaths.get(path);
}); });
@ -72,3 +76,7 @@ Handlebars.registerHelper('personImage', function(person, size, imageClass) {
Handlebars.registerHelper('localTime', function(timestamp) { Handlebars.registerHelper('localTime', function(timestamp) {
return new Date(timestamp).toLocaleString(); return new Date(timestamp).toLocaleString();
}); });
Handlebars.registerHelper('fmtText', function(text) {
return new Handlebars.SafeString(app.helpers.textFormatter(text, null));
});

View file

@ -7,7 +7,7 @@ $(function() {
(function(){ (function(){
//make it so I take text and mentions rather than the modelapp.helpers.textFormatter( //make it so I take text and mentions rather than the modelapp.helpers.textFormatter(
var textFormatter = function textFormatter(text, model) { var textFormatter = function textFormatter(text, model) {
var mentions = model.get("mentioned_people"); var mentions = model ? model.get("mentioned_people") : [];
return textFormatter.mentionify( return textFormatter.mentionify(
textFormatter.hashtagify( textFormatter.hashtagify(

View file

@ -6,7 +6,7 @@ app.pages.Profile = app.views.Base.extend({
}, },
subviews: { subviews: {
'#profile .badge': 'sidebarView', '#profile': 'sidebarView',
'.profile_header': 'headerView' '.profile_header': 'headerView'
}, },
@ -15,6 +15,10 @@ app.pages.Profile = app.views.Base.extend({
initialize: function(opts) { initialize: function(opts) {
if( app.hasPreload('person') ) if( app.hasPreload('person') )
this.model = new app.models.Person(app.parsePreload('person')); this.model = new app.models.Person(app.parsePreload('person'));
if( app.hasPreload('photos') )
this.photos = app.parsePreload('photos'); // we don't interact with it, so no model
if( app.hasPreload('contacts') )
this.contacts = app.parsePreload('contacts'); // we don't interact with it, so no model
this.model.on('change', this.render, this); this.model.on('change', this.render, this);
@ -26,7 +30,11 @@ app.pages.Profile = app.views.Base.extend({
}, },
sidebarView: function() { sidebarView: function() {
return new app.views.ProfileSidebar({model: this.model}); return new app.views.ProfileSidebar({
model: this.model,
photos: this.photos,
contacts: this.contacts
});
}, },
headerView: function() { headerView: function() {

View file

@ -2,13 +2,23 @@
app.views.ProfileSidebar = app.views.Base.extend({ app.views.ProfileSidebar = app.views.Base.extend({
templateName: 'profile_sidebar', templateName: 'profile_sidebar',
initialize: function(opts) {
this.photos = _.has(opts, 'photos') ? opts.photos : null;
this.contacts = _.has(opts, 'contacts') ? opts.contacts : null;
},
presenter: function() { presenter: function() {
return _.extend({}, this.defaultPresenter(), { return _.extend({}, this.defaultPresenter(), {
do_profile_btns: this._shouldDoProfileBtns(), do_profile_btns: this._shouldDoProfileBtns(),
do_profile_info: this._shouldDoProfileInfo(),
do_photos: this._shouldDoPhotos(),
do_contacts: this._shouldDoContacts(),
is_sharing: this.model.isSharing(), is_sharing: this.model.isSharing(),
is_receiving: this.model.isReceiving(), is_receiving: this.model.isReceiving(),
is_mutual: this.model.isMutual(), is_mutual: this.model.isMutual(),
is_not_blocked: !this.model.isBlocked() is_not_blocked: !this.model.isBlocked(),
photos: this.photos,
contacts: this.contacts
}); });
}, },
@ -16,6 +26,18 @@ app.views.ProfileSidebar = app.views.Base.extend({
return (app.currentUser.authenticated() && !this.model.get('is_own_profile')); return (app.currentUser.authenticated() && !this.model.get('is_own_profile'));
}, },
_shouldDoProfileInfo: function() {
return (this.model.isSharing() || this.model.get('is_own_profile'));
},
_shouldDoPhotos: function() {
return (this.photos && this.photos.items.length > 0);
},
_shouldDoContacts: function() {
return (this.contacts && this.contacts.items.length > 0);
},
postRenderTemplate: function() { postRenderTemplate: function() {
// UGLY (re-)attach the facebox // UGLY (re-)attach the facebox
this.$('a[rel*=facebox]').facebox(); this.$('a[rel*=facebox]').facebox();

View file

@ -17,41 +17,7 @@
}); });
}); });
this.isRTL = function(str) { this.isRTL = app.helpers.txtDirection;
if(typeof str !== "string" || str.length < 1) {
return false;
}
var charCode = str.charCodeAt(0);
if(charCode >= 1536 && charCode <= 1791) // Sarabic, Persian, ...
return true;
else if(charCode >= 65136 && charCode <= 65279) // Arabic present 1
return true;
else if(charCode >= 64336 && charCode <= 65023) // Arabic present 2
return true;
else if(charCode>=1424 && charCode<=1535) // Hebrew
return true;
else if(charCode>=64256 && charCode<=64335) // Hebrew present
return true;
else if(charCode>=1792 && charCode<=1871) // Syriac
return true;
else if(charCode>=1920 && charCode<=1983) // Thaana
return true;
else if(charCode>=1984 && charCode<=2047) // NKo
return true;
else if(charCode>=11568 && charCode<=11647) // Tifinagh
return true;
return false;
};
this.updateBinds = function() { this.updateBinds = function() {
$.each(self.binds, function(index, bind) { $.each(self.binds, function(index, bind) {
@ -73,12 +39,7 @@
var textArea = $(this), var textArea = $(this),
cleaned = textArea.val().replace(self.cleaner, "").replace(/^[ ]+/, ""); cleaned = textArea.val().replace(self.cleaner, "").replace(/^[ ]+/, "");
if(self.isRTL(cleaned)) { app.helpers.txtDirection.setCssFor(cleaned, textArea);
textArea.css("direction", "rtl");
}
else {
textArea.css("direction", "ltr");
}
}; };
}; };

View file

@ -1,5 +1,5 @@
<div class="profile_photo"> <div id="profile_photo" class="profile_photo">
{{#linkToPerson this}} {{#linkToPerson this}}
{{{personImage this "l"}}} {{{personImage this "l"}}}
{{/linkToPerson}} {{/linkToPerson}}
@ -37,3 +37,66 @@
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/if}}
{{#if do_profile_info}}
<ul id="profile_information">
{{#with profile}}
{{#if bio}}
<li>
<h4>{{t 'profile.bio'}}</h4>
<div class="{{txtDirClass bio}}">{{fmtText bio}}</div>
</li>
{{/if}}
{{#if location}}
<li>
<h4>{{t 'profile.location'}}</h4>
<div class="{{txtDirClass location}}">{{fmtText location}}</div>
</li>
{{/if}}
{{#if gender}}
<li>
<h4>{{t 'profile.gender'}}</h4>
{{gender}}
</li>
{{/if}}
{{#if birthday}}
<li>
<h4>{{t 'profile.born'}}</h4>
{{birthday}}
</li>
{{/if}}
{{/with}}
{{#if do_photos}}
<li class="image_list">
<h4>
{{t 'profile.photos'}}
<div class="item_count">{{photos.count}}</div>
</h4>
<div class="section photo_pictures">
{{#each photos.items}}
<img src="{{sizes.s}}" alt="{{guid}}" />
{{/each}}
</div>
<p class="see_all">
<a href="{{urlTo 'person_photos' guid}}">{{t 'header.view_all'}}</a>
</p>
</li>
{{/if}}
{{#if do_contacts}}
<li class="image_list">
<h4>
{{t 'profile.contacts'}}
<div class="item_count">{{contacts.count}}</div>
</h4>
<div class="section contact_pictures">
{{#each contacts.items}}
{{#linkToPerson this}}{{{personImage this "s"}}}{{/linkToPerson}}
{{/each}}
</div>
<p class="see_all">
<a href="{{urlTo 'person_contacts' guid}}">{{t 'header.view_all'}}</a>
</p>
</li>
{{/if}}
</ul>
{{/if}}

View file

@ -3,7 +3,8 @@
# the COPYRIGHT file. # the COPYRIGHT file.
class PeopleController < ApplicationController class PeopleController < ApplicationController
before_action :authenticate_user!, :except => [:show, :last_post] before_action :authenticate_user!, except: [:show, :last_post]
before_action :find_person, only: [:show, :stream]
use_bootstrap_for :index use_bootstrap_for :index
@ -74,12 +75,6 @@ class PeopleController < ApplicationController
# renders the persons user profile page # renders the persons user profile page
def show def show
@person = Person.find_from_guid_or_username(params)
# view this profile on the home pod, if you don't want to sign in...
authenticate_user! if remote_profile_with_no_user_session?
raise Diaspora::AccountClosed if @person.closed_account?
mark_corresponding_notifications_read if user_signed_in? mark_corresponding_notifications_read if user_signed_in?
@aspect = :profile # let aspect dropdown create new aspects @aspect = :profile # let aspect dropdown create new aspects
@ -89,19 +84,26 @@ class PeopleController < ApplicationController
respond_to do |format| respond_to do |format|
format.all do format.all do
@profile = @person.profile @profile = @person.profile
@photos = photos_from(@person)
if current_user if current_user
@block = current_user.blocks.where(:person_id => @person.id).first @block = current_user.blocks.where(:person_id => @person.id).first
@contact = current_user.contact_for(@person) @contact = current_user.contact_for(@person)
if @contact && !params[:only_posts] if @contact && !params[:only_posts]
@contacts_of_contact_count = @contact.contacts.count(:all) @contacts_of_contact_count = contact_contacts.count(:all)
@contacts_of_contact = @contact.contacts.limit(8) @contacts_of_contact = contact_contacts.limit(8)
else else
@contact ||= Contact.new @contact ||= Contact.new
end end
end end
gon.preloads[:person] = @person_json gon.preloads[:person] = @person_json
gon.preloads[:photos] = {
count: photos_from(@person).count(:all),
items: PhotoPresenter.as_collection(photos_from(@person).limit(8), :base_hash)
}
gon.preloads[:contacts] = {
count: contact_contacts.count(:all),
items: PersonPresenter.as_collection(contact_contacts.limit(8), :full_hash_with_avatar, current_user)
}
respond_with @person, :locals => {:post_type => :all} respond_with @person, :locals => {:post_type => :all}
end end
@ -110,11 +112,6 @@ class PeopleController < ApplicationController
end end
def stream def stream
@person = Person.find_from_guid_or_username(params)
authenticate_user! if remote_profile_with_no_user_session?
raise Diaspora::AccountClosed if @person.closed_account?
respond_to do |format| respond_to do |format|
format.all { redirect_to person_path(@person) } format.all { redirect_to person_path(@person) }
format.json do format.json do
@ -161,8 +158,8 @@ class PeopleController < ApplicationController
if @person if @person
@contact = current_user.contact_for(@person) @contact = current_user.contact_for(@person)
@aspect = :profile @aspect = :profile
@contacts_of_contact = @contact.contacts.paginate(:page => params[:page], :per_page => (params[:limit] || 15)) @contacts_of_contact = contact_contacts.paginate(:page => params[:page], :per_page => (params[:limit] || 15))
@contacts_of_contact_count = @contact.contacts.count(:all) @contacts_of_contact_count = contact_contacts.count(:all)
@hashes = hashes_for_people @contacts_of_contact, @aspects @hashes = hashes_for_people @contacts_of_contact, @aspects
else else
flash[:error] = I18n.t 'people.show.does_not_exist' flash[:error] = I18n.t 'people.show.does_not_exist'
@ -187,6 +184,14 @@ class PeopleController < ApplicationController
private private
def find_person
@person = Person.find_from_guid_or_username(params)
# view this profile on the home pod, if you don't want to sign in...
authenticate_user! if remote_profile_with_no_user_session?
raise Diaspora::AccountClosed if @person.closed_account?
end
def hashes_for_people(people, aspects) def hashes_for_people(people, aspects)
ids = people.map{|p| p.id} ids = people.map{|p| p.id}
contacts = {} contacts = {}
@ -214,13 +219,23 @@ class PeopleController < ApplicationController
end end
def photos_from(person) def photos_from(person)
photos = if user_signed_in? @photos ||= if user_signed_in?
current_user.photos_from(person) current_user.photos_from(person)
else else
Photo.where(author_id: person.id, public: true) Photo.where(author_id: person.id, public: true)
end.order('created_at desc')
end end
photos.order('created_at desc') # given a `@person` find the contacts that person has in that aspect(?)
# or use your own contacts if it's yourself
# see: `Contact#contacts`
def contact_contacts
@contact_contacts ||= if @person == current_user.person
current_user.contact_people
else
contact = current_user.contact_for(@person)
contact.try(:contacts) || Contact.none
end
end end
def mark_corresponding_notifications_read def mark_corresponding_notifications_read

View file

@ -18,7 +18,7 @@ class BasePresenter
end end
def method_missing(method, *args) def method_missing(method, *args)
@presentable.send(method, *args) if @presentable.respond_to?(method) @presentable.public_send(method, *args)
end end
class NilPresenter class NilPresenter

View file

@ -11,7 +11,7 @@ class PersonPresenter < BasePresenter
base_hash.merge({ base_hash.merge({
relationship: relationship, relationship: relationship,
block: is_blocked? ? BlockPresenter.new(current_user_person_block).base_hash : false, block: is_blocked? ? BlockPresenter.new(current_user_person_block).base_hash : false,
contact: { id: current_user_person_contact.id }, contact: (!own_profile? && has_contact?) ? { id: current_user_person_contact.id } : false,
is_own_profile: own_profile? is_own_profile: own_profile?
}) })
end end
@ -73,6 +73,10 @@ class PersonPresenter < BasePresenter
@contact ||= current_user.contact_for(@presentable) @contact ||= current_user.contact_for(@presentable)
end end
def has_contact?
current_user_person_contact.present?
end
def is_blocked? def is_blocked?
current_user_person_block.present? current_user_person_block.present?
end end

View file

@ -0,0 +1,16 @@
class PhotoPresenter < BasePresenter
def base_hash
{ id: id,
guid: guid,
dimensions: {
h: height,
w: width
},
sizes: {
s: url(:thumb_small),
m: url(:thumb_medium),
l: url(:scaled_full)
}
}
end
end

View file

@ -1,4 +1,6 @@
class ProfilePresenter < BasePresenter class ProfilePresenter < BasePresenter
include PeopleHelper
def base_hash def base_hash
{ id: id, { id: id,
tags: tag_string, tags: tag_string,
@ -15,4 +17,8 @@ class ProfilePresenter < BasePresenter
avatar: AvatarPresenter.new(@presentable).base_hash, avatar: AvatarPresenter.new(@presentable).base_hash,
}) })
end end
def formatted_birthday
birthday_format(birthday) if birthday
end
end end

View file

@ -123,6 +123,13 @@ en:
helper: helper:
is_sharing: "<%= name %> is sharing with you" is_sharing: "<%= name %> is sharing with you"
is_not_sharing: "<%= name %> is not sharing with you" is_not_sharing: "<%= name %> is not sharing with you"
profile:
bio: 'Bio'
location: "Location"
gender: "Gender"
born: "Birthday"
photos: "Photos"
contacts: "Contacts"
conversation: conversation:
participants: "Participants" participants: "Participants"