Added class clearance fixes and improvements
Some checks failed
Release / meta (push) Successful in 18s
Release / android-build (push) Successful in 11m48s
Release / linux-build (push) Successful in 1m24s
Release / web-build (push) Successful in 6m15s
Release / release-dev (push) Has been cancelled
Release / release-master (push) Has been cancelled
Some checks failed
Release / meta (push) Successful in 18s
Release / android-build (push) Successful in 11m48s
Release / linux-build (push) Successful in 1m24s
Release / web-build (push) Successful in 6m15s
Release / release-dev (push) Has been cancelled
Release / release-master (push) Has been cancelled
This commit is contained in:
@@ -55,6 +55,40 @@ class ApiService {
|
||||
return _processResponse(response);
|
||||
}
|
||||
|
||||
Future<ApiBinaryResponse> getBytes(
|
||||
String endpoint, {
|
||||
Map<String, String>? headers,
|
||||
}) async {
|
||||
final response = await _client
|
||||
.get(
|
||||
Uri.parse('$baseUrl$endpoint'),
|
||||
headers: _buildHeaders(headers),
|
||||
)
|
||||
.timeout(timeout);
|
||||
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
||||
if (response.statusCode == 401 && _onUnauthorized != null) {
|
||||
await _onUnauthorized!();
|
||||
}
|
||||
|
||||
final body = _decodeBody(response);
|
||||
final message = _extractErrorMessage(body);
|
||||
throw ApiException(
|
||||
statusCode: response.statusCode,
|
||||
message: message,
|
||||
body: body,
|
||||
);
|
||||
}
|
||||
|
||||
Future<dynamic> post(
|
||||
String endpoint,
|
||||
dynamic data, {
|
||||
@@ -176,6 +210,20 @@ class ApiService {
|
||||
}
|
||||
return body.toString();
|
||||
}
|
||||
|
||||
String? _extractFilename(String? contentDisposition) {
|
||||
if (contentDisposition == null || contentDisposition.isEmpty) return null;
|
||||
final utf8Match =
|
||||
RegExp(r"filename\\*=UTF-8''([^;]+)", caseSensitive: false)
|
||||
.firstMatch(contentDisposition);
|
||||
if (utf8Match != null) {
|
||||
return Uri.decodeComponent(utf8Match.group(1) ?? '');
|
||||
}
|
||||
final match =
|
||||
RegExp(r'filename="?([^\";]+)"?', caseSensitive: false)
|
||||
.firstMatch(contentDisposition);
|
||||
return match?.group(1);
|
||||
}
|
||||
}
|
||||
|
||||
class ApiException implements Exception {
|
||||
@@ -192,3 +240,17 @@ class ApiException implements Exception {
|
||||
@override
|
||||
String toString() => 'API error $statusCode: $message';
|
||||
}
|
||||
|
||||
class ApiBinaryResponse {
|
||||
final List<int> bytes;
|
||||
final int statusCode;
|
||||
final String? contentType;
|
||||
final String? filename;
|
||||
|
||||
ApiBinaryResponse({
|
||||
required this.bytes,
|
||||
required this.statusCode,
|
||||
this.contentType,
|
||||
this.filename,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,12 +52,16 @@ extension DataServiceBadges on DataService {
|
||||
int offset = 0,
|
||||
int limit = 20,
|
||||
bool append = false,
|
||||
bool onlyIncomplete = false,
|
||||
}) async {
|
||||
_isClassClearanceProgressLoading = true;
|
||||
if (!append) _classClearanceProgress = [];
|
||||
try {
|
||||
final json =
|
||||
await api.get('/badge/completion/class?limit=$limit&offset=$offset');
|
||||
final onlyIncompleteParam =
|
||||
onlyIncomplete ? '&only_incomplete=true' : '';
|
||||
final json = await api.get(
|
||||
'/badge/completion/class?limit=$limit&offset=$offset$onlyIncompleteParam',
|
||||
);
|
||||
List<dynamic>? list;
|
||||
if (json is List) {
|
||||
list = json;
|
||||
|
||||
Reference in New Issue
Block a user