diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 95832a2d0..f4a82b029 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -100,6 +100,8 @@ import Views } struct HomeFeedView: View { + @EnvironmentObject var dataService: DataService + let isCompact: Bool @Binding var searchQuery: String @Binding var selectedLinkItem: FeedItem? @@ -125,6 +127,32 @@ import Views itemToSnooze: $itemToSnooze, viewModel: viewModel ) + .toolbar { + ToolbarItem { + if #available(iOS 15.0, *) { + Button( + action: { + viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true) + }, + label: { Label("Refresh Feed", systemImage: "arrow.clockwise") } + ) + .disabled(viewModel.isLoading) + .opacity(viewModel.isLoading ? 0 : 1) + .overlay { + if viewModel.isLoading { + ProgressView() + } + } + } else { + Button( + action: { + viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true) + }, + label: { Label("Refresh Feed", systemImage: "arrow.clockwise") } + ) + } + } + } } } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift index 2018fe402..ae02dc4ff 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift @@ -19,60 +19,122 @@ import Views @ObservedObject var viewModel: HomeFeedViewModel var body: some View { + if #available(macOS 12.0, *) { + innerBody + } else { + innerBodyMac11 + } + } + + @available(macOS 12.0, *) + var innerBody: some View { List { Section { ForEach(viewModel.items) { item in - if #available(macOS 12.0, *) { - FeedCardNavigationLink( - item: item, - searchQuery: searchQuery, - selectedLinkItem: $selectedLinkItem, - viewModel: viewModel - ) - .contextMenu { - Button(action: { - viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: !item.isArchived) - }, label: { - Label( - item.isArchived ? "Unarchive" : "Archive", - systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox" - ) - }) - Button( - action: { - itemToRemove = item - confirmationShown = true - }, - label: { Label("Delete Link", systemImage: "trash") } + FeedCardNavigationLink( + item: item, + searchQuery: searchQuery, + selectedLinkItem: $selectedLinkItem, + viewModel: viewModel + ) + .contextMenu { + Button(action: { + viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: !item.isArchived) + }, label: { + Label( + item.isArchived ? "Unarchive" : "Archive", + systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox" ) - if FeatureFlag.enableSnooze { - Button { - itemToSnooze = item - snoozePresented = true - } label: { - Label { Text("Snooze") } icon: { Image.moon } - } - } - } - .alert("Are you sure?", isPresented: $confirmationShown) { - Button("Remove Link", role: .destructive) { - if let itemToRemove = itemToRemove { - withAnimation { - viewModel.removeLink(dataService: dataService, linkId: itemToRemove.id) - self.itemToRemove = nil - } - } - } - Button("Cancel", role: .cancel) { self.itemToRemove = nil } - } - } else { - FeedCardNavigationLink( - item: item, - searchQuery: searchQuery, - selectedLinkItem: $selectedLinkItem, - viewModel: viewModel + }) + Button( + action: { + itemToRemove = item + confirmationShown = true + }, + label: { Label("Delete Link", systemImage: "trash") } ) + if FeatureFlag.enableSnooze { + Button { + itemToSnooze = item + snoozePresented = true + } label: { + Label { Text("Snooze") } icon: { Image.moon } + } + } } + .alert("Are you sure?", isPresented: $confirmationShown) { + Button("Remove Link", role: .destructive) { + if let itemToRemove = itemToRemove { + withAnimation { + viewModel.removeLink(dataService: dataService, linkId: itemToRemove.id) + self.itemToRemove = nil + } + } + } + Button("Cancel", role: .cancel) { self.itemToRemove = nil } + } + } + } + + if viewModel.isLoading { + LoadingSection() + } + } + .listStyle(PlainListStyle()) + .navigationTitle("Home") + .searchable( + text: $searchQuery, + placement: .toolbar + ) { + if searchQuery.isEmpty { + Text("Inbox").searchCompletion("in:inbox ") + Text("All").searchCompletion("in:all ") + Text("Archived").searchCompletion("in:archive ") + Text("Files").searchCompletion("type:file ") + } + } + .onChange(of: searchQuery) { _ in + // Maybe we should debounce this, but + // it feels like it works ok without + viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true) + } + .onSubmit(of: .search) { + viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true) + } + .toolbar { + ToolbarItem { + Button( + action: { + viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true) + }, + label: { Label("Refresh Feed", systemImage: "arrow.clockwise") } + ) + .disabled(viewModel.isLoading) + .opacity(viewModel.isLoading ? 0 : 1) + .overlay { + if viewModel.isLoading { + ProgressView() + } + } + } + } + .onAppear { + if viewModel.items.isEmpty { + viewModel.loadItems(dataService: dataService, searchQuery: searchQuery, isRefresh: true) + } + } + } + + var innerBodyMac11: some View { + List { + Section { + ForEach(viewModel.items) { item in + FeedCardNavigationLink( + item: item, + searchQuery: searchQuery, + selectedLinkItem: $selectedLinkItem, + viewModel: viewModel + ) } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index 20204280e..1d7c11fed 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -45,7 +45,6 @@ final class HomeFeedViewModel: ObservableObject { searchIdx += 1 isLoading = true - startNetworkActivityIndicator() // Cache the viewer if dataService.currentViewer == nil { @@ -64,10 +63,8 @@ final class HomeFeedViewModel: ObservableObject { ) .sink( receiveCompletion: { [weak self] completion in - guard case let .failure(error) = completion else { return } + guard case .failure = completion else { return } self?.isLoading = false - stopNetworkActivityIndicator() - print(error) }, receiveValue: { [weak self] result in // Search results aren't guaranteed to return in order so this @@ -82,7 +79,6 @@ final class HomeFeedViewModel: ObservableObject { self?.isLoading = false self?.receivedIdx = thisSearchIdx self?.cursor = result.cursor - stopNetworkActivityIndicator() } ) .store(in: &subscriptions) @@ -90,7 +86,6 @@ final class HomeFeedViewModel: ObservableObject { func setLinkArchived(dataService: DataService, linkId: String, archived: Bool) { isLoading = true - startNetworkActivityIndicator() // First remove the link from the internal list, // then make a call to remove it. The isLoading block should @@ -103,15 +98,12 @@ final class HomeFeedViewModel: ObservableObject { dataService.archiveLinkPublisher(itemID: linkId, archived: archived) .sink( receiveCompletion: { [weak self] completion in - guard case let .failure(error) = completion else { return } + guard case .failure = completion else { return } self?.isLoading = false - stopNetworkActivityIndicator() - print(error) NSNotification.operationFailed(message: archived ? "Failed to archive link" : "Failed to unarchive link") }, receiveValue: { [weak self] _ in self?.isLoading = false - stopNetworkActivityIndicator() Snackbar.show(message: archived ? "Link archived" : "Link moved to Inbox") } ) @@ -120,7 +112,6 @@ final class HomeFeedViewModel: ObservableObject { func removeLink(dataService: DataService, linkId: String) { isLoading = true - startNetworkActivityIndicator() if let itemIndex = items.firstIndex(where: { $0.id == linkId }) { items.remove(at: itemIndex) @@ -131,12 +122,10 @@ final class HomeFeedViewModel: ObservableObject { receiveCompletion: { [weak self] completion in guard case .failure = completion else { return } self?.isLoading = false - stopNetworkActivityIndicator() Snackbar.show(message: "Failed to remove link") }, receiveValue: { [weak self] _ in self?.isLoading = false - stopNetworkActivityIndicator() Snackbar.show(message: "Link removed") } ) @@ -145,7 +134,6 @@ final class HomeFeedViewModel: ObservableObject { func snoozeUntil(dataService: DataService, linkId: String, until: Date, successMessage: String?) { isLoading = true - startNetworkActivityIndicator() if let itemIndex = items.firstIndex(where: { $0.id == linkId }) { items.remove(at: itemIndex) @@ -157,15 +145,12 @@ final class HomeFeedViewModel: ObservableObject { ) .sink( receiveCompletion: { [weak self] completion in - guard case let .failure(error) = completion else { return } + guard case .failure = completion else { return } self?.isLoading = false - stopNetworkActivityIndicator() - print(error) NSNotification.operationFailed(message: "Failed to snooze") }, receiveValue: { [weak self] _ in self?.isLoading = false - stopNetworkActivityIndicator() if let message = successMessage { Snackbar.show(message: message) } @@ -174,15 +159,3 @@ final class HomeFeedViewModel: ObservableObject { .store(in: &subscriptions) } } - -private func startNetworkActivityIndicator() { - #if os(iOS) - UIApplication.shared.isNetworkActivityIndicatorVisible = true - #endif -} - -private func stopNetworkActivityIndicator() { - #if os(iOS) - UIApplication.shared.isNetworkActivityIndicatorVisible = false - #endif -}