From e852940382e9a1e136ea5b3520f2fed8c4f83129 Mon Sep 17 00:00:00 2001 From: Dan Hansen Date: Sun, 3 Jul 2011 14:49:01 -0700 Subject: [PATCH 1/8] HOVERCARD --- app/views/layouts/_header.html.haml | 4 ++++ config/assets.yml | 30 ++++++++++++++---------- public/stylesheets/sass/application.sass | 7 ++++++ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 55dd8ae51..077277a3a 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -52,6 +52,10 @@ .ajax_loader = image_tag("ajax-loader.gif") + #hovercard + %img.avatar + %a.person + %ul#user_menu.dropdown %li .right diff --git a/config/assets.yml b/config/assets.yml index 28b473f00..bf4808b75 100644 --- a/config/assets.yml +++ b/config/assets.yml @@ -33,9 +33,9 @@ javascripts: - public/javascripts/widgets/infinite-scroll.js - public/javascripts/widgets/directionDetector.js - public/javascripts/widgets/notifications.js - - public/javascripts/widgets/notifications-badge.js - public/javascripts/widgets/flashes.js - public/javascripts/widgets/post.js + - public/javascripts/widgets/hovercard.js - public/javascripts/view.js - public/javascripts/stream.js - public/javascripts/content-updater.js @@ -45,18 +45,27 @@ javascripts: - public/javascripts/login.js mobile: - public/javascripts/vendor/jquery152.min.js - - public/javascripts/custom-mobile-scripting.js - - public/javascripts/vendor/jquery.mobile-1.0a4.js - - public/javascripts/jquery.infinitescroll-custom.js + + - public/javascripts/vendor/underscore.js + - public/javascripts/vendor/backbone-min.js + + - public/javascripts/vendor/Mustache.js + + - public/javascripts/mobile/app.js + - public/javascripts/mobile/helpers.js + - public/javascripts/mobile/models/* + - public/javascripts/mobile/collections/* + - public/javascripts/mobile/controllers/* + - public/javascripts/mobile/views/* + - public/javascripts/diaspora.js - public/javascripts/widgets/i18n.js - - public/javascripts/widgets/infinite-scroll.js - - public/javascripts/rails.js mailchimp: - public/javascripts/vendor/mailchimp/jquery.form.js - public/javascripts/vendor/mailchimp/jquery.validate.js - public/javascripts/vendor/mailchimp/jquery126.min.js aspects: + - public/javascripts/aspect-edit.js - public/javascripts/contact-list.js finder: - public/javascripts/friend-finder.js @@ -86,12 +95,9 @@ stylesheets: - public/stylesheets/vendor/fileuploader.css - public/stylesheets/vendor/tipsy.css - public/stylesheets/vendor/autoSuggest.css - - popup: - - public/stylesheets/application.css - - public/stylesheets/popup.css - - public/stylesheets/ui.css - rtl: - public/stylesheets/rtl.css + + mobile: + - public/stylesheets/mobile.css diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index c88e9f122..26fb451dd 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -3069,3 +3069,10 @@ ul.left_nav .tags_people .dropdown :display none + +#hovercard + :display none + :background + :color black + :z + :index 10 From 8f31e9953517aadbce1fa46505a79802bfd391a0 Mon Sep 17 00:00:00 2001 From: Dan Hansen Date: Sun, 3 Jul 2011 15:13:55 -0700 Subject: [PATCH 2/8] missed hovercard.js --- public/javascripts/widgets/hovercard.js | 78 +++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 public/javascripts/widgets/hovercard.js diff --git a/public/javascripts/widgets/hovercard.js b/public/javascripts/widgets/hovercard.js new file mode 100644 index 000000000..f944f3d98 --- /dev/null +++ b/public/javascripts/widgets/hovercard.js @@ -0,0 +1,78 @@ +(function() { + var HoverCard = function() { + var self = this; + + this.start = function() { + this.hoverCard = { + tip: $("#hovercard"), + offset: { + left: -30, + top: -60 + }, + personLink: $("#hovercard").find("a.person"), + avatar: $("#hovercard").find(".avatar") + }; + + $(document.body).delegate("a.author", "hover", this.handleHoverEvent); + this.hoverCard.tip.hover(this.hoverCardHover, this.clearTimeout); + }; + + this.handleHoverEvent = function(evt) { + self.target = $(evt.target); + + if(evt.type === "mouseenter") { + self.startHover(); + } + else { + self.clearTimeout(evt); + } + }; + + this.startHover = function(evt) { + if(!self.hoverCardTimeout) { + self.clearTimeout(false); + } + self.timeout = setTimeout(self.showHoverCard, 30); + }; + + this.showHoverCard = function() { + self.hoverCard.tip.hide(); + self.hoverCard.tip.prependTo(self.target.parent()); + + $.getJSON(self.target.attr("href"), function(person) { + var position = self.target.position(); + self.hoverCard.tip.css({ + position: "absolute", + left: position.left + self.hoverCard.offset.left, + top: position.top + self.hoverCard.offset.top + }); + + self.hoverCard.avatar.attr("src", person.avatar); + self.hoverCard.personLink.attr("href", self.target.attr("href")); + self.hoverCard.personLink.text(person.name); + + self.hoverCard.tip.show(); + }); + }; + + this.clearTimeout = function(delayed) { + function callback() { + self.timeout = clearTimeout(self.timeout); + self.hoverCard.tip.hide(); + }; + + if((typeof delayed === "boolean" && delayed) || (typeof delayed === "object" && delayed.type === "mouseleave")) { + self.hoverCardTimeout = setTimeout(callback, 300); + } + else { + callback(); + } + }; + + this.hoverCardHover = function() { + self.hoverCardTimeout = clearTimeout(self.hoverCardTimeout); + }; + }; + + Diaspora.widgets.add("hoverCard", HoverCard); +})(); From 675d9d74859ff48c8ebeaef7dd1db3553f14a728 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Sun, 3 Jul 2011 16:29:49 -0700 Subject: [PATCH 3/8] wip. Person#as_json is breaking right now --- app/controllers/people_controller.rb | 7 +++++- app/models/person.rb | 5 ++-- app/views/comments/_comment.html.haml | 2 +- app/views/layouts/_header.html.haml | 7 +++++- public/javascripts/widgets/hovercard.js | 17 +++++++------ public/stylesheets/sass/application.sass | 31 +++++++++++++++++++++++- 6 files changed, 55 insertions(+), 14 deletions(-) diff --git a/app/controllers/people_controller.rb b/app/controllers/people_controller.rb index 422bf1cbf..292d8c02d 100644 --- a/app/controllers/people_controller.rb +++ b/app/controllers/people_controller.rb @@ -106,7 +106,12 @@ class PeopleController < ApplicationController if params[:only_posts] render :partial => 'shared/stream', :locals => {:posts => @posts} else - respond_with @person, :locals => {:post_type => :all} + respond_to do |format| + format.all { respond_with @person, :locals => {:post_type => :all} } + format.json { + render :json => @person.to_json(:aspect_ids => @aspects_with_person.to_json) + } + end end else diff --git a/app/models/person.rb b/app/models/person.rb index 1b534eed4..59ac74889 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -220,13 +220,14 @@ class Person < ActiveRecord::Base end def as_json(opts={}) - { - :id => self.guid, + json = { + :id => self.id, :name => self.name, :avatar => self.profile.image_url(:thumb_small), :handle => self.diaspora_handle, :url => "/people/#{self.id}" } + json.merge(:aspect_ids => opts[:aspect_ids]) end protected diff --git a/app/views/comments/_comment.html.haml b/app/views/comments/_comment.html.haml index c32283bde..35d07f4ac 100644 --- a/app/views/comments/_comment.html.haml +++ b/app/views/comments/_comment.html.haml @@ -9,7 +9,7 @@ = person_image_link(comment.author) .content %span.from - = person_link(comment.author) + = person_link(comment.author, :class => "author") %span{:class => direction_for(comment.text)} = markdownify(comment.text, :youtube_maps => comment.youtube_titles) diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 077277a3a..63cf6cbcc 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -54,7 +54,12 @@ #hovercard %img.avatar - %a.person + %h4 + %a.person + = render 'aspect_memberships/aspect_dropdown', :contact => Contact.new, :person => Person.new, :hang => 'left' + .hovercard_footer + .footer_container + Message %ul#user_menu.dropdown %li diff --git a/public/javascripts/widgets/hovercard.js b/public/javascripts/widgets/hovercard.js index f944f3d98..a2a4458b7 100644 --- a/public/javascripts/widgets/hovercard.js +++ b/public/javascripts/widgets/hovercard.js @@ -6,11 +6,12 @@ this.hoverCard = { tip: $("#hovercard"), offset: { - left: -30, - top: -60 + left: 00, + top: 20 }, personLink: $("#hovercard").find("a.person"), - avatar: $("#hovercard").find(".avatar") + avatar: $("#hovercard").find(".avatar"), + dropdown: $("#hovercard").find(".dropdown_list") }; $(document.body).delegate("a.author", "hover", this.handleHoverEvent); @@ -32,26 +33,26 @@ if(!self.hoverCardTimeout) { self.clearTimeout(false); } - self.timeout = setTimeout(self.showHoverCard, 30); + self.timeout = setTimeout(self.showHoverCard, 100); }; this.showHoverCard = function() { - self.hoverCard.tip.hide(); + self.hoverCard.tip.fadeOut('fast'); self.hoverCard.tip.prependTo(self.target.parent()); $.getJSON(self.target.attr("href"), function(person) { var position = self.target.position(); self.hoverCard.tip.css({ - position: "absolute", left: position.left + self.hoverCard.offset.left, top: position.top + self.hoverCard.offset.top }); self.hoverCard.avatar.attr("src", person.avatar); - self.hoverCard.personLink.attr("href", self.target.attr("href")); + self.hoverCard.personLink.attr("href", person.url); self.hoverCard.personLink.text(person.name); + self.hoverCard.dropdown.attr("data-person-id", person.id); - self.hoverCard.tip.show(); + self.hoverCard.tip.fadeIn('fast'); }); }; diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index 26fb451dd..a1c23868e 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -3071,8 +3071,37 @@ ul.left_nav :display none #hovercard + @include box-shadow(0,0,7px,#333) + @include border-radius(2px) + + .avatar + :position relative + :height 70px + :width 70px + :margin + :right 10px + + :position absolute :display none :background - :color black + :color $background + :padding 5px + :bottom 25px + + :border 1px solid #ddd + + :width 240px + :z :index 10 + + .hovercard_footer + :position absolute + :bottom 0 + :left 0 + :background + :color #eee + :width 100% + + .footer_container + :padding 0 10px From 1c79c2c6f064fe904990a5ed6a116f6acb9c0a04 Mon Sep 17 00:00:00 2001 From: Ilyaaaaaaaaaaaaa Zhitomirskiy Date: Tue, 5 Jul 2011 20:13:38 -0700 Subject: [PATCH 4/8] made aspect_dropdown ajax in (created route), in the process of removing aspect_membership js responses --- .../aspect_memberships_controller.rb | 20 +++++++++------- app/controllers/contacts_controller.rb | 1 + app/controllers/people_controller.rb | 12 ++++++++-- app/helpers/aspect_global_helper.rb | 6 +---- app/models/aspect_membership.rb | 8 +++++++ app/models/person.rb | 1 - app/views/aspect_memberships/create.js.erb | 1 - app/views/layouts/_header.html.haml | 3 ++- config/routes.rb | 2 ++ public/javascripts/contact-edit.js | 24 +++++++++++-------- public/javascripts/widgets/hovercard.js | 23 +++++++++++++++++- public/stylesheets/sass/application.sass | 2 +- public/stylesheets/sass/ui.sass | 7 ++++-- .../aspect_memberships_controller_spec.rb | 14 +++++++++-- 14 files changed, 89 insertions(+), 35 deletions(-) diff --git a/app/controllers/aspect_memberships_controller.rb b/app/controllers/aspect_memberships_controller.rb index 4b1096ccc..69b6c07d9 100644 --- a/app/controllers/aspect_memberships_controller.rb +++ b/app/controllers/aspect_memberships_controller.rb @@ -6,6 +6,8 @@ class AspectMembershipsController < ApplicationController before_filter :authenticate_user! + respond_to :html, :json, :js + def destroy #note :id is garbage @@ -17,14 +19,14 @@ class AspectMembershipsController < ApplicationController if membership && membership.destroy @aspect = membership.aspect - flash.now[:notice] = I18n.t 'aspect_memberships.destroy.success' - respond_to do |format| - format.js { } - format.html{ - redirect_to :back - } + respond_with do |format| + format.all{ } + format.json{ render :json => { + :person_id => @person_id, + :aspect_ids => @contact.aspects.map{|a| a.id} + } } end else @@ -44,12 +46,12 @@ class AspectMembershipsController < ApplicationController @aspect = current_user.aspects.where(:id => params[:aspect_id]).first if @contact = current_user.share_with(@person, @aspect) - flash.now[:notice] = I18n.t 'aspects.add_to_aspect.success' - + respond_with AspectMembership.where(:contact_id => @contact.id, :aspect_id => @aspect.id).first else flash[:error] = I18n.t 'contacts.create.failure' - redirect_to :back + #TODO(dan) take this out once the .js template is removed + render :nothing => true end end diff --git a/app/controllers/contacts_controller.rb b/app/controllers/contacts_controller.rb index 5752e1c10..667dda4c4 100644 --- a/app/controllers/contacts_controller.rb +++ b/app/controllers/contacts_controller.rb @@ -24,4 +24,5 @@ class ContactsController < ApplicationController @contacts = current_user.contacts.sharing.includes(:aspect_memberships) render :layout => false end + end diff --git a/app/controllers/people_controller.rb b/app/controllers/people_controller.rb index 292d8c02d..f76adfbad 100644 --- a/app/controllers/people_controller.rb +++ b/app/controllers/people_controller.rb @@ -109,7 +109,7 @@ class PeopleController < ApplicationController respond_to do |format| format.all { respond_with @person, :locals => {:post_type => :all} } format.json { - render :json => @person.to_json(:aspect_ids => @aspects_with_person.to_json) + render :json => @person.to_json } end end @@ -120,6 +120,7 @@ class PeopleController < ApplicationController end end + def retrieve_remote if params[:diaspora_handle] webfinger(params[:diaspora_handle], :single_aspect_form => true) @@ -136,7 +137,6 @@ class PeopleController < ApplicationController @aspect = :profile @contacts_of_contact = @contact.contacts.paginate(:page => params[:page], :per_page => (params[:limit] || 15)) @hashes = hashes_for_people @contacts_of_contact, @aspects - @contact = current_user.contact_for(@person) @aspects_with_person = @contact.aspects @aspect_ids = @aspects_with_person.map(&:id) else @@ -144,8 +144,16 @@ class PeopleController < ApplicationController redirect_to people_path end end + + def aspect_membership_dropdown + @person = Person.find(params[:id]) + @contact = current_user.contact_for(@person) || Contact.new + render :partial => 'aspect_memberships/aspect_dropdown', :locals => {:contact => @contact, :person => @person, :hang => 'left'} + end + private def webfinger(account, opts = {}) Resque.enqueue(Job::SocketWebfinger, current_user.id, account, opts) end + end diff --git a/app/helpers/aspect_global_helper.rb b/app/helpers/aspect_global_helper.rb index dac264005..5d7b71c76 100644 --- a/app/helpers/aspect_global_helper.rb +++ b/app/helpers/aspect_global_helper.rb @@ -68,16 +68,12 @@ module AspectGlobalHelper def aspect_dropdown_list_item(aspect, contact, person) checked = (contact.persisted? && contact.aspect_memberships.detect{ |am| am.aspect_id == aspect.id}) klass = checked ? "selected" : "" - hidden = !checked ? "hidden" : "" str = < - + #{aspect.name} -
- #{aspect_membership_button(aspect, contact, person)} -
LISTITEM str.html_safe diff --git a/app/models/aspect_membership.rb b/app/models/aspect_membership.rb index 372e744ea..64abd8f9c 100644 --- a/app/models/aspect_membership.rb +++ b/app/models/aspect_membership.rb @@ -16,4 +16,12 @@ class AspectMembership < ActiveRecord::Base true end + def as_json(opts={}) + { + :id => self.id, + :person_id => self.person.id, + :contact_id => self.contact.id, + :aspect_ids => self.contact.aspects.map{|a| a.id} + } + end end diff --git a/app/models/person.rb b/app/models/person.rb index 59ac74889..ec2c525f6 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -227,7 +227,6 @@ class Person < ActiveRecord::Base :handle => self.diaspora_handle, :url => "/people/#{self.id}" } - json.merge(:aspect_ids => opts[:aspect_ids]) end protected diff --git a/app/views/aspect_memberships/create.js.erb b/app/views/aspect_memberships/create.js.erb index 595304cf6..2292d0ae6 100644 --- a/app/views/aspect_memberships/create.js.erb +++ b/app/views/aspect_memberships/create.js.erb @@ -19,4 +19,3 @@ if($('#aspects_list').length == 1) { }; element.fadeTo(200,1); - diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 63cf6cbcc..359b02e60 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -56,7 +56,8 @@ %img.avatar %h4 %a.person - = render 'aspect_memberships/aspect_dropdown', :contact => Contact.new, :person => Person.new, :hang => 'left' + #hovercard_dropdown_container + .hovercard_footer .footer_container Message diff --git a/config/routes.rb b/config/routes.rb index 1dd4938fa..e78d8b2ec 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -104,6 +104,8 @@ Diaspora::Application.routes.draw do end end + get "people/:id/aspect_membership_button" => "people#aspect_membership_dropdown", :as => "aspect_membership_button" + diff --git a/public/javascripts/contact-edit.js b/public/javascripts/contact-edit.js index 68a235492..bcdb86542 100644 --- a/public/javascripts/contact-edit.js +++ b/public/javascripts/contact-edit.js @@ -9,12 +9,9 @@ var ContactEdit = { }); }, - updateNumber: function(personId){ - var dropdown = $(".dropdown_list[data-person_id=" + personId.toString() +"]"), - number = dropdown.find(".selected").length, - button = dropdown.parents(".dropdown").children('.button.toggle'); - - var replacement; + updateNumber: function(dropdown, personId, number){ + var button = dropdown.parents(".dropdown").children('.button.toggle'), + replacement; if (number == 0) { button.removeClass("in_aspects"); @@ -36,7 +33,6 @@ var ContactEdit = { toggleCheckbox: function(check){ - check.toggleClass('hidden'); check.parent('li').toggleClass('selected'); }, @@ -44,10 +40,18 @@ var ContactEdit = { var button = li.find('.button'); if(button.hasClass('disabled') || li.hasClass('newItem')){ return; } - var checkbox = li.find('img.check'); - ContactEdit.toggleCheckbox(checkbox); + var checkbox = li.find('img.check'), + selected = li.hasClass("selected"), + routedId = selected ? "/42" : ""; - $.fn.callRemote.apply(button); + $.post("/aspect_memberships" + routedId + ".json", { + "aspect_id": li.data("aspect_id"), + "person_id": li.parent().data("person_id"), + "_method": (selected) ? "DELETE" : "POST" + }, function(aspectMembership) { + ContactEdit.toggleCheckbox(checkbox); + ContactEdit.updateNumber(li.closest(".dropdown_list"), li.parent().data("person_id"), aspectMembership.aspect_ids.length); + }); }, }; diff --git a/public/javascripts/widgets/hovercard.js b/public/javascripts/widgets/hovercard.js index a2a4458b7..ed9e64bd0 100644 --- a/public/javascripts/widgets/hovercard.js +++ b/public/javascripts/widgets/hovercard.js @@ -54,6 +54,27 @@ self.hoverCard.tip.fadeIn('fast'); }); + + $.get(self.target.attr('href')+'/aspect_membership_button',function(data){ + self.hoverCard.tip.find('#hovercard_dropdown_container').html(data); + }); + }; + + this.populateDropdown = function(aspect_ids){ + var dropdown = this.hoverCard.tip.find('.dropdown_list'), + listElements = dropdown.children('li'), + inAspects = false; + + // check-off aspects + $.each(listElements, function(idx,el){ + var element = $(el); + if( aspect_ids.indexOf(element.attr('data-aspect_id')) !== -1 ){ + element.addClass('selected'); + inAspects = true; + } + }); + + // make button green }; this.clearTimeout = function(delayed) { @@ -63,7 +84,7 @@ }; if((typeof delayed === "boolean" && delayed) || (typeof delayed === "object" && delayed.type === "mouseleave")) { - self.hoverCardTimeout = setTimeout(callback, 300); + self.hoverCardTimeout = setTimeout(callback, 400); } else { callback(); diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index a1c23868e..bbf8b4a52 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -3071,8 +3071,8 @@ ul.left_nav :display none #hovercard - @include box-shadow(0,0,7px,#333) @include border-radius(2px) + @include dropdown-shadow .avatar :position relative diff --git a/public/stylesheets/sass/ui.sass b/public/stylesheets/sass/ui.sass index f870ceeae..c811f7da6 100644 --- a/public/stylesheets/sass/ui.sass +++ b/public/stylesheets/sass/ui.sass @@ -150,7 +150,10 @@ &:hover :text-decoration none - + + &:not(.selected) + .check + :display none &.hang_right .wrapper @@ -170,7 +173,7 @@ @include border-radius(3px, 3px, 0, 0) :border 1px solid #444 :bottom none - + .selected :font-weight bold diff --git a/spec/controllers/aspect_memberships_controller_spec.rb b/spec/controllers/aspect_memberships_controller_spec.rb index 7772de8f1..7d4cb8c5c 100644 --- a/spec/controllers/aspect_memberships_controller_spec.rb +++ b/spec/controllers/aspect_memberships_controller_spec.rb @@ -40,7 +40,6 @@ describe AspectMembershipsController do }.should change{ alice.contact_for(bob.person).aspect_memberships.count }.by(1) - end it 'creates a contact' do @@ -62,8 +61,19 @@ describe AspectMembershipsController do :aspect_id => @aspect0.id flash[:error].should_not be_empty end - end + context 'json' do + it 'returns a list of aspect ids for the person' do + post :create, + :format => 'json', + :person_id => @person.id, + :aspect_id => @aspect0.id + + contact = @controller.current_user.contact_for(@person) + response.body.should == contact.aspect_memberships.first.to_json + end + end + end describe "#destroy" do it 'removes contacts from an aspect' do From f28bb767553507267c824e2907c2e63f122ebcd0 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Tue, 5 Jul 2011 21:51:53 -0700 Subject: [PATCH 5/8] hovercards cache both dropdown and person responses --- public/javascripts/widgets/hovercard.js | 101 ++++++++++++++--------- public/stylesheets/sass/application.sass | 4 +- 2 files changed, 65 insertions(+), 40 deletions(-) diff --git a/public/javascripts/widgets/hovercard.js b/public/javascripts/widgets/hovercard.js index ed9e64bd0..2ea406e4d 100644 --- a/public/javascripts/widgets/hovercard.js +++ b/public/javascripts/widgets/hovercard.js @@ -2,20 +2,26 @@ var HoverCard = function() { var self = this; + self.jXHRs = []; + this.start = function() { - this.hoverCard = { + self.personCache = new this.Cache(); + self.dropdownCache = new this.Cache(); + + self.hoverCard = { tip: $("#hovercard"), + dropdownContainer: $("#hovercard_dropdown_container"), offset: { - left: 00, - top: 20 + left: -80, + top: -15 }, personLink: $("#hovercard").find("a.person"), avatar: $("#hovercard").find(".avatar"), dropdown: $("#hovercard").find(".dropdown_list") }; - $(document.body).delegate("a.author", "hover", this.handleHoverEvent); - this.hoverCard.tip.hover(this.hoverCardHover, this.clearTimeout); + $(document.body).delegate("a.author", "hover", self.handleHoverEvent); + self.hoverCard.tip.hover(self.hoverCardHover, self.clearTimeout); }; this.handleHoverEvent = function(evt) { @@ -33,58 +39,49 @@ if(!self.hoverCardTimeout) { self.clearTimeout(false); } - self.timeout = setTimeout(self.showHoverCard, 100); + self.timeout = setTimeout(self.showHoverCard, 600); }; this.showHoverCard = function() { - self.hoverCard.tip.fadeOut('fast'); + self.hoverCard.tip.hide(); self.hoverCard.tip.prependTo(self.target.parent()); - $.getJSON(self.target.attr("href"), function(person) { - var position = self.target.position(); - self.hoverCard.tip.css({ - left: position.left + self.hoverCard.offset.left, - top: position.top + self.hoverCard.offset.top - }); - - self.hoverCard.avatar.attr("src", person.avatar); - self.hoverCard.personLink.attr("href", person.url); - self.hoverCard.personLink.text(person.name); - self.hoverCard.dropdown.attr("data-person-id", person.id); - - self.hoverCard.tip.fadeIn('fast'); - }); - - $.get(self.target.attr('href')+'/aspect_membership_button',function(data){ - self.hoverCard.tip.find('#hovercard_dropdown_container').html(data); + self.personCache.get(self.target.attr("href") + ".json", function(person) { + self.populateHovercard(person); }); }; - this.populateDropdown = function(aspect_ids){ - var dropdown = this.hoverCard.tip.find('.dropdown_list'), - listElements = dropdown.children('li'), - inAspects = false; - - // check-off aspects - $.each(listElements, function(idx,el){ - var element = $(el); - if( aspect_ids.indexOf(element.attr('data-aspect_id')) !== -1 ){ - element.addClass('selected'); - inAspects = true; - } + this.populateHovercard = function(person) { + var position = self.target.position(); + self.hoverCard.tip.css({ + left: position.left + self.hoverCard.offset.left, + top: position.top + self.hoverCard.offset.top }); - // make button green + self.hoverCard.avatar.attr("src", person.avatar); + self.hoverCard.personLink.attr("href", person.url); + self.hoverCard.personLink.text(person.name); + self.hoverCard.dropdown.attr("data-person-id", person.id); + + self.dropdownCache.get(self.target.attr("href") + "/aspect_membership_button", function(dropdown) { + self.hoverCard.dropdownContainer.html(dropdown); + self.hoverCard.tip.fadeIn('fast'); + }); }; this.clearTimeout = function(delayed) { + self.personCache.clearjXHRs(); + self.dropdownCache.clearjXHRs(); + function callback() { self.timeout = clearTimeout(self.timeout); - self.hoverCard.tip.hide(); + self.hoverCard.tip.fadeOut("fast", function(){ + self.hoverCard.dropdownContainer.html(""); + }); }; if((typeof delayed === "boolean" && delayed) || (typeof delayed === "object" && delayed.type === "mouseleave")) { - self.hoverCardTimeout = setTimeout(callback, 400); + self.hoverCardTimeout = setTimeout(callback, 200); } else { callback(); @@ -94,6 +91,32 @@ this.hoverCardHover = function() { self.hoverCardTimeout = clearTimeout(self.hoverCardTimeout); }; + + this.Cache = function() { + var self = this; + this.cache = {}; + this.jXHRs = []; + + this.get = function(key, callback) { + if(typeof self.cache[key] === "undefined") { + self.jXHRs.push($.get(key, function(response) { + self.cache[key] = response; + callback(response); + self.jXHRs.shift(); + })); + } + else { + callback(self.cache[key]); + } + }; + + this.clearjXHRs = function() { + $.each(self.jXHRs, function(index, jXHR) { + jXHR.abort(); + }); + self.jXHRs = []; + }; + }; }; Diaspora.widgets.add("hoverCard", HoverCard); diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index bbf8b4a52..b91959b65 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -3074,6 +3074,8 @@ ul.left_nav @include border-radius(2px) @include dropdown-shadow + @include box-shadow(0,0,12px,#444) + .avatar :position relative :height 70px @@ -3088,7 +3090,7 @@ ul.left_nav :padding 5px :bottom 25px - :border 1px solid #ddd + :border 1px solid #999 :width 240px From a96b77555e489f5af46fb044f1baed19c4aee6a7 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Tue, 5 Jul 2011 22:26:44 -0700 Subject: [PATCH 6/8] DHHHHHHHHHHHHHHHHHHHHHHHHHH hovercard caching with subscription on state change --- public/javascripts/contact-edit.js | 4 +++- public/javascripts/diaspora.js | 9 ++++++--- public/javascripts/widgets/hovercard.js | 4 ++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/public/javascripts/contact-edit.js b/public/javascripts/contact-edit.js index bcdb86542..5c6b5036d 100644 --- a/public/javascripts/contact-edit.js +++ b/public/javascripts/contact-edit.js @@ -24,7 +24,7 @@ var ContactEdit = { }else if (number > 3) { replacement = Diaspora.widgets.i18n.t('aspect_dropdown.toggle.many', { count: number.toString()}) }else { - //the above one are a totalogy, but I want to have them here once for once we figure out a neat way i18n them + //the above one are a tautology, but I want to have them here once for once we figure out a neat way i18n them replacement = Diaspora.widgets.i18n.t('aspect_dropdown.toggle.other', { count: number.toString()}) } @@ -51,6 +51,8 @@ var ContactEdit = { }, function(aspectMembership) { ContactEdit.toggleCheckbox(checkbox); ContactEdit.updateNumber(li.closest(".dropdown_list"), li.parent().data("person_id"), aspectMembership.aspect_ids.length); + + Diaspora.widgets.publish("aspectDropdown/updated", [li.parent().data("person_id"), li.parents(".dropdown").get(0).outerHTML]); }); }, }; diff --git a/public/javascripts/diaspora.js b/public/javascripts/diaspora.js index 8543f8afc..55a4bf509 100644 --- a/public/javascripts/diaspora.js +++ b/public/javascripts/diaspora.js @@ -39,11 +39,14 @@ }; Diaspora.WidgetCollection.prototype.subscribe = function(id, callback, context) { - this.eventsContainer.bind(id, $.proxy(callback, context)); + var ids = id.split(" "); + for(var id in ids) { + this.eventsContainer.bind(ids[id], $.proxy(callback, context)); + } }; - Diaspora.WidgetCollection.prototype.publish = function(id) { - this.eventsContainer.trigger(id); + Diaspora.WidgetCollection.prototype.publish = function(id, args) { + this.eventsContainer.trigger(id, args); }; Diaspora.widgets = new Diaspora.WidgetCollection(); diff --git a/public/javascripts/widgets/hovercard.js b/public/javascripts/widgets/hovercard.js index 2ea406e4d..b93bb6015 100644 --- a/public/javascripts/widgets/hovercard.js +++ b/public/javascripts/widgets/hovercard.js @@ -22,6 +22,10 @@ $(document.body).delegate("a.author", "hover", self.handleHoverEvent); self.hoverCard.tip.hover(self.hoverCardHover, self.clearTimeout); + + Diaspora.widgets.subscribe("aspectDropdown/updated aspectDropdown/blurred", function(evt, personId, dropdownHtml) { + self.dropdownCache.cache["/people/" + personId + "/aspect_membership_button"] = $(dropdownHtml).removeClass("active").get(0).outerHTML; + }); }; this.handleHoverEvent = function(evt) { From d51e9431a6f29e1b880630340b2d668a67173557 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Wed, 6 Jul 2011 01:23:30 -0700 Subject: [PATCH 7/8] fixed some styling/positioning. don't display a hovercard for the current user's person links --- app/helpers/application_helper.rb | 2 ++ public/javascripts/widgets/hovercard.js | 13 ++++++------- public/stylesheets/sass/application.sass | 17 +++++++++++------ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8ccb079d9..5e341eed3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -59,6 +59,8 @@ module ApplicationHelper end def person_link(person, opts={}) + opts[:class] ||= "" + opts[:class] << " self" if current_user.person == person " #{h(person.name)} ".html_safe diff --git a/public/javascripts/widgets/hovercard.js b/public/javascripts/widgets/hovercard.js index b93bb6015..5ed15ed77 100644 --- a/public/javascripts/widgets/hovercard.js +++ b/public/javascripts/widgets/hovercard.js @@ -12,15 +12,15 @@ tip: $("#hovercard"), dropdownContainer: $("#hovercard_dropdown_container"), offset: { - left: -80, - top: -15 + left: 0, + top: 18 }, personLink: $("#hovercard").find("a.person"), avatar: $("#hovercard").find(".avatar"), dropdown: $("#hovercard").find(".dropdown_list") }; - $(document.body).delegate("a.author", "hover", self.handleHoverEvent); + $(document.body).delegate("a.author:not(.self)", "hover", self.handleHoverEvent); self.hoverCard.tip.hover(self.hoverCardHover, self.clearTimeout); Diaspora.widgets.subscribe("aspectDropdown/updated aspectDropdown/blurred", function(evt, personId, dropdownHtml) { @@ -69,7 +69,7 @@ self.dropdownCache.get(self.target.attr("href") + "/aspect_membership_button", function(dropdown) { self.hoverCard.dropdownContainer.html(dropdown); - self.hoverCard.tip.fadeIn('fast'); + self.hoverCard.tip.fadeIn(140); }); }; @@ -79,9 +79,8 @@ function callback() { self.timeout = clearTimeout(self.timeout); - self.hoverCard.tip.fadeOut("fast", function(){ - self.hoverCard.dropdownContainer.html(""); - }); + self.hoverCard.tip.hide(); + self.hoverCard.dropdownContainer.html(""); }; if((typeof delayed === "boolean" && delayed) || (typeof delayed === "object" && delayed.type === "mouseleave")) { diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index b91959b65..beb82a38f 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -3072,9 +3072,7 @@ ul.left_nav #hovercard @include border-radius(2px) - @include dropdown-shadow - - @include box-shadow(0,0,12px,#444) + @include box-shadow(0,0,5px,#666) .avatar :position relative @@ -3087,12 +3085,13 @@ ul.left_nav :display none :background :color $background + :padding 5px - :bottom 25px + :bottom 28px :border 1px solid #999 - :width 240px + :width 220px :z :index 10 @@ -3105,5 +3104,11 @@ ul.left_nav :color #eee :width 100% + :font + :size smaller + + :border + :top 1px solid #ccc + .footer_container - :padding 0 10px + :padding 2px 5px From fd015129c4c0184355f917fb0e0b31a8557ec295 Mon Sep 17 00:00:00 2001 From: danielgrippi Date: Wed, 6 Jul 2011 13:41:42 -0700 Subject: [PATCH 8/8] final touchups to hovercard --- app/models/person.rb | 3 +- app/views/comments/_comment.html.haml | 2 +- app/views/layouts/_header.html.haml | 17 +++++------ app/views/people/_index.html.haml | 8 ++---- app/views/shared/_stream_element.html.haml | 2 +- config/assets.yml | 1 + public/javascripts/widgets/hovercard.js | 26 +++++++++++------ public/stylesheets/sass/application.sass | 33 +++++++++++++++------- 8 files changed, 57 insertions(+), 35 deletions(-) diff --git a/app/models/person.rb b/app/models/person.rb index ec2c525f6..c56adb374 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -225,7 +225,8 @@ class Person < ActiveRecord::Base :name => self.name, :avatar => self.profile.image_url(:thumb_small), :handle => self.diaspora_handle, - :url => "/people/#{self.id}" + :url => "/people/#{self.id}", + :hashtags => self.profile.tags.map{|t| "##{t.name}"} } end diff --git a/app/views/comments/_comment.html.haml b/app/views/comments/_comment.html.haml index 35d07f4ac..0809a407b 100644 --- a/app/views/comments/_comment.html.haml +++ b/app/views/comments/_comment.html.haml @@ -9,7 +9,7 @@ = person_image_link(comment.author) .content %span.from - = person_link(comment.author, :class => "author") + = person_link(comment.author, :class => "hovercardable") %span{:class => direction_for(comment.text)} = markdownify(comment.text, :youtube_maps => comment.youtube_titles) diff --git a/app/views/layouts/_header.html.haml b/app/views/layouts/_header.html.haml index 359b02e60..f5c0d084d 100644 --- a/app/views/layouts/_header.html.haml +++ b/app/views/layouts/_header.html.haml @@ -52,15 +52,16 @@ .ajax_loader = image_tag("ajax-loader.gif") - #hovercard - %img.avatar - %h4 - %a.person - #hovercard_dropdown_container + #hovercard_container + #hovercard + %img.avatar + %h4 + %a.person + #hovercard_dropdown_container - .hovercard_footer - .footer_container - Message + .hovercard_footer + .footer_container + .hashtags %ul#user_menu.dropdown %li diff --git a/app/views/people/_index.html.haml b/app/views/people/_index.html.haml index d7f36f7af..72df7e017 100644 --- a/app/views/people/_index.html.haml +++ b/app/views/people/_index.html.haml @@ -5,13 +5,9 @@ - if current_user - contact = current_user.contacts.find_by_person_id(person.id) - contact ||= Contact.new(:person => person) - - unless person == current_user.person - .right - = render 'aspect_memberships/aspect_dropdown', :contact => contact, :person => person, :hang => 'left' .content %span.from - =person_link(person) - .info - = person.profile.format_tags(person.profile.tag_string) + =person_link(person, :class => "hovercardable") + = will_paginate people, :params => {:controller => 'people', :action => 'tag_index'} diff --git a/app/views/shared/_stream_element.html.haml b/app/views/shared/_stream_element.html.haml index e65d3e039..bc075a6c9 100644 --- a/app/views/shared/_stream_element.html.haml +++ b/app/views/shared/_stream_element.html.haml @@ -18,7 +18,7 @@ .content %div.post_initial_info %span.from - = person_link(post.author, :class => 'author') + = person_link(post.author, :class => 'hovercardable') %time.time.timeago{:datetime => post.created_at, :integer => time_for_sort(post).to_i} %span.details \- diff --git a/config/assets.yml b/config/assets.yml index bf4808b75..a3a2b3037 100644 --- a/config/assets.yml +++ b/config/assets.yml @@ -36,6 +36,7 @@ javascripts: - public/javascripts/widgets/flashes.js - public/javascripts/widgets/post.js - public/javascripts/widgets/hovercard.js + - public/javascripts/widgets/notifications-badge.js - public/javascripts/view.js - public/javascripts/stream.js - public/javascripts/content-updater.js diff --git a/public/javascripts/widgets/hovercard.js b/public/javascripts/widgets/hovercard.js index 5ed15ed77..ab171a275 100644 --- a/public/javascripts/widgets/hovercard.js +++ b/public/javascripts/widgets/hovercard.js @@ -7,20 +7,22 @@ this.start = function() { self.personCache = new this.Cache(); self.dropdownCache = new this.Cache(); - + + var card = $("#hovercard"); self.hoverCard = { - tip: $("#hovercard"), + tip: $("#hovercard_container"), dropdownContainer: $("#hovercard_dropdown_container"), offset: { - left: 0, - top: 18 + left: -10, + top: 13 }, - personLink: $("#hovercard").find("a.person"), - avatar: $("#hovercard").find(".avatar"), - dropdown: $("#hovercard").find(".dropdown_list") + personLink: card.find("a.person"), + avatar: card.find(".avatar"), + dropdown: card.find(".dropdown_list"), + hashtags: card.find(".hashtags"), }; - $(document.body).delegate("a.author:not(.self)", "hover", self.handleHoverEvent); + $(document.body).delegate("a.hovercardable:not(.self)", "hover", self.handleHoverEvent); self.hoverCard.tip.hover(self.hoverCardHover, self.clearTimeout); Diaspora.widgets.subscribe("aspectDropdown/updated aspectDropdown/blurred", function(evt, personId, dropdownHtml) { @@ -67,6 +69,14 @@ self.hoverCard.personLink.text(person.name); self.hoverCard.dropdown.attr("data-person-id", person.id); + $.each(person.hashtags, function(index, hashtag) { + self.hoverCard.hashtags.append( + $("", { + href: "/tags/" + hashtag.substring(1) + }).text(hashtag) + ); + }); + self.dropdownCache.get(self.target.attr("href") + "/aspect_membership_button", function(dropdown) { self.hoverCard.dropdownContainer.html(dropdown); self.hoverCard.tip.fadeIn(140); diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass index beb82a38f..db819df67 100644 --- a/public/stylesheets/sass/application.sass +++ b/public/stylesheets/sass/application.sass @@ -3066,14 +3066,12 @@ ul.left_nav :margin :top 30px -.tags_people - .dropdown - :display none - #hovercard @include border-radius(2px) @include box-shadow(0,0,5px,#666) + :position relative + .avatar :position relative :height 70px @@ -3081,21 +3079,16 @@ ul.left_nav :margin :right 10px - :position absolute - :display none :background :color $background :padding 5px - :bottom 28px + :bottom 55px :border 1px solid #999 :width 220px - :z - :index 10 - .hovercard_footer :position absolute :bottom 0 @@ -3104,6 +3097,9 @@ ul.left_nav :color #eee :width 100% + :height 20px + :min-height 20px + :font :size smaller @@ -3112,3 +3108,20 @@ ul.left_nav .footer_container :padding 2px 5px + + .hashtags + :overflow hidden + :white-space nowrap + :text-overflow ellipsis + + a + :color #999 + :margin + :right 4px + +#hovercard_container + :padding 10px + :top 5px + :position absolute + :display none + :z-index 10