From a3f208c38031ee69e20af5cfcb86657bdfc95d27 Mon Sep 17 00:00:00 2001
From: cmrd Senya <senya@riseup.net>
Date: Mon, 18 Jul 2016 17:58:12 +0000
Subject: [PATCH] Notifications and search page backend updates

Updates introduce support for preloading contacts to Gon in order
to support client-side rendering of aspect membership dropdown box.
---
 app/controllers/notifications_controller.rb   | 10 ++--
 app/controllers/people_controller.rb          | 20 ++++----
 app/helpers/aspect_global_helper.rb           | 19 --------
 app/helpers/gon_helper.rb                     |  8 ++++
 app/models/notifications/started_sharing.rb   |  4 ++
 app/views/notifications/_notification.haml    |  8 ++--
 app/views/people/_person.html.haml            |  2 +-
 app/views/people/_relationship_action.haml    |  6 +--
 .../notifications_controller_spec.rb          |  2 +-
 spec/helpers/gon_helper_spec.rb               | 48 +++++++++++++++++++
 10 files changed, 85 insertions(+), 42 deletions(-)
 create mode 100644 app/helpers/gon_helper.rb
 create mode 100644 spec/helpers/gon_helper_spec.rb

diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb
index 2d7a5f4ef0..984a381e71 100644
--- a/app/controllers/notifications_controller.rb
+++ b/app/controllers/notifications_controller.rb
@@ -38,9 +38,6 @@ class NotificationsController < ApplicationController
 
       pager.replace(result)
     end
-    @notifications.each do |note|
-      note.note_html = render_to_string( :partial => 'notification', :locals => { :note => note } )
-    end
     @group_days = @notifications.group_by{|note| note.created_at.strftime('%Y-%m-%d')}
 
     @unread_notification_count = current_user.unread_notifications.count
@@ -54,7 +51,12 @@ class NotificationsController < ApplicationController
     respond_to do |format|
       format.html
       format.xml { render :xml => @notifications.to_xml }
-      format.json { render :json => @notifications.to_json }
+      format.json {
+        @notifications.each do |note|
+          note.note_html = render_to_string(partial: "notification", locals: {note: note, no_aspect_dropdown: true})
+        end
+        render json: @notifications.to_json
+      }
     end
 
   end
diff --git a/app/controllers/people_controller.rb b/app/controllers/people_controller.rb
index 09cc4bc0c0..5029ccc62f 100644
--- a/app/controllers/people_controller.rb
+++ b/app/controllers/people_controller.rb
@@ -3,6 +3,8 @@
 #   the COPYRIGHT file.
 
 class PeopleController < ApplicationController
+  include GonHelper
+
   before_action :authenticate_user!, except: %i(show stream hovercard)
   before_action :find_person, only: %i(show stream hovercard)
 
@@ -168,16 +170,14 @@ class PeopleController < ApplicationController
   end
 
   def hashes_for_people(people, aspects)
-    ids = people.map{|p| p.id}
-    contacts = {}
-    Contact.unscoped.where(:user_id => current_user.id, :person_id => ids).each do |contact|
-      contacts[contact.person_id] = contact
-    end
-
-    people.map{|p|
-      {:person => p,
-        :contact => contacts[p.id],
-        :aspects => aspects}
+    people.map {|person|
+      {
+        person:  person,
+        contact: current_user.contact_for(person) || Contact.new(person: person),
+        aspects: aspects
+      }.tap {|hash|
+        gon_load_contact(hash[:contact])
+      }
     }
   end
 
diff --git a/app/helpers/aspect_global_helper.rb b/app/helpers/aspect_global_helper.rb
index a35ce7fd22..421215b7d2 100644
--- a/app/helpers/aspect_global_helper.rb
+++ b/app/helpers/aspect_global_helper.rb
@@ -3,25 +3,6 @@
 #   the COPYRIGHT file.
 
 module AspectGlobalHelper
-  def aspect_membership_dropdown(contact, person, hang)
-    aspect_membership_ids = {}
-
-    selected_aspects = all_aspects.select{|aspect| contact.in_aspect?(aspect)}
-    selected_aspects.each do |a|
-      record = a.aspect_memberships.find { |am| am.contact_id == contact.id }
-      aspect_membership_ids[a.id] = record.id
-    end
-
-    button_class = selected_aspects.size > 0 ? "btn-success" : "btn-default"
-
-    render "aspect_memberships/aspect_membership_dropdown",
-      :selected_aspects => selected_aspects,
-      :aspect_membership_ids => aspect_membership_ids,
-      :person => person,
-      :hang => hang,
-      :button_class => button_class
-  end
-
   def aspect_options_for_select(aspects)
     options = {}
     aspects.each do |aspect|
diff --git a/app/helpers/gon_helper.rb b/app/helpers/gon_helper.rb
new file mode 100644
index 0000000000..f4bb9689d0
--- /dev/null
+++ b/app/helpers/gon_helper.rb
@@ -0,0 +1,8 @@
+module GonHelper
+  def gon_load_contact(contact)
+    Gon.preloads[:contacts] ||= []
+    if Gon.preloads[:contacts].none? {|stored_contact| stored_contact[:person][:id] == contact.person_id }
+      Gon.preloads[:contacts] << ContactPresenter.new(contact, current_user).full_hash_with_person
+    end
+  end
+end
diff --git a/app/models/notifications/started_sharing.rb b/app/models/notifications/started_sharing.rb
index c80ad88a97..bae3a3a1ea 100644
--- a/app/models/notifications/started_sharing.rb
+++ b/app/models/notifications/started_sharing.rb
@@ -12,5 +12,9 @@ module Notifications
       sender = contact.person
       create_notification(contact.user_id, sender, sender).email_the_user(sender, sender)
     end
+
+    def contact
+      recipient.contact_for(target)
+    end
   end
 end
diff --git a/app/views/notifications/_notification.haml b/app/views/notifications/_notification.haml
index 7a76decd92..0302216467 100644
--- a/app/views/notifications/_notification.haml
+++ b/app/views/notifications/_notification.haml
@@ -1,9 +1,11 @@
 .media.stream_element{:data=>{:guid => note.id, :type => (Notification.types.key(note.type) || '') }, :class => (note.unread ? 'unread' : 'read')}
   .unread-toggle.pull-right
     %i.entypo-eye{title: (note.unread ? t("notifications.index.mark_read") : t("notifications.index.mark_unread"))}
-  - if note.type == "Notifications::StartedSharing" && contact = current_user.contact_for(note.target)
-    .pull-right
-      = aspect_membership_dropdown(contact, note.target, "right")
+  - if note.type == "Notifications::StartedSharing" && (!defined?(no_aspect_dropdown) || !no_aspect_dropdown)
+    - if note.target.present?
+      - gon_load_contact(note.contact)
+      .pull-right
+        .aspect_membership_dropdown.placeholder{data: {person_id: note.target.id}}
 
   .media-object.pull-left
     = person_image_link note.actors.first, :size => :thumb_small, :class => 'hovercardable'
diff --git a/app/views/people/_person.html.haml b/app/views/people/_person.html.haml
index 3c008b339b..48e543d2da 100644
--- a/app/views/people/_person.html.haml
+++ b/app/views/people/_person.html.haml
@@ -1,7 +1,7 @@
 .media.stream_element{id: person.id}
   .pull-right
     = render partial: 'people/relationship_action',
-             locals: { person: person, contact: contact,
+             locals: { person: person,
                           :current_user => current_user }
   .media-object.pull-left
     = person_image_link(person, size: :thumb_small)
diff --git a/app/views/people/_relationship_action.haml b/app/views/people/_relationship_action.haml
index 4685760603..01593dec7f 100644
--- a/app/views/people/_relationship_action.haml
+++ b/app/views/people/_relationship_action.haml
@@ -1,7 +1,5 @@
 - unless person == current_user.person
-  - contact = current_user.contacts.find_by_person_id(person.id)
-  - contact ||= Contact.new(:person => person)
-  .aspect_membership_dropdown
+  .aspect_membership_dropdown.placeholder{data: {person_id: person.id}}
 -else
   %span.thats_you
-    = t('people.person.thats_you')
+    = t("people.person.thats_you")
diff --git a/spec/controllers/notifications_controller_spec.rb b/spec/controllers/notifications_controller_spec.rb
index bc98c798b0..5bb68ccb01 100644
--- a/spec/controllers/notifications_controller_spec.rb
+++ b/spec/controllers/notifications_controller_spec.rb
@@ -111,7 +111,7 @@ describe NotificationsController, :type => :controller do
         eve.share_with(alice.person, eve.aspects.first)
         get :index, "per_page" => 5
 
-        expect(Nokogiri(response.body).css(".aspect_membership")).not_to be_empty
+        expect(Nokogiri(response.body).css(".aspect_membership_dropdown")).not_to be_empty
       end
 
       it 'succeeds on mobile' do
diff --git a/spec/helpers/gon_helper_spec.rb b/spec/helpers/gon_helper_spec.rb
new file mode 100644
index 0000000000..c449c91260
--- /dev/null
+++ b/spec/helpers/gon_helper_spec.rb
@@ -0,0 +1,48 @@
+require "spec_helper"
+
+describe GonHelper, type: :helper do
+  include_context :gon
+
+  describe "#gon_load_contact" do
+    let(:contact) { FactoryGirl.build(:contact) }
+    let(:current_user) { contact.user }
+    let(:another_contact) { FactoryGirl.build(:contact, user: current_user) }
+
+    before do
+      RequestStore.store[:gon] = Gon::Request.new(controller.request.env)
+      Gon.preloads = {}
+    end
+
+    it "calls appropriate presenter" do
+      expect_any_instance_of(ContactPresenter).to receive(:full_hash_with_person)
+      gon_load_contact(contact)
+    end
+
+    shared_examples "contacts loading" do
+      it "loads contacts to gon" do
+        gon_load_contact(contact)
+        gon_load_contact(another_contact)
+        expect(Gon.preloads[:contacts].count).to eq(2)
+      end
+
+      it "avoids duplicates" do
+        gon_load_contact(contact)
+        gon_load_contact(contact)
+        expect(Gon.preloads[:contacts].count).to eq(1)
+      end
+    end
+
+    context "with non-persisted contacts" do
+      include_examples "contacts loading"
+    end
+
+    context "with persisted contacts" do
+      before do
+        contact.save!
+        another_contact.save!
+      end
+
+      include_examples "contacts loading"
+    end
+  end
+end
-- 
GitLab