diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift index 99ee55106..5b42c044a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift @@ -54,6 +54,14 @@ struct GridCardNavigationLink: View { @ObservedObject var viewModel: HomeFeedViewModel + func tapAction() { + scale = 0.95 + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(150)) { + scale = 1.0 + viewModel.selectedLinkItem = item.objectID + } + } + var body: some View { let destination = LinkItemDetailView( viewModel: LinkItemDetailViewModel( @@ -77,13 +85,7 @@ struct GridCardNavigationLink: View { EmptyView() } GridCard(item: item, isContextMenuOpen: $isContextMenuOpen, actionHandler: actionHandler, tapAction: { - withAnimation { - scale = 0.95 - DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(150)) { - scale = 1.0 - viewModel.selectedLinkItem = item.objectID - } - } + withAnimation { tapAction() } }) .onAppear { Task { await viewModel.itemAppeared(item: item, dataService: dataService) } @@ -91,5 +93,18 @@ struct GridCardNavigationLink: View { } .aspectRatio(1.8, contentMode: .fill) .scaleEffect(scale) + .background( + Color.secondarySystemGroupedBackground + .onTapGesture { + if isContextMenuOpen { + isContextMenuOpen = false + } else { + withAnimation { + tapAction() + } + } + } + ) + .cornerRadius(6) } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 745e1447e..b65ab143f 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -338,7 +338,7 @@ import Views var body: some View { ScrollView { - LazyVGrid(columns: [GridItem(.adaptive(minimum: 325), spacing: 24)], spacing: 24) { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 325), spacing: 16)], spacing: 16) { ForEach(viewModel.items) { item in GridCardNavigationLink( item: item, diff --git a/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift b/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift index 27bfc1144..934e545c5 100644 --- a/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift +++ b/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift @@ -34,6 +34,10 @@ public extension LinkedItem { var unwrappedSavedAt: Date { savedAt ?? Date() } var unwrappedCreatedAt: Date { createdAt ?? Date() } + var hasLabels: Bool { + (labels?.count ?? 0) > 0 + } + var isRead: Bool { readingProgress >= 0.98 } diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift index 6a4dc4a13..fff4cc6d1 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift @@ -75,100 +75,97 @@ public struct GridCard: View { } .onTapGesture { tapHandler() } - // Title, Subtitle, Menu Button - VStack(alignment: .leading, spacing: 4) { - HStack { - Text(item.unwrappedTitle) - .font(.appHeadline) + VStack { + // Title, Subtitle, Menu Button + VStack(alignment: .leading, spacing: 4) { + HStack { + Text(item.unwrappedTitle) + .font(.appHeadline) + .foregroundColor(.appGrayTextContrast) + .lineLimit(1) + .onTapGesture { tapHandler() } + Spacer() + + Menu( + content: { contextMenuView }, + label: { Image(systemName: "ellipsis").padding() } + ) + .frame(width: 16, height: 16, alignment: .center) + .onTapGesture { isContextMenuOpen = true } + } + + HStack { + if let author = item.author { + Text("by \(author)") + .font(.appCaptionTwo) + .foregroundColor(.appGrayText) + .lineLimit(1) + } + + if let publisherDisplayName = item.publisherDisplayName { + Text(publisherDisplayName) + .font(.appCaptionTwo) + .foregroundColor(.appGrayText) + .lineLimit(1) + } + + Spacer() + } + .onTapGesture { tapHandler() } + } + .frame(height: 30) + .padding(.horizontal) + .padding(.bottom, 16) + + // Link description and image + HStack(alignment: .top) { + Text(item.descriptionText ?? item.unwrappedTitle) + .font(.appSubheadline) .foregroundColor(.appGrayTextContrast) - .lineLimit(1) - .onTapGesture { tapHandler() } - Spacer() - - Menu( - content: { contextMenuView }, - label: { Image(systemName: "ellipsis").padding() } - ) - .frame(width: 16, height: 16, alignment: .center) - .onTapGesture { isContextMenuOpen = true } - } - - HStack { - if let author = item.author { - Text("by \(author)") - .font(.appCaptionTwo) - .foregroundColor(.appGrayText) - .lineLimit(1) - } - - if let publisherDisplayName = item.publisherDisplayName { - Text(publisherDisplayName) - .font(.appCaptionTwo) - .foregroundColor(.appGrayText) - .lineLimit(1) - } + .lineLimit(nil) + .multilineTextAlignment(.leading) Spacer() - } - .onTapGesture { tapHandler() } - } - .frame(height: 30) - .padding(.horizontal) - .padding(.bottom, 12) - // Link description and image - HStack(alignment: .top) { - Text(item.descriptionText ?? item.unwrappedTitle) - .font(.appSubheadline) - .foregroundColor(.appGrayTextContrast) - .lineLimit(nil) - .multilineTextAlignment(.leading) - .frame(height: (geo.size.width * 2) / 9, alignment: .top) - - Spacer() - - if let imageURL = item.imageURL { - AsyncLoadingImage(url: imageURL) { imageStatus in - if case let AsyncImageStatus.loaded(image) = imageStatus { - image - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: geo.size.width / 3, height: (geo.size.width * 2) / 9) - .cornerRadius(3) - } else if case AsyncImageStatus.loading = imageStatus { - Color.appButtonBackground - .frame(width: geo.size.width / 3, height: (geo.size.width * 2) / 9) - .cornerRadius(3) - } else { - EmptyView() + if let imageURL = item.imageURL { + AsyncLoadingImage(url: imageURL) { imageStatus in + if case let AsyncImageStatus.loaded(image) = imageStatus { + image + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: geo.size.width / 3, height: (geo.size.width * 2) / 9) + .cornerRadius(3) + } else if case AsyncImageStatus.loading = imageStatus { + Color.appButtonBackground + .frame(width: geo.size.width / 3, height: (geo.size.width * 2) / 9) + .cornerRadius(3) + } else { + EmptyView() + } } } } - } - .padding(.horizontal) - .onTapGesture { tapHandler() } - - // Category Labels - ScrollView(.horizontal, showsIndicators: false) { - HStack { - ForEach(item.sortedLabels, id: \.self) { - TextChip(feedItemLabel: $0) - } - // Add an empty chip to the end so that we create a Spacer equivalent - // in the case where there are no labels set on the item - TextChip(text: "", color: Color.secondarySystemGroupedBackground) - Spacer() - } .padding(.horizontal) - } - .frame(height: 40) - .onTapGesture { tapHandler() } - } - .background( - Color.secondarySystemGroupedBackground .onTapGesture { tapHandler() } - ) - .cornerRadius(6) + + // Category Labels + if item.hasLabels { + ScrollView(.horizontal, showsIndicators: false) { + HStack { + ForEach(item.sortedLabels, id: \.self) { + TextChip(feedItemLabel: $0) + } + Spacer() + } + .padding(.horizontal) + } + .onTapGesture { tapHandler() } + } + } + .padding(.horizontal, 0) + .padding(.top, 0) + .padding(.bottom, 8) + } .contextMenu { contextMenuView } } }