Get membership status only for the current group
Closes #575 Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
74923c91d8
commit
03824b898c
|
@ -446,6 +446,70 @@ export const PERSON_MEMBERSHIPS_WITH_MEMBERS = gql`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const PERSON_MEMBERSHIP_GROUP = gql`
|
||||||
|
query PersonMembershipGroup($id: ID!, $group: String!) {
|
||||||
|
person(id: $id) {
|
||||||
|
id
|
||||||
|
memberships(group: $group) {
|
||||||
|
total
|
||||||
|
elements {
|
||||||
|
id
|
||||||
|
role
|
||||||
|
parent {
|
||||||
|
id
|
||||||
|
preferredUsername
|
||||||
|
name
|
||||||
|
domain
|
||||||
|
avatar {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
invitedBy {
|
||||||
|
id
|
||||||
|
preferredUsername
|
||||||
|
name
|
||||||
|
}
|
||||||
|
insertedAt
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GROUP_MEMBERSHIP_SUBSCRIPTION_CHANGED = gql`
|
||||||
|
subscription($actorId: ID!, $group: String!) {
|
||||||
|
groupMembershipChanged(personId: $actorId, group: $group) {
|
||||||
|
id
|
||||||
|
memberships {
|
||||||
|
total
|
||||||
|
elements {
|
||||||
|
id
|
||||||
|
role
|
||||||
|
parent {
|
||||||
|
id
|
||||||
|
preferredUsername
|
||||||
|
name
|
||||||
|
domain
|
||||||
|
avatar {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
invitedBy {
|
||||||
|
id
|
||||||
|
preferredUsername
|
||||||
|
name
|
||||||
|
}
|
||||||
|
insertedAt
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const CREATE_PERSON = gql`
|
export const CREATE_PERSON = gql`
|
||||||
mutation CreatePerson(
|
mutation CreatePerson(
|
||||||
$preferredUsername: String!
|
$preferredUsername: String!
|
||||||
|
|
|
@ -592,38 +592,6 @@ export const EVENT_PERSON_PARTICIPATION_SUBSCRIPTION_CHANGED = gql`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GROUP_MEMBERSHIP_SUBSCRIPTION_CHANGED = gql`
|
|
||||||
subscription($actorId: ID!) {
|
|
||||||
groupMembershipChanged(personId: $actorId) {
|
|
||||||
id
|
|
||||||
memberships {
|
|
||||||
total
|
|
||||||
elements {
|
|
||||||
id
|
|
||||||
role
|
|
||||||
parent {
|
|
||||||
id
|
|
||||||
preferredUsername
|
|
||||||
name
|
|
||||||
domain
|
|
||||||
avatar {
|
|
||||||
id
|
|
||||||
url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invitedBy {
|
|
||||||
id
|
|
||||||
preferredUsername
|
|
||||||
name
|
|
||||||
}
|
|
||||||
insertedAt
|
|
||||||
updatedAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const FETCH_GROUP_EVENTS = gql`
|
export const FETCH_GROUP_EVENTS = gql`
|
||||||
query(
|
query(
|
||||||
$name: String!
|
$name: String!
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import { PERSON_MEMBERSHIPS, CURRENT_ACTOR_CLIENT } from "@/graphql/actor";
|
import {
|
||||||
import { GROUP_MEMBERSHIP_SUBSCRIPTION_CHANGED } from "@/graphql/event";
|
CURRENT_ACTOR_CLIENT,
|
||||||
|
GROUP_MEMBERSHIP_SUBSCRIPTION_CHANGED,
|
||||||
|
PERSON_MEMBERSHIP_GROUP,
|
||||||
|
} from "@/graphql/actor";
|
||||||
import { FETCH_GROUP } from "@/graphql/group";
|
import { FETCH_GROUP } from "@/graphql/group";
|
||||||
import RouteName from "@/router/name";
|
import RouteName from "@/router/name";
|
||||||
import { Group, IActor, IGroup, IPerson } from "@/types/actor";
|
import { Group, IActor, IGroup, IPerson } from "@/types/actor";
|
||||||
|
@ -26,11 +29,12 @@ import { Component, Vue } from "vue-property-decorator";
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
person: {
|
person: {
|
||||||
query: PERSON_MEMBERSHIPS,
|
query: PERSON_MEMBERSHIP_GROUP,
|
||||||
fetchPolicy: "cache-and-network",
|
fetchPolicy: "cache-and-network",
|
||||||
variables() {
|
variables() {
|
||||||
return {
|
return {
|
||||||
id: this.currentActor.id,
|
id: this.currentActor.id,
|
||||||
|
group: this.$route.params.preferredUsername,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
subscribeToMore: {
|
subscribeToMore: {
|
||||||
|
@ -38,14 +42,23 @@ import { Component, Vue } from "vue-property-decorator";
|
||||||
variables() {
|
variables() {
|
||||||
return {
|
return {
|
||||||
actorId: this.currentActor.id,
|
actorId: this.currentActor.id,
|
||||||
|
group: this.$route.params.preferredUsername,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
skip() {
|
skip() {
|
||||||
return !this.currentActor || !this.currentActor.id;
|
return (
|
||||||
|
!this.currentActor ||
|
||||||
|
!this.currentActor.id ||
|
||||||
|
!this.$route.params.preferredUsername
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
skip() {
|
skip() {
|
||||||
return !this.currentActor || !this.currentActor.id;
|
return (
|
||||||
|
!this.currentActor ||
|
||||||
|
!this.currentActor.id ||
|
||||||
|
!this.$route.params.preferredUsername
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentActor: CURRENT_ACTOR_CLIENT,
|
currentActor: CURRENT_ACTOR_CLIENT,
|
||||||
|
@ -71,13 +84,7 @@ export default class GroupMixin extends Vue {
|
||||||
|
|
||||||
hasCurrentActorThisRole(givenRole: string | string[]): boolean {
|
hasCurrentActorThisRole(givenRole: string | string[]): boolean {
|
||||||
const roles = Array.isArray(givenRole) ? givenRole : [givenRole];
|
const roles = Array.isArray(givenRole) ? givenRole : [givenRole];
|
||||||
return (
|
return roles.includes(this.person?.memberships?.elements[0].role);
|
||||||
this.person &&
|
|
||||||
this.person.memberships.elements.some(
|
|
||||||
({ parent: { id }, role }) =>
|
|
||||||
id === this.group.id && roles.includes(role)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleErrors(errors: any[]): void {
|
handleErrors(errors: any[]): void {
|
||||||
|
|
|
@ -75,8 +75,11 @@ import { IActor, IGroup, IPerson, usernameWithDomain } from "@/types/actor";
|
||||||
import DiscussionListItem from "@/components/Discussion/DiscussionListItem.vue";
|
import DiscussionListItem from "@/components/Discussion/DiscussionListItem.vue";
|
||||||
import RouteName from "../../router/name";
|
import RouteName from "../../router/name";
|
||||||
import { MemberRole } from "@/types/enums";
|
import { MemberRole } from "@/types/enums";
|
||||||
import { CURRENT_ACTOR_CLIENT, PERSON_MEMBERSHIPS } from "@/graphql/actor";
|
import {
|
||||||
import { GROUP_MEMBERSHIP_SUBSCRIPTION_CHANGED } from "@/graphql/event";
|
CURRENT_ACTOR_CLIENT,
|
||||||
|
GROUP_MEMBERSHIP_SUBSCRIPTION_CHANGED,
|
||||||
|
PERSON_MEMBERSHIP_GROUP,
|
||||||
|
} from "@/graphql/actor";
|
||||||
import { IMember } from "@/types/actor/member.model";
|
import { IMember } from "@/types/actor/member.model";
|
||||||
import EmptyContent from "@/components/Utils/EmptyContent.vue";
|
import EmptyContent from "@/components/Utils/EmptyContent.vue";
|
||||||
|
|
||||||
|
@ -96,11 +99,12 @@ import EmptyContent from "@/components/Utils/EmptyContent.vue";
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
person: {
|
person: {
|
||||||
query: PERSON_MEMBERSHIPS,
|
query: PERSON_MEMBERSHIP_GROUP,
|
||||||
fetchPolicy: "cache-and-network",
|
fetchPolicy: "cache-and-network",
|
||||||
variables() {
|
variables() {
|
||||||
return {
|
return {
|
||||||
id: this.currentActor.id,
|
id: this.currentActor.id,
|
||||||
|
group: this.preferredUsername,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
subscribeToMore: {
|
subscribeToMore: {
|
||||||
|
@ -108,14 +112,21 @@ import EmptyContent from "@/components/Utils/EmptyContent.vue";
|
||||||
variables() {
|
variables() {
|
||||||
return {
|
return {
|
||||||
actorId: this.currentActor.id,
|
actorId: this.currentActor.id,
|
||||||
|
group: this.preferredUsername,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
skip() {
|
skip() {
|
||||||
return !this.currentActor || !this.currentActor.id;
|
return (
|
||||||
|
!this.currentActor ||
|
||||||
|
!this.currentActor.id ||
|
||||||
|
!this.preferredUsername
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
skip() {
|
skip() {
|
||||||
return !this.currentActor || !this.currentActor.id;
|
return (
|
||||||
|
!this.currentActor || !this.currentActor.id || !this.preferredUsername
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentActor: CURRENT_ACTOR_CLIENT,
|
currentActor: CURRENT_ACTOR_CLIENT,
|
||||||
|
|
|
@ -628,15 +628,14 @@ export default class Group extends mixins(GroupMixin) {
|
||||||
}
|
}
|
||||||
|
|
||||||
get groupMember(): IMember | undefined {
|
get groupMember(): IMember | undefined {
|
||||||
if (!this.person || !this.person.id) return undefined;
|
if (this.person?.memberships?.total > 0) {
|
||||||
return this.person.memberships.elements.find(
|
return this.person?.memberships?.elements[0];
|
||||||
({ parent: { id } }) => id === this.group.id
|
}
|
||||||
);
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get groupMemberships(): (string | undefined)[] {
|
get groupMemberships(): (string | undefined)[] {
|
||||||
if (!this.person || !this.person.id) return [];
|
return this.person?.memberships?.elements
|
||||||
return this.person.memberships.elements
|
|
||||||
.filter(
|
.filter(
|
||||||
(membership: IMember) =>
|
(membership: IMember) =>
|
||||||
![
|
![
|
||||||
|
|
|
@ -33,14 +33,13 @@
|
||||||
</nav>
|
</nav>
|
||||||
<section
|
<section
|
||||||
class="container section"
|
class="container section"
|
||||||
v-if="group && isCurrentActorAGroupAdmin"
|
v-if="group && isCurrentActorAGroupAdmin && followers"
|
||||||
>
|
>
|
||||||
<h1>{{ $t("Group Followers") }} ({{ followers.total }})</h1>
|
<h1>{{ $t("Group Followers") }} ({{ followers.total }})</h1>
|
||||||
<b-field :label="$t('Status')" horizontal>
|
<b-field :label="$t('Status')" horizontal>
|
||||||
<b-switch v-model="pending">{{ $t("Pending") }}</b-switch>
|
<b-switch v-model="pending">{{ $t("Pending") }}</b-switch>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-table
|
<b-table
|
||||||
v-if="followers"
|
|
||||||
:data="followers.elements"
|
:data="followers.elements"
|
||||||
ref="queueTable"
|
ref="queueTable"
|
||||||
:loading="this.$apollo.loading"
|
:loading="this.$apollo.loading"
|
||||||
|
|
|
@ -934,7 +934,10 @@ defmodule Mobilizon.Federation.ActivityPub do
|
||||||
do: Refresher.fetch_group(member.parent.url, member.actor)
|
do: Refresher.fetch_group(member.parent.url, member.actor)
|
||||||
),
|
),
|
||||||
Absinthe.Subscription.publish(Endpoint, member.actor,
|
Absinthe.Subscription.publish(Endpoint, member.actor,
|
||||||
group_membership_changed: member.actor.id
|
group_membership_changed: [
|
||||||
|
Actor.preferred_username_and_domain(member.parent),
|
||||||
|
member.actor.id
|
||||||
|
]
|
||||||
),
|
),
|
||||||
member_as_data <- Convertible.model_to_as(member),
|
member_as_data <- Convertible.model_to_as(member),
|
||||||
audience <-
|
audience <-
|
||||||
|
|
|
@ -126,7 +126,9 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Actors do
|
||||||
}),
|
}),
|
||||||
{:ok, _} <-
|
{:ok, _} <-
|
||||||
Mobilizon.Service.Activity.Member.insert_activity(member, subject: "member_joined"),
|
Mobilizon.Service.Activity.Member.insert_activity(member, subject: "member_joined"),
|
||||||
Absinthe.Subscription.publish(Endpoint, actor, group_membership_changed: actor.id),
|
Absinthe.Subscription.publish(Endpoint, actor,
|
||||||
|
group_membership_changed: [Actor.preferred_username_and_domain(group), actor.id]
|
||||||
|
),
|
||||||
join_data <- %{
|
join_data <- %{
|
||||||
"type" => "Join",
|
"type" => "Join",
|
||||||
"id" => member.url,
|
"id" => member.url,
|
||||||
|
|
|
@ -5,11 +5,17 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Members do
|
||||||
alias Mobilizon.Federation.ActivityPub
|
alias Mobilizon.Federation.ActivityPub
|
||||||
alias Mobilizon.Federation.ActivityStream.Convertible
|
alias Mobilizon.Federation.ActivityStream.Convertible
|
||||||
alias Mobilizon.Service.Activity.Member, as: MemberActivity
|
alias Mobilizon.Service.Activity.Member, as: MemberActivity
|
||||||
|
alias Mobilizon.Web.Endpoint
|
||||||
require Logger
|
require Logger
|
||||||
import Mobilizon.Federation.ActivityPub.Utils, only: [make_update_data: 2]
|
import Mobilizon.Federation.ActivityPub.Utils, only: [make_update_data: 2]
|
||||||
|
|
||||||
def update(
|
def update(
|
||||||
%Member{parent: %Actor{id: group_id}, id: member_id, role: current_role} = old_member,
|
%Member{
|
||||||
|
parent: %Actor{id: group_id} = group,
|
||||||
|
id: member_id,
|
||||||
|
role: current_role,
|
||||||
|
actor: %Actor{id: actor_id} = actor
|
||||||
|
} = old_member,
|
||||||
%{role: updated_role} = args,
|
%{role: updated_role} = args,
|
||||||
%{moderator: %Actor{url: moderator_url, id: moderator_id} = moderator} = additional
|
%{moderator: %Actor{url: moderator_url, id: moderator_id} = moderator} = additional
|
||||||
) do
|
) do
|
||||||
|
@ -27,6 +33,9 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Members do
|
||||||
moderator: moderator,
|
moderator: moderator,
|
||||||
subject: "member_updated"
|
subject: "member_updated"
|
||||||
),
|
),
|
||||||
|
Absinthe.Subscription.publish(Endpoint, actor,
|
||||||
|
group_membership_changed: [Actor.preferred_username_and_domain(group), actor_id]
|
||||||
|
),
|
||||||
{:ok, true} <- Cachex.del(:activity_pub, "member_#{member_id}"),
|
{:ok, true} <- Cachex.del(:activity_pub, "member_#{member_id}"),
|
||||||
member_as_data <-
|
member_as_data <-
|
||||||
Convertible.model_to_as(member),
|
Convertible.model_to_as(member),
|
||||||
|
|
|
@ -6,9 +6,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do
|
||||||
import Mobilizon.Users.Guards
|
import Mobilizon.Users.Guards
|
||||||
|
|
||||||
alias Mobilizon.{Actors, Events, Users}
|
alias Mobilizon.{Actors, Events, Users}
|
||||||
alias Mobilizon.Actors.Actor
|
alias Mobilizon.Actors.{Actor, Member}
|
||||||
alias Mobilizon.Events.Participant
|
alias Mobilizon.Events.Participant
|
||||||
alias Mobilizon.Storage.Page
|
alias Mobilizon.Storage.{Page, Repo}
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
import Mobilizon.Web.Gettext
|
import Mobilizon.Web.Gettext
|
||||||
|
|
||||||
|
@ -306,10 +306,33 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do
|
||||||
@doc """
|
@doc """
|
||||||
Returns the list of events this person is going to
|
Returns the list of events this person is going to
|
||||||
"""
|
"""
|
||||||
def person_memberships(%Actor{id: actor_id}, _args, %{context: %{current_user: user}}) do
|
@spec person_memberships(Actor.t(), map(), map()) :: {:ok, Page.t()} | {:error, String.t()}
|
||||||
|
def person_memberships(%Actor{id: actor_id}, %{group: group}, %{
|
||||||
|
context: %{current_user: user}
|
||||||
|
}) do
|
||||||
|
with {:is_owned, %Actor{id: actor_id}} <- User.owns_actor(user, actor_id),
|
||||||
|
%Actor{id: group_id} <- Actors.get_actor_by_name(group, :Group),
|
||||||
|
{:ok, %Member{} = membership} <- Actors.get_member(actor_id, group_id),
|
||||||
|
memberships <- %Page{
|
||||||
|
total: 1,
|
||||||
|
elements: [Repo.preload(membership, [:actor, :parent, :invited_by])]
|
||||||
|
} do
|
||||||
|
{:ok, memberships}
|
||||||
|
else
|
||||||
|
{:error, :member_not_found} ->
|
||||||
|
{:ok, %Page{total: 0, elements: []}}
|
||||||
|
|
||||||
|
{:is_owned, nil} ->
|
||||||
|
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def person_memberships(%Actor{id: actor_id}, %{page: page, limit: limit}, %{
|
||||||
|
context: %{current_user: user}
|
||||||
|
}) do
|
||||||
with {:is_owned, %Actor{} = actor} <- User.owns_actor(user, actor_id),
|
with {:is_owned, %Actor{} = actor} <- User.owns_actor(user, actor_id),
|
||||||
participations <- Actors.list_members_for_actor(actor) do
|
memberships <- Actors.list_members_for_actor(actor, page, limit) do
|
||||||
{:ok, participations}
|
{:ok, memberships}
|
||||||
else
|
else
|
||||||
{:is_owned, nil} ->
|
{:is_owned, nil} ->
|
||||||
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
||||||
|
|
|
@ -70,7 +70,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
field(:participations, :paginated_participant_list,
|
field(:participations, :paginated_participant_list,
|
||||||
description: "The list of events this person goes to"
|
description: "The list of events this person goes to"
|
||||||
) do
|
) do
|
||||||
arg(:event_id, :id)
|
arg(:event_id, :id, description: "Filter by event ID")
|
||||||
|
|
||||||
arg(:page, :integer,
|
arg(:page, :integer,
|
||||||
default_value: 1,
|
default_value: 1,
|
||||||
|
@ -86,6 +86,14 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
field(:memberships, :paginated_member_list,
|
field(:memberships, :paginated_member_list,
|
||||||
description: "The list of group this person is member of"
|
description: "The list of group this person is member of"
|
||||||
) do
|
) do
|
||||||
|
arg(:group, :string, description: "Filter by group federated username")
|
||||||
|
|
||||||
|
arg(:page, :integer,
|
||||||
|
default_value: 1,
|
||||||
|
description: "The page in the paginated memberships list"
|
||||||
|
)
|
||||||
|
|
||||||
|
arg(:limit, :integer, default_value: 10, description: "The limit of memberships per page")
|
||||||
resolve(&Person.person_memberships/3)
|
resolve(&Person.person_memberships/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -225,9 +233,10 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
@desc "Notify when a person's membership's status changed for a group"
|
@desc "Notify when a person's membership's status changed for a group"
|
||||||
field :group_membership_changed, :person do
|
field :group_membership_changed, :person do
|
||||||
arg(:person_id, non_null(:id), description: "The person's ID")
|
arg(:person_id, non_null(:id), description: "The person's ID")
|
||||||
|
arg(:group, non_null(:string), description: "The group's federated username")
|
||||||
|
|
||||||
config(fn args, _ ->
|
config(fn args, _ ->
|
||||||
{:ok, topic: args.person_id}
|
{:ok, topic: [args.group, args.person_id]}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue