diff --git a/src/src/app/components/dashboard/dashboard.component.html b/src/src/app/components/dashboard/dashboard.component.html index ee00024..e6e0f53 100644 --- a/src/src/app/components/dashboard/dashboard.component.html +++ b/src/src/app/components/dashboard/dashboard.component.html @@ -232,15 +232,48 @@
-

TOTP

+
+

TOTP

+ @if (settings?.totp_enabled) { + + enabled + } @else { + + disabled + } +
Add a second layer of security to your account
- + +
+ +
+ @if (settings?.google_apikey) { +
+
+

Google API Key

+
+ + +
+ } @else { +

Google API Key

+ + + + + +
+ +
+ }
diff --git a/src/src/app/components/dashboard/dashboard.component.ts b/src/src/app/components/dashboard/dashboard.component.ts index f143195..15ef26e 100644 --- a/src/src/app/components/dashboard/dashboard.component.ts +++ b/src/src/app/components/dashboard/dashboard.component.ts @@ -133,6 +133,7 @@ export class DashboardComponent implements OnInit, AfterViewInit { currency: ['', Validators.required], do_not_display: [], tile_layer: ['', Validators.required], + _google_apikey: [null, { validators: [Validators.pattern('AIza[0-9A-Za-z\\-]{35}')] }], }); // HACK: Subscribe in constructor for takeUntilDestroyed @@ -666,7 +667,7 @@ export class DashboardComponent implements OnInit, AfterViewInit { updateSettings() { this.apiService - .putSettings(this.settingsForm.value) + .putSettings({ ...this.settingsForm.value, google_apikey: this.settingsForm.get('_google_apikey')?.value }) .pipe(take(1)) .subscribe({ next: (settings) => { @@ -980,4 +981,27 @@ export class DashboardComponent implements OnInit, AfterViewInit { }, }); } + + deleteGoogleApiKey() { + const modal = this.dialogService.open(YesNoModalComponent, { + header: 'Confirm', + modal: true, + closable: true, + dismissableMask: true, + breakpoints: { + '640px': '90vw', + }, + data: 'Are you sure you want to delete GMaps API Key ?', + })!; + + modal.onClose.subscribe({ + next: (bool: boolean) => { + if (!bool) return; + this.apiService.putSettings({ google_apikey: null }).subscribe({ + next: () => (this.settings!.google_apikey = false), + error: () => this.utilsService.toast('error', 'Error', 'Error deleting GMaps API key'), + }); + }, + }); + } } diff --git a/src/src/app/modals/place-create-modal/place-create-modal.component.html b/src/src/app/modals/place-create-modal/place-create-modal.component.html index f4aebc1..8b187f0 100644 --- a/src/src/app/modals/place-create-modal/place-create-modal.component.html +++ b/src/src/app/modals/place-create-modal/place-create-modal.component.html @@ -1,9 +1,13 @@
- - - - +
+ + + + + +
@@ -17,7 +21,7 @@ - + @@ -70,10 +74,17 @@
- - - - +
+ + + + + @if (descriptionInput.value) { + + } +
@if (placeForm.get("image_id")?.value) { diff --git a/src/src/app/modals/place-create-modal/place-create-modal.component.ts b/src/src/app/modals/place-create-modal/place-create-modal.component.ts index 8961c69..237d06a 100644 --- a/src/src/app/modals/place-create-modal/place-create-modal.component.ts +++ b/src/src/app/modals/place-create-modal/place-create-modal.component.ts @@ -203,4 +203,19 @@ export class PlaceCreateModalComponent { this.placeForm.get('gpx')?.setValue(null); this.placeForm.get('gpx')?.markAsDirty(); } + + gmapsSearchText() { + const query = this.placeForm.get('name')?.value; + if (!query) return; + this.apiService.gmapsSearchText(query).subscribe({ + next: (results) => { + if (results.length == 1) { + const r = results[0]; + this.placeForm.patchValue({ ...r, lat: formatLatLng(r.lat), lng: formatLatLng(r.lng), place: r.name || '' }); + this.placeForm.get('category')?.markAsDirty(); + return; + } + }, + }); + } } diff --git a/src/src/app/services/api.service.ts b/src/src/app/services/api.service.ts index 9134c12..035c46c 100644 --- a/src/src/app/services/api.service.ts +++ b/src/src/app/services/api.service.ts @@ -1,6 +1,6 @@ import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { Category, Place } from '../types/poi'; +import { Category, GooglePlaceResult, Place } from '../types/poi'; import { BehaviorSubject, map, Observable, shareReplay, tap } from 'rxjs'; import { Info } from '../types/info'; import { Backup, ImportResponse, Settings } from '../types/settings'; @@ -321,4 +321,8 @@ export class ApiService { verifyTOTP(code: string): Observable { return this.httpClient.post(this.apiBaseUrl + '/settings/totp/verify', { code }); } + + gmapsSearchText(q: string): Observable { + return this.httpClient.get(`${this.apiBaseUrl}/places/google-search`, { params: { q } }); + } } diff --git a/src/src/app/types/poi.ts b/src/src/app/types/poi.ts index 227fd18..ae86685 100644 --- a/src/src/app/types/poi.ts +++ b/src/src/app/types/poi.ts @@ -25,3 +25,13 @@ export interface Place { visited?: boolean; favorite?: boolean; } + +export interface GooglePlaceResult { + name: string; + lat: number; + lng: number; + price: number; + types: string[]; + allowdog: boolean; + description: string; +} diff --git a/src/src/app/types/settings.ts b/src/src/app/types/settings.ts index 1462434..2bac171 100644 --- a/src/src/app/types/settings.ts +++ b/src/src/app/types/settings.ts @@ -11,6 +11,7 @@ export interface Settings { mode_dark?: boolean; mode_gpx_in_place?: boolean; totp_enabled?: boolean; + google_apikey?: boolean | null; } export interface ImportResponse {