@streamlayer/feature-gamification 0.37.3 → 0.38.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.
@@ -1,5 +1,6 @@
1
1
  import { ApiStore, type StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
2
  import type { GetApiResponseType } from '@streamlayer/sdk-web-api';
3
+ import { FeedItem } from '@streamlayer/sdk-web-types';
3
4
  import '@streamlayer/sdk-web-core/store';
4
5
  import { ReadableAtom, WritableAtom } from 'nanostores';
5
6
  import * as queries from './queries';
@@ -24,7 +25,10 @@ export declare class GamificationBackground {
24
25
  */
25
26
  interactiveAllowed: WritableAtom<InteractiveAllowed>;
26
27
  /** opened question, using to download statistics */
27
- openedQuestionId: WritableAtom<string | undefined>;
28
+ openedQuestionId: WritableAtom<{
29
+ questionId: string;
30
+ question?: FeedItem;
31
+ } | undefined>;
28
32
  /** opened question statistics */
29
33
  openedQuestion: ReturnType<typeof detail>;
30
34
  /** last active question in feed */
@@ -60,7 +64,7 @@ export declare class GamificationBackground {
60
64
  /**
61
65
  * Open question and mark notification for this question as viewed
62
66
  */
63
- openQuestion: (questionId: string) => void;
67
+ openQuestion: (questionId: string, question?: FeedItem) => void;
64
68
  /**
65
69
  * Close question and mark notification for this question as viewed
66
70
  */
package/lib/background.js CHANGED
@@ -53,10 +53,10 @@ export class GamificationBackground {
53
53
  this.feedList = new ApiStore(queries.$feedList(this.slStreamId, this.interactiveAllowed, instance.transport), 'gamification:feedList');
54
54
  this.activeQuestionId = new ApiStore(queries.$activeQuestion(this.slStreamId, instance.transport), 'gamification:activeQuestionId');
55
55
  this.openedQuestion = detail(instance.transport, this.openedQuestionId, this.feedList.getStore());
56
- this.openedQuestionId.listen((questionId) => {
57
- this.log.debug({ questionId }, 'received question');
58
- if (questionId) {
59
- this.questionSubscription = queries.questionSubscription(questionId, instance.transport);
56
+ this.openedQuestionId.listen((item) => {
57
+ this.log.debug({ item }, 'received question');
58
+ if (item?.questionId) {
59
+ this.questionSubscription = queries.questionSubscription(item.questionId, instance.transport);
60
60
  this.questionSubscription.addListener('feed-subscription-opened-question', (response) => {
61
61
  window.requestAnimationFrame(() => {
62
62
  this.openedQuestion.updateExtendedQuestion(response.data?.attributes?.question);
@@ -77,6 +77,7 @@ export class GamificationBackground {
77
77
  this.feedSubscription.addListener('feed-subscription-active-question', (response) => {
78
78
  const $activeQuestionId = this.activeQuestionId.getStore();
79
79
  if ($activeQuestionId) {
80
+ this.activeQuestionId;
80
81
  $activeQuestionId.mutate(response.data?.attributes);
81
82
  }
82
83
  });
@@ -124,8 +125,8 @@ export class GamificationBackground {
124
125
  /**
125
126
  * Open question and mark notification for this question as viewed
126
127
  */
127
- openQuestion = (questionId) => {
128
- this.openedQuestionId.set(questionId);
128
+ openQuestion = (questionId, question) => {
129
+ this.openedQuestionId.set({ questionId, question });
129
130
  this.notifications.markAsViewed(this.getCurrentSessionId({ prefix: 'notification', entity: questionId }));
130
131
  };
131
132
  /**
package/lib/detail.d.ts CHANGED
@@ -1,16 +1,18 @@
1
1
  import type { Transport } from '@streamlayer/sdk-web-api';
2
+ import { FeedItem, ExtendedQuestion } from '@streamlayer/sdk-web-types';
2
3
  import { ReadableAtom } from 'nanostores';
3
- import { getQuestionByUser } from './queries';
4
4
  import { type GamificationBackground } from './background';
5
- type ExtendedQuestion = Awaited<ReturnType<typeof getQuestionByUser>>;
6
5
  type ExtendedQuestionStore = {
7
6
  data?: ExtendedQuestion;
8
7
  loading?: boolean;
9
8
  error?: string;
10
9
  };
11
- export declare const detail: (transport: Transport, $openedQuestionId: ReadableAtom<string | undefined>, $feedList: ReturnType<GamificationBackground['feedList']['getStore']>) => {
12
- $store: ReadableAtom<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedItem | undefined>;
10
+ export declare const detail: (transport: Transport, $openedQuestionId: ReadableAtom<{
11
+ questionId: string;
12
+ question?: FeedItem;
13
+ } | undefined>, $feedList: ReturnType<GamificationBackground['feedList']['getStore']>) => {
14
+ $store: import("nanostores").WritableAtom<import("@bufbuild/protobuf").PlainMessage<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedItem> | undefined>;
13
15
  $extendedStore: import("nanostores").MapStore<ExtendedQuestionStore>;
14
- updateExtendedQuestion: (question: ExtendedQuestion) => void;
16
+ updateExtendedQuestion: (question: ExtendedQuestion | undefined) => void;
15
17
  };
16
18
  export {};
package/lib/detail.js CHANGED
@@ -1,6 +1,6 @@
1
- import { createMapStore } from '@streamlayer/sdk-web-interfaces';
1
+ import { createMapStore, createSingleStore } from '@streamlayer/sdk-web-interfaces';
2
2
  import { deepmerge } from '@fastify/deepmerge';
3
- import { computed } from 'nanostores';
3
+ import { onMount } from 'nanostores';
4
4
  import { getQuestionByUser } from './queries';
5
5
  const mergeArray = (options) => (target, source) => {
6
6
  let i = 0;
@@ -20,17 +20,42 @@ const mergeArray = (options) => (target, source) => {
20
20
  };
21
21
  const mergeQuestion = deepmerge({ mergeArray });
22
22
  export const detail = (transport, $openedQuestionId, $feedList) => {
23
- const $store = computed([$openedQuestionId, $feedList], (openedQuestion, feedList) => {
24
- if (openedQuestion) {
25
- const question = feedList.data?.find((item) => item.id === openedQuestion);
26
- if (question) {
27
- return question;
23
+ const $store = createSingleStore(undefined);
24
+ onMount($store, () => {
25
+ const cancel1 = $openedQuestionId.subscribe((openedQuestion) => {
26
+ if (openedQuestion) {
27
+ if (openedQuestion.question) {
28
+ $store.set(openedQuestion.question);
29
+ return;
30
+ }
31
+ const question = $feedList.get().data?.find((item) => item.id === openedQuestion.questionId);
32
+ if (question) {
33
+ $store.set(question);
34
+ }
35
+ else {
36
+ console.error('Feed list is not loaded yet. Issue with the opened question.');
37
+ }
28
38
  }
29
39
  else {
30
- console.error('Feed list is not loaded yet. Issue with the opened question.');
40
+ $store.set(undefined);
31
41
  }
32
- }
33
- return undefined;
42
+ });
43
+ const cancel2 = $feedList.subscribe((feedList) => {
44
+ const openedQuestion = $openedQuestionId.get();
45
+ if (feedList.data && openedQuestion) {
46
+ const question = $feedList.get().data?.find((item) => item.id === openedQuestion.questionId);
47
+ if (question) {
48
+ $store.set(question);
49
+ }
50
+ else {
51
+ console.error('Feed list is not loaded yet. Issue with the opened question.');
52
+ }
53
+ }
54
+ });
55
+ return () => {
56
+ cancel1();
57
+ cancel2();
58
+ };
34
59
  });
35
60
  const $extendedStore = createMapStore({
36
61
  data: undefined,
@@ -1,5 +1,5 @@
1
1
  import { AbstractFeature, ApiStore, FeatureSource, type FeatureProps, type StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
- import { type GamesOverlaySettings } from '@streamlayer/sdk-web-types';
2
+ import { type GamesOverlaySettings, FeedItem } from '@streamlayer/sdk-web-types';
3
3
  import type { GetApiResponseType } from '@streamlayer/sdk-web-api';
4
4
  import '@streamlayer/sdk-web-core/store';
5
5
  import type { PlainMessage } from '@bufbuild/protobuf';
@@ -57,7 +57,7 @@ export declare class Gamification extends AbstractFeature<'games', PlainMessage<
57
57
  disconnect: () => void;
58
58
  submitAnswer: (questionId: string, answerId: string) => Promise<void>;
59
59
  skipQuestion: (questionId: string) => Promise<void>;
60
- openQuestion: (questionId: string) => void;
60
+ openQuestion: (questionId: string, question?: FeedItem) => void;
61
61
  closeQuestion: (questionId?: string) => void;
62
62
  openUser: (userId: string) => void;
63
63
  closeUser: () => void;
@@ -114,6 +114,7 @@ export class Gamification extends AbstractFeature {
114
114
  }
115
115
  window.requestAnimationFrame(async () => {
116
116
  const question = response.data?.attributes?.question;
117
+ const feedItem = response.data?.attributes?.feedItem;
117
118
  if (!question) {
118
119
  return;
119
120
  }
@@ -125,11 +126,11 @@ export class Gamification extends AbstractFeature {
125
126
  const question = await queries.getQuestionByUser(id, transport);
126
127
  const correctAnswer = question?.answers.find(({ correct }) => correct);
127
128
  const votedAnswer = question?.answers.find(({ youVoted }) => youVoted);
128
- if (!votedAnswer)
129
+ if (!votedAnswer || !question)
129
130
  return;
130
131
  this.notifications.add({
131
132
  type: NotificationType.QUESTION_RESOLVED,
132
- action: () => this.openQuestion(id),
133
+ action: () => this.openQuestion(question.id, feedItem),
133
134
  close: () => this.closeQuestion(id),
134
135
  autoHideDuration: correctAnswer?.youVoted ? 15000 : 12000,
135
136
  id: notificationId,
@@ -174,13 +175,12 @@ export class Gamification extends AbstractFeature {
174
175
  this.feedList.invalidate();
175
176
  this.userSummary.invalidate();
176
177
  };
177
- openQuestion = (questionId) => {
178
- this.openFeature();
178
+ openQuestion = (questionId, question) => {
179
179
  this.notifications.close(this.background.getCurrentSessionId({
180
180
  prefix: 'notification',
181
181
  entity: questionId,
182
182
  }));
183
- return this.background.openQuestion(questionId);
183
+ return this.background.openQuestion(questionId, question);
184
184
  };
185
185
  closeQuestion = (questionId) => {
186
186
  return this.background.closeQuestion(questionId);
@@ -204,15 +204,13 @@ export class Gamification extends AbstractFeature {
204
204
  if (question.data.question?.id !== undefined &&
205
205
  question.data.question.notification !== undefined &&
206
206
  question.data.question?.bypassNotifications?.inAppSilence !== SilenceSetting.ON &&
207
- question.data.question.status === QuestionStatus.ACTIVE) {
207
+ question.data.question.status === QuestionStatus.ACTIVE &&
208
+ !question.data.question.marketClosed) {
208
209
  if (InteractiveQuestionTypes.has(question.data.question.type)) {
209
210
  if (this.isInteractiveAllowed) {
210
211
  this.notifications.add({
211
212
  type: NotificationType.QUESTION,
212
- action: () => {
213
- question.data?.question && this.openQuestion(question.data.question.id);
214
- this.openFeature();
215
- },
213
+ action: () => question.data?.question && this.openQuestion(question.data.question.id, question.data.feedItem),
216
214
  close: () => question.data?.question && this.closeQuestion(question.data.question.id),
217
215
  autoHideDuration: 1000 * 60,
218
216
  id: this.background.getCurrentSessionId({
@@ -233,7 +231,7 @@ export class Gamification extends AbstractFeature {
233
231
  const instantView = {
234
232
  heading: question.data.question.notification.title,
235
233
  body: question.data.question.notification.body,
236
- imageMode: question.data.question.appearance?.images,
234
+ imageMode: optionsValue.imageMode,
237
235
  image: optionsValue?.image,
238
236
  video: {
239
237
  id: optionsValue?.video?.id || '',
@@ -247,10 +245,7 @@ export class Gamification extends AbstractFeature {
247
245
  };
248
246
  this.notifications.add({
249
247
  type: NotificationType.QUESTION,
250
- action: () => {
251
- question.data?.question && this.openQuestion(question.data.question.id);
252
- this.openFeature();
253
- },
248
+ action: () => question.data?.question && this.openQuestion(question.data.question.id, question.data.feedItem),
254
249
  close: () => question.data?.question && this.closeQuestion(question.data.question.id),
255
250
  autoHideDuration: 1000 * 120,
256
251
  id: this.background.getCurrentSessionId({ prefix: 'notification', entity: question.data.question.id }),
@@ -269,13 +264,11 @@ export class Gamification extends AbstractFeature {
269
264
  account: optionsValue?.tweetMeta?.account || '',
270
265
  accountVerified: !!optionsValue?.tweetMeta?.accountVerified,
271
266
  tweet: optionsValue?.tweetMeta?.tweet,
267
+ tweetId: question.data.question.id,
272
268
  };
273
269
  this.notifications.add({
274
270
  type: NotificationType.QUESTION,
275
- action: () => {
276
- question.data?.question && this.openQuestion(question.data.question.id);
277
- this.openFeature();
278
- },
271
+ action: () => question.data?.question && this.openQuestion(question.data.question.id, question.data.feedItem),
279
272
  close: () => question.data?.question && this.closeQuestion(question.data.question.id),
280
273
  autoHideDuration: 1000 * 120,
281
274
  id: this.background.getCurrentSessionId({ prefix: 'notification', entity: question.data.question.id }),
@@ -1,13 +1,16 @@
1
1
  import { Feed } from '@streamlayer/sl-eslib/interactive/feed/interactive.feed_connect';
2
2
  export const submitAnswer = (transport, data) => {
3
- const { client } = transport.createPromiseClient(Feed, { method: 'submitAnswer' });
4
- return client.submitAnswer({ data });
3
+ const { client, createRequestOptions } = transport.createPromiseClient(Feed, { method: 'submitAnswer' });
4
+ const contextValues = createRequestOptions({ retryAttempts: 0 });
5
+ return client.submitAnswer({ data }, { contextValues });
5
6
  };
6
7
  export const submitInplay = (transport, eventId) => {
7
- const { client } = transport.createPromiseClient(Feed, { method: 'submitInplay' });
8
- return client.submitInplay({ data: { eventId: +eventId } });
8
+ const { client, createRequestOptions } = transport.createPromiseClient(Feed, { method: 'submitInplay' });
9
+ const contextValues = createRequestOptions({ retryAttempts: 0 });
10
+ return client.submitInplay({ data: { eventId: +eventId } }, { contextValues });
9
11
  };
10
12
  export const skipQuestion = (transport, questionId) => {
11
- const { client } = transport.createPromiseClient(Feed, { method: 'skipQuestion' });
12
- return client.skipQuestion({ data: { id: questionId } });
13
+ const { client, createRequestOptions } = transport.createPromiseClient(Feed, { method: 'skipQuestion' });
14
+ const contextValues = createRequestOptions({ retryAttempts: 0 });
15
+ return client.skipQuestion({ data: { id: questionId } }, { contextValues });
13
16
  };
@@ -327,9 +327,9 @@ export declare const questionSubscription: (questionId: string, transport: Trans
327
327
  };
328
328
  };
329
329
  }, QuestionSubscriptionRequest, QuestionSubscriptionResponse, "subscription" | "votingSubscription" | "questionSubscription" | "feedSubscription", ((request: import("@bufbuild/protobuf").PartialMessage<SubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions | undefined) => AsyncIterable<SubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<VotingSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions | undefined) => AsyncIterable<VotingSubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<QuestionSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions | undefined) => AsyncIterable<QuestionSubscriptionResponse>) | ((request: import("@bufbuild/protobuf").PartialMessage<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionRequest>, options?: import("@connectrpc/connect").CallOptions | undefined) => AsyncIterable<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionResponse>)>;
330
- export declare const getQuestionByUser: (questionId: string, transport: Transport) => Promise<import("@streamlayer/sdk-web-types").ExtendedQuestion | undefined>;
330
+ export declare const getQuestionByUser: (questionId: string, transport: Transport) => Promise<import("@streamlayer/sl-eslib/interactive/interactive.common_pb").ExtendedQuestion | undefined>;
331
331
  export declare const getQuestionDetail: (questionId: string, transport: Transport) => Promise<import("@streamlayer/sl-eslib/interactive/interactive.common_pb").Question | undefined>;
332
- export declare const $questionByUser: ($questionId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<import("@streamlayer/sdk-web-types").ExtendedQuestion | undefined, any>;
332
+ export declare const $questionByUser: ($questionId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<import("@streamlayer/sl-eslib/interactive/interactive.common_pb").ExtendedQuestion | undefined, any>;
333
333
  export declare const $pickHistory: (slStreamId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<(import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").PickHistory | undefined)[], any>;
334
334
  export declare const $feedList: ($slStreamId: ReadableAtom<string | undefined>, $interactiveAllowed: ReadableAtom<InteractiveAllowed>, transport: Transport) => import("@nanostores/query").FetcherStore<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedItem[], any>;
335
335
  export { $userSummary, $leaderboardList } from './leaderboard';
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@streamlayer/feature-gamification",
3
- "version": "0.37.3",
3
+ "version": "0.38.0",
4
4
  "peerDependencies": {
5
- "@bufbuild/protobuf": "^1.6.0",
5
+ "@bufbuild/protobuf": "^1.7.2",
6
6
  "@fastify/deepmerge": "*",
7
- "@streamlayer/sl-eslib": "^5.67.0",
7
+ "@streamlayer/sl-eslib": "^5.79.3",
8
8
  "nanostores": "^0.9.5",
9
- "@streamlayer/sdk-web-api": "^0.22.0",
10
- "@streamlayer/sdk-web-core": "^0.21.1",
11
- "@streamlayer/sdk-web-interfaces": "^0.20.5",
12
- "@streamlayer/sdk-web-logger": "^0.5.16",
13
- "@streamlayer/sdk-web-notifications": "^0.14.1",
14
- "@streamlayer/sdk-web-storage": "^0.4.3",
15
- "@streamlayer/sdk-web-types": "^0.22.3"
9
+ "@streamlayer/sdk-web-api": "^0.23.0",
10
+ "@streamlayer/sdk-web-core": "^0.21.3",
11
+ "@streamlayer/sdk-web-interfaces": "^0.20.6",
12
+ "@streamlayer/sdk-web-logger": "^0.5.17",
13
+ "@streamlayer/sdk-web-notifications": "^0.14.2",
14
+ "@streamlayer/sdk-web-storage": "^0.4.4",
15
+ "@streamlayer/sdk-web-types": "^0.22.4"
16
16
  },
17
17
  "devDependencies": {
18
18
  "tslib": "^2.6.2"