- Updated Android build files: gradle plugin 8.2.2, gradle 8.3, SDK 34, minSdk 21 - Ran dart fix --apply for 87 automatic null-safety fixes - Migrated flutter_map v6 API breaking changes: - MapOptions: center → initialCenter, zoom → initialZoom - layers → children structure - TileLayerOptions/MarkerLayerOptions/PolylineLayerOptions → TileLayer/MarkerLayer/PolylineLayer - Removed plugin_api.dart imports - Converted old plugin system (ZoomMapPlugin, AttributionPlugin, etc.) to direct widgets - Updated onTap callback signature: (TapPosition) → (TapPosition, LatLng) - Migrated all marker/polyline creation to v6 API - Fixed FlatButton → TextButton deprecation - Fixed stackTrace access with catch(e, stackTrace) pattern - Removed deprecated flutter_google_places_autocomplete dependency - Removed deprecated dependencies: latlong, connectivity, launch_review - Updated all imports from latlong → latlong2 - Placeholder implementation for places autocomplete (feature temporarily disabled) Remaining tasks (non-blocking for build): - Complete null-safety fixes for _location variables in genericMap.dart - Fix theme.dart MaterialTheme parameter issues - Fix customStepper.dart null-safety issues - Final build and testing
203 lines
6.3 KiB
Dart
203 lines
6.3 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
|
|
import 'package:dio/dio.dart';
|
|
import 'package:fires_flutter/models/yourLocation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_map/flutter_map.dart';
|
|
import 'package:latlong2/latlong.dart';
|
|
|
|
import '../globals.dart' as globals;
|
|
import '../objectIdUtils.dart';
|
|
import '../redux/actions.dart';
|
|
import 'appState.dart';
|
|
import 'falsePositiveTypes.dart';
|
|
|
|
class FiresApi {
|
|
late final Dio _dio;
|
|
|
|
FiresApi() {
|
|
_dio = Dio();
|
|
}
|
|
|
|
Future<String> createUser(
|
|
AppState state, String mobileToken, String lang) async {
|
|
|
|
final params = {
|
|
"token": state.firesApiKey,
|
|
"mobileToken": mobileToken,
|
|
"lang": lang
|
|
};
|
|
final String url = '${state.firesApiUrl}mobile/users';
|
|
try {
|
|
final response = await _dio.post(url, data: params);
|
|
if (response.statusCode == 200) {
|
|
return response.data['data']['userId'];
|
|
} else {
|
|
throw "Unexpected error on create user";
|
|
}
|
|
} catch (e) {
|
|
throw "Error creating user: $e";
|
|
}
|
|
}
|
|
|
|
Future<List<YourLocation>> fetchYourLocations(AppState state) async {
|
|
final apiKey = state.firesApiKey;
|
|
final mobileToken = state.user.token;
|
|
final String url =
|
|
'${state.firesApiUrl}mobile/subscriptions/all/$apiKey/$mobileToken';
|
|
try {
|
|
final response = await _dio.get(url);
|
|
if (response.statusCode == 200) {
|
|
final dataSubscriptions = response.data['data']['subscriptions'];
|
|
List<YourLocation> subscribed = [];
|
|
for (int i = 0; i < dataSubscriptions.length; i++) {
|
|
var el = dataSubscriptions[i];
|
|
var lat = el['location']['lat'];
|
|
var lon = el['location']['lon'];
|
|
subscribed.add(YourLocation(
|
|
id: objectIdFromJson(el['_id']['_str']),
|
|
lat: lat,
|
|
lon: lon,
|
|
subscribed: true,
|
|
distance: el['distance']));
|
|
}
|
|
return subscribed;
|
|
} else {
|
|
throw "Unexpected error fetching your locations";
|
|
}
|
|
} catch (e) {
|
|
throw "Error fetching locations: $e";
|
|
}
|
|
}
|
|
|
|
Future<String> subscribe(AppState state, YourLocation loc) async {
|
|
final params = {
|
|
"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 = await _dio.post(url, data: params);
|
|
if (response.statusCode == 200) {
|
|
return response.data['data']['subsId'];
|
|
} else {
|
|
print(response.data);
|
|
throw "Unexpected error on subscribe";
|
|
}
|
|
} catch (e) {
|
|
throw "Error subscribing: $e";
|
|
}
|
|
}
|
|
|
|
Future<bool> unsubscribe(AppState state, String subsId) async {
|
|
final apiKey = state.firesApiKey;
|
|
final mobileToken = state.user.token;
|
|
final String url =
|
|
'${state.firesApiUrl}mobile/subscriptions/$apiKey/$mobileToken/$subsId';
|
|
try {
|
|
final response = await _dio.delete(url);
|
|
if (response.statusCode == 200) {
|
|
return true;
|
|
} else {
|
|
throw "Unexpected error on unsubscribe";
|
|
}
|
|
} catch (e) {
|
|
throw "Error unsubscribing: $e";
|
|
}
|
|
}
|
|
|
|
Future<UpdateFireMapStatsAction> getFiresInLocation(
|
|
{required AppState state,
|
|
required double lat,
|
|
required double lon,
|
|
required int distance}) async {
|
|
var url =
|
|
'${state.firesApiUrl}fires-in-full/${state.firesApiKey}/$lat/$lon/$distance';
|
|
if (globals.isDevelopment) print(url);
|
|
try {
|
|
final response = await _dio.get(url);
|
|
if (response.statusCode == 200) {
|
|
var resultDecoded = response.data;
|
|
int numFires = resultDecoded['real'];
|
|
List fires = resultDecoded['fires'];
|
|
List falsePos = resultDecoded['falsePos'];
|
|
List industries = resultDecoded['industries'];
|
|
|
|
if (globals.isDevelopment) {
|
|
var firesCount = fires.length;
|
|
var industriesCount = industries.length;
|
|
var falsePosCount = falsePos.length;
|
|
print(
|
|
'(Pos: $lat, $lon) real: $numFires, fire: $firesCount falsePos: $falsePosCount industries: $industriesCount');
|
|
}
|
|
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 {
|
|
var url =
|
|
'${state.firesApiUrl}status/subs-public-union/${state.firesApiKey}';
|
|
var color = const Color(0xFF145A32);
|
|
try {
|
|
final response = await _dio.get(url);
|
|
if (response.statusCode == 200) {
|
|
var resultDecoded = response.data;
|
|
List<Polyline> union = [];
|
|
final multipolygon =
|
|
json.decode(resultDecoded['data']['union']['value'])['geometry']
|
|
['coordinates'];
|
|
for (List<dynamic> polygon in multipolygon) {
|
|
for (List<dynamic> hole in polygon) {
|
|
List<LatLng> points = [];
|
|
for (List<dynamic> point in hole) {
|
|
points.add(LatLng(point[1].toDouble(), point[0].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 params = {
|
|
"token": state.firesApiKey,
|
|
"mobileToken": mobileToken,
|
|
"sealed": sealed,
|
|
"type": type.toString().split('.')[1]
|
|
};
|
|
final String url = '${state.firesApiUrl}mobile/falsepositive';
|
|
try {
|
|
final response = await _dio.post(url, data: params);
|
|
if (response.statusCode == 200) {
|
|
if (globals.isDevelopment) print(response.data['data']['upsert']);
|
|
return true;
|
|
} else {
|
|
debugPrint(response.data.toString());
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
debugPrint("Error marking false positive: $e");
|
|
return false;
|
|
}
|
|
}
|
|
}
|