Migrate to vue 3 (broken)
parent
555697a404
commit
c87134b16d
File diff suppressed because it is too large
Load Diff
29
package.json
29
package.json
|
@ -32,21 +32,22 @@
|
||||||
"@nextcloud/l10n": "^1.6.0",
|
"@nextcloud/l10n": "^1.6.0",
|
||||||
"@nextcloud/paths": "^2.1.0",
|
"@nextcloud/paths": "^2.1.0",
|
||||||
"@nextcloud/sharing": "^0.1.0",
|
"@nextcloud/sharing": "^0.1.0",
|
||||||
"@nextcloud/vue": "7.1.0",
|
"@nextcloud/vue": "7.2.0",
|
||||||
"camelcase": "^7.0.0",
|
"camelcase": "^7.0.0",
|
||||||
"filerobot-image-editor": "^4.3.7",
|
"filerobot-image-editor": "^4.3.7",
|
||||||
"justified-layout": "^4.1.0",
|
"justified-layout": "^4.1.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"node-polyfill-webpack-plugin": "^2.0.1",
|
||||||
"path-posix": "^1.0.0",
|
"path-posix": "^1.0.0",
|
||||||
"photoswipe": "^5.3.4",
|
"photoswipe": "^5.3.4",
|
||||||
"plyr": "^3.7.3",
|
"plyr": "^3.7.3",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"video.js": "^7.20.3",
|
"video.js": "^7.20.3",
|
||||||
"videojs-contrib-quality-levels": "^2.2.0",
|
"videojs-contrib-quality-levels": "^2.2.1",
|
||||||
"vue": "^2.7.10",
|
"vue": "^3.2.45",
|
||||||
"vue-material-design-icons": "^5.1.2",
|
"vue-material-design-icons": "^5.1.2",
|
||||||
"vue-router": "^3.5.4",
|
"vue-router": "^4.1.6",
|
||||||
"vue-virtual-scroller": "1.1.2",
|
"vue-virtual-scroller": "2.0.0-beta.5",
|
||||||
"webdav": "^4.11.2"
|
"webdav": "^4.11.2"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
|
@ -59,13 +60,19 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nextcloud/babel-config": "^1.0.0",
|
"@nextcloud/babel-config": "^1.0.0",
|
||||||
"@nextcloud/browserslist-config": "^2.3.0",
|
"@nextcloud/browserslist-config": "^2.3.0",
|
||||||
"@nextcloud/webpack-vue-config": "^5.4.0",
|
"@playwright/test": "^1.28.1",
|
||||||
"@playwright/test": "^1.28.0",
|
|
||||||
"@types/url-parse": "^1.4.8",
|
"@types/url-parse": "^1.4.8",
|
||||||
"@types/video.js": "^7.3.49",
|
"@types/video.js": "^7.3.50",
|
||||||
"playwright": "^1.28.0",
|
"babel-loader": "^9.1.0",
|
||||||
"ts-loader": "^9.4.1",
|
"css-loader": "^6.7.2",
|
||||||
"typescript": "^4.9.3",
|
"playwright": "^1.28.1",
|
||||||
|
"sass": "^1.56.2",
|
||||||
|
"sass-loader": "^13.2.0",
|
||||||
|
"style-loader": "^3.3.1",
|
||||||
|
"ts-loader": "^9.4.2",
|
||||||
|
"typescript": "^4.9.4",
|
||||||
|
"vue-loader": "^17.0.1",
|
||||||
|
"webpack-cli": "^5.0.1",
|
||||||
"workbox-webpack-plugin": "^6.5.4"
|
"workbox-webpack-plugin": "^6.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
src/App.vue
30
src/App.vue
|
@ -2,8 +2,8 @@
|
||||||
<FirstStart v-if="isFirstStart" />
|
<FirstStart v-if="isFirstStart" />
|
||||||
|
|
||||||
<NcContent
|
<NcContent
|
||||||
|
v-else-if="false"
|
||||||
app-name="memories"
|
app-name="memories"
|
||||||
v-else
|
|
||||||
:class="{
|
:class="{
|
||||||
'remove-gap': removeOuterGap,
|
'remove-gap': removeOuterGap,
|
||||||
}"
|
}"
|
||||||
|
@ -35,10 +35,14 @@
|
||||||
</div>
|
</div>
|
||||||
</NcAppContent>
|
</NcAppContent>
|
||||||
</NcContent>
|
</NcContent>
|
||||||
|
|
||||||
|
<div class="outer" v-else>
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue, { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
import NcContent from "@nextcloud/vue/dist/Components/NcContent";
|
import NcContent from "@nextcloud/vue/dist/Components/NcContent";
|
||||||
import NcAppContent from "@nextcloud/vue/dist/Components/NcAppContent";
|
import NcAppContent from "@nextcloud/vue/dist/Components/NcAppContent";
|
||||||
|
@ -95,16 +99,16 @@ export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
navItems: [],
|
navItems: [],
|
||||||
metadataComponent: null as Metadata,
|
metadataComponent: null as any,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
ncVersion() {
|
ncVersion(): number {
|
||||||
const version = (<any>window.OC).config.version.split(".");
|
const version = (<any>window.OC).config.version.split(".");
|
||||||
return Number(version[0]);
|
return Number(version[0]);
|
||||||
},
|
},
|
||||||
recognize() {
|
recognize(): string | boolean {
|
||||||
if (!this.config_recognizeEnabled) {
|
if (!this.config_recognizeEnabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -115,7 +119,7 @@ export default defineComponent({
|
||||||
|
|
||||||
return t("memories", "People");
|
return t("memories", "People");
|
||||||
},
|
},
|
||||||
facerecognition() {
|
facerecognition(): string | boolean {
|
||||||
if (!this.config_facerecognitionInstalled) {
|
if (!this.config_facerecognitionInstalled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -126,16 +130,16 @@ export default defineComponent({
|
||||||
|
|
||||||
return t("memories", "People");
|
return t("memories", "People");
|
||||||
},
|
},
|
||||||
isFirstStart() {
|
isFirstStart(): boolean {
|
||||||
return this.config_timelinePath === "EMPTY";
|
return this.config_timelinePath === "EMPTY";
|
||||||
},
|
},
|
||||||
showAlbums() {
|
showAlbums(): boolean {
|
||||||
return this.config_albumsEnabled;
|
return this.config_albumsEnabled;
|
||||||
},
|
},
|
||||||
removeOuterGap() {
|
removeOuterGap(): boolean {
|
||||||
return this.ncVersion >= 25;
|
return this.ncVersion >= 25;
|
||||||
},
|
},
|
||||||
showNavigation() {
|
showNavigation(): boolean {
|
||||||
return this.$route.name !== "folder-share";
|
return this.$route.name !== "folder-share";
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -174,7 +178,7 @@ export default defineComponent({
|
||||||
if (this.metadataComponent) {
|
if (this.metadataComponent) {
|
||||||
this.metadataComponent.$destroy();
|
this.metadataComponent.$destroy();
|
||||||
}
|
}
|
||||||
this.metadataComponent = new Vue(Metadata);
|
this.metadataComponent = new Metadata();
|
||||||
// Only mount after we have all the info we need
|
// Only mount after we have all the info we need
|
||||||
await this.metadataComponent.update(fileInfo);
|
await this.metadataComponent.update(fileInfo);
|
||||||
this.metadataComponent.$mount(el);
|
this.metadataComponent.$mount(el);
|
||||||
|
@ -281,9 +285,9 @@ export default defineComponent({
|
||||||
if (globalThis.windowInnerWidth <= 1024) nav?.toggleNavigation(false);
|
if (globalThis.windowInnerWidth <= 1024) nav?.toggleNavigation(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
doRouteChecks() {
|
doRouteChecks(): void {
|
||||||
if (this.$route.name === "folder-share") {
|
if (this.$route.name === "folder-share") {
|
||||||
this.putFolderShareToken(this.$route.params.token);
|
this.putFolderShareToken(<string>this.$route.params.token);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="text">
|
<div class="text">
|
||||||
{{ t("memories", "A better photos experience awaits you") }} <br />
|
{{ t("memories", "A better photos experience awaits you") }} <br />
|
||||||
{{
|
{{
|
||||||
t("memories", "Choose the root folder of your timeline to begin")
|
t("memories", "Choose the root folder of your timeline to begin")
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -160,6 +160,7 @@ export default defineComponent({
|
||||||
|
|
||||||
transition: opacity 1s ease;
|
transition: opacity 1s ease;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
||||||
&.show {
|
&.show {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
@ -174,7 +175,7 @@ export default defineComponent({
|
||||||
width: 100%;
|
width: 100%;
|
||||||
filter: var(--background-invert-if-dark);
|
filter: var(--background-invert-if-dark);
|
||||||
|
|
||||||
> img {
|
>img {
|
||||||
max-width: calc(100vw - 40px);
|
max-width: calc(100vw - 40px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,11 @@
|
||||||
|
|
||||||
<div class="edit" v-if="field.edit">
|
<div class="edit" v-if="field.edit">
|
||||||
<NcActions :inline="1">
|
<NcActions :inline="1">
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Edit')" @click="field.edit()">
|
||||||
:aria-label="t('memories', 'Edit')"
|
|
||||||
@click="field.edit()"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Edit") }}
|
{{ t("memories", "Edit") }}
|
||||||
<template #icon> <EditIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<EditIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
</div>
|
</div>
|
||||||
|
@ -67,6 +66,14 @@ import InfoIcon from "vue-material-design-icons/InformationOutline.vue";
|
||||||
import LocationIcon from "vue-material-design-icons/MapMarker.vue";
|
import LocationIcon from "vue-material-design-icons/MapMarker.vue";
|
||||||
import { API } from "../services/API";
|
import { API } from "../services/API";
|
||||||
|
|
||||||
|
interface TopField {
|
||||||
|
title: string;
|
||||||
|
subtitle: string[];
|
||||||
|
icon: any;
|
||||||
|
href?: string;
|
||||||
|
edit?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "Metadata",
|
name: "Metadata",
|
||||||
components: {
|
components: {
|
||||||
|
@ -78,7 +85,7 @@ export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fileInfo: null as IFileInfo,
|
fileInfo: null as IFileInfo,
|
||||||
exif: {} as { [prop: string]: any },
|
exif: {} as { [prop: string]: any; },
|
||||||
baseInfo: {} as any,
|
baseInfo: {} as any,
|
||||||
nominatim: null as any,
|
nominatim: null as any,
|
||||||
state: 0,
|
state: 0,
|
||||||
|
@ -94,14 +101,8 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
topFields() {
|
topFields(): TopField[] {
|
||||||
let list: {
|
let list: TopField[] = [];
|
||||||
title: string;
|
|
||||||
subtitle: string[];
|
|
||||||
icon: any;
|
|
||||||
href?: string;
|
|
||||||
edit?: () => void;
|
|
||||||
}[] = [];
|
|
||||||
|
|
||||||
if (this.dateOriginal) {
|
if (this.dateOriginal) {
|
||||||
list.push({
|
list.push({
|
||||||
|
@ -152,7 +153,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Date taken info */
|
/** Date taken info */
|
||||||
dateOriginal() {
|
dateOriginal(): moment.Moment | null {
|
||||||
const dt = this.exif["DateTimeOriginal"] || this.exif["CreateDate"];
|
const dt = this.exif["DateTimeOriginal"] || this.exif["CreateDate"];
|
||||||
if (!dt) return null;
|
if (!dt) return null;
|
||||||
|
|
||||||
|
@ -162,12 +163,12 @@ export default defineComponent({
|
||||||
return m;
|
return m;
|
||||||
},
|
},
|
||||||
|
|
||||||
dateOriginalStr() {
|
dateOriginalStr(): string | null {
|
||||||
if (!this.dateOriginal) return null;
|
if (!this.dateOriginal) return null;
|
||||||
return utils.getLongDateStr(this.dateOriginal.toDate(), true);
|
return utils.getLongDateStr(this.dateOriginal.toDate(), true);
|
||||||
},
|
},
|
||||||
|
|
||||||
dateOriginalTime() {
|
dateOriginalTime(): string[] | null {
|
||||||
if (!this.dateOriginal) return null;
|
if (!this.dateOriginal) return null;
|
||||||
|
|
||||||
// Try to get timezone
|
// Try to get timezone
|
||||||
|
@ -182,7 +183,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Camera make and model info */
|
/** Camera make and model info */
|
||||||
camera() {
|
camera(): string | null {
|
||||||
const make = this.exif["Make"];
|
const make = this.exif["Make"];
|
||||||
const model = this.exif["Model"];
|
const model = this.exif["Model"];
|
||||||
if (!make || !model) return null;
|
if (!make || !model) return null;
|
||||||
|
@ -190,7 +191,7 @@ export default defineComponent({
|
||||||
return `${make} ${model}`;
|
return `${make} ${model}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
cameraSub() {
|
cameraSub(): string[] | null {
|
||||||
const f = this.exif["FNumber"] || this.exif["Aperture"];
|
const f = this.exif["FNumber"] || this.exif["Aperture"];
|
||||||
const s = this.shutterSpeed;
|
const s = this.shutterSpeed;
|
||||||
const len = this.exif["FocalLength"];
|
const len = this.exif["FocalLength"];
|
||||||
|
@ -205,11 +206,11 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Convert shutter speed decimal to 1/x format */
|
/** Convert shutter speed decimal to 1/x format */
|
||||||
shutterSpeed() {
|
shutterSpeed(): string | null {
|
||||||
const speed = Number(
|
const speed = Number(
|
||||||
this.exif["ShutterSpeedValue"] ||
|
this.exif["ShutterSpeedValue"] ||
|
||||||
this.exif["ShutterSpeed"] ||
|
this.exif["ShutterSpeed"] ||
|
||||||
this.exif["ExposureTime"]
|
this.exif["ExposureTime"]
|
||||||
);
|
);
|
||||||
if (!speed) return null;
|
if (!speed) return null;
|
||||||
|
|
||||||
|
@ -221,11 +222,11 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Image info */
|
/** Image info */
|
||||||
imageInfo() {
|
imageInfo(): string | null {
|
||||||
return this.fileInfo.basename || (<any>this.fileInfo).name;
|
return this.fileInfo.basename || (<any>this.fileInfo).name;
|
||||||
},
|
},
|
||||||
|
|
||||||
imageInfoSub() {
|
imageInfoSub(): string[] | null {
|
||||||
let parts = [];
|
let parts = [];
|
||||||
let mp = Number(this.exif["Megapixels"]);
|
let mp = Number(this.exif["Megapixels"]);
|
||||||
|
|
||||||
|
@ -244,7 +245,7 @@ export default defineComponent({
|
||||||
return parts;
|
return parts;
|
||||||
},
|
},
|
||||||
|
|
||||||
address() {
|
address(): string | null {
|
||||||
if (!this.lat || !this.lon) return null;
|
if (!this.lat || !this.lon) return null;
|
||||||
|
|
||||||
if (!this.nominatim) return this.t("memories", "Loading …");
|
if (!this.nominatim) return this.t("memories", "Loading …");
|
||||||
|
@ -263,15 +264,15 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
lat() {
|
lat(): number | null {
|
||||||
return this.exif["GPSLatitude"];
|
return this.exif["GPSLatitude"];
|
||||||
},
|
},
|
||||||
|
|
||||||
lon() {
|
lon(): number | null {
|
||||||
return this.exif["GPSLongitude"];
|
return this.exif["GPSLongitude"];
|
||||||
},
|
},
|
||||||
|
|
||||||
mapUrl() {
|
mapUrl(): string | null {
|
||||||
const boxSize = 0.0075;
|
const boxSize = 0.0075;
|
||||||
const bbox = [
|
const bbox = [
|
||||||
this.lon - boxSize,
|
this.lon - boxSize,
|
||||||
|
@ -283,7 +284,7 @@ export default defineComponent({
|
||||||
return `https://www.openstreetmap.org/export/embed.html?bbox=${bbox.join()}&marker=${m}`;
|
return `https://www.openstreetmap.org/export/embed.html?bbox=${bbox.join()}&marker=${m}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
mapFullUrl() {
|
mapFullUrl(): string | null {
|
||||||
return `https://www.openstreetmap.org/?mlat=${this.lat}&mlon=${this.lon}#map=18/${this.lat}/${this.lon}`;
|
return `https://www.openstreetmap.org/?mlat=${this.lat}&mlon=${this.lon}#map=18/${this.lat}/${this.lon}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -345,9 +346,11 @@ export default defineComponent({
|
||||||
color: var(--color-text-lighter);
|
color: var(--color-text-lighter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit {
|
.edit {
|
||||||
transform: translateX(10px);
|
transform: translateX(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
@ -355,6 +358,7 @@ export default defineComponent({
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: 0.95em;
|
font-size: 0.95em;
|
||||||
|
|
||||||
.part {
|
.part {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="scroller" ref="scroller" v-bind:class="{
|
||||||
class="scroller"
|
'scrolling-recycler-now': scrollingRecyclerNowTimer,
|
||||||
ref="scroller"
|
'scrolling-recycler': scrollingRecyclerTimer,
|
||||||
v-bind:class="{
|
'scrolling-now': scrollingNowTimer,
|
||||||
'scrolling-recycler-now': scrollingRecyclerNowTimer,
|
scrolling: scrollingTimer,
|
||||||
'scrolling-recycler': scrollingRecyclerTimer,
|
}" @mousemove.passive="mousemove" @mouseleave.passive="mouseleave" @mousedown.passive="mousedown"
|
||||||
'scrolling-now': scrollingNowTimer,
|
@mouseup.passive="interactend" @touchmove.prevent="touchmove" @touchstart.passive="interactstart"
|
||||||
scrolling: scrollingTimer,
|
@touchend.passive="interactend" @touchcancel.passive="interactend">
|
||||||
}"
|
<span class="cursor st" ref="cursorSt" :style="{ transform: `translateY(${cursorY}px)` }">
|
||||||
@mousemove.passive="mousemove"
|
|
||||||
@mouseleave.passive="mouseleave"
|
|
||||||
@mousedown.passive="mousedown"
|
|
||||||
@mouseup.passive="interactend"
|
|
||||||
@touchmove.prevent="touchmove"
|
|
||||||
@touchstart.passive="interactstart"
|
|
||||||
@touchend.passive="interactend"
|
|
||||||
@touchcancel.passive="interactend"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="cursor st"
|
|
||||||
ref="cursorSt"
|
|
||||||
:style="{ transform: `translateY(${cursorY}px)` }"
|
|
||||||
>
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span class="cursor hv" :style="{ transform: `translateY(${hoverCursorY}px)` }" @touchmove.prevent="touchmove"
|
||||||
class="cursor hv"
|
@touchstart.passive="interactstart" @touchend.passive="interactend" @touchcancel.passive="interactend">
|
||||||
:style="{ transform: `translateY(${hoverCursorY}px)` }"
|
|
||||||
@touchmove.prevent="touchmove"
|
|
||||||
@touchstart.passive="interactstart"
|
|
||||||
@touchend.passive="interactend"
|
|
||||||
@touchcancel.passive="interactend"
|
|
||||||
>
|
|
||||||
<div class="text">{{ hoverCursorText }}</div>
|
<div class="text">{{ hoverCursorText }}</div>
|
||||||
<div class="icon"><ScrollIcon :size="22" /></div>
|
<div class="icon">
|
||||||
|
<ScrollIcon :size="22" />
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div
|
<div v-for="tick of visibleTicks" :key="tick.key" class="tick" :class="{ dash: !tick.text }"
|
||||||
v-for="tick of visibleTicks"
|
:style="{ transform: `translateY(calc(${tick.top}px - 50%))` }">
|
||||||
:key="tick.key"
|
|
||||||
class="tick"
|
|
||||||
:class="{ dash: !tick.text }"
|
|
||||||
:style="{ transform: `translateY(calc(${tick.top}px - 50%))` }"
|
|
||||||
>
|
|
||||||
<span v-if="tick.text">{{ tick.text }}</span>
|
<span v-if="tick.text">{{ tick.text }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -152,7 +129,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Recycler scroll event, must be called by timeline */
|
/** Recycler scroll event, must be called by timeline */
|
||||||
recyclerScrolled() {
|
recyclerScrolled(event: Event | null) {
|
||||||
// This isn't a renewing timer, it's a scheduled task
|
// This isn't a renewing timer, it's a scheduled task
|
||||||
if (this.scrollingRecyclerUpdateTimer) return;
|
if (this.scrollingRecyclerUpdateTimer) return;
|
||||||
this.scrollingRecyclerUpdateTimer = window.setTimeout(() => {
|
this.scrollingRecyclerUpdateTimer = window.setTimeout(() => {
|
||||||
|
@ -510,7 +487,7 @@ export default defineComponent({
|
||||||
|
|
||||||
interactend() {
|
interactend() {
|
||||||
this.interacting = false;
|
this.interacting = false;
|
||||||
this.recyclerScrolled(); // make sure final position is correct
|
this.recyclerScrolled(null); // make sure final position is correct
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Update scroller is being used to scroll recycler */
|
/** Update scroller is being used to scroll recycler */
|
||||||
|
@ -547,7 +524,7 @@ export default defineComponent({
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .tick {
|
>.tick {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
|
@ -566,6 +543,7 @@ export default defineComponent({
|
||||||
background-color: var(--color-main-text);
|
background-color: var(--color-main-text);
|
||||||
opacity: 0.15;
|
opacity: 0.15;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
@include phone {
|
@include phone {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -578,7 +556,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .cursor {
|
>.cursor {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -603,17 +581,20 @@ export default defineComponent({
|
||||||
font-size: 0.95em;
|
font-size: 0.95em;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
||||||
> .icon {
|
>.icon {
|
||||||
display: none;
|
display: none;
|
||||||
transform: translate(-16px, 6px);
|
transform: translate(-16px, 6px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.scrolling-recycler-now:not(.scrolling-now) > .cursor {
|
|
||||||
|
&.scrolling-recycler-now:not(.scrolling-now)>.cursor {
|
||||||
transition: transform 0.1s linear;
|
transition: transform 0.1s linear;
|
||||||
}
|
}
|
||||||
&:hover > .cursor {
|
|
||||||
|
&:hover>.cursor {
|
||||||
transition: none !important;
|
transition: none !important;
|
||||||
|
|
||||||
&.st {
|
&.st {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
@ -623,15 +604,17 @@ export default defineComponent({
|
||||||
@include phone {
|
@include phone {
|
||||||
// Shift pointer events to hover cursor
|
// Shift pointer events to hover cursor
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
.cursor.hv {
|
.cursor.hv {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .tick {
|
>.tick {
|
||||||
right: 40px;
|
right: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.scrolling) {
|
&:not(.scrolling) {
|
||||||
> .tick {
|
>.tick {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -643,10 +626,12 @@ export default defineComponent({
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 70px;
|
width: 70px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
> .text {
|
|
||||||
|
>.text {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
> .icon {
|
|
||||||
|
>.icon {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,31 +2,25 @@
|
||||||
<div>
|
<div>
|
||||||
<div v-if="show" class="top-bar">
|
<div v-if="show" class="top-bar">
|
||||||
<NcActions :inline="1">
|
<NcActions :inline="1">
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Cancel')" @click="clearSelection()">
|
||||||
:aria-label="t('memories', 'Cancel')"
|
|
||||||
@click="clearSelection()"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Cancel") }}
|
{{ t("memories", "Cancel") }}
|
||||||
<template #icon> <CloseIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<CloseIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
|
|
||||||
<div class="text">
|
<div class="text">
|
||||||
{{
|
{{
|
||||||
n("memories", "{n} selected", "{n} selected", size, {
|
n("memories", "{n} selected", "{n} selected", size, {
|
||||||
n: size,
|
n: size,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NcActions :inline="1">
|
<NcActions :inline="1">
|
||||||
<NcActionButton
|
<NcActionButton v-for="action of getActions()" :key="action.name" :aria-label="action.name" close-after-click
|
||||||
v-for="action of getActions()"
|
@click="click(action)">
|
||||||
:key="action.name"
|
|
||||||
:aria-label="action.name"
|
|
||||||
close-after-click
|
|
||||||
@click="click(action)"
|
|
||||||
>
|
|
||||||
{{ action.name }}
|
{{ action.name }}
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<component :is="action.icon" :size="20" />
|
<component :is="action.icon" :size="20" />
|
||||||
|
@ -38,11 +32,7 @@
|
||||||
<!-- Selection Modals -->
|
<!-- Selection Modals -->
|
||||||
<EditDate ref="editDate" @refresh="refresh" />
|
<EditDate ref="editDate" @refresh="refresh" />
|
||||||
<EditExif ref="editExif" @refresh="refresh" />
|
<EditExif ref="editExif" @refresh="refresh" />
|
||||||
<FaceMoveModal
|
<FaceMoveModal ref="faceMoveModal" @moved="deletePhotos" :updateLoading="updateLoading" />
|
||||||
ref="faceMoveModal"
|
|
||||||
@moved="deletePhotos"
|
|
||||||
:updateLoading="updateLoading"
|
|
||||||
/>
|
|
||||||
<AddToAlbumModal ref="addToAlbumModal" @added="clearSelection" />
|
<AddToAlbumModal ref="addToAlbumModal" @added="clearSelection" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -103,7 +93,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
heads: Object as PropType<{ [dayid: number]: IHeadRow }>,
|
heads: Object as PropType<{ [dayid: number]: IHeadRow; }>,
|
||||||
/** List of rows for multi selection */
|
/** List of rows for multi selection */
|
||||||
rows: Array as PropType<IRow[]>,
|
rows: Array as PropType<IRow[]>,
|
||||||
/** Rows are in ascending order (desc is normal) */
|
/** Rows are in ascending order (desc is normal) */
|
||||||
|
@ -840,8 +830,8 @@ export default defineComponent({
|
||||||
|
|
||||||
// Run query
|
// Run query
|
||||||
for await (let delIds of dav.removeFaceImages(
|
for await (let delIds of dav.removeFaceImages(
|
||||||
user,
|
<string>user,
|
||||||
name,
|
<string>name,
|
||||||
Array.from(selection.values())
|
Array.from(selection.values())
|
||||||
)) {
|
)) {
|
||||||
const delPhotos = delIds
|
const delPhotos = delIds
|
||||||
|
@ -879,7 +869,7 @@ export default defineComponent({
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|
||||||
> .text {
|
>.text {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
line-height: 42px;
|
line-height: 42px;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
|
|
|
@ -23,42 +23,20 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<label for="timeline-path">{{ t("memories", "Timeline Path") }}</label>
|
<label for="timeline-path">{{ t("memories", "Timeline Path") }}</label>
|
||||||
<input
|
<input id="timeline-path" @click="chooseTimelinePath" v-model="config_timelinePath" type="text" />
|
||||||
id="timeline-path"
|
|
||||||
@click="chooseTimelinePath"
|
|
||||||
v-model="config_timelinePath"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<label for="folders-path">{{ t("memories", "Folders Path") }}</label>
|
<label for="folders-path">{{ t("memories", "Folders Path") }}</label>
|
||||||
<input
|
<input id="folders-path" @click="chooseFoldersPath" v-model="config_foldersPath" type="text" />
|
||||||
id="folders-path"
|
|
||||||
@click="chooseFoldersPath"
|
|
||||||
v-model="config_foldersPath"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<NcCheckboxRadioSwitch
|
<NcCheckboxRadioSwitch :checked.sync="config_showHidden" @update:checked="updateShowHidden" type="switch">
|
||||||
:checked.sync="config_showHidden"
|
|
||||||
@update:checked="updateShowHidden"
|
|
||||||
type="switch"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Show hidden folders") }}
|
{{ t("memories", "Show hidden folders") }}
|
||||||
</NcCheckboxRadioSwitch>
|
</NcCheckboxRadioSwitch>
|
||||||
|
|
||||||
<NcCheckboxRadioSwitch
|
<NcCheckboxRadioSwitch :checked.sync="config_squareThumbs" @update:checked="updateSquareThumbs" type="switch">
|
||||||
:checked.sync="config_squareThumbs"
|
|
||||||
@update:checked="updateSquareThumbs"
|
|
||||||
type="switch"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Square grid mode") }}
|
{{ t("memories", "Square grid mode") }}
|
||||||
</NcCheckboxRadioSwitch>
|
</NcCheckboxRadioSwitch>
|
||||||
|
|
||||||
<MultiPathSelectionModal
|
<MultiPathSelectionModal ref="multiPathModal" :title="pathSelTitle" @close="saveTimelinePath" />
|
||||||
ref="multiPathModal"
|
|
||||||
:title="pathSelTitle"
|
|
||||||
@close="saveTimelinePath"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -86,7 +64,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
pathSelTitle() {
|
pathSelTitle(): string {
|
||||||
return this.t("memories", "Choose Timeline Paths");
|
return this.t("memories", "Choose Timeline Paths");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="container" ref="container" :class="{ 'icon-loading': loading > 0 }">
|
||||||
class="container"
|
|
||||||
ref="container"
|
|
||||||
:class="{ 'icon-loading': loading > 0 }"
|
|
||||||
>
|
|
||||||
<!-- Static top matter -->
|
<!-- Static top matter -->
|
||||||
<TopMatter ref="topmatter" />
|
<TopMatter ref="topmatter" />
|
||||||
|
|
||||||
<!-- No content found and nothing is loading -->
|
<!-- No content found and nothing is loading -->
|
||||||
<NcEmptyContent
|
<!-- <NcEmptyContent
|
||||||
title="Nothing to show here"
|
title="Nothing to show here"
|
||||||
:description="emptyViewDescription"
|
:description="emptyViewDescription"
|
||||||
v-if="loading === 0 && list.length === 0"
|
v-if="loading === 0 && list.length === 0"
|
||||||
|
@ -18,49 +14,28 @@
|
||||||
<ArchiveIcon v-else-if="routeIsArchive" />
|
<ArchiveIcon v-else-if="routeIsArchive" />
|
||||||
<ImageMultipleIcon v-else />
|
<ImageMultipleIcon v-else />
|
||||||
</template>
|
</template>
|
||||||
</NcEmptyContent>
|
</NcEmptyContent> -->
|
||||||
|
|
||||||
<!-- Main recycler view for rows -->
|
<!-- Main recycler view for rows -->
|
||||||
<RecycleScroller
|
<RecycleScroller ref="recycler" class="recycler" :class="{ empty: list.length === 0 }" :items="list"
|
||||||
ref="recycler"
|
:emit-update="true" :buffer="800" :skipHover="true" key-field="id" size-field="size" type-field="type"
|
||||||
class="recycler"
|
:updateInterval="100" @update="scrollChange" @resize="handleResizeWithDelay">
|
||||||
:class="{ empty: list.length === 0 }"
|
|
||||||
:items="list"
|
|
||||||
:emit-update="true"
|
|
||||||
:buffer="800"
|
|
||||||
:skipHover="true"
|
|
||||||
key-field="id"
|
|
||||||
size-field="size"
|
|
||||||
type-field="type"
|
|
||||||
:updateInterval="100"
|
|
||||||
@update="scrollChange"
|
|
||||||
@resize="handleResizeWithDelay"
|
|
||||||
>
|
|
||||||
<template #before>
|
<template #before>
|
||||||
<!-- Show dynamic top matter, name of the view -->
|
<!-- Show dynamic top matter, name of the view -->
|
||||||
<div class="recycler-before" ref="recyclerBefore">
|
<div class="recycler-before" ref="recyclerBefore">
|
||||||
<div class="text" v-show="!$refs.topmatter.type && list.length > 0">
|
<div class="text" v-show="!(<any>$refs.topmatter)?.type && list.length > 0">
|
||||||
{{ viewName }}
|
{{ viewName }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<OnThisDay
|
<OnThisDay v-if="routeIsBase" :key="config_timelinePath" :viewer="$refs.viewer"
|
||||||
v-if="routeIsBase"
|
@load="scrollerManager.adjust()">
|
||||||
:key="config_timelinePath"
|
|
||||||
:viewer="$refs.viewer"
|
|
||||||
@load="scrollerManager.adjust()"
|
|
||||||
>
|
|
||||||
</OnThisDay>
|
</OnThisDay>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot="{ item, index }">
|
<template v-slot="{ item, index }">
|
||||||
<div
|
<div v-if="item.type === 0" class="head-row" :class="{ selected: item.selected }"
|
||||||
v-if="item.type === 0"
|
:style="{ height: item.size + 'px' }" :key="item.id">
|
||||||
class="head-row"
|
|
||||||
:class="{ selected: item.selected }"
|
|
||||||
:style="{ height: item.size + 'px' }"
|
|
||||||
:key="item.id"
|
|
||||||
>
|
|
||||||
<div class="super" v-if="item.super !== undefined">
|
<div class="super" v-if="item.super !== undefined">
|
||||||
{{ item.super }}
|
{{ item.super }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,72 +46,34 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div
|
<div class="photo" v-for="photo of item.photos" :key="photo.key" :style="{
|
||||||
class="photo"
|
height: photo.dispH + 'px',
|
||||||
v-for="photo of item.photos"
|
width: photo.dispW + 'px',
|
||||||
:key="photo.key"
|
transform: `translate(${photo.dispX}px, ${photo.dispY}px`,
|
||||||
:style="{
|
}">
|
||||||
height: photo.dispH + 'px',
|
<Folder v-if="photo.flag & c.FLAG_IS_FOLDER" :data="photo" />
|
||||||
width: photo.dispW + 'px',
|
|
||||||
transform: `translate(${photo.dispX}px, ${photo.dispY}px`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<Folder
|
|
||||||
v-if="photo.flag & c.FLAG_IS_FOLDER"
|
|
||||||
:data="photo"
|
|
||||||
:key="photo.fileid"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Tag
|
<Tag v-else-if="photo.flag & c.FLAG_IS_TAG" :data="photo" />
|
||||||
v-else-if="photo.flag & c.FLAG_IS_TAG"
|
|
||||||
:data="photo"
|
|
||||||
:key="photo.fileid"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Photo
|
<Photo v-else :data="photo" :day="item.day" @select="selectionManager.selectPhoto"
|
||||||
v-else
|
@pointerdown="selectionManager.clickPhoto(photo, $event, index)" @touchstart="
|
||||||
:data="photo"
|
|
||||||
:day="item.day"
|
|
||||||
:key="photo.fileid"
|
|
||||||
@select="selectionManager.selectPhoto"
|
|
||||||
@pointerdown="selectionManager.clickPhoto(photo, $event, index)"
|
|
||||||
@touchstart="
|
|
||||||
selectionManager.touchstartPhoto(photo, $event, index)
|
selectionManager.touchstartPhoto(photo, $event, index)
|
||||||
"
|
" @touchend="selectionManager.touchendPhoto(photo, $event, index)"
|
||||||
@touchend="selectionManager.touchendPhoto(photo, $event, index)"
|
@touchmove="selectionManager.touchmovePhoto(photo, $event, index)" />
|
||||||
@touchmove="selectionManager.touchmovePhoto(photo, $event, index)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</RecycleScroller>
|
</RecycleScroller>
|
||||||
|
|
||||||
<!-- Managers -->
|
<!-- Managers -->
|
||||||
<ScrollerManager
|
<ScrollerManager ref="scrollerManager" :rows="list" :height="scrollerHeight" :recycler="$refs.recycler"
|
||||||
ref="scrollerManager"
|
:recyclerBefore="($refs.recyclerBefore as any)" />
|
||||||
:rows="list"
|
|
||||||
:height="scrollerHeight"
|
|
||||||
:recycler="$refs.recycler"
|
|
||||||
:recyclerBefore="$refs.recyclerBefore"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SelectionManager
|
<SelectionManager ref="selectionManager" :heads="heads" :rows="list" :isreverse="isMonthView"
|
||||||
ref="selectionManager"
|
:recycler="$refs.recycler" @refresh="softRefresh" @delete="deleteFromViewWithAnimation"
|
||||||
:heads="heads"
|
@updateLoading="updateLoading" />
|
||||||
:rows="list"
|
|
||||||
:isreverse="isMonthView"
|
|
||||||
:recycler="$refs.recycler"
|
|
||||||
@refresh="softRefresh"
|
|
||||||
@delete="deleteFromViewWithAnimation"
|
|
||||||
@updateLoading="updateLoading"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Viewer
|
<Viewer ref="viewer" @deleted="deleteFromViewWithAnimation" @fetchDay="fetchDay" @updateLoading="updateLoading" />
|
||||||
ref="viewer"
|
|
||||||
@deleted="deleteFromViewWithAnimation"
|
|
||||||
@fetchDay="fetchDay"
|
|
||||||
@updateLoading="updateLoading"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -201,7 +138,7 @@ export default defineComponent({
|
||||||
/** Computed number of columns */
|
/** Computed number of columns */
|
||||||
numCols: 0,
|
numCols: 0,
|
||||||
/** Header rows for dayId key */
|
/** Header rows for dayId key */
|
||||||
heads: {} as { [dayid: number]: IHeadRow },
|
heads: {} as { [dayid: number]: IHeadRow; },
|
||||||
|
|
||||||
/** Computed row height */
|
/** Computed row height */
|
||||||
rowHeight: 100,
|
rowHeight: 100,
|
||||||
|
@ -230,15 +167,15 @@ export default defineComponent({
|
||||||
state: Math.random(),
|
state: Math.random(),
|
||||||
|
|
||||||
/** Selection manager component */
|
/** Selection manager component */
|
||||||
selectionManager: null as SelectionManager & any,
|
selectionManager: null as InstanceType<typeof SelectionManager>,
|
||||||
/** Scroller manager component */
|
/** Scroller manager component */
|
||||||
scrollerManager: null as ScrollerManager & any,
|
scrollerManager: null as InstanceType<typeof ScrollerManager>,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.selectionManager = this.$refs.selectionManager;
|
this.selectionManager = <any>this.$refs.selectionManager;
|
||||||
this.scrollerManager = this.$refs.scrollerManager;
|
this.scrollerManager = <any>this.$refs.scrollerManager;
|
||||||
this.routeChange(this.$route);
|
this.routeChange(this.$route);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -265,20 +202,20 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
routeIsBase() {
|
routeIsBase(): boolean {
|
||||||
return this.$route.name === "timeline";
|
return this.$route.name === "timeline";
|
||||||
},
|
},
|
||||||
routeIsPeople() {
|
routeIsPeople(): boolean {
|
||||||
return ["recognize", "facerecognition"].includes(this.$route.name);
|
return ["recognize", "facerecognition"].includes(<string>this.$route.name);
|
||||||
},
|
},
|
||||||
routeIsArchive() {
|
routeIsArchive(): boolean {
|
||||||
return this.$route.name === "archive";
|
return this.$route.name === "archive";
|
||||||
},
|
},
|
||||||
isMonthView() {
|
isMonthView(): boolean {
|
||||||
return this.$route.name === "albums";
|
return this.$route.name === "albums";
|
||||||
},
|
},
|
||||||
/** Get view name for dynamic top matter */
|
/** Get view name for dynamic top matter */
|
||||||
viewName() {
|
viewName(): string {
|
||||||
switch (this.$route.name) {
|
switch (this.$route.name) {
|
||||||
case "timeline":
|
case "timeline":
|
||||||
return this.t("memories", "Your Timeline");
|
return this.t("memories", "Your Timeline");
|
||||||
|
@ -301,7 +238,7 @@ export default defineComponent({
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emptyViewDescription() {
|
emptyViewDescription(): string {
|
||||||
switch (this.$route.name) {
|
switch (this.$route.name) {
|
||||||
case "facerecognition":
|
case "facerecognition":
|
||||||
if (this.config_facerecognitionEnabled)
|
if (this.config_facerecognitionEnabled)
|
||||||
|
@ -655,7 +592,7 @@ export default defineComponent({
|
||||||
this.$route.params.name
|
this.$route.params.name
|
||||||
) {
|
) {
|
||||||
query.set(
|
query.set(
|
||||||
this.$route.name, // "recognize" or "facerecognition"
|
<string>this.$route.name, // "recognize" or "facerecognition"
|
||||||
`${this.$route.params.user}/${this.$route.params.name}`
|
`${this.$route.params.user}/${this.$route.params.name}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -667,7 +604,7 @@ export default defineComponent({
|
||||||
|
|
||||||
// Tags
|
// Tags
|
||||||
if (this.$route.name === "tags" && this.$route.params.name) {
|
if (this.$route.name === "tags" && this.$route.params.name) {
|
||||||
query.set("tag", this.$route.params.name);
|
query.set("tag", <string>this.$route.params.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Albums
|
// Albums
|
||||||
|
@ -718,7 +655,7 @@ export default defineComponent({
|
||||||
/** Fetch timeline main call */
|
/** Fetch timeline main call */
|
||||||
async fetchDays(noCache = false) {
|
async fetchDays(noCache = false) {
|
||||||
const url = API.Q(API.DAYS(), this.getQuery());
|
const url = API.Q(API.DAYS(), this.getQuery());
|
||||||
const cacheUrl = this.$route.name + url;
|
const cacheUrl = <string>this.$route.name + url;
|
||||||
|
|
||||||
// Try cache first
|
// Try cache first
|
||||||
let cache: IDay[];
|
let cache: IDay[];
|
||||||
|
@ -1349,13 +1286,15 @@ export default defineComponent({
|
||||||
padding-left: 3px;
|
padding-left: 3px;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
|
|
||||||
> div {
|
>div {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&.super {
|
&.super {
|
||||||
font-size: 1.4em;
|
font-size: 1.4em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.main {
|
&.main {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
@ -1373,6 +1312,7 @@ export default defineComponent({
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
display: block;
|
display: block;
|
||||||
transition: transform 0.2s ease;
|
transition: transform 0.2s ease;
|
||||||
|
@ -1386,10 +1326,12 @@ export default defineComponent({
|
||||||
display: flex;
|
display: flex;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
transform: translateX(24px);
|
transform: translateX(24px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected .select {
|
&.selected .select {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
|
@ -1403,16 +1345,20 @@ export default defineComponent({
|
||||||
/** Static and dynamic top matter */
|
/** Static and dynamic top matter */
|
||||||
.top-matter {
|
.top-matter {
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
|
|
||||||
@include phone {
|
@include phone {
|
||||||
padding-left: 40px;
|
padding-left: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.recycler-before {
|
.recycler-before {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
> .text {
|
|
||||||
|
>.text {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
padding-top: 13px;
|
padding-top: 13px;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
|
|
||||||
@include phone {
|
@include phone {
|
||||||
padding-left: 48px;
|
padding-left: 48px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<router-link
|
<router-link draggable="false" class="folder fill-block" :class="{
|
||||||
draggable="false"
|
hasPreview: previews.length > 0,
|
||||||
class="folder fill-block"
|
onePreview: previews.length === 1,
|
||||||
:class="{
|
hasError: error,
|
||||||
hasPreview: previews.length > 0,
|
}" :to="target">
|
||||||
onePreview: previews.length === 1,
|
|
||||||
hasError: error,
|
|
||||||
}"
|
|
||||||
:to="target"
|
|
||||||
>
|
|
||||||
<div class="big-icon fill-block">
|
<div class="big-icon fill-block">
|
||||||
<FolderIcon class="icon" />
|
<FolderIcon class="icon" />
|
||||||
<div class="name">{{ data.name }}</div>
|
<div class="name">{{ data.name }}</div>
|
||||||
|
@ -17,11 +12,7 @@
|
||||||
<div class="previews fill-block">
|
<div class="previews fill-block">
|
||||||
<div class="preview-container fill-block">
|
<div class="preview-container fill-block">
|
||||||
<div class="img-outer" v-for="info of previews" :key="info.fileid">
|
<div class="img-outer" v-for="info of previews" :key="info.fileid">
|
||||||
<img
|
<img class="fill-block" :src="getPreviewUrl(info, true, 256)" @error="$event.target.classList.add('error')" />
|
||||||
class="fill-block"
|
|
||||||
:src="getPreviewUrl(info, true, 256)"
|
|
||||||
@error="$event.target.classList.add('error')"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -132,7 +123,7 @@ export default defineComponent({
|
||||||
height: 50%;
|
height: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .name {
|
>.name {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 5%;
|
padding: 0 5%;
|
||||||
|
@ -151,36 +142,38 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make it white if there is a preview
|
// Make it white if there is a preview
|
||||||
.folder.hasPreview > & {
|
.folder.hasPreview>& {
|
||||||
.folder-icon {
|
.folder-icon {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
filter: invert(1) brightness(100);
|
filter: invert(1) brightness(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show it on hover if not a preview
|
// Show it on hover if not a preview
|
||||||
.folder:hover > & > .folder-icon {
|
.folder:hover>&>.folder-icon {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
.folder.hasPreview:hover > & {
|
|
||||||
|
.folder.hasPreview:hover>& {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make it red if has an error
|
// Make it red if has an error
|
||||||
.folder.hasError > & {
|
.folder.hasError>& {
|
||||||
.folder-icon {
|
.folder-icon {
|
||||||
filter: invert(12%) sepia(62%) saturate(5862%) hue-rotate(8deg)
|
filter: invert(12%) sepia(62%) saturate(5862%) hue-rotate(8deg) brightness(103%) contrast(128%);
|
||||||
brightness(103%) contrast(128%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
color: #bb0000;
|
color: #bb0000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .folder-icon {
|
>.folder-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 90%;
|
height: 90%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -208,12 +201,12 @@ export default defineComponent({
|
||||||
height: 50%;
|
height: 50%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
.folder.onePreview > & {
|
.folder.onePreview>& {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
> img {
|
>img {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
filter: brightness(50%);
|
filter: brightness(50%);
|
||||||
|
@ -222,6 +215,7 @@ export default defineComponent({
|
||||||
&.error {
|
&.error {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.folder:hover & {
|
.folder:hover & {
|
||||||
filter: brightness(100%);
|
filter: brightness(100%);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="p-outer fill-block" :class="{
|
||||||
class="p-outer fill-block"
|
selected: data.flag & c.FLAG_SELECTED,
|
||||||
:class="{
|
placeholder: data.flag & c.FLAG_PLACEHOLDER,
|
||||||
selected: data.flag & c.FLAG_SELECTED,
|
leaving: data.flag & c.FLAG_LEAVING,
|
||||||
placeholder: data.flag & c.FLAG_PLACEHOLDER,
|
error: data.flag & c.FLAG_LOAD_FAIL,
|
||||||
leaving: data.flag & c.FLAG_LEAVING,
|
}">
|
||||||
error: data.flag & c.FLAG_LOAD_FAIL,
|
<CheckCircle :size="18" class="select" v-if="!(data.flag & c.FLAG_PLACEHOLDER)" @click="toggleSelect" />
|
||||||
}"
|
|
||||||
>
|
|
||||||
<CheckCircle
|
|
||||||
:size="18"
|
|
||||||
class="select"
|
|
||||||
v-if="!(data.flag & c.FLAG_PLACEHOLDER)"
|
|
||||||
@click="toggleSelect"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="video" v-if="data.flag & c.FLAG_IS_VIDEO">
|
<div class="video" v-if="data.flag & c.FLAG_IS_VIDEO">
|
||||||
<span v-if="data.video_duration" class="time">
|
<span v-if="data.video_duration" class="time">
|
||||||
|
@ -22,45 +14,20 @@
|
||||||
<Video :size="22" />
|
<Video :size="22" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="livephoto" @mouseenter.passive="playVideo" @mouseleave.passive="stopVideo">
|
||||||
class="livephoto"
|
|
||||||
@mouseenter.passive="playVideo"
|
|
||||||
@mouseleave.passive="stopVideo"
|
|
||||||
>
|
|
||||||
<LivePhoto :size="22" v-if="data.liveid" />
|
<LivePhoto :size="22" v-if="data.liveid" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Star :size="22" v-if="data.flag & c.FLAG_IS_FAVORITE" />
|
<Star :size="22" v-if="data.flag & c.FLAG_IS_FAVORITE" />
|
||||||
|
|
||||||
<div
|
<div class="img-outer fill-block" :class="{
|
||||||
class="img-outer fill-block"
|
'memories-livephoto': data.liveid,
|
||||||
:class="{
|
}" @contextmenu="contextmenu" @pointerdown.passive="$emit('pointerdown', $event)"
|
||||||
'memories-livephoto': data.liveid,
|
@touchstart.passive="$emit('touchstart', $event)" @touchmove="$emit('touchmove', $event)"
|
||||||
}"
|
@touchend.passive="$emit('touchend', $event)" @touchcancel.passive="$emit('touchend', $event)">
|
||||||
@contextmenu="contextmenu"
|
<img ref="img" :class="['fill-block', `memories-thumb-${data.key}`]" draggable="false" :src="src"
|
||||||
@pointerdown.passive="$emit('pointerdown', $event)"
|
:key="data.fileid" @load="load" @error="error" />
|
||||||
@touchstart.passive="$emit('touchstart', $event)"
|
<video ref="video" v-if="videoUrl" :src="videoUrl" preload="none" muted playsinline />
|
||||||
@touchmove="$emit('touchmove', $event)"
|
|
||||||
@touchend.passive="$emit('touchend', $event)"
|
|
||||||
@touchcancel.passive="$emit('touchend', $event)"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
ref="img"
|
|
||||||
:class="['fill-block', `memories-thumb-${data.key}`]"
|
|
||||||
draggable="false"
|
|
||||||
:src="src"
|
|
||||||
:key="data.fileid"
|
|
||||||
@load="load"
|
|
||||||
@error="error"
|
|
||||||
/>
|
|
||||||
<video
|
|
||||||
ref="video"
|
|
||||||
v-if="videoUrl"
|
|
||||||
:src="videoUrl"
|
|
||||||
preload="none"
|
|
||||||
muted
|
|
||||||
playsinline
|
|
||||||
/>
|
|
||||||
<div class="overlay fill-block" />
|
<div class="overlay fill-block" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -193,7 +160,7 @@ export default defineComponent({
|
||||||
size =
|
size =
|
||||||
Math.floor(
|
Math.floor(
|
||||||
(base * Math.max(this.data.w, this.data.h)) /
|
(base * Math.max(this.data.w, this.data.h)) /
|
||||||
Math.min(this.data.w, this.data.h)
|
Math.min(this.data.w, this.data.h)
|
||||||
) - 1;
|
) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,12 +242,14 @@ export default defineComponent({
|
||||||
/* Container and selection */
|
/* Container and selection */
|
||||||
.p-outer {
|
.p-outer {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
transition: background-color 0.15s ease, opacity 0.2s ease-in,
|
transition: background-color 0.15s ease,
|
||||||
transform 0.2s ease-in;
|
opacity 0.2s ease-in,
|
||||||
|
transform 0.2s ease-in;
|
||||||
|
|
||||||
&.leaving {
|
&.leaving {
|
||||||
transform: scale(0.9);
|
transform: scale(0.9);
|
||||||
|
@ -293,6 +262,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
--icon-dist: 8px;
|
--icon-dist: 8px;
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
--icon-dist: 4px;
|
--icon-dist: 4px;
|
||||||
}
|
}
|
||||||
|
@ -312,13 +282,15 @@ $icon-size: $icon-half-size * 2;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
.p-outer:hover > & {
|
.p-outer:hover>& {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
.p-outer.selected & {
|
.p-outer.selected & {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
@ -331,13 +303,15 @@ $icon-size: $icon-half-size * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
filter: invert(1) brightness(100);
|
filter: invert(1) brightness(100);
|
||||||
.p-outer.selected > & {
|
|
||||||
|
.p-outer.selected>& {
|
||||||
display: flex;
|
display: flex;
|
||||||
filter: invert(0);
|
filter: invert(0);
|
||||||
background-color: white;
|
background-color: white;
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.video,
|
.video,
|
||||||
.star-icon,
|
.star-icon,
|
||||||
.livephoto {
|
.livephoto {
|
||||||
|
@ -347,12 +321,14 @@ $icon-size: $icon-half-size * 2;
|
||||||
transition: transform 0.15s ease;
|
transition: transform 0.15s ease;
|
||||||
filter: invert(1) brightness(100);
|
filter: invert(1) brightness(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
.video,
|
.video,
|
||||||
.livephoto {
|
.livephoto {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: var(--icon-dist);
|
top: var(--icon-dist);
|
||||||
right: var(--icon-dist);
|
right: var(--icon-dist);
|
||||||
.p-outer.selected > & {
|
|
||||||
|
.p-outer.selected>& {
|
||||||
transform: translate(-$icon-size, $icon-size);
|
transform: translate(-$icon-size, $icon-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,13 +342,16 @@ $icon-size: $icon-half-size * 2;
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.livephoto {
|
.livephoto {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star-icon {
|
.star-icon {
|
||||||
bottom: var(--icon-dist);
|
bottom: var(--icon-dist);
|
||||||
left: var(--icon-dist);
|
left: var(--icon-dist);
|
||||||
.p-outer.selected > & {
|
|
||||||
|
.p-outer.selected>& {
|
||||||
transform: translate($icon-size, -$icon-size);
|
transform: translate($icon-size, -$icon-size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,16 +363,17 @@ div.img-outer {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
transition: padding 0.15s ease;
|
transition: padding 0.15s ease;
|
||||||
.p-outer.selected > & {
|
|
||||||
|
.p-outer.selected>& {
|
||||||
padding: calc(var(--icon-dist) + $icon-half-size);
|
padding: calc(var(--icon-dist) + $icon-half-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-outer.placeholder > & {
|
.p-outer.placeholder>& {
|
||||||
background-color: var(--color-background-dark);
|
background-color: var(--color-background-dark);
|
||||||
background-clip: content-box, padding-box;
|
background-clip: content-box, padding-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
> img {
|
>img {
|
||||||
filter: contrast(1.05); // most real world images are a bit overexposed
|
filter: contrast(1.05); // most real world images are a bit overexposed
|
||||||
background-clip: content-box;
|
background-clip: content-box;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
@ -405,20 +385,21 @@ div.img-outer {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
transition: border-radius 0.1s ease-in, var(--livephoto-img-transition);
|
transition: border-radius 0.1s ease-in, var(--livephoto-img-transition);
|
||||||
|
|
||||||
.p-outer.placeholder > & {
|
.p-outer.placeholder>& {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-outer.error & {
|
.p-outer.error & {
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> video {
|
>video {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .overlay {
|
>.overlay {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -427,16 +408,17 @@ div.img-outer {
|
||||||
|
|
||||||
display: none;
|
display: none;
|
||||||
transition: border-radius 0.1s ease-in;
|
transition: border-radius 0.1s ease-in;
|
||||||
|
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
.p-outer:not(.selected):hover > & {
|
.p-outer:not(.selected):hover>& {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
>* {
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.selected > & {
|
.selected>& {
|
||||||
border-radius: $icon-size;
|
border-radius: $icon-size;
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<router-link
|
<router-link draggable="false" class="tag fill-block" :class="{ face, error }" :to="target"
|
||||||
draggable="false"
|
@click.native="openTag(data)">
|
||||||
class="tag fill-block"
|
|
||||||
:class="{ face, error }"
|
|
||||||
:to="target"
|
|
||||||
@click.native="openTag(data)"
|
|
||||||
>
|
|
||||||
<div class="bbl">
|
<div class="bbl">
|
||||||
<NcCounterBubble> {{ data.count }} </NcCounterBubble>
|
<NcCounterBubble> {{ data.count }} </NcCounterBubble>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,13 +11,8 @@
|
||||||
|
|
||||||
<div class="previews fill-block" ref="previews">
|
<div class="previews fill-block" ref="previews">
|
||||||
<div class="img-outer">
|
<div class="img-outer">
|
||||||
<img
|
<img draggable="false" class="fill-block" :class="{ error }" :src="previewUrl"
|
||||||
draggable="false"
|
@error="data.flag |= c.FLAG_LOAD_FAIL" />
|
||||||
class="fill-block"
|
|
||||||
:class="{ error }"
|
|
||||||
:src="previewUrl"
|
|
||||||
@error="data.flag |= c.FLAG_LOAD_FAIL"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
@ -163,19 +153,19 @@ img {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
|
|
||||||
> .subtitle {
|
>.subtitle {
|
||||||
font-size: 0.7em;
|
font-size: 0.7em;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag.face > & {
|
.tag.face>& {
|
||||||
top: unset;
|
top: unset;
|
||||||
bottom: 10%;
|
bottom: 10%;
|
||||||
transform: unset;
|
transform: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag.error > & {
|
.tag.error>& {
|
||||||
color: unset;
|
color: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +188,7 @@ img {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
> .img-outer {
|
>.img-outer {
|
||||||
background-color: var(--color-background-dark);
|
background-color: var(--color-background-dark);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -209,7 +199,7 @@ img {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
> img {
|
>img {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
filter: brightness(60%);
|
filter: brightness(60%);
|
||||||
|
@ -219,6 +209,7 @@ img {
|
||||||
&.error {
|
&.error {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag:hover & {
|
.tag:hover & {
|
||||||
filter: brightness(100%);
|
filter: brightness(100%);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
|
|
||||||
<div v-if="processing" class="info-pad">
|
<div v-if="processing" class="info-pad">
|
||||||
{{
|
{{
|
||||||
t("memories", "Processing … {n}/{m}", {
|
t("memories", "Processing … {n}/{m}", {
|
||||||
n: photosDone,
|
n: photosDone,
|
||||||
m: photos.length,
|
m: photos.length,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,36 +7,22 @@
|
||||||
<form class="manage-collaborators__form" @submit.prevent>
|
<form class="manage-collaborators__form" @submit.prevent>
|
||||||
<NcPopover ref="popover" :auto-size="true" :distance="0">
|
<NcPopover ref="popover" :auto-size="true" :distance="0">
|
||||||
<label slot="trigger" class="manage-collaborators__form__input">
|
<label slot="trigger" class="manage-collaborators__form__input">
|
||||||
<NcTextField
|
<NcTextField :value.sync="searchText" autocomplete="off" type="search" name="search"
|
||||||
:value.sync="searchText"
|
:aria-label="t('photos', 'Search for collaborators')" aria-autocomplete="list"
|
||||||
autocomplete="off"
|
|
||||||
type="search"
|
|
||||||
name="search"
|
|
||||||
:aria-label="t('photos', 'Search for collaborators')"
|
|
||||||
aria-autocomplete="list"
|
|
||||||
:aria-controls="`manage-collaborators__form__selection-${randomId} manage-collaborators__form__list-${randomId}`"
|
:aria-controls="`manage-collaborators__form__selection-${randomId} manage-collaborators__form__list-${randomId}`"
|
||||||
:placeholder="t('photos', 'Search people or groups')"
|
:placeholder="t('photos', 'Search people or groups')" @input="searchCollaborators">
|
||||||
@input="searchCollaborators"
|
|
||||||
>
|
|
||||||
<Magnify :size="16" />
|
<Magnify :size="16" />
|
||||||
</NcTextField>
|
</NcTextField>
|
||||||
<NcLoadingIcon v-if="loadingCollaborators" />
|
<NcLoadingIcon v-if="loadingCollaborators" />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<ul
|
<ul v-if="searchResults.length !== 0" :id="`manage-collaborators__form__list-${randomId}`"
|
||||||
v-if="searchResults.length !== 0"
|
class="manage-collaborators__form__list">
|
||||||
:id="`manage-collaborators__form__list-${randomId}`"
|
|
||||||
class="manage-collaborators__form__list"
|
|
||||||
>
|
|
||||||
<li v-for="collaboratorKey of searchResults" :key="collaboratorKey">
|
<li v-for="collaboratorKey of searchResults" :key="collaboratorKey">
|
||||||
<NcListItemIcon
|
<NcListItemIcon :id="availableCollaborators[collaboratorKey].id"
|
||||||
:id="availableCollaborators[collaboratorKey].id"
|
class="manage-collaborators__form__list__result" :title="availableCollaborators[collaboratorKey].id"
|
||||||
class="manage-collaborators__form__list__result"
|
:search="searchText" :user="availableCollaborators[collaboratorKey].id"
|
||||||
:title="availableCollaborators[collaboratorKey].id"
|
:display-name="availableCollaborators[collaboratorKey].label" :aria-label="
|
||||||
:search="searchText"
|
|
||||||
:user="availableCollaborators[collaboratorKey].id"
|
|
||||||
:display-name="availableCollaborators[collaboratorKey].label"
|
|
||||||
:aria-label="
|
|
||||||
t(
|
t(
|
||||||
'photos',
|
'photos',
|
||||||
'Add {collaboratorLabel} to the collaborators list',
|
'Add {collaboratorLabel} to the collaborators list',
|
||||||
|
@ -45,48 +31,32 @@
|
||||||
availableCollaborators[collaboratorKey].label,
|
availableCollaborators[collaboratorKey].label,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
"
|
" @click="selectEntity(collaboratorKey)" />
|
||||||
@click="selectEntity(collaboratorKey)"
|
|
||||||
/>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<NcEmptyContent
|
<NcEmptyContent v-else key="emptycontent" class="manage-collaborators__form__list--empty"
|
||||||
v-else
|
:title="t('photos', 'No collaborators available')">
|
||||||
key="emptycontent"
|
|
||||||
class="manage-collaborators__form__list--empty"
|
|
||||||
:title="t('photos', 'No collaborators available')"
|
|
||||||
>
|
|
||||||
<AccountGroup slot="icon" />
|
<AccountGroup slot="icon" />
|
||||||
</NcEmptyContent>
|
</NcEmptyContent>
|
||||||
</NcPopover>
|
</NcPopover>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<ul class="manage-collaborators__selection">
|
<ul class="manage-collaborators__selection">
|
||||||
<li
|
<li v-for="collaboratorKey of listableSelectedCollaboratorsKeys" :key="collaboratorKey"
|
||||||
v-for="collaboratorKey of listableSelectedCollaboratorsKeys"
|
class="manage-collaborators__selection__item">
|
||||||
:key="collaboratorKey"
|
<NcListItemIcon :id="availableCollaborators[collaboratorKey].id"
|
||||||
class="manage-collaborators__selection__item"
|
|
||||||
>
|
|
||||||
<NcListItemIcon
|
|
||||||
:id="availableCollaborators[collaboratorKey].id"
|
|
||||||
:display-name="availableCollaborators[collaboratorKey].label"
|
:display-name="availableCollaborators[collaboratorKey].label"
|
||||||
:title="availableCollaborators[collaboratorKey].id"
|
:title="availableCollaborators[collaboratorKey].id" :user="availableCollaborators[collaboratorKey].id">
|
||||||
:user="availableCollaborators[collaboratorKey].id"
|
<NcButton type="tertiary" :aria-label="
|
||||||
>
|
t(
|
||||||
<NcButton
|
'photos',
|
||||||
type="tertiary"
|
'Remove {collaboratorLabel} from the collaborators list',
|
||||||
:aria-label="
|
{
|
||||||
t(
|
collaboratorLabel:
|
||||||
'photos',
|
availableCollaborators[collaboratorKey].label,
|
||||||
'Remove {collaboratorLabel} from the collaborators list',
|
}
|
||||||
{
|
)
|
||||||
collaboratorLabel:
|
" @click="unselectEntity(collaboratorKey)">
|
||||||
availableCollaborators[collaboratorKey].label,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
"
|
|
||||||
@click="unselectEntity(collaboratorKey)"
|
|
||||||
>
|
|
||||||
<Close slot="icon" :size="20" />
|
<Close slot="icon" :size="20" />
|
||||||
</NcButton>
|
</NcButton>
|
||||||
</NcListItemIcon>
|
</NcListItemIcon>
|
||||||
|
@ -96,12 +66,8 @@
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<div v-if="allowPublicLink" class="actions__public-link">
|
<div v-if="allowPublicLink" class="actions__public-link">
|
||||||
<template v-if="isPublicLinkSelected">
|
<template v-if="isPublicLinkSelected">
|
||||||
<NcButton
|
<NcButton class="manage-collaborators__public-link-button" :aria-label="t('photos', 'Copy the public link')"
|
||||||
class="manage-collaborators__public-link-button"
|
:disabled="publicLink.id === ''" @click="copyPublicLink">
|
||||||
:aria-label="t('photos', 'Copy the public link')"
|
|
||||||
:disabled="publicLink.id === ''"
|
|
||||||
@click="copyPublicLink"
|
|
||||||
>
|
|
||||||
<template v-if="publicLinkCopied">
|
<template v-if="publicLinkCopied">
|
||||||
{{ t("photos", "Public link copied!") }}
|
{{ t("photos", "Public link copied!") }}
|
||||||
</template>
|
</template>
|
||||||
|
@ -113,21 +79,13 @@
|
||||||
<ContentCopy v-else />
|
<ContentCopy v-else />
|
||||||
</template>
|
</template>
|
||||||
</NcButton>
|
</NcButton>
|
||||||
<NcButton
|
<NcButton type="tertiary" :aria-label="t('photos', 'Delete the public link')" :disabled="publicLink.id === ''"
|
||||||
type="tertiary"
|
@click="deletePublicLink">
|
||||||
:aria-label="t('photos', 'Delete the public link')"
|
|
||||||
:disabled="publicLink.id === ''"
|
|
||||||
@click="deletePublicLink"
|
|
||||||
>
|
|
||||||
<NcLoadingIcon v-if="publicLink.id === ''" slot="icon" />
|
<NcLoadingIcon v-if="publicLink.id === ''" slot="icon" />
|
||||||
<Close v-else slot="icon" />
|
<Close v-else slot="icon" />
|
||||||
</NcButton>
|
</NcButton>
|
||||||
</template>
|
</template>
|
||||||
<NcButton
|
<NcButton v-else class="manage-collaborators__public-link-button" @click="createPublicLinkForAlbum">
|
||||||
v-else
|
|
||||||
class="manage-collaborators__public-link-button"
|
|
||||||
@click="createPublicLinkForAlbum"
|
|
||||||
>
|
|
||||||
<Earth slot="icon" />
|
<Earth slot="icon" />
|
||||||
{{ t("photos", "Share via public link") }}
|
{{ t("photos", "Share via public link") }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
|
@ -207,7 +165,7 @@ export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
searchText: "",
|
searchText: "",
|
||||||
availableCollaborators: {} as { [key: string]: Collaborator },
|
availableCollaborators: {} as { [key: string]: Collaborator; },
|
||||||
selectedCollaboratorsKeys: [] as string[],
|
selectedCollaboratorsKeys: [] as string[],
|
||||||
currentSearchResults: [] as Collaborator[],
|
currentSearchResults: [] as Collaborator[],
|
||||||
loadingAlbum: false,
|
loadingAlbum: false,
|
||||||
|
@ -353,14 +311,13 @@ export default defineComponent({
|
||||||
* @param {Collaborator} collaborator - A collaborator
|
* @param {Collaborator} collaborator - A collaborator
|
||||||
*/
|
*/
|
||||||
indexCollaborators(
|
indexCollaborators(
|
||||||
collaborators: { [s: string]: Collaborator },
|
collaborators: { [s: string]: Collaborator; },
|
||||||
collaborator: Collaborator
|
collaborator: Collaborator
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
...collaborators,
|
...collaborators,
|
||||||
[`${collaborator.type}${
|
[`${collaborator.type}${collaborator.type === Type.SHARE_TYPE_LINK ? "" : ":"
|
||||||
collaborator.type === Type.SHARE_TYPE_LINK ? "" : ":"
|
}${collaborator.type === Type.SHARE_TYPE_LINK ? "" : collaborator.id}`]:
|
||||||
}${collaborator.type === Type.SHARE_TYPE_LINK ? "" : collaborator.id}`]:
|
|
||||||
collaborator,
|
collaborator,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,12 +10,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="outer">
|
<div class="outer">
|
||||||
<AlbumForm
|
<AlbumForm :album="album" :display-back-button="false" :title="t('photos', 'New album')" @done="done" />
|
||||||
:album="album"
|
|
||||||
:display-back-button="false"
|
|
||||||
:title="t('photos', 'New album')"
|
|
||||||
@done="done"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
@ -52,8 +47,8 @@ export default defineComponent({
|
||||||
if (edit) {
|
if (edit) {
|
||||||
try {
|
try {
|
||||||
this.album = await dav.getAlbum(
|
this.album = await dav.getAlbum(
|
||||||
this.$route.params.user,
|
<string>this.$route.params.user,
|
||||||
this.$route.params.name
|
<string>this.$route.params.name
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
{{
|
{{
|
||||||
t(
|
t(
|
||||||
"memories",
|
"memories",
|
||||||
'Are you sure you want to permanently remove album "{name}"?',
|
'Are you sure you want to permanently remove album "{name}"?',
|
||||||
{ name }
|
{ name }
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
@ -78,8 +78,8 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshParams() {
|
refreshParams() {
|
||||||
this.user = this.$route.params.user || "";
|
this.user = <string>this.$route.params.user || "";
|
||||||
this.name = this.$route.params.name || "";
|
this.name = <string>this.$route.params.name || "";
|
||||||
},
|
},
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
|
|
|
@ -1,58 +1,29 @@
|
||||||
<template>
|
<template>
|
||||||
<form
|
<form v-if="!showCollaboratorView" class="album-form" @submit.prevent="submit">
|
||||||
v-if="!showCollaboratorView"
|
|
||||||
class="album-form"
|
|
||||||
@submit.prevent="submit"
|
|
||||||
>
|
|
||||||
<div class="form-inputs">
|
<div class="form-inputs">
|
||||||
<NcTextField
|
<NcTextField ref="nameInput" :value.sync="albumName" type="text" name="name" :required="true" autofocus="true"
|
||||||
ref="nameInput"
|
:placeholder="t('photos', 'Name of the album')" />
|
||||||
:value.sync="albumName"
|
|
||||||
type="text"
|
|
||||||
name="name"
|
|
||||||
:required="true"
|
|
||||||
autofocus="true"
|
|
||||||
:placeholder="t('photos', 'Name of the album')"
|
|
||||||
/>
|
|
||||||
<label>
|
<label>
|
||||||
<NcTextField
|
<NcTextField :value.sync="albumLocation" name="location" type="text"
|
||||||
:value.sync="albumLocation"
|
:placeholder="t('photos', 'Location of the album')" />
|
||||||
name="location"
|
|
||||||
type="text"
|
|
||||||
:placeholder="t('photos', 'Location of the album')"
|
|
||||||
/>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-buttons">
|
<div class="form-buttons">
|
||||||
<span class="left-buttons">
|
<span class="left-buttons">
|
||||||
<NcButton
|
<NcButton v-if="displayBackButton" :aria-label="t('photos', 'Go back to the previous view.')" type="tertiary"
|
||||||
v-if="displayBackButton"
|
@click="back">
|
||||||
:aria-label="t('photos', 'Go back to the previous view.')"
|
|
||||||
type="tertiary"
|
|
||||||
@click="back"
|
|
||||||
>
|
|
||||||
{{ t("photos", "Back") }}
|
{{ t("photos", "Back") }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
</span>
|
</span>
|
||||||
<span class="right-buttons">
|
<span class="right-buttons">
|
||||||
<NcButton
|
<NcButton v-if="sharingEnabled && !editMode" :aria-label="t('photos', 'Go to the add collaborators view.')"
|
||||||
v-if="sharingEnabled && !editMode"
|
type="secondary" :disabled="albumName.trim() === '' || loading" @click="showCollaboratorView = true">
|
||||||
:aria-label="t('photos', 'Go to the add collaborators view.')"
|
|
||||||
type="secondary"
|
|
||||||
:disabled="albumName.trim() === '' || loading"
|
|
||||||
@click="showCollaboratorView = true"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<AccountMultiplePlus />
|
<AccountMultiplePlus />
|
||||||
</template>
|
</template>
|
||||||
{{ t("photos", "Add collaborators") }}
|
{{ t("photos", "Add collaborators") }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
<NcButton
|
<NcButton :aria-label="saveText" type="primary" :disabled="albumName === '' || loading" @click="submit()">
|
||||||
:aria-label="saveText"
|
|
||||||
type="primary"
|
|
||||||
:disabled="albumName === '' || loading"
|
|
||||||
@click="submit()"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<NcLoadingIcon v-if="loading" />
|
<NcLoadingIcon v-if="loading" />
|
||||||
<Send v-else />
|
<Send v-else />
|
||||||
|
@ -62,29 +33,17 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<AlbumCollaborators
|
<AlbumCollaborators v-else :album-name="albumName" :allow-public-link="false" :collaborators="[]">
|
||||||
v-else
|
|
||||||
:album-name="albumName"
|
|
||||||
:allow-public-link="false"
|
|
||||||
:collaborators="[]"
|
|
||||||
>
|
|
||||||
<template slot-scope="{ collaborators }">
|
<template slot-scope="{ collaborators }">
|
||||||
<span class="left-buttons">
|
<span class="left-buttons">
|
||||||
<NcButton
|
<NcButton :aria-label="t('photos', 'Back to the new album form.')" type="tertiary"
|
||||||
:aria-label="t('photos', 'Back to the new album form.')"
|
@click="showCollaboratorView = false">
|
||||||
type="tertiary"
|
|
||||||
@click="showCollaboratorView = false"
|
|
||||||
>
|
|
||||||
{{ t("photos", "Back") }}
|
{{ t("photos", "Back") }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
</span>
|
</span>
|
||||||
<span class="right-buttons">
|
<span class="right-buttons">
|
||||||
<NcButton
|
<NcButton :aria-label="saveText" type="primary" :disabled="albumName.trim() === '' || loading"
|
||||||
:aria-label="saveText"
|
@click="submit(collaborators)">
|
||||||
type="primary"
|
|
||||||
:disabled="albumName.trim() === '' || loading"
|
|
||||||
@click="submit(collaborators)"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<NcLoadingIcon v-if="loading" />
|
<NcLoadingIcon v-if="loading" />
|
||||||
<Send v-else />
|
<Send v-else />
|
||||||
|
@ -136,6 +95,7 @@ export default defineComponent({
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
collaborators: [],
|
||||||
showCollaboratorView: false,
|
showCollaboratorView: false,
|
||||||
albumName: "",
|
albumName: "",
|
||||||
albumLocation: "",
|
albumLocation: "",
|
||||||
|
@ -176,7 +136,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
submit(collaborators = []) {
|
submit(collaborators: any = []) {
|
||||||
if (this.albumName === "" || this.loading) {
|
if (this.albumName === "" || this.loading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -251,41 +211,52 @@ export default defineComponent({
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 350px;
|
height: 350px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
|
||||||
.form-title {
|
.form-title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-subtitle {
|
.form-subtitle {
|
||||||
color: var(--color-text-lighter);
|
color: var(--color-text-lighter);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-inputs {
|
.form-inputs {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
justify-items: flex-end;
|
justify-items: flex-end;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
|
||||||
:deep svg {
|
:deep svg {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-buttons {
|
.form-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
.left-buttons,
|
.left-buttons,
|
||||||
.right-buttons {
|
.right-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-buttons {
|
.right-buttons {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-buttons {
|
.left-buttons {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,13 @@
|
||||||
<NcLoadingIcon v-if="loadingAlbums" class="loading-icon" />
|
<NcLoadingIcon v-if="loadingAlbums" class="loading-icon" />
|
||||||
|
|
||||||
<ul class="albums-container">
|
<ul class="albums-container">
|
||||||
<NcListItem
|
<NcListItem v-for="album in albums" :key="album.album_id" class="album" :title="getAlbumName(album)" :aria-label="
|
||||||
v-for="album in albums"
|
t('photos', 'Add selection to album {albumName}', {
|
||||||
:key="album.album_id"
|
albumName: getAlbumName(album),
|
||||||
class="album"
|
})
|
||||||
:title="getAlbumName(album)"
|
" @click="pickAlbum(album)">
|
||||||
:aria-label="
|
|
||||||
t('photos', 'Add selection to album {albumName}', {
|
|
||||||
albumName: getAlbumName(album),
|
|
||||||
})
|
|
||||||
"
|
|
||||||
@click="pickAlbum(album)"
|
|
||||||
>
|
|
||||||
<template slot="icon">
|
<template slot="icon">
|
||||||
<img
|
<img v-if="album.last_added_photo !== -1" class="album__image" :src="toCoverUrl(album.last_added_photo)" />
|
||||||
v-if="album.last_added_photo !== -1"
|
|
||||||
class="album__image"
|
|
||||||
:src="album.last_added_photo | toCoverUrl"
|
|
||||||
/>
|
|
||||||
<div v-else class="album__image album__image--placeholder">
|
<div v-else class="album__image album__image--placeholder">
|
||||||
<ImageMultiple :size="32" />
|
<ImageMultiple :size="32" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,12 +23,8 @@
|
||||||
</NcListItem>
|
</NcListItem>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<NcButton
|
<NcButton :aria-label="t('photos', 'Create a new album.')" class="new-album-button" type="tertiary"
|
||||||
:aria-label="t('photos', 'Create a new album.')"
|
@click="showAlbumCreationForm = true">
|
||||||
class="new-album-button"
|
|
||||||
type="tertiary"
|
|
||||||
@click="showAlbumCreationForm = true"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<Plus />
|
<Plus />
|
||||||
</template>
|
</template>
|
||||||
|
@ -47,13 +32,8 @@
|
||||||
</NcButton>
|
</NcButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AlbumForm
|
<AlbumForm v-else :display-back-button="true" :title="t('photos', 'New album')" @back="showAlbumCreationForm = false"
|
||||||
v-else
|
@done="albumCreatedHandler" />
|
||||||
:display-back-button="true"
|
|
||||||
:title="t('photos', 'New album')"
|
|
||||||
@back="showAlbumCreationForm = false"
|
|
||||||
@done="albumCreatedHandler"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -85,18 +65,6 @@ export default defineComponent({
|
||||||
NcLoadingIcon,
|
NcLoadingIcon,
|
||||||
},
|
},
|
||||||
|
|
||||||
filters: {
|
|
||||||
toCoverUrl(fileId: string) {
|
|
||||||
return getPreviewUrl(
|
|
||||||
{
|
|
||||||
fileid: Number(fileId),
|
|
||||||
} as IPhoto,
|
|
||||||
true,
|
|
||||||
256
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showAlbumCreationForm: false,
|
showAlbumCreationForm: false,
|
||||||
|
@ -110,6 +78,16 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
toCoverUrl(fileId: number | string) {
|
||||||
|
return getPreviewUrl(
|
||||||
|
{
|
||||||
|
fileid: Number(fileId),
|
||||||
|
} as IPhoto,
|
||||||
|
true,
|
||||||
|
256
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
albumCreatedHandler() {
|
albumCreatedHandler() {
|
||||||
this.showAlbumCreationForm = false;
|
this.showAlbumCreationForm = false;
|
||||||
this.loadAlbums();
|
this.loadAlbums();
|
||||||
|
|
|
@ -4,19 +4,11 @@
|
||||||
{{ t("memories", "Share Album") }}
|
{{ t("memories", "Share Album") }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<AlbumCollaborators
|
<AlbumCollaborators v-if="album" :album-name="album.basename" :collaborators="album.collaborators"
|
||||||
v-if="album"
|
:public-link="album.publicLink">
|
||||||
:album-name="album.basename"
|
|
||||||
:collaborators="album.collaborators"
|
|
||||||
:public-link="album.publicLink"
|
|
||||||
>
|
|
||||||
<template slot-scope="{ collaborators }">
|
<template slot-scope="{ collaborators }">
|
||||||
<NcButton
|
<NcButton :aria-label="t('photos', 'Save collaborators for this album.')" type="primary"
|
||||||
:aria-label="t('photos', 'Save collaborators for this album.')"
|
:disabled="loadingAddCollaborators" @click="handleSetCollaborators(collaborators)">
|
||||||
type="primary"
|
|
||||||
:disabled="loadingAddCollaborators"
|
|
||||||
@click="handleSetCollaborators(collaborators)"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<NcLoadingIcon v-if="loadingAddCollaborators" />
|
<NcLoadingIcon v-if="loadingAddCollaborators" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -52,6 +44,7 @@ export default defineComponent({
|
||||||
album: null as any,
|
album: null as any,
|
||||||
show: false,
|
show: false,
|
||||||
loadingAddCollaborators: false,
|
loadingAddCollaborators: false,
|
||||||
|
collaborators: [] as any[],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -65,8 +58,8 @@ export default defineComponent({
|
||||||
async open() {
|
async open() {
|
||||||
this.show = true;
|
this.show = true;
|
||||||
this.loadingAddCollaborators = true;
|
this.loadingAddCollaborators = true;
|
||||||
const user = this.$route.params.user || "";
|
const user = <string>this.$route.params.user || "";
|
||||||
const name = this.$route.params.name || "";
|
const name = <string>this.$route.params.name || "";
|
||||||
this.album = await dav.getAlbum(user, name);
|
this.album = await dav.getAlbum(user, name);
|
||||||
this.loadingAddCollaborators = false;
|
this.loadingAddCollaborators = false;
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,45 +15,16 @@
|
||||||
{{ longDateStr }}
|
{{ longDateStr }}
|
||||||
|
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<NcTextField
|
<NcTextField :value.sync="year" class="field" @input="newestChange()" :label="t('memories', 'Year')"
|
||||||
:value.sync="year"
|
:label-visible="true" :placeholder="t('memories', 'Year')" />
|
||||||
class="field"
|
<NcTextField :value.sync="month" class="field" @input="newestChange()" :label="t('memories', 'Month')"
|
||||||
@input="newestChange()"
|
:label-visible="true" :placeholder="t('memories', 'Month')" />
|
||||||
:label="t('memories', 'Year')"
|
<NcTextField :value.sync="day" class="field" @input="newestChange()" :label="t('memories', 'Day')"
|
||||||
:label-visible="true"
|
:label-visible="true" :placeholder="t('memories', 'Day')" />
|
||||||
:placeholder="t('memories', 'Year')"
|
<NcTextField :value.sync="hour" class="field" @input="newestChange(true)" :label="t('memories', 'Time')"
|
||||||
/>
|
:label-visible="true" :placeholder="t('memories', 'Hour')" />
|
||||||
<NcTextField
|
<NcTextField :value.sync="minute" class="field" @input="newestChange(true)" :label="t('memories', 'Minute')"
|
||||||
:value.sync="month"
|
:placeholder="t('memories', 'Minute')" />
|
||||||
class="field"
|
|
||||||
@input="newestChange()"
|
|
||||||
:label="t('memories', 'Month')"
|
|
||||||
:label-visible="true"
|
|
||||||
:placeholder="t('memories', 'Month')"
|
|
||||||
/>
|
|
||||||
<NcTextField
|
|
||||||
:value.sync="day"
|
|
||||||
class="field"
|
|
||||||
@input="newestChange()"
|
|
||||||
:label="t('memories', 'Day')"
|
|
||||||
:label-visible="true"
|
|
||||||
:placeholder="t('memories', 'Day')"
|
|
||||||
/>
|
|
||||||
<NcTextField
|
|
||||||
:value.sync="hour"
|
|
||||||
class="field"
|
|
||||||
@input="newestChange(true)"
|
|
||||||
:label="t('memories', 'Time')"
|
|
||||||
:label-visible="true"
|
|
||||||
:placeholder="t('memories', 'Hour')"
|
|
||||||
/>
|
|
||||||
<NcTextField
|
|
||||||
:value.sync="minute"
|
|
||||||
class="field"
|
|
||||||
@input="newestChange(true)"
|
|
||||||
:label="t('memories', 'Minute')"
|
|
||||||
:placeholder="t('memories', 'Minute')"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="photos.length > 1" class="oldest">
|
<div v-if="photos.length > 1" class="oldest">
|
||||||
|
@ -61,59 +32,35 @@
|
||||||
{{ longDateStrLast }}
|
{{ longDateStrLast }}
|
||||||
|
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<NcTextField
|
<NcTextField :value.sync="yearLast" class="field" :label="t('memories', 'Year')" :label-visible="true"
|
||||||
:value.sync="yearLast"
|
:placeholder="t('memories', 'Year')" />
|
||||||
class="field"
|
<NcTextField :value.sync="monthLast" class="field" :label="t('memories', 'Month')" :label-visible="true"
|
||||||
:label="t('memories', 'Year')"
|
:placeholder="t('memories', 'Month')" />
|
||||||
:label-visible="true"
|
<NcTextField :value.sync="dayLast" class="field" :label="t('memories', 'Day')" :label-visible="true"
|
||||||
:placeholder="t('memories', 'Year')"
|
:placeholder="t('memories', 'Day')" />
|
||||||
/>
|
<NcTextField :value.sync="hourLast" class="field" :label="t('memories', 'Time')" :label-visible="true"
|
||||||
<NcTextField
|
:placeholder="t('memories', 'Hour')" />
|
||||||
:value.sync="monthLast"
|
<NcTextField :value.sync="minuteLast" class="field" :label="t('memories', 'Minute')"
|
||||||
class="field"
|
:placeholder="t('memories', 'Minute')" />
|
||||||
:label="t('memories', 'Month')"
|
|
||||||
:label-visible="true"
|
|
||||||
:placeholder="t('memories', 'Month')"
|
|
||||||
/>
|
|
||||||
<NcTextField
|
|
||||||
:value.sync="dayLast"
|
|
||||||
class="field"
|
|
||||||
:label="t('memories', 'Day')"
|
|
||||||
:label-visible="true"
|
|
||||||
:placeholder="t('memories', 'Day')"
|
|
||||||
/>
|
|
||||||
<NcTextField
|
|
||||||
:value.sync="hourLast"
|
|
||||||
class="field"
|
|
||||||
:label="t('memories', 'Time')"
|
|
||||||
:label-visible="true"
|
|
||||||
:placeholder="t('memories', 'Hour')"
|
|
||||||
/>
|
|
||||||
<NcTextField
|
|
||||||
:value.sync="minuteLast"
|
|
||||||
class="field"
|
|
||||||
:label="t('memories', 'Minute')"
|
|
||||||
:placeholder="t('memories', 'Minute')"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="processing" class="info-pad">
|
<div v-if="processing" class="info-pad">
|
||||||
{{
|
{{
|
||||||
t("memories", "Processing … {n}/{m}", {
|
t("memories", "Processing … {n}/{m}", {
|
||||||
n: photosDone,
|
n: photosDone,
|
||||||
m: photos.length,
|
m: photos.length,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
{{
|
{{
|
||||||
t("memories", "Loading data … {n}/{m}", {
|
t("memories", "Loading data … {n}/{m}", {
|
||||||
n: photosDone,
|
n: photosDone,
|
||||||
m: photos.length,
|
m: photos.length,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -254,7 +201,7 @@ export default defineComponent({
|
||||||
this.minuteLast = dateLastNew.getUTCMinutes().toString();
|
this.minuteLast = dateLastNew.getUTCMinutes().toString();
|
||||||
this.secondLast = dateLastNew.getUTCSeconds().toString();
|
this.secondLast = dateLastNew.getUTCSeconds().toString();
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) { }
|
||||||
},
|
},
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
|
|
|
@ -5,28 +5,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #buttons>
|
<template #buttons>
|
||||||
<NcButton
|
<NcButton @click="save" class="button" type="error" v-if="exif" :disabled="processing">
|
||||||
@click="save"
|
|
||||||
class="button"
|
|
||||||
type="error"
|
|
||||||
v-if="exif"
|
|
||||||
:disabled="processing"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Update Exif") }}
|
{{ t("memories", "Update Exif") }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-if="exif">
|
<div v-if="exif">
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<NcTextField
|
<NcTextField v-for="field of fields" :key="field.field" :value.sync="exif[field.field]" class="field"
|
||||||
v-for="field of fields"
|
:label="field.label" :label-visible="true" :placeholder="field.label" />
|
||||||
:key="field.field"
|
|
||||||
:value.sync="exif[field.field]"
|
|
||||||
class="field"
|
|
||||||
:label="field.label"
|
|
||||||
:label-visible="true"
|
|
||||||
:placeholder="field.label"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -168,6 +155,7 @@ export default defineComponent({
|
||||||
.field {
|
.field {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep label {
|
:deep label {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<span>{{
|
<span>{{
|
||||||
t("memories", "Are you sure you want to remove {name}?", { name })
|
t("memories", "Are you sure you want to remove {name}?", { name })
|
||||||
}}</span>
|
}}</span>
|
||||||
|
|
||||||
<template #buttons>
|
<template #buttons>
|
||||||
|
@ -74,8 +74,8 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshParams() {
|
refreshParams() {
|
||||||
this.user = this.$route.params.user || "";
|
this.user = <string>this.$route.params.user || "";
|
||||||
this.name = this.$route.params.name || "";
|
this.name = <string>this.$route.params.name || "";
|
||||||
},
|
},
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
|
|
|
@ -5,14 +5,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<NcTextField
|
<NcTextField :value.sync="name" class="field" :label="t('memories', 'Name')" :label-visible="false"
|
||||||
:value.sync="name"
|
:placeholder="t('memories', 'Name')" @keypress.enter="save()" />
|
||||||
class="field"
|
|
||||||
:label="t('memories', 'Name')"
|
|
||||||
:label-visible="false"
|
|
||||||
:placeholder="t('memories', 'Name')"
|
|
||||||
@keypress.enter="save()"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #buttons>
|
<template #buttons>
|
||||||
|
@ -82,9 +76,9 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshParams() {
|
refreshParams() {
|
||||||
this.user = this.$route.params.user || "";
|
this.user = <string>this.$route.params.user || "";
|
||||||
this.name = this.$route.params.name || "";
|
this.name = <string>this.$route.params.name || "";
|
||||||
this.oldName = this.$route.params.name || "";
|
this.oldName = <string>this.$route.params.name || "";
|
||||||
},
|
},
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
|
|
|
@ -26,7 +26,7 @@ export default defineComponent({
|
||||||
return {
|
return {
|
||||||
user: "",
|
user: "",
|
||||||
name: "",
|
name: "",
|
||||||
detail: null as IPhoto[] | null,
|
detail: null as ITag[] | null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -46,8 +46,8 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
async refreshParams() {
|
async refreshParams() {
|
||||||
this.user = this.$route.params.user || "";
|
this.user = <string>this.$route.params.user || "";
|
||||||
this.name = this.$route.params.name || "";
|
this.name = <string>this.$route.params.name || "";
|
||||||
this.detail = null;
|
this.detail = null;
|
||||||
|
|
||||||
let data = [];
|
let data = [];
|
||||||
|
@ -85,6 +85,7 @@ export default defineComponent({
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.photo {
|
.photo {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<Modal @close="close" size="large" v-if="show">
|
<Modal @close="close" size="large" v-if="show">
|
||||||
<template #title>
|
<template #title>
|
||||||
{{
|
{{
|
||||||
t("memories", "Merge {name} with person", { name: $route.params.name })
|
t("memories", "Merge {name} with person", { name: $route.params.name })
|
||||||
}}
|
}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -11,10 +11,10 @@
|
||||||
|
|
||||||
<div v-if="procesingTotal > 0" class="info-pad">
|
<div v-if="procesingTotal > 0" class="info-pad">
|
||||||
{{
|
{{
|
||||||
t("memories", "Processing … {n}/{m}", {
|
t("memories", "Processing … {n}/{m}", {
|
||||||
n: processing,
|
n: processing,
|
||||||
m: procesingTotal,
|
m: procesingTotal,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -157,6 +157,7 @@ export default defineComponent({
|
||||||
.outer {
|
.outer {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-pad {
|
.info-pad {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<Modal
|
<Modal @close="close" size="normal" v-if="show" :sidebar="!isRoot ? folderPath : null">
|
||||||
@close="close"
|
|
||||||
size="normal"
|
|
||||||
v-if="show"
|
|
||||||
:sidebar="!isRoot ? this.folderPath : null"
|
|
||||||
>
|
|
||||||
<template #title>
|
<template #title>
|
||||||
{{ t("memories", "Share Folder") }}
|
{{ t("memories", "Share Folder") }}
|
||||||
</template>
|
</template>
|
||||||
|
@ -15,21 +10,15 @@
|
||||||
<div v-else>
|
<div v-else>
|
||||||
{{ t("memories", "Use the sidebar to share this folder.") }} <br />
|
{{ t("memories", "Use the sidebar to share this folder.") }} <br />
|
||||||
{{
|
{{
|
||||||
t(
|
t(
|
||||||
"memories",
|
"memories",
|
||||||
"If you create a public link share, click on refresh and a corresponding link to Memories will be shown below."
|
"If you create a public link share, click on refresh and a corresponding link to Memories will be shown below."
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<a
|
<a v-for="link of links" :key="link.url" :href="link.url" target="_blank" rel="noopener noreferrer">
|
||||||
v-for="link of links"
|
|
||||||
:key="link.url"
|
|
||||||
:href="link.url"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{{ link.url }}
|
{{ link.url }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -64,12 +53,12 @@ export default defineComponent({
|
||||||
return {
|
return {
|
||||||
show: false,
|
show: false,
|
||||||
folderPath: "",
|
folderPath: "",
|
||||||
links: [] as { url: string }[],
|
links: [] as { url: string; }[],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isRoot() {
|
isRoot(): boolean {
|
||||||
return this.folderPath === "/" || this.folderPath === "";
|
return this.folderPath === "/" || this.folderPath === "";
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -111,6 +100,7 @@ export default defineComponent({
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.links {
|
.links {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 0.2em;
|
margin-bottom: 0.2em;
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<NcModal
|
<NcModal :size="size" @close="close" :outTransition="true"
|
||||||
:size="size"
|
:style="{ width: isSidebarShown ? `calc(100% - ${sidebarWidth}px)` : null }" :additionalTrapElements="trapElements">
|
||||||
@close="close"
|
|
||||||
:outTransition="true"
|
|
||||||
:style="{ width: isSidebarShown ? `calc(100% - ${sidebarWidth}px)` : null }"
|
|
||||||
:additionalTrapElements="trapElements"
|
|
||||||
>
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<span> <slot name="title"></slot> </span>
|
<span>
|
||||||
|
<slot name="title"></slot>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
@ -125,7 +122,7 @@ export default defineComponent({
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
|
||||||
> button {
|
>button {
|
||||||
display: inline-block !important;
|
display: inline-block !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,11 @@
|
||||||
{{ path }}
|
{{ path }}
|
||||||
|
|
||||||
<NcActions :inline="1">
|
<NcActions :inline="1">
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Remove')" @click="remove(index)">
|
||||||
:aria-label="t('memories', 'Remove')"
|
|
||||||
@click="remove(index)"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Remove") }}
|
{{ t("memories", "Remove") }}
|
||||||
<template #icon> <CloseIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<CloseIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
<NcActions v-if="!isAlbumList">
|
<NcActions v-if="!isAlbumList">
|
||||||
<NcActionButton :aria-label="t('memories', 'Back')" @click="back()">
|
<NcActionButton :aria-label="t('memories', 'Back')" @click="back()">
|
||||||
{{ t("memories", "Back") }}
|
{{ t("memories", "Back") }}
|
||||||
<template #icon> <BackIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<BackIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
|
|
||||||
|
@ -11,50 +13,40 @@
|
||||||
|
|
||||||
<div class="right-actions">
|
<div class="right-actions">
|
||||||
<NcActions :inline="1">
|
<NcActions :inline="1">
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Create new album')" @click="$refs.createModal.open(false)"
|
||||||
:aria-label="t('memories', 'Create new album')"
|
close-after-click v-if="isAlbumList">
|
||||||
@click="$refs.createModal.open(false)"
|
|
||||||
close-after-click
|
|
||||||
v-if="isAlbumList"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Create new album") }}
|
{{ t("memories", "Create new album") }}
|
||||||
<template #icon> <PlusIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<PlusIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Share album')" @click="$refs.shareModal.open(false)"
|
||||||
:aria-label="t('memories', 'Share album')"
|
close-after-click v-if="canEditAlbum">
|
||||||
@click="$refs.shareModal.open(false)"
|
|
||||||
close-after-click
|
|
||||||
v-if="canEditAlbum"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Share album") }}
|
{{ t("memories", "Share album") }}
|
||||||
<template #icon> <ShareIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<ShareIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Download album')" @click="downloadAlbum()" close-after-click
|
||||||
:aria-label="t('memories', 'Download album')"
|
v-if="!isAlbumList">
|
||||||
@click="downloadAlbum()"
|
|
||||||
close-after-click
|
|
||||||
v-if="!isAlbumList"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Download album") }}
|
{{ t("memories", "Download album") }}
|
||||||
<template #icon> <DownloadIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<DownloadIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Edit album details')" @click="$refs.createModal.open(true)"
|
||||||
:aria-label="t('memories', 'Edit album details')"
|
close-after-click v-if="canEditAlbum">
|
||||||
@click="$refs.createModal.open(true)"
|
|
||||||
close-after-click
|
|
||||||
v-if="canEditAlbum"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Edit album details") }}
|
{{ t("memories", "Edit album details") }}
|
||||||
<template #icon> <EditIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<EditIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Delete album')" @click="$refs.deleteModal.open()" close-after-click
|
||||||
:aria-label="t('memories', 'Delete album')"
|
v-if="canEditAlbum">
|
||||||
@click="$refs.deleteModal.open()"
|
|
||||||
close-after-click
|
|
||||||
v-if="canEditAlbum"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Delete album") }}
|
{{ t("memories", "Delete album") }}
|
||||||
<template #icon> <DeleteIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<DeleteIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
</div>
|
</div>
|
||||||
|
@ -114,11 +106,11 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isAlbumList() {
|
isAlbumList(): boolean {
|
||||||
return !Boolean(this.$route.params.name);
|
return !Boolean(this.$route.params.name);
|
||||||
},
|
},
|
||||||
|
|
||||||
canEditAlbum() {
|
canEditAlbum(): boolean {
|
||||||
return (
|
return (
|
||||||
!this.isAlbumList && this.$route.params.user === getCurrentUser()?.uid
|
!this.isAlbumList && this.$route.params.user === getCurrentUser()?.uid
|
||||||
);
|
);
|
||||||
|
@ -137,7 +129,7 @@ export default defineComponent({
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
createMatter() {
|
createMatter() {
|
||||||
this.name = this.$route.params.name || this.t("memories", "Albums");
|
this.name = <string>this.$route.params.name || this.t("memories", "Albums");
|
||||||
},
|
},
|
||||||
|
|
||||||
back() {
|
back() {
|
||||||
|
@ -146,7 +138,7 @@ export default defineComponent({
|
||||||
|
|
||||||
async downloadAlbum() {
|
async downloadAlbum() {
|
||||||
const res = await axios.post(
|
const res = await axios.post(
|
||||||
API.ALBUM_DOWNLOAD(this.$route.params.user, this.$route.params.name)
|
API.ALBUM_DOWNLOAD(<string>this.$route.params.user, <string>this.$route.params.name)
|
||||||
);
|
);
|
||||||
if (res.status === 200 && res.data.handle) {
|
if (res.status === 200 && res.data.handle) {
|
||||||
downloadWithHandle(res.data.handle);
|
downloadWithHandle(res.data.handle);
|
||||||
|
@ -172,6 +164,7 @@ export default defineComponent({
|
||||||
.right-actions {
|
.right-actions {
|
||||||
margin-right: 40px;
|
margin-right: 40px;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
<NcActions>
|
<NcActions>
|
||||||
<NcActionButton :aria-label="t('memories', 'Back')" @click="back()">
|
<NcActionButton :aria-label="t('memories', 'Back')" @click="back()">
|
||||||
{{ t("memories", "Back") }}
|
{{ t("memories", "Back") }}
|
||||||
<template #icon> <BackIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<BackIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
|
|
||||||
|
@ -11,36 +13,29 @@
|
||||||
|
|
||||||
<div class="right-actions">
|
<div class="right-actions">
|
||||||
<NcActions :inline="1">
|
<NcActions :inline="1">
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Rename person')" @click="$refs.editModal.open()" close-after-click>
|
||||||
:aria-label="t('memories', 'Rename person')"
|
|
||||||
@click="$refs.editModal.open()"
|
|
||||||
close-after-click
|
|
||||||
>
|
|
||||||
{{ t("memories", "Rename person") }}
|
{{ t("memories", "Rename person") }}
|
||||||
<template #icon> <EditIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<EditIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Merge with different person')" @click="$refs.mergeModal.open()"
|
||||||
:aria-label="t('memories', 'Merge with different person')"
|
close-after-click>
|
||||||
@click="$refs.mergeModal.open()"
|
|
||||||
close-after-click
|
|
||||||
>
|
|
||||||
{{ t("memories", "Merge with different person") }}
|
{{ t("memories", "Merge with different person") }}
|
||||||
<template #icon> <MergeIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<MergeIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionCheckbox
|
<NcActionCheckbox :aria-label="t('memories', 'Mark person in preview')" :checked.sync="config_showFaceRect"
|
||||||
:aria-label="t('memories', 'Mark person in preview')"
|
@change="changeShowFaceRect">
|
||||||
:checked.sync="config_showFaceRect"
|
|
||||||
@change="changeShowFaceRect"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Mark person in preview") }}
|
{{ t("memories", "Mark person in preview") }}
|
||||||
</NcActionCheckbox>
|
</NcActionCheckbox>
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Remove person')" @click="$refs.deleteModal.open()"
|
||||||
:aria-label="t('memories', 'Remove person')"
|
close-after-click>
|
||||||
@click="$refs.deleteModal.open()"
|
|
||||||
close-after-click
|
|
||||||
>
|
|
||||||
{{ t("memories", "Remove person") }}
|
{{ t("memories", "Remove person") }}
|
||||||
<template #icon> <DeleteIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<DeleteIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
</div>
|
</div>
|
||||||
|
@ -99,7 +94,7 @@ export default defineComponent({
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
createMatter() {
|
createMatter() {
|
||||||
this.name = this.$route.params.name || "";
|
this.name = <string>this.$route.params.name || "";
|
||||||
},
|
},
|
||||||
|
|
||||||
back() {
|
back() {
|
||||||
|
@ -135,6 +130,7 @@ export default defineComponent({
|
||||||
.right-actions {
|
.right-actions {
|
||||||
margin-right: 40px;
|
margin-right: 40px;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,23 +6,18 @@
|
||||||
<HomeIcon :size="20" />
|
<HomeIcon :size="20" />
|
||||||
</template>
|
</template>
|
||||||
</NcBreadcrumb>
|
</NcBreadcrumb>
|
||||||
<NcBreadcrumb
|
<NcBreadcrumb v-for="folder in topMatter.list" :key="folder.path" :title="folder.text"
|
||||||
v-for="folder in topMatter.list"
|
:to="{ name: 'folders', params: { path: folder.path } }" />
|
||||||
:key="folder.path"
|
|
||||||
:title="folder.text"
|
|
||||||
:to="{ name: 'folders', params: { path: folder.path } }"
|
|
||||||
/>
|
|
||||||
</NcBreadcrumbs>
|
</NcBreadcrumbs>
|
||||||
|
|
||||||
<div class="right-actions">
|
<div class="right-actions">
|
||||||
<NcActions :inline="1">
|
<NcActions :inline="1">
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Share folder')" @click="$refs.shareModal.open(false)"
|
||||||
:aria-label="t('memories', 'Share folder')"
|
close-after-click>
|
||||||
@click="$refs.shareModal.open(false)"
|
|
||||||
close-after-click
|
|
||||||
>
|
|
||||||
{{ t("memories", "Share folder") }}
|
{{ t("memories", "Share folder") }}
|
||||||
<template #icon> <ShareIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<ShareIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
</div>
|
</div>
|
||||||
|
@ -110,6 +105,7 @@ export default defineComponent({
|
||||||
.right-actions {
|
.right-actions {
|
||||||
margin-right: 40px;
|
margin-right: 40px;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="outer" v-show="years.length > 0">
|
<div class="outer" v-show="years.length > 0">
|
||||||
<div class="inner" ref="inner">
|
<div class="inner" ref="inner">
|
||||||
<div
|
<div v-for="year of years" class="group" :key="year.year" @click="click(year)">
|
||||||
v-for="year of years"
|
|
||||||
class="group"
|
|
||||||
:key="year.year"
|
|
||||||
@click="click(year)"
|
|
||||||
>
|
|
||||||
<img class="fill-block" :src="year.url" />
|
<img class="fill-block" :src="year.url" />
|
||||||
|
|
||||||
<div class="overlay">
|
<div class="overlay">
|
||||||
|
@ -17,23 +12,21 @@
|
||||||
|
|
||||||
<div class="left-btn dir-btn" v-if="hasLeft">
|
<div class="left-btn dir-btn" v-if="hasLeft">
|
||||||
<NcActions>
|
<NcActions>
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Move left')" @click="moveLeft">
|
||||||
:aria-label="t('memories', 'Move left')"
|
|
||||||
@click="moveLeft"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Move left") }}
|
{{ t("memories", "Move left") }}
|
||||||
<template #icon> <LeftMoveIcon :size="28" /> </template>
|
<template #icon>
|
||||||
|
<LeftMoveIcon :size="28" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-btn dir-btn" v-if="hasRight">
|
<div class="right-btn dir-btn" v-if="hasRight">
|
||||||
<NcActions>
|
<NcActions>
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Move right')" @click="moveRight">
|
||||||
:aria-label="t('memories', 'Move right')"
|
|
||||||
@click="moveRight"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Move right") }}
|
{{ t("memories", "Move right") }}
|
||||||
<template #icon> <RightMoveIcon :size="28" /> </template>
|
<template #icon>
|
||||||
|
<RightMoveIcon :size="28" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
</div>
|
</div>
|
||||||
|
@ -255,13 +248,16 @@ $mobHeight: 150px;
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
width: 98%;
|
width: 98%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
.inner {
|
.inner {
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dir-btn {
|
.dir-btn {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
height: $mobHeight;
|
height: $mobHeight;
|
||||||
}
|
}
|
||||||
|
@ -312,6 +308,7 @@ $mobHeight: 150px;
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
aspect-ratio: 3/4;
|
aspect-ratio: 3/4;
|
||||||
height: $mobHeight;
|
height: $mobHeight;
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
<NcActions>
|
<NcActions>
|
||||||
<NcActionButton :aria-label="t('memories', 'Back')" @click="back()">
|
<NcActionButton :aria-label="t('memories', 'Back')" @click="back()">
|
||||||
{{ t("memories", "Back") }}
|
{{ t("memories", "Back") }}
|
||||||
<template #icon> <BackIcon :size="20" /> </template>
|
<template #icon>
|
||||||
|
<BackIcon :size="20" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
<span class="name">{{ name }}</span>
|
<span class="name">{{ name }}</span>
|
||||||
|
@ -44,7 +46,7 @@ export default defineComponent({
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
createMatter() {
|
createMatter() {
|
||||||
this.name = this.$route.params.name || "";
|
this.name = <string>this.$route.params.name || "";
|
||||||
},
|
},
|
||||||
|
|
||||||
back() {
|
back() {
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div ref="editor" class="viewer__image-editor" :class="{ loading: !imageEditor }" v-bind="themeDataAttr" />
|
||||||
ref="editor"
|
|
||||||
class="viewer__image-editor"
|
|
||||||
:class="{ loading: !imageEditor }"
|
|
||||||
v-bind="themeDataAttr"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -58,7 +53,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
config(): FilerobotImageEditorConfig & { theme: any } {
|
config(): FilerobotImageEditorConfig & { theme: any; } {
|
||||||
let src: string;
|
let src: string;
|
||||||
if (["image/png", "image/jpeg", "image/webp"].includes(this.mime)) {
|
if (["image/png", "image/jpeg", "image/webp"].includes(this.mime)) {
|
||||||
src = this.src;
|
src = this.src;
|
||||||
|
@ -275,8 +270,8 @@ export default defineComponent({
|
||||||
onExitWithoutSaving() {
|
onExitWithoutSaving() {
|
||||||
(<any>OC.dialogs).confirmDestructive(
|
(<any>OC.dialogs).confirmDestructive(
|
||||||
translations.changesLoseConfirmation +
|
translations.changesLoseConfirmation +
|
||||||
"\n\n" +
|
"\n\n" +
|
||||||
translations.changesLoseConfirmationHint,
|
translations.changesLoseConfirmationHint,
|
||||||
this.t("memories", "Unsaved changes"),
|
this.t("memories", "Unsaved changes"),
|
||||||
{
|
{
|
||||||
type: (<any>OC.dialogs).YES_NO_BUTTONS,
|
type: (<any>OC.dialogs).YES_NO_BUTTONS,
|
||||||
|
@ -361,7 +356,8 @@ export default defineComponent({
|
||||||
label,
|
label,
|
||||||
button {
|
button {
|
||||||
color: var(--color-main-text);
|
color: var(--color-main-text);
|
||||||
> span {
|
|
||||||
|
>span {
|
||||||
font-size: var(--default-font-size) !important;
|
font-size: var(--default-font-size) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -381,6 +377,7 @@ export default defineComponent({
|
||||||
.SfxInput-root {
|
.SfxInput-root {
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
|
||||||
.SfxInput-Base {
|
.SfxInput-Base {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
@ -396,18 +393,22 @@ export default defineComponent({
|
||||||
min-height: 44px !important;
|
min-height: 44px !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
border: transparent !important;
|
border: transparent !important;
|
||||||
|
|
||||||
&[color="error"] {
|
&[color="error"] {
|
||||||
color: white !important;
|
color: white !important;
|
||||||
background-color: var(--color-error) !important;
|
background-color: var(--color-error) !important;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
border-color: white !important;
|
border-color: white !important;
|
||||||
background-color: var(--color-error-hover) !important;
|
background-color: var(--color-error-hover) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&[color="primary"] {
|
&[color="primary"] {
|
||||||
color: var(--color-primary-text) !important;
|
color: var(--color-primary-text) !important;
|
||||||
background-color: var(--color-primary-element) !important;
|
background-color: var(--color-primary-element) !important;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
background-color: var(--color-primary-element-hover) !important;
|
background-color: var(--color-primary-element-hover) !important;
|
||||||
|
@ -419,8 +420,9 @@ export default defineComponent({
|
||||||
.SfxMenuItem-root {
|
.SfxMenuItem-root {
|
||||||
height: 44px;
|
height: 44px;
|
||||||
padding-left: 8px !important;
|
padding-left: 8px !important;
|
||||||
|
|
||||||
// Center the menu entry icon and fix width
|
// Center the menu entry icon and fix width
|
||||||
> div {
|
>div {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
// Minus the parent padding-left
|
// Minus the parent padding-left
|
||||||
|
@ -446,9 +448,11 @@ export default defineComponent({
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: var(--color-main-text);
|
color: var(--color-main-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.SfxModalTitle-Icon {
|
.SfxModalTitle-Icon {
|
||||||
margin-bottom: 22px !important;
|
margin-bottom: 22px !important;
|
||||||
background: none !important;
|
background: none !important;
|
||||||
|
|
||||||
// Fit EmptyContent styling
|
// Fit EmptyContent styling
|
||||||
svg {
|
svg {
|
||||||
width: 64px;
|
width: 64px;
|
||||||
|
@ -460,10 +464,12 @@ export default defineComponent({
|
||||||
--color-error: var(--color-main-text);
|
--color-error: var(--color-main-text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide close icon (use cancel button)
|
// Hide close icon (use cancel button)
|
||||||
.SfxModalTitle-Close {
|
.SfxModalTitle-Close {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modal actions buttons display
|
// Modal actions buttons display
|
||||||
.SfxModalActions-root {
|
.SfxModalActions-root {
|
||||||
justify-content: space-evenly !important;
|
justify-content: space-evenly !important;
|
||||||
|
@ -471,8 +477,8 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header buttons
|
// Header buttons
|
||||||
.FIE_topbar-center-options > button,
|
.FIE_topbar-center-options>button,
|
||||||
.FIE_topbar-center-options > label {
|
.FIE_topbar-center-options>label {
|
||||||
margin-left: 6px !important;
|
margin-left: 6px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,10 +494,12 @@ export default defineComponent({
|
||||||
height: 80px !important;
|
height: 80px !important;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: var(--border-radius-large) !important;
|
border-radius: var(--border-radius-large) !important;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-label {
|
&-label {
|
||||||
margin-top: 8px !important;
|
margin-top: 8px !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -520,8 +528,8 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matching buttons tools
|
// Matching buttons tools
|
||||||
& > div[class$="-tool-button"],
|
&>div[class$="-tool-button"],
|
||||||
& > div[class$="-tool"] {
|
&>div[class$="-tool"] {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -567,6 +575,7 @@ export default defineComponent({
|
||||||
content: attr(title);
|
content: attr(title);
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -578,6 +587,7 @@ export default defineComponent({
|
||||||
color: var(--color-primary-text) !important;
|
color: var(--color-primary-text) !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
background-color: var(--color-primary-element) !important;
|
background-color: var(--color-primary-element) !important;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
background-color: var(--color-primary-element-hover) !important;
|
background-color: var(--color-primary-element-hover) !important;
|
||||||
|
@ -586,6 +596,7 @@ export default defineComponent({
|
||||||
|
|
||||||
// Save Modal fixes
|
// Save Modal fixes
|
||||||
.FIE_resize-tool-options {
|
.FIE_resize-tool-options {
|
||||||
|
|
||||||
.FIE_resize-width-option,
|
.FIE_resize-width-option,
|
||||||
.FIE_resize-height-option {
|
.FIE_resize-height-option {
|
||||||
flex: 1 1;
|
flex: 1 1;
|
||||||
|
@ -596,10 +607,12 @@ export default defineComponent({
|
||||||
// Resize lock
|
// Resize lock
|
||||||
.FIE_resize-ratio-locker {
|
.FIE_resize-ratio-locker {
|
||||||
margin-right: 8px !important;
|
margin-right: 8px !important;
|
||||||
|
|
||||||
// Icon is very thin
|
// Icon is very thin
|
||||||
svg {
|
svg {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
|
||||||
path {
|
path {
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
stroke: var(--color-main-text);
|
stroke: var(--color-main-text);
|
||||||
|
|
|
@ -1,141 +1,85 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="memories_viewer outer" v-if="show" :class="{ fullyOpened, slideshowTimer }" :style="{ width: outerWidth }"
|
||||||
class="memories_viewer outer"
|
@fullscreenchange="fullscreenChange">
|
||||||
v-if="show"
|
<ImageEditor v-if="editorOpen" :mime="currentPhoto.mimetype" :src="currentDownloadLink"
|
||||||
:class="{ fullyOpened, slideshowTimer }"
|
:fileid="currentPhoto.fileid" @close="editorOpen = false" />
|
||||||
:style="{ width: outerWidth }"
|
|
||||||
@fullscreenchange="fullscreenChange"
|
|
||||||
>
|
|
||||||
<ImageEditor
|
|
||||||
v-if="editorOpen"
|
|
||||||
:mime="currentPhoto.mimetype"
|
|
||||||
:src="currentDownloadLink"
|
|
||||||
:fileid="currentPhoto.fileid"
|
|
||||||
@close="editorOpen = false"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div
|
<div class="inner" ref="inner" v-show="!editorOpen" @pointermove.passive="setUiVisible"
|
||||||
class="inner"
|
@pointerdown.passive="setUiVisible">
|
||||||
ref="inner"
|
|
||||||
v-show="!editorOpen"
|
|
||||||
@pointermove.passive="setUiVisible"
|
|
||||||
@pointerdown.passive="setUiVisible"
|
|
||||||
>
|
|
||||||
<div class="top-bar" v-if="photoswipe" :class="{ showControls }">
|
<div class="top-bar" v-if="photoswipe" :class="{ showControls }">
|
||||||
<NcActions
|
<NcActions :inline="numInlineActions" container=".memories_viewer .pswp">
|
||||||
:inline="numInlineActions"
|
<NcActionButton v-if="canShare" :aria-label="t('memories', 'Share')" @click="shareCurrent"
|
||||||
container=".memories_viewer .pswp"
|
:close-after-click="true">
|
||||||
>
|
|
||||||
<NcActionButton
|
|
||||||
v-if="canShare"
|
|
||||||
:aria-label="t('memories', 'Share')"
|
|
||||||
@click="shareCurrent"
|
|
||||||
:close-after-click="true"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Share") }}
|
{{ t("memories", "Share") }}
|
||||||
<template #icon> <ShareIcon :size="24" /> </template>
|
<template #icon>
|
||||||
|
<ShareIcon :size="24" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton v-if="!routeIsPublic && !routeIsAlbum" :aria-label="t('memories', 'Delete')"
|
||||||
v-if="!routeIsPublic && !routeIsAlbum"
|
@click="deleteCurrent" :close-after-click="true">
|
||||||
:aria-label="t('memories', 'Delete')"
|
|
||||||
@click="deleteCurrent"
|
|
||||||
:close-after-click="true"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Delete") }}
|
{{ t("memories", "Delete") }}
|
||||||
<template #icon> <DeleteIcon :size="24" /> </template>
|
<template #icon>
|
||||||
|
<DeleteIcon :size="24" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton v-if="!routeIsPublic && routeIsAlbum" :aria-label="t('memories', 'Remove from album')"
|
||||||
v-if="!routeIsPublic && routeIsAlbum"
|
@click="deleteCurrent" :close-after-click="true">
|
||||||
:aria-label="t('memories', 'Remove from album')"
|
|
||||||
@click="deleteCurrent"
|
|
||||||
:close-after-click="true"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Remove from album") }}
|
{{ t("memories", "Remove from album") }}
|
||||||
<template #icon> <AlbumRemoveIcon :size="24" /> </template>
|
<template #icon>
|
||||||
|
<AlbumRemoveIcon :size="24" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton v-if="!routeIsPublic" :aria-label="t('memories', 'Favorite')" @click="favoriteCurrent"
|
||||||
v-if="!routeIsPublic"
|
:close-after-click="true">
|
||||||
:aria-label="t('memories', 'Favorite')"
|
|
||||||
@click="favoriteCurrent"
|
|
||||||
:close-after-click="true"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Favorite") }}
|
{{ t("memories", "Favorite") }}
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<StarIcon v-if="isFavorite()" :size="24" />
|
<StarIcon v-if="isFavorite()" :size="24" />
|
||||||
<StarOutlineIcon v-else :size="24" />
|
<StarOutlineIcon v-else :size="24" />
|
||||||
</template>
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton v-if="!routeIsPublic" :aria-label="t('memories', 'Sidebar')" @click="toggleSidebar"
|
||||||
v-if="!routeIsPublic"
|
:close-after-click="true">
|
||||||
:aria-label="t('memories', 'Sidebar')"
|
|
||||||
@click="toggleSidebar"
|
|
||||||
:close-after-click="true"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Sidebar") }}
|
{{ t("memories", "Sidebar") }}
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<InfoIcon :size="24" />
|
<InfoIcon :size="24" />
|
||||||
</template>
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton v-if="canEdit && !routeIsPublic" :aria-label="t('memories', 'Edit')" @click="openEditor"
|
||||||
v-if="canEdit && !routeIsPublic"
|
:close-after-click="true">
|
||||||
:aria-label="t('memories', 'Edit')"
|
|
||||||
@click="openEditor"
|
|
||||||
:close-after-click="true"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Edit") }}
|
{{ t("memories", "Edit") }}
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<TuneIcon :size="24" />
|
<TuneIcon :size="24" />
|
||||||
</template>
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Download')" @click="downloadCurrent" :close-after-click="true"
|
||||||
:aria-label="t('memories', 'Download')"
|
v-if="!state_noDownload">
|
||||||
@click="downloadCurrent"
|
|
||||||
:close-after-click="true"
|
|
||||||
v-if="!this.state_noDownload"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Download") }}
|
{{ t("memories", "Download") }}
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<DownloadIcon :size="24" />
|
<DownloadIcon :size="24" />
|
||||||
</template>
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton v-if="!state_noDownload && currentPhoto?.liveid" :aria-label="t('memories', 'Download Video')"
|
||||||
v-if="!this.state_noDownload && currentPhoto?.liveid"
|
@click="downloadCurrentLiveVideo" :close-after-click="true">
|
||||||
:aria-label="t('memories', 'Download Video')"
|
|
||||||
@click="downloadCurrentLiveVideo"
|
|
||||||
:close-after-click="true"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Download Video") }}
|
{{ t("memories", "Download Video") }}
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<DownloadIcon :size="24" />
|
<DownloadIcon :size="24" />
|
||||||
</template>
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton v-if="!routeIsPublic && !routeIsAlbum" :aria-label="t('memories', 'View in folder')"
|
||||||
v-if="!routeIsPublic && !routeIsAlbum"
|
@click="viewInFolder" :close-after-click="true">
|
||||||
:aria-label="t('memories', 'View in folder')"
|
|
||||||
@click="viewInFolder"
|
|
||||||
:close-after-click="true"
|
|
||||||
>
|
|
||||||
{{ t("memories", "View in folder") }}
|
{{ t("memories", "View in folder") }}
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<OpenInNewIcon :size="24" />
|
<OpenInNewIcon :size="24" />
|
||||||
</template>
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Slideshow')" @click="startSlideshow" :close-after-click="true">
|
||||||
:aria-label="t('memories', 'Slideshow')"
|
|
||||||
@click="startSlideshow"
|
|
||||||
:close-after-click="true"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Slideshow") }}
|
{{ t("memories", "Slideshow") }}
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<SlideshowIcon :size="24" />
|
<SlideshowIcon :size="24" />
|
||||||
</template>
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton :aria-label="t('memories', 'Edit EXIF Data')" v-if="!routeIsPublic" @click="editExif"
|
||||||
:aria-label="t('memories', 'Edit EXIF Data')"
|
:close-after-click="true">
|
||||||
v-if="!routeIsPublic"
|
|
||||||
@click="editExif"
|
|
||||||
:close-after-click="true"
|
|
||||||
>
|
|
||||||
{{ t("memories", "Edit EXIF Data") }}
|
{{ t("memories", "Edit EXIF Data") }}
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<EditFileIcon :size="24" />
|
<EditFileIcon :size="24" />
|
||||||
|
@ -144,18 +88,11 @@
|
||||||
</NcActions>
|
</NcActions>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="bottom-bar" v-if="photoswipe" :class="{ showControls, showBottomBar }">
|
||||||
class="bottom-bar"
|
|
||||||
v-if="photoswipe"
|
|
||||||
:class="{ showControls, showBottomBar }"
|
|
||||||
>
|
|
||||||
<div class="exif title" v-if="currentPhoto?.imageInfo?.exif?.Title">
|
<div class="exif title" v-if="currentPhoto?.imageInfo?.exif?.Title">
|
||||||
{{ currentPhoto.imageInfo.exif.Title }}
|
{{ currentPhoto.imageInfo.exif.Title }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="exif description" v-if="currentPhoto?.imageInfo?.exif?.Description">
|
||||||
class="exif description"
|
|
||||||
v-if="currentPhoto?.imageInfo?.exif?.Description"
|
|
||||||
>
|
|
||||||
{{ currentPhoto.imageInfo.exif.Description }}
|
{{ currentPhoto.imageInfo.exif.Description }}
|
||||||
</div>
|
</div>
|
||||||
<div class="exif date" v-if="currentDateTaken">
|
<div class="exif date" v-if="currentDateTaken">
|
||||||
|
@ -270,7 +207,7 @@ export default defineComponent({
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
/** Number of buttons to show inline */
|
/** Number of buttons to show inline */
|
||||||
numInlineActions() {
|
numInlineActions(): number {
|
||||||
let base = 3;
|
let base = 3;
|
||||||
if (this.canShare) base++;
|
if (this.canShare) base++;
|
||||||
if (this.canEdit) base++;
|
if (this.canEdit) base++;
|
||||||
|
@ -283,17 +220,17 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Route is public */
|
/** Route is public */
|
||||||
routeIsPublic() {
|
routeIsPublic(): boolean {
|
||||||
return this.$route.name === "folder-share";
|
return this.$route.name === "folder-share";
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Route is album */
|
/** Route is album */
|
||||||
routeIsAlbum() {
|
routeIsAlbum(): boolean {
|
||||||
return this.$route.name === "albums";
|
return this.$route.name === "albums";
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Get the currently open photo */
|
/** Get the currently open photo */
|
||||||
currentPhoto() {
|
currentPhoto(): IPhoto | null {
|
||||||
if (!this.list.length || !this.photoswipe) {
|
if (!this.list.length || !this.photoswipe) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -305,31 +242,31 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Is the current slide a video */
|
/** Is the current slide a video */
|
||||||
isVideo() {
|
isVideo(): boolean {
|
||||||
return this.currentPhoto?.flag & this.c.FLAG_IS_VIDEO;
|
return Boolean(this.currentPhoto?.flag & this.c.FLAG_IS_VIDEO);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Show bottom bar info such as date taken */
|
/** Show bottom bar info such as date taken */
|
||||||
showBottomBar() {
|
showBottomBar(): boolean {
|
||||||
return !this.isVideo && this.fullyOpened && this.currentPhoto?.imageInfo;
|
return !this.isVideo && this.fullyOpened && Boolean(this.currentPhoto?.imageInfo);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Get date taken string */
|
/** Get date taken string */
|
||||||
currentDateTaken() {
|
currentDateTaken(): string | null {
|
||||||
const date = this.currentPhoto?.imageInfo?.datetaken;
|
const date = this.currentPhoto?.imageInfo?.datetaken;
|
||||||
if (!date) return null;
|
if (!date) return null;
|
||||||
return utils.getLongDateStr(new Date(date * 1000), false, true);
|
return utils.getLongDateStr(new Date(date * 1000), false, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Get download link for current photo */
|
/** Get download link for current photo */
|
||||||
currentDownloadLink() {
|
currentDownloadLink(): string | null {
|
||||||
return this.currentPhoto
|
return this.currentPhoto
|
||||||
? window.location.origin + getDownloadLink(this.currentPhoto)
|
? window.location.origin + getDownloadLink(this.currentPhoto)
|
||||||
: null;
|
: null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Allow opening editor */
|
/** Allow opening editor */
|
||||||
canEdit() {
|
canEdit(): boolean {
|
||||||
return (
|
return (
|
||||||
this.currentPhoto?.mimetype?.startsWith("image/") &&
|
this.currentPhoto?.mimetype?.startsWith("image/") &&
|
||||||
!this.currentPhoto.liveid
|
!this.currentPhoto.liveid
|
||||||
|
@ -337,7 +274,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Does the browser support native share API */
|
/** Does the browser support native share API */
|
||||||
canShare() {
|
canShare(): boolean {
|
||||||
return "share" in navigator && this.currentPhoto && !this.isVideo;
|
return "share" in navigator && this.currentPhoto && !this.isVideo;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -369,7 +306,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Event on file changed */
|
/** Event on file changed */
|
||||||
handleFileUpdated({ fileid }: { fileid: number }) {
|
handleFileUpdated({ fileid }: { fileid: number; }) {
|
||||||
if (this.currentPhoto && this.currentPhoto.fileid === fileid) {
|
if (this.currentPhoto && this.currentPhoto.fileid === fileid) {
|
||||||
this.currentPhoto.etag += "_";
|
this.currentPhoto.etag += "_";
|
||||||
this.currentPhoto.imageInfo = null;
|
this.currentPhoto.imageInfo = null;
|
||||||
|
@ -547,14 +484,14 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
|
|
||||||
// Video support
|
// Video support
|
||||||
new PsVideo(this.photoswipe, {
|
new PsVideo(<PhotoSwipe>this.photoswipe, {
|
||||||
videoAttributes: { controls: "", playsinline: "", preload: "none" },
|
videoAttributes: { controls: "", playsinline: "", preload: "none" },
|
||||||
autoplay: true,
|
autoplay: true,
|
||||||
preventDragOffset: 40,
|
preventDragOffset: 40,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Live photo support
|
// Live photo support
|
||||||
new PsLivePhoto(this.photoswipe, {});
|
new PsLivePhoto(<PhotoSwipe>this.photoswipe, {});
|
||||||
|
|
||||||
// Patch the close button to stop the slideshow
|
// Patch the close button to stop the slideshow
|
||||||
const _close = this.photoswipe.close.bind(this.photoswipe);
|
const _close = this.photoswipe.close.bind(this.photoswipe);
|
||||||
|
@ -1170,6 +1107,7 @@ export default defineComponent({
|
||||||
transition: opacity 0.2s ease-in-out;
|
transition: opacity 0.2s ease-in-out;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
&.showControls {
|
&.showControls {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
|
@ -1188,6 +1126,7 @@ export default defineComponent({
|
||||||
|
|
||||||
transition: opacity 0.2s ease-in-out;
|
transition: opacity 0.2s ease-in-out;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
||||||
&.showControls.showBottomBar {
|
&.showControls.showBottomBar {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
@ -1197,6 +1136,7 @@ export default defineComponent({
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.description {
|
&.description {
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
|
@ -1228,6 +1168,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep .plyr__volume {
|
:deep .plyr__volume {
|
||||||
|
|
||||||
// Cannot be vertical yet :(
|
// Cannot be vertical yet :(
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -1255,6 +1196,7 @@ export default defineComponent({
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pswp__icn-shadow {
|
.pswp__icn-shadow {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
20
src/main.ts
20
src/main.ts
|
@ -1,7 +1,7 @@
|
||||||
/// <reference types="@nextcloud/typings" />
|
/// <reference types="@nextcloud/typings" />
|
||||||
|
|
||||||
import "reflect-metadata";
|
import "reflect-metadata";
|
||||||
import Vue from "vue";
|
import { createApp } from "vue";
|
||||||
import VueVirtualScroller from "vue-virtual-scroller";
|
import VueVirtualScroller from "vue-virtual-scroller";
|
||||||
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
|
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
|
||||||
import GlobalMixin from "./mixins/GlobalMixin";
|
import GlobalMixin from "./mixins/GlobalMixin";
|
||||||
|
@ -66,10 +66,6 @@ if (!globalThis.videoClientIdPersistent) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.mixin(GlobalMixin);
|
|
||||||
Vue.mixin(UserConfig);
|
|
||||||
Vue.use(VueVirtualScroller);
|
|
||||||
|
|
||||||
// https://github.com/nextcloud/photos/blob/156f280c0476c483cb9ce81769ccb0c1c6500a4e/src/main.js
|
// https://github.com/nextcloud/photos/blob/156f280c0476c483cb9ce81769ccb0c1c6500a4e/src/main.js
|
||||||
// TODO: remove when we have a proper fileinfo standalone library
|
// TODO: remove when we have a proper fileinfo standalone library
|
||||||
// original scripts are loaded from
|
// original scripts are loaded from
|
||||||
|
@ -90,8 +86,12 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default new Vue({
|
const Vue = createApp(App);
|
||||||
el: "#content",
|
Vue.use(router);
|
||||||
router,
|
Vue.use(VueVirtualScroller);
|
||||||
render: (h) => h(App),
|
|
||||||
});
|
Vue.mixin(GlobalMixin);
|
||||||
|
Vue.mixin(UserConfig);
|
||||||
|
|
||||||
|
Vue.mount("#content");
|
||||||
|
export default Vue;
|
||||||
|
|
|
@ -2,11 +2,12 @@ import { emit, subscribe, unsubscribe } from "@nextcloud/event-bus";
|
||||||
import { loadState } from "@nextcloud/initial-state";
|
import { loadState } from "@nextcloud/initial-state";
|
||||||
import axios from "@nextcloud/axios";
|
import axios from "@nextcloud/axios";
|
||||||
import { API } from "../services/API";
|
import { API } from "../services/API";
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
const eventName = "memories:user-config-changed";
|
const eventName = "memories:user-config-changed";
|
||||||
const localSettings = ["squareThumbs", "showFaceRect"];
|
const localSettings = ["squareThumbs", "showFaceRect"];
|
||||||
|
|
||||||
export default {
|
export default defineComponent({
|
||||||
name: "UserConfig",
|
name: "UserConfig",
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
@ -83,4 +84,4 @@ export default {
|
||||||
emit(eventName, { setting, value });
|
emit(eventName, { setting, value });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
import { generateUrl } from "@nextcloud/router";
|
import { generateUrl } from "@nextcloud/router";
|
||||||
import { translate as t, translatePlural as n } from "@nextcloud/l10n";
|
import { translate as t, translatePlural as n } from "@nextcloud/l10n";
|
||||||
import Router from "vue-router";
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
import Vue from "vue";
|
|
||||||
import Timeline from "./components/Timeline.vue";
|
import Timeline from "./components/Timeline.vue";
|
||||||
|
|
||||||
Vue.use(Router);
|
export default createRouter({
|
||||||
|
|
||||||
export default new Router({
|
|
||||||
mode: "history",
|
|
||||||
// if index.php is in the url AND we got this far, then it's working:
|
// if index.php is in the url AND we got this far, then it's working:
|
||||||
// let's keep using index.php in the url
|
// let's keep using index.php in the url
|
||||||
base: generateUrl("/apps/memories"),
|
history: createWebHistory(generateUrl("/apps/memories")),
|
||||||
|
|
||||||
linkActiveClass: "active",
|
linkActiveClass: "active",
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
|
@ -23,7 +20,7 @@ export default new Router({
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: "/folders/:path*",
|
path: "/folders/:path*", // REMOVED IN VUE 3
|
||||||
component: Timeline,
|
component: Timeline,
|
||||||
name: "folders",
|
name: "folders",
|
||||||
props: (route) => ({
|
props: (route) => ({
|
||||||
|
@ -95,7 +92,7 @@ export default new Router({
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: "/tags/:name*",
|
path: "/tags/:name*", // REMOVED IN VUE 3
|
||||||
component: Timeline,
|
component: Timeline,
|
||||||
name: "tags",
|
name: "tags",
|
||||||
props: (route) => ({
|
props: (route) => ({
|
||||||
|
@ -106,9 +103,8 @@ export default new Router({
|
||||||
{
|
{
|
||||||
path: "/maps",
|
path: "/maps",
|
||||||
name: "maps",
|
name: "maps",
|
||||||
// router-link doesn't support external url, let's force the redirect
|
redirect: () => {
|
||||||
beforeEnter() {
|
return generateUrl("/apps/maps");
|
||||||
window.open(generateUrl("/apps/maps"), "_blank");
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,11 @@ const gen = generateUrl;
|
||||||
|
|
||||||
/** Add auth token to this URL */
|
/** Add auth token to this URL */
|
||||||
function tok(url: string) {
|
function tok(url: string) {
|
||||||
if (vuerouter.currentRoute.name === "folder-share") {
|
if (vuerouter.currentRoute.value.name === "folder-share") {
|
||||||
url = API.Q(url, `folder_share=${vuerouter.currentRoute.params.token}`);
|
url = API.Q(
|
||||||
|
url,
|
||||||
|
`folder_share=${vuerouter.currentRoute.value.params.token}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,15 +27,11 @@ import { generateRemoteUrl } from "@nextcloud/router";
|
||||||
|
|
||||||
// Monkey business
|
// Monkey business
|
||||||
import * as rq from "webdav/dist/node/request";
|
import * as rq from "webdav/dist/node/request";
|
||||||
(<any>rq).prepareRequestOptionsOld = rq.prepareRequestOptions.bind(rq);
|
const prepareRequestOptionsOld = rq.prepareRequestOptions.bind(rq);
|
||||||
(<any>rq).prepareRequestOptions = function (
|
(<any>rq).prepareRequestOptions = (requestOptions, context, userOptions) => {
|
||||||
requestOptions,
|
|
||||||
context,
|
|
||||||
userOptions
|
|
||||||
) {
|
|
||||||
requestOptions.method = userOptions.method || requestOptions.method;
|
requestOptions.method = userOptions.method || requestOptions.method;
|
||||||
return this.prepareRequestOptionsOld(requestOptions, context, userOptions);
|
return prepareRequestOptionsOld(requestOptions, context, userOptions);
|
||||||
}.bind(rq);
|
};
|
||||||
|
|
||||||
// force our axios
|
// force our axios
|
||||||
const patcher = webdav.getPatcher();
|
const patcher = webdav.getPatcher();
|
||||||
|
|
|
@ -235,7 +235,7 @@ export function convertFlags(photo: IPhoto) {
|
||||||
* This function does not check if this is the folder route
|
* This function does not check if this is the folder route
|
||||||
*/
|
*/
|
||||||
export function getFolderRoutePath(basePath: string) {
|
export function getFolderRoutePath(basePath: string) {
|
||||||
let path: any = vuerouter.currentRoute.params.path || "/";
|
let path: any = vuerouter.currentRoute.value.params.path || "/";
|
||||||
path = typeof path === "string" ? path : path.join("/");
|
path = typeof path === "string" ? path : path.join("/");
|
||||||
path = basePath + "/" + path;
|
path = basePath + "/" + path;
|
||||||
path = path.replace(/\/\/+/, "/"); // Remove double slashes
|
path = path.replace(/\/\/+/, "/"); // Remove double slashes
|
||||||
|
|
|
@ -44,9 +44,13 @@ const GET_FILE_CHUNK_SIZE = 50;
|
||||||
*/
|
*/
|
||||||
export async function getFiles(photos: IPhoto[]): Promise<IFileInfo[]> {
|
export async function getFiles(photos: IPhoto[]): Promise<IFileInfo[]> {
|
||||||
// Check if albums
|
// Check if albums
|
||||||
const route = vuerouter.currentRoute;
|
const route = vuerouter.currentRoute.value;
|
||||||
if (route.name === "albums") {
|
if (route.name === "albums") {
|
||||||
return getAlbumFileInfos(photos, route.params.user, route.params.name);
|
return getAlbumFileInfos(
|
||||||
|
photos,
|
||||||
|
<string>route.params.user,
|
||||||
|
<string>route.params.name
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get file infos
|
// Get file infos
|
||||||
|
|
|
@ -48,8 +48,8 @@ export async function downloadFilesByPhotos(photos: IPhoto[]) {
|
||||||
/** Get URL to download one file (e.g. for video streaming) */
|
/** Get URL to download one file (e.g. for video streaming) */
|
||||||
export function getDownloadLink(photo: IPhoto) {
|
export function getDownloadLink(photo: IPhoto) {
|
||||||
// Check if public
|
// Check if public
|
||||||
if (vuerouter.currentRoute.name === "folder-share") {
|
if (vuerouter.currentRoute.value.name === "folder-share") {
|
||||||
const token = window.vuerouter.currentRoute.params.token;
|
const token = window.vuerouter.currentRoute.value.params.token;
|
||||||
// TODO: allow proper dav access without the need of basic auth
|
// TODO: allow proper dav access without the need of basic auth
|
||||||
// https://github.com/nextcloud/server/issues/19700
|
// https://github.com/nextcloud/server/issues/19700
|
||||||
return generateUrl(`/s/${token}/download?path={dirname}&files={basename}`, {
|
return generateUrl(`/s/${token}/download?path={dirname}&files={basename}`, {
|
||||||
|
@ -59,12 +59,12 @@ export function getDownloadLink(photo: IPhoto) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if albums
|
// Check if albums
|
||||||
const route = vuerouter.currentRoute;
|
const route = vuerouter.currentRoute.value;
|
||||||
if (route.name === "albums") {
|
if (route.name === "albums") {
|
||||||
const fInfos = getAlbumFileInfos(
|
const fInfos = getAlbumFileInfos(
|
||||||
[photo],
|
[photo],
|
||||||
route.params.user,
|
route.params.user as string,
|
||||||
route.params.name
|
route.params.name as string
|
||||||
);
|
);
|
||||||
if (fInfos.length) {
|
if (fInfos.length) {
|
||||||
return generateUrl(`/remote.php/dav${fInfos[0].originalFilename}`);
|
return generateUrl(`/remote.php/dav${fInfos[0].originalFilename}`);
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { VueConstructor } from "vue";
|
|
||||||
|
|
||||||
export type IFileInfo = {
|
export type IFileInfo = {
|
||||||
/** Same as fileid */
|
/** Same as fileid */
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -229,7 +227,7 @@ export type ISelectionAction = {
|
||||||
/** Display text */
|
/** Display text */
|
||||||
name: string;
|
name: string;
|
||||||
/** Icon component */
|
/** Icon component */
|
||||||
icon: VueConstructor;
|
icon: any;
|
||||||
/** Action to perform */
|
/** Action to perform */
|
||||||
callback: (selection: Map<number, IPhoto>) => Promise<void>;
|
callback: (selection: Map<number, IPhoto>) => Promise<void>;
|
||||||
/** Condition to check for including */
|
/** Condition to check for including */
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { constants } from "./services/Utils";
|
||||||
|
import { translate as t, translatePlural as n } from "@nextcloud/l10n";
|
||||||
|
|
||||||
|
declare module "vue" {
|
||||||
|
interface ComponentCustomProperties {
|
||||||
|
// GlobalMixin.ts
|
||||||
|
t: typeof t;
|
||||||
|
n: typeof n;
|
||||||
|
|
||||||
|
c: typeof constants.c;
|
||||||
|
TagDayID: typeof constants.TagDayID;
|
||||||
|
TagDayIDValueSet: typeof constants.TagDayIDValueSet;
|
||||||
|
|
||||||
|
state_noDownload: boolean;
|
||||||
|
|
||||||
|
// UserConfig.ts
|
||||||
|
config_timelinePath: string;
|
||||||
|
config_foldersPath: string;
|
||||||
|
config_showHidden: boolean;
|
||||||
|
config_tagsEnabled: boolean;
|
||||||
|
config_recognizeEnabled: boolean;
|
||||||
|
config_facerecognitionInstalled: boolean;
|
||||||
|
config_facerecognitionEnabled: boolean;
|
||||||
|
config_mapsEnabled: boolean;
|
||||||
|
config_albumsEnabled: boolean;
|
||||||
|
config_squareThumbs: boolean;
|
||||||
|
config_showFaceRect: boolean;
|
||||||
|
config_eventName: string;
|
||||||
|
|
||||||
|
updateSetting(setting: string): Promise<void>;
|
||||||
|
updateLocalSetting({
|
||||||
|
setting,
|
||||||
|
value,
|
||||||
|
}: {
|
||||||
|
setting: string;
|
||||||
|
value: any;
|
||||||
|
}): void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
|
@ -1,10 +1,10 @@
|
||||||
declare module "*.vue" {
|
declare module "*.vue" {
|
||||||
import Vue from "vue";
|
import { defineComponent } from "vue";
|
||||||
export default Vue;
|
const Component: ReturnType<typeof defineComponent>;
|
||||||
|
export default Component;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module "*.svg" {
|
declare module "*.svg" {
|
||||||
import Vue, { VueConstructor } from "vue";
|
const content: any;
|
||||||
const content: VueConstructor<Vue>;
|
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"jsx": "preserve",
|
||||||
"lib": ["dom", "es2017"],
|
"lib": ["dom", "es2017"],
|
||||||
"target": "ES2017",
|
"target": "ES2017",
|
||||||
"module": "es2020",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"experimentalDecorators": true,
|
"noImplicitThis": true,
|
||||||
"emitDecoratorMetadata": true,
|
"esModuleInterop": true
|
||||||
"jsx": "preserve"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
|
||||||
|
*
|
||||||
|
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||||
|
*
|
||||||
|
* @license AGPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const path = require("path");
|
||||||
|
const webpack = require("webpack");
|
||||||
|
|
||||||
|
const { VueLoaderPlugin } = require("vue-loader");
|
||||||
|
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
|
||||||
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
|
|
||||||
|
const appName = process.env.npm_package_name;
|
||||||
|
const appVersion = process.env.npm_package_version;
|
||||||
|
const buildMode = process.env.NODE_ENV;
|
||||||
|
const isDev = buildMode === "development";
|
||||||
|
console.info("Building", appName, appVersion, "\n");
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
RULE_CSS: {
|
||||||
|
test: /\.css$/,
|
||||||
|
use: ["style-loader", "css-loader"],
|
||||||
|
},
|
||||||
|
RULE_SCSS: {
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: ["style-loader", "css-loader", "sass-loader"],
|
||||||
|
},
|
||||||
|
RULE_VUE: {
|
||||||
|
test: /\.vue$/,
|
||||||
|
loader: "vue-loader",
|
||||||
|
},
|
||||||
|
RULE_JS: {
|
||||||
|
test: /\.js$/,
|
||||||
|
loader: "babel-loader",
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
RULE_ASSETS: {
|
||||||
|
test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf)$/,
|
||||||
|
type: "asset/inline",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
target: "web",
|
||||||
|
mode: buildMode,
|
||||||
|
devtool: isDev ? "cheap-source-map" : "source-map",
|
||||||
|
|
||||||
|
entry: {
|
||||||
|
main: path.resolve(path.join("src", "main.js")),
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve("./js"),
|
||||||
|
publicPath: path.join("/apps/", appName, "/js/"),
|
||||||
|
|
||||||
|
// Output file names
|
||||||
|
filename: `${appName}-[name].js?v=[contenthash]`,
|
||||||
|
chunkFilename: `${appName}-[name].js?v=[contenthash]`,
|
||||||
|
|
||||||
|
// Clean output before each build
|
||||||
|
clean: true,
|
||||||
|
|
||||||
|
// Make sure sourcemaps have a proper path and do not
|
||||||
|
// leak local paths https://github.com/webpack/webpack/issues/3603
|
||||||
|
devtoolNamespace: appName,
|
||||||
|
devtoolModuleFilenameTemplate(info) {
|
||||||
|
const rootDir = process.cwd();
|
||||||
|
const rel = path.relative(rootDir, info.absoluteResourcePath);
|
||||||
|
return `webpack:///${appName}/${rel}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
hot: true,
|
||||||
|
host: "127.0.0.1",
|
||||||
|
port: 3000,
|
||||||
|
client: {
|
||||||
|
overlay: false,
|
||||||
|
},
|
||||||
|
devMiddleware: {
|
||||||
|
writeToDisk: true,
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
cache: !isDev,
|
||||||
|
|
||||||
|
optimization: {
|
||||||
|
chunkIds: "named",
|
||||||
|
splitChunks: {
|
||||||
|
automaticNameDelimiter: "-",
|
||||||
|
},
|
||||||
|
minimize: !isDev,
|
||||||
|
minimizer: [
|
||||||
|
new TerserPlugin({
|
||||||
|
terserOptions: {
|
||||||
|
output: {
|
||||||
|
comments: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extractComments: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
module: {
|
||||||
|
rules: Object.values(rules),
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
new VueLoaderPlugin(),
|
||||||
|
|
||||||
|
// Make sure we auto-inject node polyfills on demand
|
||||||
|
// https://webpack.js.org/blog/2020-10-10-webpack-5-release/#automatic-nodejs-polyfills-removed
|
||||||
|
new NodePolyfillPlugin(),
|
||||||
|
|
||||||
|
// Make appName & appVersion available as a constant
|
||||||
|
new webpack.DefinePlugin({ appName: JSON.stringify(appName) }),
|
||||||
|
new webpack.DefinePlugin({ appVersion: JSON.stringify(appVersion) }),
|
||||||
|
],
|
||||||
|
|
||||||
|
resolve: {
|
||||||
|
extensions: ["*", ".js", ".vue"],
|
||||||
|
symlinks: false,
|
||||||
|
},
|
||||||
|
};
|
19
webpack.js
19
webpack.js
|
@ -1,4 +1,4 @@
|
||||||
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
const webpackConfig = require('./webpack-base')
|
||||||
const WorkboxPlugin = require('workbox-webpack-plugin')
|
const WorkboxPlugin = require('workbox-webpack-plugin')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
|
@ -14,9 +14,6 @@ webpackConfig.module.rules.push({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
webpackConfig.resolve.extensions.push('.ts');
|
webpackConfig.resolve.extensions.push('.ts');
|
||||||
webpackConfig.resolve.alias = {
|
|
||||||
'vue$': 'vue/dist/vue.esm.js',
|
|
||||||
}
|
|
||||||
webpackConfig.entry.main = path.resolve(path.join('src', 'main'));
|
webpackConfig.entry.main = path.resolve(path.join('src', 'main'));
|
||||||
|
|
||||||
webpackConfig.watchOptions = {
|
webpackConfig.watchOptions = {
|
||||||
|
@ -24,11 +21,13 @@ webpackConfig.watchOptions = {
|
||||||
aggregateTimeout: 300,
|
aggregateTimeout: 300,
|
||||||
};
|
};
|
||||||
|
|
||||||
webpackConfig.plugins.push(
|
if (!isDev) {
|
||||||
new WorkboxPlugin.InjectManifest({
|
webpackConfig.plugins.push(
|
||||||
swSrc: path.resolve(path.join('src', 'service-worker.js')),
|
new WorkboxPlugin.InjectManifest({
|
||||||
swDest: 'memories-service-worker.js',
|
swSrc: path.resolve(path.join('src', 'service-worker.js')),
|
||||||
})
|
swDest: 'memories-service-worker.js',
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = webpackConfig
|
module.exports = webpackConfig
|
||||||
|
|
Loading…
Reference in New Issue