import SwiftUI struct WallpaperItem: View { let wallpaper: Wallpaper let action: () -> Void @ObservedObject var favoritesManager = FavoritesManager.shared @State private var retryID = UUID() @State private var isHovering = false var body: some View { Button(action: action) { AsyncImage(url: URL(string: wallpaper.thumbs.small)) { phase in switch phase { case .empty: Rectangle() .fill(Color.gray.opacity(0.2)) .overlay(ProgressView()) case .success(let image): image .resizable() .aspectRatio(contentMode: .fill) case .failure: Button { retryID = UUID() } label: { Rectangle() .fill(Color.red.opacity(0.1)) .overlay( VStack { Image(systemName: "exclamationmark.triangle") Text("Retry") .font(.caption) } ) } .buttonStyle(.plain) @unknown default: EmptyView() } } .id(retryID) .frame(height: 100) .clipped() .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(isHovering ? Color.accentColor : Color.clear, lineWidth: 2) ) .shadow(radius: isHovering ? 4 : 0) .overlay( // Favorite Button Group { if isHovering || favoritesManager.isFavorite(wallpaper) { Button { favoritesManager.toggleFavorite(wallpaper) } label: { Image(systemName: favoritesManager.isFavorite(wallpaper) ? "heart.fill" : "heart") .foregroundColor(favoritesManager.isFavorite(wallpaper) ? .red : .white) .shadow(radius: 2) .font(.system(size: 16, weight: .bold)) .padding(6) .background(Color.black.opacity(0.3)) .clipShape(Circle()) } .buttonStyle(.plain) .padding(6) .transition(.opacity) } }, alignment: .topTrailing ) .scaleEffect(isHovering ? 1.02 : 1.0) .animation(.spring(response: 0.3), value: isHovering) } .buttonStyle(.plain) .onHover { isHovering = $0 } .help("\(wallpaper.resolution)\n\(wallpaper.purity)") } }