mirror of
https://github.com/plausible/analytics.git
synced 2024-12-22 17:11:36 +03:00
Use sessionStorage for "dashboard first launch" banner tracking (#3892)
* Use sessionStorage for offer e-mail report banner tracking Keeping it within the cookie is problematic, as the banners don't expire and overflow the cookie with data when enough new sites are added. Ref https://github.com/plausible/analytics/issues/3762 * Update changelog * Extract a component * Make is_dbip evaluate to quoted boolean
This commit is contained in:
parent
7523abe93e
commit
edf70d14b6
@ -57,6 +57,7 @@ All notable changes to this project will be documented in this file.
|
||||
- Validate metric isn't queried multiple times
|
||||
|
||||
### Fixed
|
||||
- Creating many sites no longer leads to cookie overflow
|
||||
- Ignore sessions without pageviews for `entry_page` and `exit_page` breakdowns
|
||||
- Using `VersionedCollapsingMergeTree` to store visit data to avoid rare race conditions that led to wrong visit data being shown
|
||||
- Fix `conversion_rate` metric in a `browser_versions` breakdown
|
||||
|
@ -0,0 +1,50 @@
|
||||
defmodule PlausibleWeb.Components.FirstDashboardLaunchBanner do
|
||||
@moduledoc """
|
||||
A banner that appears on the first dashboard launch
|
||||
"""
|
||||
use Phoenix.Component
|
||||
use Phoenix.HTML
|
||||
|
||||
attr(:site, Plausible.Site, required: true)
|
||||
|
||||
def set(assigns) do
|
||||
~H"""
|
||||
<script>
|
||||
sessionStorage.setItem('<%= storage_key(@site) %>', false);
|
||||
</script>
|
||||
"""
|
||||
end
|
||||
|
||||
attr(:site, Plausible.Site, required: true)
|
||||
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div
|
||||
x-cloak
|
||||
x-data={x_data(@site)}
|
||||
class="w-full px-4 text-sm font-bold text-center text-blue-900 bg-blue-200 rounded transition"
|
||||
style="top: 91px"
|
||||
role="alert"
|
||||
x-bind:class="! show ? 'hidden' : ''"
|
||||
x-init={x_init(@site)}
|
||||
>
|
||||
<%= link("Team members, email reports and GA import. Explore more →",
|
||||
to: "/#{URI.encode_www_form(@site.domain)}/settings/email-reports",
|
||||
class: "py-2 block"
|
||||
) %>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp x_data(site) do
|
||||
"{show: !!sessionStorage.getItem('#{storage_key(site)}')}"
|
||||
end
|
||||
|
||||
defp x_init(site) do
|
||||
"setTimeout(() => sessionStorage.removeItem('#{storage_key(site)}'), 3000)"
|
||||
end
|
||||
|
||||
defp storage_key(site) do
|
||||
"dashboard_seen_#{site.domain}"
|
||||
end
|
||||
end
|
@ -34,9 +34,9 @@ defmodule PlausibleWeb.SiteController do
|
||||
|> Plausible.Mailer.send()
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_session(site.domain <> "_offer_email_report", true)
|
||||
|> redirect(external: Routes.site_path(conn, :add_snippet, site.domain))
|
||||
redirect(conn,
|
||||
external: Routes.site_path(conn, :add_snippet, site.domain, site_created: true)
|
||||
)
|
||||
|
||||
{:error, {:over_limit, limit}} ->
|
||||
render(conn, "new.html",
|
||||
|
@ -59,10 +59,7 @@ defmodule PlausibleWeb.StatsController do
|
||||
|
||||
cond do
|
||||
stats_start_date && can_see_stats? ->
|
||||
offer_email_report = get_session(conn, site.domain <> "_offer_email_report")
|
||||
|
||||
conn
|
||||
|> remove_email_report_banner(site)
|
||||
|> put_resp_header("x-robots-tag", "noindex, nofollow")
|
||||
|> render("stats.html",
|
||||
site: site,
|
||||
@ -72,7 +69,6 @@ defmodule PlausibleWeb.StatsController do
|
||||
stats_start_date: stats_start_date,
|
||||
native_stats_start_date: NaiveDateTime.to_date(site.native_stats_start_at),
|
||||
title: title(conn, site),
|
||||
offer_email_report: offer_email_report,
|
||||
demo: demo,
|
||||
flags: get_flags(conn.assigns[:current_user]),
|
||||
is_dbip: is_dbip(),
|
||||
@ -328,7 +324,6 @@ defmodule PlausibleWeb.StatsController do
|
||||
stats_start_date: stats_start_date,
|
||||
native_stats_start_date: NaiveDateTime.to_date(shared_link.site.native_stats_start_at),
|
||||
title: title(conn, shared_link.site),
|
||||
offer_email_report: false,
|
||||
demo: false,
|
||||
dogfood_page_path: "/share/:dashboard",
|
||||
shared_link_auth: shared_link.slug,
|
||||
@ -351,14 +346,6 @@ defmodule PlausibleWeb.StatsController do
|
||||
end
|
||||
end
|
||||
|
||||
defp remove_email_report_banner(conn, site) do
|
||||
if conn.assigns[:current_user] do
|
||||
delete_session(conn, site.domain <> "_offer_email_report")
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
defp shared_link_cookie_name(slug), do: "shared-link-" <> slug
|
||||
|
||||
defp get_flags(_user) do
|
||||
@ -367,11 +354,12 @@ defmodule PlausibleWeb.StatsController do
|
||||
|
||||
defp is_dbip() do
|
||||
on_full_build do
|
||||
false
|
||||
"false"
|
||||
else
|
||||
Plausible.Geo.database_type()
|
||||
|> to_string()
|
||||
|> String.starts_with?("DBIP")
|
||||
|> to_string()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,3 +1,8 @@
|
||||
<PlausibleWeb.Components.FirstDashboardLaunchBanner.set
|
||||
:if={@conn.params["site_created"] == "true"}
|
||||
site={@site}
|
||||
/>
|
||||
|
||||
<div class="w-full max-w-3xl mt-4 mx-auto flex">
|
||||
<%= form_for @conn, "/", [class: "max-w-lg w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
|
||||
<h2 class="text-xl font-bold dark:text-gray-100">Add JavaScript snippet</h2>
|
||||
|
@ -1,63 +0,0 @@
|
||||
<div class="<%= stats_container_class(@conn) %>" data-site-domain="<%= @site.domain %>">
|
||||
<%= if @offer_email_report do %>
|
||||
<div class="w-full px-4 text-sm font-bold text-center text-blue-900 bg-blue-200 rounded transition" style="top: 91px" role="alert">
|
||||
<%= link("Team members, email reports and GA import. Explore more →", to: "/#{URI.encode_www_form(@site.domain)}/settings/email-reports", class: "py-2 block") %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= if @site.locked do %>
|
||||
<div class="w-full px-4 py-4 text-sm font-bold text-center text-yellow-800 bg-yellow-100 rounded transition" style="top: 91px" role="alert">
|
||||
<p>This dashboard is actually locked. You are viewing it with super-admin access</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="pt-6"></div>
|
||||
<div
|
||||
id="stats-react-container"
|
||||
style="overflow-anchor: none;"
|
||||
data-domain="<%= @site.domain %>"
|
||||
data-offset="<%= Plausible.Site.tz_offset(@site) %>"
|
||||
data-has-goals="<%= @has_goals %>"
|
||||
data-conversions-opted-out="<%= Plausible.Billing.Feature.Goals.opted_out?(@site) %>"
|
||||
data-funnels-opted-out="<%= Plausible.Billing.Feature.Funnels.opted_out?(@site) %>"
|
||||
data-props-opted-out="<%= Plausible.Billing.Feature.Props.opted_out?(@site) %>"
|
||||
data-funnels-available="<%= Plausible.Billing.Feature.Funnels.check_availability(@site.owner) == :ok %>"
|
||||
data-props-available="<%= Plausible.Billing.Feature.Props.check_availability(@site.owner) == :ok %>"
|
||||
data-funnels="<%= Jason.encode!(@funnels) %>"
|
||||
data-has-props="<%= @has_props %>"
|
||||
data-logged-in="<%= !!@conn.assigns[:current_user] %>"
|
||||
data-stats-begin="<%= @stats_start_date %>"
|
||||
data-native-stats-begin="<%= @native_stats_start_date %>"
|
||||
data-shared-link-auth="<%= assigns[:shared_link_auth] %>"
|
||||
data-embedded="<%= @conn.assigns[:embedded] %>"
|
||||
data-background="<%= @conn.assigns[:background] %>"
|
||||
data-is-dbip="<%= @is_dbip %>"
|
||||
data-current-user-role="<%= @conn.assigns[:current_user_role] %>"
|
||||
data-flags="<%= Jason.encode!(@flags) %>"
|
||||
data-valid-intervals-by-period="<%= Plausible.Stats.Interval.valid_by_period(site: @site) |> Jason.encode!() %>">
|
||||
</div>
|
||||
<div id="modal_root"></div>
|
||||
<%= if !@conn.assigns[:current_user] && @conn.assigns[:demo] do %>
|
||||
<div class="bg-gray-50 dark:bg-gray-850">
|
||||
<div class="py-12 lg:py-16 lg:flex lg:items-center lg:justify-between">
|
||||
<h2 class="text-3xl font-extrabold tracking-tight text-gray-900 leading-9 sm:text-4xl sm:leading-10 dark:text-gray-100">
|
||||
Want these stats for your website?
|
||||
<br />
|
||||
<span class="text-indigo-600">Start your free trial today.</span>
|
||||
</h2>
|
||||
<div class="flex mt-8 lg:flex-shrink-0 lg:mt-0">
|
||||
<div class="inline-flex shadow rounded-md">
|
||||
<a href="/register" class="inline-flex items-center justify-center px-5 py-3 text-base font-medium text-white bg-indigo-600 border border-transparent leading-6 rounded-md hover:bg-indigo-500 focus:outline-none focus:ring transition duration-150 ease-in-out">
|
||||
Get started
|
||||
</a>
|
||||
</div>
|
||||
<div class="inline-flex ml-3 shadow rounded-md">
|
||||
<a href="/" class="inline-flex items-center justify-center px-5 py-3 text-base font-medium text-indigo-600 bg-white border border-transparent leading-6 rounded-md dark:text-gray-100 dark:bg-gray-800 hover:text-indigo-500 dark:hover:text-indigo-500 focus:outline-none focus:ring transition duration-150 ease-in-out">
|
||||
Learn more
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
73
lib/plausible_web/templates/stats/stats.html.heex
Normal file
73
lib/plausible_web/templates/stats/stats.html.heex
Normal file
@ -0,0 +1,73 @@
|
||||
<div class={stats_container_class(@conn)} data-site-domain={@site.domain}>
|
||||
<PlausibleWeb.Components.FirstDashboardLaunchBanner.render site={@site} />
|
||||
|
||||
<%= if @site.locked do %>
|
||||
<div
|
||||
class="w-full px-4 py-4 text-sm font-bold text-center text-yellow-800 bg-yellow-100 rounded transition"
|
||||
style="top: 91px"
|
||||
role="alert"
|
||||
>
|
||||
<p>This dashboard is actually locked. You are viewing it with super-admin access</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="pt-6"></div>
|
||||
<div
|
||||
id="stats-react-container"
|
||||
style="overflow-anchor: none;"
|
||||
data-domain={@site.domain}
|
||||
data-offset={Plausible.Site.tz_offset(@site)}
|
||||
data-has-goals={@has_goals}
|
||||
data-conversions-opted-out={Plausible.Billing.Feature.Goals.opted_out?(@site)}
|
||||
data-funnels-opted-out={Plausible.Billing.Feature.Funnels.opted_out?(@site)}
|
||||
data-props-opted-out={Plausible.Billing.Feature.Props.opted_out?(@site)}
|
||||
data-funnels-available={
|
||||
Plausible.Billing.Feature.Funnels.check_availability(@site.owner) == :ok
|
||||
}
|
||||
data-props-available={Plausible.Billing.Feature.Props.check_availability(@site.owner) == :ok}
|
||||
data-funnels={Jason.encode!(@funnels)}
|
||||
data-has-props={@has_props}
|
||||
data-logged-in={!!@conn.assigns[:current_user]}
|
||||
data-stats-begin={@stats_start_date}
|
||||
data-native-stats-begin={@native_stats_start_date}
|
||||
data-shared-link-auth={assigns[:shared_link_auth]}
|
||||
data-embedded={@conn.assigns[:embedded]}
|
||||
data-background={@conn.assigns[:background]}
|
||||
data-is-dbip={@is_dbip}
|
||||
data-current-user-role={@conn.assigns[:current_user_role]}
|
||||
data-flags={Jason.encode!(@flags)}
|
||||
data-valid-intervals-by-period={
|
||||
Plausible.Stats.Interval.valid_by_period(site: @site) |> Jason.encode!()
|
||||
}
|
||||
>
|
||||
</div>
|
||||
<div id="modal_root"></div>
|
||||
<%= if !@conn.assigns[:current_user] && @conn.assigns[:demo] do %>
|
||||
<div class="bg-gray-50 dark:bg-gray-850">
|
||||
<div class="py-12 lg:py-16 lg:flex lg:items-center lg:justify-between">
|
||||
<h2 class="text-3xl font-extrabold tracking-tight text-gray-900 leading-9 sm:text-4xl sm:leading-10 dark:text-gray-100">
|
||||
Want these stats for your website? <br />
|
||||
<span class="text-indigo-600">Start your free trial today.</span>
|
||||
</h2>
|
||||
<div class="flex mt-8 lg:flex-shrink-0 lg:mt-0">
|
||||
<div class="inline-flex shadow rounded-md">
|
||||
<a
|
||||
href="/register"
|
||||
class="inline-flex items-center justify-center px-5 py-3 text-base font-medium text-white bg-indigo-600 border border-transparent leading-6 rounded-md hover:bg-indigo-500 focus:outline-none focus:ring transition duration-150 ease-in-out"
|
||||
>
|
||||
Get started
|
||||
</a>
|
||||
</div>
|
||||
<div class="inline-flex ml-3 shadow rounded-md">
|
||||
<a
|
||||
href="/"
|
||||
class="inline-flex items-center justify-center px-5 py-3 text-base font-medium text-indigo-600 bg-white border border-transparent leading-6 rounded-md dark:text-gray-100 dark:bg-gray-800 hover:text-indigo-500 dark:hover:text-indigo-500 focus:outline-none focus:ring transition duration-150 ease-in-out"
|
||||
>
|
||||
Learn more
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
@ -244,7 +244,9 @@ defmodule PlausibleWeb.SiteControllerTest do
|
||||
}
|
||||
})
|
||||
|
||||
assert redirected_to(conn) == "/#{URI.encode_www_form("éxample.com")}/snippet"
|
||||
assert redirected_to(conn) ==
|
||||
"/#{URI.encode_www_form("éxample.com")}/snippet?site_created=true"
|
||||
|
||||
assert site = Repo.get_by(Plausible.Site, domain: "éxample.com")
|
||||
assert site.timezone == "Europe/London"
|
||||
assert site.ingest_rate_limit_scale_seconds == 60
|
||||
@ -341,7 +343,7 @@ defmodule PlausibleWeb.SiteControllerTest do
|
||||
}
|
||||
})
|
||||
|
||||
assert redirected_to(conn) == "/example.com/snippet"
|
||||
assert redirected_to(conn) == "/example.com/snippet?site_created=true"
|
||||
assert Repo.get_by(Plausible.Site, domain: "example.com")
|
||||
end
|
||||
|
||||
@ -361,7 +363,7 @@ defmodule PlausibleWeb.SiteControllerTest do
|
||||
}
|
||||
})
|
||||
|
||||
assert redirected_to(conn) == "/example.com/snippet"
|
||||
assert redirected_to(conn) == "/example.com/snippet?site_created=true"
|
||||
assert Plausible.Billing.Quota.site_usage(user) == 3
|
||||
end
|
||||
|
||||
@ -375,7 +377,7 @@ defmodule PlausibleWeb.SiteControllerTest do
|
||||
}
|
||||
})
|
||||
|
||||
assert redirected_to(conn) == "/example.com/snippet"
|
||||
assert redirected_to(conn) == "/example.com/snippet?site_created=true"
|
||||
assert Repo.get_by(Plausible.Site, domain: "example.com")
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user