diff --git a/assets/js/app.js b/assets/js/app.js index 560d0a824..7ce36c1fb 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -22,7 +22,8 @@ import {Socket} from "phoenix" import {LiveSocket} from "phoenix_live_view" import topbar from "../vendor/topbar" import live_select from "live_select" -import { QrScanner, Wheel, Confetti, Countdown, Sorting } from "./hooks"; +import { QrScanner, Wheel, Confetti, Sorting } from "./hooks"; +import darkModeHook from "../vendor/dark_mode" let Hooks = { QrScanner: QrScanner, @@ -32,6 +33,7 @@ let Hooks = { Sorting: Sorting, ...live_select }; +Hooks.DarkThemeToggle = darkModeHook let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") let liveSocket = new LiveSocket("/live", Socket, { @@ -40,6 +42,12 @@ let liveSocket = new LiveSocket("/live", Socket, { hooks: Hooks }) +if (localStorage.getItem('theme') === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { + document.documentElement.classList.add('dark'); +} else { + document.documentElement.classList.remove('dark') +} + // Show progress bar on live navigation and form submits topbar.config({barColors: {0: "#ffdb0d"}, shadowColor: "rgba(0, 0, 0, .3)"}) window.addEventListener("phx:page-loading-start", _info => topbar.show(300)) diff --git a/assets/vendor/dark_mode.js b/assets/vendor/dark_mode.js new file mode 100644 index 000000000..6561a9ffa --- /dev/null +++ b/assets/vendor/dark_mode.js @@ -0,0 +1,40 @@ + +const localStorageKey = 'theme'; + +const isDark = () => { + if (localStorage.getItem(localStorageKey) === 'dark') return true; + if (localStorage.getItem(localStorageKey) === 'light') return false; + return window.matchMedia('(prefers-color-scheme: dark)').matches; +} + +const setupThemeToggle = () => { + toggleVisibility = (dark) => { + const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon'); + const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon'); + if (themeToggleDarkIcon == null || themeToggleLightIcon == null) return; + const show = dark ? themeToggleDarkIcon : themeToggleLightIcon + const hide = dark ? themeToggleLightIcon : themeToggleDarkIcon + show.classList.remove('hidden', 'text-transparent'); + hide.classList.add('hidden', 'text-transparent'); + if (dark) { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } + try { localStorage.setItem(localStorageKey, dark ? 'dark' : 'light') } catch (_err) { } + } + toggleVisibility(isDark()) + document.getElementById('theme-toggle').addEventListener('click', function () { + toggleVisibility(!isDark()) + }); +} + +const darkModeHook = { + mounted() { + setupThemeToggle(); + }, + updated() { + }, +} + +export default darkModeHook; diff --git a/lib/safira_web/components/dark_mode.ex b/lib/safira_web/components/dark_mode.ex new file mode 100644 index 000000000..12dfa096f --- /dev/null +++ b/lib/safira_web/components/dark_mode.ex @@ -0,0 +1,54 @@ +defmodule DarkMode do + @moduledoc false + use Phoenix.Component + + def button(assigns) do + ~H""" + + + + """ + end +end diff --git a/lib/safira_web/components/layouts/root.html.heex b/lib/safira_web/components/layouts/root.html.heex index 2fb7f709d..d993f04ea 100644 --- a/lib/safira_web/components/layouts/root.html.heex +++ b/lib/safira_web/components/layouts/root.html.heex @@ -1,5 +1,5 @@ - + diff --git a/lib/safira_web/components/page.ex b/lib/safira_web/components/page.ex index 86582aecf..7e508e6fc 100644 --- a/lib/safira_web/components/page.ex +++ b/lib/safira_web/components/page.ex @@ -23,7 +23,7 @@ defmodule SafiraWeb.Components.Page do <.header title_class={"#{size_class(@size)} #{@title_class}"}> <%= @title %> <:subtitle> - <%= @subtitle %> + <%= @subtitle %> <%= if @back_to_link do %> <.link patch={@back_to_link}> diff --git a/lib/safira_web/components/sidebar.ex b/lib/safira_web/components/sidebar.ex index da7dc3a08..9ce046c14 100644 --- a/lib/safira_web/components/sidebar.ex +++ b/lib/safira_web/components/sidebar.ex @@ -148,7 +148,7 @@ defmodule SafiraWeb.Components.Sidebar do <:img src={"https://github.com/identicons/#{@user.handle |> String.slice(0..2)}.png"} /> <:title color={@title_color}><%= @user.name %> <:subtitle color={@subtitle_color}>@<%= @user.handle %> - <:link navigate="/profile/settings">Settings + <:link navigate="/app/profile/settings">Settings <:link href="/users/log_out" method={:delete}>Sign out """ diff --git a/lib/safira_web/live/app/profile_live/form_component.ex b/lib/safira_web/live/app/profile_live/form_component.ex new file mode 100644 index 000000000..5cebf33ae --- /dev/null +++ b/lib/safira_web/live/app/profile_live/form_component.ex @@ -0,0 +1,27 @@ +defmodule SafiraWeb.App.ProfileLive.FormComponent do + use SafiraWeb, :live_component + + @impl true + def render(assigns) do + ~H""" +
+ <.page + title="Options" + subtitle={gettext("Customize your profile.")} + title_class="text-2xl text-black dark:text-white" + > +
+
+ +
+
+ +
+ """ + end + + @impl true + def mount(socket) do + {:ok, socket} + end +end diff --git a/lib/safira_web/live/app/profile_live/index.ex b/lib/safira_web/live/app/profile_live/index.ex new file mode 100644 index 000000000..8217dd660 --- /dev/null +++ b/lib/safira_web/live/app/profile_live/index.ex @@ -0,0 +1,35 @@ +defmodule SafiraWeb.App.ProfileLive.Index do + use SafiraWeb, :app_view + + @impl true + def mount(_params, _session, socket) do + {:ok, socket} + end + + @impl true + def handle_params(_params, _, socket) do + {:noreply, + socket + |> assign(:user, socket.assigns.current_user) + |> assign(:current_page, :profile) + |> apply_action(socket.assigns.live_action)} + end + + defp apply_action(socket, :index) do + socket + |> assign(:page_title, "Profile") + end + + defp apply_action(socket, :edit) do + socket + |> assign(:page_title, "Edit Profile") + end + + defp return_path(user) do + case user.type do + :attendee -> "app" + :staff -> "dashboard" + _ -> "app" + end + end +end diff --git a/lib/safira_web/live/app/profile_live/index.html.heex b/lib/safira_web/live/app/profile_live/index.html.heex new file mode 100644 index 000000000..36f847559 --- /dev/null +++ b/lib/safira_web/live/app/profile_live/index.html.heex @@ -0,0 +1,23 @@ +<.page title="Profile"> + <:actions> + <.button> + <.icon name="hero-edit" class="w-5 h-5" /> + + + + +<.modal + :if={@live_action in [:edit]} + id="profile_settings-modal" + show + on_cancel={JS.patch(~p"/#{return_path(@current_user)}")} +> + <.live_component + module={SafiraWeb.App.ProfileLive.FormComponent} + id="profile" + current_user={@current_user} + action={@live_action} + patch={~p"/#{return_path(@current_user)}"} + page_title={@page_title} + /> + diff --git a/lib/safira_web/router.ex b/lib/safira_web/router.ex index a596b0446..54f7e2492 100644 --- a/lib/safira_web/router.ex +++ b/lib/safira_web/router.ex @@ -104,6 +104,11 @@ defmodule SafiraWeb.Router do live "/product/:id", Show, :show end + scope "/profile", ProfileLive do + live "/", Index, :index + live "/settings", Index, :edit + end + live "/vault", VaultLive.Index, :index end