todos-contra-el-fuego-mobile/lib/models/fires_api.dart
vjrj 30a89fc5c0 Fix 13 lint issues: immutability, imports, async/await, and code style
High-priority fixes (10 issues):
- assignment_to_final errors (7): Use copyWith() to respect immutability in YourLocation, active_fires.dart, generic_map.dart, location_utils.dart, and fetch_data_middleware.dart instead of direct assignment
- Fixed import paths (3): Updated falsePositiveTypes.dart → false_positive_types.dart and firesApi.dart → fires_api.dart in reducers.dart, fire_notification_actions.dart, fires_api.dart

Medium/Low priority fixes (3 issues):
- unnecessary_non_null_assertion: Removed redundant '!' in app_intro_page.dart line 54
- use_super_parameters: Updated app_intro_page.dart to use modern super(key: key) syntax
- no_logic_in_create_state: Fixed material_app_with_intro.dart by moving parameters to State instead of createState
- noop_primitive_operations: Removed redundant .toString() in string interpolation
- avoid_void_async: Changed void _selectLocation to Future<void> in places_autocomplete_utils.dart
- avoid_dynamic_calls: Fixed dynamic map access in fires_api.dart by properly casting intermediate values

Result: Reduced lint issues from 56 to 20 (all remaining are by-design: library_private_types_in_public_api and implementation_imports from external packages)
APK builds successfully with no errors.
2026-03-12 09:16:20 +01:00

248 lines
9.1 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import '../globals.dart' as globals;
import '../object_id_utils.dart';
import '../redux/actions.dart';
import 'app_state.dart';
import 'false_positive_types.dart';
import 'your_location.dart';
class FiresApi {
FiresApi() {
_dio = Dio();
}
late final Dio _dio;
Future<String> createUser(
AppState state, String mobileToken, String lang) async {
final Map<String, String> params = <String, String>{
'token': state.firesApiKey,
'mobileToken': mobileToken,
'lang': lang
};
final String url = '${state.firesApiUrl}mobile/users';
try {
final Response<Map<String, dynamic>> response =
await _dio.post<Map<String, dynamic>>(url, data: params);
if (response.statusCode == 200) {
final Map<String, dynamic>? responseData = response.data;
if (responseData == null) {
throw Exception('Response data is null');
}
final Map<String, dynamic> data = responseData;
final Map<String, dynamic> dataData =
data['data'] as Map<String, dynamic>;
return dataData['userId'] as String;
} else {
throw Exception('Unexpected error on create user');
}
} catch (e) {
throw Exception('Error creating user: $e');
}
}
Future<List<YourLocation>> fetchYourLocations(AppState state) async {
final String apiKey = state.firesApiKey;
final String mobileToken = state.user.token;
final String url =
'${state.firesApiUrl}mobile/subscriptions/all/$apiKey/$mobileToken';
try {
final Response<Map<String, dynamic>> response =
await _dio.get<Map<String, dynamic>>(url);
if (response.statusCode == 200) {
final Map<String, dynamic>? responseData = response.data;
if (responseData == null) {
throw Exception('Response data is null');
}
final Map<String, dynamic> data = responseData;
final Map<String, dynamic> dataData =
data['data'] as Map<String, dynamic>;
final List<dynamic> dataSubscriptions =
dataData['subscriptions'] as List<dynamic>;
final List<YourLocation> subscribed = <YourLocation>[];
for (int i = 0; i < dataSubscriptions.length; i++) {
final Map<String, dynamic> el =
dataSubscriptions[i] as Map<String, dynamic>;
final Map<String, dynamic> location =
el['location'] as Map<String, dynamic>;
final double lat = (location['lat'] as num).toDouble();
final double lon = (location['lon'] as num).toDouble();
final Map<String, dynamic> id = el['_id'] as Map<String, dynamic>;
subscribed.add(YourLocation(
id: objectIdFromJson(id['_str'] as String),
lat: lat,
lon: lon,
subscribed: true,
distance: (el['distance'] as num).toInt()));
}
return subscribed;
} else {
throw Exception('Unexpected error fetching your locations');
}
} catch (e) {
throw Exception('Error fetching locations: $e');
}
}
Future<String> subscribe(AppState state, YourLocation loc) async {
final Map<String, Object> params = <String, Object>{
'token': state.firesApiKey,
'mobileToken': state.user.token,
'id': loc.id.hexString,
'lat': loc.lat,
'lon': loc.lon,
'distance': loc.distance
};
final String url = '${state.firesApiUrl}mobile/subscriptions';
try {
final Response<Map<String, dynamic>> response =
await _dio.post<Map<String, dynamic>>(url, data: params);
if (response.statusCode == 200) {
final Map<String, dynamic>? responseData = response.data;
if (responseData == null) {
throw Exception('Response data is null');
}
final Map<String, dynamic> data = responseData;
final Map<String, dynamic> dataData =
data['data'] as Map<String, dynamic>;
return dataData['subsId'] as String;
} else {
throw Exception('Unexpected error on subscribe');
}
} catch (e) {
throw Exception('Error subscribing: $e');
}
}
Future<bool> unsubscribe(AppState state, String subsId) async {
final String apiKey = state.firesApiKey;
final String mobileToken = state.user.token;
final String url =
'${state.firesApiUrl}mobile/subscriptions/$apiKey/$mobileToken/$subsId';
try {
final Response<dynamic> response = await _dio.delete(url);
if (response.statusCode == 200) {
return true;
} else {
throw Exception('Unexpected error on unsubscribe');
}
} catch (e) {
throw Exception('Error unsubscribing: $e');
}
}
Future<UpdateFireMapStatsAction> getFiresInLocation(
{required AppState state,
required double lat,
required double lon,
required int distance}) async {
final String url =
'${state.firesApiUrl}fires-in-full/${state.firesApiKey}/$lat/$lon/$distance';
if (globals.isDevelopment) {
debugPrint(url);
}
try {
final Response<dynamic> response = await _dio.get(url);
if (response.statusCode == 200) {
final Map<String, dynamic> resultDecoded =
response.data as Map<String, dynamic>;
final int numFires = (resultDecoded['real'] as num).toInt();
final List<dynamic> fires = resultDecoded['fires'] as List<dynamic>;
final List<dynamic> falsePos =
resultDecoded['falsePos'] as List<dynamic>;
final List<dynamic> industries =
resultDecoded['industries'] as List<dynamic>;
if (globals.isDevelopment) {
// Could log: fires: ${fires.length} falsePos: ${falsePos.length} industries: ${industries.length}
}
return UpdateFireMapStatsAction(
numFires: numFires,
fires: fires,
falsePos: falsePos,
industries: industries);
} else
throw Exception('Wrong response trying to get fire data');
} catch (e) {
throw Exception('Error getting fires: $e');
}
}
Future<List<Polyline>> getMonitoredAreas({required AppState state}) async {
final String url =
'${state.firesApiUrl}status/subs-public-union/${state.firesApiKey}';
const Color color = Color(0xFF145A32);
try {
final Response<dynamic> response = await _dio.get(url);
if (response.statusCode == 200) {
final Map<String, dynamic> resultDecoded =
response.data as Map<String, dynamic>;
final List<Polyline> union = <Polyline>[];
final Map<String, dynamic> dataData =
resultDecoded['data'] as Map<String, dynamic>;
final Map<String, dynamic> unionData =
dataData['union'] as Map<String, dynamic>;
final String unionValue = unionData['value'] as String;
final Map<String, dynamic> decodedJson =
json.decode(unionValue) as Map<String, dynamic>;
final Map<String, dynamic> geometry =
decodedJson['geometry'] as Map<String, dynamic>;
final List<dynamic> multipolygon =
geometry['coordinates'] as List<dynamic>;
for (final dynamic polygonDynamic in multipolygon) {
final List<dynamic> polygon = polygonDynamic as List<dynamic>;
for (final dynamic holeDynamic in polygon) {
final List<dynamic> hole = holeDynamic as List<dynamic>;
final List<LatLng> points = <LatLng>[];
for (final dynamic pointDynamic in hole) {
final List<dynamic> point = pointDynamic as List<dynamic>;
points.add(LatLng(
(point[1] as num).toDouble(), (point[0] as num).toDouble()));
}
union.add(Polyline(points: points, color: color, strokeWidth: 3.0));
}
}
return union;
} else
throw Exception('Wrong response trying to get fire data');
} catch (e) {
throw Exception('Error getting monitored areas: $e');
}
}
Future<bool> markFalsePositive(AppState state, String mobileToken,
String sealed, FalsePositiveType type) async {
final Map<String, String> params = <String, String>{
'token': state.firesApiKey,
'mobileToken': mobileToken,
'sealed': sealed,
'type': type.toString().split('.')[1]
};
final String url = '${state.firesApiUrl}mobile/falsepositive';
try {
final Response<dynamic> response = await _dio.post(url, data: params);
if (response.statusCode == 200) {
if (globals.isDevelopment) {
final Map<String, dynamic> data =
response.data as Map<String, dynamic>;
final Map<String, dynamic> dataData =
data['data'] as Map<String, dynamic>;
debugPrint(dataData['upsert'].toString());
}
return true;
} else {
debugPrint(response.data.toString());
return false;
}
} catch (e) {
debugPrint('Error marking false positive: $e');
return false;
}
}
}