Add search filter for content that has no album #377
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
7787988e25
commit
581404648c
|
@ -1,8 +1,9 @@
|
|||
{
|
||||
"name": "photoprism",
|
||||
"version": "1.0.0",
|
||||
"description": "Single-page Web app frontend for PhotoPrism",
|
||||
"description": "PhotoPrism Progressive Web App (PWA)",
|
||||
"author": "Michael Mayer",
|
||||
"license": "AGPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"watch": "webpack --watch",
|
||||
|
|
|
@ -69,7 +69,7 @@ const Notify = {
|
|||
}
|
||||
},
|
||||
wait: function () {
|
||||
this.warn($gettext("Busy, please wait..."));
|
||||
this.warn($gettext("Busy, please wait…"));
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ export default class Util {
|
|||
length = 100;
|
||||
}
|
||||
if (ending == null) {
|
||||
ending = "...";
|
||||
ending = "…";
|
||||
}
|
||||
if (str.length > length) {
|
||||
return str.substring(0, length - ending.length) + ending;
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
methods: {
|
||||
editDialog() {
|
||||
if (this.selection.length !== 1) {
|
||||
this.$notify.error("select one album to edit");
|
||||
this.$notify.error(this.$gettext("You may only select one item"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@
|
|||
},
|
||||
shareDialog() {
|
||||
if (this.selection.length !== 1) {
|
||||
this.$notify.error("select one album to share");
|
||||
this.$notify.error(this.$gettext("You may only select one item"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@
|
|||
this.expanded = false;
|
||||
},
|
||||
onDownload(path) {
|
||||
Notify.success(this.$gettext("Downloading..."));
|
||||
Notify.success(this.$gettext("Downloading…"));
|
||||
const link = document.createElement('a')
|
||||
link.href = path;
|
||||
link.download = "album.zip";
|
||||
|
|
|
@ -103,7 +103,7 @@
|
|||
this.expanded = false;
|
||||
},
|
||||
onDownload(path) {
|
||||
Notify.success(this.$gettext("Downloading..."));
|
||||
Notify.success(this.$gettext("Downloading…"));
|
||||
const link = document.createElement('a')
|
||||
link.href = path;
|
||||
link.download = "photos.zip";
|
||||
|
|
|
@ -129,7 +129,7 @@
|
|||
this.expanded = false;
|
||||
},
|
||||
onDownload(path) {
|
||||
Notify.success(this.$gettext("Downloading..."));
|
||||
Notify.success(this.$gettext("Downloading…"));
|
||||
const link = document.createElement('a')
|
||||
link.href = path;
|
||||
link.download = "label.zip";
|
||||
|
|
|
@ -90,6 +90,14 @@
|
|||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile :to="{name: 'photos', query: { q: 'analog:true' }}" :exact="true" @click="">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Film Scans</translate>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile to="/review" @click="" v-if="$config.feature('review')"
|
||||
class="nav-review">
|
||||
<v-list-tile-content>
|
||||
|
@ -178,6 +186,14 @@
|
|||
class="nav-count">{{ config.count.folders }}</span></v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
||||
<v-list-tile to="/unsorted" class="nav-unsorted">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate key="Unsorted">Unsorted</translate>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
</v-list-group>
|
||||
|
||||
<v-list-tile :to="{ name: 'calendar' }" @click="" class="nav-calendar">
|
||||
|
@ -376,7 +392,7 @@
|
|||
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title class="text--warning">
|
||||
<translate key="Connecting">Connecting...</translate>
|
||||
<translate key="Offline">Offline</translate>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
<v-card v-if="photos.length === 0" class="p-photos-empty secondary-light lighten-1 ma-1" flat>
|
||||
<v-card-title primary-title>
|
||||
<div>
|
||||
<h3 class="title mb-3">
|
||||
<h3 class="title ma-0 pa-0">
|
||||
<translate>No photos or videos found</translate>
|
||||
</h3>
|
||||
<div>
|
||||
<p class="mt-4 mb-0 pa-0">
|
||||
<translate>Try using other terms and search options such as category, country and camera.</translate>
|
||||
<span v-show="$config.feature('review')">
|
||||
<translate>Non-photographic and low-quality images require a review before they appear in search results.</translate>
|
||||
</span>
|
||||
</div>
|
||||
</p>
|
||||
<p v-if="$config.feature('review')" class="mt-2 mb-0 pa-0">
|
||||
<translate>Non-photographic and low-quality images require a review before they appear in search results.</translate>
|
||||
</p>
|
||||
</div>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
|
|
|
@ -220,7 +220,7 @@
|
|||
this.expanded = false;
|
||||
},
|
||||
onDownload(path) {
|
||||
Notify.success(this.$gettext("Downloading..."));
|
||||
Notify.success(this.$gettext("Downloading…"));
|
||||
const link = document.createElement('a')
|
||||
link.href = path;
|
||||
link.download = "photos.zip";
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
<v-card v-if="photos.length === 0" class="p-photos-empty secondary-light lighten-1 ma-1" flat>
|
||||
<v-card-title primary-title>
|
||||
<div>
|
||||
<h3 class="title mb-3">
|
||||
<h3 class="title ma-0 pa-0">
|
||||
<translate>No photos or videos found</translate>
|
||||
</h3>
|
||||
<div>
|
||||
<p class="mt-4 mb-0 pa-0">
|
||||
<translate>Try using other terms and search options such as category, country and camera.</translate>
|
||||
<span v-show="$config.feature('review')">
|
||||
<translate>Non-photographic and low-quality images require a review before they appear in search results.</translate>
|
||||
</span>
|
||||
</div>
|
||||
</p>
|
||||
<p v-if="$config.feature('review')" class="mt-2 mb-0 pa-0">
|
||||
<translate>Non-photographic and low-quality images require a review before they appear in search results.</translate>
|
||||
</p>
|
||||
</div>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
Notify.success(this.$gettext("Downloading..."));
|
||||
Notify.success(this.$gettext("Downloading…"));
|
||||
let photo = new Photo();
|
||||
photo.find(this.item.uid).then((p) => {
|
||||
p.downloadAll();
|
||||
|
|
|
@ -48,15 +48,6 @@
|
|||
color="secondary-dark"
|
||||
class="input-category"
|
||||
>
|
||||
<template v-slot:no-data>
|
||||
<v-list-tile>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
<translate>Press enter to create a new category.</translate>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
</template>
|
||||
</v-combobox>
|
||||
</v-flex>
|
||||
<v-flex xs12 md6 pa-2>
|
||||
|
|
|
@ -130,6 +130,19 @@
|
|||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate key="Analog">Analog</translate>
|
||||
</td>
|
||||
<td>
|
||||
<v-switch
|
||||
@change="save"
|
||||
hide-details
|
||||
v-model="model.Analog"
|
||||
:label="model.Analog ? $gettext('Yes') : $gettext('No')"
|
||||
></v-switch>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<translate key="Created">Created</translate>
|
||||
|
|
|
@ -20,8 +20,7 @@
|
|||
<template v-slot:header>
|
||||
<button class="text-xs-left action-url ml-0 mt-0 mb-0 pa-0 mr-2" @click.stop="copyUrl(link)" style="user-select: none;">
|
||||
<v-icon size="16" class="pr-1">link</v-icon>
|
||||
/s/<strong style="font-weight: 500;" v-if="link.Token">{{ link.Token.toLowerCase()
|
||||
}}</strong><span v-else>...</span>
|
||||
/s/<strong style="font-weight: 500;" v-if="link.Token">{{ link.getToken() }}</strong><span v-else>…</span>
|
||||
</button>
|
||||
</template>
|
||||
<v-card>
|
||||
|
@ -62,6 +61,7 @@
|
|||
color="secondary-dark"
|
||||
v-model="link.Token"
|
||||
class="input-secret"
|
||||
mask="n"
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
<!-- v-flex xs12 sm6 class="pa-2">
|
||||
|
@ -97,9 +97,10 @@
|
|||
</v-expansion-panel>
|
||||
|
||||
<v-container fluid text-xs-left class="pb-0 pt-3 pr-0 pl-0 caption">
|
||||
People you share a link with will be able to view the {{model.modelName().toLowerCase()}}.<!-- TODO: translate-->
|
||||
<translate>A click will copy it to your clipboard. Any private photos remain private.
|
||||
Alternatively, you can upload files directly to WebDAV servers like Nextcloud.</translate>
|
||||
<translate :translate-params="{name: model.modelName()}">People you share a link with will be able to view public contents.</translate>
|
||||
<translate>A click will copy it to your clipboard.</translate>
|
||||
<translate>Any private photos and videos remain private and won't be shared.</translate>
|
||||
<translate>Alternatively, you can upload files directly to WebDAV servers like Nextcloud.</translate>
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
<v-card-actions class="pt-0">
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<v-select
|
||||
color="secondary-dark"
|
||||
hide-details hide-no-data flat
|
||||
:label="labels.account"
|
||||
:label="$gettext('Account')"
|
||||
item-text="AccName"
|
||||
item-value="ID"
|
||||
@change="onChange"
|
||||
|
@ -44,21 +44,21 @@
|
|||
:disabled="loading || noAccounts"
|
||||
item-text="abs"
|
||||
item-value="abs"
|
||||
:label="labels.path"
|
||||
:label="$gettext('Folder')"
|
||||
>
|
||||
</v-autocomplete>
|
||||
</v-flex>
|
||||
<v-flex xs12 text-xs-right class="pt-4">
|
||||
<v-btn @click.stop="cancel" depressed color="secondary-light" class="action-cancel ml-0 mt-0 mb-0 mr-2">
|
||||
<translate key="Cancel">Cancel</translate>
|
||||
<translate>Cancel</translate>
|
||||
</v-btn>
|
||||
<v-btn color="secondary-dark" depressed dark @click.stop="setup"
|
||||
class="action-setup ma-0" v-if="noAccounts">
|
||||
<span>{{ labels.setup }}</span>
|
||||
<translate>Setup</translate>
|
||||
</v-btn>
|
||||
<v-btn color="secondary-dark" depressed dark @click.stop="confirm"
|
||||
class="action-upload ma-0" v-else>
|
||||
<span>{{ labels.upload }}</span>
|
||||
<translate>Upload</translate>
|
||||
</v-btn>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
@ -88,12 +88,6 @@
|
|||
],
|
||||
pathItems: [],
|
||||
newPath: "",
|
||||
labels: {
|
||||
account: this.$gettext("Account"),
|
||||
path: this.$gettext("Folder"),
|
||||
upload: this.$gettext("Upload"),
|
||||
setup: this.$gettext("Setup"),
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -115,9 +109,9 @@
|
|||
this.loading = false;
|
||||
|
||||
if (files.length === 1) {
|
||||
this.$notify.success("One photo shared");
|
||||
this.$notify.success("One file uploaded");
|
||||
} else {
|
||||
this.$notify.success(this.$gettextInterpolate(this.$gettext("%{n} photos shared"), {n: files.length}));
|
||||
this.$notify.success(this.$gettextInterpolate(this.$gettext("%{n} files uploaded"), {n: files.length}));
|
||||
}
|
||||
|
||||
this.$emit('confirm', this.account);
|
||||
|
|
|
@ -50,20 +50,18 @@
|
|||
</v-combobox>
|
||||
<span v-else-if="failed"><translate key="Upload failed">Upload failed</translate></span>
|
||||
<span v-else-if="total > 0 && completed < 100">
|
||||
<translate key="Uploading">Uploading</translate> {{current}} <translate key="of">of</translate> {{total}}...
|
||||
</span>
|
||||
<span v-else-if="indexing"><translate key="Upload complete">Upload complete. Indexing...</translate></span>
|
||||
<translate :translate-params="{n: current, t: total}">Uploading %{n} of %{t}…</translate>
|
||||
</span>
|
||||
<span v-else-if="indexing"><translate key="Upload complete">Upload complete. Indexing…</translate></span>
|
||||
<span v-else-if="completed === 100"><translate key="Done">Done.</translate></span>
|
||||
</p>
|
||||
|
||||
|
||||
<v-progress-linear color="secondary-dark" v-model="completed"
|
||||
:indeterminate="indexing"></v-progress-linear>
|
||||
|
||||
|
||||
<p class="body-1" v-if="safe">
|
||||
<translate>Please don't upload photos containing offensive content. Uploads
|
||||
that may contain such images will be rejected automatically.</translate>
|
||||
<translate>Please don't upload photos containing offensive content.</translate>
|
||||
<translate>Uploads that may contain such images will be rejected automatically.</translate>
|
||||
</p>
|
||||
|
||||
<p class="body-1" v-if="review">
|
||||
|
@ -140,7 +138,7 @@
|
|||
},
|
||||
cancel() {
|
||||
if (this.busy) {
|
||||
Notify.info(this.$gettext("Uploading photos..."));
|
||||
Notify.info(this.$gettext("Uploading photos…"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -148,7 +146,7 @@
|
|||
},
|
||||
confirm() {
|
||||
if (this.busy) {
|
||||
Notify.info(this.$gettext("Uploading photos..."));
|
||||
Notify.info(this.$gettext("Uploading photos…"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -189,7 +187,7 @@
|
|||
this.completed = 0;
|
||||
this.uploads = [];
|
||||
|
||||
Notify.info(this.$gettext("Uploading photos..."));
|
||||
Notify.info(this.$gettext("Uploading photos…"));
|
||||
|
||||
let addToAlbums = [];
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ export class File extends RestModel {
|
|||
}
|
||||
|
||||
if (truncate) {
|
||||
result = Util.truncate(result, truncate, "...");
|
||||
result = Util.truncate(result, truncate, "…");
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -72,7 +72,7 @@ export class Folder extends RestModel {
|
|||
}
|
||||
|
||||
if(truncate) {
|
||||
result = Util.truncate(result, truncate, "...");
|
||||
result = Util.truncate(result, truncate, "…");
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -51,11 +51,15 @@ export default class Link extends Model {
|
|||
};
|
||||
}
|
||||
|
||||
getToken() {
|
||||
return this.Token.toLowerCase().trim();
|
||||
}
|
||||
|
||||
url() {
|
||||
let token = this.Token.toLowerCase();
|
||||
let token = this.getToken();
|
||||
|
||||
if(!token) {
|
||||
token = "...";
|
||||
token = "…";
|
||||
}
|
||||
|
||||
if(this.hasSlug()) {
|
||||
|
@ -66,7 +70,7 @@ export default class Link extends Model {
|
|||
}
|
||||
|
||||
caption() {
|
||||
return `/s/${this.Token.toLowerCase()}`;
|
||||
return `/s/${this.getToken()}`;
|
||||
}
|
||||
|
||||
getId() {
|
||||
|
|
|
@ -53,6 +53,7 @@ export class Photo extends RestModel {
|
|||
Type: TypeImage,
|
||||
Favorite: false,
|
||||
Private: false,
|
||||
Analog: false,
|
||||
TakenAt: "",
|
||||
TakenAtLocal: "",
|
||||
TakenSrc: "",
|
||||
|
@ -127,7 +128,7 @@ export class Photo extends RestModel {
|
|||
let result = this.fileBase(this.FileName ? this.FileName : this.mainFile().Name);
|
||||
|
||||
if (truncate) {
|
||||
result = Util.truncate(result, truncate, "...");
|
||||
result = Util.truncate(result, truncate, "…");
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -102,6 +102,10 @@ export class Rest extends Model {
|
|||
updateLink(link) {
|
||||
let values = link.getValues(false);
|
||||
|
||||
if(link.Token) {
|
||||
values["Token"] = link.getToken();
|
||||
}
|
||||
|
||||
if(link.Password) {
|
||||
values["Password"] = link.Password;
|
||||
}
|
||||
|
|
|
@ -78,9 +78,10 @@
|
|||
|
||||
|
||||
<h2 class="py-2 subheading"><translate>Trademarks</translate></h2>
|
||||
<p class="body-1"><translate>PhotoPrism™ is a registered trademark of Michael Mayer. You may use it as required to
|
||||
describe our software, run your own server, for educational purposes, but not for offering commercial
|
||||
goods, products, or services without prior written permission. In other words, please ask.</translate></p>
|
||||
<p class="body-1">
|
||||
<translate>PhotoPrism™ is a registered trademark of Michael Mayer.</translate>
|
||||
<translate>You may use it as required to describe our software, run your own server, for educational purposes, but not for offering commercial goods, products, or services without prior written permission. In other words, please ask.</translate>
|
||||
</p>
|
||||
</v-container>
|
||||
|
||||
<p-about-footer></p-about-footer>
|
||||
|
|
|
@ -155,13 +155,13 @@
|
|||
<v-card-text class="pl-3 pr-3 pt-0 pb-3 p-album-desc"
|
||||
v-else-if="album.Type === 'album'">
|
||||
<div v-if="album.PhotoCount === 1" class="caption">
|
||||
<translate>Contains one photo.</translate>
|
||||
<translate>Contains one entry.</translate>
|
||||
</div>
|
||||
<div v-else-if="album.PhotoCount > 0" class="caption">
|
||||
<translate :translate-params="{n: album.PhotoCount}">Contains %{n} photos.</translate>
|
||||
<translate :translate-params="{n: album.PhotoCount}">Contains %{n} entries.</translate>
|
||||
</div>
|
||||
<button v-else @click.stop.prevent="$router.push({name: 'photos'})" class="caption">
|
||||
<translate>Add photos from search results by selecting them.</translate>
|
||||
<translate>Add photos or videos from search results by selecting them.</translate>
|
||||
</button>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
|
|
@ -35,12 +35,10 @@
|
|||
<v-card-title primary-title>
|
||||
<div>
|
||||
<h3 class="title mb-3">
|
||||
<translate key="nofolders">No files matched your search</translate>
|
||||
<translate>No files matched your search</translate>
|
||||
</h3>
|
||||
<div>
|
||||
<translate key="tryagain">Please re-index your originals if a file you expect is
|
||||
missing.
|
||||
</translate>
|
||||
<translate>Please re-index your originals if a file you expect is missing.</translate>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-title>
|
||||
|
@ -405,13 +403,17 @@
|
|||
this.breadcrumbs = this.getBreadcrumbs();
|
||||
|
||||
if (response.count === 0) {
|
||||
this.$notify.warn(this.$gettext('Directory is empty'));
|
||||
} else if (response.count === 1) {
|
||||
this.$notify.info(this.$gettext('One entry found'));
|
||||
this.$notify.warn(this.$gettext('Folder is empty'));
|
||||
} else if (response.files === 1) {
|
||||
this.$notify.info(this.$gettext('One file found'));
|
||||
} else if (response.files === 0 && response.folders === 1) {
|
||||
this.$notify.info(this.$gettext('One folder found'));
|
||||
} else if (response.files === 0 && response.folders > 1) {
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} folders found"), {n: response.folders}));
|
||||
} else if (response.files < this.files.limit) {
|
||||
this.$notify.info(response.count + this.$gettext(' entries found'));
|
||||
this.$notify.info(this.$gettextInterpolate(this.$gettext("Folder contains %{n} files"), {n: response.files}));
|
||||
} else {
|
||||
this.$notify.warn(this.$gettext('Too many files in folder, showing first') + ` ${response.files}`);
|
||||
this.$notify.warn(this.$gettextInterpolate(this.$gettext("Limit reached, showing first %{n} files"), {n: response.files}));
|
||||
}
|
||||
}).finally(() => {
|
||||
this.dirty = false;
|
||||
|
|
|
@ -3,11 +3,10 @@
|
|||
<v-form ref="form" class="p-photo-import" lazy-validation @submit.prevent="submit" dense>
|
||||
<v-container fluid>
|
||||
<p class="subheading">
|
||||
<span v-if="fileName">{{ $gettext('Importing') }} {{fileName}}...</span>
|
||||
<span v-else-if="busy">{{ $gettext('Importing files to originals...') }}</span>
|
||||
<span v-else-if="completed">{{ $gettext('Done.') }}</span>
|
||||
<span v-else-if="settings.import.move">{{ $gettext('Press button to start moving...') }}</span>
|
||||
<span v-else>{{ $gettext('Press button to start copying...') }}</span>
|
||||
<span v-if="fileName"><translate :translate-params="{name: fileName}">Importing %{name}…</translate></span>
|
||||
<span v-else-if="busy"><translate>Importing files to originals…</translate></span>
|
||||
<span v-else-if="completed"><translate>Done.</translate></span>
|
||||
<span v-else><translate>Press button to start importing…</translate></span>
|
||||
</p>
|
||||
|
||||
<v-autocomplete
|
||||
|
@ -209,7 +208,7 @@
|
|||
found = true;
|
||||
}
|
||||
|
||||
this.dirs.push({path: folders[i].Path, name: "/" + Util.truncate(folders[i].Path, 100, "...")});
|
||||
this.dirs.push({path: folders[i].Path, name: "/" + Util.truncate(folders[i].Path, 100, "…")});
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<v-form ref="form" class="p-photo-index" lazy-validation @submit.prevent="submit" dense>
|
||||
<v-container fluid>
|
||||
<p class="subheading">
|
||||
<span v-if="fileName">{{ action }} {{ fileName }}...</span>
|
||||
<span v-else-if="busy">{{ $gettext('Indexing photos and sidecar files...') }}</span>
|
||||
<span v-else-if="completed">{{ $gettext('Done.') }}</span>
|
||||
<span v-else>{{ $gettext('Press button to start indexing...') }}</span>
|
||||
<span v-if="fileName">{{ action }} {{ fileName }}…</span>
|
||||
<span v-else-if="busy"><translate>Indexing media and sidecar files…</translate></span>
|
||||
<span v-else-if="completed"><translate>Done.</translate></span>
|
||||
<span v-else><translate>Press button to start indexing…</translate></span>
|
||||
</p>
|
||||
|
||||
<v-autocomplete
|
||||
|
@ -173,19 +173,19 @@
|
|||
|
||||
switch (type) {
|
||||
case "indexing":
|
||||
this.action = "Indexing";
|
||||
this.action = this.$gettext("Indexing");
|
||||
this.busy = true;
|
||||
this.completed = 0;
|
||||
this.fileName = data.fileName;
|
||||
break;
|
||||
case "converting":
|
||||
this.action = "Converting";
|
||||
this.action = this.$gettext("Converting");
|
||||
this.busy = true;
|
||||
this.completed = 0;
|
||||
this.fileName = data.fileName;
|
||||
break;
|
||||
case "thumbnails":
|
||||
this.action = "Creating thumbnails for";
|
||||
this.action = this.$gettext("Creating thumbnails for");
|
||||
this.busy = true;
|
||||
this.completed = 0;
|
||||
this.fileName = data.fileName;
|
||||
|
@ -217,7 +217,7 @@
|
|||
found = true;
|
||||
}
|
||||
|
||||
this.dirs.push({path: folders[i].Path, name: "/" + Util.truncate(folders[i].Path, 100, "...")});
|
||||
this.dirs.push({path: folders[i].Path, name: "/" + Util.truncate(folders[i].Path, 100, "…")});
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
v-model="settings.features.private"
|
||||
color="secondary-dark"
|
||||
:label="$gettext('Hide Private')"
|
||||
:hint="$gettext('Exclude photos marked as private from search results, shared albums, labels and places.')"
|
||||
:hint="$gettext('Exclude content marked as private from search results, shared albums, labels and places.')"
|
||||
prepend-icon="lock"
|
||||
persistent-hint
|
||||
>
|
||||
|
@ -374,7 +374,7 @@
|
|||
|
||||
this.settings.save().then((s) => {
|
||||
if (reload) {
|
||||
this.$notify.info(this.$gettext("Reloading..."));
|
||||
this.$notify.info(this.$gettext("Reloading…"));
|
||||
this.$notify.blockUI();
|
||||
setTimeout(() => window.location.reload(), 100);
|
||||
} else {
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
@ -136,6 +136,13 @@ export default [
|
|||
component: AlbumPhotos,
|
||||
meta: {title: $gettext("Folders"), auth: true},
|
||||
},
|
||||
{
|
||||
name: "unsorted",
|
||||
path: "/unsorted",
|
||||
component: Photos,
|
||||
meta: {title: $gettext("Unsorted"), auth: true},
|
||||
props: {staticFilter: {unsorted: true}},
|
||||
},
|
||||
{
|
||||
name: "favorites",
|
||||
path: "/favorites",
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
this.expanded = false;
|
||||
},
|
||||
onDownload(path) {
|
||||
Notify.success(this.$gettext("Downloading..."));
|
||||
Notify.success(this.$gettext("Downloading…"));
|
||||
const link = document.createElement('a')
|
||||
link.href = path;
|
||||
link.download = "album.zip";
|
||||
|
|
|
@ -3,15 +3,12 @@
|
|||
<v-card v-if="photos.length === 0" class="p-photos-empty secondary-light lighten-1 ma-1" flat>
|
||||
<v-card-title primary-title>
|
||||
<div>
|
||||
<h3 class="title mb-3">
|
||||
<h3 class="title ma-0 pa-0">
|
||||
<translate>No photos or videos found</translate>
|
||||
</h3>
|
||||
<div>
|
||||
<p class="mt-4 mb-0 pa-0">
|
||||
<translate>Try using other terms and search options such as category, country and camera.</translate>
|
||||
<span v-show="$config.feature('review')">
|
||||
<translate>Non-photographic and low-quality images require a review before they appear in search results.</translate>
|
||||
</span>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
this.expanded = false;
|
||||
},
|
||||
onDownload(path) {
|
||||
Notify.success(this.$gettext("Downloading..."));
|
||||
Notify.success(this.$gettext("Downloading…"));
|
||||
const link = document.createElement('a')
|
||||
link.href = path;
|
||||
link.download = "photos.zip";
|
||||
|
|
|
@ -3,15 +3,12 @@
|
|||
<v-card v-if="photos.length === 0" class="p-photos-empty secondary-light lighten-1 ma-1" flat>
|
||||
<v-card-title primary-title>
|
||||
<div>
|
||||
<h3 class="title mb-3">
|
||||
<h3 class="title ma-0 pa-0">
|
||||
<translate>No photos or videos found</translate>
|
||||
</h3>
|
||||
<div>
|
||||
<p class="mt-4 mb-0 pa-0">
|
||||
<translate>Try using other terms and search options such as category, country and camera.</translate>
|
||||
<span v-show="$config.feature('review')">
|
||||
<translate>Non-photographic and low-quality images require a review before they appear in search results.</translate>
|
||||
</span>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</v-card-title>
|
||||
</v-card>
|
||||
|
|
|
@ -26,7 +26,7 @@ describe("model/file", () => {
|
|||
const result = file.baseName();
|
||||
assert.equal(result, "IMG123.jpg");
|
||||
const result2 = file.baseName(8);
|
||||
assert.equal(result2, "IMG12...");
|
||||
assert.equal(result2, "IMG123.…");
|
||||
});
|
||||
|
||||
it("should return true", () => {
|
||||
|
@ -336,4 +336,4 @@ describe("model/file", () => {
|
|||
assert.equal(result, "File");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -70,7 +70,7 @@ describe("model/folder", () => {
|
|||
const result = folder.baseName();
|
||||
assert.equal(result, "10-Halloween");
|
||||
const result2 = folder.baseName(8);
|
||||
assert.equal(result2, "10-Ha...");
|
||||
assert.equal(result2, "10-Hall…");
|
||||
});
|
||||
|
||||
it("should return false", () => {
|
||||
|
@ -244,4 +244,4 @@ describe("model/folder", () => {
|
|||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,7 +31,7 @@ describe("model/link", () => {
|
|||
const values2 = {UID: 5, Token: "", Share: "family"};
|
||||
const link2 = new Link(values2);
|
||||
const result2 = link2.url();
|
||||
assert.equal(result2, "http://localhost:9876/s/.../family");
|
||||
assert.equal(result2, "http://localhost:9876/s/…/family");
|
||||
});
|
||||
|
||||
it("should get link caption", () => {
|
||||
|
@ -98,4 +98,4 @@ describe("model/link", () => {
|
|||
assert.equal(result, "Link");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -285,7 +285,7 @@ describe("model/photo", () => {
|
|||
const result = photo.baseName();
|
||||
assert.equal(result, "superCuteKitten.jpg");
|
||||
const result2 = photo.baseName(5);
|
||||
assert.equal(result2, "su...");
|
||||
assert.equal(result2, "supe…");
|
||||
});
|
||||
|
||||
it("should refresh file attributes", () => {
|
||||
|
|
|
@ -50,6 +50,7 @@ type Photo struct {
|
|||
OriginalName string `gorm:"type:varbinary(768);" json:"OriginalName" yaml:"OriginalName,omitempty"`
|
||||
PhotoFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"`
|
||||
PhotoPrivate bool `json:"Private" yaml:"Private,omitempty"`
|
||||
PhotoAnalog bool `json:"Analog" yaml:"Analog,omitempty"`
|
||||
TimeZone string `gorm:"type:varbinary(64);" json:"TimeZone" yaml:"-"`
|
||||
PlaceID string `gorm:"type:varbinary(42);index;" json:"PlaceID" yaml:"-"`
|
||||
LocationID string `gorm:"type:varbinary(42);index;" json:"LocationID" yaml:"-"`
|
||||
|
|
|
@ -19,6 +19,7 @@ type PhotoSearch struct {
|
|||
Primary bool `form:"primary"`
|
||||
Video bool `form:"video"`
|
||||
Photo bool `form:"photo"`
|
||||
Analog bool `form:"analog"`
|
||||
Duplicate bool `form:"duplicate"`
|
||||
Error bool `form:"error"`
|
||||
Hidden bool `form:"hidden"`
|
||||
|
@ -26,6 +27,7 @@ type PhotoSearch struct {
|
|||
Public bool `form:"public"`
|
||||
Private bool `form:"private"`
|
||||
Favorite bool `form:"favorite"`
|
||||
Unsorted bool `form:"unsorted"`
|
||||
Lat float32 `form:"lat"`
|
||||
Lng float32 `form:"lng"`
|
||||
Dist uint `form:"dist"`
|
||||
|
|
|
@ -41,6 +41,7 @@ type PhotoResult struct {
|
|||
PhotoExposure string `json:"Exposure"`
|
||||
PhotoQuality int `json:"Quality"`
|
||||
PhotoResolution int `json:"Resolution"`
|
||||
PhotoAnalog bool `json:"Analog"`
|
||||
CameraID uint `json:"CameraID"` // Camera
|
||||
CameraSerial string `json:"CameraSerial"`
|
||||
CameraSrc string `json:"CameraSrc"`
|
||||
|
|
|
@ -186,6 +186,10 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error
|
|||
s = s.Where("photos.photo_favorite = 1")
|
||||
}
|
||||
|
||||
if f.Analog {
|
||||
s = s.Where("photos.photo_analog = 1")
|
||||
}
|
||||
|
||||
if f.Country != "" {
|
||||
s = s.Where("photos.photo_country IN (?)", strings.Split(strings.ToLower(f.Country), ","))
|
||||
}
|
||||
|
@ -303,6 +307,8 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error
|
|||
} else {
|
||||
s = s.Joins("JOIN photos_albums ON photos_albums.photo_uid = photos.photo_uid").Where("photos_albums.hidden = 0 AND photos_albums.album_uid = ?", f.Album)
|
||||
}
|
||||
} else if f.Unsorted && f.Filter == "" {
|
||||
s = s.Where("photos.photo_uid NOT IN (SELECT photo_uid FROM photos_albums pa WHERE pa.hidden = 0)")
|
||||
}
|
||||
|
||||
// Set sort order for results.
|
||||
|
|
Loading…
Reference in a new issue