feeds-fun 0.5.1 → 1.1.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 (45) hide show
  1. package/package.json +25 -23
  2. package/postcss.config.js +6 -0
  3. package/src/components/ConfigFlag.vue +6 -14
  4. package/src/components/ConfigSelector.vue +3 -1
  5. package/src/components/DiscoveryForm.vue +9 -2
  6. package/src/components/EntriesList.vue +15 -4
  7. package/src/components/EntryForList.vue +76 -65
  8. package/src/components/FeedForList.vue +15 -32
  9. package/src/components/FeedInfo.vue +11 -4
  10. package/src/components/FeedsCollections.vue +23 -15
  11. package/src/components/FeedsList.vue +7 -18
  12. package/src/components/FfunTag.vue +6 -13
  13. package/src/components/NotificationCollections.vue +7 -0
  14. package/src/components/NotificationOpenaiApiKey.vue +28 -0
  15. package/src/components/Notifications.vue +32 -0
  16. package/src/components/OPMLUpload.vue +3 -0
  17. package/src/components/RuleConstructor.vue +19 -27
  18. package/src/components/RuleForList.vue +47 -0
  19. package/src/components/RulesList.vue +7 -43
  20. package/src/components/SimplePagination.vue +4 -6
  21. package/src/components/SupertokensLogin.vue +50 -25
  22. package/src/components/TagsFilter.vue +13 -25
  23. package/src/components/TagsList.vue +27 -25
  24. package/src/components/UserSetting.vue +41 -38
  25. package/src/components/UserSettingForNotification.vue +63 -0
  26. package/src/inputs/Marker.vue +3 -7
  27. package/src/inputs/ScoreSelector.vue +11 -12
  28. package/src/layouts/SidePanelLayout.vue +42 -96
  29. package/src/layouts/WideLayout.vue +2 -25
  30. package/src/logic/api.ts +3 -3
  31. package/src/logic/settings.ts +0 -9
  32. package/src/main.ts +12 -19
  33. package/src/stores/entries.ts +1 -6
  34. package/src/style.css +92 -0
  35. package/src/values/Score.vue +1 -10
  36. package/src/views/AuthView.vue +3 -7
  37. package/src/views/CollectionsView.vue +2 -0
  38. package/src/views/FeedsView.vue +8 -3
  39. package/src/views/MainView.vue +36 -83
  40. package/src/views/NewsView.vue +30 -13
  41. package/src/views/RulesView.vue +0 -2
  42. package/src/views/SettingsView.vue +35 -22
  43. package/tailwind.config.js +14 -0
  44. package/src/components/EntryInfo.vue +0 -23
  45. package/src/components/FfunGithubButtons.vue +0 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feeds-fun",
3
- "version": "0.5.1",
3
+ "version": "1.1.0",
4
4
  "author": "Aliaksei Yaletski (Tiendil) <a.eletsky@gmail.com> (https://tiendil.org/)",
5
5
  "description": "Frontend for the Feeds Fun — web-based news reader",
6
6
  "keywords": [
@@ -32,37 +32,39 @@
32
32
  "format-check": "prettier --check src/"
33
33
  },
34
34
  "dependencies": {
35
- "@chenfengyuan/vue-countdown": "^2.1.1",
36
- "@sentry/vue": "^7.54.0",
35
+ "@chenfengyuan/vue-countdown": "^2.1.2",
37
36
  "@vueuse/core": "^9.13.0",
38
- "axios": "^1.3.4",
39
- "dompurify": "^3.0.1",
40
- "pinia": "^2.0.32",
37
+ "axios": "^1.5.1",
38
+ "dompurify": "^3.0.6",
39
+ "pinia": "^2.1.7",
41
40
  "set-interval-async": "^3.0.3",
42
41
  "supertokens-web-js": "^0.5.0",
43
42
  "vue": "^3.2.47",
44
- "vue-github-button": "^3.1.0",
45
- "vue-router": "^4.1.6"
43
+ "vue-router": "^4.2.5"
46
44
  },
47
45
  "devDependencies": {
48
- "@rushstack/eslint-patch": "^1.2.0",
49
- "@types/dompurify": "^3.0.2",
50
- "@types/jsdom": "^21.1.0",
51
- "@types/lodash": "^4.14.196",
52
- "@types/node": "^18.14.2",
53
- "@vitejs/plugin-vue": "^4.0.0",
46
+ "@rushstack/eslint-patch": "^1.5.1",
47
+ "@tailwindcss/typography": "^0.5.10",
48
+ "@types/dompurify": "^3.0.3",
49
+ "@types/jsdom": "^21.1.3",
50
+ "@types/lodash": "^4.14.199",
51
+ "@types/node": "^18.18.5",
52
+ "@vitejs/plugin-vue": "^4.4.0",
54
53
  "@vue/eslint-config-prettier": "^7.1.0",
55
- "@vue/eslint-config-typescript": "^11.0.2",
56
- "@vue/test-utils": "^2.3.0",
54
+ "@vue/eslint-config-typescript": "^11.0.3",
55
+ "@vue/test-utils": "^2.4.1",
57
56
  "@vue/tsconfig": "^0.1.3",
58
- "eslint": "^8.34.0",
59
- "eslint-plugin-vue": "^9.9.0",
60
- "jsdom": "^21.1.0",
57
+ "autoprefixer": "^10.4.16",
58
+ "eslint": "^8.51.0",
59
+ "eslint-plugin-vue": "^9.17.0",
60
+ "jsdom": "^21.1.2",
61
61
  "npm-run-all": "^4.1.5",
62
- "prettier": "^2.8.4",
62
+ "postcss": "^8.4.31",
63
+ "prettier": "^2.8.8",
64
+ "tailwindcss": "^3.3.3",
63
65
  "typescript": "~4.8.4",
64
- "vite": "^4.1.4",
65
- "vitest": "^0.29.1",
66
- "vue-tsc": "^1.2.0"
66
+ "vite": "^4.4.11",
67
+ "vitest": "^0.29.8",
68
+ "vue-tsc": "^1.8.19"
67
69
  }
68
70
  }
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
@@ -1,16 +1,10 @@
1
1
  <template>
2
- <a
3
- v-if="flag"
4
- href="#"
5
- @click="emit('update:flag', false)"
6
- >{{ onText }}</a
7
- >
8
- <a
9
- v-if="!flag"
10
- href="#"
11
- @click="emit('update:flag', true)"
12
- >{{ offText }}</a
13
- >
2
+ <button
3
+ class="ffun-config-flag"
4
+ @click="emit('update:flag', !flag)">
5
+ <span v-if="flag">{{ offText }}</span>
6
+ <span v-else>{{ onText }}</span>
7
+ </button>
14
8
  </template>
15
9
 
16
10
  <script setup lang="ts">
@@ -18,5 +12,3 @@
18
12
 
19
13
  const emit = defineEmits(["update:flag"]);
20
14
  </script>
21
-
22
- <style scoped></style>
@@ -1,5 +1,7 @@
1
1
  <template>
2
- <select @change="updateProperty($event)">
2
+ <select
3
+ class="ffun-input"
4
+ @change="updateProperty($event)">
3
5
  <option
4
6
  v-for="[value, props] of values"
5
7
  :value="value"
@@ -2,11 +2,13 @@
2
2
  <div>
3
3
  <input
4
4
  type="text"
5
+ class="ffun-input mr-1"
5
6
  v-model="search"
6
7
  :disabled="loading"
7
8
  placeholder="Search for feeds" />
8
9
 
9
10
  <button
11
+ class="ffun-form-button"
10
12
  :disabled="loading"
11
13
  @click.prevent="searhedUrl = search">
12
14
  Search
@@ -25,12 +27,17 @@
25
27
  <feed-info :feed="feed" />
26
28
 
27
29
  <button
30
+ class="ffun-form-button"
28
31
  v-if="!addedFeeds[feed.url]"
29
32
  @click.prevent="addFeed(feed.url)">
30
33
  Add
31
34
  </button>
32
- <p v-else>Feed added</p>
33
- <hr />
35
+
36
+ <p
37
+ v-else
38
+ class="ffun-info-good"
39
+ >Feed added</p
40
+ >
34
41
  </div>
35
42
  </div>
36
43
  </div>
@@ -1,16 +1,17 @@
1
1
  <template>
2
2
  <div>
3
3
  <template v-if="entriesToShow.length > 0">
4
- <ul style="list-style-type: none; margin: 0; padding: 0">
4
+ <ul>
5
5
  <li
6
6
  v-for="entryId in entriesToShow"
7
7
  :key="entryId"
8
- style="margin-bottom: 0.25rem">
8
+ class="mb-1 entry-block">
9
9
  <entry-for-list
10
10
  :entryId="entryId"
11
11
  :time-field="timeField"
12
12
  :show-tags="showTags"
13
- :tags-count="tagsCount" />
13
+ :tags-count="tagsCount"
14
+ @entry:bodyVisibilityChanged="onBodyVisibilityChanged" />
14
15
  </li>
15
16
  </ul>
16
17
 
@@ -40,6 +41,8 @@
40
41
  tagsCount: {[key: string]: number};
41
42
  }>();
42
43
 
44
+ const emit = defineEmits(["entry:bodyVisibilityChanged"]);
45
+
43
46
  const showEntries = ref(properties.showFromStart);
44
47
 
45
48
  const entriesToShow = computed(() => {
@@ -48,6 +51,14 @@
48
51
  }
49
52
  return properties.entriesIds.slice(0, showEntries.value);
50
53
  });
54
+
55
+ function onBodyVisibilityChanged({entryId, visible}: {entryId: t.EntryId; visible: boolean}) {
56
+ emit("entry:bodyVisibilityChanged", {entryId, visible});
57
+ }
51
58
  </script>
52
59
 
53
- <style></style>
60
+ <style scoped>
61
+ .entry-block:not(:last-child) {
62
+ border-bottom-width: 1px;
63
+ }
64
+ </style>
@@ -1,71 +1,66 @@
1
1
  <template>
2
- <div class="container">
3
- <div style="flex-shrink: 0; width: 2rem; text-align: right; padding-right: 0.25rem">
2
+ <div class="flex">
3
+ <div class="flex-shrink-0 w-8 text-right pr-1">
4
4
  <value-score
5
5
  :value="entry.score"
6
- :entry-id="entry.id"
7
- class="entity-for-list-score" />
6
+ :entry-id="entry.id" />
8
7
  </div>
9
8
 
10
- <div style="flex-grow: 1">
11
- <input-marker
12
- :marker="e.Marker.Read"
13
- :entry-id="entryId"
14
- on-text="read"
15
- off-text="new!" />
16
-
17
- <a
18
- href="#"
19
- style="text-decoration: none; margin-left: 0.25rem"
20
- v-if="!showBody"
21
- @click.prevent="displayBody()"
22
- >&#9660;</a
23
- >
24
- <a
25
- href="#"
26
- style="text-decoration: none; margin-left: 0.25rem"
27
- v-if="showBody"
28
- @click.prevent="showBody = false"
29
- >&#9650;</a
30
- >
31
-
9
+ <div class="flex-shrink-0 w-8 text-right pr-1">
32
10
  <favicon-element
33
11
  :url="entry.url"
34
- style="width: 1rem; height: 1rem; vertical-align: text-bottom; margin-left: 0.25rem; margin-right: 0.25rem" />
12
+ class="w-4 h-4 align-text-bottom mx-1 inline" />
13
+ </div>
35
14
 
15
+ <div class="flex-grow">
36
16
  <a
37
17
  :href="entry.url"
38
18
  target="_blank"
39
- @click="onTitleClick()"
40
- rel="noopener noreferrer">
19
+ :class="[{'font-bold': isRead}, 'flex-grow', 'min-w-fit', 'line-clamp-1', 'pr-4', 'mb-0']"
20
+ @click="onTitleClick">
41
21
  {{ purifiedTitle }}
42
22
  </a>
43
23
 
44
- <template v-if="showTags">
45
- <br />
46
- <tags-list
47
- :tags="entry.tags"
48
- :tags-count="tagsCount"
49
- :contributions="entry.scoreContributions" />
50
- </template>
24
+ <tags-list
25
+ v-if="showTags"
26
+ class="mt-0 pt-0"
27
+ :tags="entry.tags"
28
+ :tags-count="tagsCount"
29
+ :contributions="entry.scoreContributions" />
30
+ </div>
51
31
 
52
- <div
53
- v-if="showBody"
54
- style="display: flex; justify-content: center">
55
- <div style="max-width: 50rem">
56
- <h2>{{ purifiedTitle }}</h2>
57
- <p v-if="entry.body === null">loading...</p>
58
- <div
59
- v-if="entry.body !== null"
60
- v-html="purifiedBody" />
61
- </div>
32
+ <div class="flex flex-shrink-0">
33
+ <input-marker
34
+ class="w-7 mr-2"
35
+ :marker="e.Marker.Read"
36
+ :entry-id="entryId"
37
+ on-text="read"
38
+ off-text="new" />
39
+
40
+ <div class="w-7">
41
+ <value-date-time
42
+ :value="timeFor"
43
+ :reversed="true" />
62
44
  </div>
63
45
  </div>
46
+ </div>
64
47
 
65
- <div style="flex-shrink: 0; width: 1rem; left-padding: 0.25rem">
66
- <value-date-time
67
- :value="timeFor"
68
- :reversed="true" />
48
+ <div
49
+ v-if="showBody"
50
+ class="flex justify-center my-1">
51
+ <div class="max-w-3xl flex-1 bg-slate-50 border-2 rounded p-4">
52
+ <h2 class="mt-0"
53
+ ><a
54
+ :href="entry.url"
55
+ target="_blank"
56
+ >{{ purifiedTitle }}</a
57
+ ></h2
58
+ >
59
+ <p v-if="entry.body === null">loading...</p>
60
+ <div
61
+ v-if="entry.body !== null"
62
+ class="prose max-w-none"
63
+ v-html="purifiedBody" />
69
64
  </div>
70
65
  </div>
71
66
  </template>
@@ -88,6 +83,8 @@
88
83
  tagsCount: {[key: string]: number};
89
84
  }>();
90
85
 
86
+ const emit = defineEmits(["entry:bodyVisibilityChanged"]);
87
+
91
88
  const entry = computed(() => {
92
89
  if (properties.entryId in entriesStore.entries) {
93
90
  return entriesStore.entries[properties.entryId];
@@ -96,6 +93,10 @@
96
93
  throw new Error(`Unknown entry: ${properties.entryId}`);
97
94
  });
98
95
 
96
+ const isRead = computed(() => {
97
+ return !entriesStore.entries[entry.value.id].hasMarker(e.Marker.Read);
98
+ });
99
+
99
100
  const showBody = ref(false);
100
101
 
101
102
  const timeFor = computed(() => {
@@ -109,6 +110,8 @@
109
110
  function displayBody() {
110
111
  showBody.value = true;
111
112
 
113
+ emit("entry:bodyVisibilityChanged", {entryId: properties.entryId, visible: true});
114
+
112
115
  if (entry.value === null) {
113
116
  throw new Error("entry is null");
114
117
  }
@@ -116,6 +119,11 @@
116
119
  entriesStore.requestFullEntry({entryId: entry.value.id});
117
120
  }
118
121
 
122
+ function hideBody() {
123
+ showBody.value = false;
124
+ emit("entry:bodyVisibilityChanged", {entryId: properties.entryId, visible: false});
125
+ }
126
+
119
127
  const purifiedTitle = computed(() => {
120
128
  if (entry.value === null) {
121
129
  return "";
@@ -142,21 +150,24 @@
142
150
  return DOMPurify.sanitize(entry.value.body);
143
151
  });
144
152
 
145
- async function onTitleClick() {
146
- await entriesStore.setMarker({
147
- entryId: properties.entryId,
148
- marker: e.Marker.Read
149
- });
150
- }
151
- </script>
153
+ async function onTitleClick(event: MouseEvent) {
154
+ if (!event.ctrlKey) {
155
+ event.preventDefault();
156
+ event.stopPropagation();
152
157
 
153
- <style scoped>
154
- .container {
155
- display: flex;
156
- }
158
+ if (showBody.value) {
159
+ hideBody();
160
+ } else {
161
+ displayBody();
162
+ }
163
+ }
157
164
 
158
- .container :deep(img) {
159
- max-width: 100%;
160
- height: auto;
165
+ // TODO: is it will be too slow?
166
+ if (showBody.value) {
167
+ await entriesStore.setMarker({
168
+ entryId: properties.entryId,
169
+ marker: e.Marker.Read
170
+ });
171
+ }
161
172
  }
162
- </style>
173
+ </script>
@@ -1,17 +1,18 @@
1
1
  <template>
2
2
  <div
3
3
  v-if="feed !== null"
4
- class="container">
5
- <div style="flex-shrink: 0; width: 4rem; left-padding: 0.25rem">
4
+ class="flex mb-1">
5
+ <div class="flex-shrink-0 min-w-fit pr-2">
6
6
  <a
7
7
  href="#"
8
+ class="ffun-normal-link"
8
9
  @click.prevent="unsubscribe()">
9
10
  remove
10
11
  </a>
11
12
  </div>
12
13
 
13
14
  <div
14
- style="flex-shrink: 0; width: 3rem; left-padding: 0.25rem; cursor: default"
15
+ class="flex-shrink-0 w-12 pr-2 text-right cursor-default"
15
16
  title="Time of last load">
16
17
  <value-date-time
17
18
  :value="feed.loadedAt"
@@ -19,34 +20,37 @@
19
20
  </div>
20
21
 
21
22
  <div
22
- style="flex-shrink: 0; width: 3rem; left-padding: 0.25rem; cursor: default"
23
+ class="flex-shrink-0 w-12 pr-2 text-right cursor-default"
23
24
  title="When was added">
24
25
  <value-date-time
25
26
  :value="feed.linkedAt"
26
27
  :reversed="true" />
27
28
  </div>
28
29
 
29
- <div style="flex-shrink: 0; width: 2rem; text-align: right; padding-right: 0.25rem">
30
+ <div class="flex-shrink-0 w-8 pr-1 text-right cursor-default">
30
31
  <span
31
32
  v-if="feed.isOk"
32
33
  title="everything is ok"
33
- class="state-ok"
34
+ class="text-green-700 cursor-default"
34
35
  >ok</span
35
36
  >
36
37
  <span
37
38
  v-else
38
39
  :title="feed.lastError || 'unknown error'"
39
- class="state-error"
40
+ class="text-red-700 cursor-default"
40
41
  >⚠</span
41
42
  >
42
43
  </div>
43
44
 
44
- <favicon-element
45
- :url="feed.url"
46
- style="width: 1rem; height: 1rem; vertical-align: text-bottom; margin-right: 0.25rem" />
45
+ <div class="flex-shrink-0 w-8 text-right pr-1">
46
+ <favicon-element
47
+ :url="feed.url"
48
+ class="w-4 h-4 align-text-bottom mx-1 inline" />
49
+ </div>
47
50
 
48
- <div style="flex-grow: 1">
51
+ <div class="flex-grow">
49
52
  <value-url
53
+ class="ffun-normal-link"
50
54
  :value="feed.url"
51
55
  :text="purifiedTitle" />
52
56
  <template v-if="globalSettings.showFeedsDescriptions">
@@ -96,24 +100,3 @@
96
100
  globalSettings.updateDataVersion();
97
101
  }
98
102
  </script>
99
-
100
- <style scoped>
101
- .container {
102
- display: flex;
103
- }
104
-
105
- .container :deep(img) {
106
- max-width: 100%;
107
- height: auto;
108
- }
109
-
110
- .state-ok {
111
- color: green;
112
- cursor: default;
113
- }
114
-
115
- .state-error {
116
- color: red;
117
- cursor: default;
118
- }
119
- </style>
@@ -1,21 +1,28 @@
1
1
  <template>
2
2
  <div>
3
- <h3>
3
+ <h2>
4
4
  <a
5
+ class="ffun-normal-link"
5
6
  :href="feed.url"
6
7
  target="_blank"
7
8
  rel="noopener noreferrer">
8
9
  {{ feed.title }}
9
10
  </a>
10
- </h3>
11
+ </h2>
11
12
 
12
13
  <p v-html="feed.description" />
13
14
 
14
- <ul>
15
+ <ul class="list-disc list-inside">
15
16
  <li
16
17
  v-for="entry in feed.entries"
17
18
  :key="entry.url">
18
- <entry-info :entry="entry" />
19
+ <a
20
+ class="ffun-normal-link"
21
+ :href="entry.url"
22
+ target="_blank"
23
+ rel="noopener noreferrer">
24
+ {{ entry.title }}
25
+ </a>
19
26
  </li>
20
27
  </ul>
21
28
  </div>
@@ -1,20 +1,28 @@
1
1
  <template>
2
2
  <div>
3
- <template v-for="item in collections">
4
- <input
5
- type="checkbox"
6
- :id="item"
7
- :name="item"
8
- :value="item"
9
- v-model="selectedCollections"
10
- checked />
11
- <label :for="item">{{ item }}</label>
12
- <br />
13
- </template>
14
-
15
- <br />
16
-
17
- <button @click="subscribe()">Subscribe</button>
3
+ <ul class="mb-1">
4
+ <li v-for="item in collections">
5
+ <input
6
+ class="ffun-checkbox"
7
+ type="checkbox"
8
+ :id="item"
9
+ :name="item"
10
+ :value="item"
11
+ v-model="selectedCollections"
12
+ checked />
13
+ <label
14
+ class="ml-2"
15
+ :for="item"
16
+ >{{ item }}</label
17
+ >
18
+ </li>
19
+ </ul>
20
+
21
+ <button
22
+ @click="subscribe()"
23
+ class="ffun-form-button"
24
+ >Subscribe</button
25
+ >
18
26
  </div>
19
27
  </template>
20
28
 
@@ -1,20 +1,11 @@
1
1
  <template>
2
- <div>
3
- <template v-if="feeds === null || feeds.length == 0">
4
- <p>No feeds</p>
5
- </template>
6
-
7
- <template v-else>
8
- <ul style="list-style-type: none; margin: 0; padding: 0">
9
- <li
10
- v-for="feed in feeds"
11
- :key="feed.id"
12
- style="margin-bottom: 0.25rem">
13
- <feed-for-list :feed="feed" />
14
- </li>
15
- </ul>
16
- </template>
17
- </div>
2
+ <ul>
3
+ <li
4
+ v-for="feed in feeds"
5
+ :key="feed.id">
6
+ <feed-for-list :feed="feed" />
7
+ </li>
8
+ </ul>
18
9
  </template>
19
10
 
20
11
  <script lang="ts" setup>
@@ -23,5 +14,3 @@
23
14
 
24
15
  const properties = defineProps<{feeds: Array<t.Feed>}>();
25
16
  </script>
26
-
27
- <style scoped></style>
@@ -3,8 +3,6 @@
3
3
  :class="classes"
4
4
  :title="tooltip"
5
5
  @click.prevent="onClick()">
6
- <slot name="start"> </slot>
7
-
8
6
  <span v-if="countMode == 'prefix'">[{{ count }}]</span>
9
7
 
10
8
  {{ tagInfo.name }}
@@ -14,7 +12,6 @@
14
12
  :href="tagInfo.link"
15
13
  target="_blank"
16
14
  @click.stop=""
17
- style="text-decoration: none"
18
15
  rel="noopener noreferrer">
19
16
  &#8599;
20
17
  </a>
@@ -75,30 +72,26 @@
75
72
 
76
73
  <style scoped>
77
74
  .tag {
78
- display: inline-block;
79
- cursor: pointer;
80
- padding: 0.1rem;
81
- margin-right: 0.2rem;
82
- white-space: nowrap;
75
+ @apply inline-block cursor-pointer p-0 mr-2 whitespace-nowrap;
83
76
  }
84
77
 
85
78
  .tag.selected {
86
- background-color: #ccccff;
79
+ @apply font-bold text-purple-700;
87
80
  }
88
81
 
89
82
  .tag.required {
90
- background-color: #ccffcc;
83
+ @apply text-green-700;
91
84
  }
92
85
 
93
86
  .tag.excluded {
94
- background-color: #ffcccc;
87
+ @apply text-red-700;
95
88
  }
96
89
 
97
90
  .tag.positive {
98
- color: darkgreen;
91
+ @apply text-green-700;
99
92
  }
100
93
 
101
94
  .tag.negative {
102
- color: darkred;
95
+ @apply text-red-700;
103
96
  }
104
97
  </style>
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <div class="ffun-info-attention">
3
+ <p>It looks like you have no news to read.</p>
4
+ <p> Try to subscribe for the feeds collections that we are preparing for you! </p>
5
+ <feeds-collections />
6
+ </div>
7
+ </template>