diff --git a/app/assets/javascripts/app/helpers/direction_detector.js b/app/assets/javascripts/app/helpers/direction_detector.js new file mode 100644 index 000000000..ee1495718 --- /dev/null +++ b/app/assets/javascripts/app/helpers/direction_detector.js @@ -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; + } + }; +})(); diff --git a/app/assets/javascripts/app/helpers/handlebars-helpers.js b/app/assets/javascripts/app/helpers/handlebars-helpers.js index ebb1e132b..3affa6cdc 100644 --- a/app/assets/javascripts/app/helpers/handlebars-helpers.js +++ b/app/assets/javascripts/app/helpers/handlebars-helpers.js @@ -2,6 +2,10 @@ Handlebars.registerHelper('t', function(scope, values) { return Diaspora.I18n.t(scope, values.hash) }); +Handlebars.registerHelper('txtDirClass', function(str) { + return app.helpers.txtDirection.classFor(str); +}); + Handlebars.registerHelper('imageUrl', function(path){ return ImagePaths.get(path); }); @@ -72,3 +76,7 @@ Handlebars.registerHelper('personImage', function(person, size, imageClass) { Handlebars.registerHelper('localTime', function(timestamp) { return new Date(timestamp).toLocaleString(); }); + +Handlebars.registerHelper('fmtText', function(text) { + return new Handlebars.SafeString(app.helpers.textFormatter(text, null)); +}); diff --git a/app/assets/javascripts/app/helpers/text_formatter.js b/app/assets/javascripts/app/helpers/text_formatter.js index 9f27029c6..6e15e2ed3 100644 --- a/app/assets/javascripts/app/helpers/text_formatter.js +++ b/app/assets/javascripts/app/helpers/text_formatter.js @@ -7,7 +7,7 @@ $(function() { (function(){ //make it so I take text and mentions rather than the modelapp.helpers.textFormatter( var textFormatter = function textFormatter(text, model) { - var mentions = model.get("mentioned_people"); + var mentions = model ? model.get("mentioned_people") : []; return textFormatter.mentionify( textFormatter.hashtagify( diff --git a/app/assets/javascripts/app/pages/profile.js b/app/assets/javascripts/app/pages/profile.js index 8c622ddcf..65bb87eb0 100644 --- a/app/assets/javascripts/app/pages/profile.js +++ b/app/assets/javascripts/app/pages/profile.js @@ -6,7 +6,7 @@ app.pages.Profile = app.views.Base.extend({ }, subviews: { - '#profile .badge': 'sidebarView', + '#profile': 'sidebarView', '.profile_header': 'headerView' }, @@ -15,6 +15,10 @@ app.pages.Profile = app.views.Base.extend({ initialize: function(opts) { if( app.hasPreload('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); @@ -26,7 +30,11 @@ app.pages.Profile = app.views.Base.extend({ }, 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() { diff --git a/app/assets/javascripts/app/views/profile_sidebar_view.js b/app/assets/javascripts/app/views/profile_sidebar_view.js index 9190bd544..f45f18f33 100644 --- a/app/assets/javascripts/app/views/profile_sidebar_view.js +++ b/app/assets/javascripts/app/views/profile_sidebar_view.js @@ -2,13 +2,23 @@ app.views.ProfileSidebar = app.views.Base.extend({ templateName: 'profile_sidebar', + initialize: function(opts) { + this.photos = _.has(opts, 'photos') ? opts.photos : null; + this.contacts = _.has(opts, 'contacts') ? opts.contacts : null; + }, + presenter: function() { return _.extend({}, this.defaultPresenter(), { do_profile_btns: this._shouldDoProfileBtns(), + do_profile_info: this._shouldDoProfileInfo(), + do_photos: this._shouldDoPhotos(), + do_contacts: this._shouldDoContacts(), is_sharing: this.model.isSharing(), is_receiving: this.model.isReceiving(), 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')); }, + _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() { // UGLY (re-)attach the facebox this.$('a[rel*=facebox]').facebox(); diff --git a/app/assets/javascripts/widgets/direction-detector.js b/app/assets/javascripts/widgets/direction-detector.js index 8c886e6f3..2db21a1f7 100644 --- a/app/assets/javascripts/widgets/direction-detector.js +++ b/app/assets/javascripts/widgets/direction-detector.js @@ -11,47 +11,13 @@ this.subscribe("widget/ready", function() { self.updateBinds(); - + self.globalSubscribe("stream/scrolled", function() { self.updateBinds(); }); }); - this.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; - }; + this.isRTL = app.helpers.txtDirection; this.updateBinds = function() { $.each(self.binds, function(index, bind) { @@ -71,14 +37,9 @@ this.updateDirection = function() { var textArea = $(this), - cleaned = textArea.val().replace(self.cleaner, "").replace(/^[ ]+/, ""); + cleaned = textArea.val().replace(self.cleaner, "").replace(/^[ ]+/, ""); - if(self.isRTL(cleaned)) { - textArea.css("direction", "rtl"); - } - else { - textArea.css("direction", "ltr"); - } + app.helpers.txtDirection.setCssFor(cleaned, textArea); }; }; diff --git a/app/assets/templates/profile_sidebar_tpl.jst.hbs b/app/assets/templates/profile_sidebar_tpl.jst.hbs index 4d0bba7cc..e65cfd9ae 100644 --- a/app/assets/templates/profile_sidebar_tpl.jst.hbs +++ b/app/assets/templates/profile_sidebar_tpl.jst.hbs @@ -1,5 +1,5 @@ -
+
{{#linkToPerson this}} {{{personImage this "l"}}} {{/linkToPerson}} @@ -37,3 +37,66 @@ {{/if}}
{{/if}} + +{{#if do_profile_info}} + +{{/if}} diff --git a/app/controllers/people_controller.rb b/app/controllers/people_controller.rb index e8d569ec3..bdef09e2c 100644 --- a/app/controllers/people_controller.rb +++ b/app/controllers/people_controller.rb @@ -3,7 +3,8 @@ # the COPYRIGHT file. 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 @@ -74,12 +75,6 @@ class PeopleController < ApplicationController # renders the persons user profile page 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? @aspect = :profile # let aspect dropdown create new aspects @@ -89,19 +84,26 @@ class PeopleController < ApplicationController respond_to do |format| format.all do @profile = @person.profile - @photos = photos_from(@person) if current_user @block = current_user.blocks.where(:person_id => @person.id).first @contact = current_user.contact_for(@person) if @contact && !params[:only_posts] - @contacts_of_contact_count = @contact.contacts.count(:all) - @contacts_of_contact = @contact.contacts.limit(8) + @contacts_of_contact_count = contact_contacts.count(:all) + @contacts_of_contact = contact_contacts.limit(8) else @contact ||= Contact.new end end 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} end @@ -110,11 +112,6 @@ class PeopleController < ApplicationController end 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| format.all { redirect_to person_path(@person) } format.json do @@ -161,8 +158,8 @@ class PeopleController < ApplicationController if @person @contact = current_user.contact_for(@person) @aspect = :profile - @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 = contact_contacts.paginate(:page => params[:page], :per_page => (params[:limit] || 15)) + @contacts_of_contact_count = contact_contacts.count(:all) @hashes = hashes_for_people @contacts_of_contact, @aspects else flash[:error] = I18n.t 'people.show.does_not_exist' @@ -187,6 +184,14 @@ class PeopleController < ApplicationController 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) ids = people.map{|p| p.id} contacts = {} @@ -214,13 +219,23 @@ class PeopleController < ApplicationController end def photos_from(person) - photos = if user_signed_in? + @photos ||= if user_signed_in? current_user.photos_from(person) else Photo.where(author_id: person.id, public: true) - end + end.order('created_at desc') + 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 def mark_corresponding_notifications_read diff --git a/app/presenters/base_presenter.rb b/app/presenters/base_presenter.rb index 86845a8b4..122a505b6 100644 --- a/app/presenters/base_presenter.rb +++ b/app/presenters/base_presenter.rb @@ -18,7 +18,7 @@ class BasePresenter end def method_missing(method, *args) - @presentable.send(method, *args) if @presentable.respond_to?(method) + @presentable.public_send(method, *args) end class NilPresenter diff --git a/app/presenters/person_presenter.rb b/app/presenters/person_presenter.rb index 7c0f30466..2b15f4d4e 100644 --- a/app/presenters/person_presenter.rb +++ b/app/presenters/person_presenter.rb @@ -11,7 +11,7 @@ class PersonPresenter < BasePresenter base_hash.merge({ relationship: relationship, 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? }) end @@ -73,6 +73,10 @@ class PersonPresenter < BasePresenter @contact ||= current_user.contact_for(@presentable) end + def has_contact? + current_user_person_contact.present? + end + def is_blocked? current_user_person_block.present? end diff --git a/app/presenters/photo_presenter.rb b/app/presenters/photo_presenter.rb new file mode 100644 index 000000000..59993733b --- /dev/null +++ b/app/presenters/photo_presenter.rb @@ -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 diff --git a/app/presenters/profile_presenter.rb b/app/presenters/profile_presenter.rb index 8f916431a..24f8624f7 100644 --- a/app/presenters/profile_presenter.rb +++ b/app/presenters/profile_presenter.rb @@ -1,4 +1,6 @@ class ProfilePresenter < BasePresenter + include PeopleHelper + def base_hash { id: id, tags: tag_string, @@ -15,4 +17,8 @@ class ProfilePresenter < BasePresenter avatar: AvatarPresenter.new(@presentable).base_hash, }) end + + def formatted_birthday + birthday_format(birthday) if birthday + end end diff --git a/config/locales/javascript/javascript.en.yml b/config/locales/javascript/javascript.en.yml index 688568c1f..d9a783c35 100644 --- a/config/locales/javascript/javascript.en.yml +++ b/config/locales/javascript/javascript.en.yml @@ -123,6 +123,13 @@ en: helper: is_sharing: "<%= name %> is 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: participants: "Participants"