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
@@ -3,6 +3,7 @@
3
3
  <template #side-menu-item-1>
4
4
  Show descriptions:
5
5
  <config-flag
6
+ style="min-width: 2.5rem"
6
7
  v-model:flag="globalSettings.showFeedsDescriptions"
7
8
  on-text="yes"
8
9
  off-text="no" />
@@ -19,6 +20,7 @@
19
20
  Show failed
20
21
  <config-flag
21
22
  v-model:flag="globalSettings.failedFeedsFirst"
23
+ style="min-width: 2.5rem"
22
24
  on-text="first"
23
25
  off-text="last" />
24
26
  </template>
@@ -28,11 +30,16 @@
28
30
  <span v-if="sortedFeeds"> [{{ sortedFeeds.length }}] </span>
29
31
  </template>
30
32
 
31
- <template #main-footer> </template>
33
+ <notifications
34
+ v-if="sortedFeeds !== null"
35
+ :openai-api-key="false"
36
+ :collections="sortedFeeds === null || sortedFeeds.length == 0" />
32
37
 
33
38
  <feeds-list
34
39
  v-if="sortedFeeds"
35
40
  :feeds="sortedFeeds" />
41
+
42
+ <template #main-footer> </template>
36
43
  </side-panel-layout>
37
44
  </template>
38
45
 
@@ -120,5 +127,3 @@
120
127
  return sorted;
121
128
  });
122
129
  </script>
123
-
124
- <style></style>
@@ -1,96 +1,49 @@
1
1
  <template>
2
2
  <wide-layout>
3
- <template #header> Feeds Fun </template>
4
-
5
- <div v-if="globalState.isLoggedIn">
6
- <p>You have already logged in.</p>
7
- <button @click.prevent="goToWorkspace()">Go To Feeds</button>
8
- </div>
9
-
10
- <div v-else>
11
- <supertokens-login />
3
+ <template #header>
4
+ Feeds Fun
5
+ <small class="block text-lg font-normal">Personalized news for professionals</small>
6
+ </template>
7
+
8
+ <div class="max-w-md mx-auto">
9
+ <div class="ffun-info-good">
10
+ <supertokens-login />
11
+ </div>
12
+
13
+ <h2>News reader with tags</h2>
14
+
15
+ <ul class="list-disc list-inside text-left">
16
+ <li>Reader automatically assigns tags to news.</li>
17
+ <li>You create rules to score news by tags.</li>
18
+ <li>Filter and sort news how you want ⇒ read only what you want.</li>
19
+ </ul>
20
+
21
+ <h2>You are in control</h2>
22
+
23
+ <ul class="list-disc list-inside text-left">
24
+ <li
25
+ ><a
26
+ class="ffun-normal-link"
27
+ :href="settings.githubRepo"
28
+ target="_blank"
29
+ >Open source</a
30
+ >, self-hosted.</li
31
+ >
32
+ <li>No black box recommendation algorithms.</li>
33
+ <li>No "smart" reordering.</li>
34
+ <li>No ads.</li>
35
+ <li>No selling of your data.</li>
36
+ </ul>
37
+
38
+ <h2>How it looks like</h2>
12
39
  </div>
13
40
 
14
- <br />
15
-
16
- <ffun-github-buttons :repository="settings.githubRepo" />
17
-
18
- <h2>What is it?</h2>
19
-
20
- <p>
21
- <i>Web-based news reader. Self-hosted, if it is your way.</i>
22
- </p>
23
-
24
- <ul class="main-page-element">
25
- <li>Reader automatically assigns tags to news entries.</li>
26
- <li>You create rules to score news by tags.</li>
27
- <li>Filter and sort news how you want, to read only what you want.</li>
28
- <li>?????</li>
29
- <li>Profit.</li>
30
- </ul>
31
-
32
- <h2>You are in control</h2>
33
-
34
- <ul class="main-page-element">
35
- <li>No black box recommendation algorithms.</li>
36
- <li>No "smart" reordering of your news.</li>
37
- <li>No ads.</li>
38
- <li>No selling of your data.</li>
39
- </ul>
40
-
41
- <h2>Screenshots</h2>
42
-
43
- <p><i>GUI is still in the early development stage, like the whole project. It will become more pleasurable.</i></p>
44
-
45
41
  <img
46
42
  src="/news-filtering-example.png"
47
43
  alt="News filtering example" />
48
-
49
- <p class="main-page-element"><strong>Explanation</strong></p>
50
-
51
- <ul class="main-page-element">
52
- <li>From the news for the last week, sorted by score.</li>
53
- <li>Show only news about <code>game-development</code> from <code>reddit.com</code>.</li>
54
- <li>Exclude news related to <code>employment</code>.</li>
55
- <li>Hide already read news.</li>
56
- </ul>
57
-
58
- <p class="main-page-element"><strong>Tags sorting for news records</strong></p>
59
-
60
- <ul class="main-page-element">
61
- <li>Tags are sorted by the impact on the score.</li>
62
- <li>Green tags have a positive impact.</li>
63
- <li>Red tags have a negative impact.</li>
64
- </ul>
65
44
  </wide-layout>
66
45
  </template>
67
46
 
68
47
  <script lang="ts" setup>
69
- import {useRouter} from "vue-router";
70
- import {useGlobalSettingsStore} from "@/stores/globalSettings";
71
- import {useSupertokens} from "@/stores/supertokens";
72
- import {useGlobalState} from "@/stores/globalState";
73
- import {computedAsync} from "@vueuse/core";
74
48
  import * as settings from "@/logic/settings";
75
- import * as e from "@/logic/enums";
76
-
77
- const globalSettings = useGlobalSettingsStore();
78
- const globalState = useGlobalState();
79
-
80
- const supertokens = useSupertokens();
81
-
82
- const router = useRouter();
83
-
84
- function goToWorkspace() {
85
- router.push({name: e.MainPanelMode.Entries, params: {}});
86
- }
87
49
  </script>
88
-
89
- <style>
90
- .main-page-element {
91
- text-align: left;
92
- margin-left: auto;
93
- max-width: 27rem;
94
- margin-right: auto;
95
- }
96
- </style>
@@ -1,14 +1,14 @@
1
1
  <template>
2
2
  <side-panel-layout>
3
3
  <template #side-menu-item-1>
4
- For the last
4
+ For
5
5
  <config-selector
6
6
  :values="e.LastEntriesPeriodProperties"
7
7
  v-model:property="globalSettings.lastEntriesPeriod" />
8
8
  </template>
9
9
 
10
10
  <template #side-menu-item-2>
11
- Sorted by
11
+ Sort by
12
12
  <config-selector
13
13
  :values="e.EntriesOrderProperties"
14
14
  v-model:property="globalSettings.entriesOrder" />
@@ -17,17 +17,19 @@
17
17
  <template #side-menu-item-3>
18
18
  Show tags:
19
19
  <config-flag
20
+ style="min-width: 2.5rem"
20
21
  v-model:flag="globalSettings.showEntriesTags"
21
- on-text="yes"
22
- off-text="no" />
22
+ on-text="no"
23
+ off-text="yes" />
23
24
  </template>
24
25
 
25
26
  <template #side-menu-item-4>
26
- Show already read:
27
+ Show read:
27
28
  <config-flag
29
+ style="min-width: 2.5rem"
28
30
  v-model:flag="globalSettings.showRead"
29
- on-text="yes"
30
- off-text="no" />
31
+ on-text="no"
32
+ off-text="yes" />
31
33
  </template>
32
34
 
33
35
  <template #side-footer>
@@ -43,11 +45,10 @@
43
45
 
44
46
  <template #main-footer> </template>
45
47
 
46
- <template v-if="!hasEntries && !entriesStore.firstTimeEntriesLoading">
47
- <p>It looks like you have no news to read.</p>
48
- <p> Try to subscribe for the feeds collections that we are preparing for you! </p>
49
- <feeds-collections />
50
- </template>
48
+ <notifications
49
+ v-if="entriesStore.loadedEntriesReport !== null"
50
+ :openai-api-key="true"
51
+ :collections="!hasEntries" />
51
52
 
52
53
  <entries-list
53
54
  :entriesIds="entriesReport"
@@ -55,7 +56,8 @@
55
56
  :show-tags="globalSettings.showEntriesTags"
56
57
  :tags-count="tagsCount"
57
58
  :showFromStart="25"
58
- :showPerPage="25" />
59
+ :showPerPage="25"
60
+ @entry:bodyVisibilityChanged="onBodyVisibilityChanged" />
59
61
  </side-panel-layout>
60
62
  </template>
61
63
 
@@ -79,11 +81,22 @@
79
81
 
80
82
  globalSettings.updateDataVersion();
81
83
 
84
+ const entriesWithOpenedBody = ref<{[key: t.EntryId]: boolean}>({});
85
+
82
86
  const entriesReport = computed(() => {
87
+ if (entriesStore.loadedEntriesReport === null) {
88
+ return [];
89
+ }
90
+
83
91
  let report = entriesStore.loadedEntriesReport.slice();
84
92
 
85
93
  if (!globalSettings.showRead) {
86
94
  report = report.filter((entryId) => {
95
+ if (entriesWithOpenedBody.value[entryId]) {
96
+ // always show read entries with open body
97
+ // otherwise, they will hide right after opening it
98
+ return true;
99
+ }
87
100
  return !entriesStore.entries[entryId].hasMarker(e.Marker.Read);
88
101
  });
89
102
  }
@@ -167,6 +180,10 @@
167
180
  function onTagStateChanged({tag, state}: {tag: string; state: tagsFilterState.State}) {
168
181
  tagsStates.value.onTagStateChanged({tag, state});
169
182
  }
183
+
184
+ function onBodyVisibilityChanged({entryId, visible}: {entryId: t.EntryId; visible: boolean}) {
185
+ entriesWithOpenedBody.value[entryId] = visible;
186
+ }
170
187
  </script>
171
188
 
172
189
  <style></style>
@@ -122,5 +122,3 @@
122
122
  tagsStates.value.onTagStateChanged({tag, state});
123
123
  }
124
124
  </script>
125
-
126
- <style></style>
@@ -1,53 +1,66 @@
1
1
  <template>
2
2
  <side-panel-layout :reload-button="false">
3
- <h2>Info</h2>
3
+ <template #main-header> Settings </template>
4
4
 
5
5
  <ul>
6
- <li><strong>User id</strong>: {{ userId }}</li>
6
+ <li>
7
+ <strong class="mr-1">User id</strong>
8
+ <input
9
+ class="ffun-input w-72"
10
+ disabled
11
+ :value="userId" />
12
+ </li>
7
13
  </ul>
8
14
 
9
- <h2>Settings</h2>
10
-
11
- <template #main-header> Settings </template>
15
+ <hr />
12
16
 
13
- <user-setting
14
- v-for="(value, kind) of globalSettings.userSettings"
15
- :kind="kind" />
17
+ <template v-for="(value, kind) of globalSettings.userSettings">
18
+ <user-setting :kind="kind" />
19
+ <hr />
20
+ </template>
16
21
 
17
22
  <h2>OpenAI usage</h2>
18
23
 
19
24
  <p>Token usage for your OpenAI key per month.</p>
20
25
 
21
- <ul>
22
- <li> <strong>Used tokens</strong>: the number of tokens in processed requests. </li>
26
+ <ul class="list-disc list-inside">
27
+ <li> <strong>Used tokens</strong> the number of tokens in processed requests. </li>
23
28
  <li>
24
- <strong>Reserved tokens</strong>: the number of tokens reserved for requests that currently are processing or
29
+ <strong>Reserved tokens</strong> the number of tokens reserved for requests that currently are processing or
25
30
  were not processed correctly.
26
31
  </li>
27
32
  <li>
28
- <strong>Total tokens</strong>: the total number of tokens used in the month. Should be not less than the actual
29
- used tokens, but can be bigger because we reserve more tokens than actually use.
33
+ <strong>Total tokens</strong> — the total number of tokens used in the month. Should be not less than the
34
+ actual used tokens, but can be bigger because we reserve more tokens than actually use.
30
35
  </li>
31
36
  </ul>
32
37
 
33
38
  <p v-if="openAIUsage == null">Loading...</p>
34
39
 
35
- <p v-else-if="openAIUsage.length == 0">No usage data</p>
36
-
37
- <table v-else>
38
- <thead>
40
+ <table
41
+ v-else
42
+ class="border border-gray-300 rounded-lg">
43
+ <thead class="bg-slate-200">
39
44
  <tr>
40
- <th style="padding-left: 1rem">Period</th>
41
- <th style="padding-left: 1rem">Used tokens</th>
42
- <th style="padding-left: 1rem">Reserved tokens</th>
43
- <th style="padding-left: 1rem">Total tokens</th>
44
- <th style="padding-left: 1rem">% from current maximum</th>
45
+ <th class="p-2">Period</th>
46
+ <th class="p-2">Used tokens</th>
47
+ <th class="p-2">Reserved tokens</th>
48
+ <th class="p-2">Total tokens</th>
49
+ <th class="p-2">% from current maximum</th>
45
50
  </tr>
46
51
  </thead>
47
52
  <tbody>
48
53
  <openai-tokens-usage
49
54
  :usage="usage"
50
55
  v-for="usage of openAIUsage" />
56
+
57
+ <tr v-if="openAIUsage.length == 0">
58
+ <td class="text-center">—</td>
59
+ <td class="text-center">—</td>
60
+ <td class="text-center">—</td>
61
+ <td class="text-center">—</td>
62
+ <td class="text-center">—</td>
63
+ </tr>
51
64
  </tbody>
52
65
  </table>
53
66
  </side-panel-layout>
@@ -0,0 +1,14 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: [
4
+ "./index.html",
5
+ "./src/*.{vue,js,ts,jsx,tsx}",
6
+ "./src/**/*.{vue,js,ts,jsx,tsx}",
7
+ ],
8
+ theme: {
9
+ extend: {},
10
+ },
11
+ plugins: [
12
+ require('@tailwindcss/typography'),
13
+ ],
14
+ }
@@ -1,23 +0,0 @@
1
- <template>
2
- <div>
3
- <a
4
- :href="entry.url"
5
- target="_blank"
6
- rel="noopener noreferrer">
7
- {{ entry.title }}
8
- </a>
9
- </div>
10
- </template>
11
-
12
- <script lang="ts" setup>
13
- import {computed, ref} from "vue";
14
- import type * as t from "@/logic/types";
15
- import * as e from "@/logic/enums";
16
- import * as api from "@/logic/api";
17
- import {computedAsync} from "@vueuse/core";
18
- import {useEntriesStore} from "@/stores/entries";
19
-
20
- const props = defineProps<{entry: t.EntryInfo}>();
21
- </script>
22
-
23
- <style scoped></style>
@@ -1,22 +0,0 @@
1
- <template>
2
- <div>
3
- <github-button
4
- :href="repository"
5
- data-icon="octicon-star"
6
- >Star</github-button
7
- >
8
- &nbsp;
9
- <github-button
10
- :href="repository + '/discussions'"
11
- data-show-count="true"
12
- data-icon="octicon-comment-discussion"
13
- >Discuss</github-button
14
- >
15
- </div>
16
- </template>
17
-
18
- <script lang="ts" setup>
19
- const props = defineProps<{repository: string}>();
20
- </script>
21
-
22
- <style></style>