feeds-fun 1.14.2 → 1.16.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/DiscoveryForm.vue +36 -17
- package/src/components/EntriesList.vue +5 -10
- package/src/components/EntryForList.vue +20 -39
- package/src/components/FfunTag.vue +55 -30
- package/src/components/OPMLUpload.vue +7 -0
- package/src/components/RuleConstructor.vue +56 -27
- package/src/components/RuleForList.vue +14 -3
- package/src/components/TagsFilter.vue +68 -76
- package/src/components/TagsList.vue +14 -66
- package/src/components/notifications/Block.vue +7 -2
- package/src/components/notifications/CreateRuleHelp.vue +16 -9
- package/src/inputs/ScoreSelector.vue +10 -3
- package/src/layouts/SidePanelLayout.vue +2 -2
- package/src/logic/api.ts +33 -5
- package/src/logic/asserts.ts +5 -0
- package/src/logic/tagsFilterState.ts +63 -11
- package/src/logic/types.ts +34 -7
- package/src/stores/entries.ts +27 -1
- package/src/stores/globalSettings.ts +0 -2
- package/src/values/Score.vue +11 -2
- package/src/views/DiscoveryView.vue +16 -6
- package/src/views/FeedsView.vue +9 -0
- package/src/views/NewsView.vue +51 -51
- package/src/views/RulesView.vue +34 -14
package/package.json
CHANGED
|
@@ -24,30 +24,44 @@
|
|
|
24
24
|
|
|
25
25
|
<div v-else-if="foundFeeds === null"></div>
|
|
26
26
|
|
|
27
|
-
<
|
|
27
|
+
<div
|
|
28
28
|
v-else-if="foundFeeds.length === 0"
|
|
29
|
-
class="ffun-info-attention"
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
class="ffun-info-attention">
|
|
30
|
+
<p
|
|
31
|
+
class="ffun-info-error"
|
|
32
|
+
v-for="message in messages">
|
|
33
|
+
{{ message.message }}
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
<p v-if="messages.length === 0"> No feeds found. </p>
|
|
37
|
+
</div>
|
|
32
38
|
|
|
33
39
|
<div
|
|
34
40
|
v-for="feed in foundFeeds"
|
|
35
41
|
:key="feed.url">
|
|
36
42
|
<feed-info :feed="feed" />
|
|
37
43
|
|
|
38
|
-
<button
|
|
39
|
-
class="ffun-form-button"
|
|
40
|
-
v-if="!addedFeeds[feed.url]"
|
|
41
|
-
:disabled="disableInputs"
|
|
42
|
-
@click.prevent="addFeed(feed.url)">
|
|
43
|
-
Add
|
|
44
|
-
</button>
|
|
45
|
-
|
|
46
44
|
<p
|
|
47
|
-
v-
|
|
48
|
-
class="ffun-info-good"
|
|
49
|
-
|
|
50
|
-
>
|
|
45
|
+
v-if="feed.isLinked"
|
|
46
|
+
class="ffun-info-good">
|
|
47
|
+
You are already subscribed to this feed.
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
<template v-else>
|
|
51
|
+
<button
|
|
52
|
+
class="ffun-form-button"
|
|
53
|
+
v-if="!addedFeeds[feed.url]"
|
|
54
|
+
:disabled="disableInputs"
|
|
55
|
+
@click.prevent="addFeed(feed.url)">
|
|
56
|
+
Add
|
|
57
|
+
</button>
|
|
58
|
+
|
|
59
|
+
<p
|
|
60
|
+
v-else
|
|
61
|
+
class="ffun-info-good"
|
|
62
|
+
>Feed added</p
|
|
63
|
+
>
|
|
64
|
+
</template>
|
|
51
65
|
</div>
|
|
52
66
|
</div>
|
|
53
67
|
</template>
|
|
@@ -74,17 +88,22 @@
|
|
|
74
88
|
|
|
75
89
|
const addedFeeds = ref<{[key: string]: boolean}>({});
|
|
76
90
|
|
|
91
|
+
let messages = ref<t.ApiMessage[]>([]);
|
|
92
|
+
|
|
77
93
|
const foundFeeds = computedAsync(async () => {
|
|
78
94
|
if (searhedUrl.value === "") {
|
|
79
95
|
return null;
|
|
80
96
|
}
|
|
81
97
|
|
|
82
98
|
searching.value = true;
|
|
99
|
+
messages.value = [];
|
|
83
100
|
|
|
84
101
|
let feeds: t.FeedInfo[] = [];
|
|
85
102
|
|
|
86
103
|
try {
|
|
87
|
-
|
|
104
|
+
const answer = await api.discoverFeeds({url: searhedUrl.value});
|
|
105
|
+
feeds = answer.feeds;
|
|
106
|
+
messages.value = answer.messages;
|
|
88
107
|
} catch (e) {
|
|
89
108
|
console.error(e);
|
|
90
109
|
}
|
|
@@ -9,9 +9,7 @@
|
|
|
9
9
|
<entry-for-list
|
|
10
10
|
:entryId="entryId"
|
|
11
11
|
:time-field="timeField"
|
|
12
|
-
:
|
|
13
|
-
:tags-count="tagsCount"
|
|
14
|
-
@entry:bodyVisibilityChanged="onBodyVisibilityChanged" />
|
|
12
|
+
:tags-count="tagsCount" />
|
|
15
13
|
</li>
|
|
16
14
|
</ul>
|
|
17
15
|
|
|
@@ -35,14 +33,11 @@
|
|
|
35
33
|
const properties = defineProps<{
|
|
36
34
|
entriesIds: Array<t.EntryId>;
|
|
37
35
|
timeField: string;
|
|
38
|
-
showTags: boolean;
|
|
39
36
|
showFromStart: number;
|
|
40
37
|
showPerPage: number;
|
|
41
38
|
tagsCount: {[key: string]: number};
|
|
42
39
|
}>();
|
|
43
40
|
|
|
44
|
-
const emit = defineEmits(["entry:bodyVisibilityChanged"]);
|
|
45
|
-
|
|
46
41
|
const showEntries = ref(properties.showFromStart);
|
|
47
42
|
|
|
48
43
|
const entriesToShow = computed(() => {
|
|
@@ -51,14 +46,14 @@
|
|
|
51
46
|
}
|
|
52
47
|
return properties.entriesIds.slice(0, showEntries.value);
|
|
53
48
|
});
|
|
54
|
-
|
|
55
|
-
function onBodyVisibilityChanged({entryId, visible}: {entryId: t.EntryId; visible: boolean}) {
|
|
56
|
-
emit("entry:bodyVisibilityChanged", {entryId, visible});
|
|
57
|
-
}
|
|
58
49
|
</script>
|
|
59
50
|
|
|
60
51
|
<style scoped>
|
|
52
|
+
.entry-block {
|
|
53
|
+
}
|
|
54
|
+
|
|
61
55
|
.entry-block:not(:last-child) {
|
|
62
56
|
border-bottom-width: 1px;
|
|
57
|
+
@apply py-1;
|
|
63
58
|
}
|
|
64
59
|
</style>
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
ref="entryTop"
|
|
4
|
+
class="flex text-lg">
|
|
3
5
|
<div class="flex-shrink-0 w-8 text-right pr-1">
|
|
4
6
|
<value-score
|
|
5
7
|
:value="entry.score"
|
|
@@ -9,7 +11,7 @@
|
|
|
9
11
|
<div class="flex-shrink-0 w-8 text-right pr-1">
|
|
10
12
|
<favicon-element
|
|
11
13
|
:url="entry.url"
|
|
12
|
-
class="w-
|
|
14
|
+
class="w-5 h-5 align-text-bottom mx-1 inline" />
|
|
13
15
|
</div>
|
|
14
16
|
|
|
15
17
|
<div class="flex-shrink-0 text-right">
|
|
@@ -25,16 +27,17 @@
|
|
|
25
27
|
<a
|
|
26
28
|
:href="entry.url"
|
|
27
29
|
target="_blank"
|
|
28
|
-
:class="[{'font-bold': isRead}, 'flex-grow', 'min-w-fit', 'line-clamp-1', 'pr-4', 'mb-0']"
|
|
30
|
+
:class="[{'font-bold': !isRead}, 'flex-grow', 'min-w-fit', 'line-clamp-1', 'pr-4', 'mb-0']"
|
|
29
31
|
@click="onTitleClick">
|
|
30
32
|
{{ purifiedTitle }}
|
|
31
33
|
</a>
|
|
32
34
|
|
|
33
35
|
<tags-list
|
|
34
|
-
v-if="showTags"
|
|
35
36
|
class="mt-0 pt-0"
|
|
36
37
|
:tags="entry.tags"
|
|
37
38
|
:tags-count="tagsCount"
|
|
39
|
+
:show-all="showBody"
|
|
40
|
+
@request-to-show-all="entriesStore.displayEntry({entryId: entry.id})"
|
|
38
41
|
:contributions="entry.scoreContributions" />
|
|
39
42
|
</div>
|
|
40
43
|
|
|
@@ -70,7 +73,7 @@
|
|
|
70
73
|
|
|
71
74
|
<script lang="ts" setup>
|
|
72
75
|
import _ from "lodash";
|
|
73
|
-
import {computed, ref} from "vue";
|
|
76
|
+
import {computed, ref, useTemplateRef} from "vue";
|
|
74
77
|
import type * as t from "@/logic/types";
|
|
75
78
|
import * as events from "@/logic/events";
|
|
76
79
|
import * as e from "@/logic/enums";
|
|
@@ -80,15 +83,14 @@
|
|
|
80
83
|
|
|
81
84
|
const entriesStore = useEntriesStore();
|
|
82
85
|
|
|
86
|
+
const topElement = useTemplateRef("entryTop");
|
|
87
|
+
|
|
83
88
|
const properties = defineProps<{
|
|
84
89
|
entryId: t.EntryId;
|
|
85
90
|
timeField: string;
|
|
86
|
-
showTags: boolean;
|
|
87
91
|
tagsCount: {[key: string]: number};
|
|
88
92
|
}>();
|
|
89
93
|
|
|
90
|
-
const emit = defineEmits(["entry:bodyVisibilityChanged"]);
|
|
91
|
-
|
|
92
94
|
const entry = computed(() => {
|
|
93
95
|
if (properties.entryId in entriesStore.entries) {
|
|
94
96
|
return entriesStore.entries[properties.entryId];
|
|
@@ -98,10 +100,12 @@
|
|
|
98
100
|
});
|
|
99
101
|
|
|
100
102
|
const isRead = computed(() => {
|
|
101
|
-
return
|
|
103
|
+
return entriesStore.entries[entry.value.id].hasMarker(e.Marker.Read);
|
|
102
104
|
});
|
|
103
105
|
|
|
104
|
-
const showBody =
|
|
106
|
+
const showBody = computed(() => {
|
|
107
|
+
return entry.value.id == entriesStore.displayedEntryId;
|
|
108
|
+
});
|
|
105
109
|
|
|
106
110
|
const timeFor = computed(() => {
|
|
107
111
|
if (entry.value === null) {
|
|
@@ -111,25 +115,6 @@
|
|
|
111
115
|
return _.get(entry.value, properties.timeField, null);
|
|
112
116
|
});
|
|
113
117
|
|
|
114
|
-
async function displayBody() {
|
|
115
|
-
showBody.value = true;
|
|
116
|
-
|
|
117
|
-
emit("entry:bodyVisibilityChanged", {entryId: properties.entryId, visible: true});
|
|
118
|
-
|
|
119
|
-
if (entry.value === null) {
|
|
120
|
-
throw new Error("entry is null");
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
entriesStore.requestFullEntry({entryId: entry.value.id});
|
|
124
|
-
|
|
125
|
-
await events.newsBodyOpened({entryId: entry.value.id});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function hideBody() {
|
|
129
|
-
showBody.value = false;
|
|
130
|
-
emit("entry:bodyVisibilityChanged", {entryId: properties.entryId, visible: false});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
118
|
const purifiedTitle = computed(() => {
|
|
134
119
|
if (entry.value === null) {
|
|
135
120
|
return "";
|
|
@@ -166,20 +151,16 @@
|
|
|
166
151
|
event.stopPropagation();
|
|
167
152
|
|
|
168
153
|
if (showBody.value) {
|
|
169
|
-
|
|
154
|
+
entriesStore.hideEntry({entryId: entry.value.id});
|
|
170
155
|
} else {
|
|
171
|
-
|
|
156
|
+
await entriesStore.displayEntry({entryId: entry.value.id});
|
|
157
|
+
|
|
158
|
+
if (topElement.value) {
|
|
159
|
+
topElement.value.scrollIntoView({behavior: "instant"});
|
|
160
|
+
}
|
|
172
161
|
}
|
|
173
162
|
} else {
|
|
174
163
|
await newsLinkOpenedEvent();
|
|
175
164
|
}
|
|
176
|
-
|
|
177
|
-
// TODO: is it will be too slow?
|
|
178
|
-
if (showBody.value) {
|
|
179
|
-
await entriesStore.setMarker({
|
|
180
|
-
entryId: properties.entryId,
|
|
181
|
-
marker: e.Marker.Read
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
165
|
}
|
|
185
166
|
</script>
|
|
@@ -1,35 +1,52 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
:class="classes"
|
|
4
|
-
:title="tooltip"
|
|
5
|
-
@click.prevent="onClick()">
|
|
6
|
-
<span v-if="countMode == 'prefix'">[{{ count }}]</span>
|
|
7
|
-
|
|
8
|
-
{{ tagInfo.name }}
|
|
9
|
-
|
|
2
|
+
<div class="inline-block">
|
|
10
3
|
<a
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@click.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
href="#"
|
|
5
|
+
v-if="showSwitch"
|
|
6
|
+
class="pr-1"
|
|
7
|
+
@click.prevent="onRevers()"
|
|
8
|
+
>⇄</a
|
|
9
|
+
>
|
|
10
|
+
<div
|
|
11
|
+
:class="classes"
|
|
12
|
+
:title="tooltip"
|
|
13
|
+
@click.prevent="onClick()">
|
|
14
|
+
<span v-if="countMode == 'prefix'">[{{ count }}]</span>
|
|
15
|
+
|
|
16
|
+
{{ tagInfo.name }}
|
|
17
|
+
|
|
18
|
+
<a
|
|
19
|
+
v-if="tagInfo.link"
|
|
20
|
+
:href="tagInfo.link"
|
|
21
|
+
target="_blank"
|
|
22
|
+
@click.stop=""
|
|
23
|
+
rel="noopener noreferrer">
|
|
24
|
+
↗
|
|
25
|
+
</a>
|
|
26
|
+
</div>
|
|
18
27
|
</div>
|
|
19
28
|
</template>
|
|
20
29
|
|
|
21
30
|
<script lang="ts" setup>
|
|
22
31
|
import * as t from "@/logic/types";
|
|
23
|
-
import {computed, ref} from "vue";
|
|
32
|
+
import {computed, ref, inject} from "vue";
|
|
33
|
+
import type {Ref} from "vue";
|
|
24
34
|
import {useTagsStore} from "@/stores/tags";
|
|
35
|
+
import * as tagsFilterState from "@/logic/tagsFilterState";
|
|
36
|
+
import * as asserts from "@/logic/asserts";
|
|
25
37
|
|
|
26
38
|
const tagsStore = useTagsStore();
|
|
27
39
|
|
|
40
|
+
const tagsStates = inject<Ref<tagsFilterState.Storage>>("tagsStates");
|
|
41
|
+
|
|
42
|
+
asserts.defined(tagsStates);
|
|
43
|
+
|
|
28
44
|
const properties = defineProps<{
|
|
29
45
|
uid: string;
|
|
30
46
|
count?: number | null;
|
|
31
47
|
countMode?: string | null;
|
|
32
|
-
|
|
48
|
+
secondaryMode?: string | null;
|
|
49
|
+
showSwitch?: boolean | null;
|
|
33
50
|
}>();
|
|
34
51
|
|
|
35
52
|
const tagInfo = computed(() => {
|
|
@@ -44,27 +61,39 @@
|
|
|
44
61
|
return t.noInfoTag(properties.uid);
|
|
45
62
|
});
|
|
46
63
|
|
|
47
|
-
const emit = defineEmits(["tag:clicked"]);
|
|
48
|
-
|
|
49
64
|
const classes = computed(() => {
|
|
50
65
|
const result: {[key: string]: boolean} = {
|
|
51
66
|
tag: true
|
|
52
67
|
};
|
|
53
68
|
|
|
54
|
-
if (properties.
|
|
55
|
-
result[
|
|
69
|
+
if (tagsStates.value.requiredTags[properties.uid]) {
|
|
70
|
+
result["required"] = true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (tagsStates.value.excludedTags[properties.uid]) {
|
|
74
|
+
result["excluded"] = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (properties.secondaryMode) {
|
|
78
|
+
result[properties.secondaryMode] = true;
|
|
56
79
|
}
|
|
57
80
|
|
|
58
81
|
return result;
|
|
59
82
|
});
|
|
60
83
|
|
|
61
84
|
function onClick() {
|
|
62
|
-
|
|
85
|
+
asserts.defined(tagsStates);
|
|
86
|
+
tagsStates.value.onTagClicked({tag: properties.uid});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function onRevers() {
|
|
90
|
+
asserts.defined(tagsStates);
|
|
91
|
+
tagsStates.value.onTagReversed({tag: properties.uid});
|
|
63
92
|
}
|
|
64
93
|
|
|
65
94
|
const tooltip = computed(() => {
|
|
66
95
|
if (properties.countMode == "tooltip" && properties.count) {
|
|
67
|
-
return `articles with
|
|
96
|
+
return `articles with this tag: ${properties.count}`;
|
|
68
97
|
}
|
|
69
98
|
return "";
|
|
70
99
|
});
|
|
@@ -72,19 +101,15 @@
|
|
|
72
101
|
|
|
73
102
|
<style scoped>
|
|
74
103
|
.tag {
|
|
75
|
-
@apply inline-block cursor-pointer p-0 mr-2 whitespace-nowrap;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.tag.selected {
|
|
79
|
-
@apply font-bold text-purple-700;
|
|
104
|
+
@apply inline-block cursor-pointer p-0 mr-2 whitespace-nowrap hover:bg-green-100 px-1 hover:rounded-lg;
|
|
80
105
|
}
|
|
81
106
|
|
|
82
107
|
.tag.required {
|
|
83
|
-
@apply text-green-700;
|
|
108
|
+
@apply text-green-700 font-bold;
|
|
84
109
|
}
|
|
85
110
|
|
|
86
111
|
.tag.excluded {
|
|
87
|
-
@apply text-red-700;
|
|
112
|
+
@apply text-red-700 font-bold;
|
|
88
113
|
}
|
|
89
114
|
|
|
90
115
|
.tag.positive {
|
|
@@ -43,6 +43,9 @@
|
|
|
43
43
|
import * as api from "@/logic/api";
|
|
44
44
|
import {computedAsync} from "@vueuse/core";
|
|
45
45
|
import {useEntriesStore} from "@/stores/entries";
|
|
46
|
+
import {useGlobalSettingsStore} from "@/stores/globalSettings";
|
|
47
|
+
|
|
48
|
+
const globalSettings = useGlobalSettingsStore();
|
|
46
49
|
|
|
47
50
|
const opmlFile = ref<File | null>(null);
|
|
48
51
|
|
|
@@ -76,6 +79,10 @@
|
|
|
76
79
|
try {
|
|
77
80
|
await api.addOPML({content: content});
|
|
78
81
|
|
|
82
|
+
// loading an OPML file is pretty rare and significantly changes the list of feeds
|
|
83
|
+
// => we can force data to be reloaded
|
|
84
|
+
globalSettings.updateDataVersion();
|
|
85
|
+
|
|
79
86
|
loading.value = false;
|
|
80
87
|
loaded.value = true;
|
|
81
88
|
error.value = false;
|
|
@@ -1,42 +1,73 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<div
|
|
4
|
+
v-if="tagsStates.hasSelectedTags"
|
|
5
|
+
class="flex items-center">
|
|
6
|
+
<div class="flex-none">
|
|
7
|
+
<score-selector
|
|
8
|
+
class="inline-block mr-2 my-auto"
|
|
9
|
+
v-model="currentScore" />
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<a
|
|
13
|
+
class="ffun-form-button p-1 my-1 block text-center inline-block flex-grow"
|
|
14
|
+
href="#"
|
|
15
|
+
@click.prevent="createOrUpdateRule()"
|
|
16
|
+
>Create Rule</a
|
|
17
|
+
>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<p
|
|
21
|
+
class="ffun-info-good"
|
|
22
|
+
v-else>
|
|
23
|
+
<template v-if="showSuccess"> Rule created. </template>
|
|
24
|
+
<template v-else> Select tags to create a rule. </template>
|
|
25
|
+
</p>
|
|
16
26
|
</div>
|
|
17
27
|
</template>
|
|
18
28
|
|
|
19
29
|
<script lang="ts" setup>
|
|
20
|
-
import {computed, ref} from "vue";
|
|
21
|
-
import {
|
|
30
|
+
import {computed, ref, inject, watch} from "vue";
|
|
31
|
+
import type {Ref} from "vue";
|
|
32
|
+
import {useTagsStore} from "@/stores/tags";
|
|
33
|
+
import type * as tagsFilterState from "@/logic/tagsFilterState";
|
|
34
|
+
import * as asserts from "@/logic/asserts";
|
|
22
35
|
import * as api from "@/logic/api";
|
|
23
|
-
|
|
36
|
+
import {useGlobalSettingsStore} from "@/stores/globalSettings";
|
|
37
|
+
|
|
38
|
+
const tagsStore = useTagsStore();
|
|
24
39
|
|
|
25
40
|
const globalSettings = useGlobalSettingsStore();
|
|
26
41
|
|
|
27
|
-
const
|
|
42
|
+
const currentScore = ref(1);
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
const scores = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610];
|
|
44
|
+
const showSuccess = ref(false);
|
|
31
45
|
|
|
32
|
-
const
|
|
46
|
+
const tagsStates = inject<Ref<tagsFilterState.Storage>>("tagsStates");
|
|
47
|
+
asserts.defined(tagsStates);
|
|
33
48
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
49
|
+
watch(
|
|
50
|
+
() => tagsStates.value.hasSelectedTags,
|
|
51
|
+
() => {
|
|
52
|
+
// This condition is needed to prevent immediate reset of the success message
|
|
53
|
+
// right after the rule is created in createOrUpdateRule
|
|
54
|
+
if (tagsStates.value.hasSelectedTags) {
|
|
55
|
+
showSuccess.value = false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
);
|
|
37
59
|
|
|
38
60
|
async function createOrUpdateRule() {
|
|
39
|
-
|
|
61
|
+
asserts.defined(tagsStates);
|
|
62
|
+
await api.createOrUpdateRule({
|
|
63
|
+
requiredTags: Object.keys(tagsStates.value.requiredTags),
|
|
64
|
+
excludedTags: Object.keys(tagsStates.value.excludedTags),
|
|
65
|
+
score: currentScore.value
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
tagsStates.value.clear();
|
|
69
|
+
|
|
70
|
+
showSuccess.value = true;
|
|
40
71
|
|
|
41
72
|
// this line leads to the reloading of news and any other data
|
|
42
73
|
// not an elegant solution, but it works with the current API implementation
|
|
@@ -44,7 +75,5 @@
|
|
|
44
75
|
// - without reloading
|
|
45
76
|
// - maybe, without reordering too
|
|
46
77
|
globalSettings.updateDataVersion();
|
|
47
|
-
|
|
48
|
-
emit("rule-constructor:created");
|
|
49
78
|
}
|
|
50
79
|
</script>
|
|
@@ -19,9 +19,19 @@
|
|
|
19
19
|
|
|
20
20
|
<div class="flex-grow">
|
|
21
21
|
<template
|
|
22
|
-
v-for="tag of rule.
|
|
22
|
+
v-for="tag of rule.requiredTags"
|
|
23
23
|
:key="tag">
|
|
24
|
-
<ffun-tag
|
|
24
|
+
<ffun-tag
|
|
25
|
+
:uid="tag"
|
|
26
|
+
secondary-mode="positive" />
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<template
|
|
30
|
+
v-for="tag of rule.excludedTags"
|
|
31
|
+
:key="tag">
|
|
32
|
+
<ffun-tag
|
|
33
|
+
:uid="tag"
|
|
34
|
+
secondary-mode="negative" />
|
|
25
35
|
</template>
|
|
26
36
|
</div>
|
|
27
37
|
</div>
|
|
@@ -67,7 +77,8 @@
|
|
|
67
77
|
await api.updateRule({
|
|
68
78
|
id: properties.rule.id,
|
|
69
79
|
score: newScore,
|
|
70
|
-
|
|
80
|
+
requiredTags: properties.rule.requiredTags,
|
|
81
|
+
excludedTags: properties.rule.excludedTags
|
|
71
82
|
});
|
|
72
83
|
|
|
73
84
|
scoreChanged.value = true;
|