feeds-fun 1.17.1 → 1.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/package.json +1 -1
  2. package/src/components/EntriesList.vue +11 -0
  3. package/src/components/EntryForList.vue +17 -6
  4. package/src/components/RuleForList.vue +2 -2
  5. package/src/components/RulesList.vue +18 -9
  6. package/src/components/TokensCost.vue +0 -2
  7. package/src/components/collections/DetailedItem.vue +9 -0
  8. package/src/components/collections/PublicIntro.vue +63 -0
  9. package/src/components/collections/PublicSelector.vue +43 -0
  10. package/src/components/main/Block.vue +1 -1
  11. package/src/components/main/Item.vue +1 -1
  12. package/src/components/notifications/LoadedOldNews.vue +47 -0
  13. package/src/components/page_header/ExternalLinks.vue +11 -6
  14. package/src/components/side_pannel/CollapseButton.vue +56 -0
  15. package/src/components/tags/EntryTag.vue +4 -3
  16. package/src/components/tags/FilterTag.vue +6 -3
  17. package/src/components/tags/RuleTag.vue +4 -3
  18. package/src/components/tags/TagsFilter.vue +17 -5
  19. package/src/css/panels.css +5 -5
  20. package/src/css/side_panel_layout.css +4 -0
  21. package/src/layouts/SidePanelLayout.vue +51 -26
  22. package/src/layouts/WideLayout.vue +0 -4
  23. package/src/logic/api.ts +23 -0
  24. package/src/logic/enums.ts +28 -12
  25. package/src/logic/events.ts +56 -8
  26. package/src/logic/tagsFilterState.ts +61 -1
  27. package/src/logic/types.ts +15 -2
  28. package/src/logic/utils.ts +104 -1
  29. package/src/main.ts +10 -0
  30. package/src/router/index.ts +16 -3
  31. package/src/stores/collections.ts +17 -1
  32. package/src/stores/entries.ts +154 -19
  33. package/src/stores/globalSettings.ts +11 -8
  34. package/src/views/AuthView.vue +3 -1
  35. package/src/views/CollectionsView.vue +13 -2
  36. package/src/views/DiscoveryView.vue +3 -1
  37. package/src/views/FeedsView.vue +3 -1
  38. package/src/views/MainView.vue +18 -1
  39. package/src/views/NewsView.vue +36 -97
  40. package/src/views/PublicCollectionView.vue +237 -0
  41. package/src/views/RulesView.vue +31 -29
  42. package/src/views/SettingsView.vue +3 -1
package/src/main.ts CHANGED
@@ -38,6 +38,7 @@ import PageHeaderExternalLinks from "./components/page_header/ExternalLinks.vue"
38
38
  import NotificationsApiKey from "./components/notifications/ApiKey.vue";
39
39
  import NotificationsCreateRuleHelp from "./components/notifications/CreateRuleHelp.vue";
40
40
  import Notifications from "./components/notifications/Block.vue";
41
+ import NotificationsLoadedOldNews from "./components/notifications/LoadedOldNews.vue";
41
42
 
42
43
  import CollectionsNotification from "./components/collections/Notification.vue";
43
44
  import CollectionsWarning from "./components/collections/Warning.vue";
@@ -46,6 +47,8 @@ import CollectionsBlockItem from "./components/collections/BlockItem.vue";
46
47
  import CollectionsDetailedItem from "./components/collections/DetailedItem.vue";
47
48
  import CollectionsSubscribingProgress from "./components/collections/SubscribingProgress.vue";
48
49
  import CollectionsFeedItem from "./components/collections/FeedItem.vue";
50
+ import CollectionsPublicSelector from "./components/collections/PublicSelector.vue";
51
+ import CollectionsPublicIntro from "./components/collections/PublicIntro.vue";
49
52
 
50
53
  import ScoreSelector from "./inputs/ScoreSelector.vue";
51
54
 
@@ -64,6 +67,8 @@ import MainNewsTitle from "./components/main/NewsTitle.vue";
64
67
  import MainHeaderLine from "./components/main/HeaderLine.vue";
65
68
  import MainBlock from "./components/main/Block.vue";
66
69
 
70
+ import SidePanelCollapseButton from "./components/side_pannel/CollapseButton.vue";
71
+
67
72
  import WideLayout from "./layouts/WideLayout.vue";
68
73
  import SidePanelLayout from "./layouts/SidePanelLayout.vue";
69
74
 
@@ -105,6 +110,7 @@ app.component("PageHeaderExternalLinks", PageHeaderExternalLinks);
105
110
  app.component("NotificationsApiKey", NotificationsApiKey);
106
111
  app.component("NotificationsCreateRuleHelp", NotificationsCreateRuleHelp);
107
112
  app.component("Notifications", Notifications);
113
+ app.component("NotificationsLoadedOldNews", NotificationsLoadedOldNews);
108
114
 
109
115
  app.component("CollectionsNotification", CollectionsNotification);
110
116
  app.component("CollectionsWarning", CollectionsWarning);
@@ -113,6 +119,8 @@ app.component("CollectionsBlockItem", CollectionsBlockItem);
113
119
  app.component("CollectionsDetailedItem", CollectionsDetailedItem);
114
120
  app.component("CollectionsSubscribingProgress", CollectionsSubscribingProgress);
115
121
  app.component("CollectionsFeedItem", CollectionsFeedItem);
122
+ app.component("CollectionsPublicSelector", CollectionsPublicSelector);
123
+ app.component("CollectionsPublicIntro", CollectionsPublicIntro);
116
124
 
117
125
  app.component("ScoreSelector", ScoreSelector);
118
126
 
@@ -131,6 +139,8 @@ app.component("MainNewsTitle", MainNewsTitle);
131
139
  app.component("MainHeaderLine", MainHeaderLine);
132
140
  app.component("MainBlock", MainBlock);
133
141
 
142
+ app.component("SidePanelCollapseButton", SidePanelCollapseButton);
143
+
134
144
  app.component("WideLayout", WideLayout);
135
145
  app.component("SidePanelLayout", SidePanelLayout);
136
146
 
@@ -7,6 +7,7 @@ import RulesView from "../views/RulesView.vue";
7
7
  import DiscoveryView from "../views/DiscoveryView.vue";
8
8
  import CollectionsView from "../views/CollectionsView.vue";
9
9
  import SettingsView from "../views/SettingsView.vue";
10
+ import PublicCollectionView from "../views/PublicCollectionView.vue";
10
11
  import * as e from "@/logic/enums";
11
12
 
12
13
  // lazy view loading does not work with router.push function
@@ -31,12 +32,12 @@ const router = createRouter({
31
32
  component: FeedsView
32
33
  },
33
34
  {
34
- path: "/news",
35
+ path: "/news/:tags*",
35
36
  name: e.MainPanelMode.Entries,
36
37
  component: NewsView
37
38
  },
38
39
  {
39
- path: "/rules",
40
+ path: "/rules/:tags*",
40
41
  name: e.MainPanelMode.Rules,
41
42
  component: RulesView
42
43
  },
@@ -54,8 +55,20 @@ const router = createRouter({
54
55
  path: "/settings",
55
56
  name: e.MainPanelMode.Settings,
56
57
  component: SettingsView
58
+ },
59
+ {
60
+ path: "/show/:collectionSlug/:tags*",
61
+ name: "public-collection",
62
+ component: PublicCollectionView
63
+ },
64
+ {
65
+ path: "/:pathMatch(.*)*",
66
+ redirect: "/"
57
67
  }
58
- ]
68
+ ],
69
+ scrollBehavior(to, from, savedPosition) {
70
+ return {top: 0};
71
+ }
59
72
  });
60
73
 
61
74
  export default router;
@@ -33,6 +33,10 @@ export const useCollectionsStore = defineStore("collectionsStore", () => {
33
33
  return order;
34
34
  });
35
35
 
36
+ const collectionsOrdered = computed(() => {
37
+ return collectionsOrder.value.map((id) => collections.value[id]);
38
+ });
39
+
36
40
  async function getFeeds({collectionId}: {collectionId: t.CollectionId}) {
37
41
  if (collectionId in feeds.value) {
38
42
  return feeds.value[collectionId];
@@ -45,9 +49,21 @@ export const useCollectionsStore = defineStore("collectionsStore", () => {
45
49
  return feeds.value[collectionId];
46
50
  }
47
51
 
52
+ function getCollectionBySlug({slug}: {slug: t.CollectionSlug}) {
53
+ for (const collection of Object.values(collections.value)) {
54
+ if (collection.slug === slug) {
55
+ return collection;
56
+ }
57
+ }
58
+
59
+ return null;
60
+ }
61
+
48
62
  return {
49
63
  collections,
50
64
  collectionsOrder,
51
- getFeeds
65
+ collectionsOrdered,
66
+ getFeeds,
67
+ getCollectionBySlug
52
68
  };
53
69
  });
@@ -5,13 +5,21 @@ import _ from "lodash";
5
5
  import * as t from "@/logic/types";
6
6
  import * as e from "@/logic/enums";
7
7
  import * as api from "@/logic/api";
8
+ import * as utils from "@/logic/utils";
8
9
  import {Timer} from "@/logic/timer";
9
10
  import {computedAsync} from "@vueuse/core";
10
11
  import {useGlobalSettingsStore} from "@/stores/globalSettings";
11
12
  import * as events from "@/logic/events";
13
+ import {useGlobalState} from "@/stores/globalState";
14
+
15
+ enum Mode {
16
+ News = "news",
17
+ PublicCollection = "public-collection"
18
+ }
12
19
 
13
20
  export const useEntriesStore = defineStore("entriesStore", () => {
14
21
  const globalSettings = useGlobalSettingsStore();
22
+ const globalState = useGlobalState();
15
23
 
16
24
  const entries = ref<{[key: t.EntryId]: t.Entry}>({});
17
25
  const requestedEntries = ref<{[key: t.EntryId]: boolean}>({});
@@ -20,17 +28,74 @@ export const useEntriesStore = defineStore("entriesStore", () => {
20
28
 
21
29
  const canUndoMarkRead = computed(() => readHistory.value.length > 0);
22
30
 
23
- function registerEntry(entry: t.Entry) {
24
- if (entry.id in entries.value) {
25
- if (entry.body === null && entries.value[entry.id].body !== null) {
26
- entry.body = entries.value[entry.id].body;
31
+ const mode = ref<Mode | null>(null);
32
+ const modePublicCollectionSlug = ref<t.CollectionSlug | null>(null);
33
+
34
+ function setNewsMode() {
35
+ if (mode.value == Mode.News) {
36
+ return;
37
+ }
38
+
39
+ mode.value = Mode.News;
40
+
41
+ globalSettings.updateDataVersion();
42
+ }
43
+
44
+ function setPublicCollectionMode(collectionSlug: t.CollectionSlug) {
45
+ if (mode.value == Mode.PublicCollection && modePublicCollectionSlug.value === collectionSlug) {
46
+ return;
47
+ }
48
+
49
+ mode.value = Mode.PublicCollection;
50
+ modePublicCollectionSlug.value = collectionSlug;
51
+
52
+ globalSettings.updateDataVersion();
53
+ }
54
+
55
+ // Public collections uses fixed sorting order
56
+ // News uses dynamic sorting order and should keep it between switching views
57
+ // So, if we set globalSettings.entriesOrderProperties in PublicCollection view
58
+ // we'll break News view sorting and confuse users
59
+ // => we hardcode specific order properties for PublicCollection mode
60
+ const activeOrderProperties = computed(() => {
61
+ if (mode.value === null) {
62
+ // Return most general order for the case when mode is not set yet
63
+ return e.EntriesOrderProperties.get(e.EntriesOrder.Published) as unknown as e.EntriesOrderProperty;
64
+ }
65
+
66
+ if (mode.value == Mode.News) {
67
+ // use saved order mode for News view
68
+ return globalSettings.entriesOrderProperties as unknown as e.EntriesOrderProperty;
69
+ }
70
+
71
+ if (mode.value == Mode.PublicCollection) {
72
+ // use fixed Published order for Public Collection view
73
+ return e.EntriesOrderProperties.get(e.EntriesOrder.Published) as unknown as e.EntriesOrderProperty;
74
+ }
75
+
76
+ console.error(`Unknown mode ${mode.value}`);
77
+
78
+ return e.EntriesOrderProperties.get(e.EntriesOrder.Published) as unknown as e.EntriesOrderProperty;
79
+ });
80
+
81
+ // We bulk update entries to avoid performance degradation
82
+ // on triggering multiple reactivity updates for each entry
83
+ function registerEntries(newEntries: t.Entry[]) {
84
+ let delta: {[key: t.EntryId]: t.Entry} = {};
85
+
86
+ for (const entry of newEntries) {
87
+ if (entry.id in entries.value) {
88
+ if (entry.body === null && entries.value[entry.id].body !== null) {
89
+ entry.body = entries.value[entry.id].body;
90
+ }
27
91
  }
92
+ delta[entry.id] = entry;
28
93
  }
29
94
 
30
- entries.value[entry.id] = entry;
95
+ entries.value = {...entries.value, ...delta};
31
96
  }
32
97
 
33
- const loadedEntriesReport = computedAsync(async () => {
98
+ async function loadEntriesAccordingToMode() {
34
99
  const periodProperties = e.LastEntriesPeriodProperties.get(globalSettings.lastEntriesPeriod);
35
100
 
36
101
  if (periodProperties === undefined) {
@@ -39,23 +104,74 @@ export const useEntriesStore = defineStore("entriesStore", () => {
39
104
 
40
105
  const period = periodProperties.seconds;
41
106
 
107
+ if (mode.value === Mode.News) {
108
+ return await api.getLastEntries({
109
+ period: period
110
+ });
111
+ }
112
+
113
+ if (mode.value === Mode.PublicCollection) {
114
+ return await api.getLastCollectionEntries({
115
+ period: period,
116
+ collectionSlug: modePublicCollectionSlug.value
117
+ });
118
+ }
119
+
120
+ throw new Error(`Unknown mode ${mode.value}`);
121
+ }
122
+
123
+ const loadedEntriesReport = computedAsync(async () => {
42
124
  // force refresh
43
125
  globalSettings.dataVersion;
44
126
 
45
- const loadedEntries = await api.getLastEntries({
46
- period: period
47
- });
127
+ if (mode.value === null) {
128
+ // Do nothing until the mode is set
129
+ return null;
130
+ }
131
+
132
+ const loadedEntries = await loadEntriesAccordingToMode();
48
133
 
49
134
  const report = [];
50
135
 
136
+ registerEntries(loadedEntries);
137
+
51
138
  for (const entry of loadedEntries) {
52
- registerEntry(entry);
53
139
  report.push(entry.id);
54
140
  }
55
141
 
56
142
  return report;
57
143
  }, null);
58
144
 
145
+ const _sortedEntries = computed(() => {
146
+ if (loadedEntriesReport.value === null) {
147
+ return [];
148
+ }
149
+
150
+ const field = activeOrderProperties.value.orderField;
151
+ const direction = activeOrderProperties.value.direction;
152
+
153
+ const report = utils.sortIdsList({ids: loadedEntriesReport.value, storage: entries.value, field, direction});
154
+
155
+ return report;
156
+ });
157
+
158
+ const visibleEntries = computed(() => {
159
+ let report = _sortedEntries.value.slice();
160
+
161
+ if (!globalSettings.showRead) {
162
+ report = report.filter((entryId) => {
163
+ if (displayedEntryId.value == entryId) {
164
+ // always show read entries with open body
165
+ // otherwise, they will hide right after opening it
166
+ return true;
167
+ }
168
+ return !entries.value[entryId].hasMarker(e.Marker.Read);
169
+ });
170
+ }
171
+
172
+ return report;
173
+ });
174
+
59
175
  function requestFullEntry({entryId}: {entryId: t.EntryId}) {
60
176
  if (entryId in entries.value && entries.value[entryId].body !== null) {
61
177
  return;
@@ -71,11 +187,9 @@ export const useEntriesStore = defineStore("entriesStore", () => {
71
187
  return;
72
188
  }
73
189
 
74
- const entries = await api.getEntriesByIds({ids: ids});
190
+ const loadedEntries = await api.getEntriesByIds({ids: ids});
75
191
 
76
- for (const entry of entries) {
77
- registerEntry(entry);
78
- }
192
+ registerEntries(loadedEntries);
79
193
 
80
194
  requestedEntries.value = {};
81
195
  }
@@ -96,7 +210,11 @@ export const useEntriesStore = defineStore("entriesStore", () => {
96
210
  entries.value[entryId].setMarker(marker);
97
211
  }
98
212
 
99
- await api.setMarker({entryId: entryId, marker: marker});
213
+ // This method may be called from public access pages, like public collections
214
+ // In such case user may be not logged in and we should not send API requests
215
+ if (globalState.isLoggedIn) {
216
+ await api.setMarker({entryId: entryId, marker: marker});
217
+ }
100
218
  }
101
219
 
102
220
  async function removeMarker({entryId, marker}: {entryId: t.EntryId; marker: e.Marker}) {
@@ -111,10 +229,14 @@ export const useEntriesStore = defineStore("entriesStore", () => {
111
229
  entries.value[entryId].removeMarker(marker);
112
230
  }
113
231
 
114
- await api.removeMarker({entryId: entryId, marker: marker});
232
+ // This method may be called from public access pages, like public collections
233
+ // In such case user may be not logged in and we should not send API requests
234
+ if (globalState.isLoggedIn) {
235
+ await api.removeMarker({entryId: entryId, marker: marker});
236
+ }
115
237
  }
116
238
 
117
- async function displayEntry({entryId}: {entryId: t.EntryId}) {
239
+ async function displayEntry({entryId, view}: {entryId: t.EntryId; view: events.EventsViewName}) {
118
240
  displayedEntryId.value = entryId;
119
241
 
120
242
  requestFullEntry({entryId: entryId});
@@ -126,7 +248,7 @@ export const useEntriesStore = defineStore("entriesStore", () => {
126
248
  });
127
249
  }
128
250
 
129
- await events.newsBodyOpened({entryId: entryId});
251
+ await events.newsBodyOpened({entryId: entryId, view: view});
130
252
  }
131
253
 
132
254
  function hideEntry({entryId}: {entryId: t.EntryId}) {
@@ -145,6 +267,14 @@ export const useEntriesStore = defineStore("entriesStore", () => {
145
267
  removeMarker({entryId: entryId, marker: e.Marker.Read});
146
268
  }
147
269
 
270
+ // TODO: Refactor for better loading tracking in the front code and in the GUI.
271
+ // Currently, this property is working only for the first load.
272
+ // => It should always be refactored to work correctly.
273
+ // ATTENTION: check every usage of this property while refactoring
274
+ const loading = computed(() => {
275
+ return loadedEntriesReport.value === null;
276
+ });
277
+
148
278
  return {
149
279
  entries,
150
280
  requestFullEntry,
@@ -155,6 +285,11 @@ export const useEntriesStore = defineStore("entriesStore", () => {
155
285
  displayEntry,
156
286
  hideEntry,
157
287
  undoMarkRead,
158
- canUndoMarkRead
288
+ canUndoMarkRead,
289
+ setNewsMode,
290
+ setPublicCollectionMode,
291
+ loading,
292
+ visibleEntries,
293
+ activeOrderProperties
159
294
  };
160
295
  });
@@ -1,5 +1,4 @@
1
- import {ref, watch} from "vue";
2
- import {useRouter} from "vue-router";
1
+ import {ref, computed} from "vue";
3
2
  import {defineStore} from "pinia";
4
3
  import {computedAsync} from "@vueuse/core";
5
4
  import {useGlobalState} from "@/stores/globalState";
@@ -8,18 +7,23 @@ import * as e from "@/logic/enums";
8
7
  import * as api from "@/logic/api";
9
8
 
10
9
  export const useGlobalSettingsStore = defineStore("globalSettings", () => {
11
- const router = useRouter();
12
10
  const globalState = useGlobalState();
13
11
 
14
12
  // General
15
13
  const mainPanelMode = ref(e.MainPanelMode.Entries);
16
14
  const dataVersion = ref(0);
15
+ const showSidebar = ref(true);
16
+ const showSidebarPoint = ref(false);
17
17
 
18
18
  // Entries
19
19
  const lastEntriesPeriod = ref(e.LastEntriesPeriod.Day3);
20
20
  const entriesOrder = ref(e.EntriesOrder.Score);
21
21
  const showRead = ref(true);
22
22
 
23
+ const entriesOrderProperties = computed(() => {
24
+ return e.EntriesOrderProperties.get(entriesOrder.value);
25
+ });
26
+
23
27
  // Feeds
24
28
  const showFeedsDescriptions = ref(true);
25
29
  const feedsOrder = ref(e.FeedsOrder.Title);
@@ -28,10 +32,6 @@ export const useGlobalSettingsStore = defineStore("globalSettings", () => {
28
32
  // Rules
29
33
  const rulesOrder = ref(e.RulesOrder.Tags);
30
34
 
31
- watch(mainPanelMode, (newValue, oldValue) => {
32
- router.push({name: mainPanelMode.value, params: {}});
33
- });
34
-
35
35
  function updateDataVersion() {
36
36
  dataVersion.value++;
37
37
  }
@@ -64,6 +64,7 @@ export const useGlobalSettingsStore = defineStore("globalSettings", () => {
64
64
  lastEntriesPeriod,
65
65
  entriesOrder,
66
66
  showRead,
67
+ entriesOrderProperties,
67
68
  dataVersion,
68
69
  updateDataVersion,
69
70
  showFeedsDescriptions,
@@ -71,6 +72,8 @@ export const useGlobalSettingsStore = defineStore("globalSettings", () => {
71
72
  info,
72
73
  feedsOrder,
73
74
  failedFeedsFirst,
74
- rulesOrder
75
+ rulesOrder,
76
+ showSidebar,
77
+ showSidebarPoint
75
78
  };
76
79
  });
@@ -11,12 +11,12 @@
11
11
  </template>
12
12
 
13
13
  <script lang="ts" setup>
14
+ import {computed, ref, onUnmounted, watch, provide, onBeforeMount} from "vue";
14
15
  import {useRouter} from "vue-router";
15
16
  import {useGlobalSettingsStore} from "@/stores/globalSettings";
16
17
  import {useSupertokens} from "@/stores/supertokens";
17
18
  import {useGlobalState} from "@/stores/globalState";
18
19
  import {computedAsync} from "@vueuse/core";
19
- import {computed, ref, onBeforeMount} from "vue";
20
20
  import * as settings from "@/logic/settings";
21
21
 
22
22
  const globalSettings = useGlobalSettingsStore();
@@ -26,6 +26,8 @@
26
26
 
27
27
  const router = useRouter();
28
28
 
29
+ provide("eventsViewName", "auth");
30
+
29
31
  const linkProcessed = ref(false);
30
32
 
31
33
  function goToWorkspace() {
@@ -2,11 +2,20 @@
2
2
  <side-panel-layout :reload-button="false">
3
3
  <template #main-header> Collections </template>
4
4
 
5
- <div class="ffun-info-common">
5
+ <div
6
+ v-if="collections.collectionsOrder.length > 0"
7
+ class="ffun-info-common">
6
8
  <p>We've prepared thematic collections just for you.</p>
7
9
  <p>News from collections are always tagged, ensuring you get the full power of Feeds Fun!</p>
8
10
  </div>
9
11
 
12
+ <div
13
+ v-else
14
+ class="ffun-info-common">
15
+ <p>There are no collections.</p>
16
+ <p>Ask the server administrator to create some.</p>
17
+ </div>
18
+
10
19
  <div
11
20
  v-for="collectionId in collections.collectionsOrder"
12
21
  :key="collectionId"
@@ -17,7 +26,7 @@
17
26
  </template>
18
27
 
19
28
  <script lang="ts" setup>
20
- import {computed, ref, onUnmounted, watch} from "vue";
29
+ import {computed, ref, onUnmounted, watch, provide} from "vue";
21
30
  import {computedAsync} from "@vueuse/core";
22
31
  import * as api from "@/logic/api";
23
32
  import * as t from "@/logic/types";
@@ -29,6 +38,8 @@
29
38
 
30
39
  const collections = useCollectionsStore();
31
40
 
41
+ provide("eventsViewName", "collections");
42
+
32
43
  globalSettings.mainPanelMode = e.MainPanelMode.Collections;
33
44
  </script>
34
45
 
@@ -33,7 +33,7 @@
33
33
  </template>
34
34
 
35
35
  <script lang="ts" setup>
36
- import {computed, ref, onUnmounted, watch} from "vue";
36
+ import {computed, ref, onUnmounted, watch, provide} from "vue";
37
37
  import {computedAsync} from "@vueuse/core";
38
38
  import {useGlobalSettingsStore} from "@/stores/globalSettings";
39
39
  import * as api from "@/logic/api";
@@ -42,6 +42,8 @@
42
42
 
43
43
  const globalSettings = useGlobalSettingsStore();
44
44
 
45
+ provide("eventsViewName", "discovery");
46
+
45
47
  globalSettings.mainPanelMode = e.MainPanelMode.Discovery;
46
48
  </script>
47
49
 
@@ -57,7 +57,7 @@
57
57
 
58
58
  <script lang="ts" setup>
59
59
  import _ from "lodash";
60
- import {computed, ref, onUnmounted, watch} from "vue";
60
+ import {computed, ref, onUnmounted, watch, provide} from "vue";
61
61
  import {computedAsync} from "@vueuse/core";
62
62
  import {useGlobalSettingsStore} from "@/stores/globalSettings";
63
63
  import {useFeedsStore} from "@/stores/feeds";
@@ -69,6 +69,8 @@
69
69
 
70
70
  const feedsStore = useFeedsStore();
71
71
 
72
+ provide("eventsViewName", "feeds");
73
+
72
74
  globalSettings.mainPanelMode = e.MainPanelMode.Feeds;
73
75
 
74
76
  const sortedFeeds = computed(() => {
@@ -179,7 +179,14 @@
179
179
  </template>
180
180
 
181
181
  <template #description>
182
- {{ collections.collections[collectionId].description }}
182
+ <div>{{ collections.collections[collectionId].description }}</div>
183
+ <div class="mt-2">
184
+ <a
185
+ :href="publicCollectionHref(collections.collections[collectionId].slug)"
186
+ class="ffun-normal-link pt-4">
187
+ Read the news
188
+ </a>
189
+ </div>
183
190
  </template>
184
191
  </main-item>
185
192
  </template>
@@ -197,8 +204,18 @@
197
204
  </template>
198
205
 
199
206
  <script lang="ts" setup>
207
+ import {computed, ref, onUnmounted, watch, provide} from "vue";
200
208
  import * as settings from "@/logic/settings";
209
+ import {useRouter, RouterLink, RouterView} from "vue-router";
201
210
  import {useCollectionsStore} from "@/stores/collections";
211
+ import * as t from "@/logic/types";
202
212
 
213
+ const router = useRouter();
203
214
  const collections = useCollectionsStore();
215
+
216
+ provide("eventsViewName", "main");
217
+
218
+ function publicCollectionHref(collectionSlug: t.CollectionSlug) {
219
+ return router.resolve({name: "public-collection", params: {collectionSlug: collectionSlug}}).href;
220
+ }
204
221
  </script>