feeds-fun 1.17.0 → 1.18.0

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 (41) hide show
  1. package/package.json +1 -1
  2. package/public/news-filtering-example.png +0 -0
  3. package/src/components/EntriesList.vue +11 -0
  4. package/src/components/EntryForList.vue +17 -6
  5. package/src/components/RuleForList.vue +2 -2
  6. package/src/components/RulesList.vue +18 -9
  7. package/src/components/TokensCost.vue +0 -2
  8. package/src/components/collections/DetailedItem.vue +9 -0
  9. package/src/components/collections/PublicIntro.vue +63 -0
  10. package/src/components/collections/PublicSelector.vue +43 -0
  11. package/src/components/main/Block.vue +1 -1
  12. package/src/components/main/Item.vue +1 -1
  13. package/src/components/notifications/LoadedOldNews.vue +47 -0
  14. package/src/components/page_header/ExternalLinks.vue +11 -6
  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/layouts/SidePanelLayout.vue +41 -22
  21. package/src/layouts/WideLayout.vue +0 -4
  22. package/src/logic/api.ts +23 -0
  23. package/src/logic/enums.ts +28 -12
  24. package/src/logic/events.ts +36 -8
  25. package/src/logic/tagsFilterState.ts +50 -1
  26. package/src/logic/types.ts +15 -2
  27. package/src/logic/utils.ts +104 -1
  28. package/src/main.ts +6 -0
  29. package/src/router/index.ts +16 -3
  30. package/src/stores/collections.ts +17 -1
  31. package/src/stores/entries.ts +154 -19
  32. package/src/stores/globalSettings.ts +6 -7
  33. package/src/views/AuthView.vue +3 -1
  34. package/src/views/CollectionsView.vue +13 -2
  35. package/src/views/DiscoveryView.vue +3 -1
  36. package/src/views/FeedsView.vue +3 -1
  37. package/src/views/MainView.vue +21 -2
  38. package/src/views/NewsView.vue +32 -98
  39. package/src/views/PublicCollectionView.vue +237 -0
  40. package/src/views/RulesView.vue +26 -29
  41. package/src/views/SettingsView.vue +3 -1
@@ -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,7 +7,6 @@ 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
@@ -20,6 +18,10 @@ export const useGlobalSettingsStore = defineStore("globalSettings", () => {
20
18
  const entriesOrder = ref(e.EntriesOrder.Score);
21
19
  const showRead = ref(true);
22
20
 
21
+ const entriesOrderProperties = computed(() => {
22
+ return e.EntriesOrderProperties.get(entriesOrder.value);
23
+ });
24
+
23
25
  // Feeds
24
26
  const showFeedsDescriptions = ref(true);
25
27
  const feedsOrder = ref(e.FeedsOrder.Title);
@@ -28,10 +30,6 @@ export const useGlobalSettingsStore = defineStore("globalSettings", () => {
28
30
  // Rules
29
31
  const rulesOrder = ref(e.RulesOrder.Tags);
30
32
 
31
- watch(mainPanelMode, (newValue, oldValue) => {
32
- router.push({name: mainPanelMode.value, params: {}});
33
- });
34
-
35
33
  function updateDataVersion() {
36
34
  dataVersion.value++;
37
35
  }
@@ -64,6 +62,7 @@ export const useGlobalSettingsStore = defineStore("globalSettings", () => {
64
62
  lastEntriesPeriod,
65
63
  entriesOrder,
66
64
  showRead,
65
+ entriesOrderProperties,
67
66
  dataVersion,
68
67
  updateDataVersion,
69
68
  showFeedsDescriptions,
@@ -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(() => {
@@ -162,7 +162,9 @@
162
162
  </main-description>
163
163
  </main-block>
164
164
 
165
- <main-header-line> Curated news collections <br class="md:hidden" />for easy start </main-header-line>
165
+ <main-header-line v-if="collections.collectionsOrder.length > 0">
166
+ Curated news collections <br class="md:hidden" />for easy start
167
+ </main-header-line>
166
168
 
167
169
  <main-block>
168
170
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
@@ -177,7 +179,14 @@
177
179
  </template>
178
180
 
179
181
  <template #description>
180
- {{ 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>
181
190
  </template>
182
191
  </main-item>
183
192
  </template>
@@ -195,8 +204,18 @@
195
204
  </template>
196
205
 
197
206
  <script lang="ts" setup>
207
+ import {computed, ref, onUnmounted, watch, provide} from "vue";
198
208
  import * as settings from "@/logic/settings";
209
+ import {useRouter, RouterLink, RouterView} from "vue-router";
199
210
  import {useCollectionsStore} from "@/stores/collections";
211
+ import * as t from "@/logic/types";
200
212
 
213
+ const router = useRouter();
201
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
+ }
202
221
  </script>
@@ -9,6 +9,9 @@
9
9
 
10
10
  <template #side-menu-item-2>
11
11
  Sort by
12
+ <!-- Remember that we should use entriesStore.activeOrderProperties everywhere in the News view-->
13
+ <!-- Here, It should be in sync with this selector (and globalSettings.entriesOrder) on the NewsView, -->
14
+ <!-- so, it should work fine -->
12
15
  <config-selector
13
16
  :values="e.EntriesOrderProperties"
14
17
  v-model:property="globalSettings.entriesOrder" />
@@ -35,8 +38,7 @@
35
38
  <template #side-footer>
36
39
  <tags-filter
37
40
  :tags="tagsCount"
38
- :show-create-rule="true"
39
- change-source="news_tags_filter" />
41
+ :show-create-rule="true" />
40
42
  </template>
41
43
 
42
44
  <template #main-header>
@@ -53,10 +55,16 @@
53
55
  :collections-notification_="!hasEntries"
54
56
  :collections-warning_="false" />
55
57
 
58
+ <notifications-loaded-old-news
59
+ :entries="entriesStore.loadedEntriesReport || []"
60
+ :period="e.LastEntriesPeriodProperties.get(globalSettings.lastEntriesPeriod)" />
61
+
56
62
  <entries-list
63
+ :loading="entriesStore.loading"
57
64
  :entriesIds="entriesReport"
58
- :time-field="timeField"
65
+ :time-field="entriesStore.activeOrderProperties.timeField"
59
66
  :tags-count="tagsCount"
67
+ :show-score="true"
60
68
  :showFromStart="25"
61
69
  :showPerPage="25" />
62
70
  </side-panel-layout>
@@ -64,10 +72,12 @@
64
72
 
65
73
  <script lang="ts" setup>
66
74
  import {computed, ref, onUnmounted, watch, provide} from "vue";
75
+ import {useRoute, useRouter} from "vue-router";
67
76
  import {computedAsync} from "@vueuse/core";
68
77
  import * as api from "@/logic/api";
69
78
  import * as tagsFilterState from "@/logic/tagsFilterState";
70
79
  import * as e from "@/logic/enums";
80
+ import * as utils from "@/logic/utils";
71
81
  import type * as t from "@/logic/types";
72
82
  import {useGlobalSettingsStore} from "@/stores/globalSettings";
73
83
  import {useEntriesStore} from "@/stores/entries";
@@ -76,107 +86,37 @@
76
86
  const globalSettings = useGlobalSettingsStore();
77
87
  const entriesStore = useEntriesStore();
78
88
 
79
- const tagsStates = ref<tagsFilterState.Storage>(new tagsFilterState.Storage());
80
-
81
- provide("tagsStates", tagsStates);
82
-
83
- globalSettings.mainPanelMode = e.MainPanelMode.Entries;
84
-
85
- globalSettings.updateDataVersion();
86
-
87
- const entriesWithOpenedBody = ref<{[key: t.EntryId]: boolean}>({});
88
-
89
- const _sortedEntries = computed(() => {
90
- if (entriesStore.loadedEntriesReport === null) {
91
- return [];
92
- }
93
-
94
- const orderProperties = e.EntriesOrderProperties.get(globalSettings.entriesOrder);
95
-
96
- if (orderProperties === undefined) {
97
- throw new Error(`Unknown order ${globalSettings.entriesOrder}`);
98
- }
99
-
100
- const field = orderProperties.orderField;
101
- const direction = orderProperties.direction;
102
-
103
- // let report = entriesStore.loadedEntriesReport.slice();
104
-
105
- // Pre-map to avoid repeated lookups in the comparator
106
- const mapped = entriesStore.loadedEntriesReport.map((entryId) => {
107
- // @ts-ignore
108
- return {entryId, value: entriesStore.entries[entryId][field]};
109
- });
89
+ const route = useRoute();
90
+ const router = useRouter();
110
91
 
111
- mapped.sort((a: {entryId: t.EntryId; value: any}, b: {entryId: t.EntryId; value: any}) => {
112
- if (a.value === null && b.value === null) {
113
- return 0;
114
- }
115
-
116
- if (a.value === null) {
117
- return 1;
118
- }
92
+ entriesStore.setNewsMode();
119
93
 
120
- if (b.value === null) {
121
- return -1;
122
- }
123
-
124
- if (a.value < b.value) {
125
- return direction;
126
- }
127
-
128
- if (a.value > b.value) {
129
- return -direction;
130
- }
131
-
132
- return 0;
133
- });
94
+ const tagsStates = ref<tagsFilterState.Storage>(new tagsFilterState.Storage());
134
95
 
135
- const report = mapped.map((x) => x.entryId);
96
+ provide("tagsStates", tagsStates);
97
+ provide("eventsViewName", "news");
136
98
 
137
- return report;
99
+ tagsFilterState.setSyncingTagsWithRoute({
100
+ tagsStates: tagsStates.value as unknown as tagsFilterState.Storage,
101
+ route,
102
+ router
138
103
  });
139
104
 
140
- const _visibleEntries = computed(() => {
141
- let report = _sortedEntries.value.slice();
142
-
143
- if (!globalSettings.showRead) {
144
- report = report.filter((entryId) => {
145
- if (entriesStore.displayedEntryId == entryId) {
146
- // always show read entries with open body
147
- // otherwise, they will hide right after opening it
148
- return true;
149
- }
150
- return !entriesStore.entries[entryId].hasMarker(e.Marker.Read);
151
- });
152
- }
105
+ globalSettings.mainPanelMode = e.MainPanelMode.Entries;
153
106
 
154
- return report;
155
- });
107
+ globalSettings.updateDataVersion();
156
108
 
157
109
  const entriesReport = computed(() => {
158
- let report = _visibleEntries.value.slice();
110
+ let report = entriesStore.visibleEntries.slice();
159
111
 
160
112
  report = tagsStates.value.filterByTags(report, (entryId) => entriesStore.entries[entryId].tags);
161
113
  return report;
162
114
  });
163
115
 
164
116
  const tagsCount = computed(() => {
165
- const tagsCount: {[key: string]: number} = {};
166
-
167
- for (const entryId of entriesReport.value) {
168
- const entry = entriesStore.entries[entryId];
117
+ const entriesToProcess = entriesReport.value.map((entryId) => entriesStore.entries[entryId]);
169
118
 
170
- for (const tag of entry.tags) {
171
- if (tag in tagsCount) {
172
- tagsCount[tag] += 1;
173
- } else {
174
- tagsCount[tag] = 1;
175
- }
176
- }
177
- }
178
-
179
- return tagsCount;
119
+ return utils.countTags(entriesToProcess);
180
120
  });
181
121
 
182
122
  const entriesNumber = computed(() => {
@@ -188,6 +128,10 @@
188
128
  });
189
129
 
190
130
  const hasRules = computed(() => {
131
+ if (entriesStore.loading) {
132
+ return false;
133
+ }
134
+
191
135
  if (entriesStore.loadedEntriesReport === null) {
192
136
  return false;
193
137
  }
@@ -201,16 +145,6 @@
201
145
  }
202
146
  return false;
203
147
  });
204
-
205
- const timeField = computed(() => {
206
- const orderProperties = e.EntriesOrderProperties.get(globalSettings.entriesOrder);
207
-
208
- if (orderProperties === undefined) {
209
- throw new Error(`Unknown entries order: ${globalSettings.entriesOrder}`);
210
- }
211
-
212
- return orderProperties.timeField;
213
- });
214
148
  </script>
215
149
 
216
150
  <style></style>