Refactor media upload

Use Upload Media logic from Pleroma

Backend changes for picture upload

Move AS <-> Model conversion to separate module

Front changes

Downgrade apollo-client: https://github.com/Akryum/vue-apollo/issues/577

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2019-05-22 14:12:11 +02:00
parent 9724bc8e9f
commit f90089e1bf
113 changed files with 4718 additions and 1328 deletions

View File

@@ -91,6 +91,7 @@ export default class App extends Vue {
@import "~buefy/src/scss/components/form";
@import "~buefy/src/scss/components/modal";
@import "~buefy/src/scss/components/tag";
@import "~buefy/src/scss/components/upload";
@import "~buefy/src/scss/utils/_all";
.router-enter-active,

View File

@@ -8,8 +8,8 @@
<li v-for="identity in identities" :key="identity.id">
<div class="media identity" v-bind:class="{ 'is-current-identity': isCurrentIdentity(identity) }">
<div class="media-left">
<figure class="image is-48x48">
<img class="is-rounded" :src="identity.avatarUrl">
<figure class="image is-48x48" v-if="identity.avatar">
<img class="is-rounded" :src="identity.avatar.url">
</figure>
</div>

View File

@@ -2,7 +2,7 @@
<router-link class="card" :to="{ name: 'Event', params: { uuid: event.uuid } }">
<div class="card-image" v-if="!event.image">
<figure class="image is-16by9">
<div class="tag-container">
<div class="tag-container" v-if="event.tags">
<b-tag v-for="tag in event.tags.slice(0, 3)" :key="tag.slug" type="is-secondary">{{ tag.title }}</b-tag>
</div>
<img src="https://picsum.photos/g/400/225/?random">

View File

@@ -1,6 +1,6 @@
<template>
<div class="card">
<div class="card-image" v-if="!group.bannerUrl">
<div class="card-image" v-if="!group.banner">
<figure class="image is-4by3">
<img src="https://picsum.photos/g/400/200/">
</figure>

View File

@@ -40,8 +40,8 @@
v-if="currentUser.isLoggedIn && loggedPerson"
:to="{ name: 'MyAccount' }"
>
<figure class="image is-24x24">
<img :src="loggedPerson.avatarUrl">
<figure class="image is-24x24" v-if="loggedPerson.avatar">
<img :src="loggedPerson.avatar.url">
</figure>
<span>{{ loggedPerson.preferredUsername }}</span>
</router-link>

View File

@@ -0,0 +1,29 @@
<template>
<b-field class="file">
<b-upload v-model="pictureFile" @input="onFileChanged">
<a class="button is-primary">
<b-icon icon="upload"></b-icon>
<span>Click to upload</span>
</a>
</b-upload>
<span class="file-name" v-if="pictureFile">
{{ pictureFile.name }}
</span>
</b-field>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { IPictureUpload } from '@/types/picture.model';
@Component
export default class PictureUpload extends Vue {
picture!: IPictureUpload;
pictureFile: File|null = null;
onFileChanged(file: File) {
this.picture = { file, name: file.name, alt: '' };
this.$emit('change', this.picture);
}
}
</script>

View File

@@ -10,8 +10,12 @@ query($name:String!) {
summary,
preferredUsername,
suspended,
avatarUrl,
bannerUrl,
avatar {
url
},
banner {
url
},
feedTokens {
token
},
@@ -28,7 +32,9 @@ export const LOGGED_PERSON = gql`
query {
loggedPerson {
id,
avatarUrl,
avatar {
url
},
preferredUsername,
}
}`;
@@ -37,7 +43,9 @@ export const LOGGED_PERSON_WITH_GOING_TO_EVENTS = gql`
query {
loggedPerson {
id,
avatarUrl,
avatar {
url
},
preferredUsername,
goingToEvents {
uuid,
@@ -56,7 +64,9 @@ query {
export const IDENTITIES = gql`
query {
identities {
avatarUrl,
avatar {
url
},
preferredUsername,
name
}
@@ -72,7 +82,9 @@ mutation CreatePerson($preferredUsername: String!) {
preferredUsername,
name,
summary,
avatarUrl
avatar {
url
},
}
}
`;
@@ -91,7 +103,9 @@ mutation ($preferredUsername: String!, $name: String!, $summary: String!, $email
preferredUsername,
name,
summary,
avatarUrl,
avatar {
url
},
}
}
`;
@@ -106,8 +120,12 @@ query($name:String!) {
summary,
preferredUsername,
suspended,
avatarUrl,
bannerUrl,
avatar {
url
},
banner {
url
}
organizedEvents {
uuid,
title,

View File

@@ -4,7 +4,9 @@ const participantQuery = `
role,
actor {
preferredUsername,
avatarUrl,
avatar {
url
},
name,
id
}
@@ -24,8 +26,10 @@ export const FETCH_EVENT = gql`
endsOn,
status,
visibility,
thumbnail,
largeImage,
picture {
id
url
},
publishAt,
category,
# online_address,
@@ -35,19 +39,23 @@ export const FETCH_EVENT = gql`
floor,
street,
locality,
postal_code,
postalCode,
region,
country,
geom
}
organizerActor {
avatarUrl,
avatar {
url
},
preferredUsername,
domain,
name,
},
# attributedTo {
# # avatarUrl,
# avatar {
# url,
# }
# preferredUsername,
# name,
# },
@@ -66,7 +74,9 @@ export const FETCH_EVENT = gql`
description
},
organizerActor {
avatarUrl,
avatar {
url,
},
preferredUsername,
domain,
name,
@@ -89,8 +99,10 @@ export const FETCH_EVENTS = gql`
endsOn,
status,
visibility,
thumbnail,
largeImage,
picture {
id
url
},
publishAt,
# online_address,
# phone_address,
@@ -99,12 +111,16 @@ export const FETCH_EVENTS = gql`
locality
}
organizerActor {
avatarUrl,
avatar {
url
},
preferredUsername,
name,
},
attributedTo {
avatarUrl,
avatar {
url
},
preferredUsername,
name,
},
@@ -124,20 +140,31 @@ export const CREATE_EVENT = gql`
mutation CreateEvent(
$title: String!,
$description: String!,
$organizerActorId: String!,
$organizerActorId: ID!,
$category: String!,
$beginsOn: DateTime!
$beginsOn: DateTime!,
$picture_file: Upload,
$picture_name: String,
) {
createEvent(
title: $title,
description: $description,
beginsOn: $beginsOn,
organizerActorId: $organizerActorId,
category: $category
category: $category,
picture: {
picture: {
file: $picture_file,
name: $picture_name,
}
}
) {
id,
uuid,
title
title,
picture {
url
}
}
}
`;

View File

@@ -4,14 +4,16 @@ export const LOGGED_PERSON = gql`
query {
loggedPerson {
id,
avatarUrl,
avatar {
url
},
preferredUsername,
}
}`;
export const CREATE_FEED_TOKEN_ACTOR = gql`
mutation createFeedToken($actor_id: Int!) {
createFeedToken(actor_id: $actor_id) {
createFeedToken(actorId: $actor_id) {
token,
actor {
id

View File

@@ -23,7 +23,9 @@ query SearchGroups($searchText: String!) {
searchGroups(search: $searchText) {
total,
elements {
avatarUrl,
avatar {
url
},
domain,
preferredUsername,
name,

View File

@@ -1,3 +1,5 @@
import { IPicture } from '@/types/picture.model';
export interface IActor {
id?: string;
url: string;
@@ -6,13 +8,13 @@ export interface IActor {
summary: string;
preferredUsername: string;
suspended: boolean;
avatarUrl: string;
bannerUrl: string;
avatar: IPicture | null;
banner: IPicture | null;
}
export class Actor implements IActor {
avatarUrl: string = '';
bannerUrl: string = '';
avatar: IPicture | null = null;
banner: IPicture | null = null;
domain: string | null = null;
name: string = '';
preferredUsername: string = '';

View File

@@ -1,5 +1,7 @@
import { Actor, IActor } from './actor';
import { IAddress } from '@/types/address.model';
import { ITag } from '@/types/tag.model';
import { IAbstractPicture, IPicture } from '@/types/picture.model';
export enum EventStatus {
TENTATIVE,
@@ -62,8 +64,7 @@ export interface IEvent {
joinOptions: EventJoinOptions;
thumbnail: string;
largeImage: string;
picture: IAbstractPicture|null;
organizerActor: IActor;
attributedTo: IActor;
@@ -84,12 +85,10 @@ export class EventModel implements IEvent {
description: string = '';
endsOn: Date = new Date();
joinOptions: EventJoinOptions = EventJoinOptions.FREE;
largeImage: string = '';
local: boolean = true;
participants: IParticipant[] = [];
publishAt: Date = new Date();
status: EventStatus = EventStatus.CONFIRMED;
thumbnail: string = '';
title: string = '';
url: string = '';
uuid: string = '';
@@ -99,4 +98,5 @@ export class EventModel implements IEvent {
relatedEvents: IEvent[] = [];
onlineAddress: string = '';
phoneAddress: string = '';
picture: IAbstractPicture|null = null;
}

View File

@@ -0,0 +1,16 @@
export interface IAbstractPicture {
name;
alt;
}
export interface IPicture {
url;
name;
alt;
}
export interface IPictureUpload {
file: File;
name: String;
alt: String|null;
}

View File

@@ -2,8 +2,8 @@
<section class="container">
<div v-if="person">
<div class="header">
<figure v-if="person.bannerUrl" class="image is-3by1">
<img :src="person.bannerUrl" alt="banner">
<figure v-if="person.banner" class="image is-3by1">
<img :src="person.banner.url" alt="banner">
</figure>
</div>

View File

@@ -1,16 +1,16 @@
<template>
<section class="container">
<div v-if="person">
<div class="card-image" v-if="person.bannerUrl">
<div class="card-image" v-if="person.banner">
<figure class="image">
<img :src="person.bannerUrl">
<img :src="person.banner.url">
</figure>
</div>
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-48x48">
<img :src="person.avatarUrl">
<figure class="image is-48x48" v-if="person.avatar">
<img :src="person.avatar.url">
</figure>
</div>
<div class="media-content">
@@ -18,7 +18,6 @@
<p class="subtitle">@{{ person.preferredUsername }}</p>
</div>
</div>
<div class="content">
<vue-simple-markdown :source="person.summary"></vue-simple-markdown>
</div>
@@ -58,7 +57,7 @@
</a>
</b-dropdown-item>
</b-dropdown>
<a class="button" v-else-if="loggedPerson" @click="createToken">
<a class="button" v-if="loggedPerson.id === person.id" @click="createToken">
<translate>Create token</translate>
</a>
</div>

View File

@@ -81,7 +81,7 @@ export default class Register extends Vue {
@Prop({ type: String, required: true }) email!: string;
@Prop({ type: Boolean, required: false, default: false }) userAlreadyActivated!: boolean;
host: string = MOBILIZON_INSTANCE_HOST;
host?: string = MOBILIZON_INSTANCE_HOST;
person: IPerson = {
preferredUsername: '',
@@ -90,8 +90,8 @@ export default class Register extends Vue {
id: '',
url: '',
suspended: false,
avatarUrl: '',
bannerUrl: '',
avatar: null,
banner: null,
domain: null,
feedTokens: [],
goingToEvents: [],

View File

@@ -1,5 +1,5 @@
<template>
<section>
<section class="container">
<h1 class="title">
<translate>Create a new event</translate>
</h1>
@@ -22,6 +22,8 @@
</b-select>
</b-field>
<picture-upload @change="handlePictureUploadChange" />
<button class="button is-primary">
<translate>Create my event</translate>
</button>
@@ -41,8 +43,11 @@ import {
} from '@/types/event.model';
import { LOGGED_PERSON } from '@/graphql/actor';
import { IPerson, Person } from '@/types/actor';
import PictureUpload from '@/components/PictureUpload.vue';
import { IPictureUpload } from '@/types/picture.model';
@Component({
components: { PictureUpload },
apollo: {
loggedPerson: {
query: LOGGED_PERSON,
@@ -55,6 +60,8 @@ export default class CreateEvent extends Vue {
loggedPerson: IPerson = new Person();
categories: string[] = Object.keys(Category);
event: IEvent = new EventModel();
pictureFile?: File;
pictureName?: String;
createEvent(e: Event) {
e.preventDefault();
@@ -62,15 +69,18 @@ export default class CreateEvent extends Vue {
this.event.attributedTo = this.loggedPerson;
if (this.event.uuid === '') {
console.log('event', this.event);
this.$apollo
.mutate({
mutation: CREATE_EVENT,
variables: {
title: this.event.title,
description: this.event.description,
beginsOn: this.event.beginsOn,
beginsOn: this.event.beginsOn.toISOString(),
category: this.event.category,
organizerActorId: this.event.organizerActor.id,
picture_file: this.pictureFile,
picture_name: this.pictureName,
},
})
.then(data => {
@@ -100,6 +110,12 @@ export default class CreateEvent extends Vue {
}
}
handlePictureUploadChange(picture: IPictureUpload) {
console.log('picture upload change', picture);
this.pictureFile = picture.file;
this.pictureName = picture.name;
}
// getAddressData(addressData) {
// if (addressData !== null) {
// this.event.address = {

View File

@@ -3,7 +3,10 @@
<b-loading :active.sync="$apollo.loading"></b-loading>
<div v-if="event">
<div class="header-picture container">
<figure class="image is-3by1">
<figure class="image is-3by1" v-if="event.picture">
<img :src="event.picture.url">
</figure>
<figure class="image is-3by1" v-else>
<img src="https://picsum.photos/600/200/">
</figure>
</div>
@@ -95,10 +98,10 @@
<translate
:translate-params="{name: event.organizerActor.name ? event.organizerActor.name : event.organizerActor.preferredUsername}"
v-if="event.organizerActor">By %{ name }</translate>
<figure v-if="event.organizerActor.avatarUrl" class="image is-48x48">
<figure v-if="event.organizerActor.avatar" class="image is-48x48">
<img
class="is-rounded"
:src="event.organizerActor.avatarUrl"
:src="event.organizerActor.avatar.url"
:alt="$gettextInterpolate('%{actor}\'s avatar', {actor: event.organizerActor.preferredUsername})" />
</figure>
</router-link>
@@ -185,8 +188,8 @@
<!-- >-->
<!-- <div>-->
<!-- <figure>-->
<!-- <img v-if="!participant.actor.avatarUrl" src="https://picsum.photos/125/125/">-->
<!-- <img v-else :src="participant.actor.avatarUrl">-->
<!-- <img v-if="!participant.actor.avatar.url" src="https://picsum.photos/125/125/">-->
<!-- <img v-else :src="participant.actor.avatar.url">-->
<!-- </figure>-->
<!-- <span>{{ participant.actor.preferredUsername }}</span>-->
<!-- </div>-->

View File

@@ -1,16 +1,16 @@
<template>
<section class="container">
<div v-if="group">
<div class="card-image" v-if="group.bannerUrl">
<div class="card-image" v-if="group.banner.url">
<figure class="image">
<img :src="group.bannerUrl">
<img :src="group.banner.url">
</figure>
</div>
<div class="box">
<div class="media">
<div class="media-left">
<figure class="image is-48x48">
<img :src="group.avatarUrl">
<img :src="group.avatar.url">
</figure>
</div>
<div class="media-content">