@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.
- package/lib/background.d.ts +3 -1
- package/lib/background.js +5 -2
- package/lib/detail.d.ts +16 -0
- package/lib/detail.js +33 -0
- package/lib/gamification.d.ts +6 -8
- package/lib/gamification.js +9 -14
- package/lib/highlights.d.ts +3 -3
- package/lib/highlights.js +2 -7
- package/lib/queries/index.d.ts +1 -0
- package/lib/queries/index.js +14 -0
- package/package.json +5 -5
package/lib/background.d.ts
CHANGED
|
@@ -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:
|
|
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 =
|
|
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.
|
|
63
|
+
this.openedQuestion.updateExtendedQuestion(response.data?.attributes?.question);
|
|
61
64
|
});
|
|
62
65
|
});
|
|
63
66
|
this.questionSubscription.connect();
|
package/lib/detail.d.ts
ADDED
|
@@ -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
|
+
};
|
package/lib/gamification.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { AbstractFeature, ApiStore, FeatureSource, type FeatureProps, type StreamLayerContext
|
|
2
|
-
import { type GamesOverlaySettings
|
|
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
|
-
/**
|
|
37
|
-
|
|
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:
|
|
46
|
-
loading: boolean;
|
|
47
|
-
data?: ExtendedQuestion;
|
|
48
|
-
}>>;
|
|
46
|
+
openedQuestion: GamificationBackground['openedQuestion'];
|
|
49
47
|
private notifications;
|
|
50
48
|
private transport;
|
|
51
49
|
private closeFeature;
|
package/lib/gamification.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbstractFeature, ApiStore, FeatureStatus, SingleStore, createSingleStore,
|
|
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
|
-
/**
|
|
40
|
-
|
|
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.
|
|
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 =
|
|
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.
|
|
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.
|
|
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.
|
|
246
|
+
this.feedList?.invalidate();
|
|
252
247
|
};
|
|
253
248
|
skipQuestion = async (questionId) => {
|
|
254
249
|
await actions.skipQuestion(this.transport, questionId);
|
|
255
|
-
this.
|
|
250
|
+
this.feedList?.invalidate();
|
|
256
251
|
};
|
|
257
252
|
openQuestion = (questionId) => {
|
|
258
253
|
this.openFeature();
|
package/lib/highlights.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { AbstractFeature, ApiStore, FeatureSource, type FeatureProps, type StreamLayerContext
|
|
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:
|
|
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,
|
|
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 =
|
|
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) {
|
package/lib/queries/index.d.ts
CHANGED
|
@@ -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';
|
package/lib/queries/index.js
CHANGED
|
@@ -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.
|
|
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
|
-
"@streamlayer/sdk-web-core": "^0.17.
|
|
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.
|
|
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.
|
|
14
|
+
"@streamlayer/sdk-web-notifications": "^0.10.9"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"tslib": "^2.6.2"
|