diff --git a/src/common/config/public/ClientConfig.ts b/src/common/config/public/ClientConfig.ts index 57bf3f0d..f959c9ac 100644 --- a/src/common/config/public/ClientConfig.ts +++ b/src/common/config/public/ClientConfig.ts @@ -39,7 +39,7 @@ export class ClientSearchConfig { @SubConfigClass() export class ClientAlbumConfig { @ConfigProperty() - enabled: boolean = true; + enabled: boolean = false; } @SubConfigClass() diff --git a/src/frontend/app/app.module.ts b/src/frontend/app/app.module.ts index 4e55f0d8..d2d33406 100644 --- a/src/frontend/app/app.module.ts +++ b/src/frontend/app/app.module.ts @@ -103,6 +103,7 @@ import {icon, Marker} from 'leaflet'; import {AlbumsComponent} from './ui/albums/albums.component'; import {AlbumComponent} from './ui/albums/album/album.component'; import {AlbumsService} from './ui/albums/albums.service'; +import {GallerySearchQueryBuilderComponent} from './ui/gallery/search/query-builder/query-bulder.gallery.component'; @Injectable() @@ -196,6 +197,7 @@ Marker.prototype.options.icon = iconDefault; GallerySearchComponent, GallerySearchQueryEntryComponent, GallerySearchFieldComponent, + GallerySearchQueryBuilderComponent, GalleryShareComponent, GalleryNavigatorComponent, GalleryPhotoComponent, diff --git a/src/frontend/app/ui/albums/albums.component.css b/src/frontend/app/ui/albums/albums.component.css index 4bc91196..43f801a6 100644 --- a/src/frontend/app/ui/albums/albums.component.css +++ b/src/frontend/app/ui/albums/albums.component.css @@ -3,11 +3,27 @@ app-album { display: inline-block; } -.no-item-msg{ +.no-item-msg { height: 100vh; text-align: center; } -.no-face-msg h2{ +.no-face-msg h2 { color: #6c757d; } + +.add-saved-search { + vertical-align: baseline; + position: absolute; + margin: 2px; +} + +.add-saved-search .text { + position: relative; + top: calc(50% - 40px); + text-align: center; +} + +.add-saved-search .text .oi { + font-size: 80px; +} diff --git a/src/frontend/app/ui/albums/albums.component.html b/src/frontend/app/ui/albums/albums.component.html index 2f485587..ac6efa9f 100644 --- a/src/frontend/app/ui/albums/albums.component.html +++ b/src/frontend/app/ui/albums/albums.component.html @@ -2,15 +2,73 @@
+ [album]="album" + [size]="size"> + +
-

:( No albums to show. +

:( + No albums to show.

+ + + + + + + diff --git a/src/frontend/app/ui/albums/albums.component.ts b/src/frontend/app/ui/albums/albums.component.ts index 2b24ec85..28e7e6da 100644 --- a/src/frontend/app/ui/albums/albums.component.ts +++ b/src/frontend/app/ui/albums/albums.component.ts @@ -1,5 +1,8 @@ -import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import {Component, ElementRef, OnInit, TemplateRef, ViewChild} from '@angular/core'; import {AlbumsService} from './albums.service'; +import {BsModalService} from 'ngx-bootstrap/modal'; +import {BsModalRef} from 'ngx-bootstrap/modal/bs-modal-ref.service'; +import {SearchQueryTypes, TextSearch} from '../../../../common/entities/SearchQueryDTO'; @Component({ selector: 'app-albums', @@ -9,9 +12,14 @@ import {AlbumsService} from './albums.service'; export class AlbumsComponent implements OnInit { @ViewChild('container', {static: true}) container: ElementRef; public size: number; + public savedSearch = { + name: '', + searchQuery: {type: SearchQueryTypes.any_text, text: ''} as TextSearch + }; + private modalRef: BsModalRef; - - constructor(public albumsService: AlbumsService) { + constructor(public albumsService: AlbumsService, + private modalService: BsModalService) { this.albumsService.getAlbums().catch(console.error); } @@ -20,6 +28,22 @@ export class AlbumsComponent implements OnInit { this.updateSize(); } + + public async openModal(template: TemplateRef): Promise { + this.modalRef = this.modalService.show(template, {class: 'modal-lg'}); + document.body.style.paddingRight = '0px'; + } + + public hideModal(): void { + this.modalRef.hide(); + this.modalRef = null; + } + + async saveSearch(): Promise { + await this.albumsService.addSavedSearch(this.savedSearch.name, this.savedSearch.searchQuery); + this.hideModal(); + } + private updateSize(): void { const size = 220 + 5; // body - container margin diff --git a/src/frontend/app/ui/gallery/search/query-builder/query-builder.gallery.component.css b/src/frontend/app/ui/gallery/search/query-builder/query-builder.gallery.component.css new file mode 100644 index 00000000..f63a984a --- /dev/null +++ b/src/frontend/app/ui/gallery/search/query-builder/query-builder.gallery.component.css @@ -0,0 +1,69 @@ +.autocomplete-list { + position: absolute; + left: 0; + top: 34px; + background-color: white; + width: 100%; + border: 1px solid #ccc; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + padding: 5px 0; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + z-index: 7; +} + +.insert-button { + margin-right: -15px; + display: none; + margin-top: 2px; +} + +.autocomplete-item-selected .insert-button { + display: block; +} +@media (hover: none) { + .insert-button { + display: block; + } +} + +.autocomplete-item-selected .insert-button:hover { + color: black; +} + +.autocomplete-item { + cursor: pointer; + padding-top: 2px; + padding-bottom: 2px; + font-size: 17px; +} + +.autocomplete-item { + color: #333; + padding: 0 20px; + line-height: 1.42857143; + font-weight: 400; + display: block; +} + +.autocomplete-item-selected { + background-color: #007bff; + color: #FFF; +} + + +.search-text { + z-index: 6; + width: 100%; + background: transparent; +} + +.search-hint { + left: 0; + z-index: 1; + width: 100%; + position: absolute; + margin-left: 0 !important; +} + diff --git a/src/frontend/app/ui/gallery/search/query-builder/query-builder.gallery.component.html b/src/frontend/app/ui/gallery/search/query-builder/query-builder.gallery.component.html new file mode 100644 index 00000000..2976710a --- /dev/null +++ b/src/frontend/app/ui/gallery/search/query-builder/query-builder.gallery.component.html @@ -0,0 +1,15 @@ + + + +
+ + + diff --git a/src/frontend/app/ui/gallery/search/query-builder/query-bulder.gallery.component.ts b/src/frontend/app/ui/gallery/search/query-builder/query-bulder.gallery.component.ts new file mode 100644 index 00000000..24237a5b --- /dev/null +++ b/src/frontend/app/ui/gallery/search/query-builder/query-bulder.gallery.component.ts @@ -0,0 +1,82 @@ +import {Component, EventEmitter, forwardRef, Output} from '@angular/core'; +import {SearchQueryDTO, SearchQueryTypes, TextSearch} from '../../../../../../common/entities/SearchQueryDTO'; +import {ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator} from '@angular/forms'; +import {SearchQueryParserService} from '../search-query-parser.service'; + +@Component({ + selector: 'app-gallery-search-query-builder', + templateUrl: './query-builder.gallery.component.html', + styleUrls: ['./query-builder.gallery.component.css'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => GallerySearchQueryBuilderComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => GallerySearchQueryBuilderComponent), + multi: true + } + ] +}) +export class GallerySearchQueryBuilderComponent implements ControlValueAccessor, Validator { + public searchQueryDTO: SearchQueryDTO = {type: SearchQueryTypes.any_text, text: ''} as TextSearch; + @Output() search = new EventEmitter(); + public rawSearchText = ''; + + + constructor( + private searchQueryParserService: SearchQueryParserService) { + } + + + validateRawSearchText(): void { + try { + this.searchQueryDTO = this.searchQueryParserService.parse(this.rawSearchText); + this.onChange(); + } catch (e) { + console.error(e); + } + } + + + resetQuery(): void { + this.searchQueryDTO = ({text: '', type: SearchQueryTypes.any_text} as TextSearch); + } + + onQueryChange(): void { + this.rawSearchText = this.searchQueryParserService.stringify(this.searchQueryDTO); + this.onChange(); + } + + validate(control: FormControl): ValidationErrors { + return {required: true}; + } + + public onTouched(): void { + } + + public writeValue(obj: any): void { + this.searchQueryDTO = obj; + } + + registerOnChange(fn: (_: any) => void): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.propagateTouch = fn; + } + + public onChange(): void { + this.propagateChange(this.searchQueryDTO); + } + + + private propagateChange = (_: any): void => { + }; + + private propagateTouch = (_: any): void => { + }; +} diff --git a/src/frontend/app/ui/gallery/search/search.gallery.component.html b/src/frontend/app/ui/gallery/search/search.gallery.component.html index 7643a825..92257f0a 100644 --- a/src/frontend/app/ui/gallery/search/search.gallery.component.html +++ b/src/frontend/app/ui/gallery/search/search.gallery.component.html @@ -36,24 +36,19 @@