diff --git a/lib/graphql/schema/conversation.ex b/lib/graphql/schema/conversation.ex index 30883eaa7..c2c302c58 100644 --- a/lib/graphql/schema/conversation.ex +++ b/lib/graphql/schema/conversation.ex @@ -20,6 +20,7 @@ defmodule Mobilizon.GraphQL.Schema.ConversationType do ) field(:last_comment, :comment, description: "The last comment of the conversation") + field(:origin_comment, :comment, description: "The first comment of the conversation") field :comments, :paginated_comment_list do arg(:page, :integer, default_value: 1) diff --git a/lib/graphql/schema/discussions/comment.ex b/lib/graphql/schema/discussions/comment.ex index f8b4a8c09..2d2774e6a 100644 --- a/lib/graphql/schema/discussions/comment.ex +++ b/lib/graphql/schema/discussions/comment.ex @@ -47,6 +47,12 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do field(:threadLanguages, non_null(list_of(:string)), description: "The thread languages") field(:actor, :person, resolve: dataloader(Actors), description: "The comment's author") + + field(:attributed_to, :actor, + resolve: dataloader(Actors), + description: "The comment's attributed to actor" + ) + field(:inserted_at, :datetime, description: "When was the comment inserted in database") field(:updated_at, :datetime, description: "When was the comment updated") field(:deleted_at, :datetime, description: "When was the comment deleted") diff --git a/lib/mobilizon/conversations/conversations.ex b/lib/mobilizon/conversations/conversations.ex index 996119998..c8ca736af 100644 --- a/lib/mobilizon/conversations/conversations.ex +++ b/lib/mobilizon/conversations/conversations.ex @@ -137,7 +137,7 @@ defmodule Mobilizon.Conversations do subquery |> subquery() |> order_by([cp], desc: cp.unread, desc: cp.updated_at) - |> preload([:actor, conversation: [:last_comment, :participants]]) + |> preload([:actor, conversation: [:last_comment, :origin_comment, :participants, :event]]) |> Page.build_page(page, limit) end @@ -151,7 +151,7 @@ defmodule Mobilizon.Conversations do ConversationParticipant |> join(:inner, [cp], a in Actor, on: cp.actor_id == a.id) |> where([_cp, a], a.user_id == ^user_id) - |> preload([:actor, conversation: [:last_comment, :participants]]) + |> preload([:actor, conversation: [:last_comment, :origin_comment, :participants, :event]]) |> Page.build_page(page, limit) end diff --git a/src/graphql/comment.ts b/src/graphql/comment.ts index 6455cb1bc..20c073d60 100644 --- a/src/graphql/comment.ts +++ b/src/graphql/comment.ts @@ -13,6 +13,9 @@ export const COMMENT_FIELDS_FRAGMENT = gql` actor { ...ActorFragment } + attributedTo { + ...ActorFragment + } totalReplies insertedAt updatedAt diff --git a/src/graphql/conversations.ts b/src/graphql/conversations.ts index 32af7f823..c14c46f4d 100644 --- a/src/graphql/conversations.ts +++ b/src/graphql/conversations.ts @@ -12,6 +12,9 @@ export const CONVERSATION_QUERY_FRAGMENT = gql` lastComment { ...CommentFields } + originComment { + ...CommentFields + } participants { ...ActorFragment } @@ -19,6 +22,12 @@ export const CONVERSATION_QUERY_FRAGMENT = gql` id uuid title + organizerActor { + id + } + attributedTo { + id + } picture { id url diff --git a/src/types/conversation.ts b/src/types/conversation.ts index dbe4fb9fb..28054b67e 100644 --- a/src/types/conversation.ts +++ b/src/types/conversation.ts @@ -8,6 +8,7 @@ export interface IConversation { id?: string; actor?: IActor; lastComment?: IComment; + originComment?: IComment; comments: Paginate; participants: IActor[]; updatedAt: string; diff --git a/src/views/Conversations/ConversationView.vue b/src/views/Conversations/ConversationView.vue index f3705a322..79402efa2 100644 --- a/src/views/Conversations/ConversationView.vue +++ b/src/views/Conversations/ConversationView.vue @@ -14,7 +14,11 @@ ]" />
@@ -132,7 +136,11 @@ >
@@ -292,6 +300,16 @@ const isCurrentActorAuthor = computed( currentActor.value.id !== conversation.value?.actor?.id ); +const isOriginCommentAuthorEventOrganizer = computed( + () => + conversation.value?.originComment?.actor && + conversation.value?.event && + [ + conversation.value?.event?.organizerActor?.id, + conversation.value?.event?.attributedTo?.id, + ].includes(conversation.value?.originComment?.actor.id) +); + useHead({ title: () => title.value, }); diff --git a/test/federation/activity_pub/transmogrifier/create_test.exs b/test/federation/activity_pub/transmogrifier/create_test.exs index 11e949a63..bbe692c57 100644 --- a/test/federation/activity_pub/transmogrifier/create_test.exs +++ b/test/federation/activity_pub/transmogrifier/create_test.exs @@ -6,6 +6,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CreateTest do alias Mobilizon.Actors.Actor alias Mobilizon.Conversations.Conversation alias Mobilizon.Discussions.Comment + alias Mobilizon.Events.Event alias Mobilizon.Federation.ActivityPub.Transmogrifier alias Mobilizon.Service.HTTP.ActivityPub.Mock @@ -103,5 +104,57 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CreateTest do {:ok, admin} = Mobilizon.Actors.get_actor_by_url("https://framapiaf.org/users/admin") assert participant_ids == MapSet.new([actor.id, admin.id]) end + + test "it creates conversations for received comments if we're concerned even with reply to an event" do + actor_data = File.read!("test/fixtures/mastodon-actor.json") |> Jason.decode!() + + Mock + |> expect(:call, 1, fn + %{method: :get, url: "https://framapiaf.org/users/admin"}, _opts -> + {:ok, + %Tesla.Env{ + status: 200, + body: + actor_data + |> Map.put("id", "https://framapiaf.org/users/admin") + |> Map.put("preferredUsername", "admin") + }} + end) + + actor = insert(:actor) + data = File.read!("test/fixtures/mastodon-post-activity-private.json") |> Jason.decode!() + data = Map.put(data, "to", [actor.url]) + + %Event{id: event_id, organizer_actor_id: organizer_actor_id} = event = insert(:event) + + %Comment{url: reply_to_url, id: first_reply_comment_id} = + insert(:comment, visibility: :private, event: event) + + object = + data["object"] + |> Map.put("to", [actor.url]) + |> Map.put("inReplyTo", reply_to_url) + |> Map.put("tag", [ + data["object"]["tag"] + |> hd() + |> Map.put("href", actor.url) + |> Map.put("name", Actor.preferred_username_and_domain(actor)) + ]) + + data = Map.put(data, "object", object) + + {:ok, _activity, + %Conversation{ + origin_comment: %Comment{visibility: :private, id: origin_comment_id}, + last_comment: %Comment{visibility: :private, id: _last_comment_id}, + participants: participants, + event: %Event{id: ^event_id} + }} = Transmogrifier.handle_incoming(data) + + assert origin_comment_id == first_reply_comment_id + participant_ids = participants |> Enum.map(& &1.id) |> MapSet.new() + {:ok, admin} = Mobilizon.Actors.get_actor_by_url("https://framapiaf.org/users/admin") + assert participant_ids == MapSet.new([actor.id, organizer_actor_id, admin.id]) + end end end