// Questo file fa parte di MyTown.
// MyTown è software libero: puoi ridistribuirlo e/o modificarlo secondo
// i termini della GNU Affero General Public License come pubblicata dalla
// Free Software Foundation, versione 3.
// Distribuito senza ALCUNA GARANZIA; vedi la GNU AGPL v3 per i dettagli.
// Copyright (C) Kondor Consulting & Services S.r.l.
// SPDX-License-Identifier: AGPL-3.0-or-later

import { Component, OnInit, NgZone } from '@angular/core';
import { ActionSheetController, InfiniteScrollCustomEvent } from '@ionic/angular';
import { Router, ActivatedRoute } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
import { CapacitorBarcodeScanner, CapacitorBarcodeScannerTypeHint } from '@capacitor/barcode-scanner';

import { ConfigService } from 'src/app/providers/config.service';
import { CompaniesService } from 'src/app/providers/companies.service';
import { BakekaService } from 'src/app/providers/bakeka.service';
import { PlacesService } from 'src/app/providers/places.service';
import { GeolocationService } from 'src/app/providers/geolocation.service';
import { LayoutService } from 'src/app/providers/layout.service';

@Component({
	selector: 'app-eventboard',
	templateUrl: './eventboard.component.html',
	styleUrls: ['./eventboard.component.scss'],
	standalone: false,
})
export class EventboardComponent implements OnInit {
	imageBaseURL?: string;
	// VARIABLES
	cmp_id?: number;
	limitda!: number;
	pageelement?: number;
	area_code?: string;
	objcomune: any;
	comuni: any[] = [];
	place: any = {};
	lstCompaniesArea: any[] = [];
	filter: any = {};
	cerca?: any;
	dataDA?: any;
	dataA?: any;
	tabIndex: number = 0;
	tabValue?: string;
	urlPhotoPlace = ''; // popolato da ConfigService.placesImageUrl
	urlPhotoBakeka = ''; // popolato da ConfigService.bakekaImageUrl
	errorMessage = '';
	funzioni: any[] = [];
	filtriModalOpen = false;
	audioModalOpen = false;
	audio: any;
	urlAudio = ''; // popolato da ConfigService.placesAudioUrl
	lstCompanies: any[] = [];
	lstLocandine: any[] = [];
	like = false;
	urlPhoto = ''; // popolato da ConfigService.bakekaImageUrl
	paramsSubscription?: Subscription;
	// Paginazione infinite-scroll: false quando l'ultima pagina ha restituito
	// meno di `pageelement` elementi (= non ci sono altre pagine da caricare).
	eventsHasMore = true;
	placesHasMore = true;
	// Caricamento in corso: mostra gli skeleton al posto dell'empty-state
	// (evita il flash "Nessun evento/luogo" mentre la prima pagina sta arrivando).
	loadingEvents = true;
	loadingPlaces = true;
	responsiveOptions: any[] = [
		{ breakpoint: '1024px', numVisible: 5 },
		{ breakpoint: '768px', numVisible: 3 },
		{ breakpoint: '560px', numVisible: 1 },
	];
	// VARIABLES EVENTS
	filterEV: any = {};
	loc?: any; // locandina per dialog likes users
	visible = false; // dialog likes users
	// SEGNALAZIONE POST
	reportModalOpen = false;
	reportTarget: any = null; // post in fase di segnalazione
	reportNote = ''; // nota facoltativa
	reportSending = false;
	// DETTAGLIO POST (tap sull'immagine → modale ingrandita con descrizione completa)
	detailModalOpen = false;
	detailTarget: any = null;
	// DOPPIO TAP → MI PIACE (cuore "burst" stile social)
	heartBurstId: any = null;
	private heartBurstTimer: any = null;
	private tapTimer: any = null; // distingue tap singolo (dettaglio) da doppio tap (like)
	private lastTapId: any = null;
	// VARIABLES LOCATIONS
	lstLocations: any[] = [];
	filterLOC: any = {};
	// MAPPA LUOGHI
	mapCenter: google.maps.LatLngLiteral = { lat: 42.5, lng: 13.0 }; // default centro Italia
	mapZoom = 6;
	mapOptions: google.maps.MapOptions = {
		disableDefaultUI: true,
		zoomControl: true,
		gestureHandling: 'cooperative',
	};
	placeMarkers: { position: google.maps.LatLngLiteral; plc_id: number; title: string; options: google.maps.MarkerOptions }[] = [];
	myPosition?: google.maps.LatLngLiteral;
	myPositionOptions: google.maps.MarkerOptions = {
		icon: 'https://maps.google.com/mapfiles/ms/icons/blue-dot.png',
		title: 'La tua posizione',
		zIndex: 9999,
	};
	private geocoder?: google.maps.Geocoder;
	private geoCache = new Map<number, google.maps.LatLngLiteral>(); // plc_id -> coordinate (evita ri-geocoding)
	private geoRetries = 0;
	resetButtons = [
		{
			text: 'No',
			handler: () => {},
		},
		{
			text: 'Sì',
			cssClass: 'alert-button-confirm',
			handler: () => {
				this.resetFilters();
				this.getList();
				this.filtriModalOpen = false;
				this.audioModalOpen = false;
			},
		},
	];

	constructor(
		private bakekaService: BakekaService,
		private configService: ConfigService,
		private router: Router,
		private placesService: PlacesService,
		private activated_route: ActivatedRoute,
		private companiesService: CompaniesService,
		private translate: TranslateService,
		private ngZone: NgZone,
		private geo: GeolocationService,
		private layoutService: LayoutService,
		private actionSheetCtrl: ActionSheetController,
	) {}

	// srcset responsive (WebP sm/md/lg): il browser sceglie la misura più adatta.
	// Fallback su lg per i record vecchi che hanno solo _lg valorizzato.
	bakekaSrcset(p: any): string {
		const u = this.urlPhotoBakeka;
		const sm = p.prph_path_photo_sm || p.prph_path_photo_lg;
		const md = p.prph_path_photo_md || p.prph_path_photo_lg;
		const lg = p.prph_path_photo_lg;
		return `${u}${sm} 400w, ${u}${md} 800w, ${u}${lg} 1280w`;
	}
	placeSrcset(p: any): string {
		const u = this.urlPhotoPlace;
		const sm = p.plm_path_sm || p.plm_path_lg;
		const md = p.plm_path_md || p.plm_path_lg;
		const lg = p.plm_path_lg;
		return `${u}${sm} 400w, ${u}${md} 800w, ${u}${lg} 1280w`;
	}

	onTabChange(value: any) {
		this.tabValue = value;

		if (value == 'events') {
			this.tabIndex = 0;
			this.getList();
		}
		if (value == 'places') {
			this.tabIndex = 1;
			this.getListLOC();
		}
	}

	ngOnInit(): void {
		this.cmp_id = -1;
		this.limitda = 1;
		this.pageelement = 20;
		this.area_code = '';
		this.imageBaseURL = this.configService.imageBaseURL;
		this.urlPhotoBakeka = this.configService.bakekaImageUrl;
		this.urlPhoto = this.configService.bakekaImageUrl;
		this.urlPhotoPlace = this.configService.placesImageUrl;
		this.urlAudio = this.configService.placesAudioUrl;
		this.resetFilters();
	}

	ionViewWillEnter() {
		// La bacheca ha la sua search-zone brand in cima: nascondi l'header
		// app-layout (niente barra "App"). Ripristinato in ionViewWillLeave.
		this.layoutService.setPageConfig({ title: '', showBack: false, showHeader: false, tab: 'bakeka' });
		this.tabValue = 'events';
		this.getList();
		this.loadCmp();
	}

	ionViewWillLeave() {
		this.layoutService.setPageConfig({ showHeader: true });
	}

	ngOnDestroy() {
		this.paramsSubscription?.unsubscribe();
		clearTimeout(this.tapTimer);
		clearTimeout(this.heartBurstTimer);
	}

	resetFilters() {
		// events
		this.filterEV.bk_cmp_id = this.cmp_id;
		this.filterEV.pagina = this.limitda;
		this.filterEV.bk_plc_id = -1;
		this.filterEV.lang = this.configService.getLanguage();
		this.filterEV.righePagina = this.pageelement;
		this.filterEV.bk_cmp_ac = '';

		// locations
		this.filterLOC.plc_cmp_id = this.cmp_id;
		this.filterLOC.pagina = this.limitda;
		this.filterLOC.righePagina = this.pageelement;
		this.filterLOC.plc_name = '';
		this.filterLOC.plc_city_code = '';

		this.getListLOC();
		this.onTabChange(this.tabValue);
		this.filter.bk_cmp_id = this.cmp_id;
		this.filter.pagina = this.limitda;
		this.filter.righePagina = this.pageelement;
		this.filter.bk_title = '';
	}

	openFiltri() {
		this.filtriModalOpen = true;
		this.area_code = this.filter.bk_cmp_ac;
	}

	searchFilter() {
		// events
		this.filterEV.bk_cmp_ac = this.area_code;
		this.filterEV.bk_plc_id = this.place.plc_id;

		this.resetPaging();
		this.onTabChange(this.tabValue);
		this.filtriModalOpen = false;
	}

	filterEvents() {
		this.filterEV.bk_title = this.cerca;
		this.filterLOC.plc_name = this.cerca;

		this.resetPaging();
		this.onTabChange(this.tabValue);
	}
	onSearchClear() {
		this.cerca = '';
		this.filterEV.bk_title = this.cerca;
		this.filterLOC.plc_name = this.cerca;

		this.resetPaging();
		this.onTabChange(this.tabValue);
	}

	// Riporta entrambe le liste alla prima pagina e riabilita l'infinite-scroll
	// (da chiamare quando cambia la query: ricerca, filtri, ripulisci).
	private resetPaging() {
		this.filterEV.pagina = 1;
		this.filterLOC.pagina = 1;
		this.eventsHasMore = true;
		this.placesHasMore = true;
	}

	// ----------------------------------------------------------------------------------------
	// -------------------------------- TAB EVENTS --------------------------------------------
	// ----------------------------------------------------------------------------------------

	getList(append = false) {
		if (!append) {
			this.lstLocandine = [];
			this.loadingEvents = true;
		}
		this.bakekaService.getListEvents(this.filterEV).subscribe({
			next: (res) => {
				res = res || [];

				// ORDINAMENTO: evento più prossimo alla data di oggi per primo
				// (le date arrivano come array Java [aaaa, mm, gg, ...]: uso formatDate per ottenere un Date)
				const todayMs = new Date(new Date().toDateString()).getTime();
				res.sort((a: any, b: any) => {
					const da = this.configService.formatDate(a.bk_date);
					const db = this.configService.formatDate(b.bk_date);
					const ka = da ? Math.abs(da.getTime() - todayMs) : Infinity;
					const kb = db ? Math.abs(db.getTime() - todayMs) : Infinity;
					return ka - kb;
				});

				// GESTIONE DATE (formattazione display, dopo l'ordinamento)
				for (let i = 0; i < res.length; i++) {
					res[i].bk_date = this.configService.formatDate(res[i].bk_date);
					res[i].bk_date_to = this.configService.formatDate(res[i].bk_date_to);
					res[i].bk_dateins_d = this.configService.formatDate(res[i].bk_dateins); // data inserimento -> Date
				}

				// pagina piena => potrebbero esserci altre pagine
				this.eventsHasMore = res.length >= (this.pageelement ?? 20);
				this.lstLocandine = append ? [...this.lstLocandine, ...res] : res;
				this.loadingEvents = false;
			},
			error: () => {
				this.loadingEvents = false;
			},
		});
	}

	loadCmp() {
		this.companiesService.getNaturaImpresa().subscribe((res) => {
			this.lstCompaniesArea = res;
		});
	}

	// Crea un Set per salvare gli ID delle locandine espanse
	// evita che si modifichino tutte con una variabile in comune
	espanseIds = new Set<any>();

	SeeMore(id: any) {
		this.espanseIds.add(id);
	}

	SeeLess(id: any) {
		this.espanseIds.delete(id);
	}

	// Nome mese abbreviato in italiano (es. "lug"). L'app non registra il
	// locale 'it' di Angular → uso toLocaleDateString come nel resto del codice.
	meseAbbr(d: any): string {
		if (!d) return '';
		const date: Date = d instanceof Date ? d : new Date(d);
		return isNaN(date.getTime()) ? '' : date.toLocaleDateString('it-IT', { month: 'short' });
	}

	// Data di pubblicazione in forma relativa: "Pubblicato oggi / ieri / N giorni fa".
	pubblicato(d: any): string {
		if (!d) return '';
		const date: Date = d instanceof Date ? d : new Date(d);
		if (isNaN(date.getTime())) return '';
		const inizioGiorno = (x: Date) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime();
		const giorni = Math.round((inizioGiorno(new Date()) - inizioGiorno(date)) / 86400000);
		if (giorni <= 0) return 'Pubblicato oggi';
		if (giorni === 1) return 'Pubblicato ieri';
		return `Pubblicato ${giorni} giorni fa`;
	}

	fav(loc: any) {
		// Il "mi piace" richiede l'accesso: i non registrati vengono portati al login.
		if (!this.configService.isLogged()) {
			this.router.navigate(['/login']);
			return;
		}
		this.bakekaService.addLike(loc.bk_id).subscribe(() => {
			// aggiornamento locale: niente reload della lista (preserva scroll e paginazione)
			loc.melike = true;
			loc.likes = (loc.likes || 0) + 1;
		});
	}

	notfav(loc: any) {
		if (!this.configService.isLogged()) {
			this.router.navigate(['/login']);
			return;
		}
		this.bakekaService.removeLike(loc.bk_id).subscribe(() => {
			loc.melike = false;
			loc.likes = Math.max(0, (loc.likes || 0) - 1);
		});
	}

	// Menu unico (…) del post: mi piace / condividi / segnala in un action sheet dal basso.
	async openPostActions(loc: any) {
		if (!loc) return;
		const actionSheet = await this.actionSheetCtrl.create({
			header: loc.bk_title,
			buttons: [
				loc.melike == true ? { text: this.translate.instant('Rimuovi mi piace'), icon: 'heart-dislike-outline', handler: () => this.notfav(loc) } : { text: this.translate.instant('Mi piace'), icon: 'heart-outline', handler: () => this.fav(loc) },
				{ text: this.translate.instant('Condividi'), icon: 'paper-plane-outline', handler: () => this.share(loc) },
				{ text: this.translate.instant('Segnala'), icon: 'flag-outline', role: 'destructive', handler: () => this.openReport(loc) },
				{ text: this.translate.instant('Annulla'), role: 'cancel' },
			],
		});
		await actionSheet.present();
	}

	// Tap sul cover del post: singolo = apre il dettaglio ingrandito,
	// doppio = mette "mi piace" (rimovibile solo dal menu …).
	onCoverClick(ev: Event, loc: any) {
		// ignora i click sulle frecce/pallini dello swiper (vivono nello shadow DOM)
		const path = (ev.composedPath?.() || []) as any[];
		if (path.some((el) => el?.classList?.contains?.('swiper-button-next') || el?.classList?.contains?.('swiper-button-prev') || el?.classList?.contains?.('swiper-pagination'))) {
			return;
		}
		if (this.tapTimer && this.lastTapId === loc.bk_id) {
			// secondo tap entro la finestra → doppio tap
			clearTimeout(this.tapTimer);
			this.tapTimer = null;
			this.doubleTapLike(loc);
		} else {
			clearTimeout(this.tapTimer);
			this.lastTapId = loc.bk_id;
			this.tapTimer = setTimeout(() => {
				this.tapTimer = null;
				this.openDetail(loc);
			}, 280);
		}
	}

	private doubleTapLike(loc: any) {
		if (!this.configService.isLogged()) {
			this.router.navigate(['/login']);
			return;
		}
		// cuore "burst" anche se già piaciuto (come i social: il doppio tap non toglie il like)
		this.heartBurstId = loc.bk_id;
		clearTimeout(this.heartBurstTimer);
		this.heartBurstTimer = setTimeout(() => (this.heartBurstId = null), 800);
		if (loc.melike != true) {
			this.fav(loc);
		}
	}

	openDetail(loc: any) {
		this.detailTarget = loc;
		this.detailModalOpen = true;
	}

	// Testo data evento per la modale dettaglio (es. "5 lug 2026" o "5 lug → 7 lug 2026").
	dataEvento(loc: any): string {
		if (!loc?.bk_date) return '';
		const d: Date = loc.bk_date instanceof Date ? loc.bk_date : new Date(loc.bk_date);
		if (isNaN(d.getTime())) return '';
		const to: Date | null = loc.bk_date_to ? (loc.bk_date_to instanceof Date ? loc.bk_date_to : new Date(loc.bk_date_to)) : null;
		if (to && !isNaN(to.getTime()) && to.toDateString() !== d.toDateString()) {
			return `${d.getDate()} ${this.meseAbbr(d)} → ${to.getDate()} ${this.meseAbbr(to)} ${to.getFullYear()}`;
		}
		return `${d.getDate()} ${this.meseAbbr(d)} ${d.getFullYear()}`;
	}

	share(loc: any) {
		const shareData = {
			title: loc.bk_title || 'Guarda questo post!',
			text: 'Ho trovato questo contenuto interessante.',
			url: window.location.href,
		};

		// controllo se il browser supporta la condivisione nativa del sistema operativo
		if (navigator.share) {
			navigator
				.share(shareData)
				.then(() => this.configService.presentToastSuccess('Condiviso con successo!'))
				.catch((error) => {
					if (error.name !== 'AbortError') {
						console.error('Errore durante lo share:', error);
					}
				});
		} else {
			this.copyLink(shareData.url);
		}
	}

	// Apre la modale di segnalazione (richiede login, come il "mi piace").
	openReport(loc: any) {
		if (!this.configService.isLogged()) {
			this.router.navigate(['/login']);
			return;
		}
		this.reportTarget = loc;
		this.reportNote = '';
		this.reportModalOpen = true;
	}

	// Invia la segnalazione: email agli amministratori con nota facoltativa.
	submitReport() {
		if (!this.reportTarget || this.reportSending) {
			return;
		}
		this.reportSending = true;
		this.bakekaService
			.reportPost(this.reportTarget.bk_id, {
				note: this.reportNote?.trim() || '',
				bk_title: this.reportTarget.bk_title,
				cmp_name: this.reportTarget.cmp_name,
			})
			.subscribe({
				next: () => {
					this.reportSending = false;
					this.reportModalOpen = false;
					this.reportTarget = null;
					this.reportNote = '';
					this.configService.presentToastSuccess('Segnalazione inviata. Grazie per la collaborazione.');
				},
				error: () => {
					this.reportSending = false;
					this.configService.presentToastError('Impossibile inviare la segnalazione. Riprova più tardi.');
				},
			});
	}

	private copyLink(url: string) {
		navigator.clipboard
			.writeText(url)
			.then(() => {
				this.configService.presentToastSuccess('Link copiato negli appunti! Condividilo dove vuoi.');
			})
			.catch((err) => {
				console.error('Impossibile copiare il link:', err);
			});
	}

	// -------------------------------------------------------------------------------------------
	// -------------------------------- TAB LOCATIONS --------------------------------------------
	// -------------------------------------------------------------------------------------------

	getListLOC(append = false) {
		if (!append) {
			this.lstLocations = [];
			this.loadingPlaces = true;
		}
		this.placesService.getListLocations(this.filterLOC).subscribe({
			next: (res) => {
				res = res || [];
				this.placesHasMore = res.length >= (this.pageelement ?? 20);
				this.lstLocations = append ? [...this.lstLocations, ...res] : res;
				this.buildPlaceMarkers(this.lstLocations);
				this.loadingPlaces = false;
			},
			error: () => {
				this.loadingPlaces = false;
			},
		});
	}

	// Geocodifica gli indirizzi dei luoghi e crea i marker sulla mappa.
	private buildPlaceMarkers(places: any[]) {
		const g = (window as any).google;
		if (!g?.maps) {
			// API Maps non ancora caricata: riprovo qualche volta
			if (this.geoRetries++ < 10) {
				setTimeout(() => this.buildPlaceMarkers(places), 600);
			}
			return;
		}
		this.geoRetries = 0;
		this.geocoder = this.geocoder || new g.maps.Geocoder();
		this.placeMarkers = [];

		places.forEach((plc) => {
			const img = plc.photo?.length ? this.urlPhotoPlace + plc.photo[0].plm_path_sm : null;
			const addMarker = (position: google.maps.LatLngLiteral) => {
				this.ngZone.run(() => {
					this.placeMarkers = [
						...this.placeMarkers,
						{
							position,
							plc_id: plc.plc_id,
							title: plc.plc_name,
							options: img ? { icon: { url: img, scaledSize: new g.maps.Size(46, 46) } } : {},
						},
					];
					// centra sul primo luogo trovato
					if (this.placeMarkers.length === 1) {
						this.mapCenter = position;
						this.mapZoom = 13;
					}
				});
			};

			// 1) coordinate direttamente dal backend (preferito: niente geocoding/billing)
			const lat = plc.plc_lat ?? plc.plc_latitude ?? plc.plc_lat_dec;
			const lng = plc.plc_lng ?? plc.plc_longitude ?? plc.plc_lon ?? plc.plc_lng_dec;
			if (lat != null && lng != null && !isNaN(+lat) && !isNaN(+lng)) {
				addMarker({ lat: +lat, lng: +lng });
				return;
			}

			// 2) cache geocoding
			const cached = this.geoCache.get(plc.plc_id);
			if (cached) {
				addMarker(cached);
				return;
			}

			// 3) fallback: geocoding dall'indirizzo (richiede Geocoding API + Billing attivo)
			const address = `${plc.plc_address || ''} ${plc.plc_number || ''}, ${plc.plc_city || ''} ${plc.plc_codiceprovincia ? '(' + plc.plc_codiceprovincia + ')' : ''}`.trim();
			if (!address || address === ',') return;

			this.geocoder!.geocode({ address }, (results: any, status: any) => {
				if (status === 'OK' && results && results[0]) {
					const loc = results[0].geometry.location;
					const position = { lat: loc.lat(), lng: loc.lng() };
					this.geoCache.set(plc.plc_id, position);
					addMarker(position);
				}
			});
		});
	}

	// Click su un marker: porta la lista sotto sulla card del luogo scelto.
	onMarkerPlaceClick(plc_id: number) {
		const el = document.getElementById('place-' + plc_id);
		if (el) {
			el.scrollIntoView({ behavior: 'smooth', block: 'start' });
			this.espanseIds.add(plc_id);
		}
	}

	// Torna in cima, alla mappa.
	scrollToMap() {
		document.getElementById('places-top')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
	}

	// Ricentra la mappa sulla posizione dell'utente (per vedere i luoghi vicini).
	async centerOnMyPosition() {
		try {
			const pos = await this.geo.requestPosition();
			this.ngZone.run(() => {
				this.myPosition = { lat: pos.latitude, lng: pos.longitude };
				this.mapCenter = this.myPosition;
				this.mapZoom = 14;
			});
		} catch (e) {
			this.configService.presentToastError('Impossibile ottenere la posizione. Controlla i permessi di localizzazione.');
		}
	}

	// SCANSIONE QR

	async scanQrCode() {
		try {
			const result = await CapacitorBarcodeScanner.scanBarcode({
				hint: CapacitorBarcodeScannerTypeHint.QR_CODE,
			});
			if (!result.ScanResult) return;

			let scan = JSON.parse(result.ScanResult);

			if (scan.type == 'audioguide') {
				this.placesService.getAudiobyUUID(scan.uuid).subscribe({
					next: (res) => {
						this.audio = res;
						this.audioModalOpen = true;
					},
					error: (err) => this.configService.presentToastError(err),
				});
			}
		} catch (e: any) {
			this.configService.presentToastError('Impossibile avviare lo scanner: ' + e?.message);
		}
	}

	openAudio(uuid: string) {
		this.placesService.getAudiobyUUID(uuid).subscribe({
			next: (res) => {
				this.audio = res;
				this.audioModalOpen = true;
			},
			error: (err) => this.configService.presentToastError(err),
		});
	}

	// INDIRIZAMENTI

	goToEventsPlace(plc_id: number) {
		this.tabValue = 'events';
		this.place.plc_id = plc_id;
		this.searchFilter();
	}

	gotoReservation(bk_id: number, bk_cmp_id: number) {
		//, 'reservation', bk_id
		this.router.navigate(['/bakeka', 'company', bk_cmp_id, 'reservation', bk_id]);
	}

	// Carica la pagina successiva del tab attivo accodando i risultati.
	onIonInfinite(ev: InfiniteScrollCustomEvent) {
		if (this.tabValue === 'places') {
			this.filterLOC.pagina++;
			this.getListLOC(true);
		} else {
			this.filterEV.pagina++;
			this.getList(true);
		}
		setTimeout(() => ev.target.complete(), 800);
	}
}
