@stackedapp/utils 1.11.2 → 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 +303 -123
- 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
|
}
|
|
@@ -425,11 +532,17 @@ additionalData, }) => {
|
|
|
425
532
|
const val = dynamicObj[firstCond.key];
|
|
426
533
|
trackerAmount = typeof val === 'number' ? val : 0;
|
|
427
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
|
+
});
|
|
428
540
|
conditionData.push({
|
|
429
541
|
isMet: dynamicResult,
|
|
430
542
|
kind: 'dynamic',
|
|
431
543
|
trackerAmount,
|
|
432
544
|
trackerGoal,
|
|
545
|
+
percentCompleted,
|
|
433
546
|
text: (0, template_1.renderTemplate)(conditions.dynamic.template, dynamicObj) || 'Dynamic conditions',
|
|
434
547
|
});
|
|
435
548
|
if (!dynamicResult)
|
|
@@ -437,7 +550,7 @@ additionalData, }) => {
|
|
|
437
550
|
}
|
|
438
551
|
else {
|
|
439
552
|
if (!dynamicResult)
|
|
440
|
-
return { isValid: false };
|
|
553
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
441
554
|
}
|
|
442
555
|
}
|
|
443
556
|
if (conditions?.identifiers?.platforms?.length && 'identifiers' in playerSnap) {
|
|
@@ -460,6 +573,7 @@ additionalData, }) => {
|
|
|
460
573
|
kind: 'identifiers',
|
|
461
574
|
trackerAmount: isMet ? 1 : 0,
|
|
462
575
|
trackerGoal: 1,
|
|
576
|
+
percentCompleted: isMet ? 100 : 0, // Binary
|
|
463
577
|
text: displayText,
|
|
464
578
|
});
|
|
465
579
|
if (!isMet)
|
|
@@ -467,7 +581,7 @@ additionalData, }) => {
|
|
|
467
581
|
}
|
|
468
582
|
else {
|
|
469
583
|
if (!isMet)
|
|
470
|
-
return { isValid: false };
|
|
584
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
471
585
|
}
|
|
472
586
|
}
|
|
473
587
|
// Evaluate token balance conditions
|
|
@@ -480,30 +594,36 @@ additionalData, }) => {
|
|
|
480
594
|
totalBalance += fetchedBalances[balanceKey] || 0;
|
|
481
595
|
}
|
|
482
596
|
if (tokenCond.min !== undefined) {
|
|
483
|
-
const
|
|
597
|
+
const trackerAmount = totalBalance;
|
|
598
|
+
const trackerGoal = tokenCond.min;
|
|
599
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
484
600
|
if (addDetails) {
|
|
485
601
|
conditionData.push({
|
|
486
602
|
isMet: !isDisqualify,
|
|
487
603
|
kind: 'tokenBalances',
|
|
488
|
-
trackerAmount
|
|
489
|
-
trackerGoal
|
|
490
|
-
|
|
604
|
+
trackerAmount,
|
|
605
|
+
trackerGoal,
|
|
606
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
607
|
+
text: `Have at least ${trackerGoal} ${tokenCond.name || 'tokens'}`,
|
|
491
608
|
});
|
|
492
609
|
if (isDisqualify)
|
|
493
610
|
isValid = false;
|
|
494
611
|
}
|
|
495
612
|
else {
|
|
496
613
|
if (isDisqualify)
|
|
497
|
-
return { isValid: false };
|
|
614
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
498
615
|
}
|
|
499
616
|
}
|
|
500
617
|
if (tokenCond.max !== undefined) {
|
|
501
|
-
const
|
|
618
|
+
const trackerAmount = totalBalance;
|
|
619
|
+
const isDisqualify = trackerAmount > tokenCond.max;
|
|
502
620
|
if (addDetails) {
|
|
503
621
|
conditionData.push({
|
|
504
622
|
isMet: !isDisqualify,
|
|
505
623
|
kind: 'tokenBalances',
|
|
506
|
-
trackerAmount
|
|
624
|
+
trackerAmount,
|
|
625
|
+
trackerGoal: tokenCond.max,
|
|
626
|
+
percentCompleted: !isDisqualify ? 100 : 0, // Binary for max conditions
|
|
507
627
|
text: `Have at most ${tokenCond.max} ${tokenCond.name || 'tokens'}`,
|
|
508
628
|
});
|
|
509
629
|
if (isDisqualify)
|
|
@@ -511,11 +631,23 @@ additionalData, }) => {
|
|
|
511
631
|
}
|
|
512
632
|
else {
|
|
513
633
|
if (isDisqualify)
|
|
514
|
-
return { isValid: false };
|
|
634
|
+
return { isValid: false, canClaim: false, percentCompleted: 0 };
|
|
515
635
|
}
|
|
516
636
|
}
|
|
517
637
|
}
|
|
518
|
-
|
|
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
|
+
};
|
|
519
651
|
};
|
|
520
652
|
exports.meetsBaseConditions = meetsBaseConditions;
|
|
521
653
|
const hasCompletionConditions = (conditions) => {
|
|
@@ -617,6 +749,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
617
749
|
kind: 'context',
|
|
618
750
|
trackerAmount: hasTrackedContext ? 1 : 0,
|
|
619
751
|
trackerGoal: 1,
|
|
752
|
+
percentCompleted: !isDisqualify ? 100 : 0, // Binary
|
|
620
753
|
text: completionConditions.context.name,
|
|
621
754
|
});
|
|
622
755
|
if (isDisqualify)
|
|
@@ -624,7 +757,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
624
757
|
}
|
|
625
758
|
else {
|
|
626
759
|
if (isDisqualify)
|
|
627
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
760
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
628
761
|
}
|
|
629
762
|
}
|
|
630
763
|
if (conditions?.buyItem) {
|
|
@@ -641,6 +774,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
641
774
|
kind: 'buyItem',
|
|
642
775
|
trackerAmount: trackerValue,
|
|
643
776
|
trackerGoal: scaledAmount,
|
|
777
|
+
percentCompleted: calculatePercent(trackerValue, scaledAmount, !isDisqualify),
|
|
644
778
|
text: `Buy ${scaledAmount} ${conditions.buyItem.name}`,
|
|
645
779
|
});
|
|
646
780
|
if (isDisqualify)
|
|
@@ -648,7 +782,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
648
782
|
}
|
|
649
783
|
else {
|
|
650
784
|
if (isDisqualify)
|
|
651
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
785
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
652
786
|
}
|
|
653
787
|
}
|
|
654
788
|
if (conditions?.spendCurrency) {
|
|
@@ -665,6 +799,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
665
799
|
kind: 'spendCurrency',
|
|
666
800
|
trackerAmount: trackerValue,
|
|
667
801
|
trackerGoal: scaledAmount,
|
|
802
|
+
percentCompleted: calculatePercent(trackerValue, scaledAmount, !isDisqualify),
|
|
668
803
|
text: `Spend ${scaledAmount} ${conditions.spendCurrency.name}`,
|
|
669
804
|
});
|
|
670
805
|
if (isDisqualify)
|
|
@@ -672,7 +807,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
672
807
|
}
|
|
673
808
|
else {
|
|
674
809
|
if (isDisqualify)
|
|
675
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
810
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
676
811
|
}
|
|
677
812
|
}
|
|
678
813
|
if (conditions?.depositCurrency) {
|
|
@@ -689,6 +824,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
689
824
|
kind: 'depositCurrency',
|
|
690
825
|
trackerAmount: trackerValue,
|
|
691
826
|
trackerGoal: scaledAmount,
|
|
827
|
+
percentCompleted: calculatePercent(trackerValue, scaledAmount, !isDisqualify),
|
|
692
828
|
text: `Deposit ${scaledAmount} ${conditions.depositCurrency.name}`,
|
|
693
829
|
});
|
|
694
830
|
if (isDisqualify)
|
|
@@ -696,7 +832,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
696
832
|
}
|
|
697
833
|
else {
|
|
698
834
|
if (isDisqualify)
|
|
699
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
835
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
700
836
|
}
|
|
701
837
|
}
|
|
702
838
|
if (conditions?.login) {
|
|
@@ -707,6 +843,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
707
843
|
kind: 'login',
|
|
708
844
|
trackerAmount: isMet ? 1 : 0,
|
|
709
845
|
trackerGoal: 1,
|
|
846
|
+
percentCompleted: isMet ? 100 : 0, // Binary
|
|
710
847
|
text: `Login to the game`,
|
|
711
848
|
});
|
|
712
849
|
if (!isMet)
|
|
@@ -714,28 +851,30 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
714
851
|
}
|
|
715
852
|
else {
|
|
716
853
|
if (!isMet)
|
|
717
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
854
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
718
855
|
}
|
|
719
856
|
}
|
|
720
857
|
if (conditions?.loginStreak) {
|
|
721
858
|
// player's login streak snapshot right now - their login streak when offer was surfaced = their login streak since the offer was surfaced
|
|
722
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
|
|
723
|
-
const
|
|
724
|
-
const
|
|
860
|
+
const trackerAmount = (playerSnap.loginStreak || 0) - (completionTrackers?.currentLoginStreak || 0) + 1;
|
|
861
|
+
const trackerGoal = conditions.loginStreak;
|
|
862
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
725
863
|
if (addDetails) {
|
|
726
864
|
conditionData.push({
|
|
727
865
|
isMet: !isDisqualify,
|
|
728
866
|
kind: 'loginStreak',
|
|
729
|
-
trackerAmount
|
|
730
|
-
trackerGoal
|
|
731
|
-
|
|
867
|
+
trackerAmount,
|
|
868
|
+
trackerGoal,
|
|
869
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
870
|
+
text: `Login streak of ${trackerGoal || 0} days`,
|
|
732
871
|
});
|
|
733
872
|
if (isDisqualify)
|
|
734
873
|
isValid = false;
|
|
735
874
|
}
|
|
736
875
|
else {
|
|
737
876
|
if (isDisqualify)
|
|
738
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
877
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
739
878
|
}
|
|
740
879
|
}
|
|
741
880
|
if (conditions?.social) {
|
|
@@ -768,6 +907,21 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
768
907
|
if (baseComments > 0)
|
|
769
908
|
updateMax(Math.floor(comments / baseComments));
|
|
770
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;
|
|
771
925
|
if (addDetails) {
|
|
772
926
|
const platformMap = {
|
|
773
927
|
tiktok: 'TikTok',
|
|
@@ -779,10 +933,11 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
779
933
|
if (mode === 'accumulate') {
|
|
780
934
|
const matchCount = tSocialAccumulate?.matchCount || 0;
|
|
781
935
|
conditionData.push({
|
|
782
|
-
isMet: hasContent,
|
|
936
|
+
isMet: hasContent && !isDisqualify,
|
|
783
937
|
kind: 'social',
|
|
784
938
|
trackerAmount: matchCount,
|
|
785
939
|
trackerGoal: 1,
|
|
940
|
+
percentCompleted: socialPercent,
|
|
786
941
|
text: hasContent
|
|
787
942
|
? `Found ${matchCount} matching ${platformText} post${matchCount !== 1 ? 's' : ''}`
|
|
788
943
|
: requiredWords.length > 0
|
|
@@ -793,10 +948,11 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
793
948
|
else {
|
|
794
949
|
const title = tSocialAttach?.title;
|
|
795
950
|
conditionData.push({
|
|
796
|
-
isMet: hasContent,
|
|
951
|
+
isMet: hasContent && !isDisqualify,
|
|
797
952
|
kind: 'social',
|
|
798
953
|
trackerAmount: hasContent ? 1 : 0,
|
|
799
954
|
trackerGoal: 1,
|
|
955
|
+
percentCompleted: socialPercent,
|
|
800
956
|
text: !hasContent
|
|
801
957
|
? requiredWords.length > 0
|
|
802
958
|
? `Attach a ${platformText} post with ${requiredWords.map((w) => `"${w}"`).join(', ')} in the title`
|
|
@@ -810,6 +966,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
810
966
|
kind: 'social',
|
|
811
967
|
trackerAmount: likes,
|
|
812
968
|
trackerGoal: minLikes,
|
|
969
|
+
percentCompleted: calculatePercent(likes, minLikes, hasContent && likes >= minLikes),
|
|
813
970
|
text: mode === 'accumulate' ? `Combined ${minLikes} Likes` : `Reach ${minLikes} Likes`,
|
|
814
971
|
});
|
|
815
972
|
}
|
|
@@ -819,6 +976,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
819
976
|
kind: 'social',
|
|
820
977
|
trackerAmount: views,
|
|
821
978
|
trackerGoal: minViews,
|
|
979
|
+
percentCompleted: calculatePercent(views, minViews, hasContent && views >= minViews),
|
|
822
980
|
text: mode === 'accumulate' ? `Combined ${minViews} Views` : `Reach ${minViews} Views`,
|
|
823
981
|
});
|
|
824
982
|
}
|
|
@@ -828,6 +986,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
828
986
|
kind: 'social',
|
|
829
987
|
trackerAmount: comments,
|
|
830
988
|
trackerGoal: minComments,
|
|
989
|
+
percentCompleted: calculatePercent(comments, minComments, hasContent && comments >= minComments),
|
|
831
990
|
text: mode === 'accumulate' ? `Combined ${minComments} Comments` : `Reach ${minComments} Comments`,
|
|
832
991
|
});
|
|
833
992
|
}
|
|
@@ -836,37 +995,38 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
836
995
|
}
|
|
837
996
|
else {
|
|
838
997
|
if (isDisqualify)
|
|
839
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
998
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
840
999
|
}
|
|
841
1000
|
}
|
|
842
1001
|
// Linked completions - wait for N linked entities to complete
|
|
843
1002
|
if (conditions?.linkedCompletions?.min) {
|
|
844
1003
|
const baseMin = conditions.linkedCompletions.min;
|
|
845
|
-
const
|
|
846
|
-
const
|
|
847
|
-
const isDisqualify =
|
|
1004
|
+
const trackerAmount = completionTrackers?.linkedCompletions || 0;
|
|
1005
|
+
const trackerGoal = baseMin * claimMultiplier;
|
|
1006
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
848
1007
|
if (shouldScale && baseMin > 0) {
|
|
849
|
-
updateMax(Math.floor(
|
|
1008
|
+
updateMax(Math.floor(trackerAmount / baseMin));
|
|
850
1009
|
}
|
|
851
1010
|
if (addDetails) {
|
|
852
1011
|
conditionData.push({
|
|
853
1012
|
isMet: !isDisqualify,
|
|
854
1013
|
kind: 'linkedCompletions',
|
|
855
|
-
trackerAmount
|
|
856
|
-
trackerGoal
|
|
1014
|
+
trackerAmount,
|
|
1015
|
+
trackerGoal,
|
|
1016
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
857
1017
|
text: conditions.linkedCompletions.template
|
|
858
1018
|
? (0, template_1.renderTemplate)(conditions.linkedCompletions.template, {
|
|
859
|
-
current:
|
|
860
|
-
required:
|
|
1019
|
+
current: trackerAmount,
|
|
1020
|
+
required: trackerGoal,
|
|
861
1021
|
})
|
|
862
|
-
: `Wait for ${
|
|
1022
|
+
: `Wait for ${trackerGoal} linked ${trackerGoal === 1 ? 'entity' : 'entities'} to complete`,
|
|
863
1023
|
});
|
|
864
1024
|
if (isDisqualify)
|
|
865
1025
|
isValid = false;
|
|
866
1026
|
}
|
|
867
1027
|
else {
|
|
868
1028
|
if (isDisqualify)
|
|
869
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
1029
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
870
1030
|
}
|
|
871
1031
|
}
|
|
872
1032
|
if (conditions?.dynamicTracker?.conditions?.length) {
|
|
@@ -896,11 +1056,17 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
896
1056
|
const val = primitiveTrackers[firstCond.key];
|
|
897
1057
|
trackerAmount = typeof val === 'number' ? val : 0;
|
|
898
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
|
+
});
|
|
899
1064
|
conditionData.push({
|
|
900
1065
|
isMet: dynamicResult,
|
|
901
1066
|
kind: 'dynamicTracker',
|
|
902
1067
|
trackerAmount,
|
|
903
1068
|
trackerGoal,
|
|
1069
|
+
percentCompleted,
|
|
904
1070
|
text: (0, template_1.renderTemplate)(conditions.dynamicTracker.template, primitiveTrackers) || 'Dynamic conditions',
|
|
905
1071
|
});
|
|
906
1072
|
if (!dynamicResult)
|
|
@@ -908,29 +1074,30 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
908
1074
|
}
|
|
909
1075
|
else {
|
|
910
1076
|
if (!dynamicResult)
|
|
911
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
1077
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
912
1078
|
}
|
|
913
1079
|
}
|
|
914
1080
|
// Evaluate contractInteractions completion trackers
|
|
915
1081
|
if (conditions?.contractInteractions) {
|
|
916
1082
|
for (const [conditionId, condition] of Object.entries(conditions.contractInteractions)) {
|
|
917
1083
|
const baseAmount = condition.amount || 0;
|
|
918
|
-
const
|
|
919
|
-
const
|
|
920
|
-
const isDisqualify =
|
|
1084
|
+
const trackerGoal = baseAmount * claimMultiplier;
|
|
1085
|
+
const trackerAmount = completionTrackers?.contractInteractions?.[conditionId] || 0;
|
|
1086
|
+
const isDisqualify = trackerAmount < trackerGoal;
|
|
921
1087
|
if (shouldScale && baseAmount > 0) {
|
|
922
|
-
updateMax(Math.floor(
|
|
1088
|
+
updateMax(Math.floor(trackerAmount / baseAmount));
|
|
923
1089
|
}
|
|
924
1090
|
if (addDetails) {
|
|
925
1091
|
const displayText = (0, template_1.renderTemplate)(condition.template, {
|
|
926
|
-
current:
|
|
927
|
-
amount:
|
|
1092
|
+
current: trackerAmount,
|
|
1093
|
+
amount: trackerGoal,
|
|
928
1094
|
});
|
|
929
1095
|
conditionData.push({
|
|
930
1096
|
isMet: !isDisqualify,
|
|
931
1097
|
kind: 'contractInteractions',
|
|
932
|
-
trackerAmount
|
|
933
|
-
trackerGoal
|
|
1098
|
+
trackerAmount,
|
|
1099
|
+
trackerGoal,
|
|
1100
|
+
percentCompleted: calculatePercent(trackerAmount, trackerGoal, !isDisqualify),
|
|
934
1101
|
text: displayText,
|
|
935
1102
|
});
|
|
936
1103
|
if (isDisqualify)
|
|
@@ -938,7 +1105,7 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
938
1105
|
}
|
|
939
1106
|
else {
|
|
940
1107
|
if (isDisqualify)
|
|
941
|
-
return { isValid: false, availableClaimsNow: 0 };
|
|
1108
|
+
return { isValid: false, canClaim: false, percentCompleted: 0, availableClaimsNow: 0 };
|
|
942
1109
|
}
|
|
943
1110
|
}
|
|
944
1111
|
}
|
|
@@ -960,9 +1127,22 @@ const meetsCompletionConditions = ({ completionConditions, completionTrackers, p
|
|
|
960
1127
|
: maxTotalClaimsFromScaling === Infinity
|
|
961
1128
|
? -1
|
|
962
1129
|
: Math.max(0, maxTotalClaimsFromScaling - claimedCount);
|
|
963
|
-
|
|
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
|
+
};
|
|
964
1144
|
}
|
|
965
|
-
return { isValid: true, conditionData: [], availableClaimsNow: -1 };
|
|
1145
|
+
return { isValid: true, canClaim: true, percentCompleted: 100, conditionData: [], availableClaimsNow: -1 };
|
|
966
1146
|
};
|
|
967
1147
|
exports.meetsCompletionConditions = meetsCompletionConditions;
|
|
968
1148
|
/**
|