forked from potsda.mn/mobilizon
Prevent picture resend on event update, handle duplicate pictures
properly in backend and add a proper default picture Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
18cabe4d42
commit
cbe1dd2868
BIN
js/public/img/mobilizon_default_card.png
Normal file
BIN
js/public/img/mobilizon_default_card.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export interface IPicture {
|
export interface IPicture {
|
||||||
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
name: string;
|
name: string;
|
||||||
alt: string;
|
alt: string;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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"""
|
||||||
|
|
Loading…
Reference in a new issue