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";
|
||||
|
||||
/* Bulma imports */
|
||||
@import "~bulma/sass/utilities/_all";
|
||||
@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";
|
||||
@import "~bulma/bulma";
|
||||
|
||||
/* Buefy imports */
|
||||
@import "~buefy/src/scss/utils/_all";
|
||||
@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";
|
||||
@import "~buefy/src/scss/buefy";
|
||||
|
||||
.router-enter-active,
|
||||
.router-leave-active {
|
||||
|
@ -6,7 +6,7 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
|
||||
@Component({})
|
||||
@Component
|
||||
export default class DateTimePicker extends Vue {
|
||||
@Prop({ required: true, type: Date }) value!: Date;
|
||||
@Prop({ required: false, type: String, default: 'Datetime' }) label!: string;
|
||||
|
@ -1,64 +1,66 @@
|
||||
<template>
|
||||
<article class="box columns">
|
||||
<div class="content column">
|
||||
<div class="title-wrapper">
|
||||
<div class="date-component" v-if="!mergedOptions.hideDate">
|
||||
<date-calendar-icon :date="participation.event.beginsOn" />
|
||||
</div>
|
||||
<h2 class="title" ref="title">{{ participation.event.title }}</h2>
|
||||
</div>
|
||||
<div>
|
||||
<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-else>
|
||||
<span v-if="participation.event.beginsOn < new Date()">{{ $t('Organized by {name}', { name: participation.event.organizerActor.displayName() } ) }}</span>
|
||||
|
|
||||
<span>{{ $t('Going as {name}', { name: participation.actor.displayName() }) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<span class="column is-narrow">
|
||||
<b-icon icon="earth" v-if=" participation.event.visibility === EventVisibility.PUBLIC" />
|
||||
<b-icon icon="lock_opened" v-if=" participation.event.visibility === EventVisibility.RESTRICTED" />
|
||||
<b-icon icon="lock" v-if=" participation.event.visibility === EventVisibility.PRIVATE" />
|
||||
</span>
|
||||
<span class="column">
|
||||
<span v-if="!participation.event.options.maximumAttendeeCapacity">
|
||||
{{ $tc('{count} participants', participation.event.participantStats.approved, { count: participation.event.participantStats.approved })}}
|
||||
</span>
|
||||
<b-progress
|
||||
v-if="participation.event.options.maximumAttendeeCapacity > 0"
|
||||
type="is-primary"
|
||||
size="is-medium"
|
||||
:value="participation.event.participantStats.approved * 100 / participation.event.options.maximumAttendeeCapacity" show-value>
|
||||
{{ $t('{approved} / {total} seats', {approved: participation.event.participantStats.approved, total: participation.event.options.maximumAttendeeCapacity }) }}
|
||||
</b-progress>
|
||||
<span
|
||||
v-if="participation.event.participantStats.unapproved > 0">
|
||||
{{ $tc('{count} requests waiting', participation.event.participantStats.unapproved, { count: participation.event.participantStats.unapproved })}}
|
||||
</span>
|
||||
</span>
|
||||
<article class="box">
|
||||
<div class="title-wrapper">
|
||||
<div class="date-component" v-if="!mergedOptions.hideDate">
|
||||
<date-calendar-icon :date="participation.event.beginsOn" />
|
||||
</div>
|
||||
<h2 class="title" ref="title">{{ participation.event.title }}</h2>
|
||||
</div>
|
||||
<div class="actions column is-narrow">
|
||||
<ul>
|
||||
<li v-if="!([ParticipantRole.PARTICIPANT, ParticipantRole.NOT_APPROVED].includes(participation.role))">
|
||||
<router-link :to="{ name: EventRouteName.EDIT_EVENT, params: { eventId: participation.event.uuid } }">
|
||||
<b-icon icon="pencil" /> {{ $t('Edit') }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="!([ParticipantRole.PARTICIPANT, ParticipantRole.NOT_APPROVED].includes(participation.role))">
|
||||
<a @click="openDeleteEventModalWrapper"><b-icon icon="delete" /> {{ $t('Delete') }}</a>
|
||||
</li>
|
||||
<li v-if="!([ParticipantRole.PARTICIPANT, ParticipantRole.NOT_APPROVED].includes(participation.role))">
|
||||
<router-link :to="{ name: EventRouteName.PARTICIPATIONS, params: { eventId: participation.event.uuid } }">
|
||||
<b-icon icon="account-multiple-plus" /> {{ $t('Manage participations') }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: EventRouteName.EVENT, params: { uuid: participation.event.uuid } }"><b-icon icon="view-compact" /> {{ $t('View event page') }}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="columns">
|
||||
<div class="content column">
|
||||
<div>
|
||||
<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-else>
|
||||
<span v-if="participation.event.beginsOn < new Date()">{{ $t('Organized by {name}', { name: participation.event.organizerActor.displayName() } ) }}</span>
|
||||
|
|
||||
<span>{{ $t('Going as {name}', { name: participation.actor.displayName() }) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<span class="column is-narrow">
|
||||
<b-icon icon="earth" v-if=" participation.event.visibility === EventVisibility.PUBLIC" />
|
||||
<b-icon icon="lock_opened" v-if=" participation.event.visibility === EventVisibility.RESTRICTED" />
|
||||
<b-icon icon="lock" v-if=" participation.event.visibility === EventVisibility.PRIVATE" />
|
||||
</span>
|
||||
<span class="column">
|
||||
<span v-if="!participation.event.options.maximumAttendeeCapacity">
|
||||
{{ $tc('{count} participants', participation.event.participantStats.approved, { count: participation.event.participantStats.approved })}}
|
||||
</span>
|
||||
<b-progress
|
||||
v-if="participation.event.options.maximumAttendeeCapacity > 0"
|
||||
type="is-primary"
|
||||
size="is-medium"
|
||||
:value="participation.event.participantStats.approved * 100 / participation.event.options.maximumAttendeeCapacity" show-value>
|
||||
{{ $t('{approved} / {total} seats', {approved: participation.event.participantStats.approved, total: participation.event.options.maximumAttendeeCapacity }) }}
|
||||
</b-progress>
|
||||
<span
|
||||
v-if="participation.event.participantStats.unapproved > 0">
|
||||
{{ $tc('{count} requests waiting', participation.event.participantStats.unapproved, { count: participation.event.participantStats.unapproved })}}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions column is-narrow">
|
||||
<ul>
|
||||
<li v-if="!([ParticipantRole.PARTICIPANT, ParticipantRole.NOT_APPROVED].includes(participation.role))">
|
||||
<router-link :to="{ name: EventRouteName.EDIT_EVENT, params: { eventId: participation.event.uuid } }">
|
||||
<b-icon icon="pencil" /> {{ $t('Edit') }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="!([ParticipantRole.PARTICIPANT, ParticipantRole.NOT_APPROVED].includes(participation.role))">
|
||||
<a @click="openDeleteEventModalWrapper"><b-icon icon="delete" /> {{ $t('Delete') }}</a>
|
||||
</li>
|
||||
<li v-if="!([ParticipantRole.PARTICIPANT, ParticipantRole.NOT_APPROVED].includes(participation.role))">
|
||||
<router-link :to="{ name: EventRouteName.PARTICIPATIONS, params: { eventId: participation.event.uuid } }">
|
||||
<b-icon icon="account-multiple-plus" /> {{ $t('Manage participations') }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: EventRouteName.EVENT, params: { uuid: participation.event.uuid } }"><b-icon icon="view-compact" /> {{ $t('View event page') }}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</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,
|
||||
id
|
||||
},
|
||||
event {
|
||||
id
|
||||
}
|
||||
`;
|
||||
|
||||
@ -52,7 +55,7 @@ const optionsQuery = `
|
||||
`;
|
||||
|
||||
export const FETCH_EVENT = gql`
|
||||
query($uuid:UUID!, $roles: String) {
|
||||
query($uuid:UUID!) {
|
||||
event(uuid: $uuid) {
|
||||
id,
|
||||
uuid,
|
||||
@ -95,9 +98,6 @@ export const FETCH_EVENT = gql`
|
||||
# preferredUsername,
|
||||
# name,
|
||||
# },
|
||||
participants (roles: $roles) {
|
||||
${participantQuery}
|
||||
},
|
||||
participantStats {
|
||||
approved,
|
||||
unapproved
|
||||
@ -363,9 +363,10 @@ export const DELETE_EVENT = 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) {
|
||||
participants(page: $page, limit: $limit, roles: $roles) {
|
||||
id,
|
||||
participants(page: $page, limit: $limit, roles: $roles, actorId: $actorId) {
|
||||
${participantQuery}
|
||||
},
|
||||
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.",
|
||||
"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}",
|
||||
"Cancel my participation request…": "Cancel my participation request…",
|
||||
"Cancel my participation…": "Cancel my participation…",
|
||||
"Cancel": "Cancel",
|
||||
"Category": "Category",
|
||||
"Change my identity…": "Change my identity…",
|
||||
"Change my password": "Change my password",
|
||||
"Change password": "Change password",
|
||||
"Change": "Change",
|
||||
"Clear": "Clear",
|
||||
"Click to select": "Click to select",
|
||||
@ -82,6 +87,7 @@
|
||||
"Group": "Group",
|
||||
"Groups": "Groups",
|
||||
"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",
|
||||
"Identities": "Identities",
|
||||
"Identity {displayName} created": "Identity {displayName} created",
|
||||
@ -116,6 +122,7 @@
|
||||
"My events": "My events",
|
||||
"My identities": "My identities",
|
||||
"Name": "Name",
|
||||
"New password": "New password",
|
||||
"No address defined": "No address defined",
|
||||
"No events found": "No events found",
|
||||
"No group found": "No group found",
|
||||
@ -123,6 +130,7 @@
|
||||
"No participants yet.": "No participants yet.",
|
||||
"No results for \"{queryText}\"": "No results for \"{queryText}\"",
|
||||
"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",
|
||||
"Only accessible through link and search (private)": "Only accessible through link and search (private)",
|
||||
"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.",
|
||||
"Page limited to my group (asks for auth)": "Page limited to my group (asks for auth)",
|
||||
"Participants": "Participants",
|
||||
"Participate": "Participate",
|
||||
"Participation approval": "Participation approval",
|
||||
"Participation requested!": "Participation requested!",
|
||||
"Password (confirmation)": "Password (confirmation)",
|
||||
"Password change": "Password change",
|
||||
"Password reset": "Password reset",
|
||||
"Password": "Password",
|
||||
"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 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 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 {date} at {time}": "The {date} at {time}",
|
||||
"The {date} from {startTime} to {endTime}": "The {date} from {startTime} to {endTime}",
|
||||
@ -208,6 +220,7 @@
|
||||
"View event page": "View event page",
|
||||
"View everything": "View everything",
|
||||
"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",
|
||||
"We just sent an email to {email}": "We just sent an email to {email}",
|
||||
"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 are already logged-in.": "You are already logged-in.",
|
||||
"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 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",
|
||||
@ -233,6 +247,8 @@
|
||||
"e.g. 10 Rue Jangot": "e.g. 10 Rue Jangot",
|
||||
"iCal Feed": "iCal Feed",
|
||||
"meditate a bit": "meditate a bit",
|
||||
"with another identity…": "with another identity…",
|
||||
"with {identity}": "with {identity}",
|
||||
"{actor}'s avatar": "{actor}'s avatar",
|
||||
"{approved} / {total} seats": "{approved} / {total} seats",
|
||||
"{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.",
|
||||
"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}",
|
||||
"Cancel my participation request…": "Cancel my participation request…",
|
||||
"Cancel my participation…": "Annuler ma participation…",
|
||||
"Cancel": "Annuler",
|
||||
"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",
|
||||
"Clear": "Effacer",
|
||||
"Click to select": "Cliquez pour sélectionner",
|
||||
@ -82,6 +87,7 @@
|
||||
"Group": "Groupe",
|
||||
"Groups": "Groupes",
|
||||
"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",
|
||||
"Identities": "Identités",
|
||||
"Identity {displayName} created": "Identité {displayName} créée",
|
||||
@ -116,6 +122,7 @@
|
||||
"My events": "Mes événements",
|
||||
"My identities": "Mes identités",
|
||||
"Name": "Nom",
|
||||
"New password": "Nouveau mot de passe",
|
||||
"No address defined": "Aucune adresse définie",
|
||||
"No events found": "Aucun événement trouvé",
|
||||
"No group found": "Aucun groupe trouvé",
|
||||
@ -123,6 +130,7 @@
|
||||
"No participants yet.": "Pas de participants pour le moment.",
|
||||
"No results for \"{queryText}\"": "Pas de résultats pour « {queryText} »",
|
||||
"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",
|
||||
"Only accessible through link and search (private)": "Uniquement accessibles par lien et la recherche (privé)",
|
||||
"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.",
|
||||
"Page limited to my group (asks for auth)": "Accès limité à mon groupe (demande authentification)",
|
||||
"Participants": "Participants",
|
||||
"Participate": "Participer",
|
||||
"Participation approval": "Validation des participations",
|
||||
"Participation requested!": "Participation demandée !",
|
||||
"Password (confirmation)": "Mot de passe (confirmation)",
|
||||
"Password change": "Changement de mot de passe",
|
||||
"Password reset": "Réinitialisation du mot de passe",
|
||||
"Password": "Mot de passe",
|
||||
"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 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 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 {date} at {time}": "Le {date} à {time}",
|
||||
"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 everything": "Voir tout",
|
||||
"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",
|
||||
"We just sent an email to {email}": "Nous venons d'envoyer un email à {email}",
|
||||
"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 are already logged-in.": "Vous êtes déjà connecté.",
|
||||
"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 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",
|
||||
@ -233,6 +247,8 @@
|
||||
"e.g. 10 Rue Jangot": "par exemple : 10 Rue Jangot",
|
||||
"iCal Feed": "Flux iCal",
|
||||
"meditate a bit": "méditez un peu",
|
||||
"with another identity…": "avec une autre identité…",
|
||||
"with {identity}": "avec {identity}",
|
||||
"{actor}'s avatar": "Avatar de {actor}",
|
||||
"{approved} / {total} seats": "{approved} / {total} places",
|
||||
"{count} participants": "Un⋅e participant⋅e|{count} participant⋅e⋅s",
|
||||
|
@ -1,5 +1,5 @@
|
||||
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';
|
||||
|
||||
export interface IFeedToken {
|
||||
@ -11,11 +11,13 @@ export interface IFeedToken {
|
||||
export interface IPerson extends IActor {
|
||||
feedTokens: IFeedToken[];
|
||||
goingToEvents: IEvent[];
|
||||
participations: IParticipant[];
|
||||
}
|
||||
|
||||
export class Person extends Actor implements IPerson {
|
||||
feedTokens: IFeedToken[] = [];
|
||||
goingToEvents: IEvent[] = [];
|
||||
participations: IParticipant[] = [];
|
||||
|
||||
constructor(hash: IPerson | {} = {}) {
|
||||
super(hash);
|
||||
|
@ -1,29 +1,22 @@
|
||||
<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">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">{{ $t('Pick an identity') }}</p>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<div class="list is-hoverable">
|
||||
<a class="list-item" v-for="identity in identities" :class="{ 'is-active': identity.id === currentIdentity.id }" @click="changeCurrentIdentity(identity)">
|
||||
<div class="media">
|
||||
<img class="media-left image" v-if="identity.avatar" :src="identity.avatar.url" />
|
||||
<div class="media-content">
|
||||
<h3>@{{ identity.preferredUsername }}</h3>
|
||||
<small>{{ identity.name }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">{{ $t('Pick an identity') }}</p>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<div class="list is-hoverable">
|
||||
<a class="list-item" v-for="identity in identities" :class="{ 'is-active': identity.id === currentIdentity.id }" @click="changeCurrentIdentity(identity)">
|
||||
<div class="media">
|
||||
<img class="media-left image" v-if="identity.avatar" :src="identity.avatar.url" />
|
||||
<div class="media-content">
|
||||
<h3>@{{ identity.preferredUsername }}</h3>
|
||||
<small>{{ identity.name }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</a>
|
||||
</div>
|
||||
</b-modal>
|
||||
</section>
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
@ -40,22 +33,13 @@ import { IDENTITIES } from '@/graphql/actor';
|
||||
})
|
||||
export default class IdentityPicker extends Vue {
|
||||
@Prop() value!: IActor;
|
||||
isComponentModalActive: boolean = false;
|
||||
identities: IActor[] = [];
|
||||
currentIdentity: IActor = this.value;
|
||||
|
||||
changeCurrentIdentity(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>
|
||||
</script>
|
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,
|
||||
feedTokens: [],
|
||||
goingToEvents: [],
|
||||
participations: [],
|
||||
};
|
||||
errors: object = {};
|
||||
validationSent: boolean = false;
|
||||
|
@ -29,7 +29,7 @@ import {EventJoinOptions} from "@/types/event.model";
|
||||
<address-auto-complete v-model="event.physicalAddress" />
|
||||
|
||||
<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>
|
||||
|
||||
<div class="field">
|
||||
@ -188,7 +188,6 @@ import {
|
||||
EventModel,
|
||||
EventStatus,
|
||||
EventVisibility,
|
||||
EventVisibilityJoinOptions,
|
||||
} from '@/types/event.model';
|
||||
import { CURRENT_ACTOR_CLIENT } from '@/graphql/actor';
|
||||
import { Person } from '@/types/actor';
|
||||
@ -200,10 +199,10 @@ import { TAGS } from '@/graphql/tags';
|
||||
import { ITag } from '@/types/tag.model';
|
||||
import AddressAutoComplete from '@/components/Event/AddressAutoComplete.vue';
|
||||
import { buildFileFromIPicture, buildFileVariable } from '@/utils/image';
|
||||
import IdentityPicker from '@/views/Account/IdentityPicker.vue';
|
||||
import IdentityPickerWrapper from '@/views/Account/IdentityPickerWrapper.vue';
|
||||
|
||||
@Component({
|
||||
components: { AddressAutoComplete, TagInput, DateTimePicker, PictureUpload, Editor, IdentityPicker },
|
||||
components: { IdentityPickerWrapper, AddressAutoComplete, TagInput, DateTimePicker, PictureUpload, Editor },
|
||||
apollo: {
|
||||
currentActor: {
|
||||
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>
|
||||
<div>
|
||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||
@ -10,7 +13,7 @@
|
||||
<img src="https://picsum.photos/600/200/">
|
||||
</figure>
|
||||
</div>
|
||||
<section class="container">
|
||||
<section>
|
||||
<div class="title-and-participate-button">
|
||||
<div class="title-wrapper">
|
||||
<div class="date-component">
|
||||
@ -18,21 +21,21 @@
|
||||
</div>
|
||||
<h1 class="title">{{ event.title }}</h1>
|
||||
</div>
|
||||
<span v-if="event.participantStats.approved > 0 && !actorIsParticipant()">
|
||||
{{ $tc('One person is going', event.participantStats.approved, {approved: event.participantStats.approved}) }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ $tc('You and one other person are going to this event', event.participantStats.approved - 1, {approved: event.participantStats.approved - 1}) }}
|
||||
</span>
|
||||
<div v-if="!actorIsOrganizer()" class="participate-button has-text-centered">
|
||||
<a v-if="!actorIsParticipant()" @click="isJoinModalActive = true" class="button is-large is-primary is-rounded">
|
||||
<b-icon icon="circle-outline"></b-icon>
|
||||
{{ $t('Join') }}
|
||||
</a>
|
||||
<a v-if="actorIsParticipant()" @click="confirmLeave()" class="button is-large is-primary is-rounded">
|
||||
<b-icon icon="check-circle"></b-icon>
|
||||
{{ $t('Leave') }}
|
||||
</a>
|
||||
<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}) }}
|
||||
</small>
|
||||
<small v-else>
|
||||
{{ $tc('You and one other person are going to this event', event.participantStats.approved - 1, {approved: event.participantStats.approved - 1}) }}
|
||||
</small>
|
||||
<participation-button
|
||||
v-if="currentActor.id && !actorIsOrganizer"
|
||||
:participation="participations[0]"
|
||||
:current-actor="currentActor"
|
||||
@joinEvent="joinEvent"
|
||||
@joinModal="isJoinModalActive = true"
|
||||
@confirmLeave="confirmLeave"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="metadata columns">
|
||||
@ -60,8 +63,8 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="column sidebar">
|
||||
<div class="field has-addons">
|
||||
<p class="control" v-if="actorIsOrganizer()">
|
||||
<div class="field has-addons" v-if="currentActor.id">
|
||||
<p class="control" v-if="actorIsOrganizer">
|
||||
<router-link
|
||||
class="button"
|
||||
:to="{ name: 'EditEvent', params: {eventId: event.uuid}}"
|
||||
@ -69,7 +72,7 @@
|
||||
{{ $t('Edit') }}
|
||||
</router-link>
|
||||
</p>
|
||||
<p class="control" v-if="actorIsOrganizer()">
|
||||
<p class="control" v-if="actorIsOrganizer">
|
||||
<a class="button is-danger" @click="openDeleteEventModalWrapper">
|
||||
{{ $t('Delete') }}
|
||||
</a>
|
||||
@ -133,26 +136,6 @@
|
||||
</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">
|
||||
<div class="container">
|
||||
<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()" />
|
||||
</b-modal>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { DELETE_EVENT, FETCH_EVENT, JOIN_EVENT, LEAVE_EVENT } from '@/graphql/event';
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { EVENT_PERSON_PARTICIPATION, FETCH_EVENT, JOIN_EVENT, LEAVE_EVENT } from '@/graphql/event';
|
||||
import { Component, Prop } from 'vue-property-decorator';
|
||||
import { CURRENT_ACTOR_CLIENT } from '@/graphql/actor';
|
||||
import { EventVisibility, IEvent, IParticipant, ParticipantRole } from '@/types/event.model';
|
||||
import { IPerson } from '@/types/actor';
|
||||
import { RouteName } from '@/router';
|
||||
import { IPerson, Person } from '@/types/actor';
|
||||
import { GRAPHQL_API_ENDPOINT } from '@/api/_entrypoint';
|
||||
import DateCalendarIcon from '@/components/Event/DateCalendarIcon.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 ActorLink from '@/components/Account/ActorLink.vue';
|
||||
import ReportModal from '@/components/Report/ReportModal.vue';
|
||||
import ParticipationModal from '@/components/Event/ParticipationModal.vue';
|
||||
import { IReport } from '@/types/report.model';
|
||||
import { CREATE_REPORT } from '@/graphql/report';
|
||||
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({
|
||||
components: {
|
||||
@ -222,7 +221,8 @@ import { EventRouteName } from '@/router/event';
|
||||
BIcon,
|
||||
DateCalendarIcon,
|
||||
ReportModal,
|
||||
ParticipationModal,
|
||||
IdentityPicker,
|
||||
ParticipationButton,
|
||||
// tslint:disable:space-in-parens
|
||||
'map-leaflet': () => import(/* webpackChunkName: "map" */ '@/components/Map.vue'),
|
||||
// tslint:enable
|
||||
@ -233,13 +233,25 @@ import { EventRouteName } from '@/router/event';
|
||||
variables() {
|
||||
return {
|
||||
uuid: this.uuid,
|
||||
roles: [ParticipantRole.CREATOR, ParticipantRole.MODERATOR, ParticipantRole.MODERATOR, ParticipantRole.PARTICIPANT].join(),
|
||||
};
|
||||
},
|
||||
},
|
||||
currentActor: {
|
||||
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 {
|
||||
@ -247,13 +259,17 @@ export default class Event extends EventMixin {
|
||||
|
||||
event!: IEvent;
|
||||
currentActor!: IPerson;
|
||||
validationSent: boolean = false;
|
||||
identity: IPerson = new Person();
|
||||
participations: IParticipant[] = [];
|
||||
showMap: boolean = false;
|
||||
isReportModalActive: boolean = false;
|
||||
isJoinModalActive: boolean = false;
|
||||
|
||||
EventVisibility = EventVisibility;
|
||||
EventRouteName = EventRouteName;
|
||||
|
||||
mounted() {
|
||||
this.identity = this.currentActor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the event, then redirect to home.
|
||||
@ -298,6 +314,24 @@ export default class Event extends EventMixin {
|
||||
},
|
||||
update: (store, { data }) => {
|
||||
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 } });
|
||||
if (cachedData == null) return;
|
||||
const { event } = cachedData;
|
||||
@ -306,9 +340,13 @@ export default class Event extends EventMixin {
|
||||
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) {
|
||||
@ -338,19 +376,38 @@ export default class Event extends EventMixin {
|
||||
},
|
||||
update: (store, { data }) => {
|
||||
if (data == null) return;
|
||||
const cachedData = store.readQuery<{ event: IEvent }>({ query: FETCH_EVENT, variables: { uuid: this.event.uuid } });
|
||||
if (cachedData == null) return;
|
||||
const { event } = cachedData;
|
||||
if (event === null) {
|
||||
console.error('Cannot update event participant cache, because of null value.');
|
||||
|
||||
const participationCachedData = store.readQuery<{ person: IPerson }>({
|
||||
query: EVENT_PERSON_PARTICIPATION,
|
||||
variables: { eventId: this.event.id, name: this.currentActor.preferredUsername },
|
||||
});
|
||||
if (participationCachedData == null) return;
|
||||
const { person } = participationCachedData;
|
||||
if (person === null) {
|
||||
console.error('Cannot update participation cache, because of null value.');
|
||||
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
|
||||
.filter(p => p.actor.id !== data.leaveEvent.actor.id);
|
||||
event.participantStats.approved = event.participantStats.approved - 1;
|
||||
|
||||
store.writeQuery({ query: FETCH_EVENT, data: { event } });
|
||||
const eventCachedData = store.readQuery<{ event: IEvent }>({ query: FETCH_EVENT, variables: { uuid: this.event.uuid } });
|
||||
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;
|
||||
}
|
||||
store.writeQuery({ query: FETCH_EVENT, variables: { uuid: this.uuid }, data: { event } });
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
@ -369,17 +426,14 @@ export default class Event extends EventMixin {
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
actorIsParticipant() {
|
||||
if (this.actorIsOrganizer()) return true;
|
||||
get actorIsParticipant() {
|
||||
if (this.actorIsOrganizer) return true;
|
||||
|
||||
return this.currentActor &&
|
||||
this.event.participants
|
||||
.some(participant => participant.actor.id === this.currentActor.id);
|
||||
return this.participations.length > 0 && this.participations[0].role === ParticipantRole.PARTICIPANT;
|
||||
}
|
||||
|
||||
actorIsOrganizer() {
|
||||
return this.currentActor && this.event.organizerActor &&
|
||||
this.currentActor.id === this.event.organizerActor.id;
|
||||
get actorIsOrganizer() {
|
||||
return this.participations.length > 0 && this.participations[0].role === ParticipantRole.CREATOR;
|
||||
}
|
||||
|
||||
get twitterShareUrl(): string {
|
||||
|
@ -68,6 +68,7 @@ import { IPerson } from '@/types/actor';
|
||||
page: 1,
|
||||
limit: 10,
|
||||
roles: [ParticipantRole.PARTICIPANT].join(),
|
||||
actorId: this.currentActor.id,
|
||||
};
|
||||
},
|
||||
},
|
||||
@ -79,6 +80,7 @@ import { IPerson } from '@/types/actor';
|
||||
page: 1,
|
||||
limit: 20,
|
||||
roles: [ParticipantRole.CREATOR].join(),
|
||||
actorId: this.currentActor.id,
|
||||
};
|
||||
},
|
||||
update: data => data.event.participants.map(participation => new Participant(participation)),
|
||||
@ -91,6 +93,7 @@ import { IPerson } from '@/types/actor';
|
||||
page: 1,
|
||||
limit: 20,
|
||||
roles: [ParticipantRole.NOT_APPROVED].join(),
|
||||
actorId: this.currentActor.id,
|
||||
};
|
||||
},
|
||||
update: data => data.event.participants.map(participation => new Participant(participation)),
|
||||
|
@ -35,7 +35,6 @@
|
||||
<h3 class="title">
|
||||
{{ $t("Upcoming") }}
|
||||
</h3>
|
||||
<pre>{{ Array.from(goingToEvents.entries()) }}</pre>
|
||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||
<div v-for="row in goingToEvents" class="upcoming-events">
|
||||
<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])}) }}
|
||||
</h3>
|
||||
</span>
|
||||
<div class="level">
|
||||
<div>
|
||||
<EventListCard
|
||||
v-for="participation in row[1]"
|
||||
v-if="isInLessThanSevenDays(row[0])"
|
||||
:key="participation[1].event.uuid"
|
||||
:participation="participation[1]"
|
||||
class="level-item"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -72,12 +70,11 @@
|
||||
{{ $t("Last week") }}
|
||||
</h3>
|
||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||
<div class="level">
|
||||
<div>
|
||||
<EventListCard
|
||||
v-for="participation in lastWeekEvents"
|
||||
:key="participation.id"
|
||||
:participation="participation"
|
||||
class="level-item"
|
||||
:options="{ hideDate: false }"
|
||||
/>
|
||||
</div>
|
||||
@ -295,12 +292,6 @@ export default class Home extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
.upcoming-events {
|
||||
.level {
|
||||
margin-left: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
section.container {
|
||||
margin: auto auto 3rem;
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ export default class Report extends Vue {
|
||||
|
||||
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;
|
||||
report.status = updatedReport.status;
|
||||
|
||||
store.writeQuery({ query: REPORT, data: { report } });
|
||||
store.writeQuery({ query: REPORT, variables: { id: this.report.id }, data: { report } });
|
||||
} |