From 441be4ca623f93797ef115a86b5e0267dbbb4f5a Mon Sep 17 00:00:00 2001 From: Jorge Lainfiesta Date: Mon, 4 Jun 2018 14:07:32 +0200 Subject: [PATCH] Model types and other type annotations --- src/ui/components/Breethe/component.ts | 10 +-- src/ui/components/FogBackground/component.ts | 2 +- src/ui/components/Location/component.ts | 35 +++++---- src/ui/components/MeasurementRow/component.ts | 10 +-- src/ui/components/Search/component.ts | 71 +++++++++++-------- src/ui/components/SearchForm/component.ts | 2 +- src/utils/data/models.d.ts | 23 ++++++ src/utils/data/schema.ts | 2 +- src/utils/data/setup-store.ts | 2 +- tslint.json | 3 +- 10 files changed, 104 insertions(+), 56 deletions(-) create mode 100644 src/utils/data/models.d.ts diff --git a/src/ui/components/Breethe/component.ts b/src/ui/components/Breethe/component.ts index be434f19..be1b73f3 100644 --- a/src/ui/components/Breethe/component.ts +++ b/src/ui/components/Breethe/component.ts @@ -7,11 +7,11 @@ import Location from '../Location/component'; const MODE_SEARCH = 'search'; const MODE_RESULTS = 'results'; -interface ISearchParams { +interface SearchParams { searchTerm?: string; coordinates?: number[]; } -interface ILocationParams { +interface LocationParams { locationId?: string; } @@ -118,18 +118,18 @@ export default class Breethe extends Component { .resolve(this.appState.route); } - _setMode(mode, params: ISearchParams | ILocationParams = {}) { + _setMode(mode, params: SearchParams | LocationParams = {}) { this.mode = mode; switch (mode) { case MODE_SEARCH: - params = params as ISearchParams; + params = params as SearchParams; this.locationId = null; this.searchTerm = params.searchTerm; this.coordinates = params.coordinates; break; case MODE_RESULTS: - params = params as ILocationParams; + params = params as LocationParams; this.locationId = params.locationId; this.searchTerm = null; this.coordinates = null; diff --git a/src/ui/components/FogBackground/component.ts b/src/ui/components/FogBackground/component.ts index a699b332..f103d7ff 100644 --- a/src/ui/components/FogBackground/component.ts +++ b/src/ui/components/FogBackground/component.ts @@ -2,7 +2,7 @@ import Component, { tracked } from '@glimmer/component'; export default class FogBackground extends Component { @tracked('args') - get opacityStyle() { + get opacityStyle(): string { let { intensity } = this.args; return `opacity: ${0.2 * intensity}`; } diff --git a/src/ui/components/Location/component.ts b/src/ui/components/Location/component.ts index 27c91280..a79a7894 100644 --- a/src/ui/components/Location/component.ts +++ b/src/ui/components/Location/component.ts @@ -7,28 +7,37 @@ const QUALITY_SCALE = ['very_low', 'low', 'medium', 'high', 'very_high']; const QUALITY_LABEL = ['Excellent', 'Good', 'Ok', 'Poor', 'Very poor']; export default class LocationComponent extends Component { - @tracked loading = false; + @tracked + location: Location | {} = {}; - @tracked location: any = {}; + @tracked + measurements: Measurement[] = []; - @tracked measurements = []; + @tracked + loading: boolean = false; - @tracked notFound = false; + @tracked + notFound: boolean = false; @tracked('measurements') - get measurementLists() { + get measurementLists(): { first: Measurement[]; second: Measurement[] } { let { measurements } = this; - let orderedMeasurements = ORDERED_PARAMS.map((param) => { + let orderedMeasurements = ORDERED_PARAMS.map((parameter) => { let measurement = measurements.find((record) => { - return record.attributes.parameter === param; + return record.attributes.parameter === parameter; }); if (measurement) { return measurement; } return { + id: '', attributes: { - parameter: param + parameter, + measuredAt: '', + unit: '', + value: '', + qualityIndex: '' } }; }); @@ -41,7 +50,7 @@ export default class LocationComponent extends Component { } @tracked('measurements') - get updatedDate() { + get updatedDate(): string { let { measurements } = this; if (measurements.length === 0) { return '–'; @@ -68,12 +77,12 @@ export default class LocationComponent extends Component { return dates[0].toLocaleString(); } - get recordsFound() { + get recordsFound(): boolean { return this.location && this.measurements.length > 0; } @tracked('measurements') - get qualityIndex() { + get qualityIndex(): number { let { measurements } = this; let indexes = measurements .filter((measurement) => { @@ -95,7 +104,7 @@ export default class LocationComponent extends Component { } @tracked('qualityIndex') - get qualityLabel() { + get qualityLabel(): string { let { qualityIndex } = this; return QUALITY_LABEL[qualityIndex]; } @@ -105,7 +114,7 @@ export default class LocationComponent extends Component { this.loadMeasurements(this.args.locationId); } - async loadMeasurements(locationId) { + async loadMeasurements(locationId: string) { let { pullIndexedDB, store, isSSR, localStore } = this.args; let locationSignature = { type: 'location', id: locationId }; diff --git a/src/ui/components/MeasurementRow/component.ts b/src/ui/components/MeasurementRow/component.ts index 9c8e3d27..6a54c6b3 100644 --- a/src/ui/components/MeasurementRow/component.ts +++ b/src/ui/components/MeasurementRow/component.ts @@ -4,12 +4,12 @@ const SUP_PARAMS = ['SO2', 'O3', 'NO2']; export default class SearchForm extends Component { @tracked('args') - get isPPM() { + get isPPM(): boolean { return this.args.unit === 'ppm'; } @tracked('args') - get paramName() { + get paramName(): { base: string, sup?: string } { let type = this.args.parameter.toUpperCase(); if (SUP_PARAMS.indexOf(type) >= 0) { return { @@ -23,18 +23,18 @@ export default class SearchForm extends Component { } @tracked('args') - get valuePresent() { + get valuePresent(): boolean { let value = this.args.value; return value !== null && value !== undefined; } - precisionRound(value, precision) { + precisionRound(value: number, precision: number): number { let factor = Math.pow(10, precision); return Math.round(value * factor) / factor; } @tracked('args') - get value() { + get value(): string { let { value } = this.args; let isNumber = !isNaN(parseFloat(value)); return `${(isNumber) ? this.precisionRound(value, 2) : ''}`; diff --git a/src/ui/components/Search/component.ts b/src/ui/components/Search/component.ts index 12c9c4a6..79086133 100644 --- a/src/ui/components/Search/component.ts +++ b/src/ui/components/Search/component.ts @@ -7,66 +7,82 @@ const LOCATION_PERMISSION_DENIED: number = 1; export default class Home extends Component { @tracked - coordinates; + coordinates: number[] | null; @tracked - error; + locations: Location[] = []; @tracked - locations = []; + searchTerm: string = ''; @tracked - searchTerm = ''; + loading: boolean = false; @tracked - loading = false; + error: null | string; @tracked('args') - get isSearchDisabled() { + get isSearchDisabled(): boolean { return this.args.isSSR || !this.args.isOnline; } @tracked('locations', 'searchTerm', 'coordinates') - get showRecent() { + get showRecent(): boolean { return this.locations.length > 0 && !this.searchTerm && !this.coordinates; } constructor(options) { super(options); - assert('Argument \'store\' must be supplied to this component.', this.args.store); - this.searchTerm = this.args.searchTerm; - this.coordinates = this.args.coordinates; - if ((this.searchTerm && this.searchTerm.length > 0) || (this.coordinates && this.coordinates.length > 0)) { - this.findLocations(this.searchTerm, this.coordinates, this.args.searchResults); - } else if (!this.args.isSSR) { + assert( + 'Argument \'store\' must be supplied to this component.', + this.args.store + ); + + let { + searchTerm, + coordinates, + searchResults, + isSSR, + updateFogEffect + } = this.args; + + this.searchTerm = searchTerm; + this.coordinates = coordinates; + + if ( + (searchTerm && searchTerm.length > 0) || + (coordinates && coordinates.length > 0) + ) { + this.findLocations(searchTerm, coordinates, searchResults); + } else if (!isSSR) { this.loadRecent(); } - this.args.updateFogEffect(0); + updateFogEffect(0); } - async findLocations(searchTerm, coordinates, searchResults = []) { + async findLocations(searchTerm: string, coordinates: number[], searchResults: string[] = []) { this.error = null; let { store } = this.args; + if (searchResults.length > 0) { - let locations = searchResults.map((id) => { + this.locations = searchResults.map((id) => { try { return store.cache.query((q) => q.findRecord({ type: 'location', id })); } catch (e) { return; } }); - this.locations = locations; } else { this.loading = true; try { - let url; + let url: string; if (searchTerm) { url = `${__ENV_API_HOST__}/api/locations?filter[name]=${searchTerm}`; } else { url = `${__ENV_API_HOST__}/api/locations?filter[coordinates]=${coordinates}`; } let locationsResponse = await fetch(url); - let locationsPayload: { data: any[] } = await locationsResponse.json(); + let locationsPayload: { data: Location[] } = await locationsResponse.json(); this.locations = locationsPayload.data; } catch (e) { this.locations = []; @@ -79,9 +95,7 @@ export default class Home extends Component { this.error = null; let { pullIndexedDB, store } = this.args; await pullIndexedDB(); - let locations = store.cache.query( - (q) => q.findRecords('location') - ); + let locations: Location[] = store.cache.query((q) => q.findRecords('location')); locations = locations.filter((location) => { return !!location.attributes.visitedAt; }); @@ -118,10 +132,15 @@ export default class Home extends Component { this.searchTerm = ''; this.loading = true; - navigator.geolocation.getCurrentPosition(onSuccess, onError, { timeout: 5 * 1000 }); + navigator.geolocation.getCurrentPosition(onSuccess, onError, { + timeout: 5 * 1000 + }); } - goToRoute(search, coordinates, event = null) { + goToRoute(search: string, coordinates: number[], event = null) { + if (event) { + event.preventDefault(); + } if (search && search.length > 0) { this.searchTerm = search; this.coordinates = null; @@ -143,9 +162,5 @@ export default class Home extends Component { this.loadRecent(); this.args.router.navigate(`/`); } - - if (event) { - event.preventDefault(); - } } } diff --git a/src/ui/components/SearchForm/component.ts b/src/ui/components/SearchForm/component.ts index ac420e19..266f25e0 100644 --- a/src/ui/components/SearchForm/component.ts +++ b/src/ui/components/SearchForm/component.ts @@ -4,7 +4,7 @@ export default class SearchForm extends Component { @tracked search: string = this.args.term || ''; - cacheArgTerm; + cacheArgTerm: string; updateSearch(event) { this.search = event.target.value; diff --git a/src/utils/data/models.d.ts b/src/utils/data/models.d.ts new file mode 100644 index 00000000..0e5c221a --- /dev/null +++ b/src/utils/data/models.d.ts @@ -0,0 +1,23 @@ +interface Location { + id: string; + attributes: { + name: string; + city: string; + label: string; + coordinates: string; + country: string; + identifier: string; + visitedAt: string; + }; +} + +interface Measurement { + id: string; + attributes: { + parameter: string; + measuredAt: string; + unit: string; + value: string; + qualityIndex: string; + }; +} diff --git a/src/utils/data/schema.ts b/src/utils/data/schema.ts index 6035f6eb..7f7e1bb4 100644 --- a/src/utils/data/schema.ts +++ b/src/utils/data/schema.ts @@ -9,7 +9,7 @@ export const location: ModelDefinition = { coordinates: { type: 'string' }, country: { type: 'string' }, identifier: { type: 'string' }, - lastVisited: { type: 'string' } + visitedAt: { type: 'string' } }, relationships: { measurements: { type: 'hasMany', model: 'measurement', inverse: 'location' } diff --git a/src/utils/data/setup-store.ts b/src/utils/data/setup-store.ts index 2020d9da..4318f84c 100644 --- a/src/utils/data/setup-store.ts +++ b/src/utils/data/setup-store.ts @@ -7,7 +7,7 @@ import { schema as schemaDefinition } from './schema'; declare const __ENV_API_HOST__: string; -export function setupStore(appState) { +export function setupStore(appState): { store: Store; local?: IndexedDBStore; coordinator?: Coordinator } { let schema = new Schema(schemaDefinition); let store = new Store({ schema }); diff --git a/tslint.json b/tslint.json index fd3aa2ef..2803a8d1 100644 --- a/tslint.json +++ b/tslint.json @@ -10,7 +10,8 @@ "only-arrow-functions": false, "prefer-const": false, "member-access": false, - "object-literal-sort-keys": false + "object-literal-sort-keys": false, + "interface-name": [true, "never-prefix"] }, "rulesDirectory": [] }