// If you want to use Phoenix channels, run `mix help phx.gen.channel` // to get started and then uncomment the line below. // import "./user_socket.js" // You can include dependencies in two ways. // // The simplest option is to put them in assets/vendor and // import them using relative paths: // // import "../vendor/some-package.js" // // Alternatively, you can `npm install some-package --prefix assets` and import // them using a path starting with the package name: // // import "some-package" // // If you have dependencies that try to import CSS, esbuild will generate a separate `app.css` file. // To load it, simply add a second `` to your `root.html.heex` file. // Include phoenix_html to handle method=PUT/DELETE in forms and buttons. import "phoenix_html" // Establish Phoenix Socket and LiveView configuration. import {Socket} from "phoenix" import {LiveSocket} from "phoenix_live_view" import {hooks as colocatedHooks} from "phoenix-colocated/who_need_help" import topbar from "../vendor/topbar" import {Hooks} from "./hooks" const systemTheme = () => matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" const setTheme = theme => { if (theme === "system") { localStorage.removeItem("phx:theme") document.documentElement.setAttribute("data-theme", systemTheme()) document.documentElement.setAttribute("data-theme-source", "system") } else { localStorage.setItem("phx:theme", theme) document.documentElement.setAttribute("data-theme", theme) document.documentElement.setAttribute("data-theme-source", "user") } } if (!document.documentElement.hasAttribute("data-theme")) { setTheme(localStorage.getItem("phx:theme") || "system") } window.addEventListener("storage", event => { if (event.key === "phx:theme") setTheme(event.newValue || "system") }) window.addEventListener("phx:set-theme", event => setTheme(event.target.dataset.phxTheme)) matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => { if (document.documentElement.getAttribute("data-theme-source") === "system") { document.documentElement.setAttribute("data-theme", systemTheme()) } }) const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") const activateProtectedTokenFragment = () => { if (!window.location.hash.startsWith("#token=")) return const token = new URLSearchParams(window.location.hash.slice(1)).get("token") window.history.replaceState(null, "", `${window.location.pathname}${window.location.search}`) if (!token || !/^[A-Za-z0-9_-]{43}$/.test(token)) return const candidates = [ { form: document.getElementById("magic-link-fragment-form"), input: document.getElementById("magic-link-fragment-token"), options: document.getElementById("standard-login-options") }, { form: document.getElementById("email-change-fragment-form"), input: document.getElementById("email-change-fragment-token") } ] const candidate = candidates.find(({form, input}) => form instanceof HTMLFormElement && input instanceof HTMLInputElement ) if (!candidate) return candidate.input.value = token candidate.form.hidden = false if (candidate.options instanceof HTMLElement) candidate.options.hidden = true candidate.form.querySelector("button")?.focus() } activateProtectedTokenFragment() window.addEventListener("hashchange", activateProtectedTokenFragment) const liveSocket = new LiveSocket("/live", Socket, { longPollFallbackMs: 2500, params: { _csrf_token: csrfToken, client_type: typeof window.WhoNeedHelpAndroid?.postMessage === "function" ? "android" : "browser" }, hooks: {...colocatedHooks, ...Hooks}, }) // Show progress bar on live navigation and form submits topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"}) window.addEventListener("phx:page-loading-start", _info => topbar.show(300)) window.addEventListener("phx:page-loading-stop", _info => topbar.hide()) window.addEventListener("phx:reset-message-form", ({detail}) => { const form = document.getElementById(detail.id) if (form instanceof HTMLFormElement) form.reset() }) window.addEventListener("phx:native-tracking-start", ({detail}) => { window.WhoNeedHelpAndroid?.postMessage?.(JSON.stringify({ action: "start", assignment_id: detail.assignment_id, csrf_token: csrfToken })) }) window.addEventListener("phx:native-tracking-stop", () => { window.WhoNeedHelpAndroid?.postMessage?.(JSON.stringify({action: "stop"})) }) // connect if there are any LiveViews on the page liveSocket.connect() // expose liveSocket on window for web console debug logs and latency simulation: // >> liveSocket.enableDebug() // >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session // >> liveSocket.disableLatencySim() window.liveSocket = liveSocket if ("serviceWorker" in navigator) { window.addEventListener("load", () => { navigator.serviceWorker.register("/sw.js").catch(error => { console.warn("Service worker registration failed", error) }) }) } // The lines below enable quality of life phoenix_live_reload // development features: // // 1. stream server logs to the browser console // 2. click on elements to jump to their definitions in your code editor // if (process.env.NODE_ENV === "development") { window.addEventListener("phx:live_reload:attached", ({detail: reloader}) => { // Enable server log streaming to client. // Disable with reloader.disableServerLogs() reloader.enableServerLogs() // Open configured PLUG_EDITOR at file:line of the clicked element's HEEx component // // * click with "c" key pressed to open at caller location // * click with "d" key pressed to open at function component definition location let keyDown window.addEventListener("keydown", e => keyDown = e.key) window.addEventListener("keyup", _e => keyDown = null) window.addEventListener("click", e => { if(keyDown === "c"){ e.preventDefault() e.stopImmediatePropagation() reloader.openEditorAtCaller(e.target) } else if(keyDown === "d"){ e.preventDefault() e.stopImmediatePropagation() reloader.openEditorAtDef(e.target) } }, true) window.liveReloader = reloader }) }