@playcademy/sdk 0.4.1-beta.3 → 0.4.1-beta.5
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/index.d.ts +86 -69
- package/dist/index.js +327 -61
- package/dist/internal.d.ts +1775 -1753
- package/dist/internal.js +337 -61
- package/dist/types.d.ts +776 -759
- package/package.json +1 -1
package/dist/internal.js
CHANGED
|
@@ -1274,7 +1274,8 @@ var BADGES = {
|
|
|
1274
1274
|
// ../constants/src/timeback.ts
|
|
1275
1275
|
var TIMEBACK_ROUTES = {
|
|
1276
1276
|
END_ACTIVITY: "/integrations/timeback/end-activity",
|
|
1277
|
-
GET_XP: "/integrations/timeback/xp"
|
|
1277
|
+
GET_XP: "/integrations/timeback/xp",
|
|
1278
|
+
HEARTBEAT: "/integrations/timeback/heartbeat"
|
|
1278
1279
|
};
|
|
1279
1280
|
// src/core/cache/singleton-cache.ts
|
|
1280
1281
|
function createSingletonCache() {
|
|
@@ -1378,6 +1379,325 @@ function createRealtimeNamespace(client) {
|
|
|
1378
1379
|
}
|
|
1379
1380
|
};
|
|
1380
1381
|
}
|
|
1382
|
+
// src/core/activity-tracker.ts
|
|
1383
|
+
var DEFAULT_HIDDEN_TIMEOUT_MS = 10 * 60 * 1000;
|
|
1384
|
+
var DEFAULT_HEARTBEAT_INTERVAL_MS = 15000;
|
|
1385
|
+
function getCurrentPausedTotal(activity, now = Date.now()) {
|
|
1386
|
+
if (activity.pauseStartTime === null) {
|
|
1387
|
+
return activity.pausedTime;
|
|
1388
|
+
}
|
|
1389
|
+
return activity.pausedTime + (now - activity.pauseStartTime);
|
|
1390
|
+
}
|
|
1391
|
+
function computeWindowAndReset(activity) {
|
|
1392
|
+
const now = Date.now();
|
|
1393
|
+
const totalPaused = getCurrentPausedTotal(activity, now);
|
|
1394
|
+
if (activity.pauseStartTime !== null) {
|
|
1395
|
+
activity.pausedTime = totalPaused;
|
|
1396
|
+
activity.pauseStartTime = now;
|
|
1397
|
+
}
|
|
1398
|
+
const windowPaused = totalPaused - activity.windowPausedAtStart;
|
|
1399
|
+
const windowElapsed = now - activity.windowStartTime;
|
|
1400
|
+
const activeMs = Math.max(0, windowElapsed - windowPaused);
|
|
1401
|
+
activity.windowStartTime = now;
|
|
1402
|
+
activity.windowPausedAtStart = totalPaused;
|
|
1403
|
+
activity.windowSequence++;
|
|
1404
|
+
return { activeMs, pausedMs: windowPaused };
|
|
1405
|
+
}
|
|
1406
|
+
function markPersistedTiming(activity, timing) {
|
|
1407
|
+
activity.totalPersistedActiveMs += timing.activeMs;
|
|
1408
|
+
activity.totalPersistedPausedMs += timing.pausedMs;
|
|
1409
|
+
}
|
|
1410
|
+
function queueHeartbeatFlush(activity, timing, flush) {
|
|
1411
|
+
markPersistedTiming(activity, timing);
|
|
1412
|
+
async function doFlush() {
|
|
1413
|
+
await flush();
|
|
1414
|
+
}
|
|
1415
|
+
if (activity.flushInFlight) {
|
|
1416
|
+
const previousFlush = activity.flushInFlight.catch(() => {
|
|
1417
|
+
return;
|
|
1418
|
+
});
|
|
1419
|
+
activity.flushInFlight = previousFlush.then(doFlush);
|
|
1420
|
+
} else {
|
|
1421
|
+
activity.flushInFlight = doFlush();
|
|
1422
|
+
}
|
|
1423
|
+
return activity.flushInFlight;
|
|
1424
|
+
}
|
|
1425
|
+
function stopHeartbeatInterval(activity) {
|
|
1426
|
+
if (activity.heartbeatIntervalId === null) {
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
clearInterval(activity.heartbeatIntervalId);
|
|
1430
|
+
activity.heartbeatIntervalId = null;
|
|
1431
|
+
}
|
|
1432
|
+
function createTimebackActivityTracker(client) {
|
|
1433
|
+
let currentActivity = null;
|
|
1434
|
+
let boundVisibilityHandler = null;
|
|
1435
|
+
let boundShellPauseHandler = null;
|
|
1436
|
+
let boundShellResumeHandler = null;
|
|
1437
|
+
let boundPageHideHandler = null;
|
|
1438
|
+
function startHeartbeatInterval(activity) {
|
|
1439
|
+
if (activity.heartbeatIntervalMs === Infinity || activity.heartbeatIntervalId !== null) {
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
activity.heartbeatIntervalId = setInterval(() => {
|
|
1443
|
+
flushHeartbeat();
|
|
1444
|
+
}, activity.heartbeatIntervalMs);
|
|
1445
|
+
}
|
|
1446
|
+
function addPauseReason(reason) {
|
|
1447
|
+
if (!currentActivity) {
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
const wasPaused = currentActivity.pauseReasons.size > 0;
|
|
1451
|
+
currentActivity.pauseReasons.add(reason);
|
|
1452
|
+
if (!wasPaused && currentActivity.pauseReasons.size > 0) {
|
|
1453
|
+
currentActivity.pauseStartTime = Date.now();
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
function removePauseReason(reason) {
|
|
1457
|
+
if (!currentActivity) {
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
currentActivity.pauseReasons.delete(reason);
|
|
1461
|
+
if (currentActivity.pauseReasons.size === 0 && currentActivity.pauseStartTime !== null) {
|
|
1462
|
+
currentActivity.pausedTime += Date.now() - currentActivity.pauseStartTime;
|
|
1463
|
+
currentActivity.pauseStartTime = null;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
function handleVisibilityChange() {
|
|
1467
|
+
if (!currentActivity) {
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
if (document.visibilityState === "hidden") {
|
|
1471
|
+
addPauseReason("hidden");
|
|
1472
|
+
if (currentActivity.hiddenTimeoutMs !== Infinity) {
|
|
1473
|
+
if (currentActivity.hiddenTimeoutId !== null) {
|
|
1474
|
+
clearTimeout(currentActivity.hiddenTimeoutId);
|
|
1475
|
+
}
|
|
1476
|
+
const activity = currentActivity;
|
|
1477
|
+
currentActivity.hiddenTimeoutId = setTimeout(() => {
|
|
1478
|
+
if (currentActivity === activity) {
|
|
1479
|
+
activity.hiddenTimeoutId = null;
|
|
1480
|
+
activity.hiddenTimedOut = true;
|
|
1481
|
+
stopHeartbeatInterval(activity);
|
|
1482
|
+
}
|
|
1483
|
+
}, activity.hiddenTimeoutMs);
|
|
1484
|
+
}
|
|
1485
|
+
} else {
|
|
1486
|
+
const shouldResetWindow = currentActivity.hiddenTimedOut;
|
|
1487
|
+
if (currentActivity.hiddenTimeoutId !== null) {
|
|
1488
|
+
clearTimeout(currentActivity.hiddenTimeoutId);
|
|
1489
|
+
currentActivity.hiddenTimeoutId = null;
|
|
1490
|
+
}
|
|
1491
|
+
removePauseReason("hidden");
|
|
1492
|
+
if (shouldResetWindow) {
|
|
1493
|
+
const now = Date.now();
|
|
1494
|
+
const pausedAtReset = getCurrentPausedTotal(currentActivity, now);
|
|
1495
|
+
currentActivity.hiddenTimedOut = false;
|
|
1496
|
+
currentActivity.windowStartTime = now;
|
|
1497
|
+
currentActivity.windowPausedAtStart = pausedAtReset;
|
|
1498
|
+
startHeartbeatInterval(currentActivity);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
function handleShellPause() {
|
|
1503
|
+
addPauseReason("shell");
|
|
1504
|
+
}
|
|
1505
|
+
function handleShellResume() {
|
|
1506
|
+
removePauseReason("shell");
|
|
1507
|
+
}
|
|
1508
|
+
function buildHeartbeatBody(activity, timing, sequence, isFinal) {
|
|
1509
|
+
return {
|
|
1510
|
+
runId: activity.runId,
|
|
1511
|
+
activityData: activity.metadata,
|
|
1512
|
+
timingData: { activeMs: timing.activeMs, pausedMs: timing.pausedMs },
|
|
1513
|
+
windowSequence: sequence,
|
|
1514
|
+
isFinal
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
async function flushHeartbeat(isFinal) {
|
|
1518
|
+
const activity = currentActivity;
|
|
1519
|
+
if (!activity) {
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
const trackedActivity = activity;
|
|
1523
|
+
const sequence = trackedActivity.windowSequence;
|
|
1524
|
+
const timing = computeWindowAndReset(trackedActivity);
|
|
1525
|
+
if (timing.activeMs === 0 && timing.pausedMs === 0) {
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
const body = buildHeartbeatBody(trackedActivity, timing, sequence, isFinal);
|
|
1529
|
+
await queueHeartbeatFlush(trackedActivity, timing, async () => {
|
|
1530
|
+
try {
|
|
1531
|
+
await client["requestGameBackend"](TIMEBACK_ROUTES.HEARTBEAT, "POST", body);
|
|
1532
|
+
} catch {}
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
function handlePageHide() {
|
|
1536
|
+
const activity = currentActivity;
|
|
1537
|
+
if (!activity) {
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
const sequence = activity.windowSequence;
|
|
1541
|
+
const timing = computeWindowAndReset(activity);
|
|
1542
|
+
if (timing.activeMs === 0 && timing.pausedMs === 0) {
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
const body = buildHeartbeatBody(activity, timing, sequence, true);
|
|
1546
|
+
queueHeartbeatFlush(activity, timing, async () => {
|
|
1547
|
+
try {
|
|
1548
|
+
const baseUrl = client["getGameBackendUrl"]();
|
|
1549
|
+
const url = `${baseUrl}${TIMEBACK_ROUTES.HEARTBEAT}`;
|
|
1550
|
+
const headers = {
|
|
1551
|
+
"Content-Type": "application/json",
|
|
1552
|
+
...client["authStrategy"].getHeaders()
|
|
1553
|
+
};
|
|
1554
|
+
const response = await fetch(url, {
|
|
1555
|
+
method: "POST",
|
|
1556
|
+
headers,
|
|
1557
|
+
body: JSON.stringify(body),
|
|
1558
|
+
keepalive: true
|
|
1559
|
+
});
|
|
1560
|
+
if (response.ok) {
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
} catch {}
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
function cleanupListeners() {
|
|
1567
|
+
if (boundVisibilityHandler && typeof document !== "undefined") {
|
|
1568
|
+
document.removeEventListener("visibilitychange", boundVisibilityHandler);
|
|
1569
|
+
boundVisibilityHandler = null;
|
|
1570
|
+
}
|
|
1571
|
+
if (boundShellPauseHandler) {
|
|
1572
|
+
messaging.unlisten("PLAYCADEMY_PAUSE" /* PAUSE */, boundShellPauseHandler);
|
|
1573
|
+
boundShellPauseHandler = null;
|
|
1574
|
+
}
|
|
1575
|
+
if (boundShellResumeHandler) {
|
|
1576
|
+
messaging.unlisten("PLAYCADEMY_RESUME" /* RESUME */, boundShellResumeHandler);
|
|
1577
|
+
boundShellResumeHandler = null;
|
|
1578
|
+
}
|
|
1579
|
+
if (boundPageHideHandler && typeof globalThis.window !== "undefined") {
|
|
1580
|
+
globalThis.window.removeEventListener("pagehide", boundPageHideHandler);
|
|
1581
|
+
boundPageHideHandler = null;
|
|
1582
|
+
}
|
|
1583
|
+
if (currentActivity?.hiddenTimeoutId != null) {
|
|
1584
|
+
clearTimeout(currentActivity.hiddenTimeoutId);
|
|
1585
|
+
}
|
|
1586
|
+
if (currentActivity?.heartbeatIntervalId != null) {
|
|
1587
|
+
stopHeartbeatInterval(currentActivity);
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
return {
|
|
1591
|
+
startActivity(metadata, options) {
|
|
1592
|
+
cleanupListeners();
|
|
1593
|
+
const now = Date.now();
|
|
1594
|
+
const heartbeatIntervalMs = options?.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
1595
|
+
currentActivity = {
|
|
1596
|
+
runId: crypto.randomUUID(),
|
|
1597
|
+
startTime: now,
|
|
1598
|
+
metadata,
|
|
1599
|
+
pausedTime: 0,
|
|
1600
|
+
pauseStartTime: null,
|
|
1601
|
+
pauseReasons: new Set,
|
|
1602
|
+
hiddenTimeoutId: null,
|
|
1603
|
+
hiddenTimedOut: false,
|
|
1604
|
+
hiddenTimeoutMs: options?.hiddenTimeoutMs ?? DEFAULT_HIDDEN_TIMEOUT_MS,
|
|
1605
|
+
windowStartTime: now,
|
|
1606
|
+
windowPausedAtStart: 0,
|
|
1607
|
+
windowSequence: 0,
|
|
1608
|
+
heartbeatIntervalId: null,
|
|
1609
|
+
heartbeatIntervalMs,
|
|
1610
|
+
flushInFlight: null,
|
|
1611
|
+
totalPersistedActiveMs: 0,
|
|
1612
|
+
totalPersistedPausedMs: 0
|
|
1613
|
+
};
|
|
1614
|
+
if (typeof document !== "undefined") {
|
|
1615
|
+
boundVisibilityHandler = handleVisibilityChange;
|
|
1616
|
+
document.addEventListener("visibilitychange", boundVisibilityHandler);
|
|
1617
|
+
if (document.visibilityState === "hidden") {
|
|
1618
|
+
handleVisibilityChange();
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
startHeartbeatInterval(currentActivity);
|
|
1622
|
+
if (typeof globalThis.window !== "undefined") {
|
|
1623
|
+
boundPageHideHandler = handlePageHide;
|
|
1624
|
+
globalThis.window.addEventListener("pagehide", boundPageHideHandler);
|
|
1625
|
+
}
|
|
1626
|
+
boundShellPauseHandler = handleShellPause;
|
|
1627
|
+
boundShellResumeHandler = handleShellResume;
|
|
1628
|
+
messaging.listen("PLAYCADEMY_PAUSE" /* PAUSE */, boundShellPauseHandler);
|
|
1629
|
+
messaging.listen("PLAYCADEMY_RESUME" /* RESUME */, boundShellResumeHandler);
|
|
1630
|
+
},
|
|
1631
|
+
pauseActivity() {
|
|
1632
|
+
if (!currentActivity) {
|
|
1633
|
+
throw new Error("No activity in progress. Call startActivity() before pauseActivity().");
|
|
1634
|
+
}
|
|
1635
|
+
if (currentActivity.pauseReasons.has("manual")) {
|
|
1636
|
+
throw new Error("Activity is already paused.");
|
|
1637
|
+
}
|
|
1638
|
+
addPauseReason("manual");
|
|
1639
|
+
},
|
|
1640
|
+
resumeActivity() {
|
|
1641
|
+
if (!currentActivity) {
|
|
1642
|
+
throw new Error("No activity in progress. Call startActivity() before resumeActivity().");
|
|
1643
|
+
}
|
|
1644
|
+
if (!currentActivity.pauseReasons.has("manual")) {
|
|
1645
|
+
throw new Error("Activity is not paused.");
|
|
1646
|
+
}
|
|
1647
|
+
removePauseReason("manual");
|
|
1648
|
+
},
|
|
1649
|
+
async endActivity(data) {
|
|
1650
|
+
if (!currentActivity) {
|
|
1651
|
+
throw new Error("No activity in progress. Call startActivity() before endActivity().");
|
|
1652
|
+
}
|
|
1653
|
+
const activity = currentActivity;
|
|
1654
|
+
cleanupListeners();
|
|
1655
|
+
await flushHeartbeat(true);
|
|
1656
|
+
if (activity.pauseStartTime !== null) {
|
|
1657
|
+
activity.pausedTime += Date.now() - activity.pauseStartTime;
|
|
1658
|
+
activity.pauseStartTime = null;
|
|
1659
|
+
}
|
|
1660
|
+
const endTime = Date.now();
|
|
1661
|
+
const totalElapsed = endTime - activity.startTime;
|
|
1662
|
+
const activeTime = Math.max(0, totalElapsed - activity.pausedTime);
|
|
1663
|
+
const durationSeconds = Math.floor(activeTime / 1000);
|
|
1664
|
+
const unreportedActiveMs = Math.max(0, activeTime - activity.totalPersistedActiveMs);
|
|
1665
|
+
const unreportedPausedMs = Math.max(0, activity.pausedTime - activity.totalPersistedPausedMs);
|
|
1666
|
+
const { correctQuestions, totalQuestions } = data;
|
|
1667
|
+
const request = {
|
|
1668
|
+
runId: activity.runId,
|
|
1669
|
+
activityData: activity.metadata,
|
|
1670
|
+
scoreData: {
|
|
1671
|
+
correctQuestions,
|
|
1672
|
+
totalQuestions
|
|
1673
|
+
},
|
|
1674
|
+
timingData: {
|
|
1675
|
+
durationSeconds
|
|
1676
|
+
},
|
|
1677
|
+
sessionTimingData: {
|
|
1678
|
+
activeSeconds: unreportedActiveMs / 1000,
|
|
1679
|
+
...unreportedPausedMs > 0 ? { inactiveSeconds: unreportedPausedMs / 1000 } : {}
|
|
1680
|
+
},
|
|
1681
|
+
xpEarned: data.xpAwarded,
|
|
1682
|
+
masteredUnits: data.masteredUnits,
|
|
1683
|
+
extensions: data.extensions
|
|
1684
|
+
};
|
|
1685
|
+
try {
|
|
1686
|
+
const response = await client["requestGameBackend"](TIMEBACK_ROUTES.END_ACTIVITY, "POST", request);
|
|
1687
|
+
if (currentActivity === activity) {
|
|
1688
|
+
currentActivity = null;
|
|
1689
|
+
}
|
|
1690
|
+
return response;
|
|
1691
|
+
} catch (error) {
|
|
1692
|
+
if (currentActivity === activity) {
|
|
1693
|
+
currentActivity = null;
|
|
1694
|
+
}
|
|
1695
|
+
throw error;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1381
1701
|
// src/core/cache/ttl-cache.ts
|
|
1382
1702
|
function createTTLCache(options) {
|
|
1383
1703
|
const cache = new Map;
|
|
@@ -1473,7 +1793,7 @@ function isValidSubject(value) {
|
|
|
1473
1793
|
|
|
1474
1794
|
// src/namespaces/game/timeback.ts
|
|
1475
1795
|
function createTimebackNamespace(client) {
|
|
1476
|
-
|
|
1796
|
+
const activityTracker = createTimebackActivityTracker(client);
|
|
1477
1797
|
const userCache = createTTLCache({
|
|
1478
1798
|
ttl: 5 * 60 * 1000,
|
|
1479
1799
|
keyPrefix: "game.timeback.user"
|
|
@@ -1558,70 +1878,16 @@ function createTimebackNamespace(client) {
|
|
|
1558
1878
|
}
|
|
1559
1879
|
};
|
|
1560
1880
|
},
|
|
1561
|
-
startActivity: (metadata) => {
|
|
1562
|
-
|
|
1563
|
-
startTime: Date.now(),
|
|
1564
|
-
metadata,
|
|
1565
|
-
pausedTime: 0,
|
|
1566
|
-
pauseStartTime: null
|
|
1567
|
-
};
|
|
1881
|
+
startActivity: (metadata, options) => {
|
|
1882
|
+
activityTracker.startActivity(metadata, options);
|
|
1568
1883
|
},
|
|
1569
1884
|
pauseActivity: () => {
|
|
1570
|
-
|
|
1571
|
-
throw new Error("No activity in progress. Call startActivity() before pauseActivity().");
|
|
1572
|
-
}
|
|
1573
|
-
if (currentActivity.pauseStartTime !== null) {
|
|
1574
|
-
throw new Error("Activity is already paused.");
|
|
1575
|
-
}
|
|
1576
|
-
currentActivity.pauseStartTime = Date.now();
|
|
1885
|
+
activityTracker.pauseActivity();
|
|
1577
1886
|
},
|
|
1578
1887
|
resumeActivity: () => {
|
|
1579
|
-
|
|
1580
|
-
throw new Error("No activity in progress. Call startActivity() before resumeActivity().");
|
|
1581
|
-
}
|
|
1582
|
-
if (currentActivity.pauseStartTime === null) {
|
|
1583
|
-
throw new Error("Activity is not paused.");
|
|
1584
|
-
}
|
|
1585
|
-
const pauseDuration = Date.now() - currentActivity.pauseStartTime;
|
|
1586
|
-
currentActivity.pausedTime += pauseDuration;
|
|
1587
|
-
currentActivity.pauseStartTime = null;
|
|
1888
|
+
activityTracker.resumeActivity();
|
|
1588
1889
|
},
|
|
1589
|
-
endActivity: async (data) =>
|
|
1590
|
-
if (!currentActivity) {
|
|
1591
|
-
throw new Error("No activity in progress. Call startActivity() before endActivity().");
|
|
1592
|
-
}
|
|
1593
|
-
if (currentActivity.pauseStartTime !== null) {
|
|
1594
|
-
const pauseDuration = Date.now() - currentActivity.pauseStartTime;
|
|
1595
|
-
currentActivity.pausedTime += pauseDuration;
|
|
1596
|
-
currentActivity.pauseStartTime = null;
|
|
1597
|
-
}
|
|
1598
|
-
const endTime = Date.now();
|
|
1599
|
-
const totalElapsed = endTime - currentActivity.startTime;
|
|
1600
|
-
const activeTime = totalElapsed - currentActivity.pausedTime;
|
|
1601
|
-
const durationSeconds = Math.floor(activeTime / 1000);
|
|
1602
|
-
const { correctQuestions, totalQuestions } = data;
|
|
1603
|
-
const request = {
|
|
1604
|
-
activityData: currentActivity.metadata,
|
|
1605
|
-
scoreData: {
|
|
1606
|
-
correctQuestions,
|
|
1607
|
-
totalQuestions
|
|
1608
|
-
},
|
|
1609
|
-
timingData: {
|
|
1610
|
-
durationSeconds
|
|
1611
|
-
},
|
|
1612
|
-
xpEarned: data.xpAwarded,
|
|
1613
|
-
masteredUnits: data.masteredUnits,
|
|
1614
|
-
extensions: data.extensions
|
|
1615
|
-
};
|
|
1616
|
-
try {
|
|
1617
|
-
const response = await client["requestGameBackend"](TIMEBACK_ROUTES.END_ACTIVITY, "POST", request);
|
|
1618
|
-
currentActivity = null;
|
|
1619
|
-
return response;
|
|
1620
|
-
} catch (error) {
|
|
1621
|
-
currentActivity = null;
|
|
1622
|
-
throw error;
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1890
|
+
endActivity: async (data) => activityTracker.endActivity(data)
|
|
1625
1891
|
};
|
|
1626
1892
|
}
|
|
1627
1893
|
// src/namespaces/platform/auth.ts
|
|
@@ -2570,6 +2836,16 @@ function createTimebackNamespace2(client) {
|
|
|
2570
2836
|
}),
|
|
2571
2837
|
toggleCompletion: (request) => client["request"]("/timeback/toggle-completion", "POST", {
|
|
2572
2838
|
body: request
|
|
2839
|
+
}),
|
|
2840
|
+
searchStudents: (gameId, courseId, query) => {
|
|
2841
|
+
const params = new URLSearchParams({ q: query });
|
|
2842
|
+
return client["request"](`/timeback/search-students/${gameId}/${courseId}?${params}`, "GET");
|
|
2843
|
+
},
|
|
2844
|
+
enrollStudent: (request) => client["request"]("/timeback/enroll-student", "POST", {
|
|
2845
|
+
body: request
|
|
2846
|
+
}),
|
|
2847
|
+
unenrollStudent: (request) => client["request"]("/timeback/unenroll-student", "POST", {
|
|
2848
|
+
body: request
|
|
2573
2849
|
})
|
|
2574
2850
|
}
|
|
2575
2851
|
};
|