feeds-fun 1.17.1 → 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.
- package/package.json +1 -1
- package/src/components/EntriesList.vue +11 -0
- package/src/components/EntryForList.vue +17 -6
- package/src/components/RuleForList.vue +2 -2
- package/src/components/RulesList.vue +18 -9
- package/src/components/TokensCost.vue +0 -2
- package/src/components/collections/DetailedItem.vue +9 -0
- package/src/components/collections/PublicIntro.vue +63 -0
- package/src/components/collections/PublicSelector.vue +43 -0
- package/src/components/main/Block.vue +1 -1
- package/src/components/main/Item.vue +1 -1
- package/src/components/notifications/LoadedOldNews.vue +47 -0
- package/src/components/page_header/ExternalLinks.vue +11 -6
- package/src/components/tags/EntryTag.vue +4 -3
- package/src/components/tags/FilterTag.vue +6 -3
- package/src/components/tags/RuleTag.vue +4 -3
- package/src/components/tags/TagsFilter.vue +17 -5
- package/src/css/panels.css +5 -5
- package/src/layouts/SidePanelLayout.vue +41 -22
- package/src/layouts/WideLayout.vue +0 -4
- package/src/logic/api.ts +23 -0
- package/src/logic/enums.ts +28 -12
- package/src/logic/events.ts +36 -8
- package/src/logic/tagsFilterState.ts +50 -1
- package/src/logic/types.ts +15 -2
- package/src/logic/utils.ts +104 -1
- package/src/main.ts +6 -0
- package/src/router/index.ts +16 -3
- package/src/stores/collections.ts +17 -1
- package/src/stores/entries.ts +154 -19
- package/src/stores/globalSettings.ts +6 -7
- package/src/views/AuthView.vue +3 -1
- package/src/views/CollectionsView.vue +13 -2
- package/src/views/DiscoveryView.vue +3 -1
- package/src/views/FeedsView.vue +3 -1
- package/src/views/MainView.vue +18 -1
- package/src/views/NewsView.vue +32 -98
- package/src/views/PublicCollectionView.vue +237 -0
- package/src/views/RulesView.vue +26 -29
- package/src/views/SettingsView.vue +3 -1
package/src/stores/entries.ts
CHANGED
|
@@ -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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
95
|
+
entries.value = {...entries.value, ...delta};
|
|
31
96
|
}
|
|
32
97
|
|
|
33
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
|
190
|
+
const loadedEntries = await api.getEntriesByIds({ids: ids});
|
|
75
191
|
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
package/src/views/AuthView.vue
CHANGED
|
@@ -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
|
|
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
|
|
package/src/views/FeedsView.vue
CHANGED
|
@@ -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(() => {
|
package/src/views/MainView.vue
CHANGED
|
@@ -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>
|
package/src/views/NewsView.vue
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
96
|
+
provide("tagsStates", tagsStates);
|
|
97
|
+
provide("eventsViewName", "news");
|
|
136
98
|
|
|
137
|
-
|
|
99
|
+
tagsFilterState.setSyncingTagsWithRoute({
|
|
100
|
+
tagsStates: tagsStates.value as unknown as tagsFilterState.Storage,
|
|
101
|
+
route,
|
|
102
|
+
router
|
|
138
103
|
});
|
|
139
104
|
|
|
140
|
-
|
|
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
|
-
|
|
155
|
-
});
|
|
107
|
+
globalSettings.updateDataVersion();
|
|
156
108
|
|
|
157
109
|
const entriesReport = computed(() => {
|
|
158
|
-
let report =
|
|
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
|
|
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
|
-
|
|
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>
|