From f6fab01ba7a08fe0e5147f82d9e3dd294922dc93 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Sun, 17 Mar 2019 17:06:28 +0100 Subject: Web.Router: Add routes for Conversation mastoAPI --- lib/pleroma/web/router.ex | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index befd382ba..6d000b7f5 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -243,6 +243,9 @@ defmodule Pleroma.Web.Router do get("/suggestions", MastodonAPIController, :suggestions) + get("/conversations", MastodonAPIController, :conversations) + get("/conversations/:id/read", MastodonAPIController, :get_conversation) + get("/endorsements", MastodonAPIController, :empty_array) get("/pleroma/flavour", MastodonAPIController, :get_flavour) -- cgit v1.2.3 From d1da6b155ab758ae4eb8fa154997a0a2a179897c Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 09:34:53 +0200 Subject: Conversation: Add Conversations and Participations. --- lib/conversation.ex | 30 ++++++++++++++++++++++++++++++ lib/conversation/participation.ex | 31 +++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 lib/conversation.ex create mode 100644 lib/conversation/participation.ex (limited to 'lib') diff --git a/lib/conversation.ex b/lib/conversation.ex new file mode 100644 index 000000000..cfb78d925 --- /dev/null +++ b/lib/conversation.ex @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Conversation do + alias Pleroma.Repo + alias Pleroma.Conversation.Participation + use Ecto.Schema + import Ecto.Changeset + + schema "conversations" do + field(:ap_id, :string) + has_many(:participations, Participation) + + timestamps() + end + + def creation_cng(struct, params) do + struct + |> cast(params, [:ap_id]) + |> validate_required([:ap_id]) + |> unique_constraint(:ap_id) + end + + def create_for_ap_id(ap_id) do + %__MODULE__{} + |> creation_cng(%{ap_id: ap_id}) + |> Repo.insert() + end +end diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex new file mode 100644 index 000000000..244d37c46 --- /dev/null +++ b/lib/conversation/participation.ex @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Conversation.Participation do + use Ecto.Schema + alias Pleroma.User + alias Pleroma.Conversation + alias Pleroma.Repo + import Ecto.Changeset + + schema "conversation_participations" do + belongs_to(:user, User, type: Pleroma.FlakeId) + belongs_to(:conversation, Conversation) + field(:read, :boolean, default: false) + + timestamps() + end + + def creation_cng(struct, params) do + struct + |> cast(params, [:user_id, :conversation_id]) + |> validate_required([:user_id, :conversation_id]) + end + + def create_for_user_and_conversation(user, conversation) do + %__MODULE__{} + |> creation_cng(%{user_id: user.id, conversation_id: conversation.id}) + |> Repo.insert() + end +end -- cgit v1.2.3 From 64c1c3a4071f3f99a59f38e2dcde499bda3969cf Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 15:12:01 +0200 Subject: Participations: Add marking as read and unread. --- lib/conversation/participation.ex | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'lib') diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex index 244d37c46..ab59a529e 100644 --- a/lib/conversation/participation.ex +++ b/lib/conversation/participation.ex @@ -28,4 +28,22 @@ defmodule Pleroma.Conversation.Participation do |> creation_cng(%{user_id: user.id, conversation_id: conversation.id}) |> Repo.insert() end + + def read_cng(struct, params) do + struct + |> cast(params, [:read]) + |> validate_required([:read]) + end + + def mark_as_read(participation) do + participation + |> read_cng(%{read: true}) + |> Repo.update() + end + + def mark_as_unread(participation) do + participation + |> read_cng(%{read: false}) + |> Repo.update() + end end -- cgit v1.2.3 From 280172f6f6d74872349e3b4e6f1feaa9c95b3900 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 16:33:45 +0200 Subject: Conversations: Create or bump on inserting a dm. --- lib/conversation.ex | 42 +++++++++++++++++++++++++++- lib/conversation/participation.ex | 6 +++- lib/pleroma/web/activity_pub/activity_pub.ex | 2 ++ 3 files changed, 48 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/conversation.ex b/lib/conversation.ex index cfb78d925..3d53e91b7 100644 --- a/lib/conversation.ex +++ b/lib/conversation.ex @@ -5,10 +5,12 @@ defmodule Pleroma.Conversation do alias Pleroma.Repo alias Pleroma.Conversation.Participation + alias Pleroma.User use Ecto.Schema import Ecto.Changeset schema "conversations" do + # This is the context ap id. field(:ap_id, :string) has_many(:participations, Participation) @@ -25,6 +27,44 @@ defmodule Pleroma.Conversation do def create_for_ap_id(ap_id) do %__MODULE__{} |> creation_cng(%{ap_id: ap_id}) - |> Repo.insert() + |> Repo.insert( + on_conflict: [set: [updated_at: NaiveDateTime.utc_now()]], + returning: true, + conflict_target: :ap_id + ) + end + + def get_for_ap_id(ap_id) do + Repo.get_by(__MODULE__, ap_id: ap_id) + end + + @doc """ + This will + 1. Create a conversation if there isn't one already + 2. Create a participation for all the people involved who don't have one already + 3. Bump all relevant participations to 'unread' + """ + def create_or_bump_for(activity) do + with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), + "Create" <- activity.data["type"], + "Note" <- activity.data["object"]["type"], + ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do + {:ok, conversation} = create_for_ap_id(ap_id) + + local_users = User.get_users_from_set(activity.recipients, true) + + participations = + Enum.map(local_users, fn user -> + {:ok, participation} = + Participation.create_for_user_and_conversation(user, conversation) + + participation + end) + + %{ + conversation + | participations: participations + } + end end end diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex index ab59a529e..a58d0ca0d 100644 --- a/lib/conversation/participation.ex +++ b/lib/conversation/participation.ex @@ -26,7 +26,11 @@ defmodule Pleroma.Conversation.Participation do def create_for_user_and_conversation(user, conversation) do %__MODULE__{} |> creation_cng(%{user_id: user.id, conversation_id: conversation.id}) - |> Repo.insert() + |> Repo.insert( + on_conflict: [set: [read: false, updated_at: NaiveDateTime.utc_now()]], + returning: true, + conflict_target: [:user_id, :conversation_id] + ) end def read_cng(struct, params) do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index f217e7bac..880d19a5e 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.Activity + alias Pleroma.Conversation alias Pleroma.Instances alias Pleroma.Notification alias Pleroma.Object @@ -143,6 +144,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end) Notification.create_notifications(activity) + Conversation.create_or_bump_for(activity) stream_out(activity) {:ok, activity} else -- cgit v1.2.3 From 20d9b9076051d2dea60919ad85aaf88154629dc4 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 17:05:33 +0200 Subject: Participation: Get for a user. --- lib/conversation.ex | 4 ++-- lib/conversation/participation.ex | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/conversation.ex b/lib/conversation.ex index 3d53e91b7..6eedc72b6 100644 --- a/lib/conversation.ex +++ b/lib/conversation.ex @@ -51,10 +51,10 @@ defmodule Pleroma.Conversation do ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do {:ok, conversation} = create_for_ap_id(ap_id) - local_users = User.get_users_from_set(activity.recipients, true) + users = User.get_users_from_set(activity.recipients) participations = - Enum.map(local_users, fn user -> + Enum.map(users, fn user -> {:ok, participation} = Participation.create_for_user_and_conversation(user, conversation) diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex index a58d0ca0d..23e6409f1 100644 --- a/lib/conversation/participation.ex +++ b/lib/conversation/participation.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Conversation.Participation do alias Pleroma.Conversation alias Pleroma.Repo import Ecto.Changeset + import Ecto.Query schema "conversation_participations" do belongs_to(:user, User, type: Pleroma.FlakeId) @@ -50,4 +51,12 @@ defmodule Pleroma.Conversation.Participation do |> read_cng(%{read: false}) |> Repo.update() end + + def for_user(user, params \\ %{}) do + from(p in __MODULE__, + where: p.user_id == ^user.id, + order_by: [desc: p.updated_at] + ) + |> Pleroma.Pagination.fetch_paginated(params) + end end -- cgit v1.2.3 From cf353514feff50c2ccb9a8079ce5e695eb7f8cb6 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 17:28:02 +0200 Subject: Participations: Add last activity. --- lib/conversation/participation.ex | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'lib') diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex index 23e6409f1..4183af8a7 100644 --- a/lib/conversation/participation.ex +++ b/lib/conversation/participation.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Conversation.Participation do alias Pleroma.User alias Pleroma.Conversation alias Pleroma.Repo + alias Pleroma.Web.ActivityPub.ActivityPub import Ecto.Changeset import Ecto.Query @@ -14,6 +15,7 @@ defmodule Pleroma.Conversation.Participation do belongs_to(:user, User, type: Pleroma.FlakeId) belongs_to(:conversation, Conversation) field(:read, :boolean, default: false) + field(:last_activity_id, Pleroma.FlakeId, virtual: true) timestamps() end @@ -59,4 +61,29 @@ defmodule Pleroma.Conversation.Participation do ) |> Pleroma.Pagination.fetch_paginated(params) end + + def for_user_with_last_activity_id(user, params \\ %{}) do + for_user(user, params) + |> Repo.preload(:conversation) + |> Enum.map(fn participation -> + # TODO: Don't load all those activities, just get the most recent + # Involves splitting up the query. + activities = + ActivityPub.fetch_activities_for_context(participation.conversation.ap_id, %{ + "user" => user, + "blocking_user" => user + }) + + activity_id = + case activities do + [activity | _] -> activity.id + _ -> nil + end + + %{ + participation + | last_activity_id: activity_id + } + end) + end end -- cgit v1.2.3 From c352a0aba601ae444bf5b479ab3c643728a8b35e Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 17:48:31 +0200 Subject: Conversations: Make tests run. --- .../web/mastodon_api/mastodon_api_controller.ex | 36 ++++++++++++++++++++++ lib/pleroma/web/router.ex | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 5462ce8be..57f73dacd 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Ecto.Changeset alias Pleroma.Activity alias Pleroma.Config + alias Pleroma.Conversation.Participation alias Pleroma.Filter alias Pleroma.Notification alias Pleroma.Object @@ -1584,6 +1585,41 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end + def conversations(%{assigns: %{user: user}} = conn, params) do + participations = Participation.for_user_with_last_activity_id(user, params) + + conversations = + Enum.map(participations, fn participation -> + %{ + id: participation.id, + # TODO: Add this. + accounts: [], + unread: !participation.read, + last_status: participation.last_activity_id + } + end) + + conn + |> add_link_headers(:conversations, participations) + |> json(conversations) + end + + def conversation_read(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do + with %Participation{} = participation <- + Repo.get_by(Participation, id: participation_id, user_id: user.id), + {:ok, participation} <- Participation.mark_as_read(participation) do + conn + |> json(%{ + id: participation.id, + # TODO: Add this. + accounts: [], + unread: !participation.read, + # TODO: Add this. + last_status: nil + }) + end + end + def try_render(conn, target, params) when is_binary(target) do res = render(conn, target, params) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 0af743b80..dc5119c50 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -273,7 +273,7 @@ defmodule Pleroma.Web.Router do get("/suggestions", MastodonAPIController, :suggestions) get("/conversations", MastodonAPIController, :conversations) - get("/conversations/:id/read", MastodonAPIController, :get_conversation) + post("/conversations/:id/read", MastodonAPIController, :conversation_read) get("/endorsements", MastodonAPIController, :empty_array) -- cgit v1.2.3 From d115d2a27e2e7a9df466fc4393416f804cb7e8e2 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 18:17:22 +0200 Subject: Conversations: Tidying up. --- lib/conversation.ex | 70 ------------------------ lib/conversation/participation.ex | 89 ------------------------------- lib/pleroma/conversation.ex | 70 ++++++++++++++++++++++++ lib/pleroma/conversation/participation.ex | 89 +++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 159 deletions(-) delete mode 100644 lib/conversation.ex delete mode 100644 lib/conversation/participation.ex create mode 100644 lib/pleroma/conversation.ex create mode 100644 lib/pleroma/conversation/participation.ex (limited to 'lib') diff --git a/lib/conversation.ex b/lib/conversation.ex deleted file mode 100644 index 6eedc72b6..000000000 --- a/lib/conversation.ex +++ /dev/null @@ -1,70 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Conversation do - alias Pleroma.Repo - alias Pleroma.Conversation.Participation - alias Pleroma.User - use Ecto.Schema - import Ecto.Changeset - - schema "conversations" do - # This is the context ap id. - field(:ap_id, :string) - has_many(:participations, Participation) - - timestamps() - end - - def creation_cng(struct, params) do - struct - |> cast(params, [:ap_id]) - |> validate_required([:ap_id]) - |> unique_constraint(:ap_id) - end - - def create_for_ap_id(ap_id) do - %__MODULE__{} - |> creation_cng(%{ap_id: ap_id}) - |> Repo.insert( - on_conflict: [set: [updated_at: NaiveDateTime.utc_now()]], - returning: true, - conflict_target: :ap_id - ) - end - - def get_for_ap_id(ap_id) do - Repo.get_by(__MODULE__, ap_id: ap_id) - end - - @doc """ - This will - 1. Create a conversation if there isn't one already - 2. Create a participation for all the people involved who don't have one already - 3. Bump all relevant participations to 'unread' - """ - def create_or_bump_for(activity) do - with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), - "Create" <- activity.data["type"], - "Note" <- activity.data["object"]["type"], - ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do - {:ok, conversation} = create_for_ap_id(ap_id) - - users = User.get_users_from_set(activity.recipients) - - participations = - Enum.map(users, fn user -> - {:ok, participation} = - Participation.create_for_user_and_conversation(user, conversation) - - participation - end) - - %{ - conversation - | participations: participations - } - end - end -end diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex deleted file mode 100644 index 4183af8a7..000000000 --- a/lib/conversation/participation.ex +++ /dev/null @@ -1,89 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Conversation.Participation do - use Ecto.Schema - alias Pleroma.User - alias Pleroma.Conversation - alias Pleroma.Repo - alias Pleroma.Web.ActivityPub.ActivityPub - import Ecto.Changeset - import Ecto.Query - - schema "conversation_participations" do - belongs_to(:user, User, type: Pleroma.FlakeId) - belongs_to(:conversation, Conversation) - field(:read, :boolean, default: false) - field(:last_activity_id, Pleroma.FlakeId, virtual: true) - - timestamps() - end - - def creation_cng(struct, params) do - struct - |> cast(params, [:user_id, :conversation_id]) - |> validate_required([:user_id, :conversation_id]) - end - - def create_for_user_and_conversation(user, conversation) do - %__MODULE__{} - |> creation_cng(%{user_id: user.id, conversation_id: conversation.id}) - |> Repo.insert( - on_conflict: [set: [read: false, updated_at: NaiveDateTime.utc_now()]], - returning: true, - conflict_target: [:user_id, :conversation_id] - ) - end - - def read_cng(struct, params) do - struct - |> cast(params, [:read]) - |> validate_required([:read]) - end - - def mark_as_read(participation) do - participation - |> read_cng(%{read: true}) - |> Repo.update() - end - - def mark_as_unread(participation) do - participation - |> read_cng(%{read: false}) - |> Repo.update() - end - - def for_user(user, params \\ %{}) do - from(p in __MODULE__, - where: p.user_id == ^user.id, - order_by: [desc: p.updated_at] - ) - |> Pleroma.Pagination.fetch_paginated(params) - end - - def for_user_with_last_activity_id(user, params \\ %{}) do - for_user(user, params) - |> Repo.preload(:conversation) - |> Enum.map(fn participation -> - # TODO: Don't load all those activities, just get the most recent - # Involves splitting up the query. - activities = - ActivityPub.fetch_activities_for_context(participation.conversation.ap_id, %{ - "user" => user, - "blocking_user" => user - }) - - activity_id = - case activities do - [activity | _] -> activity.id - _ -> nil - end - - %{ - participation - | last_activity_id: activity_id - } - end) - end -end diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex new file mode 100644 index 000000000..a77a7cd6e --- /dev/null +++ b/lib/pleroma/conversation.ex @@ -0,0 +1,70 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Conversation do + alias Pleroma.Conversation.Participation + alias Pleroma.Repo + alias Pleroma.User + use Ecto.Schema + import Ecto.Changeset + + schema "conversations" do + # This is the context ap id. + field(:ap_id, :string) + has_many(:participations, Participation) + + timestamps() + end + + def creation_cng(struct, params) do + struct + |> cast(params, [:ap_id]) + |> validate_required([:ap_id]) + |> unique_constraint(:ap_id) + end + + def create_for_ap_id(ap_id) do + %__MODULE__{} + |> creation_cng(%{ap_id: ap_id}) + |> Repo.insert( + on_conflict: [set: [updated_at: NaiveDateTime.utc_now()]], + returning: true, + conflict_target: :ap_id + ) + end + + def get_for_ap_id(ap_id) do + Repo.get_by(__MODULE__, ap_id: ap_id) + end + + @doc """ + This will + 1. Create a conversation if there isn't one already + 2. Create a participation for all the people involved who don't have one already + 3. Bump all relevant participations to 'unread' + """ + def create_or_bump_for(activity) do + with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), + "Create" <- activity.data["type"], + "Note" <- activity.data["object"]["type"], + ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do + {:ok, conversation} = create_for_ap_id(ap_id) + + users = User.get_users_from_set(activity.recipients) + + participations = + Enum.map(users, fn user -> + {:ok, participation} = + Participation.create_for_user_and_conversation(user, conversation) + + participation + end) + + %{ + conversation + | participations: participations + } + end + end +end diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex new file mode 100644 index 000000000..1a2ceafeb --- /dev/null +++ b/lib/pleroma/conversation/participation.ex @@ -0,0 +1,89 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Conversation.Participation do + use Ecto.Schema + alias Pleroma.Conversation + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + import Ecto.Changeset + import Ecto.Query + + schema "conversation_participations" do + belongs_to(:user, User, type: Pleroma.FlakeId) + belongs_to(:conversation, Conversation) + field(:read, :boolean, default: false) + field(:last_activity_id, Pleroma.FlakeId, virtual: true) + + timestamps() + end + + def creation_cng(struct, params) do + struct + |> cast(params, [:user_id, :conversation_id]) + |> validate_required([:user_id, :conversation_id]) + end + + def create_for_user_and_conversation(user, conversation) do + %__MODULE__{} + |> creation_cng(%{user_id: user.id, conversation_id: conversation.id}) + |> Repo.insert( + on_conflict: [set: [read: false, updated_at: NaiveDateTime.utc_now()]], + returning: true, + conflict_target: [:user_id, :conversation_id] + ) + end + + def read_cng(struct, params) do + struct + |> cast(params, [:read]) + |> validate_required([:read]) + end + + def mark_as_read(participation) do + participation + |> read_cng(%{read: true}) + |> Repo.update() + end + + def mark_as_unread(participation) do + participation + |> read_cng(%{read: false}) + |> Repo.update() + end + + def for_user(user, params \\ %{}) do + from(p in __MODULE__, + where: p.user_id == ^user.id, + order_by: [desc: p.updated_at] + ) + |> Pleroma.Pagination.fetch_paginated(params) + end + + def for_user_with_last_activity_id(user, params \\ %{}) do + for_user(user, params) + |> Repo.preload(:conversation) + |> Enum.map(fn participation -> + # TODO: Don't load all those activities, just get the most recent + # Involves splitting up the query. + activities = + ActivityPub.fetch_activities_for_context(participation.conversation.ap_id, %{ + "user" => user, + "blocking_user" => user + }) + + activity_id = + case activities do + [activity | _] -> activity.id + _ -> nil + end + + %{ + participation + | last_activity_id: activity_id + } + end) + end +end -- cgit v1.2.3 From c1ebb38d3adc1d222be832405ec0d7497b61f94a Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 15 Apr 2019 21:45:25 +0200 Subject: Conversation: Also create participations for remote users. Needed to get the participating user list. --- lib/pleroma/conversation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index a77a7cd6e..5a2a3fc6d 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -51,7 +51,7 @@ defmodule Pleroma.Conversation do ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do {:ok, conversation} = create_for_ap_id(ap_id) - users = User.get_users_from_set(activity.recipients) + users = User.get_users_from_set(activity.recipients, false) participations = Enum.map(users, fn user -> -- cgit v1.2.3 From 0da985182f26927de0d2d9733816600afb89e79e Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 15 Apr 2019 21:58:58 +0200 Subject: Conversation: Return full status object, id is a string. --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 57f73dacd..3ffb767b9 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1590,12 +1590,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do conversations = Enum.map(participations, fn participation -> + activity = Activity.get_by_id_with_object(participation.last_activity_id) + + last_status = StatusView.render("status.json", %{activity: activity, for: user}) + %{ - id: participation.id, + id: participation.id |> to_string(), # TODO: Add this. accounts: [], unread: !participation.read, - last_status: participation.last_activity_id + last_status: last_status } end) -- cgit v1.2.3 From 76999c73a790232ff0c30fff7a51589fb44651a3 Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 15 Apr 2019 22:28:42 +0200 Subject: Conversation: Add accounts to output. --- lib/pleroma/conversation.ex | 1 + lib/pleroma/conversation/participation.ex | 1 + lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 9 +++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index 5a2a3fc6d..d9c84cb1b 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -13,6 +13,7 @@ defmodule Pleroma.Conversation do # This is the context ap id. field(:ap_id, :string) has_many(:participations, Participation) + has_many(:users, through: [:participations, :user]) timestamps() end diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index 1a2ceafeb..f200c1df5 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -60,6 +60,7 @@ defmodule Pleroma.Conversation.Participation do order_by: [desc: p.updated_at] ) |> Pleroma.Pagination.fetch_paginated(params) + |> Repo.preload(conversation: [:users]) end def for_user_with_last_activity_id(user, params \\ %{}) do diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 3ffb767b9..c7166ff28 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1594,10 +1594,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do last_status = StatusView.render("status.json", %{activity: activity, for: user}) + accounts = + AccountView.render("accounts.json", %{ + users: participation.conversation.users, + as: :user + }) + %{ id: participation.id |> to_string(), - # TODO: Add this. - accounts: [], + accounts: accounts, unread: !participation.read, last_status: last_status } -- cgit v1.2.3 From 24073f829f18d1ebd2cb23dd815c775b39145e42 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 21 Apr 2019 00:40:41 +0700 Subject: Refactor query to return only 1 message instead of 20 --- lib/pleroma/conversation/participation.ex | 13 ++----- lib/pleroma/web/activity_pub/activity_pub.ex | 53 ++++++++++++++++------------ 2 files changed, 33 insertions(+), 33 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index f200c1df5..61021fb18 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -65,22 +65,13 @@ defmodule Pleroma.Conversation.Participation do def for_user_with_last_activity_id(user, params \\ %{}) do for_user(user, params) - |> Repo.preload(:conversation) |> Enum.map(fn participation -> - # TODO: Don't load all those activities, just get the most recent - # Involves splitting up the query. - activities = - ActivityPub.fetch_activities_for_context(participation.conversation.ap_id, %{ + activity_id = + ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{ "user" => user, "blocking_user" => user }) - activity_id = - case activities do - [activity | _] -> activity.id - _ -> nil - end - %{ participation | last_activity_id: activity_id diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 880d19a5e..577e6a59e 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -459,35 +459,44 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - def fetch_activities_for_context(context, opts \\ %{}) do + defp fetch_activities_for_context_query(context, opts) do public = ["https://www.w3.org/ns/activitystreams#Public"] recipients = if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public - query = from(activity in Activity) - - query = - query - |> restrict_blocked(opts) - |> restrict_recipients(recipients, opts["user"]) - - query = - from( - activity in query, - where: - fragment( - "?->>'type' = ? and ?->>'context' = ?", - activity.data, - "Create", - activity.data, - ^context - ), - order_by: [desc: :id] + from(activity in Activity) + |> restrict_blocked(opts) + |> restrict_recipients(recipients, opts["user"]) + |> where( + [activity], + fragment( + "?->>'type' = ? and ?->>'context' = ?", + activity.data, + "Create", + activity.data, + ^context ) - |> Activity.with_preloaded_object() + ) + |> order_by([activity], desc: activity.id) + end + + @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()] + def fetch_activities_for_context(context, opts \\ %{}) do + context + |> fetch_activities_for_context_query(opts) + |> Activity.with_preloaded_object() + |> Repo.all() + end - Repo.all(query) + @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) :: + Pleroma.FlakeId.t() | nil + def fetch_latest_activity_id_for_context(context, opts \\ %{}) do + context + |> fetch_activities_for_context_query(opts) + |> limit(1) + |> select([a], a.id) + |> Repo.one() end def fetch_public_activities(opts \\ %{}) do -- cgit v1.2.3 From 2662bea4e0d945edf7a24a44edf3ed39b2e64de9 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 21 Apr 2019 20:26:13 +0700 Subject: Add accounts and last_status to conversation read response --- .../web/mastodon_api/mastodon_api_controller.ex | 24 ++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index c7166ff28..86cacb0b0 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1617,14 +1617,30 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do with %Participation{} = participation <- Repo.get_by(Participation, id: participation_id, user_id: user.id), {:ok, participation} <- Participation.mark_as_read(participation) do + participation = Repo.preload(participation, conversation: :users) + + accounts = + AccountView.render("accounts.json", %{ + users: participation.conversation.users, + as: :user + }) + + last_activity_id = + ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{ + "user" => user, + "blocking_user" => user + }) + + activity = Activity.get_by_id_with_object(last_activity_id) + + last_status = StatusView.render("status.json", %{activity: activity, for: user}) + conn |> json(%{ id: participation.id, - # TODO: Add this. - accounts: [], + accounts: accounts, unread: !participation.read, - # TODO: Add this. - last_status: nil + last_status: last_status }) end end -- cgit v1.2.3 From e56afefef9c0f256561c6ecab79e5520fdeb6d5e Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 21 Apr 2019 23:14:27 +0700 Subject: Refactor conversation function in MastodonAPIController to use a View --- .../web/mastodon_api/mastodon_api_controller.ex | 44 +++------------------- .../web/mastodon_api/views/conversation_view.ex | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+), 39 deletions(-) create mode 100644 lib/pleroma/web/mastodon_api/views/conversation_view.ex (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 86cacb0b0..d5b6a943f 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -22,6 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.AppView + alias Pleroma.Web.MastodonAPI.ConversationView alias Pleroma.Web.MastodonAPI.FilterView alias Pleroma.Web.MastodonAPI.ListView alias Pleroma.Web.MastodonAPI.MastodonAPI @@ -1590,22 +1591,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do conversations = Enum.map(participations, fn participation -> - activity = Activity.get_by_id_with_object(participation.last_activity_id) - - last_status = StatusView.render("status.json", %{activity: activity, for: user}) - - accounts = - AccountView.render("accounts.json", %{ - users: participation.conversation.users, - as: :user - }) - - %{ - id: participation.id |> to_string(), - accounts: accounts, - unread: !participation.read, - last_status: last_status - } + ConversationView.render("participation.json", %{participation: participation, user: user}) end) conn @@ -1617,31 +1603,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do with %Participation{} = participation <- Repo.get_by(Participation, id: participation_id, user_id: user.id), {:ok, participation} <- Participation.mark_as_read(participation) do - participation = Repo.preload(participation, conversation: :users) - - accounts = - AccountView.render("accounts.json", %{ - users: participation.conversation.users, - as: :user - }) - - last_activity_id = - ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{ - "user" => user, - "blocking_user" => user - }) - - activity = Activity.get_by_id_with_object(last_activity_id) - - last_status = StatusView.render("status.json", %{activity: activity, for: user}) + participation_view = + ConversationView.render("participation.json", %{participation: participation, user: user}) conn - |> json(%{ - id: participation.id, - accounts: accounts, - unread: !participation.read, - last_status: last_status - }) + |> json(participation_view) end end diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex new file mode 100644 index 000000000..d841a840c --- /dev/null +++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex @@ -0,0 +1,38 @@ +defmodule Pleroma.Web.MastodonAPI.ConversationView do + use Pleroma.Web, :view + + alias Pleroma.Activity + alias Pleroma.Repo + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.MastodonAPI.StatusView + alias Pleroma.Web.MastodonAPI.AccountView + + def render("participation.json", %{participation: participation, user: user}) do + participation = Repo.preload(participation, conversation: :users) + + last_activity_id = + with nil <- participation.last_activity_id do + ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{ + "user" => user, + "blocking_user" => user + }) + end + + activity = Activity.get_by_id_with_object(last_activity_id) + + last_status = StatusView.render("status.json", %{activity: activity, for: user}) + + accounts = + AccountView.render("accounts.json", %{ + users: participation.conversation.users, + as: :user + }) + + %{ + id: participation.id |> to_string(), + accounts: accounts, + unread: !participation.read, + last_status: last_status + } + end +end -- cgit v1.2.3 From eeb093631cd50583feb4864a016d0dc1e4c58f5e Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 21 Apr 2019 23:19:36 +0700 Subject: Fix Credo warning --- lib/pleroma/web/mastodon_api/views/conversation_view.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex index d841a840c..eb61baa03 100644 --- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex +++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex @@ -3,9 +3,9 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do alias Pleroma.Activity alias Pleroma.Repo + alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.MastodonAPI.StatusView - alias Pleroma.Web.MastodonAPI.AccountView def render("participation.json", %{participation: participation, user: user}) do participation = Repo.preload(participation, conversation: :users) -- cgit v1.2.3 From 4908e0eeee2ecb58b204198c20720d52548b6f4a Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 21 Apr 2019 23:24:33 +0700 Subject: Fix Credo warning --- lib/pleroma/web/mastodon_api/views/conversation_view.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex index eb61baa03..8e8f7cf31 100644 --- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex +++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex @@ -3,8 +3,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do alias Pleroma.Activity alias Pleroma.Repo - alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.StatusView def render("participation.json", %{participation: participation, user: user}) do -- cgit v1.2.3 From 8af55728e47f9f62d237704cd5a33fba5f946fa2 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Mon, 29 Apr 2019 03:44:04 +0700 Subject: Fix tests --- lib/pleroma/conversation.ex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index d9c84cb1b..e6a4ccc85 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -47,9 +47,10 @@ defmodule Pleroma.Conversation do """ def create_or_bump_for(activity) do with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), + object <- Pleroma.Object.normalize(activity), "Create" <- activity.data["type"], - "Note" <- activity.data["object"]["type"], - ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do + "Note" <- object.data["type"], + ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do {:ok, conversation} = create_for_ap_id(ap_id) users = User.get_users_from_set(activity.recipients, false) -- cgit v1.2.3 From 81d1aa424d65b364ec8f2aee45247e7d95e3f255 Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 3 May 2019 13:39:14 +0200 Subject: Streamer: Stream out Conversations/Participations. --- lib/pleroma/conversation.ex | 11 +++++++---- lib/pleroma/web/activity_pub/activity_pub.ex | 22 ++++++++++++++++++++- lib/pleroma/web/streamer.ex | 29 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index e6a4ccc85..6e26c5fd4 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -63,10 +63,13 @@ defmodule Pleroma.Conversation do participation end) - %{ - conversation - | participations: participations - } + {:ok, + %{ + conversation + | participations: participations + }} + else + e -> {:error, e} end end end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 28754e864..6c737d0a4 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -142,8 +142,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end) Notification.create_notifications(activity) - Conversation.create_or_bump_for(activity) + + participations = + activity + |> Conversation.create_or_bump_for() + |> get_participations() + stream_out(activity) + stream_out_participations(participations) {:ok, activity} else %Activity{} = activity -> @@ -166,6 +172,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + defp get_participations({:ok, %{participations: participations}}), do: participations + defp get_participations(_), do: [] + + def stream_out_participations(participations) do + participations = + participations + |> Repo.preload(:user) + + Enum.each(participations, fn participation -> + Pleroma.Web.Streamer.stream("participation", participation) + end) + end + def stream_out(activity) do public = "https://www.w3.org/ns/activitystreams#Public" @@ -197,6 +216,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end else + # TODO: Write test, replace with visibility test if !Enum.member?(activity.data["cc"] || [], public) && !Enum.member?( activity.data["to"], diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 72eaf2084..b8f6663a1 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.Streamer do use GenServer require Logger + alias Pleroma.Conversation.Participation alias Pleroma.Activity alias Pleroma.Notification alias Pleroma.Object @@ -71,6 +72,15 @@ defmodule Pleroma.Web.Streamer do {:noreply, topics} end + def handle_cast(%{action: :stream, topic: "participation", item: participation}, topics) do + user_topic = "direct:#{participation.user_id}" + Logger.debug("Trying to push a conversation participation to #{user_topic}\n\n") + + push_to_socket(topics, user_topic, participation) + + {:noreply, topics} + end + def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do # filter the recipient list if the activity is not public, see #270. recipient_lists = @@ -192,6 +202,19 @@ defmodule Pleroma.Web.Streamer do |> Jason.encode!() end + def represent_conversation(%Participation{} = participation) do + %{ + event: "conversation", + payload: + Pleroma.Web.MastodonAPI.ConversationView.render("participation.json", %{ + participation: participation, + user: participation.user + }) + |> Jason.encode!() + } + |> Jason.encode!() + end + def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = item) do Enum.each(topics[topic] || [], fn socket -> # Get the current user so we have up-to-date blocks etc. @@ -214,6 +237,12 @@ defmodule Pleroma.Web.Streamer do end) end + def push_to_socket(topics, topic, %Participation{} = participation) do + Enum.each(topics[topic] || [], fn socket -> + send(socket.transport_pid, {:text, represent_conversation(participation)}) + end) + end + def push_to_socket(topics, topic, %Activity{ data: %{"type" => "Delete", "deleted_activity_id" => deleted_activity_id} }) do -- cgit v1.2.3 From a0c755cc4ac3af7ddb7e8fe91fcfc1bae9750e9b Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 3 May 2019 13:40:43 +0200 Subject: MastodonApi: Bump api level. --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index aa3f46482..754946624 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -159,7 +159,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end - @mastodon_api_level "2.5.0" + @mastodon_api_level "2.6.5" def masto_instance(conn, _params) do instance = Config.get(:instance) -- cgit v1.2.3 From c42ded13a2caa02f3f5aa00accce69b183546f9e Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 3 May 2019 13:53:17 +0200 Subject: Credo fixes. --- lib/pleroma/web/streamer.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index b8f6663a1..133decfc4 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -5,8 +5,8 @@ defmodule Pleroma.Web.Streamer do use GenServer require Logger - alias Pleroma.Conversation.Participation alias Pleroma.Activity + alias Pleroma.Conversation.Participation alias Pleroma.Notification alias Pleroma.Object alias Pleroma.User -- cgit v1.2.3