@streamlayer/feature-gamification 0.19.5 → 0.21.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 +11 -8
- package/lib/gamification.js +19 -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 +6 -6
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,13 @@
|
|
|
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 { LeaderboardItem } from './queries/leaderboard';
|
|
10
|
+
import { GamificationBackground } from './';
|
|
9
11
|
/**
|
|
10
12
|
* Required: in-app should be displayed and questions not available
|
|
11
13
|
* Optional: in-app should be displayed but questions are available
|
|
@@ -33,8 +35,8 @@ export declare enum OnboardingStatus {
|
|
|
33
35
|
export declare class Gamification extends AbstractFeature<'games', PlainMessage<GamesOverlaySettings>> {
|
|
34
36
|
/** user statistics (leaderboard panel) */
|
|
35
37
|
userSummary: ApiStore<GetApiResponseType<typeof queries.$userSummary>>;
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
+
/** feed list (pick history) */
|
|
39
|
+
feedList: ApiStore<GetApiResponseType<typeof queries.$feedList>>;
|
|
38
40
|
/** pinned leaderboard id */
|
|
39
41
|
leaderboardId: WritableAtom<string | undefined>;
|
|
40
42
|
/** leaderboard list */
|
|
@@ -42,10 +44,9 @@ export declare class Gamification extends AbstractFeature<'games', PlainMessage<
|
|
|
42
44
|
/** onboarding status */
|
|
43
45
|
onboardingStatus: WritableAtom<OnboardingStatus | undefined>;
|
|
44
46
|
/** opened question */
|
|
45
|
-
openedQuestion:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}>>;
|
|
47
|
+
openedQuestion: GamificationBackground['openedQuestion'];
|
|
48
|
+
/** pinned leaderboard id */
|
|
49
|
+
openedUser: WritableAtom<LeaderboardItem | undefined>;
|
|
49
50
|
private notifications;
|
|
50
51
|
private transport;
|
|
51
52
|
private closeFeature;
|
|
@@ -68,4 +69,6 @@ export declare class Gamification extends AbstractFeature<'games', PlainMessage<
|
|
|
68
69
|
skipQuestion: (questionId: string) => Promise<void>;
|
|
69
70
|
openQuestion: (questionId: string) => void;
|
|
70
71
|
closeQuestion: (questionId?: string) => void;
|
|
72
|
+
openUser: (userId: string) => void;
|
|
73
|
+
closeUser: () => void;
|
|
71
74
|
}
|
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 */
|
|
@@ -46,6 +46,8 @@ export class Gamification extends AbstractFeature {
|
|
|
46
46
|
onboardingStatus;
|
|
47
47
|
/** opened question */
|
|
48
48
|
openedQuestion;
|
|
49
|
+
/** pinned leaderboard id */
|
|
50
|
+
openedUser;
|
|
49
51
|
notifications;
|
|
50
52
|
transport;
|
|
51
53
|
closeFeature;
|
|
@@ -59,19 +61,15 @@ export class Gamification extends AbstractFeature {
|
|
|
59
61
|
this.background = gamificationBackground(instance);
|
|
60
62
|
this.storage = new GamificationStorage();
|
|
61
63
|
this.userSummary = new ApiStore(queries.$userSummary(this.background.slStreamId, this.background.userId, instance.transport), 'gamification:userSummary');
|
|
62
|
-
this.
|
|
64
|
+
this.feedList = this.background.feedList;
|
|
65
|
+
this.openedUser = createSingleStore(undefined);
|
|
63
66
|
this.leaderboardId = new SingleStore(createSingleStore(this.settings.getValue('pinnedLeaderboardId')), 'pinnedLeaderboardId').getStore();
|
|
64
67
|
this.onboardingStatus = new SingleStore(createSingleStore(OnboardingStatus.Unset), 'onboardingStatus').getStore();
|
|
65
68
|
this.notifications = instance.notifications;
|
|
66
69
|
this.transport = instance.transport;
|
|
67
70
|
this.closeFeature = instance.sdk.closeFeature;
|
|
68
71
|
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
|
-
});
|
|
72
|
+
this.openedQuestion = this.background.openedQuestion;
|
|
75
73
|
this.leaderboardList = leaderboard(this.transport, this.background.slStreamId);
|
|
76
74
|
this.onboardingStatus.subscribe((onboardingStatus) => {
|
|
77
75
|
if (onboardingStatus === OnboardingStatus.Optional || OnboardingStatus.Required) {
|
|
@@ -194,7 +192,7 @@ export class Gamification extends AbstractFeature {
|
|
|
194
192
|
connect = (transport) => {
|
|
195
193
|
this.userSummary.invalidate();
|
|
196
194
|
this.leaderboardList.invalidate();
|
|
197
|
-
this.
|
|
195
|
+
this.feedList.invalidate();
|
|
198
196
|
this.background.feedSubscription.addListener('feed-subscription-prediction-close', (response) => {
|
|
199
197
|
window.requestAnimationFrame(async () => {
|
|
200
198
|
const question = response.data?.attributes?.question;
|
|
@@ -225,7 +223,7 @@ export class Gamification extends AbstractFeature {
|
|
|
225
223
|
});
|
|
226
224
|
this.background.feedSubscription.addListener('feed-subscription-questions-list', () => {
|
|
227
225
|
window.requestAnimationFrame(() => {
|
|
228
|
-
this.
|
|
226
|
+
this.feedList?.invalidate();
|
|
229
227
|
});
|
|
230
228
|
});
|
|
231
229
|
};
|
|
@@ -248,11 +246,11 @@ export class Gamification extends AbstractFeature {
|
|
|
248
246
|
submitAnswer = async (questionId, answerId) => {
|
|
249
247
|
await actions.submitAnswer(this.transport, { questionId, answerId });
|
|
250
248
|
// Todo: add invalidate openedQuestion
|
|
251
|
-
this.
|
|
249
|
+
this.feedList?.invalidate();
|
|
252
250
|
};
|
|
253
251
|
skipQuestion = async (questionId) => {
|
|
254
252
|
await actions.skipQuestion(this.transport, questionId);
|
|
255
|
-
this.
|
|
253
|
+
this.feedList?.invalidate();
|
|
256
254
|
};
|
|
257
255
|
openQuestion = (questionId) => {
|
|
258
256
|
this.openFeature();
|
|
@@ -261,4 +259,11 @@ export class Gamification extends AbstractFeature {
|
|
|
261
259
|
closeQuestion = (questionId) => {
|
|
262
260
|
return this.background.closeQuestion(questionId);
|
|
263
261
|
};
|
|
262
|
+
openUser = (userId) => {
|
|
263
|
+
const user = this.leaderboardList.$store.get().data?.find((item) => item.userId === userId);
|
|
264
|
+
this.openedUser.set(user);
|
|
265
|
+
};
|
|
266
|
+
closeUser = () => {
|
|
267
|
+
this.openedUser.set(undefined);
|
|
268
|
+
};
|
|
264
269
|
}
|
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.21.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
|
-
"@streamlayer/sdk-web-
|
|
14
|
-
"@streamlayer/sdk-web-
|
|
13
|
+
"@streamlayer/sdk-web-notifications": "^0.10.9",
|
|
14
|
+
"@streamlayer/sdk-web-logger": "^0.5.10"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"tslib": "^2.6.2"
|