@stackedapp/utils 1.11.1 → 1.12.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/dist/conditions.d.ts +24 -0
- package/dist/conditions.js +337 -129
- package/package.json +1 -1
package/dist/conditions.d.ts
CHANGED
|
@@ -12,14 +12,22 @@ export declare const meetsBaseConditions: ({ conditions, playerSnap, addDetails,
|
|
|
12
12
|
additionalData?: StackedBaseUserExtra;
|
|
13
13
|
}) => {
|
|
14
14
|
isValid: boolean;
|
|
15
|
+
canClaim: boolean;
|
|
16
|
+
percentCompleted: number;
|
|
15
17
|
conditionData?: undefined;
|
|
16
18
|
} | {
|
|
19
|
+
/** @deprecated Use canClaim instead */
|
|
17
20
|
isValid: boolean;
|
|
21
|
+
canClaim: boolean;
|
|
22
|
+
percentCompleted: number;
|
|
18
23
|
conditionData: {
|
|
19
24
|
isMet: boolean;
|
|
20
25
|
kind?: keyof StackedBaseConditions;
|
|
26
|
+
/** @deprecated Use percentCompleted instead */
|
|
21
27
|
trackerAmount?: number;
|
|
28
|
+
/** @deprecated Use percentCompleted instead */
|
|
22
29
|
trackerGoal?: number;
|
|
30
|
+
percentCompleted: number;
|
|
23
31
|
text: string;
|
|
24
32
|
}[] | undefined;
|
|
25
33
|
};
|
|
@@ -41,15 +49,23 @@ export declare const meetsLinkedEntityOffersCondition: ({ linkedEntityOffers, ma
|
|
|
41
49
|
};
|
|
42
50
|
export declare const offerMeetsCompletionConditions: (offer: StackedOffer, snapshot: StackedSnapshot, additionalData?: StackedBaseUserExtra) => {
|
|
43
51
|
isValid: boolean;
|
|
52
|
+
canClaim: boolean;
|
|
53
|
+
percentCompleted: number;
|
|
44
54
|
availableClaimsNow: number;
|
|
45
55
|
conditionData?: undefined;
|
|
46
56
|
} | {
|
|
57
|
+
/** @deprecated Use canClaim instead */
|
|
47
58
|
isValid: boolean;
|
|
59
|
+
canClaim: boolean;
|
|
60
|
+
percentCompleted: number;
|
|
48
61
|
conditionData: {
|
|
49
62
|
isMet: boolean;
|
|
50
63
|
kind?: keyof StackedCompletionConditions | "context";
|
|
64
|
+
/** @deprecated Use percentCompleted instead */
|
|
51
65
|
trackerAmount?: number;
|
|
66
|
+
/** @deprecated Use percentCompleted instead */
|
|
52
67
|
trackerGoal?: number;
|
|
68
|
+
percentCompleted: number;
|
|
53
69
|
text: string;
|
|
54
70
|
}[];
|
|
55
71
|
availableClaimsNow: number;
|
|
@@ -67,15 +83,23 @@ export declare const meetsCompletionConditions: ({ completionConditions, complet
|
|
|
67
83
|
additionalData?: StackedBaseUserExtra;
|
|
68
84
|
}) => {
|
|
69
85
|
isValid: boolean;
|
|
86
|
+
canClaim: boolean;
|
|
87
|
+
percentCompleted: number;
|
|
70
88
|
availableClaimsNow: number;
|
|
71
89
|
conditionData?: undefined;
|
|
72
90
|
} | {
|
|
91
|
+
/** @deprecated Use canClaim instead */
|
|
73
92
|
isValid: boolean;
|
|
93
|
+
canClaim: boolean;
|
|
94
|
+
percentCompleted: number;
|
|
74
95
|
conditionData: {
|
|
75
96
|
isMet: boolean;
|
|
76
97
|
kind?: keyof StackedCompletionConditions | "context";
|
|
98
|
+
/** @deprecated Use percentCompleted instead */
|
|
77
99
|
trackerAmount?: number;
|
|
100
|
+
/** @deprecated Use percentCompleted instead */
|
|
78
101
|
trackerGoal?: number;
|
|
102
|
+
percentCompleted: number;
|
|
79
103
|
text: string;
|
|
80
104
|
}[];
|
|
81
105
|
availableClaimsNow: number;
|
package/dist/conditions.js
CHANGED
|
@@ -10,6 +10,58 @@ const template_1 = require("./template");
|
|
|
10
10
|
const dynamic_1 = require("./dynamic");
|
|
11
11
|
const blockchain_utils_1 = require("./blockchain_utils");
|
|
12
12
|
exports.DEFAULT_ENTITY_KIND = '_default';
|
|
13
|
+
const calculatePercent = (amount, goal, isMet) => {
|
|
14
|
+
if (goal && goal > 0 && amount !== undefined) {
|
|
15
|
+
return Math.min(100, (amount / goal) * 100);
|
|
16
|
+
}
|
|
17
|
+
return isMet ? 100 : 0;
|
|
18
|
+
};
|
|
19
|
+
const calculateDynamicConditionPercent = (dynamicObj, cond) => {
|
|
20
|
+
if (!dynamicObj)
|
|
21
|
+
return 0;
|
|
22
|
+
const val = dynamicObj[cond.key];
|
|
23
|
+
if (typeof val !== 'number')
|
|
24
|
+
return 0;
|
|
25
|
+
const compareTo = typeof cond.compareTo === 'number' ? cond.compareTo : 0;
|
|
26
|
+
switch (cond.operator) {
|
|
27
|
+
case '>=':
|
|
28
|
+
case '>':
|
|
29
|
+
return compareTo > 0 ? Math.min(100, (val / compareTo) * 100) : val >= compareTo ? 100 : 0;
|
|
30
|
+
case '==':
|
|
31
|
+
return val === compareTo ? 100 : 0;
|
|
32
|
+
case '!=':
|
|
33
|
+
return val !== compareTo ? 100 : 0;
|
|
34
|
+
case '<=':
|
|
35
|
+
case '<':
|
|
36
|
+
return val <= compareTo ? 100 : 0; // Binary for max-style
|
|
37
|
+
default:
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const calculateDynamicGroupPercent = (dynamicObj, group) => {
|
|
42
|
+
if (!group?.conditions?.length)
|
|
43
|
+
return 100;
|
|
44
|
+
const percentages = group.conditions.map((c) => calculateDynamicConditionPercent(dynamicObj, c));
|
|
45
|
+
if (!group.links?.length) {
|
|
46
|
+
return percentages.reduce((sum, p) => sum + p, 0) / percentages.length;
|
|
47
|
+
}
|
|
48
|
+
let result = percentages[0];
|
|
49
|
+
for (let i = 0; i < group.links.length; i++) {
|
|
50
|
+
const nextPct = percentages[i + 1];
|
|
51
|
+
switch (group.links[i]) {
|
|
52
|
+
case 'AND':
|
|
53
|
+
result = (result + nextPct) / 2;
|
|
54
|
+
break;
|
|
55
|
+
case 'OR':
|
|
56
|
+
result = Math.max(result, nextPct);
|
|
57
|
+
break;
|
|
58
|
+
case 'AND NOT':
|
|
59
|
+
result = (result + (100 - nextPct)) / 2;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
13
65
|
const meetsBaseConditions = ({ conditions, playerSnap, addDetails,
|
|
14
66
|
/** this exists if calling meetsBaseConditions from meetsCompletionConditions. but surfacing
|
|
15
67
|
* check doesn't use this since we don't have a playerOffer at surfacing time
|
|
@@ -20,48 +72,57 @@ additionalData, }) => {
|
|
|
20
72
|
const conditionData = [];
|
|
21
73
|
let isValid = true;
|
|
22
74
|
if (conditions?.minDaysInGame) {
|
|
23
|
-
const
|
|
75
|
+
const trackerAmount = playerSnap.daysInGame || 0;
|
|
76
|
+
const trackerGoal = conditions.minDaysInGame;
|
|
77
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
24
78
|
if (addDetails) {
|
|
25
79
|
conditionData.push({
|
|
26
80
|
isMet: !isDisqualify,
|
|
27
81
|
kind: 'minDaysInGame',
|
|
28
|
-
trackerAmount
|
|
29
|
-
trackerGoal
|
|
30
|
-
|
|
82
|
+
trackerAmount,
|
|
83
|
+
trackerGoal,
|
|
84
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
85
|
+
text: `More than ${trackerGoal} Days in Game`,
|
|
31
86
|
});
|
|
32
87
|
if (isDisqualify)
|
|
33
88
|
isValid = false;
|
|
34
89
|
}
|
|
35
90
|
else {
|
|
36
91
|
if (isDisqualify)
|
|
37
|
-
return { isValid: false };
|
|
92
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
38
93
|
}
|
|
39
94
|
}
|
|
40
95
|
if (conditions?.minTrustScore) {
|
|
41
|
-
const
|
|
96
|
+
const trackerAmount = playerSnap.trustScore || 0;
|
|
97
|
+
const trackerGoal = conditions.minTrustScore;
|
|
98
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
42
99
|
if (addDetails) {
|
|
43
100
|
conditionData.push({
|
|
44
101
|
isMet: !isDisqualify,
|
|
45
102
|
kind: 'minTrustScore',
|
|
46
|
-
trackerAmount
|
|
47
|
-
trackerGoal
|
|
48
|
-
|
|
103
|
+
trackerAmount,
|
|
104
|
+
trackerGoal,
|
|
105
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
106
|
+
text: `More than ${trackerGoal} Rep`,
|
|
49
107
|
});
|
|
50
108
|
if (isDisqualify)
|
|
51
109
|
isValid = false;
|
|
52
110
|
}
|
|
53
111
|
else {
|
|
54
112
|
if (isDisqualify)
|
|
55
|
-
return { isValid: false };
|
|
113
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
56
114
|
}
|
|
57
115
|
}
|
|
58
116
|
if (conditions?.maxTrustScore) {
|
|
59
|
-
const
|
|
117
|
+
const trackerAmount = playerSnap.trustScore || 0;
|
|
118
|
+
const isDisqualify = trackerAmount > conditions.maxTrustScore;
|
|
60
119
|
if (addDetails) {
|
|
61
120
|
conditionData.push({
|
|
62
121
|
isMet: !isDisqualify,
|
|
63
122
|
kind: 'maxTrustScore',
|
|
64
|
-
trackerAmount
|
|
123
|
+
trackerAmount,
|
|
124
|
+
trackerGoal: conditions.maxTrustScore,
|
|
125
|
+
percentCompleted: !isDisqualify ? 100 : 0, // Binary for max conditions
|
|
65
126
|
text: `Less than ${conditions.maxTrustScore} Rep`,
|
|
66
127
|
});
|
|
67
128
|
if (isDisqualify)
|
|
@@ -69,7 +130,7 @@ additionalData, }) => {
|
|
|
69
130
|
}
|
|
70
131
|
else {
|
|
71
132
|
if (isDisqualify)
|
|
72
|
-
return { isValid: false };
|
|
133
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
73
134
|
}
|
|
74
135
|
}
|
|
75
136
|
for (const key in conditions?.achievements) {
|
|
@@ -83,6 +144,7 @@ additionalData, }) => {
|
|
|
83
144
|
kind: 'achievements',
|
|
84
145
|
trackerAmount: 0,
|
|
85
146
|
trackerGoal: 1,
|
|
147
|
+
percentCompleted: 0,
|
|
86
148
|
text: `Have the achievement ${a.name}`,
|
|
87
149
|
});
|
|
88
150
|
if (isDisqualify)
|
|
@@ -90,25 +152,28 @@ additionalData, }) => {
|
|
|
90
152
|
}
|
|
91
153
|
else {
|
|
92
154
|
if (isDisqualify)
|
|
93
|
-
return { isValid: false };
|
|
155
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
94
156
|
}
|
|
95
157
|
}
|
|
96
158
|
if (a.minCount) {
|
|
97
|
-
const
|
|
159
|
+
const trackerAmount = playerAchData?.count || 0;
|
|
160
|
+
const trackerGoal = a.minCount;
|
|
161
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
98
162
|
if (addDetails) {
|
|
99
163
|
conditionData.push({
|
|
100
164
|
isMet: !isDisqualify,
|
|
101
165
|
kind: 'achievements',
|
|
102
|
-
trackerAmount
|
|
103
|
-
trackerGoal
|
|
104
|
-
|
|
166
|
+
trackerAmount,
|
|
167
|
+
trackerGoal,
|
|
168
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
169
|
+
text: `Have the achievement ${a.name} more than ${trackerGoal} times`,
|
|
105
170
|
});
|
|
106
171
|
if (isDisqualify)
|
|
107
172
|
isValid = false;
|
|
108
173
|
}
|
|
109
174
|
else {
|
|
110
175
|
if (isDisqualify)
|
|
111
|
-
return { isValid: false };
|
|
176
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
112
177
|
}
|
|
113
178
|
}
|
|
114
179
|
}
|
|
@@ -116,30 +181,36 @@ additionalData, }) => {
|
|
|
116
181
|
const c = conditions.currencies[key];
|
|
117
182
|
const playerCurrencyData = playerSnap.currencies?.[key];
|
|
118
183
|
if (c.min) {
|
|
119
|
-
const
|
|
184
|
+
const trackerAmount = playerCurrencyData?.balance || 0;
|
|
185
|
+
const trackerGoal = c.min;
|
|
186
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
120
187
|
if (addDetails) {
|
|
121
188
|
conditionData.push({
|
|
122
189
|
isMet: !isDisqualify,
|
|
123
190
|
kind: 'currencies',
|
|
124
|
-
trackerAmount
|
|
125
|
-
trackerGoal
|
|
126
|
-
|
|
191
|
+
trackerAmount,
|
|
192
|
+
trackerGoal,
|
|
193
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
194
|
+
text: `Have more than ${trackerGoal} ${c.name}`,
|
|
127
195
|
});
|
|
128
196
|
if (isDisqualify)
|
|
129
197
|
isValid = false;
|
|
130
198
|
}
|
|
131
199
|
else {
|
|
132
200
|
if (isDisqualify)
|
|
133
|
-
return { isValid: false };
|
|
201
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
134
202
|
}
|
|
135
203
|
}
|
|
136
204
|
if (c.max) {
|
|
137
|
-
const
|
|
205
|
+
const trackerAmount = playerCurrencyData?.balance || 0;
|
|
206
|
+
const isDisqualify = trackerAmount > c.max;
|
|
138
207
|
if (addDetails) {
|
|
139
208
|
conditionData.push({
|
|
140
209
|
isMet: !isDisqualify,
|
|
141
210
|
kind: 'currencies',
|
|
142
|
-
trackerAmount
|
|
211
|
+
trackerAmount,
|
|
212
|
+
trackerGoal: c.max,
|
|
213
|
+
percentCompleted: !isDisqualify ? 100 : 0, // Binary for max conditions
|
|
143
214
|
text: `Have less than ${c.max} ${c.name}`,
|
|
144
215
|
});
|
|
145
216
|
if (isDisqualify)
|
|
@@ -147,43 +218,49 @@ additionalData, }) => {
|
|
|
147
218
|
}
|
|
148
219
|
else {
|
|
149
220
|
if (isDisqualify)
|
|
150
|
-
return { isValid: false };
|
|
221
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
151
222
|
}
|
|
152
223
|
}
|
|
153
224
|
if (c.in) {
|
|
154
|
-
const
|
|
225
|
+
const trackerAmount = playerCurrencyData?.in || 0;
|
|
226
|
+
const trackerGoal = c.in;
|
|
227
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
155
228
|
if (addDetails) {
|
|
156
229
|
conditionData.push({
|
|
157
230
|
isMet: !isDisqualify,
|
|
158
231
|
kind: 'currencies',
|
|
159
|
-
trackerAmount
|
|
160
|
-
trackerGoal
|
|
161
|
-
|
|
232
|
+
trackerAmount,
|
|
233
|
+
trackerGoal,
|
|
234
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
235
|
+
text: `Deposit at least ${trackerGoal} ${c.name}`,
|
|
162
236
|
});
|
|
163
237
|
if (isDisqualify)
|
|
164
238
|
isValid = false;
|
|
165
239
|
}
|
|
166
240
|
else {
|
|
167
241
|
if (isDisqualify)
|
|
168
|
-
return { isValid: false };
|
|
242
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
169
243
|
}
|
|
170
244
|
}
|
|
171
245
|
if (c.out) {
|
|
172
|
-
const
|
|
246
|
+
const trackerAmount = playerCurrencyData?.out || 0;
|
|
247
|
+
const trackerGoal = c.out;
|
|
248
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
173
249
|
if (addDetails) {
|
|
174
250
|
conditionData.push({
|
|
175
251
|
isMet: !isDisqualify,
|
|
176
252
|
kind: 'currencies',
|
|
177
|
-
trackerAmount
|
|
178
|
-
trackerGoal
|
|
179
|
-
|
|
253
|
+
trackerAmount,
|
|
254
|
+
trackerGoal,
|
|
255
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
256
|
+
text: `Withdraw at least ${trackerGoal} ${c.name}`,
|
|
180
257
|
});
|
|
181
258
|
if (isDisqualify)
|
|
182
259
|
isValid = false;
|
|
183
260
|
}
|
|
184
261
|
else {
|
|
185
262
|
if (isDisqualify)
|
|
186
|
-
return { isValid: false };
|
|
263
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
187
264
|
}
|
|
188
265
|
}
|
|
189
266
|
}
|
|
@@ -191,30 +268,36 @@ additionalData, }) => {
|
|
|
191
268
|
const l = conditions.levels[key];
|
|
192
269
|
const playerLevelData = playerSnap.levels?.[key];
|
|
193
270
|
if (l.min) {
|
|
194
|
-
const
|
|
271
|
+
const trackerAmount = playerLevelData?.level || 0;
|
|
272
|
+
const trackerGoal = l.min;
|
|
273
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
195
274
|
if (addDetails) {
|
|
196
275
|
conditionData.push({
|
|
197
276
|
isMet: !isDisqualify,
|
|
198
277
|
kind: 'levels',
|
|
199
|
-
trackerAmount
|
|
200
|
-
trackerGoal
|
|
201
|
-
|
|
278
|
+
trackerAmount,
|
|
279
|
+
trackerGoal,
|
|
280
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
281
|
+
text: `Be above level ${trackerGoal} ${l.name}`,
|
|
202
282
|
});
|
|
203
283
|
if (isDisqualify)
|
|
204
284
|
isValid = false;
|
|
205
285
|
}
|
|
206
286
|
else {
|
|
207
287
|
if (isDisqualify)
|
|
208
|
-
return { isValid: false };
|
|
288
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
209
289
|
}
|
|
210
290
|
}
|
|
211
291
|
if (l.max) {
|
|
212
|
-
const
|
|
292
|
+
const trackerAmount = playerLevelData?.level || 0;
|
|
293
|
+
const isDisqualify = trackerAmount > l.max;
|
|
213
294
|
if (addDetails) {
|
|
214
295
|
conditionData.push({
|
|
215
296
|
isMet: !isDisqualify,
|
|
216
297
|
kind: 'levels',
|
|
217
|
-
trackerAmount
|
|
298
|
+
trackerAmount,
|
|
299
|
+
trackerGoal: l.max,
|
|
300
|
+
percentCompleted: !isDisqualify ? 100 : 0, // Binary for max conditions
|
|
218
301
|
text: `Be under level ${l.max} ${l.name}`,
|
|
219
302
|
});
|
|
220
303
|
if (isDisqualify)
|
|
@@ -222,7 +305,7 @@ additionalData, }) => {
|
|
|
222
305
|
}
|
|
223
306
|
else {
|
|
224
307
|
if (isDisqualify)
|
|
225
|
-
return { isValid: false };
|
|
308
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
226
309
|
}
|
|
227
310
|
}
|
|
228
311
|
}
|
|
@@ -230,13 +313,16 @@ additionalData, }) => {
|
|
|
230
313
|
for (const questId in conditions.quests) {
|
|
231
314
|
const quest = conditions.quests[questId];
|
|
232
315
|
const playerQuestData = playerSnap.quests?.[questId];
|
|
233
|
-
const
|
|
316
|
+
const trackerAmount = playerQuestData?.completions || 0;
|
|
317
|
+
const trackerGoal = quest.completions || 1;
|
|
318
|
+
const isDisqualify = playerQuestData ? trackerAmount < (quest.completions || 0) : true;
|
|
234
319
|
if (addDetails) {
|
|
235
320
|
conditionData.push({
|
|
236
321
|
isMet: !isDisqualify,
|
|
237
322
|
kind: 'quests',
|
|
238
|
-
trackerAmount
|
|
239
|
-
trackerGoal
|
|
323
|
+
trackerAmount,
|
|
324
|
+
trackerGoal,
|
|
325
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
240
326
|
text: quest.completions === 1
|
|
241
327
|
? `Complete the quest ${quest.name}`
|
|
242
328
|
: (quest.completions || 0) < 1
|
|
@@ -248,7 +334,7 @@ additionalData, }) => {
|
|
|
248
334
|
}
|
|
249
335
|
else {
|
|
250
336
|
if (isDisqualify)
|
|
251
|
-
return { isValid: false };
|
|
337
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
252
338
|
}
|
|
253
339
|
}
|
|
254
340
|
}
|
|
@@ -256,30 +342,36 @@ additionalData, }) => {
|
|
|
256
342
|
const m = conditions.memberships[key];
|
|
257
343
|
const playerMembershipsData = playerSnap.memberships?.[key];
|
|
258
344
|
if (m.minCount) {
|
|
259
|
-
const
|
|
345
|
+
const trackerAmount = playerMembershipsData?.count || 0;
|
|
346
|
+
const trackerGoal = m.minCount;
|
|
347
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
260
348
|
if (addDetails) {
|
|
261
349
|
conditionData.push({
|
|
262
350
|
isMet: !isDisqualify,
|
|
263
351
|
kind: 'memberships',
|
|
264
|
-
trackerAmount
|
|
265
|
-
trackerGoal
|
|
266
|
-
|
|
352
|
+
trackerAmount,
|
|
353
|
+
trackerGoal,
|
|
354
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
355
|
+
text: trackerGoal > 1 ? `Have at least ${trackerGoal} ${m.name} memberships` : `Have a ${m.name} membership`,
|
|
267
356
|
});
|
|
268
357
|
if (isDisqualify)
|
|
269
358
|
isValid = false;
|
|
270
359
|
}
|
|
271
360
|
else {
|
|
272
361
|
if (isDisqualify)
|
|
273
|
-
return { isValid: false };
|
|
362
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
274
363
|
}
|
|
275
364
|
}
|
|
276
365
|
if (m.maxCount) {
|
|
277
|
-
const
|
|
366
|
+
const trackerAmount = playerMembershipsData?.count || 0;
|
|
367
|
+
const isDisqualify = trackerAmount > m.maxCount;
|
|
278
368
|
if (addDetails) {
|
|
279
369
|
conditionData.push({
|
|
280
370
|
isMet: !isDisqualify,
|
|
281
371
|
kind: 'memberships',
|
|
282
|
-
trackerAmount
|
|
372
|
+
trackerAmount,
|
|
373
|
+
trackerGoal: m.maxCount,
|
|
374
|
+
percentCompleted: !isDisqualify ? 100 : 0, // Binary for max conditions
|
|
283
375
|
text: `Have less than ${m.maxCount} ${m.name} memberships`,
|
|
284
376
|
});
|
|
285
377
|
if (isDisqualify)
|
|
@@ -287,35 +379,41 @@ additionalData, }) => {
|
|
|
287
379
|
}
|
|
288
380
|
else {
|
|
289
381
|
if (isDisqualify)
|
|
290
|
-
return { isValid: false };
|
|
382
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
291
383
|
}
|
|
292
384
|
}
|
|
293
385
|
const timeOwned = (playerMembershipsData?.expiresAt || 0) - Date.now();
|
|
294
386
|
if (m.minMs) {
|
|
387
|
+
const trackerAmount = Number((timeOwned / (1000 * 60 * 60 * 24)).toFixed(1));
|
|
388
|
+
const trackerGoal = Number((m.minMs / (1000 * 60 * 60 * 24)).toFixed(1));
|
|
295
389
|
const isDisqualify = timeOwned < m.minMs;
|
|
296
390
|
if (addDetails) {
|
|
297
391
|
conditionData.push({
|
|
298
392
|
isMet: !isDisqualify,
|
|
299
393
|
kind: 'memberships',
|
|
300
|
-
trackerAmount
|
|
301
|
-
trackerGoal
|
|
302
|
-
|
|
394
|
+
trackerAmount,
|
|
395
|
+
trackerGoal,
|
|
396
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
397
|
+
text: `Own ${m.name} membership for at least ${trackerGoal} days`,
|
|
303
398
|
});
|
|
304
399
|
if (isDisqualify)
|
|
305
400
|
isValid = false;
|
|
306
401
|
}
|
|
307
402
|
else {
|
|
308
403
|
if (isDisqualify)
|
|
309
|
-
return { isValid: false };
|
|
404
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
310
405
|
}
|
|
311
406
|
}
|
|
312
407
|
if (m.maxMs) {
|
|
408
|
+
const trackerAmount = Number((timeOwned / (1000 * 60 * 60 * 24)).toFixed(1));
|
|
313
409
|
const isDisqualify = timeOwned > m.maxMs;
|
|
314
410
|
if (addDetails) {
|
|
315
411
|
conditionData.push({
|
|
316
412
|
isMet: !isDisqualify,
|
|
317
413
|
kind: 'memberships',
|
|
318
|
-
trackerAmount
|
|
414
|
+
trackerAmount,
|
|
415
|
+
trackerGoal: Number((m.maxMs / (1000 * 60 * 60 * 24)).toFixed(1)),
|
|
416
|
+
percentCompleted: !isDisqualify ? 100 : 0, // Binary for max conditions
|
|
319
417
|
text: `Own ${m.name} membership for less than ${(m.maxMs / (1000 * 60 * 60 * 24)).toFixed(1)} days`,
|
|
320
418
|
});
|
|
321
419
|
if (isDisqualify)
|
|
@@ -323,7 +421,7 @@ additionalData, }) => {
|
|
|
323
421
|
}
|
|
324
422
|
else {
|
|
325
423
|
if (isDisqualify)
|
|
326
|
-
return { isValid: false };
|
|
424
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
327
425
|
}
|
|
328
426
|
}
|
|
329
427
|
}
|
|
@@ -331,21 +429,24 @@ additionalData, }) => {
|
|
|
331
429
|
const s = conditions.stakedTokens[key];
|
|
332
430
|
const playerStakedData = playerSnap.stakedTokens?.[key];
|
|
333
431
|
if (s.min) {
|
|
334
|
-
const
|
|
432
|
+
const trackerAmount = playerStakedData?.balance || 0;
|
|
433
|
+
const trackerGoal = s.min;
|
|
434
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
335
435
|
if (addDetails) {
|
|
336
436
|
conditionData.push({
|
|
337
437
|
isMet: !isDisqualify,
|
|
338
438
|
kind: 'stakedTokens',
|
|
339
|
-
trackerAmount
|
|
340
|
-
trackerGoal
|
|
341
|
-
|
|
439
|
+
trackerAmount,
|
|
440
|
+
trackerGoal,
|
|
441
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
442
|
+
text: `Have at least ${trackerGoal} ${s.name} staked`,
|
|
342
443
|
});
|
|
343
444
|
if (isDisqualify)
|
|
344
445
|
isValid = false;
|
|
345
446
|
}
|
|
346
447
|
else {
|
|
347
448
|
if (isDisqualify)
|
|
348
|
-
return { isValid: false };
|
|
449
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
349
450
|
}
|
|
350
451
|
}
|
|
351
452
|
}
|
|
@@ -355,13 +456,16 @@ additionalData, }) => {
|
|
|
355
456
|
// linkType should always exist. and be default is none was specified
|
|
356
457
|
const linkCount = playerSnap.entityLinks?.filter((link) => (link.kind || exports.DEFAULT_ENTITY_KIND) === linkType).length || 0;
|
|
357
458
|
if (constraint.min !== undefined) {
|
|
358
|
-
const
|
|
459
|
+
const trackerAmount = linkCount;
|
|
460
|
+
const trackerGoal = constraint.min;
|
|
461
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
359
462
|
if (addDetails) {
|
|
360
463
|
conditionData.push({
|
|
361
464
|
isMet: !isDisqualify,
|
|
362
465
|
kind: 'links',
|
|
363
|
-
trackerAmount
|
|
364
|
-
trackerGoal
|
|
466
|
+
trackerAmount,
|
|
467
|
+
trackerGoal,
|
|
468
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
365
469
|
text: constraint.template
|
|
366
470
|
? (0, template_1.renderTemplate)(constraint.template, {
|
|
367
471
|
current: linkCount,
|
|
@@ -369,23 +473,26 @@ additionalData, }) => {
|
|
|
369
473
|
max: constraint.max ?? 0,
|
|
370
474
|
type: linkType,
|
|
371
475
|
})
|
|
372
|
-
: `At least ${
|
|
476
|
+
: `At least ${trackerGoal} ${linkType} link(s)`,
|
|
373
477
|
});
|
|
374
478
|
if (isDisqualify)
|
|
375
479
|
isValid = false;
|
|
376
480
|
}
|
|
377
481
|
else {
|
|
378
482
|
if (isDisqualify)
|
|
379
|
-
return { isValid: false };
|
|
483
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
380
484
|
}
|
|
381
485
|
}
|
|
382
486
|
if (constraint.max !== undefined) {
|
|
383
|
-
const
|
|
487
|
+
const trackerAmount = linkCount;
|
|
488
|
+
const isDisqualify = trackerAmount > constraint.max;
|
|
384
489
|
if (addDetails) {
|
|
385
490
|
conditionData.push({
|
|
386
491
|
isMet: !isDisqualify,
|
|
387
492
|
kind: 'links',
|
|
388
|
-
trackerAmount
|
|
493
|
+
trackerAmount,
|
|
494
|
+
trackerGoal: constraint.max,
|
|
495
|
+
percentCompleted: !isDisqualify ? 100 : 0, // Binary for max conditions
|
|
389
496
|
text: constraint.template
|
|
390
497
|
? (0, template_1.renderTemplate)(constraint.template, {
|
|
391
498
|
current: linkCount,
|
|
@@ -400,7 +507,7 @@ additionalData, }) => {
|
|
|
400
507
|
}
|
|
401
508
|
else {
|
|
402
509
|
if (isDisqualify)
|
|
403
|
-
return { isValid: false };
|
|
510
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
404
511
|
}
|
|
405
512
|
}
|
|
406
513
|
}
|
|
@@ -408,22 +515,42 @@ additionalData, }) => {
|
|
|
408
515
|
// Evaluate dynamic conditions
|
|
409
516
|
if (conditions?.dynamic?.conditions?.length) {
|
|
410
517
|
const resolvedConditions = (0, template_1.replaceDynamicConditionKeys)(conditions.dynamic.conditions, playerOffer?.trackers || {});
|
|
411
|
-
const
|
|
518
|
+
const dynamicObj = playerSnap.dynamic || {};
|
|
519
|
+
const dynamicResult = meetsDynamicConditions(dynamicObj, {
|
|
412
520
|
...conditions.dynamic,
|
|
413
521
|
conditions: resolvedConditions,
|
|
414
522
|
});
|
|
415
523
|
if (addDetails) {
|
|
524
|
+
// Extract tracker amount and goal from the first condition for display
|
|
525
|
+
let trackerAmount;
|
|
526
|
+
let trackerGoal;
|
|
527
|
+
if (resolvedConditions.length > 0) {
|
|
528
|
+
const firstCond = resolvedConditions[0];
|
|
529
|
+
if (typeof firstCond.compareTo === 'number') {
|
|
530
|
+
trackerGoal = firstCond.compareTo;
|
|
531
|
+
}
|
|
532
|
+
const val = dynamicObj[firstCond.key];
|
|
533
|
+
trackerAmount = typeof val === 'number' ? val : 0;
|
|
534
|
+
}
|
|
535
|
+
// Calculate percentage using the dynamic group helper for AND/OR logic
|
|
536
|
+
const percentCompleted = calculateDynamicGroupPercent(dynamicObj, {
|
|
537
|
+
...conditions.dynamic,
|
|
538
|
+
conditions: resolvedConditions,
|
|
539
|
+
});
|
|
416
540
|
conditionData.push({
|
|
417
541
|
isMet: dynamicResult,
|
|
418
542
|
kind: 'dynamic',
|
|
419
|
-
|
|
543
|
+
trackerAmount,
|
|
544
|
+
trackerGoal,
|
|
545
|
+
percentCompleted,
|
|
546
|
+
text: (0, template_1.renderTemplate)(conditions.dynamic.template, dynamicObj) || 'Dynamic conditions',
|
|
420
547
|
});
|
|
421
548
|
if (!dynamicResult)
|
|
422
549
|
isValid = false;
|
|
423
550
|
}
|
|
424
551
|
else {
|
|
425
552
|
if (!dynamicResult)
|
|
426
|
-
return { isValid: false };
|
|
553
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
427
554
|
}
|
|
428
555
|
}
|
|
429
556
|
if (conditions?.identifiers?.platforms?.length && 'identifiers' in playerSnap) {
|
|
@@ -446,6 +573,7 @@ additionalData, }) => {
|
|
|
446
573
|
kind: 'identifiers',
|
|
447
574
|
trackerAmount: isMet ? 1 : 0,
|
|
448
575
|
trackerGoal: 1,
|
|
576
|
+
percentCompleted: isMet ? 100 : 0, // Binary
|
|
449
577
|
text: displayText,
|
|
450
578
|
});
|
|
451
579
|
if (!isMet)
|
|
@@ -453,7 +581,7 @@ additionalData, }) => {
|
|
|
453
581
|
}
|
|
454
582
|
else {
|
|
455
583
|
if (!isMet)
|
|
456
|
-
return { isValid: false };
|
|
584
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
457
585
|
}
|
|
458
586
|
}
|
|
459
587
|
// Evaluate token balance conditions
|
|
@@ -466,30 +594,36 @@ additionalData, }) => {
|
|
|
466
594
|
totalBalance += fetchedBalances[balanceKey] || 0;
|
|
467
595
|
}
|
|
468
596
|
if (tokenCond.min !== undefined) {
|
|
469
|
-
const
|
|
597
|
+
const trackerAmount = totalBalance;
|
|
598
|
+
const trackerGoal = tokenCond.min;
|
|
599
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
470
600
|
if (addDetails) {
|
|
471
601
|
conditionData.push({
|
|
472
602
|
isMet: !isDisqualify,
|
|
473
603
|
kind: 'tokenBalances',
|
|
474
|
-
trackerAmount
|
|
475
|
-
trackerGoal
|
|
476
|
-
|
|
604
|
+
trackerAmount,
|
|
605
|
+
trackerGoal,
|
|
606
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
607
|
+
text: `Have at least ${trackerGoal} ${tokenCond.name || 'tokens'}`,
|
|
477
608
|
});
|
|
478
609
|
if (isDisqualify)
|
|
479
610
|
isValid = false;
|
|
480
611
|
}
|
|
481
612
|
else {
|
|
482
613
|
if (isDisqualify)
|
|
483
|
-
return { isValid: false };
|
|
614
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
484
615
|
}
|
|
485
616
|
}
|
|
486
617
|
if (tokenCond.max !== undefined) {
|
|
487
|
-
const
|
|
618
|
+
const trackerAmount = totalBalance;
|
|
619
|
+
const isDisqualify = trackerAmount > tokenCond.max;
|
|
488
620
|
if (addDetails) {
|
|
489
621
|
conditionData.push({
|
|
490
622
|
isMet: !isDisqualify,
|
|
491
623
|
kind: 'tokenBalances',
|
|
492
|
-
trackerAmount
|
|
624
|
+
trackerAmount,
|
|
625
|
+
trackerGoal: tokenCond.max,
|
|
626
|
+
percentCompleted: !isDisqualify ? 100 : 0, // Binary for max conditions
|
|
493
627
|
text: `Have at most ${tokenCond.max} ${tokenCond.name || 'tokens'}`,
|
|
494
628
|
});
|
|
495
629
|
if (isDisqualify)
|
|
@@ -497,11 +631,23 @@ additionalData, }) => {
|
|
|
497
631
|
}
|
|
498
632
|
else {
|
|
499
633
|
if (isDisqualify)
|
|
500
|
-
return { isValid: false };
|
|
634
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
501
635
|
}
|
|
502
636
|
}
|
|
503
637
|
}
|
|
504
|
-
|
|
638
|
+
// Calculate top-level percentCompleted as average of all condition percentages
|
|
639
|
+
const percentCompleted = conditionData.length > 0
|
|
640
|
+
? conditionData.reduce((sum, c) => sum + c.percentCompleted, 0) / conditionData.length
|
|
641
|
+
: isValid
|
|
642
|
+
? 100
|
|
643
|
+
: 0;
|
|
644
|
+
return {
|
|
645
|
+
/** @deprecated Use canClaim instead */
|
|
646
|
+
isValid,
|
|
647
|
+
canClaim: isValid,
|
|
648
|
+
percentCompleted,
|
|
649
|
+
conditionData: addDetails ? conditionData : undefined,
|
|
650
|
+
};
|
|
505
651
|
};
|
|
506
652
|
exports.meetsBaseConditions = meetsBaseConditions;
|
|
507
653
|
const hasCompletionConditions = (conditions) => {
|
|
@@ -603,6 +749,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
603
749
|
kind: 'context',
|
|
604
750
|
trackerAmount: hasTrackedContext ? 1 : 0,
|
|
605
751
|
trackerGoal: 1,
|
|
752
|
+
percentCompleted: !isDisqualify ? 100 : 0, // Binary
|
|
606
753
|
text: completionConditions.context.name,
|
|
607
754
|
});
|
|
608
755
|
if (isDisqualify)
|
|
@@ -610,7 +757,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
610
757
|
}
|
|
611
758
|
else {
|
|
612
759
|
if (isDisqualify)
|
|
613
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
760
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
614
761
|
}
|
|
615
762
|
}
|
|
616
763
|
if (conditions?.buyItem) {
|
|
@@ -627,6 +774,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
627
774
|
kind: 'buyItem',
|
|
628
775
|
trackerAmount: trackerValue,
|
|
629
776
|
trackerGoal: scaledAmount,
|
|
777
|
+
percentCompleted: calculatePercent(trackerValue, scaledAmount, !isDisqualify),
|
|
630
778
|
text: `Buy ${scaledAmount} ${conditions.buyItem.name}`,
|
|
631
779
|
});
|
|
632
780
|
if (isDisqualify)
|
|
@@ -634,7 +782,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
634
782
|
}
|
|
635
783
|
else {
|
|
636
784
|
if (isDisqualify)
|
|
637
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
785
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
638
786
|
}
|
|
639
787
|
}
|
|
640
788
|
if (conditions?.spendCurrency) {
|
|
@@ -651,6 +799,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
651
799
|
kind: 'spendCurrency',
|
|
652
800
|
trackerAmount: trackerValue,
|
|
653
801
|
trackerGoal: scaledAmount,
|
|
802
|
+
percentCompleted: calculatePercent(trackerValue, scaledAmount, !isDisqualify),
|
|
654
803
|
text: `Spend ${scaledAmount} ${conditions.spendCurrency.name}`,
|
|
655
804
|
});
|
|
656
805
|
if (isDisqualify)
|
|
@@ -658,7 +807,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
658
807
|
}
|
|
659
808
|
else {
|
|
660
809
|
if (isDisqualify)
|
|
661
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
810
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
662
811
|
}
|
|
663
812
|
}
|
|
664
813
|
if (conditions?.depositCurrency) {
|
|
@@ -675,6 +824,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
675
824
|
kind: 'depositCurrency',
|
|
676
825
|
trackerAmount: trackerValue,
|
|
677
826
|
trackerGoal: scaledAmount,
|
|
827
|
+
percentCompleted: calculatePercent(trackerValue, scaledAmount, !isDisqualify),
|
|
678
828
|
text: `Deposit ${scaledAmount} ${conditions.depositCurrency.name}`,
|
|
679
829
|
});
|
|
680
830
|
if (isDisqualify)
|
|
@@ -682,7 +832,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
682
832
|
}
|
|
683
833
|
else {
|
|
684
834
|
if (isDisqualify)
|
|
685
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
835
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
686
836
|
}
|
|
687
837
|
}
|
|
688
838
|
if (conditions?.login) {
|
|
@@ -693,6 +843,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
693
843
|
kind: 'login',
|
|
694
844
|
trackerAmount: isMet ? 1 : 0,
|
|
695
845
|
trackerGoal: 1,
|
|
846
|
+
percentCompleted: isMet ? 100 : 0, // Binary
|
|
696
847
|
text: `Login to the game`,
|
|
697
848
|
});
|
|
698
849
|
if (!isMet)
|
|
@@ -700,28 +851,30 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
700
851
|
}
|
|
701
852
|
else {
|
|
702
853
|
if (!isMet)
|
|
703
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
854
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
704
855
|
}
|
|
705
856
|
}
|
|
706
857
|
if (conditions?.loginStreak) {
|
|
707
858
|
// player's login streak snapshot right now - their login streak when offer was surfaced = their login streak since the offer was surfaced
|
|
708
859
|
// if their login streak since the offer was surfaced is less than the required login streak, then they are not yet eligible for the offer
|
|
709
|
-
const
|
|
710
|
-
const
|
|
860
|
+
const trackerAmount = (playerSnap.loginStreak || 0) - (completionTrackers?.currentLoginStreak || 0) + 1;
|
|
861
|
+
const trackerGoal = conditions.loginStreak;
|
|
862
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
711
863
|
if (addDetails) {
|
|
712
864
|
conditionData.push({
|
|
713
865
|
isMet: !isDisqualify,
|
|
714
866
|
kind: 'loginStreak',
|
|
715
|
-
trackerAmount
|
|
716
|
-
trackerGoal
|
|
717
|
-
|
|
867
|
+
trackerAmount,
|
|
868
|
+
trackerGoal,
|
|
869
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
870
|
+
text: `Login streak of ${trackerGoal || 0} days`,
|
|
718
871
|
});
|
|
719
872
|
if (isDisqualify)
|
|
720
873
|
isValid = false;
|
|
721
874
|
}
|
|
722
875
|
else {
|
|
723
876
|
if (isDisqualify)
|
|
724
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
877
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
725
878
|
}
|
|
726
879
|
}
|
|
727
880
|
if (conditions?.social) {
|
|
@@ -754,6 +907,21 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
754
907
|
if (baseComments > 0)
|
|
755
908
|
updateMax(Math.floor(comments / baseComments));
|
|
756
909
|
}
|
|
910
|
+
// Calculate social percentage - average of all applicable metrics
|
|
911
|
+
const socialPercentages = [];
|
|
912
|
+
if (minLikes > 0)
|
|
913
|
+
socialPercentages.push(calculatePercent(likes, minLikes, likes >= minLikes));
|
|
914
|
+
if (minViews > 0)
|
|
915
|
+
socialPercentages.push(calculatePercent(views, minViews, views >= minViews));
|
|
916
|
+
if (minComments > 0)
|
|
917
|
+
socialPercentages.push(calculatePercent(comments, minComments, comments >= minComments));
|
|
918
|
+
if (!hasContent)
|
|
919
|
+
socialPercentages.push(0);
|
|
920
|
+
const socialPercent = socialPercentages.length > 0
|
|
921
|
+
? socialPercentages.reduce((a, b) => a + b, 0) / socialPercentages.length
|
|
922
|
+
: hasContent
|
|
923
|
+
? 100
|
|
924
|
+
: 0;
|
|
757
925
|
if (addDetails) {
|
|
758
926
|
const platformMap = {
|
|
759
927
|
tiktok: 'TikTok',
|
|
@@ -765,10 +933,11 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
765
933
|
if (mode === 'accumulate') {
|
|
766
934
|
const matchCount = tSocialAccumulate?.matchCount || 0;
|
|
767
935
|
conditionData.push({
|
|
768
|
-
isMet: hasContent,
|
|
936
|
+
isMet: hasContent && !isDisqualify,
|
|
769
937
|
kind: 'social',
|
|
770
938
|
trackerAmount: matchCount,
|
|
771
939
|
trackerGoal: 1,
|
|
940
|
+
percentCompleted: socialPercent,
|
|
772
941
|
text: hasContent
|
|
773
942
|
? `Found ${matchCount} matching ${platformText} post${matchCount !== 1 ? 's' : ''}`
|
|
774
943
|
: requiredWords.length > 0
|
|
@@ -779,10 +948,11 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
779
948
|
else {
|
|
780
949
|
const title = tSocialAttach?.title;
|
|
781
950
|
conditionData.push({
|
|
782
|
-
isMet: hasContent,
|
|
951
|
+
isMet: hasContent && !isDisqualify,
|
|
783
952
|
kind: 'social',
|
|
784
953
|
trackerAmount: hasContent ? 1 : 0,
|
|
785
954
|
trackerGoal: 1,
|
|
955
|
+
percentCompleted: socialPercent,
|
|
786
956
|
text: !hasContent
|
|
787
957
|
? requiredWords.length > 0
|
|
788
958
|
? `Attach a ${platformText} post with ${requiredWords.map((w) => `"${w}"`).join(', ')} in the title`
|
|
@@ -796,6 +966,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
796
966
|
kind: 'social',
|
|
797
967
|
trackerAmount: likes,
|
|
798
968
|
trackerGoal: minLikes,
|
|
969
|
+
percentCompleted: calculatePercent(likes, minLikes, hasContent && likes >= minLikes),
|
|
799
970
|
text: mode === 'accumulate' ? `Combined ${minLikes} Likes` : `Reach ${minLikes} Likes`,
|
|
800
971
|
});
|
|
801
972
|
}
|
|
@@ -805,6 +976,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
805
976
|
kind: 'social',
|
|
806
977
|
trackerAmount: views,
|
|
807
978
|
trackerGoal: minViews,
|
|
979
|
+
percentCompleted: calculatePercent(views, minViews, hasContent && views >= minViews),
|
|
808
980
|
text: mode === 'accumulate' ? `Combined ${minViews} Views` : `Reach ${minViews} Views`,
|
|
809
981
|
});
|
|
810
982
|
}
|
|
@@ -814,6 +986,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
814
986
|
kind: 'social',
|
|
815
987
|
trackerAmount: comments,
|
|
816
988
|
trackerGoal: minComments,
|
|
989
|
+
percentCompleted: calculatePercent(comments, minComments, hasContent && comments >= minComments),
|
|
817
990
|
text: mode === 'accumulate' ? `Combined ${minComments} Comments` : `Reach ${minComments} Comments`,
|
|
818
991
|
});
|
|
819
992
|
}
|
|
@@ -822,87 +995,109 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
822
995
|
}
|
|
823
996
|
else {
|
|
824
997
|
if (isDisqualify)
|
|
825
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
998
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
826
999
|
}
|
|
827
1000
|
}
|
|
828
1001
|
// Linked completions - wait for N linked entities to complete
|
|
829
1002
|
if (conditions?.linkedCompletions?.min) {
|
|
830
1003
|
const baseMin = conditions.linkedCompletions.min;
|
|
831
|
-
const
|
|
832
|
-
const
|
|
833
|
-
const isDisqualify =
|
|
1004
|
+
const trackerAmount = completionTrackers?.linkedCompletions || 0;
|
|
1005
|
+
const trackerGoal = baseMin * claimMultiplier;
|
|
1006
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
834
1007
|
if (shouldScale && baseMin > 0) {
|
|
835
|
-
updateMax(Math.floor(
|
|
1008
|
+
updateMax(Math.floor(trackerAmount / baseMin));
|
|
836
1009
|
}
|
|
837
1010
|
if (addDetails) {
|
|
838
1011
|
conditionData.push({
|
|
839
1012
|
isMet: !isDisqualify,
|
|
840
1013
|
kind: 'linkedCompletions',
|
|
841
|
-
trackerAmount
|
|
842
|
-
trackerGoal
|
|
1014
|
+
trackerAmount,
|
|
1015
|
+
trackerGoal,
|
|
1016
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
843
1017
|
text: conditions.linkedCompletions.template
|
|
844
1018
|
? (0, template_1.renderTemplate)(conditions.linkedCompletions.template, {
|
|
845
|
-
current:
|
|
846
|
-
required:
|
|
1019
|
+
current: trackerAmount,
|
|
1020
|
+
required: trackerGoal,
|
|
847
1021
|
})
|
|
848
|
-
: `Wait for ${
|
|
1022
|
+
: `Wait for ${trackerGoal} linked ${trackerGoal === 1 ? 'entity' : 'entities'} to complete`,
|
|
849
1023
|
});
|
|
850
1024
|
if (isDisqualify)
|
|
851
1025
|
isValid = false;
|
|
852
1026
|
}
|
|
853
1027
|
else {
|
|
854
1028
|
if (isDisqualify)
|
|
855
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
1029
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
856
1030
|
}
|
|
857
1031
|
}
|
|
858
1032
|
if (conditions?.dynamicTracker?.conditions?.length) {
|
|
859
1033
|
const resolvedConditions = (0, template_1.replaceDynamicConditionKeys)(conditions.dynamicTracker.conditions, playerOffer?.trackers || {});
|
|
860
|
-
//
|
|
861
|
-
const
|
|
1034
|
+
// Convert { key: { value: X } } to { key: X } format
|
|
1035
|
+
const primitiveTrackers = (0, dynamic_1.dynamicTrackerToPrimitive)(completionTrackers?.dynamicTracker || {});
|
|
1036
|
+
const dynamicResult = meetsDynamicConditions(primitiveTrackers, {
|
|
862
1037
|
...conditions.dynamicTracker,
|
|
863
1038
|
conditions: resolvedConditions,
|
|
864
1039
|
}, claimMultiplier);
|
|
865
1040
|
if (shouldScale) {
|
|
866
|
-
const dynamicMax = getMaxClaimsForDynamicGroup(
|
|
1041
|
+
const dynamicMax = getMaxClaimsForDynamicGroup(primitiveTrackers, {
|
|
867
1042
|
...conditions.dynamicTracker,
|
|
868
1043
|
conditions: resolvedConditions,
|
|
869
1044
|
}, playerOffer?.trackers?.claimedCount || 0);
|
|
870
1045
|
updateMax(dynamicMax);
|
|
871
1046
|
}
|
|
872
1047
|
if (addDetails) {
|
|
1048
|
+
// Extract tracker amount and goal from the first condition for display
|
|
1049
|
+
let trackerAmount;
|
|
1050
|
+
let trackerGoal;
|
|
1051
|
+
if (resolvedConditions.length > 0) {
|
|
1052
|
+
const firstCond = resolvedConditions[0];
|
|
1053
|
+
if (typeof firstCond.compareTo === 'number') {
|
|
1054
|
+
trackerGoal = firstCond.compareTo;
|
|
1055
|
+
}
|
|
1056
|
+
const val = primitiveTrackers[firstCond.key];
|
|
1057
|
+
trackerAmount = typeof val === 'number' ? val : 0;
|
|
1058
|
+
}
|
|
1059
|
+
// Calculate percentage using the dynamic group helper for AND/OR logic
|
|
1060
|
+
const percentCompleted = calculateDynamicGroupPercent(primitiveTrackers, {
|
|
1061
|
+
...conditions.dynamicTracker,
|
|
1062
|
+
conditions: resolvedConditions,
|
|
1063
|
+
});
|
|
873
1064
|
conditionData.push({
|
|
874
1065
|
isMet: dynamicResult,
|
|
875
1066
|
kind: 'dynamicTracker',
|
|
876
|
-
|
|
1067
|
+
trackerAmount,
|
|
1068
|
+
trackerGoal,
|
|
1069
|
+
percentCompleted,
|
|
1070
|
+
text: (0, template_1.renderTemplate)(conditions.dynamicTracker.template, primitiveTrackers) || 'Dynamic conditions',
|
|
877
1071
|
});
|
|
878
1072
|
if (!dynamicResult)
|
|
879
1073
|
isValid = false;
|
|
880
1074
|
}
|
|
881
1075
|
else {
|
|
882
1076
|
if (!dynamicResult)
|
|
883
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
1077
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
884
1078
|
}
|
|
885
1079
|
}
|
|
886
1080
|
// Evaluate contractInteractions completion trackers
|
|
887
1081
|
if (conditions?.contractInteractions) {
|
|
888
1082
|
for (const [conditionId, condition] of Object.entries(conditions.contractInteractions)) {
|
|
889
1083
|
const baseAmount = condition.amount || 0;
|
|
890
|
-
const
|
|
891
|
-
const
|
|
892
|
-
const isDisqualify =
|
|
1084
|
+
const trackerGoal = baseAmount * claimMultiplier;
|
|
1085
|
+
const trackerAmount = completionTrackers?.contractInteractions?.[conditionId] || 0;
|
|
1086
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
893
1087
|
if (shouldScale && baseAmount > 0) {
|
|
894
|
-
updateMax(Math.floor(
|
|
1088
|
+
updateMax(Math.floor(trackerAmount / baseAmount));
|
|
895
1089
|
}
|
|
896
1090
|
if (addDetails) {
|
|
897
1091
|
const displayText = (0, template_1.renderTemplate)(condition.template, {
|
|
898
|
-
current:
|
|
899
|
-
amount:
|
|
1092
|
+
current: trackerAmount,
|
|
1093
|
+
amount: trackerGoal,
|
|
900
1094
|
});
|
|
901
1095
|
conditionData.push({
|
|
902
1096
|
isMet: !isDisqualify,
|
|
903
1097
|
kind: 'contractInteractions',
|
|
904
|
-
trackerAmount
|
|
905
|
-
trackerGoal
|
|
1098
|
+
trackerAmount,
|
|
1099
|
+
trackerGoal,
|
|
1100
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
906
1101
|
text: displayText,
|
|
907
1102
|
});
|
|
908
1103
|
if (isDisqualify)
|
|
@@ -910,7 +1105,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
910
1105
|
}
|
|
911
1106
|
else {
|
|
912
1107
|
if (isDisqualify)
|
|
913
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
1108
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
914
1109
|
}
|
|
915
1110
|
}
|
|
916
1111
|
}
|
|
@@ -932,9 +1127,22 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
932
1127
|
: maxTotalClaimsFromScaling === Infinity
|
|
933
1128
|
? -1
|
|
934
1129
|
: Math.max(0, maxTotalClaimsFromScaling - claimedCount);
|
|
935
|
-
|
|
1130
|
+
// Calculate top-level percentCompleted as average of all condition percentages
|
|
1131
|
+
const percentCompleted = conditionData.length > 0
|
|
1132
|
+
? conditionData.reduce((sum, c) => sum + c.percentCompleted, 0) / conditionData.length
|
|
1133
|
+
: isValid
|
|
1134
|
+
? 100
|
|
1135
|
+
: 0;
|
|
1136
|
+
return {
|
|
1137
|
+
/** @deprecated Use canClaim instead */
|
|
1138
|
+
isValid,
|
|
1139
|
+
canClaim: isValid,
|
|
1140
|
+
percentCompleted,
|
|
1141
|
+
conditionData,
|
|
1142
|
+
availableClaimsNow,
|
|
1143
|
+
};
|
|
936
1144
|
}
|
|
937
|
-
return { isValid: true, conditionData: [], availableClaimsNow: -1 };
|
|
1145
|
+
return { isValid: true, canClaim: true, percentCompleted: 100, conditionData: [], availableClaimsNow: -1 };
|
|
938
1146
|
};
|
|
939
1147
|
exports.meetsCompletionConditions = meetsCompletionConditions;
|
|
940
1148
|
/**
|