ChatMessageReference: Introduce and switch in chat controller.
[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_by_id(id) do
76 __MODULE__
77 |> Repo.get(id)
78 end
79
80 def get(user_id, recipient) do
81 __MODULE__
82 |> Repo.get_by(user_id: user_id, recipient: recipient)
83 end
84
85 def get_or_create(user_id, recipient) do
86 %__MODULE__{}
87 |> creation_cng(%{user_id: user_id, recipient: recipient})
88 |> Repo.insert(
89 # Need to set something, otherwise we get nothing back at all
90 on_conflict: [set: [recipient: recipient]],
91 returning: true,
92 conflict_target: [:user_id, :recipient]
93 )
94 end
95
96 def bump_or_create(user_id, recipient) do
97 %__MODULE__{}
98 |> creation_cng(%{user_id: user_id, recipient: recipient, unread: 1})
99 |> Repo.insert(
100 on_conflict: [set: [updated_at: NaiveDateTime.utc_now()], inc: [unread: 1]],
101 conflict_target: [:user_id, :recipient]
102 )
103 end
104
105 def mark_as_read(chat) do
106 chat
107 |> change(%{unread: 0})
108 |> Repo.update()
109 end
110 end