From 60aa05c56743e97e6a9167075ad0332302df8e7c Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Mon, 9 May 2022 11:43:15 -0700 Subject: [PATCH 1/6] add a shimmering loader --- .../App/Views/Home/HomeFeedViewIOS.swift | 1 + .../Sources/Views/ShimmerView.swift | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 apple/OmnivoreKit/Sources/Views/ShimmerView.swift diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 9c1b17f0e..043ff1e9d 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -155,6 +155,7 @@ private let enableGrid = UIDevice.isIPad || FeatureFlag.enableGridCardsOnPhone } } } + ShimmeringLoader() if prefersListLayout || !enableGrid { HomeFeedListView(prefersListLayout: $prefersListLayout, viewModel: viewModel) } else { diff --git a/apple/OmnivoreKit/Sources/Views/ShimmerView.swift b/apple/OmnivoreKit/Sources/Views/ShimmerView.swift new file mode 100644 index 000000000..b977259f7 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/ShimmerView.swift @@ -0,0 +1,54 @@ +import SwiftUI + +public struct ShimmeringLoader: View { + @State private var phase: CGFloat = 0 + + public init() {} + + public var body: some View { + ZStack { + Color.systemBackground + Color.appGraySolid + .contentShape(Rectangle()) + .modifier(AnimatedMask(phase: phase).animation( + Animation.linear(duration: 2.0) + .repeatForever(autoreverses: false) + )) + .onAppear { phase = 0.8 } + } + .frame(height: 2) + .frame(maxWidth: .infinity) + } + + /// An animatable modifier to interpolate between `phase` values. + struct AnimatedMask: AnimatableModifier { + var phase: CGFloat = 0 + + var animatableData: CGFloat { + get { phase } + set { phase = newValue } + } + + func body(content: Content) -> some View { + content + .mask(GradientMask(phase: phase).scaleEffect(3)) + } + } + + /// An animatable gradient between transparent and opaque to use as mask. + /// The `phase` parameter shifts the gradient, moving the opaque band. + struct GradientMask: View { + let phase: CGFloat + let centerColor = Color.appGraySolid + let edgeColor = Color.clear + + var body: some View { + LinearGradient(gradient: + Gradient(stops: [ + .init(color: edgeColor, location: phase), + .init(color: centerColor, location: phase + 0.1), + .init(color: edgeColor, location: phase + 0.2) + ]), startPoint: .leading, endPoint: .trailing) + } + } +} From e6d1a0840b42ed545410b1b55d945df4f1eaae74 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Mon, 9 May 2022 11:48:03 -0700 Subject: [PATCH 2/6] set loading bar on zstack over labels bar --- .../App/Views/Home/HomeFeedViewIOS.swift | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 043ff1e9d..a86dcce2f 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -136,26 +136,28 @@ private let enableGrid = UIDevice.isIPad || FeatureFlag.enableGridCardsOnPhone var body: some View { VStack(spacing: 0) { - ScrollView(.horizontal, showsIndicators: false) { - HStack { - TextChipButton.makeAddLabelButton { - showLabelsSheet = true + ZStack(alignment: .bottom) { + ScrollView(.horizontal, showsIndicators: false) { + HStack { + TextChipButton.makeAddLabelButton { + showLabelsSheet = true + } + ForEach(viewModel.selectedLabels, id: \.self) { label in + TextChipButton.makeRemovableLabelButton(feedItemLabel: label) { + viewModel.selectedLabels.removeAll { $0.id == label.id } + } + } + Spacer() } - ForEach(viewModel.selectedLabels, id: \.self) { label in - TextChipButton.makeRemovableLabelButton(feedItemLabel: label) { - viewModel.selectedLabels.removeAll { $0.id == label.id } + .padding(.horizontal) + .sheet(isPresented: $showLabelsSheet) { + ApplyLabelsView(mode: .list(viewModel.selectedLabels)) { + viewModel.selectedLabels = $0 } } - Spacer() - } - .padding(.horizontal) - .sheet(isPresented: $showLabelsSheet) { - ApplyLabelsView(mode: .list(viewModel.selectedLabels)) { - viewModel.selectedLabels = $0 - } } + ShimmeringLoader() } - ShimmeringLoader() if prefersListLayout || !enableGrid { HomeFeedListView(prefersListLayout: $prefersListLayout, viewModel: viewModel) } else { From 65f2baa8c48eedf2560f7c13b4370882089579d4 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Mon, 9 May 2022 11:55:15 -0700 Subject: [PATCH 3/6] show loading bar as item content is prefetched --- .../OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift | 4 +++- .../Sources/App/Views/Home/HomeFeedViewModel.swift | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index a86dcce2f..6d1098678 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -156,7 +156,9 @@ private let enableGrid = UIDevice.isIPad || FeatureFlag.enableGridCardsOnPhone } } } - ShimmeringLoader() + if viewModel.showLoadingBar { + ShimmeringLoader() + } } if prefersListLayout || !enableGrid { HomeFeedListView(prefersListLayout: $prefersListLayout, viewModel: viewModel) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index 4c08a1dcd..1d46f3235 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -17,6 +17,7 @@ import Views @Published var snoozePresented = false @Published var itemToSnoozeID: String? @Published var selectedLinkItem: LinkedItem? + @Published var showLoadingBar = false var cursor: String? @@ -47,6 +48,7 @@ import Views searchIdx += 1 isLoading = true + showLoadingBar = true // Cache the viewer if dataService.currentViewer == nil { @@ -81,6 +83,7 @@ import Views receivedIdx = thisSearchIdx cursor = queryResult.cursor await dataService.prefetchPages(itemIDs: newItems.map(\.unwrappedID)) + showLoadingBar = false } else if searchTermIsEmpty { await dataService.viewContext.perform { let fetchRequest: NSFetchRequest = LinkedItem.fetchRequest() @@ -94,6 +97,7 @@ import Views self.isLoading = false } } + showLoadingBar = false } } From 9cab60b1070508058df85935403292b0c937a5c7 Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Mon, 9 May 2022 13:40:20 -0700 Subject: [PATCH 4/6] set an empty nav title for reader view to prevent nav bar from reappearing --- .../Views/Home/Components/FeedCardNavigationLink.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift index 3776647b7..075a09464 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift @@ -13,7 +13,9 @@ struct FeedCardNavigationLink: View { var body: some View { let destination = LinkItemDetailView(viewModel: LinkItemDetailViewModel(item: item, homeFeedViewModel: viewModel)) #if os(iOS) - let modifiedDestination = destination.navigationBarHidden(true) + let modifiedDestination = destination + .navigationBarHidden(true) + .navigationTitle("") #else let modifiedDestination = destination #endif @@ -51,7 +53,9 @@ struct GridCardNavigationLink: View { var body: some View { let destination = LinkItemDetailView(viewModel: LinkItemDetailViewModel(item: item, homeFeedViewModel: viewModel)) #if os(iOS) - let modifiedDestination = destination.navigationBarHidden(true) + let modifiedDestination = destination + .navigationBarHidden(true) + .navigationTitle("") #else let modifiedDestination = destination #endif From bca28bb6995d83d381bfbef014ca9e62c9134a5c Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Mon, 9 May 2022 15:33:02 -0700 Subject: [PATCH 5/6] remove aspect ratio constraint --- apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift index 96690b7f6..52a477c39 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift @@ -50,7 +50,7 @@ public struct FeedCard: View { if case let AsyncImageStatus.loaded(image) = imageStatus { image .resizable() - .aspectRatio(1, contentMode: .fill) + .aspectRatio(contentMode: .fill) .frame(width: 80, height: 80) .cornerRadius(6) } else if case AsyncImageStatus.loading = imageStatus { From e2f08e71416edb6a0446402df4f788fded7d122e Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Mon, 9 May 2022 16:18:59 -0700 Subject: [PATCH 6/6] ensure nav bar is hidden if searchable field is active in home view --- .../Components/FeedCardNavigationLink.swift | 2 -- .../App/Views/Home/HomeFeedViewIOS.swift | 3 +-- .../Sources/App/Views/Home/HomeView.swift | 2 +- .../Sources/App/Views/LinkItemDetailView.swift | 17 +++++++++++++++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift index 075a09464..0ff39e4e4 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift @@ -14,7 +14,6 @@ struct FeedCardNavigationLink: View { let destination = LinkItemDetailView(viewModel: LinkItemDetailViewModel(item: item, homeFeedViewModel: viewModel)) #if os(iOS) let modifiedDestination = destination - .navigationBarHidden(true) .navigationTitle("") #else let modifiedDestination = destination @@ -54,7 +53,6 @@ struct GridCardNavigationLink: View { let destination = LinkItemDetailView(viewModel: LinkItemDetailViewModel(item: item, homeFeedViewModel: viewModel)) #if os(iOS) let modifiedDestination = destination - .navigationBarHidden(true) .navigationTitle("") #else let modifiedDestination = destination diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 6d1098678..62bd31f04 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -27,8 +27,7 @@ private let enableGrid = UIDevice.isIPad || FeatureFlag.enableGridCardsOnPhone loadItems(isRefresh: true) } .searchable( - text: $viewModel.searchTerm, - placement: .navigationBarDrawer + text: $viewModel.searchTerm ) { if viewModel.searchTerm.isEmpty { Text("Inbox").searchCompletion("in:inbox ") diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeView.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeView.swift index 1fbbe840b..b025250da 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeView.swift @@ -9,7 +9,7 @@ struct HomeView: View { NavigationView { HomeFeedContainerView(viewModel: viewModel) } - .navigationViewStyle(StackNavigationViewStyle()) + .navigationViewStyle(.stack) .accentColor(.appGrayTextContrast) } else { HomeFeedContainerView(viewModel: viewModel) diff --git a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift index 6230a4cfb..1b5124b36 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift @@ -136,14 +136,27 @@ struct LinkItemDetailView: View { ) } + // We always want this hidden but setting it to false initially + // fixes a bug where SwiftUI searchable will always show the nav bar + // if the search field is active when pushing. + @State var hideNavBar = false + var body: some View { #if os(iOS) if viewModel.item.isPDF { fixedNavBarReader - .task { viewModel.trackReadEvent() } + .navigationBarHidden(hideNavBar) + .task { + hideNavBar = true + viewModel.trackReadEvent() + } } else { WebReaderContainerView(item: viewModel.item, homeFeedViewModel: viewModel.homeFeedViewModel) - .task { viewModel.trackReadEvent() } + .navigationBarHidden(hideNavBar) + .task { + hideNavBar = true + viewModel.trackReadEvent() + } } #else fixedNavBarReader