def notify_and_stream(activity) do
Notification.create_notifications(activity)
- conversation = create_or_bump_conversation(activity, activity.actor)
+ original_activity =
+ case activity do
+ %{data: %{"type" => "Update"}, object: %{data: %{"id" => id}}} ->
+ Activity.get_create_by_object_ap_id_with_object(id)
+
+ _ ->
+ activity
+ end
+
+ conversation = create_or_bump_conversation(original_activity, original_activity.actor)
participations = get_participations(conversation)
stream_out(activity)
stream_out_participations(participations)
@impl true
def stream_out(%Activity{data: %{"type" => data_type}} = activity)
- when data_type in ["Create", "Announce", "Delete"] do
+ when data_type in ["Create", "Announce", "Delete", "Update"] do
activity
|> Topics.get_activity_topics()
|> Streamer.stream(activity)
end
end
- defp do_unfollow(follower, followed, activity_id, local) do
+ defp do_unfollow(follower, followed, activity_id, local)
+
+ defp do_unfollow(follower, followed, activity_id, local) when local == true do
with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
- {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
{:ok, activity} <- insert(unfollow_data, local),
+ {:ok, _activity} <- Repo.delete(follow_activity),
_ <- notify_and_stream(activity),
:ok <- maybe_federate(activity) do
{:ok, activity}
end
end
+ defp do_unfollow(follower, followed, activity_id, false) do
+ # On a remote unfollow, _remove_ their activity from the database, since some software (MISSKEEEEY)
+ # uses deterministic ids for follows.
+ with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
+ {:ok, _activity} <- Repo.delete(follow_activity),
+ unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
+ unfollow_activity <- make_unfollow_activity(unfollow_data, false),
+ _ <- notify_and_stream(unfollow_activity) do
+ {:ok, unfollow_activity}
+ else
+ nil -> nil
+ {:error, error} -> Repo.rollback(error)
+ end
+ end
+
+ defp make_unfollow_activity(data, local) do
+ {recipients, _, _} = get_recipients(data)
+
+ %Activity{
+ data: data,
+ local: local,
+ actor: data["actor"],
+ recipients: recipients
+ }
+ end
+
@spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
def flag(params) do
with {:ok, result} <- Repo.transaction(fn -> do_flag(params) end) do
@spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()]
def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do
+ includes_local_public = Map.get(opts, :includes_local_public, false)
+
opts = Map.delete(opts, :user)
- [Constants.as_public()]
+ intended_recipients =
+ if includes_local_public do
+ [Constants.as_public(), as_local_public()]
+ else
+ [Constants.as_public()]
+ end
+
+ intended_recipients
|> fetch_activities_query(opts)
|> restrict_unlisted(opts)
|> fetch_paginated_optimized(opts, pagination)
do: query
defp restrict_thread_visibility(query, %{user: %User{ap_id: ap_id}}, _) do
+ local_public = as_local_public()
+
from(
a in query,
- where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
+ where: fragment("thread_visibility(?, (?)->>'id', ?) = true", ^ap_id, a.data, ^local_public)
)
end
defp user_activities_recipients(%{godmode: true}), do: []
defp user_activities_recipients(%{reading_user: reading_user}) do
- if reading_user do
- [Constants.as_public(), reading_user.ap_id | User.following(reading_user)]
+ if not is_nil(reading_user) and reading_user.local do
+ [
+ Constants.as_public(),
+ as_local_public(),
+ reading_user.ap_id | User.following(reading_user)
+ ]
else
[Constants.as_public()]
end
# we request WebFinger here
nickname = additional[:nickname_from_acct] || generate_nickname(data)
+ # also_known_as must be a URL
+ also_known_as =
+ data
+ |> Map.get("alsoKnownAs", [])
+ |> Enum.filter(fn url ->
+ case URI.parse(url) do
+ %URI{scheme: "http"} -> true
+ %URI{scheme: "https"} -> true
+ _ -> false
+ end
+ end)
+
%{
ap_id: data["id"],
uri: get_actor_url(data["url"]),
featured_address: featured_address,
bio: data["summary"] || "",
actor_type: actor_type,
- also_known_as: Map.get(data, "alsoKnownAs", []),
+ also_known_as: also_known_as,
public_key: public_key,
inbox: data["inbox"],
shared_inbox: shared_inbox,