counter on feed select
This commit is contained in:
@@ -6,7 +6,7 @@ import Modal from './modal/AddUrl.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const { sync, showModal, viewMode, toggleViewMode, layout, toggleLayout, markAllRead, feedFilter, feedTitles, setFeedFilter, unreadCount, lastProgrammaticScroll } = useFeeds()
|
||||
const { sync, showModal, viewMode, toggleViewMode, layout, toggleLayout, markAllRead, feedFilter, feedTitles, feedUnreadCounts, setFeedFilter, unreadCount, lastProgrammaticScroll } = useFeeds()
|
||||
|
||||
const headerRef = ref(null)
|
||||
|
||||
@@ -155,7 +155,7 @@ function handleToggleLayout() {
|
||||
@change="setFeedFilter($event.target.value || null)"
|
||||
>
|
||||
<option value="">All feeds</option>
|
||||
<option v-for="title in feedTitles" :key="title" :value="title">{{ title }}</option>
|
||||
<option v-for="title in feedTitles" :key="title" :value="title">{{ title }} ({{ feedUnreadCounts[title] ?? 0 }})</option>
|
||||
</select>
|
||||
<button
|
||||
class="app-nav__hamburger"
|
||||
|
||||
@@ -241,8 +241,9 @@ describe('AppNav', () => {
|
||||
await flushPromises()
|
||||
|
||||
const options = wrapper.find('.app-nav__filter').findAll('option').map(o => o.text())
|
||||
// "All feeds" first, then distinct titles sorted alphabetically.
|
||||
expect(options).toEqual(['All feeds', 'Feed A', 'Feed B'])
|
||||
// "All feeds" first, then distinct titles sorted alphabetically, each with
|
||||
// its unread count.
|
||||
expect(options).toEqual(['All feeds', 'Feed A (2)', 'Feed B (1)'])
|
||||
})
|
||||
|
||||
it('does not render the feed filter when there are no feeds', async () => {
|
||||
|
||||
@@ -13,7 +13,7 @@ class FakeIntersectionObserver {
|
||||
vi.stubGlobal('IntersectionObserver', FakeIntersectionObserver)
|
||||
|
||||
describe('useFeeds', () => {
|
||||
const { feeds, allItems, feedFilter, feedTitles, unreadCount, setFeedFilter, showMessage, message, showModal, fetchData, sync, getReadable, setInitialLoad, handleIntersection, markAllRead, setupIntersectionObserver } = useFeeds()
|
||||
const { feeds, allItems, feedFilter, feedTitles, feedUnreadCounts, unreadCount, setFeedFilter, showMessage, message, showModal, fetchData, sync, getReadable, setInitialLoad, handleIntersection, markAllRead, setupIntersectionObserver } = useFeeds()
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.setItem('user-token', 'test-token')
|
||||
@@ -252,6 +252,18 @@ describe('useFeeds', () => {
|
||||
await setFeedFilter(null)
|
||||
expect(unreadCount.value).toBe(3)
|
||||
})
|
||||
|
||||
it('exposes per-feed unread counts, excluding items marked read in place', async () => {
|
||||
axios.get.mockResolvedValueOnce(twoFeedsResponse)
|
||||
await fetchData()
|
||||
|
||||
expect(feedUnreadCounts.value).toEqual({ 'Feed A': 2, 'Feed B': 1 })
|
||||
|
||||
// Article view marks items read in place (feed.read = true) without
|
||||
// removing them from the master — the counts must not include those.
|
||||
allItems.value.find(i => i.id === 3).read = true
|
||||
expect(feedUnreadCounts.value).toEqual({ 'Feed A': 1, 'Feed B': 1 })
|
||||
})
|
||||
})
|
||||
|
||||
it('strips leftover embedded-video placeholder headings', async () => {
|
||||
|
||||
@@ -22,6 +22,17 @@ const feedTitles = computed(() => {
|
||||
if (feedFilter.value) titles.add(feedFilter.value)
|
||||
return [...titles].sort((a, b) => a.localeCompare(b))
|
||||
})
|
||||
// Unread count per feed title, shown next to each option in the filter
|
||||
// <select>. 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
|
||||
@@ -466,6 +477,7 @@ export function useFeeds() {
|
||||
allItems,
|
||||
feedFilter,
|
||||
feedTitles,
|
||||
feedUnreadCounts,
|
||||
unreadCount,
|
||||
setFeedFilter,
|
||||
showMessage,
|
||||
|
||||
Reference in New Issue
Block a user