fix: more alignment with old app
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -4,10 +4,9 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
use Phoenix.Component
|
||||
|
||||
import Ecto.Query
|
||||
import Phoenix.HTML
|
||||
|
||||
alias BDS.Desktop.ShellData
|
||||
alias BDS.{I18n, Metadata, PostLinks, Posts, Repo, Tags, Templates}
|
||||
alias BDS.{AI, I18n, Metadata, PostLinks, Posts, Preview, Repo, Tags, Templates}
|
||||
alias BDS.Media.Media
|
||||
alias BDS.Posts.{Post, Translation}
|
||||
alias BDS.UI.Workbench
|
||||
@@ -40,16 +39,14 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
end
|
||||
|
||||
draft = normalize_params(params, current_language, next_language)
|
||||
workbench = Workbench.mark_dirty(socket.assigns.workbench, :post, post_id)
|
||||
current = current_draft(socket.assigns, post, metadata, next_language)
|
||||
dirty? = draft != current
|
||||
|
||||
socket
|
||||
|> assign(:workbench, workbench)
|
||||
|> assign(:post_editor_drafts, put_nested_map(socket.assigns.post_editor_drafts, post_id, next_language, draft))
|
||||
|> assign(:post_editor_active_languages, Map.put(socket.assigns.post_editor_active_languages, post_id, next_language))
|
||||
|> assign(:post_editor_save_states, Map.put(socket.assigns.post_editor_save_states, post_id, :dirty))
|
||||
|> assign(:tab_meta, Map.put(socket.assigns.tab_meta, {:post, post_id}, %{title: draft["title"], subtitle: Atom.to_string(post.status || :draft)}))
|
||||
|> maybe_drop_old_language_draft(post_id, current_language, next_language)
|
||||
|> reload.(workbench)
|
||||
|> put_query_state(post_id, :tags, Map.get(params, "tag_query", ""))
|
||||
|> put_query_state(post_id, :categories, Map.get(params, "category_query", ""))
|
||||
|> maybe_update_draft(post_id, post, current_language, next_language, draft, dirty?)
|
||||
|> reload_with_assigned_workbench(reload)
|
||||
end
|
||||
|
||||
_other ->
|
||||
@@ -126,6 +123,9 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
|> 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_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))
|
||||
@@ -162,6 +162,186 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
|> reload.(workbench)
|
||||
end
|
||||
|
||||
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)))
|
||||
|> reload.(workbench)
|
||||
end
|
||||
|
||||
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")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
else
|
||||
case Repo.get(Post, post_id) do
|
||||
nil ->
|
||||
socket
|
||||
|
||||
%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)
|
||||
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 != "" ->
|
||||
socket
|
||||
|> put_draft_field(post_id, post, active_language, "language", normalize_language(language_code, canonical_language))
|
||||
|> reload_with_assigned_workbench(reload)
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Detect Language"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
|
||||
_other ->
|
||||
socket
|
||||
|> append_output.(translated("Detect Language"), translated("Language detection failed."), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
else
|
||||
normalized_language = normalize_language(language, "")
|
||||
|
||||
case AI.translate_post(post_id, normalized_language) do
|
||||
{:ok, translation} ->
|
||||
with {:ok, _saved_translation} <-
|
||||
Posts.upsert_post_translation(post_id, normalized_language, %{
|
||||
title: translation.title,
|
||||
excerpt: translation.excerpt,
|
||||
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))
|
||||
|> reload.(socket.assigns.workbench)
|
||||
else
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Translate"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("Translate"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def apply_ai_suggestions(socket, post_id, fields, reload, append_output) do
|
||||
case Repo.get(Post, post_id) do
|
||||
nil ->
|
||||
socket
|
||||
|
||||
%Post{} ->
|
||||
attrs =
|
||||
fields
|
||||
|> Enum.reduce(%{}, fn field, acc ->
|
||||
case field.key do
|
||||
"title" -> Map.put(acc, :title, blank_to_nil(field.suggested_value))
|
||||
"excerpt" -> Map.put(acc, :excerpt, blank_to_nil(field.suggested_value))
|
||||
"slug" -> Map.put(acc, :slug, blank_to_nil(field.suggested_value))
|
||||
_other -> acc
|
||||
end
|
||||
end)
|
||||
|
||||
if map_size(attrs) == 0 do
|
||||
socket |> assign(:shell_overlay, nil)
|
||||
else
|
||||
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))
|
||||
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(:shell_overlay, nil)
|
||||
|> reload.(socket.assigns.workbench)
|
||||
|
||||
{:error, reason} ->
|
||||
socket
|
||||
|> append_output.(translated("AI Suggestions"), inspect(reason), nil, "error")
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def insert_content(socket, post_id, snippet, reload) do
|
||||
socket
|
||||
|> Phoenix.LiveView.push_event("post-editor-insert-content", %{id: post_id, content: snippet})
|
||||
|> assign(:shell_overlay, nil)
|
||||
|> reload.(socket.assigns.workbench)
|
||||
end
|
||||
|
||||
def add_list_value(socket, post_id, kind, value, reload) when kind in [:tags, :categories] do
|
||||
case Repo.get(Post, post_id) do
|
||||
nil ->
|
||||
socket
|
||||
|
||||
%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)
|
||||
draft = current_draft(socket.assigns, post, metadata, active_language)
|
||||
normalized = normalize_list_entry(value)
|
||||
|
||||
if normalized in [nil, ""] do
|
||||
socket
|
||||
else
|
||||
ensure_list_value(post.project_id, kind, normalized)
|
||||
|
||||
updated =
|
||||
draft
|
||||
|> Map.get(field_key(kind), "")
|
||||
|> csv_to_list()
|
||||
|> Kernel.++([normalized])
|
||||
|> Enum.uniq()
|
||||
|> Enum.join(", ")
|
||||
|
||||
socket
|
||||
|> put_query_state(post_id, kind, "")
|
||||
|> put_draft_field(post_id, post, active_language, field_key(kind), updated)
|
||||
|> reload_with_assigned_workbench(reload)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def remove_list_value(socket, post_id, kind, value, reload) when kind in [:tags, :categories] do
|
||||
case Repo.get(Post, post_id) do
|
||||
nil ->
|
||||
socket
|
||||
|
||||
%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)
|
||||
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(", ")
|
||||
|
||||
socket
|
||||
|> put_draft_field(post_id, post, active_language, field_key(kind), updated)
|
||||
|> reload_with_assigned_workbench(reload)
|
||||
end
|
||||
end
|
||||
|
||||
def build(%{current_tab: %{type: :post, id: post_id}} = assigns) do
|
||||
case Repo.get(Post, post_id) do
|
||||
nil ->
|
||||
@@ -190,20 +370,40 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
%{
|
||||
id: post.id,
|
||||
display_title: display_title(form["title"], post.slug, post.id),
|
||||
subtitle: active_language_subtitle(active_language, canonical_language),
|
||||
subtitle: nil,
|
||||
slug: post.slug || post.id,
|
||||
status: current_status(post.status, active_language, canonical_language, current_translation),
|
||||
status: post.status || :draft,
|
||||
dirty?: Workbench.dirty?(assigns.workbench, :post, post.id),
|
||||
save_state: Map.get(assigns.post_editor_save_states, post.id, :idle),
|
||||
quick_actions_open?: Map.get(assigns.post_editor_quick_actions_open, post.id, false),
|
||||
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),
|
||||
can_publish?: (post.status || :draft) == :draft,
|
||||
can_delete?: (post.status || :draft) == :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")),
|
||||
can_translate?: Enum.any?(languages(metadata), &(&1 != canonical_language)),
|
||||
languages: languages(metadata),
|
||||
form: form,
|
||||
template_options: template_options(post.project_id),
|
||||
tag_options: Enum.map(Tags.list_tags(post.project_id), & &1.name),
|
||||
show_template_selector?: template_options(post.project_id) != [],
|
||||
tag_options: Tags.list_tags(post.project_id),
|
||||
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),
|
||||
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)),
|
||||
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),
|
||||
linked_media: linked_media(post.id),
|
||||
post_links: post_links(post.id),
|
||||
@@ -313,6 +513,8 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
|
||||
def translated(text, bindings \\ %{}), do: ShellData.translate(text, bindings, Process.get(:bds_ui_locale))
|
||||
|
||||
def ai_overlay_fields(selected), do: Enum.filter(selected, & &1.accepted)
|
||||
|
||||
def project_metadata(nil), do: %{main_language: "en", blog_languages: []}
|
||||
|
||||
def project_metadata(project_id) do
|
||||
@@ -322,32 +524,161 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
_error -> %{main_language: "en", blog_languages: []}
|
||||
end
|
||||
|
||||
defp editor_toolbar(assigns) do
|
||||
~H"""
|
||||
<%= if Enum.any?(@toolbar_buttons) do %>
|
||||
<div class="editor-toolbar">
|
||||
<%= for button <- @toolbar_buttons do %>
|
||||
<button
|
||||
class={["editor-toolbar-button", if(button.destructive, do: "is-destructive")]}
|
||||
data-testid="editor-toolbar-overlay-button"
|
||||
type="button"
|
||||
phx-click="open_overlay"
|
||||
phx-value-kind={button.kind}
|
||||
>
|
||||
<%= translated(button.label) %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
"""
|
||||
def tag_chip_style(nil), do: nil
|
||||
|
||||
def tag_chip_style(color) do
|
||||
normalized = normalize_color(color)
|
||||
|
||||
if normalized do
|
||||
"background-color: #{normalized}; color: #{contrast_color(normalized)}; border-color: #{normalized};"
|
||||
end
|
||||
end
|
||||
|
||||
defp assigned_project_metadata(assigns), do: Map.get(assigns, :project_metadata, %{})
|
||||
|
||||
defp current_status(post_status, active_language, canonical_language, current_translation) do
|
||||
if active_language == canonical_language, do: post_status, else: translation_status(current_translation)
|
||||
defp maybe_update_draft(socket, post_id, post, current_language, next_language, draft, true) do
|
||||
workbench = Workbench.mark_dirty(socket.assigns.workbench, :post, post_id)
|
||||
|
||||
socket
|
||||
|> assign(:workbench, workbench)
|
||||
|> assign(:post_editor_drafts, put_nested_map(socket.assigns.post_editor_drafts, post_id, next_language, draft))
|
||||
|> assign(:post_editor_active_languages, Map.put(socket.assigns.post_editor_active_languages, post_id, next_language))
|
||||
|> assign(:post_editor_save_states, Map.put(socket.assigns.post_editor_save_states, post_id, :dirty))
|
||||
|> assign(:tab_meta, Map.put(socket.assigns.tab_meta, {:post, post_id}, %{title: draft["title"], subtitle: Atom.to_string(post.status || :draft)}))
|
||||
|> maybe_drop_old_language_draft(post_id, current_language, next_language)
|
||||
end
|
||||
|
||||
defp maybe_update_draft(socket, post_id, _post, _current_language, next_language, _draft, false) do
|
||||
assign(socket, :post_editor_active_languages, Map.put(socket.assigns.post_editor_active_languages, post_id, next_language))
|
||||
end
|
||||
|
||||
defp put_draft_field(socket, post_id, post, active_language, field, value) do
|
||||
metadata = project_metadata(post.project_id)
|
||||
draft = Map.put(current_draft(socket.assigns, post, metadata, active_language), field, value)
|
||||
workbench = Workbench.mark_dirty(socket.assigns.workbench, :post, post_id)
|
||||
|
||||
socket
|
||||
|> assign(:workbench, workbench)
|
||||
|> assign(:post_editor_drafts, put_nested_map(socket.assigns.post_editor_drafts, post_id, active_language, draft))
|
||||
|> assign(:post_editor_save_states, Map.put(socket.assigns.post_editor_save_states, post_id, :dirty))
|
||||
end
|
||||
|
||||
defp put_query_state(socket, post_id, kind, value) do
|
||||
key = query_key(kind)
|
||||
assign(socket, key, Map.put(Map.get(socket.assigns, key, %{}), post_id, to_string(value || "")))
|
||||
end
|
||||
|
||||
defp query_value(assigns, kind, post_id) do
|
||||
assigns
|
||||
|> Map.get(query_key(kind), %{})
|
||||
|> Map.get(post_id, "")
|
||||
end
|
||||
|
||||
defp query_key(:tags), do: :post_editor_tag_queries
|
||||
defp query_key(:categories), do: :post_editor_category_queries
|
||||
|
||||
defp field_key(:tags), do: "tags"
|
||||
defp field_key(:categories), do: "categories"
|
||||
|
||||
defp tag_values(form), do: csv_to_list(Map.get(form, "tags", ""))
|
||||
defp category_values(form), do: csv_to_list(Map.get(form, "categories", ""))
|
||||
|
||||
defp tag_suggestions(form, options, query) do
|
||||
selected = MapSet.new(tag_values(form))
|
||||
filter_suggestions(options, query, fn option -> option.name end, selected)
|
||||
end
|
||||
|
||||
defp tag_chips(form, options) do
|
||||
option_map = Map.new(options, fn option -> {option.name, option} end)
|
||||
|
||||
Enum.map(tag_values(form), fn name ->
|
||||
option = Map.get(option_map, name)
|
||||
%{name: name, color: option && option.color}
|
||||
end)
|
||||
end
|
||||
|
||||
defp category_suggestions(form, options, query) do
|
||||
selected = MapSet.new(category_values(form))
|
||||
filter_suggestions(options, query, & &1, selected)
|
||||
end
|
||||
|
||||
defp filter_suggestions(options, query, labeler, selected) do
|
||||
query = normalize_query(query)
|
||||
|
||||
options
|
||||
|> Enum.filter(fn option ->
|
||||
label = labeler.(option)
|
||||
not MapSet.member?(selected, label) and (query == "" or String.contains?(String.downcase(label), query))
|
||||
end)
|
||||
|> Enum.take(8)
|
||||
end
|
||||
|
||||
defp query_addable?(query, selected_values, options, labeler) do
|
||||
normalized = normalize_query(query)
|
||||
|
||||
normalized != "" and
|
||||
normalized not in Enum.map(selected_values, &String.downcase/1) and
|
||||
not Enum.any?(options, fn option -> String.downcase(labeler.(option)) == normalized end)
|
||||
end
|
||||
|
||||
defp normalize_query(value) do
|
||||
value
|
||||
|> to_string()
|
||||
|> String.trim()
|
||||
|> String.downcase()
|
||||
end
|
||||
|
||||
defp normalize_list_entry(value) do
|
||||
value
|
||||
|> to_string()
|
||||
|> String.trim()
|
||||
|> String.downcase()
|
||||
end
|
||||
|
||||
defp ensure_list_value(project_id, :tags, value) do
|
||||
if Enum.any?(Tags.list_tags(project_id), &(String.downcase(&1.name) == value)) do
|
||||
:ok
|
||||
else
|
||||
_ = Tags.create_tag(%{project_id: project_id, name: value})
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp ensure_list_value(project_id, :categories, value) do
|
||||
{:ok, metadata} = Metadata.get_project_metadata(project_id)
|
||||
|
||||
if value in (metadata.categories || []) do
|
||||
:ok
|
||||
else
|
||||
_ = Metadata.add_category(project_id, value)
|
||||
:ok
|
||||
end
|
||||
rescue
|
||||
_error -> :ok
|
||||
end
|
||||
|
||||
defp normalize_color(nil), do: nil
|
||||
defp normalize_color(""), do: nil
|
||||
|
||||
defp normalize_color("#" <> rest = color) when byte_size(rest) == 6 do
|
||||
if String.match?(rest, ~r/\A[0-9a-fA-F]{6}\z/), do: color, else: nil
|
||||
end
|
||||
|
||||
defp normalize_color(_color), do: nil
|
||||
|
||||
defp contrast_color("#" <> rgb) do
|
||||
<<r::binary-size(2), g::binary-size(2), b::binary-size(2)>> = rgb
|
||||
{red, _} = Integer.parse(r, 16)
|
||||
{green, _} = Integer.parse(g, 16)
|
||||
{blue, _} = Integer.parse(b, 16)
|
||||
luminance = (red * 299 + green * 587 + blue * 114) / 1000
|
||||
if luminance > 150, do: "#1e1e1e", else: "#ffffff"
|
||||
end
|
||||
|
||||
defp contrast_color(_color), do: "#ffffff"
|
||||
|
||||
defp reload_with_assigned_workbench(socket, reload), do: reload.(socket, socket.assigns.workbench)
|
||||
|
||||
defp persisted_form(post, metadata, active_language, translations) do
|
||||
canonical_language = canonical_language(post, metadata)
|
||||
translation = Map.get(translations, active_language)
|
||||
@@ -417,10 +748,6 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
|> Enum.uniq()
|
||||
end
|
||||
|
||||
defp translation_status(nil), do: :draft
|
||||
defp translation_status(%Translation{status: status}) when not is_nil(status), do: status
|
||||
defp translation_status(_translation), do: :draft
|
||||
|
||||
defp template_options(project_id) do
|
||||
Repo.all(
|
||||
from template in Templates.Template,
|
||||
@@ -523,14 +850,52 @@ defmodule BDS.Desktop.ShellLive.PostEditor do
|
||||
blank_to_nil(title) || blank_to_nil(slug) || fallback_id || translated("Untitled")
|
||||
end
|
||||
|
||||
defp active_language_subtitle(active_language, canonical_language) do
|
||||
if active_language == canonical_language do
|
||||
translated("Canonical draft")
|
||||
defp has_published_version?(%Post{} = post), do: not is_nil(post.published_at) or post.file_path not in [nil, ""]
|
||||
|
||||
defp discard_label(%Post{} = post) do
|
||||
if has_published_version?(post), do: translated("Discard Changes"), else: translated("Discard Draft")
|
||||
end
|
||||
|
||||
defp discard_title(%Post{} = post) do
|
||||
if has_published_version?(post), do: translated("Discard changes and restore the published version"), else: translated("Delete this unpublished draft")
|
||||
end
|
||||
|
||||
defp gallery_count(form) do
|
||||
form
|
||||
|> Map.get("content", "")
|
||||
|> to_string()
|
||||
|> then(&Regex.scan(~r/!\[[^\]]*\]\([^\)]+\)/, &1))
|
||||
|> length()
|
||||
end
|
||||
|
||||
defp preview_url(_post, _active_language, _canonical_language, mode) when mode != :preview, do: nil
|
||||
|
||||
defp preview_url(%Post{} = post, active_language, canonical_language, :preview) do
|
||||
with {:ok, server} <- Preview.start_preview(post.project_id) do
|
||||
base_url = "http://#{server.host}:#{server.port}"
|
||||
query =
|
||||
%{}
|
||||
|> maybe_put_query("draft", "true")
|
||||
|> maybe_put_query("post_id", post.id)
|
||||
|> maybe_put_query("lang", active_language != canonical_language && active_language)
|
||||
|
||||
base_url <> canonical_preview_path(post.created_at, post.slug) <> "?" <> URI.encode_query(query)
|
||||
else
|
||||
translated("Translation: %{language}", %{language: String.upcase(active_language)})
|
||||
_other -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp canonical_preview_path(created_at_ms, slug) do
|
||||
datetime = DateTime.from_unix!(created_at_ms, :millisecond)
|
||||
"/#{datetime.year}/#{pad2(datetime.month)}/#{pad2(datetime.day)}/#{slug || ""}"
|
||||
end
|
||||
|
||||
defp pad2(value), do: value |> Integer.to_string() |> String.pad_leading(2, "0")
|
||||
|
||||
defp maybe_put_query(query, _key, false), do: query
|
||||
defp maybe_put_query(query, _key, nil), do: query
|
||||
defp maybe_put_query(query, key, value), do: Map.put(query, key, value)
|
||||
|
||||
defp save_canonical_draft(%Post{id: post_id}, draft) do
|
||||
Posts.update_post(post_id, %{
|
||||
title: blank_to_nil(Map.get(draft, "title")),
|
||||
|
||||
Reference in New Issue
Block a user