chore: added more @spec

This commit is contained in:
2026-05-01 17:49:50 +02:00
parent abcae1dad7
commit 881056eb61
157 changed files with 6223 additions and 1647 deletions

View File

@@ -74,13 +74,21 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
defdelegate tag_chip_style(color), to: ListValues
embed_templates "post_editor_html/*"
embed_templates("post_editor_html/*")
@spec assign_socket(term()) :: term()
def assign_socket(socket) do
assigns = Map.put(socket.assigns, :project_metadata, project_metadata(socket.assigns.projects.active_project_id))
assigns =
Map.put(
socket.assigns,
:project_metadata,
project_metadata(socket.assigns.projects.active_project_id)
)
assign(socket, :post_editor, build(assigns))
end
@spec update(term(), term(), term()) :: term()
def update(socket, params, reload) do
case socket.assigns.current_tab do
%{type: :post, id: post_id} ->
@@ -91,7 +99,10 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
%Post{} = post ->
metadata = project_metadata(post.project_id)
canonical_language = canonical_language(post, metadata)
current_language = Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
current_language =
Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
requested_language = normalize_language(Map.get(params, "language"), current_language)
next_language =
@@ -117,6 +128,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
end
end
@spec persist_socket(term(), term(), term(), term(), term()) :: term()
def persist_socket(socket, post_id, action, reload, append_output) do
case Posts.get_post(post_id) do
nil ->
@@ -125,7 +137,10 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
%Post{} = post ->
metadata = project_metadata(post.project_id)
canonical_language = canonical_language(post, metadata)
active_language = Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
active_language =
Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
draft = current_draft(socket.assigns, post, metadata, active_language)
case persist(post, draft, active_language, metadata, action) do
@@ -135,9 +150,30 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
socket
|> assign(:workbench, workbench)
|> assign(:post_editor_drafts, put_nested_map(socket.assigns.post_editor_drafts, post_id, active_language, normalized_form))
|> assign(:post_editor_save_states, Map.put(socket.assigns.post_editor_save_states, post_id, save_state_for_action(action)))
|> assign(:tab_meta, Map.put(socket.assigns.tab_meta, {:post, post_id}, %{title: record_title(record, Posts.get_post!(post_id)), subtitle: Atom.to_string(record_status(record))}))
|> assign(
:post_editor_drafts,
put_nested_map(
socket.assigns.post_editor_drafts,
post_id,
active_language,
normalized_form
)
)
|> assign(
:post_editor_save_states,
Map.put(
socket.assigns.post_editor_save_states,
post_id,
save_state_for_action(action)
)
)
|> assign(
:tab_meta,
Map.put(socket.assigns.tab_meta, {:post, post_id}, %{
title: record_title(record, Posts.get_post!(post_id)),
subtitle: Atom.to_string(record_status(record))
})
)
|> reload.(workbench)
{:error, reason} ->
@@ -148,6 +184,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
end
end
@spec discard_socket(term(), term(), term(), term()) :: term()
def discard_socket(socket, post_id, reload, append_output) do
case Posts.get_post(post_id) do
nil ->
@@ -156,7 +193,9 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
%Post{} = post ->
metadata = project_metadata(post.project_id)
canonical_language = canonical_language(post, metadata)
active_language = Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
active_language =
Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
case discard(post, active_language, metadata) do
{:ok, restored_post} ->
@@ -164,9 +203,21 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
socket
|> assign(:workbench, workbench)
|> assign(:post_editor_drafts, delete_nested_map(socket.assigns.post_editor_drafts, post_id, active_language))
|> assign(:post_editor_save_states, Map.put(socket.assigns.post_editor_save_states, post_id, :discarded))
|> assign(:tab_meta, Map.put(socket.assigns.tab_meta, {:post, post_id}, %{title: restored_post.title || restored_post.slug || restored_post.id, subtitle: Atom.to_string(restored_post.status || :draft)}))
|> assign(
:post_editor_drafts,
delete_nested_map(socket.assigns.post_editor_drafts, post_id, active_language)
)
|> assign(
:post_editor_save_states,
Map.put(socket.assigns.post_editor_save_states, post_id, :discarded)
)
|> assign(
:tab_meta,
Map.put(socket.assigns.tab_meta, {:post, post_id}, %{
title: restored_post.title || restored_post.slug || restored_post.id,
subtitle: Atom.to_string(restored_post.status || :draft)
})
)
|> reload.(workbench)
{:error, reason} ->
@@ -177,6 +228,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
end
end
@spec delete_socket(term(), term(), term(), term()) :: term()
def delete_socket(socket, post_id, reload, append_output) do
case Posts.delete_post(post_id) do
{:ok, :deleted} ->
@@ -185,13 +237,28 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
socket
|> assign(:tab_meta, Map.delete(socket.assigns.tab_meta, {:post, post_id}))
|> assign(:post_editor_drafts, Map.delete(socket.assigns.post_editor_drafts, post_id))
|> assign(:post_editor_active_languages, Map.delete(socket.assigns.post_editor_active_languages, post_id))
|> assign(:post_editor_tag_queries, Map.delete(socket.assigns.post_editor_tag_queries, post_id))
|> assign(:post_editor_category_queries, Map.delete(socket.assigns.post_editor_category_queries, post_id))
|> assign(:post_editor_quick_actions_open, Map.delete(socket.assigns.post_editor_quick_actions_open, post_id))
|> assign(
:post_editor_active_languages,
Map.delete(socket.assigns.post_editor_active_languages, post_id)
)
|> assign(
:post_editor_tag_queries,
Map.delete(socket.assigns.post_editor_tag_queries, post_id)
)
|> assign(
:post_editor_category_queries,
Map.delete(socket.assigns.post_editor_category_queries, post_id)
)
|> assign(
:post_editor_quick_actions_open,
Map.delete(socket.assigns.post_editor_quick_actions_open, post_id)
)
|> assign(:post_editor_modes, Map.delete(socket.assigns.post_editor_modes, post_id))
|> assign(:post_editor_expanded, Map.delete(socket.assigns.post_editor_expanded, post_id))
|> assign(:post_editor_save_states, Map.delete(socket.assigns.post_editor_save_states, post_id))
|> assign(
:post_editor_save_states,
Map.delete(socket.assigns.post_editor_save_states, post_id)
)
|> reload.(workbench)
{:error, reason} ->
@@ -201,6 +268,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
end
end
@spec set_mode(term(), term(), term(), term()) :: term()
def set_mode(socket, post_id, mode, reload) do
workbench = socket.assigns.workbench
normalized_mode = normalize_mode(mode)
@@ -216,38 +284,67 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
end
socket
|> assign(:post_editor_modes, Map.put(socket.assigns.post_editor_modes, post_id, normalized_mode))
|> assign(
:post_editor_modes,
Map.put(socket.assigns.post_editor_modes, post_id, normalized_mode)
)
|> reload.(workbench)
end
@spec toggle_section(term(), term(), term(), term()) :: term()
def toggle_section(socket, post_id, section, reload) when section in [:metadata, :excerpt] do
workbench = socket.assigns.workbench
socket
|> assign(:post_editor_expanded, Map.put(socket.assigns.post_editor_expanded, post_id, toggled_sections(socket.assigns.post_editor_expanded, post_id, section)))
|> assign(
:post_editor_expanded,
Map.put(
socket.assigns.post_editor_expanded,
post_id,
toggled_sections(socket.assigns.post_editor_expanded, post_id, section)
)
)
|> reload.(workbench)
end
@spec select_language(term(), term(), term(), term()) :: term()
def select_language(socket, post_id, language, reload) do
workbench = socket.assigns.workbench
socket
|> assign(:post_editor_active_languages, Map.put(socket.assigns.post_editor_active_languages, post_id, normalize_language(language, language)))
|> assign(
:post_editor_active_languages,
Map.put(
socket.assigns.post_editor_active_languages,
post_id,
normalize_language(language, language)
)
)
|> reload.(workbench)
end
@spec toggle_quick_actions(term(), term(), term()) :: term()
def toggle_quick_actions(socket, post_id, reload) do
workbench = socket.assigns.workbench
socket
|> assign(:post_editor_quick_actions_open, Map.update(socket.assigns.post_editor_quick_actions_open, post_id, true, &(!&1)))
|> assign(
:post_editor_quick_actions_open,
Map.update(socket.assigns.post_editor_quick_actions_open, post_id, true, &(!&1))
)
|> reload.(workbench)
end
@spec detect_language(term(), term(), term(), term()) :: term()
def detect_language(socket, post_id, reload, append_output) do
if Map.get(socket.assigns, :offline_mode, true) do
socket
|> append_output.(translated("Detect Language"), translated("Automatic AI actions stay gated by airplane mode."), nil, "info")
|> append_output.(
translated("Detect Language"),
translated("Automatic AI actions stay gated by airplane mode."),
nil,
"info"
)
|> reload.(socket.assigns.workbench)
else
case Posts.get_post(post_id) do
@@ -257,14 +354,24 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
%Post{} = post ->
metadata = project_metadata(post.project_id)
canonical_language = canonical_language(post, metadata)
active_language = Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
active_language =
Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
draft = current_draft(socket.assigns, post, metadata, active_language)
text = Enum.join([Map.get(draft, "title", ""), Map.get(draft, "content", "")], "\n\n")
case AI.detect_language(text) do
{:ok, %{language_code: language_code}} when is_binary(language_code) and language_code != "" ->
{:ok, %{language_code: language_code}}
when is_binary(language_code) and language_code != "" ->
socket
|> put_draft_field(post_id, post, active_language, "language", normalize_language(language_code, canonical_language))
|> put_draft_field(
post_id,
post,
active_language,
"language",
normalize_language(language_code, canonical_language)
)
|> reload_with_assigned_workbench(reload)
{:error, reason} ->
@@ -274,17 +381,28 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
_other ->
socket
|> append_output.(translated("Detect Language"), translated("Language detection failed."), nil, "error")
|> append_output.(
translated("Detect Language"),
translated("Language detection failed."),
nil,
"error"
)
|> reload.(socket.assigns.workbench)
end
end
end
end
@spec translate(term(), term(), term(), term(), term()) :: term()
def translate(socket, post_id, language, reload, append_output) do
if Map.get(socket.assigns, :offline_mode, true) do
socket
|> append_output.(translated("Translate"), translated("Automatic AI actions stay gated by airplane mode."), nil, "info")
|> append_output.(
translated("Translate"),
translated("Automatic AI actions stay gated by airplane mode."),
nil,
"info"
)
|> reload.(socket.assigns.workbench)
else
normalized_language = normalize_language(language, "")
@@ -298,9 +416,18 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
content: translation.content
}) do
socket
|> assign(:post_editor_active_languages, Map.put(socket.assigns.post_editor_active_languages, post_id, normalized_language))
|> assign(:post_editor_drafts, delete_nested_map(socket.assigns.post_editor_drafts, post_id, normalized_language))
|> assign(:post_editor_quick_actions_open, Map.put(socket.assigns.post_editor_quick_actions_open, post_id, false))
|> assign(
:post_editor_active_languages,
Map.put(socket.assigns.post_editor_active_languages, post_id, normalized_language)
)
|> assign(
:post_editor_drafts,
delete_nested_map(socket.assigns.post_editor_drafts, post_id, normalized_language)
)
|> assign(
:post_editor_quick_actions_open,
Map.put(socket.assigns.post_editor_quick_actions_open, post_id, false)
)
|> reload.(socket.assigns.workbench)
else
{:error, reason} ->
@@ -317,6 +444,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
end
end
@spec apply_ai_suggestions(term(), term(), term(), term(), term()) :: term()
def apply_ai_suggestions(socket, post_id, fields, reload, append_output) do
case Posts.get_post(post_id) do
nil ->
@@ -340,12 +468,30 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
case Posts.update_post(post_id, attrs) do
{:ok, updated_post} ->
metadata = project_metadata(updated_post.project_id)
active_language = Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language(updated_post, metadata))
active_language =
Map.get(
socket.assigns.post_editor_active_languages,
post_id,
canonical_language(updated_post, metadata)
)
refreshed_form = persisted_form(updated_post, metadata, active_language)
socket
|> assign(:post_editor_drafts, put_nested_map(socket.assigns.post_editor_drafts, post_id, active_language, refreshed_form))
|> assign(:post_editor_save_states, Map.put(socket.assigns.post_editor_save_states, post_id, :dirty))
|> assign(
:post_editor_drafts,
put_nested_map(
socket.assigns.post_editor_drafts,
post_id,
active_language,
refreshed_form
)
)
|> assign(
:post_editor_save_states,
Map.put(socket.assigns.post_editor_save_states, post_id, :dirty)
)
|> assign(:shell_overlay, nil)
|> reload.(socket.assigns.workbench)
@@ -358,6 +504,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
end
end
@spec insert_content(term(), term(), term(), term()) :: term()
def insert_content(socket, post_id, snippet, reload) do
socket
|> Phoenix.LiveView.push_event("post-editor-insert-content", %{id: post_id, content: snippet})
@@ -365,6 +512,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|> reload.(socket.assigns.workbench)
end
@spec add_list_value(term(), term(), term(), term(), term()) :: term()
def add_list_value(socket, post_id, kind, value, reload) when kind in [:tags, :categories] do
case Posts.get_post(post_id) do
nil ->
@@ -373,7 +521,10 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
%Post{} = post ->
metadata = project_metadata(post.project_id)
canonical_language = canonical_language(post, metadata)
active_language = Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
active_language =
Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
draft = current_draft(socket.assigns, post, metadata, active_language)
normalized = normalize_list_entry(value)
@@ -398,6 +549,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
end
end
@spec remove_list_value(term(), term(), term(), term(), term()) :: term()
def remove_list_value(socket, post_id, kind, value, reload) when kind in [:tags, :categories] do
case Posts.get_post(post_id) do
nil ->
@@ -406,9 +558,18 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
%Post{} = post ->
metadata = project_metadata(post.project_id)
canonical_language = canonical_language(post, metadata)
active_language = Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
active_language =
Map.get(socket.assigns.post_editor_active_languages, post_id, canonical_language)
draft = current_draft(socket.assigns, post, metadata, active_language)
updated = draft |> Map.get(field_key(kind), "") |> csv_to_list() |> Enum.reject(&(&1 == value)) |> Enum.join(", ")
updated =
draft
|> Map.get(field_key(kind), "")
|> csv_to_list()
|> Enum.reject(&(&1 == value))
|> Enum.join(", ")
socket
|> put_draft_field(post_id, post, active_language, field_key(kind), updated)
@@ -416,6 +577,7 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
end
end
@spec build(term()) :: term()
def build(%{current_tab: %{type: :post, id: post_id}} = assigns) do
case Posts.get_post(post_id) do
nil ->
@@ -424,7 +586,10 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
%Post{} = post ->
metadata = assigned_project_metadata(assigns)
canonical_language = canonical_language(post, metadata)
active_language = Map.get(assigns.post_editor_active_languages, post.id, canonical_language)
active_language =
Map.get(assigns.post_editor_active_languages, post.id, canonical_language)
translations = translations(post.id)
persisted = DraftManagement.persisted_form(post, metadata, active_language, translations)
@@ -453,13 +618,15 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
metadata_expanded: Map.get(expanded, :metadata, false),
excerpt_expanded: Map.get(expanded, :excerpt, false),
mode: Map.get(assigns.post_editor_modes, post.id, :markdown),
editing_canonical?: editing_canonical_language?(translations, active_language, canonical_language),
editing_canonical?:
editing_canonical_language?(translations, active_language, canonical_language),
can_publish?: post.status == :draft,
can_delete?: post.status == :published,
has_published_version?: has_published_version?(post),
discard_label: discard_label(post),
discard_title: discard_title(post),
detect_language_enabled?: not blank?(Map.get(form, "title")) or not blank?(Map.get(form, "content")),
detect_language_enabled?:
not blank?(Map.get(form, "title")) or not blank?(Map.get(form, "content")),
can_translate?: Enum.any?(languages(metadata), &(&1 != canonical_language)),
languages: languages(metadata),
form: form,
@@ -469,16 +636,45 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
tag_values: tag_values(form),
tag_chips: tag_chips(form, Tags.list_tags(post.project_id)),
tag_query: query_value(assigns, :tags, post.id),
tag_query_addable?: query_addable?(query_value(assigns, :tags, post.id), tag_values(form), Tags.list_tags(post.project_id), fn option -> option.name end),
tag_query_addable?:
query_addable?(
query_value(assigns, :tags, post.id),
tag_values(form),
Tags.list_tags(post.project_id),
fn option -> option.name end
),
category_values: category_values(form),
category_query: query_value(assigns, :categories, post.id),
category_options: metadata.categories || [],
category_query_addable?: query_addable?(query_value(assigns, :categories, post.id), category_values(form), metadata.categories || [], & &1),
tag_suggestions: tag_suggestions(form, Tags.list_tags(post.project_id), query_value(assigns, :tags, post.id)),
category_suggestions: category_suggestions(form, metadata.categories || [], query_value(assigns, :categories, post.id)),
category_query_addable?:
query_addable?(
query_value(assigns, :categories, post.id),
category_values(form),
metadata.categories || [],
& &1
),
tag_suggestions:
tag_suggestions(
form,
Tags.list_tags(post.project_id),
query_value(assigns, :tags, post.id)
),
category_suggestions:
category_suggestions(
form,
metadata.categories || [],
query_value(assigns, :categories, post.id)
),
gallery_count: gallery_count(form),
preview_url: preview_url(post, active_language, canonical_language, Map.get(assigns.post_editor_modes, post.id, :markdown)),
translation_flags: translation_flags(post, canonical_language, active_language, translations),
preview_url:
preview_url(
post,
active_language,
canonical_language,
Map.get(assigns.post_editor_modes, post.id, :markdown)
),
translation_flags:
translation_flags(post, canonical_language, active_language, translations),
linked_media: linked_media(post.id),
post_links: post_links(post.id),
footer: footer(post, current_translation, active_language, canonical_language)
@@ -488,17 +684,21 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
def build(_assigns), do: nil
@spec post_status_label(term()) :: term()
def post_status_label(status), do: ShellData.dashboard_status_label(status)
@spec post_editor_save_state_label(term()) :: term()
def post_editor_save_state_label(:dirty), do: translated("Unsaved")
def post_editor_save_state_label(:saved), do: translated("Saved")
def post_editor_save_state_label(:published), do: translated("Published")
def post_editor_save_state_label(:discarded), do: translated("Reverted")
def post_editor_save_state_label(_state), do: translated("Idle")
@spec post_editor_mode_label(term()) :: term()
def post_editor_mode_label(:markdown), do: translated("Markdown")
def post_editor_mode_label(:preview), do: translated("Preview")
@spec translated(term(), term()) :: term()
def translated(text, bindings \\ %{}),
do: ShellData.translate(text, bindings, BDS.Desktop.UILocale.current())