Skip to content

Commit

Permalink
perf(rss): Improve duplicate RSS article handling (#1191)
Browse files Browse the repository at this point in the history
  • Loading branch information
Larsluph committed Oct 24, 2023
1 parent 3f34cfc commit ee01382
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 47 deletions.
22 changes: 11 additions & 11 deletions src/pages/RssArticles.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function downloadArticle(item: RssArticle) {
}
async function markAsRead(item: RssArticle) {
await rssStore.markArticleAsRead(item)
await rssStore.markArticleAsRead(item.id)
}
async function markAllAsRead() {
Expand Down Expand Up @@ -121,28 +121,28 @@ onUnmounted(() => {

<v-list-item>
<v-list>
<template v-for="(feed, index) in paginatedResults">
<template v-for="(article, index) in paginatedResults">
<v-divider v-if="index > 0" color="white" />

<v-list-item :class="{ 'rss-read': feed.isRead }" @click="showDescription(feed)">
<v-list-item :class="{ 'rss-read': article.isRead }" @click="showDescription(article)">
<div class="d-flex">
<div>
<v-list-item-title class="text-wrap">{{ feed.title }}</v-list-item-title>
<v-list-item-title class="text-wrap">{{ article.title }}</v-list-item-title>

<v-list-item-subtitle class="d-block">
<div>{{ feed.parsedDate.toLocaleString() }}</div>
<div v-if="feed.feedName">{{ t('rssArticles.item.feedName', { name: feed.feedName }) }}</div>
<div v-if="feed.author">{{ t('rssArticles.item.author', { author: feed.author }) }}</div>
<div v-if="feed.category">{{ t('rssArticles.item.category', { category: feed.category }) }}</div>
<div>{{ article.parsedDate.toLocaleString() }}</div>
<div>{{ t('rssArticles.item.feedName', { name: rssStore.getFeedNames(article.id).join(' | ') }) }}</div>
<div v-if="article.author">{{ t('rssArticles.item.author', { author: article.author }) }}</div>
<div v-if="article.category">{{ t('rssArticles.item.category', { category: article.category }) }}</div>
</v-list-item-subtitle>
</div>

<v-spacer />

<div class="d-flex flex-column">
<v-btn icon="mdi-open-in-new" variant="text" @click.stop="openLink(feed.link)" />
<v-btn color="accent" icon="mdi-check" variant="text" @click.stop="markAsRead(feed)" />
<v-btn icon="mdi-download" variant="text" @click.stop="downloadArticle(feed)" />
<v-btn icon="mdi-open-in-new" variant="text" @click.stop="openLink(article.link)" />
<v-btn color="accent" icon="mdi-check" variant="text" @click.stop="markAsRead(article)" />
<v-btn icon="mdi-download" variant="text" @click.stop="downloadArticle(article)" />
</div>
</div>

Expand Down
81 changes: 46 additions & 35 deletions src/stores/rss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,24 @@ import { qbit } from '@/services'
import { Feed, FeedRule } from '@/types/qbit/models'
import { RssArticle } from '@/types/vuetorrent'
import { defineStore } from 'pinia'
import { computed, reactive, ref } from 'vue'
import { computed, reactive, ref, shallowRef, triggerRef } from 'vue'

export const useRssStore = defineStore(
'rss',
() => {
const feeds = ref<Feed[]>([])
const rules = ref<FeedRule[]>([])

const _articles = shallowRef<RssArticle[]>([])
const keyMap = shallowRef<Record<string, string[]>>({})

const filters = reactive({
title: '',
unread: false
})

const articles = computed(() => {
const articles: RssArticle[] = []
const keySet = new Set<string>()

feeds.value.forEach((feed: Feed) => {
if (!feed.articles) return

feed.articles.forEach(article => {
if (keySet.has(article.id) || (filters.unread && article.isRead)) return

keySet.add(article.id)
articles.push({
feedName: feed.name,
parsedDate: new Date(article.date),
...article
})
})
})

return articles
})
const unreadArticles = computed(() => _articles.value.filter(article => !article.isRead))
const articles = computed(() => filters.unread ? unreadArticles.value : _articles.value)

async function refreshFeed(feedName: string) {
await qbit.refreshFeed(feedName)
Expand Down Expand Up @@ -67,25 +51,50 @@ export const useRssStore = defineStore(

async function fetchFeeds() {
feeds.value = await qbit.getFeeds(true)

_articles.value = []
keyMap.value = {}

feeds.value.forEach((feed: Feed) => {
if (!feed.articles) return

feed.articles.forEach(article => {
if (keyMap.value[article.id]) {
keyMap.value[article.id].push(feed.name)
} else {
keyMap.value[article.id] = [feed.name]
_articles.value.push({
parsedDate: new Date(article.date),
...article
})
}
})
})

triggerRef(_articles)
triggerRef(keyMap)
}

async function markArticleAsRead(article: RssArticle) {
await qbit.markAsRead(article.feedName, article.id)
function getFeedNames(articleId: string) {
return keyMap.value[articleId]
}

const feed = feeds.value.find(feed => feed.name === article.feedName)
if (!feed || !feed.articles) return
const art = feed.articles.find(a => a.id === article.id)
if (!art) return
art.isRead = true
async function markArticleAsRead(articleId: string) {
const feedNames = keyMap.value[articleId]
if (!feedNames) return

const promises: Promise<any>[] = []
feedNames.forEach(feedName => promises.push(qbit.markAsRead(feedName, articleId)))
await Promise.all(promises)

_articles.value.forEach(article => {
if (article.id === articleId) article.isRead = true
})
triggerRef(_articles)
}

async function markAllAsRead() {
feeds.value.forEach(feed => {
if (!feed.articles) return
feed.articles.forEach(async article => {
article.isRead || (await qbit.markAsRead(feed.name, article.id))
})
})
await Promise.all(unreadArticles.value.map(article => article.id).map(markArticleAsRead))
await fetchFeeds()
}

Expand All @@ -102,6 +111,7 @@ export const useRssStore = defineStore(
rules,
filters,
articles,
unreadArticles,
refreshFeed,
createFeed,
setRule,
Expand All @@ -110,6 +120,7 @@ export const useRssStore = defineStore(
deleteFeed,
deleteRule,
fetchFeeds,
getFeedNames,
markArticleAsRead,
markAllAsRead,
fetchRules,
Expand Down
1 change: 0 additions & 1 deletion src/types/vuetorrent/RssArticle.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { FeedArticle } from '@/types/qbit/models'

export interface RssArticle extends FeedArticle {
feedName: string
parsedDate: Date
}

0 comments on commit ee01382

Please sign in to comment.