Merge branch 'feature/cleanup-event' into 'master'
Add leave/join/delete event logic See merge request framasoft/mobilizon!74
This commit is contained in:
commit
24dfa3b2d1
@ -1,134 +1,157 @@
|
|||||||
import gql from 'graphql-tag';
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
const participantQuery = `
|
||||||
|
role,
|
||||||
|
actor {
|
||||||
|
preferredUsername,
|
||||||
|
avatarUrl,
|
||||||
|
name
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const FETCH_EVENT = gql`
|
export const FETCH_EVENT = gql`
|
||||||
query($uuid:UUID!) {
|
query($uuid:UUID!) {
|
||||||
event(uuid: $uuid) {
|
event(uuid: $uuid) {
|
||||||
id,
|
id,
|
||||||
uuid,
|
uuid,
|
||||||
url,
|
url,
|
||||||
local,
|
local,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
beginsOn,
|
beginsOn,
|
||||||
endsOn,
|
endsOn,
|
||||||
status,
|
status,
|
||||||
visibility,
|
visibility,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
large_image,
|
large_image,
|
||||||
publish_at,
|
publish_at,
|
||||||
# online_address,
|
# online_address,
|
||||||
# phone_address,
|
# phone_address,
|
||||||
organizerActor {
|
organizerActor {
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
preferredUsername,
|
preferredUsername,
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
# attributedTo {
|
# attributedTo {
|
||||||
# # avatarUrl,
|
# # avatarUrl,
|
||||||
# preferredUsername,
|
# preferredUsername,
|
||||||
# name,
|
# name,
|
||||||
# },
|
# },
|
||||||
participants {
|
participants {
|
||||||
actor {
|
${participantQuery}
|
||||||
avatarUrl,
|
},
|
||||||
preferredUsername,
|
category {
|
||||||
name,
|
title,
|
||||||
},
|
},
|
||||||
role,
|
}
|
||||||
},
|
}
|
||||||
category {
|
|
||||||
title,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const FETCH_EVENTS = gql`
|
export const FETCH_EVENTS = gql`
|
||||||
query {
|
query {
|
||||||
events {
|
events {
|
||||||
id,
|
id,
|
||||||
uuid,
|
uuid,
|
||||||
url,
|
url,
|
||||||
local,
|
local,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
beginsOn,
|
||||||
|
endsOn,
|
||||||
|
status,
|
||||||
|
visibility,
|
||||||
|
thumbnail,
|
||||||
|
large_image,
|
||||||
|
publish_at,
|
||||||
|
# online_address,
|
||||||
|
# phone_address,
|
||||||
|
organizerActor {
|
||||||
|
avatarUrl,
|
||||||
|
preferredUsername,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
attributedTo {
|
||||||
|
avatarUrl,
|
||||||
|
preferredUsername,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
category {
|
||||||
title,
|
title,
|
||||||
description,
|
},
|
||||||
beginsOn,
|
participants {
|
||||||
endsOn,
|
${participantQuery}
|
||||||
status,
|
}
|
||||||
visibility,
|
|
||||||
thumbnail,
|
|
||||||
large_image,
|
|
||||||
publish_at,
|
|
||||||
# online_address,
|
|
||||||
# phone_address,
|
|
||||||
organizerActor {
|
|
||||||
avatarUrl,
|
|
||||||
preferredUsername,
|
|
||||||
name,
|
|
||||||
},
|
|
||||||
attributedTo {
|
|
||||||
avatarUrl,
|
|
||||||
preferredUsername,
|
|
||||||
name,
|
|
||||||
},
|
|
||||||
category {
|
|
||||||
title,
|
|
||||||
},
|
|
||||||
participants {
|
|
||||||
role,
|
|
||||||
actor {
|
|
||||||
preferredUsername,
|
|
||||||
avatarUrl,
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CREATE_EVENT = gql`
|
export const CREATE_EVENT = gql`
|
||||||
mutation CreateEvent(
|
mutation CreateEvent(
|
||||||
$title: String!,
|
$title: String!,
|
||||||
$description: String!,
|
$description: String!,
|
||||||
$organizerActorId: String!,
|
$organizerActorId: String!,
|
||||||
$category: String!,
|
$category: String!,
|
||||||
$beginsOn: DateTime!
|
$beginsOn: DateTime!
|
||||||
|
) {
|
||||||
|
createEvent(
|
||||||
|
title: $title,
|
||||||
|
description: $description,
|
||||||
|
beginsOn: $beginsOn,
|
||||||
|
organizerActorId: $organizerActorId,
|
||||||
|
category: $category
|
||||||
) {
|
) {
|
||||||
createEvent(
|
id,
|
||||||
title: $title,
|
uuid,
|
||||||
description: $description,
|
title
|
||||||
beginsOn: $beginsOn,
|
}
|
||||||
organizerActorId: $organizerActorId,
|
}
|
||||||
category: $category
|
|
||||||
) {
|
|
||||||
id,
|
|
||||||
uuid,
|
|
||||||
title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const EDIT_EVENT = gql`
|
export const EDIT_EVENT = gql`
|
||||||
mutation EditEvent(
|
mutation EditEvent(
|
||||||
$title: String!,
|
$title: String!,
|
||||||
$description: String!,
|
$description: String!,
|
||||||
$organizerActorId: Int!,
|
$organizerActorId: Int!,
|
||||||
$categoryId: Int!
|
$categoryId: Int!
|
||||||
) {
|
) {
|
||||||
EditEvent(title: $title, description: $description, organizerActorId: $organizerActorId, categoryId: $categoryId) {
|
EditEvent(title: $title, description: $description, organizerActorId: $organizerActorId, categoryId: $categoryId) {
|
||||||
uuid
|
uuid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const JOIN_EVENT = gql`
|
export const JOIN_EVENT = gql`
|
||||||
mutation JoinEvent(
|
mutation JoinEvent($id: Int!, $actorId: Int!) {
|
||||||
$uuid: String!,
|
joinEvent(
|
||||||
$username: String!
|
id: $id,
|
||||||
|
actorId: $actorId
|
||||||
) {
|
) {
|
||||||
joinEvent(
|
actor {
|
||||||
uuid: $uuid,
|
${participantQuery}
|
||||||
username: $username
|
},
|
||||||
)
|
role
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const LEAVE_EVENT = gql`
|
||||||
|
mutation LeaveEvent($id: Int!, $actorId: Int!) {
|
||||||
|
leaveEvent(
|
||||||
|
id: $id,
|
||||||
|
actorId: $actorId
|
||||||
|
) {
|
||||||
|
actor {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const DELETE_EVENT = gql`
|
||||||
|
mutation DeleteEvent($id: Int!, $actorId: Int!) {
|
||||||
|
deleteEvent(
|
||||||
|
id: $id,
|
||||||
|
actorId: $actorId
|
||||||
|
)
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -7,54 +7,54 @@ import SendPasswordReset from '@/views/User/SendPasswordReset.vue';
|
|||||||
import PasswordReset from '@/views/User/PasswordReset.vue';
|
import PasswordReset from '@/views/User/PasswordReset.vue';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
path: '/register/user',
|
path: '/register/user',
|
||||||
name: 'Register',
|
name: 'Register',
|
||||||
component: RegisterUser,
|
component: RegisterUser,
|
||||||
props: true,
|
props: true,
|
||||||
meta: { requiredAuth: false },
|
meta: { requiredAuth: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/register/profile',
|
path: '/register/profile',
|
||||||
name: 'RegisterProfile',
|
name: 'RegisterProfile',
|
||||||
component: RegisterProfile,
|
component: RegisterProfile,
|
||||||
props: true,
|
props: true,
|
||||||
meta: { requiredAuth: false },
|
meta: { requiredAuth: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/resend-instructions',
|
path: '/resend-instructions',
|
||||||
name: 'ResendConfirmation',
|
name: 'ResendConfirmation',
|
||||||
component: ResendConfirmation,
|
component: ResendConfirmation,
|
||||||
props: true,
|
props: true,
|
||||||
meta: { requiresAuth: false },
|
meta: { requiresAuth: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/password-reset/send',
|
path: '/password-reset/send',
|
||||||
name: 'SendPasswordReset',
|
name: 'SendPasswordReset',
|
||||||
component: SendPasswordReset,
|
component: SendPasswordReset,
|
||||||
props: true,
|
props: true,
|
||||||
meta: { requiresAuth: false },
|
meta: { requiresAuth: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/password-reset/:token',
|
path: '/password-reset/:token',
|
||||||
name: 'PasswordReset',
|
name: 'PasswordReset',
|
||||||
component: PasswordReset,
|
component: PasswordReset,
|
||||||
meta: { requiresAuth: false },
|
meta: { requiresAuth: false },
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/validate/:token',
|
path: '/validate/:token',
|
||||||
name: 'Validate',
|
name: 'Validate',
|
||||||
component: Validate,
|
component: Validate,
|
||||||
// We can only pass string values through params, therefore
|
// We can only pass string values through params, therefore
|
||||||
props: (route) => ({ email: route.params.email, userAlreadyActivated: route.params.userAlreadyActivated === 'true'}),
|
props: (route) => ({ email: route.params.email, userAlreadyActivated: route.params.userAlreadyActivated === 'true'}),
|
||||||
meta: { requiresAuth: false },
|
meta: { requiresAuth: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
component: Login,
|
component: Login,
|
||||||
props: true,
|
props: true,
|
||||||
meta: { requiredAuth: false },
|
meta: { requiredAuth: false },
|
||||||
},
|
},
|
||||||
];
|
];
|
@ -1,13 +1,13 @@
|
|||||||
export interface IActor {
|
export interface IActor {
|
||||||
id: string;
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
name: string;
|
name: string;
|
||||||
domain: string|null;
|
domain: string|null;
|
||||||
summary: string;
|
summary: string;
|
||||||
preferredUsername: string;
|
preferredUsername: string;
|
||||||
suspended: boolean;
|
suspended: boolean;
|
||||||
avatarUrl: string;
|
avatarUrl: string;
|
||||||
bannerUrl: string;
|
bannerUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPerson extends IActor {
|
export interface IPerson extends IActor {
|
||||||
@ -15,15 +15,18 @@ export interface IPerson extends IActor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IGroup extends IActor {
|
export interface IGroup extends IActor {
|
||||||
members: IMember[];
|
members: IMember[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MemberRole {
|
export enum MemberRole {
|
||||||
PENDING, MEMBER, MODERATOR, ADMIN
|
PENDING,
|
||||||
|
MEMBER,
|
||||||
|
MODERATOR,
|
||||||
|
ADMIN,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMember {
|
export interface IMember {
|
||||||
role: MemberRole;
|
role: MemberRole;
|
||||||
parent: IGroup;
|
parent: IGroup;
|
||||||
actor: IActor;
|
actor: IActor;
|
||||||
}
|
}
|
@ -1,62 +1,70 @@
|
|||||||
import { IActor } from "./actor.model";
|
import { IActor } from './actor.model';
|
||||||
|
|
||||||
export enum EventStatus {
|
export enum EventStatus {
|
||||||
TENTATIVE,
|
TENTATIVE,
|
||||||
CONFIRMED,
|
CONFIRMED,
|
||||||
CANCELLED
|
CANCELLED,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EventVisibility {
|
export enum EventVisibility {
|
||||||
PUBLIC,
|
PUBLIC,
|
||||||
UNLISTED,
|
UNLISTED,
|
||||||
RESTRICTED,
|
RESTRICTED,
|
||||||
PRIVATE
|
PRIVATE,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EventJoinOptions {
|
export enum EventJoinOptions {
|
||||||
FREE,
|
FREE,
|
||||||
RESTRICTED,
|
RESTRICTED,
|
||||||
INVITE
|
INVITE,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ParticipantRole {
|
export enum ParticipantRole {
|
||||||
NOT_APPROVED = 'not_approved',
|
NOT_APPROVED = 'not_approved',
|
||||||
PARTICIPANT = 'participant',
|
PARTICIPANT = 'participant',
|
||||||
MODERATOR = 'moderator',
|
MODERATOR = 'moderator',
|
||||||
ADMINSTRATOR = 'administrator',
|
ADMINISTRATOR = 'administrator',
|
||||||
CREATOR = 'creator'
|
CREATOR = 'creator',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICategory {
|
export interface ICategory {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
picture: string;
|
picture: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IParticipant {
|
export interface IParticipant {
|
||||||
role: ParticipantRole,
|
role: ParticipantRole;
|
||||||
actor: IActor,
|
actor: IActor;
|
||||||
event: IEvent
|
event: IEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEvent {
|
export interface IEvent {
|
||||||
uuid: string;
|
id?: number;
|
||||||
url: string;
|
uuid: string;
|
||||||
local: boolean;
|
url: string;
|
||||||
title: string;
|
local: boolean;
|
||||||
description: string;
|
|
||||||
begins_on: Date;
|
title: string;
|
||||||
ends_on: Date;
|
description: string;
|
||||||
status: EventStatus;
|
category: ICategory;
|
||||||
visibility: EventVisibility;
|
|
||||||
join_options: EventJoinOptions;
|
begins_on: Date;
|
||||||
thumbnail: string;
|
ends_on: Date;
|
||||||
large_image: string;
|
publish_at: Date;
|
||||||
publish_at: Date;
|
|
||||||
// online_address: Adress;
|
status: EventStatus;
|
||||||
// phone_address: string;
|
visibility: EventVisibility;
|
||||||
organizerActor: IActor;
|
|
||||||
attributedTo: IActor;
|
join_options: EventJoinOptions;
|
||||||
participants: IParticipant[];
|
|
||||||
category: ICategory;
|
thumbnail: string;
|
||||||
|
large_image: string;
|
||||||
|
|
||||||
|
organizerActor: IActor;
|
||||||
|
attributedTo: IActor;
|
||||||
|
participants: IParticipant[];
|
||||||
|
|
||||||
|
// online_address: Address;
|
||||||
|
// phone_address: string;
|
||||||
}
|
}
|
@ -93,13 +93,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { FETCH_EVENT } from "@/graphql/event";
|
import { DELETE_EVENT, FETCH_EVENT, LEAVE_EVENT } from '@/graphql/event';
|
||||||
import { Component, Prop, Vue } from "vue-property-decorator";
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
import VueMarkdown from "vue-markdown";
|
import { LOGGED_PERSON } from '@/graphql/actor';
|
||||||
import { LOGGED_PERSON } from "../../graphql/actor";
|
import { IEvent, IParticipant } from '@/types/event.model';
|
||||||
import { IEvent } from "@/types/event.model";
|
import { JOIN_EVENT } from '@/graphql/event';
|
||||||
import { JOIN_EVENT } from "../../graphql/event";
|
import { IPerson } from '@/types/actor.model';
|
||||||
import { IPerson } from "@/types/actor.model";
|
|
||||||
|
// No typings for this component, so we use require
|
||||||
|
const VueMarkdown = require('vue-markdown');
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
@ -126,31 +128,73 @@ export default class Event extends Vue {
|
|||||||
loggedPerson!: IPerson;
|
loggedPerson!: IPerson;
|
||||||
validationSent: boolean = false;
|
validationSent: boolean = false;
|
||||||
|
|
||||||
deleteEvent() {
|
async deleteEvent() {
|
||||||
const router = this.$router;
|
const router = this.$router;
|
||||||
// FIXME: remove eventFetch
|
|
||||||
// eventFetch(`/events/${this.uuid}`, this.$store, { method: 'DELETE' })
|
try {
|
||||||
// .then(() => router.push({ name: 'EventList' }));
|
await this.$apollo.mutate<IParticipant>({
|
||||||
|
mutation: DELETE_EVENT,
|
||||||
|
variables: {
|
||||||
|
id: this.event.id,
|
||||||
|
actorId: this.loggedPerson.id,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push({ name: 'EventList' })
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async joinEvent() {
|
async joinEvent() {
|
||||||
try {
|
try {
|
||||||
this.validationSent = true;
|
await this.$apollo.mutate<IParticipant>({
|
||||||
await this.$apollo.mutate({
|
mutation: JOIN_EVENT,
|
||||||
mutation: JOIN_EVENT
|
variables: {
|
||||||
|
id: this.event.id,
|
||||||
|
actorId: this.loggedPerson.id,
|
||||||
|
},
|
||||||
|
update: (store, { data: { joinEvent } }) => {
|
||||||
|
const event = store.readQuery<IEvent>({ query: FETCH_EVENT });
|
||||||
|
if (event === null) {
|
||||||
|
console.error('Cannot update event participant cache, because of null value.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event.participants = event.participants.concat([ joinEvent ]);
|
||||||
|
|
||||||
|
store.writeQuery({ query: FETCH_EVENT, data: event });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
leaveEvent() {
|
async leaveEvent() {
|
||||||
// FIXME: remove eventFetch
|
try {
|
||||||
// eventFetch(`/events/${this.uuid}/leave`, this.$store)
|
await this.$apollo.mutate<IParticipant>({
|
||||||
// .then(response => response.json())
|
mutation: LEAVE_EVENT,
|
||||||
// .then((data) => {
|
variables: {
|
||||||
// console.log(data);
|
id: this.event.id,
|
||||||
// });
|
actorId: this.loggedPerson.id,
|
||||||
|
},
|
||||||
|
update: (store, { data: { leaveEvent } }) => {
|
||||||
|
const event = store.readQuery<IEvent>({ query: FETCH_EVENT });
|
||||||
|
if (event === null) {
|
||||||
|
console.error('Cannot update event participant cache, because of null value.');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event.participants = event.participants
|
||||||
|
.filter(p => p.actor.id !== leaveEvent.actor.id);
|
||||||
|
|
||||||
|
store.writeQuery({ query: FETCH_EVENT, data: event });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadIcsEvent() {
|
downloadIcsEvent() {
|
||||||
@ -169,28 +213,16 @@ export default class Event extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
actorIsParticipant() {
|
actorIsParticipant() {
|
||||||
return (
|
if (this.actorIsOrganizer()) return true;
|
||||||
(this.loggedPerson &&
|
|
||||||
this.event.participants
|
return this.loggedPerson &&
|
||||||
.map(participant => participant.actor.preferredUsername)
|
this.event.participants
|
||||||
.includes(this.loggedPerson.preferredUsername)) ||
|
.some(participant => participant.actor.id === this.loggedPerson.id);
|
||||||
this.actorIsOrganizer()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
//
|
|
||||||
actorIsOrganizer() {
|
actorIsOrganizer() {
|
||||||
return (
|
return this.loggedPerson &&
|
||||||
this.loggedPerson &&
|
this.loggedPerson.id === this.event.organizerActor.id;
|
||||||
this.loggedPerson.preferredUsername ===
|
|
||||||
this.event.organizerActor.preferredUsername
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style>
|
|
||||||
.v-card__media__background {
|
|
||||||
filter: contrast(0.4);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
Loading…
Reference in New Issue
Block a user