logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

participation.ex (5881B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Conversation.Participation do
  5. use Ecto.Schema
  6. alias Pleroma.Conversation
  7. alias Pleroma.Conversation.Participation.RecipientShip
  8. alias Pleroma.Repo
  9. alias Pleroma.User
  10. alias Pleroma.Web.ActivityPub.ActivityPub
  11. import Ecto.Changeset
  12. import Ecto.Query
  13. schema "conversation_participations" do
  14. belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
  15. belongs_to(:conversation, Conversation)
  16. field(:read, :boolean, default: false)
  17. field(:last_activity_id, FlakeId.Ecto.CompatType, virtual: true)
  18. has_many(:recipient_ships, RecipientShip)
  19. has_many(:recipients, through: [:recipient_ships, :user])
  20. timestamps()
  21. end
  22. def creation_cng(struct, params) do
  23. struct
  24. |> cast(params, [:user_id, :conversation_id, :read])
  25. |> validate_required([:user_id, :conversation_id])
  26. end
  27. def create_for_user_and_conversation(user, conversation, opts \\ []) do
  28. read = !!opts[:read]
  29. invisible_conversation = !!opts[:invisible_conversation]
  30. update_on_conflict =
  31. if(invisible_conversation, do: [], else: [read: read])
  32. |> Keyword.put(:updated_at, NaiveDateTime.utc_now())
  33. %__MODULE__{}
  34. |> creation_cng(%{
  35. user_id: user.id,
  36. conversation_id: conversation.id,
  37. read: invisible_conversation || read
  38. })
  39. |> Repo.insert(
  40. on_conflict: [set: update_on_conflict],
  41. returning: true,
  42. conflict_target: [:user_id, :conversation_id]
  43. )
  44. end
  45. def read_cng(struct, params) do
  46. struct
  47. |> cast(params, [:read])
  48. |> validate_required([:read])
  49. end
  50. def mark_as_read(%User{} = user, %Conversation{} = conversation) do
  51. with %__MODULE__{} = participation <- for_user_and_conversation(user, conversation) do
  52. mark_as_read(participation)
  53. end
  54. end
  55. def mark_as_read(%__MODULE__{} = participation) do
  56. participation
  57. |> change(read: true)
  58. |> Repo.update()
  59. end
  60. def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do
  61. target_conversation_ids =
  62. __MODULE__
  63. |> where([p], p.user_id == ^target_user.id)
  64. |> select([p], p.conversation_id)
  65. |> Repo.all()
  66. __MODULE__
  67. |> where([p], p.user_id == ^user.id)
  68. |> where([p], p.conversation_id in ^target_conversation_ids)
  69. |> update([p], set: [read: true])
  70. |> Repo.update_all([])
  71. {:ok, user, []}
  72. end
  73. def mark_all_as_read(%User{} = user, %User{}), do: {:ok, user, []}
  74. def mark_all_as_read(%User{} = user) do
  75. {_, participations} =
  76. __MODULE__
  77. |> where([p], p.user_id == ^user.id)
  78. |> where([p], not p.read)
  79. |> update([p], set: [read: true])
  80. |> select([p], p)
  81. |> Repo.update_all([])
  82. {:ok, user, participations}
  83. end
  84. def mark_as_unread(participation) do
  85. participation
  86. |> read_cng(%{read: false})
  87. |> Repo.update()
  88. end
  89. def for_user(user, params \\ %{}) do
  90. from(p in __MODULE__,
  91. where: p.user_id == ^user.id,
  92. order_by: [desc: p.updated_at],
  93. preload: [conversation: [:users]]
  94. )
  95. |> restrict_recipients(user, params)
  96. |> Pleroma.Pagination.fetch_paginated(params)
  97. end
  98. def restrict_recipients(query, user, %{recipients: user_ids}) do
  99. user_binary_ids =
  100. [user.id | user_ids]
  101. |> Enum.uniq()
  102. |> User.binary_id()
  103. conversation_subquery =
  104. __MODULE__
  105. |> group_by([p], p.conversation_id)
  106. |> having(
  107. [p],
  108. count(p.user_id) == ^length(user_binary_ids) and
  109. fragment("array_agg(?) @> ?", p.user_id, ^user_binary_ids)
  110. )
  111. |> select([p], %{id: p.conversation_id})
  112. query
  113. |> join(:inner, [p], c in subquery(conversation_subquery), on: p.conversation_id == c.id)
  114. end
  115. def restrict_recipients(query, _, _), do: query
  116. def for_user_and_conversation(user, conversation) do
  117. from(p in __MODULE__,
  118. where: p.user_id == ^user.id,
  119. where: p.conversation_id == ^conversation.id
  120. )
  121. |> Repo.one()
  122. end
  123. def for_user_with_last_activity_id(user, params \\ %{}) do
  124. for_user(user, params)
  125. |> Enum.map(fn participation ->
  126. activity_id =
  127. ActivityPub.fetch_latest_direct_activity_id_for_context(
  128. participation.conversation.ap_id,
  129. %{
  130. user: user,
  131. blocking_user: user
  132. }
  133. )
  134. %{
  135. participation
  136. | last_activity_id: activity_id
  137. }
  138. end)
  139. |> Enum.reject(&is_nil(&1.last_activity_id))
  140. end
  141. def get(_, _ \\ [])
  142. def get(nil, _), do: nil
  143. def get(id, params) do
  144. query =
  145. if preload = params[:preload] do
  146. from(p in __MODULE__,
  147. preload: ^preload
  148. )
  149. else
  150. __MODULE__
  151. end
  152. Repo.get(query, id)
  153. end
  154. def set_recipients(participation, user_ids) do
  155. user_ids =
  156. [participation.user_id | user_ids]
  157. |> Enum.uniq()
  158. Repo.transaction(fn ->
  159. query =
  160. from(r in RecipientShip,
  161. where: r.participation_id == ^participation.id
  162. )
  163. Repo.delete_all(query)
  164. users =
  165. from(u in User,
  166. where: u.id in ^user_ids
  167. )
  168. |> Repo.all()
  169. RecipientShip.create(users, participation)
  170. :ok
  171. end)
  172. {:ok, Repo.preload(participation, :recipients, force: true)}
  173. end
  174. @spec unread_count(User.t()) :: integer()
  175. def unread_count(%User{id: user_id}) do
  176. from(q in __MODULE__, where: q.user_id == ^user_id and q.read == false)
  177. |> Repo.aggregate(:count, :id)
  178. end
  179. def unread_conversation_count_for_user(user) do
  180. from(p in __MODULE__,
  181. where: p.user_id == ^user.id,
  182. where: not p.read,
  183. select: %{count: count(p.id)}
  184. )
  185. end
  186. def delete(%__MODULE__{} = participation) do
  187. Repo.delete(participation)
  188. end
  189. end