@streamlayer/feature-gamification 1.13.3 → 1.14.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.
@@ -51,30 +51,31 @@ export const advertisement = ($slStreamId, $feedSubscription, instance) => {
51
51
  const $store = createMapStore({});
52
52
  const $activeAdvertisement = $activePromotionId($slStreamId, transport);
53
53
  const open = (options) => {
54
+ const payload = $store.get();
55
+ const id = payload.data?.question.id;
56
+ const type = payload.data?.promotion?.type;
57
+ if (!id) {
58
+ return;
59
+ }
60
+ eventBus.emit('advertisement', {
61
+ action: 'opened',
62
+ payload: {
63
+ id,
64
+ type,
65
+ hasBanner: adHasBanner(payload.data),
66
+ openedFrom: options?.fromNotification ? 'notification' : 'auto',
67
+ },
68
+ });
54
69
  if (options?.fromNotification) {
55
70
  $store.setKey('hasNotification', false);
56
- const payload = $store.get();
57
- const id = payload.data?.question.id;
58
- const type = payload.data?.promotion?.type;
59
- if (id) {
60
- eventBus.emit('advertisement', {
61
- action: 'opened',
62
- payload: {
63
- id,
64
- type,
65
- hasBanner: adHasBanner(payload.data),
66
- openedFrom: 'notification',
67
- },
68
- });
69
- eventBus.emit('advertisement', {
70
- action: 'notification-opened',
71
- payload: {
72
- id,
73
- type,
74
- hasBanner: adHasBanner(payload.data),
75
- },
76
- });
77
- }
71
+ eventBus.emit('advertisement', {
72
+ action: 'notification-opened',
73
+ payload: {
74
+ id,
75
+ type,
76
+ hasBanner: adHasBanner(payload.data),
77
+ },
78
+ });
78
79
  }
79
80
  };
80
81
  const markAsViewed = () => {
@@ -37,6 +37,7 @@ export declare class GamificationBackground {
37
37
  /** last active question in feed */
38
38
  activeQuestionId: ReturnType<typeof queries.$activeQuestion>;
39
39
  feedList: ApiStore<GetApiResponseType<typeof queries.$feedList>>;
40
+ betPack: ApiStore<GetApiResponseType<typeof queries.$betPack>>;
40
41
  /** moderation id */
41
42
  moderationId: ReadableAtom<string | undefined>;
42
43
  /** moderation */
package/lib/background.js CHANGED
@@ -32,6 +32,7 @@ export class GamificationBackground {
32
32
  /** last active question in feed */
33
33
  activeQuestionId;
34
34
  feedList;
35
+ betPack;
35
36
  /** moderation id */
36
37
  moderationId;
37
38
  /** moderation */
@@ -57,6 +58,7 @@ export class GamificationBackground {
57
58
  this.notifications = instance.notifications;
58
59
  this.moderation = new ApiStore(queries.$moderation(this.slStreamId, instance.transport), 'gamification:moderation');
59
60
  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');
60
62
  this.activeQuestionId = queries.$activeQuestion(this.slStreamId, instance.transport);
61
63
  this.openedQuestion = detail(instance.transport, this.openedQuestionId, this.feedList.getStore());
62
64
  this.cancels.add(this.openedQuestionId.listen((item) => {
@@ -78,6 +80,33 @@ export class GamificationBackground {
78
80
  }
79
81
  }));
80
82
  this.feedSubscription = queries.feedSubscription(this.slStreamId, instance.transport);
83
+ this.cancels.add(this.feedSubscription.addListener('bet-pack-update', async (response) => {
84
+ const question = response.data?.attributes?.question;
85
+ if (question?.type === QuestionType.PREDICTION) {
86
+ const betPackData = this.betPack.getValues().data || {};
87
+ const betPackItem = betPackData?.[question.id];
88
+ if (betPackItem || Object.keys(betPackData).length < 5) {
89
+ const data = queries.$questionByUser(question.id, this.transport);
90
+ // order of operations is important here
91
+ const cancel = data.subscribe(() => { });
92
+ await data.get().promise;
93
+ // if question is not in the feed list, get extended question data from the server
94
+ let extendedQuestion = data.get().data;
95
+ if (!extendedQuestion) {
96
+ extendedQuestion = await queries.questionByUser(question.id, this.transport);
97
+ }
98
+ cancel();
99
+ window.requestAnimationFrame(() => {
100
+ data.invalidate();
101
+ });
102
+ // get extended question data and mark as dirty
103
+ this.betPack.getStore().mutate({
104
+ ...betPackData,
105
+ [question.id]: extendedQuestion,
106
+ });
107
+ }
108
+ }
109
+ }));
81
110
  this.cancels.add(this.feedSubscription.addListener('feed-subscription-active-question', (response) => {
82
111
  const activeQuestionId = this.activeQuestionId.get().data?.question?.id;
83
112
  const question = response.data?.attributes?.question;
@@ -25,6 +25,8 @@ export declare class Gamification extends AbstractFeature<'games', PlainMessage<
25
25
  userSummary: ReturnType<typeof summary>;
26
26
  /** feed list (pick history) */
27
27
  feedList: ApiStore<GetApiResponseType<typeof queries.$feedList>>;
28
+ /** list (pack) of predefined questions */
29
+ betPack: ApiStore<GetApiResponseType<typeof queries.$betPack>>;
28
30
  /** friends list */
29
31
  friends: ApiStore<GetApiResponseType<typeof queries.$friends>>;
30
32
  /** pinned leaderboard id */
@@ -65,6 +67,7 @@ export declare class Gamification extends AbstractFeature<'games', PlainMessage<
65
67
  checkInteractiveFlag: () => void;
66
68
  connect: () => void;
67
69
  disconnect: () => void;
70
+ betPackVote: (questionId: string, answerId: string) => Promise<void>;
68
71
  submitAnswer: (questionId: string, answerId: string) => Promise<void>;
69
72
  openQuestion: (questionId?: string, question?: FeedItem & {
70
73
  openedFrom?: "list" | "notification";
@@ -29,6 +29,8 @@ export class Gamification extends AbstractFeature {
29
29
  userSummary;
30
30
  /** feed list (pick history) */
31
31
  feedList;
32
+ /** list (pack) of predefined questions */
33
+ betPack;
32
34
  /** friends list */
33
35
  friends;
34
36
  /** pinned leaderboard id */
@@ -71,6 +73,7 @@ export class Gamification extends AbstractFeature {
71
73
  this.openedQuestionId = this.background.openedQuestionId;
72
74
  this.storage = new GamificationStorage();
73
75
  this.feedList = this.background.feedList;
76
+ this.betPack = this.background.betPack;
74
77
  this.friends = new ApiStore(queries.$friends(this.background.userId, instance.transport), 'gamification:friends');
75
78
  this.currentUserId = this.background.userId;
76
79
  this.openedUser = createSingleStore(undefined);
@@ -325,6 +328,33 @@ export class Gamification extends AbstractFeature {
325
328
  this.background.feedSubscription.removeListener('feed-subscription-prediction-close');
326
329
  this.background.feedSubscription.removeListener('feed-subscription-questions-list');
327
330
  };
331
+ betPackVote = async (questionId, answerId) => {
332
+ await this.submitAnswer(questionId, answerId);
333
+ const betPackList = { ...this.betPack.getValues().data };
334
+ const question = betPackList?.[questionId];
335
+ 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
+ eventBus.emit('poll', {
346
+ action: 'voted',
347
+ payload: {
348
+ questionId,
349
+ questionType: question.type,
350
+ },
351
+ });
352
+ this.betPack.getStore().mutate({
353
+ ...betPackList,
354
+ [questionId]: question,
355
+ });
356
+ }
357
+ };
328
358
  submitAnswer = async (questionId, answerId) => {
329
359
  const data = $questionByUser(questionId, this.transport);
330
360
  const cancel = data.subscribe(() => { });
@@ -116,6 +116,12 @@ export declare const feedSubscription: ($slStreamId: ReadableAtom<string | undef
116
116
  readonly O: typeof import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionResponse;
117
117
  readonly kind: import("@bufbuild/protobuf").MethodKind.ServerStreaming;
118
118
  };
119
+ readonly betPack: {
120
+ readonly name: "BetPack";
121
+ readonly I: typeof import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").BetPackRequest;
122
+ readonly O: typeof import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").BetPackResponse;
123
+ readonly kind: import("@bufbuild/protobuf").MethodKind.Unary;
124
+ };
119
125
  };
120
126
  }, SubscriptionRequest, SubscriptionResponse, "subscription" | "votingSubscription" | "questionSubscription" | "feedSubscription", ((request: import("@bufbuild/protobuf").PartialMessage<SubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<SubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<VotingSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<VotingSubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<QuestionSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<QuestionSubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionResponse>)>;
121
127
  export declare const votingSubscription: (params: {
@@ -232,6 +238,12 @@ export declare const votingSubscription: (params: {
232
238
  readonly O: typeof import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionResponse;
233
239
  readonly kind: import("@bufbuild/protobuf").MethodKind.ServerStreaming;
234
240
  };
241
+ readonly betPack: {
242
+ readonly name: "BetPack";
243
+ readonly I: typeof import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").BetPackRequest;
244
+ readonly O: typeof import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").BetPackResponse;
245
+ readonly kind: import("@bufbuild/protobuf").MethodKind.Unary;
246
+ };
235
247
  };
236
248
  }, VotingSubscriptionRequest, VotingSubscriptionResponse, "subscription" | "votingSubscription" | "questionSubscription" | "feedSubscription", ((request: import("@bufbuild/protobuf").PartialMessage<SubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<SubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<VotingSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<VotingSubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<QuestionSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<QuestionSubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionResponse>)>;
237
249
  export declare const questionSubscription: (questionId: string, 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<{
@@ -345,6 +357,12 @@ export declare const questionSubscription: (questionId: string, transport: Trans
345
357
  readonly O: typeof import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionResponse;
346
358
  readonly kind: import("@bufbuild/protobuf").MethodKind.ServerStreaming;
347
359
  };
360
+ readonly betPack: {
361
+ readonly name: "BetPack";
362
+ readonly I: typeof import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").BetPackRequest;
363
+ readonly O: typeof import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").BetPackResponse;
364
+ readonly kind: import("@bufbuild/protobuf").MethodKind.Unary;
365
+ };
348
366
  };
349
367
  }, QuestionSubscriptionRequest, QuestionSubscriptionResponse, "subscription" | "votingSubscription" | "questionSubscription" | "feedSubscription", ((request: import("@bufbuild/protobuf").PartialMessage<SubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<SubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<VotingSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<VotingSubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<QuestionSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<QuestionSubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions) => AsyncIterable<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionResponse>)>;
350
368
  export declare const getQuestionByUser: (questionId: string, transport: Transport) => Promise<import("@streamlayer/sl-eslib/interactive/interactive.common_pb").ExtendedQuestion | undefined>;
@@ -416,3 +434,4 @@ export declare const $activePromotionId: ($slStreamId: ReadableAtom<string | und
416
434
  export { $userSummary, $leaderboardList } from './leaderboard';
417
435
  export { $friends } from './friends';
418
436
  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>;
@@ -170,3 +170,32 @@ export const $activePromotionId = ($slStreamId, transport) => {
170
170
  export { $userSummary, $leaderboardList } from './leaderboard';
171
171
  export { $friends } from './friends';
172
172
  export { $moderation } from './moderation';
173
+ export const $betPack = ($slStreamId, transport) => {
174
+ const { client, queryKey } = transport.createPromiseClient(Feed, {
175
+ method: 'betPack',
176
+ params: [$slStreamId],
177
+ });
178
+ return transport.nanoquery.createFetcherStore(queryKey, {
179
+ fetcher: async (_, __, slStreamId) => {
180
+ if (!slStreamId) {
181
+ return null;
182
+ }
183
+ const res = await client.betPack({
184
+ eventId: slStreamId,
185
+ types: [QuestionType.PREDICTION],
186
+ statuses: [QuestionStatus.ACTIVE, QuestionStatus.RESOLVED],
187
+ limit: 5,
188
+ sort: {
189
+ field: 'activated_at',
190
+ order: 'asc',
191
+ },
192
+ });
193
+ if (!res.data || !res.data.length) {
194
+ return null;
195
+ }
196
+ return res.data?.reduce((acc, item) => item?.attributes?.question?.id ? { ...acc, [item.attributes.question.id]: item.attributes.question } : acc, {});
197
+ },
198
+ dedupeTime: 0,
199
+ refetchInterval: 0,
200
+ });
201
+ };
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@streamlayer/feature-gamification",
3
- "version": "1.13.3",
3
+ "version": "1.14.0",
4
4
  "peerDependencies": {
5
5
  "@bufbuild/protobuf": "^1.10.0",
6
6
  "@fastify/deepmerge": "^2.0.0",
7
- "@streamlayer/sl-eslib": "^5.130.0",
7
+ "@streamlayer/sl-eslib": "^5.149.1",
8
8
  "nanostores": "^0.10.3",
9
- "@streamlayer/sdk-web-api": "^1.6.20",
10
- "@streamlayer/sdk-web-interfaces": "^1.4.7",
11
- "@streamlayer/sdk-web-core": "^1.9.3",
12
- "@streamlayer/sdk-web-logger": "^1.0.41",
13
- "@streamlayer/sdk-web-notifications": "^1.3.3",
14
- "@streamlayer/sdk-web-storage": "^1.0.41",
15
- "@streamlayer/sdk-web-types": "^1.9.3"
9
+ "@streamlayer/sdk-web-api": "^1.7.0",
10
+ "@streamlayer/sdk-web-core": "^1.10.0",
11
+ "@streamlayer/sdk-web-interfaces": "^1.4.9",
12
+ "@streamlayer/sdk-web-logger": "^1.0.43",
13
+ "@streamlayer/sdk-web-notifications": "^1.3.5",
14
+ "@streamlayer/sdk-web-storage": "^1.0.43",
15
+ "@streamlayer/sdk-web-types": "^1.10.0"
16
16
  },
17
17
  "devDependencies": {
18
18
  "tslib": "^2.7.0"