Packages
phoenix_kit
1.7.23
1.7.210
1.7.209
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/phoenix_kit_web/components/dashboard/live_tabs.ex
defmodule PhoenixKitWeb.Components.Dashboard.LiveTabs do
@moduledoc """
LiveView integration for dashboard tabs with real-time updates.
This module provides hooks and helpers for LiveViews to integrate with
the dashboard tab system, including:
- Automatic tab subscription and updates
- Badge value synchronization via PubSub
- Presence tracking for viewer counts
- Group collapse state management
## Usage in LiveViews
defmodule MyAppWeb.DashboardLive do
use MyAppWeb, :live_view
use PhoenixKitWeb.Components.Dashboard.LiveTabs
def mount(_params, _session, socket) do
socket =
socket
|> init_dashboard_tabs()
|> track_tab_presence(:my_tab)
{:ok, socket}
end
# Tabs automatically update when badges change
end
## Manual Integration
If you prefer manual control:
def mount(_params, _session, socket) do
if connected?(socket) do
Phoenix.PubSub.subscribe(PhoenixKit.PubSub, PhoenixKit.Dashboard.pubsub_topic())
# Subscribe to badge topics for live updates
for tab <- PhoenixKit.Dashboard.get_tabs() do
if tab.badge && tab.badge.subscribe do
topic = PhoenixKit.Dashboard.Badge.get_topic(tab.badge)
Phoenix.PubSub.subscribe(PhoenixKit.PubSub, topic)
end
end
end
{:ok, assign(socket, :dashboard_tabs, PhoenixKit.Dashboard.get_tabs())}
end
def handle_info({:tab_updated, _tab}, socket) do
{:noreply, assign(socket, :dashboard_tabs, PhoenixKit.Dashboard.get_tabs())}
end
"""
alias PhoenixKit.Dashboard
alias PhoenixKit.Dashboard.{Badge, Presence, Registry}
@doc """
Use this module in a LiveView to get dashboard tab helpers.
"""
defmacro __using__(_opts) do
quote do
import PhoenixKitWeb.Components.Dashboard.LiveTabs
@before_compile PhoenixKitWeb.Components.Dashboard.LiveTabs
end
end
@doc false
# credo:disable-for-lines:60 Credo.Check.Refactor.CyclomaticComplexity
defmacro __before_compile__(_env) do
quote do
def handle_info({:tab_updated, tab}, socket) do
tabs = update_tab_in_list(socket.assigns[:dashboard_tabs] || [], tab)
{:noreply, assign(socket, :dashboard_tabs, tabs)}
end
def handle_info(:tabs_refreshed, socket) do
{:noreply, assign(socket, :dashboard_tabs, load_dashboard_tabs(socket))}
end
def handle_info({:tab_viewers_updated, tab_id, count}, socket) do
viewer_counts = Map.put(socket.assigns[:tab_viewer_counts] || %{}, tab_id, count)
{:noreply, assign(socket, :tab_viewer_counts, viewer_counts)}
end
def handle_info({:badge_update, tab_id, value}, socket) do
Dashboard.update_badge(tab_id, value)
{:noreply, socket}
end
def handle_event("toggle_dashboard_group", %{"group" => group_id}, socket) do
{:noreply, toggle_collapsed_group(socket, group_id)}
rescue
_ -> {:noreply, socket}
end
defp toggle_collapsed_group(socket, group_id) do
group_atom = String.to_existing_atom(group_id)
collapsed = socket.assigns[:collapsed_dashboard_groups] || MapSet.new()
updated = toggle_group_membership(collapsed, group_atom)
assign(socket, :collapsed_dashboard_groups, updated)
end
defp toggle_group_membership(set, item) do
if MapSet.member?(set, item),
do: MapSet.delete(set, item),
else: MapSet.put(set, item)
end
defp update_tab_in_list(tabs, updated_tab) do
Enum.map(tabs, fn tab ->
if tab.id == updated_tab.id, do: updated_tab, else: tab
end)
end
defp load_dashboard_tabs(socket) do
scope = socket.assigns[:phoenix_kit_current_scope]
path = socket.assigns[:url_path] || "/dashboard"
Registry.get_tabs_with_active(path, scope: scope)
end
end
end
@doc """
Initializes dashboard tabs in a LiveView socket.
This function:
1. Loads tabs from the registry
2. Subscribes to tab updates
3. Subscribes to badge update topics
4. Initializes viewer counts
## Options
- `:show_presence` - Load and track presence counts (default: true)
- `:subscribe_badges` - Subscribe to live badge topics (default: true)
## Examples
socket = init_dashboard_tabs(socket)
socket = init_dashboard_tabs(socket, show_presence: false)
"""
@spec init_dashboard_tabs(Phoenix.LiveView.Socket.t(), keyword()) :: Phoenix.LiveView.Socket.t()
def init_dashboard_tabs(socket, opts \\ []) do
show_presence = Keyword.get(opts, :show_presence, true)
subscribe_badges = Keyword.get(opts, :subscribe_badges, true)
scope = socket.assigns[:phoenix_kit_current_scope]
current_path = socket.assigns[:url_path] || "/dashboard"
# Load tabs
tabs = Registry.get_tabs_with_active(current_path, scope: scope)
# Subscribe to updates if connected
if Phoenix.LiveView.connected?(socket) do
# Subscribe to tab updates
Phoenix.PubSub.subscribe(PhoenixKit.PubSub, Registry.pubsub_topic())
# Subscribe to presence updates
if show_presence do
Presence.subscribe()
end
# Subscribe to badge topics
if subscribe_badges do
subscribe_to_badge_topics(tabs)
end
end
# Load viewer counts
viewer_counts =
if show_presence do
Presence.get_all_tab_counts()
else
%{}
end
socket
|> Phoenix.Component.assign(:dashboard_tabs, tabs)
|> Phoenix.Component.assign(:tab_viewer_counts, viewer_counts)
|> Phoenix.Component.assign(:collapsed_dashboard_groups, MapSet.new())
end
@doc """
Tracks the current user's presence on a specific tab.
Call this in mount/3 to track which tab the user is viewing.
## Examples
socket = track_tab_presence(socket, :orders)
socket = track_tab_presence(socket, :printers, meta: %{printer_id: 123})
"""
@spec track_tab_presence(Phoenix.LiveView.Socket.t(), atom(), keyword()) ::
Phoenix.LiveView.Socket.t()
def track_tab_presence(socket, tab_id, opts \\ []) do
if Phoenix.LiveView.connected?(socket) do
current_path = socket.assigns[:url_path]
opts = Keyword.put_new(opts, :tab_path, current_path)
case Presence.track_tab(socket, tab_id, opts) do
{:ok, _ref} ->
Phoenix.Component.assign(socket, :tracked_tab, tab_id)
{:error, _reason} ->
socket
end
else
socket
end
end
@doc """
Untracks the current user from their tracked tab.
Call this when leaving a tab or on terminate.
"""
@spec untrack_tab_presence(Phoenix.LiveView.Socket.t()) :: Phoenix.LiveView.Socket.t()
def untrack_tab_presence(socket) do
case socket.assigns[:tracked_tab] do
nil ->
socket
tab_id ->
Presence.untrack_tab(socket, tab_id)
Phoenix.Component.assign(socket, :tracked_tab, nil)
end
end
@doc """
Refreshes the dashboard tabs from the registry.
Call this when you need to force a refresh of tab data.
"""
@spec refresh_dashboard_tabs(Phoenix.LiveView.Socket.t()) :: Phoenix.LiveView.Socket.t()
def refresh_dashboard_tabs(socket) do
scope = socket.assigns[:phoenix_kit_current_scope]
current_path = socket.assigns[:url_path] || "/dashboard"
tabs = Registry.get_tabs_with_active(current_path, scope: scope)
Phoenix.Component.assign(socket, :dashboard_tabs, tabs)
end
@doc """
Updates a specific tab's badge and broadcasts the update.
This is a convenience wrapper that updates the badge and triggers
a broadcast to all connected LiveViews.
## Examples
update_tab_badge(socket, :notifications, 5)
update_tab_badge(socket, :alerts, count: 3, color: :error)
"""
@spec update_tab_badge(Phoenix.LiveView.Socket.t(), atom(), any()) ::
Phoenix.LiveView.Socket.t()
def update_tab_badge(socket, tab_id, value) do
Dashboard.update_badge(tab_id, value)
socket
end
@doc """
Sets attention animation on a tab.
## Examples
set_tab_attention(socket, :alerts, :pulse)
"""
@spec set_tab_attention(Phoenix.LiveView.Socket.t(), atom(), atom()) ::
Phoenix.LiveView.Socket.t()
def set_tab_attention(socket, tab_id, animation) do
Dashboard.set_attention(tab_id, animation)
socket
end
@doc """
Clears attention animation from a tab.
"""
@spec clear_tab_attention(Phoenix.LiveView.Socket.t(), atom()) :: Phoenix.LiveView.Socket.t()
def clear_tab_attention(socket, tab_id) do
Dashboard.clear_attention(tab_id)
socket
end
# Private helpers
defp subscribe_to_badge_topics(tabs) do
for tab <- tabs, tab.badge, Badge.live?(tab.badge) do
topic = Badge.get_topic(tab.badge)
if topic do
Phoenix.PubSub.subscribe(PhoenixKit.PubSub, topic)
end
end
:ok
end
end