@streamlayer/feature-gamification 0.37.4 → 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.
- package/lib/background.d.ts +6 -2
- package/lib/background.js +7 -6
- package/lib/detail.d.ts +7 -5
- package/lib/detail.js +35 -10
- package/lib/gamification.d.ts +2 -2
- package/lib/gamification.js +12 -19
- package/lib/queries/actions.js +9 -6
- package/lib/queries/index.d.ts +2 -2
- package/package.json +10 -10
package/lib/background.d.ts
CHANGED
|
@@ -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<
|
|
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((
|
|
57
|
-
this.log.debug({
|
|
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<
|
|
12
|
-
|
|
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 {
|
|
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 =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
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
|
-
|
|
40
|
+
$store.set(undefined);
|
|
31
41
|
}
|
|
32
|
-
}
|
|
33
|
-
|
|
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,
|
package/lib/gamification.d.ts
CHANGED
|
@@ -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;
|
package/lib/gamification.js
CHANGED
|
@@ -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:
|
|
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 }),
|
package/lib/queries/actions.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|
package/lib/queries/index.d.ts
CHANGED
|
@@ -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/
|
|
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/
|
|
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.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@bufbuild/protobuf": "^1.
|
|
5
|
+
"@bufbuild/protobuf": "^1.7.2",
|
|
6
6
|
"@fastify/deepmerge": "*",
|
|
7
|
-
"@streamlayer/sl-eslib": "^5.
|
|
7
|
+
"@streamlayer/sl-eslib": "^5.79.3",
|
|
8
8
|
"nanostores": "^0.9.5",
|
|
9
|
-
"@streamlayer/sdk-web-api": "^0.
|
|
10
|
-
"@streamlayer/sdk-web-core": "^0.21.
|
|
11
|
-
"@streamlayer/sdk-web-interfaces": "^0.20.
|
|
12
|
-
"@streamlayer/sdk-web-logger": "^0.5.
|
|
13
|
-
"@streamlayer/sdk-web-notifications": "^0.14.
|
|
14
|
-
"@streamlayer/sdk-web-storage": "^0.4.
|
|
15
|
-
"@streamlayer/sdk-web-types": "^0.22.
|
|
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"
|