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
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:
parent
275ff88615
commit
9ed1a00a38
1 changed files with 117 additions and 7 deletions
|
|
@ -173,6 +173,22 @@ class _ExpiringCardImageState extends State<ExpiringCardImage> {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (mounted) {
|
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(() {
|
setState(() {
|
||||||
_currentUrl = newUrl;
|
_currentUrl = newUrl;
|
||||||
_isRefreshing = false;
|
_isRefreshing = false;
|
||||||
|
|
@ -194,22 +210,111 @@ class _ExpiringCardImageState extends State<ExpiringCardImage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isExpiredError(dynamic error) {
|
bool _isExpiredError(dynamic error) {
|
||||||
// Check if error is a 403 or 404 (expired or not found)
|
if (error == null) return false;
|
||||||
if (error is Exception) {
|
|
||||||
final errorString = error.toString().toLowerCase();
|
// Log error details for debugging
|
||||||
return errorString.contains('403') ||
|
log(
|
||||||
errorString.contains('forbidden') ||
|
'Checking error for 403/expired: type=${error.runtimeType}, value=$error',
|
||||||
errorString.contains('expired');
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildErrorWidget(BuildContext context, String url, dynamic error) {
|
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
|
// Check if we can refresh the URL
|
||||||
final objectId = _getObjectId();
|
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) {
|
if (canRefresh && !_isRefreshing) {
|
||||||
|
log(
|
||||||
|
'Scheduling presigned URL refresh for objectId: $objectId',
|
||||||
|
name: 'ExpiringCardImage',
|
||||||
|
);
|
||||||
// Automatically try to refresh
|
// Automatically try to refresh
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_refreshPresignedUrl();
|
_refreshPresignedUrl();
|
||||||
|
|
@ -238,7 +343,9 @@ class _ExpiringCardImageState extends State<ExpiringCardImage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.useCachedNetworkImage) {
|
if (widget.useCachedNetworkImage) {
|
||||||
|
// Use key based on URL to force rebuild when URL changes
|
||||||
return CachedNetworkImage(
|
return CachedNetworkImage(
|
||||||
|
key: ValueKey('cached_image_$_currentUrl'),
|
||||||
imageUrl: _currentUrl!,
|
imageUrl: _currentUrl!,
|
||||||
fit: widget.fit,
|
fit: widget.fit,
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
|
|
@ -249,10 +356,13 @@ class _ExpiringCardImageState extends State<ExpiringCardImage> {
|
||||||
fadeOutDuration: widget.fadeOutDuration,
|
fadeOutDuration: widget.fadeOutDuration,
|
||||||
placeholder: widget.placeholder ?? (context, url) => const SizedBox.shrink(),
|
placeholder: widget.placeholder ?? (context, url) => const SizedBox.shrink(),
|
||||||
errorWidget: _buildErrorWidget,
|
errorWidget: _buildErrorWidget,
|
||||||
|
// Clear cache on error to allow retry with new URL
|
||||||
|
cacheKey: _currentUrl,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Image.network(
|
return Image.network(
|
||||||
_currentUrl!,
|
_currentUrl!,
|
||||||
|
key: ValueKey('network_image_$_currentUrl'),
|
||||||
fit: widget.fit,
|
fit: widget.fit,
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
height: widget.height,
|
height: widget.height,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue