add filter by network for legs, add full export for traction
All checks were successful
Release / meta (push) Successful in 1m45s
Release / linux-build (push) Successful in 1m48s
Release / web-build (push) Successful in 1m56s
Release / android-build (push) Successful in 7m33s
Release / release-dev (push) Successful in 30s
Release / release-master (push) Successful in 5s
All checks were successful
Release / meta (push) Successful in 1m45s
Release / linux-build (push) Successful in 1m48s
Release / web-build (push) Successful in 1m56s
Release / android-build (push) Successful in 7m33s
Release / release-dev (push) Successful in 30s
Release / release-master (push) Successful in 5s
This commit is contained in:
@@ -119,6 +119,45 @@ class ApiService {
|
||||
return _processResponse(response);
|
||||
}
|
||||
|
||||
Future<ApiBinaryResponse> postBytes(
|
||||
String endpoint,
|
||||
dynamic data, {
|
||||
Map<String, String>? headers,
|
||||
bool includeAuth = true,
|
||||
bool allowRetry = true,
|
||||
}) async {
|
||||
final hasBody = data != null;
|
||||
final response = await _sendWithRetry(
|
||||
() => _client.post(
|
||||
Uri.parse('$baseUrl$endpoint'),
|
||||
headers: _buildHeaders(
|
||||
hasBody ? _jsonHeaders(headers) : headers,
|
||||
includeAuth: includeAuth,
|
||||
),
|
||||
body: hasBody ? jsonEncode(data) : null,
|
||||
),
|
||||
allowRetry: allowRetry,
|
||||
);
|
||||
|
||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||
final contentDisposition = response.headers['content-disposition'];
|
||||
return ApiBinaryResponse(
|
||||
bytes: response.bodyBytes,
|
||||
statusCode: response.statusCode,
|
||||
contentType: response.headers['content-type'],
|
||||
filename: _extractFilename(contentDisposition),
|
||||
);
|
||||
}
|
||||
|
||||
final body = _decodeBody(response);
|
||||
final message = _extractErrorMessage(body);
|
||||
throw ApiException(
|
||||
statusCode: response.statusCode,
|
||||
message: message,
|
||||
body: body,
|
||||
);
|
||||
}
|
||||
|
||||
Future<dynamic> postMultipartFile(
|
||||
String endpoint, {
|
||||
required List<int> bytes,
|
||||
|
||||
@@ -7,6 +7,7 @@ class _LegFetchOptions {
|
||||
final String? dateRangeStart;
|
||||
final String? dateRangeEnd;
|
||||
final bool unallocatedOnly;
|
||||
final List<String> networkFilter;
|
||||
|
||||
const _LegFetchOptions({
|
||||
this.limit = 100,
|
||||
@@ -15,6 +16,7 @@ class _LegFetchOptions {
|
||||
this.dateRangeStart,
|
||||
this.dateRangeEnd,
|
||||
this.unallocatedOnly = false,
|
||||
this.networkFilter = const [],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -118,6 +120,7 @@ class DataService extends ChangeNotifier {
|
||||
List<String> _stationNetworks = [];
|
||||
Map<String, List<String>> _stationCountryNetworks = {};
|
||||
DateTime? _stationFiltersFetchedAt;
|
||||
DateTime? _stationNetworksFetchedAt;
|
||||
List<String> get stationNetworks => _stationNetworks;
|
||||
Map<String, List<String>> get stationCountryNetworks =>
|
||||
_stationCountryNetworks;
|
||||
@@ -391,9 +394,14 @@ class DataService extends ChangeNotifier {
|
||||
String? dateRangeEnd,
|
||||
bool append = false,
|
||||
bool unallocatedOnly = false,
|
||||
List<String> networkFilter = const [],
|
||||
}) async {
|
||||
_isLegsLoading = true;
|
||||
if (!append) {
|
||||
final normalizedNetworks = networkFilter
|
||||
.map((network) => network.trim())
|
||||
.where((network) => network.isNotEmpty)
|
||||
.toList();
|
||||
_lastLegsFetch = _LegFetchOptions(
|
||||
limit: limit,
|
||||
sortBy: sortBy,
|
||||
@@ -401,6 +409,7 @@ class DataService extends ChangeNotifier {
|
||||
dateRangeStart: dateRangeStart,
|
||||
dateRangeEnd: dateRangeEnd,
|
||||
unallocatedOnly: unallocatedOnly,
|
||||
networkFilter: normalizedNetworks,
|
||||
);
|
||||
}
|
||||
final buffer = StringBuffer(
|
||||
@@ -415,6 +424,13 @@ class DataService extends ChangeNotifier {
|
||||
if (unallocatedOnly) {
|
||||
buffer.write('&unallocated_only=true');
|
||||
}
|
||||
final networks = networkFilter
|
||||
.map((network) => network.trim())
|
||||
.where((network) => network.isNotEmpty)
|
||||
.toList();
|
||||
for (final network in networks) {
|
||||
buffer.write('&network_filter=${Uri.encodeQueryComponent(network)}');
|
||||
}
|
||||
try {
|
||||
final json = await api.get('/user/legs${buffer.toString()}');
|
||||
|
||||
@@ -444,6 +460,7 @@ class DataService extends ChangeNotifier {
|
||||
dateRangeStart: _lastLegsFetch.dateRangeStart,
|
||||
dateRangeEnd: _lastLegsFetch.dateRangeEnd,
|
||||
unallocatedOnly: _lastLegsFetch.unallocatedOnly,
|
||||
networkFilter: _lastLegsFetch.networkFilter,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -669,6 +686,7 @@ class DataService extends ChangeNotifier {
|
||||
_stationNetworks = [];
|
||||
_stationCountryNetworks = {};
|
||||
_stationFiltersFetchedAt = null;
|
||||
_stationNetworksFetchedAt = null;
|
||||
_notifications = [];
|
||||
_isNotificationsLoading = false;
|
||||
_userEntriesVisibility = 'private';
|
||||
@@ -736,6 +754,9 @@ class DataService extends ChangeNotifier {
|
||||
final networks = (map['networks'] as List? ?? const [])
|
||||
.whereType<String>()
|
||||
.toList();
|
||||
networks.sort(
|
||||
(a, b) => a.toLowerCase().compareTo(b.toLowerCase()),
|
||||
);
|
||||
final countryNetworksRaw =
|
||||
map['country_networks'] as Map? ?? const <String, dynamic>{};
|
||||
final countryNetworks = <String, List<String>>{};
|
||||
@@ -753,6 +774,31 @@ class DataService extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchStationNetworks() async {
|
||||
final now = DateTime.now();
|
||||
final recent = _stationNetworks.isNotEmpty &&
|
||||
((_stationNetworksFetchedAt != null &&
|
||||
now.difference(_stationNetworksFetchedAt!) <
|
||||
const Duration(minutes: 30)) ||
|
||||
(_stationFiltersFetchedAt != null &&
|
||||
now.difference(_stationFiltersFetchedAt!) <
|
||||
const Duration(minutes: 30)));
|
||||
if (recent) return;
|
||||
try {
|
||||
final response = await api.get('/stations/networks');
|
||||
if (response is List) {
|
||||
final networks = response.whereType<String>().toList();
|
||||
networks.sort(
|
||||
(a, b) => a.toLowerCase().compareTo(b.toLowerCase()),
|
||||
);
|
||||
_stationNetworks = networks;
|
||||
_stationNetworksFetchedAt = now;
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Failed to fetch station networks: $e');
|
||||
}
|
||||
}
|
||||
|
||||
String _stationKey(List<String> countries, List<String> networks) {
|
||||
final c = countries..sort();
|
||||
final n = networks..sort();
|
||||
|
||||
Reference in New Issue
Block a user