Skip to content

maxvoltage/heex3

Repository files navigation

Heex3

To start your Phoenix server:

  • Run mix setup to install and setup dependencies
  • Start Phoenix endpoint with mix phx.server or inside IEx with iex -S mix phx.server

Now you can visit localhost:4000 from your browser.

Ready to run in production? Please check our deployment guides.

Learn more

MapLibre GL JS Integration in Phoenix LiveView

This project integrates MapLibre GL JS to provide a full-screen, interactive map in a Phoenix LiveView application.

Usage in LiveView

  1. Add a Full-Screen Map Container
    • In your LiveView template (e.g., map_live.html.heex):
      <div id="map" phx-hook="MapLibreMap" phx-update="ignore" class="fixed inset-0 w-screen h-screen z-0">
        <!-- Map rendered by JS hook -->
      </div>
  2. MapLibreMap JS Hook
    • The hook in assets/js/app.js:
      • Initializes the map with a vector tile source.
      • Uses browser geolocation to center the map and add a blue marker for you.
      • Sends your geolocation to the backend, which shares your location in real time with all connected users.
      • Only users within a 2 mile radius of your current location are shown on the map.
      • Efficient geohash windowing: The backend uses geohash partitioning (precision 6, ~1 mile cells) and subscribes to your cell and its 8 neighbors (3x3 window) for real-time presence updates.
      • True proximity filtering: Even after geohash windowing, only users within a true 2-mile radius (using the Haversine formula) are shown, avoiding blocky/geohash-only artifacts.
      • Real-time presence partitioning: User presence is tracked and broadcast per geohash cell, ensuring scalable and privacy-preserving updates.
      • Markers for all nearby users are shown in real time:
        • Your marker is always red and rendered on top.
        • Other users' markers are assigned random, distinguishable colors.
        • Markers are slightly offset so they don't overlap, even if users are at the same location.
        • Click/tap any marker to bring it to the top.
      • Adds navigation and geolocate controls.
      • Ensures responsive resizing.

Geolocation, Proximity, and Geohash Windowing

  • When you load the map, your browser will ask for geolocation permission.
  • If allowed, your location is sent to the backend and shared with all connected users in real time.
  • You will only see users who are within a 2 mile (~3.2 km) radius of your current location.
  • Efficient geohash windowing: The backend subscribes to your geohash cell and its 8 neighbors (3x3 window, precision 6, ~1 mile per cell) for presence updates.
  • True proximity filtering: Even after windowing, only users within a true 2-mile radius (using the Haversine formula) are shown.
  • If denied, the map defaults to New York City.
  • All users' locations are updated live as they move or connect/disconnect.
  • Markers are color-coded and offset for visibility; your marker is always red and on top.

If you are logged in, you will only ever see one marker for yourself, even if you open the map in multiple tabs or devices at the same time. This is because the system tracks users by their unique user id, not by browser tab or session.

Geohash, Presence, and Proximity Details

  • Geohash partitioning: The backend uses the geohash library to encode user locations at precision 6 (~1 mile cells).
  • Windowing: Each user subscribes to their cell and its 8 neighbors for real-time updates.
  • Presence partitioning: User presence is tracked per geohash cell, so only relevant users receive updates, improving privacy and scalability.
  • Proximity filtering: After windowing, a Haversine distance check ensures only users within 2 miles are shown, avoiding blocky/geohash-only artifacts.
  • Dependency: The backend now depends on the geohash Elixir package.

Real-Time Chat Creation & Messaging

  • Chats are now created only when the first message is sent, preventing empty chats from appearing in the chat list.
  • Real-time updates for new chats and messages are handled via Phoenix PubSub and Ash.Notifier.PubSub.
  • The chat modal opens in a "pending" state if no chat exists; the chat is created and the modal updates as soon as the first message is sent.
  • All chat and message actions use Ash code interface functions for maintainability and best practices.

PubSub & LiveView Integration

  • The MapLive LiveView subscribes to all relevant chat topics and the user's chat list topic for robust real-time updates.
  • New chats and messages appear instantly in the chat list and chat modal for both sender and recipient, without requiring a page refresh.
  • Subscriptions are managed automatically as chat membership changes.

ChatHelpers Module

  • The new Heex3Web.ChatHelpers module provides robust helpers for:
    • Grouping chat messages by time (5 min gap, 30 min max group, day change)
    • Formatting chat timestamps in a user-friendly, industry-standard way ("Today 11:03 AM", "Yesterday 1:05 PM", etc.)
    • All formatting is timezone-aware and uses the user's device timezone (detected in JS and sent to LiveView)

Timezone Support

  • The app now detects the user's browser timezone and passes it to all chat components, ensuring all chat times are shown in the user's local time.
  • This is handled via a new Timezone JS hook and LiveView event.

Profile Photo Uploads

  • Users can now upload a profile photo, which is stored in Cloudinary under the heex3_profile folder.
  • The photo_url attribute has been added to the users table and Ash resource.
  • Uploaded images are displayed in the profile view and edit profile modal, with instant preview using LiveView's <.live_img_preview /> component.
  • Cloudinary transformations are used for efficient image delivery and sizing.

Cloudinary Configuration

To enable Cloudinary uploads, set the following environment variables (see .env.example):

CLOUDINARY_API_KEY=your_api_key_here
CLOUDINARY_SECRET=your_secret_here
CLOUDINARY_CLOUD_NAME=your_cloud_name_here

These can be set in a .env file for development, and are required in production.

Map Markers with Profile Photo Streaming

The map now supports profile photos in markers with an efficient streaming approach:

Three-Step Photo Streaming Process

  1. Immediate Display: SVG circles with user initials are shown instantly
  2. Map Rendering: Map displays immediately with placeholder markers
  3. Photo Streaming: Profile images load asynchronously and replace initials

Technical Implementation

  • Circular SVG Generation: Fixed positioning and sizing for consistent 60px markers
  • Canvas Processing: Uses HTML5 Canvas to handle CORS and convert images to base64
  • Duplicate Prevention: Prevents "image already exists" errors with proper cleanup
  • Error Handling: Falls back to SVG initials if profile image fails to load
  • Bandwidth Optimization: Uses WebP format and 60px width for map markers

Cloudinary Integration

  • URL Parsing: New extract_public_id_from_url/1 function for parsing Cloudinary URLs
  • Transformation Presets: Map markers use ar_1:1,c_auto,g_auto,w_60/r_max/f_webp transformation
  • Presence Integration: Photo URLs are included in user presence data for real-time updates

Theme System

The application now includes a comprehensive theme system with support for multiple DaisyUI themes:

Theme Helper Module

  • The Heex3Web.ThemeHelper module provides robust theme management:
    • default_theme/0: Returns the configured default theme ("garden")
    • get_user_theme/1: Returns user's theme or default if not set
    • get_socket_theme/1: Returns theme from LiveView socket assigns
    • get_theme_gradient/1: Returns CSS gradient classes for theme backgrounds

Theme Support

  • Supports all DaisyUI themes including light, dark, cupcake, cyberpunk, and more
  • User themes are stored in the ui_theme field of the users table
  • Theme changes are applied in real-time via LiveView events
  • Fallback gradients are provided for unknown themes

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published