Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms
[akkoma] / lib / pleroma / chat.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Chat do
6 use Ecto.Schema
7
8 import Ecto.Changeset
9 import Ecto.Query
10
11 alias Pleroma.Object
12 alias Pleroma.Repo
13 alias Pleroma.User
14
15 @moduledoc """
16 Chat keeps a reference to ChatMessage conversations between a user and an recipient. The recipient can be a user (for now) or a group (not implemented yet).
17
18 It is a helper only, to make it easy to display a list of chats with other people, ordered by last bump. The actual messages are retrieved by querying the recipients of the ChatMessages.
19 """
20
21 schema "chats" do
22 belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
23 field(:recipient, :string)
24 field(:unread, :integer, default: 0, read_after_writes: true)
25
26 timestamps()
27 end
28
29 def last_message_for_chat(chat) do
30 messages_for_chat_query(chat)
31 |> order_by(desc: :id)
32 |> limit(1)
33 |> Repo.one()
34 end
35
36 def messages_for_chat_query(chat) do
37 chat =
38 chat
39 |> Repo.preload(:user)
40
41 from(o in Object,
42 where: fragment("?->>'type' = ?", o.data, "ChatMessage"),
43 where:
44 fragment(
45 """
46 (?->>'actor' = ? and ?->'to' = ?)
47 OR (?->>'actor' = ? and ?->'to' = ?)
48 """,
49 o.data,
50 ^chat.user.ap_id,
51 o.data,
52 ^[chat.recipient],
53 o.data,
54 ^chat.recipient,
55 o.data,
56 ^[chat.user.ap_id]
57 )
58 )
59 end
60
61 def creation_cng(struct, params) do
62 struct
63 |> cast(params, [:user_id, :recipient, :unread])
64 |> validate_change(:recipient, fn
65 :recipient, recipient ->
66 case User.get_cached_by_ap_id(recipient) do
67 nil -> [recipient: "must be an existing user"]
68 _ -> []
69 end
70 end)
71 |> validate_required([:user_id, :recipient])
72 |> unique_constraint(:user_id, name: :chats_user_id_recipient_index)
73 end
74
75 def get(user_id, recipient) do
76 __MODULE__
77 |> Repo.get_by(user_id: user_id, recipient: recipient)
78 end
79
80 def get_or_create(user_id, recipient) do
81 %__MODULE__{}
82 |> creation_cng(%{user_id: user_id, recipient: recipient})
83 |> Repo.insert(
84 # Need to set something, otherwise we get nothing back at all
85 on_conflict: [set: [recipient: recipient]],
86 returning: true,
87 conflict_target: [:user_id, :recipient]
88 )
89 end
90
91 def bump_or_create(user_id, recipient) do
92 %__MODULE__{}
93 |> creation_cng(%{user_id: user_id, recipient: recipient, unread: 1})
94 |> Repo.insert(
95 on_conflict: [set: [updated_at: NaiveDateTime.utc_now()], inc: [unread: 1]],
96 conflict_target: [:user_id, :recipient]
97 )
98 end
99
100 def mark_as_read(chat) do
101 chat
102 |> change(%{unread: 0})
103 |> Repo.update()
104 end
105 end