diff --git a/apple/OmnivoreKit/Sources/App/Views/File.swift b/apple/OmnivoreKit/Sources/App/Views/File.swift index 9a6b8f4d3..83da77db3 100644 --- a/apple/OmnivoreKit/Sources/App/Views/File.swift +++ b/apple/OmnivoreKit/Sources/App/Views/File.swift @@ -13,38 +13,47 @@ class SlideAnimatedTransitioning: NSObject {} extension SlideAnimatedTransitioning: UIViewControllerAnimatedTransitioning { func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let containerView = transitionContext.containerView - let fromView = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!.view - let toView = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!.view + guard + let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from), + let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) + else { + return + } let width = containerView.frame.width - var offsetLeft = fromView?.frame + var offsetLeft = fromVC.view?.frame offsetLeft?.origin.x = width - var offscreenRight = toView?.frame + var offscreenRight = fromVC.view?.frame offscreenRight?.origin.x = -width / 3.33 - toView?.frame = offscreenRight! + toVC.view?.frame = offscreenRight! - fromView?.layer.shadowRadius = 5.0 - fromView?.layer.shadowOpacity = 1.0 - toView?.layer.opacity = 0.9 + fromVC.view?.layer.shadowRadius = 5.0 + fromVC.view?.layer.shadowOpacity = 1.0 + toVC.view?.layer.opacity = 0.9 - containerView.insertSubview(toView!, belowSubview: fromView!) + transitionContext.containerView.addSubview(toVC.view) + transitionContext.containerView.addSubview(fromVC.view) + + // containerView.insertSubview(toView!, belowSubview: fromView!) + print("CONTAINER VIEW: ", containerView) UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: .curveLinear, animations: { - toView?.frame = (fromView?.frame)! - fromView?.frame = offsetLeft! + toVC.view?.frame = (fromVC.view?.frame)! + fromVC.view?.frame = offsetLeft! - toView?.layer.opacity = 1.0 - fromView?.layer.shadowOpacity = 0.1 + toVC.view?.layer.opacity = 1.0 + fromVC.view?.layer.shadowOpacity = 0.1 }, completion: { _ in - toView?.layer.opacity = 1.0 - toView?.layer.shadowOpacity = 0 - fromView?.layer.opacity = 1.0 - fromView?.layer.shadowOpacity = 0 + toVC.view?.layer.opacity = 1.0 + toVC.view?.layer.shadowOpacity = 0 + fromVC.view?.layer.opacity = 1.0 + fromVC.view?.layer.shadowOpacity = 0 + fromVC.view.removeFromSuperview() // when cancelling or completing the animation, ios simulator seems to sometimes flash black backgrounds during the animation. on devices, this doesn't seem to happen though. // containerView.backgroundColor = [UIColor whiteColor]; transitionContext.completeTransition(!transitionContext.transitionWasCancelled) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift index c57a9013e..f76e90378 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift @@ -66,28 +66,16 @@ struct GridCardNavigationLink: View { @ObservedObject var viewModel: HomeFeedViewModel var body: some View { - ZStack { - Button { - if isContextMenuOpen { - isContextMenuOpen = false - } else { - // viewModel.selectedItem = item - // viewModel.linkIsActive = true - } - } label: { - NavigationLink(destination: EmptyView()) { - EmptyView() - } - .opacity(0) - .buttonStyle(PlainButtonStyle()) - .onAppear { - Task { await viewModel.itemAppeared(item: item, dataService: dataService) } - } - GridCard(item: item, isContextMenuOpen: $isContextMenuOpen, actionHandler: actionHandler) - } + NavigationLink(destination: LinkItemDetailView( + linkedItemObjectID: item.objectID, + isPDF: item.isPDF + )) { + GridCard(item: item, isContextMenuOpen: $isContextMenuOpen, actionHandler: actionHandler) } - .aspectRatio(1.8, contentMode: .fill) - .scaleEffect(scale) + .onAppear { + Task { await viewModel.itemAppeared(item: item, dataService: dataService) } + } + .aspectRatio(1.0, contentMode: .fill) .background( Color.secondarySystemGroupedBackground .onTapGesture { diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryFeatureCardNavigationLink.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryFeatureCardNavigationLink.swift index 21bbbf243..78a8c7409 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryFeatureCardNavigationLink.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryFeatureCardNavigationLink.swift @@ -20,7 +20,10 @@ struct LibraryFeatureCardNavigationLink: View { @State var showFeatureActions = false var body: some View { - NavigationLink(destination: EmptyView()) { + NavigationLink(destination: LinkItemDetailView( + linkedItemObjectID: item.objectID, + isPDF: item.isPDF + )) { LibraryFeatureCard(item: item, viewer: dataService.currentViewer) } .confirmationDialog("", isPresented: $showFeatureActions) { diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index b127f0114..b4ce54777 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -159,8 +159,8 @@ struct AnimatingCellHeight: AnimatableModifier { guard let objectID = dataService.persist(jsonArticle: jsonArticle) else { return } guard let linkedItem = dataService.viewContext.object(with: objectID) as? LinkedItem else { return } viewModel.pushFeedItem(item: linkedItem) - // viewModel.selectedItem = linkedItem - // viewModel.linkIsActive = true + viewModel.selectedItem = linkedItem + viewModel.linkIsActive = true } .onOpenURL { url in viewModel.linkRequest = nil @@ -228,6 +228,19 @@ struct AnimatingCellHeight: AnimatableModifier { var body: some View { VStack(spacing: 0) { + if let linkRequest = viewModel.linkRequest { + NavigationLink( + destination: WebReaderLoadingContainer(requestID: linkRequest.serverID), + tag: linkRequest, + selection: $viewModel.linkRequest + ) { + EmptyView() + } + } + NavigationLink(destination: LinkDestination(selectedItem: viewModel.selectedItem), isActive: $viewModel.linkIsActive) { + EmptyView() + } + if prefersListLayout || !enableGrid { HomeFeedListView(listTitle: $listTitle, isListScrolled: $isListScrolled, prefersListLayout: $prefersListLayout, viewModel: viewModel) } else { @@ -702,7 +715,7 @@ struct AnimatingCellHeight: AnimatableModifier { .padding(.leading, 16) .padding(.bottom, 25) - LazyVGrid(columns: [GridItem(.adaptive(minimum: 325), spacing: 16)], alignment: .leading, spacing: 16) { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 325, maximum: 400), spacing: 16)], alignment: .center, spacing: 30) { ForEach(viewModel.items) { item in GridCardNavigationLink( item: item, @@ -710,9 +723,9 @@ struct AnimatingCellHeight: AnimatableModifier { isContextMenuOpen: $isContextMenuOpen, viewModel: viewModel ) - .contextMenu { - libraryItemMenu(dataService: dataService, viewModel: viewModel, item: item) - } +// .contextMenu { +// libraryItemMenu(dataService: dataService, viewModel: viewModel, item: item) +// } } Spacer() } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index 2cb2a8c0d..d309dcc3a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -27,7 +27,7 @@ import Views @Published var appliedSort = LinkedItemSort.newest.rawValue @Published var selectedLinkItem: NSManagedObjectID? // used by mac app only - // @Published var selectedItem: LinkedItem? + @Published var selectedItem: LinkedItem? @Published var linkIsActive = false @Published var showLabelsSheet = false diff --git a/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift b/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift index d31ca60a6..9d3347b7a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift @@ -43,50 +43,14 @@ struct LibraryTabView: View { ) var body: some View { - NavigationView { -// ZStack { -// NavigationLink( -// destination: LinkDestination(selectedItem: libraryViewModel.selectedItem), -// isActive: $libraryViewModel.linkIsActive -// ) { -// EmptyView() -// } - // TabView(selection: $selection) { - // BriefingView( - // articleId: "98e017a3-79d5-4049-97bc-ff170153792a" - // ) - // .tabItem { - // Label { - // Text("Your Briefing") - // } icon: { - // Image.tabBriefing.padding(.trailing, 5) - // } - // }.tag(0) - - HomeView(viewModel: libraryViewModel) - // .tabItem { - // Label { - // Text("Library") - // } icon: { - // Image.tabLibrary - // } - // }.tag(1) - - // HomeView(viewModel: highlightsViewModel) - // .tabItem { - // Label { - // Text("Highlights") - // } icon: { - // Image.tabHighlights - // } - // }.tag(2) - // } + if #available(iOS 16.0, *) { + NavigationView { + HomeView(viewModel: libraryViewModel) + .navigationBarHidden(false) + } + } else { + // Fallback on earlier versions + EmptyView() } -// }.introspectNavigationController { controller in -// print("INTROSPECTING CONTROLLER: ", controller.transitionCoordinator) -// controller.transitionCoordinator?.notifyWhenInteractionChanges { context in -// print("interaction changed: ", context) -// } -// } } } diff --git a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift index fa6aea0f6..83422da7f 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift @@ -89,15 +89,15 @@ struct LinkItemDetailView: View { pdfContainerView } else if let item = viewModel.item { WebReaderContainerView(item: item, pop: { dismiss() }) + .navigationBarHidden(true) + .lazyPop(pop: { + dismiss() + }, isEnabled: $isEnabled) } } .task { await viewModel.loadItem(linkedItemObjectID: linkedItemObjectID, dataService: dataService) } - .navigationBarHidden(true) - .lazyPop(pop: { - dismiss() - }, isEnabled: $isEnabled) } @ViewBuilder private var pdfContainerView: some View { @@ -117,30 +117,3 @@ struct LinkItemDetailView: View { } } } - -// -// #if os(iOS) -// // Enable swipe to go back behavior if nav bar is hidden -// extension UINavigationController: UIGestureRecognizerDelegate, UINavigationControllerDelegate { -// override open func viewDidLoad() { -// super.viewDidLoad() -// print("INIT: ", viewControllers) -// delegate = self -// interactivePopGestureRecognizer?.delegate = nil -// } -//// -//// public func gestureRecognizerShouldBegin(_ gesture: UIGestureRecognizer) -> Bool { -//// print("SHOULD BEGIN: ", viewControllers, gesture) -//// return viewControllers.count > 1 -//// } -//// -//// public func navigationController(_ navigationController: UINavigationController, willShow _: UIViewController, animated _: Bool) { -//// navigationController.transitionCoordinator?.notifyWhenInteractionChanges { context in -//// print("DID SHOW CONTEXT: ", context.percentComplete, navigationController.viewControllers) -//// navigationController.interactivePopGestureRecognizer?.delegate = nil -//// navigationController.popToRootViewController(animated: false) -//// // interactivePopGestureRecognizer. -//// } -//// } -// } -// #endif diff --git a/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift b/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift index 19384d98b..292810f96 100644 --- a/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift @@ -49,22 +49,22 @@ struct InnerRootView: View { @ViewBuilder private var innerBody: some View { if authenticator.isLoggedIn { - GeometryReader { geo in - PrimaryContentView() - #if os(iOS) - .miniPlayer() - .formSheet(isPresented: $viewModel.showNewFeaturePrimer, - modalSize: CGSize(width: geo.size.width * 0.66, height: geo.size.width * 0.66)) { - FeaturePrimer.recommendationsPrimer - } - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) { - viewModel.showNewFeaturePrimer = viewModel.shouldShowNewFeaturePrimer - viewModel.shouldShowNewFeaturePrimer = false - } - } - #endif - } + // GeometryReader { geo in + PrimaryContentView() +// #if os(iOS) +// .miniPlayer() +// .formSheet(isPresented: $viewModel.showNewFeaturePrimer, +// modalSize: CGSize(width: geo.size.width * 0.66, height: geo.size.width * 0.66)) { +// FeaturePrimer.recommendationsPrimer +// } +// .onAppear { +// DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) { +// viewModel.showNewFeaturePrimer = viewModel.shouldShowNewFeaturePrimer +// viewModel.shouldShowNewFeaturePrimer = false +// } +// } +// #endif +// } } else { WelcomeView() .accessibilityElement() diff --git a/apple/OmnivoreKit/Sources/App/Views/SwipeRightToPopViewController.swift b/apple/OmnivoreKit/Sources/App/Views/SwipeRightToPopViewController.swift index 94eff4279..82faaa9e2 100644 --- a/apple/OmnivoreKit/Sources/App/Views/SwipeRightToPopViewController.swift +++ b/apple/OmnivoreKit/Sources/App/Views/SwipeRightToPopViewController.swift @@ -121,7 +121,11 @@ class SwipeRightToPopViewController: UIHostingController, UINa from _: UIViewController, to _: UIViewController) -> UIViewControllerAnimatedTransitioning? { - SlideAnimatedTransitioning() + if #available(iOS 17.0, *) { + return nil + } else { + return SlideAnimatedTransitioning() + } } func navigationController(_: UINavigationController, @@ -129,7 +133,7 @@ class SwipeRightToPopViewController: UIHostingController, UINa -> UIViewControllerInteractiveTransitioning? { parentNavigationControllerToUse?.delegate = nil -// navigationController.delegate = nil + // navigationController.delegate = nil if panGestureRecognizer.state == .began { percentDrivenInteractiveTransition = UIPercentDrivenInteractiveTransition() diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift index b0202e141..545968187 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderLoadingContainer.swift @@ -34,8 +34,11 @@ import Views public struct WebReaderLoadingContainer: View { let requestID: String + @Environment(\.dismiss) private var dismiss @EnvironmentObject var dataService: DataService @EnvironmentObject var audioController: AudioController + + @State var lazyPopIsEnabled = true @StateObject var viewModel = WebReaderLoadingContainerViewModel() public var body: some View { @@ -53,9 +56,11 @@ public struct WebReaderLoadingContainer: View { } #endif } else { - WebReaderContainerView(item: item, pop: {}) + WebReaderContainerView(item: item, pop: { dismiss() }) #if os(iOS) .navigationViewStyle(.stack) + .navigationBarHidden(true) + .lazyPop(pop: { dismiss() }, isEnabled: $lazyPopIsEnabled) #endif .accentColor(.appGrayTextContrast) .task { viewModel.trackReadEvent() } diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift index e6b6fe9d5..f1c7acb27 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift @@ -69,20 +69,67 @@ public struct GridCard: View { } } + var imageBox: some View { + GeometryReader { geo in + + ZStack(alignment: .bottomLeading) { + if let imageURL = item.imageURL { + AsyncImage(url: imageURL) { phase in + switch phase { + case .empty: + Color.systemBackground + .frame(maxWidth: .infinity, maxHeight: geo.size.height) + case let .success(image): + image.resizable() + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: geo.size.height) + .clipped() + case .failure: + fallbackImage + + @unknown default: + // Since the AsyncImagePhase enum isn't frozen, + // we need to add this currently unused fallback + // to handle any new cases that might be added + // in the future: + Color.systemBackground + .frame(maxWidth: .infinity, maxHeight: geo.size.height) + } + } + } else { + fallbackImage + } + Color(hex: "#D9D9D9")?.opacity(0.65).frame(width: geo.size.width, height: 5) + Color(hex: "#FFD234").frame(width: geo.size.width * (item.readingProgress / 100), height: 5) + } + } + .cornerRadius(5) + } + + var fallbackImage: some View { + GeometryReader { geo in + HStack { + Text(item.unwrappedTitle.prefix(1)) + .font(Font.system(size: 128, weight: .bold)) + .offset(CGSize(width: -48, height: 12)) + .frame(alignment: .bottomLeading) + .foregroundColor(Gradient.randomColor(str: item.unwrappedTitle, offset: 1)) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Gradient.randomColor(str: item.unwrappedTitle, offset: 0)) + .background(LinearGradient(gradient: Gradient(fromStr: item.unwrappedTitle)!, startPoint: .top, endPoint: .bottom)) + .frame(width: geo.size.width, height: geo.size.height) + } + } + public var body: some View { GeometryReader { geo in VStack(alignment: .leading, spacing: 0) { - // Progress Bar - Group { - ProgressView(value: min(abs(item.readingProgress) / 100, 1)) - .tint(.appYellow48) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.bottom, 16) - } - // .onTapGesture { tapHandler() } - VStack { - // Title, Subtitle, Menu Button + imageBox + .frame(height: geo.size.height / 2.0) + VStack(alignment: .leading, spacing: 4) { HStack { Text(item.unwrappedTitle) @@ -111,8 +158,9 @@ public struct GridCard: View { // .onTapGesture { tapHandler() } } .frame(height: 30) - .padding(.horizontal) - .padding(.bottom, 16) + .padding(.horizontal, 10) + .padding(.bottom, 10) + .padding(.top, 10) // Link description and image HStack(alignment: .top) { @@ -123,27 +171,8 @@ public struct GridCard: View { .multilineTextAlignment(.leading) Spacer() - - if let imageURL = item.imageURL { - AsyncImage(url: imageURL) { phase in - if let image = phase.image { - image - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: geo.size.width / 3, height: (geo.size.width * 2) / 9) - .cornerRadius(3) - } else if phase.error != nil { - EmptyView() - } else { - Color.appButtonBackground - .frame(width: geo.size.width / 3, height: (geo.size.width * 2) / 9) - .cornerRadius(3) - } - } - } } - .padding(.horizontal) - // .onTapGesture { tapHandler() } + .padding(.horizontal, 10) // Category Labels if item.hasLabels { @@ -154,7 +183,7 @@ public struct GridCard: View { } Spacer() } - .padding(.horizontal) + .padding(.horizontal, 10) } // .onTapGesture { tapHandler() } } @@ -165,7 +194,6 @@ public struct GridCard: View { } .padding(.horizontal, 0) .padding(.top, 0) - .padding(.bottom, 8) } .contextMenu { contextMenuView } } diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/LibraryItemCard.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/LibraryItemCard.swift index 7c9e3214d..f52199f98 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/LibraryItemCard.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/LibraryItemCard.swift @@ -149,7 +149,7 @@ public struct LibraryItemCard: View { } var imageBox: some View { - Group { + ZStack(alignment: .bottomLeading) { if let imageURL = item.imageURL { AsyncImage(url: imageURL) { phase in if let image = phase.image { @@ -166,8 +166,24 @@ public struct LibraryItemCard: View { .padding(.top, 2) } } + } else { + fallbackImage } } + .cornerRadius(5) + } + + var fallbackImage: some View { + HStack { + Text(item.unwrappedTitle.prefix(1)) + .font(Font.system(size: 32, weight: .bold)) + .frame(alignment: .bottomLeading) + .foregroundColor(Gradient.randomColor(str: item.unwrappedTitle, offset: 1)) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Gradient.randomColor(str: item.unwrappedTitle, offset: 0)) + .background(LinearGradient(gradient: Gradient(fromStr: item.unwrappedTitle)!, startPoint: .top, endPoint: .bottom)) + .frame(width: 74 * 0.666, height: 74) } var bylineStr: String { diff --git a/apple/OmnivoreKit/Sources/Views/UINavigationControllerExtension.swift b/apple/OmnivoreKit/Sources/Views/UINavigationControllerExtension.swift index 2fcc4bddc..32a2a920f 100644 --- a/apple/OmnivoreKit/Sources/Views/UINavigationControllerExtension.swift +++ b/apple/OmnivoreKit/Sources/Views/UINavigationControllerExtension.swift @@ -1,12 +1,12 @@ -#if os(iOS) - - import UIKit - - extension UINavigationController { - // Remove back button text - override open func viewWillLayoutSubviews() { - navigationBar.topItem?.backButtonDisplayMode = .minimal - } - } - -#endif +// #if os(iOS) +// +// import UIKit +// +// extension UINavigationController { +// // Remove back button text +// override open func viewWillLayoutSubviews() { +// navigationBar.topItem?.backButtonDisplayMode = .minimal +// } +// } +// +// #endif