Allow to create an event from a group preconfigured with the organizer

Refactored the organizer-picker components a lot

Close #464

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-03-29 10:33:19 +02:00
parent cde9f8873e
commit 13c8080097
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
8 changed files with 194 additions and 188 deletions

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="list is-hoverable"> <div class="list is-hoverable">
<b-radio-button <b-radio-button
v-model="currentActor" v-model="selectedActor"
:native-value="availableActor" :native-value="availableActor"
class="list-item" class="list-item"
v-for="availableActor in actualAvailableActors" v-for="availableActor in actualAvailableActors"
:class="{ 'is-active': availableActor.id === currentActor.id }" :class="{ 'is-active': availableActor.id === selectedActor.id }"
:key="availableActor.id" :key="availableActor.id"
> >
<div class="media"> <div class="media">
@ -31,9 +31,13 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue, Watch } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
import { IPerson, IActor, Actor } from "@/types/actor"; import { IPerson, IActor, Actor } from "@/types/actor";
import { PERSON_MEMBERSHIPS } from "@/graphql/actor"; import {
CURRENT_ACTOR_CLIENT,
IDENTITIES,
LOGGED_USER_MEMBERSHIPS,
} from "@/graphql/actor";
import { Paginate } from "@/types/paginate"; import { Paginate } from "@/types/paginate";
import { IMember } from "@/types/actor/member.model"; import { IMember } from "@/types/actor/member.model";
import { MemberRole } from "@/types/enums"; import { MemberRole } from "@/types/enums";
@ -41,29 +45,37 @@ import { MemberRole } from "@/types/enums";
@Component({ @Component({
apollo: { apollo: {
groupMemberships: { groupMemberships: {
query: PERSON_MEMBERSHIPS, query: LOGGED_USER_MEMBERSHIPS,
variables() { update: (data) => data.loggedUser.memberships,
return {
id: this.identity.id,
};
},
update: (data) => data.person.memberships,
skip() {
return !this.identity.id;
},
}, },
identities: IDENTITIES,
currentActor: CURRENT_ACTOR_CLIENT,
}, },
}) })
export default class OrganizerPicker extends Vue { export default class OrganizerPicker extends Vue {
@Prop() value!: IActor; @Prop() value!: IActor;
@Prop() identity!: IPerson;
@Prop({ required: false, default: false }) restrictModeratorLevel!: boolean; @Prop({ required: false, default: false }) restrictModeratorLevel!: boolean;
groupMemberships: Paginate<IMember> = { elements: [], total: 0 }; groupMemberships: Paginate<IMember> = { elements: [], total: 0 };
currentActor: IActor = this.value; currentActor!: IPerson;
get selectedActor(): IActor | undefined {
if (this.value?.id) {
return this.value;
}
if (this.currentActor) {
return this.currentActor;
}
return undefined;
}
set selectedActor(actor: IActor | undefined) {
this.$emit("input", actor);
}
identities: IActor[] = [];
Actor = Actor; Actor = Actor;
@ -82,14 +94,12 @@ export default class OrganizerPicker extends Vue {
get actualAvailableActors(): IActor[] { get actualAvailableActors(): IActor[] {
return [ return [
this.identity, this.currentActor,
...this.identities.filter(
(identity: IActor) => identity.id !== this.currentActor?.id
),
...this.actualMemberships.map((member) => member.parent), ...this.actualMemberships.map((member) => member.parent),
]; ].filter((elem) => elem);
}
@Watch("currentActor")
async fetchMembersForGroup(): Promise<void> {
this.$emit("input", this.currentActor);
} }
} }
</script> </script>

View File

@ -1,30 +1,30 @@
<template> <template>
<div class="organizer-picker"> <div class="organizer-picker" v-if="selectedActor">
<!-- If we have a current actor (inline) --> <!-- If we have a current actor (inline) -->
<div <div
v-if="inline && currentActor.id" v-if="inline && selectedActor.id"
class="inline box" class="inline box"
@click="isComponentModalActive = true" @click="isComponentModalActive = true"
> >
<div class="media"> <div class="media">
<div class="media-left"> <div class="media-left">
<figure class="image is-48x48" v-if="currentActor.avatar"> <figure class="image is-48x48" v-if="selectedActor.avatar">
<img <img
class="image is-rounded" class="image is-rounded"
:src="currentActor.avatar.url" :src="selectedActor.avatar.url"
:alt="currentActor.avatar.alt" :alt="selectedActor.avatar.alt"
/> />
</figure> </figure>
<b-icon v-else size="is-large" icon="account-circle" /> <b-icon v-else size="is-large" icon="account-circle" />
</div> </div>
<div class="media-content" v-if="currentActor.name"> <div class="media-content" v-if="selectedActor.name">
<p class="is-4">{{ currentActor.name }}</p> <p class="is-4">{{ selectedActor.name }}</p>
<p class="is-6 has-text-grey"> <p class="is-6 has-text-grey">
{{ `@${currentActor.preferredUsername}` }} {{ `@${selectedActor.preferredUsername}` }}
</p> </p>
</div> </div>
<div class="media-content" v-else> <div class="media-content" v-else>
{{ `@${currentActor.preferredUsername}` }} {{ `@${selectedActor.preferredUsername}` }}
</div> </div>
<b-button type="is-text" @click="isComponentModalActive = true"> <b-button type="is-text" @click="isComponentModalActive = true">
{{ $t("Change") }} {{ $t("Change") }}
@ -33,45 +33,18 @@
</div> </div>
<!-- If we have a current actor --> <!-- If we have a current actor -->
<span <span
v-else-if="currentActor.id" v-else-if="selectedActor.id"
class="block" class="block"
@click="isComponentModalActive = true" @click="isComponentModalActive = true"
> >
<img <img
class="image is-48x48" class="image is-48x48"
v-if="currentActor.avatar" v-if="selectedActor.avatar"
:src="currentActor.avatar.url" :src="selectedActor.avatar.url"
:alt="currentActor.avatar.alt" :alt="selectedActor.avatar.alt"
/> />
<b-icon v-else size="is-large" icon="account-circle" /> <b-icon v-else size="is-large" icon="account-circle" />
</span> </span>
<!-- If we have no current actor -->
<div v-if="groupMemberships.total === 0 || !currentActor.id" class="box">
<div class="media">
<div class="media-left">
<figure class="image is-48x48" v-if="identity.avatar">
<img
class="image is-rounded"
:src="identity.avatar.url"
:alt="identity.avatar.alt"
/>
</figure>
<b-icon v-else size="is-large" icon="account-circle" />
</div>
<div class="media-content" v-if="identity.name">
<p class="is-4">{{ identity.name }}</p>
<p class="is-6 has-text-grey">
{{ `@${identity.preferredUsername}` }}
</p>
</div>
<div class="media-content" v-else>
{{ `@${identity.preferredUsername}` }}
</div>
<b-button type="is-text" @click="isComponentModalActive = true">
{{ $t("Change") }}
</b-button>
</div>
</div>
<b-modal :active.sync="isComponentModalActive" has-modal-card> <b-modal :active.sync="isComponentModalActive" has-modal-card>
<div class="modal-card"> <div class="modal-card">
<header class="modal-card-head"> <header class="modal-card-head">
@ -81,20 +54,15 @@
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<organizer-picker <organizer-picker
v-model="currentActor" v-model="selectedActor"
:identity.sync="identity"
@input="relay" @input="relay"
:restrict-moderator-level="true" :restrict-moderator-level="true"
/> />
</div> </div>
<div class="column"> <div class="column">
<div v-if="actorMembersForCurrentActor.length > 0"> <div v-if="actorMembers.length > 0">
<p>{{ $t("Add a contact") }}</p> <p>{{ $t("Add a contact") }}</p>
<p <p class="field" v-for="actor in actorMembers" :key="actor.id">
class="field"
v-for="actor in actorMembersForCurrentActor"
:key="actor.id"
>
<b-checkbox v-model="actualContacts" :native-value="actor.id"> <b-checkbox v-model="actualContacts" :native-value="actor.id">
<div class="media"> <div class="media">
<div class="media-left"> <div class="media-left">
@ -138,79 +106,121 @@
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue, Watch } from "vue-property-decorator"; import { Component, Prop, Vue, Watch } from "vue-property-decorator";
import { IMember } from "@/types/actor/member.model"; import { IMember } from "@/types/actor/member.model";
import { IActor, IGroup, IPerson } from "../../types/actor"; import { IActor, IGroup, IPerson, usernameWithDomain } from "../../types/actor";
import OrganizerPicker from "./OrganizerPicker.vue"; import OrganizerPicker from "./OrganizerPicker.vue";
import { PERSON_MEMBERSHIPS_WITH_MEMBERS } from "../../graphql/actor"; import {
CURRENT_ACTOR_CLIENT,
LOGGED_USER_MEMBERSHIPS,
} from "../../graphql/actor";
import { Paginate } from "../../types/paginate"; import { Paginate } from "../../types/paginate";
import { GROUP_MEMBERS } from "@/graphql/member";
import { ActorType, MemberRole } from "@/types/enums";
const MEMBER_ROLES = [
MemberRole.CREATOR,
MemberRole.ADMINISTRATOR,
MemberRole.MODERATOR,
MemberRole.MEMBER,
];
@Component({ @Component({
components: { OrganizerPicker }, components: { OrganizerPicker },
apollo: { apollo: {
groupMemberships: { members: {
query: PERSON_MEMBERSHIPS_WITH_MEMBERS, query: GROUP_MEMBERS,
variables() { variables() {
return { return {
id: this.identity.id, name: usernameWithDomain(this.selectedActor),
page: this.membersPage,
limit: 10,
roles: MEMBER_ROLES.join(","),
}; };
}, },
update: (data) => data.person.memberships, update: (data) => data.group.members,
skip() { skip() {
return !this.identity.id; return (
!this.selectedActor || this.selectedActor.type !== ActorType.GROUP
);
}, },
}, },
currentActor: CURRENT_ACTOR_CLIENT,
userMemberships: {
query: LOGGED_USER_MEMBERSHIPS,
variables: {
page: 1,
limit: 100,
},
update: (data) => data.loggedUser.memberships,
},
}, },
}) })
export default class OrganizerPickerWrapper extends Vue { export default class OrganizerPickerWrapper extends Vue {
@Prop({ type: Object, required: true }) value!: IActor; @Prop({ type: Object, required: false }) value!: IActor;
@Prop({ default: true, type: Boolean }) inline!: boolean; @Prop({ default: true, type: Boolean }) inline!: boolean;
@Prop({ type: Object, required: true }) identity!: IPerson; currentActor!: IPerson;
isComponentModalActive = false; isComponentModalActive = false;
currentActor: IActor = this.value;
groupMemberships: Paginate<IMember> = { elements: [], total: 0 };
@Prop({ type: Array, required: false, default: () => [] }) @Prop({ type: Array, required: false, default: () => [] })
contacts!: IActor[]; contacts!: IActor[];
members: Paginate<IMember> = { elements: [], total: 0 };
actualContacts: (string | undefined)[] = this.contacts.map(({ id }) => id); membersPage = 1;
@Watch("contacts") userMemberships: Paginate<IMember> = { elements: [], total: 0 };
updateActualContacts(contacts: IActor[]): void {
this.actualContacts = contacts.map(({ id }) => id); get actualContacts(): (string | undefined)[] {
return this.contacts.map(({ id }) => id);
} }
@Watch("value") set actualContacts(contactsIds: (string | undefined)[]) {
updateCurrentActor(value: IGroup): void { this.$emit(
this.currentActor = value; "update:contacts",
this.actorMembers.filter(({ id }) => contactsIds.includes(id))
);
}
@Watch("userMemberships")
setInitialActor(): void {
if (this.$route.query?.actorId) {
const actorId = this.$route.query?.actorId as string;
this.$router.replace({ query: undefined });
const actor = this.userMemberships.elements.find(
({ parent: { id }, role }) =>
actorId === id && MEMBER_ROLES.includes(role)
)?.parent as IActor;
this.selectedActor = actor;
}
}
get selectedActor(): IActor | undefined {
if (this.value?.id) {
return this.value;
}
if (this.currentActor) {
return this.currentActor;
}
return undefined;
}
set selectedActor(selectedActor: IActor | undefined) {
this.$emit("input", selectedActor);
} }
async relay(group: IGroup): Promise<void> { async relay(group: IGroup): Promise<void> {
this.currentActor = group; this.actualContacts = [];
this.selectedActor = group;
} }
pickActor(): void { pickActor(): void {
this.$emit(
"update:contacts",
this.actorMembersForCurrentActor.filter(({ id }) =>
this.actualContacts.includes(id)
)
);
this.$emit("input", this.currentActor);
this.isComponentModalActive = false; this.isComponentModalActive = false;
} }
get actorMembersForCurrentActor(): IActor[] { get actorMembers(): IActor[] {
const currentMembership = this.groupMemberships.elements.find( if (this.selectedActor?.type === ActorType.GROUP) {
({ parent: { id } }) => id === this.currentActor.id return this.members.elements.map(({ actor }: { actor: IActor }) => actor);
);
if (currentMembership) {
return currentMembership.parent.members.elements.map(
({ actor }: { actor: IActor }) => actor
);
} }
return []; return [];
} }

View File

@ -319,6 +319,7 @@ export const LOGGED_USER_MEMBERSHIPS = gql`
preferredUsername preferredUsername
domain domain
name name
type
avatar { avatar {
id id
url url
@ -359,6 +360,7 @@ export const IDENTITIES = gql`
id id
url url
} }
type
preferredUsername preferredUsername
name name
} }
@ -379,6 +381,7 @@ export const PERSON_MEMBERSHIPS = gql`
preferredUsername preferredUsername
name name
domain domain
type
avatar { avatar {
id id
url url
@ -397,55 +400,6 @@ export const PERSON_MEMBERSHIPS = gql`
} }
`; `;
export const PERSON_MEMBERSHIPS_WITH_MEMBERS = gql`
query PersonMembershipsWithMembers($id: ID!) {
person(id: $id) {
id
memberships {
total
elements {
id
role
parent {
id
preferredUsername
name
domain
avatar {
id
url
}
members {
total
elements {
id
role
actor {
id
preferredUsername
name
domain
avatar {
id
url
}
}
}
}
}
invitedBy {
id
preferredUsername
name
}
insertedAt
updatedAt
}
}
}
}
`;
export const PERSON_MEMBERSHIP_GROUP = gql` export const PERSON_MEMBERSHIP_GROUP = gql`
query PersonMembershipGroup($id: ID!, $group: String!) { query PersonMembershipGroup($id: ID!, $group: String!) {
person(id: $id) { person(id: $id) {

View File

@ -977,5 +977,7 @@
"Create new links": "Create new links", "Create new links": "Create new links",
"You'll need to change the URLs where there were previously entered.": "You'll need to change the URLs where there were previously entered.", "You'll need to change the URLs where there were previously entered.": "You'll need to change the URLs where there were previously entered.",
"Personal feeds": "Personal feeds", "Personal feeds": "Personal feeds",
"These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.": "These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page." "These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.": "These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.",
"The event will show as attributed to this profile.": "The event will show as attributed to this profile.",
"You may show some members as contacts.": "You may show some members as contacts."
} }

View File

@ -1071,5 +1071,7 @@
"Create new links": "Créer de nouveaux liens", "Create new links": "Créer de nouveaux liens",
"You'll need to change the URLs where there were previously entered.": "Vous devrez changer les URLs là où vous les avez entrées précédemment.", "You'll need to change the URLs where there were previously entered.": "Vous devrez changer les URLs là où vous les avez entrées précédemment.",
"Personal feeds": "Flux personnels", "Personal feeds": "Flux personnels",
"These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.": "Ces flux contiennent des informations sur les événements pour lesquels n'importe lequel de vos profils est un⋅e participant⋅e ou un⋅e créateur⋅ice. Vous devriez les garder privés. Vous pouvez trouver des flux spécifiques à chaque profil sur la page d'édition des profils." "These feeds contain event data for the events for which any of your profiles is a participant or creator. You should keep these private. You can find feeds for specific profiles on each profile edition page.": "Ces flux contiennent des informations sur les événements pour lesquels n'importe lequel de vos profils est un⋅e participant⋅e ou un⋅e créateur⋅ice. Vous devriez les garder privés. Vous pouvez trouver des flux spécifiques à chaque profil sur la page d'édition des profils.",
"The event will show as attributed to this profile.": "L'événement sera affiché comme attribué à ce profil.",
"You may show some members as contacts.": "Vous pouvez afficher certain⋅es membres en tant que contacts."
} }

View File

@ -81,19 +81,21 @@
<subtitle>{{ $t("Organizers") }}</subtitle> <subtitle>{{ $t("Organizers") }}</subtitle>
<div v-if="config && config.features.groups"> <div v-if="config && config.features.groups && organizerActor.id">
<b-field> <b-field>
<organizer-picker-wrapper <organizer-picker-wrapper
v-model="event.attributedTo" v-model="organizerActor"
:contacts.sync="event.contacts" :contacts.sync="event.contacts"
:identity="event.organizerActor"
/> />
</b-field> </b-field>
<p v-if="!event.attributedTo.id || attributedToEqualToOrganizerActor"> <p v-if="!attributedToAGroup && organizerActorEqualToCurrentActor">
{{ {{
$t("The event will show as attributed to your personal profile.") $t("The event will show as attributed to your personal profile.")
}} }}
</p> </p>
<p v-else-if="!attributedToAGroup">
{{ $t("The event will show as attributed to this profile.") }}
</p>
<p v-else> <p v-else>
<span>{{ <span>{{
$t("The event will show as attributed to this group.") $t("The event will show as attributed to this group.")
@ -101,6 +103,7 @@
<span <span
v-if="event.contacts && event.contacts.length" v-if="event.contacts && event.contacts.length"
v-html=" v-html="
' ' +
$tc( $tc(
'<b>{contact}</b> will be displayed as contact.', '<b>{contact}</b> will be displayed as contact.',
event.contacts.length, event.contacts.length,
@ -114,6 +117,9 @@
) )
" "
/> />
<span v-else>
{{ $t("You may show some members as contacts.") }}
</span>
</p> </p>
</div> </div>
<subtitle>{{ $t("Who can view this event and participate") }}</subtitle> <subtitle>{{ $t("Who can view this event and participate") }}</subtitle>
@ -432,6 +438,7 @@ import Subtitle from "@/components/Utils/Subtitle.vue";
import { Route } from "vue-router"; import { Route } from "vue-router";
import { formatList } from "@/utils/i18n"; import { formatList } from "@/utils/i18n";
import { import {
ActorType,
CommentModeration, CommentModeration,
EventJoinOptions, EventJoinOptions,
EventStatus, EventStatus,
@ -448,10 +455,11 @@ import {
import { EventModel, IEvent } from "../../types/event.model"; import { EventModel, IEvent } from "../../types/event.model";
import { import {
CURRENT_ACTOR_CLIENT, CURRENT_ACTOR_CLIENT,
IDENTITIES,
LOGGED_USER_DRAFTS, LOGGED_USER_DRAFTS,
LOGGED_USER_PARTICIPATIONS, LOGGED_USER_PARTICIPATIONS,
} from "../../graphql/actor"; } from "../../graphql/actor";
import { IPerson, Person, displayNameAndUsername } from "../../types/actor"; import { displayNameAndUsername, IActor, IGroup } from "../../types/actor";
import { TAGS } from "../../graphql/tags"; import { TAGS } from "../../graphql/tags";
import { ITag } from "../../types/tag.model"; import { ITag } from "../../types/tag.model";
import { import {
@ -480,6 +488,7 @@ const DEFAULT_LIMIT_NUMBER_OF_PLACES = 10;
currentActor: CURRENT_ACTOR_CLIENT, currentActor: CURRENT_ACTOR_CLIENT,
tags: TAGS, tags: TAGS,
config: CONFIG, config: CONFIG,
identities: IDENTITIES,
event: { event: {
query: FETCH_EVENT, query: FETCH_EVENT,
variables() { variables() {
@ -513,12 +522,14 @@ export default class EditEvent extends Vue {
@Prop({ type: Boolean, default: false }) isDuplicate!: boolean; @Prop({ type: Boolean, default: false }) isDuplicate!: boolean;
currentActor = new Person(); currentActor!: IActor;
tags: ITag[] = []; tags: ITag[] = [];
event: IEvent = new EventModel(); event: IEvent = new EventModel();
identities: IActor[] = [];
config!: IConfig; config!: IConfig;
unmodifiedEvent!: IEvent; unmodifiedEvent!: IEvent;
@ -573,16 +584,32 @@ export default class EditEvent extends Vue {
this.event.beginsOn = now; this.event.beginsOn = now;
this.event.endsOn = end; this.event.endsOn = end;
this.event.organizerActor = this.getDefaultActor();
} }
private getDefaultActor() { get organizerActor(): IActor {
if (this.event.organizerActor?.id) { if (this.event?.attributedTo?.id) {
return this.event.attributedTo;
}
if (this.event?.organizerActor?.id) {
return this.event.organizerActor; return this.event.organizerActor;
} }
return this.currentActor; return this.currentActor;
} }
set organizerActor(actor: IActor) {
if (actor?.type === ActorType.GROUP) {
this.event.attributedTo = actor as IGroup;
this.event.organizerActor = this.currentActor;
} else {
this.event.attributedTo = undefined;
this.event.organizerActor = actor;
}
}
get attributedToAGroup(): boolean {
return this.event.attributedTo?.id !== undefined;
}
async mounted(): Promise<void> { async mounted(): Promise<void> {
this.observer = new IntersectionObserver( this.observer = new IntersectionObserver(
(entries) => { (entries) => {
@ -724,8 +751,10 @@ export default class EditEvent extends Vue {
return !( return !(
this.eventId && this.eventId &&
this.event.organizerActor?.id !== undefined && this.event.organizerActor?.id !== undefined &&
this.currentActor.id !== this.event.organizerActor.id !this.identities
) as boolean; .map(({ id }) => id)
.includes(this.event.organizerActor?.id)
);
} }
get updateEventMessage(): string { get updateEventMessage(): string {
@ -752,8 +781,7 @@ export default class EditEvent extends Vue {
*/ */
private postCreateOrUpdate(store: any, updateEvent: IEvent) { private postCreateOrUpdate(store: any, updateEvent: IEvent) {
const resultEvent: IEvent = { ...updateEvent }; const resultEvent: IEvent = { ...updateEvent };
const organizerActor: IPerson = this.event.organizerActor as Person; resultEvent.organizerActor = this.event.organizerActor;
resultEvent.organizerActor = organizerActor;
resultEvent.relatedEvents = []; resultEvent.relatedEvents = [];
store.writeQuery({ store.writeQuery({
@ -766,12 +794,12 @@ export default class EditEvent extends Vue {
query: EVENT_PERSON_PARTICIPATION, query: EVENT_PERSON_PARTICIPATION,
variables: { variables: {
eventId: updateEvent.id, eventId: updateEvent.id,
name: organizerActor.preferredUsername, name: this.event.organizerActor?.preferredUsername,
}, },
data: { data: {
person: { person: {
__typename: "Person", __typename: "Person",
id: organizerActor.id, id: this.event?.organizerActor?.id,
participations: { participations: {
__typename: "PaginatedParticipantList", __typename: "PaginatedParticipantList",
total: 1, total: 1,
@ -782,7 +810,7 @@ export default class EditEvent extends Vue {
role: ParticipantRole.CREATOR, role: ParticipantRole.CREATOR,
actor: { actor: {
__typename: "Actor", __typename: "Actor",
id: organizerActor.id, id: this.event?.organizerActor?.id,
}, },
event: { event: {
__typename: "Event", __typename: "Event",
@ -819,28 +847,24 @@ export default class EditEvent extends Vue {
]; ];
} }
get attributedToEqualToOrganizerActor(): boolean { get organizerActorEqualToCurrentActor(): boolean {
return (this.event.organizerActor?.id !== undefined && return (
this.event.attributedTo?.id === this.event.organizerActor?.id) as boolean; this.currentActor?.id !== undefined &&
this.organizerActor?.id === this.currentActor?.id
);
} }
/** /**
* Build variables for Event GraphQL creation query * Build variables for Event GraphQL creation query
*/ */
private async buildVariables() { private async buildVariables() {
this.event.organizerActor = this.event.organizerActor?.id
? this.event.organizerActor
: this.currentActor;
let res = this.event.toEditJSON(); let res = this.event.toEditJSON();
if (this.event.organizerActor) { if (this.event.organizerActor) {
res = Object.assign(res, { res = Object.assign(res, {
organizerActorId: this.event.organizerActor.id, organizerActorId: this.event.organizerActor.id,
}); });
} }
const attributedToId = const attributedToId = this.event.attributedTo?.id
this.event.attributedTo &&
!this.attributedToEqualToOrganizerActor &&
this.event.attributedTo.id
? this.event.attributedTo.id ? this.event.attributedTo.id
: null; : null;
res = Object.assign(res, { attributedToId }); res = Object.assign(res, { attributedToId });

View File

@ -327,6 +327,7 @@
v-if="isCurrentActorAGroupModerator" v-if="isCurrentActorAGroupModerator"
:to="{ :to="{
name: RouteName.CREATE_EVENT, name: RouteName.CREATE_EVENT,
query: { actorId: group.id },
}" }"
class="button is-primary" class="button is-primary"
>{{ $t("+ Create an event") }}</router-link >{{ $t("+ Create an event") }}</router-link

View File

@ -315,7 +315,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do
context: %{current_user: user} context: %{current_user: user}
}) do }) do
with {:is_owned, %Actor{id: actor_id}} <- User.owns_actor(user, actor_id), with {:is_owned, %Actor{id: actor_id}} <- User.owns_actor(user, actor_id),
%Actor{id: group_id} <- Actors.get_actor_by_name(group, :Group), {:group, %Actor{id: group_id}} <- {:group, Actors.get_actor_by_name(group, :Group)},
{:ok, %Member{} = membership} <- Actors.get_member(actor_id, group_id), {:ok, %Member{} = membership} <- Actors.get_member(actor_id, group_id),
memberships <- %Page{ memberships <- %Page{
total: 1, total: 1,
@ -326,6 +326,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do
{:error, :member_not_found} -> {:error, :member_not_found} ->
{:ok, %Page{total: 0, elements: []}} {:ok, %Page{total: 0, elements: []}}
{:group, nil} ->
{:error, :group_not_found}
{: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")}
end end