@lessonkit/react 0.6.0 → 0.7.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/index.cjs +33 -18
- package/dist/index.js +33 -18
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -250,6 +250,9 @@ function createProgressController() {
|
|
|
250
250
|
completeLesson: (lessonId, completedAtMs) => {
|
|
251
251
|
if (completedLessonIds.has(lessonId)) return { didComplete: false };
|
|
252
252
|
completedLessonIds = new Set(completedLessonIds).add(lessonId);
|
|
253
|
+
if (activeLessonId === lessonId) {
|
|
254
|
+
activeLessonId = void 0;
|
|
255
|
+
}
|
|
253
256
|
const startedAt = lessonStartTimes.get(lessonId);
|
|
254
257
|
lessonStartTimes.delete(lessonId);
|
|
255
258
|
const durationMs = typeof startedAt === "number" ? Math.max(0, completedAtMs - startedAt) : void 0;
|
|
@@ -416,7 +419,9 @@ function LessonkitProvider(props) {
|
|
|
416
419
|
);
|
|
417
420
|
}
|
|
418
421
|
return () => {
|
|
419
|
-
|
|
422
|
+
if (prev !== trackingRef.current) {
|
|
423
|
+
disposeTrackingClient(prev);
|
|
424
|
+
}
|
|
420
425
|
};
|
|
421
426
|
}, [
|
|
422
427
|
trackingEnabled,
|
|
@@ -453,9 +458,15 @@ function LessonkitProvider(props) {
|
|
|
453
458
|
const prevCourseIdRef = (0, import_react.useRef)(config.courseId);
|
|
454
459
|
(0, import_react.useEffect)(() => {
|
|
455
460
|
if (prevCourseIdRef.current === config.courseId) return;
|
|
461
|
+
const previousActiveLesson = progressRef.current.getState().activeLessonId;
|
|
456
462
|
prevCourseIdRef.current = config.courseId;
|
|
457
463
|
progressRef.current = createProgressController();
|
|
458
464
|
syncProgress();
|
|
465
|
+
if (previousActiveLesson) {
|
|
466
|
+
progressRef.current.setActiveLesson(previousActiveLesson, Date.now());
|
|
467
|
+
syncProgress();
|
|
468
|
+
track("lesson_started", { lessonId: previousActiveLesson }, { lessonId: previousActiveLesson });
|
|
469
|
+
}
|
|
459
470
|
const sessionId = sessionIdRef.current;
|
|
460
471
|
const cid = config.courseId;
|
|
461
472
|
if (!hasCourseStarted(defaultStorage, sessionId, cid)) {
|
|
@@ -473,13 +484,7 @@ function LessonkitProvider(props) {
|
|
|
473
484
|
{ lxpackBridge: lxpackBridgeModeRef.current }
|
|
474
485
|
);
|
|
475
486
|
}
|
|
476
|
-
}, [config.courseId, syncProgress]);
|
|
477
|
-
(0, import_react.useEffect)(() => {
|
|
478
|
-
return () => {
|
|
479
|
-
trackingRef.current?.flush?.();
|
|
480
|
-
void xapiRef.current?.flush();
|
|
481
|
-
};
|
|
482
|
-
}, []);
|
|
487
|
+
}, [config.courseId, syncProgress, track]);
|
|
483
488
|
const emitLessonCompleted = (0, import_react.useCallback)(
|
|
484
489
|
(lessonId, durationMs) => {
|
|
485
490
|
track("lesson_completed", { lessonId, durationMs }, { lessonId });
|
|
@@ -495,9 +500,22 @@ function LessonkitProvider(props) {
|
|
|
495
500
|
if (!result.didComplete) return;
|
|
496
501
|
syncProgress();
|
|
497
502
|
emitLessonCompleted(lessonId, result.durationMs);
|
|
503
|
+
void trackingRef.current?.flush?.();
|
|
498
504
|
},
|
|
499
505
|
[syncProgress, emitLessonCompleted]
|
|
500
506
|
);
|
|
507
|
+
(0, import_react.useEffect)(() => {
|
|
508
|
+
return () => {
|
|
509
|
+
const client = trackingRef.current;
|
|
510
|
+
void xapiRef.current?.flush();
|
|
511
|
+
setTimeout(() => {
|
|
512
|
+
client?.flush?.();
|
|
513
|
+
setTimeout(() => {
|
|
514
|
+
client?.dispose?.();
|
|
515
|
+
}, 0);
|
|
516
|
+
}, 0);
|
|
517
|
+
};
|
|
518
|
+
}, []);
|
|
501
519
|
const setActiveLesson = (0, import_react.useCallback)(
|
|
502
520
|
(lessonId) => {
|
|
503
521
|
const current = progressRef.current.getState();
|
|
@@ -620,24 +638,21 @@ function Course(props) {
|
|
|
620
638
|
}
|
|
621
639
|
function Lesson(props) {
|
|
622
640
|
warnInvalidComponentId(props.lessonId, "lessonId");
|
|
623
|
-
const { setActiveLesson } = useLessonkit();
|
|
641
|
+
const { setActiveLesson, config } = useLessonkit();
|
|
624
642
|
const { completeLesson } = useCompletion();
|
|
625
643
|
const id = props.lessonId;
|
|
626
|
-
const
|
|
644
|
+
const lessonMountGenerationRef = (0, import_react3.useRef)(0);
|
|
627
645
|
(0, import_react3.useEffect)(() => {
|
|
628
|
-
|
|
629
|
-
clearTimeout(pendingCompleteRef.current);
|
|
630
|
-
pendingCompleteRef.current = null;
|
|
631
|
-
}
|
|
646
|
+
const generation = ++lessonMountGenerationRef.current;
|
|
632
647
|
setActiveLesson(id);
|
|
633
648
|
return () => {
|
|
634
649
|
const lessonId = id;
|
|
635
|
-
|
|
636
|
-
|
|
650
|
+
queueMicrotask(() => {
|
|
651
|
+
if (lessonMountGenerationRef.current !== generation) return;
|
|
637
652
|
completeLesson(lessonId);
|
|
638
|
-
}
|
|
653
|
+
});
|
|
639
654
|
};
|
|
640
|
-
}, [id, setActiveLesson, completeLesson]);
|
|
655
|
+
}, [id, config.courseId, setActiveLesson, completeLesson]);
|
|
641
656
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { "aria-label": props.title, children: [
|
|
642
657
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: props.title }),
|
|
643
658
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: props.children })
|
package/dist/index.js
CHANGED
|
@@ -211,6 +211,9 @@ function createProgressController() {
|
|
|
211
211
|
completeLesson: (lessonId, completedAtMs) => {
|
|
212
212
|
if (completedLessonIds.has(lessonId)) return { didComplete: false };
|
|
213
213
|
completedLessonIds = new Set(completedLessonIds).add(lessonId);
|
|
214
|
+
if (activeLessonId === lessonId) {
|
|
215
|
+
activeLessonId = void 0;
|
|
216
|
+
}
|
|
214
217
|
const startedAt = lessonStartTimes.get(lessonId);
|
|
215
218
|
lessonStartTimes.delete(lessonId);
|
|
216
219
|
const durationMs = typeof startedAt === "number" ? Math.max(0, completedAtMs - startedAt) : void 0;
|
|
@@ -377,7 +380,9 @@ function LessonkitProvider(props) {
|
|
|
377
380
|
);
|
|
378
381
|
}
|
|
379
382
|
return () => {
|
|
380
|
-
|
|
383
|
+
if (prev !== trackingRef.current) {
|
|
384
|
+
disposeTrackingClient(prev);
|
|
385
|
+
}
|
|
381
386
|
};
|
|
382
387
|
}, [
|
|
383
388
|
trackingEnabled,
|
|
@@ -414,9 +419,15 @@ function LessonkitProvider(props) {
|
|
|
414
419
|
const prevCourseIdRef = useRef(config.courseId);
|
|
415
420
|
useEffect(() => {
|
|
416
421
|
if (prevCourseIdRef.current === config.courseId) return;
|
|
422
|
+
const previousActiveLesson = progressRef.current.getState().activeLessonId;
|
|
417
423
|
prevCourseIdRef.current = config.courseId;
|
|
418
424
|
progressRef.current = createProgressController();
|
|
419
425
|
syncProgress();
|
|
426
|
+
if (previousActiveLesson) {
|
|
427
|
+
progressRef.current.setActiveLesson(previousActiveLesson, Date.now());
|
|
428
|
+
syncProgress();
|
|
429
|
+
track("lesson_started", { lessonId: previousActiveLesson }, { lessonId: previousActiveLesson });
|
|
430
|
+
}
|
|
420
431
|
const sessionId = sessionIdRef.current;
|
|
421
432
|
const cid = config.courseId;
|
|
422
433
|
if (!hasCourseStarted(defaultStorage, sessionId, cid)) {
|
|
@@ -434,13 +445,7 @@ function LessonkitProvider(props) {
|
|
|
434
445
|
{ lxpackBridge: lxpackBridgeModeRef.current }
|
|
435
446
|
);
|
|
436
447
|
}
|
|
437
|
-
}, [config.courseId, syncProgress]);
|
|
438
|
-
useEffect(() => {
|
|
439
|
-
return () => {
|
|
440
|
-
trackingRef.current?.flush?.();
|
|
441
|
-
void xapiRef.current?.flush();
|
|
442
|
-
};
|
|
443
|
-
}, []);
|
|
448
|
+
}, [config.courseId, syncProgress, track]);
|
|
444
449
|
const emitLessonCompleted = useCallback(
|
|
445
450
|
(lessonId, durationMs) => {
|
|
446
451
|
track("lesson_completed", { lessonId, durationMs }, { lessonId });
|
|
@@ -456,9 +461,22 @@ function LessonkitProvider(props) {
|
|
|
456
461
|
if (!result.didComplete) return;
|
|
457
462
|
syncProgress();
|
|
458
463
|
emitLessonCompleted(lessonId, result.durationMs);
|
|
464
|
+
void trackingRef.current?.flush?.();
|
|
459
465
|
},
|
|
460
466
|
[syncProgress, emitLessonCompleted]
|
|
461
467
|
);
|
|
468
|
+
useEffect(() => {
|
|
469
|
+
return () => {
|
|
470
|
+
const client = trackingRef.current;
|
|
471
|
+
void xapiRef.current?.flush();
|
|
472
|
+
setTimeout(() => {
|
|
473
|
+
client?.flush?.();
|
|
474
|
+
setTimeout(() => {
|
|
475
|
+
client?.dispose?.();
|
|
476
|
+
}, 0);
|
|
477
|
+
}, 0);
|
|
478
|
+
};
|
|
479
|
+
}, []);
|
|
462
480
|
const setActiveLesson = useCallback(
|
|
463
481
|
(lessonId) => {
|
|
464
482
|
const current = progressRef.current.getState();
|
|
@@ -581,24 +599,21 @@ function Course(props) {
|
|
|
581
599
|
}
|
|
582
600
|
function Lesson(props) {
|
|
583
601
|
warnInvalidComponentId(props.lessonId, "lessonId");
|
|
584
|
-
const { setActiveLesson } = useLessonkit();
|
|
602
|
+
const { setActiveLesson, config } = useLessonkit();
|
|
585
603
|
const { completeLesson } = useCompletion();
|
|
586
604
|
const id = props.lessonId;
|
|
587
|
-
const
|
|
605
|
+
const lessonMountGenerationRef = useRef2(0);
|
|
588
606
|
useEffect2(() => {
|
|
589
|
-
|
|
590
|
-
clearTimeout(pendingCompleteRef.current);
|
|
591
|
-
pendingCompleteRef.current = null;
|
|
592
|
-
}
|
|
607
|
+
const generation = ++lessonMountGenerationRef.current;
|
|
593
608
|
setActiveLesson(id);
|
|
594
609
|
return () => {
|
|
595
610
|
const lessonId = id;
|
|
596
|
-
|
|
597
|
-
|
|
611
|
+
queueMicrotask(() => {
|
|
612
|
+
if (lessonMountGenerationRef.current !== generation) return;
|
|
598
613
|
completeLesson(lessonId);
|
|
599
|
-
}
|
|
614
|
+
});
|
|
600
615
|
};
|
|
601
|
-
}, [id, setActiveLesson, completeLesson]);
|
|
616
|
+
}, [id, config.courseId, setActiveLesson, completeLesson]);
|
|
602
617
|
return /* @__PURE__ */ jsxs("article", { "aria-label": props.title, children: [
|
|
603
618
|
/* @__PURE__ */ jsx2("h2", { children: props.title }),
|
|
604
619
|
/* @__PURE__ */ jsx2("div", { children: props.children })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lessonkit/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "React components and hooks for building learning experiences with LessonKit.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
"react-dom": ">=18"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@lessonkit/accessibility": "0.
|
|
54
|
-
"@lessonkit/core": "0.
|
|
55
|
-
"@lessonkit/lxpack": "0.
|
|
56
|
-
"@lessonkit/themes": "0.
|
|
57
|
-
"@lessonkit/xapi": "0.
|
|
53
|
+
"@lessonkit/accessibility": "0.7.0",
|
|
54
|
+
"@lessonkit/core": "0.7.0",
|
|
55
|
+
"@lessonkit/lxpack": "0.7.0",
|
|
56
|
+
"@lessonkit/themes": "0.7.0",
|
|
57
|
+
"@lessonkit/xapi": "0.7.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@testing-library/react": "^16.3.0",
|