2019-10-06 23:41:18 +00:00
|
|
|
defmodule ClacksWeb.FrontendView do
|
|
|
|
use ClacksWeb, :view
|
2020-05-24 20:26:10 +00:00
|
|
|
alias Clacks.{Actor, Activity, Repo, Notification}
|
2020-04-26 18:45:51 +00:00
|
|
|
alias ClacksWeb.Router.Helpers, as: Routes
|
|
|
|
alias ClacksWeb.Endpoint
|
2020-05-25 02:50:40 +00:00
|
|
|
require Logger
|
2020-04-25 16:30:47 +00:00
|
|
|
|
|
|
|
@spec display_username(actor :: Actor.t()) :: String.t()
|
|
|
|
|
|
|
|
def display_username(%Actor{local: true, data: %{"name" => name}}) do
|
|
|
|
"@" <> name
|
|
|
|
end
|
|
|
|
|
|
|
|
def display_username(%Actor{local: false, ap_id: ap_id, data: %{"name" => name}}) do
|
|
|
|
%URI{host: host} = URI.parse(ap_id)
|
|
|
|
"@" <> name <> "@" <> host
|
|
|
|
end
|
|
|
|
|
2020-04-26 18:45:51 +00:00
|
|
|
def local_actor_link(%Actor{local: true, ap_id: ap_id}), do: ap_id
|
|
|
|
|
|
|
|
def local_actor_link(%Actor{local: false, id: id}),
|
|
|
|
do: Routes.frontend_path(Endpoint, :actor, id)
|
|
|
|
|
2020-04-27 02:57:47 +00:00
|
|
|
@spec display_timestamp(datetime :: String.t() | DateTime.t() | NaiveDateTime.t()) :: String.t()
|
2020-04-26 18:45:51 +00:00
|
|
|
|
2020-04-25 16:30:47 +00:00
|
|
|
def display_timestamp(str) when is_binary(str) do
|
|
|
|
display_timestamp(Timex.parse!(str, "{ISO:Extended}"))
|
|
|
|
end
|
|
|
|
|
|
|
|
def display_timestamp(datetime) do
|
|
|
|
diff = Timex.diff(Timex.now(), datetime, :seconds)
|
|
|
|
|
|
|
|
cond do
|
|
|
|
diff < 60 ->
|
|
|
|
# less than a minute, seconds
|
|
|
|
"#{diff}sec"
|
|
|
|
|
|
|
|
diff < 60 * 60 ->
|
|
|
|
# less than an hour, minutes
|
|
|
|
"#{Integer.floor_div(diff, 60)}min"
|
|
|
|
|
|
|
|
diff < 60 * 60 * 24 ->
|
|
|
|
# less than a day, hours
|
|
|
|
"#{Integer.floor_div(diff, 60 * 60)}hr"
|
|
|
|
|
|
|
|
diff < 60 * 60 * 24 * 7 ->
|
|
|
|
# less than a week, days
|
|
|
|
"#{Integer.floor_div(diff, 60 * 60 * 24)}d"
|
|
|
|
|
2020-05-19 02:30:16 +00:00
|
|
|
diff < 60 * 60 * 24 * 30 ->
|
|
|
|
# less than a month(ish), weeks
|
|
|
|
"#{Integer.floor_div(diff, 60 * 60 * 24 * 7)}wk"
|
|
|
|
|
|
|
|
diff < 60 * 60 * 24 * 365 ->
|
|
|
|
# less than a year, months(ish)
|
|
|
|
# todo: figure out actually how many months
|
|
|
|
"#{Integer.floor_div(diff, 60 * 60 * 24 * 30)}mo"
|
|
|
|
|
2020-04-25 16:30:47 +00:00
|
|
|
true ->
|
2020-05-19 02:30:16 +00:00
|
|
|
Timex.format!(datetime, "%F", :strftime)
|
2020-04-25 16:30:47 +00:00
|
|
|
end
|
|
|
|
end
|
2020-04-25 19:47:22 +00:00
|
|
|
|
2020-05-24 20:26:10 +00:00
|
|
|
@spec prev_page_path(conn :: Plug.Conn.t(), [
|
|
|
|
Activity.t() | {Activity.t(), Actor.t()} | Notification.t()
|
|
|
|
]) ::
|
2020-04-27 02:57:47 +00:00
|
|
|
String.t() | nil
|
2020-04-26 18:45:51 +00:00
|
|
|
|
2020-04-25 19:47:22 +00:00
|
|
|
def prev_page_path(conn, activities) do
|
|
|
|
if Map.has_key?(conn.query_params, "max_id") do
|
|
|
|
Phoenix.Controller.current_path(conn, %{
|
2020-05-24 20:26:10 +00:00
|
|
|
since_id: activities |> List.first() |> timeline_id()
|
2020-04-25 19:47:22 +00:00
|
|
|
})
|
|
|
|
else
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-05-24 20:26:10 +00:00
|
|
|
@spec next_page_path(conn :: Plug.Conn.t(), [
|
|
|
|
Activity.t() | {Activity.t(), Actor.t()} | Notification.t()
|
|
|
|
]) ::
|
2020-04-27 02:57:47 +00:00
|
|
|
String.t() | nil
|
2020-04-26 18:45:51 +00:00
|
|
|
|
2020-04-25 19:47:22 +00:00
|
|
|
def next_page_path(conn, activities) do
|
|
|
|
if length(activities) < 20 do
|
|
|
|
nil
|
|
|
|
else
|
|
|
|
Phoenix.Controller.current_path(conn, %{
|
2020-05-24 20:26:10 +00:00
|
|
|
max_id: activities |> List.last() |> timeline_id()
|
2020-04-25 19:47:22 +00:00
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-05-24 20:26:10 +00:00
|
|
|
defp timeline_id(%Activity{id: id}), do: id
|
|
|
|
defp timeline_id({%Activity{id: id}, _}), do: id
|
|
|
|
defp timeline_id({%Activity{id: id}, _, _}), do: id
|
|
|
|
defp timeline_id(%Notification{id: id}), do: id
|
2020-05-23 03:45:48 +00:00
|
|
|
|
|
|
|
@spec mentions_for_replying_to(Activity.t()) :: String.t()
|
|
|
|
defp mentions_for_replying_to(conn, %Activity{
|
|
|
|
data: %{"object" => %{"actor" => actor, "tag" => tags}}
|
|
|
|
}) do
|
|
|
|
current_user = conn.assigns[:user] |> Repo.preload(:actor)
|
|
|
|
|
|
|
|
tag_actors =
|
|
|
|
tags
|
|
|
|
|> Enum.filter(fn
|
|
|
|
%{"type" => "Mention"} -> true
|
|
|
|
_ -> false
|
|
|
|
end)
|
|
|
|
|> Enum.map(fn %{"href" => ap_id} -> ap_id end)
|
|
|
|
|
|
|
|
actors =
|
|
|
|
[actor | tag_actors]
|
|
|
|
|> List.delete(current_user.actor.ap_id)
|
|
|
|
|> Enum.uniq()
|
|
|
|
|
|
|
|
mentions =
|
|
|
|
actors
|
|
|
|
|> Enum.map(fn ap_id ->
|
|
|
|
actor = Actor.get_cached_by_ap_id(ap_id)
|
|
|
|
"@#{actor.nickname}"
|
|
|
|
end)
|
|
|
|
|
2020-05-25 23:30:00 +00:00
|
|
|
case mentions do
|
|
|
|
[] ->
|
|
|
|
""
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
Enum.join(mentions, " ") <> ""
|
|
|
|
end
|
2020-05-23 03:45:48 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
defp mentions_for_replying_to(_), do: ""
|
2020-05-25 02:50:40 +00:00
|
|
|
|
|
|
|
@spec render_status_content(activity :: Activity.t()) :: String.t()
|
|
|
|
defp render_status_content(%Activity{
|
|
|
|
data: %{
|
|
|
|
"type" => "Create",
|
|
|
|
"object" => %{"type" => "Note", "content" => content} = note
|
|
|
|
}
|
|
|
|
}) do
|
|
|
|
with %{"tag" => tags} <- note,
|
|
|
|
{:ok, tree} <- Floki.parse_fragment(content) do
|
|
|
|
tree
|
|
|
|
|> Floki.traverse_and_update(fn
|
|
|
|
{"a", attrs, _children} = orig_tree ->
|
|
|
|
{"href", href} = Enum.find(attrs, fn {name, _} -> name == "href" end)
|
|
|
|
|
|
|
|
has_matching_tag =
|
|
|
|
Enum.any?(tags, fn
|
|
|
|
%{"type" => "Mention", "href" => ^href} -> true
|
|
|
|
_ -> false
|
|
|
|
end)
|
|
|
|
|
|
|
|
with true <- has_matching_tag,
|
|
|
|
%Actor{local: false} = actor <- Actor.get_cached_by_ap_id(href) do
|
|
|
|
{
|
|
|
|
"span",
|
|
|
|
[],
|
|
|
|
[
|
|
|
|
orig_tree,
|
|
|
|
{"a", [{"href", local_actor_link(actor)}, {"class", "local-actor-link"}], ["🔗"]}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
else
|
|
|
|
_ ->
|
|
|
|
orig_tree
|
|
|
|
end
|
|
|
|
|
|
|
|
tree ->
|
|
|
|
tree
|
|
|
|
end)
|
|
|
|
|> Floki.raw_html()
|
|
|
|
# remove the <html> and </html> from the floki rendered output
|
|
|
|
|> String.slice(6..-8)
|
|
|
|
|> raw()
|
|
|
|
else
|
|
|
|
_ ->
|
|
|
|
content
|
|
|
|
end
|
|
|
|
end
|
2019-10-06 23:41:18 +00:00
|
|
|
end
|