Improve member management
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
3be91d7e6c
commit
8c9546ff2a
@ -14,6 +14,7 @@ import { ACCEPT_INVITATION, REJECT_INVITATION } from "@/graphql/member";
|
|||||||
import { IMember } from "@/types/actor";
|
import { IMember } from "@/types/actor";
|
||||||
import { Component, Prop, Vue } from "vue-property-decorator";
|
import { Component, Prop, Vue } from "vue-property-decorator";
|
||||||
import InvitationCard from "@/components/Group/InvitationCard.vue";
|
import InvitationCard from "@/components/Group/InvitationCard.vue";
|
||||||
|
import { LOGGED_USER_MEMBERSHIPS } from "@/graphql/actor";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
@ -30,6 +31,7 @@ export default class Invitations extends Vue {
|
|||||||
variables: {
|
variables: {
|
||||||
id,
|
id,
|
||||||
},
|
},
|
||||||
|
refetchQueries: [{ query: LOGGED_USER_MEMBERSHIPS }],
|
||||||
});
|
});
|
||||||
if (data) {
|
if (data) {
|
||||||
this.$emit("accept-invitation", data.acceptInvitation);
|
this.$emit("accept-invitation", data.acceptInvitation);
|
||||||
@ -49,6 +51,7 @@ export default class Invitations extends Vue {
|
|||||||
variables: {
|
variables: {
|
||||||
id,
|
id,
|
||||||
},
|
},
|
||||||
|
refetchQueries: [{ query: LOGGED_USER_MEMBERSHIPS }],
|
||||||
});
|
});
|
||||||
if (data) {
|
if (data) {
|
||||||
this.$emit("reject-invitation", data.rejectInvitation);
|
this.$emit("reject-invitation", data.rejectInvitation);
|
||||||
|
@ -125,6 +125,7 @@ export const GROUP_FIELDS_FRAGMENTS = gql`
|
|||||||
}
|
}
|
||||||
members {
|
members {
|
||||||
elements {
|
elements {
|
||||||
|
id
|
||||||
role
|
role
|
||||||
actor {
|
actor {
|
||||||
id
|
id
|
||||||
|
@ -803,5 +803,7 @@
|
|||||||
"Please read the {fullRules} published by {instance}'s administrators.": "Please read the {fullRules} published by {instance}'s administrators.",
|
"Please read the {fullRules} published by {instance}'s administrators.": "Please read the {fullRules} published by {instance}'s administrators.",
|
||||||
"Instances following you": "Instances following you",
|
"Instances following you": "Instances following you",
|
||||||
"Instances you follow": "Instances you follow",
|
"Instances you follow": "Instances you follow",
|
||||||
"Last group created": "Last group created"
|
"Last group created": "Last group created",
|
||||||
|
"{username} was invited to {group}": "{username} was invited to {group}",
|
||||||
|
"The member was removed from the group {group}": "The member was removed from the group {group}"
|
||||||
}
|
}
|
||||||
|
@ -853,5 +853,7 @@
|
|||||||
"Please read the {fullRules} published by {instance}'s administrators.": "Merci de lire les {fullRules} publiées par les administrateur·ices de {instance}.",
|
"Please read the {fullRules} published by {instance}'s administrators.": "Merci de lire les {fullRules} publiées par les administrateur·ices de {instance}.",
|
||||||
"Instances following you": "Instances vous suivant",
|
"Instances following you": "Instances vous suivant",
|
||||||
"Instances you follow": "Instances que vous suivez",
|
"Instances you follow": "Instances que vous suivez",
|
||||||
"Last group created": "Dernier groupe créé"
|
"Last group created": "Dernier groupe créé",
|
||||||
|
"{username} was invited to {group}": "{username} a été invité à {group}",
|
||||||
|
"The member was removed from the group {group}": "Le ou la membre a été supprimé·e du groupe {group}"
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ import { Component, Vue } from "vue-property-decorator";
|
|||||||
})
|
})
|
||||||
export default class GroupMixin extends Vue {
|
export default class GroupMixin extends Vue {
|
||||||
group: IGroup = new Group();
|
group: IGroup = new Group();
|
||||||
|
|
||||||
currentActor!: IActor;
|
currentActor!: IActor;
|
||||||
|
|
||||||
person!: IPerson;
|
person!: IPerson;
|
||||||
@ -51,7 +52,7 @@ export default class GroupMixin extends Vue {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleErrors(errors: any[]) {
|
handleErrors(errors: any[]): void {
|
||||||
if (
|
if (
|
||||||
errors.some((error) => error.status_code === 404) ||
|
errors.some((error) => error.status_code === 404) ||
|
||||||
errors.some(({ message }) => message.includes("has invalid value $uuid"))
|
errors.some(({ message }) => message.includes("has invalid value $uuid"))
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
v-if="isCurrentActorAnInvitedGroupMember"
|
v-if="isCurrentActorAnInvitedGroupMember"
|
||||||
:invitations="[groupMember]"
|
:invitations="[groupMember]"
|
||||||
@acceptInvitation="acceptInvitation"
|
@acceptInvitation="acceptInvitation"
|
||||||
|
@reject-invitation="rejectInvitation"
|
||||||
/>
|
/>
|
||||||
<b-message v-if="isCurrentActorARejectedGroupMember" type="is-danger">
|
<b-message v-if="isCurrentActorARejectedGroupMember" type="is-danger">
|
||||||
{{ $t("You have been removed from this group's members.") }}
|
{{ $t("You have been removed from this group's members.") }}
|
||||||
@ -431,6 +432,16 @@ export default class Group extends mixins(GroupMixin) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rejectInvitation({ id: memberId }: { id: string }): void {
|
||||||
|
const index = this.person.memberships.elements.findIndex(
|
||||||
|
(membership) => membership.role === MemberRole.INVITED && membership.id === memberId
|
||||||
|
);
|
||||||
|
if (index > -1) {
|
||||||
|
this.person.memberships.elements.splice(index, 1);
|
||||||
|
this.person.memberships.total -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async reportGroup(content: string, forward: boolean): Promise<void> {
|
async reportGroup(content: string, forward: boolean): Promise<void> {
|
||||||
this.isReportModalActive = false;
|
this.isReportModalActive = false;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
@ -611,6 +622,8 @@ div.container {
|
|||||||
div.address {
|
div.address {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
justify-content: flex-end;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
.map-show-button {
|
.map-show-button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -181,12 +181,28 @@
|
|||||||
import { Component, Watch } from "vue-property-decorator";
|
import { Component, Watch } from "vue-property-decorator";
|
||||||
import GroupMixin from "@/mixins/group";
|
import GroupMixin from "@/mixins/group";
|
||||||
import { mixins } from "vue-class-component";
|
import { mixins } from "vue-class-component";
|
||||||
|
import { FETCH_GROUP } from "@/graphql/group";
|
||||||
import RouteName from "../../router/name";
|
import RouteName from "../../router/name";
|
||||||
import { INVITE_MEMBER, GROUP_MEMBERS, REMOVE_MEMBER, UPDATE_MEMBER } from "../../graphql/member";
|
import { INVITE_MEMBER, GROUP_MEMBERS, REMOVE_MEMBER, UPDATE_MEMBER } from "../../graphql/member";
|
||||||
import { IGroup, usernameWithDomain } from "../../types/actor";
|
import { IGroup, usernameWithDomain } from "../../types/actor";
|
||||||
import { IMember, MemberRole } from "../../types/actor/group.model";
|
import { IMember, MemberRole } from "../../types/actor/group.model";
|
||||||
|
|
||||||
@Component
|
@Component({
|
||||||
|
apollo: {
|
||||||
|
members: {
|
||||||
|
query: GROUP_MEMBERS,
|
||||||
|
variables() {
|
||||||
|
return {
|
||||||
|
name: this.$route.params.preferredUsername,
|
||||||
|
page: 1,
|
||||||
|
limit: this.MEMBERS_PER_PAGE,
|
||||||
|
roles: this.roles,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
update: (data) => data.group.members,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
export default class GroupMembers extends mixins(GroupMixin) {
|
export default class GroupMembers extends mixins(GroupMixin) {
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
@ -221,31 +237,16 @@ export default class GroupMembers extends mixins(GroupMixin) {
|
|||||||
groupId: this.group.id,
|
groupId: this.group.id,
|
||||||
targetActorUsername: this.newMemberUsername,
|
targetActorUsername: this.newMemberUsername,
|
||||||
},
|
},
|
||||||
update: (store, { data }) => {
|
refetchQueries: [
|
||||||
if (data == null) return;
|
{ query: FETCH_GROUP, variables: { name: this.$route.params.preferredUsername } },
|
||||||
const query = {
|
],
|
||||||
query: GROUP_MEMBERS,
|
|
||||||
variables: {
|
|
||||||
name: this.$route.params.preferredUsername,
|
|
||||||
page: 1,
|
|
||||||
limit: this.MEMBERS_PER_PAGE,
|
|
||||||
roles: this.roles,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const memberData: IMember = data.inviteMember;
|
|
||||||
const groupData = store.readQuery<{ group: IGroup }>(query);
|
|
||||||
if (!groupData) return;
|
|
||||||
const { group } = groupData;
|
|
||||||
const index = group.members.elements.findIndex((m) => m.actor.id === memberData.actor.id);
|
|
||||||
if (index === -1) {
|
|
||||||
group.members.elements.push(memberData);
|
|
||||||
group.members.total += 1;
|
|
||||||
} else {
|
|
||||||
group.members.elements.splice(index, 1, memberData);
|
|
||||||
}
|
|
||||||
store.writeQuery({ ...query, data: { group } });
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
this.$notifier.success(
|
||||||
|
this.$t("{username} was invited to {group}", {
|
||||||
|
username: this.newMemberUsername,
|
||||||
|
group: this.group.name || usernameWithDomain(this.group),
|
||||||
|
}) as string
|
||||||
|
);
|
||||||
this.newMemberUsername = "";
|
this.newMemberUsername = "";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -283,34 +284,30 @@ export default class GroupMembers extends mixins(GroupMixin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeMember(memberId: string): Promise<void> {
|
async removeMember(memberId: string): Promise<void> {
|
||||||
await this.$apollo.mutate<{ removeMember: IMember }>({
|
console.log("removeMember", memberId);
|
||||||
mutation: REMOVE_MEMBER,
|
try {
|
||||||
variables: {
|
await this.$apollo.mutate<{ removeMember: IMember }>({
|
||||||
groupId: this.group.id,
|
mutation: REMOVE_MEMBER,
|
||||||
memberId,
|
variables: {
|
||||||
},
|
groupId: this.group.id,
|
||||||
update: (store, { data }) => {
|
memberId,
|
||||||
if (data == null) return;
|
},
|
||||||
const query = {
|
refetchQueries: [
|
||||||
query: GROUP_MEMBERS,
|
{ query: FETCH_GROUP, variables: { name: this.$route.params.preferredUsername } },
|
||||||
variables: {
|
],
|
||||||
name: this.$route.params.preferredUsername,
|
});
|
||||||
page: 1,
|
this.$notifier.success(
|
||||||
limit: this.MEMBERS_PER_PAGE,
|
this.$t("The member was removed from the group {group}", {
|
||||||
roles: this.roles,
|
username: this.newMemberUsername,
|
||||||
},
|
group: this.group.name || usernameWithDomain(this.group),
|
||||||
};
|
}) as string
|
||||||
const groupData = store.readQuery<{ group: IGroup }>(query);
|
);
|
||||||
if (!groupData) return;
|
} catch (error) {
|
||||||
const { group } = groupData;
|
console.error(error);
|
||||||
const index = group.members.elements.findIndex((m) => m.id === memberId);
|
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
|
||||||
if (index !== -1) {
|
this.$notifier.error(error.graphQLErrors[0].message);
|
||||||
group.members.elements.splice(index, 1);
|
}
|
||||||
group.members.total -= 1;
|
}
|
||||||
store.writeQuery({ ...query, data: { group } });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
promoteMember(member: IMember): void {
|
promoteMember(member: IMember): void {
|
||||||
@ -341,7 +338,23 @@ export default class GroupMembers extends mixins(GroupMixin) {
|
|||||||
memberId,
|
memberId,
|
||||||
role,
|
role,
|
||||||
},
|
},
|
||||||
|
refetchQueries: [
|
||||||
|
{ query: FETCH_GROUP, variables: { name: this.$route.params.preferredUsername } },
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
let successMessage;
|
||||||
|
switch (role) {
|
||||||
|
case MemberRole.MODERATOR:
|
||||||
|
successMessage = "The member role was updated to moderator";
|
||||||
|
break;
|
||||||
|
case MemberRole.ADMINISTRATOR:
|
||||||
|
successMessage = "The member role was updated to administrator";
|
||||||
|
break;
|
||||||
|
case MemberRole.MEMBER:
|
||||||
|
default:
|
||||||
|
successMessage = "The member role was updated to simple member";
|
||||||
|
}
|
||||||
|
this.$notifier.success(this.$t(successMessage) as string);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
|
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
|
||||||
|
@ -71,7 +71,7 @@ import RouteName from "../../router/name";
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class MyEvents extends Vue {
|
export default class MyGroups extends Vue {
|
||||||
membershipsPages!: Paginate<IMember>;
|
membershipsPages!: Paginate<IMember>;
|
||||||
|
|
||||||
RouteName = RouteName;
|
RouteName = RouteName;
|
||||||
|
@ -66,10 +66,13 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do
|
|||||||
{:has_rights_to_invite, {:ok, %Member{role: role}}}
|
{:has_rights_to_invite, {:ok, %Member{role: role}}}
|
||||||
when role in [:moderator, :administrator, :creator] <-
|
when role in [:moderator, :administrator, :creator] <-
|
||||||
{:has_rights_to_invite, Actors.get_member(actor_id, group_id)},
|
{:has_rights_to_invite, Actors.get_member(actor_id, group_id)},
|
||||||
|
target_actor_username <-
|
||||||
|
target_actor_username |> String.trim() |> String.trim_leading("@"),
|
||||||
{:target_actor_username, {:ok, %Actor{id: target_actor_id} = target_actor}} <-
|
{:target_actor_username, {:ok, %Actor{id: target_actor_id} = target_actor}} <-
|
||||||
{:target_actor_username,
|
{:target_actor_username,
|
||||||
ActivityPub.find_or_make_actor_from_nickname(target_actor_username)},
|
ActivityPub.find_or_make_actor_from_nickname(target_actor_username)},
|
||||||
true <- check_member_not_existant_or_rejected(target_actor_id, group.id),
|
{:existant, true} <-
|
||||||
|
{:existant, check_member_not_existant_or_rejected(target_actor_id, group.id)},
|
||||||
{:ok, _activity, %Member{} = member} <- ActivityPub.invite(group, actor, target_actor) do
|
{:ok, _activity, %Member{} = member} <- ActivityPub.invite(group, actor, target_actor) do
|
||||||
{:ok, member}
|
{:ok, member}
|
||||||
else
|
else
|
||||||
@ -88,6 +91,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do
|
|||||||
{:has_rights_to_invite, _} ->
|
{:has_rights_to_invite, _} ->
|
||||||
{:error, dgettext("errors", "You cannot invite to this group")}
|
{:error, dgettext("errors", "You cannot invite to this group")}
|
||||||
|
|
||||||
|
{:existant, _} ->
|
||||||
|
{:error, dgettext("errors", "Profile is already a member of this group")}
|
||||||
|
|
||||||
|
# Remove me ?
|
||||||
{:ok, %Member{}} ->
|
{:ok, %Member{}} ->
|
||||||
{:error, dgettext("errors", "Profile is already a member of this group")}
|
{:error, dgettext("errors", "Profile is already a member of this group")}
|
||||||
end
|
end
|
||||||
@ -115,7 +122,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do
|
|||||||
|
|
||||||
def reject_invitation(_parent, %{id: member_id}, %{context: %{current_user: %User{} = user}}) do
|
def reject_invitation(_parent, %{id: member_id}, %{context: %{current_user: %User{} = user}}) do
|
||||||
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
|
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
|
||||||
%Member{actor: %Actor{id: member_actor_id}} = member <- Actors.get_member(member_id),
|
{:invitation_exists, %Member{actor: %Actor{id: member_actor_id}} = member} <-
|
||||||
|
{:invitation_exists, Actors.get_member(member_id)},
|
||||||
{:is_same_actor, true} <- {:is_same_actor, member_actor_id === actor_id},
|
{:is_same_actor, true} <- {:is_same_actor, member_actor_id === actor_id},
|
||||||
{:ok, _activity, %Member{} = member} <-
|
{:ok, _activity, %Member{} = member} <-
|
||||||
ActivityPub.reject(
|
ActivityPub.reject(
|
||||||
@ -127,6 +135,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do
|
|||||||
else
|
else
|
||||||
{:is_same_actor, false} ->
|
{:is_same_actor, false} ->
|
||||||
{:error, dgettext("errors", "You can't reject this invitation with this profile.")}
|
{:error, dgettext("errors", "You can't reject this invitation with this profile.")}
|
||||||
|
|
||||||
|
{:invitation_exists, _} ->
|
||||||
|
{:error, dgettext("errors", "This invitation doesn't exist.")}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -158,13 +169,27 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do
|
|||||||
context: %{current_user: %User{} = user}
|
context: %{current_user: %User{} = user}
|
||||||
}) do
|
}) do
|
||||||
with %Actor{id: moderator_id} = moderator <- Users.get_actor_for_user(user),
|
with %Actor{id: moderator_id} = moderator <- Users.get_actor_for_user(user),
|
||||||
%Member{} = member <- Actors.get_member(member_id),
|
%Member{role: role} = member when role != :rejected <- Actors.get_member(member_id),
|
||||||
%Actor{type: :Group} = group <- Actors.get_actor(group_id),
|
%Actor{type: :Group} = group <- Actors.get_actor(group_id),
|
||||||
{:has_rights_to_invite, {:ok, %Member{role: role}}}
|
{:has_rights_to_remove, {:ok, %Member{role: role}}}
|
||||||
when role in [:moderator, :administrator, :creator] <-
|
when role in [:moderator, :administrator, :creator] <-
|
||||||
{:has_rights_to_invite, Actors.get_member(moderator_id, group_id)},
|
{:has_rights_to_remove, Actors.get_member(moderator_id, group_id)},
|
||||||
{:ok, _activity, %Member{}} <- ActivityPub.remove(member, group, moderator, true) do
|
{:ok, _activity, %Member{}} <- ActivityPub.remove(member, group, moderator, true) do
|
||||||
{:ok, member}
|
{:ok, member}
|
||||||
|
else
|
||||||
|
%Member{role: :rejected} ->
|
||||||
|
{:error,
|
||||||
|
dgettext(
|
||||||
|
"errors",
|
||||||
|
"This member already has been rejected."
|
||||||
|
)}
|
||||||
|
|
||||||
|
{:has_rights_to_remove, _} ->
|
||||||
|
{:error,
|
||||||
|
dgettext(
|
||||||
|
"errors",
|
||||||
|
"You don't have the right to remove this member."
|
||||||
|
)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -479,7 +479,7 @@ msgstr "Le profil invité n'existe pas"
|
|||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/graphql/resolvers/member.ex:92
|
#: lib/graphql/resolvers/member.ex:92
|
||||||
msgid "Profile is already a member of this group"
|
msgid "Profile is already a member of this group"
|
||||||
msgstr "Vous êtes déjà membre de ce groupe"
|
msgstr "Ce profil est déjà membre de ce groupe"
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/graphql/resolvers/post.ex:131 lib/graphql/resolvers/post.ex:171
|
#: lib/graphql/resolvers/post.ex:131 lib/graphql/resolvers/post.ex:171
|
||||||
@ -549,12 +549,12 @@ msgstr "Membre non trouvé"
|
|||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/graphql/resolvers/person.ex:235
|
#: lib/graphql/resolvers/person.ex:235
|
||||||
msgid "You already have a profile for this user"
|
msgid "You already have a profile for this user"
|
||||||
msgstr "Vous êtes déjà membre de ce groupe"
|
msgstr "Vous avez déjà un profil pour cet utilisateur"
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/graphql/resolvers/participant.ex:134
|
#: lib/graphql/resolvers/participant.ex:134
|
||||||
msgid "You are already a participant of this event"
|
msgid "You are already a participant of this event"
|
||||||
msgstr "Vous êtes déjà membre de ce groupe"
|
msgstr "Vous êtes déjà un·e participant·e à cet événement"
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/graphql/resolvers/discussion.ex:185
|
#: lib/graphql/resolvers/discussion.ex:185
|
||||||
@ -564,7 +564,7 @@ msgstr "Vous n'êtes pas un membre du groupe dans lequel se fait la discussion"
|
|||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/graphql/resolvers/member.ex:86
|
#: lib/graphql/resolvers/member.ex:86
|
||||||
msgid "You are not a member of this group"
|
msgid "You are not a member of this group"
|
||||||
msgstr "Vous êtes déjà membre de ce groupe"
|
msgstr "Vous n'êtes pas membre de ce groupe"
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/graphql/resolvers/member.ex:143
|
#: lib/graphql/resolvers/member.ex:143
|
||||||
|
Loading…
Reference in New Issue
Block a user