@streamlayer/feature-gamification 0.39.0 → 0.39.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/lib/background.js +13 -1
- package/lib/detail.js +3 -0
- package/lib/gamification.d.ts +2 -1
- package/lib/gamification.js +26 -5
- package/lib/leaderboard.js +24 -27
- package/lib/queries/leaderboard.d.ts +2 -1
- package/lib/queries/leaderboard.js +4 -0
- package/lib/userSummary.d.ts +8 -0
- package/lib/userSummary.js +38 -0
- package/package.json +3 -3
package/lib/background.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ApiStore, SingleStore, createSingleStore } from '@streamlayer/sdk-web-interfaces';
|
|
2
2
|
import { createLogger } from '@streamlayer/sdk-web-logger';
|
|
3
|
+
import { QuestionStatus } from '@streamlayer/sdk-web-types';
|
|
3
4
|
import '@streamlayer/sdk-web-core/store';
|
|
4
5
|
import * as queries from './queries';
|
|
5
6
|
import { detail } from './detail';
|
|
@@ -75,7 +76,18 @@ export class GamificationBackground {
|
|
|
75
76
|
});
|
|
76
77
|
this.feedSubscription = queries.feedSubscription(this.slStreamId, instance.transport);
|
|
77
78
|
this.feedSubscription.addListener('feed-subscription-active-question', (response) => {
|
|
78
|
-
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
80
|
+
// @ts-ignore
|
|
81
|
+
const $activeQuestionId = this.activeQuestionId.store;
|
|
82
|
+
const activeQuestionId = $activeQuestionId.get().data?.question?.id;
|
|
83
|
+
const question = response.data?.attributes?.question;
|
|
84
|
+
if (!question) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// skip update question, avoid race condition
|
|
88
|
+
if (activeQuestionId && question.status === QuestionStatus.RESOLVED && question.id !== activeQuestionId) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
79
91
|
if ($activeQuestionId) {
|
|
80
92
|
$activeQuestionId.mutate(response.data?.attributes);
|
|
81
93
|
}
|
package/lib/detail.js
CHANGED
|
@@ -38,6 +38,9 @@ export const detail = (transport, $openedQuestionId, $feedList) => {
|
|
|
38
38
|
};
|
|
39
39
|
$extendedStore.mutate({
|
|
40
40
|
...question,
|
|
41
|
+
options: currentQuestion?.options,
|
|
42
|
+
subject: currentQuestion?.subject,
|
|
43
|
+
appearance: currentQuestion?.appearance,
|
|
41
44
|
answers: mergeQuestionAnswers(currentQuestion?.answers, question?.answers),
|
|
42
45
|
});
|
|
43
46
|
};
|
package/lib/gamification.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { deepLink } from './deepLink';
|
|
|
10
10
|
import { OnboardingStatus } from './onboarding';
|
|
11
11
|
import { LeaderboardItem } from './queries/leaderboard';
|
|
12
12
|
import { GamificationBackground } from './background';
|
|
13
|
+
import { summary } from './userSummary';
|
|
13
14
|
/**
|
|
14
15
|
* Gamification (Games) Overlay
|
|
15
16
|
* Includes:
|
|
@@ -21,7 +22,7 @@ import { GamificationBackground } from './background';
|
|
|
21
22
|
*/
|
|
22
23
|
export declare class Gamification extends AbstractFeature<'games', PlainMessage<GamesOverlaySettings>> {
|
|
23
24
|
/** user statistics (leaderboard panel) */
|
|
24
|
-
userSummary:
|
|
25
|
+
userSummary: ReturnType<typeof summary>;
|
|
25
26
|
/** feed list (pick history) */
|
|
26
27
|
feedList: ApiStore<GetApiResponseType<typeof queries.$feedList>>;
|
|
27
28
|
/** friends list */
|
package/lib/gamification.js
CHANGED
|
@@ -12,6 +12,7 @@ import { OnboardingStatus, onboarding } from './onboarding';
|
|
|
12
12
|
import { GamificationBackground, InteractiveAllowed } from './background';
|
|
13
13
|
import { ERROR } from './constants';
|
|
14
14
|
import { $questionByUser } from './queries';
|
|
15
|
+
import { summary } from './userSummary';
|
|
15
16
|
const InteractiveQuestionTypes = new Set([QuestionType.POLL, QuestionType.PREDICTION, QuestionType.TRIVIA]);
|
|
16
17
|
/**
|
|
17
18
|
* Gamification (Games) Overlay
|
|
@@ -61,7 +62,6 @@ export class Gamification extends AbstractFeature {
|
|
|
61
62
|
this.activeQuestionId = this.background.activeQuestionId;
|
|
62
63
|
this.openedQuestionId = this.background.openedQuestionId;
|
|
63
64
|
this.storage = new GamificationStorage();
|
|
64
|
-
this.userSummary = new ApiStore(queries.$userSummary(this.background.slStreamId, this.background.userId, instance.transport), 'gamification:userSummary');
|
|
65
65
|
this.feedList = this.background.feedList;
|
|
66
66
|
this.friends = new ApiStore(queries.$friends(this.background.userId, instance.transport), 'gamification:friends');
|
|
67
67
|
this.currentUserId = this.background.userId;
|
|
@@ -74,11 +74,12 @@ export class Gamification extends AbstractFeature {
|
|
|
74
74
|
this.openFeature = () => instance.sdk.openFeature(FeatureType.GAMES);
|
|
75
75
|
this.openedQuestion = this.background.openedQuestion;
|
|
76
76
|
this.deepLink = deepLink(this.transport, this.background.slStreamId, instance.stores.providerStreamId.getStore(), this.background.userId);
|
|
77
|
+
this.userSummary = summary(this.background.slStreamId, this.background.userId, this.friends, this.transport);
|
|
77
78
|
this.leaderboardList = leaderboard(this.transport, this.background.slStreamId, this.background.userId, this.friends);
|
|
78
79
|
this.connect();
|
|
79
80
|
// refresh leaderboard on user summary update after earning points
|
|
80
|
-
this.userSummary.listen((userSummary) => {
|
|
81
|
-
if (this.leaderboardList.$store.lc !== 0 && userSummary?.
|
|
81
|
+
this.userSummary.$store.listen((userSummary) => {
|
|
82
|
+
if (this.leaderboardList.$store.lc !== 0 && userSummary?.summary) {
|
|
82
83
|
this.leaderboardList.invalidate(); // verified, it's necessary
|
|
83
84
|
}
|
|
84
85
|
});
|
|
@@ -235,7 +236,24 @@ export class Gamification extends AbstractFeature {
|
|
|
235
236
|
}
|
|
236
237
|
}
|
|
237
238
|
else {
|
|
238
|
-
feedList[questionIndex]
|
|
239
|
+
const prev = feedList[questionIndex];
|
|
240
|
+
if (prev.attributes && feedItem.attributes) {
|
|
241
|
+
feedList[questionIndex] = {
|
|
242
|
+
...feedList[questionIndex],
|
|
243
|
+
attributes: {
|
|
244
|
+
...prev.attributes,
|
|
245
|
+
attributes: {
|
|
246
|
+
...prev.attributes.attributes,
|
|
247
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
248
|
+
// @ts-ignore
|
|
249
|
+
value: {
|
|
250
|
+
...prev.attributes.attributes.value,
|
|
251
|
+
...feedItem.attributes.attributes.value,
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
239
257
|
}
|
|
240
258
|
}
|
|
241
259
|
if (questionIndex === -1) {
|
|
@@ -295,6 +313,9 @@ export class Gamification extends AbstractFeature {
|
|
|
295
313
|
this.feedList.getStore().mutate([...feedList]);
|
|
296
314
|
extendedQuestion.answers[votedAnswerIdx].correct = correctAnswer?.id === answerId;
|
|
297
315
|
extendedQuestion.answers[votedAnswerIdx].youVoted = true;
|
|
316
|
+
extendedQuestion.answers.forEach((answer) => {
|
|
317
|
+
answer.percentageDecimal = 0;
|
|
318
|
+
});
|
|
298
319
|
if (correctAnswer?.id === answerId) {
|
|
299
320
|
extendedQuestion.answers[votedAnswerIdx].pointsEarned =
|
|
300
321
|
extendedQuestion.status === QuestionStatus.RESOLVED ? 0 : correctAnswer.points;
|
|
@@ -394,7 +415,7 @@ export class Gamification extends AbstractFeature {
|
|
|
394
415
|
const instantView = {
|
|
395
416
|
heading: question.data.question.notification.title,
|
|
396
417
|
body: question.data.question.notification.body,
|
|
397
|
-
imageMode: optionsValue
|
|
418
|
+
imageMode: optionsValue?.imageMode,
|
|
398
419
|
image: optionsValue?.image,
|
|
399
420
|
video: {
|
|
400
421
|
id: optionsValue?.video?.id || '',
|
package/lib/leaderboard.js
CHANGED
|
@@ -34,19 +34,17 @@ export const leaderboard = (transport, $eventId, $userId, $friends, options) =>
|
|
|
34
34
|
const request = {
|
|
35
35
|
eventId: eventId,
|
|
36
36
|
usersIds: friendsIds,
|
|
37
|
-
pagination: { page: 0, pageSize: options?.pageSize || defaultOptions.pageSize },
|
|
38
37
|
};
|
|
39
38
|
const newData = await fetch(request);
|
|
40
39
|
$store.set({
|
|
41
40
|
data: newData.data.map((item) => item.attributes),
|
|
42
|
-
hasMore:
|
|
41
|
+
hasMore: false,
|
|
43
42
|
key: Date.now(),
|
|
44
43
|
loading: false,
|
|
45
44
|
});
|
|
46
45
|
if (newData.meta) {
|
|
47
46
|
maxPage = Math.round(newData.meta.count / newData.meta.pageSize);
|
|
48
47
|
}
|
|
49
|
-
$pagination.set(request.pagination);
|
|
50
48
|
}
|
|
51
49
|
};
|
|
52
50
|
const invalidate = () => {
|
|
@@ -65,33 +63,32 @@ export const leaderboard = (transport, $eventId, $userId, $friends, options) =>
|
|
|
65
63
|
onMount($store, () => {
|
|
66
64
|
const cancelRefetchListener = $eventId.listen(refetch);
|
|
67
65
|
const cancelRefetchByFriendsListener = $friends.listen(refetch);
|
|
68
|
-
const cancelPaginationListener = $pagination.listen(async (pagination) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
66
|
+
// const cancelPaginationListener = $pagination.listen(async (pagination) => {
|
|
67
|
+
// const eventId = $eventId.get()
|
|
68
|
+
// if (pagination.page > 0 && eventId) {
|
|
69
|
+
// if (pagination.page < maxPage) {
|
|
70
|
+
// $store.setKey('loading', true)
|
|
71
|
+
// const request = {
|
|
72
|
+
// eventId: eventId as unknown as bigint,
|
|
73
|
+
// pagination,
|
|
74
|
+
// }
|
|
75
|
+
// const newData = await fetch(request)
|
|
76
|
+
// const prevData = $store.get().data || []
|
|
77
|
+
// $store.set({
|
|
78
|
+
// data: [...prevData, ...newData.data.map((item) => item.attributes as LeaderboardItem)],
|
|
79
|
+
// key: $store.get().key,
|
|
80
|
+
// loading: false,
|
|
81
|
+
// hasMore: true,
|
|
82
|
+
// })
|
|
83
|
+
// } else {
|
|
84
|
+
// $store.setKey('hasMore', false)
|
|
85
|
+
// }
|
|
86
|
+
// }
|
|
87
|
+
// })
|
|
91
88
|
return () => {
|
|
92
89
|
cancelRefetchListener();
|
|
93
90
|
cancelRefetchByFriendsListener();
|
|
94
|
-
cancelPaginationListener()
|
|
91
|
+
// cancelPaginationListener()
|
|
95
92
|
};
|
|
96
93
|
});
|
|
97
94
|
return { $store, fetchMore, invalidate };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Transport } from '@streamlayer/sdk-web-api';
|
|
2
2
|
import { ReadableAtom } from 'nanostores';
|
|
3
|
-
import { ListRequest } from '@streamlayer/sl-eslib/interactive/leaderboard/interactive.leaderboard_pb';
|
|
3
|
+
import { ListRequest, SummaryRequest } from '@streamlayer/sl-eslib/interactive/leaderboard/interactive.leaderboard_pb';
|
|
4
4
|
import { PartialMessage } from '@bufbuild/protobuf';
|
|
5
5
|
export { LeaderboardItem } from '@streamlayer/sl-eslib/interactive/leaderboard/interactive.leaderboard_pb';
|
|
6
6
|
export declare const $userSummary: ($eventId: ReadableAtom<string | undefined>, $userId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<import("@streamlayer/sl-eslib/interactive/leaderboard/interactive.leaderboard_pb").LeaderboardSummaryItem | undefined, any>;
|
|
7
7
|
export declare const $leaderboardList: ($eventId: ReadableAtom<string | undefined>, _: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<import("@streamlayer/sl-eslib/interactive/leaderboard/interactive.leaderboard_pb").ListResponse_ListResponseData[], any>;
|
|
8
8
|
export declare const createLeaderboardListFetch: (transport: Transport) => (params: PartialMessage<ListRequest>) => Promise<import("@streamlayer/sl-eslib/interactive/leaderboard/interactive.leaderboard_pb").ListResponse>;
|
|
9
|
+
export declare const createUserSummaryFetch: (transport: Transport) => (params: PartialMessage<SummaryRequest>) => Promise<import("@streamlayer/sl-eslib/interactive/leaderboard/interactive.leaderboard_pb").SummaryResponse>;
|
|
@@ -34,3 +34,7 @@ export const createLeaderboardListFetch = (transport) => {
|
|
|
34
34
|
const { client } = transport.createPromiseClient(Leaderboard, { method: 'list' });
|
|
35
35
|
return (params) => client.list(params);
|
|
36
36
|
};
|
|
37
|
+
export const createUserSummaryFetch = (transport) => {
|
|
38
|
+
const { client } = transport.createPromiseClient(Leaderboard, { method: 'summary' });
|
|
39
|
+
return (params) => client.summary(params);
|
|
40
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Transport } from '@streamlayer/sdk-web-api';
|
|
2
|
+
import { ReadableAtom } from 'nanostores';
|
|
3
|
+
import { LeaderboardSummaryItem } from '@streamlayer/sl-eslib/interactive/leaderboard/interactive.leaderboard_pb';
|
|
4
|
+
import { Gamification } from '.';
|
|
5
|
+
export declare const summary: ($eventId: ReadableAtom<string | undefined>, $userId: ReadableAtom<string | undefined>, $friends: Gamification['friends'], transport: Transport) => {
|
|
6
|
+
$store: import("nanostores").MapStore<LeaderboardSummaryItem | undefined>;
|
|
7
|
+
invalidate: () => void;
|
|
8
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createMapStore } from '@streamlayer/sdk-web-interfaces';
|
|
2
|
+
import { onMount } from 'nanostores';
|
|
3
|
+
import { createUserSummaryFetch } from './queries/leaderboard';
|
|
4
|
+
export const summary = ($eventId, $userId, $friends, transport) => {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
const $store = createMapStore(undefined);
|
|
8
|
+
const fetch = createUserSummaryFetch(transport);
|
|
9
|
+
const refetch = async () => {
|
|
10
|
+
const eventId = $eventId.get();
|
|
11
|
+
const userId = $userId.get();
|
|
12
|
+
const usersIds = $friends
|
|
13
|
+
.getStore()
|
|
14
|
+
.get()
|
|
15
|
+
.data?.map((friend) => friend.slId) || [];
|
|
16
|
+
const request = {
|
|
17
|
+
eventId: eventId,
|
|
18
|
+
userId: userId,
|
|
19
|
+
usersIds: [...usersIds, userId],
|
|
20
|
+
};
|
|
21
|
+
const res = await fetch(request);
|
|
22
|
+
$store.set(res.data?.attributes);
|
|
23
|
+
};
|
|
24
|
+
const invalidate = () => {
|
|
25
|
+
void refetch();
|
|
26
|
+
};
|
|
27
|
+
onMount($store, () => {
|
|
28
|
+
const cancelRefetchListener = $eventId.listen(refetch);
|
|
29
|
+
const cancelRefetchByFriendsListener = $friends.listen(refetch);
|
|
30
|
+
const cancelRefetchByUserListener = $userId.listen(refetch);
|
|
31
|
+
return () => {
|
|
32
|
+
cancelRefetchListener();
|
|
33
|
+
cancelRefetchByFriendsListener();
|
|
34
|
+
cancelRefetchByUserListener();
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
return { $store, invalidate };
|
|
38
|
+
};
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamlayer/feature-gamification",
|
|
3
|
-
"version": "0.39.
|
|
3
|
+
"version": "0.39.1",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@bufbuild/protobuf": "^1.7.2",
|
|
6
6
|
"@fastify/deepmerge": "^1.3.0",
|
|
7
7
|
"@streamlayer/sl-eslib": "^5.83.1",
|
|
8
8
|
"nanostores": "^0.10.0",
|
|
9
|
-
"@streamlayer/sdk-web-api": "^0.24.
|
|
10
|
-
"@streamlayer/sdk-web-core": "^0.22.
|
|
9
|
+
"@streamlayer/sdk-web-api": "^0.24.1",
|
|
10
|
+
"@streamlayer/sdk-web-core": "^0.22.1",
|
|
11
11
|
"@streamlayer/sdk-web-interfaces": "^0.21.0",
|
|
12
12
|
"@streamlayer/sdk-web-logger": "^0.5.18",
|
|
13
13
|
"@streamlayer/sdk-web-notifications": "^0.15.0",
|