// 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/localspot" import topbar from "../vendor/topbar" // Custom hooks const Geolocation = { mounted() { this.el.addEventListener("click", () => { if ("geolocation" in navigator) { this.el.disabled = true this.el.textContent = "Getting location..." navigator.geolocation.getCurrentPosition( (position) => { this.pushEvent("set_location", { latitude: position.coords.latitude.toString(), longitude: position.coords.longitude.toString() }) }, (error) => { console.error("Geolocation error:", error) this.el.disabled = false this.el.textContent = "Use My Location" alert("Could not get your location. Please check your browser permissions.") } ) } else { alert("Geolocation is not supported by your browser.") } }) } } const LeafletMap = { mounted() { // Load Leaflet CSS dynamically if (!document.querySelector('link[href*="leaflet"]')) { const link = document.createElement('link') link.rel = 'stylesheet' link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css' document.head.appendChild(link) } // Load Leaflet JS dynamically if (!window.L) { const script = document.createElement('script') script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js' script.onload = () => this.initMap() document.head.appendChild(script) } else { this.initMap() } }, initMap() { const businesses = JSON.parse(this.el.dataset.businesses) const center = JSON.parse(this.el.dataset.center) const zoom = parseInt(this.el.dataset.zoom) // Clear the loading indicator this.el.innerHTML = '' // Initialize map this.map = L.map(this.el).setView([center.lat, center.lng], zoom) // Add OpenStreetMap tiles L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(this.map) // Add markers for each business this.markers = businesses.map(business => { const marker = L.marker([business.lat, business.lng]) .addTo(this.map) .bindPopup(`
${business.name} ${business.locally_owned ? '✓ Local' : ''}
${business.category}
`) marker.on('click', () => { this.pushEvent('select_business', { slug: business.slug }) }) return marker }) // Fit bounds to show all markers if there are any if (this.markers.length > 0) { const group = L.featureGroup(this.markers) this.map.fitBounds(group.getBounds().pad(0.1)) } }, destroyed() { if (this.map) { this.map.remove() } } } const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") const liveSocket = new LiveSocket("/live", Socket, { longPollFallbackMs: 2500, params: {_csrf_token: csrfToken}, hooks: {...colocatedHooks, Geolocation, LeafletMap}, }) // 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()) // 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 // 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 }) }