Skip to content

Commit

Permalink
Fixes: live search, parallel querying of weather data, handling of mi…
Browse files Browse the repository at this point in the history
…ssing data, layout improvements.

Signed-off-by: Pawel Mandes <[email protected]>
  • Loading branch information
pmandes authored and Francesco Pham committed Jan 17, 2025
1 parent 11c9941 commit a5454e4
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 109 deletions.
5 changes: 5 additions & 0 deletions entry/src/main/ets/model/WeatherModel.ets
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,9 @@ export interface DailyUnits {
wind_direction_10m_dominant?: string;
shortwave_radiation_sum?: string;
et0_fao_evapotranspiration?: string;
}

export interface WeatherData {
currentWeather: CurrentWeather | null;
forecast: Forecast | null;
}
5 changes: 4 additions & 1 deletion entry/src/main/ets/view/CityDetailsComponent.ets
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ export struct CityDetailsComponent {
Column() {
if (this.city) {
CurrentWeatherComponent({ city: this.city, currentWeather: this.currentWeather })
WeekForecastComponent({ forecast: this.forecast })

if (this.forecast)
WeekForecastComponent({ forecast: this.forecast })

Blank(100)
}
}
Expand Down
143 changes: 83 additions & 60 deletions entry/src/main/ets/view/CurrentWeatherComponent.ets
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TemperatureIcon } from './ui/TemperatureIcon';
import { WeatherIcon } from './ui/WeatherIcon';
import { CoordinateType, toDMS } from '../common/CoordinatesConverter';
import { City, CurrentWeather, Current, CurrentUnits, Forecast } from '../model/WeatherModel';
import { City, CurrentWeather, Current, CurrentUnits } from '../model/WeatherModel';

@Component
export struct CurrentWeatherComponent {
Expand Down Expand Up @@ -30,7 +30,7 @@ export struct CurrentWeatherComponent {
.fillColor(Color.White)
.width(16)
.height(16)
.margin({right: 4});
.margin({ right: 4 });

Text(toDMS(this.city?.lat, CoordinateType.LATITUDE) + ", "
+ toDMS(this.city?.lon, CoordinateType.LONGITUDE))
Expand All @@ -40,73 +40,96 @@ export struct CurrentWeatherComponent {
color: 0x88000000,
offsetX: 1,
offsetY: 1,
radius: 1})
}.padding({top: 6, bottom: 6})
radius: 1 })
}.padding({ top: 6, bottom: 6 })

Row() {
Column() {

Row() {
Image($r('app.media.ic_small_wind')).width(16).margin({right: 4});
Text($r('app.string.wind')).fontColor(Color.White).fontSize(14).fontWeight(FontWeight.Bold)
}.margin({bottom: 2})

Text($r('app.string.wind_speed_10m', `${this.current?.wind_speed_10m} ${this.units?.wind_speed_10m}`)).fontColor(Color.White).fontSize(12).margin({left: 21})
Text($r('app.string.wind_direction_10m', `${this.current?.wind_direction_10m}${this.units?.wind_direction_10m}`)).fontColor(Color.White).fontSize(12).margin({left: 21})
Text($r('app.string.wind_gusts_10m', `${this.current?.wind_gusts_10m} ${this.units?.wind_gusts_10m}`)).fontColor(Color.White).fontSize(12).margin({left: 21})

Row() {
Image($r('app.media.ic_small_pressure')).width(16).margin({right: 4})
Text($r('app.string.pressure')).fontColor(Color.White).fontSize(14).fontWeight(FontWeight.Bold)
}.margin({top: 8, bottom: 2})
if (this.currentWeather) {

Text(`${this.current?.surface_pressure} ${this.units?.surface_pressure}`).fontColor(Color.White).fontSize(12).margin({left: 21})
Row() {
Column() {

Row() {
Image($r('app.media.ic_small_humidity')).width(16).margin({right: 4})
Text($r('app.string.humidity')).fontColor(Color.White).fontSize(14).fontWeight(FontWeight.Bold)
}.margin({top: 8, bottom: 2})
Row() {
Image($r('app.media.ic_small_wind')).width(16).margin({ right: 4 });
Text($r('app.string.wind')).fontColor(Color.White).fontSize(14).fontWeight(FontWeight.Bold)
}.margin({ bottom: 2 })

Text($r('app.string.relative_humidity_2m', `${this.current?.relative_humidity_2m}${this.units?.relative_humidity_2m}`)).fontColor(Color.White).fontSize(12).margin({left: 21})

}.layoutWeight(0.5)
.alignItems(HorizontalAlign.Start)
.margin({top: 8})

Column() {
WeatherIcon({
weatherCode: this.current?.weather_code,
isDay: this.current?.is_day === 1 ? true : false
}).width(128).height(128)
Text($r('app.string.wind_speed_10m', `${this.current?.wind_speed_10m} ${this.units?.wind_speed_10m}`))
.fontColor(Color.White)
.fontSize(12)
.margin({ left: 21 })
Text($r('app.string.wind_direction_10m', `${this.current?.wind_direction_10m}${this.units?.wind_direction_10m}`))
.fontColor(Color.White)
.fontSize(12)
.margin({ left: 21 })
Text($r('app.string.wind_gusts_10m', `${this.current?.wind_gusts_10m} ${this.units?.wind_gusts_10m}`))
.fontColor(Color.White)
.fontSize(12)
.margin({ left: 21 })

Row() {
TemperatureIcon({
temp: this.current?.temperature_2m
}).width(32);
Row() {
Image($r('app.media.ic_small_pressure')).width(16).margin({ right: 4 })
Text($r('app.string.pressure')).fontColor(Color.White).fontSize(14).fontWeight(FontWeight.Bold)
}.margin({ top: 8, bottom: 2 })

Text(`${this.current?.temperature_2m}`)
.fontSize(36)
.fontColor(Color.White)
.textShadow({
color: '#000000',
offsetX: 2,
offsetY: 2,
radius: 2
})

Text(`${this.units?.temperature_2m}`)
.fontSize(16)
Text(`${this.current?.surface_pressure} ${this.units?.surface_pressure}`)
.fontColor(Color.White)
.textShadow({
color: '#000000',
offsetX: 2,
offsetY: 2,
radius: 2
}).margin({top: -13, left: 3})
}
.fontSize(12)
.margin({ left: 21 })

}.layoutWeight(0.5).alignItems(HorizontalAlign.Center)
Row() {
Image($r('app.media.ic_small_humidity')).width(16).margin({ right: 4 })
Text($r('app.string.humidity')).fontColor(Color.White).fontSize(14).fontWeight(FontWeight.Bold)
}.margin({ top: 8, bottom: 2 })

Text($r('app.string.relative_humidity_2m', `${this.current?.relative_humidity_2m}${this.units?.relative_humidity_2m}`))
.fontColor(Color.White)
.fontSize(12)
.margin({ left: 21 })

}.width("50%")
.alignItems(HorizontalAlign.Start)
.margin({ top: 8 })

Column() {
WeatherIcon({
weatherCode: this.current?.weather_code,
isDay: this.current?.is_day === 1 ? true : false
}).width(128).height(128)

Row() {
TemperatureIcon({
temp: this.current?.temperature_2m
}).width(32);

Text(`${this.current?.temperature_2m}`)
.fontSize(36)
.fontColor(Color.White)
.textShadow({
color: '#000000',
offsetX: 2,
offsetY: 2,
radius: 2
})

Text(`${this.units?.temperature_2m}`)
.fontSize(16)
.fontColor(Color.White)
.textShadow({
color: '#000000',
offsetX: 2,
offsetY: 2,
radius: 2
}).margin({ top: -13, left: 3 })
}

}.width("50%").alignItems(HorizontalAlign.Center)
}.width("100%")

} else {
Text($r('app.string.no_weather_data'))
.fontColor(Color.White)
.fontSize(12)
.margin({top: 16, left: 4})
}

//Text(JSON.stringify(this.currentWeather)).fontSize(8).fontColor(Color.Gray);
Expand Down
28 changes: 13 additions & 15 deletions entry/src/main/ets/view/ResultsComponent.ets
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,22 @@ export struct ResultsComponent {

CityInfoItem({ city: item })

}.onClick(event => {
}.onClick(event => this.handleOnClick(item))

const cw = CategoryViewModel.getCurrentWeather(item)
const f = CategoryViewModel.getForecast(item)

this.selectedCity = item;

cw.then(currentWeather => {
this.currentWeather = currentWeather;
});

f.then(forecast => {
this.forecast = forecast;
this.viewState = ViewState.SHOW_CITY
});
})
})
}.height("90%").scrollBar(BarState.Off)
}
}

handleOnClick(city: City) {

this.selectedCity = city;
const weatherData = CategoryViewModel.getWeatherData(city);

weatherData.then(data => {
this.currentWeather = data.currentWeather;
this.forecast = data.forecast;
this.viewState = ViewState.SHOW_CITY
});
}
}
31 changes: 25 additions & 6 deletions entry/src/main/ets/view/SearchBarComponent.ets
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,23 @@ import { AppError } from '../common/AppError';
@Component
export struct SearchBarComponent {

private static readonly SEARCH_DELAY_MS: number = 500;

@Link cities: City[]
@Link viewState: ViewState
@Link error: AppError
@State submitValue: string = ''
@State changeValue: string = ''
controller: SearchController = new SearchController()
lastSearchTime: number = 0
searchTimeout: number = 0

build() {

Column() {

Search({ value: this.changeValue, placeholder: CommonConstants.SEARCH_PLACEHOLDER, controller: this.controller })
.enableKeyboardOnFocus(true)
.searchButton('SEARCH')
.height($r('app.float.search_height'))
.border({ radius: $r('app.float.search_radius') })
.shadow(ShadowStyle.OUTER_DEFAULT_SM)
Expand All @@ -37,15 +40,31 @@ export struct SearchBarComponent {
top: $r('app.float.search_margin_top'),
bottom: $r('app.float.search_margin_bottom')
})
.onChange((value: string) => {
console.debug('onChange: ', value);
this.changeValue = value;
})
.onChange((value: string) => this.handleOnChange(value))
.onSubmit((value: string) => this.handleOnSubmit(value))
}
}

handleOnSubmit(value: string) {
handleOnChange(value: string): void {
this.changeValue = value;

if (value.trim().length < 3) {
return;
}

const currentTime = Date.now();
if (currentTime - this.lastSearchTime < SearchBarComponent.SEARCH_DELAY_MS) {
clearTimeout(this.searchTimeout);
this.searchTimeout = setTimeout(() => this.handleOnSubmit(value), SearchBarComponent.SEARCH_DELAY_MS);

} else {
this.handleOnSubmit(value);
}

this.lastSearchTime = currentTime;
}

handleOnSubmit(value: string): void {

if (value.trim() === '') {
return
Expand Down
40 changes: 16 additions & 24 deletions entry/src/main/ets/viewmodel/CategoryViewModel.ets
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import hilog from '@ohos.hilog';
import { City, CurrentWeather, Forecast } from '../model/WeatherModel';
import { City, WeatherData } from '../model/WeatherModel';
import { WeatherAPIService } from '../net/WeatherAPIService';
import { MapBoxAPIService } from '../net/MapBoxAPIService';

export class CategoryViewModel {

Expand All @@ -13,36 +11,30 @@ export class CategoryViewModel {
return apiService.searchCity(query);
}

async getCurrentWeather(city: City): Promise<CurrentWeather|null> {
async getWeatherData(city: City): Promise<WeatherData> {
console.info('getWeatherData: ', city);

const apiService = new WeatherAPIService();

try {
const currentWeather = apiService.getCurrentWeather(city.lat, city.lon);
return currentWeather;

} catch (err) {

hilog.error(0x0000, 'CategoryViewModel', '%{public}s %{public}s ', 'Error: ', JSON.stringify(err));
}

return null;
}

async getForecast(city: City): Promise<Forecast|null> {

const apiService = new WeatherAPIService();

try {
const forecast = apiService.getForecast(city.lat, city.lon);
return forecast;
const data = await Promise.all([
apiService.getCurrentWeather(city.lat, city.lon),
apiService.getForecast(city.lat, city.lon)
]);

return {
currentWeather: data[0],
forecast: data[1]
};
} catch (err) {
console.info('Get weather data error: ', err);

hilog.error(0x0000, 'CategoryViewModel', '%{public}s %{public}s ', 'Error: ', JSON.stringify(err));
return {
currentWeather: null,
forecast: null
};
}

return null;
}
}

Expand Down
2 changes: 1 addition & 1 deletion entry/src/main/module.json5
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"srcEntry": "./ets/entryability/EntryAbility.ts",
"description": "$string:ability_desc",
"icon": "$media:icon",
"label": "$string:ability_label",
"label": "$string:app_name",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
Expand Down
10 changes: 8 additions & 2 deletions entry/src/main/resources/base/element/string.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"string": [

{
"name": "app_name",
"value": "MyMeteo"
},
{
"name": "time",
"value": "Time: %s"
Expand Down Expand Up @@ -81,7 +84,6 @@
"name": "wind_gusts_10m",
"value": "Gusts: %s"
},

{
"name": "module_desc",
"value": "This module template implements Category functions."
Expand Down Expand Up @@ -117,6 +119,10 @@
{
"name": "title",
"value": "Category"
},
{
"name": "no_weather_data",
"value": "No weather data..."
}
]
}

0 comments on commit a5454e4

Please sign in to comment.