Skip to main content

lib/mob_ash.ex

defmodule MobAsh do
  @moduledoc """
  Resource-driven Mob screens from Ash.

  Declare Ash resources in your host app, register the domains, and mob_ash
  generates a list / detail / create screen set per resource — Ash runs
  on-device in the host BEAM (use a device-friendly data layer such as
  `Ash.DataLayer.Ets` or AshSqlite over the bundled SQLite).

  ## Host setup

      # mix.exs
      {:mob_ash, "~> 0.1"}

      # mob.exs
      config :mob, :plugins, [:mob_ash]

      # config/config.exs
      config :my_app, :ash_domains, [MyApp.Blog]

  The build's screens generator (spec v2) introspects each domain's resources
  and emits routes:

      /ash/post          MobAsh.ListScreen   (params: %{resource: MyApp.Blog.Post})
      /ash/post/detail   MobAsh.DetailScreen
      /ash/post/new      MobAsh.FormScreen

  All three are SHARED parameterized screens — the resource module rides the
  route as route-bound params (`Mob.Nav.Registry.register/3`), so
  `push_screen(socket, :"/ash/post")` works with no extra arguments, and
  `MobAsh.navigate/3` works without routes at all.

  ## Navigating programmatically

      MobAsh.navigate(socket, MyApp.Blog.Post, :list)
      MobAsh.navigate(socket, MyApp.Blog.Post, :new)
  """

  @doc """
  Push the mob_ash screen for `resource`: `:list`, `:detail` (needs `id:` in
  `params`), or `:new`.
  """
  @spec navigate(Mob.Socket.t(), module(), :list | :detail | :new, map()) :: Mob.Socket.t()
  def navigate(socket, resource, action, params \\ %{}) do
    screen =
      case action do
        :list -> MobAsh.ListScreen
        :detail -> MobAsh.DetailScreen
        :new -> MobAsh.FormScreen
      end

    Mob.Socket.push_screen(socket, screen, Map.put(params, :resource, resource))
  end
end