Current section
Files
Jump to
Current section
Files
lib/xmtp/metadata/group_app_data.ex
defmodule Xmtp.Metadata.GroupAppData do
@moduledoc """
Regent group appData codec.
The appData document is a small JSON object with one Regent-owned section.
Product apps can put room and member display metadata here without exposing
raw XMTP group internals to UI code.
"""
@schema "regent.xmtp.group_app_data.v1"
@spec encode(map()) :: {:ok, String.t()} | {:error, term()}
def encode(attrs) when is_map(attrs) do
attrs
|> normalize()
|> Jason.encode()
end
@spec decode(String.t() | nil) :: {:ok, map()} | {:error, term()}
def decode(nil), do: {:ok, empty()}
def decode(""), do: {:ok, empty()}
def decode(data) when is_binary(data) do
with {:ok, decoded} <- Jason.decode(data) do
{:ok, normalize(decoded)}
end
end
@spec put_member_profile(String.t() | nil, String.t(), map()) ::
{:ok, String.t()} | {:error, term()}
def put_member_profile(app_data, principal_id, profile)
when is_binary(principal_id) and is_map(profile) do
with {:ok, decoded} <- decode(app_data) do
decoded
|> put_in([:member_profiles, principal_id], profile)
|> encode()
end
end
@spec get_member_profile(String.t() | nil, String.t()) :: {:ok, map() | nil} | {:error, term()}
def get_member_profile(app_data, principal_id) when is_binary(principal_id) do
with {:ok, decoded} <- decode(app_data) do
{:ok, get_in(decoded, [:member_profiles, principal_id])}
end
end
defp normalize(attrs) do
%{
schema: @schema,
product: fetch(attrs, :product),
room_key: fetch(attrs, :room_key),
room_profile: fetch(attrs, :room_profile) || %{},
member_profiles: fetch(attrs, :member_profiles) || %{}
}
|> Enum.reject(fn {_key, value} -> is_nil(value) end)
|> Map.new()
end
defp empty do
%{schema: @schema, room_profile: %{}, member_profiles: %{}}
end
defp fetch(attrs, key) do
Map.get(attrs, key) || Map.get(attrs, Atom.to_string(key))
end
end