@lessonkit/react 0.2.1 → 0.3.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/README.md +2 -1
- package/dist/index.cjs +42 -16
- package/dist/index.js +42 -16
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ export default function App() {
|
|
|
53
53
|
}
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
## API (0.
|
|
56
|
+
## API (0.3.0)
|
|
57
57
|
|
|
58
58
|
### Components
|
|
59
59
|
|
|
@@ -83,4 +83,5 @@ export default function App() {
|
|
|
83
83
|
for that lesson. Use stable `lessonId` values so completion and time-on-task telemetry stay consistent.
|
|
84
84
|
- If you omit `session.sessionId`, the provider reuses a tab-scoped id via `sessionStorage` so React
|
|
85
85
|
Strict Mode remounts do not split analytics sessions in development.
|
|
86
|
+
- Accessibility guidance lives in [`docs/ACCESSIBILITY.md`](../../docs/ACCESSIBILITY.md).
|
|
86
87
|
|
package/dist/index.cjs
CHANGED
|
@@ -52,27 +52,39 @@ function disposeTrackingClient(client) {
|
|
|
52
52
|
client?.flush?.();
|
|
53
53
|
client?.dispose?.();
|
|
54
54
|
}
|
|
55
|
+
function safeSessionStorageGetItem(key) {
|
|
56
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
57
|
+
try {
|
|
58
|
+
return sessionStorage.getItem(key);
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function safeSessionStorageSetItem(key, value) {
|
|
64
|
+
if (typeof sessionStorage === "undefined") return;
|
|
65
|
+
try {
|
|
66
|
+
sessionStorage.setItem(key, value);
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
}
|
|
55
70
|
function resolveSessionId(provided) {
|
|
56
71
|
if (provided) return provided;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return id;
|
|
63
|
-
}
|
|
64
|
-
return (0, import_core.createSessionId)();
|
|
72
|
+
const existing = safeSessionStorageGetItem(SESSION_STORAGE_KEY);
|
|
73
|
+
if (existing) return existing;
|
|
74
|
+
const id = (0, import_core.createSessionId)();
|
|
75
|
+
safeSessionStorageSetItem(SESSION_STORAGE_KEY, id);
|
|
76
|
+
return id;
|
|
65
77
|
}
|
|
66
78
|
function courseStartedStorageKey(sessionId, courseId) {
|
|
67
79
|
return `${COURSE_STARTED_PREFIX}${sessionId}:${courseId ?? ""}`;
|
|
68
80
|
}
|
|
69
81
|
function hasCourseStarted(sessionId, courseId) {
|
|
70
|
-
if (
|
|
71
|
-
return
|
|
82
|
+
if (!courseId) return false;
|
|
83
|
+
return safeSessionStorageGetItem(courseStartedStorageKey(sessionId, courseId)) === "1";
|
|
72
84
|
}
|
|
73
85
|
function markCourseStarted(sessionId, courseId) {
|
|
74
|
-
if (
|
|
75
|
-
|
|
86
|
+
if (!courseId) return;
|
|
87
|
+
safeSessionStorageSetItem(courseStartedStorageKey(sessionId, courseId), "1");
|
|
76
88
|
}
|
|
77
89
|
function createTrackingClientFromConfig(config) {
|
|
78
90
|
if (config.tracking?.enabled === false) {
|
|
@@ -102,6 +114,7 @@ function LessonkitProvider(props) {
|
|
|
102
114
|
courseIdRef.current = config.courseId;
|
|
103
115
|
const trackingRef = (0, import_react.useRef)((0, import_core.createTrackingClient)());
|
|
104
116
|
const [tracking, setTracking] = (0, import_react.useState)(() => trackingRef.current);
|
|
117
|
+
const courseStartedInProviderRef = (0, import_react.useRef)(false);
|
|
105
118
|
const trackingEnabled = config.tracking?.enabled;
|
|
106
119
|
const trackingSink = config.tracking?.sink;
|
|
107
120
|
const trackingBatchSink = config.tracking?.batchSink;
|
|
@@ -115,8 +128,13 @@ function LessonkitProvider(props) {
|
|
|
115
128
|
setTracking(next);
|
|
116
129
|
const sessionId = sessionIdRef.current;
|
|
117
130
|
const cid = courseIdRef.current;
|
|
118
|
-
|
|
119
|
-
|
|
131
|
+
const shouldEmitCourseStarted = cid ? !hasCourseStarted(sessionId, cid) : !courseStartedInProviderRef.current;
|
|
132
|
+
if (shouldEmitCourseStarted) {
|
|
133
|
+
if (cid) {
|
|
134
|
+
markCourseStarted(sessionId, cid);
|
|
135
|
+
} else {
|
|
136
|
+
courseStartedInProviderRef.current = true;
|
|
137
|
+
}
|
|
120
138
|
next.track({
|
|
121
139
|
name: "course_started",
|
|
122
140
|
timestamp: (0, import_core.nowIso)(),
|
|
@@ -150,8 +168,16 @@ function LessonkitProvider(props) {
|
|
|
150
168
|
xapiRef.current = next;
|
|
151
169
|
setXapi(next);
|
|
152
170
|
void (async () => {
|
|
153
|
-
if (prev)
|
|
154
|
-
|
|
171
|
+
if (prev) {
|
|
172
|
+
try {
|
|
173
|
+
await prev.flush();
|
|
174
|
+
} catch {
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
await next?.flush();
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
155
181
|
})();
|
|
156
182
|
return () => {
|
|
157
183
|
void prev?.flush();
|
package/dist/index.js
CHANGED
|
@@ -22,27 +22,39 @@ function disposeTrackingClient(client) {
|
|
|
22
22
|
client?.flush?.();
|
|
23
23
|
client?.dispose?.();
|
|
24
24
|
}
|
|
25
|
+
function safeSessionStorageGetItem(key) {
|
|
26
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
27
|
+
try {
|
|
28
|
+
return sessionStorage.getItem(key);
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function safeSessionStorageSetItem(key, value) {
|
|
34
|
+
if (typeof sessionStorage === "undefined") return;
|
|
35
|
+
try {
|
|
36
|
+
sessionStorage.setItem(key, value);
|
|
37
|
+
} catch {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
25
40
|
function resolveSessionId(provided) {
|
|
26
41
|
if (provided) return provided;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return id;
|
|
33
|
-
}
|
|
34
|
-
return createSessionId();
|
|
42
|
+
const existing = safeSessionStorageGetItem(SESSION_STORAGE_KEY);
|
|
43
|
+
if (existing) return existing;
|
|
44
|
+
const id = createSessionId();
|
|
45
|
+
safeSessionStorageSetItem(SESSION_STORAGE_KEY, id);
|
|
46
|
+
return id;
|
|
35
47
|
}
|
|
36
48
|
function courseStartedStorageKey(sessionId, courseId) {
|
|
37
49
|
return `${COURSE_STARTED_PREFIX}${sessionId}:${courseId ?? ""}`;
|
|
38
50
|
}
|
|
39
51
|
function hasCourseStarted(sessionId, courseId) {
|
|
40
|
-
if (
|
|
41
|
-
return
|
|
52
|
+
if (!courseId) return false;
|
|
53
|
+
return safeSessionStorageGetItem(courseStartedStorageKey(sessionId, courseId)) === "1";
|
|
42
54
|
}
|
|
43
55
|
function markCourseStarted(sessionId, courseId) {
|
|
44
|
-
if (
|
|
45
|
-
|
|
56
|
+
if (!courseId) return;
|
|
57
|
+
safeSessionStorageSetItem(courseStartedStorageKey(sessionId, courseId), "1");
|
|
46
58
|
}
|
|
47
59
|
function createTrackingClientFromConfig(config) {
|
|
48
60
|
if (config.tracking?.enabled === false) {
|
|
@@ -72,6 +84,7 @@ function LessonkitProvider(props) {
|
|
|
72
84
|
courseIdRef.current = config.courseId;
|
|
73
85
|
const trackingRef = useRef(createTrackingClient());
|
|
74
86
|
const [tracking, setTracking] = useState(() => trackingRef.current);
|
|
87
|
+
const courseStartedInProviderRef = useRef(false);
|
|
75
88
|
const trackingEnabled = config.tracking?.enabled;
|
|
76
89
|
const trackingSink = config.tracking?.sink;
|
|
77
90
|
const trackingBatchSink = config.tracking?.batchSink;
|
|
@@ -85,8 +98,13 @@ function LessonkitProvider(props) {
|
|
|
85
98
|
setTracking(next);
|
|
86
99
|
const sessionId = sessionIdRef.current;
|
|
87
100
|
const cid = courseIdRef.current;
|
|
88
|
-
|
|
89
|
-
|
|
101
|
+
const shouldEmitCourseStarted = cid ? !hasCourseStarted(sessionId, cid) : !courseStartedInProviderRef.current;
|
|
102
|
+
if (shouldEmitCourseStarted) {
|
|
103
|
+
if (cid) {
|
|
104
|
+
markCourseStarted(sessionId, cid);
|
|
105
|
+
} else {
|
|
106
|
+
courseStartedInProviderRef.current = true;
|
|
107
|
+
}
|
|
90
108
|
next.track({
|
|
91
109
|
name: "course_started",
|
|
92
110
|
timestamp: nowIso(),
|
|
@@ -120,8 +138,16 @@ function LessonkitProvider(props) {
|
|
|
120
138
|
xapiRef.current = next;
|
|
121
139
|
setXapi(next);
|
|
122
140
|
void (async () => {
|
|
123
|
-
if (prev)
|
|
124
|
-
|
|
141
|
+
if (prev) {
|
|
142
|
+
try {
|
|
143
|
+
await prev.flush();
|
|
144
|
+
} catch {
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
await next?.flush();
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
125
151
|
})();
|
|
126
152
|
return () => {
|
|
127
153
|
void prev?.flush();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lessonkit/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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,9 +50,9 @@
|
|
|
50
50
|
"react-dom": ">=18"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@lessonkit/accessibility": "0.
|
|
54
|
-
"@lessonkit/core": "0.
|
|
55
|
-
"@lessonkit/xapi": "0.
|
|
53
|
+
"@lessonkit/accessibility": "0.3.0",
|
|
54
|
+
"@lessonkit/core": "0.3.0",
|
|
55
|
+
"@lessonkit/xapi": "0.3.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@testing-library/react": "^16.3.0",
|