import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../auth/auth_provider.dart'; /// Image.network wrapper that resolves server-relative URLs (e.g. /// `/api/albums//cover`) against the configured server base URL /// from [serverUrlProvider]. Absolute URLs (with scheme) pass through /// unchanged. Empty/null base or empty URL render the [fallback] /// (defaults to a transparent SizedBox so callers can supply their own /// surrounding placeholder). /// /// Mirrors the absolute-or-relative logic the audio handler uses for /// stream URLs, so cover art and audio resolve URLs the same way. class ServerImage extends ConsumerWidget { const ServerImage({ super.key, required this.url, this.fit, this.fallback, }); final String url; final BoxFit? fit; final Widget? fallback; @override Widget build(BuildContext context, WidgetRef ref) { final empty = fallback ?? const SizedBox.shrink(); if (url.isEmpty) return empty; final base = ref.watch(serverUrlProvider).value; final resolved = _resolve(base, url); if (resolved == null) return empty; return Image.network(resolved, fit: fit); } static String? _resolve(String? baseUrl, String url) { final parsed = Uri.tryParse(url); if (parsed != null && parsed.hasScheme) return url; if (baseUrl == null || baseUrl.isEmpty) return null; final base = baseUrl.endsWith('/') ? baseUrl.substring(0, baseUrl.length - 1) : baseUrl; final path = url.startsWith('/') ? url : '/$url'; return '$base$path'; } }