minio fix
Some checks are pending
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions

This commit is contained in:
Dmitry 2026-01-08 20:32:57 +03:00
parent 275ff88615
commit 9ed1a00a38

View file

@ -173,6 +173,22 @@ class _ExpiringCardImageState extends State<ExpiringCardImage> {
);
if (mounted) {
// Clear cache for old URL before updating
if (_currentUrl != null && widget.useCachedNetworkImage) {
try {
await CachedNetworkImage.evictFromCache(_currentUrl!);
log(
'Cleared cache for old URL: $_currentUrl',
name: 'ExpiringCardImage',
);
} catch (e) {
log(
'Failed to clear cache: $e',
name: 'ExpiringCardImage',
);
}
}
setState(() {
_currentUrl = newUrl;
_isRefreshing = false;
@ -194,22 +210,111 @@ class _ExpiringCardImageState extends State<ExpiringCardImage> {
}
bool _isExpiredError(dynamic error) {
// Check if error is a 403 or 404 (expired or not found)
if (error is Exception) {
final errorString = error.toString().toLowerCase();
return errorString.contains('403') ||
errorString.contains('forbidden') ||
errorString.contains('expired');
if (error == null) return false;
// Log error details for debugging
log(
'Checking error for 403/expired: type=${error.runtimeType}, value=$error',
name: 'ExpiringCardImage',
);
// Check error string representation for HTTP status codes
final errorString = error.toString().toLowerCase();
final contains403 = errorString.contains('403') ||
errorString.contains('forbidden') ||
errorString.contains('expired') ||
errorString.contains('http status 403') ||
errorString.contains('status code 403');
if (contains403) {
log(
'Detected expired error: $errorString',
name: 'ExpiringCardImage',
);
return true;
}
// Try to extract status code from error object if it has statusCode property
try {
final statusCode = (error as dynamic).statusCode;
if (statusCode != null) {
final code = statusCode is int ? statusCode : int.tryParse(statusCode.toString());
if (code == 403 || code == 401) {
log(
'Detected expired error via statusCode: $code',
name: 'ExpiringCardImage',
);
return true;
}
}
} catch (_) {
// statusCode property doesn't exist, continue
}
// Try to check if error has response property with statusCode
try {
final response = (error as dynamic).response;
if (response != null) {
final statusCode = (response as dynamic).statusCode;
if (statusCode != null) {
final code = statusCode is int ? statusCode : int.tryParse(statusCode.toString());
if (code == 403 || code == 401) {
log(
'Detected expired error via response.statusCode: $code',
name: 'ExpiringCardImage',
);
return true;
}
}
}
} catch (_) {
// response property doesn't exist, continue
}
// Check if error is a NetworkImageLoadException (Flutter's image loading error)
try {
if (error.toString().contains('NetworkImageLoadException') ||
error.toString().contains('Failed to load network image')) {
// For network image errors, assume it might be 403 if we have a valid objectId
final objectId = _getObjectId();
if (objectId != null && _isUuid(objectId)) {
log(
'Detected NetworkImageLoadException with valid objectId, assuming expired',
name: 'ExpiringCardImage',
);
return true;
}
}
} catch (_) {
// Continue
}
return false;
}
Widget _buildErrorWidget(BuildContext context, String url, dynamic error) {
// Log error for debugging
log(
'Error widget called: url=$url, error=$error, errorType=${error?.runtimeType}',
name: 'ExpiringCardImage',
);
// Check if we can refresh the URL
final objectId = _getObjectId();
final canRefresh = objectId != null && _isUuid(objectId) && _isExpiredError(error);
final isExpired = _isExpiredError(error);
final hasValidObjectId = objectId != null && _isUuid(objectId);
final canRefresh = hasValidObjectId && isExpired;
log(
'Refresh check: objectId=$objectId, isUuid=$hasValidObjectId, isExpired=$isExpired, canRefresh=$canRefresh, isRefreshing=$_isRefreshing',
name: 'ExpiringCardImage',
);
if (canRefresh && !_isRefreshing) {
log(
'Scheduling presigned URL refresh for objectId: $objectId',
name: 'ExpiringCardImage',
);
// Automatically try to refresh
WidgetsBinding.instance.addPostFrameCallback((_) {
_refreshPresignedUrl();
@ -238,7 +343,9 @@ class _ExpiringCardImageState extends State<ExpiringCardImage> {
}
if (widget.useCachedNetworkImage) {
// Use key based on URL to force rebuild when URL changes
return CachedNetworkImage(
key: ValueKey('cached_image_$_currentUrl'),
imageUrl: _currentUrl!,
fit: widget.fit,
width: widget.width,
@ -249,10 +356,13 @@ class _ExpiringCardImageState extends State<ExpiringCardImage> {
fadeOutDuration: widget.fadeOutDuration,
placeholder: widget.placeholder ?? (context, url) => const SizedBox.shrink(),
errorWidget: _buildErrorWidget,
// Clear cache on error to allow retry with new URL
cacheKey: _currentUrl,
);
} else {
return Image.network(
_currentUrl!,
key: ValueKey('network_image_$_currentUrl'),
fit: widget.fit,
width: widget.width,
height: widget.height,