From 96ead235ffb16315044d20b5eeda3f77394ac749 Mon Sep 17 00:00:00 2001 From: Evan Morikawa Date: Thu, 14 May 2015 14:58:42 -0700 Subject: [PATCH] feat(salesforce): add Lead and Contact syncing Summary: associating salesforce contacts and leads with Nylas contacts adding fetcher form salesforce shows conencted leads and contacts auto associates acount fixing salesforce forms Salesforce composers are styled fix opportunity association with account on object creation fix specs Test Plan: edgehill --test Reviewers: bengotow Reviewed By: bengotow Differential Revision: https://review.inboxapp.com/D1507 --- .../lib/fullcontact-store.coffee | 57 +++++++++++-------- .../lib/sidebar-fullcontact-details.cjsx | 9 ++- .../lib/sidebar-fullcontact.cjsx | 10 +++- spec-inbox/components/form-builder-spec.cjsx | 2 +- src/components/generated-form.cjsx | 2 +- src/flux/models/contact.coffee | 21 +++++++ src/flux/stores/contact-store.coffee | 3 +- static/inputs.less | 15 ++++- 8 files changed, 87 insertions(+), 32 deletions(-) diff --git a/internal_packages/sidebar-fullcontact/lib/fullcontact-store.coffee b/internal_packages/sidebar-fullcontact/lib/fullcontact-store.coffee index fc8bc3eb8..f3cac07b4 100644 --- a/internal_packages/sidebar-fullcontact/lib/fullcontact-store.coffee +++ b/internal_packages/sidebar-fullcontact/lib/fullcontact-store.coffee @@ -1,37 +1,41 @@ _ = require 'underscore-plus' Reflux = require 'reflux' request = require 'request' -{FocusedContactsStore} = require 'inbox-exports' +{Contact, ContactStore, DatabaseStore, FocusedContactsStore} = require 'inbox-exports' module.exports = FullContactStore = Reflux.createStore init: -> - @_fetchAPIData = _.debounce(_.bind(@__fetchAPIData, @), 50) - @_cachedContactData = {} - @listenTo FocusedContactsStore, @_onFocusedContacts + @_loadContactDataFromAPI = _.debounce(_.bind(@__loadContactDataFromAPI, @), 50) + # @_cachedContactData = {} + @_resolvedFocusedContact = null + @_loadFocusedContact = _.debounce(_.bind(@_loadFocusedContact, @), 20) + @_loadFocusedContact() - sortedContacts: -> FocusedContactsStore.sortedContacts() - focusedContact: -> FocusedContactsStore.focusedContact() + @listenTo ContactStore, @_loadFocusedContact + @listenTo FocusedContactsStore, @_loadFocusedContact - fullContactCache: -> - emails = {} - contacts = FocusedContactsStore.sortedContacts() - emails[contact.email] = contact for contact in contacts - fullContactCache = {} - _.each @_cachedContactData, (fullContactData, email) -> - if email of emails then fullContactCache[email] = fullContactData - return fullContactCache + focusedContact: -> @_resolvedFocusedContact - _onFocusedContacts: -> - contact = FocusedContactsStore.focusedContact() ? {} - if not @_cachedContactData[contact.email] - @_fetchAPIData(contact.email) - @trigger() + # We need to pull fresh from the database so when we update data in the + # for the contact, we get it anew. + _loadFocusedContact: -> + contact = FocusedContactsStore.focusedContact() + if contact + @_resolvedFocusedContact = contact + DatabaseStore.findBy(Contact, email: contact.email).then (contact) => + @_resolvedFocusedContact = contact + if contact and not contact.thirdPartyData?["FullContact"]? + @_loadContactDataFromAPI(contact) + @trigger() + else + @_resolvedFocusedContact = null + @trigger() - __fetchAPIData: (email="") -> + __loadContactDataFromAPI: (contact) -> # Swap the url's to see real data - email = email.toLowerCase().trim() + email = contact.email.toLowerCase().trim() return if email.length is 0 url = "https://api.fullcontact.com/v2/person.json?email=#{email}&apiKey=eadcbaf0286562a" request url, (err, resp, data) => @@ -39,5 +43,12 @@ FullContactStore = Reflux.createStore return {} if resp.statusCode != 200 try data = JSON.parse data - @_cachedContactData[email] = data - @trigger(@) + contact = @_mergeDataIntoContact(contact, data) + DatabaseStore.persistModel(contact).then => @trigger(@) + + _mergeDataIntoContact: (contact, data) -> + contact.title = data.organizations?[0]?["title"] + contact.company = data.organizations?[0]?["name"] + contact.thirdPartyData ?= {} + contact.thirdPartyData["FullContact"] = data + return contact diff --git a/internal_packages/sidebar-fullcontact/lib/sidebar-fullcontact-details.cjsx b/internal_packages/sidebar-fullcontact/lib/sidebar-fullcontact-details.cjsx index 9faee5adc..84f49149f 100644 --- a/internal_packages/sidebar-fullcontact/lib/sidebar-fullcontact-details.cjsx +++ b/internal_packages/sidebar-fullcontact/lib/sidebar-fullcontact-details.cjsx @@ -16,6 +16,7 @@ class SidebarFullContactDetails extends React.Component
{@_profilePhoto()}

{@_name()}

+
{@_email()}
@@ -81,7 +82,13 @@ class SidebarFullContactDetails extends React.Component @_title().length > 0 or @_company().length > 0 _name: => - (@props.fullContact.contactInfo?.fullName) ? @props.contact?.name + (@props.fullContact.contactInfo?.fullName) ? @props.contact?.name ? "" + + _email: => + email = @props.contact.email ? "" + if @_name().toLowerCase().trim() isnt email.toLowerCase().trim() + return email + else return "" _title: => org = @_primaryOrg() diff --git a/internal_packages/sidebar-fullcontact/lib/sidebar-fullcontact.cjsx b/internal_packages/sidebar-fullcontact/lib/sidebar-fullcontact.cjsx index 80981bf14..6951f8e9a 100644 --- a/internal_packages/sidebar-fullcontact/lib/sidebar-fullcontact.cjsx +++ b/internal_packages/sidebar-fullcontact/lib/sidebar-fullcontact.cjsx @@ -2,6 +2,8 @@ _ = require 'underscore-plus' React = require "react" FullContactStore = require "./fullcontact-store" +{InjectedComponentSet} = require 'ui-components' + SidebarFullContactDetails = require "./sidebar-fullcontact-details" class SidebarFullContact extends React.Component @@ -10,6 +12,7 @@ class SidebarFullContact extends React.Component order: 1 maxWidth: 300 minWidth: 200 + flexShrink: 0 constructor: (@props) -> @state = @_getStateFromStores() @@ -24,11 +27,13 @@ class SidebarFullContact extends React.Component
+
_fullContact: => - if @state.focusedContact?.email - return @state.fullContactCache[@state.focusedContact.email] ? {} + if @state.focusedContact?.thirdPartyData + return @state.focusedContact?.thirdPartyData["FullContact"] ? {} else return {} @@ -36,7 +41,6 @@ class SidebarFullContact extends React.Component @setState(@_getStateFromStores()) _getStateFromStores: => - fullContactCache: FullContactStore.fullContactCache() focusedContact: FullContactStore.focusedContact() diff --git a/spec-inbox/components/form-builder-spec.cjsx b/spec-inbox/components/form-builder-spec.cjsx index 422214fc7..fe9f3d9f2 100644 --- a/spec-inbox/components/form-builder-spec.cjsx +++ b/spec-inbox/components/form-builder-spec.cjsx @@ -10,7 +10,7 @@ fixtureModule = 'internal_packages/salesforce' Adapter = require path.join('../../', fixtureModule, 'lib/salesforce-schema-adapter.coffee') fpath = path.join(fixtureModule, 'spec/fixtures/opportunity-layouts.json') rawData = JSON.parse(fs.readFileSync(fpath, 'utf-8')) -testData = Adapter.convertLayout("opportunity", rawData) +testData = Adapter.convertFullEditLayout("opportunity", rawData) describe "Form Builder", -> beforeEach -> diff --git a/src/components/generated-form.cjsx b/src/components/generated-form.cjsx index b6036e31a..015316e70 100644 --- a/src/components/generated-form.cjsx +++ b/src/components/generated-form.cjsx @@ -232,7 +232,7 @@ class GeneratedFieldset extends React.Component for item, i in items itemsWithSpacers.push(item) - if i isnt items.length - 1 or items.length is 1 + if i isnt items.length - 1 itemsWithSpacers.push(spacer: true)
"#{(@name ? "").toLowerCase().trim()} #{@email.toLowerCase().trim()}" diff --git a/src/flux/stores/contact-store.coffee b/src/flux/stores/contact-store.coffee index cf5bf2334..005b7bee3 100644 --- a/src/flux/stores/contact-store.coffee +++ b/src/flux/stores/contact-store.coffee @@ -81,7 +81,7 @@ class ContactStore matches - _refreshCache: => + __refreshCache: => new Promise (resolve, reject) => DatabaseStore.findAll(Contact) .then (contacts=[]) => @@ -89,6 +89,7 @@ class ContactStore @trigger() resolve() .catch(reject) + _refreshCache: _.debounce(ContactStore::__refreshCache, 20) _onDatabaseChanged: (change) => return unless change?.objectClass is Contact.name diff --git a/static/inputs.less b/static/inputs.less index 2b6847271..6e4e8df32 100644 --- a/static/inputs.less +++ b/static/inputs.less @@ -2,7 +2,18 @@ @import "ui-mixins"; input[type="text"], -input[type="email"] { +input[type="email"], +input[type="date"], +input[type="datetime"], +input[type="datetime-local"], +input[type="month"], +input[type="number"], +input[type="password"], +input[type="range"], +input[type="search"], +input[type="tel"], +input[type="time"], +input[type="url"] { width: 100%; padding-left: @padding-xs-horizontal; padding-right: @padding-xs-horizontal; @@ -20,4 +31,4 @@ input[type="email"]:focus, { &.input-bordered { box-shadow: 0 0 3px @accent-primary; } -} \ No newline at end of file +}