feeds-fun 1.21.6 → 1.22.1
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 +4 -3
- package/src/App.vue +2 -2
- package/src/components/EntryForList.vue +3 -3
- package/src/components/OPMLUpload.vue +21 -10
- 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/components/tags/TagsFilter.vue +2 -2
- package/src/css/inputs.css +21 -0
- package/src/layouts/SidePanelLayout.vue +19 -28
- package/src/logic/api.ts +279 -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 +36 -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/FeedsView.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,126 @@ 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";
|
|
8
|
+
import {ok, err, Result} from "neverthrow";
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
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
|
-
}
|
|
10
|
+
///////////////
|
|
11
|
+
// API handlers
|
|
12
|
+
///////////////
|
|
13
|
+
|
|
14
|
+
const publicEntryPoint = "/spa/api/public";
|
|
15
|
+
const privateEntryPoint = "/spa/api/private";
|
|
16
|
+
|
|
17
|
+
export const downloadOPMLUrl = `${privateEntryPoint}/get-opml`;
|
|
18
|
+
|
|
19
|
+
const apiPublic = axios.create({baseURL: publicEntryPoint, withCredentials: true});
|
|
20
|
+
const apiPrivate = axios.create({baseURL: privateEntryPoint, withCredentials: true});
|
|
56
21
|
|
|
57
|
-
|
|
22
|
+
// It is an open question what should we do in case of session expiration:
|
|
23
|
+
// - redirect to login page
|
|
24
|
+
// - redirect to home page & show notification
|
|
25
|
+
// Currently the easiest way to handle it is to always redirect to login page.
|
|
26
|
+
export function redirectToLogin(returnTo?: string) {
|
|
27
|
+
if (!returnTo) {
|
|
28
|
+
returnTo = window.location.pathname + window.location.search;
|
|
58
29
|
}
|
|
30
|
+
window.location.assign(`/spa/auth/login?return_to=${encodeURIComponent(returnTo)}`);
|
|
59
31
|
}
|
|
60
32
|
|
|
61
|
-
export
|
|
62
|
-
|
|
33
|
+
export function redirectToJoin(returnTo?: string) {
|
|
34
|
+
if (!returnTo) {
|
|
35
|
+
returnTo = window.location.pathname + window.location.search;
|
|
36
|
+
}
|
|
37
|
+
window.location.assign(`/spa/auth/join?return_to=${encodeURIComponent(returnTo)}`);
|
|
38
|
+
}
|
|
63
39
|
|
|
64
|
-
|
|
40
|
+
export function logoutRedirect() {
|
|
41
|
+
window.location.assign("/spa/auth/logout");
|
|
42
|
+
}
|
|
65
43
|
|
|
66
|
-
|
|
67
|
-
const feed = t.feedFromJSON(rawFeed);
|
|
68
|
-
feeds.push(feed);
|
|
69
|
-
}
|
|
44
|
+
let _refreshingAuth: Promise<void> | null = null;
|
|
70
45
|
|
|
71
|
-
|
|
46
|
+
enum Ffun401Behaviour {
|
|
47
|
+
RedirectToLogin = "redirectToLogin",
|
|
48
|
+
DoNotRetry = "doNotRetry",
|
|
49
|
+
ReturnNull = "returnNull"
|
|
72
50
|
}
|
|
73
51
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
52
|
+
// We try to refresh auth on 401 responses for private API.
|
|
53
|
+
// For the public API we do nothing, because it most likely means infrastructure issue.
|
|
54
|
+
apiPrivate.interceptors.response.use(
|
|
55
|
+
(r) => r,
|
|
56
|
+
async (error) => {
|
|
57
|
+
const {config, response} = error;
|
|
58
|
+
|
|
59
|
+
if (!response) {
|
|
60
|
+
throw error;
|
|
80
61
|
}
|
|
81
|
-
});
|
|
82
62
|
|
|
83
|
-
|
|
63
|
+
if (response.status !== 401) {
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
84
66
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
67
|
+
if (config?.ffunRequestRetried) {
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (config?.ffun401Behaviour === Ffun401Behaviour.RedirectToLogin) {
|
|
72
|
+
redirectToLogin();
|
|
73
|
+
return; // never reached
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (config?.ffun401Behaviour === Ffun401Behaviour.DoNotRetry) {
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (config?.ffun401Behaviour === Ffun401Behaviour.ReturnNull) {
|
|
81
|
+
return {data: null};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
(config as any).ffunRequestRetried = true;
|
|
85
|
+
|
|
86
|
+
if (!_refreshingAuth) {
|
|
87
|
+
_refreshingAuth = apiPrivate
|
|
88
|
+
// @ts-ignore
|
|
89
|
+
.post("/refresh-auth", undefined, {ffun401Behaviour: Ffun401Behaviour.RedirectToLogin})
|
|
90
|
+
.then(() => {})
|
|
91
|
+
.finally(() => {
|
|
92
|
+
_refreshingAuth = null;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
await _refreshingAuth; // all 401s await the same refresh
|
|
97
|
+
|
|
98
|
+
return await apiPrivate(config); // retry the original request generically
|
|
88
99
|
}
|
|
100
|
+
);
|
|
89
101
|
|
|
90
|
-
|
|
102
|
+
async function postPublic({url, data}: {url: string; data: any}) {
|
|
103
|
+
const response = await apiPublic.post(url, data);
|
|
104
|
+
return response.data;
|
|
91
105
|
}
|
|
92
106
|
|
|
107
|
+
// TODO: deprecated, use postPrivateResult instead
|
|
108
|
+
async function postPrivate({url, data, config}: {url: string; data: any; config?: any}) {
|
|
109
|
+
const response = await apiPrivate.post(url, data, config);
|
|
110
|
+
return response.data;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function postPrivateResult({url, data, config}: {url: string; data: any; config?: any}) {
|
|
114
|
+
const response = await apiPrivate.post(url, data, config);
|
|
115
|
+
|
|
116
|
+
if (response.data.status === "error") {
|
|
117
|
+
return err(new t.ApiError(response.data.code, response.data.message));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return ok(response.data.data);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/////////////
|
|
124
|
+
// Public API
|
|
125
|
+
/////////////
|
|
126
|
+
|
|
93
127
|
export async function getLastCollectionEntries({
|
|
94
128
|
period,
|
|
95
129
|
collectionSlug,
|
|
@@ -99,8 +133,8 @@ export async function getLastCollectionEntries({
|
|
|
99
133
|
collectionSlug: t.CollectionSlug | null;
|
|
100
134
|
minTagCount: number;
|
|
101
135
|
}) {
|
|
102
|
-
const response = await
|
|
103
|
-
url:
|
|
136
|
+
const response = await postPublic({
|
|
137
|
+
url: "/get-last-collection-entries",
|
|
104
138
|
data: {period: period, collectionSlug: collectionSlug, minTagCount: minTagCount}
|
|
105
139
|
});
|
|
106
140
|
|
|
@@ -115,9 +149,157 @@ export async function getLastCollectionEntries({
|
|
|
115
149
|
}
|
|
116
150
|
|
|
117
151
|
export async function getEntriesByIds({ids}: {ids: t.EntryId[]}) {
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
152
|
+
const globalState = useGlobalState();
|
|
153
|
+
|
|
154
|
+
let response = null;
|
|
155
|
+
|
|
156
|
+
if (globalState.loginConfirmed) {
|
|
157
|
+
response = await postPrivate({
|
|
158
|
+
url: "/get-entries-by-ids",
|
|
159
|
+
data: {ids: ids},
|
|
160
|
+
config: {ffun401Behaviour: Ffun401Behaviour.ReturnNull}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!response) {
|
|
165
|
+
response = await postPublic({
|
|
166
|
+
url: "/get-entries-by-ids",
|
|
167
|
+
data: {ids: ids}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const entries = [];
|
|
172
|
+
|
|
173
|
+
for (let rawEntry of response.entries) {
|
|
174
|
+
const entry = t.entryFromJSON(rawEntry, response.tagsMapping);
|
|
175
|
+
entries.push(entry);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return entries;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export async function getCollections() {
|
|
182
|
+
const response = await postPublic({url: "/get-collections", data: {}});
|
|
183
|
+
|
|
184
|
+
const collections = [];
|
|
185
|
+
|
|
186
|
+
for (let rawCollection of response.collections) {
|
|
187
|
+
const collection = t.collectionFromJSON(rawCollection);
|
|
188
|
+
collections.push(collection);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return collections;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function getCollectionFeeds({collectionId}: {collectionId: t.CollectionId}) {
|
|
195
|
+
const response = await postPublic({
|
|
196
|
+
url: "/get-collection-feeds",
|
|
197
|
+
data: {collectionId: collectionId}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const feeds = [];
|
|
201
|
+
|
|
202
|
+
for (let rawFeed of response.feeds) {
|
|
203
|
+
const feed = t.collectionFeedInfoFromJSON(rawFeed);
|
|
204
|
+
feeds.push(feed);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return feeds;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export async function getTagsInfo({uids}: {uids: string[]}) {
|
|
211
|
+
const response = await postPublic({url: "/get-tags-info", data: {uids: uids}});
|
|
212
|
+
|
|
213
|
+
const tags: {[key: string]: t.TagInfo} = {};
|
|
214
|
+
|
|
215
|
+
for (let uid in response.tags) {
|
|
216
|
+
const rawTag = response.tags[uid];
|
|
217
|
+
const tag = t.tagInfoFromJSON(rawTag);
|
|
218
|
+
tags[uid] = tag;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return tags;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export async function getInfo() {
|
|
225
|
+
const response = await postPublic({url: "/get-info", data: {}});
|
|
226
|
+
|
|
227
|
+
return t.stateInfoFromJSON(response);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export async function getUser() {
|
|
231
|
+
const response = await postPrivate({
|
|
232
|
+
url: "/get-user",
|
|
233
|
+
data: {},
|
|
234
|
+
config: {ffun401Behaviour: Ffun401Behaviour.ReturnNull}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
if (!response) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return t.userInfoFromJSON(response);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function trackEvent(data: {[key: string]: string | number | null}) {
|
|
245
|
+
if (!settings.trackEvents) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!cookieConsent.isAnalyticsAllowed()) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const globalState = useGlobalState();
|
|
254
|
+
|
|
255
|
+
let url: string;
|
|
256
|
+
|
|
257
|
+
if (globalState.loginConfirmed) {
|
|
258
|
+
url = privateEntryPoint + "/track-event";
|
|
259
|
+
} else {
|
|
260
|
+
url = publicEntryPoint + "/track-event";
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let payload = JSON.stringify({event: data});
|
|
264
|
+
|
|
265
|
+
if ("sendBeacon" in navigator) {
|
|
266
|
+
return navigator.sendBeacon(url, payload);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Fallback: fire-and-forget; avoid preflight by using text/plain + no-cors
|
|
270
|
+
fetch(url, {
|
|
271
|
+
method: "POST",
|
|
272
|
+
keepalive: true,
|
|
273
|
+
mode: "no-cors",
|
|
274
|
+
headers: {"Content-Type": "text/plain;charset=UTF-8"},
|
|
275
|
+
body: payload
|
|
276
|
+
}).catch(() => {});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
//////////////
|
|
280
|
+
// Private API
|
|
281
|
+
//////////////
|
|
282
|
+
|
|
283
|
+
export async function getFeeds() {
|
|
284
|
+
const response = await postPrivate({url: "/get-feeds", data: {}});
|
|
285
|
+
|
|
286
|
+
const feeds = [];
|
|
287
|
+
|
|
288
|
+
for (let rawFeed of response.feeds) {
|
|
289
|
+
const feed = t.feedFromJSON(rawFeed);
|
|
290
|
+
feeds.push(feed);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return feeds;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export async function getLastEntries({period, minTagCount}: {period: number; minTagCount: number}) {
|
|
297
|
+
const response = await postPrivate({
|
|
298
|
+
url: "/get-last-entries",
|
|
299
|
+
data: {
|
|
300
|
+
period: period,
|
|
301
|
+
minTagCount: minTagCount
|
|
302
|
+
}
|
|
121
303
|
});
|
|
122
304
|
|
|
123
305
|
const entries = [];
|
|
@@ -139,8 +321,8 @@ export async function createOrUpdateRule({
|
|
|
139
321
|
excludedTags: string[];
|
|
140
322
|
score: number;
|
|
141
323
|
}) {
|
|
142
|
-
const response = await
|
|
143
|
-
url:
|
|
324
|
+
const response = await postPrivate({
|
|
325
|
+
url: "/create-or-update-rule",
|
|
144
326
|
data: {
|
|
145
327
|
requiredTags: requiredTags,
|
|
146
328
|
excludedTags: excludedTags,
|
|
@@ -151,7 +333,7 @@ export async function createOrUpdateRule({
|
|
|
151
333
|
}
|
|
152
334
|
|
|
153
335
|
export async function deleteRule({id}: {id: t.RuleId}) {
|
|
154
|
-
const response = await
|
|
336
|
+
const response = await postPrivate({url: "/delete-rule", data: {id: id}});
|
|
155
337
|
return response;
|
|
156
338
|
}
|
|
157
339
|
|
|
@@ -166,15 +348,15 @@ export async function updateRule({
|
|
|
166
348
|
excludedTags: string[];
|
|
167
349
|
score: number;
|
|
168
350
|
}) {
|
|
169
|
-
const response = await
|
|
170
|
-
url:
|
|
351
|
+
const response = await postPrivate({
|
|
352
|
+
url: "/update-rule",
|
|
171
353
|
data: {id: id, score: score, requiredTags: requiredTags, excludedTags: excludedTags}
|
|
172
354
|
});
|
|
173
355
|
return response;
|
|
174
356
|
}
|
|
175
357
|
|
|
176
358
|
export async function getRules() {
|
|
177
|
-
const response = await
|
|
359
|
+
const response = await postPrivate({url: "/get-rules", data: {}});
|
|
178
360
|
|
|
179
361
|
const rules = [];
|
|
180
362
|
|
|
@@ -187,8 +369,8 @@ export async function getRules() {
|
|
|
187
369
|
}
|
|
188
370
|
|
|
189
371
|
export async function getScoreDetails({entryId}: {entryId: t.EntryId}) {
|
|
190
|
-
const response = await
|
|
191
|
-
url:
|
|
372
|
+
const response = await postPrivate({
|
|
373
|
+
url: "/get-score-details",
|
|
192
374
|
data: {entryId: entryId}
|
|
193
375
|
});
|
|
194
376
|
|
|
@@ -203,21 +385,21 @@ export async function getScoreDetails({entryId}: {entryId: t.EntryId}) {
|
|
|
203
385
|
}
|
|
204
386
|
|
|
205
387
|
export async function setMarker({entryId, marker}: {entryId: t.EntryId; marker: e.Marker}) {
|
|
206
|
-
await
|
|
207
|
-
url:
|
|
388
|
+
await postPrivate({
|
|
389
|
+
url: "/set-marker",
|
|
208
390
|
data: {entryId: entryId, marker: marker}
|
|
209
391
|
});
|
|
210
392
|
}
|
|
211
393
|
|
|
212
394
|
export async function removeMarker({entryId, marker}: {entryId: t.EntryId; marker: e.Marker}) {
|
|
213
|
-
await
|
|
214
|
-
url:
|
|
395
|
+
await postPrivate({
|
|
396
|
+
url: "/remove-marker",
|
|
215
397
|
data: {entryId: entryId, marker: marker}
|
|
216
398
|
});
|
|
217
399
|
}
|
|
218
400
|
|
|
219
401
|
export async function discoverFeeds({url}: {url: string}) {
|
|
220
|
-
const response = await
|
|
402
|
+
const response = await postPrivate({url: "/discover-feeds", data: {url: url}});
|
|
221
403
|
|
|
222
404
|
const feeds = [];
|
|
223
405
|
const messages = [];
|
|
@@ -236,71 +418,28 @@ export async function discoverFeeds({url}: {url: string}) {
|
|
|
236
418
|
}
|
|
237
419
|
|
|
238
420
|
export async function addFeed({url}: {url: string}) {
|
|
239
|
-
const response = await
|
|
421
|
+
const response = await postPrivate({url: "/add-feed", data: {url: url}});
|
|
240
422
|
|
|
241
423
|
return t.feedFromJSON(response.feed);
|
|
242
424
|
}
|
|
243
425
|
|
|
244
426
|
export async function addOPML({content}: {content: string}) {
|
|
245
|
-
await
|
|
427
|
+
return await postPrivateResult({url: "/add-opml", data: {content: content}});
|
|
246
428
|
}
|
|
247
429
|
|
|
248
430
|
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;
|
|
431
|
+
await postPrivate({url: "/unsubscribe", data: {feedId: feedId}});
|
|
279
432
|
}
|
|
280
433
|
|
|
281
434
|
export async function subscribeToCollections({collectionsIds}: {collectionsIds: t.CollectionId[]}) {
|
|
282
|
-
await
|
|
283
|
-
url:
|
|
435
|
+
await postPrivate({
|
|
436
|
+
url: "/subscribe-to-collections",
|
|
284
437
|
data: {collections: collectionsIds}
|
|
285
438
|
});
|
|
286
439
|
}
|
|
287
440
|
|
|
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
441
|
export async function getUserSettings() {
|
|
303
|
-
const response = await
|
|
442
|
+
const response = await postPrivate({url: "/get-user-settings", data: {}});
|
|
304
443
|
|
|
305
444
|
const settings: {[key: string]: t.UserSetting} = {};
|
|
306
445
|
|
|
@@ -313,12 +452,12 @@ export async function getUserSettings() {
|
|
|
313
452
|
}
|
|
314
453
|
|
|
315
454
|
export async function setUserSetting({kind, value}: {kind: string; value: string | number | boolean}) {
|
|
316
|
-
await
|
|
455
|
+
await postPrivate({url: "/set-user-setting", data: {kind: kind, value: value}});
|
|
317
456
|
}
|
|
318
457
|
|
|
319
458
|
export async function getResourceHistory({kind}: {kind: string}) {
|
|
320
|
-
const response = await
|
|
321
|
-
url:
|
|
459
|
+
const response = await postPrivate({
|
|
460
|
+
url: "/get-resource-history",
|
|
322
461
|
data: {kind: kind}
|
|
323
462
|
});
|
|
324
463
|
|
|
@@ -332,24 +471,6 @@ export async function getResourceHistory({kind}: {kind: string}) {
|
|
|
332
471
|
return history;
|
|
333
472
|
}
|
|
334
473
|
|
|
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
474
|
export async function removeUser() {
|
|
354
|
-
await
|
|
475
|
+
await postPrivate({url: "/remove-user", data: {}});
|
|
355
476
|
}
|
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,
|