blob: e90b9c2a199a5dd1355a2ef7771a50a3545bd519 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
import 'package:dio/dio.dart';
// ---------------------------------------------------------------------------
// Shared Dio error-mapping utilities
// ---------------------------------------------------------------------------
//
// These top-level functions centralise the conversion of [DioException]
// values into human-readable UI strings, eliminating duplicate
// _dioErrorMessage implementations that previously existed in
// bootstrap_screen.dart, login_screen.dart, and home_screen.dart (DRY/DIP).
//
// All functions are pure data-transformations: no widget state, no Riverpod
// reads, no BuildContext — making them easy to unit-test in isolation.
/// Maps an exception thrown by any API call to a human-readable UI string.
///
/// Prefers messages extracted from the [DioException] response body; falls
/// back to status-code–specific text; finally uses a generic connectivity
/// message. Pass [statusFallbacks] to supply caller-specific status-code
/// messages (e.g. 401 → "Invalid username or password." for login).
String dioErrorMessage(
DioException e, {
Map<int, String> statusFallbacks = const {},
}) {
// Prefer a human-readable message from the server's JSON response body.
final body = e.response?.data;
if (body is Map<String, dynamic>) {
final msg = body['message'] as String? ?? body['error'] as String?;
if (msg != null && msg.isNotEmpty) return msg;
}
// Apply caller-specific status-code fallbacks (e.g. auth screens).
final statusCode = e.response?.statusCode;
if (statusCode != null) {
final fallback = statusFallbacks[statusCode];
if (fallback != null) return fallback;
}
// Generic status-code fallback.
if (statusCode != null) {
return 'Server error ($statusCode). Please try again.';
}
// No HTTP response: connectivity or DNS failure.
return 'Could not reach the server. Check your network connection.';
}
/// Maps a [DioException] using connection-type heuristics instead of status
/// codes — suited for read-only data-fetching calls (e.g. listing sets)
/// where there is no login-specific 401/403 semantics.
///
/// Distinguishes between connectivity/timeout failures and server-side HTTP
/// errors so the user knows whether to check their network or contact support.
String dioConnectionErrorMessage(DioException e) {
switch (e.type) {
case DioExceptionType.connectionError:
case DioExceptionType.sendTimeout:
case DioExceptionType.receiveTimeout:
case DioExceptionType.connectionTimeout:
return 'Could not reach the server. Check your connection and try again.';
case DioExceptionType.badResponse:
final code = e.response?.statusCode ?? 0;
if (code == 401) return 'Session expired. Please log in again.';
return 'Server error ($code). Please try again.';
default:
return 'Unexpected error. Please try again.';
}
}
/// Maps any thrown object from [PlayerApiClient.listSets] to a UI string.
///
/// Delegates to [dioConnectionErrorMessage] for [DioException]; returns a
/// generic fallback for all other exception types.
String setsErrorMessage(Object error) {
if (error is DioException) {
return dioConnectionErrorMessage(error);
}
return 'Unexpected error. Please try again.';
}
/// Maps any thrown object from [PlayerApiClient.listMedia] to a UI string.
///
/// Identical delegation strategy to [setsErrorMessage]: DioExceptions are
/// mapped by [dioConnectionErrorMessage]; all other exceptions fall back to a
/// generic message. Having a separate function preserves the option to add
/// media-specific status-code overrides (e.g. 403 permission errors) later
/// without altering the sets helper (Open-Closed Principle).
String mediaErrorMessage(Object error) {
if (error is DioException) {
return dioConnectionErrorMessage(error);
}
return 'Unexpected error. Please try again.';
}
/// Maps any thrown object from [PlayerApiClient.getMedia] to a UI string.
///
/// Adds a 404-specific message ("Media not found") on top of the generic
/// connection-error mapping so the detail screen can distinguish between a
/// missing item and a network/server failure (Open-Closed: isolated from the
/// list-media helper so either can evolve independently).
String mediaDetailErrorMessage(Object error) {
if (error is DioException) {
// Surface a friendly "not found" message for 404 so users know the item
// no longer exists rather than seeing a generic server-error message.
if (error.response?.statusCode == 404) {
return 'Media not found. It may have been deleted.';
}
return dioConnectionErrorMessage(error);
}
return 'Unexpected error. Please try again.';
}
|