diff --git a/Packages/Status/Sources/Status/Row/Subviews/StatusRowCardView.swift b/Packages/Status/Sources/Status/Row/Subviews/StatusRowCardView.swift index 273ec117..74b5709c 100644 --- a/Packages/Status/Sources/Status/Row/Subviews/StatusRowCardView.swift +++ b/Packages/Status/Sources/Status/Row/Subviews/StatusRowCardView.swift @@ -46,44 +46,12 @@ public struct StatusRowCardView: View { } label: { if let title = card.title, let url = URL(string: card.url) { VStack(alignment: .leading) { - if let imageURL = card.image, !isInCaptureMode { - LazyResizableImage(url: imageURL) { state, proxy in - let width = imageWidthFor(proxy: proxy) - if let image = state.image { - image - .resizable() - .aspectRatio(contentMode: .fill) - .frame(height: imageHeight) - .frame(maxWidth: width) - .clipped() - } else if state.isLoading { - Rectangle() - .fill(Color.gray) - .frame(height: imageHeight) - } - } - // This image is decorative - .accessibilityHidden(true) - .frame(height: imageHeight) + let sitesWithIcons = [ "apps.apple.com", "music.apple.com", "open.spotify.com" ] + if let host = url.host(), sitesWithIcons.contains(host) { + iconLinkPreview(title, url) + } else { + defaultLinkPreview(title, url) } - HStack { - VStack(alignment: .leading, spacing: 6) { - Text(title) - .font(.scaledHeadline) - .lineLimit(3) - if let description = card.description, !description.isEmpty { - Text(description) - .font(.scaledBody) - .foregroundStyle(.secondary) - .lineLimit(3) - } - Text(url.host() ?? url.absoluteString) - .font(.scaledFootnote) - .foregroundColor(theme.tintColor) - .lineLimit(1) - } - Spacer() - }.padding(16) } .frame(maxWidth: maxWidth) .fixedSize(horizontal: false, vertical: true) @@ -114,4 +82,89 @@ public struct StatusRowCardView: View { } .buttonStyle(.plain) } + + @MainActor + private func defaultLinkPreview(_ title: String, _ url: URL) -> some View { + Group { + if let imageURL = card.image, !isInCaptureMode { + LazyResizableImage(url: imageURL) { state, proxy in + let width = imageWidthFor(proxy: proxy) + if let image = state.image { + image + .resizable() + .aspectRatio(contentMode: .fill) + .frame(height: imageHeight) + .frame(maxWidth: width) + .clipped() + } else if state.isLoading { + Rectangle() + .fill(Color.gray) + .frame(height: imageHeight) + } + } + // This image is decorative + .accessibilityHidden(true) + .frame(height: imageHeight) + } + HStack { + VStack(alignment: .leading, spacing: 6) { + Text(title) + .font(.scaledHeadline) + .lineLimit(3) + if let description = card.description, !description.isEmpty { + Text(description) + .font(.scaledBody) + .foregroundStyle(.secondary) + .lineLimit(3) + } + Text(url.host() ?? url.absoluteString) + .font(.scaledFootnote) + .foregroundColor(theme.tintColor) + .lineLimit(1) + } + Spacer() + }.padding(16) + } + } + + @MainActor + private func iconLinkPreview(_ title: String, _ url: URL) -> some View { + // ..where the image is known to be a square icon + HStack { + if let imageURL = card.image, !isInCaptureMode { + LazyResizableImage(url: imageURL) { state, proxy in + if let image = state.image { + image + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: imageHeight, height: imageHeight) + .clipped() + } else if state.isLoading { + Rectangle() + .fill(Color.gray) + .frame(width: imageHeight, height: imageHeight) + } + } + // This image is decorative + .accessibilityHidden(true) + .frame(width: imageHeight, height: imageHeight) + } + + VStack(alignment: .leading, spacing: 6) { + Text(title) + .font(.scaledHeadline) + .lineLimit(3) + if let description = card.description, !description.isEmpty { + Text(description) + .font(.scaledBody) + .foregroundStyle(.secondary) + .lineLimit(3) + } + Text(url.host() ?? url.absoluteString) + .font(.scaledFootnote) + .foregroundColor(theme.tintColor) + .lineLimit(1) + }.padding(16) + } + } }