8000 Add rich link preview metadata by mwoods79 · Pull Request #122 · huddlz-hq/huddlz · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions lib/huddlz_web/components/layouts/root.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="csrf-token" content={get_csrf_token()} />
<% meta = assigns[:meta] || %{} %>
<% meta_title = meta[:title] || assigns[:page_title] || "huddlz" %>
<% meta_description =
meta[:description] || "Find and join local community gatherings on huddlz." %>
<meta name="description" content={meta_description} />
<meta property="og:site_name" content="huddlz" />
<meta property="og:type" content={meta[:type] || "website"} />
<meta property="og:title" content={meta_title} />
<meta property="og:description" content={meta_description} />
<%= if meta[:url] do %>
<meta property="og:url" content={meta[:url]} />
<% end %>
<%= if meta[:image] do %>
<meta property="og:image" content={meta[:image]} />
<meta name="twitter:card" content="summary_large_image" />
<% else %>
<meta name="twitter:card" content="summary" />
<% end %>
<meta name="twitter:title" content={meta_title} />
<meta name="twitter:description" content={meta_description} />
<%= if meta[:image] do %>
<meta name="twitter:image" content={meta[:image]} />
<% end %>
<.live_title default="huddlz" suffix=" · huddlz">
{assigns[:page_title]}
</.live_title>
Expand Down
12 changes: 12 additions & 0 deletions lib/huddlz_web/live/group_live/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ defmodule HuddlzWeb.GroupLive.Show do
alias Huddlz.Communities.GroupMember
alias Huddlz.Storage.GroupImages
alias HuddlzWeb.Layouts
alias HuddlzWeb.MetaHelpers

require Ash.Query

Expand All @@ -30,6 +31,7 @@ defmodule HuddlzWeb.GroupLive.Show do
{:noreply,
socket
|> assign(:page_title, group.name)
|> assign(:meta, group_meta(group))
|> assign(:group, group)
|> assign(:members, members)
|> assign(:member_count, member_count_unchecked(group))
Expand Down Expand Up @@ -300,6 +302,16 @@ defmodule HuddlzWeb.GroupLive.Show do
end
end

defp group_meta(group) do
%{
title: "#{group.name} · huddlz",
description: MetaHelpers.description(group, "Find and join this group on huddlz."),
type: "website",
url: url(~p"/groups/#{group.slug}"),
image: MetaHelpers.image_url(group.current_image_url, GroupImages)
}
end

defp member_unchecked?(_group, nil), do: false

defp member_unchecked?(group, user) do
Expand Down
12 changes: 12 additions & 0 deletions lib/huddlz_web/live/huddl_live/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ defmodule HuddlzWeb.HuddlLive.Show do
alias Huddlz.Communities
alias Huddlz.Storage.HuddlImages
alias HuddlzWeb.Layouts
alias HuddlzWeb.MetaHelpers

on_mount {HuddlzWeb.LiveUserAuth, :live_user_optional}

Expand All @@ -24,6 +25,7 @@ defmodule HuddlzWeb.HuddlLive.Show do
{:noreply,
socket
|> assign(:page_title, huddl.title)
|> assign(:meta, huddl_meta(huddl))
|> assign(:huddl, huddl)
|> assign(:has_rsvped, has_rsvped)
|> assign(:is_creator, creator?(huddl, socket.assigns.current_user))}
Expand Down Expand Up @@ -326,6 +328,16 @@ defmodule HuddlzWeb.HuddlLive.Show do
)
end

defp huddl_meta(huddl) do
%{
title: "#{huddl.title} · huddlz",
description: MetaHelpers.description(huddl, "Find and join this huddl on huddlz."),
type: "event",
url: url(~p"/groups/#{huddl.group.slug}/huddlz/#{huddl.id}"),
image: MetaHelpers.image_url(huddl.display_image_url, HuddlImages)
}
end

defp creator?(_huddl, nil), do: false

defp creator?(huddl, user) do
Expand Down
51 changes: 51 additions & 0 deletions lib/huddlz_web/meta_helpers.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
defmodule HuddlzWeb.MetaHelpers do
@moduledoc """
Helpers for building social sharing metadata.
"""

@default_description_length 200

def description(record, fallback, max_length \\ @default_description_length)

def description(%{description: nil}, fallback, _max_length), do: fallback

def description(%{description: description}, fallback, max_length) do
description
|> to_string()
|> String.replace(~r/\s+/, " ")
|> String.trim()
|> truncate(max_length)
|> case do
"" -> fallback
text -> text
end
end

def description(_record, fallback, _max_length), do: fallback

def image_url(nil, _storage_module), do: nil

def image_url(path, storage_module) do
path
|> storage_module.url()
|> absolute_url()
end

defp absolute_url("http" <> _ = url), do: url
defp absolute_url(path), do: HuddlzWeb.Endpoint.url() <> path

defp truncate(text, max_length) do
if String.length(text) <= max_length do
text
else
text
|> String.slice(0, max_length)
|> String.replace(~r/\s+\S*$/, "")
|> case do
"" -> String.slice(text, 0, max_length)
truncated -> truncated
end
|> Kernel.<>("...")
end
end
end
58 changes: 58 additions & 0 deletions test/huddlz_web/live/group_live/show_tabs_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ defmodule HuddlzWeb.GroupLive.ShowTabsTest do
use HuddlzWeb.ConnCase, async: true
import Phoenix.LiveViewTest

alias Huddlz.Communities.GroupImage

describe "Group show page tabs" do
setup do
# Create a user who can create groups and huddls
Expand Down Expand Up @@ -74,6 +76,62 @@ defmodule HuddlzWeb.GroupLive.ShowTabsTest do
refute has_element?(view, "h3", "Upcoming Event 12")
end

test "renders rich link preview metadata", %{conn: conn, group: group} do
html =
conn
|> get(~p"/groups/#{group.slug}")
|> html_response(200)

assert meta_content(html, ~s(meta[property="og:type"])) == "website"
assert meta_content(html, ~s(meta[property="og:title"])) == "#{group.name} · huddlz"
assert meta_content(html, ~s(meta[name="description"])) == to_string(group.description)

assert meta_content(html, ~s(meta[property="og:description"])) ==
to_string(group.description)

assert meta_content(html, ~s(meta[property="og:url"])) =~ "/groups/#{group.slug}"
end

test "renders rich link preview image metadata", %{conn: conn, group: group} do
thumbnail_path = "/uploads/group_images/#{group.id}/preview_thumb.jpg"

GroupImage
|> Ash.Changeset.for_create(:create, %{
filename: "preview.jpg",
content_type: "image/jpeg",
size_bytes: 123,
storage_path: "/uploads/group_images/#{group.id}/preview.jpg",
thumbnail_path: thumbnail_path,
group_id: group.id
})
|> Ash.create!(authorize?: false)

html =
conn
|> get(~p"/groups/#{group.slug}")
|> html_response(200)
BD0
assert meta_content(html, ~s(meta[property="og:image"])) ==
HuddlzWeb.Endpoint.url() <> thumbnail_path

assert meta_content(html, ~s(meta[name="twitter:image"])) ==
HuddlzWeb.Endpoint.url() <> thumbnail_path

assert meta_content(html, ~s(meta[name="twitter:card"])) == "summary_large_image"
end

test "renders fallback description metadata", %{conn: conn, user: user} do
group = generate(group(owner_id: user.id, is_public: true, actor: user, description: nil))

html =
conn
|> get(~p"/groups/#{group.slug}")
|> html_response(200)

assert meta_content(html, ~s(meta[name="description"])) ==
"Find and join this group on huddlz."
end

test "switches to past events tab when clicked", %{conn: conn, group: group} do
{:ok, view, _html} = live(conn, ~p"/groups/#{group.slug}")

Expand Down
19 changes: 19 additions & 0 deletions test/huddlz_web/live/group_live_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@ defmodule HuddlzWeb.GroupLiveTest do
|> refute_has("a", text: "New Group")
end

test "renders default rich link preview metadata", %{conn: conn} do
html =
conn
|> get(~p"/groups")
|> html_response(200)

assert meta_content(html, ~s(meta[name="description"])) ==
"Find and join local community gatherings on huddlz."

assert meta_content(html, ~s(meta[property="og:type"])) == "website"
assert meta_content(html, ~s(meta[property="og:title"])) == "Groups"

assert meta_content(html, ~s(meta[property="og:description"])) ==
"Find and join local community gatherings on huddlz."

assert meta_content(html, ~s(meta[name="twitter:card"])) == "summary"
assert meta_content(html, ~s(meta[property="og:image"])) == nil
end

test "shows New Group button for users", %{conn: conn, verified: verified} do
conn
|> login(verified)
Expand Down
4 changes: 2 additions & 2 deletions test/huddlz_web/live/huddl_live/new_image_upload_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ defmodule HuddlzWeb.HuddlLive.NewImageUploadTest do
|> render_submit()

# Should redirect to group page
assert_redirected(view, "/groups/#{group.slug}")
assert_redirected(view, ~p"/groups/#{group.slug}")

# Verify huddl was created without image
huddl =
Expand Down Expand Up @@ -146,7 +146,7 @@ defmodule HuddlzWeb.HuddlLive.NewImageUploadTest do
|> render_submit()

# Should redirect to group page
assert_redirected(view, "/groups/#{group.slug}")
assert_redirected(view, ~p"/groups/#{group.slug}")

# Verify huddl has image
huddl =
Expand Down
75 changes: 75 additions & 0 deletions test/huddlz_web/live/huddl_live/show_test.exs
EFE4
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule HuddlzWeb.HuddlLive.ShowTest do
alias Huddlz.Communities.Group
alias Huddlz.Communities.GroupMember
alias Huddlz.Communities.Huddl
alias Huddlz.Communities.HuddlImage

describe "Show huddl details" do
setup do
Expand Down Expand Up @@ -81,6 +82,80 @@ defmodule HuddlzWeb.HuddlLive.ShowTest do
|> assert_has("dd", text: "Be the first to RSVP!")
end

test "renders rich link preview metadata", %{conn: conn, group: group, huddl: huddl} do
html =
conn
|> get(~p"/groups/#{group.slug}/huddlz/#{huddl.id}")
|> html_response(200)

assert meta_content(html, ~s(meta[property="og:type"])) == "event"
assert meta_content(html, ~s(meta[property="og:title"])) == "#{huddl.title} · huddlz"
assert meta_content(html, ~s(meta[name="description"])) == huddl.description
assert meta_content(html, ~s(meta[property="og:description"])) == huddl.description
assert meta_content(html, ~s(meta[name="twitter:description"])) == huddl.description

assert meta_content(html, ~s(meta[property="og:url"])) =~
"/groups/#{group.slug}/huddlz/#{huddl.id}"
end

test "renders rich link preview image metadata", %{conn: conn, group: group, huddl: huddl} do
thumbnail_path = "/uploads/huddl_images/#{huddl.id}/preview_thumb.jpg"

HuddlImage
|> Ash.Changeset.for_create(:create, %{
filename: "preview.jpg",
content_type: "image/jpeg",
size_bytes: 123,
storage_path: "/uploads/huddl_images/#{huddl.id}/preview.jpg",
thumbnail_path: thumbnail_path,
huddl_id: huddl.id
})
|> Ash.create!(authorize?: false)

html =
conn
|> get(~p"/groups/#{group.slug}/huddlz/#{huddl.id}")
|> html_response(200)

assert meta_content(html, ~s(meta[property="og:image"])) ==
HuddlzWeb.Endpoint.url() <> thumbnail_path

assert meta_content(html, ~s(meta[name="twitter:image"])) ==
HuddlzWeb.Endpoint.url() <> thumbnail_path

assert meta_content(html, ~s(meta[name="twitter:card"])) == "summary_large_image"
end

test "renders fallback description metadata", %{conn: conn, group: group, owner: owner} do
huddl =
Huddl
|> Ash.Changeset.for_create(
:create,
%{
title: "Description-free Huddl",
description: nil,
date: Date.add(Date.utc_today(), 2),
start_time: ~T[14:00:00],
duration_minutes: 120,
event_type: :in_person,
physical_location: "123 Main St",
is_private: false,
group_id: group.id,
creator_id: owner.id
},
actor: owner
)
|> Ash.create!()

html =
conn
|> get(~p"/groups/#{group.slug}/huddlz/#{huddl.id}")
|> html_response(200)

assert meta_content(html, ~s(meta[name="description"])) ==
"Find and join this huddl on huddlz."
end

test "shows RSVP button for authenticated users", %{
conn: conn,
member: member,
Expand Down
12 changes: 9 additions & 3 deletions test/huddlz_web/live/huddl_live_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,15 @@ defmodule HuddlzWeb.HuddlLiveTest do
group(is_public: true, owner_id: host.id, actor: host, name: "Linked Group Test")
)

conn
|> visit("/")
|> assert_has("a[href='/groups/#{group.slug}']")
{:ok, _view, html} = live(conn, ~p"/")

assert html
|> Floki.parse_document!()
|> Floki.find("a")
|> Enum.any?(fn link ->
Floki.attribute(link, "href") == ["/groups/#{group.slug}"] &&
link |> Floki.text() |> String.contains?("Linked Group Test")
end)
end
end
end
Loading
Loading
0