fixed client-reported crashes in the pdf viewer, image picker and prosemirror images, mapped webdav errors to user messages and hardened keychain, cache and push stores against io failures

This commit is contained in:
MineTec committed 2026-10-12 17:00:14 +02:00
1 parent c5592f117b
commit 23e4510bf8
19 files changed
+545 -55

No files matched your search

+50
View File
@@ -54,11 +54,61 @@ AppException? _dioToAppException(DioException error) {
}
}
final RegExp _davException = RegExp(
r'<s:exception>\s*([^<]*?)\s*</s:exception>',
);
final RegExp _davMessage = RegExp(r'<s:message>\s*([^<]*?)\s*</s:message>');
/// The Sabre DAV error body of a failed WebDAV request: the exception class
/// (without its `Sabre\DAV\Exception\` namespace) and the server's message.
/// The class says what actually went wrong where the status is ambiguous —
/// MKCOL on an existing folder is a plain 405.
({String exception, String message})? parseDavError(String body) {
final exception = _davException.firstMatch(body)?.group(1);
if (exception == null || exception.isEmpty) return null;
return (
exception: exception.split('\\').last,
message: _davMessage.firstMatch(body)?.group(1) ?? '',
);
}
AppException? _davToAppException(
int status,
({String exception, String message}) dav,
String detail,
) {
final userMessage = switch (dav.exception) {
'MethodNotAllowed' when status == 405 =>
'Ein Ordner oder eine Datei mit diesem Namen existiert bereits.',
'PreconditionFailed' =>
'Am Zielort existiert bereits ein Eintrag mit diesem Namen.',
'Conflict' => 'Der übergeordnete Ordner existiert nicht mehr.',
'Locked' =>
'Der Eintrag wird gerade bearbeitet. Bitte versuche es gleich erneut.',
'InsufficientStorage' =>
'Der verfügbare Speicherplatz reicht dafür nicht aus.',
_ => null,
};
if (userMessage == null) return null;
return ServerException(
statusCode: status,
userMessage: userMessage,
technicalDetails: detail,
allowRetry: dav.exception == 'Locked',
);
}
/// The nextcloud/dynamite clients throw [DynamiteApiException] on non-2xx.
/// Its toString() dumps every response header, so the details keep only the
/// status plus a trimmed body preview (same format as the Talk API errors).
AppException _dynamiteToAppException(DynamiteApiException error) {
final status = error.statusCode;
final dav = parseDavError(error.body);
if (dav != null) {
final detail = 'HTTP $status ${dav.exception}: ${dav.message}';
return _davToAppException(status, dav, detail) ??
exceptionForStatus(status, detail);
}
final preview = previewBody(error.body);
final detail = preview.isEmpty
? 'HTTP $status'