@lessonkit/react 0.3.0 → 0.3.1
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 +58 -39
- package/dist/index.js +53 -34
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -42,55 +42,74 @@ var import_accessibility = require("@lessonkit/accessibility");
|
|
|
42
42
|
|
|
43
43
|
// src/context.tsx
|
|
44
44
|
var import_react = require("react");
|
|
45
|
-
var
|
|
45
|
+
var import_core2 = require("@lessonkit/core");
|
|
46
46
|
var import_xapi = require("@lessonkit/xapi");
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
}
|
|
47
|
+
|
|
48
|
+
// src/runtime/ports.ts
|
|
49
|
+
function createNoopStorage() {
|
|
50
|
+
return {
|
|
51
|
+
getItem: () => null,
|
|
52
|
+
setItem: () => {
|
|
53
|
+
}
|
|
54
|
+
};
|
|
62
55
|
}
|
|
63
|
-
function
|
|
64
|
-
if (typeof sessionStorage === "undefined") return;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
function createSessionStoragePort() {
|
|
57
|
+
if (typeof sessionStorage === "undefined") return createNoopStorage();
|
|
58
|
+
return {
|
|
59
|
+
getItem: (key) => {
|
|
60
|
+
try {
|
|
61
|
+
return sessionStorage.getItem(key);
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
setItem: (key, value) => {
|
|
67
|
+
try {
|
|
68
|
+
sessionStorage.setItem(key, value);
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
69
73
|
}
|
|
70
|
-
|
|
74
|
+
|
|
75
|
+
// src/runtime/session.ts
|
|
76
|
+
var import_core = require("@lessonkit/core");
|
|
77
|
+
var SESSION_STORAGE_KEY = "lessonkit:sessionId";
|
|
78
|
+
var COURSE_STARTED_PREFIX = "lessonkit:course_started:";
|
|
79
|
+
function resolveSessionId(storage, provided) {
|
|
71
80
|
if (provided) return provided;
|
|
72
|
-
const existing =
|
|
81
|
+
const existing = storage.getItem(SESSION_STORAGE_KEY);
|
|
73
82
|
if (existing) return existing;
|
|
74
83
|
const id = (0, import_core.createSessionId)();
|
|
75
|
-
|
|
84
|
+
storage.setItem(SESSION_STORAGE_KEY, id);
|
|
76
85
|
return id;
|
|
77
86
|
}
|
|
78
87
|
function courseStartedStorageKey(sessionId, courseId) {
|
|
79
88
|
return `${COURSE_STARTED_PREFIX}${sessionId}:${courseId ?? ""}`;
|
|
80
89
|
}
|
|
81
|
-
function hasCourseStarted(sessionId, courseId) {
|
|
90
|
+
function hasCourseStarted(storage, sessionId, courseId) {
|
|
82
91
|
if (!courseId) return false;
|
|
83
|
-
return
|
|
92
|
+
return storage.getItem(courseStartedStorageKey(sessionId, courseId)) === "1";
|
|
84
93
|
}
|
|
85
|
-
function markCourseStarted(sessionId, courseId) {
|
|
94
|
+
function markCourseStarted(storage, sessionId, courseId) {
|
|
86
95
|
if (!courseId) return;
|
|
87
|
-
|
|
96
|
+
storage.setItem(courseStartedStorageKey(sessionId, courseId), "1");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/context.tsx
|
|
100
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
101
|
+
var LessonkitContext = (0, import_react.createContext)(null);
|
|
102
|
+
var useIsoLayoutEffect = typeof window !== "undefined" ? import_react.useLayoutEffect : import_react.useEffect;
|
|
103
|
+
function disposeTrackingClient(client) {
|
|
104
|
+
client?.flush?.();
|
|
105
|
+
client?.dispose?.();
|
|
88
106
|
}
|
|
107
|
+
var defaultStorage = createSessionStoragePort();
|
|
89
108
|
function createTrackingClientFromConfig(config) {
|
|
90
109
|
if (config.tracking?.enabled === false) {
|
|
91
|
-
return (0,
|
|
110
|
+
return (0, import_core2.createTrackingClient)();
|
|
92
111
|
}
|
|
93
|
-
return (0,
|
|
112
|
+
return (0, import_core2.createTrackingClient)({
|
|
94
113
|
sink: config.tracking?.sink,
|
|
95
114
|
batchSink: config.tracking?.batchSink,
|
|
96
115
|
batch: config.tracking?.batch
|
|
@@ -104,7 +123,7 @@ function createXapiClientFromConfig(config, queue) {
|
|
|
104
123
|
}
|
|
105
124
|
function LessonkitProvider(props) {
|
|
106
125
|
const config = props.config ?? {};
|
|
107
|
-
const sessionIdRef = (0, import_react.useRef)(resolveSessionId(config.session?.sessionId));
|
|
126
|
+
const sessionIdRef = (0, import_react.useRef)(resolveSessionId(defaultStorage, config.session?.sessionId));
|
|
108
127
|
if (config.session?.sessionId) sessionIdRef.current = config.session.sessionId;
|
|
109
128
|
const attemptIdRef = (0, import_react.useRef)(config.session?.attemptId);
|
|
110
129
|
const userRef = (0, import_react.useRef)(config.session?.user);
|
|
@@ -112,7 +131,7 @@ function LessonkitProvider(props) {
|
|
|
112
131
|
userRef.current = config.session?.user;
|
|
113
132
|
const courseIdRef = (0, import_react.useRef)(config.courseId);
|
|
114
133
|
courseIdRef.current = config.courseId;
|
|
115
|
-
const trackingRef = (0, import_react.useRef)((0,
|
|
134
|
+
const trackingRef = (0, import_react.useRef)((0, import_core2.createTrackingClient)());
|
|
116
135
|
const [tracking, setTracking] = (0, import_react.useState)(() => trackingRef.current);
|
|
117
136
|
const courseStartedInProviderRef = (0, import_react.useRef)(false);
|
|
118
137
|
const trackingEnabled = config.tracking?.enabled;
|
|
@@ -121,23 +140,23 @@ function LessonkitProvider(props) {
|
|
|
121
140
|
const batchEnabled = config.tracking?.batch?.enabled;
|
|
122
141
|
const batchFlushIntervalMs = config.tracking?.batch?.flushIntervalMs;
|
|
123
142
|
const batchMaxBatchSize = config.tracking?.batch?.maxBatchSize;
|
|
124
|
-
(
|
|
143
|
+
useIsoLayoutEffect(() => {
|
|
125
144
|
const prev = trackingRef.current;
|
|
126
145
|
const next = createTrackingClientFromConfig(config);
|
|
127
146
|
trackingRef.current = next;
|
|
128
147
|
setTracking(next);
|
|
129
148
|
const sessionId = sessionIdRef.current;
|
|
130
149
|
const cid = courseIdRef.current;
|
|
131
|
-
const shouldEmitCourseStarted = cid ? !hasCourseStarted(sessionId, cid) : !courseStartedInProviderRef.current;
|
|
150
|
+
const shouldEmitCourseStarted = cid ? !hasCourseStarted(defaultStorage, sessionId, cid) : !courseStartedInProviderRef.current;
|
|
132
151
|
if (shouldEmitCourseStarted) {
|
|
133
152
|
if (cid) {
|
|
134
|
-
markCourseStarted(sessionId, cid);
|
|
153
|
+
markCourseStarted(defaultStorage, sessionId, cid);
|
|
135
154
|
} else {
|
|
136
155
|
courseStartedInProviderRef.current = true;
|
|
137
156
|
}
|
|
138
157
|
next.track({
|
|
139
158
|
name: "course_started",
|
|
140
|
-
timestamp: (0,
|
|
159
|
+
timestamp: (0, import_core2.nowIso)(),
|
|
141
160
|
courseId: cid,
|
|
142
161
|
sessionId,
|
|
143
162
|
attemptId: attemptIdRef.current,
|
|
@@ -162,7 +181,7 @@ function LessonkitProvider(props) {
|
|
|
162
181
|
const xapiClient = config.xapi?.client;
|
|
163
182
|
const xapiTransport = config.xapi?.transport;
|
|
164
183
|
const courseId = config.courseId;
|
|
165
|
-
(
|
|
184
|
+
useIsoLayoutEffect(() => {
|
|
166
185
|
const prev = xapiRef.current;
|
|
167
186
|
const next = createXapiClientFromConfig(config, xapiQueueRef.current);
|
|
168
187
|
xapiRef.current = next;
|
|
@@ -197,7 +216,7 @@ function LessonkitProvider(props) {
|
|
|
197
216
|
(name, data, opts) => {
|
|
198
217
|
trackingRef.current?.track({
|
|
199
218
|
name,
|
|
200
|
-
timestamp: (0,
|
|
219
|
+
timestamp: (0, import_core2.nowIso)(),
|
|
201
220
|
courseId: courseIdRef.current,
|
|
202
221
|
lessonId: opts?.lessonId ?? activeLessonIdRef.current,
|
|
203
222
|
sessionId: sessionIdRef.current,
|
package/dist/index.js
CHANGED
|
@@ -12,50 +12,69 @@ import {
|
|
|
12
12
|
useRef,
|
|
13
13
|
useState
|
|
14
14
|
} from "react";
|
|
15
|
-
import {
|
|
15
|
+
import { createTrackingClient, nowIso } from "@lessonkit/core";
|
|
16
16
|
import { createInMemoryXAPIQueue, createXAPIClient } from "@lessonkit/xapi";
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
}
|
|
17
|
+
|
|
18
|
+
// src/runtime/ports.ts
|
|
19
|
+
function createNoopStorage() {
|
|
20
|
+
return {
|
|
21
|
+
getItem: () => null,
|
|
22
|
+
setItem: () => {
|
|
23
|
+
}
|
|
24
|
+
};
|
|
32
25
|
}
|
|
33
|
-
function
|
|
34
|
-
if (typeof sessionStorage === "undefined") return;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
function createSessionStoragePort() {
|
|
27
|
+
if (typeof sessionStorage === "undefined") return createNoopStorage();
|
|
28
|
+
return {
|
|
29
|
+
getItem: (key) => {
|
|
30
|
+
try {
|
|
31
|
+
return sessionStorage.getItem(key);
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
setItem: (key, value) => {
|
|
37
|
+
try {
|
|
38
|
+
sessionStorage.setItem(key, value);
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
39
43
|
}
|
|
40
|
-
|
|
44
|
+
|
|
45
|
+
// src/runtime/session.ts
|
|
46
|
+
import { createSessionId } from "@lessonkit/core";
|
|
47
|
+
var SESSION_STORAGE_KEY = "lessonkit:sessionId";
|
|
48
|
+
var COURSE_STARTED_PREFIX = "lessonkit:course_started:";
|
|
49
|
+
function resolveSessionId(storage, provided) {
|
|
41
50
|
if (provided) return provided;
|
|
42
|
-
const existing =
|
|
51
|
+
const existing = storage.getItem(SESSION_STORAGE_KEY);
|
|
43
52
|
if (existing) return existing;
|
|
44
53
|
const id = createSessionId();
|
|
45
|
-
|
|
54
|
+
storage.setItem(SESSION_STORAGE_KEY, id);
|
|
46
55
|
return id;
|
|
47
56
|
}
|
|
48
57
|
function courseStartedStorageKey(sessionId, courseId) {
|
|
49
58
|
return `${COURSE_STARTED_PREFIX}${sessionId}:${courseId ?? ""}`;
|
|
50
59
|
}
|
|
51
|
-
function hasCourseStarted(sessionId, courseId) {
|
|
60
|
+
function hasCourseStarted(storage, sessionId, courseId) {
|
|
52
61
|
if (!courseId) return false;
|
|
53
|
-
return
|
|
62
|
+
return storage.getItem(courseStartedStorageKey(sessionId, courseId)) === "1";
|
|
54
63
|
}
|
|
55
|
-
function markCourseStarted(sessionId, courseId) {
|
|
64
|
+
function markCourseStarted(storage, sessionId, courseId) {
|
|
56
65
|
if (!courseId) return;
|
|
57
|
-
|
|
66
|
+
storage.setItem(courseStartedStorageKey(sessionId, courseId), "1");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/context.tsx
|
|
70
|
+
import { jsx } from "react/jsx-runtime";
|
|
71
|
+
var LessonkitContext = createContext(null);
|
|
72
|
+
var useIsoLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
73
|
+
function disposeTrackingClient(client) {
|
|
74
|
+
client?.flush?.();
|
|
75
|
+
client?.dispose?.();
|
|
58
76
|
}
|
|
77
|
+
var defaultStorage = createSessionStoragePort();
|
|
59
78
|
function createTrackingClientFromConfig(config) {
|
|
60
79
|
if (config.tracking?.enabled === false) {
|
|
61
80
|
return createTrackingClient();
|
|
@@ -74,7 +93,7 @@ function createXapiClientFromConfig(config, queue) {
|
|
|
74
93
|
}
|
|
75
94
|
function LessonkitProvider(props) {
|
|
76
95
|
const config = props.config ?? {};
|
|
77
|
-
const sessionIdRef = useRef(resolveSessionId(config.session?.sessionId));
|
|
96
|
+
const sessionIdRef = useRef(resolveSessionId(defaultStorage, config.session?.sessionId));
|
|
78
97
|
if (config.session?.sessionId) sessionIdRef.current = config.session.sessionId;
|
|
79
98
|
const attemptIdRef = useRef(config.session?.attemptId);
|
|
80
99
|
const userRef = useRef(config.session?.user);
|
|
@@ -91,17 +110,17 @@ function LessonkitProvider(props) {
|
|
|
91
110
|
const batchEnabled = config.tracking?.batch?.enabled;
|
|
92
111
|
const batchFlushIntervalMs = config.tracking?.batch?.flushIntervalMs;
|
|
93
112
|
const batchMaxBatchSize = config.tracking?.batch?.maxBatchSize;
|
|
94
|
-
|
|
113
|
+
useIsoLayoutEffect(() => {
|
|
95
114
|
const prev = trackingRef.current;
|
|
96
115
|
const next = createTrackingClientFromConfig(config);
|
|
97
116
|
trackingRef.current = next;
|
|
98
117
|
setTracking(next);
|
|
99
118
|
const sessionId = sessionIdRef.current;
|
|
100
119
|
const cid = courseIdRef.current;
|
|
101
|
-
const shouldEmitCourseStarted = cid ? !hasCourseStarted(sessionId, cid) : !courseStartedInProviderRef.current;
|
|
120
|
+
const shouldEmitCourseStarted = cid ? !hasCourseStarted(defaultStorage, sessionId, cid) : !courseStartedInProviderRef.current;
|
|
102
121
|
if (shouldEmitCourseStarted) {
|
|
103
122
|
if (cid) {
|
|
104
|
-
markCourseStarted(sessionId, cid);
|
|
123
|
+
markCourseStarted(defaultStorage, sessionId, cid);
|
|
105
124
|
} else {
|
|
106
125
|
courseStartedInProviderRef.current = true;
|
|
107
126
|
}
|
|
@@ -132,7 +151,7 @@ function LessonkitProvider(props) {
|
|
|
132
151
|
const xapiClient = config.xapi?.client;
|
|
133
152
|
const xapiTransport = config.xapi?.transport;
|
|
134
153
|
const courseId = config.courseId;
|
|
135
|
-
|
|
154
|
+
useIsoLayoutEffect(() => {
|
|
136
155
|
const prev = xapiRef.current;
|
|
137
156
|
const next = createXapiClientFromConfig(config, xapiQueueRef.current);
|
|
138
157
|
xapiRef.current = next;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lessonkit/react",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
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.3.
|
|
54
|
-
"@lessonkit/core": "0.3.
|
|
55
|
-
"@lessonkit/xapi": "0.3.
|
|
53
|
+
"@lessonkit/accessibility": "0.3.1",
|
|
54
|
+
"@lessonkit/core": "0.3.1",
|
|
55
|
+
"@lessonkit/xapi": "0.3.1"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@testing-library/react": "^16.3.0",
|