Packages

phoenix_kit

2.14.2
2.14.2 2.14.1 2.14.0 2.13.19 2.13.18 2.13.17 2.13.16 2.13.15 2.13.13 2.13.12 2.13.11 2.13.10 2.13.9 2.13.8 2.13.7 2.13.6 2.13.5 2.13.4 2.13.3 2.13.2 2.13.1 2.13.0 2.12.1 2.12.0 2.11.0 2.10.0 2.9.0 2.8.1 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.0 2.0.1 2.0.0 1.7.236 1.7.235 1.7.234 1.7.233 1.7.232 1.7.231 1.7.230 1.7.229 1.7.228 1.7.227 1.7.226 1.7.225 1.7.224 1.7.223 1.7.222 1.7.221 1.7.220 1.7.219 1.7.218 1.7.217 1.7.216 1.7.215 1.7.214 1.7.213 1.7.212 1.7.211 1.7.210 1.7.209 1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib modules storage etcher_adapter.ex
Raw

lib/modules/storage/etcher_adapter.ex

defmodule PhoenixKit.Modules.Storage.EtcherAdapter do
@moduledoc """
Persistence helper for the MediaBrowser's annotation flow.
Etcher 0.3 dropped the `Etcher.Storage` behaviour entirely — annotations
now live inside the host `<Fresco.canvas>`'s `extensions.etcher` blob
and the library doesn't reach into the consumer's DB anymore. PhoenixKit
still needs to persist its annotations (they're per-file, not per-canvas-
file-on-disk), so this module survives as a thin helper module called
from the MediaBrowser LV's `etcher:annotations-changed` event handler —
not as a behaviour implementation.
The four public functions (`create/1`, `list_for/2`, `update/2`,
`delete/1`) keep their pre-0.3 signatures so the diff in MediaBrowser
stays small. None of them are `@impl` annotations anymore; they're
just plain helpers wrapping the `PhoenixKit.Annotations` context.
Etcher's generic API is keyed by `target_type` + `target_uuid` so the
library can annotate any kind of resource. A `"file"` target is a media
File and is stored with `file_uuid` set as well (the hard FK the
file-side features key on); any other target — a projects whiteboard,
say — is stored by the pair alone, with no file (V183). Both shapes
are pinned by the schema and a CHECK.
## Comment threads
An annotation's discussion thread is **not** created at draw time —
it's instantiated lazily when the user posts the first comment on the
annotation. The comments are anchored to the **file**
(`resource_type = "file"`, `resource_uuid = file_uuid`) with
`metadata.annotation_uuid` carrying the back-reference, so they
appear in the file's main thread alongside non-annotated discussion.
No `comment_uuid` column on annotations is needed.
"""
alias PhoenixKit.Annotations
alias PhoenixKit.Annotations.Annotation
# Whitelist of annotation schema fields the helper accepts from event
# payloads, sourced from `Annotation.adapter_writable_fields/0` so the
# set stays in sync with the schema's `@cast_fields`. Anything else
# (Etcher routing keys, JS-side anchor coords, comment-derived metadata
# we hydrate server-side) is silently dropped — `String.to_existing_atom`
# on unknown payload keys used to crash the LV when Etcher's payload
# shape grew new client-side keys. Stored as strings here since the
# filter compares against `to_string(payload_key)`.
@schema_keys Enum.map(Annotation.adapter_writable_fields(), &Atom.to_string/1)
def create(attrs) do
with {:ok, target_type, target_uuid} <- target(attrs) do
attrs
|> filter_to_schema()
|> Map.put(:target_type, target_type)
|> Map.put(:target_uuid, target_uuid)
|> Map.put(:file_uuid, if(target_type == "file", do: target_uuid))
|> Annotations.create()
end
end
def list_for(target_type, target_uuid) when is_binary(target_type) and is_binary(target_uuid),
do: Annotations.list_for_target(target_type, target_uuid)
def list_for(_other, _uuid), do: []
def update(uuid, attrs) do
attrs
|> filter_to_schema()
|> then(&Annotations.update(uuid, &1))
end
def delete(uuid), do: Annotations.delete(uuid)
# ---------------------------------------------------------------------------
# The schema owns the shape; this is the early rejection the Etcher protocol
# wants (`{:error, :unsupported_target}` rather than a changeset). Reading it
# from `Annotation` keeps the two from drifting.
defp target(%{"target_type" => type, "target_uuid" => uuid}), do: target(type, uuid)
defp target(%{target_type: type, target_uuid: uuid}), do: target(type, uuid)
defp target(_attrs), do: {:error, :unsupported_target}
defp target(type, uuid) when is_binary(type) and is_binary(uuid) do
if Regex.match?(Annotation.target_type_format(), type),
do: {:ok, type, uuid},
else: {:error, :unsupported_target}
end
defp target(_type, _uuid), do: {:error, :unsupported_target}
defp filter_to_schema(attrs) do
Enum.reduce(attrs, %{}, fn {k, v}, acc ->
key = to_string(k)
if key in @schema_keys, do: Map.put(acc, String.to_existing_atom(key), v), else: acc
end)
end
end