diff --git a/ffun/ffun/api/entities.py b/ffun/ffun/api/entities.py
index 91d7b672..6af3c421 100644
--- a/ffun/ffun/api/entities.py
+++ b/ffun/ffun/api/entities.py
@@ -270,10 +270,12 @@ class CollectionFeedInfo(pydantic.BaseModel):
url: str
title: str
description: str
+ id: f_entities.FeedId
@classmethod
def from_internal(cls, record: fc_entities.FeedInfo) -> "CollectionFeedInfo":
- return cls(url=record.url, title=record.title, description=record.description)
+ assert record.feed_id is not None
+ return cls(url=record.url, title=record.title, description=record.description, id=record.feed_id)
##################
diff --git a/site/src/components/DiscoveryForm.vue b/site/src/components/DiscoveryForm.vue
index 7f2e58f5..18c828e3 100644
--- a/site/src/components/DiscoveryForm.vue
+++ b/site/src/components/DiscoveryForm.vue
@@ -59,6 +59,9 @@
import * as api from "@/logic/api";
import {computedAsync} from "@vueuse/core";
import {useEntriesStore} from "@/stores/entries";
+ import {useFeedsStore} from "@/stores/feeds";
+
+ const feedsStore = useFeedsStore();
const search = ref("");
@@ -91,10 +94,10 @@
return feeds;
}, null);
- async function addFeed(url: string) {
+ async function addFeed(url: t.URL) {
adding.value = true;
- await api.addFeed({url: url});
+ await feedsStore.subscribe(url);
addedFeeds.value[url] = true;
diff --git a/site/src/components/FeedForList.vue b/site/src/components/FeedForList.vue
index 4604d388..afeec708 100644
--- a/site/src/components/FeedForList.vue
+++ b/site/src/components/FeedForList.vue
@@ -6,7 +6,7 @@
+ @click.prevent="feedsStore.unsubscribe(feed.id)">
remove
@@ -82,11 +82,11 @@
import {computedAsync} from "@vueuse/core";
import DOMPurify from "dompurify";
import {useGlobalSettingsStore} from "@/stores/globalSettings";
-
+ import {useFeedsStore} from "@/stores/feeds";
import {useCollectionsStore} from "@/stores/collections";
const globalSettings = useGlobalSettingsStore();
-
+ const feedsStore = useFeedsStore();
const collections = useCollectionsStore();
const properties = defineProps<{feed: t.Feed}>();
@@ -111,9 +111,4 @@
}
return DOMPurify.sanitize(properties.feed.description);
});
-
- async function unsubscribe() {
- await api.unsubscribe({feedId: properties.feed.id});
- globalSettings.updateDataVersion();
- }
diff --git a/site/src/components/collections/DetailedItem.vue b/site/src/components/collections/DetailedItem.vue
index 2837ee16..89b53e01 100644
--- a/site/src/components/collections/DetailedItem.vue
+++ b/site/src/components/collections/DetailedItem.vue
@@ -99,9 +99,13 @@
showFeeds.value = false;
}
- const feeds = computedAsync(async () => {
- return await collections.getFeeds({collectionId: properties.collectionId});
- }, []);
+ const feeds = computedAsync(
+ async () => {
+ return await collections.getFeeds({collectionId: properties.collectionId});
+ },
+ [],
+ {lazy: true}
+ );
diff --git a/site/src/layouts/SidePanelLayout.vue b/site/src/layouts/SidePanelLayout.vue
index bc6800fc..e728da8d 100644
--- a/site/src/layouts/SidePanelLayout.vue
+++ b/site/src/layouts/SidePanelLayout.vue
@@ -149,13 +149,11 @@
import {useRouter, RouterLink, RouterView} from "vue-router";
import {useGlobalSettingsStore} from "@/stores/globalSettings";
import {useGlobalState} from "@/stores/globalState";
- import {useEntriesStore} from "@/stores/entries";
import {useSupertokens} from "@/stores/supertokens";
import * as e from "@/logic/enums";
import * as settings from "@/logic/settings";
const globalSettings = useGlobalSettingsStore();
- const entriesStore = useEntriesStore();
const supertokens = useSupertokens();
const globalState = useGlobalState();
diff --git a/site/src/logic/types.ts b/site/src/logic/types.ts
index 30ce4af7..7c329879 100644
--- a/site/src/logic/types.ts
+++ b/site/src/logic/types.ts
@@ -451,26 +451,31 @@ export class CollectionFeedInfo {
readonly url: URL;
readonly title: string;
readonly description: string;
+ readonly id: FeedId;
- constructor({url, title, description}: {url: URL; title: string; description: string}) {
+ constructor({url, title, description, id}: {url: URL; title: string; description: string; id: FeedId}) {
this.url = url;
this.title = title;
this.description = description;
+ this.id = id;
}
}
export function collectionFeedInfoFromJSON({
url,
title,
- description
+ description,
+ id
}: {
url: string;
title: string;
description: string;
+ id: string;
}): CollectionFeedInfo {
return new CollectionFeedInfo({
url: toURL(url),
title: title,
- description: description
+ description: description,
+ id: toFeedId(id)
});
}
diff --git a/site/src/stores/feeds.ts b/site/src/stores/feeds.ts
new file mode 100644
index 00000000..875afba6
--- /dev/null
+++ b/site/src/stores/feeds.ts
@@ -0,0 +1,48 @@
+import {computed, ref, watch} from "vue";
+import {useRouter} from "vue-router";
+import {defineStore} from "pinia";
+
+import type * as t from "@/logic/types";
+import * as e from "@/logic/enums";
+import * as api from "@/logic/api";
+import {Timer} from "@/logic/timer";
+import {computedAsync} from "@vueuse/core";
+import {useGlobalSettingsStore} from "@/stores/globalSettings";
+
+export const useFeedsStore = defineStore("feedsStore", () => {
+ const globalSettings = useGlobalSettingsStore();
+
+ const feeds = computedAsync(async () => {
+ // force refresh
+ globalSettings.dataVersion;
+
+ const feedsList = await api.getFeeds();
+
+ const feedsDict: {[key: t.FeedId]: t.Feed} = {};
+
+ for (const feed of feedsList) {
+ feedsDict[feed.id] = feed;
+ }
+
+ return feedsDict;
+ }, {});
+
+ async function unsubscribe(feedId: t.FeedId) {
+ await api.unsubscribe({feedId: feedId});
+ globalSettings.updateDataVersion();
+ }
+
+ async function subscribe(url: t.URL) {
+ await api.addFeed({
+ url: url
+ });
+
+ globalSettings.updateDataVersion();
+ }
+
+ return {
+ feeds,
+ unsubscribe,
+ subscribe
+ };
+});
diff --git a/site/src/views/CollectionsView.vue b/site/src/views/CollectionsView.vue
index 46c3f4e6..524b6f2d 100644
--- a/site/src/views/CollectionsView.vue
+++ b/site/src/views/CollectionsView.vue
@@ -18,7 +18,6 @@
import * as t from "@/logic/types";
import * as e from "@/logic/enums";
import {useGlobalSettingsStore} from "@/stores/globalSettings";
- import {useEntriesStore} from "@/stores/entries";
import {useCollectionsStore} from "@/stores/collections";
const globalSettings = useGlobalSettingsStore();
diff --git a/site/src/views/FeedsView.vue b/site/src/views/FeedsView.vue
index c900e48c..f6fece64 100644
--- a/site/src/views/FeedsView.vue
+++ b/site/src/views/FeedsView.vue
@@ -49,27 +49,24 @@
import {computed, ref, onUnmounted, watch} from "vue";
import {computedAsync} from "@vueuse/core";
import {useGlobalSettingsStore} from "@/stores/globalSettings";
+ import {useFeedsStore} from "@/stores/feeds";
import * as api from "@/logic/api";
import type * as t from "@/logic/types";
import * as e from "@/logic/enums";
const globalSettings = useGlobalSettingsStore();
- globalSettings.mainPanelMode = e.MainPanelMode.Feeds;
+ const feedsStore = useFeedsStore();
- const feeds = computedAsync(async () => {
- // force refresh
- globalSettings.dataVersion;
- return await api.getFeeds();
- }, null);
+ globalSettings.mainPanelMode = e.MainPanelMode.Feeds;
const sortedFeeds = computed(() => {
- if (!feeds.value) {
+ let sorted = Object.values(feedsStore.feeds);
+
+ if (sorted.length === 0) {
return null;
}
- let sorted = feeds.value.slice();
-
const orderProperties = e.FeedsOrderProperties.get(globalSettings.feedsOrder);
if (!orderProperties) {