forge-openclaw-plugin 0.2.98 → 0.2.100
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/assets/activity-copy-Bj4h9OcF.js +1 -0
- package/dist/assets/activity-page-5oyCFOns.js +1 -0
- package/dist/assets/ai-surface-workspace-qgk_B57-.js +1 -0
- package/dist/assets/atlas-panel-rfH2qOez.js +1 -0
- package/dist/assets/{board-Ju0h0SeG.js → board-BkDRaMp6.js} +1 -1
- package/dist/assets/calendar-display-preferences-Cid-2RnL.js +1 -0
- package/dist/assets/calendar-page-Bo2iua-a.js +1 -0
- package/dist/assets/calendar-rules-DA1g3QUk.js +1 -0
- package/dist/assets/calendar-ui-Cy1XRwzV.js +1 -0
- package/dist/assets/calendar-week-toolbar-DU1Q4RYj.js +1 -0
- package/dist/assets/charts-P7EVhIog.js +36 -0
- package/dist/assets/companion-sync-lab-page-CosNknOK.js +1 -0
- package/dist/assets/daily-metrics-dashboard-LjuGAB3f.js +1 -0
- package/dist/assets/date-keys-Cj1G3TOn.js +1 -0
- package/dist/assets/entity-links-DwpxhW2H.js +1 -0
- package/dist/assets/entity-note-count-link-BmGDB572.js +1 -0
- package/dist/assets/entity-notes-surface-DgEgicaE.js +1 -0
- package/dist/assets/execution-board-CDRXQB85.js +1 -0
- package/dist/assets/faceted-token-search-CE1YauRd.js +1 -0
- package/dist/assets/flagship-signal-deck-DDds90Gl.js +1 -0
- package/dist/assets/floating-action-menu-CJkI2iFy.js +1 -0
- package/dist/assets/forms-BFlTgZ3W.js +1 -0
- package/dist/assets/goal-detail-page-cJvHaLMQ.js +1 -0
- package/dist/assets/goals-page-f_39hvUV.js +1 -0
- package/dist/assets/graph-BZV40eAE.css +1 -0
- package/dist/assets/graph-D6JLqDbD.js +318 -0
- package/dist/assets/habits-page-DKb96_mj.js +1 -0
- package/dist/assets/health-link-options-Cpx8w7uM.js +1 -0
- package/dist/assets/index-BHTUu_4M.js +19 -0
- package/dist/assets/index-CZbuZQjw.css +1 -0
- package/dist/assets/insight-flow-dialog-pzAzyayN.js +1 -0
- package/dist/assets/insights-page-Dc9oFltJ.js +8 -0
- package/dist/assets/kanban-page-JAxerYh6.js +1 -0
- package/dist/assets/knowledge-graph-page-UQ3skqEi.js +1 -0
- package/dist/assets/life-force-page-BGDbQuVh.js +1 -0
- package/dist/assets/life-force-workspace-B1fYSXRC.js +1 -0
- package/dist/assets/maps-B-YMMjus.css +1 -0
- package/dist/assets/maps-ClgJoCjz.js +803 -0
- package/dist/assets/metric-tile-DX6TclqM.js +1 -0
- package/dist/assets/{motion-DRPJkN3a.js → motion-BeD44FeG.js} +1 -1
- package/dist/assets/movement-page-6HP6nGJx.js +1 -0
- package/dist/assets/note-markdown-DiW2-5d3.js +3 -0
- package/dist/assets/note-tags-input-DDLXf54U.js +1 -0
- package/dist/assets/notes-page-BuguDjhz.js +1 -0
- package/dist/assets/open-in-graph-button-Cg5VrKsC.js +1 -0
- package/dist/assets/orbit-map-GD05-0oS.js +1 -0
- package/dist/assets/overview-page-DuOs2OCB.js +1 -0
- package/dist/assets/page-hero-CQWo1Mm_.js +1 -0
- package/dist/assets/pill-cluster-BJogDRDJ.js +1 -0
- package/dist/assets/preference-entity-handoff-button-D4WAs9pC.js +1 -0
- package/dist/assets/preferences-page-BaJTMU1I.js +1 -0
- package/dist/assets/project-collections-DvaX20q_.js +1 -0
- package/dist/assets/project-detail-page-drPIFZGb.js +1 -0
- package/dist/assets/project-management-hierarchy-page-BUbRXvny.js +1 -0
- package/dist/assets/project-management-section-nav-C2Ud8Zdd.js +1 -0
- package/dist/assets/projects-page-BGzEZUtg.js +1 -0
- package/dist/assets/psyche-behaviors-page-Dmm_Io9D.js +5 -0
- package/dist/assets/psyche-flashcards-page-BgNKJ6QJ.js +1 -0
- package/dist/assets/psyche-goal-map-page-DXJs98Vr.js +1 -0
- package/dist/assets/psyche-graph-CFgs_Bqc.js +1 -0
- package/dist/assets/psyche-metrics-page-zYTJDbyZ.js +1 -0
- package/dist/assets/psyche-mode-guide-page-XPgRfCOf.js +1 -0
- package/dist/assets/psyche-modes-page-B-GA8oRF.js +1 -0
- package/dist/assets/psyche-page--r6a3e1t.js +1 -0
- package/dist/assets/psyche-patterns-page-BM5-3bMm.js +5 -0
- package/dist/assets/psyche-questionnaire-builder-page-CJshQ-mg.js +1 -0
- package/dist/assets/psyche-questionnaire-detail-page-USmR5G5A.js +1 -0
- package/dist/assets/psyche-questionnaire-run-detail-page-D7iBCmTi.js +1 -0
- package/dist/assets/psyche-questionnaire-run-page-Cpil-kDh.js +1 -0
- package/dist/assets/psyche-questionnaires-page-C-_y3VwS.js +1 -0
- package/dist/assets/psyche-report-detail-page--dkSPRaj.js +3 -0
- package/dist/assets/psyche-reports-page-CUaOXmIN.js +1 -0
- package/dist/assets/psyche-schemas-HFmg37Wj.js +1 -0
- package/dist/assets/psyche-schemas-beliefs-page-BX6xaap3.js +9 -0
- package/dist/assets/psyche-screen-time-page-CAAI4mD7.js +1 -0
- package/dist/assets/psyche-self-observation-page-BZ6FLuwa.js +1 -0
- package/dist/assets/psyche-values-page-yEV6MGt8.js +5 -0
- package/dist/assets/query-cache-IQ8W-LNC.js +1 -0
- package/dist/assets/report-chain-fields-fZ8Xd4H6.js +1 -0
- package/dist/assets/rewards-page-C2HQjIAf.js +1 -0
- package/dist/assets/scheduling-rules-editor-BHOpHOrV.js +1 -0
- package/dist/assets/schema-badge-DyKbxb51.js +1 -0
- package/dist/assets/schema-visuals-D6nxjbYC.js +1 -0
- package/dist/assets/select-menu-BX-pZNqL.js +1 -0
- package/dist/assets/settings-agents-page-VuYXTiyc.js +6 -0
- package/dist/assets/settings-bin-page-BNzvYaOk.js +1 -0
- package/dist/assets/settings-calendar-page-CjSFB53S.js +5 -0
- package/dist/assets/settings-data-page-CGSlryuI.js +1 -0
- package/dist/assets/settings-logs-page-BTK5fine.js +1 -0
- package/dist/assets/settings-mobile-page-CRaObOGo.js +1 -0
- package/dist/assets/settings-models-page-DFshpYF8.js +1 -0
- package/dist/assets/settings-page-BP81Mb5R.js +1 -0
- package/dist/assets/settings-rewards-page-CDJ1PH2G.js +1 -0
- package/dist/assets/settings-section-nav-CCFm27r2.js +1 -0
- package/dist/assets/settings-users-page-TdUocFPa.js +1 -0
- package/dist/assets/settings-wiki-page-B2zX0QQG.js +1 -0
- package/dist/assets/sleep-page-cI1GMVzk.js +1 -0
- package/dist/assets/sports-page-06LTqp0V.js +1 -0
- package/dist/assets/state-B-4sS1xO.js +1 -0
- package/dist/assets/strategies-page-DXP9Kx8s.js +1 -0
- package/dist/assets/strategy-detail-page-D6mx_Mik.js +1 -0
- package/dist/assets/strategy-dialog-BvzomTaF.js +1 -0
- package/dist/assets/{table-DewbFlTh.js → table-WfAPUppN.js} +1 -1
- package/dist/assets/task-detail-page-BIWIggdp.js +1 -0
- package/dist/assets/timebox-planning-dialog-CaCnoslG.js +1 -0
- package/dist/assets/today-page-DO2mRPT2.js +1 -0
- package/dist/assets/training-load-page-CyZ0mlEr.js +1 -0
- package/dist/assets/{ui-C2IvSrAz.js → ui-C13Nbgas.js} +4 -4
- package/dist/assets/use-psyche-focus-target-C1C_XjYG.js +1 -0
- package/dist/assets/vendor-CRS-psbw.css +1 -0
- package/dist/assets/vendor-DHkYh85p.js +1052 -0
- package/dist/assets/vitals-page-BQvEjTc6.js +1 -0
- package/dist/assets/weekly-review-page-Tp6Q9CRj.js +1 -0
- package/dist/assets/weight-loss-page-BBzlhLVV.js +1 -0
- package/dist/assets/wiki-article-markdown-DQYohmW2.js +4 -0
- package/dist/assets/wiki-editor-page-Dem_3eZv.js +26 -0
- package/dist/assets/wiki-ingest-history-page-BxoOcCoJ.js +1 -0
- package/dist/assets/wiki-ingest-modal-DhguKk3J.js +1 -0
- package/dist/assets/wiki-page-BLRxVXkl.js +1 -0
- package/dist/assets/workbench-flow-page-DqMkCCTy.js +5 -0
- package/dist/assets/workbench-page-BWd02wPw.js +1 -0
- package/dist/assets/workout-detail-page-BD8u7GyL.js +2 -0
- package/dist/index.html +148 -9
- package/dist/openclaw/tools.js +340 -0
- package/dist/server/server/migrations/065_weight_loss_nutrition_insights.sql +236 -0
- package/dist/server/server/migrations/066_watch_action_receipts.sql +20 -0
- package/dist/server/server/src/app.js +266 -13
- package/dist/server/server/src/health-weight-loss.js +1378 -0
- package/dist/server/server/src/health.js +188 -35
- package/dist/server/server/src/openapi.js +449 -0
- package/dist/server/server/src/services/context.js +6 -7
- package/dist/server/server/src/services/doctor.js +39 -4
- package/dist/server/server/src/services/gamification.js +146 -34
- package/dist/server/server/src/watch-mobile.js +564 -4
- package/dist/server/server/src/web.js +18 -5
- package/dist/server/src/components/ui/info-tooltip.js +48 -3
- package/dist/server/src/lib/api.js +131 -0
- package/dist/server/src/lib/weight-loss-types.js +1 -0
- package/openclaw.plugin.json +14 -1
- package/package.json +1 -1
- package/server/migrations/065_weight_loss_nutrition_insights.sql +236 -0
- package/server/migrations/066_watch_action_receipts.sql +20 -0
- package/skills/forge-openclaw/SKILL.md +26 -5
- package/skills/forge-openclaw/entity_conversation_playbooks.md +134 -5
- package/skills/forge-openclaw/psyche_entity_playbooks.md +45 -0
- package/dist/assets/index-Cn5Wpwau.css +0 -1
- package/dist/assets/index-CwvGs8n4.js +0 -91
- package/dist/assets/vendor-B-Lq_OG3.css +0 -1
- package/dist/assets/vendor-DL2K5ayT.js +0 -2186
|
@@ -133,6 +133,12 @@ function resolveScopeUsers(requestedUserIds) {
|
|
|
133
133
|
return { mode: "selected_user", users: selectedUsers };
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
if (uniqueRequestedUserIds.length > 1) {
|
|
137
|
+
const selectedUsers = listUsersByIds(uniqueRequestedUserIds);
|
|
138
|
+
if (selectedUsers.length > 0) {
|
|
139
|
+
return { mode: "aggregate_fallback", users: selectedUsers };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
136
142
|
try {
|
|
137
143
|
return { mode: "operator_fallback", users: [getDefaultUser()] };
|
|
138
144
|
}
|
|
@@ -154,6 +160,23 @@ export function resolveGamificationScope(requestedUserIds) {
|
|
|
154
160
|
label
|
|
155
161
|
};
|
|
156
162
|
}
|
|
163
|
+
function persistableUserIdForScope(scope) {
|
|
164
|
+
if (scope.users.length === 1 &&
|
|
165
|
+
(scope.mode === "selected_user" || scope.mode === "operator_fallback")) {
|
|
166
|
+
return scope.users[0].id;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
function emptyGamificationEquipment() {
|
|
171
|
+
return {
|
|
172
|
+
selectedMascotSkin: null,
|
|
173
|
+
selectedHudTreatment: null,
|
|
174
|
+
selectedStreakEffect: null,
|
|
175
|
+
selectedTrophyShelf: null,
|
|
176
|
+
selectedCelebrationVariant: null,
|
|
177
|
+
updatedAt: null
|
|
178
|
+
};
|
|
179
|
+
}
|
|
157
180
|
function buildOwnerResolver(defaultUserId) {
|
|
158
181
|
const ownerRows = getDatabase()
|
|
159
182
|
.prepare(`SELECT entity_type, entity_id, user_id
|
|
@@ -210,7 +233,7 @@ function loadScopedRewardEvents(scope) {
|
|
|
210
233
|
ORDER BY reward_ledger.created_at ASC`)
|
|
211
234
|
.all();
|
|
212
235
|
const scopeUserIds = new Set(scope.userIds);
|
|
213
|
-
const defaultUserId = scope
|
|
236
|
+
const defaultUserId = persistableUserIdForScope(scope);
|
|
214
237
|
const resolveOwner = buildOwnerResolver(defaultUserId);
|
|
215
238
|
return rows
|
|
216
239
|
.map((row) => {
|
|
@@ -242,6 +265,9 @@ function loadScopedRewardEvents(scope) {
|
|
|
242
265
|
: event.ownerUserId !== null && scopeUserIds.has(event.ownerUserId));
|
|
243
266
|
}
|
|
244
267
|
function syncEntityCreationRewards(scope) {
|
|
268
|
+
if (!persistableUserIdForScope(scope)) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
245
271
|
const database = getDatabase();
|
|
246
272
|
const scopeUserIds = [...new Set(scope.userIds)];
|
|
247
273
|
const scopePlaceholders = scopeUserIds.map(() => "?").join(", ");
|
|
@@ -282,7 +308,7 @@ function isQualifyingStreakReward(event) {
|
|
|
282
308
|
event.reversedByRewardId === null &&
|
|
283
309
|
event.metadata.manual !== true);
|
|
284
310
|
}
|
|
285
|
-
function
|
|
311
|
+
function deriveDailyActivityRows(userId, scopedRewards, timezone) {
|
|
286
312
|
const byDate = new Map();
|
|
287
313
|
for (const event of scopedRewards.filter(isQualifyingStreakReward)) {
|
|
288
314
|
const dateKeyValue = dateKeyInTimezone(event.createdAt, timezone);
|
|
@@ -300,7 +326,7 @@ function syncDailyActivity(userId, scopedRewards, timezone) {
|
|
|
300
326
|
current.lastRewardEventId = event.id;
|
|
301
327
|
byDate.set(dateKeyValue, current);
|
|
302
328
|
}
|
|
303
|
-
|
|
329
|
+
return [...byDate.values()].map((row) => ({
|
|
304
330
|
userId,
|
|
305
331
|
dateKey: row.dateKey,
|
|
306
332
|
timezone,
|
|
@@ -308,7 +334,11 @@ function syncDailyActivity(userId, scopedRewards, timezone) {
|
|
|
308
334
|
eventCount: row.eventCount,
|
|
309
335
|
firstRewardEventId: row.firstRewardEventId,
|
|
310
336
|
lastRewardEventId: row.lastRewardEventId
|
|
311
|
-
}))
|
|
337
|
+
}));
|
|
338
|
+
}
|
|
339
|
+
function syncDailyActivity(userId, scopedRewards, timezone) {
|
|
340
|
+
const rows = deriveDailyActivityRows(userId, scopedRewards, timezone);
|
|
341
|
+
replaceGamificationDailyActivity(userId, rows);
|
|
312
342
|
return listGamificationDailyActivity(userId);
|
|
313
343
|
}
|
|
314
344
|
function calculateStreakFromActivity(activeDateKeys, now, timezone) {
|
|
@@ -695,14 +725,16 @@ function evaluateCatalogItem(item, metricValues) {
|
|
|
695
725
|
return evaluateRequirement(item.requirement, metricValues);
|
|
696
726
|
}
|
|
697
727
|
function syncCatalog(input) {
|
|
698
|
-
const userId = input.
|
|
728
|
+
const userId = input.persistableUserId;
|
|
699
729
|
const nowIso = input.now.toISOString();
|
|
700
730
|
const catalogItemIds = new Set(GAMIFICATION_CATALOG.map((item) => item.id));
|
|
701
|
-
const existingUnlocks =
|
|
731
|
+
const existingUnlocks = userId
|
|
732
|
+
? listGamificationUnlocks(userId).filter((unlock) => catalogItemIds.has(unlock.itemId))
|
|
733
|
+
: [];
|
|
702
734
|
const isInitialBackfill = existingUnlocks.length === 0;
|
|
703
735
|
for (const item of GAMIFICATION_CATALOG) {
|
|
704
736
|
const evaluation = evaluateCatalogItem(item, input.metricValues);
|
|
705
|
-
if (evaluation.met) {
|
|
737
|
+
if (userId && evaluation.met) {
|
|
706
738
|
const inserted = insertGamificationUnlock({
|
|
707
739
|
userId,
|
|
708
740
|
itemId: item.id,
|
|
@@ -731,7 +763,7 @@ function syncCatalog(input) {
|
|
|
731
763
|
}
|
|
732
764
|
}
|
|
733
765
|
}
|
|
734
|
-
if (input.profile.level > 1) {
|
|
766
|
+
if (userId && input.profile.level > 1) {
|
|
735
767
|
enqueueGamificationCelebration({
|
|
736
768
|
id: `gce_${userId}_level_${input.profile.level}`,
|
|
737
769
|
userId,
|
|
@@ -747,7 +779,7 @@ function syncCatalog(input) {
|
|
|
747
779
|
createdAt: nowIso
|
|
748
780
|
});
|
|
749
781
|
}
|
|
750
|
-
if (input.mascot.missedDays > 0) {
|
|
782
|
+
if (userId && input.mascot.missedDays > 0) {
|
|
751
783
|
enqueueGamificationCelebration({
|
|
752
784
|
id: `gce_${userId}_comeback_pressure_${input.mascot.lastActiveDateKey ?? "none"}`,
|
|
753
785
|
userId,
|
|
@@ -762,15 +794,16 @@ function syncCatalog(input) {
|
|
|
762
794
|
createdAt: nowIso
|
|
763
795
|
});
|
|
764
796
|
}
|
|
765
|
-
const unlocksByItemId = new Map(listGamificationUnlocks(userId)
|
|
797
|
+
const unlocksByItemId = new Map((userId ? listGamificationUnlocks(userId) : existingUnlocks)
|
|
766
798
|
.filter((unlock) => catalogItemIds.has(unlock.itemId))
|
|
767
799
|
.map((unlock) => [unlock.itemId, unlock]));
|
|
768
800
|
const entries = GAMIFICATION_CATALOG.map((item) => {
|
|
769
801
|
const evaluation = evaluateCatalogItem(item, input.metricValues);
|
|
770
802
|
const unlock = unlocksByItemId.get(item.id);
|
|
803
|
+
const readOnlyAggregateUnlock = !userId && evaluation.met;
|
|
771
804
|
return {
|
|
772
805
|
...item,
|
|
773
|
-
unlocked: Boolean(unlock),
|
|
806
|
+
unlocked: Boolean(unlock) || readOnlyAggregateUnlock,
|
|
774
807
|
unlockedAt: unlock?.unlockedAt ?? null,
|
|
775
808
|
progressCurrent: Math.max(0, Math.min(evaluation.current, evaluation.target)),
|
|
776
809
|
progressTarget: evaluation.target,
|
|
@@ -819,11 +852,13 @@ function syncCatalog(input) {
|
|
|
819
852
|
function buildGamificationState(goals, tasks, habits, options = {}) {
|
|
820
853
|
const now = options.now ?? new Date();
|
|
821
854
|
const scope = resolveGamificationScope(options.userIds);
|
|
855
|
+
const persistableUserId = persistableUserIdForScope(scope);
|
|
822
856
|
syncEntityCreationRewards(scope);
|
|
823
857
|
const scopedRewards = loadScopedRewardEvents(scope);
|
|
824
858
|
const timezone = resolveTimezone();
|
|
825
|
-
const
|
|
826
|
-
|
|
859
|
+
const dailyActivity = persistableUserId
|
|
860
|
+
? syncDailyActivity(persistableUserId, scopedRewards, timezone)
|
|
861
|
+
: deriveDailyActivityRows("aggregate", scopedRewards, timezone);
|
|
827
862
|
const activeDateKeys = dailyActivity.map((row) => row.dateKey);
|
|
828
863
|
const activeDateSet = new Set(activeDateKeys);
|
|
829
864
|
const streakDays = calculateStreakFromActivity(activeDateSet, now, timezone);
|
|
@@ -871,7 +906,9 @@ function buildGamificationState(goals, tasks, habits, options = {}) {
|
|
|
871
906
|
topGoalId: topGoal?.goalId ?? null,
|
|
872
907
|
topGoalTitle: topGoal?.goalTitle ?? null
|
|
873
908
|
});
|
|
874
|
-
const equipment =
|
|
909
|
+
const equipment = persistableUserId
|
|
910
|
+
? getGamificationEquipment(persistableUserId)
|
|
911
|
+
: emptyGamificationEquipment();
|
|
875
912
|
const metricValues = buildMetricValues({
|
|
876
913
|
scope,
|
|
877
914
|
profile,
|
|
@@ -888,6 +925,7 @@ function buildGamificationState(goals, tasks, habits, options = {}) {
|
|
|
888
925
|
});
|
|
889
926
|
const catalog = syncCatalog({
|
|
890
927
|
scope,
|
|
928
|
+
persistableUserId,
|
|
891
929
|
profile,
|
|
892
930
|
metricValues,
|
|
893
931
|
equipment,
|
|
@@ -901,7 +939,8 @@ function buildGamificationState(goals, tasks, habits, options = {}) {
|
|
|
901
939
|
metricValues,
|
|
902
940
|
equipment,
|
|
903
941
|
mascot,
|
|
904
|
-
catalog
|
|
942
|
+
catalog,
|
|
943
|
+
persistableUserId
|
|
905
944
|
};
|
|
906
945
|
}
|
|
907
946
|
export function buildGamificationProfile(goals, tasks, habits, now = new Date(), options = {}) {
|
|
@@ -910,12 +949,8 @@ export function buildGamificationProfile(goals, tasks, habits, now = new Date(),
|
|
|
910
949
|
now
|
|
911
950
|
}).profile;
|
|
912
951
|
}
|
|
913
|
-
|
|
914
|
-
const
|
|
915
|
-
userIds: options.userIds,
|
|
916
|
-
now
|
|
917
|
-
});
|
|
918
|
-
const profile = state.profile;
|
|
952
|
+
function buildAchievementSignalsFromProfile(input) {
|
|
953
|
+
const { goals, tasks, habits, now, profile } = input;
|
|
919
954
|
const doneTasks = tasks.filter((task) => task.status === "done");
|
|
920
955
|
const alignedDoneTasks = doneTasks.filter((task) => task.goalId !== null && task.tagIds.length > 0);
|
|
921
956
|
const focusTasks = tasks.filter((task) => task.status === "focus" || task.status === "in_progress");
|
|
@@ -1000,8 +1035,21 @@ export function buildAchievementSignals(goals, tasks, habits, now = new Date(),
|
|
|
1000
1035
|
}
|
|
1001
1036
|
].map((achievement) => achievementSignalSchema.parse(achievement));
|
|
1002
1037
|
}
|
|
1003
|
-
export function
|
|
1004
|
-
const
|
|
1038
|
+
export function buildAchievementSignals(goals, tasks, habits, now = new Date(), options = {}) {
|
|
1039
|
+
const state = buildGamificationState(goals, tasks, habits, {
|
|
1040
|
+
userIds: options.userIds,
|
|
1041
|
+
now
|
|
1042
|
+
});
|
|
1043
|
+
return buildAchievementSignalsFromProfile({
|
|
1044
|
+
goals,
|
|
1045
|
+
tasks,
|
|
1046
|
+
habits,
|
|
1047
|
+
now,
|
|
1048
|
+
profile: state.profile
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
function buildMilestoneRewardsFromProfile(input) {
|
|
1052
|
+
const { goals, tasks, habits, now, profile } = input;
|
|
1005
1053
|
const doneTasks = tasks.filter((task) => task.status === "done");
|
|
1006
1054
|
const topGoal = profile.topGoalId
|
|
1007
1055
|
? goals.find((goal) => goal.id === profile.topGoalId) ?? null
|
|
@@ -1071,10 +1119,12 @@ export function buildMilestoneRewards(goals, tasks, habits, now = new Date(), op
|
|
|
1071
1119
|
}
|
|
1072
1120
|
].map((reward) => milestoneRewardSchema.parse(reward));
|
|
1073
1121
|
}
|
|
1074
|
-
export function
|
|
1122
|
+
export function buildMilestoneRewards(goals, tasks, habits, now = new Date(), options = {}) {
|
|
1075
1123
|
const profile = buildGamificationProfile(goals, tasks, habits, now, options);
|
|
1076
|
-
|
|
1077
|
-
|
|
1124
|
+
return buildMilestoneRewardsFromProfile({ goals, tasks, habits, now, profile });
|
|
1125
|
+
}
|
|
1126
|
+
function buildXpMomentumPulseFromParts(input) {
|
|
1127
|
+
const { profile, achievements, milestoneRewards } = input;
|
|
1078
1128
|
const nextMilestone = milestoneRewards.find((reward) => !reward.completed) ??
|
|
1079
1129
|
milestoneRewards[0] ??
|
|
1080
1130
|
null;
|
|
@@ -1106,6 +1156,31 @@ export function buildXpMomentumPulse(goals, tasks, habits, now = new Date(), opt
|
|
|
1106
1156
|
nextMilestoneLabel: nextMilestone?.rewardLabel ?? "Keep building visible momentum"
|
|
1107
1157
|
};
|
|
1108
1158
|
}
|
|
1159
|
+
export function buildXpMomentumPulse(goals, tasks, habits, now = new Date(), options = {}) {
|
|
1160
|
+
const state = buildGamificationState(goals, tasks, habits, {
|
|
1161
|
+
userIds: options.userIds,
|
|
1162
|
+
now
|
|
1163
|
+
});
|
|
1164
|
+
const achievements = buildAchievementSignalsFromProfile({
|
|
1165
|
+
goals,
|
|
1166
|
+
tasks,
|
|
1167
|
+
habits,
|
|
1168
|
+
now,
|
|
1169
|
+
profile: state.profile
|
|
1170
|
+
});
|
|
1171
|
+
const milestoneRewards = buildMilestoneRewardsFromProfile({
|
|
1172
|
+
goals,
|
|
1173
|
+
tasks,
|
|
1174
|
+
habits,
|
|
1175
|
+
now,
|
|
1176
|
+
profile: state.profile
|
|
1177
|
+
});
|
|
1178
|
+
return buildXpMomentumPulseFromParts({
|
|
1179
|
+
profile: state.profile,
|
|
1180
|
+
achievements,
|
|
1181
|
+
milestoneRewards
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1109
1184
|
export function buildGamificationCatalogPayload(goals, tasks, habits, options = {}) {
|
|
1110
1185
|
return buildGamificationState(goals, tasks, habits, options).catalog;
|
|
1111
1186
|
}
|
|
@@ -1146,7 +1221,7 @@ export function updateGamificationEquipmentSelection(input) {
|
|
|
1146
1221
|
const state = buildGamificationState(input.goals, input.tasks, input.habits, {
|
|
1147
1222
|
userIds: input.userIds
|
|
1148
1223
|
});
|
|
1149
|
-
const userId = state.
|
|
1224
|
+
const userId = state.persistableUserId;
|
|
1150
1225
|
if (!userId) {
|
|
1151
1226
|
throw new LockedGamificationCosmeticError("Equipment can only be changed for a concrete Forge user.");
|
|
1152
1227
|
}
|
|
@@ -1166,10 +1241,28 @@ export function updateGamificationEquipmentSelection(input) {
|
|
|
1166
1241
|
return upsertGamificationEquipment(userId, input.equipment);
|
|
1167
1242
|
}
|
|
1168
1243
|
export function buildGamificationOverview(goals, tasks, habits, now = new Date(), options = {}) {
|
|
1244
|
+
const state = buildGamificationState(goals, tasks, habits, {
|
|
1245
|
+
userIds: options.userIds,
|
|
1246
|
+
now
|
|
1247
|
+
});
|
|
1248
|
+
const achievements = buildAchievementSignalsFromProfile({
|
|
1249
|
+
goals,
|
|
1250
|
+
tasks,
|
|
1251
|
+
habits,
|
|
1252
|
+
now,
|
|
1253
|
+
profile: state.profile
|
|
1254
|
+
});
|
|
1255
|
+
const milestoneRewards = buildMilestoneRewardsFromProfile({
|
|
1256
|
+
goals,
|
|
1257
|
+
tasks,
|
|
1258
|
+
habits,
|
|
1259
|
+
now,
|
|
1260
|
+
profile: state.profile
|
|
1261
|
+
});
|
|
1169
1262
|
return {
|
|
1170
|
-
profile:
|
|
1171
|
-
achievements
|
|
1172
|
-
milestoneRewards
|
|
1263
|
+
profile: state.profile,
|
|
1264
|
+
achievements,
|
|
1265
|
+
milestoneRewards
|
|
1173
1266
|
};
|
|
1174
1267
|
}
|
|
1175
1268
|
export function buildXpMetricsPayloadModel(input) {
|
|
@@ -1182,8 +1275,25 @@ export function buildXpMetricsPayloadModel(input) {
|
|
|
1182
1275
|
const dailyAmbientCap = rules
|
|
1183
1276
|
.filter((rule) => rule.family === "ambient")
|
|
1184
1277
|
.reduce((max, rule) => Math.max(max, Number(rule.config.dailyCap ?? 0)), 0) || 12;
|
|
1185
|
-
const achievements =
|
|
1186
|
-
|
|
1278
|
+
const achievements = buildAchievementSignalsFromProfile({
|
|
1279
|
+
goals: input.goals,
|
|
1280
|
+
tasks: input.tasks,
|
|
1281
|
+
habits: input.habits,
|
|
1282
|
+
now,
|
|
1283
|
+
profile: state.profile
|
|
1284
|
+
});
|
|
1285
|
+
const milestoneRewards = buildMilestoneRewardsFromProfile({
|
|
1286
|
+
goals: input.goals,
|
|
1287
|
+
tasks: input.tasks,
|
|
1288
|
+
habits: input.habits,
|
|
1289
|
+
now,
|
|
1290
|
+
profile: state.profile
|
|
1291
|
+
});
|
|
1292
|
+
const momentumPulse = buildXpMomentumPulseFromParts({
|
|
1293
|
+
profile: state.profile,
|
|
1294
|
+
achievements,
|
|
1295
|
+
milestoneRewards
|
|
1296
|
+
});
|
|
1187
1297
|
const visibleCatalog = [
|
|
1188
1298
|
...(state.catalog.newestUnlock ? [state.catalog.newestUnlock] : []),
|
|
1189
1299
|
...(state.catalog.nextUnlock ? [state.catalog.nextUnlock] : []),
|
|
@@ -1198,7 +1308,7 @@ export function buildXpMetricsPayloadModel(input) {
|
|
|
1198
1308
|
profile: state.profile,
|
|
1199
1309
|
achievements,
|
|
1200
1310
|
milestoneRewards,
|
|
1201
|
-
momentumPulse
|
|
1311
|
+
momentumPulse,
|
|
1202
1312
|
catalogPreview: uniquePreview,
|
|
1203
1313
|
unlockedItemCount: state.catalog.unlockedCount,
|
|
1204
1314
|
totalItemCount: state.catalog.totalCount,
|
|
@@ -1207,7 +1317,9 @@ export function buildXpMetricsPayloadModel(input) {
|
|
|
1207
1317
|
nextTargets: state.catalog.nextTargets,
|
|
1208
1318
|
equipment: state.equipment,
|
|
1209
1319
|
mascot: state.mascot,
|
|
1210
|
-
celebrations:
|
|
1320
|
+
celebrations: state.persistableUserId
|
|
1321
|
+
? listUnseenGamificationCelebrations(state.persistableUserId, 5)
|
|
1322
|
+
: [],
|
|
1211
1323
|
recentLedger: state.scopedRewards
|
|
1212
1324
|
.slice(-25)
|
|
1213
1325
|
.reverse()
|