import { ref, computed, nextTick } from 'vue'; import axios from 'axios'; import { Readability } from '@mozilla/readability'; // Module-level state — declared outside useFeeds() so every caller shares the // same singleton refs (a Pinia-free "store" for the feed list and its UI state). const showMessage = ref(false) // `allItems` is the full flattened+sorted stream from the last fetch (the // master); `feeds` is the currently displayed projection of it (all items, or a // single feed when a filter is active). Every view/observer consumer operates on // `feeds` — the filter layer only ever swaps what `feeds` points at, so none of // the index-based observer / currentIndex logic has to know a filter exists. const allItems = ref([]); const feeds = ref([]); const feedFilter = ref(null) // selected feedTitle, or null = all feeds (not persisted) // Distinct feed titles present in the loaded (unread) items — the filter options. // The active filter is always kept in the list even once its feed runs out of // unread items, so the . Derived from the master (allItems) so article view's // read-but-still-shown items are excluded. A title kept in feedTitles with no // unread items left has no key here — render it as 0. const feedUnreadCounts = computed(() => { const counts = {} for (const item of allItems.value) { if (!item.read) counts[item.feedTitle] = (counts[item.feedTitle] ?? 0) + 1 } return counts }) // The header badge count: unread items in the selected feed when a filter is // active, otherwise the global unread total. Derived from the master (allItems) // rather than the displayed `feeds` so article view's read-but-still-shown // items are excluded and the count matches the filter regardless of view. const unreadCount = computed(() => { const items = feedFilter.value ? allItems.value.filter(i => i.feedTitle === feedFilter.value) : allItems.value return items.filter(i => !i.read).length }) // AppNav renders this snapshot (refreshed via refreshUnreadDisplay, below) // for the filter