Files
bDS2/lib/bds/desktop/shell_live/overlay_components.ex

397 lines
11 KiB
Elixir

defmodule BDS.Desktop.ShellLive.OverlayComponents do
@moduledoc false
use Phoenix.Component
use Gettext, backend: BDS.Gettext
import Ecto.Query
alias BDS.{I18n, Media, Metadata, Posts, Repo}
alias BDS.Media.Media, as: MediaRecord
alias BDS.Media.Translation, as: MediaTranslation
alias BDS.Posts.{Post, PostMedia, Translation}
alias BDS.Tags.Tag
embed_templates("overlay_html/*")
def context(assigns, tab_title, tab_subtitle) do
project_id = assigns.projects.active_project_id
metadata = project_metadata(project_id)
current_tab = assigns.current_tab
page_language = assigns.page_language
posts = posts(project_id)
media = media(project_id)
%{
current_tab: %{
type: current_tab.type,
id: current_tab.id,
title: tab_title,
subtitle: tab_subtitle
},
current_post_language: source_language(current_tab, metadata),
current_media_language: source_language(current_tab, metadata),
posts: posts,
media: media,
post_media_ids: post_media_ids(current_tab),
blog_languages: blog_languages(metadata),
language_names: language_names(),
language_flags: language_flags(),
existing_translations: existing_translations(current_tab),
ai_title: BDS.Gettext.lgettext(page_language, "ui", "AI Suggestions"),
insert_link_title: BDS.Gettext.lgettext(page_language, "ui", "Insert Link"),
insert_media_title: BDS.Gettext.lgettext(page_language, "ui", "Insert Media"),
language_picker_title: BDS.Gettext.lgettext(page_language, "ui", "Translate"),
gallery_title: tab_title,
ai_fields: ai_fields(current_tab, tab_title, tab_subtitle, page_language),
delete_details: delete_details(current_tab, page_language),
merge_details: merge_details(project_id, page_language)
}
end
def kind("ai_suggestions"), do: :ai_suggestions
def kind("insert_link"), do: :insert_link
def kind("insert_media"), do: :insert_media
def kind("language_picker"), do: :language_picker
def kind("confirm_delete"), do: :confirm_delete
def kind("confirm_merge"), do: :confirm_merge
def kind("gallery"), do: :gallery
def kind(_kind), do: nil
def tab("internal"), do: :internal
def tab("external"), do: :external
def tab(_tab), do: :internal
def markdown_link(text, url), do: "[#{text}](#{url})"
def project_metadata(nil), do: %{main_language: "en", blog_languages: []}
def project_metadata(project_id) do
{:ok, metadata} = Metadata.get_project_metadata(project_id)
metadata
rescue
_error -> %{main_language: "en", blog_languages: []}
end
defp posts(nil), do: []
defp posts(project_id) do
Repo.all(
from post in Post,
where: post.project_id == ^project_id,
order_by: [desc: post.updated_at, desc: post.created_at],
select: %{
id: post.id,
title: post.title,
slug: post.slug,
status: post.status,
published_at: post.published_at,
updated_at: post.updated_at,
language: post.language
}
)
|> Enum.map(fn post ->
%{
id: post.id,
title: post.title || post.slug || post.id,
status: Atom.to_string(post.status || :draft),
canonical_url: canonical_post_url(post)
}
end)
end
defp media(nil), do: []
defp media(project_id) do
Repo.all(
from media in MediaRecord,
where: media.project_id == ^project_id,
order_by: [desc: media.updated_at, desc: media.created_at],
select: %{
id: media.id,
title: media.title,
original_name: media.original_name,
mime_type: media.mime_type,
alt: media.alt,
caption: media.caption
}
)
|> Enum.map(fn media ->
%{
id: media.id,
title: media.title || media.original_name || media.id,
original_name: media.original_name || media.id,
is_image: String.starts_with?(to_string(media.mime_type || ""), "image/"),
thumbnail_url: "/media-thumbnail/#{media.id}",
image_url: "/media-thumbnail/#{media.id}?size=large",
alt_text: media.alt || media.caption || media.title
}
end)
end
defp post_media_ids(%{type: :post, id: post_id}) do
Repo.all(
from pm in PostMedia,
where: pm.post_id == ^post_id,
order_by: [asc: pm.sort_order, asc: pm.media_id],
select: pm.media_id
)
rescue
_error -> []
end
defp post_media_ids(_tab), do: []
defp existing_translations(%{type: :post, id: post_id}) do
Repo.all(
from translation in Translation,
where: translation.translation_for == ^post_id,
select: {translation.language, translation.status}
)
|> Map.new(fn {language, status} -> {language, Atom.to_string(status || :draft)} end)
rescue
_error -> %{}
end
defp existing_translations(%{type: :media, id: media_id}) do
Repo.all(
from translation in MediaTranslation,
where: translation.translation_for == ^media_id,
select: {translation.language, "draft"}
)
|> Map.new(fn {language, status} -> {language, status} end)
rescue
_error -> %{}
end
defp existing_translations(_tab), do: %{}
defp blog_languages(metadata) do
([metadata.main_language || "en"] ++
(metadata.blog_languages || []) ++ Enum.map(I18n.supported_languages(), & &1.code))
|> Enum.reject(&is_nil/1)
|> Enum.uniq()
end
defp source_language(%{type: :post, id: post_id}, metadata) do
case Posts.get_post(post_id) do
%Post{language: language} when is_binary(language) and language != "" -> language
_other -> metadata.main_language || "en"
end
rescue
_error -> metadata.main_language || "en"
end
defp source_language(%{type: :media, id: media_id}, metadata) do
case Media.get_media(media_id) do
%MediaRecord{language: language} when is_binary(language) and language != "" -> language
_other -> metadata.main_language || "en"
end
rescue
_error -> metadata.main_language || "en"
end
defp source_language(_tab, metadata), do: metadata.main_language || "en"
defp language_names do
%{
"en" => "English",
"de" => "Deutsch",
"fr" => "Francais",
"it" => "Italiano",
"es" => "Espanol"
}
end
defp language_flags do
I18n.supported_languages()
|> Enum.into(%{}, fn language -> {language.code, I18n.flag(language.code)} end)
end
defp ai_fields(%{type: :post, id: post_id}, title, subtitle, page_language) do
case Posts.get_post(post_id) do
%Post{} = post ->
[
%{
key: "title",
label: BDS.Gettext.lgettext(page_language, "ui", "Title"),
current_value: post.title || title,
suggested_value: "",
locked: false,
loading: true
},
%{
key: "excerpt",
label: BDS.Gettext.lgettext(page_language, "ui", "Excerpt"),
current_value: post.excerpt || subtitle,
suggested_value: "",
locked: false,
loading: true
},
%{
key: "slug",
label: BDS.Gettext.lgettext(page_language, "ui", "Slug"),
current_value: post.slug || slugify(post.title || title),
suggested_value: "",
locked: post.status == :published,
loading: true
}
]
_other ->
[]
end
rescue
_error -> []
end
defp ai_fields(%{type: :media, id: media_id}, title, _subtitle, page_language) do
case Media.get_media(media_id) do
%MediaRecord{} = media ->
[
%{
key: "title",
label: BDS.Gettext.lgettext(page_language, "ui", "Title"),
current_value: media.title || title,
suggested_value: "",
locked: false,
loading: true
},
%{
key: "alt",
label: BDS.Gettext.lgettext(page_language, "ui", "Alt Text"),
current_value: media.alt || "",
suggested_value: "",
locked: false,
loading: true
},
%{
key: "caption",
label: BDS.Gettext.lgettext(page_language, "ui", "Caption"),
current_value: media.caption || "",
suggested_value: "",
locked: false,
loading: true
}
]
_other ->
[]
end
rescue
_error -> []
end
defp ai_fields(_tab, _title, _subtitle, _page_language), do: []
defp delete_details(%{type: :media, id: media_id}, page_language) do
entity_name =
case Media.get_media(media_id) do
%MediaRecord{} = media -> media.title || media.original_name || media.id
_other -> media_id
end
reference_list =
Repo.all(
from post in Post,
join: pm in PostMedia,
on: pm.post_id == post.id,
where: pm.media_id == ^media_id,
order_by: [asc: pm.sort_order, desc: post.updated_at],
select: post.title
)
|> Enum.map(&(&1 || media_id))
%{
title: BDS.Gettext.lgettext(page_language, "ui", "Delete Media"),
entity_name: entity_name,
entity_type: "media",
reference_list: reference_list
}
rescue
_error ->
%{
title: BDS.Gettext.lgettext(page_language, "ui", "Delete Media"),
entity_name: media_id,
entity_type: "media",
reference_list: []
}
end
defp delete_details(%{type: :tags}, page_language) do
tag_name =
Repo.one(from tag in Tag, order_by: [asc: tag.name], limit: 1, select: tag.name)
|> Kernel.||("tag")
%{
title: BDS.Gettext.lgettext(page_language, "ui", "Delete Tag"),
entity_name: tag_name,
entity_type: "tag",
reference_list: []
}
rescue
_error ->
%{
title: BDS.Gettext.lgettext(page_language, "ui", "Delete Tag"),
entity_name: "tag",
entity_type: "tag",
reference_list: []
}
end
defp delete_details(_tab, page_language) do
%{
title: BDS.Gettext.lgettext(page_language, "ui", "Delete"),
entity_name: "",
entity_type: "item",
reference_list: []
}
end
defp merge_details(project_id, page_language) do
tags =
Repo.all(
from tag in Tag,
where: tag.project_id == ^project_id,
order_by: [asc: tag.name],
limit: 3,
select: tag.name
)
target = List.first(tags) || "tag"
%{
target: target,
count: max(length(tags), 1),
title: BDS.Gettext.lgettext(page_language, "ui", "Merge Tags"),
message: BDS.Gettext.lgettext(page_language, "ui", "Cannot be undone.")
}
rescue
_error ->
%{
target: "tag",
count: 1,
title: BDS.Gettext.lgettext(page_language, "ui", "Merge Tags"),
message: BDS.Gettext.lgettext(page_language, "ui", "Cannot be undone.")
}
end
defp canonical_post_url(post) do
timestamp = post.published_at || post.updated_at || System.system_time(:millisecond)
date = DateTime.from_unix!(timestamp, :millisecond)
"/#{date.year}/#{pad2(date.month)}/#{pad2(date.day)}/#{post.slug || post.id}"
end
defp pad2(value), do: value |> Integer.to_string() |> String.pad_leading(2, "0")
defp slugify(value) do
value
|> to_string()
|> String.downcase()
|> String.replace(~r/[^a-z0-9]+/u, "-")
|> String.trim("-")
end
end