Frontend: Fix photo model and card view
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
724ec41273
commit
7181adb360
|
@ -3,7 +3,6 @@ import PNavigation from "./p-navigation.vue";
|
|||
import PLoadingBar from "./p-loading-bar.vue";
|
||||
import PPhotoSearch from "./p-photo-search.vue";
|
||||
import PPhotoCards from "./p-photo-cards.vue";
|
||||
import PPhotoCard from "./p-photo-card.vue";
|
||||
import PPhotoMosaic from "./p-photo-mosaic.vue";
|
||||
import PPhotoList from "./p-photo-list.vue";
|
||||
import PPhotoClipboard from "./p-photo-clipboard.vue";
|
||||
|
@ -21,7 +20,6 @@ components.install = (Vue) => {
|
|||
Vue.component("p-loading-bar", PLoadingBar);
|
||||
Vue.component("p-photo-viewer", PPhotoViewer);
|
||||
Vue.component("p-photo-cards", PPhotoCards);
|
||||
Vue.component("p-photo-card", PPhotoCard);
|
||||
Vue.component("p-photo-mosaic", PPhotoMosaic);
|
||||
Vue.component("p-photo-list", PPhotoList);
|
||||
Vue.component("p-photo-search", PPhotoSearch);
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
<template>
|
||||
<v-hover>
|
||||
<v-card tile slot-scope="{ hover }"
|
||||
@contextmenu="contextMenu($event)"
|
||||
:dark="isSelected"
|
||||
:class="isSelected ? 'elevation-10 ma-0 accent darken-1 white--text' : 'elevation-0 ma-1 accent lighten-3'">
|
||||
<v-img
|
||||
:src="thumbnailUrl"
|
||||
aspect-ratio="1"
|
||||
v-bind:class="{ selected: isSelected }"
|
||||
style="cursor: pointer;"
|
||||
class="accent lighten-2"
|
||||
v-longclick="longClick"
|
||||
@click="onClick($event)"
|
||||
>
|
||||
<v-layout
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
>
|
||||
<v-progress-circular indeterminate color="accent lighten-5"></v-progress-circular>
|
||||
</v-layout>
|
||||
|
||||
<v-btn v-if="hover || selection.length > 0" :flat="!hover" :ripple="false"
|
||||
icon large absolute
|
||||
:class="isSelected ? 'p-photo-select' : 'p-photo-select opacity-50'"
|
||||
@click.stop.prevent="onSelect($event)">
|
||||
<v-icon v-if="selection.length && isSelected" color="white"
|
||||
class="t-select t-on">check_circle
|
||||
</v-icon>
|
||||
<v-icon v-else color="accent lighten-3" class="t-select t-off">radio_button_off</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn :flat="!hover" :ripple="false"
|
||||
icon large absolute
|
||||
:class="photo.PhotoFavorite ? 'p-photo-like opacity-75' : 'p-photo-like opacity-50'"
|
||||
@click.stop.prevent="photo.toggleLike()">
|
||||
<v-icon v-if="photo.PhotoFavorite" color="white" class="t-like t-on">favorite</v-icon>
|
||||
<v-icon v-else color="accent lighten-3" class="t-like t-off">favorite_border</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="photo.Files.length > 1" :flat="!hover" :ripple="false"
|
||||
icon large absolute class="p-photo-merged opacity-75"
|
||||
@click.stop.prevent="openPhoto(index, true)">
|
||||
<v-icon color="white" class="action-burst">burst_mode</v-icon>
|
||||
</v-btn>
|
||||
</v-img>
|
||||
|
||||
<v-card-title primary-title class="pa-3 p-photo-desc" style="user-select: none;">
|
||||
<div>
|
||||
<h3 class="body-2 mb-2" :title="photo.PhotoTitle">
|
||||
<button @click.exact="editPhoto(index)">
|
||||
{{ photo.PhotoTitle | truncate(80) }}
|
||||
</button>
|
||||
</h3>
|
||||
<div class="caption">
|
||||
<button @click.exact="editPhoto(index)">
|
||||
<v-icon size="14">date_range</v-icon>
|
||||
{{ photo.getDateString() }}
|
||||
</button>
|
||||
<br/>
|
||||
<button @click.exact="editPhoto(index)">
|
||||
<v-icon size="14">photo_camera</v-icon>
|
||||
{{ photo.getCamera() }}
|
||||
</button>
|
||||
<br/>
|
||||
<button @click.exact="openLocation(index)" v-if="showLocation && photo.LocationID">
|
||||
<v-icon size="14">location_on</v-icon>
|
||||
{{ photo.getLocation() }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
</v-hover>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'p-photo-card',
|
||||
props: {
|
||||
index: Number,
|
||||
photo: Object,
|
||||
selection: Array,
|
||||
selectRange: Function,
|
||||
openPhoto: Function,
|
||||
editPhoto: Function,
|
||||
openLocation: Function,
|
||||
showLocation: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isSelected: this.$clipboard.has(this.photo),
|
||||
thumbnailUrl: this.photo.getThumbnailUrl('tile_500'),
|
||||
wasLong: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
longClick() {
|
||||
this.wasLong = true;
|
||||
},
|
||||
onSelect(ev) {
|
||||
if (this.wasLong || ev.shiftKey) {
|
||||
this.selectRange(this.index);
|
||||
} else {
|
||||
this.$clipboard.toggle(this.photo);
|
||||
}
|
||||
|
||||
this.wasLong = false;
|
||||
},
|
||||
onClick(ev) {
|
||||
if (this.wasLong || this.selection.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this.wasLong || ev.shiftKey) {
|
||||
this.selectRange(this.index);
|
||||
} else {
|
||||
this.$clipboard.toggle(this.photo);
|
||||
}
|
||||
} else {
|
||||
this.openPhoto(this.index, false);
|
||||
}
|
||||
|
||||
this.wasLong = false;
|
||||
},
|
||||
contextMenu(ev) {
|
||||
if (this.$isMobile) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this.wasLong) {
|
||||
this.selectRange(this.index);
|
||||
} else {
|
||||
this.$clipboard.toggle(this.photo);
|
||||
}
|
||||
}
|
||||
|
||||
this.wasLong = false;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selection() {
|
||||
this.isSelected = this.$clipboard.has(this.photo);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -21,10 +21,82 @@
|
|||
xs12 sm6 md4 lg3 d-flex
|
||||
v-bind:class="{ 'is-selected': $clipboard.has(photo) }"
|
||||
>
|
||||
<p-photo-card :photo="photo" :selection="selection" :index="index" :open-photo="openPhoto"
|
||||
:select-range="selectRange"
|
||||
:edit-photo="editPhoto" :open-location="openLocation" :show-location="places">
|
||||
</p-photo-card>
|
||||
<v-hover>
|
||||
<v-card tile slot-scope="{ hover }"
|
||||
@contextmenu="contextMenu($event, photo, index)"
|
||||
:dark="$clipboard.has(photo)"
|
||||
:class="$clipboard.has(photo) ? 'elevation-10 ma-0 accent darken-1 white--text' : 'elevation-0 ma-1 accent lighten-3'">
|
||||
<v-img
|
||||
:src="photo.getThumbnailUrl('tile_500')"
|
||||
aspect-ratio="1"
|
||||
v-bind:class="{ selected: $clipboard.has(photo) }"
|
||||
style="cursor: pointer;"
|
||||
class="accent lighten-2"
|
||||
v-longclick="longClick"
|
||||
@click="onClick($event, photo, index)"
|
||||
>
|
||||
<v-layout
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
>
|
||||
<v-progress-circular indeterminate color="accent lighten-5"></v-progress-circular>
|
||||
</v-layout>
|
||||
|
||||
<v-btn v-if="hover || selection.length > 0" :flat="!hover" :ripple="false"
|
||||
icon large absolute
|
||||
:class="$clipboard.has(photo) ? 'p-photo-select' : 'p-photo-select opacity-50'"
|
||||
@click.stop.prevent="onSelect($event, photo, index)">
|
||||
<v-icon v-if="selection.length && $clipboard.has(photo)" color="white"
|
||||
class="t-select t-on">check_circle
|
||||
</v-icon>
|
||||
<v-icon v-else color="accent lighten-3" class="t-select t-off">radio_button_off</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn :flat="!hover" :ripple="false"
|
||||
icon large absolute
|
||||
:class="photo.PhotoFavorite ? 'p-photo-like opacity-75' : 'p-photo-like opacity-50'"
|
||||
@click.stop.prevent="photo.toggleLike()">
|
||||
<v-icon v-if="photo.PhotoFavorite" color="white" class="t-like t-on">favorite</v-icon>
|
||||
<v-icon v-else color="accent lighten-3" class="t-like t-off">favorite_border</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="photo.Files.length > 1" :flat="!hover" :ripple="false"
|
||||
icon large absolute class="p-photo-merged opacity-75"
|
||||
@click.stop.prevent="openPhoto(index, true)">
|
||||
<v-icon color="white" class="action-burst">burst_mode</v-icon>
|
||||
</v-btn>
|
||||
</v-img>
|
||||
|
||||
<v-card-title primary-title class="pa-3 p-photo-desc" style="user-select: none;">
|
||||
<div>
|
||||
<h3 class="body-2 mb-2" :title="photo.PhotoTitle">
|
||||
<button @click.exact="editPhoto(index)">
|
||||
{{ photo.PhotoTitle | truncate(80) }}
|
||||
</button>
|
||||
</h3>
|
||||
<div class="caption">
|
||||
<button @click.exact="editPhoto(index)">
|
||||
<v-icon size="14">date_range</v-icon>
|
||||
{{ photo.getDateString() }}
|
||||
</button>
|
||||
<br/>
|
||||
<button @click.exact="editPhoto(index)">
|
||||
<v-icon size="14">photo_camera</v-icon>
|
||||
{{ photo.getCamera() }}
|
||||
</button>
|
||||
<br/>
|
||||
<button @click.exact="openLocation(index)" v-if="showLocation && photo.LocationID">
|
||||
<v-icon size="14">location_on</v-icon>
|
||||
{{ photo.getLocation() }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
</v-hover>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
|
@ -42,10 +114,53 @@
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
places: this.$config.settings().features.places,
|
||||
showLocation: this.$config.settings().features.places,
|
||||
wasLong: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
longClick() {
|
||||
this.wasLong = true;
|
||||
},
|
||||
onSelect(ev, model, index) {
|
||||
if (this.wasLong || ev.shiftKey) {
|
||||
this.selectRange(index);
|
||||
} else {
|
||||
this.$clipboard.toggle(model);
|
||||
}
|
||||
|
||||
this.wasLong = false;
|
||||
},
|
||||
onClick(ev, model, index) {
|
||||
if (this.wasLong || this.selection.length > 0) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this.wasLong || ev.shiftKey) {
|
||||
this.selectRange(index);
|
||||
} else {
|
||||
this.$clipboard.toggle(model);
|
||||
}
|
||||
} else {
|
||||
this.openPhoto(index, false);
|
||||
}
|
||||
|
||||
this.wasLong = false;
|
||||
},
|
||||
contextMenu(ev, model, index) {
|
||||
if (this.$isMobile) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (this.wasLong) {
|
||||
this.selectRange(index);
|
||||
} else {
|
||||
this.$clipboard.toggle(model);
|
||||
}
|
||||
}
|
||||
|
||||
this.wasLong = false;
|
||||
},
|
||||
selectRange(index) {
|
||||
this.$clipboard.addRange(index, this.photos);
|
||||
}
|
||||
|
|
|
@ -91,15 +91,15 @@ class Rest extends Model {
|
|||
|
||||
if (response.headers) {
|
||||
if (response.headers["x-count"]) {
|
||||
count = response.headers["x-count"];
|
||||
count = parseInt(response.headers["x-count"]);
|
||||
}
|
||||
|
||||
if (response.headers["x-limit"]) {
|
||||
limit = response.headers["x-limit"];
|
||||
limit = parseInt(response.headers["x-limit"]);
|
||||
}
|
||||
|
||||
if (response.headers["x-offset"]) {
|
||||
offset = response.headers["x-offset"];
|
||||
offset = parseInt(response.headers["x-offset"]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,12 +29,12 @@
|
|||
:edit-photo="editPhoto"
|
||||
:open-location="openLocation"></p-photo-list>
|
||||
<p-photo-cards v-else
|
||||
:photos="results"
|
||||
:selection="selection"
|
||||
:album="model"
|
||||
:open-photo="openPhoto"
|
||||
:edit-photo="editPhoto"
|
||||
:open-location="openLocation"></p-photo-cards>
|
||||
:photos="results"
|
||||
:selection="selection"
|
||||
:album="model"
|
||||
:open-photo="openPhoto"
|
||||
:edit-photo="editPhoto"
|
||||
:open-location="openLocation"></p-photo-cards>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -331,11 +331,13 @@
|
|||
case 'updated':
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const values = data.entities[i];
|
||||
const model = this.results.find((m) => m.ID === values.ID);
|
||||
const model = this.results.find((m) => m.PhotoUUID === values.PhotoUUID);
|
||||
|
||||
for (let key in values) {
|
||||
if (values.hasOwnProperty(key)) {
|
||||
model[key] = values[key];
|
||||
if (model) {
|
||||
for (let key in values) {
|
||||
if (values.hasOwnProperty(key)) {
|
||||
model[key] = values[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
:edit-photo="editPhoto"
|
||||
:open-location="openLocation"></p-photo-list>
|
||||
<p-photo-cards v-else
|
||||
:photos="results"
|
||||
:selection="selection"
|
||||
:open-photo="openPhoto"
|
||||
:edit-photo="editPhoto"
|
||||
:open-location="openLocation"></p-photo-cards>
|
||||
:photos="results"
|
||||
:selection="selection"
|
||||
:open-photo="openPhoto"
|
||||
:edit-photo="editPhoto"
|
||||
:open-location="openLocation"></p-photo-cards>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -305,11 +305,13 @@
|
|||
case 'updated':
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const values = data.entities[i];
|
||||
const model = this.results.find((m) => m.ID === values.ID);
|
||||
const model = this.results.find((m) => m.PhotoUUID === values.PhotoUUID);
|
||||
|
||||
for (let key in values) {
|
||||
if (values.hasOwnProperty(key)) {
|
||||
model[key] = values[key];
|
||||
if (model) {
|
||||
for (let key in values) {
|
||||
if (values.hasOwnProperty(key)) {
|
||||
model[key] = values[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user