Packages
mob
0.5.18
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.2
0.6.1
0.6.0
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.11
0.5.10
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
BEAM-on-device mobile framework for Elixir
Current section
Files
Jump to
Current section
Files
lib/mob/component_server.ex
defmodule Mob.ComponentServer do
@moduledoc false
# GenServer wrapping a Mob.Component module. Each native_view instance on a
# screen gets its own process. Started unlinked (isolated from the screen).
use GenServer
@doc "Start a component process (not linked to the caller)."
@spec start(keyword()) :: {:ok, pid()} | {:error, term()}
def start(opts) do
GenServer.start(__MODULE__, opts)
end
@doc "Get the current rendered props from the component."
@spec render_props(pid()) :: map()
def render_props(pid), do: GenServer.call(pid, :render_props)
@doc "Get the persistent NIF handle allocated at mount time."
@spec get_handle(pid()) :: integer()
def get_handle(pid), do: GenServer.call(pid, :get_handle)
@doc "Update the component with new props from the parent screen re-render."
@spec update(pid(), map()) :: :ok
def update(pid, props), do: GenServer.cast(pid, {:update, props})
@doc "Deliver a native event to the component (called from the NIF callback path)."
@spec dispatch(pid(), String.t(), map()) :: :ok
def dispatch(pid, event, payload), do: GenServer.cast(pid, {:event, event, payload})
# ── GenServer ──────────────────────────────────────────────────────────────
@impl GenServer
def init(opts) do
module = opts[:module]
id = opts[:id]
screen_pid = opts[:screen_pid]
props = opts[:props]
platform = opts[:platform]
socket = Mob.Socket.new(module, platform: platform)
case module.mount(props, socket) do
{:ok, socket} ->
Mob.ComponentRegistry.register(screen_pid, id, module, self())
handle =
if platform != :no_render do
:mob_nif.register_component(self())
else
0
end
{:ok, %{module: module, socket: socket, screen_pid: screen_pid, id: id, handle: handle}}
{:error, reason} ->
{:stop, reason}
end
end
@impl GenServer
def handle_call(:render_props, _from, %{module: module, socket: socket} = state) do
{:reply, module.render(socket.assigns), state}
end
def handle_call(:get_handle, _from, %{handle: handle} = state) do
{:reply, handle, state}
end
@impl GenServer
def handle_cast({:update, new_props}, %{module: module, socket: socket} = state) do
case module.update(new_props, socket) do
{:ok, new_socket} -> {:noreply, %{state | socket: new_socket}}
_ -> {:noreply, state}
end
end
def handle_cast(
{:event, event, payload},
%{module: module, socket: socket, screen_pid: screen_pid, id: id} = state
) do
{:noreply, new_socket} = module.handle_event(event, payload, socket)
send(screen_pid, {:component_changed, id, module})
{:noreply, %{state | socket: new_socket}}
end
@impl GenServer
def handle_info(
{:component_event, event, payload_json},
%{module: module, socket: socket, screen_pid: screen_pid, id: id} = state
) do
payload =
case :json.decode(payload_json) do
map when is_map(map) -> map
_ -> %{}
end
{:noreply, new_socket} = module.handle_event(event, payload, socket)
send(screen_pid, {:component_changed, id, module})
{:noreply, %{state | socket: new_socket}}
end
def handle_info(
message,
%{module: module, socket: socket, screen_pid: screen_pid, id: id} = state
) do
{:noreply, new_socket} = module.handle_info(message, socket)
send(screen_pid, {:component_changed, id, module})
{:noreply, %{state | socket: new_socket}}
end
@impl GenServer
def terminate(reason, %{
module: module,
socket: socket,
screen_pid: screen_pid,
id: id,
handle: handle
}) do
Mob.ComponentRegistry.deregister(screen_pid, id, module)
if handle != 0, do: :mob_nif.deregister_component(handle)
module.terminate(reason, socket)
end
end