@streamlayer/feature-gamification 1.14.1 → 1.15.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.d.ts +2 -0
- package/lib/background.js +25 -3
- package/lib/friendSummary.js +1 -1
- package/lib/gamification.d.ts +3 -3
- package/lib/gamification.js +39 -38
- package/lib/leaderboard.js +3 -3
- package/lib/onboarding.d.ts +3 -1
- package/lib/onboarding.js +22 -13
- package/lib/queries/index.d.ts +2 -1
- package/lib/queries/index.js +27 -5
- package/lib/storage.d.ts +4 -0
- package/lib/storage.js +14 -0
- package/lib/userSummary.d.ts +1 -1
- package/lib/userSummary.js +8 -5
- package/package.json +8 -8
package/lib/background.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { ReadableAtom, WritableAtom } from 'nanostores';
|
|
|
6
6
|
import * as queries from './queries';
|
|
7
7
|
import { detail } from './detail';
|
|
8
8
|
import { advertisement } from './advertisement';
|
|
9
|
+
import { GamificationStorage } from './storage';
|
|
9
10
|
export declare enum InteractiveAllowed {
|
|
10
11
|
ALLOWED = "allowed",
|
|
11
12
|
DISALLOWED = "disallowed"
|
|
@@ -47,6 +48,7 @@ export declare class GamificationBackground {
|
|
|
47
48
|
/** subscription to opened question (vote percentage) */
|
|
48
49
|
questionSubscription?: ReturnType<typeof queries.questionSubscription>;
|
|
49
50
|
advertisement: ReturnType<typeof advertisement>;
|
|
51
|
+
storage: GamificationStorage;
|
|
50
52
|
private notifications;
|
|
51
53
|
private log;
|
|
52
54
|
private transport;
|
package/lib/background.js
CHANGED
|
@@ -5,6 +5,7 @@ import '@streamlayer/sdk-web-core/store';
|
|
|
5
5
|
import * as queries from './queries';
|
|
6
6
|
import { detail } from './detail';
|
|
7
7
|
import { advertisement } from './advertisement';
|
|
8
|
+
import { GamificationStorage } from './storage';
|
|
8
9
|
export var InteractiveAllowed;
|
|
9
10
|
(function (InteractiveAllowed) {
|
|
10
11
|
InteractiveAllowed["ALLOWED"] = "allowed";
|
|
@@ -42,6 +43,7 @@ export class GamificationBackground {
|
|
|
42
43
|
/** subscription to opened question (vote percentage) */
|
|
43
44
|
questionSubscription;
|
|
44
45
|
advertisement;
|
|
46
|
+
storage;
|
|
45
47
|
notifications;
|
|
46
48
|
log;
|
|
47
49
|
transport;
|
|
@@ -49,6 +51,7 @@ export class GamificationBackground {
|
|
|
49
51
|
constructor(instance) {
|
|
50
52
|
this.transport = instance.transport;
|
|
51
53
|
this.log = createLogger('gamification-background');
|
|
54
|
+
this.storage = new GamificationStorage();
|
|
52
55
|
this.slStreamId = instance.stores.slStreamId.getAtomStore();
|
|
53
56
|
this.organizationId = instance.stores.organizationSettings.getAtomStore();
|
|
54
57
|
this.userId = instance.stores.user.getAtomStore();
|
|
@@ -58,15 +61,34 @@ export class GamificationBackground {
|
|
|
58
61
|
this.notifications = instance.notifications;
|
|
59
62
|
this.moderation = new ApiStore(queries.$moderation(this.slStreamId, instance.transport), 'gamification:moderation');
|
|
60
63
|
this.feedList = new ApiStore(queries.$feedList(this.slStreamId, this.interactiveAllowed, instance.transport), 'gamification:feedList');
|
|
61
|
-
this.betPack = new ApiStore(queries.$betPack(this.slStreamId, instance.transport), 'gamification:betPack');
|
|
64
|
+
this.betPack = new ApiStore(queries.$betPack(this.slStreamId, this.userId, this.organizationId, this.storage, instance.transport), 'gamification:betPack');
|
|
62
65
|
this.activeQuestionId = queries.$activeQuestion(this.slStreamId, instance.transport);
|
|
63
66
|
this.openedQuestion = detail(instance.transport, this.openedQuestionId, this.feedList.getStore());
|
|
64
67
|
this.cancels.add(this.openedQuestionId.listen((item) => {
|
|
65
68
|
this.log.debug({ item }, 'received question');
|
|
66
69
|
if (item?.questionId) {
|
|
67
70
|
this.questionSubscription = queries.questionSubscription(item.questionId, instance.transport);
|
|
68
|
-
this.questionSubscription.addListener('feed-subscription-opened-question', (response) => {
|
|
69
|
-
|
|
71
|
+
this.questionSubscription.addListener('feed-subscription-opened-question', async (response) => {
|
|
72
|
+
const question = response.data?.attributes?.question;
|
|
73
|
+
this.openedQuestion.updateExtendedQuestion(question);
|
|
74
|
+
if (question?.type === QuestionType.PREDICTION) {
|
|
75
|
+
const betPackData = this.betPack.getValues().data || {};
|
|
76
|
+
const betPackItem = betPackData?.[question.id];
|
|
77
|
+
if (betPackItem || Object.keys(betPackData).length < 5) {
|
|
78
|
+
const data = queries.$questionByUser(question.id, this.transport);
|
|
79
|
+
const cancel = data.subscribe(() => { });
|
|
80
|
+
await data.get().promise;
|
|
81
|
+
const extendedQuestion = await queries.questionByUser(question.id, this.transport);
|
|
82
|
+
cancel();
|
|
83
|
+
window.requestAnimationFrame(() => {
|
|
84
|
+
data.invalidate();
|
|
85
|
+
});
|
|
86
|
+
this.betPack.getStore().mutate({
|
|
87
|
+
...betPackData,
|
|
88
|
+
[question.id]: extendedQuestion,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
70
92
|
});
|
|
71
93
|
this.questionSubscription.connect();
|
|
72
94
|
}
|
package/lib/friendSummary.js
CHANGED
|
@@ -4,7 +4,7 @@ export const friendSummary = async ($eventId, $userId, $friends, friendId, trans
|
|
|
4
4
|
const eventId = $eventId.get();
|
|
5
5
|
const userId = $userId.get();
|
|
6
6
|
const usersIds = $friends
|
|
7
|
-
|
|
7
|
+
?.getStore()
|
|
8
8
|
.get()
|
|
9
9
|
.data?.map((friend) => friend.slId) || [];
|
|
10
10
|
const request = {
|
package/lib/gamification.d.ts
CHANGED
|
@@ -28,11 +28,11 @@ export declare class Gamification extends AbstractFeature<'games', PlainMessage<
|
|
|
28
28
|
/** list (pack) of predefined questions */
|
|
29
29
|
betPack: ApiStore<GetApiResponseType<typeof queries.$betPack>>;
|
|
30
30
|
/** friends list */
|
|
31
|
-
friends
|
|
31
|
+
friends?: ApiStore<GetApiResponseType<typeof queries.$friends>>;
|
|
32
32
|
/** pinned leaderboard id */
|
|
33
33
|
leaderboardId: WritableAtom<string | undefined>;
|
|
34
34
|
/** leaderboard list */
|
|
35
|
-
leaderboardList
|
|
35
|
+
leaderboardList?: ReturnType<typeof leaderboard>;
|
|
36
36
|
deepLink: ReturnType<typeof deepLink>;
|
|
37
37
|
/** onboarding status */
|
|
38
38
|
onboardingStatus: {
|
|
@@ -44,7 +44,7 @@ export declare class Gamification extends AbstractFeature<'games', PlainMessage<
|
|
|
44
44
|
/** current user id */
|
|
45
45
|
currentUserId: GamificationBackground['userId'];
|
|
46
46
|
/** pinned leaderboard id */
|
|
47
|
-
openedUser
|
|
47
|
+
openedUser?: WritableAtom<LeaderboardItem | undefined>;
|
|
48
48
|
closeFeature: (destroy?: boolean) => void;
|
|
49
49
|
openFeature: () => void;
|
|
50
50
|
feedSubscription: GamificationBackground['feedSubscription'];
|
package/lib/gamification.js
CHANGED
|
@@ -5,7 +5,6 @@ import { NotificationType } from '@streamlayer/sdk-web-notifications';
|
|
|
5
5
|
import '@streamlayer/sdk-web-core/store';
|
|
6
6
|
import * as queries from './queries';
|
|
7
7
|
import * as actions from './queries/actions';
|
|
8
|
-
import { GamificationStorage } from './storage';
|
|
9
8
|
import { leaderboard } from './leaderboard';
|
|
10
9
|
import { deepLink } from './deepLink';
|
|
11
10
|
import { OnboardingStatus, onboarding } from './onboarding';
|
|
@@ -71,15 +70,15 @@ export class Gamification extends AbstractFeature {
|
|
|
71
70
|
this.feedSubscription = this.background.feedSubscription;
|
|
72
71
|
this.activeQuestionId = this.background.activeQuestionId;
|
|
73
72
|
this.openedQuestionId = this.background.openedQuestionId;
|
|
74
|
-
this.storage =
|
|
73
|
+
this.storage = this.background.storage;
|
|
75
74
|
this.feedList = this.background.feedList;
|
|
76
75
|
this.betPack = this.background.betPack;
|
|
77
|
-
this.friends = new ApiStore(queries.$friends(this.background.userId, instance.transport), 'gamification:friends');
|
|
78
76
|
this.currentUserId = this.background.userId;
|
|
79
|
-
this.openedUser = createSingleStore(undefined);
|
|
80
77
|
this.onboardingProcessed = createSingleStore(!instance.sdk.withAuth);
|
|
81
78
|
this.leaderboardId = new SingleStore(createSingleStore(this.settings.getValue('pinnedLeaderboardId')), 'pinnedLeaderboardId').getStore();
|
|
82
|
-
this.onboardingStatus = onboarding(this, this.background, instance.transport, instance.notifications
|
|
79
|
+
this.onboardingStatus = onboarding(this, this.background, instance.transport, instance.notifications, {
|
|
80
|
+
skipOnboarding: instance.sdk.options.get().skipOnboarding,
|
|
81
|
+
});
|
|
83
82
|
this.notifications = instance.notifications;
|
|
84
83
|
this.transport = instance.transport;
|
|
85
84
|
this.closeFeature = (destroy = true) => instance.sdk.closeFeature(destroy);
|
|
@@ -87,14 +86,18 @@ export class Gamification extends AbstractFeature {
|
|
|
87
86
|
this.openedQuestion = this.background.openedQuestion;
|
|
88
87
|
this.deepLink = deepLink(this.transport, this.background.slStreamId, instance.stores.providerStreamId.getStore(), this.background.userId);
|
|
89
88
|
this.userSummary = summary(this.background.slStreamId, this.background.userId, this.friends, this.transport);
|
|
90
|
-
|
|
89
|
+
if (!instance.sdk.options.get().hideFriends) {
|
|
90
|
+
this.friends = new ApiStore(queries.$friends(this.background.userId, instance.transport), 'gamification:friends');
|
|
91
|
+
this.openedUser = createSingleStore(undefined);
|
|
92
|
+
const leaderboardList = (this.leaderboardList = leaderboard(this.transport, this.background.slStreamId, this.background.userId, this.friends));
|
|
93
|
+
// refresh leaderboard on user summary update after earning points
|
|
94
|
+
this.cancels.add(this.userSummary.$store.listen((userSummary, prevValue) => {
|
|
95
|
+
if (prevValue?.summary && userSummary?.summary) {
|
|
96
|
+
leaderboardList.invalidate(); // verified, it's necessary
|
|
97
|
+
}
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
91
100
|
this.connect();
|
|
92
|
-
// refresh leaderboard on user summary update after earning points
|
|
93
|
-
this.cancels.add(this.userSummary.$store.listen((userSummary, prevValue) => {
|
|
94
|
-
if (prevValue?.summary && userSummary?.summary) {
|
|
95
|
-
this.leaderboardList.invalidate(); // verified, it's necessary
|
|
96
|
-
}
|
|
97
|
-
}));
|
|
98
101
|
/**
|
|
99
102
|
* listen for onboarding status, moderation onboarding changes and opt-in settings
|
|
100
103
|
*/
|
|
@@ -333,15 +336,6 @@ export class Gamification extends AbstractFeature {
|
|
|
333
336
|
const betPackList = { ...this.betPack.getValues().data };
|
|
334
337
|
const question = betPackList?.[questionId];
|
|
335
338
|
if (question) {
|
|
336
|
-
question.answers = betPackList[questionId].answers.map((answer) => {
|
|
337
|
-
if (answer.id === answerId) {
|
|
338
|
-
return {
|
|
339
|
-
...answer,
|
|
340
|
-
youVoted: true,
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
return answer;
|
|
344
|
-
});
|
|
345
339
|
eventBus.emit('poll', {
|
|
346
340
|
action: 'voted',
|
|
347
341
|
payload: {
|
|
@@ -349,10 +343,6 @@ export class Gamification extends AbstractFeature {
|
|
|
349
343
|
questionType: question.type,
|
|
350
344
|
},
|
|
351
345
|
});
|
|
352
|
-
this.betPack.getStore().mutate({
|
|
353
|
-
...betPackList,
|
|
354
|
-
[questionId]: question,
|
|
355
|
-
});
|
|
356
346
|
}
|
|
357
347
|
};
|
|
358
348
|
submitAnswer = async (questionId, answerId) => {
|
|
@@ -444,14 +434,22 @@ export class Gamification extends AbstractFeature {
|
|
|
444
434
|
const feedList = this.feedList.getStore().value?.data || [];
|
|
445
435
|
questionType = feedList.find((item) => item.id === questionId)?.attributes?.type;
|
|
446
436
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
437
|
+
const flags = {
|
|
438
|
+
eventId: this.background.slStreamId.get() || '',
|
|
439
|
+
userId: this.background.userId.get() || '',
|
|
440
|
+
organizationId: this.background.organizationId.get() || '',
|
|
441
|
+
};
|
|
442
|
+
if (!this.storage.isQuestionOpened(flags, questionId)) {
|
|
443
|
+
eventBus.emit('poll', {
|
|
444
|
+
action: 'opened',
|
|
445
|
+
payload: {
|
|
446
|
+
questionId,
|
|
447
|
+
questionType,
|
|
448
|
+
questionOpenedFrom: question?.openedFrom,
|
|
449
|
+
},
|
|
450
|
+
});
|
|
451
|
+
this.storage.saveQuestionOpened(flags, questionId);
|
|
452
|
+
}
|
|
455
453
|
this.openFeature();
|
|
456
454
|
return this.background.openQuestion(questionId, question);
|
|
457
455
|
};
|
|
@@ -475,20 +473,23 @@ export class Gamification extends AbstractFeature {
|
|
|
475
473
|
return this.background.closeQuestion(questionId);
|
|
476
474
|
};
|
|
477
475
|
openUser = async (friendId) => {
|
|
478
|
-
|
|
476
|
+
if (!this.leaderboardList) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const user = this.leaderboardList?.$store.get().data?.find((item) => item.userId === friendId);
|
|
479
480
|
if (!user) {
|
|
480
|
-
this.openedUser
|
|
481
|
+
this.openedUser?.set(user);
|
|
481
482
|
return;
|
|
482
483
|
}
|
|
483
484
|
if (user.summaryLoaded) {
|
|
484
|
-
this.openedUser
|
|
485
|
+
this.openedUser?.set(user);
|
|
485
486
|
return;
|
|
486
487
|
}
|
|
487
488
|
const userCopy = { ...user };
|
|
488
489
|
try {
|
|
489
490
|
const friendDetail = await friendSummary(this.background.slStreamId, this.background.userId, this.friends, friendId, this.transport);
|
|
490
491
|
if (friendDetail?.inTop !== undefined) {
|
|
491
|
-
this.leaderboardList
|
|
492
|
+
this.leaderboardList?.$store.setKey('data', this.leaderboardList.$store.get().data?.map((item) => {
|
|
492
493
|
if (item.userId === friendId) {
|
|
493
494
|
item.inTop = friendDetail.inTop;
|
|
494
495
|
}
|
|
@@ -504,7 +505,7 @@ export class Gamification extends AbstractFeature {
|
|
|
504
505
|
this.openedUser.set(userCopy);
|
|
505
506
|
};
|
|
506
507
|
closeUser = () => {
|
|
507
|
-
this.openedUser
|
|
508
|
+
this.openedUser?.set(undefined);
|
|
508
509
|
};
|
|
509
510
|
/**
|
|
510
511
|
* Show in-app notification for active question
|
package/lib/leaderboard.js
CHANGED
|
@@ -21,7 +21,7 @@ export const leaderboard = (transport, $eventId, $userId, $friends, options) =>
|
|
|
21
21
|
const refetch = async () => {
|
|
22
22
|
const eventId = $eventId.get();
|
|
23
23
|
const friendsIds = $friends
|
|
24
|
-
|
|
24
|
+
?.getStore()
|
|
25
25
|
.get()
|
|
26
26
|
.data?.map((friend) => friend.slId) || [];
|
|
27
27
|
if (eventId && friendsIds?.length) {
|
|
@@ -65,7 +65,7 @@ export const leaderboard = (transport, $eventId, $userId, $friends, options) =>
|
|
|
65
65
|
};
|
|
66
66
|
onMount($store, () => {
|
|
67
67
|
const cancelRefetchListener = $eventId.listen(refetch);
|
|
68
|
-
const cancelRefetchByFriendsListener = $friends
|
|
68
|
+
const cancelRefetchByFriendsListener = $friends?.listen(refetch);
|
|
69
69
|
// const cancelPaginationListener = $pagination.listen(async (pagination) => {
|
|
70
70
|
// const eventId = $eventId.get()
|
|
71
71
|
// if (pagination.page > 0 && eventId) {
|
|
@@ -90,7 +90,7 @@ export const leaderboard = (transport, $eventId, $userId, $friends, options) =>
|
|
|
90
90
|
// })
|
|
91
91
|
return () => {
|
|
92
92
|
cancelRefetchListener();
|
|
93
|
-
cancelRefetchByFriendsListener();
|
|
93
|
+
cancelRefetchByFriendsListener?.();
|
|
94
94
|
// cancelPaginationListener()
|
|
95
95
|
};
|
|
96
96
|
});
|
package/lib/onboarding.d.ts
CHANGED
|
@@ -17,7 +17,9 @@ export declare enum OnboardingStatus {
|
|
|
17
17
|
Disabled = "disabled",
|
|
18
18
|
Unavailable = "unavailable"
|
|
19
19
|
}
|
|
20
|
-
export declare const onboarding: (service: Gamification, background: GamificationBackground, transport: Transport, notifications: Notifications
|
|
20
|
+
export declare const onboarding: (service: Gamification, background: GamificationBackground, transport: Transport, notifications: Notifications, options?: {
|
|
21
|
+
skipOnboarding?: boolean;
|
|
22
|
+
}) => {
|
|
21
23
|
$store: import("nanostores").WritableAtom<OnboardingStatus>;
|
|
22
24
|
submitInplay: () => Promise<void>;
|
|
23
25
|
};
|
package/lib/onboarding.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createSingleStore, eventBus } from '@streamlayer/sdk-web-interfaces';
|
|
2
2
|
import { NotificationType } from '@streamlayer/sdk-web-notifications';
|
|
3
3
|
import { QuestionType } from '@streamlayer/sdk-web-types';
|
|
4
|
-
import { GamificationStorage } from './storage';
|
|
5
4
|
import { submitInplay as submitInplayApi } from './queries/actions';
|
|
6
5
|
/**
|
|
7
6
|
* Required: in-app should be displayed and questions not available
|
|
@@ -52,22 +51,32 @@ const showOnboardingInApp = (service, background, notifications, storage) => {
|
|
|
52
51
|
* check onboarding status, sync with browser cache
|
|
53
52
|
* retrieve onboarding settings from api
|
|
54
53
|
*/
|
|
55
|
-
const onboardingProcess = ($store, background, service, notifications, storage, listeners) => {
|
|
54
|
+
const onboardingProcess = ($store, background, service, notifications, storage, listeners, transport, options) => {
|
|
56
55
|
try {
|
|
57
56
|
const userId = background.userId.get();
|
|
58
|
-
|
|
57
|
+
const organizationId = background.organizationId.get();
|
|
58
|
+
const eventId = background.slStreamId.get();
|
|
59
|
+
if (!userId || !organizationId || !eventId) {
|
|
59
60
|
return;
|
|
60
61
|
}
|
|
61
62
|
const onboardingStatus = storage.getOnboardingStatus({
|
|
62
63
|
userId,
|
|
63
|
-
organizationId
|
|
64
|
-
eventId
|
|
64
|
+
organizationId,
|
|
65
|
+
eventId,
|
|
65
66
|
});
|
|
66
|
-
if (onboardingStatus === OnboardingStatus.Completed) {
|
|
67
|
+
if (onboardingStatus === OnboardingStatus.Completed || options?.skipOnboarding) {
|
|
67
68
|
$store.set(OnboardingStatus.Completed);
|
|
68
69
|
for (const cancel of listeners) {
|
|
69
70
|
cancel();
|
|
70
71
|
}
|
|
72
|
+
if (options?.skipOnboarding && onboardingStatus !== OnboardingStatus.Completed) {
|
|
73
|
+
storage.saveOnboardingStatus({
|
|
74
|
+
organizationId,
|
|
75
|
+
userId,
|
|
76
|
+
eventId,
|
|
77
|
+
}, OnboardingStatus.Completed);
|
|
78
|
+
void submitInplayApi(transport, background.slStreamId.get() || '');
|
|
79
|
+
}
|
|
71
80
|
return;
|
|
72
81
|
}
|
|
73
82
|
const moderation = background.moderation.getStore().value?.data;
|
|
@@ -100,10 +109,10 @@ const onboardingProcess = ($store, background, service, notifications, storage,
|
|
|
100
109
|
console.error(error);
|
|
101
110
|
}
|
|
102
111
|
};
|
|
103
|
-
export const onboarding = (service, background, transport, notifications) => {
|
|
112
|
+
export const onboarding = (service, background, transport, notifications, options) => {
|
|
104
113
|
// show onboarding each time when user open new sdk session
|
|
105
|
-
let onboardingShowed =
|
|
106
|
-
const storage =
|
|
114
|
+
let onboardingShowed = !!options?.skipOnboarding;
|
|
115
|
+
const storage = background.storage;
|
|
107
116
|
const $store = createSingleStore(OnboardingStatus.Unset);
|
|
108
117
|
$store.subscribe((onboardingStatus) => {
|
|
109
118
|
if (onboardingStatus === OnboardingStatus.Unset) {
|
|
@@ -128,24 +137,24 @@ export const onboarding = (service, background, transport, notifications) => {
|
|
|
128
137
|
});
|
|
129
138
|
});
|
|
130
139
|
const listeners = [];
|
|
131
|
-
onboardingProcess($store, background, service, notifications, storage, listeners);
|
|
132
140
|
if ($store.get() !== OnboardingStatus.Completed) {
|
|
133
141
|
listeners.push(background.userId.listen((userId) => {
|
|
134
142
|
if (userId) {
|
|
135
|
-
onboardingProcess($store, background, service, notifications, storage, listeners);
|
|
143
|
+
onboardingProcess($store, background, service, notifications, storage, listeners, transport, options);
|
|
136
144
|
}
|
|
137
145
|
}));
|
|
138
146
|
listeners.push(background.moderation.listen((value) => {
|
|
139
147
|
if (value.data) {
|
|
140
|
-
onboardingProcess($store, background, service, notifications, storage, listeners);
|
|
148
|
+
onboardingProcess($store, background, service, notifications, storage, listeners, transport, options);
|
|
141
149
|
}
|
|
142
150
|
}));
|
|
143
151
|
listeners.push(service.featureSettings.listen((value) => {
|
|
144
152
|
if (value) {
|
|
145
|
-
onboardingProcess($store, background, service, notifications, storage, listeners);
|
|
153
|
+
onboardingProcess($store, background, service, notifications, storage, listeners, transport, options);
|
|
146
154
|
}
|
|
147
155
|
}));
|
|
148
156
|
}
|
|
157
|
+
onboardingProcess($store, background, service, notifications, storage, listeners, transport, options);
|
|
149
158
|
const submitInplay = async () => {
|
|
150
159
|
const eventId = background.slStreamId.get();
|
|
151
160
|
if (eventId) {
|
package/lib/queries/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { QuestionStatus, QuestionType } from '@streamlayer/sdk-web-types';
|
|
|
3
3
|
import { ReadableAtom } from 'nanostores';
|
|
4
4
|
import type { SubscriptionRequest, SubscriptionResponse, VotingSubscriptionRequest, VotingSubscriptionResponse, QuestionSubscriptionRequest, QuestionSubscriptionResponse } from '@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb';
|
|
5
5
|
import { InteractiveAllowed } from '../background';
|
|
6
|
+
import { GamificationStorage } from '../storage';
|
|
6
7
|
export declare const $activeQuestion: (slStreamId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedQuestion | undefined, any>;
|
|
7
8
|
export declare const getFeedItem: (questionId: string, transport: Transport) => Promise<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedItem | undefined>;
|
|
8
9
|
export declare const feedSubscription: ($slStreamId: ReadableAtom<string | undefined>, transport: Transport) => import("packages/sdk-web-api/lib/grpc/subscription").ServerStreamSubscription<import("@bufbuild/protobuf").ServiceType, import("@bufbuild/protobuf").Message<import("@bufbuild/protobuf").AnyMessage>, import("@bufbuild/protobuf").Message<import("@bufbuild/protobuf").AnyMessage>, never, never> | import("packages/sdk-web-api/lib/grpc/subscription").ServerStreamSubscription<{
|
|
@@ -434,4 +435,4 @@ export declare const $activePromotionId: ($slStreamId: ReadableAtom<string | und
|
|
|
434
435
|
export { $userSummary, $leaderboardList } from './leaderboard';
|
|
435
436
|
export { $friends } from './friends';
|
|
436
437
|
export { $moderation } from './moderation';
|
|
437
|
-
export declare const $betPack: ($slStreamId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<Record<string, import("@bufbuild/protobuf").PlainMessage<import("@streamlayer/sl-eslib/interactive/interactive.common_pb").ExtendedQuestion>> | null, any>;
|
|
438
|
+
export declare const $betPack: ($slStreamId: ReadableAtom<string | undefined>, $slUserId: ReadableAtom<string | undefined>, $slOrganizationId: ReadableAtom<string | undefined>, storage: GamificationStorage, transport: Transport) => import("@nanostores/query").FetcherStore<Record<string, import("@bufbuild/protobuf").PlainMessage<import("@streamlayer/sl-eslib/interactive/interactive.common_pb").ExtendedQuestion>> | null, any>;
|
package/lib/queries/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { QuestionStatus, QuestionType } from '@streamlayer/sdk-web-types';
|
|
2
|
+
import { eventBus } from '@streamlayer/sdk-web-interfaces';
|
|
2
3
|
import { atom } from 'nanostores';
|
|
3
4
|
import { Feed } from '@streamlayer/sl-eslib/interactive/feed/interactive.feed_connect';
|
|
4
5
|
import { InteractiveAllowed } from '../background';
|
|
@@ -170,14 +171,14 @@ export const $activePromotionId = ($slStreamId, transport) => {
|
|
|
170
171
|
export { $userSummary, $leaderboardList } from './leaderboard';
|
|
171
172
|
export { $friends } from './friends';
|
|
172
173
|
export { $moderation } from './moderation';
|
|
173
|
-
export const $betPack = ($slStreamId, transport) => {
|
|
174
|
+
export const $betPack = ($slStreamId, $slUserId, $slOrganizationId, storage, transport) => {
|
|
174
175
|
const { client, queryKey } = transport.createPromiseClient(Feed, {
|
|
175
176
|
method: 'betPack',
|
|
176
|
-
params: [$slStreamId],
|
|
177
|
+
params: [$slStreamId, $slUserId, $slOrganizationId],
|
|
177
178
|
});
|
|
178
179
|
return transport.nanoquery.createFetcherStore(queryKey, {
|
|
179
|
-
fetcher: async (_, __, slStreamId) => {
|
|
180
|
-
if (!slStreamId) {
|
|
180
|
+
fetcher: async (_, __, slStreamId, userId, organizationId) => {
|
|
181
|
+
if (!slStreamId || !userId || !organizationId) {
|
|
181
182
|
return null;
|
|
182
183
|
}
|
|
183
184
|
const res = await client.betPack({
|
|
@@ -193,7 +194,28 @@ export const $betPack = ($slStreamId, transport) => {
|
|
|
193
194
|
if (!res.data || !res.data.length) {
|
|
194
195
|
return null;
|
|
195
196
|
}
|
|
196
|
-
return res.data?.reduce((acc, item) =>
|
|
197
|
+
return res.data?.reduce((acc, item) => {
|
|
198
|
+
const question = item?.attributes?.question;
|
|
199
|
+
if (!question?.id) {
|
|
200
|
+
return acc;
|
|
201
|
+
}
|
|
202
|
+
const flags = {
|
|
203
|
+
eventId: slStreamId,
|
|
204
|
+
userId: userId,
|
|
205
|
+
organizationId: organizationId,
|
|
206
|
+
};
|
|
207
|
+
if (!storage.isBetPackQuestionReceived(flags, question.id)) {
|
|
208
|
+
eventBus.emit('poll', {
|
|
209
|
+
action: 'received',
|
|
210
|
+
payload: {
|
|
211
|
+
questionId: question.id,
|
|
212
|
+
questionType: question.type,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
storage.saveBetPackQuestionReceived(flags, question.id);
|
|
216
|
+
}
|
|
217
|
+
return { ...acc, [question.id]: question };
|
|
218
|
+
}, {});
|
|
197
219
|
},
|
|
198
220
|
dedupeTime: 0,
|
|
199
221
|
refetchInterval: 0,
|
package/lib/storage.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ type UserProps = {
|
|
|
8
8
|
export declare class GamificationStorage extends Storage {
|
|
9
9
|
private sessionStorage;
|
|
10
10
|
constructor();
|
|
11
|
+
saveBetPackQuestionReceived: ({ userId, eventId, organizationId }: UserProps, questionId: string) => void;
|
|
12
|
+
isBetPackQuestionReceived: ({ userId, eventId, organizationId }: UserProps, questionId: string) => boolean;
|
|
13
|
+
saveQuestionOpened: ({ userId, eventId, organizationId }: UserProps, questionId: string) => void;
|
|
14
|
+
isQuestionOpened: ({ userId, eventId, organizationId }: UserProps, questionId: string) => boolean;
|
|
11
15
|
saveOnboardingStatus: ({ userId, eventId, organizationId }: UserProps, status: OnboardingStatus) => void;
|
|
12
16
|
getOnboardingStatus: ({ userId, eventId, organizationId }: UserProps) => string | undefined;
|
|
13
17
|
setOnboardingInstantOpen: ({ userId, eventId, organizationId }: UserProps) => void;
|
package/lib/storage.js
CHANGED
|
@@ -3,6 +3,8 @@ var KEY_PREFIX;
|
|
|
3
3
|
(function (KEY_PREFIX) {
|
|
4
4
|
KEY_PREFIX["ONBOARDING"] = "onboarding";
|
|
5
5
|
KEY_PREFIX["ONBOARDING_IO"] = "onboarding_io";
|
|
6
|
+
KEY_PREFIX["BET_PACK_ITEM_RECEIVED"] = "bp-item-received";
|
|
7
|
+
KEY_PREFIX["QUESTION_OPENED"] = "q-opened";
|
|
6
8
|
})(KEY_PREFIX || (KEY_PREFIX = {}));
|
|
7
9
|
class GamificationSessionStorage extends Storage {
|
|
8
10
|
constructor() {
|
|
@@ -21,6 +23,18 @@ export class GamificationStorage extends Storage {
|
|
|
21
23
|
super('gamification');
|
|
22
24
|
this.sessionStorage = new GamificationSessionStorage();
|
|
23
25
|
}
|
|
26
|
+
saveBetPackQuestionReceived = ({ userId, eventId, organizationId }, questionId) => {
|
|
27
|
+
this.write(KEY_PREFIX.BET_PACK_ITEM_RECEIVED, organizationId, userId, eventId, questionId);
|
|
28
|
+
};
|
|
29
|
+
isBetPackQuestionReceived = ({ userId, eventId, organizationId }, questionId) => {
|
|
30
|
+
return !!this.read(KEY_PREFIX.BET_PACK_ITEM_RECEIVED, organizationId, userId, eventId, questionId);
|
|
31
|
+
};
|
|
32
|
+
saveQuestionOpened = ({ userId, eventId, organizationId }, questionId) => {
|
|
33
|
+
this.write(KEY_PREFIX.QUESTION_OPENED, organizationId, userId, eventId, questionId);
|
|
34
|
+
};
|
|
35
|
+
isQuestionOpened = ({ userId, eventId, organizationId }, questionId) => {
|
|
36
|
+
return !!this.read(KEY_PREFIX.QUESTION_OPENED, organizationId, userId, eventId, questionId);
|
|
37
|
+
};
|
|
24
38
|
saveOnboardingStatus = ({ userId, eventId, organizationId }, status) => {
|
|
25
39
|
this.write(KEY_PREFIX.ONBOARDING, organizationId, userId, eventId, status);
|
|
26
40
|
};
|
package/lib/userSummary.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Transport } from '@streamlayer/sdk-web-api';
|
|
|
2
2
|
import { ReadableAtom } from 'nanostores';
|
|
3
3
|
import { LeaderboardSummaryItem } from '@streamlayer/sl-eslib/interactive/leaderboard/interactive.leaderboard_pb';
|
|
4
4
|
import { Gamification } from '.';
|
|
5
|
-
export declare const summary: ($eventId: ReadableAtom<string | undefined>, $userId: ReadableAtom<string | undefined>, $friends: Gamification["friends"], transport: Transport) => {
|
|
5
|
+
export declare const summary: ($eventId: ReadableAtom<string | undefined>, $userId: ReadableAtom<string | undefined>, $friends: Gamification["friends"] | undefined, transport: Transport) => {
|
|
6
6
|
$store: import("nanostores").MapStore<LeaderboardSummaryItem | undefined>;
|
|
7
7
|
invalidate: () => void;
|
|
8
8
|
};
|
package/lib/userSummary.js
CHANGED
|
@@ -10,9 +10,11 @@ export const summary = ($eventId, $userId, $friends, transport) => {
|
|
|
10
10
|
const eventId = $eventId.get();
|
|
11
11
|
const userId = $userId.get();
|
|
12
12
|
const usersIds = $friends
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
? $friends
|
|
14
|
+
.getStore()
|
|
15
|
+
.get()
|
|
16
|
+
.data?.map((friend) => friend.slId)
|
|
17
|
+
: [];
|
|
16
18
|
if (!usersIds) {
|
|
17
19
|
return;
|
|
18
20
|
}
|
|
@@ -28,12 +30,13 @@ export const summary = ($eventId, $userId, $friends, transport) => {
|
|
|
28
30
|
void refetch();
|
|
29
31
|
};
|
|
30
32
|
onMount($store, () => {
|
|
33
|
+
invalidate();
|
|
31
34
|
const cancelRefetchListener = $eventId.listen(refetch);
|
|
32
|
-
const cancelRefetchByFriendsListener = $friends
|
|
35
|
+
const cancelRefetchByFriendsListener = $friends?.listen(refetch);
|
|
33
36
|
const cancelRefetchByUserListener = $userId.listen(refetch);
|
|
34
37
|
return () => {
|
|
35
38
|
cancelRefetchListener();
|
|
36
|
-
cancelRefetchByFriendsListener();
|
|
39
|
+
cancelRefetchByFriendsListener?.();
|
|
37
40
|
cancelRefetchByUserListener();
|
|
38
41
|
};
|
|
39
42
|
});
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamlayer/feature-gamification",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@bufbuild/protobuf": "^1.10.0",
|
|
6
6
|
"@fastify/deepmerge": "^2.0.0",
|
|
7
7
|
"@streamlayer/sl-eslib": "^5.149.1",
|
|
8
8
|
"nanostores": "^0.10.3",
|
|
9
|
-
"@streamlayer/sdk-web-api": "^1.7.
|
|
10
|
-
"@streamlayer/sdk-web-
|
|
11
|
-
"@streamlayer/sdk-web-
|
|
12
|
-
"@streamlayer/sdk-web-logger": "^1.0.
|
|
13
|
-
"@streamlayer/sdk-web-notifications": "^1.3.
|
|
14
|
-
"@streamlayer/sdk-web-storage": "^1.0.
|
|
15
|
-
"@streamlayer/sdk-web-types": "^1.10.
|
|
9
|
+
"@streamlayer/sdk-web-api": "^1.7.3",
|
|
10
|
+
"@streamlayer/sdk-web-core": "^1.11.1",
|
|
11
|
+
"@streamlayer/sdk-web-interfaces": "^1.4.12",
|
|
12
|
+
"@streamlayer/sdk-web-logger": "^1.0.46",
|
|
13
|
+
"@streamlayer/sdk-web-notifications": "^1.3.8",
|
|
14
|
+
"@streamlayer/sdk-web-storage": "^1.0.46",
|
|
15
|
+
"@streamlayer/sdk-web-types": "^1.10.3"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"tslib": "^2.7.0"
|