Merge branch 'bug/picture-fixes' into 'master'

Prevent picture resend on event update, handle duplicate pictures properly in backend and add a proper default picture

See merge request framasoft/mobilizon!264
This commit is contained in:
Thomas Citharel 2019-10-14 12:04:17 +02:00
commit eff38f901b
9 changed files with 45 additions and 23 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -28,13 +28,11 @@ A simple card for an event
<template> <template>
<router-link class="card" :to="{ name: 'Event', params: { uuid: event.uuid } }"> <router-link class="card" :to="{ name: 'Event', params: { uuid: event.uuid } }">
<div class="card-image" v-if="!event.image"> <div class="card-image">
<figure class="image is-16by9"> <figure class="image is-16by9" :style="`background-image: url('${event.picture ? event.picture.url : '/img/mobilizon_default_card.png'}')`">
<div class="tag-container" v-if="event.tags"> <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> <b-tag v-for="tag in event.tags.slice(0, 3)" :key="tag.slug" type="is-secondary">{{ tag.title }}</b-tag>
</div> </div>
<img v-if="event.picture" :src="event.picture.url" />
<img v-else src="https://picsum.photos/g/400/225/?random" />
</figure> </figure>
</div> </div>
<div class="content"> <div class="content">
@ -168,6 +166,11 @@ export default class EventCard extends Vue {
div.card-image { div.card-image {
background: $secondary; background: $secondary;
figure.image {
background-size: cover;
background-position: center;
}
} }
div.content { div.content {

View File

@ -28,6 +28,7 @@
margin-right: 30px; margin-right: 30px;
max-height: 200px; max-height: 200px;
max-width: 200px; max-width: 200px;
overflow: hidden;
} }
.image-placeholder { .image-placeholder {

View File

@ -1,4 +1,5 @@
export interface IPicture { export interface IPicture {
id: string;
url: string; url: string;
name: string; name: string;
alt: string; alt: string;

View File

@ -259,7 +259,7 @@ import TagInput from '@/components/Event/TagInput.vue';
import { TAGS } from '@/graphql/tags'; import { TAGS } from '@/graphql/tags';
import { ITag } from '@/types/tag.model'; import { ITag } from '@/types/tag.model';
import AddressAutoComplete from '@/components/Event/AddressAutoComplete.vue'; import AddressAutoComplete from '@/components/Event/AddressAutoComplete.vue';
import { buildFileFromIPicture, buildFileVariable } from '@/utils/image'; import { buildFileFromIPicture, buildFileVariable, readFileAsync } from '@/utils/image';
import IdentityPickerWrapper from '@/views/Account/IdentityPickerWrapper.vue'; import IdentityPickerWrapper from '@/views/Account/IdentityPickerWrapper.vue';
import { RouteName } from '@/router'; import { RouteName } from '@/router';
@ -377,10 +377,12 @@ export default class EditEvent extends Vue {
} }
async createEvent() { async createEvent() {
const variables = await this.buildVariables();
try { try {
const { data } = await this.$apollo.mutate({ const { data } = await this.$apollo.mutate({
mutation: CREATE_EVENT, mutation: CREATE_EVENT,
variables: this.buildVariables(), variables,
update: (store, { data: { createEvent } }) => this.postCreateOrUpdate(store, createEvent), update: (store, { data: { createEvent } }) => this.postCreateOrUpdate(store, createEvent),
refetchQueries: ({ data: { createEvent } }) => this.postRefetchQueries(createEvent), refetchQueries: ({ data: { createEvent } }) => this.postRefetchQueries(createEvent),
}); });
@ -403,10 +405,12 @@ export default class EditEvent extends Vue {
} }
async updateEvent() { async updateEvent() {
const variables = await this.buildVariables();
try { try {
await this.$apollo.mutate({ await this.$apollo.mutate({
mutation: EDIT_EVENT, mutation: EDIT_EVENT,
variables: this.buildVariables(), variables,
update: (store, { data: { updateEvent } }) => this.postCreateOrUpdate(store, updateEvent), update: (store, { data: { updateEvent } }) => this.postCreateOrUpdate(store, updateEvent),
refetchQueries: ({ data: { updateEvent } }) => this.postRefetchQueries(updateEvent), refetchQueries: ({ data: { updateEvent } }) => this.postRefetchQueries(updateEvent),
}); });
@ -489,7 +493,7 @@ export default class EditEvent extends Vue {
/** /**
* Build variables for Event GraphQL creation query * Build variables for Event GraphQL creation query
*/ */
private buildVariables() { private async buildVariables() {
let res = this.event.toEditJSON(); let res = this.event.toEditJSON();
if (this.event.organizerActor) { if (this.event.organizerActor) {
res = Object.assign(res, { organizerActorId: this.event.organizerActor.id }); res = Object.assign(res, { organizerActorId: this.event.organizerActor.id });
@ -502,8 +506,17 @@ export default class EditEvent extends Vue {
} }
const pictureObj = buildFileVariable(this.pictureFile, 'picture'); const pictureObj = buildFileVariable(this.pictureFile, 'picture');
res = Object.assign({}, res, pictureObj);
return Object.assign({}, res, pictureObj); if (this.event.picture) {
const oldPictureFile = await buildFileFromIPicture(this.event.picture) as File;
const oldPictureFileContent = await readFileAsync(oldPictureFile);
const newPictureFileContent = await readFileAsync(this.pictureFile as File);
if (oldPictureFileContent === newPictureFileContent) {
res.picture = { pictureId: this.event.picture.id };
}
}
return res;
} }
private async getEvent() { private async getEvent() {

View File

@ -4,14 +4,8 @@ import {ParticipantRole} from "@/types/event.model";
<b-loading :active.sync="$apollo.loading"></b-loading> <b-loading :active.sync="$apollo.loading"></b-loading>
<transition appear name="fade" mode="out-in"> <transition appear name="fade" mode="out-in">
<div v-if="event"> <div v-if="event">
<div class="header-picture" :style="`background-image: url('${event.picture ? event.picture.url : 'https://picsum.photos/600/200/'}')`"> <div class="header-picture" v-if="event.picture" :style="`background-image: url('${event.picture.url}')`" />
<!--<figure class="image is-3by1" v-if="event.picture"> <div class="header-picture-default" v-else />
<img :src="event.picture.url">
</figure>
<figure class="image is-3by1" v-else>
<img src="https://picsum.photos/600/200/">
</figure>-->
</div>
<section> <section>
<div class="title-and-participate-button"> <div class="title-and-participate-button">
<div class="title-wrapper"> <div class="title-wrapper">
@ -552,14 +546,17 @@ export default class Event extends EventMixin {
opacity: 0; opacity: 0;
} }
.header-picture { .header-picture, .header-picture-default {
height: 400px; height: 400px;
background-size: cover; background-size: cover;
// background-position: center center; background-position: center;
background-attachment: fixed;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
.header-picture-default {
background-image: url('/img/mobilizon_default_card.png');
}
div.sidebar { div.sidebar {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;

View File

@ -20,7 +20,7 @@ defmodule MobilizonWeb.Schema.PictureType do
# Either a full picture object # Either a full picture object
field(:picture, :picture_input_object) field(:picture, :picture_input_object)
# Or directly the ID of an existing picture # Or directly the ID of an existing picture
field(:picture_id, :string) field(:picture_id, :id)
end end
@desc "An attached picture" @desc "An attached picture"

View File

@ -261,6 +261,7 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
def make_picture_data(picture) when is_map(picture) do def make_picture_data(picture) when is_map(picture) do
with {:ok, %{"url" => [%{"href" => url, "mediaType" => content_type}], "size" => size}} <- with {:ok, %{"url" => [%{"href" => url, "mediaType" => content_type}], "size" => size}} <-
MobilizonWeb.Upload.store(picture.file), MobilizonWeb.Upload.store(picture.file),
{:picture_exists, nil} <- {:picture_exists, Mobilizon.Media.get_picture_by_url(url)},
{:ok, %Picture{file: _file} = picture} <- {:ok, %Picture{file: _file} = picture} <-
Mobilizon.Media.create_picture(%{ Mobilizon.Media.create_picture(%{
"file" => %{ "file" => %{
@ -272,6 +273,12 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
"actor_id" => picture.actor_id "actor_id" => picture.actor_id
}) do }) do
Converter.Picture.model_to_as(picture) Converter.Picture.model_to_as(picture)
else
{:picture_exists, %Picture{file: _file} = picture} ->
Converter.Picture.model_to_as(picture)
err ->
err
end end
end end

View File

@ -1,5 +1,5 @@
# source: http://localhost:4000/api # source: http://localhost:4000/api
# timestamp: Fri Oct 11 2019 11:53:52 GMT+0200 (Central European Summer Time) # timestamp: Mon Oct 14 2019 10:27:57 GMT+0200 (Central European Summer Time)
schema { schema {
query: RootQueryType query: RootQueryType
@ -796,7 +796,7 @@ type Picture {
"""An attached picture or a link to a picture""" """An attached picture or a link to a picture"""
input PictureInput { input PictureInput {
picture: PictureInputObject picture: PictureInputObject
pictureId: String pictureId: ID
} }
"""An attached picture""" """An attached picture"""