PhoenixKit.Annotations (phoenix_kit v1.7.111)

Copy Markdown View Source

Context for phoenix_kit_annotations — drawn-on-image shapes created via the Etcher overlay layer in the MediaBrowser modal.

Most callers won't use this directly — they go through PhoenixKit.Modules.Storage.EtcherAdapter which implements the Etcher.Storage behaviour and dispatches to this context. The module is exposed so admin tooling, audits, or background workers can do CRUD without reaching for the adapter.

Summary

Functions

Create an annotation for a file.

Delete an annotation by UUID.

Fetch a single annotation by UUID, or nil.

List annotations for a file, ordered by position then insertion time.

List annotations for a file together with a tooltip-friendly preview of their comment thread. Each element is a map

Restore comments soft-deleted by delete/1 and re-link them to a newly-created annotation row. Used by the etcher restore (undo of delete) flow: the original annotation uuid is gone, but the soft-deleted comments are still in the DB carrying metadata.annotation_uuid = original_uuid. We flip them back to status: "published" and rewrite metadata.annotation_uuid to point at the recreated row's uuid.

Update an annotation's geometry / style / metadata / position.

Types

attrs()

@type attrs() :: map()

uuid()

@type uuid() :: String.t()

Functions

create(attrs)

@spec create(attrs()) ::
  {:ok, PhoenixKit.Annotations.Annotation.t()} | {:error, Ecto.Changeset.t()}

Create an annotation for a file.

attrs accepts both atom- and string-keyed maps (the latter is what flows in from LiveView events). Keys: :file_uuid, :kind, :geometry, optional :creator_uuid, :style, :metadata, :position.

delete(uuid)

@spec delete(uuid()) :: :ok | {:error, :not_found | Ecto.Changeset.t()}

Delete an annotation by UUID.

Cascades to any linked comments — i.e. comments on the annotation's file that carry metadata.annotation_uuid pointing at this row. The cascade is a soft delete (status: "deleted") so reply chains stay attached as [removed] placeholders rather than disappearing silently. No-ops cleanly when PhoenixKitComments isn't installed.

get(uuid)

@spec get(uuid()) :: PhoenixKit.Annotations.Annotation.t() | nil

Fetch a single annotation by UUID, or nil.

list_for_file(file_uuid)

@spec list_for_file(uuid()) :: [PhoenixKit.Annotations.Annotation.t()]

List annotations for a file, ordered by position then insertion time.

list_for_file_with_previews(file_uuid)

@spec list_for_file_with_previews(uuid()) :: [map()]

List annotations for a file together with a tooltip-friendly preview of their comment thread. Each element is a map:

%{
  annotation: %Annotation{},
  first_comment: %{content, author, thumbnail_url} | nil,
  comment_count: integer()
}

Annotation comments are stored against the file (resource_type: "file", resource_uuid: file_uuid) with metadata.annotation_uuid pointing at the annotation, so they show up in the file's main comments thread alongside non-annotated discussion. This loader pulls every file comment in one query and groups by that metadata key.

If the comments package isn't installed, returns the same shape with first_comment: nil and comment_count: 0 so callers always handle one schema.

restore_linked_comments(file_uuid, original_uuid, new_uuid)

@spec restore_linked_comments(uuid(), uuid(), uuid()) :: non_neg_integer()

Restore comments soft-deleted by delete/1 and re-link them to a newly-created annotation row. Used by the etcher restore (undo of delete) flow: the original annotation uuid is gone, but the soft-deleted comments are still in the DB carrying metadata.annotation_uuid = original_uuid. We flip them back to status: "published" and rewrite metadata.annotation_uuid to point at the recreated row's uuid.

Returns the number of comments restored. No-ops cleanly when PhoenixKitComments isn't installed or when no soft-deleted matches are found.

update(uuid, attrs)

@spec update(uuid(), attrs()) ::
  {:ok, PhoenixKit.Annotations.Annotation.t()}
  | {:error, :not_found | Ecto.Changeset.t()}

Update an annotation's geometry / style / metadata / position.