Merge branch 'develop' into 'reactions'
# Conflicts: # CHANGELOG.md
This commit is contained in:
commit
61097ba6ab
28 changed files with 592 additions and 187 deletions
|
|
@ -67,6 +67,8 @@ defmodule Pleroma.Conversation do
|
|||
|
||||
participations =
|
||||
Enum.map(users, fn user ->
|
||||
User.increment_unread_conversation_count(conversation, user)
|
||||
|
||||
{:ok, participation} =
|
||||
Participation.create_for_user_and_conversation(user, conversation, opts)
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,15 @@ defmodule Pleroma.Conversation.Participation do
|
|||
participation
|
||||
|> read_cng(%{read: true})
|
||||
|> Repo.update()
|
||||
|> case do
|
||||
{:ok, participation} ->
|
||||
participation = Repo.preload(participation, :user)
|
||||
User.set_unread_conversation_count(participation.user)
|
||||
{:ok, participation}
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
def mark_as_unread(participation) do
|
||||
|
|
@ -135,4 +144,12 @@ defmodule Pleroma.Conversation.Participation do
|
|||
|
||||
{:ok, Repo.preload(participation, :recipients, force: true)}
|
||||
end
|
||||
|
||||
def unread_conversation_count_for_user(user) do
|
||||
from(p in __MODULE__,
|
||||
where: p.user_id == ^user.id,
|
||||
where: not p.read,
|
||||
select: %{count: count(p.id)}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.User do
|
|||
alias Comeonin.Pbkdf2
|
||||
alias Ecto.Multi
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Delivery
|
||||
alias Pleroma.Keys
|
||||
alias Pleroma.Notification
|
||||
|
|
@ -842,6 +843,61 @@ defmodule Pleroma.User do
|
|||
|
||||
def maybe_update_following_count(user), do: user
|
||||
|
||||
def set_unread_conversation_count(%User{local: true} = user) do
|
||||
unread_query = Participation.unread_conversation_count_for_user(user)
|
||||
|
||||
User
|
||||
|> join(:inner, [u], p in subquery(unread_query))
|
||||
|> update([u, p],
|
||||
set: [
|
||||
info:
|
||||
fragment(
|
||||
"jsonb_set(?, '{unread_conversation_count}', ?::varchar::jsonb, true)",
|
||||
u.info,
|
||||
p.count
|
||||
)
|
||||
]
|
||||
)
|
||||
|> where([u], u.id == ^user.id)
|
||||
|> select([u], u)
|
||||
|> Repo.update_all([])
|
||||
|> case do
|
||||
{1, [user]} -> set_cache(user)
|
||||
_ -> {:error, user}
|
||||
end
|
||||
end
|
||||
|
||||
def set_unread_conversation_count(_), do: :noop
|
||||
|
||||
def increment_unread_conversation_count(conversation, %User{local: true} = user) do
|
||||
unread_query =
|
||||
Participation.unread_conversation_count_for_user(user)
|
||||
|> where([p], p.conversation_id == ^conversation.id)
|
||||
|
||||
User
|
||||
|> join(:inner, [u], p in subquery(unread_query))
|
||||
|> update([u, p],
|
||||
set: [
|
||||
info:
|
||||
fragment(
|
||||
"jsonb_set(?, '{unread_conversation_count}', (coalesce((?->>'unread_conversation_count')::int, 0) + 1)::varchar::jsonb, true)",
|
||||
u.info,
|
||||
u.info
|
||||
)
|
||||
]
|
||||
)
|
||||
|> where([u], u.id == ^user.id)
|
||||
|> where([u, p], p.count == 0)
|
||||
|> select([u], u)
|
||||
|> Repo.update_all([])
|
||||
|> case do
|
||||
{1, [user]} -> set_cache(user)
|
||||
_ -> {:error, user}
|
||||
end
|
||||
end
|
||||
|
||||
def increment_unread_conversation_count(_, _), do: :noop
|
||||
|
||||
def remove_duplicated_following(%User{following: following} = user) do
|
||||
uniq_following = Enum.uniq(following)
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ defmodule Pleroma.User.Info do
|
|||
field(:hide_followers, :boolean, default: false)
|
||||
field(:hide_follows, :boolean, default: false)
|
||||
field(:hide_favorites, :boolean, default: true)
|
||||
field(:unread_conversation_count, :integer, default: 0)
|
||||
field(:pinned_activities, {:array, :string}, default: [])
|
||||
field(:email_notifications, :map, default: %{"digest" => false})
|
||||
field(:mascot, :map, default: nil)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.Streamer
|
||||
alias Pleroma.Web.WebFinger
|
||||
alias Pleroma.Workers.BackgroundWorker
|
||||
|
|
@ -291,8 +292,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
|
||||
def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||
# only accept false as false value
|
||||
local = !(params[:local] == false)
|
||||
activity_id = params[:activity_id]
|
||||
|
||||
with data <- %{
|
||||
"to" => to,
|
||||
|
|
@ -301,6 +302,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
"actor" => actor,
|
||||
"object" => object
|
||||
},
|
||||
data <- Utils.maybe_put(data, "id", activity_id),
|
||||
{:ok, activity} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
|
|
|
|||
|
|
@ -601,7 +601,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
) do
|
||||
with actor <- Containment.get_actor(data),
|
||||
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
|
||||
{:ok, object} <- get_obj_helper(object_id),
|
||||
{:ok, object} <- get_embedded_obj_helper(object_id, actor),
|
||||
public <- Visibility.is_public?(data),
|
||||
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
|
||||
{:ok, activity}
|
||||
|
|
@ -642,7 +642,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
to: data["to"] || [],
|
||||
cc: data["cc"] || [],
|
||||
object: object,
|
||||
actor: actor_id
|
||||
actor: actor_id,
|
||||
activity_id: data["id"]
|
||||
})
|
||||
else
|
||||
e ->
|
||||
|
|
@ -824,6 +825,29 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
end
|
||||
end
|
||||
|
||||
@spec get_embedded_obj_helper(String.t() | Object.t(), User.t()) :: {:ok, Object.t()} | nil
|
||||
def get_embedded_obj_helper(%{"attributedTo" => attributed_to, "id" => object_id} = data, %User{
|
||||
ap_id: ap_id
|
||||
})
|
||||
when attributed_to == ap_id do
|
||||
with {:ok, activity} <-
|
||||
handle_incoming(%{
|
||||
"type" => "Create",
|
||||
"to" => data["to"],
|
||||
"cc" => data["cc"],
|
||||
"actor" => attributed_to,
|
||||
"object" => data
|
||||
}) do
|
||||
{:ok, Object.normalize(activity)}
|
||||
else
|
||||
_ -> get_obj_helper(object_id)
|
||||
end
|
||||
end
|
||||
|
||||
def get_embedded_obj_helper(object_id, _) do
|
||||
get_obj_helper(object_id)
|
||||
end
|
||||
|
||||
def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) when is_binary(in_reply_to) do
|
||||
with false <- String.starts_with?(in_reply_to, "http"),
|
||||
{:ok, %{data: replied_to_object}} <- get_obj_helper(in_reply_to) do
|
||||
|
|
|
|||
|
|
@ -830,6 +830,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
defp maybe_put(map, _key, nil), do: map
|
||||
defp maybe_put(map, key, value), do: Map.put(map, key, value)
|
||||
def maybe_put(map, _key, nil), do: map
|
||||
def maybe_put(map, key, value), do: Map.put(map, key, value)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
import Pleroma.Web.Gettext
|
||||
import Pleroma.Web.CommonAPI.Utils
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
def follow(follower, followed) do
|
||||
timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
|
||||
|
||||
|
|
@ -290,7 +292,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
|
||||
ActivityPub.update(%{
|
||||
local: true,
|
||||
to: [user.follower_address],
|
||||
to: [Pleroma.Constants.as_public(), user.follower_address],
|
||||
cc: [],
|
||||
actor: user.ap_id,
|
||||
object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
||||
|
|
|
|||
|
|
@ -105,6 +105,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text))
|
||||
|> Enum.dedup()
|
||||
|
||||
params =
|
||||
if Map.has_key?(params, "fields_attributes") do
|
||||
Map.update!(params, "fields_attributes", fn fields ->
|
||||
fields
|
||||
|> normalize_fields_attributes()
|
||||
|> Enum.filter(fn %{"name" => n} -> n != "" end)
|
||||
end)
|
||||
else
|
||||
params
|
||||
end
|
||||
|
||||
info_params =
|
||||
[
|
||||
:no_rich_text,
|
||||
|
|
@ -122,12 +133,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)})
|
||||
end)
|
||||
|> add_if_present(params, "default_scope", :default_scope)
|
||||
|> add_if_present(params, "fields", :fields, fn fields ->
|
||||
|> add_if_present(params, "fields_attributes", :fields, fn fields ->
|
||||
fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
|
||||
|
||||
{:ok, fields}
|
||||
end)
|
||||
|> add_if_present(params, "fields", :raw_fields)
|
||||
|> add_if_present(params, "fields_attributes", :raw_fields)
|
||||
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value ->
|
||||
{:ok, Map.merge(user.info.pleroma_settings_store, value)}
|
||||
end)
|
||||
|
|
@ -168,6 +179,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
end
|
||||
end
|
||||
|
||||
defp normalize_fields_attributes(fields) do
|
||||
if Enum.all?(fields, &is_tuple/1) do
|
||||
Enum.map(fields, fn {_, v} -> v end)
|
||||
else
|
||||
fields
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/accounts/relationships"
|
||||
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
targets = User.get_all_by_ids(List.wrap(id))
|
||||
|
|
@ -301,4 +320,26 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/follows"
|
||||
def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
||||
with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)},
|
||||
{_, true} <- {:followed, follower.id != followed.id},
|
||||
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
||||
render(conn, "show.json", user: followed, for: follower)
|
||||
else
|
||||
{:followed, _} -> {:error, :not_found}
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/mutes"
|
||||
def mutes(%{assigns: %{user: user}} = conn, _) do
|
||||
render(conn, "index.json", users: User.muted_users(user), for: user, as: :user)
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/blocks"
|
||||
def blocks(%{assigns: %{user: user}} = conn, _) do
|
||||
render(conn, "index.json", users: User.blocked_users(user), for: user, as: :user)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,86 +5,10 @@
|
|||
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
|
||||
|
||||
alias Pleroma.Bookmark
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
require Logger
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
||||
with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)},
|
||||
{_, true} <- {:followed, follower.id != followed.id},
|
||||
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
||||
conn
|
||||
|> put_view(AccountView)
|
||||
|> render("show.json", %{user: followed, for: follower})
|
||||
else
|
||||
{:followed, _} ->
|
||||
{:error, :not_found}
|
||||
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
def mutes(%{assigns: %{user: user}} = conn, _) do
|
||||
with muted_accounts <- User.muted_users(user) do
|
||||
res = AccountView.render("index.json", users: muted_accounts, for: user, as: :user)
|
||||
json(conn, res)
|
||||
end
|
||||
end
|
||||
|
||||
def blocks(%{assigns: %{user: user}} = conn, _) do
|
||||
with blocked_accounts <- User.blocked_users(user) do
|
||||
res = AccountView.render("index.json", users: blocked_accounts, for: user, as: :user)
|
||||
json(conn, res)
|
||||
end
|
||||
end
|
||||
|
||||
def favourites(%{assigns: %{user: user}} = conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", "Create")
|
||||
|> Map.put("favorited_by", user.ap_id)
|
||||
|> Map.put("blocking_user", user)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_activities([], params)
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities)
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||
end
|
||||
|
||||
def bookmarks(%{assigns: %{user: user}} = conn, params) do
|
||||
user = User.get_cached_by_id(user.id)
|
||||
|
||||
bookmarks =
|
||||
Bookmark.for_user_query(user.id)
|
||||
|> Pagination.fetch_paginated(params)
|
||||
|
||||
activities =
|
||||
bookmarks
|
||||
|> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end)
|
||||
|
||||
conn
|
||||
|> add_link_headers(bookmarks)
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||
end
|
||||
|
||||
# Stubs for unimplemented mastodon api
|
||||
#
|
||||
def empty_array(conn, _) do
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [try_render: 3]
|
||||
import Pleroma.Web.ControllerHelper, only: [try_render: 3, add_link_headers: 2]
|
||||
|
||||
require Ecto.Query
|
||||
|
||||
|
|
@ -283,4 +283,39 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
render(conn, "context.json", activity: activity, activities: activities, user: user)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/favourites"
|
||||
def favourites(%{assigns: %{user: user}} = conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", "Create")
|
||||
|> Map.put("favorited_by", user.ap_id)
|
||||
|> Map.put("blocking_user", user)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_activities([], params)
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities)
|
||||
|> render("index.json", activities: activities, for: user, as: :activity)
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/bookmarks"
|
||||
def bookmarks(%{assigns: %{user: user}} = conn, params) do
|
||||
user = User.get_cached_by_id(user.id)
|
||||
|
||||
bookmarks =
|
||||
user.id
|
||||
|> Bookmark.for_user_query()
|
||||
|> Pleroma.Pagination.fetch_paginated(params)
|
||||
|
||||
activities =
|
||||
bookmarks
|
||||
|> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end)
|
||||
|
||||
conn
|
||||
|> add_link_headers(bookmarks)
|
||||
|> render("index.json", %{activities: activities, for: user, as: :activity})
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
|> maybe_put_chat_token(user, opts[:for], opts)
|
||||
|> maybe_put_activation_status(user, opts[:for])
|
||||
|> maybe_put_follow_requests_count(user, opts[:for])
|
||||
|> maybe_put_unread_conversation_count(user, opts[:for])
|
||||
end
|
||||
|
||||
defp username_from_nickname(string) when is_binary(string) do
|
||||
|
|
@ -248,6 +249,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
|
||||
defp maybe_put_activation_status(data, _, _), do: data
|
||||
|
||||
defp maybe_put_unread_conversation_count(data, %User{id: user_id} = user, %User{id: user_id}) do
|
||||
data
|
||||
|> Kernel.put_in(
|
||||
[:pleroma, :unread_conversation_count],
|
||||
user.info.unread_conversation_count
|
||||
)
|
||||
end
|
||||
|
||||
defp maybe_put_unread_conversation_count(data, _, _), do: data
|
||||
|
||||
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
||||
defp image_url(_), do: nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -355,14 +355,14 @@ defmodule Pleroma.Web.Router do
|
|||
get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array)
|
||||
|
||||
get("/follow_requests", FollowRequestController, :index)
|
||||
get("/blocks", MastodonAPIController, :blocks)
|
||||
get("/mutes", MastodonAPIController, :mutes)
|
||||
get("/blocks", AccountController, :blocks)
|
||||
get("/mutes", AccountController, :mutes)
|
||||
|
||||
get("/timelines/home", TimelineController, :home)
|
||||
get("/timelines/direct", TimelineController, :direct)
|
||||
|
||||
get("/favourites", MastodonAPIController, :favourites)
|
||||
get("/bookmarks", MastodonAPIController, :bookmarks)
|
||||
get("/favourites", StatusController, :favourites)
|
||||
get("/bookmarks", StatusController, :bookmarks)
|
||||
|
||||
get("/notifications", NotificationController, :index)
|
||||
get("/notifications/:id", NotificationController, :show)
|
||||
|
|
@ -434,7 +434,7 @@ defmodule Pleroma.Web.Router do
|
|||
scope [] do
|
||||
pipe_through(:oauth_follow)
|
||||
|
||||
post("/follows", MastodonAPIController, :follows)
|
||||
post("/follows", AccountController, :follows)
|
||||
post("/accounts/:id/follow", AccountController, :follow)
|
||||
post("/accounts/:id/unfollow", AccountController, :unfollow)
|
||||
post("/accounts/:id/block", AccountController, :block)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue