feeds-fun 1.16.2 → 1.16.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feeds-fun",
3
- "version": "1.16.2",
3
+ "version": "1.16.4",
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": [
@@ -2,7 +2,30 @@
2
2
  <div
3
3
  ref="entryTop"
4
4
  class="flex text-lg">
5
- <div class="flex-shrink-0 w-8 text-right pr-1">
5
+ <div :class="['flex-shrink-0', 'text-right', {'ml-8': isRead}]">
6
+ <input-marker
7
+ class="w-7 mr-2"
8
+ :marker="e.Marker.Read"
9
+ :entry-id="entryId">
10
+ <template v-slot:marked>
11
+ <span
12
+ class="text-green-700 no-underline"
13
+ title="Mark as unread">
14
+ <i class="ti ti-chevrons-left" />
15
+ </span>
16
+ </template>
17
+
18
+ <template v-slot:unmarked>
19
+ <span
20
+ class="text-orange-700 no-underline"
21
+ title="Mark as read">
22
+ <i class="ti ti-chevrons-right" />
23
+ </span>
24
+ </template>
25
+ </input-marker>
26
+ </div>
27
+
28
+ <div class="flex-shrink-0 w-8 text-center pr-1">
6
29
  <value-score
7
30
  :value="entry.score"
8
31
  :entry-id="entry.id" />
@@ -14,15 +37,6 @@
14
37
  class="w-5 h-5 align-text-bottom mx-1 inline" />
15
38
  </div>
16
39
 
17
- <div class="flex-shrink-0 text-right">
18
- <input-marker
19
- class="w-7 mr-2"
20
- :marker="e.Marker.Read"
21
- :entry-id="entryId"
22
- on-text="read"
23
- off-text="new" />
24
- </div>
25
-
26
40
  <div class="flex-grow">
27
41
  <a
28
42
  :href="entry.url"
@@ -73,7 +87,7 @@
73
87
 
74
88
  <script lang="ts" setup>
75
89
  import _ from "lodash";
76
- import {computed, ref, useTemplateRef} from "vue";
90
+ import {computed, ref, useTemplateRef, onMounted} from "vue";
77
91
  import type * as t from "@/logic/types";
78
92
  import * as events from "@/logic/events";
79
93
  import * as e from "@/logic/enums";
@@ -156,11 +170,25 @@
156
170
  await entriesStore.displayEntry({entryId: entry.value.id});
157
171
 
158
172
  if (topElement.value) {
159
- topElement.value.scrollIntoView({behavior: "instant"});
173
+ const rect = topElement.value.getBoundingClientRect();
174
+
175
+ const isVisible =
176
+ rect.top >= 0 &&
177
+ rect.left >= 0 &&
178
+ rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
179
+ rect.right <= (window.innerWidth || document.documentElement.clientWidth);
180
+
181
+ if (!isVisible) {
182
+ topElement.value.scrollIntoView({behavior: "instant"});
183
+ }
160
184
  }
161
185
  }
162
186
  } else {
163
187
  await newsLinkOpenedEvent();
164
188
  }
165
189
  }
190
+
191
+ onMounted(() => {
192
+ entriesStore.requestFullEntry({entryId: properties.entryId});
193
+ });
166
194
  </script>
@@ -3,19 +3,17 @@
3
3
  <template v-if="hasMarker">
4
4
  <a
5
5
  href="#"
6
- class="marked"
7
- @click.prevent="unmark()"
8
- >{{ onText }}</a
9
- >
6
+ @click.prevent="unmark()">
7
+ <slot name="marked" />
8
+ </a>
10
9
  </template>
11
10
 
12
11
  <template v-else>
13
12
  <a
14
13
  href="#"
15
- class="unmarked"
16
- @click.prevent="mark()"
17
- >{{ offText }}</a
18
- >
14
+ @click.prevent="mark()">
15
+ <slot name="unmarked" />
16
+ </a>
19
17
  </template>
20
18
  </div>
21
19
  </template>
@@ -23,7 +21,7 @@
23
21
  <script lang="ts" setup>
24
22
  import {computed, ref} from "vue";
25
23
  import * as api from "@/logic/api";
26
- import type * as e from "@/logic/enums";
24
+ import * as e from "@/logic/enums";
27
25
  import type * as t from "@/logic/types";
28
26
  import {useEntriesStore} from "@/stores/entries";
29
27
 
@@ -32,8 +30,6 @@
32
30
  const properties = defineProps<{
33
31
  marker: e.Marker;
34
32
  entryId: t.EntryId;
35
- onText: string;
36
- offText: string;
37
33
  }>();
38
34
 
39
35
  const hasMarker = computed(() => {
@@ -55,12 +51,4 @@
55
51
  }
56
52
  </script>
57
53
 
58
- <style scoped>
59
- .marked {
60
- @apply text-green-700 no-underline;
61
- }
62
-
63
- .unmarked {
64
- @apply text-orange-700 font-bold no-underline;
65
- }
66
- </style>
54
+ <style scoped></style>
@@ -16,6 +16,9 @@ export const useEntriesStore = defineStore("entriesStore", () => {
16
16
  const entries = ref<{[key: t.EntryId]: t.Entry}>({});
17
17
  const requestedEntries = ref<{[key: t.EntryId]: boolean}>({});
18
18
  const displayedEntryId = ref<t.EntryId | null>(null);
19
+ const readHistory = ref<t.EntryId[]>([]);
20
+
21
+ const canUndoMarkRead = computed(() => readHistory.value.length > 0);
19
22
 
20
23
  function registerEntry(entry: t.Entry) {
21
24
  if (entry.id in entries.value) {
@@ -82,19 +85,33 @@ export const useEntriesStore = defineStore("entriesStore", () => {
82
85
  requestedEntriesTimer.start();
83
86
 
84
87
  async function setMarker({entryId, marker}: {entryId: t.EntryId; marker: e.Marker}) {
85
- await api.setMarker({entryId: entryId, marker: marker});
88
+ if (marker === e.Marker.Read) {
89
+ readHistory.value.push(entryId);
90
+ }
86
91
 
92
+ // This code must be before the actual API request
93
+ // to guarantee smooth UI transition to the new state
94
+ // otherwise the UI will be updated two times which leads to flickering
87
95
  if (entryId in entries.value) {
88
96
  entries.value[entryId].setMarker(marker);
89
97
  }
98
+
99
+ await api.setMarker({entryId: entryId, marker: marker});
90
100
  }
91
101
 
92
102
  async function removeMarker({entryId, marker}: {entryId: t.EntryId; marker: e.Marker}) {
93
- await api.removeMarker({entryId: entryId, marker: marker});
103
+ if (marker === e.Marker.Read) {
104
+ _.pull(readHistory.value, entryId);
105
+
106
+ hideEntry({entryId: entryId});
107
+ }
94
108
 
109
+ // This code must be before the actual API request, see comment above
95
110
  if (entryId in entries.value) {
96
111
  entries.value[entryId].removeMarker(marker);
97
112
  }
113
+
114
+ await api.removeMarker({entryId: entryId, marker: marker});
98
115
  }
99
116
 
100
117
  async function displayEntry({entryId}: {entryId: t.EntryId}) {
@@ -118,6 +135,16 @@ export const useEntriesStore = defineStore("entriesStore", () => {
118
135
  }
119
136
  }
120
137
 
138
+ function undoMarkRead() {
139
+ if (readHistory.value.length === 0) {
140
+ return;
141
+ }
142
+
143
+ const entryId = readHistory.value.pop() as t.EntryId;
144
+
145
+ removeMarker({entryId: entryId, marker: e.Marker.Read});
146
+ }
147
+
121
148
  return {
122
149
  entries,
123
150
  requestFullEntry,
@@ -126,6 +153,8 @@ export const useEntriesStore = defineStore("entriesStore", () => {
126
153
  loadedEntriesReport,
127
154
  displayedEntryId,
128
155
  displayEntry,
129
- hideEntry
156
+ hideEntry,
157
+ undoMarkRead,
158
+ canUndoMarkRead
130
159
  };
131
160
  });
package/src/style.css CHANGED
@@ -81,7 +81,7 @@
81
81
  }
82
82
 
83
83
  .ffun-form-button {
84
- @apply ffun-input-common border-blue-300 disabled:bg-blue-700/75 py-1;
84
+ @apply ffun-input-common border-blue-300 disabled:bg-blue-100/25 py-1;
85
85
  }
86
86
 
87
87
  .ffun-file-button {
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <div
3
- class="inline-block cursor-pointer font-semibold text-purple-700"
3
+ title="Click to see score details"
4
+ class="inline-block cursor-pointer text-purple-700"
4
5
  @click.prevent="onClick()">
5
6
  {{ value }}
6
7
  </div>
@@ -8,7 +8,7 @@
8
8
  :collections-notification_="false"
9
9
  :collections-warning_="true" />
10
10
 
11
- <h2>Lood feeds from an OPML file</h2>
11
+ <h2>Load feeds from an OPML file</h2>
12
12
 
13
13
  <div class="ffun-info-good">
14
14
  <p>
@@ -15,12 +15,21 @@
15
15
  </template>
16
16
 
17
17
  <template #side-menu-item-3>
18
- Show read:
18
+ Show read
19
+
19
20
  <config-flag
20
21
  style="min-width: 2.5rem"
21
22
  v-model:flag="globalSettings.showRead"
22
23
  on-text="no"
23
24
  off-text="yes" />
25
+
26
+ <button
27
+ class="ffun-form-button py-0 ml-1"
28
+ title='Undo last "mark read" operation'
29
+ :disabled="!entriesStore.canUndoMarkRead"
30
+ @click="entriesStore.undoMarkRead()">
31
+
32
+ </button>
24
33
  </template>
25
34
 
26
35
  <template #side-footer>