Encore des commits degeu

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2018-06-06 17:42:53 +02:00
parent 90de5a105b
commit 63b63d9ee2
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
31 changed files with 3200 additions and 2937 deletions

5416
js/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@
"test:e2e": "vue-cli-service test:e2e" "test:e2e": "vue-cli-service test:e2e"
}, },
"dependencies": { "dependencies": {
"material-design-icons-iconfont": "^3.0.3", "material-design-icons": "^3.0.1",
"moment": "^2.22.1", "moment": "^2.22.1",
"ngeohash": "^0.6.0", "ngeohash": "^0.6.0",
"register-service-worker": "^1.0.0", "register-service-worker": "^1.0.0",

View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>eventos</title> <title>eventos</title>
</head> </head>
<body> <body>
<noscript> <noscript>

View File

@ -23,7 +23,7 @@
<v-avatar size="125px"> <v-avatar size="125px">
<img v-if="!account.avatar_url" <img v-if="!account.avatar_url"
class="img-circle elevation-7 mb-1" class="img-circle elevation-7 mb-1"
src="http://lorempixel.com/125/125/" src="https://picsum.photos/125/125/"
> >
<img v-else <img v-else
class="img-circle elevation-7 mb-1" class="img-circle elevation-7 mb-1"
@ -84,7 +84,7 @@
<v-card-media <v-card-media
class="black--text" class="black--text"
height="200px" height="200px"
src="http://lorempixel.com/400/200/" src="https://picsum.photos/400/200/"
> >
<v-container fill-height fluid> <v-container fill-height fluid>
<v-layout fill-height> <v-layout fill-height>
@ -125,7 +125,7 @@
<v-card-media <v-card-media
class="black--text" class="black--text"
height="200px" height="200px"
src="http://lorempixel.com/400/200/" src="https://picsum.photos/400/200/"
> >
<v-container fill-height fluid> <v-container fill-height fluid>
<v-layout fill-height> <v-layout fill-height>

View File

@ -28,54 +28,63 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-card-title> </v-card-title>
<v-container grid-list-md text-xs-center>
<v-card-media
src="https://picsum.photos/600/400/"
height="200px"
>
</v-card-media>
<v-layout row wrap>
<v-flex xs6>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<div class="text-xs-center"> <span class="subheading grey--text">{{ event.begins_on | formatDay }}</span>
<v-card-title class="pl-5 pt-5"> <h1 class="display-2">{{ event.title }}</h1>
<div class="display-1 pl-5 pt-5">{{ event.title }}</div> <div>
</v-card-title> <router-link :to="{name: 'Account', params: { name: event.organizer.username } }">
<v-avatar size="25px">
<img class="img-circle elevation-7 mb-1"
:src="event.organizer.avatar"
>
</v-avatar>
</router-link>
<span v-if="event.organizer">Organisé par {{ event.organizer.display_name }}</span>
</div>
<p>
<vue-markdown :source="event.description" />
</p>
<!--<p><router-link :to="{ name: 'Account', params: {id: event.organizer.id} }"><span class="grey&#45;&#45;text">{{ event.organizer.username }}</span></router-link> organises {{ event.title }} <span v-if="event.address.addressLocality">in {{ event.address.addressLocality }}</span> on the {{ event.startDate | formatDate }}.</p> <!--<p><router-link :to="{ name: 'Account', params: {id: event.organizer.id} }"><span class="grey&#45;&#45;text">{{ event.organizer.username }}</span></router-link> organises {{ event.title }} <span v-if="event.address.addressLocality">in {{ event.address.addressLocality }}</span> on the {{ event.startDate | formatDate }}.</p>
<v-card-text v-if="event.description"><vue-markdown :source="event.description"></vue-markdown></v-card-text>--> <v-card-text v-if="event.description"><vue-markdown :source="event.description"></vue-markdown></v-card-text>-->
</div> </v-flex>
<v-flex xs6>
<v-card-actions>
<v-btn color="success" v-if="!event.participants.map(participant => participant.id).includes($store.state.user.actor.id)" @click="joinEvent" class="btn btn-primary"><v-icon>check</v-icon> Join</v-btn>
<v-btn v-if="event.participants.map(participant => participant.id).includes($store.state.user.actor.id)" @click="leaveEvent" class="btn btn-primary">Leave</v-btn>
</v-card-actions>
</v-flex>
</v-layout>
</v-container>
<v-container fluid grid-list-md> <v-container fluid grid-list-md>
<v-subheader>Membres</v-subheader> <v-subheader>Membres</v-subheader>
<v-layout row> <v-layout row>
<v-flex xs2> <v-flex xs2 v-for="actor in event.participants" :key="actor.uuid">
<router-link :to="{name: 'Account', params: {'id': event.organizer.id}}"> <router-link :to="{name: 'Account', params: { name: actor.username }}">
<v-avatar size="75px"> <v-avatar size="75px">
<img v-if="!event.organizer.avatar_url" <img v-if="!actor.avatar"
class="img-circle elevation-7 mb-1" class="img-circle elevation-7 mb-1"
src="http://lorempixel.com/125/125/" src="https://picsum.photos/125/125/"
> >
<img v-else <img v-else
class="img-circle elevation-7 mb-1" class="img-circle elevation-7 mb-1"
:src="event.organizer.avatar_url" :src="actor.avatar"
>
</v-avatar>
</router-link>
Organisateur <span>{{ event.organizer.username }}</span>
</v-flex>
<v-flex xs2 v-for="actor in event.participants" :key="actor.id">
<router-link :to="{name: 'Account', params: {'id': actor.id}}">
<v-avatar size="75px">
<img v-if="!account.avatar_url"
class="img-circle elevation-7 mb-1"
src="http://lorempixel.com/125/125/"
>
<img v-else
class="img-circle elevation-7 mb-1"
:src="account.avatar_url"
> >
</v-avatar> </v-avatar>
</router-link> </router-link>
<span>{{ actor.username }}</span> <span>{{ actor.username }}</span>
</v-flex> </v-flex>
<span v-if="event.participants.length === 0">No participants yet.</span>
</v-layout> </v-layout>
</v-container> </v-container>
<v-card-actions>
<button v-if="!event.participants.map(participant => participant.id).includes($store.state.user.actor.id)" @click="joinEvent" class="btn btn-primary">Join</button>
<button v-if="event.participants.map(participant => participant.id).includes($store.state.user.actor.id)" @click="leaveEvent" class="btn btn-primary">Leave</button>
<button @click="deleteEvent" class="btn btn-danger">Delete</button>
</v-card-actions>
</v-layout> </v-layout>
</v-card> </v-card>
</v-flex> </v-flex>
@ -97,9 +106,10 @@
loading: true, loading: true,
error: false, error: false,
event: { event: {
name: this.name, name: '',
slug: this.slug, slug: '',
title: '', title: '',
uuid: this.uuid,
description: '', description: '',
organizer: { organizer: {
id: null, id: null,
@ -112,11 +122,11 @@
methods: { methods: {
deleteEvent() { deleteEvent() {
const router = this.$router; const router = this.$router;
eventFetch(`/events/${this.name}/${this.slug}`, this.$store, { method: 'DELETE' }) eventFetch(`/events/${this.uuid}`, this.$store, { method: 'DELETE' })
.then(() => router.push({'name': 'EventList'})); .then(() => router.push({'name': 'EventList'}));
}, },
fetchData() { fetchData() {
eventFetch(`/events/${this.name}/${this.slug}`, this.$store) eventFetch(`/events/${this.uuid}`, this.$store)
.then(response => response.json()) .then(response => response.json())
.then((data) => { .then((data) => {
this.loading = false; this.loading = false;
@ -130,21 +140,21 @@
}); });
}, },
joinEvent() { joinEvent() {
eventFetch(`/events/${this.name}/${this.slug}/join`, this.$store) eventFetch(`/events/${this.uuid}/join`, this.$store)
.then(response => response.json()) .then(response => response.json())
.then((data) => { .then((data) => {
console.log(data); console.log(data);
}); });
}, },
leaveEvent() { leaveEvent() {
eventFetch(`/events/${this.name}/${this.slug}/leave`, this.$store) eventFetch(`/events/${this.uuid}/leave`, this.$store)
.then(response => response.json()) .then(response => response.json())
.then((data) => { .then((data) => {
console.log(data); console.log(data);
}); });
}, },
downloadIcsEvent() { downloadIcsEvent() {
eventFetch(`/events/${this.name}/${this.slug}/ics`, this.$store, {responseType: 'arraybuffer'}) eventFetch(`/events/${this.uuid}/ics`, this.$store, {responseType: 'arraybuffer'})
.then((response) => response.text()) .then((response) => response.text())
.then(response => { .then(response => {
const blob = new Blob([response],{type: 'text/calendar'}); const blob = new Blob([response],{type: 'text/calendar'});
@ -158,14 +168,10 @@
}, },
}, },
props: { props: {
name: { uuid: {
type: String, type: String,
required: true, required: true,
}, },
slug: {
type: String,
required: true
},
}, },
created() { created() {
this.fetchData(); this.fetchData();

View File

@ -1,35 +1,43 @@
<template> <template>
<v-container> <v-layout>
<v-flex xs12 sm8 offset-sm2>
<v-card>
<h1>{{ $t("event.list.title") }}</h1> <h1>{{ $t("event.list.title") }}</h1>
<v-progress-circular v-if="loading" indeterminate color="primary"></v-progress-circular> <v-progress-circular v-if="loading" indeterminate color="primary"></v-progress-circular>
<v-chip close v-model="locationChip" label color="pink" text-color="white" v-if="$router.currentRoute.params.location"> <v-chip close v-model="locationChip" label color="pink" text-color="white" v-if="$router.currentRoute.params.location">
<v-icon left>location_city</v-icon>{{ locationText }} <v-icon left>location_city</v-icon>{{ locationText }}
</v-chip> </v-chip>
<v-layout row wrap justify-space-around> <v-container grid-list-sm fluid>
<v-flex xs12 md3 v-for="event in events" :key="event.id"> <v-layout row wrap>
<v-flex xs4 v-for="event in events" :key="event.id">
<v-card> <v-card>
<v-card-media v-if="event.image" <v-card-media v-if="!event.image"
class="white--text" class="white--text"
height="200px" height="200px"
src="http://lorempixel.com/400/200/" src="https://picsum.photos/g/400/200/"
> >
<v-container fill-height fluid> <v-container fill-height fluid>
<v-layout fill-height> <v-layout fill-height>
<v-flex xs12 align-end flexbox> <v-flex xs12 align-end flexbox>
<span class="headline">{{ event.title }}</span> <span class="headline black--text">{{ event.title }}</span>
</v-flex> </v-flex>
</v-layout> </v-layout>
</v-container> </v-container>
</v-card-media> </v-card-media>
<v-card-title v-else primary-title> <v-card-title primary-title>
<div class="headline">{{ event.title }}</div> <div>
</v-card-title> <span class="grey--text">{{ event.begins_on | formatDate }}</span><br>
<v-container> <router-link :to="{name: 'Account', params: { name: event.organizer.username } }">
<!--<span class="grey&#45;&#45;text">{{ event.startDate | formatDate }} à <router-link :to="{name: 'EventList', params: {location: geocode(event.address.geo.latitude, event.address.geo.longitude, 10) }}">{{ event.address.addressLocality }}</router-link></span><br>--> <v-avatar size="25px">
<p><vue-markdown>{{ event.description }}</vue-markdown></p> <img class="img-circle elevation-7 mb-1"
<p v-if="event.organizer">Organisé par <router-link :to="{name: 'Account', params: {'name': event.organizer.username}}">{{ event.organizer.username }}</router-link></p> :src="event.organizer.avatar"
</v-container> >
</v-avatar>
</router-link>
<span v-if="event.organizer">Organisé par <router-link :to="{name: 'Account', params: {'name': event.organizer.username}}">{{ event.organizer.username }}</router-link></span>
</div>
</v-card-title>
<v-card-actions> <v-card-actions>
<v-btn flat color="orange" @click="downloadIcsEvent(event)">Share</v-btn> <v-btn flat color="orange" @click="downloadIcsEvent(event)">Share</v-btn>
<v-btn flat color="orange" @click="viewEvent(event)">Explore</v-btn> <v-btn flat color="orange" @click="viewEvent(event)">Explore</v-btn>
@ -38,8 +46,11 @@
</v-card> </v-card>
</v-flex> </v-flex>
</v-layout> </v-layout>
</v-container>
<router-link :to="{ name: 'CreateEvent' }" class="btn btn-default">Create</router-link> <router-link :to="{ name: 'CreateEvent' }" class="btn btn-default">Create</router-link>
</v-container> </v-card>
</v-flex>
</v-layout>
</template> </template>
<script> <script>
@ -96,18 +107,19 @@
.then((response) => { .then((response) => {
this.loading = false; this.loading = false;
this.events = response.data; this.events = response.data;
console.log(this.events);
}); });
}, },
deleteEvent(event) { deleteEvent(event) {
const router = this.$router; const router = this.$router;
eventFetch(`/events/${event.organizer.username}/${event.slug}`, this.$store, {'method': 'DELETE'}) eventFetch(`/events/${event.uuid}`, this.$store, {'method': 'DELETE'})
.then(() => router.push('/events')); .then(() => router.push('/events'));
}, },
viewEvent(event) { viewEvent(event) {
this.$router.push({ name: 'Event', params: { name: event.organizer.username, slug: event.slug } }) this.$router.push({ name: 'Event', params: { uuid: event.uuid } })
}, },
downloadIcsEvent(event) { downloadIcsEvent(event) {
eventFetch(`/events/${event.organizer.username}/${event.slug}/export`, this.$store, {responseType: 'arraybuffer'}) eventFetch(`/events/${event.uuid}/ics`, this.$store, {responseType: 'arraybuffer'})
.then((response) => response.text()) .then((response) => response.text())
.then(response => { .then(response => {
const blob = new Blob([response],{type: 'text/calendar'}); const blob = new Blob([response],{type: 'text/calendar'});

View File

@ -4,37 +4,57 @@
<v-flex xs12 sm6 offset-sm3> <v-flex xs12 sm6 offset-sm3>
<v-progress-circular v-if="loading" indeterminate color="primary"></v-progress-circular> <v-progress-circular v-if="loading" indeterminate color="primary"></v-progress-circular>
<v-card v-if="!loading"> <v-card v-if="!loading">
<v-layout column class="media"> <v-card-media :src="group.banner" height="400px">
<v-card-title> <v-layout column class="media">
<v-btn icon @click="$router.go(-1)"> <v-card-title>
<v-icon>chevron_left</v-icon> <v-btn icon @click="$router.go(-1)">
</v-btn> <v-icon>chevron_left</v-icon>
<v-spacer></v-spacer> </v-btn>
<v-btn icon class="mr-3" v-if="$store.state.user"> <v-spacer></v-spacer>
<v-icon>edit</v-icon> <!--<v-btn icon class="mr-3" v-if="$store.state.user && $store.state.user.actor.id === actor.id">-->
</v-btn> <!--<v-icon>edit</v-icon>-->
<v-btn icon> <!--</v-btn>-->
<v-icon>more_vert</v-icon> <v-btn icon>
</v-btn> <v-icon>more_vert</v-icon>
</v-card-title> </v-btn>
<v-spacer></v-spacer>
<div class="text-xs-center">
<v-avatar size="125px">
<img v-if="!group.avatar_url"
class="img-circle elevation-7 mb-1"
src="http://lorempixel.com/125/125/"
>
<img v-else
class="img-circle elevation-7 mb-1"
:src="group.avatar_url"
>
</v-avatar>
<v-card-title class="pl-5 pt-5">
<div class="display-1 pl-5 pt-5">{{ group.title }}<span v-if="group.server">@{{ group.server.address }}</span></div>
</v-card-title> </v-card-title>
<v-card-text v-html="group.description"></v-card-text> <v-spacer></v-spacer>
</div> <div class="text-xs-center">
</v-layout> <v-avatar size="125px">
<img v-if="!group.avatar"
class="img-circle elevation-7 mb-1"
src="https://picsum.photos/125/125/"
>
<img v-else
class="img-circle elevation-7 mb-1"
:src="group.avatar"
>
</v-avatar>
</div>
<v-container fluid grid-list-lg>
<v-layout row>
<v-flex xs7>
<div class="headline">{{ group.display_name }}</div>
<div>
<span class="subheading">
~{{ group.username }}
<span v-if="group.domain">
@{{ group.domain }}
</span>
</span>
<v-chip color="indigo" text-color="white">
<v-avatar>
<v-icon>group</v-icon>
</v-avatar>
Group
</v-chip>
</div>
<v-card-text v-if="group.description" v-html="group.description"></v-card-text>
</v-flex>
</v-layout>
</v-container>
</v-layout>
</v-card-media>
<v-list three-line> <v-list three-line>
<v-list-tile> <v-list-tile>
<v-list-tile-action> <v-list-tile-action>
@ -59,48 +79,90 @@
</v-list-tile-content> </v-list-tile-content>
</v-list-tile> </v-list-tile>
<v-divider inset></v-divider> <v-divider inset></v-divider>
<v-list-tile v-if="group.address"> <v-list-tile>
<v-list-tile-action> <v-list-tile-action>
<v-icon color="indigo">location_on</v-icon> <v-icon color="indigo">location_on</v-icon>
</v-list-tile-action> </v-list-tile-action>
<v-list-tile-content> <v-list-tile-content>
<v-list-tile-title>{{ group.address.streetAddress }}</v-list-tile-title> <v-list-tile-title>1400 Main Street</v-list-tile-title>
<v-list-tile-sub-title>{{ group.address.postalCode }} {{ group.address.locality }}</v-list-tile-sub-title> <v-list-tile-sub-title>Orlando, FL 79938</v-list-tile-sub-title>
</v-list-tile-content> </v-list-tile-content>
</v-list-tile> </v-list-tile>
</v-list> </v-list>
<v-container fluid grid-list-md v-if="group.members.length > 0"> <v-container fluid grid-list-md v-if="group.members.length > 0">
<v-subheader>Membres</v-subheader> <v-subheader>Membres</v-subheader>
<v-layout row> <v-layout row>
<v-flex xs2 v-for="member in group.members" :key="member.id"> <v-flex xs2 v-for="member in group.members" :key="member.actor.username">
<router-link :to="{name: 'Account', params: {'id': member.actor.id}}"> <router-link :to="{name: 'Account', params: { name: member.actor.username } }">
<v-badge overlap> <v-badge overlap>
<span slot="badge" v-if="member.role == 3"><v-icon>stars</v-icon></span> <span slot="badge" v-if="member.role === 1"><v-icon>star_half</v-icon></span>
<span slot="badge" v-if="member.role === 2"><v-icon>star</v-icon></span>
<v-avatar size="75px"> <v-avatar size="75px">
<img v-if="!member.account.avatar_url" <img v-if="!member.actor.avatar"
class="img-circle elevation-7 mb-1" class="img-circle elevation-7 mb-1"
src="http://lorempixel.com/125/125/" src="https://picsum.photos/125/125/"
> >
<img v-else <img v-else
class="img-circle elevation-7 mb-1" class="img-circle elevation-7 mb-1"
:src="member.account.avatar_url" :src="member.actor.avatar"
> >
</v-avatar> </v-avatar>
</v-badge> </v-badge>
</router-link> </router-link>
<span>{{ groupAccount.actor.username }}</span> <span>{{ member.actor.username }}</span>
</v-flex> </v-flex>
</v-layout> </v-layout>
</v-container> </v-container>
<v-container fluid grid-list-md v-if="group.events.length > 0"> <v-container fluid grid-list-md v-if="group.participatingEvents && group.participatingEvents.length > 0">
<v-subheader>Participated at</v-subheader> <v-subheader>Participated at</v-subheader>
<v-layout row wrap> <v-layout row wrap>
<v-flex v-for="event in group.events" :key="event.id"> <v-flex v-for="event in group.participatingEvents" :key="event.id">
<v-card> <v-card>
<v-card-media <v-card-media
class="black--text" class="black--text"
height="200px" height="200px"
src="http://lorempixel.com/400/200/" src="https://picsum.photos/400/200/"
>
<v-container fill-height fluid>
<v-layout fill-height>
<v-flex xs12 align-end flexbox>
<span class="headline">{{ event.title }}</span>
</v-flex>
</v-layout>
</v-container>
</v-card-media>
<v-card-title>
<div>
<span class="grey--text">{{ event.startDate | formatDate }} à {{ event.location }}</span><br>
<p>{{ event.description }}</p>
<p v-if="event.organizer">Organisé par <router-link :to="{name: 'Account', params: {'id': event.organizer.id}}">{{ event.organizer.username }}</router-link></p>
</div>
</v-card-title>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn icon>
<v-icon>favorite</v-icon>
</v-btn>
<v-btn icon>
<v-icon>bookmark</v-icon>
</v-btn>
<v-btn icon>
<v-icon>share</v-icon>
</v-btn>
</v-card-actions>
</v-card>
</v-flex>
</v-layout>
</v-container>
<v-container fluid grid-list-md v-if="group.organizingEvents && group.organizingEvents.length > 0">
<v-subheader>Organized events</v-subheader>
<v-layout row wrap>
<v-flex v-for="event in group.organizingEvents" :key="event.id">
<v-card>
<v-card-media
class="black--text"
height="200px"
src="https://picsum.photos/400/200/"
> >
<v-container fill-height fluid> <v-container fill-height fluid>
<v-layout fill-height> <v-layout fill-height>
@ -140,40 +202,39 @@
</template> </template>
<script> <script>
import eventFetch from '@/api/eventFetch';
import eventFetch from '@/api/eventFetch'; export default {
name: 'Group',
export default { data() {
name: 'Group', return {
props: ['id'], group: null,
data() { loading: true,
return { }
group: {
id: this.id,
title: '',
description: '',
}, },
loading: true, props: {
} name: {
}, type: String,
methods: { required: true,
fetchData() { }
eventFetch(`/groups/${this.id}`, this.$store) },
.then(response => response.json()) created() {
.then((data) => { this.fetchData();
this.loading = false; },
this.group = data.data; watch: {
}); // call again the method if the route changes
}, '$route': 'fetchData'
deleteGroup() { },
const router = this.$router; methods: {
eventFetch(`/groups/${this.id}`, this.$store, { method: 'DELETE' }) fetchData() {
.then(response => response.json()) eventFetch(`/actors/${this.name}`, this.$store)
.then(() => router.push('/groups')); .then(response => response.json())
}, .then((response) => {
}, this.group = response.data;
created() { this.loading = false;
this.fetchData(); console.log(this.group);
}, })
} }
}
}
</script> </script>

View File

@ -9,7 +9,7 @@
<v-card-media <v-card-media
class="black--text" class="black--text"
height="200px" height="200px"
src="http://lorempixel.com/400/200/" src="https://picsum.photos/400/200/"
> >
<v-container fill-height fluid> <v-container fill-height fluid>
<v-layout fill-height> <v-layout fill-height>
@ -26,9 +26,9 @@
</div> </div>
</v-card-title> </v-card-title>
<v-card-actions> <v-card-actions>
<v-btn flat color="green" @click="joinGroup(group.id)"><v-icon v-if="group.locked">lock</v-icon>Join</v-btn> <v-btn flat color="green" @click="joinGroup(group)"><v-icon v-if="group.locked">lock</v-icon>Join</v-btn>
<v-btn flat color="orange" @click="viewActor(group)">Explore</v-btn> <v-btn flat color="orange" @click="viewActor(group)">Explore</v-btn>
<v-btn flat color="red" @click="deleteEvent(group.id)">Delete</v-btn> <v-btn flat color="red" @click="deleteGroup(group)">Delete</v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-flex> </v-flex>
@ -64,20 +64,20 @@
this.groups = data.data; this.groups = data.data;
}); });
}, },
deleteEvent(id) { deleteGroup(group) {
const router = this.$router; const router = this.$router;
eventFetch('/groups/' + id, this.$store, {'method': 'DELETE'}) eventFetch(`/groups/${this.username_with_domain(group)}`, this.$store, {'method': 'DELETE'})
.then(response => response.json()) .then(response => response.json())
.then(() => router.push('/groups')); .then(() => router.push('/groups'));
}, },
viewActor(actor) { viewActor(actor) {
this.$router.push({ name: 'Account', params: { name: this.username_with_domain(actor) } }) this.$router.push({ name: 'Group', params: { name: this.username_with_domain(actor) } })
}, },
joinGroup(id) { joinGroup(group) {
const router = this.$router; const router = this.$router;
eventFetch('/groups/' + id + '/join', this.$store) eventFetch(`/groups/${this.username_with_domain(group)}/join`, this.$store, { method: 'POST' })
.then(response => response.json()) .then(response => response.json())
.then(() => router.push('/group/' + id)) .then(() => router.push({ name: 'Group', params: { name: this.username_with_domain(group) } }));
} }
}, },
}; };

View File

@ -1,10 +1,59 @@
<template> <template>
<v-container> <v-container>
<h1 class="welcome" v-if="$store.state.user">{{ $t("home.welcome", { 'username': this.displayed_name }) }}</h1> <v-jumbotron
<h1 class="welcome" v-else>{{ $t("home.welcome_off", { 'username': $store.state.user.username}) }}</h1> :gradient="gradient"
<router-link :to="{ name: 'EventList' }">{{ $t('home.events') }}</router-link> src="https://picsum.photos/1200/900"
<router-link v-if="$store.state.user === false" :to="{ name: 'Login' }">{{ $t('home.login') }}</router-link> dark
<router-link v-if="$store.state.user === false" :to="{ name: 'Register' }">{{ $t('home.register') }}</router-link> v-if="$store.state.user === false"
>
<v-container fill-height>
<v-layout align-center>
<v-flex text-xs-center>
<h1 class="display-3">Find events you like</h1>
<h2>Share it with Eventos</h2>
<v-btn>{{ $t("home.register") }}</v-btn>
</v-flex>
</v-layout>
</v-container>
</v-jumbotron>
<v-layout>
<v-flex xs12 sm8 offset-sm2>
<v-card>
<v-layout row wrap>
<v-flex xs4 v-for="event in events" :key="event.uuid">
<v-card :to="{ name: 'Event', params:{ uuid: event.uuid } }">
<v-card-media v-if="!event.image"
class="white--text"
height="200px"
src="https://picsum.photos/g/400/200/"
>
<v-container fill-height fluid>
<v-layout fill-height>
<v-flex xs12 align-end flexbox>
<span class="headline black--text">{{ event.title }}</span>
</v-flex>
</v-layout>
</v-container>
</v-card-media>
<v-card-title primary-title>
<div>
<span class="grey--text">{{ event.begins_on | formatDate }}</span><br>
<router-link :to="{name: 'Account', params: { name: event.organizer.username } }">
<v-avatar size="25px">
<img class="img-circle elevation-7 mb-1"
:src="event.organizer.avatar"
>
</v-avatar>
</router-link>
<span v-if="event.organizer">Organisé par {{ event.organizer.display_name }}</span>
</div>
</v-card-title>
</v-card>
</v-flex>
</v-layout>
</v-card>
</v-flex>
</v-layout>
<v-layout row> <v-layout row>
<v-flex xs6> <v-flex xs6>
<v-btn large @click="geoLocalize"><v-icon>my_location</v-icon>Me géolocaliser</v-btn> <v-btn large @click="geoLocalize"><v-icon>my_location</v-icon>Me géolocaliser</v-btn>
@ -36,6 +85,7 @@ export default {
name: 'Home', name: 'Home',
data() { data() {
return { return {
gradient: 'to top right, rgba(63,81,181, .7), rgba(25,32,72, .7)',
user: null, user: null,
searchTerm: null, searchTerm: null,
location_field: { location_field: {
@ -43,10 +93,11 @@ export default {
search: null, search: null,
}, },
locations: [], locations: [],
events: [],
}; };
}, },
mounted() { created() {
// this.fetchLocations(); this.fetchData();
}, },
computed: { computed: {
displayed_name: function() { displayed_name: function() {
@ -61,6 +112,14 @@ export default {
this.locations = response; this.locations = response;
}); });
}, },
fetchData() {
eventFetch('/events', this.$store)
.then(response => response.json())
.then((response) => {
this.loading = false;
this.events = response.data;
});
},
geoLocalize() { geoLocalize() {
const router = this.$router; const router = this.$router;
if (sessionStorage.getItem('City')) { if (sessionStorage.getItem('City')) {
@ -85,6 +144,9 @@ export default {
sessionStorage.setItem('City', geohash); sessionStorage.setItem('City', geohash);
this.$router.push({name: 'EventList', params: {location: geohash}}); this.$router.push({name: 'EventList', params: {location: geohash}});
}, },
viewEvent(event) {
this.$router.push({ name: 'Event', params: { uuid: event.uuid } })
},
}, },
}; };
</script> </script>

View File

@ -9,7 +9,7 @@
<v-toolbar-title style="width: 300px" class="ml-0 pl-3"> <v-toolbar-title style="width: 300px" class="ml-0 pl-3">
<v-toolbar-side-icon @click.stop="drawer = !drawer"></v-toolbar-side-icon> <v-toolbar-side-icon @click.stop="drawer = !drawer"></v-toolbar-side-icon>
<router-link :to="{ name: 'Home' }"> <router-link :to="{ name: 'Home' }">
Libre-Event Eventos
</router-link> </router-link>
</v-toolbar-title> </v-toolbar-title>
<v-select <v-select

View File

@ -1,7 +1,7 @@
export default { export default {
home: { home: {
welcome: 'Welcome on Libre-Event, {username}', welcome: 'Welcome on Eventos, {username}',
welcome_off: 'Welcome on Libre-Event', welcome_off: 'Welcome on Eventos',
events: 'Events', events: 'Events',
groups: 'Groups', groups: 'Groups',
login: 'Login', login: 'Login',

View File

@ -1,7 +1,7 @@
export default { export default {
home: { home: {
welcome: 'Bienvenue sur Libre-Event, {username}!', welcome: 'Bienvenue sur Eventos, {username}!',
welcome_off: 'Bienvenue sur Libre-Event', welcome_off: 'Bienvenue sur Eventos',
events: 'Événements', events: 'Événements',
groups: 'Groupes', groups: 'Groupes',
login: 'Se connecter', login: 'Se connecter',

View File

@ -8,8 +8,8 @@ import Vuetify from 'vuetify';
import Vuex from 'vuex'; import Vuex from 'vuex';
import moment from 'moment'; import moment from 'moment';
import VuexI18n from 'vuex-i18n'; import VuexI18n from 'vuex-i18n';
import 'material-design-icons/iconfont/material-icons.css';
import 'vuetify/dist/vuetify.min.css'; import 'vuetify/dist/vuetify.min.css';
import 'material-design-icons-iconfont/dist/material-design-icons.css';
import App from '@/App'; import App from '@/App';
import router from '@/router'; import router from '@/router';
import storeData from '@/store/index'; import storeData from '@/store/index';
@ -37,6 +37,7 @@ let language = window.navigator.userLanguage || window.navigator.language;
moment.locale(language); moment.locale(language);
Vue.filter('formatDate', value => (value ? moment(String(value)).format('LLLL') : null)); Vue.filter('formatDate', value => (value ? moment(String(value)).format('LLLL') : null));
Vue.filter('formatDay', value => (value ? moment(String(value)).format('LL') : null));
if (!(language in translations)) { if (!(language in translations)) {
[language] = language.split('-', 1); [language] = language.split('-', 1);

View File

@ -103,7 +103,7 @@ const router = new Router({
meta: { requiredAuth: false }, meta: { requiredAuth: false },
}, },
{ {
path: '/@:name/:slug', path: '/events/:uuid',
name: 'Event', name: 'Event',
component: Event, component: Event,
props: true, props: true,

View File

@ -133,6 +133,15 @@ defmodule Eventos.Actors do
Repo.all(from a in Actor, where: a.type == "Group") Repo.all(from a in Actor, where: a.type == "Group")
end end
def get_group_by_name(name) do
actor = case String.split(name, "@") do
[name] ->
Repo.get_by(Actor, preferred_username: name, type: :Group)
[name, domain] ->
Repo.get_by(Actor, preferred_username: name, domain: domain, type: :Group)
end
end
@doc """ @doc """
Creates a group. Creates a group.
@ -176,7 +185,7 @@ defmodule Eventos.Actors do
def insert_or_update_actor(data) do def insert_or_update_actor(data) do
cs = Actor.remote_actor_creation(data) cs = Actor.remote_actor_creation(data)
Repo.insert(cs, on_conflict: [set: [public_key: data.public_key, avatar_url: data.avatar_url, banner: data.banner_url, name: data.name]], conflict_target: [:preferred_username, :domain]) Repo.insert(cs, on_conflict: [set: [public_key: data.public_key, avatar_url: data.avatar_url, banner_url: data.banner_url, name: data.name]], conflict_target: [:preferred_username, :domain])
end end
# def increase_event_count(%Actor{} = actor) do # def increase_event_count(%Actor{} = actor) do
@ -494,7 +503,8 @@ defmodule Eventos.Actors do
def create_member(attrs \\ %{}) do def create_member(attrs \\ %{}) do
%Member{} %Member{}
|> Member.changeset(attrs) |> Member.changeset(attrs)
|> Repo.insert() |> Repo.insert!()
|> Repo.preload([:actor, :parent])
end end
@doc """ @doc """
@ -544,6 +554,22 @@ defmodule Eventos.Actors do
Member.changeset(member, %{}) Member.changeset(member, %{})
end end
def groups_for_actor(%Actor{id: id} = _actor) do
Repo.all(
from m in Member,
where: m.actor_id == ^id,
preload: [:parent]
)
end
def members_for_group(%Actor{type: :Group, id: id} = _group) do
Repo.all(
from m in Member,
where: m.parent_id == ^id,
preload: [:parent, :actor]
)
end
alias Eventos.Actors.Bot alias Eventos.Actors.Bot

View File

@ -7,10 +7,10 @@ defmodule Eventos.Actors.Member do
alias Eventos.Actors.Member alias Eventos.Actors.Member
alias Eventos.Actors.Actor alias Eventos.Actors.Actor
@primary_key false
schema "members" do schema "members" do
field :approved, :boolean field :approved, :boolean, default: true
field :role, :integer field :role, :integer, default: 0 # 0 : Member, 1 : Moderator, 2 : Admin
belongs_to :parent, Actor belongs_to :parent, Actor
belongs_to :actor, Actor belongs_to :actor, Actor
@ -20,7 +20,7 @@ defmodule Eventos.Actors.Member do
@doc false @doc false
def changeset(%Member{} = member, attrs) do def changeset(%Member{} = member, attrs) do
member member
|> cast(attrs, [:role]) |> cast(attrs, [:role, :approved, :parent_id, :actor_id])
|> validate_required([:role]) |> validate_required([:parent_id, :actor_id])
end end
end end

View File

@ -26,6 +26,5 @@ defmodule Eventos.Addresses.Address do
def changeset(%Address{} = address, attrs) do def changeset(%Address{} = address, attrs) do
address address
|> cast(attrs, [:description, :floor, :geom, :addressCountry, :addressLocality, :addressRegion, :postalCode, :streetAddress]) |> cast(attrs, [:description, :floor, :geom, :addressCountry, :addressLocality, :addressRegion, :postalCode, :streetAddress])
|> validate_required([:geom])
end end
end end

View File

@ -51,6 +51,7 @@ defmodule Eventos.Events.Event do
field :thumbnail, :string field :thumbnail, :string
field :large_image, :string field :large_image, :string
field :publish_at, Timex.Ecto.DateTimeWithTimezone field :publish_at, Timex.Ecto.DateTimeWithTimezone
field :uuid, Ecto.UUID, default: Ecto.UUID.generate()
belongs_to :organizer_actor, Actor, [foreign_key: :organizer_actor_id] belongs_to :organizer_actor, Actor, [foreign_key: :organizer_actor_id]
many_to_many :tags, Tag, join_through: "events_tags" many_to_many :tags, Tag, join_through: "events_tags"
belongs_to :category, Category belongs_to :category, Category
@ -64,12 +65,17 @@ defmodule Eventos.Events.Event do
@doc false @doc false
def changeset(%Event{} = event, attrs) do def changeset(%Event{} = event, attrs) do
event changeset = event
|> cast(attrs, [:title, :description, :url, :begins_on, :ends_on, :organizer_actor_id, :category_id, :state, :status, :public, :thumbnail, :large_image, :publish_at]) |> cast(attrs, [:title, :description, :url, :begins_on, :ends_on, :organizer_actor_id, :category_id, :state, :status, :public, :thumbnail, :large_image, :publish_at])
|> cast_assoc(:tags) |> cast_assoc(:tags)
|> cast_assoc(:address) |> cast_assoc(:address)
|> validate_required([:title, :description, :begins_on, :ends_on, :organizer_actor_id, :category_id]) |> validate_required([:title, :description, :begins_on, :ends_on, :organizer_actor_id, :category_id])
|> TitleSlug.maybe_generate_slug() |> TitleSlug.maybe_generate_slug()
|> TitleSlug.unique_constraint() |> TitleSlug.unique_constraint()
|> put_change(:uuid, Ecto.UUID.generate())
import Logger
Logger.debug(inspect changeset)
changeset
end end
end end

View File

@ -77,6 +77,13 @@ defmodule Eventos.Events do
Repo.get_by(Event, url: url) Repo.get_by(Event, url: url)
end end
@doc """
Gets an event by it's UUID
"""
def get_event_by_uuid(uuid) do
Repo.get_by(Event, uuid: uuid)
end
@doc """ @doc """
Gets a single event, with all associations loaded. Gets a single event, with all associations loaded.
""" """
@ -93,6 +100,14 @@ defmodule Eventos.Events do
Repo.preload(event, [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :address]) Repo.preload(event, [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :address])
end end
@doc """
Gets a full event by it's UUID
"""
def get_event_full_by_uuid(uuid) do
event = Repo.get_by(Event, uuid: uuid)
Repo.preload(event, [:organizer_actor, :category, :sessions, :tracks, :tags, :participants, :address])
end
@spec get_event_full_by_name_and_slug!(String.t, String.t) :: Event.t @spec get_event_full_by_name_and_slug!(String.t, String.t) :: Event.t
def get_event_full_by_name_and_slug!(name, slug) do def get_event_full_by_name_and_slug!(name, slug) do
query = case String.split(name, "@") do query = case String.split(name, "@") do
@ -139,7 +154,8 @@ defmodule Eventos.Events do
def create_event(attrs \\ %{}) do def create_event(attrs \\ %{}) do
%Event{} %Event{}
|> Event.changeset(attrs) |> Event.changeset(attrs)
|> Repo.insert() |> Repo.insert!()
|> Repo.preload([:organizer_actor])
end end
@doc """ @doc """

View File

@ -9,7 +9,7 @@ defmodule Eventos.Events.Participant do
@primary_key false @primary_key false
schema "participants" do schema "participants" do
field :role, :integer field :role, :integer, default: 0 # 0 : participant, 1 : moderator, 2 : administrator, 3 : creator
field :approved, :boolean field :approved, :boolean
belongs_to :event, Event, primary_key: true belongs_to :event, Event, primary_key: true
belongs_to :actor, Actor, primary_key: true belongs_to :actor, Actor, primary_key: true

View File

@ -11,7 +11,8 @@ defmodule Eventos.Export.ICalendar do
summary: event.title, summary: event.title,
dtstart: event.begins_on, dtstart: event.begins_on,
dtend: event.ends_on, dtend: event.ends_on,
description: event.description description: event.description,
uid: event.uuid
}] }]
%ICalendar{events: events} %ICalendar{events: events}
|> ICalendar.to_ics() |> ICalendar.to_ics()

View File

@ -27,6 +27,27 @@ defmodule EventosWeb.AddressController do
end end
end end
def process_geom(%{"type" => type, "data" => data}) do
import Logger
Logger.debug("Process geom")
Logger.debug(inspect data)
Logger.debug(inspect type)
types = [:point]
unless is_atom(type) do
type = String.to_existing_atom(type)
end
case type do
:point ->
%Geo.Point{coordinates: {data["latitude"], data["longitude"]}, srid: 4326}
nil ->
nil
end
end
def process_geom(nil) do
nil
end
def show(conn, %{"id" => id}) do def show(conn, %{"id" => id}) do
address = Addresses.get_address!(id) address = Addresses.get_address!(id)
render(conn, "show.json", address: address) render(conn, "show.json", address: address)

View File

@ -17,18 +17,30 @@ defmodule EventosWeb.EventController do
end end
def create(conn, %{"event" => event_params}) do def create(conn, %{"event" => event_params}) do
event_params = %{event_params | "address" => process_address(event_params["address"])} address = process_address(event_params["address"])
event_params = if is_nil address do
event_params
else
%{event_params | "address" => address}
end
with {:ok, %Event{} = event} <- Events.create_event(event_params) do with {:ok, %Event{} = event} <- Events.create_event(event_params) do
conn conn
|> put_status(:created) |> put_status(:created)
|> put_resp_header("location", event_path(conn, :show, event)) |> put_resp_header("location", event_path(conn, :show, event.uuid))
|> render("show_simple.json", event: event) |> render("show_simple.json", event: event)
end end
end end
defp process_address(address) do defp process_address(address) do
case Addresses.process_geom(address["geom"]) do import Logger
{:ok, geom} -> Logger.debug("process address")
Logger.debug(inspect address)
geom = EventosWeb.AddressController.process_geom(address["geom"])
Logger.debug(inspect geom)
case geom do
nil ->
address
_ ->
%{address | "geom" => geom} %{address | "geom" => geom}
_ -> _ ->
address address
@ -40,27 +52,26 @@ defmodule EventosWeb.EventController do
render(conn, "index.json", events: events) render(conn, "index.json", events: events)
end end
def show(conn, %{"username" => username, "slug" => slug}) do def show(conn, %{"uuid" => uuid}) do
event = Events.get_event_full_by_name_and_slug!(username, slug) event = Events.get_event_full_by_uuid(uuid)
render(conn, "show.json", event: event) render(conn, "show.json", event: event)
end end
def export_to_ics(conn, %{"username" => username, "slug" => slug}) do def export_to_ics(conn, %{"uuid" => uuid}) do
event = Events.get_event_full_by_name_and_slug!(username, slug) event = Events.get_event_full_by_uuid(uuid) |> ICalendar.export_event()
|> ICalendar.export_event()
send_resp(conn, 200, event) send_resp(conn, 200, event)
end end
def update(conn, %{"username" => username, "slug" => slug, "event" => event_params}) do def update(conn, %{"uuid" => uuid, "event" => event_params}) do
event = Events.get_event_full_by_name_and_slug!(username, slug) event = Events.get_event_full_by_uuid(uuid)
with {:ok, %Event{} = event} <- Events.update_event(event, event_params) do with {:ok, %Event{} = event} <- Events.update_event(event, event_params) do
render(conn, "show_simple.json", event: event) render(conn, "show_simple.json", event: event)
end end
end end
def delete(conn, %{"username" => username, "slug" => slug}) do def delete(conn, %{"uuid" => uuid}) do
event = Events.get_event_full_by_name_and_slug!(username, slug) event = Events.get_event_by_uuid(uuid)
with {:ok, %Event{}} <- Events.delete_event(event) do with {:ok, %Event{}} <- Events.delete_event(event) do
send_resp(conn, :no_content, "") send_resp(conn, :no_content, "")
end end

View File

@ -5,7 +5,7 @@ defmodule EventosWeb.GroupController do
use EventosWeb, :controller use EventosWeb, :controller
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Actors.Actor alias Eventos.Actors.{Actor, Member}
action_fallback EventosWeb.FallbackController action_fallback EventosWeb.FallbackController
@ -23,4 +23,18 @@ defmodule EventosWeb.GroupController do
|> render(EventosWeb.ActorView, "acccount_basic.json", actor: group) |> render(EventosWeb.ActorView, "acccount_basic.json", actor: group)
end end
end end
def join(conn, %{"name" => group_name}) do
with actor = Guardian.Plug.current_resource(conn).actor,
group <- Actors.get_group_by_name(group_name),
%Member{} = member <- Actors.create_member(%{"parent_id" => group.id, "actor_id" => actor.id}) do
conn
|> put_status(:created)
|> render(EventosWeb.MemberView, "member.json", member: member)
else
err ->
import Logger
Logger.debug(inspect err)
end
end
end end

View File

@ -40,10 +40,10 @@ defmodule EventosWeb.Router do
get "/groups", GroupController, :index get "/groups", GroupController, :index
get "/events", EventController, :index get "/events", EventController, :index
get "/events/search/:name", EventController, :search get "/events/search/:name", EventController, :search
get "/events/:username/:slug", EventController, :show get "/events/:uuid/ics", EventController, :export_to_ics
get "/events/:username/:slug/ics", EventController, :export_to_ics get "/events/:uuid/tracks", TrackController, :show_tracks_for_event
get "/events/:username/:slug/tracks", TrackController, :show_tracks_for_event get "/events/:uuid/sessions", SessionController, :show_sessions_for_event
get "/events/:username/:slug/sessions", SessionController, :show_sessions_for_event get "/events/:uuid", EventController, :show
resources "/comments", CommentController, only: [:show] resources "/comments", CommentController, only: [:show]
get "/bots/:id", BotController, :view get "/bots/:id", BotController, :view
@ -71,18 +71,17 @@ defmodule EventosWeb.Router do
resources "/users", UserController, except: [:new, :edit, :show] resources "/users", UserController, except: [:new, :edit, :show]
patch "/actors/:name", ActorController, :update patch "/actors/:name", ActorController, :update
post "/events", EventController, :create post "/events", EventController, :create
patch "/events/:username/:slug", EventController, :update patch "/events/:uuid", EventController, :update
put "/events/:username/:slug", EventController, :update put "/events/:uuid", EventController, :update
delete "/events/:username/:slug", EventController, :delete delete "/events/:uuid", EventController, :delete
resources "/comments", CommentController, except: [:new, :edit, :show] resources "/comments", CommentController, except: [:new, :edit, :show]
#post "/events/:id/request", EventRequestController, :create_for_event #post "/events/:id/request", EventRequestController, :create_for_event
resources "/participant", ParticipantController resources "/participant", ParticipantController
resources "/bots", BotController, except: [:new, :edit, :show] resources "/bots", BotController, except: [:new, :edit, :show]
#resources "/requests", EventRequestController #resources "/requests", EventRequestController
post "/groups", GroupController, :create post "/groups", GroupController, :create
#post "/groups/:id/request", GroupRequestController, :create_for_group post "/groups/:name/join", GroupController, :join
resources "/members", MemberController resources "/members", MemberController
#resources "/requests", GroupRequestController
resources "/sessions", SessionController, except: [:index, :show] resources "/sessions", SessionController, except: [:index, :show]
resources "/tracks", TrackController, except: [:index, :show] resources "/tracks", TrackController, except: [:index, :show]
get "/tracks/:id/sessions", SessionController, :show_sessions_for_track get "/tracks/:id/sessions", SessionController, :show_sessions_for_track

View File

@ -3,7 +3,8 @@ defmodule EventosWeb.ActorView do
View for Actors View for Actors
""" """
use EventosWeb, :view use EventosWeb, :view
alias EventosWeb.{ActorView, EventView} alias EventosWeb.{ActorView, EventView, MemberView}
alias Eventos.Actors
def render("index.json", %{actors: actors}) do def render("index.json", %{actors: actors}) do
%{data: render_many(actors, ActorView, "acccount_basic.json")} %{data: render_many(actors, ActorView, "acccount_basic.json")}
@ -32,7 +33,7 @@ defmodule EventosWeb.ActorView do
end end
def render("actor.json", %{actor: actor}) do def render("actor.json", %{actor: actor}) do
%{id: actor.id, output = %{id: actor.id,
username: actor.preferred_username, username: actor.preferred_username,
domain: actor.domain, domain: actor.domain,
display_name: actor.name, display_name: actor.name,
@ -45,5 +46,14 @@ defmodule EventosWeb.ActorView do
banner: actor.banner_url, banner: actor.banner_url,
organized_events: render_many(actor.organized_events, EventView, "event_for_actor.json") organized_events: render_many(actor.organized_events, EventView, "event_for_actor.json")
} }
import Logger
Logger.debug(inspect actor.type)
if actor.type == :Group do
Logger.debug("I'm a group !")
Map.put(output, :members, render_many(Actors.members_for_group(actor), MemberView, "member.json"))
else
Logger.debug("not a group")
output
end
end end
end end

View File

@ -20,7 +20,8 @@ defmodule EventosWeb.EventView do
def render("event_for_actor.json", %{event: event}) do def render("event_for_actor.json", %{event: event}) do
%{id: event.id, %{id: event.id,
title: event.title, title: event.title,
slug: event.slug slug: event.slug,
uuid: event.uuid,
} }
end end
@ -31,8 +32,11 @@ defmodule EventosWeb.EventView do
description: event.description, description: event.description,
begins_on: event.begins_on, begins_on: event.begins_on,
ends_on: event.ends_on, ends_on: event.ends_on,
uuid: event.uuid,
organizer: %{ organizer: %{
username: event.organizer_actor.preferred_username username: event.organizer_actor.preferred_username,
display_name: event.organizer_actor.name,
avatar: event.organizer_actor.avatar_url,
}, },
type: "Event", type: "Event",
} }
@ -44,6 +48,7 @@ defmodule EventosWeb.EventView do
description: event.description, description: event.description,
begins_on: event.begins_on, begins_on: event.begins_on,
ends_on: event.ends_on, ends_on: event.ends_on,
uuid: event.uuid,
organizer: render_one(event.organizer_actor, ActorView, "acccount_basic.json"), organizer: render_one(event.organizer_actor, ActorView, "acccount_basic.json"),
participants: render_many(event.participants, ActorView, "show_basic.json"), participants: render_many(event.participants, ActorView, "show_basic.json"),
address: render_one(event.address, AddressView, "address.json"), address: render_one(event.address, AddressView, "address.json"),

View File

@ -3,7 +3,7 @@ defmodule EventosWeb.MemberView do
View for Members View for Members
""" """
use EventosWeb, :view use EventosWeb, :view
alias EventosWeb.MemberView alias EventosWeb.{MemberView, ActorView}
def render("index.json", %{members: members}) do def render("index.json", %{members: members}) do
%{data: render_many(members, MemberView, "member.json")} %{data: render_many(members, MemberView, "member.json")}
@ -14,7 +14,10 @@ defmodule EventosWeb.MemberView do
end end
def render("member.json", %{member: member}) do def render("member.json", %{member: member}) do
%{id: member.id, %{
role: member.role} role: member.role,
actor: render_one(member.actor, ActorView, "acccount_basic.json"),
group: render_one(member.parent, ActorView, "acccount_basic.json")
}
end end
end end

View File

@ -1,20 +0,0 @@
defmodule EventosWeb.RequestView do
@moduledoc """
View for event request
"""
use EventosWeb, :view
alias EventosWeb.RequestView
def render("index.json", %{requests: requests}) do
%{data: render_many(requests, RequestView, "request.json")}
end
def render("show.json", %{request: request}) do
%{data: render_one(request, RequestView, "request.json")}
end
def render("request.json", %{request: request}) do
%{id: request.id,
state: request.state}
end
end

View File

@ -0,0 +1,15 @@
defmodule Eventos.Repo.Migrations.AddUUIDIdToEvents do
use Ecto.Migration
def up do
alter table(:events) do
add :uuid, :uuid
end
end
def down do
alter table(:events) do
remove :uuid
end
end
end

View File

@ -0,0 +1,15 @@
defmodule Eventos.Repo.Migrations.AddApprovedStatusToMember do
use Ecto.Migration
def up do
alter table(:members) do
add :approved, :boolean, default: true
end
end
def down do
alter table(:members) do
remove :approved
end
end
end