mirror of
https://github.com/plausible/analytics.git
synced 2024-12-23 09:33:19 +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
|
- Validate metric isn't queried multiple times
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- Creating many sites no longer leads to cookie overflow
|
||||||
- Ignore sessions without pageviews for `entry_page` and `exit_page` breakdowns
|
- 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
|
- 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
|
- 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()
|
|> Plausible.Mailer.send()
|
||||||
end
|
end
|
||||||
|
|
||||||
conn
|
redirect(conn,
|
||||||
|> put_session(site.domain <> "_offer_email_report", true)
|
external: Routes.site_path(conn, :add_snippet, site.domain, site_created: true)
|
||||||
|> redirect(external: Routes.site_path(conn, :add_snippet, site.domain))
|
)
|
||||||
|
|
||||||
{:error, {:over_limit, limit}} ->
|
{:error, {:over_limit, limit}} ->
|
||||||
render(conn, "new.html",
|
render(conn, "new.html",
|
||||||
|
@ -59,10 +59,7 @@ defmodule PlausibleWeb.StatsController do
|
|||||||
|
|
||||||
cond do
|
cond do
|
||||||
stats_start_date && can_see_stats? ->
|
stats_start_date && can_see_stats? ->
|
||||||
offer_email_report = get_session(conn, site.domain <> "_offer_email_report")
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> remove_email_report_banner(site)
|
|
||||||
|> put_resp_header("x-robots-tag", "noindex, nofollow")
|
|> put_resp_header("x-robots-tag", "noindex, nofollow")
|
||||||
|> render("stats.html",
|
|> render("stats.html",
|
||||||
site: site,
|
site: site,
|
||||||
@ -72,7 +69,6 @@ defmodule PlausibleWeb.StatsController do
|
|||||||
stats_start_date: stats_start_date,
|
stats_start_date: stats_start_date,
|
||||||
native_stats_start_date: NaiveDateTime.to_date(site.native_stats_start_at),
|
native_stats_start_date: NaiveDateTime.to_date(site.native_stats_start_at),
|
||||||
title: title(conn, site),
|
title: title(conn, site),
|
||||||
offer_email_report: offer_email_report,
|
|
||||||
demo: demo,
|
demo: demo,
|
||||||
flags: get_flags(conn.assigns[:current_user]),
|
flags: get_flags(conn.assigns[:current_user]),
|
||||||
is_dbip: is_dbip(),
|
is_dbip: is_dbip(),
|
||||||
@ -328,7 +324,6 @@ defmodule PlausibleWeb.StatsController do
|
|||||||
stats_start_date: stats_start_date,
|
stats_start_date: stats_start_date,
|
||||||
native_stats_start_date: NaiveDateTime.to_date(shared_link.site.native_stats_start_at),
|
native_stats_start_date: NaiveDateTime.to_date(shared_link.site.native_stats_start_at),
|
||||||
title: title(conn, shared_link.site),
|
title: title(conn, shared_link.site),
|
||||||
offer_email_report: false,
|
|
||||||
demo: false,
|
demo: false,
|
||||||
dogfood_page_path: "/share/:dashboard",
|
dogfood_page_path: "/share/:dashboard",
|
||||||
shared_link_auth: shared_link.slug,
|
shared_link_auth: shared_link.slug,
|
||||||
@ -351,14 +346,6 @@ defmodule PlausibleWeb.StatsController do
|
|||||||
end
|
end
|
||||||
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 shared_link_cookie_name(slug), do: "shared-link-" <> slug
|
||||||
|
|
||||||
defp get_flags(_user) do
|
defp get_flags(_user) do
|
||||||
@ -367,11 +354,12 @@ defmodule PlausibleWeb.StatsController do
|
|||||||
|
|
||||||
defp is_dbip() do
|
defp is_dbip() do
|
||||||
on_full_build do
|
on_full_build do
|
||||||
false
|
"false"
|
||||||
else
|
else
|
||||||
Plausible.Geo.database_type()
|
Plausible.Geo.database_type()
|
||||||
|> to_string()
|
|> to_string()
|
||||||
|> String.starts_with?("DBIP")
|
|> String.starts_with?("DBIP")
|
||||||
|
|> to_string()
|
||||||
end
|
end
|
||||||
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">
|
<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 -> %>
|
<%= 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>
|
<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 = Repo.get_by(Plausible.Site, domain: "éxample.com")
|
||||||
assert site.timezone == "Europe/London"
|
assert site.timezone == "Europe/London"
|
||||||
assert site.ingest_rate_limit_scale_seconds == 60
|
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")
|
assert Repo.get_by(Plausible.Site, domain: "example.com")
|
||||||
end
|
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
|
assert Plausible.Billing.Quota.site_usage(user) == 3
|
||||||
end
|
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")
|
assert Repo.get_by(Plausible.Site, domain: "example.com")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user