@streamlayer/feature-gamification 0.29.0 → 0.30.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.
@@ -5,11 +5,7 @@ import { ReadableAtom, WritableAtom } from 'nanostores';
5
5
  import * as queries from './queries';
6
6
  import { detail } from './detail';
7
7
  /**
8
- * get GamificationBackground singleton
9
- */
10
- export declare const gamificationBackground: (instance: StreamLayerContext) => GamificationBackground;
11
- /**
12
- * Background Singleton class for Gamification and Highlights overlays
8
+ * Background class for Gamification feature
13
9
  */
14
10
  export declare class GamificationBackground {
15
11
  /** sl event id */
package/lib/background.js CHANGED
@@ -4,16 +4,7 @@ import '@streamlayer/sdk-web-core/store';
4
4
  import * as queries from './queries';
5
5
  import { detail } from './detail';
6
6
  /**
7
- * get GamificationBackground singleton
8
- */
9
- export const gamificationBackground = (instance) => {
10
- if (!instance.gamification) {
11
- instance.gamification = new GamificationBackground(instance);
12
- }
13
- return instance.gamification;
14
- };
15
- /**
16
- * Background Singleton class for Gamification and Highlights overlays
7
+ * Background class for Gamification feature
17
8
  */
18
9
  export class GamificationBackground {
19
10
  /** sl event id */
@@ -40,9 +31,6 @@ export class GamificationBackground {
40
31
  notifications;
41
32
  log;
42
33
  constructor(instance) {
43
- if (instance.gamification) {
44
- throw new Error('GamificationBackground Singleton error');
45
- }
46
34
  this.log = createLogger('gamification-background');
47
35
  this.slStreamId = instance.stores.slStreamId.getAtomStore();
48
36
  this.organizationId = instance.stores.organizationSettings.getAtomStore();
@@ -83,6 +71,7 @@ export class GamificationBackground {
83
71
  }
84
72
  });
85
73
  });
74
+ this.feedSubscription.connect();
86
75
  }
87
76
  /**
88
77
  * Get id for notifications and link with current session
@@ -7,8 +7,8 @@ import * as actions from './queries/actions';
7
7
  import { GamificationStorage } from './storage';
8
8
  import { leaderboard } from './leaderboard';
9
9
  import { deepLink } from './deepLink';
10
- import { onboarding } from './onboarding';
11
- import { gamificationBackground } from './';
10
+ import { OnboardingStatus, onboarding } from './onboarding';
11
+ import { GamificationBackground } from './';
12
12
  const GamificationQuestionTypes = new Set([QuestionType.POLL, QuestionType.PREDICTION, QuestionType.TRIVIA]);
13
13
  /**
14
14
  * Gamification (Games) Overlay
@@ -47,7 +47,7 @@ export class Gamification extends AbstractFeature {
47
47
  storage;
48
48
  constructor(config, source, instance) {
49
49
  super(config, source);
50
- this.background = gamificationBackground(instance);
50
+ this.background = new GamificationBackground(instance);
51
51
  this.storage = new GamificationStorage();
52
52
  this.userSummary = new ApiStore(queries.$userSummary(this.background.slStreamId, this.background.userId, instance.transport), 'gamification:userSummary');
53
53
  this.feedList = this.background.feedList;
@@ -57,7 +57,7 @@ export class Gamification extends AbstractFeature {
57
57
  this.onboardingStatus = onboarding(this, this.background, instance.transport, instance.notifications);
58
58
  this.notifications = instance.notifications;
59
59
  this.transport = instance.transport;
60
- this.closeFeature = instance.sdk.closeFeature;
60
+ this.closeFeature = () => instance.sdk.closeFeature(true);
61
61
  this.openFeature = () => instance.sdk.openFeature(FeatureType.GAMES);
62
62
  this.openedQuestion = this.background.openedQuestion;
63
63
  this.deepLink = deepLink(this.transport, this.background.slStreamId, this.background.userId);
@@ -74,25 +74,35 @@ export class Gamification extends AbstractFeature {
74
74
  * listen for active question and show in-app notification
75
75
  */
76
76
  this.background.activeQuestionId.listen((question) => {
77
- if (question && question.data && this.onboardingStatus.$store.get()) {
77
+ const onboardingStatus = this.onboardingStatus.$store.get();
78
+ if (question && question.data && onboardingStatus) {
78
79
  if (question.data.question?.id !== undefined &&
79
80
  question.data.question.notification !== undefined &&
80
- question.data.moderation?.bypassNotifications?.inAppSilence !== SilenceSetting.ON &&
81
+ question.data.question?.bypassNotifications?.inAppSilence !== SilenceSetting.ON &&
81
82
  question.data.question.status === QuestionStatus.ACTIVE) {
82
83
  if (GamificationQuestionTypes.has(question.data.question.type)) {
83
- this.notifications.add({
84
- type: NotificationType.QUESTION,
85
- action: () => question.data?.question && this.openQuestion(question.data.question.id),
86
- close: () => question.data?.question && this.closeQuestion(question.data.question.id),
87
- autoHideDuration: 1000 * 60,
88
- id: this.background.getCurrentSessionId({ prefix: 'notification', entity: question.data.question.id }),
89
- data: {
90
- questionType: question.data.question.type,
91
- question: {
92
- title: question.data.question.notification.title,
93
- },
94
- },
95
- });
84
+ const optInEnabled = this.settings.getValues().inplayGame?.titleCard?.optIn;
85
+ const onboardingCompleted = onboardingStatus === OnboardingStatus.Completed;
86
+ if (optInEnabled !== undefined) {
87
+ if (!optInEnabled || onboardingCompleted) {
88
+ this.notifications.add({
89
+ type: NotificationType.QUESTION,
90
+ action: () => question.data?.question && this.openQuestion(question.data.question.id),
91
+ close: () => question.data?.question && this.closeQuestion(question.data.question.id),
92
+ autoHideDuration: 1000 * 60,
93
+ id: this.background.getCurrentSessionId({
94
+ prefix: 'notification',
95
+ entity: question.data.question.id,
96
+ }),
97
+ data: {
98
+ questionType: question.data.question.type,
99
+ question: {
100
+ title: question.data.question.notification.title,
101
+ },
102
+ },
103
+ });
104
+ }
105
+ }
96
106
  }
97
107
  else if (question.data.question.type === QuestionType.FACTOID) {
98
108
  const optionsValue = question.data.question.options?.options.value;
package/lib/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- export { GamificationBackground, gamificationBackground } from './background';
1
+ export { GamificationBackground } from './background';
2
2
  export { Gamification } from './gamification';
3
- export { Highlights } from './highlights';
4
3
  declare module '@streamlayer/sdk-web-interfaces' {
5
4
  interface StreamLayerContext {
6
5
  gamification: import('./background').GamificationBackground;
package/lib/index.js CHANGED
@@ -1,3 +1,2 @@
1
- export { GamificationBackground, gamificationBackground } from './background';
1
+ export { GamificationBackground } from './background';
2
2
  export { Gamification } from './gamification';
3
- export { Highlights } from './highlights';
@@ -108,7 +108,7 @@ export declare const feedSubscription: ($slStreamId: ReadableAtom<string | undef
108
108
  readonly kind: import("@bufbuild/protobuf").MethodKind.ServerStreaming;
109
109
  };
110
110
  };
111
- }, SubscriptionRequest, SubscriptionResponse, "subscription" | "votingSubscription" | "questionSubscription" | "feedSubscription", ((request: import("@bufbuild/protobuf").PartialMessage<SubscriptionRequest>, messageCallback: (response: SubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void) | ((request: import("@bufbuild/protobuf").PartialMessage<VotingSubscriptionRequest>, messageCallback: (response: VotingSubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void) | ((request: import("@bufbuild/protobuf").PartialMessage<QuestionSubscriptionRequest>, messageCallback: (response: QuestionSubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void) | ((request: import("@bufbuild/protobuf").PartialMessage<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionRequest>, messageCallback: (response: import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void)>;
111
+ }, SubscriptionRequest, SubscriptionResponse, "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>)>;
112
112
  export declare const votingSubscription: (params: {
113
113
  questionId: string;
114
114
  feedId: string;
@@ -218,7 +218,7 @@ export declare const votingSubscription: (params: {
218
218
  readonly kind: import("@bufbuild/protobuf").MethodKind.ServerStreaming;
219
219
  };
220
220
  };
221
- }, VotingSubscriptionRequest, VotingSubscriptionResponse, "subscription" | "votingSubscription" | "questionSubscription" | "feedSubscription", ((request: import("@bufbuild/protobuf").PartialMessage<SubscriptionRequest>, messageCallback: (response: SubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void) | ((request: import("@bufbuild/protobuf").PartialMessage<VotingSubscriptionRequest>, messageCallback: (response: VotingSubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void) | ((request: import("@bufbuild/protobuf").PartialMessage<QuestionSubscriptionRequest>, messageCallback: (response: QuestionSubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void) | ((request: import("@bufbuild/protobuf").PartialMessage<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionRequest>, messageCallback: (response: import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void)>;
221
+ }, VotingSubscriptionRequest, VotingSubscriptionResponse, "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>)>;
222
222
  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<{
223
223
  readonly typeName: "streamlayer.interactive.feed.Feed";
224
224
  readonly methods: {
@@ -325,7 +325,7 @@ export declare const questionSubscription: (questionId: string, transport: Trans
325
325
  readonly kind: import("@bufbuild/protobuf").MethodKind.ServerStreaming;
326
326
  };
327
327
  };
328
- }, QuestionSubscriptionRequest, QuestionSubscriptionResponse, "subscription" | "votingSubscription" | "questionSubscription" | "feedSubscription", ((request: import("@bufbuild/protobuf").PartialMessage<SubscriptionRequest>, messageCallback: (response: SubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void) | ((request: import("@bufbuild/protobuf").PartialMessage<VotingSubscriptionRequest>, messageCallback: (response: VotingSubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void) | ((request: import("@bufbuild/protobuf").PartialMessage<QuestionSubscriptionRequest>, messageCallback: (response: QuestionSubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void) | ((request: import("@bufbuild/protobuf").PartialMessage<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionRequest>, messageCallback: (response: import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedSubscriptionResponse) => void, closeCallback: (error: import("@connectrpc/connect").ConnectError | undefined) => void, options?: import("@connectrpc/connect").CallOptions | undefined) => () => void)>;
328
+ }, 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>)>;
329
329
  export declare const getQuestionByUser: (questionId: string, transport: Transport) => Promise<import("@streamlayer/sdk-web-types").ExtendedQuestion | undefined>;
330
330
  export declare const $questionByUser: ($questionId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<import("@streamlayer/sdk-web-types").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>;
@@ -18,7 +18,7 @@ export const $activeQuestion = (slStreamId, transport) => {
18
18
  });
19
19
  };
20
20
  export const feedSubscription = ($slStreamId, transport) => {
21
- const { client } = transport.createCallbackClient(Feed);
21
+ const { client } = transport.createStreamClient(Feed);
22
22
  const params = atom({ eventId: $slStreamId.get() || '', feedId: '' });
23
23
  $slStreamId.subscribe((eventId = '') => {
24
24
  params.set({ eventId, feedId: '' });
@@ -27,12 +27,12 @@ export const feedSubscription = ($slStreamId, transport) => {
27
27
  return subscription;
28
28
  };
29
29
  export const votingSubscription = (params, transport) => {
30
- const { client } = transport.createCallbackClient(Feed);
30
+ const { client } = transport.createStreamClient(Feed);
31
31
  const subscription = transport.addSubscription(client.votingSubscription, params, { name: 'votingSubscription', withStore: true });
32
32
  return subscription;
33
33
  };
34
34
  export const questionSubscription = (questionId, transport) => {
35
- const { client } = transport.createCallbackClient(Feed);
35
+ const { client } = transport.createStreamClient(Feed);
36
36
  const subscription = transport.addSubscription(client.questionSubscription, { questionId }, { name: 'questionSubscription' });
37
37
  return subscription;
38
38
  };
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@streamlayer/feature-gamification",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "peerDependencies": {
5
5
  "@bufbuild/protobuf": "^1.4.2",
6
6
  "@streamlayer/sl-eslib": "^5.53.6",
7
7
  "@fastify/deepmerge": "*",
8
8
  "nanostores": "^0.9.5",
9
9
  "@streamlayer/sdk-web-api": "^0.1.0",
10
- "@streamlayer/sdk-web-core": "^0.0.7",
11
- "@streamlayer/sdk-web-interfaces": "^0.0.1",
10
+ "@streamlayer/sdk-web-core": "^0.1.0",
11
+ "@streamlayer/sdk-web-interfaces": "^0.1.0",
12
12
  "@streamlayer/sdk-web-logger": "^0.0.1",
13
13
  "@streamlayer/sdk-web-notifications": "^0.1.0",
14
- "@streamlayer/sdk-web-types": "^0.1.0",
15
- "@streamlayer/sdk-web-storage": "^0.0.4"
14
+ "@streamlayer/sdk-web-storage": "^0.0.4",
15
+ "@streamlayer/sdk-web-types": "^0.1.0"
16
16
  },
17
17
  "devDependencies": {
18
18
  "tslib": "^2.6.2"
@@ -1,19 +0,0 @@
1
- import { AbstractFeature, ApiStore, FeatureSource, type FeatureProps, type StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
- import type { GetApiResponseType } from '@streamlayer/sdk-web-api';
3
- import '@streamlayer/sdk-web-core/store';
4
- import * as queries from './queries';
5
- import { GamificationBackground } from './';
6
- export declare class Highlights extends AbstractFeature<undefined> {
7
- insights?: ApiStore<GetApiResponseType<typeof queries.$insightHistory>>;
8
- closeFeature: () => void;
9
- openFeature: () => void;
10
- openedInsight: GamificationBackground['openedQuestion'];
11
- private notifications;
12
- private transport;
13
- private background;
14
- constructor(config: FeatureProps, source: FeatureSource, instance: StreamLayerContext);
15
- connect: () => void;
16
- disconnect: () => void;
17
- openHighlight: (questionId: string) => void;
18
- closeHighlight: (questionId?: string) => void;
19
- }
package/lib/highlights.js DELETED
@@ -1,76 +0,0 @@
1
- import { AbstractFeature, ApiStore, FeatureStatus, } from '@streamlayer/sdk-web-interfaces';
2
- import { QuestionStatus, QuestionType, FeatureType, SilenceSetting } from '@streamlayer/sdk-web-types';
3
- import { NotificationType } from '@streamlayer/sdk-web-notifications';
4
- import '@streamlayer/sdk-web-core/store';
5
- import * as queries from './queries';
6
- import { gamificationBackground } from './';
7
- export class Highlights extends AbstractFeature {
8
- insights;
9
- closeFeature;
10
- openFeature;
11
- openedInsight;
12
- notifications;
13
- transport;
14
- background;
15
- constructor(config, source, instance) {
16
- super(config, source);
17
- this.background = gamificationBackground(instance);
18
- this.notifications = instance.notifications;
19
- this.transport = instance.transport;
20
- this.closeFeature = instance.sdk.closeFeature;
21
- this.openFeature = () => instance.sdk.openFeature(FeatureType.HIGHLIGHTS);
22
- this.status.subscribe((status) => {
23
- if (status === FeatureStatus.Ready) {
24
- this.connect();
25
- }
26
- else {
27
- this.disconnect();
28
- }
29
- });
30
- this.background.activeQuestionId.listen((question) => {
31
- if (question?.data?.question?.id &&
32
- question?.data?.question?.notification &&
33
- question.data.moderation?.bypassNotifications?.inAppSilence !== SilenceSetting.ON &&
34
- question.data.question.type === QuestionType.FACTOID &&
35
- question.data.question.status === QuestionStatus.ACTIVE) {
36
- this.notifications.add({
37
- type: NotificationType.QUESTION,
38
- action: () => question.data?.question && this.openHighlight(question.data.question.id),
39
- close: () => question.data?.question && this.closeHighlight(question.data.question.id),
40
- autoHideDuration: 1000 * +(question.data.question?.appearance?.autoHideInterval || '5'),
41
- id: this.background.getCurrentSessionId({ prefix: 'notification', entity: question.data.question.id }),
42
- data: {
43
- questionType: question.data.question.type,
44
- question: {
45
- title: question.data.question.notification.title,
46
- },
47
- },
48
- });
49
- }
50
- });
51
- this.openedInsight = this.background.openedQuestion;
52
- }
53
- connect = () => {
54
- if (!this.insights) {
55
- this.insights = new ApiStore(queries.$insightHistory(this.background.slStreamId, this.transport), 'gamification:insights');
56
- }
57
- else {
58
- this.insights.invalidate();
59
- }
60
- this.background.feedSubscription.addListener('feed-subscription-insights-list', () => {
61
- window.requestAnimationFrame(() => {
62
- this.insights?.invalidate();
63
- });
64
- });
65
- };
66
- disconnect = () => {
67
- this.background.feedSubscription.removeListener('feed-subscription-insights-list');
68
- };
69
- openHighlight = (questionId) => {
70
- this.openFeature();
71
- return this.background.openQuestion(questionId);
72
- };
73
- closeHighlight = (questionId) => {
74
- return this.background.closeQuestion(questionId);
75
- };
76
- }