Skip to content

Commit 4fa1176

Browse files
DrewProebstelpowersurge360DrewProebstelCfa
authored
FYST-2312/add-intercom-to-pya (#236)
* Early work on integrating intercom * wip * wip * intercom styling * update to use fyst * lint * wip * wip * working * no jwt in tests and lint * pr review updates * mock doppler app id --------- Co-authored-by: Kevin Diale <powersurge360@gmail.com> Co-authored-by: Drew Proebstel <dproebstel@codeforamerica.org>
1 parent 9010493 commit 4fa1176

13 files changed

Lines changed: 212 additions & 0 deletions

File tree

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ gem "devise"
1313
gem "mailgun-ruby"
1414
gem "aws-sdk-s3"
1515
gem "csv", "~> 3.3"
16+
gem "jwt"
1617

1718
# Use the Puma web server [https://github.com/puma/puma]
1819
gem "puma", ">= 5.0"
@@ -83,3 +84,5 @@ gem "sentry-ruby"
8384
gem "sentry-rails"
8485

8586
gem "openssl", ">= 3.3.1"
87+
88+
gem "intercom-rails", "~> 1.0"

Gemfile.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ GEM
233233
actionpack (>= 6.0.0)
234234
activesupport (>= 6.0.0)
235235
railties (>= 6.0.0)
236+
intercom-rails (1.0.6)
237+
activesupport (> 4.0)
238+
jwt (~> 2.0)
236239
io-console (0.8.1)
237240
irb (1.15.2)
238241
pp (>= 0.6.0)
@@ -588,7 +591,9 @@ DEPENDENCIES
588591
http_accept_language
589592
i18n-tasks (~> 1.0)
590593
importmap-rails
594+
intercom-rails (~> 1.0)
591595
jbuilder
596+
jwt
592597
mailgun-ruby
593598
openssl (>= 3.3.1)
594599
pg
Lines changed: 1 addition & 0 deletions
Loading

app/assets/stylesheets/application.scss

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,43 @@ ol.with-bullets {
299299
}
300300
}
301301

302+
.state-file .button--green {
303+
background: $color-green-money;
304+
border-color: $color-green-money;
305+
color: $color-black;
306+
307+
&:active {
308+
background-color: $color-green-money;
309+
border-color: $color-green-money;
310+
}
311+
312+
&:hover {
313+
background-color: darken($color-green-money, 5%);
314+
border-color: darken($color-green-money, 5%);
315+
}
316+
}
317+
318+
.intercom-button-container {
319+
position: fixed;
320+
bottom: 2rem;
321+
right: 2rem;
322+
z-index: 1000;
323+
}
324+
325+
.button--intercom {
326+
display: flex;
327+
align-items: center;
328+
max-width: 21rem;
329+
border-radius: 0.5rem;
330+
box-shadow: 0 0.3rem 0.6rem rgba(0, 0, 0, 0.3);
331+
}
332+
333+
.intercom-button-icon {
334+
width: 1.6rem;
335+
height: 1.6rem;
336+
margin-right: 0.6rem;
337+
}
338+
302339
.contact-preference-buttons {
303340
display: flex;
304341
flex-wrap: wrap;

app/services/intercom_jwt.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
require "jwt"
2+
3+
class IntercomJwt
4+
ALGORITHM = "HS256".freeze
5+
6+
def self.for(intake)
7+
return nil unless intake
8+
9+
payload = {
10+
user_id: intake.id.to_s,
11+
exp: 30.minutes.from_now.to_i
12+
}.compact
13+
14+
secret = ENV["INTERCOM_SECURE_MODE_SECRET_KEY"]
15+
JWT.encode(payload, secret, ALGORITHM)
16+
end
17+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
window.intercomSettings = window.intercomSettings || {};
3+
window.intercomSettings.app_id = "<%= ENV.fetch('INTERCOM_APP_ID') %>";
4+
<% if current_state_file_archived_intake.present? %>
5+
window.intercomSettings.intercom_user_jwt = "<%= j IntercomJwt.for(current_state_file_archived_intake) %>";
6+
<% end %>
7+
</script>

app/views/layouts/application.html.erb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
2222
<%= javascript_importmap_tags "application" %>
2323
<%= Sentry.get_trace_propagation_meta.html_safe %>
24+
25+
<%= render "layouts/intercom_jwt" %>
26+
<%= intercom_script_tag %>
2427
</head>
2528

2629
<body <% if content_for?(:body_class) %>class="<%= yield :body_class %>"<% end %>>
@@ -40,6 +43,7 @@
4043
</footer>
4144
</div>
4245
</div>
46+
<%= render "shared/intercom_email_button" %>
4347
</div>
4448
</body>
4549
</html>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<div class="intercom-button-container">
2+
<button
3+
type="button"
4+
class="open-intercom button button--small button--green button--intercom"
5+
onclick="Intercom('showNewMessage');">
6+
<%= image_tag("intercom-chat.svg", "aria-hidden": "true", class: "intercom-button-icon") %>
7+
<%= I18n.t("general.intercom_chat_with_us") %>
8+
</button>
9+
</div>

config/initializers/intercom.rb

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
IntercomRails.config do |config|
2+
# == Intercom app_id
3+
#
4+
config.app_id = ENV["INTERCOM_APP_ID"] || "rtcpj4hf"
5+
6+
# == Intercom session_duration
7+
#
8+
# config.session_duration = 300000
9+
# == Intercom secret key
10+
# This is required to enable Identity Verification, you can find it on your Setup
11+
# guide in the "Identity Verification" step.
12+
#
13+
# config.api_secret = "..."
14+
15+
# == Enabled Environments
16+
# Which environments is auto inclusion of the Javascript enabled for
17+
#
18+
config.enabled_environments = ["development", "production"]
19+
20+
# == Current user method/variable
21+
# The method/variable that contains the logged in user in your controllers.
22+
# If it is `current_user` or `@user`, then you can ignore this
23+
24+
# config.user.current = proc {}
25+
26+
# == Include for logged out Users
27+
# If set to true, include the Intercom messenger on all pages, regardless of whether
28+
# The user model class (set below) is present.
29+
config.include_for_logged_out_users = true
30+
31+
# == User model class
32+
# The class which defines your user model
33+
#
34+
# config.user.model = Proc.new { User }
35+
36+
# == Lead/custom attributes for non-signed up users
37+
# Pass additional attributes to for potential leads or
38+
# non-signed up users as an an array.
39+
# Any attribute contained in config.user.lead_attributes can be used
40+
# as custom attribute in the application.
41+
# config.user.lead_attributes = %w(ref_data utm_source)
42+
43+
# == Exclude users
44+
# A Proc that given a user returns true if the user should be excluded
45+
# from imports and Javascript inclusion, false otherwise.
46+
#
47+
# config.user.exclude_if = Proc.new { |user| user.deleted? }
48+
49+
# == User Custom Data
50+
# A hash of additional data you wish to send about your users.
51+
# You can provide either a method name which will be sent to the current
52+
# user object, or a Proc which will be passed the current user.
53+
#
54+
# config.user.custom_data = {
55+
# :plan => Proc.new { |current_user| current_user.plan.name },
56+
# :favorite_color => :favorite_color
57+
# }
58+
#
59+
config.user.custom_data = {}
60+
#
61+
# == Current company method/variable
62+
# The method/variable that contains the current company for the current user,
63+
# in your controllers. 'Companies' are generic groupings of users, so this
64+
# could be a company, app or group.
65+
#
66+
# config.company.current = Proc.new { current_company }
67+
#
68+
# Or if you are using devise you can just use the following config
69+
#
70+
# config.company.current = Proc.new { current_user.company }
71+
72+
# == Exclude company
73+
# A Proc that given a company returns true if the company should be excluded
74+
# from imports and Javascript inclusion, false otherwise.
75+
#
76+
# config.company.exclude_if = Proc.new { |app| app.subdomain == 'demo' }
77+
78+
# == Company Custom Data
79+
# A hash of additional data you wish to send about a company.
80+
# This works the same as User custom data above.
81+
#
82+
# config.company.custom_data = {
83+
# :number_of_messages => Proc.new { |app| app.messages.count },
84+
# :is_interesting => :is_interesting?
85+
# }
86+
87+
# == Company Plan name
88+
# This is the name of the plan a company is currently paying (or not paying) for.
89+
# e.g. Messaging, Free, Pro, etc.
90+
#
91+
# config.company.plan = Proc.new { |current_company| current_company.plan.name }
92+
93+
# == Company Monthly Spend
94+
# This is the amount the company spends each month on your app. If your company
95+
# has a plan, it will set the 'total value' of that plan appropriately.
96+
#
97+
# config.company.monthly_spend = Proc.new { |current_company| current_company.plan.price }
98+
# config.company.monthly_spend = Proc.new { |current_company| (current_company.plan.price - current_company.subscription.discount) }
99+
100+
# == Custom Style
101+
# By default, Intercom will add a button that opens the messenger to
102+
# the page. If you'd like to use your own link to open the messenger,
103+
# uncomment this line and clicks on any element with id 'Intercom' will
104+
# open the messenger.
105+
#
106+
config.inbox.style = :custom
107+
#
108+
# If you'd like to use your own link activator CSS selector
109+
# uncomment this line and clicks on any element that matches the query will
110+
# open the messenger
111+
config.inbox.custom_activator = ".intercom"
112+
#
113+
# If you'd like to hide default launcher button uncomment this line
114+
config.hide_default_launcher = true
115+
#
116+
# If you need to route your Messenger requests through a different endpoint than the default, uncomment the below line. Generally speaking, this is not needed.
117+
# config.api_base = "https://#{config.app_id}.intercom-messenger.com"
118+
#
119+
end

config/locales/en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ en:
1616
messages:
1717
blank: Can't be blank.
1818
general:
19+
intercom_chat_with_us: Chat with us
1920
continue: Continue
2021
faq: FAQ
2122
return_home: Return to homepage

0 commit comments

Comments
 (0)