feeds-fun 1.13.1 → 1.13.2

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.13.1",
3
+ "version": "1.13.2",
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": [
@@ -33,38 +33,39 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@chenfengyuan/vue-countdown": "^2.1.2",
36
- "@vueuse/core": "^9.13.0",
37
- "axios": "^1.5.1",
38
- "dompurify": "^3.0.6",
39
- "pinia": "^2.1.7",
36
+ "@vueuse/core": "^11.2.0",
37
+ "axios": "^1.7.7",
38
+ "dompurify": "^3.1.7",
39
+ "pinia": "^2.2.6",
40
40
  "set-interval-async": "^3.0.3",
41
41
  "supertokens-web-js": "^0.5.0",
42
- "vue": "^3.2.47",
43
- "vue-router": "^4.2.5"
42
+ "vue": "^3.5.12",
43
+ "vue-router": "^4.4.5",
44
+ "lodash": "^4.17.21"
44
45
  },
45
46
  "devDependencies": {
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",
53
- "@vue/eslint-config-prettier": "^7.1.0",
54
- "@vue/eslint-config-typescript": "^11.0.3",
55
- "@vue/test-utils": "^2.4.1",
56
- "@vue/tsconfig": "^0.1.3",
57
- "autoprefixer": "^10.4.16",
58
- "eslint": "^8.51.0",
59
- "eslint-plugin-vue": "^9.17.0",
60
- "jsdom": "^21.1.2",
47
+ "@rushstack/eslint-patch": "^1.10.4",
48
+ "@tailwindcss/typography": "^0.5.15",
49
+ "@types/dompurify": "^3.0.5",
50
+ "@types/jsdom": "^21.1.7",
51
+ "@types/lodash": "^4.17.13",
52
+ "@types/node": "^22.9.0",
53
+ "@vitejs/plugin-vue": "^5.1.4",
54
+ "@vue/eslint-config-prettier": "^10.1.0",
55
+ "@vue/eslint-config-typescript": "^14.1.3",
56
+ "@vue/test-utils": "^2.4.6",
57
+ "@vue/tsconfig": "^0.5.1",
58
+ "autoprefixer": "^10.4.20",
59
+ "eslint": "^9.14.0",
60
+ "eslint-plugin-vue": "^9.30.0",
61
+ "jsdom": "^25.0.1",
61
62
  "npm-run-all": "^4.1.5",
62
- "postcss": "^8.4.31",
63
- "prettier": "^2.8.8",
64
- "tailwindcss": "^3.3.3",
65
- "typescript": "~4.8.4",
66
- "vite": "^4.4.11",
67
- "vitest": "^0.29.8",
68
- "vue-tsc": "^1.8.19"
63
+ "postcss": "^8.4.47",
64
+ "prettier": "^3.3.3",
65
+ "tailwindcss": "^3.4.14",
66
+ "typescript": "~5.6.3",
67
+ "vite": "^5.4.10",
68
+ "vitest": "^2.1.4",
69
+ "vue-tsc": "^2.1.10"
69
70
  }
70
71
  }
@@ -55,6 +55,7 @@
55
55
  ><a
56
56
  :href="entry.url"
57
57
  target="_blank"
58
+ @click="newsLinkOpenedEvent"
58
59
  >{{ purifiedTitle }}</a
59
60
  ></h2
60
61
  >
@@ -71,6 +72,7 @@
71
72
  import _ from "lodash";
72
73
  import {computed, ref} from "vue";
73
74
  import type * as t from "@/logic/types";
75
+ import * as events from "@/logic/events";
74
76
  import * as e from "@/logic/enums";
75
77
  import {computedAsync} from "@vueuse/core";
76
78
  import DOMPurify from "dompurify";
@@ -109,7 +111,7 @@
109
111
  return _.get(entry.value, properties.timeField, null);
110
112
  });
111
113
 
112
- function displayBody() {
114
+ async function displayBody() {
113
115
  showBody.value = true;
114
116
 
115
117
  emit("entry:bodyVisibilityChanged", {entryId: properties.entryId, visible: true});
@@ -119,6 +121,8 @@
119
121
  }
120
122
 
121
123
  entriesStore.requestFullEntry({entryId: entry.value.id});
124
+
125
+ await events.newsBodyOpened({entryId: entry.value.id});
122
126
  }
123
127
 
124
128
  function hideBody() {
@@ -152,6 +156,10 @@
152
156
  return DOMPurify.sanitize(entry.value.body);
153
157
  });
154
158
 
159
+ async function newsLinkOpenedEvent() {
160
+ await events.newsLinkOpened({entryId: entry.value.id});
161
+ }
162
+
155
163
  async function onTitleClick(event: MouseEvent) {
156
164
  if (!event.ctrlKey) {
157
165
  event.preventDefault();
@@ -162,6 +170,8 @@
162
170
  } else {
163
171
  displayBody();
164
172
  }
173
+ } else {
174
+ await newsLinkOpenedEvent();
165
175
  }
166
176
 
167
177
  // TODO: is it will be too slow?
@@ -72,6 +72,7 @@
72
72
  target="_blank"
73
73
  class="ffun-header-link"
74
74
  style="text-decoration: none"
75
+ @click="events.socialLinkClicked({linkType: 'api'})"
75
76
  >API</a
76
77
  >
77
78
  </li>
@@ -82,6 +83,7 @@
82
83
  target="_blank"
83
84
  class="ffun-header-link"
84
85
  style="text-decoration: none"
86
+ @click="events.socialLinkClicked({linkType: 'blog'})"
85
87
  >Blog</a
86
88
  >
87
89
  </li>
@@ -93,6 +95,7 @@
93
95
  class="ffun-header-link text-xl align-middle"
94
96
  title="Reddit"
95
97
  style="text-decoration: none"
98
+ @click="events.socialLinkClicked({linkType: 'reddit'})"
96
99
  ><i class="ti ti-brand-reddit"></i
97
100
  ></a>
98
101
  </li>
@@ -104,6 +107,7 @@
104
107
  class="ffun-header-link text-xl align-middle"
105
108
  title="Discord"
106
109
  style="text-decoration: none"
110
+ @click="events.socialLinkClicked({linkType: 'discord'})"
107
111
  ><i class="ti ti-brand-discord"></i
108
112
  ></a>
109
113
  </li>
@@ -114,7 +118,8 @@
114
118
  target="_blank"
115
119
  class="ffun-header-link text-xl align-middle"
116
120
  title="GitHub"
117
- style="text-decoration: none">
121
+ style="text-decoration: none"
122
+ @click="events.socialLinkClicked({linkType: 'github'})">
118
123
  <i class="ti ti-brand-github"></i
119
124
  ></a>
120
125
  </li>
@@ -150,6 +155,7 @@
150
155
  import {useGlobalSettingsStore} from "@/stores/globalSettings";
151
156
  import {useGlobalState} from "@/stores/globalState";
152
157
  import {useSupertokens} from "@/stores/supertokens";
158
+ import * as events from "@/logic/events";
153
159
  import * as e from "@/logic/enums";
154
160
  import * as settings from "@/logic/settings";
155
161
 
package/src/logic/api.ts CHANGED
@@ -28,6 +28,7 @@ const API_GET_USER_SETTINGS = `${ENTRY_POINT}/get-user-settings`;
28
28
  const API_SET_USER_SETTING = `${ENTRY_POINT}/set-user-setting`;
29
29
  const API_GET_RESOURCE_HISTORY = `${ENTRY_POINT}/get-resource-history`;
30
30
  const API_GET_INFO = `${ENTRY_POINT}/get-info`;
31
+ const API_TRACK_EVENT = `${ENTRY_POINT}/track-event`;
31
32
 
32
33
  let _onSessionLost: () => void = () => {};
33
34
 
@@ -176,7 +177,9 @@ export async function discoverFeeds({url}: {url: string}) {
176
177
  }
177
178
 
178
179
  export async function addFeed({url}: {url: string}) {
179
- await post({url: API_ADD_FEED, data: {url: url}});
180
+ const response = await post({url: API_ADD_FEED, data: {url: url}});
181
+
182
+ return t.feedFromJSON(response.feed);
180
183
  }
181
184
 
182
185
  export async function addOPML({content}: {content: string}) {
@@ -275,3 +278,7 @@ export async function getInfo() {
275
278
 
276
279
  return response;
277
280
  }
281
+
282
+ export async function trackEvent(data: {[key: string]: string | number | null}) {
283
+ await post({url: API_TRACK_EVENT, data: {event: data}});
284
+ }
@@ -0,0 +1,14 @@
1
+ import * as api from "@/logic/api";
2
+ import type * as t from "@/logic/types";
3
+
4
+ export async function newsLinkOpened({entryId}: {entryId: t.EntryId}) {
5
+ await api.trackEvent({name: "news_link_opened", entry_id: entryId});
6
+ }
7
+
8
+ export async function newsBodyOpened({entryId}: {entryId: t.EntryId}) {
9
+ await api.trackEvent({name: "news_body_opened", entry_id: entryId});
10
+ }
11
+
12
+ export async function socialLinkClicked({linkType}: {linkType: string}) {
13
+ await api.trackEvent({name: "social_link_clicked", link_type: linkType});
14
+ }
@@ -1,4 +1,4 @@
1
- import {computed, ref, watch} from "vue";
1
+ import {computed, ref, watch, triggerRef} from "vue";
2
2
  import {useRouter} from "vue-router";
3
3
  import {defineStore} from "pinia";
4
4
 
@@ -30,19 +30,25 @@ export const useFeedsStore = defineStore("feedsStore", () => {
30
30
  async function unsubscribe(feedId: t.FeedId) {
31
31
  await api.unsubscribe({feedId: feedId});
32
32
 
33
- // Attention, do not call globalSettings.updateDataVersion
33
+ // Attention, do not update globalSettings.updateDataVersion here
34
34
  // it cause a lot of unnecessary requests to the server without any benefit
35
35
  // we just remove feed from frontend
36
36
 
37
37
  delete feeds.value[feedId];
38
+
39
+ triggerRef(feeds);
38
40
  }
39
41
 
40
42
  async function subscribe(url: t.URL) {
41
- await api.addFeed({
43
+ const newFeed = await api.addFeed({
42
44
  url: url
43
45
  });
44
46
 
45
- globalSettings.updateDataVersion();
47
+ // Attention, do not update globalSettings.updateDataVersion here (see above)
48
+
49
+ feeds.value[newFeed.id] = newFeed;
50
+
51
+ triggerRef(feeds);
46
52
  }
47
53
 
48
54
  return {
package/tsconfig.app.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "@vue/tsconfig/tsconfig.web.json",
2
+ "extends": "@vue/tsconfig/tsconfig.dom.json",
3
3
  "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
4
4
  "exclude": ["src/**/__tests__/*"],
5
5
  "compilerOptions": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "@vue/tsconfig/tsconfig.node.json",
2
+ "extends": "@vue/tsconfig/tsconfig.dom.json",
3
3
  "include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "playwright.config.*"],
4
4
  "compilerOptions": {
5
5
  "composite": true,