diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryFeatureCardNavigationLink.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryFeatureCardNavigationLink.swift index 389a3b4a8..07e1d86d9 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryFeatureCardNavigationLink.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryFeatureCardNavigationLink.swift @@ -39,7 +39,7 @@ struct LibraryFeatureCardNavigationLink: View { } ) .confirmationDialog("", isPresented: $showFeatureActions) { - if FeaturedItemFilter(rawValue: viewModel.featureFilter) == .pinned { + if FeaturedItemFilter(rawValue: viewModel.fetcher.featureFilter) == .pinned { Button("Unpin", action: { viewModel.unpinItem(dataService: dataService, item: item) }) @@ -53,7 +53,7 @@ struct LibraryFeatureCardNavigationLink: View { Button("Remove", action: { viewModel.removeLibraryItem(dataService: dataService, objectID: item.objectID) }) - if FeaturedItemFilter(rawValue: viewModel.featureFilter) != .pinned { + if FeaturedItemFilter(rawValue: viewModel.fetcher.featureFilter) != .pinned { Button("Mark Read", action: { viewModel.markRead(dataService: dataService, item: item) }) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryItemFetcher.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryItemFetcher.swift index 09fb440a8..3e1501ea0 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryItemFetcher.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryItemFetcher.swift @@ -9,13 +9,13 @@ import Utils import Views @MainActor final class LibraryItemFetcher: NSObject, ObservableObject { - let folder = "inbox" - @Published var items = [Models.LibraryItem]() - var itemsPublisher: Published<[Models.LibraryItem]>.Publisher { $items } + @Published var featureItems = [Models.LibraryItem]() private var fetchedResultsController: NSFetchedResultsController? + @AppStorage(UserDefaultKey.lastSelectedFeaturedItemFilter.rawValue) var featureFilter = FeaturedItemFilter.continueReading.rawValue + var cursor: String? // These are used to make sure we handle search result @@ -23,8 +23,11 @@ import Views var searchIdx = 0 var receivedIdx = 0 - func setItems(_: NSManagedObjectContext, _ items: [Models.LibraryItem]) { + func setItems(_ context: NSManagedObjectContext, _ items: [Models.LibraryItem]) { self.items = items + if let filter = FeaturedItemFilter(rawValue: featureFilter) { + updateFeatureFilter(context: context, filter: filter) + } } func loadCurrentViewer(dataService: DataService) async { @@ -226,6 +229,29 @@ import Views return query } + + func refreshFeatureItems(dataService: DataService) { + if let featureFilter = FeaturedItemFilter(rawValue: self.featureFilter) { + updateFeatureFilter(context: dataService.viewContext, filter: featureFilter) + } + } + + func updateFeatureFilter(context: NSManagedObjectContext, filter: FeaturedItemFilter?) { + if let filter = filter { + Task { + featureFilter = filter.rawValue + + featureItems = await loadFeatureItems( + context: context, + predicate: filter.predicate, + sort: filter.sortDescriptor + ) + print("FEATURE ITEMS: ", featureItems) + } + } else { + featureItems = [] + } + } } extension LibraryItemFetcher: NSFetchedResultsControllerDelegate { diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 4a1a5db12..a24141112 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -131,15 +131,9 @@ struct AnimatingCellHeight: AnimatableModifier { .fullScreenCover(isPresented: $showExpandedAudioPlayer) { ExpandedAudioPlayer() } - .sheet(isPresented: $showOpenAIVoices) { - OpenAIVoicesModal(audioController: audioController) - } - .onAppear { - if !openAIPrimerDisplayed, !Voices.isOpenAIVoice(self.audioController.currentVoice) { - showOpenAIVoices = true - openAIPrimerDisplayed = true - } - } +// .onAppear { +// viewModel.refreshFeatureItems(dataService: dataService) +// } .toolbar { toolbarItems } @@ -182,6 +176,9 @@ struct AnimatingCellHeight: AnimatableModifier { LibraryAddLinkView() } } + .introspectNavigationController { nav in + nav.delegate = viewModel + } .task { await viewModel.loadFilters(dataService: dataService) } @@ -469,17 +466,17 @@ struct AnimatingCellHeight: AnimatableModifier { HStack { Menu(content: { Button(action: { - viewModel.updateFeatureFilter(context: dataService.viewContext, filter: .continueReading) + viewModel.fetcher.updateFeatureFilter(context: dataService.viewContext, filter: .continueReading) }, label: { Text("Continue Reading") }) Button(action: { - viewModel.updateFeatureFilter(context: dataService.viewContext, filter: .pinned) + viewModel.fetcher.updateFeatureFilter(context: dataService.viewContext, filter: .pinned) }, label: { Text("Pinned") }) Button(action: { - viewModel.updateFeatureFilter(context: dataService.viewContext, filter: .newsletters) + viewModel.fetcher.updateFeatureFilter(context: dataService.viewContext, filter: .newsletters) }, label: { Text("Newsletters") }) @@ -493,7 +490,7 @@ struct AnimatingCellHeight: AnimatableModifier { HStack(alignment: .center) { Image(systemName: "line.3.horizontal.decrease") .font(Font.system(size: 13, weight: .regular)) - Text((FeaturedItemFilter(rawValue: viewModel.featureFilter) ?? .continueReading).title) + Text((FeaturedItemFilter(rawValue: viewModel.fetcher.featureFilter) ?? .continueReading).title) .font(Font.system(size: 13, weight: .medium)) } .tint(Color(hex: "#007AFF")) @@ -510,17 +507,17 @@ struct AnimatingCellHeight: AnimatableModifier { GeometryReader { geo in ScrollView(.horizontal, showsIndicators: false) { - if viewModel.featureItems.count > 0 { + if viewModel.fetcher.featureItems.count > 0 { HStack(alignment: .top, spacing: 15) { Spacer(minLength: 1).frame(width: 1) - ForEach(viewModel.featureItems) { item in + ForEach(viewModel.fetcher.featureItems) { item in LibraryFeatureCardNavigationLink(item: item, viewModel: viewModel) } Spacer(minLength: 1).frame(width: 1) } .padding(.top, 0) } else { - Text((FeaturedItemFilter(rawValue: viewModel.featureFilter) ?? .continueReading).emptyMessage) + Text((FeaturedItemFilter(rawValue: viewModel.fetcher.featureFilter) ?? .continueReading).emptyMessage) .padding(.horizontal, UIDevice.isIPad ? 20 : 10) .font(Font.system(size: 14, weight: .regular)) .foregroundColor(Color(hex: "#898989")) @@ -1034,10 +1031,10 @@ struct LinkDestination: View { func fakeLibraryItems(dataService _: DataService) -> [LibraryItemData] { Array( - repeatElement(UUID().uuidString, count: 20) - .map { uuid in + repeatElement(0, count: 20) + .map { _ in LibraryItemData( - id: uuid, + id: UUID().uuidString, title: "fake title that is kind of long so it looks better", pageURLString: "", isArchived: false, diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index bc103eb3c..60236702d 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -5,7 +5,7 @@ import SwiftUI import Utils import Views -@MainActor final class HomeFeedViewModel: NSObject, ObservableObject, NSFetchedResultsControllerDelegate { +@MainActor final class HomeFeedViewModel: NSObject, ObservableObject, UINavigationControllerDelegate { let folder: String let fetcher: LibraryItemFetcher let listConfig: LibraryListConfig @@ -21,15 +21,10 @@ import Views @Published var presentWebContainer = false @Published var showLoadingBar = true - @Published var selectedLinkItem: NSManagedObjectID? // used by mac app only @Published var selectedItem: Models.LibraryItem? @Published var linkIsActive = false @Published var showLabelsSheet = false - @Published var showFiltersModal = false - @Published var showCommunityModal = false - @Published var featureItems = [Models.LibraryItem]() - @Published var showSnackbar = false @Published var snackbarOperation: SnackbarOperation? @@ -41,8 +36,6 @@ import Views @Published var appliedSort = LinkedItemSort.newest.rawValue @AppStorage(UserDefaultKey.hideFeatureSection.rawValue) var hideFeatureSection = false - @AppStorage(UserDefaultKey.lastSelectedFeaturedItemFilter.rawValue) var featureFilter = FeaturedItemFilter.continueReading.rawValue - @AppStorage("LibraryTabView::hideFollowingTab") var hideFollowingTab = false @Published var appliedFilter: InternalFilter? { @@ -63,22 +56,6 @@ import Views super.init() } - func updateFeatureFilter(context: NSManagedObjectContext, filter: FeaturedItemFilter?) { - if let filter = filter { - Task { - featureFilter = filter.rawValue - - featureItems = await loadFeatureItems( - context: context, - predicate: filter.predicate, - sort: filter.sortDescriptor - ) - } - } else { - featureItems = [] - } - } - func loadFilters(dataService: DataService) async { switch folder { case "following": @@ -156,7 +133,6 @@ import Views showLoadingBar = isRefresh await fetcher.loadItems(dataService: dataService, filterState: filterState, isRefresh: isRefresh) - updateFeatureFilter(context: dataService.viewContext, filter: FeaturedItemFilter(rawValue: featureFilter)) isLoading = false showLoadingBar = false @@ -176,7 +152,16 @@ import Views fetchRequest.predicate = predicate fetchRequest.sortDescriptors = [sort] - return (try? context.fetch(fetchRequest)) ?? [] + print("using predicate to load feature items: ", predicate) + + do { + let fetched = try context.fetch(fetchRequest) + return fetched + } catch { + print("ERROR FETCHING: ", error) + } + return [] +// return (try? context.fetch(fetchRequest)) ?? [] } func snackbar(_ message: String, undoAction: SnackbarUndoAction? = nil) { @@ -220,7 +205,7 @@ import Views dataService.setItemLabels(itemID: item.unwrappedID, labels: InternalLinkedItemLabel.make(Set(existingLabels + [label]) as NSSet)) item.update(inContext: dataService.viewContext) - updateFeatureFilter(context: dataService.viewContext, filter: FeaturedItemFilter(rawValue: featureFilter)) + fetcher.refreshFeatureItems(dataService: dataService) } } @@ -234,16 +219,12 @@ import Views func pinItem(dataService: DataService, item: Models.LibraryItem) { addLabel(dataService: dataService, item: item, label: "Pinned", color: "#0A84FF") - if featureFilter == FeaturedItemFilter.pinned.rawValue { - updateFeatureFilter(context: dataService.viewContext, filter: .pinned) - } + fetcher.refreshFeatureItems(dataService: dataService) } func unpinItem(dataService: DataService, item: Models.LibraryItem) { removeLabel(dataService: dataService, item: item, named: "Pinned") - if featureFilter == FeaturedItemFilter.pinned.rawValue { - updateFeatureFilter(context: dataService.viewContext, filter: .pinned) - } + fetcher.refreshFeatureItems(dataService: dataService) } func markRead(dataService: DataService, item: Models.LibraryItem) { diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/LibraryItemMenu.swift b/apple/OmnivoreKit/Sources/App/Views/Home/LibraryItemMenu.swift index b91ffdd46..2b68877bf 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/LibraryItemMenu.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/LibraryItemMenu.swift @@ -17,33 +17,6 @@ import Views action: { viewModel.itemUnderLabelEdit = item }, label: { Label(item.labels?.count == 0 ? "Add Labels" : "Edit Labels", systemImage: "tag") } ) -// Button(action: { -// withAnimation(.linear(duration: 0.4)) { -// viewModel.setLinkArchived( -// dataService: dataService, -// objectID: item.objectID, -// archived: !item.isArchived -// ) -// } -// }, label: { -// Label( -// item.isArchived ? "Unarchive" : "Archive", -// systemImage: item.isArchived ? "tray.and.arrow.down.fill" : "archivebox" -// ) -// }) -// Button("Remove Item", role: .destructive) { -// viewModel.removeLink(dataService: dataService, objectID: item.objectID) -// } -// if let author = item.author { -// Button( -// action: { -// viewModel.filterState.searchTerm = "author:\"\(author)\"" -// }, -// label: { -// Label(String("More by \(author)"), systemImage: "person") -// } -// ) -// } } else { Button( action: { viewModel.recoverItem(dataService: dataService, itemID: item.unwrappedID) }, diff --git a/apple/OmnivoreKit/Sources/App/Views/LibrarySidebar.swift b/apple/OmnivoreKit/Sources/App/Views/LibrarySidebar.swift index e50b7c169..b21cc0ba7 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LibrarySidebar.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LibrarySidebar.swift @@ -21,36 +21,6 @@ import SwiftUI @AppStorage("inboxMenuState") var inboxMenuState = "open" @AppStorage("followingMenuState") var followingMenuState = "open" - func createInboxViewModel(_ filter: InternalFilter) -> HomeFeedViewModel { - let result = HomeFeedViewModel( - folder: "inbox", - fetcher: LibraryItemFetcher(), - listConfig: LibraryListConfig( - hasFeatureCards: true, - leadingSwipeActions: [.pin], - trailingSwipeActions: [.archive, .delete], - cardStyle: .library - ) - ) - result.appliedFilter = filter - return result - } - - func createFollowingViewModel(_ filter: InternalFilter) -> HomeFeedViewModel { - let result = HomeFeedViewModel( - folder: "following", - fetcher: LibraryItemFetcher(), - listConfig: LibraryListConfig( - hasFeatureCards: false, - leadingSwipeActions: [.moveToInbox], - trailingSwipeActions: [.archive, .delete], - cardStyle: .library - ) - ) - result.appliedFilter = filter - return result - } - var innerBody: some View { ZStack { NavigationLink("", destination: HomeView(viewModel: inboxViewModel), isActive: $inboxActive) diff --git a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift index 44b35f264..90dfc2612 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift @@ -22,28 +22,6 @@ import Views trackReadEvent() } - func handleArchiveAction(dataService: DataService) { - guard let objectID = item?.objectID ?? pdfItem?.objectID else { return } - dataService.archiveLink(objectID: objectID, archived: !isItemArchived) - showInLibrarySnackbar(!isItemArchived ? "Link archived" : "Link unarchived") - } - - func handleDeleteAction(dataService: DataService) { - guard let objectID = item?.objectID ?? pdfItem?.objectID else { return } - removeLibraryItemAction(dataService: dataService, objectID: objectID) - } - - func updateItemReadStatus(dataService: DataService) { - guard let itemID = item?.unwrappedID ?? pdfItem?.itemID else { return } - - dataService.updateLinkReadingProgress( - itemID: itemID, - readingProgress: isItemRead ? 0 : 100, - anchorIndex: 0, - force: false - ) - } - private func trackReadEvent() { guard let itemID = item?.unwrappedID ?? pdfItem?.itemID else { return } guard let slug = item?.unwrappedSlug ?? pdfItem?.slug else { return }