@streamlayer/feature-gamification 0.37.4 → 0.39.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 +11 -2
- package/lib/background.js +14 -9
- package/lib/constants.d.ts +4 -0
- package/lib/constants.js +5 -0
- package/lib/detail.d.ts +12 -12
- package/lib/detail.js +35 -61
- package/lib/gamification.d.ts +9 -4
- package/lib/gamification.js +229 -69
- package/lib/onboarding.js +7 -2
- package/lib/queries/actions.js +21 -7
- package/lib/queries/deepLink.js +2 -0
- package/lib/queries/index.d.ts +2 -2
- package/lib/queries/index.js +5 -0
- package/lib/queries/moderation.js +2 -0
- package/package.json +12 -12
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,12 @@ 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
|
+
openedFrom?: 'list' | 'notification';
|
|
32
|
+
};
|
|
33
|
+
} | undefined>;
|
|
28
34
|
/** opened question statistics */
|
|
29
35
|
openedQuestion: ReturnType<typeof detail>;
|
|
30
36
|
/** last active question in feed */
|
|
@@ -40,6 +46,7 @@ export declare class GamificationBackground {
|
|
|
40
46
|
questionSubscription?: ReturnType<typeof queries.questionSubscription>;
|
|
41
47
|
private notifications;
|
|
42
48
|
private log;
|
|
49
|
+
private transport;
|
|
43
50
|
constructor(instance: StreamLayerContext);
|
|
44
51
|
/**
|
|
45
52
|
* Get id for notifications and link with current session
|
|
@@ -60,7 +67,9 @@ export declare class GamificationBackground {
|
|
|
60
67
|
/**
|
|
61
68
|
* Open question and mark notification for this question as viewed
|
|
62
69
|
*/
|
|
63
|
-
openQuestion: (questionId: string
|
|
70
|
+
openQuestion: (questionId: string, question?: FeedItem & {
|
|
71
|
+
openedFrom?: 'list' | 'notification';
|
|
72
|
+
}) => void;
|
|
64
73
|
/**
|
|
65
74
|
* Close question and mark notification for this question as viewed
|
|
66
75
|
*/
|
package/lib/background.js
CHANGED
|
@@ -40,7 +40,9 @@ export class GamificationBackground {
|
|
|
40
40
|
questionSubscription;
|
|
41
41
|
notifications;
|
|
42
42
|
log;
|
|
43
|
+
transport;
|
|
43
44
|
constructor(instance) {
|
|
45
|
+
this.transport = instance.transport;
|
|
44
46
|
this.log = createLogger('gamification-background');
|
|
45
47
|
this.slStreamId = instance.stores.slStreamId.getAtomStore();
|
|
46
48
|
this.organizationId = instance.stores.organizationSettings.getAtomStore();
|
|
@@ -53,14 +55,12 @@ export class GamificationBackground {
|
|
|
53
55
|
this.feedList = new ApiStore(queries.$feedList(this.slStreamId, this.interactiveAllowed, instance.transport), 'gamification:feedList');
|
|
54
56
|
this.activeQuestionId = new ApiStore(queries.$activeQuestion(this.slStreamId, instance.transport), 'gamification:activeQuestionId');
|
|
55
57
|
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);
|
|
58
|
+
this.openedQuestionId.listen((item) => {
|
|
59
|
+
this.log.debug({ item }, 'received question');
|
|
60
|
+
if (item?.questionId) {
|
|
61
|
+
this.questionSubscription = queries.questionSubscription(item.questionId, instance.transport);
|
|
60
62
|
this.questionSubscription.addListener('feed-subscription-opened-question', (response) => {
|
|
61
|
-
|
|
62
|
-
this.openedQuestion.updateExtendedQuestion(response.data?.attributes?.question);
|
|
63
|
-
});
|
|
63
|
+
this.openedQuestion.updateExtendedQuestion(response.data?.attributes?.question);
|
|
64
64
|
});
|
|
65
65
|
this.questionSubscription.connect();
|
|
66
66
|
}
|
|
@@ -120,12 +120,17 @@ export class GamificationBackground {
|
|
|
120
120
|
};
|
|
121
121
|
disconnect = () => {
|
|
122
122
|
this.feedSubscription?.disconnect();
|
|
123
|
+
if (this.questionSubscription !== undefined) {
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
+
this.transport.removeSubscription(this.questionSubscription);
|
|
126
|
+
this.questionSubscription = undefined;
|
|
127
|
+
}
|
|
123
128
|
};
|
|
124
129
|
/**
|
|
125
130
|
* Open question and mark notification for this question as viewed
|
|
126
131
|
*/
|
|
127
|
-
openQuestion = (questionId) => {
|
|
128
|
-
this.openedQuestionId.set(questionId);
|
|
132
|
+
openQuestion = (questionId, question) => {
|
|
133
|
+
this.openedQuestionId.set({ questionId, question });
|
|
129
134
|
this.notifications.markAsViewed(this.getCurrentSessionId({ prefix: 'notification', entity: questionId }));
|
|
130
135
|
};
|
|
131
136
|
/**
|
package/lib/constants.js
ADDED
package/lib/detail.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
export declare const detail: (transport: Transport, $openedQuestionId: ReadableAtom<{
|
|
6
|
+
questionId: string;
|
|
7
|
+
question?: FeedItem & {
|
|
8
|
+
openedFrom?: 'list' | 'notification';
|
|
9
|
+
};
|
|
10
|
+
} | undefined>, $feedList: ReturnType<GamificationBackground['feedList']['getStore']>) => {
|
|
11
|
+
$store: ReadableAtom<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedItem | (import("@bufbuild/protobuf").PlainMessage<import("@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb").FeedItem> & {
|
|
12
|
+
openedFrom?: "list" | "notification" | undefined;
|
|
13
|
+
}) | undefined>;
|
|
14
|
+
$extendedStore: import("@nanostores/query").FetcherStore<import("@bufbuild/protobuf").PlainMessage<import("@streamlayer/sl-eslib/interactive/interactive.common_pb").ExtendedQuestion>, any>;
|
|
15
|
+
updateExtendedQuestion: (question: ExtendedQuestion | undefined) => void;
|
|
10
16
|
};
|
|
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
CHANGED
|
@@ -1,71 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { computed } from 'nanostores';
|
|
4
|
-
import { getQuestionByUser } from './queries';
|
|
5
|
-
const mergeArray = (options) => (target, source) => {
|
|
6
|
-
let i = 0;
|
|
7
|
-
const tl = target.length;
|
|
8
|
-
const sl = source.length;
|
|
9
|
-
const il = Math.max(tl, sl);
|
|
10
|
-
const result = new Array(il);
|
|
11
|
-
for (i = 0; i < il; ++i) {
|
|
12
|
-
if (i < sl) {
|
|
13
|
-
result[i] = options.deepmerge(target[i], source[i]);
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
result[i] = options.clone(target[i]);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
|
-
const mergeQuestion = deepmerge({ mergeArray });
|
|
1
|
+
import { batched } from 'nanostores';
|
|
2
|
+
import { $questionByUser } from './queries';
|
|
22
3
|
export const detail = (transport, $openedQuestionId, $feedList) => {
|
|
23
|
-
const $store =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return question;
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
console.error('Feed list is not loaded yet. Issue with the opened question.');
|
|
31
|
-
}
|
|
4
|
+
const $store = batched([$openedQuestionId, $feedList], () => {
|
|
5
|
+
const openedQuestion = $openedQuestionId.get();
|
|
6
|
+
if (!openedQuestion) {
|
|
7
|
+
return undefined;
|
|
32
8
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
-
$store.subscribe(async (item) => {
|
|
42
|
-
if (item) {
|
|
43
|
-
if (item.type === 'question') {
|
|
44
|
-
$extendedStore.setKey('loading', true);
|
|
45
|
-
const question = await getQuestionByUser(item.id, transport);
|
|
46
|
-
$extendedStore.set({ data: question, loading: false });
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
9
|
+
const question = $feedList.get().data?.find((item) => item.id === openedQuestion?.questionId);
|
|
10
|
+
const openedFrom = openedQuestion?.question?.openedFrom;
|
|
11
|
+
if (question) {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
question.openedFrom = openedFrom;
|
|
15
|
+
return question;
|
|
49
16
|
}
|
|
50
|
-
|
|
17
|
+
return openedQuestion.question;
|
|
51
18
|
});
|
|
19
|
+
const $storeQuestionId = batched($store, (item) => (item && item.type === 'question' ? item.id : undefined));
|
|
20
|
+
const $extendedStore = $questionByUser($storeQuestionId, transport);
|
|
52
21
|
const updateExtendedQuestion = (question) => {
|
|
53
22
|
const currentQuestion = $extendedStore.get().data;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
* can be overwritten by the subscription response,
|
|
58
|
-
* which does not include user-specific data.
|
|
59
|
-
*/
|
|
60
|
-
for (const answer of question.answers) {
|
|
61
|
-
if (answer.youVoted !== true) {
|
|
62
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
63
|
-
// @ts-ignore
|
|
64
|
-
delete answer.youVoted;
|
|
65
|
-
}
|
|
23
|
+
const mergeQuestionAnswers = (currentAnswers, newAnswers) => {
|
|
24
|
+
if (!currentAnswers || !newAnswers) {
|
|
25
|
+
return currentAnswers || newAnswers || [];
|
|
66
26
|
}
|
|
67
|
-
|
|
68
|
-
|
|
27
|
+
const answers = [];
|
|
28
|
+
for (let i = 0; i < currentAnswers.length; i++) {
|
|
29
|
+
answers.push({
|
|
30
|
+
...currentAnswers[i],
|
|
31
|
+
...newAnswers[i],
|
|
32
|
+
correct: currentAnswers[i].correct,
|
|
33
|
+
youVoted: currentAnswers[i].youVoted,
|
|
34
|
+
pointsEarned: currentAnswers[i].pointsEarned,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return answers;
|
|
38
|
+
};
|
|
39
|
+
$extendedStore.mutate({
|
|
40
|
+
...question,
|
|
41
|
+
answers: mergeQuestionAnswers(currentQuestion?.answers, question?.answers),
|
|
42
|
+
});
|
|
69
43
|
};
|
|
70
44
|
return { $store, $extendedStore, updateExtendedQuestion };
|
|
71
45
|
};
|
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';
|
|
@@ -44,20 +44,25 @@ export declare class Gamification extends AbstractFeature<'games', PlainMessage<
|
|
|
44
44
|
openedUser: WritableAtom<LeaderboardItem | undefined>;
|
|
45
45
|
closeFeature: () => void;
|
|
46
46
|
openFeature: () => void;
|
|
47
|
+
feedSubscription: GamificationBackground['feedSubscription'];
|
|
48
|
+
activeQuestionId: GamificationBackground['activeQuestionId'];
|
|
49
|
+
openedQuestionId: GamificationBackground['openedQuestionId'];
|
|
47
50
|
private notifications;
|
|
48
51
|
private transport;
|
|
49
52
|
/** gamification background class, handle subscriptions and notifications for closed overlay */
|
|
50
53
|
private background;
|
|
51
54
|
/** Browser cache */
|
|
52
55
|
private storage;
|
|
56
|
+
private submitAnswerTimeout;
|
|
53
57
|
constructor(config: FeatureProps, source: FeatureSource, instance: StreamLayerContext);
|
|
54
58
|
get isInteractiveAllowed(): boolean;
|
|
55
59
|
checkInteractiveFlag: () => void;
|
|
56
|
-
connect: (
|
|
60
|
+
connect: () => void;
|
|
57
61
|
disconnect: () => void;
|
|
58
62
|
submitAnswer: (questionId: string, answerId: string) => Promise<void>;
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
openQuestion: (questionId: string, question?: FeedItem & {
|
|
64
|
+
openedFrom?: 'list' | 'notification';
|
|
65
|
+
}) => void;
|
|
61
66
|
closeQuestion: (questionId?: string) => void;
|
|
62
67
|
openUser: (userId: string) => void;
|
|
63
68
|
closeUser: () => void;
|
package/lib/gamification.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
+
import { AbstractFeature, ApiStore, SingleStore, createSingleStore, eventBus, } from '@streamlayer/sdk-web-interfaces';
|
|
3
|
+
import { QuestionStatus, QuestionType, FeatureType, SilenceSetting, PickHistoryStatus, } from '@streamlayer/sdk-web-types';
|
|
3
4
|
import { NotificationType } from '@streamlayer/sdk-web-notifications';
|
|
4
5
|
import '@streamlayer/sdk-web-core/store';
|
|
5
6
|
import * as queries from './queries';
|
|
@@ -9,6 +10,8 @@ import { leaderboard } from './leaderboard';
|
|
|
9
10
|
import { deepLink } from './deepLink';
|
|
10
11
|
import { OnboardingStatus, onboarding } from './onboarding';
|
|
11
12
|
import { GamificationBackground, InteractiveAllowed } from './background';
|
|
13
|
+
import { ERROR } from './constants';
|
|
14
|
+
import { $questionByUser } from './queries';
|
|
12
15
|
const InteractiveQuestionTypes = new Set([QuestionType.POLL, QuestionType.PREDICTION, QuestionType.TRIVIA]);
|
|
13
16
|
/**
|
|
14
17
|
* Gamification (Games) Overlay
|
|
@@ -41,15 +44,22 @@ export class Gamification extends AbstractFeature {
|
|
|
41
44
|
openedUser;
|
|
42
45
|
closeFeature;
|
|
43
46
|
openFeature;
|
|
47
|
+
feedSubscription;
|
|
48
|
+
activeQuestionId;
|
|
49
|
+
openedQuestionId;
|
|
44
50
|
notifications;
|
|
45
51
|
transport;
|
|
46
52
|
/** gamification background class, handle subscriptions and notifications for closed overlay */
|
|
47
53
|
background;
|
|
48
54
|
/** Browser cache */
|
|
49
55
|
storage;
|
|
56
|
+
submitAnswerTimeout;
|
|
50
57
|
constructor(config, source, instance) {
|
|
51
58
|
super(config, source);
|
|
52
59
|
this.background = new GamificationBackground(instance);
|
|
60
|
+
this.feedSubscription = this.background.feedSubscription;
|
|
61
|
+
this.activeQuestionId = this.background.activeQuestionId;
|
|
62
|
+
this.openedQuestionId = this.background.openedQuestionId;
|
|
53
63
|
this.storage = new GamificationStorage();
|
|
54
64
|
this.userSummary = new ApiStore(queries.$userSummary(this.background.slStreamId, this.background.userId, instance.transport), 'gamification:userSummary');
|
|
55
65
|
this.feedList = this.background.feedList;
|
|
@@ -65,20 +75,11 @@ export class Gamification extends AbstractFeature {
|
|
|
65
75
|
this.openedQuestion = this.background.openedQuestion;
|
|
66
76
|
this.deepLink = deepLink(this.transport, this.background.slStreamId, instance.stores.providerStreamId.getStore(), this.background.userId);
|
|
67
77
|
this.leaderboardList = leaderboard(this.transport, this.background.slStreamId, this.background.userId, this.friends);
|
|
68
|
-
this.
|
|
69
|
-
if (status === FeatureStatus.Ready) {
|
|
70
|
-
this.connect(instance.transport);
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
this.disconnect();
|
|
74
|
-
}
|
|
75
|
-
});
|
|
78
|
+
this.connect();
|
|
76
79
|
// refresh leaderboard on user summary update after earning points
|
|
77
80
|
this.userSummary.listen((userSummary) => {
|
|
78
81
|
if (this.leaderboardList.$store.lc !== 0 && userSummary?.data?.summary) {
|
|
79
|
-
|
|
80
|
-
this.leaderboardList.invalidate();
|
|
81
|
-
});
|
|
82
|
+
this.leaderboardList.invalidate(); // verified, it's necessary
|
|
82
83
|
}
|
|
83
84
|
});
|
|
84
85
|
/**
|
|
@@ -103,40 +104,79 @@ export class Gamification extends AbstractFeature {
|
|
|
103
104
|
const allowed = !onboardingEnabled || onboardingCompleted || optInEnabled !== true;
|
|
104
105
|
this.background.interactiveAllowed.set(allowed ? InteractiveAllowed.ALLOWED : InteractiveAllowed.DISALLOWED);
|
|
105
106
|
};
|
|
106
|
-
connect = (
|
|
107
|
-
this.
|
|
108
|
-
this.leaderboardList.invalidate();
|
|
109
|
-
this.feedList.invalidate();
|
|
110
|
-
this.friends.invalidate();
|
|
111
|
-
this.background.feedSubscription.addListener('feed-subscription-prediction-close', (response) => {
|
|
107
|
+
connect = () => {
|
|
108
|
+
this.background.feedSubscription.addListener('feed-subscription-prediction-close', async (response) => {
|
|
112
109
|
if (!this.isInteractiveAllowed) {
|
|
113
110
|
return;
|
|
114
111
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
const question = response.data?.attributes?.question;
|
|
113
|
+
const feedItem = response.data?.attributes?.feedItem;
|
|
114
|
+
if (!question || !feedItem?.attributes) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const { status, type, id, answers } = question;
|
|
118
|
+
if (status === QuestionStatus.RESOLVED && type === QuestionType.PREDICTION) {
|
|
119
|
+
const notificationId = this.background.getCurrentSessionId({
|
|
120
|
+
prefix: `notification-id:${id}`,
|
|
121
|
+
});
|
|
122
|
+
const feedList = [...(this.feedList.getValues().data || [])];
|
|
123
|
+
const questionFromFeedListIndex = feedList.findIndex((item) => item.id === id);
|
|
124
|
+
const questionFromFeedList = feedList[questionFromFeedListIndex];
|
|
125
|
+
// @ts-ignore
|
|
126
|
+
let votedAnswerId = questionFromFeedList?.attributes?.attributes?.value?.answerId;
|
|
127
|
+
// get voted answer id from extended question or feed list
|
|
128
|
+
const data = $questionByUser(id, this.transport);
|
|
129
|
+
// order of operations is important here
|
|
130
|
+
const cancel = data.subscribe(() => { });
|
|
131
|
+
await data.get().promise;
|
|
132
|
+
const extendedQuestion = data.get().data;
|
|
133
|
+
cancel();
|
|
134
|
+
window.requestAnimationFrame(() => {
|
|
135
|
+
data.invalidate();
|
|
136
|
+
});
|
|
137
|
+
// get extended question data and mark as dirty
|
|
138
|
+
if (!votedAnswerId) {
|
|
139
|
+
votedAnswerId = extendedQuestion?.answers.find(({ youVoted }) => youVoted)?.id;
|
|
119
140
|
}
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
141
|
+
const correctAnswer = answers.find(({ correct }) => correct);
|
|
142
|
+
const votedAnswer = votedAnswerId ? answers.find(({ id }) => id === votedAnswerId) : undefined;
|
|
143
|
+
const votedCorrect = !!votedAnswer?.correct;
|
|
144
|
+
// update question in feed list if it's there
|
|
145
|
+
if (questionFromFeedList) {
|
|
146
|
+
if (feedList[questionFromFeedListIndex]?.attributes?.attributes.case === 'question') {
|
|
147
|
+
try {
|
|
148
|
+
// @ts-ignore
|
|
149
|
+
feedList[questionFromFeedListIndex].attributes.attributes.value.answerId = votedAnswerId;
|
|
150
|
+
// @ts-ignore
|
|
151
|
+
feedList[questionFromFeedListIndex].attributes.attributes.value.openForVoting = false;
|
|
152
|
+
if (votedAnswerId) {
|
|
153
|
+
// @ts-ignore
|
|
154
|
+
feedList[questionFromFeedListIndex].attributes.attributes.value.status = votedCorrect
|
|
155
|
+
? PickHistoryStatus.WON
|
|
156
|
+
: PickHistoryStatus.LOST;
|
|
157
|
+
}
|
|
158
|
+
// eslint-disable-next-line no-empty
|
|
159
|
+
}
|
|
160
|
+
catch (e) { }
|
|
161
|
+
this.feedList.getStore().mutate(feedList);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (!votedAnswer || !correctAnswer)
|
|
165
|
+
return;
|
|
166
|
+
// avoid showing notification if question already opened
|
|
167
|
+
if (this.openedQuestionId.get()?.questionId !== question.id) {
|
|
130
168
|
this.notifications.add({
|
|
131
169
|
type: NotificationType.QUESTION_RESOLVED,
|
|
132
|
-
action: () => this.openQuestion(id),
|
|
170
|
+
action: () => this.openQuestion(question.id, feedItem),
|
|
133
171
|
close: () => this.closeQuestion(id),
|
|
134
|
-
autoHideDuration:
|
|
172
|
+
autoHideDuration: votedCorrect ? 15000 : 12000,
|
|
135
173
|
id: notificationId,
|
|
174
|
+
emitEvent: false,
|
|
136
175
|
data: {
|
|
176
|
+
questionId: id,
|
|
137
177
|
questionType: QuestionType.PREDICTION,
|
|
138
178
|
question: {
|
|
139
|
-
title:
|
|
179
|
+
title: votedCorrect
|
|
140
180
|
? `Congratulations! You answered correctly! You won ${correctAnswer.points} pts!`
|
|
141
181
|
: `Better luck next time! Correct: ${correctAnswer?.text}!`,
|
|
142
182
|
votedAnswer: {
|
|
@@ -144,43 +184,164 @@ export class Gamification extends AbstractFeature {
|
|
|
144
184
|
points: votedAnswer?.points,
|
|
145
185
|
},
|
|
146
186
|
correctAnswerTitle: correctAnswer?.text,
|
|
147
|
-
correct:
|
|
187
|
+
correct: !!votedCorrect,
|
|
148
188
|
predictionResult: status === QuestionStatus.RESOLVED,
|
|
149
189
|
questionTitle: question?.subject,
|
|
150
190
|
},
|
|
151
191
|
},
|
|
152
192
|
});
|
|
153
|
-
this.userSummary.invalidate();
|
|
154
193
|
}
|
|
155
|
-
|
|
194
|
+
this.userSummary.invalidate(); // verified, it's necessary
|
|
195
|
+
}
|
|
156
196
|
});
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
197
|
+
// update feed list on question update received from subscription
|
|
198
|
+
// add new question to the top of the list
|
|
199
|
+
this.background.feedSubscription.addListener('feed-subscription-questions-list', (response) => {
|
|
200
|
+
const feedList = [...(this.feedList.getStore().value?.data || [])];
|
|
201
|
+
const feedItem = response.data?.attributes?.feedItem;
|
|
202
|
+
const questionIndex = feedList.findIndex((item) => item.id === feedItem?.id);
|
|
203
|
+
if (!feedItem) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (questionIndex !== -1) {
|
|
207
|
+
if (feedItem.attributes?.attributes.case === 'question' &&
|
|
208
|
+
feedList[questionIndex].attributes?.attributes.case === 'question') {
|
|
209
|
+
const prev = feedList[questionIndex];
|
|
210
|
+
if (prev.attributes) {
|
|
211
|
+
feedList[questionIndex] = {
|
|
212
|
+
...feedList[questionIndex],
|
|
213
|
+
attributes: {
|
|
214
|
+
...prev.attributes,
|
|
215
|
+
attributes: {
|
|
216
|
+
...prev.attributes.attributes,
|
|
217
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
218
|
+
// @ts-ignore
|
|
219
|
+
value: {
|
|
220
|
+
...prev.attributes.attributes.value,
|
|
221
|
+
...feedItem.attributes.attributes.value,
|
|
222
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
223
|
+
// @ts-ignore
|
|
224
|
+
answerId: prev.attributes.attributes.value.answerId,
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
226
|
+
// @ts-ignore
|
|
227
|
+
status: prev.attributes.attributes.value.status,
|
|
228
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
229
|
+
// @ts-ignore
|
|
230
|
+
openForVoting: prev.attributes.attributes.value.openForVoting,
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
feedList[questionIndex] = feedItem;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (questionIndex === -1) {
|
|
242
|
+
feedList.unshift(feedItem);
|
|
243
|
+
eventBus.emit('poll', {
|
|
244
|
+
action: 'received',
|
|
245
|
+
payload: {
|
|
246
|
+
questionId: feedItem.id,
|
|
247
|
+
questionType: feedItem.attributes?.type,
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
this.feedList.getStore().mutate(feedList);
|
|
161
252
|
});
|
|
162
253
|
};
|
|
254
|
+
// not used
|
|
163
255
|
disconnect = () => {
|
|
256
|
+
this.background.feedSubscription.removeListener('feed-subscription-prediction-close');
|
|
164
257
|
this.background.feedSubscription.removeListener('feed-subscription-questions-list');
|
|
165
258
|
};
|
|
166
259
|
submitAnswer = async (questionId, answerId) => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
260
|
+
const data = $questionByUser(questionId, this.transport);
|
|
261
|
+
const updateQuestionAndFieldList = () => {
|
|
262
|
+
const feedList = this.feedList.getValues().data;
|
|
263
|
+
if (!feedList) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const questionIndex = feedList.findIndex((item) => item.id === questionId);
|
|
267
|
+
const poll = feedList[questionIndex];
|
|
268
|
+
const question = poll?.attributes?.attributes.case === 'question' && poll.attributes.attributes.value;
|
|
269
|
+
if (question) {
|
|
270
|
+
eventBus.emit('poll', {
|
|
271
|
+
action: 'voted',
|
|
272
|
+
payload: {
|
|
273
|
+
questionId,
|
|
274
|
+
questionType: question.questionType,
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
const cancel = data.subscribe(() => { });
|
|
278
|
+
const extendedQuestion = data.get().data;
|
|
279
|
+
cancel();
|
|
280
|
+
if (extendedQuestion) {
|
|
281
|
+
const correctAnswer = extendedQuestion.answers.find((answer) => answer.correct === true);
|
|
282
|
+
const votedAnswerIdx = extendedQuestion.answers.findIndex((answer) => answer.id === answerId);
|
|
283
|
+
const votedAnswer = extendedQuestion.answers[votedAnswerIdx];
|
|
284
|
+
// @ts-ignore
|
|
285
|
+
feedList[questionIndex].attributes.attributes.value.answerId = answerId;
|
|
286
|
+
// @ts-ignore
|
|
287
|
+
feedList[questionIndex].attributes.attributes.value.openForVoting = false;
|
|
288
|
+
// @ts-ignore
|
|
289
|
+
feedList[questionIndex].attributes.attributes.value.text = votedAnswer?.text || '';
|
|
290
|
+
if (correctAnswer) {
|
|
291
|
+
// @ts-ignore
|
|
292
|
+
feedList[questionIndex].attributes.attributes.value.status =
|
|
293
|
+
correctAnswer.id === answerId ? PickHistoryStatus.WON : PickHistoryStatus.LOST;
|
|
294
|
+
}
|
|
295
|
+
this.feedList.getStore().mutate([...feedList]);
|
|
296
|
+
extendedQuestion.answers[votedAnswerIdx].correct = correctAnswer?.id === answerId;
|
|
297
|
+
extendedQuestion.answers[votedAnswerIdx].youVoted = true;
|
|
298
|
+
if (correctAnswer?.id === answerId) {
|
|
299
|
+
extendedQuestion.answers[votedAnswerIdx].pointsEarned =
|
|
300
|
+
extendedQuestion.status === QuestionStatus.RESOLVED ? 0 : correctAnswer.points;
|
|
301
|
+
}
|
|
302
|
+
data.mutate({ ...extendedQuestion });
|
|
303
|
+
}
|
|
304
|
+
if (this.submitAnswerTimeout) {
|
|
305
|
+
clearTimeout(this.submitAnswerTimeout);
|
|
306
|
+
}
|
|
307
|
+
this.submitAnswerTimeout = setTimeout(() => {
|
|
308
|
+
this.userSummary.invalidate(); // verified, it's necessary
|
|
309
|
+
}, 1000);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
try {
|
|
313
|
+
await actions.submitAnswer(this.transport, { questionId, answerId });
|
|
314
|
+
updateQuestionAndFieldList();
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
if (error.message === ERROR.ALREADY_VOTED) {
|
|
318
|
+
this.userSummary.invalidate();
|
|
319
|
+
const cancel = data.subscribe(() => { });
|
|
320
|
+
data.invalidate();
|
|
321
|
+
cancel();
|
|
322
|
+
}
|
|
323
|
+
throw error;
|
|
324
|
+
}
|
|
176
325
|
};
|
|
177
|
-
openQuestion = (questionId) => {
|
|
178
|
-
this.openFeature();
|
|
326
|
+
openQuestion = (questionId, question) => {
|
|
179
327
|
this.notifications.close(this.background.getCurrentSessionId({
|
|
180
328
|
prefix: 'notification',
|
|
181
329
|
entity: questionId,
|
|
182
330
|
}));
|
|
183
|
-
|
|
331
|
+
let questionType = question?.attributes?.type;
|
|
332
|
+
if (!questionType) {
|
|
333
|
+
const feedList = this.feedList.getStore().value?.data || [];
|
|
334
|
+
questionType = feedList.find((item) => item.id === questionId)?.attributes?.type;
|
|
335
|
+
}
|
|
336
|
+
eventBus.emit('poll', {
|
|
337
|
+
action: 'opened',
|
|
338
|
+
payload: {
|
|
339
|
+
questionId,
|
|
340
|
+
questionType,
|
|
341
|
+
questionOpenedFrom: question?.openedFrom,
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
return this.background.openQuestion(questionId, question);
|
|
184
345
|
};
|
|
185
346
|
closeQuestion = (questionId) => {
|
|
186
347
|
return this.background.closeQuestion(questionId);
|
|
@@ -204,22 +365,22 @@ export class Gamification extends AbstractFeature {
|
|
|
204
365
|
if (question.data.question?.id !== undefined &&
|
|
205
366
|
question.data.question.notification !== undefined &&
|
|
206
367
|
question.data.question?.bypassNotifications?.inAppSilence !== SilenceSetting.ON &&
|
|
207
|
-
question.data.question.status === QuestionStatus.ACTIVE
|
|
368
|
+
question.data.question.status === QuestionStatus.ACTIVE &&
|
|
369
|
+
!question.data.question.marketClosed) {
|
|
208
370
|
if (InteractiveQuestionTypes.has(question.data.question.type)) {
|
|
209
371
|
if (this.isInteractiveAllowed) {
|
|
210
372
|
this.notifications.add({
|
|
211
373
|
type: NotificationType.QUESTION,
|
|
212
|
-
action: () =>
|
|
213
|
-
question.data?.question && this.openQuestion(question.data.question.id);
|
|
214
|
-
this.openFeature();
|
|
215
|
-
},
|
|
374
|
+
action: () => question.data?.question && this.openQuestion(question.data.question.id, question.data.feedItem),
|
|
216
375
|
close: () => question.data?.question && this.closeQuestion(question.data.question.id),
|
|
217
376
|
autoHideDuration: 1000 * 60,
|
|
218
377
|
id: this.background.getCurrentSessionId({
|
|
219
378
|
prefix: 'notification',
|
|
220
379
|
entity: question.data.question.id,
|
|
221
380
|
}),
|
|
381
|
+
emitEvent: true,
|
|
222
382
|
data: {
|
|
383
|
+
questionId: question.data.question.id,
|
|
223
384
|
questionType: question.data.question.type,
|
|
224
385
|
question: {
|
|
225
386
|
title: question.data.question.notification.title,
|
|
@@ -233,7 +394,7 @@ export class Gamification extends AbstractFeature {
|
|
|
233
394
|
const instantView = {
|
|
234
395
|
heading: question.data.question.notification.title,
|
|
235
396
|
body: question.data.question.notification.body,
|
|
236
|
-
imageMode:
|
|
397
|
+
imageMode: optionsValue.imageMode,
|
|
237
398
|
image: optionsValue?.image,
|
|
238
399
|
video: {
|
|
239
400
|
id: optionsValue?.video?.id || '',
|
|
@@ -247,14 +408,13 @@ export class Gamification extends AbstractFeature {
|
|
|
247
408
|
};
|
|
248
409
|
this.notifications.add({
|
|
249
410
|
type: NotificationType.QUESTION,
|
|
250
|
-
action: () =>
|
|
251
|
-
question.data?.question && this.openQuestion(question.data.question.id);
|
|
252
|
-
this.openFeature();
|
|
253
|
-
},
|
|
411
|
+
action: () => question.data?.question && this.openQuestion(question.data.question.id, question.data.feedItem),
|
|
254
412
|
close: () => question.data?.question && this.closeQuestion(question.data.question.id),
|
|
255
413
|
autoHideDuration: 1000 * 120,
|
|
414
|
+
emitEvent: true,
|
|
256
415
|
id: this.background.getCurrentSessionId({ prefix: 'notification', entity: question.data.question.id }),
|
|
257
416
|
data: {
|
|
417
|
+
questionId: question.data.question.id,
|
|
258
418
|
questionType: question.data.question.type,
|
|
259
419
|
insight: instantView,
|
|
260
420
|
},
|
|
@@ -269,17 +429,17 @@ export class Gamification extends AbstractFeature {
|
|
|
269
429
|
account: optionsValue?.tweetMeta?.account || '',
|
|
270
430
|
accountVerified: !!optionsValue?.tweetMeta?.accountVerified,
|
|
271
431
|
tweet: optionsValue?.tweetMeta?.tweet,
|
|
432
|
+
tweetId: question.data.question.id,
|
|
272
433
|
};
|
|
273
434
|
this.notifications.add({
|
|
274
435
|
type: NotificationType.QUESTION,
|
|
275
|
-
action: () =>
|
|
276
|
-
question.data?.question && this.openQuestion(question.data.question.id);
|
|
277
|
-
this.openFeature();
|
|
278
|
-
},
|
|
436
|
+
action: () => question.data?.question && this.openQuestion(question.data.question.id, question.data.feedItem),
|
|
279
437
|
close: () => question.data?.question && this.closeQuestion(question.data.question.id),
|
|
280
438
|
autoHideDuration: 1000 * 120,
|
|
439
|
+
emitEvent: true,
|
|
281
440
|
id: this.background.getCurrentSessionId({ prefix: 'notification', entity: question.data.question.id }),
|
|
282
441
|
data: {
|
|
442
|
+
questionId: question.data.question.id,
|
|
283
443
|
questionType: question.data.question.type,
|
|
284
444
|
tweet: tweetView,
|
|
285
445
|
},
|
package/lib/onboarding.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createSingleStore } from '@streamlayer/sdk-web-interfaces';
|
|
1
|
+
import { createSingleStore, eventBus } from '@streamlayer/sdk-web-interfaces';
|
|
2
2
|
import { NotificationType } from '@streamlayer/sdk-web-notifications';
|
|
3
3
|
import { QuestionType } from '@streamlayer/sdk-web-types';
|
|
4
4
|
import { GamificationStorage } from './storage';
|
|
@@ -31,6 +31,7 @@ const showOnboardingInApp = (service, background, notifications, storage) => {
|
|
|
31
31
|
persistent: true,
|
|
32
32
|
autoHideDuration: 1000000,
|
|
33
33
|
data: {
|
|
34
|
+
questionId: 'onboarding',
|
|
34
35
|
questionType: QuestionType.UNSET,
|
|
35
36
|
onboarding: {
|
|
36
37
|
...inplayGame,
|
|
@@ -112,7 +113,7 @@ export const onboarding = (service, background, transport, notifications) => {
|
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
if (onboardingStatus === OnboardingStatus.Completed) {
|
|
115
|
-
background.activeQuestionId.invalidate();
|
|
116
|
+
background.activeQuestionId.invalidate(); // verified, it's necessary
|
|
116
117
|
}
|
|
117
118
|
storage.setOnboardingInstantOpen({
|
|
118
119
|
userId: background.userId.get() || '',
|
|
@@ -149,6 +150,10 @@ export const onboarding = (service, background, transport, notifications) => {
|
|
|
149
150
|
userId: background.userId.get() || '',
|
|
150
151
|
eventId,
|
|
151
152
|
}, OnboardingStatus.Completed);
|
|
153
|
+
eventBus.emit('poll', {
|
|
154
|
+
action: 'onboardingPassed',
|
|
155
|
+
payload: {},
|
|
156
|
+
});
|
|
152
157
|
const notificationId = background.getCurrentSessionId({ prefix: 'onboarding' });
|
|
153
158
|
notifications.close(notificationId);
|
|
154
159
|
}
|
package/lib/queries/actions.js
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
|
+
import { ConnectError, Code } from '@connectrpc/connect';
|
|
1
2
|
import { Feed } from '@streamlayer/sl-eslib/interactive/feed/interactive.feed_connect';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { ERROR } from '../constants';
|
|
4
|
+
export const submitAnswer = async (transport, data) => {
|
|
5
|
+
const { client, createRequestOptions } = transport.createPromiseClient(Feed, { method: 'submitAnswer' });
|
|
6
|
+
const contextValues = createRequestOptions({ retryAttempts: 0 });
|
|
7
|
+
try {
|
|
8
|
+
return await client.submitAnswer({ data }, { contextValues });
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
const cErr = ConnectError.from(error);
|
|
12
|
+
if (cErr?.code === Code.AlreadyExists) {
|
|
13
|
+
throw new Error(ERROR.ALREADY_VOTED);
|
|
14
|
+
}
|
|
15
|
+
throw new Error(ERROR.UNKNOWN);
|
|
16
|
+
}
|
|
5
17
|
};
|
|
6
18
|
export const submitInplay = (transport, eventId) => {
|
|
7
|
-
const { client } = transport.createPromiseClient(Feed, { method: 'submitInplay' });
|
|
8
|
-
|
|
19
|
+
const { client, createRequestOptions } = transport.createPromiseClient(Feed, { method: 'submitInplay' });
|
|
20
|
+
const contextValues = createRequestOptions({ retryAttempts: 0 });
|
|
21
|
+
return client.submitInplay({ data: { eventId: +eventId } }, { contextValues });
|
|
9
22
|
};
|
|
10
23
|
export const skipQuestion = (transport, questionId) => {
|
|
11
|
-
const { client } = transport.createPromiseClient(Feed, { method: 'skipQuestion' });
|
|
12
|
-
|
|
24
|
+
const { client, createRequestOptions } = transport.createPromiseClient(Feed, { method: 'skipQuestion' });
|
|
25
|
+
const contextValues = createRequestOptions({ retryAttempts: 0 });
|
|
26
|
+
return client.skipQuestion({ data: { id: questionId } }, { contextValues });
|
|
13
27
|
};
|
package/lib/queries/deepLink.js
CHANGED
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
|
|
332
|
+
export declare const $questionByUser: ($questionId: ReadableAtom<string | undefined> | string, transport: Transport) => import("@nanostores/query").FetcherStore<import("@bufbuild/protobuf").PlainMessage<import("@streamlayer/sl-eslib/interactive/interactive.common_pb").ExtendedQuestion>, 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/lib/queries/index.js
CHANGED
|
@@ -16,6 +16,8 @@ export const $activeQuestion = (slStreamId, transport) => {
|
|
|
16
16
|
});
|
|
17
17
|
return res.data?.attributes;
|
|
18
18
|
},
|
|
19
|
+
dedupeTime: 1000 * 60 * 10, // 10 minutes
|
|
20
|
+
refetchInterval: 0,
|
|
19
21
|
});
|
|
20
22
|
};
|
|
21
23
|
export const feedSubscription = ($slStreamId, transport) => {
|
|
@@ -60,6 +62,7 @@ export const $questionByUser = ($questionId, transport) => {
|
|
|
60
62
|
});
|
|
61
63
|
return res.data?.attributes?.question;
|
|
62
64
|
},
|
|
65
|
+
dedupeTime: 1000 * 60 * 5,
|
|
63
66
|
});
|
|
64
67
|
};
|
|
65
68
|
export const $pickHistory = (slStreamId, transport) => {
|
|
@@ -97,6 +100,8 @@ export const $feedList = ($slStreamId, $interactiveAllowed, transport) => {
|
|
|
97
100
|
});
|
|
98
101
|
return res.data;
|
|
99
102
|
},
|
|
103
|
+
dedupeTime: 0,
|
|
104
|
+
refetchInterval: 0,
|
|
100
105
|
});
|
|
101
106
|
};
|
|
102
107
|
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.39.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@bufbuild/protobuf": "^1.
|
|
6
|
-
"@fastify/deepmerge": "
|
|
7
|
-
"@streamlayer/sl-eslib": "^5.
|
|
8
|
-
"nanostores": "^0.
|
|
9
|
-
"@streamlayer/sdk-web-api": "^0.
|
|
10
|
-
"@streamlayer/sdk-web-core": "^0.
|
|
11
|
-
"@streamlayer/sdk-web-interfaces": "^0.
|
|
12
|
-
"@streamlayer/sdk-web-logger": "^0.5.
|
|
13
|
-
"@streamlayer/sdk-web-notifications": "^0.
|
|
14
|
-
"@streamlayer/sdk-web-storage": "^0.4.
|
|
15
|
-
"@streamlayer/sdk-web-types": "^0.
|
|
5
|
+
"@bufbuild/protobuf": "^1.7.2",
|
|
6
|
+
"@fastify/deepmerge": "^1.3.0",
|
|
7
|
+
"@streamlayer/sl-eslib": "^5.83.1",
|
|
8
|
+
"nanostores": "^0.10.0",
|
|
9
|
+
"@streamlayer/sdk-web-api": "^0.24.0",
|
|
10
|
+
"@streamlayer/sdk-web-core": "^0.22.0",
|
|
11
|
+
"@streamlayer/sdk-web-interfaces": "^0.21.0",
|
|
12
|
+
"@streamlayer/sdk-web-logger": "^0.5.18",
|
|
13
|
+
"@streamlayer/sdk-web-notifications": "^0.15.0",
|
|
14
|
+
"@streamlayer/sdk-web-storage": "^0.4.5",
|
|
15
|
+
"@streamlayer/sdk-web-types": "^0.23.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"tslib": "^2.6.2"
|