lib/seo/open_graph/article.ex

defmodule SEO.OpenGraph.Article do
  @moduledoc """
  Metadata describing an article.

  ### Resources

  - https://ogp.me/#type_article
  """

  defstruct [
    :published_time,
    :modified_time,
    :expiration_time,
    :author,
    :section,
    :tag,
    namespace: "https://ogp.me/ns/article#"
  ]

  @type t :: %__MODULE__{
          namespace: String.t(),
          published_time: DateTime.t() | NaiveDateTime.t() | Date.t(),
          modified_time: DateTime.t() | NaiveDateTime.t() | Date.t(),
          expiration_time: DateTime.t() | NaiveDateTime.t() | Date.t(),
          author:
            URI.t() | String.t() | SEO.OpenGraph.Profile.t() | list(SEO.OpenGraph.Profile.t()),
          section: String.t(),
          tag: String.t() | list(String.t())
        }

  @doc """
  Build metadata about an article.

  - `:published_time` - when the article was first published.
  - `:modified_time` - when the article was last changed.
  - `:expiration_time` - when the article is out of date after.
  - `:author` - Writers of the article. This can be nested author OpenGraph metadata or URLs that
  provide the metadata or simply the author's name.
  - `:section` - A high-level section name. E.g. `"Technology"`
  - `:tag` - Tag words associated with this article. E.g. `["Elixir", "Ecto"]`
  """

  @spec build(SEO.attrs(), SEO.config()) :: t() | nil
  def build(attrs, default \\ nil)

  def build(attrs, default) do
    SEO.Utils.merge_defaults(__MODULE__, attrs, default)
  end

  use Phoenix.Component
  alias SEO.Utils

  attr(:content, __MODULE__, default: nil)

  def meta(assigns) do
    assigns = assign(assigns, :content, build(assigns[:content], assigns[:config]))

    ~H"""
    <%= if @content do %>
    <%= if @content.published_time do %>
    <meta property="article:published_time" content={Utils.to_iso8601(@content.published_time)} />
    <% end %><%= if @content.modified_time do %>
    <meta property="article:modified_time" content={Utils.to_iso8601(@content.modified_time)} />
    <% end %><%= if @content.expiration_time do %>
    <meta property="article:expiration_time" content={Utils.to_iso8601(@content.expiration_time)} />
    <% end %><%= if @content.section do %>
    <meta property="article:section" content={@content.section} />
    <% end %><%= if (authors = List.wrap(@content.author)) != [] do %>
    <SEO.OpenGraph.Profile.meta :for={author <- authors} property="article:author" content={author} />
    <% end %><%= if (tags = List.wrap(@content.tag)) != [] do %>
    <meta :for={tag <- tags} property="article:tag" content={tag} />
    <% end %>
    <% end %>
    """
  end
end