Frontend: Select photos in fullscreen mode #657

This commit is contained in:
Michael Mayer 2020-12-16 14:10:03 +01:00
parent 37f79324ce
commit fcad9e49a1
5 changed files with 172 additions and 142 deletions

View file

@ -51,6 +51,20 @@ export default class Clipboard {
this.loadFromStorage();
}
isModel(model) {
if (!model) {
console.warn("Clipboard::isModel() - empty model", model);
return false;
}
if (typeof model.getId !== "function") {
console.warn("Clipboard::isModel() - model.getId() is not a function", model);
return false;
}
return true;
}
loadFromStorage() {
const photosJson = this.storage.getItem(this.storageKey);
@ -64,8 +78,7 @@ export default class Clipboard {
}
toggle(model) {
if (!model || !(model instanceof RestModel)) {
console.log("Clipboard::toggle() - not a model:", model);
if (!this.isModel(model)) {
return;
}
@ -95,8 +108,7 @@ export default class Clipboard {
}
add(model) {
if (!model || !(model instanceof RestModel)) {
console.log("Clipboard::add() - not a model:", model);
if (!this.isModel(model)) {
return;
}
@ -149,8 +161,7 @@ export default class Clipboard {
}
has(model) {
if (!model || !(model instanceof RestModel)) {
console.log("Clipboard::has() - not a model:", model);
if (!this.isModel(model)) {
return;
}
@ -162,8 +173,7 @@ export default class Clipboard {
}
remove(model) {
if (!model || !(model instanceof RestModel)) {
console.log("Clipboard::remove() - not a model:", model);
if (!this.isModel(model)) {
return;
}

View file

@ -178,7 +178,7 @@
<v-flex xs12>
<div class="text-xs-center">
<v-btn color="secondary-dark" small depressed dark @click.stop="photo.archive()"
class="action-archive text-xs-center" :title="labels.archive">
class="action-archive text-xs-center" :title="labels.archive">
<v-icon dark>archive</v-icon>
</v-btn>
<v-btn color="secondary-dark" small depressed dark @click.stop="photo.approve()"

View file

@ -27,17 +27,25 @@
<v-icon size="16" color="white">edit</v-icon>
</button>
<button class="pswp__button action-select" style="background: none;"
@click.exact="onSelect" :title="$gettext('Select')">
<v-icon v-if="selection.length && $clipboard.has(item)" size="16" color="white">check_circle</v-icon>
<v-icon v-else size="16" color="white">radio_button_off</v-icon>
</button>
<button class="pswp__button action-like hidden-shared-only" style="background: none;"
@click.exact="onLike" :title="$gettext('Like')">
<v-icon v-if="item.favorite" size="16" color="white">favorite</v-icon>
<v-icon v-else size="16" color="white">favorite_border</v-icon>
</button>
<button class="pswp__button pswp__button--fs action-toggle-fullscreen" :title="$gettext('Fullscreen')"></button>
<button class="pswp__button pswp__button--fs action-toggle-fullscreen"
:title="$gettext('Fullscreen')"></button>
<button class="pswp__button pswp__button--zoom action-zoom" :title="$gettext('Zoom in/out')"></button>
<button class="pswp__button" style="background: none;" @click.exact="onSlideshow" :title="$gettext('Start/Stop Slideshow')">
<button class="pswp__button" style="background: none;" @click.exact="onSlideshow"
:title="$gettext('Start/Stop Slideshow')">
<v-icon v-show="!interval" size="18" color="white">play_arrow</v-icon>
<v-icon v-show="interval" size="16" color="white">pause</v-icon>
</button>
@ -71,126 +79,130 @@
</template>
<script>
import 'photoswipe/dist/photoswipe.css'
import 'photoswipe/dist/default-skin/default-skin.css'
import Event from "pubsub-js";
import Thumb from "model/thumb";
import Photo from "model/photo";
import Notify from "common/notify";
import 'photoswipe/dist/photoswipe.css'
import 'photoswipe/dist/default-skin/default-skin.css'
import Event from "pubsub-js";
import Thumb from "model/thumb";
import Photo from "model/photo";
import Notify from "common/notify";
export default {
name: "p-photo-viewer",
data() {
return {
config: this.$config.values,
item: new Thumb(),
subscriptions: [],
interval: false,
slideshow: {
active: false,
next: 0,
},
};
},
created() {
this.subscriptions['viewer.change'] = Event.subscribe('viewer.change', this.onChange);
this.subscriptions['viewer.pause'] = Event.subscribe('viewer.pause', this.onPause);
},
destroyed() {
this.onPause();
export default {
name: "p-photo-viewer",
data() {
return {
selection: this.$clipboard.selection,
config: this.$config.values,
item: new Thumb(),
subscriptions: [],
interval: false,
slideshow: {
active: false,
next: 0,
},
};
},
created() {
this.subscriptions['viewer.change'] = Event.subscribe('viewer.change', this.onChange);
this.subscriptions['viewer.pause'] = Event.subscribe('viewer.pause', this.onPause);
},
destroyed() {
this.onPause();
for (let i = 0; i < this.subscriptions.length; i++) {
Event.unsubscribe(this.subscriptions[i]);
}
},
methods: {
onChange(ev, data) {
const psp = this.$viewer.gallery;
if(psp && this.slideshow.next !== psp.getCurrentIndex()) {
this.onPause();
}
this.item = data.item;
},
onLike() {
this.item.toggleLike();
},
onPlay() {
if (this.item && this.item.playable) {
let photo = new Photo();
photo.find(this.item.uid).then((p) => {
this.$modal.show('video', {video: p, album: null});
});
}
},
onPause() {
this.slideshow.active = false;
if (this.interval) {
clearInterval(this.interval);
this.interval = false;
}
},
onSlideshow() {
if (this.interval) {
this.onPause();
return;
}
this.slideshow.active = true;
const self = this;
const psp = this.$viewer.gallery;
self.interval = setInterval(() => {
if (psp && typeof psp.next === "function") {
psp.next();
this.slideshow.next = psp.getCurrentIndex();
} else {
this.onPause();
}
}, 5000);
},
onDownload() {
this.onPause();
if (!this.item || !this.item.download_url) {
console.warn("photo viewer: no download url");
return;
}
Notify.success(this.$gettext("Downloading…"));
let photo = new Photo();
photo.find(this.item.uid).then((p) => {
p.downloadAll();
});
},
onEdit() {
this.onPause();
const g = this.$viewer.gallery; // Gallery
let index = 0;
// remove duplicates
let filtered = g.items.filter(function (p, i, s) {
return !(i > 0 && p.uid === s[i - 1].uid);
});
let selection = filtered.map((p, i) => {
if (g.currItem.uid === p.uid) {
index = i;
}
return p.uid
});
let album = null;
g.close(); // Close Gallery
Event.publish("dialog.edit", {selection, album, index}); // Open Edit Dialog
}
}
for (let i = 0; i < this.subscriptions.length; i++) {
Event.unsubscribe(this.subscriptions[i]);
}
},
methods: {
onChange(ev, data) {
const psp = this.$viewer.gallery;
if (psp && this.slideshow.next !== psp.getCurrentIndex()) {
this.onPause();
}
this.item = data.item;
},
onLike() {
this.item.toggleLike();
},
onSelect() {
this.$clipboard.toggle(this.item);
},
onPlay() {
if (this.item && this.item.playable) {
let photo = new Photo();
photo.find(this.item.uid).then((p) => {
this.$modal.show('video', {video: p, album: null});
});
}
},
onPause() {
this.slideshow.active = false;
if (this.interval) {
clearInterval(this.interval);
this.interval = false;
}
},
onSlideshow() {
if (this.interval) {
this.onPause();
return;
}
this.slideshow.active = true;
const self = this;
const psp = this.$viewer.gallery;
self.interval = setInterval(() => {
if (psp && typeof psp.next === "function") {
psp.next();
this.slideshow.next = psp.getCurrentIndex();
} else {
this.onPause();
}
}, 5000);
},
onDownload() {
this.onPause();
if (!this.item || !this.item.download_url) {
console.warn("photo viewer: no download url");
return;
}
Notify.success(this.$gettext("Downloading…"));
let photo = new Photo();
photo.find(this.item.uid).then((p) => {
p.downloadAll();
});
},
onEdit() {
this.onPause();
const g = this.$viewer.gallery; // Gallery
let index = 0;
// remove duplicates
let filtered = g.items.filter(function (p, i, s) {
return !(i > 0 && p.uid === s[i - 1].uid);
});
let selection = filtered.map((p, i) => {
if (g.currItem.uid === p.uid) {
index = i;
}
return p.uid
});
let album = null;
g.close(); // Close Gallery
Event.publish("dialog.edit", {selection, album, index}); // Open Edit Dialog
}
}
}
</script>

View file

@ -50,6 +50,14 @@ export class Thumb extends Model {
};
}
getId() {
return this.uid;
}
hasId() {
return !!this.getId();
}
toggleLike() {
this.favorite = !this.favorite;

View file

@ -20,14 +20,14 @@ describe("common/clipboard", () => {
});
it("should toggle model", () => {
let spy = sinon.spy(console, "log");
// let spy = sinon.spy(console, "log");
const storage = new StorageShim();
const key = "clipboard";
const clipboard = new Clipboard(storage, key);
clipboard.clear();
clipboard.toggle();
assert(spy.calledWith("Clipboard::toggle() - not a model:"));
// assert(spy.calledWith("Clipboard::isModel() - empty model"));
assert.equal(clipboard.storageKey, "clipboard");
assert.equal(clipboard.selection, "");
@ -42,7 +42,7 @@ describe("common/clipboard", () => {
assert.equal(clipboard.selection[1], "ABC124");
clipboard.toggle(photo);
assert.equal(clipboard.selection[0], "ABC124");
console.log.restore();
// console.log.restore();
});
it("should toggle id", () => {
@ -58,7 +58,7 @@ describe("common/clipboard", () => {
});
it("should add model", () => {
let spy = sinon.spy(console, "log");
//let spy = sinon.spy(console, "log");
const storage = new StorageShim();
const key = "clipboard";
@ -67,7 +67,7 @@ describe("common/clipboard", () => {
clipboard.add();
assert.equal(clipboard.storageKey, "clipboard");
assert.equal(clipboard.selection, "");
assert(spy.calledWith("Clipboard::add() - not a model:"));
// assert(spy.calledWith("Clipboard::add() - not a model:"));
const values = {ID: 5, UID: "ABC124", Title: "Crazy Cat"};
const photo = new Photo(values);
@ -75,7 +75,7 @@ describe("common/clipboard", () => {
assert.equal(clipboard.selection[0], "ABC124");
clipboard.add(photo);
assert.equal(clipboard.selection[0], "ABC124");
console.log.restore();
// console.log.restore();
});
it("should add id", () => {
@ -89,7 +89,7 @@ describe("common/clipboard", () => {
});
it("should test whether clipboard has model", () => {
let spy = sinon.spy(console, "log");
// let spy = sinon.spy(console, "log");
const storage = new StorageShim();
const key = "clipboard";
@ -98,7 +98,7 @@ describe("common/clipboard", () => {
clipboard.has();
assert.equal(clipboard.storageKey, "clipboard");
assert.equal(clipboard.selection, "");
assert(spy.calledWith("Clipboard::has() - not a model:"));
// assert(spy.calledWith("Clipboard::has() - not a model:"));
const values = {ID: 5, UID: "ABC124", Title: "Crazy Cat"};
const photo = new Photo(values);
@ -110,7 +110,7 @@ describe("common/clipboard", () => {
const album = new Album(values2);
const result2 = clipboard.has(album);
assert.equal(result2, false);
console.log.restore();
// console.log.restore();
});
it("should test whether clipboard has id", () => {
@ -125,7 +125,7 @@ describe("common/clipboard", () => {
});
it("should remove model", () => {
let spy = sinon.spy(console, "log");
// let spy = sinon.spy(console, "log");
const storage = new StorageShim();
const key = "clipboard";
@ -134,7 +134,7 @@ describe("common/clipboard", () => {
clipboard.remove();
assert.equal(clipboard.storageKey, "clipboard");
assert.equal(clipboard.selection, "");
assert(spy.calledWith("Clipboard::remove() - not a model:"));
// assert(spy.calledWith("Clipboard::remove() - not a model:"));
const values = {ID: 5, UID: "ABC123", Title: "Crazy Cat"};
const photo = new Photo(values);
@ -147,7 +147,7 @@ describe("common/clipboard", () => {
const album = new Album(values2);
clipboard.remove(album);
assert.equal(clipboard.selection, "");
console.log.restore();
// console.log.restore();
});
it("should set and get ids", () => {