Search: Improve handling of search query updates in UI components #1995

This commit is contained in:
Michael Mayer 2022-04-16 15:59:47 +02:00
parent ac9fc4108b
commit adb40433a5
18 changed files with 436 additions and 243 deletions

View file

@ -1,7 +1,7 @@
<template>
<v-form ref="form" lazy-validation
dense autocomplete="off" class="p-photo-toolbar p-album-toolbar" accept-charset="UTF-8"
@submit.prevent="updateQuery">
@submit.prevent="updateQuery()">
<v-toolbar flat :dense="$vuetify.breakpoint.smAndDown" class="page-toolbar" color="secondary">
<v-toolbar-title :title="album.Title">
{{ album.Title }}
@ -9,7 +9,7 @@
<v-spacer></v-spacer>
<v-btn icon class="hidden-xs-only action-reload" :title="$gettext('Reload')" @click.stop="refresh">
<v-btn icon class="hidden-xs-only action-reload" :title="$gettext('Reload')" @click.stop="refresh()">
<v-icon>refresh</v-icon>
</v-btn>
@ -23,7 +23,7 @@
</v-btn>
<v-btn v-if="$config.feature('download')" icon class="hidden-xs-only action-download" :title="$gettext('Download')"
@click.stop="download">
@click.stop="download()">
<v-icon>get_app</v-icon>
</v-btn>
@ -83,12 +83,22 @@ export default {
type: Object,
default: () => {},
},
updateFilter: {
type: Function,
default: () => {},
},
updateQuery: {
type: Function,
default: () => {},
},
settings: {
type: Object,
default: () => {},
},
refresh: Function,
filterChange: Function,
refresh: {
type: Function,
default: () => {},
},
},
data() {
const cameras = [{
@ -149,24 +159,10 @@ export default {
this.album.update();
}
},
dropdownChange() {
this.updateQuery();
if (window.innerWidth < 600) {
this.searchExpanded = false;
}
if (this.filter.order !== this.album.Order) {
this.album.Order = this.filter.order;
this.updateAlbum();
}
},
setView(name) {
this.settings.view = name;
this.updateQuery();
},
updateQuery() {
this.filterChange();
if (name) {
this.refresh({'view': name});
}
},
download() {
this.onDownload(`${this.$config.apiUri}/albums/${this.album.UID}/dl?t=${this.$config.downloadToken()}`);

View file

@ -207,21 +207,15 @@ export default {
},
openPhoto: {
type: Function,
default: () => () => {
console.warn('cards view: openPhoto is undefined');
},
default: () => {},
},
editPhoto: {
type: Function,
default: () => () => {
console.warn('cards view: editPhoto is undefined');
},
default: () => {},
},
openLocation: {
type: Function,
default: () => () => {
console.warn('cards view: openLocation is undefined');
},
default: () => {},
},
album: {
type: Object,

View file

@ -167,12 +167,18 @@ export default {
type: Array,
default: () => [],
},
refresh: Function,
refresh: {
type: Function,
default: () => {},
},
album: {
type: Object,
default: () => {},
},
context: String,
context: {
type: String,
default: '',
},
},
data() {
return {

View file

@ -122,21 +122,15 @@ export default {
},
openPhoto: {
type: Function,
default: () => () => {
console.warn('list view: openPhoto is undefined');
},
default: () => {},
},
editPhoto: {
type: Function,
default: () => () => {
console.warn('list view: editPhoto is undefined');
},
default: () => {},
},
openLocation: {
type: Function,
default: () => () => {
console.warn('list view: openLocation is undefined');
},
default:() => {},
},
album: {
type: Object,

View file

@ -128,15 +128,11 @@ export default {
},
openPhoto: {
type: Function,
default: () => () => {
console.warn('mosaic view: openPhoto is undefined');
},
default:() => {},
},
editPhoto: {
type: Function,
default: () => () => {
console.warn('mosaic view: editPhoto is undefined');
},
default: () => {},
},
album: {
type: Object,

View file

@ -1,23 +1,24 @@
<template>
<v-form ref="form" lazy-validation
dense autocomplete="off" class="p-photo-toolbar" accept-charset="UTF-8"
@submit.prevent="updateQuery">
@submit.prevent="updateQuery()">
<v-toolbar flat :dense="$vuetify.breakpoint.smAndDown" class="page-toolbar" color="secondary">
<v-text-field :value="filter.q"
class="input-search background-inherit elevation-0"
solo hide-details clearable overflow single-line validate-on-blur
solo hide-details clearable overflow single-line
validate-on-blur
autocorrect="off"
autocapitalize="none"
browser-autocomplete="off"
:label="$gettext('Search')"
prepend-inner-icon="search"
color="secondary-dark"
@input="onChangeQuery"
@keyup.enter.native="updateQuery"
@click:clear="clearQuery"
@change="(v) => {updateFilter({'q': v})}"
@keyup.enter.native="refresh()"
@click:clear="() => {updateQuery({'q': ''})}"
></v-text-field>
<v-btn icon class="hidden-xs-only action-reload" :title="$gettext('Reload')" @click.stop="refresh">
<v-btn icon class="hidden-xs-only action-reload" :title="$gettext('Reload')" @click.stop="refresh()">
<v-icon>refresh</v-icon>
</v-btn>
@ -49,7 +50,7 @@
<v-card-text>
<v-layout row wrap>
<v-flex xs12 sm6 md3 pa-2 class="p-countries-select">
<v-select v-model="filter.country"
<v-select :value="filter.country"
:label="$gettext('Country')"
flat solo hide-details
color="secondary-dark"
@ -57,60 +58,60 @@
item-text="Name"
:items="countryOptions"
class="input-countries"
@change="dropdownChange"
@change="(v) => {updateQuery({'country': v})}"
>
</v-select>
</v-flex>
<v-flex xs12 sm6 md3 pa-2 class="p-camera-select">
<v-select v-model="filter.camera"
<v-select :value="filter.camera"
:label="$gettext('Camera')"
flat solo hide-details
color="secondary-dark"
item-value="ID"
item-text="Name"
:items="cameraOptions"
@change="dropdownChange">
@change="(v) => {updateQuery({'camera': v})}">
</v-select>
</v-flex>
<v-flex xs12 sm6 md3 pa-2 class="p-view-select">
<v-select id="viewSelect"
v-model="settings.view"
:value="settings.view"
:label="$gettext('View')" flat solo
hide-details
color="secondary-dark"
:items="options.views"
@change="dropdownChange">
@change="(v) => {setView(v)}">
</v-select>
</v-flex>
<v-flex xs12 sm6 md3 pa-2 class="p-time-select">
<v-select v-model="filter.order"
<v-select :value="filter.order"
:label="$gettext('Sort Order')"
flat solo hide-details
color="secondary-dark"
:items="options.sorting"
@change="dropdownChange">
@change="(v) => {updateQuery({'order': v})}">
</v-select>
</v-flex>
<v-flex xs12 sm6 md3 pa-2 class="p-year-select">
<v-select v-model="filter.year"
<v-select :value="filter.year"
:label="$gettext('Year')"
flat solo hide-details
color="secondary-dark"
item-value="value"
item-text="text"
:items="yearOptions()"
@change="dropdownChange">
@change="(v) => {updateQuery({'year': v})}">
</v-select>
</v-flex>
<v-flex xs12 sm6 md3 pa-2 class="p-month-select">
<v-select v-model="filter.month"
<v-select :value="filter.month"
:label="$gettext('Month')"
flat solo hide-details
color="secondary-dark"
item-value="value"
item-text="text"
:items="monthOptions()"
@change="dropdownChange">
@change="(v) => {updateQuery({'month': v})}">
</v-select>
</v-flex>
<!-- v-flex xs12 sm6 md3 pa-2 class="p-lens-select">
@ -125,25 +126,25 @@
</v-select>
</v-flex -->
<v-flex xs12 sm6 md3 pa-2 class="p-color-select">
<v-select v-model="filter.color"
<v-select :value="filter.color"
:label="$gettext('Color')"
flat solo hide-details
color="secondary-dark"
item-value="Slug"
item-text="Name"
:items="colorOptions()"
@change="dropdownChange">
@change="(v) => {updateQuery({'color': v})}">
</v-select>
</v-flex>
<v-flex xs12 sm6 md3 pa-2 class="p-category-select">
<v-select v-model="filter.label"
<v-select :value="filter.label"
:label="$gettext('Category')"
flat solo hide-details
color="secondary-dark"
item-value="Slug"
item-text="Name"
:items="categoryOptions"
@change="dropdownChange">
@change="(v) => {updateQuery({'label': v})}">
</v-select>
</v-flex>
</v-layout>
@ -158,17 +159,26 @@ import * as options from "options/options";
export default {
name: 'PPhotoToolbar',
props: {
dirty: Boolean,
filter: {
type: Object,
default: () => {},
},
updateFilter: {
type: Function,
default: () => {},
},
updateQuery: {
type: Function,
default: () => {},
},
settings: {
type: Object,
default: () => {},
},
refresh: Function,
filterChange: Function,
refresh: {
type: Function,
default: () => {},
},
},
data() {
return {
@ -176,7 +186,6 @@ export default {
isFullScreen: !!document.fullscreenElement,
config: this.$config.values,
searchExpanded: false,
q: this.filter.q ? this.filter.q : '',
all: {
countries: [{ID: "", Name: this.$gettext("All Countries")}],
cameras: [{ID: 0, Name: this.$gettext("All Cameras")}],
@ -228,27 +237,10 @@ export default {
yearOptions() {
return this.all.years.concat(options.IndexedYears());
},
dropdownChange() {
this.updateQuery();
if (window.innerWidth < 600) {
this.searchExpanded = false;
}
},
setView(name) {
this.settings.view = name;
this.updateQuery();
},
onChangeQuery(val) {
this.q = val ? String(val) : '';
},
clearQuery() {
this.q = '';
this.updateQuery();
},
updateQuery() {
this.filter.q = this.q.trim();
this.filterChange();
if (name) {
this.refresh({'view': name});
}
},
showUpload() {
Event.publish("dialog.upload");

View file

@ -54,15 +54,11 @@ export default {
},
success: {
type: Function,
default() {
return false;
}
default: () => {},
},
error: {
type: Function,
default() {
return false;
}
default: () => {},
}
},
data: () => ({

View file

@ -2,8 +2,8 @@
<div v-infinite-scroll="loadMore" class="p-page p-page-album-photos" :infinite-scroll-disabled="scrollDisabled"
:infinite-scroll-distance="scrollDistance" :infinite-scroll-listen-for-event="'scrollRefresh'">
<p-album-toolbar :album="model" :settings="settings" :filter="filter" :filter-change="updateQuery"
:refresh="refresh"></p-album-toolbar>
<p-album-toolbar :filter="filter" :album="model" :settings="settings" :refresh="refresh"
:update-filter="updateFilter" :update-query="updateQuery"></p-album-toolbar>
<v-container v-if="loading" fluid class="pa-4">
<v-progress-linear color="secondary-dark" :indeterminate="true"></v-progress-linear>
@ -55,7 +55,10 @@ import Viewer from "common/viewer";
export default {
name: 'PPageAlbumPhotos',
props: {
staticFilter: Object
staticFilter: {
type: Object,
default: () => {},
},
},
data() {
const uid = this.$route.params.uid;
@ -261,8 +264,51 @@ export default {
this.listen = true;
});
},
updateQuery() {
this.filter.q = this.filter.q.trim();
updateSettings(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.settings.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.settings[key] = value.trim();
break;
default:
this.settings[key] = value;
}
}
},
updateFilter(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.filter.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.filter[key] = value.trim();
break;
default:
this.filter[key] = value;
}
}
},
updateQuery(props) {
this.updateFilter(props);
if (this.model.Order !== this.filter.order) {
this.model.Order = this.filter.order;
this.updateAlbum();
}
if (this.loading) return;
const query = {
view: this.settings.view
@ -299,10 +345,10 @@ export default {
return params;
},
refresh() {
if (this.loading) {
return;
}
refresh(props) {
this.updateSettings(props);
if (this.loading) return;
this.loading = true;
this.page = 0;

View file

@ -3,7 +3,7 @@
:infinite-scroll-disabled="scrollDisabled" :infinite-scroll-distance="scrollDistance"
:infinite-scroll-listen-for-event="'scrollRefresh'">
<v-form ref="form" class="p-albums-search" lazy-validation dense @submit.prevent="updateQuery">
<v-form ref="form" class="p-albums-search" lazy-validation dense @submit.prevent="updateQuery()">
<v-toolbar flat :dense="$vuetify.breakpoint.smAndDown" class="page-toolbar" color="secondary">
<v-text-field :value="filter.q"
solo hide-details clearable overflow single-line validate-on-blur
@ -14,12 +14,12 @@
autocapitalize="none"
prepend-inner-icon="search"
color="secondary-dark"
@input="onChangeQuery"
@keyup.enter.native="updateQuery"
@click:clear="clearQuery"
@change="(v) => {updateFilter({'q': v})}"
@keyup.enter.native="refresh()"
@click:clear="() => {updateQuery({'q': ''})}"
></v-text-field>
<v-overflow-btn v-model="filter.category"
<v-overflow-btn :value="filter.category"
solo hide-details single-line
:label="$gettext('Category')"
color="secondary-dark"
@ -28,11 +28,11 @@
append-icon=""
:items="categories"
class="hidden-xs-only input-category background-inherit elevation-0"
@change="updateQuery"
@change="(v) => {updateFilter({'category': v})}"
>
</v-overflow-btn>
<v-btn icon class="action-reload" :title="$gettext('Reload')" @click.stop="refresh">
<v-btn icon class="action-reload" :title="$gettext('Reload')" @click.stop="refresh()">
<v-icon>refresh</v-icon>
</v-btn>
@ -42,7 +42,7 @@
</v-btn>
<v-btn v-if="staticFilter.type === 'album'" icon class="action-add" :title="$gettext('Add Album')"
@click.prevent="create">
@click.prevent="create()">
<v-icon>add</v-icon>
</v-btn>
</v-toolbar>
@ -218,8 +218,14 @@ import {Input, InputInvalid, ClickShort, ClickLong} from "common/input";
export default {
name: 'PPageAlbums',
props: {
staticFilter: Object,
view: String,
staticFilter: {
type: Object,
default: () => {},
},
view: {
type: String,
default: "",
},
},
data() {
const query = this.$route.query;
@ -483,15 +489,44 @@ export default {
this.listen = true;
});
},
onChangeQuery(val) {
this.q = val ? String(val) : '';
updateSettings(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.settings.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.settings[key] = value.trim();
break;
default:
this.settings[key] = value;
}
}
},
clearQuery() {
this.q = '';
this.updateQuery();
updateFilter(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.filter.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.filter[key] = value.trim();
break;
default:
this.filter[key] = value;
}
}
},
updateQuery() {
this.filter.q = this.q.trim();
updateQuery(props) {
this.updateFilter(props);
if (this.loading) return;
@ -574,8 +609,11 @@ export default {
this.listen = true;
});
},
refresh() {
refresh(props) {
this.updateSettings(props);
if (this.loading) return;
this.loading = true;
this.page = 0;
this.dirty = true;

View file

@ -3,7 +3,7 @@
:infinite-scroll-disabled="scrollDisabled" :infinite-scroll-distance="scrollDistance"
:infinite-scroll-listen-for-event="'scrollRefresh'">
<v-form ref="form" class="p-labels-search" lazy-validation dense @submit.stop.prevent>
<v-form ref="form" class="p-labels-search" lazy-validation dense @submit.stop.prevent="updateQuery()">
<v-toolbar flat :dense="$vuetify.breakpoint.smAndDown" class="page-toolbar" color="secondary">
<v-text-field :value="filter.q"
solo hide-details clearable overflow single-line validate-on-blur
@ -14,19 +14,19 @@
autocorrect="off"
autocapitalize="none"
color="secondary-dark"
@input="onChangeQuery"
@keyup.enter.native="updateQuery"
@click:clear="clearQuery"
@change="(v) => {updateFilter({'q': v})}"
@keyup.enter.native="refresh()"
@click:clear="() => {updateQuery({'q': ''})}"
></v-text-field>
<v-btn icon class="action-reload" :title="$gettext('Reload')" @click.stop="refresh">
<v-btn icon class="action-reload" :title="$gettext('Reload')" @click.stop="refresh()">
<v-icon>refresh</v-icon>
</v-btn>
<v-btn v-if="!filter.all" icon class="action-show-all" :title="$gettext('Show more')" @click.stop="showAll">
<v-btn v-if="!filter.all" icon class="action-show-all" :title="$gettext('Show more')" @click.stop="showAll()">
<v-icon>unfold_more</v-icon>
</v-btn>
<v-btn v-else icon class="action-show-important" :title="$gettext('Show less')" @click.stop="showImportant">
<v-btn v-else icon class="action-show-important" :title="$gettext('Show less')" @click.stop="showImportant()">
<v-icon>unfold_less</v-icon>
</v-btn>
</v-toolbar>
@ -167,8 +167,6 @@ export default {
const routeName = this.$route.name;
const q = query['q'] ? query['q'] : '';
const all = query['all'] ? query['all'] : '';
const filter = {"q": String(q), all: String(all)};
const settings = {};
return {
view: 'all',
@ -184,9 +182,8 @@ export default {
offset: 0,
page: 0,
selection: [],
settings: settings,
q: q,
filter: filter,
settings: {},
filter: {q, all},
lastFilter: {},
routeName: routeName,
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Name too long"),
@ -200,8 +197,7 @@ export default {
this.routeName = this.$route.name;
this.lastFilter = {};
this.q = query['q'] ? query['q'] : '';
this.filter.q = this.q;
this.filter.q = query['q'] ? query['q'] : '';
this.filter.all = query['all'] ? query['all'] : '';
this.search();
@ -368,7 +364,7 @@ export default {
this.lastId = "";
},
loadMore() {
if (this.scrollDisabled) return;
if (this.scrollDisabled || this.$scrollbar.disabled()) return;
this.scrollDisabled = true;
this.listen = false;
@ -387,8 +383,12 @@ export default {
Object.assign(params, this.staticFilter);
}
if (offset === 0) {
this.results = [];
}
Label.search(params).then(resp => {
this.results = this.dirty ? resp.models : this.results.concat(resp.models);
this.results = (offset === 0) ? resp.models : this.results.concat(resp.models);
this.scrollDisabled = (resp.count < resp.limit);
@ -415,15 +415,44 @@ export default {
this.listen = true;
});
},
onChangeQuery(val) {
this.q = val ? String(val) : '';
updateSettings(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.settings.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.settings[key] = value.trim();
break;
default:
this.settings[key] = value;
}
}
},
clearQuery() {
this.q = '';
this.updateQuery();
updateFilter(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.filter.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.filter[key] = value.trim();
break;
default:
this.filter[key] = value;
}
}
},
updateQuery() {
this.filter.q = this.q.trim();
updateQuery(props) {
this.updateFilter(props);
if (this.loading) return;
@ -459,12 +488,16 @@ export default {
return params;
},
refresh() {
refresh(props) {
this.updateSettings(props);
if (this.loading) return;
this.loading = true;
this.page = 0;
this.dirty = true;
this.scrollDisabled = false;
this.loadMore();
},
search() {

View file

@ -11,15 +11,15 @@
:label="$gettext('Search')"
prepend-inner-icon="search"
color="secondary-dark"
@input="onChangeQuery"
@keyup.enter.native="updateQuery"
@click:clear="clearQuery"
@change="(v) => {updateFilter({'q': v})}"
@keyup.enter.native="updateQuery()"
@click:clear="() => {updateQuery({'q': ''})}"
></v-text-field>
<v-spacer></v-spacer>
<v-btn icon class="action-reload" :title="$gettext('Reload')" @click.stop="onReload">
<v-btn icon class="action-reload" :title="$gettext('Reload')" @click.stop="onReload()">
<v-icon>refresh</v-icon>
</v-btn>
<v-btn v-if="!isPublic" icon class="action-delete" :title="$gettext('Delete')" @click.stop="onDelete">
<v-btn v-if="!isPublic" icon class="action-delete" :title="$gettext('Delete')" @click.stop="onDelete()">
<v-icon>delete</v-icon>
</v-btn>
<v-btn icon href="https://docs.photoprism.app/getting-started/troubleshooting/" target="_blank" class="action-bug-report"
@ -108,7 +108,6 @@ export default {
loading: false,
scrollDisabled: false,
scrollDistance: window.innerHeight*2,
q: q,
filter: {q},
isPublic: this.$config.get("public"),
batchSize: 100,
@ -127,10 +126,7 @@ export default {
watch: {
'$route'() {
const query = this.$route.query;
this.q = query['q'] ? query['q'] : '';
this.filter.q = this.q;
this.filter.q = query['q'] ? query['q'] : '';
this.onReload();
}
},
@ -138,15 +134,26 @@ export default {
this.loadMore();
},
methods: {
onChangeQuery(val) {
this.q = val ? String(val) : '';
updateFilter(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.filter.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.filter[key] = value.trim();
break;
default:
this.filter[key] = value;
}
}
},
clearQuery() {
this.q = '';
this.updateQuery();
},
updateQuery() {
this.filter.q = this.q.trim();
updateQuery(props) {
this.updateFilter(props);
if (this.loading) return;

View file

@ -3,7 +3,7 @@
:infinite-scroll-disabled="scrollDisabled" :infinite-scroll-distance="scrollDistance"
:infinite-scroll-listen-for-event="'scrollRefresh'">
<v-form ref="form" class="p-people-search" lazy-validation dense @submit.prevent="updateQuery">
<v-form ref="form" class="p-people-search" lazy-validation dense @submit.prevent="updateQuery()">
<v-toolbar dense flat class="page-toolbar" color="secondary-light pa-0">
<v-text-field :value="filter.q"
solo hide-details clearable overflow single-line validate-on-blur
@ -14,21 +14,21 @@
autocorrect="off"
autocapitalize="none"
color="secondary-dark"
@input="onChangeQuery"
@keyup.enter.native="updateQuery"
@click:clear="clearQuery"
@change="(v) => {updateFilter({'q': v})}"
@keyup.enter.native="refresh()"
@click:clear="() => {updateQuery({'q': ''})}"
></v-text-field>
<v-divider vertical></v-divider>
<v-btn icon overflow flat depressed color="secondary-dark" class="action-reload" :title="$gettext('Reload')" @click.stop="refresh">
<v-btn icon overflow flat depressed color="secondary-dark" class="action-reload" :title="$gettext('Reload')" @click.stop="refresh()">
<v-icon>refresh</v-icon>
</v-btn>
<v-btn v-if="!filter.hidden" icon class="action-show-hidden" :title="$gettext('Show hidden')" @click.stop="onShowHidden">
<v-btn v-if="!filter.hidden" icon class="action-show-hidden" :title="$gettext('Show hidden')" @click.stop="onShowHidden()">
<v-icon>visibility</v-icon>
</v-btn>
<v-btn v-else icon class="action-exclude-hidden" :title="$gettext('Exclude hidden')" @click.stop="onExcludeHidden">
<v-btn v-else icon class="action-exclude-hidden" :title="$gettext('Exclude hidden')" @click.stop="onExcludeHidden()">
<v-icon>visibility_off</v-icon>
</v-btn>
</v-toolbar>
@ -193,8 +193,6 @@ export default {
const q = query['q'] ? query['q'] : '';
const hidden = query['hidden'] ? query['hidden'] : '';
const order = this.sortOrder();
const filter = {q, hidden, order};
const settings = {};
return {
view: 'all',
@ -210,9 +208,8 @@ export default {
offset: 0,
page: 0,
selection: [],
settings: settings,
q: q,
filter: filter,
settings: {},
filter: {q, hidden, order},
lastFilter: {},
routeName: routeName,
titleRule: v => v.length <= this.$config.get("clip") || this.$gettext("Name too long"),
@ -244,8 +241,7 @@ export default {
const query = this.$route.query;
this.routeName = this.$route.name;
this.q = query["q"] ? query["q"] : "";
this.filter.q = this.q;
this.filter.q = query["q"] ? query["q"] : "";
this.filter.hidden = query["hidden"] ? query["hidden"] : "";
this.filter.order = this.sortOrder();
@ -527,15 +523,44 @@ export default {
this.listen = true;
});
},
onChangeQuery(val) {
this.q = val ? String(val) : '';
updateSettings(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.settings.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.settings[key] = value.trim();
break;
default:
this.settings[key] = value;
}
}
},
clearQuery() {
this.q = '';
this.updateQuery();
updateFilter(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.filter.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.filter[key] = value.trim();
break;
default:
this.filter[key] = value;
}
}
},
updateQuery() {
this.filter.q = this.q.trim();
updateQuery(props) {
this.updateFilter(props);
if (this.loading || !this.active) {
return;
@ -573,10 +598,10 @@ export default {
return params;
},
refresh() {
if (this.loading || !this.active) {
return;
}
refresh(props) {
this.updateSettings(props);
if (this.loading || !this.active) return;
this.loading = true;
this.page = 0;

View file

@ -3,8 +3,8 @@
:infinite-scroll-disabled="scrollDisabled" :infinite-scroll-distance="scrollDistance"
:infinite-scroll-listen-for-event="'scrollRefresh'">
<p-photo-toolbar :settings="settings" :filter="filter" :filter-change="updateQuery" :dirty="dirty"
:refresh="refresh"></p-photo-toolbar>
<p-photo-toolbar :filter="filter" :settings="settings" :refresh="refresh"
:update-filter="updateFilter" :update-query="updateQuery"></p-photo-toolbar>
<v-container v-if="loading" fluid class="pa-4">
<v-progress-linear color="secondary-dark" :indeterminate="true"></v-progress-linear>
@ -42,7 +42,7 @@
</template>
<script>
import {Photo, MediaLive, MediaRaw, MediaVideo, MediaAnimated} from "model/photo";
import {MediaAnimated, MediaLive, MediaRaw, MediaVideo, Photo} from "model/photo";
import Thumb from "model/thumb";
import Viewer from "common/viewer";
import Event from "pubsub-js";
@ -50,7 +50,11 @@ import Event from "pubsub-js";
export default {
name: 'PPagePhotos',
props: {
staticFilter: Object
staticFilter: {
type: Object,
default: () => {
},
},
},
data() {
const query = this.$route.query;
@ -98,7 +102,7 @@ export default {
complete: false,
results: [],
scrollDisabled: true,
scrollDistance: window.innerHeight*2,
scrollDistance: window.innerHeight * 2,
batchSize: batchSize,
offset: 0,
page: 0,
@ -232,7 +236,7 @@ export default {
showMerged = false;
}
if (showMerged && selected.Type === MediaLive || selected.Type === MediaVideo|| selected.Type === MediaAnimated) {
if (showMerged && selected.Type === MediaLive || selected.Type === MediaVideo || selected.Type === MediaAnimated) {
if (selected.isPlayable()) {
this.$viewer.play({video: selected});
} else {
@ -303,10 +307,46 @@ export default {
this.listen = true;
});
},
updateQuery() {
if (this.loading) return;
updateSettings(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
this.filter.q = this.filter.q.trim();
for (const [key, value] of Object.entries(props)) {
if (!this.settings.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.settings[key] = value.trim();
break;
default:
this.settings[key] = value;
}
}
},
updateFilter(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.filter.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.filter[key] = value.trim();
break;
default:
this.filter[key] = value;
}
}
},
updateQuery(props) {
this.updateFilter(props);
if (this.loading) return;
const query = {
view: this.settings.view
@ -341,10 +381,10 @@ export default {
return params;
},
refresh() {
if (this.loading) {
return;
}
refresh(props) {
this.updateSettings(props);
if (this.loading) return;
this.loading = true;
this.page = 0;

View file

@ -231,11 +231,11 @@ export default {
this.search();
},
clearQuery() {
this.filter.q = "";
this.filter.q = '';
this.search();
},
updateQuery() {
this.filter.q = this.filter.q.trim();
if (this.loading) return;
if (this.query() !== this.filter.q) {
if (this.filter.q) {
@ -247,6 +247,7 @@ export default {
},
search() {
if (this.loading) return;
// Don't query the same data more than once
if (JSON.stringify(this.lastFilter) === JSON.stringify(this.filter)) return;
this.loading = true;

View file

@ -122,15 +122,16 @@ export default {
type: Object,
default: () => {},
},
view: String,
view: {
type: String,
default: "",
},
},
data() {
const query = this.$route.query;
const routeName = this.$route.name;
const q = query["q"] ? query["q"] : "";
const category = query["category"] ? query["category"] : "";
const filter = {q, category};
const settings = {};
let categories = [{"value": "", "text": this.$gettext("All Categories")}];
@ -154,8 +155,8 @@ export default {
offset: 0,
page: 0,
selection: [],
settings: settings,
filter: filter,
settings: {},
filter: {q, category},
lastFilter: {},
routeName: routeName,
titleRule: v => v.length <= this.$config.get('clip') || this.$gettext("Title too long"),

View file

@ -157,21 +157,15 @@ export default {
},
openPhoto: {
type: Function,
default: () => () => {
console.warn('cards view: openPhoto is undefined');
},
default: () => {},
},
editPhoto: {
type: Function,
default: () => () => {
console.warn('cards view: editPhoto is undefined');
},
default: () => {},
},
openLocation: {
type: Function,
default: () => () => {
console.warn('cards view: openLocation is undefined');
},
default: () => {},
},
album: {
type: Object,

View file

@ -104,21 +104,15 @@ export default {
},
openPhoto: {
type: Function,
default: () => () => {
console.warn('list view: openPhoto is undefined');
},
default: () => {},
},
editPhoto: {
type: Function,
default: () => () => {
console.warn('list view: editPhoto is undefined');
},
default: () => {},
},
openLocation: {
type: Function,
default: () => () => {
console.warn('list view: openLocation is undefined');
},
default: () => {},
},
album: {
type: Object,

View file

@ -3,8 +3,9 @@
:infinite-scroll-disabled="scrollDisabled" :infinite-scroll-distance="scrollDistance"
:infinite-scroll-listen-for-event="'scrollRefresh'">
<v-form ref="form" lazy-validation
dense autocomplete="off" class="p-photo-toolbar p-album-toolbar" accept-charset="UTF-8">
<v-form ref="form" lazy-validation dense
autocomplete="off" class="p-photo-toolbar p-album-toolbar"
accept-charset="UTF-8" @submit.prevent="updateQuery()">
<v-toolbar flat color="secondary" :dense="$vuetify.breakpoint.smAndDown">
<v-toolbar-title>
{{ model.Title }}
@ -12,12 +13,12 @@
<v-spacer></v-spacer>
<v-btn icon class="hidden-xs-only action-reload" @click.stop="refresh">
<v-btn icon class="hidden-xs-only action-reload" @click.stop="refresh()">
<v-icon>refresh</v-icon>
</v-btn>
<v-btn v-if="$config.feature('download')" icon class="hidden-xs-only action-download" :title="$gettext('Download')"
@click.stop="download">
@click.stop="download()">
<v-icon>get_app</v-icon>
</v-btn>
@ -98,7 +99,10 @@ import Viewer from "common/viewer";
export default {
name: 'PPageAlbumPhotos',
props: {
staticFilter: Object
staticFilter: {
type: Object,
default: () => {},
},
},
data() {
const uid = this.$route.params.uid;
@ -317,8 +321,44 @@ export default {
this.listen = true;
});
},
updateQuery() {
this.filter.q = this.filter.q.trim();
updateSettings(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.settings.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.settings[key] = value.trim();
break;
default:
this.settings[key] = value;
}
}
},
updateFilter(props) {
if (!props || typeof props !== "object" || props.target) {
return;
}
for (const [key, value] of Object.entries(props)) {
if (!this.filter.hasOwnProperty(key)) {
continue;
}
switch (typeof value) {
case "string":
this.filter[key] = value.trim();
break;
default:
this.filter[key] = value;
}
}
},
updateQuery(props) {
this.updateFilter(props);
const query = {
view: this.settings.view
@ -355,10 +395,10 @@ export default {
return params;
},
refresh() {
if (this.loading) {
return;
}
refresh(props) {
this.updateSettings(props);
if (this.loading) return;
this.loading = true;
this.page = 0;