fix(announcements): make sure only valid announcements are shown to the user

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2024-01-05 17:51:41 +01:00
parent dcbb8eae01
commit c9a1c35aa7
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
8 changed files with 95 additions and 4 deletions

View file

@ -20,6 +20,7 @@ defmodule Mobilizon.GraphQL.Schema.ConversationType do
) )
field(:last_comment, :comment, description: "The last comment of the conversation") 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 field :comments, :paginated_comment_list do
arg(:page, :integer, default_value: 1) arg(:page, :integer, default_value: 1)

View file

@ -47,6 +47,12 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
field(:threadLanguages, non_null(list_of(:string)), description: "The thread languages") field(:threadLanguages, non_null(list_of(:string)), description: "The thread languages")
field(:actor, :person, resolve: dataloader(Actors), description: "The comment's author") 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(:inserted_at, :datetime, description: "When was the comment inserted in database")
field(:updated_at, :datetime, description: "When was the comment updated") field(:updated_at, :datetime, description: "When was the comment updated")
field(:deleted_at, :datetime, description: "When was the comment deleted") field(:deleted_at, :datetime, description: "When was the comment deleted")

View file

@ -137,7 +137,7 @@ defmodule Mobilizon.Conversations do
subquery subquery
|> subquery() |> subquery()
|> order_by([cp], desc: cp.unread, desc: cp.updated_at) |> 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) |> Page.build_page(page, limit)
end end
@ -151,7 +151,7 @@ defmodule Mobilizon.Conversations do
ConversationParticipant ConversationParticipant
|> join(:inner, [cp], a in Actor, on: cp.actor_id == a.id) |> join(:inner, [cp], a in Actor, on: cp.actor_id == a.id)
|> where([_cp, a], a.user_id == ^user_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) |> Page.build_page(page, limit)
end end

View file

@ -13,6 +13,9 @@ export const COMMENT_FIELDS_FRAGMENT = gql`
actor { actor {
...ActorFragment ...ActorFragment
} }
attributedTo {
...ActorFragment
}
totalReplies totalReplies
insertedAt insertedAt
updatedAt updatedAt

View file

@ -12,6 +12,9 @@ export const CONVERSATION_QUERY_FRAGMENT = gql`
lastComment { lastComment {
...CommentFields ...CommentFields
} }
originComment {
...CommentFields
}
participants { participants {
...ActorFragment ...ActorFragment
} }
@ -19,6 +22,12 @@ export const CONVERSATION_QUERY_FRAGMENT = gql`
id id
uuid uuid
title title
organizerActor {
id
}
attributedTo {
id
}
picture { picture {
id id
url url

View file

@ -8,6 +8,7 @@ export interface IConversation {
id?: string; id?: string;
actor?: IActor; actor?: IActor;
lastComment?: IComment; lastComment?: IComment;
originComment?: IComment;
comments: Paginate<IComment>; comments: Paginate<IComment>;
participants: IActor[]; participants: IActor[];
updatedAt: string; updatedAt: string;

View file

@ -14,7 +14,11 @@
]" ]"
/> />
<div <div
v-if="conversation.event && !isCurrentActorAuthor" v-if="
conversation.event &&
!isCurrentActorAuthor &&
isOriginCommentAuthorEventOrganizer
"
class="bg-mbz-yellow p-6 mb-3 rounded flex gap-2 items-center" class="bg-mbz-yellow p-6 mb-3 rounded flex gap-2 items-center"
> >
<Calendar :size="36" /> <Calendar :size="36" />
@ -132,7 +136,11 @@
> >
</form> </form>
<div <div
v-else-if="conversation.event" v-else-if="
conversation.event &&
!isCurrentActorAuthor &&
isOriginCommentAuthorEventOrganizer
"
class="bg-mbz-yellow p-6 rounded flex gap-2 items-center mt-3" class="bg-mbz-yellow p-6 rounded flex gap-2 items-center mt-3"
> >
<Calendar :size="36" /> <Calendar :size="36" />
@ -292,6 +300,16 @@ const isCurrentActorAuthor = computed(
currentActor.value.id !== conversation.value?.actor?.id 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({ useHead({
title: () => title.value, title: () => title.value,
}); });

View file

@ -6,6 +6,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CreateTest do
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Conversations.Conversation alias Mobilizon.Conversations.Conversation
alias Mobilizon.Discussions.Comment alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.Event
alias Mobilizon.Federation.ActivityPub.Transmogrifier alias Mobilizon.Federation.ActivityPub.Transmogrifier
alias Mobilizon.Service.HTTP.ActivityPub.Mock 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") {:ok, admin} = Mobilizon.Actors.get_actor_by_url("https://framapiaf.org/users/admin")
assert participant_ids == MapSet.new([actor.id, admin.id]) assert participant_ids == MapSet.new([actor.id, admin.id])
end 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
end end