forked from potsda.mn/mobilizon
Add a dropdown on participate menu, disallow listing participations
Now requires quering the person endpoint to know if an actor participates in an event, organizers can make authenticated requests to event { participants { } } to see the pending / approved participants. Also closes #174 Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
8a3e606c15
commit
757d2cabec
|
@ -71,49 +71,10 @@ export default class App extends Vue {
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
/* Bulma imports */
|
/* Bulma imports */
|
||||||
@import "~bulma/sass/utilities/_all";
|
@import "~bulma/bulma";
|
||||||
@import "~bulma/sass/base/_all.sass";
|
|
||||||
@import "~bulma/sass/components/card.sass";
|
|
||||||
@import "~bulma/sass/components/media.sass";
|
|
||||||
@import "~bulma/sass/components/message.sass";
|
|
||||||
@import "~bulma/sass/components/modal.sass";
|
|
||||||
@import "~bulma/sass/components/navbar.sass";
|
|
||||||
@import "~bulma/sass/components/pagination.sass";
|
|
||||||
@import "~bulma/sass/components/dropdown.sass";
|
|
||||||
@import "~bulma/sass/components/breadcrumb.sass";
|
|
||||||
@import "~bulma/sass/components/list.sass";
|
|
||||||
@import "~bulma/sass/components/tabs";
|
|
||||||
@import "~bulma/sass/elements/box.sass";
|
|
||||||
@import "~bulma/sass/elements/button.sass";
|
|
||||||
@import "~bulma/sass/elements/container.sass";
|
|
||||||
@import "~bulma/sass/form/_all";
|
|
||||||
@import "~bulma/sass/elements/icon.sass";
|
|
||||||
@import "~bulma/sass/elements/image.sass";
|
|
||||||
@import "~bulma/sass/elements/other.sass";
|
|
||||||
@import "~bulma/sass/elements/progress.sass";
|
|
||||||
@import "~bulma/sass/elements/tag.sass";
|
|
||||||
@import "~bulma/sass/elements/title.sass";
|
|
||||||
@import "~bulma/sass/elements/notification";
|
|
||||||
@import "~bulma/sass/elements/table";
|
|
||||||
@import "~bulma/sass/grid/_all.sass";
|
|
||||||
@import "~bulma/sass/layout/_all.sass";
|
|
||||||
|
|
||||||
/* Buefy imports */
|
/* Buefy imports */
|
||||||
@import "~buefy/src/scss/utils/_all";
|
@import "~buefy/src/scss/buefy";
|
||||||
@import "~buefy/src/scss/components/datepicker";
|
|
||||||
@import "~buefy/src/scss/components/notices";
|
|
||||||
@import "~buefy/src/scss/components/dropdown";
|
|
||||||
@import "~buefy/src/scss/components/autocomplete";
|
|
||||||
@import "~buefy/src/scss/components/form";
|
|
||||||
@import "~buefy/src/scss/components/modal";
|
|
||||||
@import "~buefy/src/scss/components/progress";
|
|
||||||
@import "~buefy/src/scss/components/tag";
|
|
||||||
@import "~buefy/src/scss/components/taginput";
|
|
||||||
@import "~buefy/src/scss/components/upload";
|
|
||||||
@import "~buefy/src/scss/components/radio";
|
|
||||||
@import "~buefy/src/scss/components/switch";
|
|
||||||
@import "~buefy/src/scss/components/table";
|
|
||||||
@import "~buefy/src/scss/components/tabs";
|
|
||||||
|
|
||||||
.router-enter-active,
|
.router-enter-active,
|
||||||
.router-leave-active {
|
.router-leave-active {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
||||||
@Component({})
|
@Component
|
||||||
export default class DateTimePicker extends Vue {
|
export default class DateTimePicker extends Vue {
|
||||||
@Prop({ required: true, type: Date }) value!: Date;
|
@Prop({ required: true, type: Date }) value!: Date;
|
||||||
@Prop({ required: false, type: String, default: 'Datetime' }) label!: string;
|
@Prop({ required: false, type: String, default: 'Datetime' }) label!: string;
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<article class="box columns">
|
<article class="box">
|
||||||
<div class="content column">
|
|
||||||
<div class="title-wrapper">
|
<div class="title-wrapper">
|
||||||
<div class="date-component" v-if="!mergedOptions.hideDate">
|
<div class="date-component" v-if="!mergedOptions.hideDate">
|
||||||
<date-calendar-icon :date="participation.event.beginsOn" />
|
<date-calendar-icon :date="participation.event.beginsOn" />
|
||||||
</div>
|
</div>
|
||||||
<h2 class="title" ref="title">{{ participation.event.title }}</h2>
|
<h2 class="title" ref="title">{{ participation.event.title }}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="content column">
|
||||||
<div>
|
<div>
|
||||||
<span v-if="participation.event.physicalAddress && participation.event.physicalAddress.locality">{{ participation.event.physicalAddress.locality }} - </span>
|
<span v-if="participation.event.physicalAddress && participation.event.physicalAddress.locality">{{ participation.event.physicalAddress.locality }} - </span>
|
||||||
<span v-if="participation.actor.id === participation.event.organizerActor.id">{{ $t("You're organizing this event") }}</span>
|
<span v-if="participation.actor.id === participation.event.organizerActor.id">{{ $t("You're organizing this event") }}</span>
|
||||||
|
@ -60,6 +61,7 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
111
js/src/components/Event/ParticipationButton.vue
Normal file
111
js/src/components/Event/ParticipationButton.vue
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
<template>
|
||||||
|
<div class="participation-button">
|
||||||
|
<b-dropdown aria-role="list" position="is-bottom-left" v-if="participation && participation.role === ParticipantRole.PARTICIPANT">
|
||||||
|
<button class="button is-success" type="button" slot="trigger">
|
||||||
|
<template>
|
||||||
|
<span>{{ $t('I participate') }}</span>
|
||||||
|
</template>
|
||||||
|
<b-icon icon="menu-down"></b-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- <b-dropdown-item :value="false" aria-role="listitem">-->
|
||||||
|
<!-- {{ $t('Change my identity…')}}-->
|
||||||
|
<!-- </b-dropdown-item>-->
|
||||||
|
|
||||||
|
<b-dropdown-item :value="false" aria-role="listitem" @click="confirmLeave">
|
||||||
|
{{ $t('Cancel my participation…')}}
|
||||||
|
</b-dropdown-item>
|
||||||
|
</b-dropdown>
|
||||||
|
|
||||||
|
<div v-else-if="participation && participation.role === ParticipantRole.NOT_APPROVED">
|
||||||
|
<b-dropdown aria-role="list" position="is-bottom-left" class="dropdown-disabled">
|
||||||
|
<button class="button is-success" type="button" slot="trigger">
|
||||||
|
<template>
|
||||||
|
<span>{{ $t('I participate') }}</span>
|
||||||
|
</template>
|
||||||
|
<b-icon icon="menu-down"></b-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- <b-dropdown-item :value="false" aria-role="listitem">-->
|
||||||
|
<!-- {{ $t('Change my identity…')}}-->
|
||||||
|
<!-- </b-dropdown-item>-->
|
||||||
|
|
||||||
|
<b-dropdown-item :value="false" aria-role="listitem" @click="confirmLeave">
|
||||||
|
{{ $t('Cancel my participation request…')}}
|
||||||
|
</b-dropdown-item>
|
||||||
|
</b-dropdown>
|
||||||
|
<small>{{ $t('Participation requested!')}}</small><br />
|
||||||
|
<small>{{ $t('Waiting for organization team approval.')}}</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<b-dropdown aria-role="list" position="is-bottom-left" v-if="!participation">
|
||||||
|
<button class="button is-primary" type="button" slot="trigger">
|
||||||
|
<template>
|
||||||
|
<span>{{ $t('Participate') }}</span>
|
||||||
|
</template>
|
||||||
|
<b-icon icon="menu-down"></b-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<b-dropdown-item :value="true" aria-role="listitem" @click="joinEvent(currentActor)">
|
||||||
|
<div class="media">
|
||||||
|
<div class="media-left">
|
||||||
|
<figure class="image is-32x32" v-if="currentActor.avatar">
|
||||||
|
<img class="is-rounded" :src="currentActor.avatar.url" alt="" />
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
<div class="media-content">
|
||||||
|
<span>{{ $t('with {identity}', {identity: currentActor.preferredUsername }) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</b-dropdown-item>
|
||||||
|
|
||||||
|
<b-dropdown-item :value="false" aria-role="listitem" @click="joinModal">
|
||||||
|
{{ $t('with another identity…')}}
|
||||||
|
</b-dropdown-item>
|
||||||
|
</b-dropdown>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
|
import { IParticipant, ParticipantRole } from '@/types/event.model';
|
||||||
|
import { IPerson } from '@/types/actor';
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class ParticipationButton extends Vue {
|
||||||
|
@Prop({ required: true }) participation!: IParticipant;
|
||||||
|
@Prop({ required: true }) currentActor!: IPerson;
|
||||||
|
|
||||||
|
ParticipantRole = ParticipantRole;
|
||||||
|
|
||||||
|
joinEvent(actor: IPerson) {
|
||||||
|
this.$emit('joinEvent', actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
joinModal() {
|
||||||
|
this.$emit('joinModal');
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmLeave() {
|
||||||
|
this.$emit('confirmLeave');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.participation-button {
|
||||||
|
.dropdown {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
&.dropdown-disabled button {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,92 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="modal-card">
|
|
||||||
<header class="modal-card-head">
|
|
||||||
<p class="modal-card-title">{{ $t('Join event {title}', {title: event.title}) }}</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<section class="modal-card-body is-flex">
|
|
||||||
<div class="media">
|
|
||||||
<div
|
|
||||||
class="media-left">
|
|
||||||
<b-icon
|
|
||||||
icon="alert"
|
|
||||||
type="is-warning"
|
|
||||||
size="is-large"/>
|
|
||||||
</div>
|
|
||||||
<div class="media-content">
|
|
||||||
<p>{{ $t('Do you want to participate in {title}?', {title: event.title}) }}?</p>
|
|
||||||
|
|
||||||
<b-field :label="$t('Identity')">
|
|
||||||
<identity-picker v-model="identity"></identity-picker>
|
|
||||||
</b-field>
|
|
||||||
|
|
||||||
<p v-if="event.joinOptions === EventJoinOptions.RESTRICTED">
|
|
||||||
{{ $t('The event organizer has chosen to approve manually the participations to this event. You will receive a notification when your participation has been approved')}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p v-if="!event.local">
|
|
||||||
{{ $t('The event came from another instance. Your participation will be confirmed after we confirm it with the other instance.') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<footer class="modal-card-foot">
|
|
||||||
<button
|
|
||||||
class="button"
|
|
||||||
ref="cancelButton"
|
|
||||||
@click="close">
|
|
||||||
{{ $t('Cancel') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="button is-primary"
|
|
||||||
ref="confirmButton"
|
|
||||||
@click="confirm">
|
|
||||||
{{ $t('Confirm my particpation') }}
|
|
||||||
</button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
|
||||||
import { IEvent, EventJoinOptions } from '@/types/event.model';
|
|
||||||
import IdentityPicker from '@/views/Account/IdentityPicker.vue';
|
|
||||||
import { IPerson } from '@/types/actor';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
components: {
|
|
||||||
IdentityPicker,
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$data.isActive = true;
|
|
||||||
},
|
|
||||||
})
|
|
||||||
export default class ReportModal extends Vue {
|
|
||||||
@Prop({ type: Function, default: () => {} }) onConfirm;
|
|
||||||
@Prop({ type: Object }) event! : IEvent;
|
|
||||||
@Prop({ type: Object }) defaultIdentity!: IPerson;
|
|
||||||
|
|
||||||
isActive: boolean = false;
|
|
||||||
identity: IPerson = this.defaultIdentity;
|
|
||||||
|
|
||||||
EventJoinOptions = EventJoinOptions;
|
|
||||||
|
|
||||||
confirm() {
|
|
||||||
this.onConfirm(this.identity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the Dialog.
|
|
||||||
*/
|
|
||||||
close() {
|
|
||||||
this.isActive = false;
|
|
||||||
this.$emit('close');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
|
||||||
.modal-card .modal-card-foot {
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -10,6 +10,9 @@ const participantQuery = `
|
||||||
},
|
},
|
||||||
name,
|
name,
|
||||||
id
|
id
|
||||||
|
},
|
||||||
|
event {
|
||||||
|
id
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -52,7 +55,7 @@ const optionsQuery = `
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const FETCH_EVENT = gql`
|
export const FETCH_EVENT = gql`
|
||||||
query($uuid:UUID!, $roles: String) {
|
query($uuid:UUID!) {
|
||||||
event(uuid: $uuid) {
|
event(uuid: $uuid) {
|
||||||
id,
|
id,
|
||||||
uuid,
|
uuid,
|
||||||
|
@ -95,9 +98,6 @@ export const FETCH_EVENT = gql`
|
||||||
# preferredUsername,
|
# preferredUsername,
|
||||||
# name,
|
# name,
|
||||||
# },
|
# },
|
||||||
participants (roles: $roles) {
|
|
||||||
${participantQuery}
|
|
||||||
},
|
|
||||||
participantStats {
|
participantStats {
|
||||||
approved,
|
approved,
|
||||||
unapproved
|
unapproved
|
||||||
|
@ -363,9 +363,10 @@ export const DELETE_EVENT = gql`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const PARTICIPANTS = gql`
|
export const PARTICIPANTS = gql`
|
||||||
query($uuid: UUID!, $page: Int, $limit: Int, $roles: String) {
|
query($uuid: UUID!, $page: Int, $limit: Int, $roles: String, $actorId: ID!) {
|
||||||
event(uuid: $uuid) {
|
event(uuid: $uuid) {
|
||||||
participants(page: $page, limit: $limit, roles: $roles) {
|
id,
|
||||||
|
participants(page: $page, limit: $limit, roles: $roles, actorId: $actorId) {
|
||||||
${participantQuery}
|
${participantQuery}
|
||||||
},
|
},
|
||||||
participantStats {
|
participantStats {
|
||||||
|
@ -375,3 +376,21 @@ export const PARTICIPANTS = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const EVENT_PERSON_PARTICIPATION = gql`
|
||||||
|
query($name: String!, $eventId: ID!) {
|
||||||
|
person(preferredUsername: $name) {
|
||||||
|
id,
|
||||||
|
participations(eventId: $eventId) {
|
||||||
|
id,
|
||||||
|
role,
|
||||||
|
actor {
|
||||||
|
id
|
||||||
|
},
|
||||||
|
event {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
|
@ -17,8 +17,13 @@
|
||||||
"Are you sure you want to delete this event? This action cannot be reverted.": "Are you sure you want to delete this event? This action cannot be reverted.",
|
"Are you sure you want to delete this event? This action cannot be reverted.": "Are you sure you want to delete this event? This action cannot be reverted.",
|
||||||
"Before you can login, you need to click on the link inside it to validate your account": "Before you can login, you need to click on the link inside it to validate your account",
|
"Before you can login, you need to click on the link inside it to validate your account": "Before you can login, you need to click on the link inside it to validate your account",
|
||||||
"By {name}": "By {name}",
|
"By {name}": "By {name}",
|
||||||
|
"Cancel my participation request…": "Cancel my participation request…",
|
||||||
|
"Cancel my participation…": "Cancel my participation…",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"Category": "Category",
|
"Category": "Category",
|
||||||
|
"Change my identity…": "Change my identity…",
|
||||||
|
"Change my password": "Change my password",
|
||||||
|
"Change password": "Change password",
|
||||||
"Change": "Change",
|
"Change": "Change",
|
||||||
"Clear": "Clear",
|
"Clear": "Clear",
|
||||||
"Click to select": "Click to select",
|
"Click to select": "Click to select",
|
||||||
|
@ -82,6 +87,7 @@
|
||||||
"Group": "Group",
|
"Group": "Group",
|
||||||
"Groups": "Groups",
|
"Groups": "Groups",
|
||||||
"I create an identity": "I create an identity",
|
"I create an identity": "I create an identity",
|
||||||
|
"I participate": "I participate",
|
||||||
"I want to approve every participation request": "I want to approve every participation request",
|
"I want to approve every participation request": "I want to approve every participation request",
|
||||||
"Identities": "Identities",
|
"Identities": "Identities",
|
||||||
"Identity {displayName} created": "Identity {displayName} created",
|
"Identity {displayName} created": "Identity {displayName} created",
|
||||||
|
@ -116,6 +122,7 @@
|
||||||
"My events": "My events",
|
"My events": "My events",
|
||||||
"My identities": "My identities",
|
"My identities": "My identities",
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
|
"New password": "New password",
|
||||||
"No address defined": "No address defined",
|
"No address defined": "No address defined",
|
||||||
"No events found": "No events found",
|
"No events found": "No events found",
|
||||||
"No group found": "No group found",
|
"No group found": "No group found",
|
||||||
|
@ -123,6 +130,7 @@
|
||||||
"No participants yet.": "No participants yet.",
|
"No participants yet.": "No participants yet.",
|
||||||
"No results for \"{queryText}\"": "No results for \"{queryText}\"",
|
"No results for \"{queryText}\"": "No results for \"{queryText}\"",
|
||||||
"Number of places": "Number of places",
|
"Number of places": "Number of places",
|
||||||
|
"Old password": "Old password",
|
||||||
"One person is going": "No one is going | One person is going | {approved} persons are going",
|
"One person is going": "No one is going | One person is going | {approved} persons are going",
|
||||||
"Only accessible through link and search (private)": "Only accessible through link and search (private)",
|
"Only accessible through link and search (private)": "Only accessible through link and search (private)",
|
||||||
"Opened reports": "Opened reports",
|
"Opened reports": "Opened reports",
|
||||||
|
@ -133,8 +141,11 @@
|
||||||
"Otherwise this identity will just be removed from the group administrators.": "Otherwise this identity will just be removed from the group administrators.",
|
"Otherwise this identity will just be removed from the group administrators.": "Otherwise this identity will just be removed from the group administrators.",
|
||||||
"Page limited to my group (asks for auth)": "Page limited to my group (asks for auth)",
|
"Page limited to my group (asks for auth)": "Page limited to my group (asks for auth)",
|
||||||
"Participants": "Participants",
|
"Participants": "Participants",
|
||||||
|
"Participate": "Participate",
|
||||||
"Participation approval": "Participation approval",
|
"Participation approval": "Participation approval",
|
||||||
|
"Participation requested!": "Participation requested!",
|
||||||
"Password (confirmation)": "Password (confirmation)",
|
"Password (confirmation)": "Password (confirmation)",
|
||||||
|
"Password change": "Password change",
|
||||||
"Password reset": "Password reset",
|
"Password reset": "Password reset",
|
||||||
"Password": "Password",
|
"Password": "Password",
|
||||||
"Past events": "Passed events",
|
"Past events": "Passed events",
|
||||||
|
@ -187,6 +198,7 @@
|
||||||
"The event organizer has chosen to approve manually the participations to this event. You will receive a notification when your participation has been approved": "The event organizer has chosen to approve manually the participations to this event. You will receive a notification when your participation has been approved",
|
"The event organizer has chosen to approve manually the participations to this event. You will receive a notification when your participation has been approved": "The event organizer has chosen to approve manually the participations to this event. You will receive a notification when your participation has been approved",
|
||||||
"The event title will be ellipsed.": "The event title will be ellipsed.",
|
"The event title will be ellipsed.": "The event title will be ellipsed.",
|
||||||
"The page you're looking for doesn't exist.": "The page you're looking for doesn't exist.",
|
"The page you're looking for doesn't exist.": "The page you're looking for doesn't exist.",
|
||||||
|
"The password was successfully changed": "The password was successfully changed",
|
||||||
"The report will be sent to the moderators of your instance. You can explain why you report this content below.": "The report will be sent to the moderators of your instance. You can explain why you report this content below.",
|
"The report will be sent to the moderators of your instance. You can explain why you report this content below.": "The report will be sent to the moderators of your instance. You can explain why you report this content below.",
|
||||||
"The {date} at {time}": "The {date} at {time}",
|
"The {date} at {time}": "The {date} at {time}",
|
||||||
"The {date} from {startTime} to {endTime}": "The {date} from {startTime} to {endTime}",
|
"The {date} from {startTime} to {endTime}": "The {date} from {startTime} to {endTime}",
|
||||||
|
@ -208,6 +220,7 @@
|
||||||
"View event page": "View event page",
|
"View event page": "View event page",
|
||||||
"View everything": "View everything",
|
"View everything": "View everything",
|
||||||
"Visible everywhere on the web (public)": "Visible everywhere on the web (public)",
|
"Visible everywhere on the web (public)": "Visible everywhere on the web (public)",
|
||||||
|
"Waiting for organization team approval.": "Waiting for organization team approval.",
|
||||||
"Waiting list": "Waiting list",
|
"Waiting list": "Waiting list",
|
||||||
"We just sent an email to {email}": "We just sent an email to {email}",
|
"We just sent an email to {email}": "We just sent an email to {email}",
|
||||||
"Website / URL": "Website / URL",
|
"Website / URL": "Website / URL",
|
||||||
|
@ -220,6 +233,7 @@
|
||||||
"You announced that you're going to this event.": "You announced that you're going to this event.",
|
"You announced that you're going to this event.": "You announced that you're going to this event.",
|
||||||
"You are already logged-in.": "You are already logged-in.",
|
"You are already logged-in.": "You are already logged-in.",
|
||||||
"You are an organizer.": "You are an organizer.",
|
"You are an organizer.": "You are an organizer.",
|
||||||
|
"You have been disconnected": "You have been disconnected",
|
||||||
"You have one event in {days} days.": "You have no events in {days} days | You have one event in {days} days. | You have {count} events in {days} days",
|
"You have one event in {days} days.": "You have no events in {days} days | You have one event in {days} days. | You have {count} events in {days} days",
|
||||||
"You have one event today.": "You have no events today | You have one event today. | You have {count} events today",
|
"You have one event today.": "You have no events today | You have one event today. | You have {count} events today",
|
||||||
"You have one event tomorrow.": "You have no events tomorrow | You have one event tomorrow. | You have {count} events tomorrow",
|
"You have one event tomorrow.": "You have no events tomorrow | You have one event tomorrow. | You have {count} events tomorrow",
|
||||||
|
@ -233,6 +247,8 @@
|
||||||
"e.g. 10 Rue Jangot": "e.g. 10 Rue Jangot",
|
"e.g. 10 Rue Jangot": "e.g. 10 Rue Jangot",
|
||||||
"iCal Feed": "iCal Feed",
|
"iCal Feed": "iCal Feed",
|
||||||
"meditate a bit": "meditate a bit",
|
"meditate a bit": "meditate a bit",
|
||||||
|
"with another identity…": "with another identity…",
|
||||||
|
"with {identity}": "with {identity}",
|
||||||
"{actor}'s avatar": "{actor}'s avatar",
|
"{actor}'s avatar": "{actor}'s avatar",
|
||||||
"{approved} / {total} seats": "{approved} / {total} seats",
|
"{approved} / {total} seats": "{approved} / {total} seats",
|
||||||
"{count} participants": "{count} participants",
|
"{count} participants": "{count} participants",
|
||||||
|
|
|
@ -17,8 +17,13 @@
|
||||||
"Are you sure you want to delete this event? This action cannot be reverted.": "Êtes-vous certain⋅e de vouloir supprimer cet événement ? Cette action ne peut être annulée.",
|
"Are you sure you want to delete this event? This action cannot be reverted.": "Êtes-vous certain⋅e de vouloir supprimer cet événement ? Cette action ne peut être annulée.",
|
||||||
"Before you can login, you need to click on the link inside it to validate your account": "Avant que vous puissiez vous enregistrer, vous devez cliquer sur le lien à l'intérieur pour valider votre compte",
|
"Before you can login, you need to click on the link inside it to validate your account": "Avant que vous puissiez vous enregistrer, vous devez cliquer sur le lien à l'intérieur pour valider votre compte",
|
||||||
"By {name}": "Par {name}",
|
"By {name}": "Par {name}",
|
||||||
|
"Cancel my participation request…": "Cancel my participation request…",
|
||||||
|
"Cancel my participation…": "Annuler ma participation…",
|
||||||
"Cancel": "Annuler",
|
"Cancel": "Annuler",
|
||||||
"Category": "Catégorie",
|
"Category": "Catégorie",
|
||||||
|
"Change my identity…": "Changer mon identité…",
|
||||||
|
"Change my password": "Modifier mon mot de passe",
|
||||||
|
"Change password": "Modifier mot de passe",
|
||||||
"Change": "Modifier",
|
"Change": "Modifier",
|
||||||
"Clear": "Effacer",
|
"Clear": "Effacer",
|
||||||
"Click to select": "Cliquez pour sélectionner",
|
"Click to select": "Cliquez pour sélectionner",
|
||||||
|
@ -82,6 +87,7 @@
|
||||||
"Group": "Groupe",
|
"Group": "Groupe",
|
||||||
"Groups": "Groupes",
|
"Groups": "Groupes",
|
||||||
"I create an identity": "Je crée une identité",
|
"I create an identity": "Je crée une identité",
|
||||||
|
"I participate": "Je participe",
|
||||||
"I want to approve every participation request": "Je veux approuver chaque demande de participation",
|
"I want to approve every participation request": "Je veux approuver chaque demande de participation",
|
||||||
"Identities": "Identités",
|
"Identities": "Identités",
|
||||||
"Identity {displayName} created": "Identité {displayName} créée",
|
"Identity {displayName} created": "Identité {displayName} créée",
|
||||||
|
@ -116,6 +122,7 @@
|
||||||
"My events": "Mes événements",
|
"My events": "Mes événements",
|
||||||
"My identities": "Mes identités",
|
"My identities": "Mes identités",
|
||||||
"Name": "Nom",
|
"Name": "Nom",
|
||||||
|
"New password": "Nouveau mot de passe",
|
||||||
"No address defined": "Aucune adresse définie",
|
"No address defined": "Aucune adresse définie",
|
||||||
"No events found": "Aucun événement trouvé",
|
"No events found": "Aucun événement trouvé",
|
||||||
"No group found": "Aucun groupe trouvé",
|
"No group found": "Aucun groupe trouvé",
|
||||||
|
@ -123,6 +130,7 @@
|
||||||
"No participants yet.": "Pas de participants pour le moment.",
|
"No participants yet.": "Pas de participants pour le moment.",
|
||||||
"No results for \"{queryText}\"": "Pas de résultats pour « {queryText} »",
|
"No results for \"{queryText}\"": "Pas de résultats pour « {queryText} »",
|
||||||
"Number of places": "Nombre de places",
|
"Number of places": "Nombre de places",
|
||||||
|
"Old password": "Ancien mot de passe",
|
||||||
"One person is going": "Personne n'y va | Une personne y va | {approved} personnes y vont",
|
"One person is going": "Personne n'y va | Une personne y va | {approved} personnes y vont",
|
||||||
"Only accessible through link and search (private)": "Uniquement accessibles par lien et la recherche (privé)",
|
"Only accessible through link and search (private)": "Uniquement accessibles par lien et la recherche (privé)",
|
||||||
"Opened reports": "Signalements ouverts",
|
"Opened reports": "Signalements ouverts",
|
||||||
|
@ -133,8 +141,11 @@
|
||||||
"Otherwise this identity will just be removed from the group administrators.": "Sinon cette identité sera juste supprimée des administrateurs du groupe.",
|
"Otherwise this identity will just be removed from the group administrators.": "Sinon cette identité sera juste supprimée des administrateurs du groupe.",
|
||||||
"Page limited to my group (asks for auth)": "Accès limité à mon groupe (demande authentification)",
|
"Page limited to my group (asks for auth)": "Accès limité à mon groupe (demande authentification)",
|
||||||
"Participants": "Participants",
|
"Participants": "Participants",
|
||||||
|
"Participate": "Participer",
|
||||||
"Participation approval": "Validation des participations",
|
"Participation approval": "Validation des participations",
|
||||||
|
"Participation requested!": "Participation demandée !",
|
||||||
"Password (confirmation)": "Mot de passe (confirmation)",
|
"Password (confirmation)": "Mot de passe (confirmation)",
|
||||||
|
"Password change": "Changement de mot de passe",
|
||||||
"Password reset": "Réinitialisation du mot de passe",
|
"Password reset": "Réinitialisation du mot de passe",
|
||||||
"Password": "Mot de passe",
|
"Password": "Mot de passe",
|
||||||
"Past events": "Événements passés",
|
"Past events": "Événements passés",
|
||||||
|
@ -187,6 +198,7 @@
|
||||||
"The event organizer has chosen to approve manually the participations to this event. You will receive a notification when your participation has been approved": "L'organisateur⋅ice de l'événement a choisi d'approuver manuellement les participations à cet événement. Vous recevrez une notification lorsque votre participation sera approuvée",
|
"The event organizer has chosen to approve manually the participations to this event. You will receive a notification when your participation has been approved": "L'organisateur⋅ice de l'événement a choisi d'approuver manuellement les participations à cet événement. Vous recevrez une notification lorsque votre participation sera approuvée",
|
||||||
"The event title will be ellipsed.": "Le titre de l'événement sera ellipsé.",
|
"The event title will be ellipsed.": "Le titre de l'événement sera ellipsé.",
|
||||||
"The page you're looking for doesn't exist.": "La page que vous recherchez n'existe pas.",
|
"The page you're looking for doesn't exist.": "La page que vous recherchez n'existe pas.",
|
||||||
|
"The password was successfully changed": "Le mot de passe a été changé avec succès",
|
||||||
"The report will be sent to the moderators of your instance. You can explain why you report this content below.": "Le signalement sera envoyé aux modérateur⋅ices de votre instance. Vous pouvez expliquer pourquoi vous signalez ce contenu ci-dessous.",
|
"The report will be sent to the moderators of your instance. You can explain why you report this content below.": "Le signalement sera envoyé aux modérateur⋅ices de votre instance. Vous pouvez expliquer pourquoi vous signalez ce contenu ci-dessous.",
|
||||||
"The {date} at {time}": "Le {date} à {time}",
|
"The {date} at {time}": "Le {date} à {time}",
|
||||||
"The {date} from {startTime} to {endTime}": "Le {date} de {startTime} à {endTime}",
|
"The {date} from {startTime} to {endTime}": "Le {date} de {startTime} à {endTime}",
|
||||||
|
@ -208,6 +220,7 @@
|
||||||
"View event page": "Voir la page de l'événement",
|
"View event page": "Voir la page de l'événement",
|
||||||
"View everything": "Voir tout",
|
"View everything": "Voir tout",
|
||||||
"Visible everywhere on the web (public)": "Visible partout sur le web (public)",
|
"Visible everywhere on the web (public)": "Visible partout sur le web (public)",
|
||||||
|
"Waiting for organization team approval.": "En attente d'approbation par l'organisation.",
|
||||||
"Waiting list": "Liste d'attente",
|
"Waiting list": "Liste d'attente",
|
||||||
"We just sent an email to {email}": "Nous venons d'envoyer un email à {email}",
|
"We just sent an email to {email}": "Nous venons d'envoyer un email à {email}",
|
||||||
"Website / URL": "Site web / URL",
|
"Website / URL": "Site web / URL",
|
||||||
|
@ -220,6 +233,7 @@
|
||||||
"You announced that you're going to this event.": "Vous avez annoncé vous rendre à cet événement.",
|
"You announced that you're going to this event.": "Vous avez annoncé vous rendre à cet événement.",
|
||||||
"You are already logged-in.": "Vous êtes déjà connecté.",
|
"You are already logged-in.": "Vous êtes déjà connecté.",
|
||||||
"You are an organizer.": "Vous êtes un organisateur.",
|
"You are an organizer.": "Vous êtes un organisateur.",
|
||||||
|
"You have been disconnected": "Vous avez été déconnecté⋅e",
|
||||||
"You have one event in {days} days.": "Vous n'avez pas d'événements dans {days} jours | Vous avez un événement dans {days} jours. | Vous avez {count} événements dans {days} jours",
|
"You have one event in {days} days.": "Vous n'avez pas d'événements dans {days} jours | Vous avez un événement dans {days} jours. | Vous avez {count} événements dans {days} jours",
|
||||||
"You have one event today.": "Vous n'avez pas d'évenement aujourd'hui | Vous avez un événement aujourd'hui. | Vous avez {count} événements aujourd'hui",
|
"You have one event today.": "Vous n'avez pas d'évenement aujourd'hui | Vous avez un événement aujourd'hui. | Vous avez {count} événements aujourd'hui",
|
||||||
"You have one event tomorrow.": "Vous n'avez pas d'événement demain | Vous avez un événement demain. | Vous avez {count} événements demain",
|
"You have one event tomorrow.": "Vous n'avez pas d'événement demain | Vous avez un événement demain. | Vous avez {count} événements demain",
|
||||||
|
@ -233,6 +247,8 @@
|
||||||
"e.g. 10 Rue Jangot": "par exemple : 10 Rue Jangot",
|
"e.g. 10 Rue Jangot": "par exemple : 10 Rue Jangot",
|
||||||
"iCal Feed": "Flux iCal",
|
"iCal Feed": "Flux iCal",
|
||||||
"meditate a bit": "méditez un peu",
|
"meditate a bit": "méditez un peu",
|
||||||
|
"with another identity…": "avec une autre identité…",
|
||||||
|
"with {identity}": "avec {identity}",
|
||||||
"{actor}'s avatar": "Avatar de {actor}",
|
"{actor}'s avatar": "Avatar de {actor}",
|
||||||
"{approved} / {total} seats": "{approved} / {total} places",
|
"{approved} / {total} seats": "{approved} / {total} places",
|
||||||
"{count} participants": "Un⋅e participant⋅e|{count} participant⋅e⋅s",
|
"{count} participants": "Un⋅e participant⋅e|{count} participant⋅e⋅s",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { ICurrentUser } from '@/types/current-user.model';
|
import { ICurrentUser } from '@/types/current-user.model';
|
||||||
import { IEvent } from '@/types/event.model';
|
import { IEvent, IParticipant } from '@/types/event.model';
|
||||||
import { Actor, IActor } from '@/types/actor/actor.model';
|
import { Actor, IActor } from '@/types/actor/actor.model';
|
||||||
|
|
||||||
export interface IFeedToken {
|
export interface IFeedToken {
|
||||||
|
@ -11,11 +11,13 @@ export interface IFeedToken {
|
||||||
export interface IPerson extends IActor {
|
export interface IPerson extends IActor {
|
||||||
feedTokens: IFeedToken[];
|
feedTokens: IFeedToken[];
|
||||||
goingToEvents: IEvent[];
|
goingToEvents: IEvent[];
|
||||||
|
participations: IParticipant[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Person extends Actor implements IPerson {
|
export class Person extends Actor implements IPerson {
|
||||||
feedTokens: IFeedToken[] = [];
|
feedTokens: IFeedToken[] = [];
|
||||||
goingToEvents: IEvent[] = [];
|
goingToEvents: IEvent[] = [];
|
||||||
|
participations: IParticipant[] = [];
|
||||||
|
|
||||||
constructor(hash: IPerson | {} = {}) {
|
constructor(hash: IPerson | {} = {}) {
|
||||||
super(hash);
|
super(hash);
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="identity-picker">
|
|
||||||
<img class="image" v-if="currentIdentity.avatar" :src="currentIdentity.avatar.url" :alt="currentIdentity.avatar.alt"/> {{ currentIdentity.name || `@${currentIdentity.preferredUsername}` }}
|
|
||||||
<b-button type="is-text" @click="isComponentModalActive = true">
|
|
||||||
{{ $t('Change') }}
|
|
||||||
</b-button>
|
|
||||||
<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">
|
||||||
<p class="modal-card-title">{{ $t('Pick an identity') }}</p>
|
<p class="modal-card-title">{{ $t('Pick an identity') }}</p>
|
||||||
|
@ -22,8 +16,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
<slot name="footer"></slot>
|
||||||
</b-modal>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -40,22 +33,13 @@ import { IDENTITIES } from '@/graphql/actor';
|
||||||
})
|
})
|
||||||
export default class IdentityPicker extends Vue {
|
export default class IdentityPicker extends Vue {
|
||||||
@Prop() value!: IActor;
|
@Prop() value!: IActor;
|
||||||
isComponentModalActive: boolean = false;
|
|
||||||
identities: IActor[] = [];
|
identities: IActor[] = [];
|
||||||
currentIdentity: IActor = this.value;
|
currentIdentity: IActor = this.value;
|
||||||
|
|
||||||
changeCurrentIdentity(identity: IActor) {
|
changeCurrentIdentity(identity: IActor) {
|
||||||
this.currentIdentity = identity;
|
this.currentIdentity = identity;
|
||||||
this.$emit('input', identity);
|
this.$emit('input', identity);
|
||||||
this.isComponentModalActive = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
|
||||||
.identity-picker img.image {
|
|
||||||
display: inline;
|
|
||||||
height: 1.5em;
|
|
||||||
vertical-align: text-bottom;
|
|
||||||
}
|
|
||||||
</style>
|
|
39
js/src/views/Account/IdentityPickerWrapper.vue
Normal file
39
js/src/views/Account/IdentityPickerWrapper.vue
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<template>
|
||||||
|
<div class="identity-picker">
|
||||||
|
<img class="image" v-if="currentIdentity.avatar" :src="currentIdentity.avatar.url" :alt="currentIdentity.avatar.alt"/> {{ currentIdentity.name || `@${currentIdentity.preferredUsername}` }}
|
||||||
|
<b-button type="is-text" @click="isComponentModalActive = true">
|
||||||
|
{{ $t('Change') }}
|
||||||
|
</b-button>
|
||||||
|
<b-modal :active.sync="isComponentModalActive" has-modal-card>
|
||||||
|
<identity-picker :currentIdentity="currentIdentity" @input="relay" />
|
||||||
|
</b-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
|
import { IActor } from '@/types/actor';
|
||||||
|
import IdentityPicker from './IdentityPicker.vue';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: { IdentityPicker },
|
||||||
|
})
|
||||||
|
export default class IdentityPickerWrapper extends Vue {
|
||||||
|
@Prop() value!: IActor;
|
||||||
|
isComponentModalActive: boolean = false;
|
||||||
|
currentIdentity: IActor = this.value;
|
||||||
|
|
||||||
|
relay(identity: IActor) {
|
||||||
|
this.currentIdentity = identity;
|
||||||
|
this.$emit('input', identity);
|
||||||
|
this.isComponentModalActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
.identity-picker img.image {
|
||||||
|
display: inline;
|
||||||
|
height: 1.5em;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -92,6 +92,7 @@ export default class Register extends Vue {
|
||||||
domain: null,
|
domain: null,
|
||||||
feedTokens: [],
|
feedTokens: [],
|
||||||
goingToEvents: [],
|
goingToEvents: [],
|
||||||
|
participations: [],
|
||||||
};
|
};
|
||||||
errors: object = {};
|
errors: object = {};
|
||||||
validationSent: boolean = false;
|
validationSent: boolean = false;
|
||||||
|
|
|
@ -29,7 +29,7 @@ import {EventJoinOptions} from "@/types/event.model";
|
||||||
<address-auto-complete v-model="event.physicalAddress" />
|
<address-auto-complete v-model="event.physicalAddress" />
|
||||||
|
|
||||||
<b-field :label="$t('Organizer')">
|
<b-field :label="$t('Organizer')">
|
||||||
<identity-picker v-model="event.organizerActor"></identity-picker>
|
<identity-picker-wrapper v-model="event.organizerActor"></identity-picker-wrapper>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
@ -188,7 +188,6 @@ import {
|
||||||
EventModel,
|
EventModel,
|
||||||
EventStatus,
|
EventStatus,
|
||||||
EventVisibility,
|
EventVisibility,
|
||||||
EventVisibilityJoinOptions,
|
|
||||||
} from '@/types/event.model';
|
} from '@/types/event.model';
|
||||||
import { CURRENT_ACTOR_CLIENT } from '@/graphql/actor';
|
import { CURRENT_ACTOR_CLIENT } from '@/graphql/actor';
|
||||||
import { Person } from '@/types/actor';
|
import { Person } from '@/types/actor';
|
||||||
|
@ -200,10 +199,10 @@ import { TAGS } from '@/graphql/tags';
|
||||||
import { ITag } from '@/types/tag.model';
|
import { ITag } from '@/types/tag.model';
|
||||||
import AddressAutoComplete from '@/components/Event/AddressAutoComplete.vue';
|
import AddressAutoComplete from '@/components/Event/AddressAutoComplete.vue';
|
||||||
import { buildFileFromIPicture, buildFileVariable } from '@/utils/image';
|
import { buildFileFromIPicture, buildFileVariable } from '@/utils/image';
|
||||||
import IdentityPicker from '@/views/Account/IdentityPicker.vue';
|
import IdentityPickerWrapper from '@/views/Account/IdentityPickerWrapper.vue';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: { AddressAutoComplete, TagInput, DateTimePicker, PictureUpload, Editor, IdentityPicker },
|
components: { IdentityPickerWrapper, AddressAutoComplete, TagInput, DateTimePicker, PictureUpload, Editor },
|
||||||
apollo: {
|
apollo: {
|
||||||
currentActor: {
|
currentActor: {
|
||||||
query: CURRENT_ACTOR_CLIENT,
|
query: CURRENT_ACTOR_CLIENT,
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import {ParticipantRole} from "@/types/event.model";
|
||||||
|
import {ParticipantRole} from "@/types/event.model";
|
||||||
|
import {ParticipantRole} from "@/types/event.model";
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||||
|
@ -10,7 +13,7 @@
|
||||||
<img src="https://picsum.photos/600/200/">
|
<img src="https://picsum.photos/600/200/">
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
<section class="container">
|
<section>
|
||||||
<div class="title-and-participate-button">
|
<div class="title-and-participate-button">
|
||||||
<div class="title-wrapper">
|
<div class="title-wrapper">
|
||||||
<div class="date-component">
|
<div class="date-component">
|
||||||
|
@ -18,21 +21,21 @@
|
||||||
</div>
|
</div>
|
||||||
<h1 class="title">{{ event.title }}</h1>
|
<h1 class="title">{{ event.title }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<span v-if="event.participantStats.approved > 0 && !actorIsParticipant()">
|
<div class="has-text-right">
|
||||||
|
<small v-if="event.participantStats.approved > 0 && !actorIsParticipant">
|
||||||
{{ $tc('One person is going', event.participantStats.approved, {approved: event.participantStats.approved}) }}
|
{{ $tc('One person is going', event.participantStats.approved, {approved: event.participantStats.approved}) }}
|
||||||
</span>
|
</small>
|
||||||
<span v-else>
|
<small v-else>
|
||||||
{{ $tc('You and one other person are going to this event', event.participantStats.approved - 1, {approved: event.participantStats.approved - 1}) }}
|
{{ $tc('You and one other person are going to this event', event.participantStats.approved - 1, {approved: event.participantStats.approved - 1}) }}
|
||||||
</span>
|
</small>
|
||||||
<div v-if="!actorIsOrganizer()" class="participate-button has-text-centered">
|
<participation-button
|
||||||
<a v-if="!actorIsParticipant()" @click="isJoinModalActive = true" class="button is-large is-primary is-rounded">
|
v-if="currentActor.id && !actorIsOrganizer"
|
||||||
<b-icon icon="circle-outline"></b-icon>
|
:participation="participations[0]"
|
||||||
{{ $t('Join') }}
|
:current-actor="currentActor"
|
||||||
</a>
|
@joinEvent="joinEvent"
|
||||||
<a v-if="actorIsParticipant()" @click="confirmLeave()" class="button is-large is-primary is-rounded">
|
@joinModal="isJoinModalActive = true"
|
||||||
<b-icon icon="check-circle"></b-icon>
|
@confirmLeave="confirmLeave"
|
||||||
{{ $t('Leave') }}
|
/>
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="metadata columns">
|
<div class="metadata columns">
|
||||||
|
@ -60,8 +63,8 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="column sidebar">
|
<div class="column sidebar">
|
||||||
<div class="field has-addons">
|
<div class="field has-addons" v-if="currentActor.id">
|
||||||
<p class="control" v-if="actorIsOrganizer()">
|
<p class="control" v-if="actorIsOrganizer">
|
||||||
<router-link
|
<router-link
|
||||||
class="button"
|
class="button"
|
||||||
:to="{ name: 'EditEvent', params: {eventId: event.uuid}}"
|
:to="{ name: 'EditEvent', params: {eventId: event.uuid}}"
|
||||||
|
@ -69,7 +72,7 @@
|
||||||
{{ $t('Edit') }}
|
{{ $t('Edit') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</p>
|
</p>
|
||||||
<p class="control" v-if="actorIsOrganizer()">
|
<p class="control" v-if="actorIsOrganizer">
|
||||||
<a class="button is-danger" @click="openDeleteEventModalWrapper">
|
<a class="button is-danger" @click="openDeleteEventModalWrapper">
|
||||||
{{ $t('Delete') }}
|
{{ $t('Delete') }}
|
||||||
</a>
|
</a>
|
||||||
|
@ -133,26 +136,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section class="container">
|
|
||||||
<h3 class="title">{{ $t('Participants') }}</h3>
|
|
||||||
<router-link v-if="currentActor.id === event.organizerActor.id" :to="{ name: EventRouteName.PARTICIPATIONS, params: { eventId: event.uuid } }">
|
|
||||||
{{ $t('Manage participants') }}
|
|
||||||
</router-link>
|
|
||||||
<span v-if="event.participants.length === 0">{{ $t('No participants yet.') }}</span>
|
|
||||||
<div class="columns">
|
|
||||||
<div
|
|
||||||
class="column"
|
|
||||||
v-for="participant in event.participants"
|
|
||||||
:key="participant.id"
|
|
||||||
>
|
|
||||||
<figure class="image is-48x48">
|
|
||||||
<img v-if="!participant.actor.avatar.url" src="https://picsum.photos/48/48/" class="is-rounded">
|
|
||||||
<img v-else :src="participant.actor.avatar.url" class="is-rounded">
|
|
||||||
</figure>
|
|
||||||
<span>{{ participant.actor.preferredUsername }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section class="share">
|
<section class="share">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
|
@ -188,19 +171,35 @@
|
||||||
<report-modal :on-confirm="reportEvent" :title="$t('Report this event')" :outside-domain="event.organizerActor.domain" @close="$refs.reportModal.close()" />
|
<report-modal :on-confirm="reportEvent" :title="$t('Report this event')" :outside-domain="event.organizerActor.domain" @close="$refs.reportModal.close()" />
|
||||||
</b-modal>
|
</b-modal>
|
||||||
<b-modal :active.sync="isJoinModalActive" has-modal-card ref="participationModal">
|
<b-modal :active.sync="isJoinModalActive" has-modal-card ref="participationModal">
|
||||||
<participation-modal :on-confirm="joinEvent" :event="event" :defaultIdentity="currentActor" @close="$refs.participationModal.close()" />
|
<identity-picker v-model="identity">
|
||||||
|
<template v-slot:footer>
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
ref="cancelButton"
|
||||||
|
@click="isJoinModalActive = false">
|
||||||
|
{{ $t('Cancel') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button is-primary"
|
||||||
|
ref="confirmButton"
|
||||||
|
@click="joinEvent(identity)">
|
||||||
|
{{ $t('Confirm my particpation') }}
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</template>
|
||||||
|
</identity-picker>
|
||||||
</b-modal>
|
</b-modal>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { DELETE_EVENT, FETCH_EVENT, JOIN_EVENT, LEAVE_EVENT } from '@/graphql/event';
|
import { EVENT_PERSON_PARTICIPATION, FETCH_EVENT, JOIN_EVENT, LEAVE_EVENT } from '@/graphql/event';
|
||||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
import { Component, Prop } from 'vue-property-decorator';
|
||||||
import { CURRENT_ACTOR_CLIENT } from '@/graphql/actor';
|
import { CURRENT_ACTOR_CLIENT } from '@/graphql/actor';
|
||||||
import { EventVisibility, IEvent, IParticipant, ParticipantRole } from '@/types/event.model';
|
import { EventVisibility, IEvent, IParticipant, ParticipantRole } from '@/types/event.model';
|
||||||
import { IPerson } from '@/types/actor';
|
import { IPerson, Person } from '@/types/actor';
|
||||||
import { RouteName } from '@/router';
|
|
||||||
import { GRAPHQL_API_ENDPOINT } from '@/api/_entrypoint';
|
import { GRAPHQL_API_ENDPOINT } from '@/api/_entrypoint';
|
||||||
import DateCalendarIcon from '@/components/Event/DateCalendarIcon.vue';
|
import DateCalendarIcon from '@/components/Event/DateCalendarIcon.vue';
|
||||||
import BIcon from 'buefy/src/components/icon/Icon.vue';
|
import BIcon from 'buefy/src/components/icon/Icon.vue';
|
||||||
|
@ -208,11 +207,11 @@ import EventCard from '@/components/Event/EventCard.vue';
|
||||||
import EventFullDate from '@/components/Event/EventFullDate.vue';
|
import EventFullDate from '@/components/Event/EventFullDate.vue';
|
||||||
import ActorLink from '@/components/Account/ActorLink.vue';
|
import ActorLink from '@/components/Account/ActorLink.vue';
|
||||||
import ReportModal from '@/components/Report/ReportModal.vue';
|
import ReportModal from '@/components/Report/ReportModal.vue';
|
||||||
import ParticipationModal from '@/components/Event/ParticipationModal.vue';
|
|
||||||
import { IReport } from '@/types/report.model';
|
import { IReport } from '@/types/report.model';
|
||||||
import { CREATE_REPORT } from '@/graphql/report';
|
import { CREATE_REPORT } from '@/graphql/report';
|
||||||
import EventMixin from '@/mixins/event';
|
import EventMixin from '@/mixins/event';
|
||||||
import { EventRouteName } from '@/router/event';
|
import IdentityPicker from '@/views/Account/IdentityPicker.vue';
|
||||||
|
import ParticipationButton from '@/components/Event/ParticipationButton.vue';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
|
@ -222,7 +221,8 @@ import { EventRouteName } from '@/router/event';
|
||||||
BIcon,
|
BIcon,
|
||||||
DateCalendarIcon,
|
DateCalendarIcon,
|
||||||
ReportModal,
|
ReportModal,
|
||||||
ParticipationModal,
|
IdentityPicker,
|
||||||
|
ParticipationButton,
|
||||||
// tslint:disable:space-in-parens
|
// tslint:disable:space-in-parens
|
||||||
'map-leaflet': () => import(/* webpackChunkName: "map" */ '@/components/Map.vue'),
|
'map-leaflet': () => import(/* webpackChunkName: "map" */ '@/components/Map.vue'),
|
||||||
// tslint:enable
|
// tslint:enable
|
||||||
|
@ -233,13 +233,25 @@ import { EventRouteName } from '@/router/event';
|
||||||
variables() {
|
variables() {
|
||||||
return {
|
return {
|
||||||
uuid: this.uuid,
|
uuid: this.uuid,
|
||||||
roles: [ParticipantRole.CREATOR, ParticipantRole.MODERATOR, ParticipantRole.MODERATOR, ParticipantRole.PARTICIPANT].join(),
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentActor: {
|
currentActor: {
|
||||||
query: CURRENT_ACTOR_CLIENT,
|
query: CURRENT_ACTOR_CLIENT,
|
||||||
},
|
},
|
||||||
|
participations: {
|
||||||
|
query: EVENT_PERSON_PARTICIPATION,
|
||||||
|
variables() {
|
||||||
|
return {
|
||||||
|
eventId: this.event.id,
|
||||||
|
name: this.currentActor.preferredUsername,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
update: (data) => {
|
||||||
|
if (data && data.person) return data.person.participations;
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class Event extends EventMixin {
|
export default class Event extends EventMixin {
|
||||||
|
@ -247,13 +259,17 @@ export default class Event extends EventMixin {
|
||||||
|
|
||||||
event!: IEvent;
|
event!: IEvent;
|
||||||
currentActor!: IPerson;
|
currentActor!: IPerson;
|
||||||
validationSent: boolean = false;
|
identity: IPerson = new Person();
|
||||||
|
participations: IParticipant[] = [];
|
||||||
showMap: boolean = false;
|
showMap: boolean = false;
|
||||||
isReportModalActive: boolean = false;
|
isReportModalActive: boolean = false;
|
||||||
isJoinModalActive: boolean = false;
|
isJoinModalActive: boolean = false;
|
||||||
|
|
||||||
EventVisibility = EventVisibility;
|
EventVisibility = EventVisibility;
|
||||||
EventRouteName = EventRouteName;
|
|
||||||
|
mounted() {
|
||||||
|
this.identity = this.currentActor;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the event, then redirect to home.
|
* Delete the event, then redirect to home.
|
||||||
|
@ -298,6 +314,24 @@ export default class Event extends EventMixin {
|
||||||
},
|
},
|
||||||
update: (store, { data }) => {
|
update: (store, { data }) => {
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
|
|
||||||
|
const participationCachedData = store.readQuery<{ person: IPerson }>({
|
||||||
|
query: EVENT_PERSON_PARTICIPATION,
|
||||||
|
variables: { eventId: this.event.id, name: identity.preferredUsername },
|
||||||
|
});
|
||||||
|
if (participationCachedData == null) return;
|
||||||
|
const { person } = participationCachedData;
|
||||||
|
if (person === null) {
|
||||||
|
console.error('Cannot update participation cache, because of null value.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
person.participations.push(data.joinEvent);
|
||||||
|
store.writeQuery({
|
||||||
|
query: EVENT_PERSON_PARTICIPATION,
|
||||||
|
variables: { eventId: this.event.id, name: identity.preferredUsername },
|
||||||
|
data: { person },
|
||||||
|
});
|
||||||
|
|
||||||
const cachedData = store.readQuery<{ event: IEvent }>({ query: FETCH_EVENT, variables: { uuid: this.event.uuid } });
|
const cachedData = store.readQuery<{ event: IEvent }>({ query: FETCH_EVENT, variables: { uuid: this.event.uuid } });
|
||||||
if (cachedData == null) return;
|
if (cachedData == null) return;
|
||||||
const { event } = cachedData;
|
const { event } = cachedData;
|
||||||
|
@ -306,9 +340,13 @@ export default class Event extends EventMixin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
event.participants = event.participants.concat([data.joinEvent]);
|
if (data.joinEvent.role === ParticipantRole.NOT_APPROVED) {
|
||||||
|
event.participantStats.unapproved = event.participantStats.unapproved + 1;
|
||||||
|
} else {
|
||||||
|
event.participantStats.approved = event.participantStats.approved + 1;
|
||||||
|
}
|
||||||
|
|
||||||
store.writeQuery({ query: FETCH_EVENT, data: { event } });
|
store.writeQuery({ query: FETCH_EVENT, variables: { uuid: this.uuid }, data: { event } });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -338,19 +376,38 @@ export default class Event extends EventMixin {
|
||||||
},
|
},
|
||||||
update: (store, { data }) => {
|
update: (store, { data }) => {
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
const cachedData = store.readQuery<{ event: IEvent }>({ query: FETCH_EVENT, variables: { uuid: this.event.uuid } });
|
|
||||||
if (cachedData == null) return;
|
const participationCachedData = store.readQuery<{ person: IPerson }>({
|
||||||
const { event } = cachedData;
|
query: EVENT_PERSON_PARTICIPATION,
|
||||||
if (event === null) {
|
variables: { eventId: this.event.id, name: this.currentActor.preferredUsername },
|
||||||
console.error('Cannot update event participant cache, because of null value.');
|
});
|
||||||
|
if (participationCachedData == null) return;
|
||||||
|
const { person } = participationCachedData;
|
||||||
|
if (person === null) {
|
||||||
|
console.error('Cannot update participation cache, because of null value.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const participation = person.participations[0];
|
||||||
|
person.participations = [];
|
||||||
|
store.writeQuery({
|
||||||
|
query: EVENT_PERSON_PARTICIPATION,
|
||||||
|
variables: { eventId: this.event.id, name: this.currentActor.preferredUsername },
|
||||||
|
data: { person },
|
||||||
|
});
|
||||||
|
|
||||||
event.participants = event.participants
|
const eventCachedData = store.readQuery<{ event: IEvent }>({ query: FETCH_EVENT, variables: { uuid: this.event.uuid } });
|
||||||
.filter(p => p.actor.id !== data.leaveEvent.actor.id);
|
if (eventCachedData == null) return;
|
||||||
|
const { event } = eventCachedData;
|
||||||
|
if (event === null) {
|
||||||
|
console.error('Cannot update event cache, because of null value.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (participation.role === ParticipantRole.NOT_APPROVED) {
|
||||||
|
event.participantStats.unapproved = event.participantStats.unapproved - 1;
|
||||||
|
} else {
|
||||||
event.participantStats.approved = event.participantStats.approved - 1;
|
event.participantStats.approved = event.participantStats.approved - 1;
|
||||||
|
}
|
||||||
store.writeQuery({ query: FETCH_EVENT, data: { event } });
|
store.writeQuery({ query: FETCH_EVENT, variables: { uuid: this.uuid }, data: { event } });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -369,17 +426,14 @@ export default class Event extends EventMixin {
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
actorIsParticipant() {
|
get actorIsParticipant() {
|
||||||
if (this.actorIsOrganizer()) return true;
|
if (this.actorIsOrganizer) return true;
|
||||||
|
|
||||||
return this.currentActor &&
|
return this.participations.length > 0 && this.participations[0].role === ParticipantRole.PARTICIPANT;
|
||||||
this.event.participants
|
|
||||||
.some(participant => participant.actor.id === this.currentActor.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
actorIsOrganizer() {
|
get actorIsOrganizer() {
|
||||||
return this.currentActor && this.event.organizerActor &&
|
return this.participations.length > 0 && this.participations[0].role === ParticipantRole.CREATOR;
|
||||||
this.currentActor.id === this.event.organizerActor.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get twitterShareUrl(): string {
|
get twitterShareUrl(): string {
|
||||||
|
|
|
@ -68,6 +68,7 @@ import { IPerson } from '@/types/actor';
|
||||||
page: 1,
|
page: 1,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
roles: [ParticipantRole.PARTICIPANT].join(),
|
roles: [ParticipantRole.PARTICIPANT].join(),
|
||||||
|
actorId: this.currentActor.id,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -79,6 +80,7 @@ import { IPerson } from '@/types/actor';
|
||||||
page: 1,
|
page: 1,
|
||||||
limit: 20,
|
limit: 20,
|
||||||
roles: [ParticipantRole.CREATOR].join(),
|
roles: [ParticipantRole.CREATOR].join(),
|
||||||
|
actorId: this.currentActor.id,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
update: data => data.event.participants.map(participation => new Participant(participation)),
|
update: data => data.event.participants.map(participation => new Participant(participation)),
|
||||||
|
@ -91,6 +93,7 @@ import { IPerson } from '@/types/actor';
|
||||||
page: 1,
|
page: 1,
|
||||||
limit: 20,
|
limit: 20,
|
||||||
roles: [ParticipantRole.NOT_APPROVED].join(),
|
roles: [ParticipantRole.NOT_APPROVED].join(),
|
||||||
|
actorId: this.currentActor.id,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
update: data => data.event.participants.map(participation => new Participant(participation)),
|
update: data => data.event.participants.map(participation => new Participant(participation)),
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
<h3 class="title">
|
<h3 class="title">
|
||||||
{{ $t("Upcoming") }}
|
{{ $t("Upcoming") }}
|
||||||
</h3>
|
</h3>
|
||||||
<pre>{{ Array.from(goingToEvents.entries()) }}</pre>
|
|
||||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||||
<div v-for="row in goingToEvents" class="upcoming-events">
|
<div v-for="row in goingToEvents" class="upcoming-events">
|
||||||
<span class="date-component-container" v-if="isInLessThanSevenDays(row[0])">
|
<span class="date-component-container" v-if="isInLessThanSevenDays(row[0])">
|
||||||
|
@ -53,13 +52,12 @@
|
||||||
{{ $tc('You have one event in {days} days.', row[1].length, {count: row[1].length, days: calculateDiffDays(row[0])}) }}
|
{{ $tc('You have one event in {days} days.', row[1].length, {count: row[1].length, days: calculateDiffDays(row[0])}) }}
|
||||||
</h3>
|
</h3>
|
||||||
</span>
|
</span>
|
||||||
<div class="level">
|
<div>
|
||||||
<EventListCard
|
<EventListCard
|
||||||
v-for="participation in row[1]"
|
v-for="participation in row[1]"
|
||||||
v-if="isInLessThanSevenDays(row[0])"
|
v-if="isInLessThanSevenDays(row[0])"
|
||||||
:key="participation[1].event.uuid"
|
:key="participation[1].event.uuid"
|
||||||
:participation="participation[1]"
|
:participation="participation[1]"
|
||||||
class="level-item"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,12 +70,11 @@
|
||||||
{{ $t("Last week") }}
|
{{ $t("Last week") }}
|
||||||
</h3>
|
</h3>
|
||||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||||
<div class="level">
|
<div>
|
||||||
<EventListCard
|
<EventListCard
|
||||||
v-for="participation in lastWeekEvents"
|
v-for="participation in lastWeekEvents"
|
||||||
:key="participation.id"
|
:key="participation.id"
|
||||||
:participation="participation"
|
:participation="participation"
|
||||||
class="level-item"
|
|
||||||
:options="{ hideDate: false }"
|
:options="{ hideDate: false }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -295,12 +292,6 @@ export default class Home extends Vue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.upcoming-events {
|
|
||||||
.level {
|
|
||||||
margin-left: 4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
section.container {
|
section.container {
|
||||||
margin: auto auto 3rem;
|
margin: auto auto 3rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,7 +169,7 @@ export default class Report extends Vue {
|
||||||
|
|
||||||
report.notes = report.notes.concat([note]);
|
report.notes = report.notes.concat([note]);
|
||||||
|
|
||||||
store.writeQuery({ query: REPORT, data: { report } });
|
store.writeQuery({ query: REPORT, variables: { id: this.report.id }, data: { report } });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ export default class Report extends Vue {
|
||||||
const updatedReport = data.updateReportStatus;
|
const updatedReport = data.updateReportStatus;
|
||||||
report.status = updatedReport.status;
|
report.status = updatedReport.status;
|
||||||
|
|
||||||
store.writeQuery({ query: REPORT, data: { report } });
|
store.writeQuery({ query: REPORT, variables: { id: this.report.id }, data: { report } });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -593,12 +593,12 @@ defmodule Mobilizon.Events do
|
||||||
@spec list_participants_for_event(String.t(), list(atom()), integer | nil, integer | nil) ::
|
@spec list_participants_for_event(String.t(), list(atom()), integer | nil, integer | nil) ::
|
||||||
[Participant.t()]
|
[Participant.t()]
|
||||||
def list_participants_for_event(
|
def list_participants_for_event(
|
||||||
uuid,
|
id,
|
||||||
roles \\ @default_participant_roles,
|
roles \\ @default_participant_roles,
|
||||||
page \\ nil,
|
page \\ nil,
|
||||||
limit \\ nil
|
limit \\ nil
|
||||||
) do
|
) do
|
||||||
uuid
|
id
|
||||||
|> list_participants_for_event_query()
|
|> list_participants_for_event_query()
|
||||||
|> filter_role(roles)
|
|> filter_role(roles)
|
||||||
|> Page.paginate(page, limit)
|
|> Page.paginate(page, limit)
|
||||||
|
@ -688,7 +688,7 @@ defmodule Mobilizon.Events do
|
||||||
Returns the list of participations for an actor.
|
Returns the list of participations for an actor.
|
||||||
"""
|
"""
|
||||||
@spec list_event_participations_for_actor(Actor.t(), integer | nil, integer | nil) ::
|
@spec list_event_participations_for_actor(Actor.t(), integer | nil, integer | nil) ::
|
||||||
[Event.t()]
|
[Participant.t()]
|
||||||
def list_event_participations_for_actor(%Actor{id: actor_id}, page \\ nil, limit \\ nil) do
|
def list_event_participations_for_actor(%Actor{id: actor_id}, page \\ nil, limit \\ nil) do
|
||||||
actor_id
|
actor_id
|
||||||
|> event_participations_for_actor_query()
|
|> event_participations_for_actor_query()
|
||||||
|
@ -1241,13 +1241,11 @@ defmodule Mobilizon.Events do
|
||||||
@spec event_participations_for_actor_query(integer) :: Ecto.Query.t()
|
@spec event_participations_for_actor_query(integer) :: Ecto.Query.t()
|
||||||
def event_participations_for_actor_query(actor_id) do
|
def event_participations_for_actor_query(actor_id) do
|
||||||
from(
|
from(
|
||||||
e in Event,
|
p in Participant,
|
||||||
join: p in Participant,
|
join: e in Event,
|
||||||
join: a in Actor,
|
|
||||||
on: p.actor_id == a.id,
|
|
||||||
on: p.event_id == e.id,
|
on: p.event_id == e.id,
|
||||||
where: a.id == ^actor_id and p.role != ^:not_approved,
|
where: p.actor_id == ^actor_id and p.role != ^:not_approved,
|
||||||
preload: [:picture, :tags]
|
preload: [:event]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1281,12 +1279,12 @@ defmodule Mobilizon.Events do
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec list_participants_for_event_query(String.t()) :: Ecto.Query.t()
|
@spec list_participants_for_event_query(String.t()) :: Ecto.Query.t()
|
||||||
defp list_participants_for_event_query(event_uuid) do
|
defp list_participants_for_event_query(event_id) do
|
||||||
from(
|
from(
|
||||||
p in Participant,
|
p in Participant,
|
||||||
join: e in Event,
|
join: e in Event,
|
||||||
on: p.event_id == e.id,
|
on: p.event_id == e.id,
|
||||||
where: e.uuid == ^event_uuid,
|
where: e.id == ^event_id,
|
||||||
preload: [:actor]
|
preload: [:actor]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,21 +38,18 @@ defmodule MobilizonWeb.Resolvers.Event do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
|
||||||
List participant for event (separate request)
|
|
||||||
"""
|
|
||||||
def list_participants_for_event(_parent, %{uuid: uuid, page: page, limit: limit}, _resolution) do
|
|
||||||
{:ok, Mobilizon.Events.list_participants_for_event(uuid, [], page, limit)}
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
List participants for event (through an event request)
|
List participants for event (through an event request)
|
||||||
"""
|
"""
|
||||||
def list_participants_for_event(
|
def list_participants_for_event(
|
||||||
%Event{uuid: uuid},
|
%Event{id: event_id},
|
||||||
%{page: page, limit: limit, roles: roles},
|
%{page: page, limit: limit, roles: roles, actor_id: actor_id},
|
||||||
_resolution
|
%{context: %{current_user: %User{} = user}} = _resolution
|
||||||
) do
|
) do
|
||||||
|
with {:is_owned, %Actor{} = _actor} <- User.owns_actor(user, actor_id),
|
||||||
|
# Check that moderator has right
|
||||||
|
{:actor_approve_permission, true} <-
|
||||||
|
{:actor_approve_permission, Mobilizon.Events.moderator_for_event?(event_id, actor_id)} do
|
||||||
roles =
|
roles =
|
||||||
case roles do
|
case roles do
|
||||||
"" ->
|
"" ->
|
||||||
|
@ -65,7 +62,18 @@ defmodule MobilizonWeb.Resolvers.Event do
|
||||||
|> Enum.map(&String.to_existing_atom/1)
|
|> Enum.map(&String.to_existing_atom/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, Mobilizon.Events.list_participants_for_event(uuid, roles, page, limit)}
|
{:ok, Mobilizon.Events.list_participants_for_event(event_id, roles, page, limit)}
|
||||||
|
else
|
||||||
|
{:is_owned, nil} ->
|
||||||
|
{:error, "Moderator Actor ID is not owned by authenticated user"}
|
||||||
|
|
||||||
|
{:actor_approve_permission, _} ->
|
||||||
|
{:error, "Provided moderator actor ID doesn't have permission on this event"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_participants_for_event(_, _args, _resolution) do
|
||||||
|
{:ok, []}
|
||||||
end
|
end
|
||||||
|
|
||||||
def stats_participants_for_event(%Event{id: id}, _args, _resolution) do
|
def stats_participants_for_event(%Event{id: id}, _args, _resolution) do
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule MobilizonWeb.Resolvers.Person do
|
||||||
alias Mobilizon.Actors
|
alias Mobilizon.Actors
|
||||||
alias Mobilizon.Actors.Actor
|
alias Mobilizon.Actors.Actor
|
||||||
alias Mobilizon.Events
|
alias Mobilizon.Events
|
||||||
|
alias Mobilizon.Events.Participant
|
||||||
alias Mobilizon.Service.ActivityPub
|
alias Mobilizon.Service.ActivityPub
|
||||||
alias Mobilizon.Users
|
alias Mobilizon.Users
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
|
@ -173,27 +174,33 @@ defmodule MobilizonWeb.Resolvers.Person do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the list of events this person is going to
|
Returns the participation for a specific event
|
||||||
"""
|
"""
|
||||||
def person_going_to_events(%Actor{id: actor_id}, _args, %{context: %{current_user: user}}) do
|
def person_participations(%Actor{id: actor_id}, %{event_id: event_id}, %{
|
||||||
with {:is_owned, %Actor{} = actor} <- User.owns_actor(user, actor_id),
|
context: %{current_user: user}
|
||||||
events <- Events.list_event_participations_for_actor(actor) do
|
}) do
|
||||||
{:ok, events}
|
with {:is_owned, %Actor{} = _actor} <- User.owns_actor(user, actor_id),
|
||||||
|
{:no_participant, {:ok, %Participant{} = participant}} <-
|
||||||
|
{:no_participant, Events.get_participant(event_id, actor_id)} do
|
||||||
|
{:ok, [participant]}
|
||||||
else
|
else
|
||||||
{:is_owned, nil} ->
|
{:is_owned, nil} ->
|
||||||
{:error, "Actor id is not owned by authenticated user"}
|
{:error, "Actor id is not owned by authenticated user"}
|
||||||
|
|
||||||
|
{:no_participant, _} ->
|
||||||
|
{:ok, []}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the list of events this person is going to
|
Returns the list of events this person is going to
|
||||||
"""
|
"""
|
||||||
def person_going_to_events(_parent, %{}, %{context: %{current_user: user}}) do
|
def person_participations(%Actor{id: actor_id}, _args, %{context: %{current_user: user}}) do
|
||||||
with %Actor{} = actor <- Users.get_actor_for_user(user),
|
with {:is_owned, %Actor{} = actor} <- User.owns_actor(user, actor_id),
|
||||||
events <- Events.list_event_participations_for_actor(actor) do
|
participations <- Events.list_event_participations_for_actor(actor) do
|
||||||
{:ok, events}
|
{:ok, participations}
|
||||||
else
|
else
|
||||||
{:is_owned, false} ->
|
{:is_owned, nil} ->
|
||||||
{:error, "Actor id is not owned by authenticated user"}
|
{:error, "Actor id is not owned by authenticated user"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -225,10 +225,11 @@ defmodule MobilizonWeb.Resolvers.User do
|
||||||
@doc """
|
@doc """
|
||||||
Returns the list of events for all of this user's identities are going to
|
Returns the list of events for all of this user's identities are going to
|
||||||
"""
|
"""
|
||||||
def user_participations(_parent, args, %{
|
def user_participations(%User{id: user_id}, args, %{
|
||||||
context: %{current_user: %User{id: user_id}}
|
context: %{current_user: %User{id: logged_user_id}}
|
||||||
}) do
|
}) do
|
||||||
with participations <-
|
with true <- user_id == logged_user_id,
|
||||||
|
participations <-
|
||||||
Events.list_participations_for_user(
|
Events.list_participations_for_user(
|
||||||
user_id,
|
user_id,
|
||||||
Map.get(args, :after_datetime),
|
Map.get(args, :after_datetime),
|
||||||
|
|
|
@ -116,7 +116,6 @@ defmodule MobilizonWeb.Schema do
|
||||||
import_fields(:person_queries)
|
import_fields(:person_queries)
|
||||||
import_fields(:group_queries)
|
import_fields(:group_queries)
|
||||||
import_fields(:event_queries)
|
import_fields(:event_queries)
|
||||||
import_fields(:participant_queries)
|
|
||||||
import_fields(:tag_queries)
|
import_fields(:tag_queries)
|
||||||
import_fields(:address_queries)
|
import_fields(:address_queries)
|
||||||
import_fields(:config_queries)
|
import_fields(:config_queries)
|
||||||
|
|
|
@ -56,8 +56,11 @@ defmodule MobilizonWeb.Schema.Actors.PersonType do
|
||||||
)
|
)
|
||||||
|
|
||||||
@desc "The list of events this person goes to"
|
@desc "The list of events this person goes to"
|
||||||
field :going_to_events, list_of(:event) do
|
field(:participations, list_of(:participant),
|
||||||
resolve(&Person.person_going_to_events/3)
|
description: "The list of events this person goes to"
|
||||||
|
) do
|
||||||
|
arg(:event_id, :id)
|
||||||
|
resolve(&Person.person_participations/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ defmodule MobilizonWeb.Schema.EventType do
|
||||||
arg(:page, :integer, default_value: 1)
|
arg(:page, :integer, default_value: 1)
|
||||||
arg(:limit, :integer, default_value: 10)
|
arg(:limit, :integer, default_value: 10)
|
||||||
arg(:roles, :string, default_value: "")
|
arg(:roles, :string, default_value: "")
|
||||||
|
arg(:actor_id, :id)
|
||||||
resolve(&Event.list_participants_for_event/3)
|
resolve(&Event.list_participants_for_event/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -44,16 +44,6 @@ defmodule MobilizonWeb.Schema.Events.ParticipantType do
|
||||||
field(:actor, :deleted_object)
|
field(:actor, :deleted_object)
|
||||||
end
|
end
|
||||||
|
|
||||||
object :participant_queries do
|
|
||||||
@desc "Get all participants for an event uuid"
|
|
||||||
field :participants, list_of(:participant) do
|
|
||||||
arg(:uuid, non_null(:uuid))
|
|
||||||
arg(:page, :integer, default_value: 1)
|
|
||||||
arg(:limit, :integer, default_value: 10)
|
|
||||||
resolve(&Resolvers.Event.list_participants_for_event/3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
object :participant_mutations do
|
object :participant_mutations do
|
||||||
@desc "Join an event"
|
@desc "Join an event"
|
||||||
field :join_event, :participant do
|
field :join_event, :participant do
|
||||||
|
|
|
@ -47,7 +47,7 @@ defmodule MobilizonWeb.Schema.UserType do
|
||||||
field(:role, :user_role, description: "The role for the user")
|
field(:role, :user_role, description: "The role for the user")
|
||||||
|
|
||||||
field(:participations, list_of(:participant),
|
field(:participations, list_of(:participant),
|
||||||
description: "The list of events this person goes to"
|
description: "The list of events this user goes to"
|
||||||
) do
|
) do
|
||||||
arg(:after_datetime, :datetime)
|
arg(:after_datetime, :datetime)
|
||||||
arg(:before_datetime, :datetime)
|
arg(:before_datetime, :datetime)
|
||||||
|
|
|
@ -128,14 +128,18 @@ defmodule Mobilizon.Service.Export.Feed do
|
||||||
%FeedToken{actor: actor, user: %User{} = user} <- Events.get_feed_token(token) do
|
%FeedToken{actor: actor, user: %User{} = user} <- Events.get_feed_token(token) do
|
||||||
case actor do
|
case actor do
|
||||||
%Actor{} = actor ->
|
%Actor{} = actor ->
|
||||||
events = fetch_identity_going_to_events(actor)
|
events = actor |> fetch_identity_participations() |> participations_to_events()
|
||||||
{:ok, build_actor_feed(actor, events, false)}
|
{:ok, build_actor_feed(actor, events, false)}
|
||||||
|
|
||||||
nil ->
|
nil ->
|
||||||
with actors <- Users.get_actors_for_user(user),
|
with actors <- Users.get_actors_for_user(user),
|
||||||
events <-
|
events <-
|
||||||
actors
|
actors
|
||||||
|> Enum.map(&Events.list_event_participations_for_actor/1)
|
|> Enum.map(fn actor ->
|
||||||
|
actor
|
||||||
|
|> Events.list_event_participations_for_actor()
|
||||||
|
|> participations_to_events()
|
||||||
|
end)
|
||||||
|> Enum.concat() do
|
|> Enum.concat() do
|
||||||
{:ok, build_user_feed(events, user, token)}
|
{:ok, build_user_feed(events, user, token)}
|
||||||
end
|
end
|
||||||
|
@ -143,12 +147,18 @@ defmodule Mobilizon.Service.Export.Feed do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fetch_identity_going_to_events(%Actor{} = actor) do
|
defp fetch_identity_participations(%Actor{} = actor) do
|
||||||
with events <- Events.list_event_participations_for_actor(actor) do
|
with events <- Events.list_event_participations_for_actor(actor) do
|
||||||
events
|
events
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp participations_to_events(participations) do
|
||||||
|
participations
|
||||||
|
|> Enum.map(& &1.event_id)
|
||||||
|
|> Enum.map(&Events.get_event_with_preload!/1)
|
||||||
|
end
|
||||||
|
|
||||||
# Build an atom feed from actor and it's public events
|
# Build an atom feed from actor and it's public events
|
||||||
@spec build_user_feed(list(), User.t(), String.t()) :: String.t()
|
@spec build_user_feed(list(), User.t(), String.t()) :: String.t()
|
||||||
defp build_user_feed(events, %User{email: email}, token) do
|
defp build_user_feed(events, %User{email: email}, token) do
|
||||||
|
|
|
@ -33,7 +33,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
|
||||||
dtend: event.ends_on,
|
dtend: event.ends_on,
|
||||||
description: event.description,
|
description: event.description,
|
||||||
uid: event.uuid,
|
uid: event.uuid,
|
||||||
categories: [event.category] ++ (event.tags |> Enum.map(& &1.slug))
|
categories: event.tags |> Enum.map(& &1.slug)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,7 +52,8 @@ defmodule Mobilizon.Service.Export.ICalendar do
|
||||||
|
|
||||||
@spec export_private_actor(Actor.t()) :: String.t()
|
@spec export_private_actor(Actor.t()) :: String.t()
|
||||||
def export_private_actor(%Actor{} = actor) do
|
def export_private_actor(%Actor{} = actor) do
|
||||||
with events <- Events.list_event_participations_for_actor(actor) do
|
with events <-
|
||||||
|
actor |> Events.list_event_participations_for_actor() |> participations_to_events() do
|
||||||
{:ok, %ICalendar{events: events |> Enum.map(&do_export_event/1)} |> ICalendar.to_ics()}
|
{:ok, %ICalendar{events: events |> Enum.map(&do_export_event/1)} |> ICalendar.to_ics()}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -107,7 +108,11 @@ defmodule Mobilizon.Service.Export.ICalendar do
|
||||||
with actors <- Users.get_actors_for_user(user),
|
with actors <- Users.get_actors_for_user(user),
|
||||||
events <-
|
events <-
|
||||||
actors
|
actors
|
||||||
|> Enum.map(&Events.list_event_participations_for_actor/1)
|
|> Enum.map(fn actor ->
|
||||||
|
actor
|
||||||
|
|> Events.list_event_participations_for_actor()
|
||||||
|
|> participations_to_events()
|
||||||
|
end)
|
||||||
|> Enum.concat() do
|
|> Enum.concat() do
|
||||||
{:ok,
|
{:ok,
|
||||||
%ICalendar{events: events |> Enum.map(&do_export_event/1)} |> ICalendar.to_ics()}
|
%ICalendar{events: events |> Enum.map(&do_export_event/1)} |> ICalendar.to_ics()}
|
||||||
|
@ -115,4 +120,10 @@ defmodule Mobilizon.Service.Export.ICalendar do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp participations_to_events(participations) do
|
||||||
|
participations
|
||||||
|
|> Enum.map(& &1.event_id)
|
||||||
|
|> Enum.map(&Events.get_event_with_preload!/1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# source: http://localhost:4000/api
|
# source: http://localhost:4000/api
|
||||||
# timestamp: Tue Sep 24 2019 18:20:05 GMT+0200 (GMT+02:00)
|
# timestamp: Wed Sep 25 2019 16:41:05 GMT+0200 (GMT+02:00)
|
||||||
|
|
||||||
schema {
|
schema {
|
||||||
query: RootQueryType
|
query: RootQueryType
|
||||||
|
@ -287,7 +287,7 @@ type Event implements ActionLogObject {
|
||||||
participantStats: ParticipantStats
|
participantStats: ParticipantStats
|
||||||
|
|
||||||
"""The event's participants"""
|
"""The event's participants"""
|
||||||
participants(limit: Int = 10, page: Int = 1, roles: String = ""): [Participant]
|
participants(actorId: ID, limit: Int = 10, page: Int = 1, roles: String = ""): [Participant]
|
||||||
|
|
||||||
"""Phone address for the event"""
|
"""Phone address for the event"""
|
||||||
phoneAddress: String
|
phoneAddress: String
|
||||||
|
@ -710,9 +710,6 @@ type Person implements Actor {
|
||||||
"""Number of actors following this actor"""
|
"""Number of actors following this actor"""
|
||||||
followingCount: Int
|
followingCount: Int
|
||||||
|
|
||||||
"""The list of events this person goes to"""
|
|
||||||
goingToEvents: [Event]
|
|
||||||
|
|
||||||
"""Internal ID for this person"""
|
"""Internal ID for this person"""
|
||||||
id: ID
|
id: ID
|
||||||
|
|
||||||
|
@ -734,6 +731,9 @@ type Person implements Actor {
|
||||||
"""A list of the events this actor has organized"""
|
"""A list of the events this actor has organized"""
|
||||||
organizedEvents: [Event]
|
organizedEvents: [Event]
|
||||||
|
|
||||||
|
"""The list of events this person goes to"""
|
||||||
|
participations(eventId: ID): [Participant]
|
||||||
|
|
||||||
"""The actor's preferred username"""
|
"""The actor's preferred username"""
|
||||||
preferredUsername: String
|
preferredUsername: String
|
||||||
|
|
||||||
|
@ -1128,9 +1128,6 @@ type RootQueryType {
|
||||||
"""Get the current user"""
|
"""Get the current user"""
|
||||||
loggedUser: User
|
loggedUser: User
|
||||||
|
|
||||||
"""Get all participants for an event uuid"""
|
|
||||||
participants(limit: Int = 10, page: Int = 1, uuid: UUID!): [Participant]
|
|
||||||
|
|
||||||
"""Get a person by it's preferred username"""
|
"""Get a person by it's preferred username"""
|
||||||
person(preferredUsername: String!): Person
|
person(preferredUsername: String!): Person
|
||||||
|
|
||||||
|
@ -1223,7 +1220,7 @@ type User {
|
||||||
"""The user's ID"""
|
"""The user's ID"""
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
"""The list of events this person goes to"""
|
"""The list of events this user goes to"""
|
||||||
participations(afterDatetime: DateTime, beforeDatetime: DateTime, limit: Int = 10, page: Int = 1): [Participant]
|
participations(afterDatetime: DateTime, beforeDatetime: DateTime, limit: Int = 10, page: Int = 1): [Participant]
|
||||||
|
|
||||||
"""The user's list of profiles (identities)"""
|
"""The user's list of profiles (identities)"""
|
||||||
|
|
|
@ -99,8 +99,8 @@ defmodule Mobilizon.EventsTest do
|
||||||
assert event.ends_on == DateTime.from_naive!(~N[2010-04-17 14:00:00Z], "Etc/UTC")
|
assert event.ends_on == DateTime.from_naive!(~N[2010-04-17 14:00:00Z], "Etc/UTC")
|
||||||
assert event.title == "some title"
|
assert event.title == "some title"
|
||||||
|
|
||||||
assert hd(Events.list_participants_for_event(event.uuid)).actor.id == actor.id
|
assert hd(Events.list_participants_for_event(event.id)).actor.id == actor.id
|
||||||
assert hd(Events.list_participants_for_event(event.uuid)).role == :creator
|
assert hd(Events.list_participants_for_event(event.id)).role == :creator
|
||||||
end
|
end
|
||||||
|
|
||||||
test "create_event/1 with invalid data returns error changeset" do
|
test "create_event/1 with invalid data returns error changeset" do
|
||||||
|
|
|
@ -784,7 +784,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
|
||||||
assert :error == Transmogrifier.handle_incoming(reject_data)
|
assert :error == Transmogrifier.handle_incoming(reject_data)
|
||||||
|
|
||||||
# Organiser is not present since we use factories directly
|
# Organiser is not present since we use factories directly
|
||||||
assert Events.list_participants_for_event(event.uuid) |> Enum.map(& &1.id) ==
|
assert Events.list_participants_for_event(event.id) |> Enum.map(& &1.id) ==
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -812,7 +812,7 @@ defmodule Mobilizon.Service.ActivityPub.TransmogrifierTest do
|
||||||
assert activity.data["actor"] == participant_url
|
assert activity.data["actor"] == participant_url
|
||||||
|
|
||||||
# The only participant left is the organizer
|
# The only participant left is the organizer
|
||||||
assert Events.list_participants_for_event(event.uuid) |> Enum.map(& &1.id) == [
|
assert Events.list_participants_for_event(event.id) |> Enum.map(& &1.id) == [
|
||||||
organizer_participation.id
|
organizer_participation.id
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -106,8 +106,8 @@ defmodule MobilizonWeb.FeedControllerTest do
|
||||||
assert entry.summary in [event1.title, event2.title]
|
assert entry.summary in [event1.title, event2.title]
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert entry1.categories == [event1.category, tag1.slug]
|
assert entry1.categories == [tag1.slug]
|
||||||
assert entry2.categories == [event2.category, tag1.slug, tag2.slug]
|
assert entry2.categories == [tag1.slug, tag2.slug]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns a 404 page for the actor's public events iCal feed with an actor not publicly visible",
|
test "it returns a 404 page for the actor's public events iCal feed with an actor not publicly visible",
|
||||||
|
@ -174,7 +174,7 @@ defmodule MobilizonWeb.FeedControllerTest do
|
||||||
|
|
||||||
assert entry1.summary == event1.title
|
assert entry1.summary == event1.title
|
||||||
|
|
||||||
assert entry1.categories == [event1.category, tag1.slug, tag2.slug]
|
assert entry1.categories == [tag1.slug, tag2.slug]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -311,6 +311,7 @@ defmodule MobilizonWeb.FeedControllerTest do
|
||||||
|
|
||||||
[entry1] = ExIcal.parse(conn.resp_body)
|
[entry1] = ExIcal.parse(conn.resp_body)
|
||||||
assert entry1.summary == event1.title
|
assert entry1.summary == event1.title
|
||||||
|
assert entry1.categories == event1.tags |> Enum.map(& &1.slug)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns 404 for an not existing feed", %{conn: conn} do
|
test "it returns 404 for an not existing feed", %{conn: conn} do
|
||||||
|
|
|
@ -129,13 +129,15 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do
|
||||||
actor: actor
|
actor: actor
|
||||||
} do
|
} do
|
||||||
event = insert(:event, %{organizer_actor: actor})
|
event = insert(:event, %{organizer_actor: actor})
|
||||||
participant = insert(:participant, %{actor: actor, event: event})
|
insert(:participant, %{actor: actor, event: event, role: :creator})
|
||||||
participant2 = insert(:participant, %{event: event})
|
user2 = insert(:user)
|
||||||
|
actor2 = insert(:actor, user: user2)
|
||||||
|
participant2 = insert(:participant, %{event: event, actor: actor2, role: :participant})
|
||||||
|
|
||||||
mutation = """
|
mutation = """
|
||||||
mutation {
|
mutation {
|
||||||
leaveEvent(
|
leaveEvent(
|
||||||
actor_id: #{participant.actor.id},
|
actor_id: #{participant2.actor.id},
|
||||||
event_id: #{event.id}
|
event_id: #{event.id}
|
||||||
) {
|
) {
|
||||||
actor {
|
actor {
|
||||||
|
@ -150,23 +152,24 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user)
|
|> auth_conn(user2)
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] == nil
|
assert json_response(res, 200)["errors"] == nil
|
||||||
assert json_response(res, 200)["data"]["leaveEvent"]["event"]["id"] == to_string(event.id)
|
assert json_response(res, 200)["data"]["leaveEvent"]["event"]["id"] == to_string(event.id)
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["leaveEvent"]["actor"]["id"] ==
|
assert json_response(res, 200)["data"]["leaveEvent"]["actor"]["id"] ==
|
||||||
to_string(participant.actor.id)
|
to_string(participant2.actor.id)
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
{
|
{
|
||||||
event(uuid: "#{event.uuid}") {
|
person(preferredUsername: "#{actor.preferred_username}") {
|
||||||
participants {
|
participations(eventId: "#{event.id}") {
|
||||||
role,
|
event {
|
||||||
actor {
|
uuid,
|
||||||
preferredUsername
|
title
|
||||||
}
|
},
|
||||||
|
role
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,16 +177,39 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "participants"))
|
|> auth_conn(user)
|
||||||
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "person"))
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["event"]["participants"] == [
|
assert json_response(res, 200)["data"]["person"]["participations"] == [
|
||||||
%{
|
%{
|
||||||
"actor" => %{
|
"event" => %{
|
||||||
"preferredUsername" => participant2.actor.preferred_username
|
"uuid" => event.uuid,
|
||||||
|
"title" => event.title
|
||||||
},
|
},
|
||||||
"role" => "CREATOR"
|
"role" => "CREATOR"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
query = """
|
||||||
|
{
|
||||||
|
person(preferredUsername: "#{actor2.preferred_username}") {
|
||||||
|
participations(eventId: "#{event.id}") {
|
||||||
|
event {
|
||||||
|
uuid,
|
||||||
|
title
|
||||||
|
},
|
||||||
|
role
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user2)
|
||||||
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "person"))
|
||||||
|
|
||||||
|
assert json_response(res, 200)["data"]["person"]["participations"] == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "actor_leave_event/3 should check if the participant is the only creator", %{
|
test "actor_leave_event/3 should check if the participant is the only creator", %{
|
||||||
|
@ -324,17 +350,23 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do
|
||||||
assert hd(json_response(res, 200)["errors"])["message"] =~ "Participant not found"
|
assert hd(json_response(res, 200)["errors"])["message"] =~ "Participant not found"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "list_participants_for_event/3 returns participants for an event", context do
|
test "list_participants_for_event/3 returns participants for an event", %{
|
||||||
|
conn: conn,
|
||||||
|
actor: actor,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
event =
|
event =
|
||||||
@event
|
@event
|
||||||
|> Map.put(:organizer_actor_id, context.actor.id)
|
|> Map.put(:organizer_actor_id, actor.id)
|
||||||
|
|
||||||
{:ok, event} = Events.create_event(event)
|
{:ok, event} = Events.create_event(event)
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
{
|
{
|
||||||
event(uuid: "#{event.uuid}") {
|
event(uuid: "#{event.uuid}") {
|
||||||
participants(roles: "participant,moderator,administrator,creator") {
|
participants(roles: "participant,moderator,administrator,creator", actor_id: "#{
|
||||||
|
actor.id
|
||||||
|
}") {
|
||||||
role,
|
role,
|
||||||
actor {
|
actor {
|
||||||
preferredUsername
|
preferredUsername
|
||||||
|
@ -345,13 +377,16 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
res =
|
||||||
context.conn
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "participants"))
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "participants"))
|
||||||
|
|
||||||
|
assert json_response(res, 200)["errors"] == nil
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["event"]["participants"] == [
|
assert json_response(res, 200)["data"]["event"]["participants"] == [
|
||||||
%{
|
%{
|
||||||
"actor" => %{
|
"actor" => %{
|
||||||
"preferredUsername" => context.actor.preferred_username
|
"preferredUsername" => actor.preferred_username
|
||||||
},
|
},
|
||||||
"role" => "CREATOR"
|
"role" => "CREATOR"
|
||||||
}
|
}
|
||||||
|
@ -368,7 +403,9 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do
|
||||||
query = """
|
query = """
|
||||||
{
|
{
|
||||||
event(uuid: "#{event.uuid}") {
|
event(uuid: "#{event.uuid}") {
|
||||||
participants(page: 1, limit: 1, roles: "participant,moderator,administrator,creator") {
|
participants(page: 1, limit: 1, roles: "participant,moderator,administrator,creator", actorId: "#{
|
||||||
|
actor.id
|
||||||
|
}") {
|
||||||
role,
|
role,
|
||||||
actor {
|
actor {
|
||||||
preferredUsername
|
preferredUsername
|
||||||
|
@ -379,7 +416,8 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
res =
|
||||||
context.conn
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "participants"))
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "participants"))
|
||||||
|
|
||||||
sorted_participants =
|
sorted_participants =
|
||||||
|
@ -402,7 +440,9 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do
|
||||||
query = """
|
query = """
|
||||||
{
|
{
|
||||||
event(uuid: "#{event.uuid}") {
|
event(uuid: "#{event.uuid}") {
|
||||||
participants(page: 2, limit: 1, roles: "participant,moderator,administrator,creator") {
|
participants(page: 2, limit: 1, roles: "participant,moderator,administrator,creator", actorId: "#{
|
||||||
|
actor.id
|
||||||
|
}") {
|
||||||
role,
|
role,
|
||||||
actor {
|
actor {
|
||||||
preferredUsername
|
preferredUsername
|
||||||
|
@ -413,7 +453,8 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
res =
|
||||||
context.conn
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "participants"))
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "participants"))
|
||||||
|
|
||||||
sorted_participants =
|
sorted_participants =
|
||||||
|
@ -427,7 +468,7 @@ defmodule MobilizonWeb.Resolvers.ParticipantResolverTest do
|
||||||
assert sorted_participants == [
|
assert sorted_participants == [
|
||||||
%{
|
%{
|
||||||
"actor" => %{
|
"actor" => %{
|
||||||
"preferredUsername" => context.actor.preferred_username
|
"preferredUsername" => actor.preferred_username
|
||||||
},
|
},
|
||||||
"role" => "CREATOR"
|
"role" => "CREATOR"
|
||||||
}
|
}
|
||||||
|
|
|
@ -473,7 +473,9 @@ defmodule MobilizonWeb.Resolvers.PersonResolverTest do
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["errors"])["message"] == "Person with name riri not found"
|
assert hd(json_response(res, 200)["errors"])["message"] == "Person with name riri not found"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "get_current_person/3" do
|
||||||
test "get_current_person/3 can return the events the person is going to", context do
|
test "get_current_person/3 can return the events the person is going to", context do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
actor = insert(:actor, user: user)
|
actor = insert(:actor, user: user)
|
||||||
|
@ -481,12 +483,14 @@ defmodule MobilizonWeb.Resolvers.PersonResolverTest do
|
||||||
query = """
|
query = """
|
||||||
{
|
{
|
||||||
loggedPerson {
|
loggedPerson {
|
||||||
goingToEvents {
|
participations {
|
||||||
|
event {
|
||||||
uuid,
|
uuid,
|
||||||
title
|
title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
res =
|
||||||
|
@ -494,7 +498,7 @@ defmodule MobilizonWeb.Resolvers.PersonResolverTest do
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "logged_person"))
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "logged_person"))
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["loggedPerson"]["goingToEvents"] == []
|
assert json_response(res, 200)["data"]["loggedPerson"]["participations"] == []
|
||||||
|
|
||||||
event = insert(:event, %{organizer_actor: actor})
|
event = insert(:event, %{organizer_actor: actor})
|
||||||
insert(:participant, %{actor: actor, event: event})
|
insert(:participant, %{actor: actor, event: event})
|
||||||
|
@ -504,8 +508,8 @@ defmodule MobilizonWeb.Resolvers.PersonResolverTest do
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "logged_person"))
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "logged_person"))
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["loggedPerson"]["goingToEvents"] == [
|
assert json_response(res, 200)["data"]["loggedPerson"]["participations"] == [
|
||||||
%{"title" => event.title, "uuid" => event.uuid}
|
%{"event" => %{"title" => event.title, "uuid" => event.uuid}}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -519,12 +523,14 @@ defmodule MobilizonWeb.Resolvers.PersonResolverTest do
|
||||||
query = """
|
query = """
|
||||||
{
|
{
|
||||||
person(preferredUsername: "#{actor.preferred_username}") {
|
person(preferredUsername: "#{actor.preferred_username}") {
|
||||||
goingToEvents {
|
participations {
|
||||||
|
event {
|
||||||
uuid,
|
uuid,
|
||||||
title
|
title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
res =
|
||||||
|
@ -532,17 +538,19 @@ defmodule MobilizonWeb.Resolvers.PersonResolverTest do
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "person"))
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "person"))
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["person"]["goingToEvents"] == []
|
assert json_response(res, 200)["data"]["person"]["participations"] == []
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
{
|
{
|
||||||
person(preferredUsername: "#{actor_from_other_user.preferred_username}") {
|
person(preferredUsername: "#{actor_from_other_user.preferred_username}") {
|
||||||
goingToEvents {
|
participations {
|
||||||
|
event {
|
||||||
uuid,
|
uuid,
|
||||||
title
|
title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
res =
|
||||||
|
@ -550,10 +558,45 @@ defmodule MobilizonWeb.Resolvers.PersonResolverTest do
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "person"))
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "person"))
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["person"]["goingToEvents"] == nil
|
assert json_response(res, 200)["data"]["person"]["participations"] == nil
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["errors"])["message"] ==
|
assert hd(json_response(res, 200)["errors"])["message"] ==
|
||||||
"Actor id is not owned by authenticated user"
|
"Actor id is not owned by authenticated user"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "find_person/3 can return the participation for an identity on a specific event",
|
||||||
|
context do
|
||||||
|
user = insert(:user)
|
||||||
|
actor = insert(:actor, user: user)
|
||||||
|
event = insert(:event, organizer_actor: actor)
|
||||||
|
insert(:participant, event: event, actor: actor)
|
||||||
|
|
||||||
|
query = """
|
||||||
|
{
|
||||||
|
person(preferredUsername: "#{actor.preferred_username}") {
|
||||||
|
participations(eventId: "#{event.id}") {
|
||||||
|
event {
|
||||||
|
uuid,
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
res =
|
||||||
|
context.conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "person"))
|
||||||
|
|
||||||
|
assert json_response(res, 200)["data"]["person"]["participations"] == [
|
||||||
|
%{
|
||||||
|
"event" => %{
|
||||||
|
"uuid" => event.uuid,
|
||||||
|
"title" => event.title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue