@streamlayer/feature-gamification 0.19.4 → 0.20.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.
@@ -3,6 +3,7 @@ import type { GetApiResponseType } from '@streamlayer/sdk-web-api';
3
3
  import '@streamlayer/sdk-web-core/store';
4
4
  import { ReadableAtom, WritableAtom } from 'nanostores';
5
5
  import * as queries from './queries';
6
+ import { detail } from './detail';
6
7
  /**
7
8
  * get GamificationBackground singleton
8
9
  */
@@ -20,9 +21,10 @@ export declare class GamificationBackground {
20
21
  /** opened question, using to download statistics */
21
22
  openedQuestionId: WritableAtom<string | undefined>;
22
23
  /** opened question statistics */
23
- openedQuestion: ApiStore<GetApiResponseType<typeof queries.$questionByUser>>;
24
+ openedQuestion: ReturnType<typeof detail>;
24
25
  /** last active question in feed */
25
26
  activeQuestionId: ApiStore<GetApiResponseType<typeof queries.$activeQuestion>>;
27
+ feedList: ApiStore<GetApiResponseType<typeof queries.$feedList>>;
26
28
  /** moderation id */
27
29
  moderationId: ReadableAtom<string | undefined>;
28
30
  /** moderation */
package/lib/background.js CHANGED
@@ -2,6 +2,7 @@ import { ApiStore, SingleStore, createSingleStore } from '@streamlayer/sdk-web-i
2
2
  import { createLogger } from '@streamlayer/sdk-web-logger';
3
3
  import '@streamlayer/sdk-web-core/store';
4
4
  import * as queries from './queries';
5
+ import { detail } from './detail';
5
6
  /**
6
7
  * get GamificationBackground singleton
7
8
  */
@@ -27,6 +28,7 @@ export class GamificationBackground {
27
28
  openedQuestion;
28
29
  /** last active question in feed */
29
30
  activeQuestionId;
31
+ feedList;
30
32
  /** moderation id */
31
33
  moderationId;
32
34
  /** moderation */
@@ -49,15 +51,16 @@ export class GamificationBackground {
49
51
  this.openedQuestionId = new SingleStore(createSingleStore(undefined), 'openedQuestionId').getStore();
50
52
  this.notifications = instance.notifications;
51
53
  this.moderation = new ApiStore(queries.$moderation(this.slStreamId, instance.transport), 'gamification:moderation');
54
+ this.feedList = new ApiStore(queries.$feedList(this.slStreamId, instance.transport), 'gamification:feedList');
52
55
  this.activeQuestionId = new ApiStore(queries.$activeQuestion(this.slStreamId, instance.transport), 'gamification:activeQuestionId');
53
- this.openedQuestion = new ApiStore(queries.$questionByUser(this.openedQuestionId, instance.transport), 'gamification:openedQuestion');
56
+ this.openedQuestion = detail(instance.transport, this.openedQuestionId, this.feedList.getStore());
54
57
  this.openedQuestionId.listen((questionId) => {
55
58
  this.log.debug({ questionId }, 'received question');
56
59
  if (questionId) {
57
60
  this.questionSubscription = queries.questionSubscription(questionId, instance.transport);
58
61
  this.questionSubscription.addListener('feed-subscription-opened-question', (response) => {
59
62
  window.requestAnimationFrame(() => {
60
- this.openedQuestion.getStore().mutate(response.data?.attributes?.question);
63
+ this.openedQuestion.updateExtendedQuestion(response.data?.attributes?.question);
61
64
  });
62
65
  });
63
66
  this.questionSubscription.connect();
@@ -0,0 +1,16 @@
1
+ import type { Transport } from '@streamlayer/sdk-web-api';
2
+ import { ReadableAtom } from 'nanostores';
3
+ import { getQuestionByUser } from './queries';
4
+ import { type GamificationBackground } from './background';
5
+ type ExtendedQuestion = Awaited<ReturnType<typeof getQuestionByUser>>;
6
+ type ExtendedQuestionStore = {
7
+ data?: ExtendedQuestion;
8
+ loading?: boolean;
9
+ error?: string;
10
+ };
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>;
13
+ $extendedStore: import("nanostores").MapStore<ExtendedQuestionStore>;
14
+ updateExtendedQuestion: (question: ExtendedQuestion) => void;
15
+ };
16
+ export {};
package/lib/detail.js ADDED
@@ -0,0 +1,33 @@
1
+ import { createMapStore } from '@streamlayer/sdk-web-interfaces';
2
+ import { computed } from 'nanostores';
3
+ import { getQuestionByUser } from './queries';
4
+ export const detail = (transport, $openedQuestionId, $feedList) => {
5
+ const $store = computed([$openedQuestionId, $feedList], (openedQuestion, feedList) => {
6
+ if (openedQuestion) {
7
+ if (feedList.data) {
8
+ return feedList.data.find((item) => item.id === openedQuestion);
9
+ }
10
+ }
11
+ return undefined;
12
+ });
13
+ const $extendedStore = createMapStore({
14
+ data: undefined,
15
+ loading: undefined,
16
+ error: undefined,
17
+ });
18
+ $store.subscribe(async (item) => {
19
+ if (item) {
20
+ if (item.type === 'question') {
21
+ $extendedStore.setKey('loading', true);
22
+ const question = await getQuestionByUser(item.id, transport);
23
+ $extendedStore.set({ data: question, loading: false });
24
+ return;
25
+ }
26
+ }
27
+ $extendedStore.set({ data: undefined, loading: false });
28
+ });
29
+ const updateExtendedQuestion = (question) => {
30
+ $extendedStore.set({ data: question });
31
+ };
32
+ return { $store, $extendedStore, updateExtendedQuestion };
33
+ };
@@ -1,11 +1,12 @@
1
- import { AbstractFeature, ApiStore, FeatureSource, type FeatureProps, type StreamLayerContext, createComputedStore } from '@streamlayer/sdk-web-interfaces';
2
- import { type GamesOverlaySettings, ExtendedQuestion } from '@streamlayer/sdk-web-types';
1
+ import { AbstractFeature, ApiStore, FeatureSource, type FeatureProps, type StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
+ import { type GamesOverlaySettings } 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';
6
6
  import { WritableAtom } from 'nanostores';
7
7
  import * as queries from './queries';
8
8
  import { leaderboard } from './leaderboard';
9
+ import { GamificationBackground } from './';
9
10
  /**
10
11
  * Required: in-app should be displayed and questions not available
11
12
  * Optional: in-app should be displayed but questions are available
@@ -33,8 +34,8 @@ export declare enum OnboardingStatus {
33
34
  export declare class Gamification extends AbstractFeature<'games', PlainMessage<GamesOverlaySettings>> {
34
35
  /** user statistics (leaderboard panel) */
35
36
  userSummary: ApiStore<GetApiResponseType<typeof queries.$userSummary>>;
36
- /** questions list (pick history) */
37
- questions: ApiStore<GetApiResponseType<typeof queries.$pickHistory>>;
37
+ /** feed list (pick history) */
38
+ feedList: ApiStore<GetApiResponseType<typeof queries.$feedList>>;
38
39
  /** pinned leaderboard id */
39
40
  leaderboardId: WritableAtom<string | undefined>;
40
41
  /** leaderboard list */
@@ -42,10 +43,7 @@ export declare class Gamification extends AbstractFeature<'games', PlainMessage<
42
43
  /** onboarding status */
43
44
  onboardingStatus: WritableAtom<OnboardingStatus | undefined>;
44
45
  /** opened question */
45
- openedQuestion: ReturnType<typeof createComputedStore<{
46
- loading: boolean;
47
- data?: ExtendedQuestion;
48
- }>>;
46
+ openedQuestion: GamificationBackground['openedQuestion'];
49
47
  private notifications;
50
48
  private transport;
51
49
  private closeFeature;
@@ -1,4 +1,4 @@
1
- import { AbstractFeature, ApiStore, FeatureStatus, SingleStore, createSingleStore, createComputedStore, } from '@streamlayer/sdk-web-interfaces';
1
+ import { AbstractFeature, ApiStore, FeatureStatus, SingleStore, createSingleStore, } from '@streamlayer/sdk-web-interfaces';
2
2
  import { QuestionStatus, QuestionType, FeatureType, SilenceSetting, } from '@streamlayer/sdk-web-types';
3
3
  import { NotificationType } from '@streamlayer/sdk-web-notifications';
4
4
  import '@streamlayer/sdk-web-core/store';
@@ -36,8 +36,8 @@ export var OnboardingStatus;
36
36
  export class Gamification extends AbstractFeature {
37
37
  /** user statistics (leaderboard panel) */
38
38
  userSummary;
39
- /** questions list (pick history) */
40
- questions;
39
+ /** feed list (pick history) */
40
+ feedList;
41
41
  /** pinned leaderboard id */
42
42
  leaderboardId;
43
43
  /** leaderboard list */
@@ -59,19 +59,14 @@ export class Gamification extends AbstractFeature {
59
59
  this.background = gamificationBackground(instance);
60
60
  this.storage = new GamificationStorage();
61
61
  this.userSummary = new ApiStore(queries.$userSummary(this.background.slStreamId, this.background.userId, instance.transport), 'gamification:userSummary');
62
- this.questions = new ApiStore(queries.$pickHistory(this.background.slStreamId, instance.transport), 'gamification:questions');
62
+ this.feedList = this.background.feedList;
63
63
  this.leaderboardId = new SingleStore(createSingleStore(this.settings.getValue('pinnedLeaderboardId')), 'pinnedLeaderboardId').getStore();
64
64
  this.onboardingStatus = new SingleStore(createSingleStore(OnboardingStatus.Unset), 'onboardingStatus').getStore();
65
65
  this.notifications = instance.notifications;
66
66
  this.transport = instance.transport;
67
67
  this.closeFeature = instance.sdk.closeFeature;
68
68
  this.openFeature = () => instance.sdk.openFeature(FeatureType.GAMES);
69
- this.openedQuestion = createComputedStore(this.background.openedQuestion.getStore(), (openedQuestion) => {
70
- if (openedQuestion.data?.type && GamificationQuestionTypes.has(openedQuestion.data.type)) {
71
- return { loading: false, data: openedQuestion.data };
72
- }
73
- return { loading: openedQuestion.loading, data: undefined };
74
- });
69
+ this.openedQuestion = this.background.openedQuestion;
75
70
  this.leaderboardList = leaderboard(this.transport, this.background.slStreamId);
76
71
  this.onboardingStatus.subscribe((onboardingStatus) => {
77
72
  if (onboardingStatus === OnboardingStatus.Optional || OnboardingStatus.Required) {
@@ -194,7 +189,7 @@ export class Gamification extends AbstractFeature {
194
189
  connect = (transport) => {
195
190
  this.userSummary.invalidate();
196
191
  this.leaderboardList.invalidate();
197
- this.questions.invalidate();
192
+ this.feedList.invalidate();
198
193
  this.background.feedSubscription.addListener('feed-subscription-prediction-close', (response) => {
199
194
  window.requestAnimationFrame(async () => {
200
195
  const question = response.data?.attributes?.question;
@@ -225,7 +220,7 @@ export class Gamification extends AbstractFeature {
225
220
  });
226
221
  this.background.feedSubscription.addListener('feed-subscription-questions-list', () => {
227
222
  window.requestAnimationFrame(() => {
228
- this.questions?.invalidate();
223
+ this.feedList?.invalidate();
229
224
  });
230
225
  });
231
226
  };
@@ -248,11 +243,11 @@ export class Gamification extends AbstractFeature {
248
243
  submitAnswer = async (questionId, answerId) => {
249
244
  await actions.submitAnswer(this.transport, { questionId, answerId });
250
245
  // Todo: add invalidate openedQuestion
251
- this.questions?.invalidate();
246
+ this.feedList?.invalidate();
252
247
  };
253
248
  skipQuestion = async (questionId) => {
254
249
  await actions.skipQuestion(this.transport, questionId);
255
- this.questions?.invalidate();
250
+ this.feedList?.invalidate();
256
251
  };
257
252
  openQuestion = (questionId) => {
258
253
  this.openFeature();
@@ -1,13 +1,13 @@
1
- import { AbstractFeature, ApiStore, FeatureSource, type FeatureProps, type StreamLayerContext, createComputedStore } from '@streamlayer/sdk-web-interfaces';
2
- import { ExtendedQuestion } from '@streamlayer/sdk-web-types';
1
+ import { AbstractFeature, ApiStore, FeatureSource, type FeatureProps, type StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
3
2
  import type { GetApiResponseType } from '@streamlayer/sdk-web-api';
4
3
  import '@streamlayer/sdk-web-core/store';
5
4
  import * as queries from './queries';
5
+ import { GamificationBackground } from './';
6
6
  export declare class Highlights extends AbstractFeature<undefined> {
7
7
  insights?: ApiStore<GetApiResponseType<typeof queries.$insightHistory>>;
8
8
  closeFeature: () => void;
9
9
  openFeature: () => void;
10
- openedInsight: ReturnType<typeof createComputedStore<ExtendedQuestion | undefined>>;
10
+ openedInsight: GamificationBackground['openedQuestion'];
11
11
  private notifications;
12
12
  private transport;
13
13
  private background;
package/lib/highlights.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AbstractFeature, ApiStore, FeatureStatus, createComputedStore, } from '@streamlayer/sdk-web-interfaces';
1
+ import { AbstractFeature, ApiStore, FeatureStatus, } from '@streamlayer/sdk-web-interfaces';
2
2
  import { QuestionStatus, QuestionType, FeatureType, SilenceSetting } from '@streamlayer/sdk-web-types';
3
3
  import { NotificationType } from '@streamlayer/sdk-web-notifications';
4
4
  import '@streamlayer/sdk-web-core/store';
@@ -49,12 +49,7 @@ export class Highlights extends AbstractFeature {
49
49
  });
50
50
  }
51
51
  });
52
- this.openedInsight = createComputedStore(this.background.openedQuestion.getStore(), (openedQuestion) => {
53
- if (openedQuestion.data?.type === QuestionType.FACTOID) {
54
- return openedQuestion.data;
55
- }
56
- return undefined;
57
- });
52
+ this.openedInsight = this.background.openedQuestion;
58
53
  }
59
54
  connect = () => {
60
55
  if (!this.insights) {
@@ -329,6 +329,7 @@ export declare const questionSubscription: (questionId: string, transport: Trans
329
329
  export declare const getQuestionByUser: (questionId: string, transport: Transport) => Promise<import("packages/sdk-web-types/lib").ExtendedQuestion | undefined>;
330
330
  export declare const $questionByUser: ($questionId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<import("packages/sdk-web-types/lib").ExtendedQuestion | undefined, any>;
331
331
  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>;
332
+ export declare const $feedList: (slStreamId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedItem[], any>;
332
333
  export declare const $insightHistory: (slStreamId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<(import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").InsightHistory | undefined)[] | undefined, any>;
333
334
  export { $userSummary, $leaderboardList } from './leaderboard';
334
335
  export { $moderation } from './moderation';
@@ -67,6 +67,20 @@ export const $pickHistory = (slStreamId, transport) => {
67
67
  },
68
68
  });
69
69
  };
70
+ export const $feedList = (slStreamId, transport) => {
71
+ const { client, queryKey } = transport.createPromiseClient(Feed, { method: 'list', params: [slStreamId] });
72
+ return transport.nanoquery.createFetcherStore(queryKey, {
73
+ fetcher: async (_, __, eventId) => {
74
+ if (!eventId) {
75
+ return [];
76
+ }
77
+ const res = await client.list({
78
+ eventId: eventId,
79
+ });
80
+ return res.data;
81
+ },
82
+ });
83
+ };
70
84
  export const $insightHistory = (slStreamId, transport) => {
71
85
  const { client, queryKey } = transport.createPromiseClient(Feed, { method: 'insightHistory', params: [slStreamId] });
72
86
  return transport.nanoquery.createFetcherStore(queryKey, {
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@streamlayer/feature-gamification",
3
- "version": "0.19.4",
3
+ "version": "0.20.0",
4
4
  "peerDependencies": {
5
5
  "@bufbuild/protobuf": "^1.4.2",
6
6
  "@streamlayer/sl-eslib": "^5.53.5",
7
7
  "nanostores": "^0.9.5",
8
- "@streamlayer/sdk-web-interfaces": "^0.18.9",
9
- "@streamlayer/sdk-web-core": "^0.17.1",
8
+ "@streamlayer/sdk-web-interfaces": "^0.18.10",
9
+ "@streamlayer/sdk-web-core": "^0.17.3",
10
10
  "@streamlayer/sdk-web-api": "^0.0.1",
11
- "@streamlayer/sdk-web-types": "^0.17.1",
11
+ "@streamlayer/sdk-web-types": "^0.18.0",
12
12
  "@streamlayer/sdk-web-storage": "^0.3.10",
13
13
  "@streamlayer/sdk-web-logger": "^0.5.10",
14
- "@streamlayer/sdk-web-notifications": "^0.10.8"
14
+ "@streamlayer/sdk-web-notifications": "^0.10.9"
15
15
  },
16
16
  "devDependencies": {
17
17
  "tslib": "^2.6.2"