|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
defmodule Clacks.Timeline do
|
|
|
|
|
alias Clacks.{Repo, Actor, Activity, User, Notification}
|
|
|
|
|
alias Clacks.{Repo, Actor, Activity, Object, User, Notification}
|
|
|
|
|
import Clacks.Paginator
|
|
|
|
|
import Ecto.Query
|
|
|
|
|
|
|
|
|
@ -82,34 +82,54 @@ defmodule Clacks.Timeline do
|
|
|
|
|
|> Repo.all()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@spec notifications(actor :: Actor.t(), params :: map()) :: [
|
|
|
|
|
{:follow, activity :: Activity.t(), actor :: Actor.t()}
|
|
|
|
|
| {:mention, activity :: Activity.t(), actor :: Actor.t()}
|
|
|
|
|
| {:announce, announce :: Activity.t(), announce_actor :: Actor.t(),
|
|
|
|
|
activity :: Activity.t(), actor :: Actor.t()}
|
|
|
|
|
| {:like, like :: Activity.t(), like_actor :: Actor.t(), activity :: Activity.t(),
|
|
|
|
|
actor :: Actor.t()}
|
|
|
|
|
]
|
|
|
|
|
@spec notifications(Actor.t(), map()) :: [Notification.t()]
|
|
|
|
|
def notifications(actor, params) do
|
|
|
|
|
Notification
|
|
|
|
|
|> where([n], n.user_id == ^actor.user_id)
|
|
|
|
|
|> join(:inner, [n], activity in Activity, on: activity.id == n.activity_id)
|
|
|
|
|
|> join(:inner, [n, activity], actor in Actor, on: activity.actor == actor.ap_id)
|
|
|
|
|
|> join(:left, [n, activity, actor], other in Activity,
|
|
|
|
|
on:
|
|
|
|
|
n.type in ["announce", "like"] and fragment("?->>'type'", other.data) == "Create" and
|
|
|
|
|
fragment("?->>'object'", activity.data) == fragment("?->'object'->>'id'", other.data)
|
|
|
|
|
)
|
|
|
|
|
|> paginate(params)
|
|
|
|
|
|> limit(^Map.get(params, "limit", 20))
|
|
|
|
|
|> join(:left, [n], a in Activity,
|
|
|
|
|
as: :activity,
|
|
|
|
|
on: a.id == n.activity_id
|
|
|
|
|
)
|
|
|
|
|
|> join(:left, [n], a in Activity,
|
|
|
|
|
as: :referenced_activity,
|
|
|
|
|
on: a.id == n.referenced_activity_id
|
|
|
|
|
)
|
|
|
|
|
|> join(:left, [n, activity: a], o in Object,
|
|
|
|
|
as: :object,
|
|
|
|
|
on:
|
|
|
|
|
fragment("?->>'id'", o.data) ==
|
|
|
|
|
fragment("COALESCE(?->'object'->>'id', ?->>'object')", a.data, a.data)
|
|
|
|
|
)
|
|
|
|
|
|> join(:left, [n, referenced_activity: a], o in Object,
|
|
|
|
|
as: :referenced_object,
|
|
|
|
|
on:
|
|
|
|
|
fragment("?->>'id'", o.data) ==
|
|
|
|
|
fragment("COALESCE(?->'object'->>'id', ?->>'object')", a.data, a.data)
|
|
|
|
|
)
|
|
|
|
|
|> join(:left, [n, activity: activity], actor in Actor,
|
|
|
|
|
as: :activity_actor,
|
|
|
|
|
on: activity.actor_ap_id == actor.ap_id
|
|
|
|
|
)
|
|
|
|
|
# note: we shouldn't need to load the actor for the referenced_activity here,
|
|
|
|
|
# because notifications for a given actor always reference activities from that same actor
|
|
|
|
|
|> select(
|
|
|
|
|
[notification, activity, actor, original_activity],
|
|
|
|
|
{notification, activity, actor, original_activity}
|
|
|
|
|
[
|
|
|
|
|
n,
|
|
|
|
|
activity: activity,
|
|
|
|
|
referenced_activity: referenced_activity,
|
|
|
|
|
object: object,
|
|
|
|
|
referenced_object: referenced_object,
|
|
|
|
|
activity_actor: activity_actor
|
|
|
|
|
],
|
|
|
|
|
%Notification{
|
|
|
|
|
n
|
|
|
|
|
| activity: %Activity{activity | object: object, actor: activity_actor},
|
|
|
|
|
referenced_activity: %Activity{referenced_activity | object: referenced_object}
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|> Repo.all()
|
|
|
|
|
|> Enum.map(fn {notification, activity, actor, original_activity} ->
|
|
|
|
|
{String.to_existing_atom(notification.type), activity, actor, original_activity}
|
|
|
|
|
end)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
defp restrict_to_actor(query, actor_id) do
|
|
|
|
|