diff --git a/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Model/CardStyle.swift b/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Model/CardStyle.swift index 06db8977..a051fe53 100644 --- a/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Model/CardStyle.swift +++ b/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Model/CardStyle.swift @@ -14,13 +14,14 @@ extension NewsCategory { public var style: CardStyle? { switch self { case .highlights: .highlight - case .news, .all: nil + case .news: .leadingImage case .podcast: .glass case .youtube: .glass case .appletv: .leadingImage case .reviews: .highlight - case .tutorials: .simple - case .rumors: .simple + case .tutorials: .leadingImage + case .rumors: .leadingImage + case .all: nil } } } diff --git a/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/CardView.swift b/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/CardView.swift new file mode 100644 index 00000000..5fe2314c --- /dev/null +++ b/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/CardView.swift @@ -0,0 +1,158 @@ +import AnalyticsLibrary +import MacMagazineLibrary +import SwiftUI +import UIComponentsLibrary +import UtilityLibrary + +struct CardView: View { + @EnvironmentObject private var analytics: AnalyticsManager + @Environment(\.dynamicTypeSize) private var typeSize + @Namespace var namespace + + @State private var cardWidth: CGFloat = .zero + private let data: CardContent + private let style: CardStyle + private var density: CardDensity { .from(width: cardWidth) } + + init(data: CardContent, style: CardStyle) { + self.data = data + self.style = style + } + + var body: some View { + content + .background(.background) + .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous)) + .cardSize { value in + cardWidth = value + } + } +} + +private extension CardView { + @ViewBuilder + var content: some View { + switch style { + case .leadingImage: leadingImageView + case .bottomImage: bottomImageView + case .glass: GlassCardView(data: data) + case .highlight: topImageView + case .simple: simpleView + case .topImage: topImageView + } + } +} + +private extension CardView { + @ViewBuilder + var simpleView: some View { + metadataContent + .padding(10) + } +} + +private extension CardView { + @ViewBuilder + var leadingImageView: some View { + HStack { + if let artworkUrl = URL(string: data.artworkUrl) { + thumbnail(artworkUrl, width: 95, height: 95) + metadataContent + } else { + metadataContent + } + } + .padding(10) + } +} + +private extension CardView { + @ViewBuilder + var topImageView: some View { + VStack { + if let artworkUrl = URL(string: data.artworkUrl) { + thumbnail(artworkUrl, maxWidth: .infinity, height: 150) + metadataContent + } else { + metadataContent + } + } + .padding(10) + } +} + +private extension CardView { + @ViewBuilder + var bottomImageView: some View { + VStack { + if let artworkUrl = URL(string: data.artworkUrl) { + metadataContent + thumbnail(artworkUrl, maxWidth: .infinity, height: 150) + } else { + metadataContent + } + } + .padding(10) + } +} + +private extension CardView { + @ViewBuilder + func thumbnail(_ imageUrl: URL, width: CGFloat, height: CGFloat) -> some View { + CachedAsyncImage(image: imageUrl, contentMode: .fill) + .frame(width: width, height: height) + .cornerRadius(12) + } + + @ViewBuilder + func thumbnail(_ imageUrl: URL, maxWidth: CGFloat?, height: CGFloat) -> some View { + CachedAsyncImage(image: imageUrl, contentMode: .fill) + .frame(maxWidth: maxWidth) + .frame(height: height) + .cornerRadius(12) + } +} + +private extension CardView { + var metadataContent: some View { + VStack(alignment: .leading) { + titleRow + + Spacer() + + dateRow + } + .frame(maxWidth: .infinity, alignment: .leading) + } + + var titleRow: some View { + HStack(alignment: .top) { + Text(data.title) + .font(density.titleFont) + .multilineTextAlignment(.leading) + .lineLimit(density.titleLineLimit) + .foregroundStyle(.primary) + + Spacer() + + Image(systemName: "star\(data.favorite ? ".fill" : "")") + .font(.system(size: 12)) + } + } + + var dateRow: some View { + HStack { + MetadataContent( + image: "calendar", + text: data.pubDate.toTimeAgoDisplay(showTime: true) + ) + + Spacer() + + Text(data.type.categories.mostRelevant.rawValue) + } + .foregroundStyle(.primary.opacity(0.9)) + .font(.caption2) + } + +} diff --git a/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/LeadingImageCard.swift b/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/LeadingImageCard.swift deleted file mode 100644 index 0db3c0d5..00000000 --- a/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/LeadingImageCard.swift +++ /dev/null @@ -1,87 +0,0 @@ -import AnalyticsLibrary -import MacMagazineLibrary -import SwiftUI -import UIComponentsLibrary -import UtilityLibrary - -struct LeadingImageCard: View { - @EnvironmentObject private var analytics: AnalyticsManager - @Environment(\.dynamicTypeSize) private var typeSize - @Namespace var namespace - - let data: CardContent - - @State private var cardWidth: CGFloat = .zero - - private var density: CardDensity { .from(width: cardWidth) } - - init(data: CardContent) { - self.data = data - } - - var body: some View { - content - .background(.background) - .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous)) - .cardSize { value in - cardWidth = value - } - } -} - -// MARK: - Card - - -private extension LeadingImageCard { - @ViewBuilder - var content: some View { - HStack { - if let artworkUrl = URL(string: data.artworkUrl) { - thumbnail(artworkUrl) - metadataContent - } else { - metadataContent - } - } - .padding(10) - } -} - -// MARK: - Thumbnail - - -private extension LeadingImageCard { - @ViewBuilder - func thumbnail(_ imageUrl: URL) -> some View { - CachedAsyncImage(image: imageUrl, contentMode: .fill) - .frame(width: 100, height: 100) - .cornerRadius(12) - } -} - -// MARK: - Content block - - -private extension LeadingImageCard { - var metadataContent: some View { - VStack(alignment: .leading, spacing: 6) { - titleRow - dateRow - } - .frame(maxWidth: .infinity, alignment: .leading) - } - - var titleRow: some View { - Text(data.title) - .font(density.titleFont) - .multilineTextAlignment(.leading) - .lineLimit(density.titleLineLimit) - .foregroundStyle(.primary) - } - - var dateRow: some View { - MetadataContent( - image: "calendar", - text: data.pubDate.toTimeAgoDisplay(showTime: true) - ) - .foregroundStyle(.primary.opacity(0.9)) - .font(.caption2) - } -} diff --git a/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/NewsCard.swift b/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/NewsCard.swift index 5de37248..51f69fc9 100644 --- a/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/NewsCard.swift +++ b/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/NewsCard.swift @@ -23,23 +23,12 @@ public struct NewsCard: View { private extension NewsCard { @ViewBuilder var content: some View { - switch data.type.style { - case .leadingImage: LeadingImageCard(data: data) - case .topImage: SimpleCard(data: data) - case .bottomImage: SimpleCard(data: data) - case .highlight: SimpleCard(data: data) - case .simple: SimpleCard(data: data) - case .glass: GlassCardView(data: data) - case .none: - switch data.type.categories.mostRelevant.style { - case .leadingImage: LeadingImageCard(data: data) - case .topImage: SimpleCard(data: data) - case .bottomImage: SimpleCard(data: data) - case .highlight: SimpleCard(data: data) - case .simple: SimpleCard(data: data) - case .glass: GlassCardView(data: data) - case .none: SimpleCard(data: data) - } + if let style = data.type.style { + CardView(data: data, style: style) + } else if let mostRelevantStyle = data.type.categories.mostRelevant.style { + CardView(data: data, style: mostRelevantStyle) + } else { + CardView(data: data, style: .simple) } } } diff --git a/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/SimpleCard.swift b/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/SimpleCard.swift deleted file mode 100644 index d48bec82..00000000 --- a/MacMagazine/Features/MacMagazineUILibrary/Sources/MacMagazineUILibrary/Cards/Views/SimpleCard.swift +++ /dev/null @@ -1,82 +0,0 @@ -import AnalyticsLibrary -import MacMagazineLibrary -import SwiftUI -import UIComponentsLibrary -import UtilityLibrary - -struct SimpleCard: View { - @EnvironmentObject private var analytics: AnalyticsManager - @Environment(\.dynamicTypeSize) private var typeSize - @Namespace var namespace - - let data: CardContent - - @State private var cardWidth: CGFloat = .zero - - private var density: CardDensity { .from(width: cardWidth) } - - init(data: CardContent) { - self.data = data - } - - var body: some View { - content - .background(.background) - .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous)) - .cardSize { value in - cardWidth = value - } - } -} - -// MARK: - Card - - -private extension SimpleCard { - @ViewBuilder - var content: some View { - metadataContent - .padding(10) - } -} - -// MARK: - Content block - - -private extension SimpleCard { - var metadataContent: some View { - VStack(alignment: .leading, spacing: 6) { - titleRow - dateRow - } - .frame(maxWidth: .infinity, alignment: .leading) - } - - var titleRow: some View { - HStack(alignment: .top, spacing: 4) { - Text(data.title) - .font(density.titleFont) - .multilineTextAlignment(.leading) - .lineLimit(density.titleLineLimit) - .foregroundStyle(.primary) - - Spacer() - - Image(systemName: "star\(data.favorite ? ".fill" : "")") - .font(.system(size: 12)) - } - } - - var dateRow: some View { - HStack { - MetadataContent( - image: "calendar", - text: data.pubDate.toTimeAgoDisplay(showTime: true) - ) - - Spacer() - - Text(data.type.categories.mostRelevant.rawValue) - } - .foregroundStyle(.primary.opacity(0.9)) - .font(.caption2) - } -}