2023-02-02 21:41:10 -06:00
|
|
|
//
|
|
|
|
// FeedDetailCardView.swift
|
|
|
|
// NewsBlur
|
|
|
|
//
|
|
|
|
// Created by David Sinclair on 2023-02-01.
|
|
|
|
// Copyright © 2023 NewsBlur. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import SwiftUI
|
2023-03-31 16:49:22 -07:00
|
|
|
import SwipeCell
|
2023-02-02 21:41:10 -06:00
|
|
|
|
|
|
|
/// Card view within the feed detail view, representing a story row in list layout or a story card in grid layout.
|
|
|
|
struct CardView: View {
|
|
|
|
let cache: StoryCache
|
|
|
|
|
|
|
|
let story: Story
|
|
|
|
|
2023-03-31 16:49:22 -07:00
|
|
|
// @Binding var swipeState: SwipeState
|
|
|
|
// @Binding var currentUserInteractionCellID: String?
|
|
|
|
|
|
|
|
@State private var isPinned: Bool = false
|
|
|
|
|
2023-02-02 21:41:10 -06:00
|
|
|
var body: some View {
|
2023-03-31 16:49:22 -07:00
|
|
|
let swipeSaveButton = SwipeCellButton(
|
|
|
|
buttonStyle: .view,
|
|
|
|
title: "",
|
|
|
|
systemImage: "",
|
|
|
|
titleColor: .white,
|
|
|
|
imageColor: .white,
|
|
|
|
view: {
|
|
|
|
// AnyView(
|
|
|
|
// Circle()
|
|
|
|
// .fill(Color.blue)
|
|
|
|
// .frame(width: 40, height: 40)
|
|
|
|
// .overlay(
|
|
|
|
// Image(uiImage: UIImage(named: "saved-stories") ?? UIImage())
|
|
|
|
// .resizable()
|
|
|
|
// .frame(width: 32, height: 32)
|
|
|
|
// .font(.headline)
|
|
|
|
// .foregroundColor(.white)
|
|
|
|
// )
|
|
|
|
// )
|
|
|
|
// AnyView(
|
|
|
|
// Image(uiImage: UIImage(named: "saved-stories") ?? UIImage())
|
|
|
|
// .resizable()
|
|
|
|
// .frame(width: 32, height: 32)
|
|
|
|
// .font(.headline)
|
|
|
|
// .foregroundColor(.white)
|
|
|
|
// )
|
|
|
|
AnyView(
|
|
|
|
VStack(spacing: 5) {
|
|
|
|
Image(uiImage: UIImage(named: "saved-stories") ?? UIImage())
|
|
|
|
.resizable()
|
|
|
|
.frame(width: 32, height: 32)
|
|
|
|
.foregroundColor(.white)
|
|
|
|
Text(story.isSaved ? "Unsave" : "Save")
|
|
|
|
.font(.callout)
|
|
|
|
.bold()
|
|
|
|
.foregroundColor(.white)
|
|
|
|
}
|
|
|
|
)},
|
|
|
|
backgroundColor: .purple,
|
|
|
|
action: {
|
|
|
|
cache.appDelegate.storiesCollection.toggleStorySaved(story.dictionary)
|
|
|
|
cache.appDelegate.feedDetailViewController.reload()
|
|
|
|
},
|
|
|
|
feedback: true
|
|
|
|
)
|
|
|
|
|
|
|
|
let swipeReadButton = SwipeCellButton(
|
|
|
|
buttonStyle: .view,
|
|
|
|
title: "",
|
|
|
|
systemImage: "",
|
|
|
|
titleColor: .white,
|
|
|
|
imageColor: .white,
|
|
|
|
view: {
|
|
|
|
AnyView(
|
|
|
|
VStack(spacing: 5) {
|
|
|
|
Image(uiImage: UIImage(named: "mark-read") ?? UIImage())
|
|
|
|
.resizable()
|
|
|
|
.frame(width: 32, height: 32)
|
|
|
|
.foregroundColor(.white)
|
|
|
|
Text(story.isRead ? "Unread" : "Read")
|
|
|
|
.font(.callout)
|
|
|
|
.bold()
|
|
|
|
.foregroundColor(.white)
|
|
|
|
}
|
|
|
|
)},
|
|
|
|
// AnyView(
|
|
|
|
// Image(uiImage: UIImage(named: "mark-read") ?? UIImage())
|
|
|
|
// .resizable()
|
|
|
|
// .frame(width: 32, height: 32)
|
|
|
|
// .font(.headline)
|
|
|
|
// .foregroundColor(.white)
|
|
|
|
// )
|
|
|
|
// },
|
|
|
|
backgroundColor: .blue,
|
|
|
|
action: {
|
|
|
|
cache.appDelegate.storiesCollection.toggleStoryUnread(story.dictionary)
|
|
|
|
cache.appDelegate.feedDetailViewController.reload()
|
|
|
|
},
|
|
|
|
feedback: true
|
|
|
|
)
|
|
|
|
|
2023-02-02 21:41:10 -06:00
|
|
|
ZStack(alignment: .leading) {
|
|
|
|
if story.isSelected || cache.isGrid {
|
2023-03-02 15:31:21 -07:00
|
|
|
RoundedRectangle(cornerRadius: 10).foregroundColor(highlightColor)
|
2023-02-02 21:41:10 -06:00
|
|
|
|
|
|
|
CardFeedBarView(cache: cache, story: story)
|
|
|
|
.padding(.leading, 2)
|
|
|
|
} else {
|
|
|
|
CardFeedBarView(cache: cache, story: story)
|
|
|
|
.padding(.leading, 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
VStack {
|
|
|
|
if cache.isGrid, let previewImage {
|
|
|
|
gridPreview(image: previewImage)
|
|
|
|
// Image(uiImage: previewImage)
|
|
|
|
// .resizable()
|
|
|
|
// .scaledToFill()
|
|
|
|
// .frame(height: cache.settings.gridHeight / 3)
|
|
|
|
// .cornerRadius(12, corners: [.topLeft, .topRight])
|
|
|
|
// .padding(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
HStack {
|
2023-03-02 15:31:21 -07:00
|
|
|
if !cache.isGrid, cache.settings.preview.isLeft, let previewImage {
|
2023-02-02 21:41:10 -06:00
|
|
|
listPreview(image: previewImage)
|
|
|
|
}
|
|
|
|
|
|
|
|
CardContentView(cache: cache, story: story)
|
|
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
|
2023-03-02 15:31:21 -07:00
|
|
|
.padding([.leading, .trailing], 15)
|
|
|
|
.padding([.top, .bottom], cache.settings.spacing == .compact ? 10 : 15)
|
2023-02-02 21:41:10 -06:00
|
|
|
|
2023-03-02 15:31:21 -07:00
|
|
|
if !cache.isGrid, !cache.settings.preview.isLeft, let previewImage {
|
2023-02-02 21:41:10 -06:00
|
|
|
listPreview(image: previewImage)
|
|
|
|
|
|
|
|
// Image(uiImage: previewImage)
|
|
|
|
// .resizable()
|
|
|
|
// .scaledToFill()
|
|
|
|
// .frame(width: 80)
|
|
|
|
// .clipShape(RoundedRectangle(cornerRadius: 10))
|
|
|
|
// .padding([.top, .bottom, .trailing], 10)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-21 21:54:21 -07:00
|
|
|
.opacity(story.isRead ? 0.7 : 1)
|
2023-03-02 15:31:21 -07:00
|
|
|
.if(cache.isGrid || story.isSelected) { view in
|
|
|
|
view.clipShape(RoundedRectangle(cornerRadius: 10))
|
|
|
|
}
|
2023-02-02 21:41:10 -06:00
|
|
|
.if(story.isSelected) { view in
|
|
|
|
view.padding(10)
|
|
|
|
}
|
2023-03-31 16:49:22 -07:00
|
|
|
// .addFullSwipeAction(swipeColor: .clear, swipeRole: .defaults, state: $swipeState) {
|
|
|
|
// Leading {
|
|
|
|
// Button {
|
|
|
|
// print("edit \(story)")
|
|
|
|
// } label: {
|
|
|
|
// Image(systemName: "pencil")
|
|
|
|
// .foregroundColor(.white)
|
|
|
|
// }
|
|
|
|
// .frame(width: 80, alignment: .center)
|
|
|
|
// .frame(maxHeight: .infinity)
|
|
|
|
// .contentShape(Rectangle())
|
|
|
|
// .background(Color.green)
|
|
|
|
// }
|
|
|
|
// Trailing {
|
|
|
|
// Button {
|
|
|
|
// print("remove \(story)")
|
|
|
|
// } label: {
|
|
|
|
// Image(systemName: "trash")
|
|
|
|
// .foregroundColor(.white)
|
|
|
|
// }
|
|
|
|
// .frame(width: 80, alignment: .center)
|
|
|
|
// .frame(maxHeight: .infinity)
|
|
|
|
// .contentShape(Rectangle())
|
|
|
|
// .background(Color.blue)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// .swipeCell(id: story.id.uuidString, cellWidth: 60, leadingSideGroup: leftGroup(), trailingSideGroup: rightGroup(), currentUserInteractionCellID: $currentUserInteractionCellID, settings: SwipeCellSettings())
|
|
|
|
// .onSwipe(leading: slots, trailing: slots)
|
|
|
|
.swipeCell(cellPosition: .both, leftSlot: SwipeCellSlot(slots: [swipeSaveButton], slotStyle: .destructive, buttonWidth: 80), rightSlot: SwipeCellSlot(slots: [swipeReadButton], slotStyle: .destructive, buttonWidth: 80))
|
|
|
|
.dismissSwipeCellForScrollViewForLazyVStack()
|
2023-03-22 21:38:46 -07:00
|
|
|
.contextMenu {
|
|
|
|
Button {
|
|
|
|
cache.appDelegate.storiesCollection.toggleStoryUnread(story.dictionary)
|
|
|
|
cache.appDelegate.feedDetailViewController.reload()
|
|
|
|
} label: {
|
|
|
|
Label(story.isRead ? "Mark as unread" : "Mark as read", image: "mark-read")
|
|
|
|
}
|
|
|
|
|
|
|
|
Button {
|
|
|
|
cache.appDelegate.feedDetailViewController.markFeedsRead(fromTimestamp: story.timestamp, andOlder: false)
|
|
|
|
cache.appDelegate.feedDetailViewController.reload()
|
|
|
|
} label: {
|
|
|
|
Label("Mark newer stories read", image: "mark-read")
|
|
|
|
}
|
|
|
|
|
|
|
|
Button {
|
|
|
|
cache.appDelegate.feedDetailViewController.markFeedsRead(fromTimestamp: story.timestamp, andOlder: true)
|
|
|
|
cache.appDelegate.feedDetailViewController.reload()
|
|
|
|
} label: {
|
|
|
|
Label("Mark older stories read", image: "mark-read")
|
|
|
|
}
|
|
|
|
|
|
|
|
Button {
|
|
|
|
cache.appDelegate.storiesCollection.toggleStorySaved(story.dictionary)
|
|
|
|
cache.appDelegate.feedDetailViewController.reload()
|
|
|
|
} label: {
|
|
|
|
Label(story.isSaved ? "Unsave this story" : "Save this story", image: "saved-stories")
|
|
|
|
}
|
|
|
|
|
|
|
|
Button {
|
|
|
|
cache.appDelegate.showSend(to: cache.appDelegate.feedDetailViewController, sender: cache.appDelegate.feedDetailViewController.view)
|
|
|
|
} label: {
|
|
|
|
Label("Send this story to…", image: "email")
|
|
|
|
}
|
|
|
|
|
|
|
|
Button {
|
|
|
|
cache.appDelegate.openTrainStory(nil)
|
|
|
|
} label: {
|
|
|
|
Label("Train this story", image: "train")
|
|
|
|
}
|
|
|
|
}
|
2023-02-02 21:41:10 -06:00
|
|
|
}
|
|
|
|
|
2023-03-31 16:49:22 -07:00
|
|
|
// var body: some View {
|
|
|
|
// if cache.isGrid {
|
|
|
|
// ZStack(alignment: .leading) {
|
|
|
|
// RoundedRectangle(cornerRadius: 12).foregroundColor(.init(white: 0.9))
|
|
|
|
//
|
|
|
|
// CardFeedBarView(cache: cache, story: story)
|
|
|
|
// .clipShape(RoundedRectangle(cornerRadius: 12))
|
|
|
|
// .padding(.leading, 1)
|
|
|
|
//
|
|
|
|
// VStack {
|
|
|
|
// // RoundedRectangle(cornerRadius: 12).foregroundColor(.random)
|
|
|
|
// // .frame(height: 200)
|
|
|
|
//
|
|
|
|
// if let previewImage {
|
|
|
|
// Image(uiImage: previewImage)
|
|
|
|
// .resizable()
|
|
|
|
// .scaledToFill()
|
|
|
|
// .frame(height: 200)
|
|
|
|
// // .clipped()
|
|
|
|
// // .clipShape(RoundedRectangle(cornerRadius: 12))
|
|
|
|
// .cornerRadius(12, corners: [.topLeft, .topRight])
|
|
|
|
// .padding(0)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// CardContentView(cache: cache, story: story)
|
|
|
|
// .frame(maxHeight: .infinity, alignment: .leading)
|
|
|
|
// .padding(10)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// } else {
|
|
|
|
// ZStack(alignment: .leading) {
|
|
|
|
// if story.isSelected {
|
|
|
|
// RoundedRectangle(cornerRadius: 12).foregroundColor(.init(white: 0.9))
|
|
|
|
// // .padding(10)
|
|
|
|
//
|
|
|
|
// CardFeedBarView(cache: cache, story: story)
|
|
|
|
// .clipShape(RoundedRectangle(cornerRadius: 12))
|
|
|
|
// // .padding(10)
|
|
|
|
// } else {
|
|
|
|
// CardFeedBarView(cache: cache, story: story)
|
|
|
|
// .padding(.leading, 2)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// HStack {
|
|
|
|
// CardContentView(cache: cache, story: story)
|
|
|
|
// .frame(maxWidth: .infinity, alignment: .leading)
|
|
|
|
// .padding([.top, .bottom], 10)
|
|
|
|
// .padding(.leading, 15)
|
|
|
|
//
|
|
|
|
// if let previewImage {
|
|
|
|
// Image(uiImage: previewImage)
|
|
|
|
// .resizable()
|
|
|
|
// .scaledToFill()
|
|
|
|
// .frame(width: 80)
|
|
|
|
// .clipShape(RoundedRectangle(cornerRadius: 10))
|
|
|
|
// .padding([.top, .bottom, .trailing], 10)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// .if(story.isSelected) { view in
|
|
|
|
// view.padding(10)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
2023-02-02 21:41:10 -06:00
|
|
|
|
2023-03-02 15:31:21 -07:00
|
|
|
var highlightColor: Color {
|
|
|
|
if cache.isGrid {
|
|
|
|
return Color.themed([0xFDFCFA, 0xFFFDEF, 0x4F4F4F, 0x292B2C])
|
|
|
|
} else {
|
|
|
|
return Color.themed([0xFFFDEF, 0xEEECCD, 0x303A40, 0x303030])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-02 21:41:10 -06:00
|
|
|
var previewImage: UIImage? {
|
2023-03-02 15:31:21 -07:00
|
|
|
guard cache.settings.preview != .none, let image = cache.appDelegate.cachedImage(forStoryHash: story.hash), image.isKind(of: UIImage.self) else {
|
2023-02-02 21:41:10 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return image
|
|
|
|
}
|
|
|
|
|
|
|
|
@ViewBuilder
|
|
|
|
func gridPreview(image: UIImage) -> some View {
|
|
|
|
Image(uiImage: image)
|
|
|
|
.resizable()
|
|
|
|
.scaledToFill()
|
|
|
|
.frame(height: cache.settings.gridHeight / 3)
|
|
|
|
.cornerRadius(10, corners: .topRight)
|
|
|
|
.padding(0)
|
|
|
|
.padding(.leading, 8)
|
|
|
|
}
|
|
|
|
|
|
|
|
@ViewBuilder
|
|
|
|
func listPreview(image: UIImage) -> some View {
|
2023-03-02 15:31:21 -07:00
|
|
|
let isLeft = cache.settings.preview.isLeft
|
2023-02-02 21:41:10 -06:00
|
|
|
|
2023-03-02 15:31:21 -07:00
|
|
|
if cache.settings.preview.isSmall {
|
2023-02-02 21:41:10 -06:00
|
|
|
Image(uiImage: image)
|
|
|
|
.resizable()
|
|
|
|
.scaledToFill()
|
|
|
|
.frame(width: 80)
|
|
|
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
|
|
|
.padding([.top, .bottom], 10)
|
|
|
|
.padding(.leading, isLeft ? 15 : -10)
|
|
|
|
.padding(.trailing, isLeft ? -10 : 10)
|
|
|
|
} else {
|
|
|
|
Image(uiImage: image)
|
|
|
|
.resizable()
|
|
|
|
.scaledToFill()
|
|
|
|
.frame(width: 90)
|
2023-03-02 15:31:21 -07:00
|
|
|
.clipped()
|
2023-02-02 21:41:10 -06:00
|
|
|
.padding(.leading, isLeft ? 8 : -10)
|
|
|
|
.padding(.trailing, isLeft ? -10 : 0)
|
|
|
|
}
|
|
|
|
|
2023-03-31 16:49:22 -07:00
|
|
|
// if cache.settings.listPreview.isLeft {
|
|
|
|
// return image.padding(.trailing, 10)
|
|
|
|
// } else {
|
|
|
|
// return image.padding(.leading, 10)
|
|
|
|
// }
|
|
|
|
// //these cause it to crash; too complex? could try assigning the above to variable then add these
|
|
|
|
// .if(cache.settings.listPreview.isLeft) { view in
|
|
|
|
// self.padding(.trailing, 10)
|
|
|
|
// }
|
|
|
|
// .if(!cache.settings.listPreview.isLeft) { view in
|
|
|
|
// self.padding(.leading, 10)
|
|
|
|
// }
|
2023-02-02 21:41:10 -06:00
|
|
|
}
|
2023-03-31 16:49:22 -07:00
|
|
|
|
|
|
|
|
|
|
|
// func leftGroup()->[SwipeCellActionItem] {
|
|
|
|
// return [SwipeCellActionItem(buttonView: {
|
|
|
|
//
|
|
|
|
// VStack(spacing: 2) {
|
|
|
|
// Image(systemName: "person.crop.circle.badge.plus").font(.system(size: 22)).foregroundColor(.white)
|
|
|
|
// Text("Share").fixedSize().font(.system(size: 12)).foregroundColor(.white)
|
|
|
|
// }.frame(maxHeight: 80).castToAnyView()
|
|
|
|
//
|
|
|
|
// }, backgroundColor: .blue)
|
|
|
|
// {
|
|
|
|
// print("share action!")
|
|
|
|
// }]
|
|
|
|
|
|
|
|
// return [ SwipeCellActionItem(buttonView: {
|
|
|
|
//
|
|
|
|
// self.pinView(swipeOut: false)
|
|
|
|
//
|
|
|
|
// }, swipeOutButtonView: {
|
|
|
|
// self.pinView(swipeOut: true)
|
|
|
|
// }, buttonWidth: 80, backgroundColor: .yellow, swipeOutAction: true, swipeOutHapticFeedbackType: .success, swipeOutIsDestructive: false)
|
|
|
|
// {
|
|
|
|
// print("pin action!")
|
|
|
|
// self.isPinned.toggle()
|
|
|
|
// }]
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// func pinView(swipeOut: Bool)->AnyView {
|
|
|
|
//
|
|
|
|
// Group {
|
|
|
|
// Spacer()
|
|
|
|
// VStack(spacing: 2) {
|
|
|
|
// Image(systemName: self.isPinned ? "pin.slash": "pin").font(.system(size: 24)).foregroundColor(.white)
|
|
|
|
// Text(self.isPinned ? "Unpin": "Pin").fixedSize().font(.system(size: 14)).foregroundColor(.white)
|
|
|
|
// }.frame(maxHeight: 80).padding(.horizontal, swipeOut ? 20 : 5)
|
|
|
|
// if swipeOut == false {
|
|
|
|
// Spacer()
|
|
|
|
// }
|
|
|
|
// }.animation(.default).castToAnyView()
|
|
|
|
//
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// func rightGroup()->[SwipeCellActionItem] {
|
|
|
|
//
|
|
|
|
// let items = [
|
|
|
|
// SwipeCellActionItem(buttonView: {
|
|
|
|
//
|
|
|
|
// VStack(spacing: 2) {
|
|
|
|
// Image(systemName: "person.crop.circle.badge.plus").font(.system(size: 22)).foregroundColor(.white)
|
|
|
|
// Text("Share").fixedSize().font(.system(size: 12)).foregroundColor(.white)
|
|
|
|
// }.frame(maxHeight: 80).castToAnyView()
|
|
|
|
//
|
|
|
|
// }, backgroundColor: .blue)
|
|
|
|
// {
|
|
|
|
// print("share action!")
|
|
|
|
// },
|
|
|
|
// SwipeCellActionItem(buttonView: {
|
|
|
|
// VStack(spacing: 2) {
|
|
|
|
// Image(systemName: "folder.fill").font(.system(size: 22)).foregroundColor(.white)
|
|
|
|
// Text("Move").fixedSize().font(.system(size: 12)).foregroundColor(.white)
|
|
|
|
// }.frame(maxHeight: 80).castToAnyView()
|
|
|
|
//
|
|
|
|
// }, backgroundColor: .purple, actionCallback: {
|
|
|
|
// print("folder action")
|
|
|
|
// }),
|
|
|
|
//
|
|
|
|
// SwipeCellActionItem(buttonView: {
|
|
|
|
// self.trashView(swipeOut: false)
|
|
|
|
// }, swipeOutButtonView: {
|
|
|
|
// self.trashView(swipeOut: true)
|
|
|
|
// }, backgroundColor: .red, swipeOutAction: true, swipeOutHapticFeedbackType: .warning, swipeOutIsDestructive: true) {
|
|
|
|
// print("delete action")
|
|
|
|
//// self.deletionCallback(item)
|
|
|
|
// }
|
|
|
|
// ]
|
|
|
|
//
|
|
|
|
// return items
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// func trashView(swipeOut: Bool)->AnyView {
|
|
|
|
// VStack(spacing: 3) {
|
|
|
|
// Image(systemName: "trash").font(.system(size: swipeOut ? 28 : 22)).foregroundColor(.white)
|
|
|
|
// Text("Delete").fixedSize().font(.system(size: swipeOut ? 16 : 12)).foregroundColor(.white)
|
|
|
|
// }.frame(maxHeight: 80).animation(.default).castToAnyView()
|
|
|
|
//
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// var slots = [
|
|
|
|
// // First item
|
|
|
|
// Slot(
|
|
|
|
// image: {
|
|
|
|
// Image(systemName: "envelope.open.fill")
|
|
|
|
// },
|
|
|
|
// title: {
|
|
|
|
// Text("Read")
|
|
|
|
// .foregroundColor(.white)
|
|
|
|
// .font(.footnote)
|
|
|
|
// .fontWeight(.semibold)
|
|
|
|
// .embedInAnyView()
|
|
|
|
// },
|
|
|
|
// action: { print("Read Slot tapped") },
|
|
|
|
// style: .init(background: .orange)
|
|
|
|
// ),
|
|
|
|
// // Second item
|
|
|
|
// Slot(
|
|
|
|
// image: {
|
|
|
|
// Image(systemName: "hand.raised.fill")
|
|
|
|
// },
|
|
|
|
// title: {
|
|
|
|
// Text("Block")
|
|
|
|
// .foregroundColor(.white)
|
|
|
|
// .font(.footnote)
|
|
|
|
// .fontWeight(.semibold)
|
|
|
|
// .embedInAnyView()
|
|
|
|
// },
|
|
|
|
// action: { print("Block Slot Tapped") },
|
|
|
|
// style: .init(background: .blue, imageColor: .red)
|
|
|
|
// )
|
|
|
|
// ]
|
2023-02-02 21:41:10 -06:00
|
|
|
|
|
|
|
//struct CardView_Previews: PreviewProvider {
|
|
|
|
// static var previews: some View {
|
|
|
|
// CardView(cache: StoryCache(), story: Story(index: 0))
|
|
|
|
// }
|
2023-03-31 16:49:22 -07:00
|
|
|
}
|
2023-02-02 21:41:10 -06:00
|
|
|
|
|
|
|
struct CardContentView: View {
|
|
|
|
let cache: StoryCache
|
|
|
|
|
|
|
|
let story: Story
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
VStack(alignment: .leading) {
|
|
|
|
if story.isRiverOrSocial, let feedImage {
|
|
|
|
HStack {
|
|
|
|
Image(uiImage: feedImage)
|
|
|
|
.resizable()
|
|
|
|
.frame(width: 16, height: 16)
|
|
|
|
.padding(.leading, 24)
|
|
|
|
|
|
|
|
Text(story.feedName)
|
2023-03-30 10:36:31 -07:00
|
|
|
.font(font(named: "WhitneySSm-Medium", size: 12))
|
2023-02-02 21:41:10 -06:00
|
|
|
.lineLimit(1)
|
2023-03-02 15:31:21 -07:00
|
|
|
.foregroundColor(feedColor)
|
2023-02-02 21:41:10 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HStack(alignment: .top) {
|
|
|
|
if let unreadImage {
|
|
|
|
Image(uiImage: unreadImage)
|
|
|
|
.resizable()
|
2023-03-21 21:54:21 -07:00
|
|
|
.opacity(story.isRead ? 0.15 : 1)
|
2023-02-02 21:41:10 -06:00
|
|
|
.frame(width: 16, height: 16)
|
|
|
|
.padding(.top, 3)
|
|
|
|
}
|
|
|
|
|
|
|
|
VStack(alignment: .leading) {
|
|
|
|
HStack(alignment: .top) {
|
|
|
|
if story.isSaved, let image = UIImage(named: "saved-stories") {
|
|
|
|
Image(uiImage: image)
|
|
|
|
.resizable()
|
2023-03-21 21:54:21 -07:00
|
|
|
.opacity(story.isRead ? 0.15 : 1)
|
2023-02-02 21:41:10 -06:00
|
|
|
.frame(width: 16, height: 16)
|
|
|
|
.padding(.top, 3)
|
|
|
|
}
|
|
|
|
|
|
|
|
if story.isShared, let image = UIImage(named: "share") {
|
|
|
|
Image(uiImage: image)
|
|
|
|
.resizable()
|
2023-03-21 21:54:21 -07:00
|
|
|
.opacity(story.isRead ? 0.15 : 1)
|
2023-02-02 21:41:10 -06:00
|
|
|
.frame(width: 16, height: 16)
|
|
|
|
.padding(.top, 3)
|
|
|
|
}
|
|
|
|
|
|
|
|
Text(story.title)
|
2023-03-02 15:31:21 -07:00
|
|
|
.font(font(named: "WhitneySSm-Medium", size: 18).bold())
|
|
|
|
.foregroundColor(titleColor)
|
|
|
|
.lineLimit(cache.isGrid ? StorySettings.Content.titleLimit : cache.settings.content.limit)
|
|
|
|
.truncationMode(.tail)
|
2023-02-02 21:41:10 -06:00
|
|
|
}
|
2023-03-02 15:31:21 -07:00
|
|
|
.padding(.bottom, cache.settings.spacing == .compact ? -5 : 0)
|
|
|
|
|
|
|
|
if cache.isGrid || cache.settings.content != .title {
|
|
|
|
Text(story.content)
|
|
|
|
.font(font(named: "WhitneySSm-Book", size: 13))
|
|
|
|
.foregroundColor(contentColor)
|
|
|
|
.lineLimit(cache.isGrid ? StorySettings.Content.contentLimit : cache.settings.content.limit)
|
|
|
|
.truncationMode(.tail)
|
|
|
|
.padding(.top, 5)
|
|
|
|
.padding(.bottom, cache.settings.spacing == .compact ? -5 : 0)
|
|
|
|
}
|
|
|
|
|
2023-02-02 21:41:10 -06:00
|
|
|
Spacer()
|
2023-03-02 15:31:21 -07:00
|
|
|
|
2023-02-02 21:41:10 -06:00
|
|
|
Text(story.dateAndAuthor)
|
2023-03-30 10:36:31 -07:00
|
|
|
.font(font(named: "WhitneySSm-Medium", size: 12))
|
2023-03-02 15:31:21 -07:00
|
|
|
.foregroundColor(dateAndAuthorColor)
|
2023-02-02 21:41:10 -06:00
|
|
|
.padding(.top, 5)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var feedImage: UIImage? {
|
|
|
|
if let image = cache.appDelegate.getFavicon(story.feedID) {
|
|
|
|
return Utilities.roundCorneredImage(image, radius: 4, convertTo: CGSizeMake(16, 16))
|
|
|
|
} else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var unreadImage: UIImage? {
|
2023-03-21 21:54:21 -07:00
|
|
|
guard story.isReadAvailable else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-02-02 21:41:10 -06:00
|
|
|
switch story.score {
|
|
|
|
case -1:
|
|
|
|
return UIImage(named: "indicator-hidden")
|
|
|
|
case 1:
|
|
|
|
return UIImage(named: "indicator-focus")
|
|
|
|
default:
|
|
|
|
return UIImage(named: "indicator-unread")
|
|
|
|
}
|
|
|
|
}
|
2023-03-02 15:31:21 -07:00
|
|
|
|
|
|
|
func font(named: String, size: CGFloat) -> Font {
|
|
|
|
return Font.custom(named, size: size + cache.settings.fontSize.offset, relativeTo: .caption)
|
|
|
|
}
|
|
|
|
|
|
|
|
var feedColor: Color {
|
|
|
|
return contentColor
|
|
|
|
}
|
|
|
|
|
|
|
|
var titleColor: Color {
|
|
|
|
if story.isSelected {
|
|
|
|
return Color.themed([0x686868, 0xA0A0A0])
|
|
|
|
} else if story.isRead {
|
|
|
|
return Color.themed([0x585858, 0x585858, 0x989898, 0x888888])
|
|
|
|
} else {
|
|
|
|
return Color.themed([0x111111, 0x333333, 0xD0D0D0, 0xCCCCCC])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var contentColor: Color {
|
|
|
|
if story.isSelected, story.isRead {
|
|
|
|
return Color.themed([0xB8B8B8, 0xB8B8B8, 0xA0A0A0, 0x707070])
|
|
|
|
} else if story.isSelected {
|
|
|
|
return Color.themed([0x888785, 0x686868, 0xA9A9A9, 0x989898])
|
|
|
|
} else if story.isRead {
|
|
|
|
return Color.themed([0xB8B8B8, 0xB8B8B8, 0xA0A0A0, 0x707070])
|
|
|
|
} else {
|
|
|
|
return Color.themed([0x404040, 0x404040, 0xC0C0C0, 0xB0B0B0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var dateAndAuthorColor: Color {
|
|
|
|
return contentColor
|
|
|
|
}
|
2023-02-02 21:41:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
struct CardFeedBarView: View {
|
|
|
|
let cache: StoryCache
|
|
|
|
|
|
|
|
let story: Story
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
GeometryReader { geometry in
|
|
|
|
if let color = story.feedColorBarLeft {
|
|
|
|
Path { path in
|
|
|
|
path.move(to: CGPoint(x: 0, y: 0))
|
|
|
|
path.addLine(to: CGPoint(x: 0, y: geometry.size.height))
|
|
|
|
}
|
|
|
|
.stroke(Color(color), lineWidth: 4)
|
|
|
|
}
|
|
|
|
|
|
|
|
if let color = story.feedColorBarRight {
|
|
|
|
Path { path in
|
|
|
|
path.move(to: CGPoint(x: 4, y: 0))
|
|
|
|
path.addLine(to: CGPoint(x: 4, y: geometry.size.height))
|
|
|
|
}
|
|
|
|
.stroke(Color(color), lineWidth: 4)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// if #available(iOS 15.0, *) {
|
|
|
|
// Canvas { context, size in
|
|
|
|
// context.stroke(
|
|
|
|
// Path(CGRect(origin: .zero, size: CGSize(width: 0, height: size.height))),
|
|
|
|
// with: .color(.green),
|
|
|
|
// lineWidth: 4)
|
|
|
|
//
|
|
|
|
// context.stroke(
|
|
|
|
// Path(CGRect(origin: CGPoint(x: 5, y: 0), size: CGSize(width: 0, height: size.height))),
|
|
|
|
// with: .color(.blue),
|
|
|
|
// lineWidth: 4)
|
|
|
|
// }
|
|
|
|
// Canvas { context, size in
|
|
|
|
// let frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
|
|
|
|
//
|
|
|
|
// Path { path in
|
|
|
|
// path.move(to: CGPoint(x: 0, y: 0))
|
|
|
|
// path.addLine(to: CGPoint(x: 0, y: size.height))
|
|
|
|
// }
|
|
|
|
// .stroke(.blue, lineWidth: 10)
|
|
|
|
//
|
|
|
|
// Path { path in
|
|
|
|
// path.move(to: CGPoint(x: 10, y: 0))
|
|
|
|
// path.addLine(to: CGPoint(x: 10, y: size.height))
|
|
|
|
// }
|
|
|
|
// .stroke(.green, lineWidth: 10)
|
|
|
|
// }
|
|
|
|
// } else {
|
|
|
|
// EmptyView()
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// Path { path in
|
|
|
|
// path.move(to: CGPoint(x: 0, y: 0))
|
|
|
|
// path.addLine(to: CGPoint(x: 100, y: 300))
|
|
|
|
// path.addLine(to: CGPoint(x: 300, y: 300))
|
|
|
|
// path.addLine(to: CGPoint(x: 200, y: 100))
|
|
|
|
// path.closeSubpath()
|
|
|
|
// }
|
|
|
|
// .stroke(.blue, lineWidth: 10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CGFloat feedBarOffset = isHighlighted ? 2 : 0;
|
|
|
|
// CGContextSetStrokeColor(context, CGColorGetComponents([self.feedColorBarTopBorder CGColor]));
|
|
|
|
// if (self.isRead) {
|
|
|
|
// CGContextSetAlpha(context, 0.15);
|
|
|
|
// }
|
|
|
|
// CGContextSetLineWidth(context, 4.0f);
|
|
|
|
// CGContextBeginPath(context);
|
|
|
|
// CGContextMoveToPoint(context, 2.0f + feedBarOffset, 0);
|
|
|
|
// CGContextAddLineToPoint(context, 2.0f + feedBarOffset, self.frame.size.height);
|
|
|
|
// CGContextStrokePath(context);
|
|
|
|
//
|
|
|
|
// CGContextSetStrokeColor(context, CGColorGetComponents([self.feedColorBar CGColor]));
|
|
|
|
// CGContextBeginPath(context);
|
|
|
|
// CGContextMoveToPoint(context, 6.0f + feedBarOffset, 0);
|
|
|
|
// CGContextAddLineToPoint(context, 6.0 + feedBarOffset, self.frame.size.height);
|
|
|
|
// CGContextStrokePath(context);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//extension ShapeStyle where Self == Color {
|
|
|
|
// static var darkBackground: Color {
|
|
|
|
// Color(red: 0.1, green: 0.1, blue: 0.2)
|
|
|
|
// }
|
|
|
|
// static var lightBackground: Color {
|
|
|
|
// Color(red: 0.2, green: 0.2, blue: 0.3)
|
|
|
|
// }
|
|
|
|
//}
|