@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 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
- const $activeQuestionId = this.activeQuestionId.getStore();
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
  };
@@ -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: ApiStore<GetApiResponseType<typeof queries.$userSummary>>;
25
+ userSummary: ReturnType<typeof summary>;
25
26
  /** feed list (pick history) */
26
27
  feedList: ApiStore<GetApiResponseType<typeof queries.$feedList>>;
27
28
  /** friends list */
@@ -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?.data?.summary) {
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] = feedItem;
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.imageMode,
418
+ imageMode: optionsValue?.imageMode,
398
419
  image: optionsValue?.image,
399
420
  video: {
400
421
  id: optionsValue?.video?.id || '',
@@ -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: true,
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
- const eventId = $eventId.get();
70
- if (pagination.page > 0 && eventId) {
71
- if (pagination.page < maxPage) {
72
- $store.setKey('loading', true);
73
- const request = {
74
- eventId: eventId,
75
- pagination,
76
- };
77
- const newData = await fetch(request);
78
- const prevData = $store.get().data || [];
79
- $store.set({
80
- data: [...prevData, ...newData.data.map((item) => item.attributes)],
81
- key: $store.get().key,
82
- loading: false,
83
- hasMore: true,
84
- });
85
- }
86
- else {
87
- $store.setKey('hasMore', false);
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.0",
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.0",
10
- "@streamlayer/sdk-web-core": "^0.22.0",
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",