feeds-fun 1.21.5 → 1.22.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 -2
- package/src/App.vue +2 -2
- package/src/components/EntryForList.vue +3 -3
- package/src/components/page_footer/Footer.vue +4 -1
- package/src/components/page_header/ExternalLinks.vue +3 -4
- package/src/components/side_pannel/CollapseButton.vue +0 -1
- package/src/components/tags/FilterTag.vue +4 -4
- package/src/css/inputs.css +21 -0
- package/src/layouts/SidePanelLayout.vue +19 -28
- package/src/logic/api.ts +265 -158
- package/src/logic/enums.ts +10 -0
- package/src/logic/events.ts +20 -12
- package/src/logic/settings.ts +4 -23
- package/src/logic/types.ts +26 -0
- package/src/main.ts +1 -54
- package/src/router/index.ts +0 -6
- package/src/stores/entries.ts +4 -4
- package/src/stores/globalSettings.ts +3 -11
- package/src/stores/globalState.ts +107 -9
- package/src/values/SocialLink.vue +8 -4
- package/src/views/CRMView.vue +1 -1
- package/src/views/MainView.vue +51 -15
- package/src/views/PublicCollectionView.vue +8 -3
- package/src/views/SettingsView.vue +5 -6
- package/vite.config.ts +0 -13
- package/src/components/SupertokensLogin.vue +0 -141
- package/src/stores/supertokens.ts +0 -144
- package/src/views/AuthView.vue +0 -77
package/src/logic/api.ts
CHANGED
|
@@ -4,92 +4,112 @@ import * as t from "@/logic/types";
|
|
|
4
4
|
import type * as e from "@/logic/enums";
|
|
5
5
|
import * as settings from "@/logic/settings";
|
|
6
6
|
import * as cookieConsent from "@/plugins/CookieConsent";
|
|
7
|
+
import {useGlobalState} from "@/stores/globalState";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const API_CREATE_OR_UPDATE_RULE = `${ENTRY_POINT}/create-or-update-rule`;
|
|
15
|
-
|
|
16
|
-
const API_DELETE_RULE = `${ENTRY_POINT}/delete-rule`;
|
|
17
|
-
const API_UPDATE_RULE = `${ENTRY_POINT}/update-rule`;
|
|
18
|
-
const API_GET_RULES = `${ENTRY_POINT}/get-rules`;
|
|
19
|
-
const API_GET_SCORE_DETAILS = `${ENTRY_POINT}/get-score-details`;
|
|
20
|
-
const API_SET_MARKER = `${ENTRY_POINT}/set-marker`;
|
|
21
|
-
const API_REMOVE_MARKER = `${ENTRY_POINT}/remove-marker`;
|
|
22
|
-
const API_DISCOVER_FEEDS = `${ENTRY_POINT}/discover-feeds`;
|
|
23
|
-
const API_ADD_FEED = `${ENTRY_POINT}/add-feed`;
|
|
24
|
-
const API_ADD_OPML = `${ENTRY_POINT}/add-opml`;
|
|
25
|
-
const API_UNSUBSCRIBE = `${ENTRY_POINT}/unsubscribe`;
|
|
26
|
-
const API_GET_COLLECTIONS = `${ENTRY_POINT}/get-collections`;
|
|
27
|
-
const API_GET_COLLECTION_FEEDS = `${ENTRY_POINT}/get-collection-feeds`;
|
|
28
|
-
const API_SUBSCRIBE_TO_COLLECTIONS = `${ENTRY_POINT}/subscribe-to-collections`;
|
|
29
|
-
const API_GET_TAGS_INFO = `${ENTRY_POINT}/get-tags-info`;
|
|
30
|
-
const API_GET_USER_SETTINGS = `${ENTRY_POINT}/get-user-settings`;
|
|
31
|
-
const API_SET_USER_SETTING = `${ENTRY_POINT}/set-user-setting`;
|
|
32
|
-
const API_GET_RESOURCE_HISTORY = `${ENTRY_POINT}/get-resource-history`;
|
|
33
|
-
const API_GET_INFO = `${ENTRY_POINT}/get-info`;
|
|
34
|
-
const API_TRACK_EVENT = `${ENTRY_POINT}/track-event`;
|
|
35
|
-
const API_REMOVE_USER = `${ENTRY_POINT}/remove-user`;
|
|
36
|
-
|
|
37
|
-
let _onSessionLost: () => void = () => {};
|
|
38
|
-
|
|
39
|
-
export function init({onSessionLost}: {onSessionLost: () => void}) {
|
|
40
|
-
_onSessionLost = onSessionLost;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function post({url, data}: {url: string; data: any}) {
|
|
44
|
-
try {
|
|
45
|
-
const response = await axios.post(url, data);
|
|
46
|
-
return response.data;
|
|
47
|
-
} catch (error) {
|
|
48
|
-
console.log(error);
|
|
49
|
-
|
|
50
|
-
if (error instanceof Error && "response" in error) {
|
|
51
|
-
const axiosError = error as AxiosError;
|
|
52
|
-
if (axiosError.response && axiosError.response.status === 401) {
|
|
53
|
-
await _onSessionLost();
|
|
54
|
-
}
|
|
55
|
-
}
|
|
9
|
+
///////////////
|
|
10
|
+
// API handlers
|
|
11
|
+
///////////////
|
|
12
|
+
|
|
13
|
+
const publicEntryPoint = "/spa/api/public";
|
|
14
|
+
const privateEntryPoint = "/spa/api/private";
|
|
56
15
|
|
|
57
|
-
|
|
16
|
+
const apiPublic = axios.create({baseURL: publicEntryPoint, withCredentials: true});
|
|
17
|
+
const apiPrivate = axios.create({baseURL: privateEntryPoint, withCredentials: true});
|
|
18
|
+
|
|
19
|
+
// It is an open question what should we do in case of session expiration:
|
|
20
|
+
// - redirect to login page
|
|
21
|
+
// - redirect to home page & show notification
|
|
22
|
+
// Currently the easiest way to handle it is to always redirect to login page.
|
|
23
|
+
export function redirectToLogin(returnTo?: string) {
|
|
24
|
+
if (!returnTo) {
|
|
25
|
+
returnTo = window.location.pathname + window.location.search;
|
|
58
26
|
}
|
|
27
|
+
window.location.assign(`/spa/auth/login?return_to=${encodeURIComponent(returnTo)}`);
|
|
59
28
|
}
|
|
60
29
|
|
|
61
|
-
export
|
|
62
|
-
|
|
30
|
+
export function redirectToJoin(returnTo?: string) {
|
|
31
|
+
if (!returnTo) {
|
|
32
|
+
returnTo = window.location.pathname + window.location.search;
|
|
33
|
+
}
|
|
34
|
+
window.location.assign(`/spa/auth/join?return_to=${encodeURIComponent(returnTo)}`);
|
|
35
|
+
}
|
|
63
36
|
|
|
64
|
-
|
|
37
|
+
export function logoutRedirect() {
|
|
38
|
+
window.location.assign("/spa/auth/logout");
|
|
39
|
+
}
|
|
65
40
|
|
|
66
|
-
|
|
67
|
-
const feed = t.feedFromJSON(rawFeed);
|
|
68
|
-
feeds.push(feed);
|
|
69
|
-
}
|
|
41
|
+
let _refreshingAuth: Promise<void> | null = null;
|
|
70
42
|
|
|
71
|
-
|
|
43
|
+
enum Ffun401Behaviour {
|
|
44
|
+
RedirectToLogin = "redirectToLogin",
|
|
45
|
+
DoNotRetry = "doNotRetry",
|
|
46
|
+
ReturnNull = "returnNull"
|
|
72
47
|
}
|
|
73
48
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
49
|
+
// We try to refresh auth on 401 responses for private API.
|
|
50
|
+
// For the public API we do nothing, because it most likely means infrastructure issue.
|
|
51
|
+
apiPrivate.interceptors.response.use(
|
|
52
|
+
(r) => r,
|
|
53
|
+
async (error) => {
|
|
54
|
+
const {config, response} = error;
|
|
55
|
+
|
|
56
|
+
if (!response) {
|
|
57
|
+
throw error;
|
|
80
58
|
}
|
|
81
|
-
});
|
|
82
59
|
|
|
83
|
-
|
|
60
|
+
if (response.status !== 401) {
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
84
63
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
64
|
+
if (config?.ffunRequestRetried) {
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (config?.ffun401Behaviour === Ffun401Behaviour.RedirectToLogin) {
|
|
69
|
+
redirectToLogin();
|
|
70
|
+
return; // never reached
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (config?.ffun401Behaviour === Ffun401Behaviour.DoNotRetry) {
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (config?.ffun401Behaviour === Ffun401Behaviour.ReturnNull) {
|
|
78
|
+
return {data: null};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
(config as any).ffunRequestRetried = true;
|
|
82
|
+
|
|
83
|
+
if (!_refreshingAuth) {
|
|
84
|
+
_refreshingAuth = apiPrivate
|
|
85
|
+
// @ts-ignore
|
|
86
|
+
.post("/refresh-auth", undefined, {ffun401Behaviour: Ffun401Behaviour.RedirectToLogin})
|
|
87
|
+
.then(() => {})
|
|
88
|
+
.finally(() => {
|
|
89
|
+
_refreshingAuth = null;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
await _refreshingAuth; // all 401s await the same refresh
|
|
94
|
+
|
|
95
|
+
return await apiPrivate(config); // retry the original request generically
|
|
88
96
|
}
|
|
97
|
+
);
|
|
89
98
|
|
|
90
|
-
|
|
99
|
+
async function postPublic({url, data}: {url: string; data: any}) {
|
|
100
|
+
const response = await apiPublic.post(url, data);
|
|
101
|
+
return response.data;
|
|
91
102
|
}
|
|
92
103
|
|
|
104
|
+
async function postPrivate({url, data, config}: {url: string; data: any; config?: any}) {
|
|
105
|
+
const response = await apiPrivate.post(url, data, config);
|
|
106
|
+
return response.data;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/////////////
|
|
110
|
+
// Public API
|
|
111
|
+
/////////////
|
|
112
|
+
|
|
93
113
|
export async function getLastCollectionEntries({
|
|
94
114
|
period,
|
|
95
115
|
collectionSlug,
|
|
@@ -99,8 +119,8 @@ export async function getLastCollectionEntries({
|
|
|
99
119
|
collectionSlug: t.CollectionSlug | null;
|
|
100
120
|
minTagCount: number;
|
|
101
121
|
}) {
|
|
102
|
-
const response = await
|
|
103
|
-
url:
|
|
122
|
+
const response = await postPublic({
|
|
123
|
+
url: "/get-last-collection-entries",
|
|
104
124
|
data: {period: period, collectionSlug: collectionSlug, minTagCount: minTagCount}
|
|
105
125
|
});
|
|
106
126
|
|
|
@@ -115,9 +135,157 @@ export async function getLastCollectionEntries({
|
|
|
115
135
|
}
|
|
116
136
|
|
|
117
137
|
export async function getEntriesByIds({ids}: {ids: t.EntryId[]}) {
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
138
|
+
const globalState = useGlobalState();
|
|
139
|
+
|
|
140
|
+
let response = null;
|
|
141
|
+
|
|
142
|
+
if (globalState.loginConfirmed) {
|
|
143
|
+
response = await postPrivate({
|
|
144
|
+
url: "/get-entries-by-ids",
|
|
145
|
+
data: {ids: ids},
|
|
146
|
+
config: {ffun401Behaviour: Ffun401Behaviour.ReturnNull}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!response) {
|
|
151
|
+
response = await postPublic({
|
|
152
|
+
url: "/get-entries-by-ids",
|
|
153
|
+
data: {ids: ids}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const entries = [];
|
|
158
|
+
|
|
159
|
+
for (let rawEntry of response.entries) {
|
|
160
|
+
const entry = t.entryFromJSON(rawEntry, response.tagsMapping);
|
|
161
|
+
entries.push(entry);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return entries;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export async function getCollections() {
|
|
168
|
+
const response = await postPublic({url: "/get-collections", data: {}});
|
|
169
|
+
|
|
170
|
+
const collections = [];
|
|
171
|
+
|
|
172
|
+
for (let rawCollection of response.collections) {
|
|
173
|
+
const collection = t.collectionFromJSON(rawCollection);
|
|
174
|
+
collections.push(collection);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return collections;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function getCollectionFeeds({collectionId}: {collectionId: t.CollectionId}) {
|
|
181
|
+
const response = await postPublic({
|
|
182
|
+
url: "/get-collection-feeds",
|
|
183
|
+
data: {collectionId: collectionId}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const feeds = [];
|
|
187
|
+
|
|
188
|
+
for (let rawFeed of response.feeds) {
|
|
189
|
+
const feed = t.collectionFeedInfoFromJSON(rawFeed);
|
|
190
|
+
feeds.push(feed);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return feeds;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export async function getTagsInfo({uids}: {uids: string[]}) {
|
|
197
|
+
const response = await postPublic({url: "/get-tags-info", data: {uids: uids}});
|
|
198
|
+
|
|
199
|
+
const tags: {[key: string]: t.TagInfo} = {};
|
|
200
|
+
|
|
201
|
+
for (let uid in response.tags) {
|
|
202
|
+
const rawTag = response.tags[uid];
|
|
203
|
+
const tag = t.tagInfoFromJSON(rawTag);
|
|
204
|
+
tags[uid] = tag;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return tags;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export async function getInfo() {
|
|
211
|
+
const response = await postPublic({url: "/get-info", data: {}});
|
|
212
|
+
|
|
213
|
+
return t.stateInfoFromJSON(response);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export async function getUser() {
|
|
217
|
+
const response = await postPrivate({
|
|
218
|
+
url: "/get-user",
|
|
219
|
+
data: {},
|
|
220
|
+
config: {ffun401Behaviour: Ffun401Behaviour.ReturnNull}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (!response) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return t.userInfoFromJSON(response);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function trackEvent(data: {[key: string]: string | number | null}) {
|
|
231
|
+
if (!settings.trackEvents) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!cookieConsent.isAnalyticsAllowed()) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const globalState = useGlobalState();
|
|
240
|
+
|
|
241
|
+
let url: string;
|
|
242
|
+
|
|
243
|
+
if (globalState.loginConfirmed) {
|
|
244
|
+
url = privateEntryPoint + "/track-event";
|
|
245
|
+
} else {
|
|
246
|
+
url = publicEntryPoint + "/track-event";
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
let payload = JSON.stringify({event: data});
|
|
250
|
+
|
|
251
|
+
if ("sendBeacon" in navigator) {
|
|
252
|
+
return navigator.sendBeacon(url, payload);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Fallback: fire-and-forget; avoid preflight by using text/plain + no-cors
|
|
256
|
+
fetch(url, {
|
|
257
|
+
method: "POST",
|
|
258
|
+
keepalive: true,
|
|
259
|
+
mode: "no-cors",
|
|
260
|
+
headers: {"Content-Type": "text/plain;charset=UTF-8"},
|
|
261
|
+
body: payload
|
|
262
|
+
}).catch(() => {});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
//////////////
|
|
266
|
+
// Private API
|
|
267
|
+
//////////////
|
|
268
|
+
|
|
269
|
+
export async function getFeeds() {
|
|
270
|
+
const response = await postPrivate({url: "/get-feeds", data: {}});
|
|
271
|
+
|
|
272
|
+
const feeds = [];
|
|
273
|
+
|
|
274
|
+
for (let rawFeed of response.feeds) {
|
|
275
|
+
const feed = t.feedFromJSON(rawFeed);
|
|
276
|
+
feeds.push(feed);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return feeds;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export async function getLastEntries({period, minTagCount}: {period: number; minTagCount: number}) {
|
|
283
|
+
const response = await postPrivate({
|
|
284
|
+
url: "/get-last-entries",
|
|
285
|
+
data: {
|
|
286
|
+
period: period,
|
|
287
|
+
minTagCount: minTagCount
|
|
288
|
+
}
|
|
121
289
|
});
|
|
122
290
|
|
|
123
291
|
const entries = [];
|
|
@@ -139,8 +307,8 @@ export async function createOrUpdateRule({
|
|
|
139
307
|
excludedTags: string[];
|
|
140
308
|
score: number;
|
|
141
309
|
}) {
|
|
142
|
-
const response = await
|
|
143
|
-
url:
|
|
310
|
+
const response = await postPrivate({
|
|
311
|
+
url: "/create-or-update-rule",
|
|
144
312
|
data: {
|
|
145
313
|
requiredTags: requiredTags,
|
|
146
314
|
excludedTags: excludedTags,
|
|
@@ -151,7 +319,7 @@ export async function createOrUpdateRule({
|
|
|
151
319
|
}
|
|
152
320
|
|
|
153
321
|
export async function deleteRule({id}: {id: t.RuleId}) {
|
|
154
|
-
const response = await
|
|
322
|
+
const response = await postPrivate({url: "/delete-rule", data: {id: id}});
|
|
155
323
|
return response;
|
|
156
324
|
}
|
|
157
325
|
|
|
@@ -166,15 +334,15 @@ export async function updateRule({
|
|
|
166
334
|
excludedTags: string[];
|
|
167
335
|
score: number;
|
|
168
336
|
}) {
|
|
169
|
-
const response = await
|
|
170
|
-
url:
|
|
337
|
+
const response = await postPrivate({
|
|
338
|
+
url: "/update-rule",
|
|
171
339
|
data: {id: id, score: score, requiredTags: requiredTags, excludedTags: excludedTags}
|
|
172
340
|
});
|
|
173
341
|
return response;
|
|
174
342
|
}
|
|
175
343
|
|
|
176
344
|
export async function getRules() {
|
|
177
|
-
const response = await
|
|
345
|
+
const response = await postPrivate({url: "/get-rules", data: {}});
|
|
178
346
|
|
|
179
347
|
const rules = [];
|
|
180
348
|
|
|
@@ -187,8 +355,8 @@ export async function getRules() {
|
|
|
187
355
|
}
|
|
188
356
|
|
|
189
357
|
export async function getScoreDetails({entryId}: {entryId: t.EntryId}) {
|
|
190
|
-
const response = await
|
|
191
|
-
url:
|
|
358
|
+
const response = await postPrivate({
|
|
359
|
+
url: "/get-score-details",
|
|
192
360
|
data: {entryId: entryId}
|
|
193
361
|
});
|
|
194
362
|
|
|
@@ -203,21 +371,21 @@ export async function getScoreDetails({entryId}: {entryId: t.EntryId}) {
|
|
|
203
371
|
}
|
|
204
372
|
|
|
205
373
|
export async function setMarker({entryId, marker}: {entryId: t.EntryId; marker: e.Marker}) {
|
|
206
|
-
await
|
|
207
|
-
url:
|
|
374
|
+
await postPrivate({
|
|
375
|
+
url: "/set-marker",
|
|
208
376
|
data: {entryId: entryId, marker: marker}
|
|
209
377
|
});
|
|
210
378
|
}
|
|
211
379
|
|
|
212
380
|
export async function removeMarker({entryId, marker}: {entryId: t.EntryId; marker: e.Marker}) {
|
|
213
|
-
await
|
|
214
|
-
url:
|
|
381
|
+
await postPrivate({
|
|
382
|
+
url: "/remove-marker",
|
|
215
383
|
data: {entryId: entryId, marker: marker}
|
|
216
384
|
});
|
|
217
385
|
}
|
|
218
386
|
|
|
219
387
|
export async function discoverFeeds({url}: {url: string}) {
|
|
220
|
-
const response = await
|
|
388
|
+
const response = await postPrivate({url: "/discover-feeds", data: {url: url}});
|
|
221
389
|
|
|
222
390
|
const feeds = [];
|
|
223
391
|
const messages = [];
|
|
@@ -236,71 +404,28 @@ export async function discoverFeeds({url}: {url: string}) {
|
|
|
236
404
|
}
|
|
237
405
|
|
|
238
406
|
export async function addFeed({url}: {url: string}) {
|
|
239
|
-
const response = await
|
|
407
|
+
const response = await postPrivate({url: "/add-feed", data: {url: url}});
|
|
240
408
|
|
|
241
409
|
return t.feedFromJSON(response.feed);
|
|
242
410
|
}
|
|
243
411
|
|
|
244
412
|
export async function addOPML({content}: {content: string}) {
|
|
245
|
-
await
|
|
413
|
+
await postPrivate({url: "/add-opml", data: {content: content}});
|
|
246
414
|
}
|
|
247
415
|
|
|
248
416
|
export async function unsubscribe({feedId}: {feedId: t.FeedId}) {
|
|
249
|
-
await
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export async function getCollections() {
|
|
253
|
-
const response = await post({url: API_GET_COLLECTIONS, data: {}});
|
|
254
|
-
|
|
255
|
-
const collections = [];
|
|
256
|
-
|
|
257
|
-
for (let rawCollection of response.collections) {
|
|
258
|
-
const collection = t.collectionFromJSON(rawCollection);
|
|
259
|
-
collections.push(collection);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return collections;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export async function getCollectionFeeds({collectionId}: {collectionId: t.CollectionId}) {
|
|
266
|
-
const response = await post({
|
|
267
|
-
url: API_GET_COLLECTION_FEEDS,
|
|
268
|
-
data: {collectionId: collectionId}
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const feeds = [];
|
|
272
|
-
|
|
273
|
-
for (let rawFeed of response.feeds) {
|
|
274
|
-
const feed = t.collectionFeedInfoFromJSON(rawFeed);
|
|
275
|
-
feeds.push(feed);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return feeds;
|
|
417
|
+
await postPrivate({url: "/unsubscribe", data: {feedId: feedId}});
|
|
279
418
|
}
|
|
280
419
|
|
|
281
420
|
export async function subscribeToCollections({collectionsIds}: {collectionsIds: t.CollectionId[]}) {
|
|
282
|
-
await
|
|
283
|
-
url:
|
|
421
|
+
await postPrivate({
|
|
422
|
+
url: "/subscribe-to-collections",
|
|
284
423
|
data: {collections: collectionsIds}
|
|
285
424
|
});
|
|
286
425
|
}
|
|
287
426
|
|
|
288
|
-
export async function getTagsInfo({uids}: {uids: string[]}) {
|
|
289
|
-
const response = await post({url: API_GET_TAGS_INFO, data: {uids: uids}});
|
|
290
|
-
|
|
291
|
-
const tags: {[key: string]: t.TagInfo} = {};
|
|
292
|
-
|
|
293
|
-
for (let uid in response.tags) {
|
|
294
|
-
const rawTag = response.tags[uid];
|
|
295
|
-
const tag = t.tagInfoFromJSON(rawTag);
|
|
296
|
-
tags[uid] = tag;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return tags;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
427
|
export async function getUserSettings() {
|
|
303
|
-
const response = await
|
|
428
|
+
const response = await postPrivate({url: "/get-user-settings", data: {}});
|
|
304
429
|
|
|
305
430
|
const settings: {[key: string]: t.UserSetting} = {};
|
|
306
431
|
|
|
@@ -313,12 +438,12 @@ export async function getUserSettings() {
|
|
|
313
438
|
}
|
|
314
439
|
|
|
315
440
|
export async function setUserSetting({kind, value}: {kind: string; value: string | number | boolean}) {
|
|
316
|
-
await
|
|
441
|
+
await postPrivate({url: "/set-user-setting", data: {kind: kind, value: value}});
|
|
317
442
|
}
|
|
318
443
|
|
|
319
444
|
export async function getResourceHistory({kind}: {kind: string}) {
|
|
320
|
-
const response = await
|
|
321
|
-
url:
|
|
445
|
+
const response = await postPrivate({
|
|
446
|
+
url: "/get-resource-history",
|
|
322
447
|
data: {kind: kind}
|
|
323
448
|
});
|
|
324
449
|
|
|
@@ -332,24 +457,6 @@ export async function getResourceHistory({kind}: {kind: string}) {
|
|
|
332
457
|
return history;
|
|
333
458
|
}
|
|
334
459
|
|
|
335
|
-
export async function getInfo() {
|
|
336
|
-
const response = await post({url: API_GET_INFO, data: {}});
|
|
337
|
-
|
|
338
|
-
return response;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
export async function trackEvent(data: {[key: string]: string | number | null}) {
|
|
342
|
-
if (!settings.trackEvents) {
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if (!cookieConsent.isAnalyticsAllowed()) {
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
await post({url: API_TRACK_EVENT, data: {event: data}});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
460
|
export async function removeUser() {
|
|
354
|
-
await
|
|
461
|
+
await postPrivate({url: "/remove-user", data: {}});
|
|
355
462
|
}
|
package/src/logic/enums.ts
CHANGED
|
@@ -5,6 +5,16 @@ export type AnyEnum = {
|
|
|
5
5
|
[key in keyof any]: string | number;
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
//////////////
|
|
9
|
+
// Login state
|
|
10
|
+
//////////////
|
|
11
|
+
|
|
12
|
+
export enum LoginState {
|
|
13
|
+
Unknown = "unknown",
|
|
14
|
+
LoggedOut = "logged-out",
|
|
15
|
+
LoggedIn = "logged-in"
|
|
16
|
+
}
|
|
17
|
+
|
|
8
18
|
///////////////////
|
|
9
19
|
// Main panel modes
|
|
10
20
|
///////////////////
|
package/src/logic/events.ts
CHANGED
|
@@ -18,31 +18,39 @@ export type TagChangeSource = "tags_filter" | "entry_record" | "rule_record";
|
|
|
18
18
|
export type SidebarVisibilityChangeEvent = "hide" | "show";
|
|
19
19
|
export type SidebarVisibilityChangeSource = "top_sidebar_button";
|
|
20
20
|
|
|
21
|
-
export
|
|
22
|
-
|
|
21
|
+
export function newsLinkOpened({entryId, view}: {entryId: t.EntryId; view: EventsViewName}) {
|
|
22
|
+
api.trackEvent({
|
|
23
23
|
name: "news_link_opened",
|
|
24
24
|
view: view,
|
|
25
25
|
entry_id: entryId
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export
|
|
30
|
-
|
|
29
|
+
export function newsBodyOpened({entryId, view}: {entryId: t.EntryId; view: EventsViewName}) {
|
|
30
|
+
api.trackEvent({
|
|
31
31
|
name: "news_body_opened",
|
|
32
32
|
view: view,
|
|
33
33
|
entry_id: entryId
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export
|
|
38
|
-
|
|
37
|
+
export function socialLinkClicked({linkType, view}: {linkType: string; view: EventsViewName}) {
|
|
38
|
+
api.trackEvent({
|
|
39
39
|
name: "social_link_clicked",
|
|
40
40
|
view: view,
|
|
41
41
|
link_type: linkType
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export
|
|
45
|
+
export function authButtonClicked({buttonType, view}: {buttonType: string; view: EventsViewName}) {
|
|
46
|
+
api.trackEvent({
|
|
47
|
+
name: "auth_button_clicked",
|
|
48
|
+
view: view,
|
|
49
|
+
button_type: buttonType
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function sidebarStateChanged({
|
|
46
54
|
subEvent,
|
|
47
55
|
view,
|
|
48
56
|
source
|
|
@@ -51,7 +59,7 @@ export async function sidebarStateChanged({
|
|
|
51
59
|
view: EventsViewName;
|
|
52
60
|
source: SidebarVisibilityChangeSource;
|
|
53
61
|
}) {
|
|
54
|
-
|
|
62
|
+
api.trackEvent({
|
|
55
63
|
name: "sidebar_state_changed",
|
|
56
64
|
view: view,
|
|
57
65
|
sub_event: subEvent,
|
|
@@ -59,7 +67,7 @@ export async function sidebarStateChanged({
|
|
|
59
67
|
});
|
|
60
68
|
}
|
|
61
69
|
|
|
62
|
-
export
|
|
70
|
+
export function tagStateChanged({
|
|
63
71
|
tag,
|
|
64
72
|
fromState,
|
|
65
73
|
toState,
|
|
@@ -74,7 +82,7 @@ export async function tagStateChanged({
|
|
|
74
82
|
}) {
|
|
75
83
|
// const eventsView = inject<events.EventViewName>("eventsViewName");
|
|
76
84
|
|
|
77
|
-
|
|
85
|
+
api.trackEvent({
|
|
78
86
|
name: "tag_filter_state_changed",
|
|
79
87
|
tag: tag,
|
|
80
88
|
from_state: fromState,
|
|
@@ -84,7 +92,7 @@ export async function tagStateChanged({
|
|
|
84
92
|
});
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
export
|
|
95
|
+
export function trackUtm({
|
|
88
96
|
utm_source,
|
|
89
97
|
utm_medium,
|
|
90
98
|
utm_campaign
|
|
@@ -93,7 +101,7 @@ export async function trackUtm({
|
|
|
93
101
|
utm_medium: string;
|
|
94
102
|
utm_campaign: string;
|
|
95
103
|
}) {
|
|
96
|
-
|
|
104
|
+
api.trackEvent({
|
|
97
105
|
name: "user_utm",
|
|
98
106
|
utm_source: utm_source,
|
|
99
107
|
utm_medium: utm_medium,
|