Add ahoy + welcome notification tracking (#8758) [deploy]

* Set up ahoy gem, add ahoy-js

* Import ahoy.js into base.js.erb

* Add trackNotification function to notifications/index.html.erb

* Add sanitized_broadcast_id helper

* Use sanitized_broadcast_id in broadcast partial

* Add specs around tracking welcome notifications

* Remove optional fields from Ahoy::Visit migration + table

* Fiddle with trait to see if it helps with CI failures

* Disable geocode tracking in ahoy

* Stub out SiteConfig in notifications page spec
This commit is contained in:
Vaidehi Joshi 2020-06-18 13:40:14 -07:00 committed by GitHub
parent 0e765b47de
commit 904f0dac78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 168 additions and 2 deletions

View file

@ -16,6 +16,7 @@ gem "acts_as_follower", github: "thepracticaldev/acts_as_follower", branch: "mas
gem "addressable", "~> 2.7" # A replacement for the URI implementation that is part of Ruby's standard library
gem "administrate", "~> 0.13" # A Rails engine that helps you put together a super-flexible admin dashboard
gem "ahoy_email", "~> 1.1" # Email analytics for Rails
gem "ahoy_matey", "~> 3.0" # Tracking analytics for Rails
gem "ancestry", "~> 3.0" # Ancestry allows the records of a ActiveRecord model to be organized in a tree structure
gem "autoprefixer-rails", "~> 9.7" # Parse CSS and add vendor prefixes to CSS rules using values from the Can I Use website
gem "aws-sdk-lambda", "~> 1.42" # Official AWS Ruby gem for AWS Lambda

View file

@ -99,6 +99,11 @@ GEM
addressable (>= 2.3.2)
nokogiri
safely_block (>= 0.1.1)
ahoy_matey (3.0.4)
activesupport (>= 5)
device_detector
geocoder (>= 1.4.5)
safely_block (>= 0.2.1)
amazing_print (1.2.0)
ancestry (3.0.7)
activerecord (>= 3.2.0)
@ -235,6 +240,7 @@ GEM
unicode_plot (>= 0.0.4, < 1.0.0)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
device_detector (1.0.3)
devise (4.7.2)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
@ -369,6 +375,7 @@ GEM
formatador (0.2.5)
front_matter_parser (0.2.1)
gemoji (4.0.0.rc2)
geocoder (1.6.3)
get_process_mem (0.2.5)
ffi (~> 1.0)
gibbon (3.3.4)
@ -876,6 +883,7 @@ DEPENDENCIES
addressable (~> 2.7)
administrate (~> 0.13)
ahoy_email (~> 1.1)
ahoy_matey (~> 3.0)
amazing_print (~> 1.2)
ancestry (~> 3.0)
approvals (~> 0.0)

View file

@ -4,6 +4,7 @@
//= require initializePage
//= require utilities/getImageForLink
//= require honeybadger-js/dist/honeybadger.js
//= require ahoy.js/dist/ahoy.js
//= require serviceworker-companion
var instantClick

View file

@ -8,4 +8,8 @@ module BroadcastsHelper
"crayons-banner crayons-banner--#{broadcast.banner_style}"
end
end
def sanitized_broadcast_id(broadcast_title)
broadcast_title.downcase.delete(":").tr(" ", "_")
end
end

8
app/models/ahoy/event.rb Normal file
View file

@ -0,0 +1,8 @@
class Ahoy::Event < ApplicationRecord
include Ahoy::QueryMethods
self.table_name = "ahoy_events"
belongs_to :visit
belongs_to :user, optional: true
end

6
app/models/ahoy/visit.rb Normal file
View file

@ -0,0 +1,6 @@
class Ahoy::Visit < ApplicationRecord
self.table_name = "ahoy_visits"
has_many :events, class_name: "Ahoy::Event"
belongs_to :user, optional: true
end

View file

@ -6,7 +6,7 @@
<img src="<%= ProfileImage.new(user).get %>" alt="link to <%= json_data["user"]["username"] %>'s profile">
</div>
</a>
<div class="content notification-content broadcast-content">
<div class="content notification-content broadcast-content" id="<%= sanitized_broadcast_id(json_data['broadcast']['title']) %>">
<%= json_data["broadcast"]["processed_html"].html_safe %>
<% if notification.json_data['broadcast']['type_of'] == "Welcome" %>

View file

@ -17,6 +17,20 @@
<meta name="twitter:description" content="Notifications inbox for <%= community_name %>">
<% end %>
<script>
/**
* A script that tracks clicks for welcome notifications.
* The notification must have `trackNotification` as its onclick event
* in order for this to be triggered.
*
* @param {Event} The click event on the notification.
*/
function trackNotification(e) {
let title = e.target.parentElement.id
ahoy.track("Clicked Welcome Notification", { title });
}
</script>
<% if user_signed_in? %>
<div class="home" id="notifications-container">
<div class="side-bar">

View file

@ -0,0 +1,8 @@
class Ahoy::Store < Ahoy::DatabaseStore
end
# set to true for JavaScript tracking
Ahoy.api = true
# set to false to disable geocode tracking
Ahoy.geocode = false

View file

@ -0,0 +1,28 @@
class CreateAhoyVisitsAndEvents < ActiveRecord::Migration[6.0]
# This migration includes a subset of the available fields provided by ahoy.
# For the full list of options, please see https://github.com/ankane/ahoy.
def change
create_table :ahoy_visits do |t|
t.string :visit_token
t.string :visitor_token
t.references :user
t.timestamp :started_at
end
add_index :ahoy_visits, [:visit_token], unique: true
create_table :ahoy_events do |t|
t.references :visit
t.references :user
t.string :name
t.jsonb :properties
t.timestamp :time
end
add_index :ahoy_events, [:name, :time]
add_index :ahoy_events, :properties, using: :gin, opclass: :jsonb_path_ops
end
end

View file

@ -10,11 +10,23 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_06_17_014509) do
ActiveRecord::Schema.define(version: 2020_06_17_183058) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "ahoy_events", force: :cascade do |t|
t.string "name"
t.jsonb "properties"
t.datetime "time"
t.bigint "user_id"
t.bigint "visit_id"
t.index ["name", "time"], name: "index_ahoy_events_on_name_and_time"
t.index ["properties"], name: "index_ahoy_events_on_properties", opclass: :jsonb_path_ops, using: :gin
t.index ["user_id"], name: "index_ahoy_events_on_user_id"
t.index ["visit_id"], name: "index_ahoy_events_on_visit_id"
end
create_table "ahoy_messages", id: :serial, force: :cascade do |t|
t.datetime "clicked_at"
t.text "content"
@ -37,6 +49,15 @@ ActiveRecord::Schema.define(version: 2020_06_17_014509) do
t.index ["user_id", "user_type"], name: "index_ahoy_messages_on_user_id_and_user_type"
end
create_table "ahoy_visits", force: :cascade do |t|
t.datetime "started_at"
t.bigint "user_id"
t.string "visit_token"
t.string "visitor_token"
t.index ["user_id"], name: "index_ahoy_visits_on_user_id"
t.index ["visit_token"], name: "index_ahoy_visits_on_visit_token", unique: true
end
create_table "api_secrets", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "description", null: false

View file

@ -128,6 +128,7 @@
"@babel/preset-env": "^7.9.6",
"@github/clipboard-copy-element": "^1.1.2",
"@rails/webpacker": "5.1.1",
"ahoy.js": "^0.3.6",
"babel-preset-preact": "^2.0.0",
"chart.js": "^2.9.3",
"clipboard-polyfill": "^2.8.6",

View file

@ -68,5 +68,9 @@ FactoryBot.define do
type_of { "Announcement" }
processed_html { "<p>Hello, World!</p>" }
end
trait :with_tracking do
processed_html { "Sloan here again! 👋 DEV is a friendly community. Why not introduce yourself by leaving a comment in <a href='/welcome' onclick='trackNotification(event)'>the welcome thread</a>!" }
end
end
end

View file

@ -85,4 +85,54 @@ RSpec.describe "Notifications page", type: :system, js: true do
validate_reply(comment.id)
end
end
context "with welcome notifications" do
let(:mascot_account) { create(:user) }
before do
allow(Notification).to receive(:send_welcome_notification).and_call_original
allow(User).to receive(:mascot_account).and_return(mascot_account)
allow(SiteConfig).to receive(:staff_user_id).and_return(mascot_account.id)
alex.update!(created_at: 1.day.ago)
end
context "without tracking enabled" do
before do
create(:welcome_broadcast)
Broadcasts::WelcomeNotification::Generator.call(alex.id)
sidekiq_perform_enqueued_jobs
end
it "renders the notification" do
visit "/notifications"
expect(page).to have_css(".broadcast-content")
expect(page).to have_css("#welcome_notification_welcome_thread")
end
it "does not track events" do
visit "/notifications"
click_link("the welcome thread")
expect(page).to have_current_path("/welcome")
expect(Ahoy::Event.count).to eq(0)
end
end
context "with tracking enabled" do
before do
create(:welcome_broadcast, :with_tracking)
Broadcasts::WelcomeNotification::Generator.call(alex.id)
sidekiq_perform_enqueued_jobs
end
it "tracks events" do
visit "/notifications"
click_link("the welcome thread")
expect(page).to have_current_path("/welcome")
expect(Ahoy::Event.count).to eq(1)
end
end
end
end

BIN
vendor/cache/ahoy_matey-3.0.4.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/device_detector-1.0.3.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/geocoder-1.6.3.gem vendored Normal file

Binary file not shown.

View file

@ -2550,6 +2550,13 @@ aggregate-error@^3.0.0:
clean-stack "^2.0.0"
indent-string "^4.0.0"
ahoy.js@^0.3.6:
version "0.3.6"
resolved "https://registry.yarnpkg.com/ahoy.js/-/ahoy.js-0.3.6.tgz#ca27cb816a1de1f537616e4ea3213950259cd821"
integrity sha512-9X1g5xJioXk+AY5ti1LIlHExZ+8SjhuQOzAAXkNAZrZ6lmp88lFW7IeHADfii1/p5Cf3azg9hF2dk8Zsu4OfWA==
dependencies:
object-to-formdata ">=3.0.0"
airbnb-js-shims@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/airbnb-js-shims/-/airbnb-js-shims-2.2.1.tgz#db481102d682b98ed1daa4c5baa697a05ce5c040"
@ -10057,6 +10064,11 @@ object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object-to-formdata@>=3.0.0:
version "3.0.9"
resolved "https://registry.yarnpkg.com/object-to-formdata/-/object-to-formdata-3.0.9.tgz#40e3314522345789259738811c0222b7d6e556e9"
integrity sha512-1JDHRFSpk6wzhPBAAqdVncdvbjZ+bjB0tioruNdKn8UyudBBXiBxRa7PJyvYqp4ioEKX98dEOX9Fw1RxA+vLzg==
object-visit@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"