diff --git a/js/src/graphql/group.ts b/js/src/graphql/group.ts index 6b0baf8a0..5f5b27584 100644 --- a/js/src/graphql/group.ts +++ b/js/src/graphql/group.ts @@ -326,6 +326,7 @@ export const GROUP_TIMELINE = gql` query GroupTimeline( $preferredUsername: String! $type: ActivityType + $author: ActivityAuthor $page: Int $limit: Int ) { @@ -334,7 +335,7 @@ export const GROUP_TIMELINE = gql` preferredUsername domain name - activity(type: $type, page: $page, limit: $limit) { + activity(type: $type, author: $author, page: $page, limit: $limit) { total elements { id diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json index 8a58bda0e..fda891bd5 100644 --- a/js/src/i18n/en_US.json +++ b/js/src/i18n/en_US.json @@ -960,5 +960,8 @@ "@{username}'s follow request was accepted": "@{username}'s follow request was accepted", "Delete this discussion": "Delete this discussion", "Are you sure you want to delete this entire discussion?": "Are you sure you want to delete this entire discussion?", - "Delete discussion": "Delete discussion" + "Delete discussion": "Delete discussion", + "All activities": "All activities", + "From yourself": "From yourself", + "By others": "By others" } diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json index d52a037c8..4c5777190 100644 --- a/js/src/i18n/fr_FR.json +++ b/js/src/i18n/fr_FR.json @@ -1054,5 +1054,8 @@ "@{username}'s follow request was accepted": "@{username}'s follow request was accepted", "Delete this discussion": "Supprimer cette discussion", "Are you sure you want to delete this entire discussion?": "Êtes-vous certain⋅e de vouloir supprimer l'entièreté de cette discussion ?", - "Delete discussion": "Supprimer la discussion" + "Delete discussion": "Supprimer la discussion", + "All activities": "Toutes les activités", + "From yourself": "De vous", + "By others": "Des autres" } diff --git a/js/src/views/Group/Timeline.vue b/js/src/views/Group/Timeline.vue index bab7d7b80..ff89edd43 100644 --- a/js/src/views/Group/Timeline.vue +++ b/js/src/views/Group/Timeline.vue @@ -23,6 +23,74 @@
+ + + + {{ $t("All activities") }} + + + {{ $t("Members") }} + + + {{ $t("Settings") }} + + + {{ $t("Events") }} + + + {{ $t("Posts") }} + + + {{ $t("Discussions") }} + + + {{ $t("Resources") }} + + + + + {{ $t("All activities") }} + + + {{ $t("From yourself") }} + + + {{ $t("By others") }} +
{ if (this.group) { return this.group.activity; diff --git a/lib/graphql/resolvers/activity.ex b/lib/graphql/resolvers/activity.ex index e76de34f7..9b7f589a6 100644 --- a/lib/graphql/resolvers/activity.ex +++ b/lib/graphql/resolvers/activity.ex @@ -13,13 +13,19 @@ defmodule Mobilizon.GraphQL.Resolvers.Activity do require Logger - def group_activity(%Actor{type: :Group, id: group_id}, %{page: page, limit: limit}, %{ + def group_activity(%Actor{type: :Group, id: group_id}, %{page: page, limit: limit} = args, %{ context: %{current_user: %User{role: role} = user} }) do with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, {:member, true} <- {:member, Actors.is_member?(actor_id, group_id) or is_moderator(role)} do %Page{total: total, elements: elements} = - Activities.list_group_activities_for_member(group_id, actor_id, [], page, limit) + Activities.list_group_activities_for_member( + group_id, + actor_id, + [type: Map.get(args, :type), author: Map.get(args, :author)], + page, + limit + ) elements = Enum.map(elements, fn %Activity{} = activity -> diff --git a/lib/graphql/schema/activity.ex b/lib/graphql/schema/activity.ex index 9a03b3f79..7901c87d5 100644 --- a/lib/graphql/schema/activity.ex +++ b/lib/graphql/schema/activity.ex @@ -19,6 +19,11 @@ defmodule Mobilizon.GraphQL.Schema.ActivityType do value(:member, description: "Activities concerning members") end + enum :activity_author do + value(:self, description: "Activities created by the current actor") + value(:by, description: "Activities created by others") + end + object :activity_param_item do field(:key, :string) field(:value, :string) diff --git a/lib/graphql/schema/actors/group.ex b/lib/graphql/schema/actors/group.ex index 83c2f9b6d..59d4690f1 100644 --- a/lib/graphql/schema/actors/group.ex +++ b/lib/graphql/schema/actors/group.ex @@ -153,6 +153,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do arg(:limit, :integer, default_value: 10, description: "The limit of activity items per page") arg(:type, :activity_type, description: "Filter by type of activity") + arg(:author, :activity_author, description: "Filter by activity author") resolve(&Activity.group_activity/3) description("The group activity") end diff --git a/lib/mobilizon/activities/activities.ex b/lib/mobilizon/activities/activities.ex index bb263c798..8c04a4e66 100644 --- a/lib/mobilizon/activities/activities.ex +++ b/lib/mobilizon/activities/activities.ex @@ -93,6 +93,7 @@ defmodule Mobilizon.Activities do ) |> where([a, m], a.inserted_at >= m.member_since) |> filter_object_type(Keyword.get(filters, :type)) + |> filter_author(Keyword.get(filters, :author), actor_asking_id) |> order_by(desc: :inserted_at) |> preload([:author, :group]) |> Page.build_page(page, limit) @@ -158,12 +159,21 @@ defmodule Mobilizon.Activities do def activity_types, do: @activity_types - @spec filter_object_type(Query.t(), atom()) :: Query.t() - defp filter_object_type(query, :type) do - where(query, [q], q.type == ^:type) + @spec filter_object_type(Query.t(), atom() | nil) :: Query.t() + defp filter_object_type(query, nil), do: query + + defp filter_object_type(query, type) do + where(query, [q], q.type == ^type) end - defp filter_object_type(query, _) do - query + @spec filter_author(Query.t(), atom() | nil, integer() | String.t()) :: Query.t() + defp filter_author(query, nil, _), do: query + + defp filter_author(query, :self, actor_asking_id) do + where(query, [q], q.author_id == ^actor_asking_id) + end + + defp filter_author(query, :by, actor_asking_id) do + where(query, [q], q.author_id != ^actor_asking_id) end end diff --git a/test/graphql/resolvers/activity_test.exs b/test/graphql/resolvers/activity_test.exs index b3d149fea..437c0e55d 100644 --- a/test/graphql/resolvers/activity_test.exs +++ b/test/graphql/resolvers/activity_test.exs @@ -21,6 +21,7 @@ defmodule Mobilizon.GraphQL.Resolvers.ActivityTest do query GroupTimeline( $preferredUsername: String! $type: ActivityType + $author: ActivityAuthor $page: Int $limit: Int ) { @@ -29,7 +30,7 @@ defmodule Mobilizon.GraphQL.Resolvers.ActivityTest do preferredUsername domain name - activity(type: $type, page: $page, limit: $limit) { + activity(type: $type, author: $author, page: $page, limit: $limit) { total elements { id @@ -210,6 +211,92 @@ defmodule Mobilizon.GraphQL.Resolvers.ActivityTest do event.uuid end + test "group_activity/3 list group activities filtered by type", %{ + conn: conn, + group: %Actor{preferred_username: preferred_username, id: group_id} = group + } do + user = insert(:user) + actor = insert(:actor, user: user) + + insert(:member, + parent: group, + actor: actor, + role: :member, + member_since: DateTime.truncate(DateTime.utc_now(), :second) + ) + + event = insert(:event, attributed_to: group, organizer_actor: actor) + post = insert(:post, author: actor, attributed_to: group) + EventActivity.insert_activity(event, subject: "event_created") + PostActivity.insert_activity(post, subject: "post_created") + assert %{success: 2, failure: 0} == Oban.drain_queue(queue: :activity) + assert Activities.list_activities() |> length() == 2 + + res = + conn + |> auth_conn(user) + |> AbsintheHelpers.graphql_query( + query: @group_activities_query, + variables: %{preferredUsername: preferred_username, type: "POST"} + ) + + assert res["errors"] == nil + assert res["data"]["group"]["id"] == to_string(group_id) + assert res["data"]["group"]["activity"]["total"] == 1 + activity = hd(res["data"]["group"]["activity"]["elements"]) + assert activity["object"]["id"] == to_string(post.id) + assert activity["subject"] == "post_created" + + assert Enum.find(activity["subjectParams"], &(&1["key"] == "post_title"))["value"] == + post.title + + assert Enum.find(activity["subjectParams"], &(&1["key"] == "post_slug"))["value"] == + post.slug + end + + test "group_activity/3 list group activities filtered by author", %{ + conn: conn, + group: %Actor{preferred_username: preferred_username, id: group_id} = group + } do + user = insert(:user) + actor = insert(:actor, user: user) + + insert(:member, + parent: group, + actor: actor, + role: :member, + member_since: DateTime.truncate(DateTime.utc_now(), :second) + ) + + event = insert(:event, attributed_to: group, organizer_actor: actor) + post = insert(:post, attributed_to: group) + EventActivity.insert_activity(event, subject: "event_created") + PostActivity.insert_activity(post, subject: "post_created") + assert %{success: 2, failure: 0} == Oban.drain_queue(queue: :activity) + assert Activities.list_activities() |> length() == 2 + + res = + conn + |> auth_conn(user) + |> AbsintheHelpers.graphql_query( + query: @group_activities_query, + variables: %{preferredUsername: preferred_username, author: "BY"} + ) + + assert res["errors"] == nil + assert res["data"]["group"]["id"] == to_string(group_id) + assert res["data"]["group"]["activity"]["total"] == 1 + activity = hd(res["data"]["group"]["activity"]["elements"]) + assert activity["object"]["id"] == to_string(post.id) + assert activity["subject"] == "post_created" + + assert Enum.find(activity["subjectParams"], &(&1["key"] == "post_title"))["value"] == + post.title + + assert Enum.find(activity["subjectParams"], &(&1["key"] == "post_slug"))["value"] == + post.slug + end + test "group_activity/3 list group activities from deleted object", %{ conn: conn, group: %Actor{preferred_username: preferred_username, id: group_id} = group