@speakableio/core 0.1.99 → 0.1.101

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/utils.js ADDED
@@ -0,0 +1,340 @@
1
+ // src/domains/assignment/utils/check-assignment-availability.ts
2
+ import dayjs from "dayjs";
3
+ var checkAssignmentAvailability = (scheduledTime) => {
4
+ if (!scheduledTime) return true;
5
+ const scheduledDate = typeof scheduledTime === "string" ? dayjs(scheduledTime) : dayjs(scheduledTime.toDate());
6
+ if (!scheduledDate.isValid()) return true;
7
+ return dayjs().isAfter(scheduledDate);
8
+ };
9
+
10
+ // src/lib/firebase/api.ts
11
+ var FirebaseAPI = class _FirebaseAPI {
12
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
13
+ constructor() {
14
+ this.config = null;
15
+ }
16
+ static getInstance() {
17
+ if (!_FirebaseAPI.instance) {
18
+ _FirebaseAPI.instance = new _FirebaseAPI();
19
+ }
20
+ return _FirebaseAPI.instance;
21
+ }
22
+ initialize(config) {
23
+ this.config = config;
24
+ }
25
+ get db() {
26
+ if (!this.config) throw new Error("Firebase API not initialized");
27
+ return this.config.db;
28
+ }
29
+ get helpers() {
30
+ if (!this.config) throw new Error("Firebase API not initialized");
31
+ return this.config.helpers;
32
+ }
33
+ get httpsCallable() {
34
+ var _a;
35
+ return (_a = this.config) == null ? void 0 : _a.httpsCallable;
36
+ }
37
+ logEvent(name, data) {
38
+ var _a;
39
+ (_a = this.config) == null ? void 0 : _a.logEvent(name, data);
40
+ }
41
+ accessQueryConstraints() {
42
+ const { query, orderBy, limit, startAt, startAfter, endAt, endBefore, where, increment } = this.helpers;
43
+ return {
44
+ query,
45
+ orderBy,
46
+ limit,
47
+ startAt,
48
+ startAfter,
49
+ endAt,
50
+ endBefore,
51
+ where,
52
+ increment
53
+ };
54
+ }
55
+ accessHelpers() {
56
+ const { doc, collection, writeBatch, serverTimestamp, setDoc } = this.helpers;
57
+ return {
58
+ doc: (path) => doc(this.db, path),
59
+ collection: (path) => collection(this.db, path),
60
+ writeBatch: () => writeBatch(this.db),
61
+ serverTimestamp,
62
+ setDoc
63
+ };
64
+ }
65
+ async getDoc(path) {
66
+ const { getDoc, doc } = this.helpers;
67
+ const docRef = doc(this.db, path);
68
+ const docSnap = await getDoc(docRef);
69
+ const data = docSnap.exists() ? {
70
+ ...docSnap.data(),
71
+ id: docSnap.id
72
+ } : null;
73
+ return {
74
+ id: docSnap.id,
75
+ data
76
+ };
77
+ }
78
+ async getDocs(path, ...queryConstraints) {
79
+ const { getDocs, query, collection } = this.helpers;
80
+ const collectionRef = collection(this.db, path);
81
+ const q = queryConstraints.length > 0 ? query(collectionRef, ...queryConstraints) : collectionRef;
82
+ const querySnapshot = await getDocs(q);
83
+ const data = querySnapshot.docs.map((doc) => ({
84
+ data: doc.data(),
85
+ id: doc.id
86
+ }));
87
+ return {
88
+ data,
89
+ querySnapshot,
90
+ empty: querySnapshot.empty
91
+ };
92
+ }
93
+ async addDoc(path, data) {
94
+ const { addDoc, collection } = this.helpers;
95
+ const collectionRef = collection(this.db, path);
96
+ const docRef = await addDoc(collectionRef, data);
97
+ return {
98
+ ...data,
99
+ id: docRef.id
100
+ };
101
+ }
102
+ async setDoc(path, data, options = {}) {
103
+ const { setDoc, doc } = this.helpers;
104
+ const docRef = doc(this.db, path);
105
+ await setDoc(docRef, data, options);
106
+ }
107
+ async updateDoc(path, data) {
108
+ const { updateDoc, doc } = this.helpers;
109
+ const docRef = doc(this.db, path);
110
+ await updateDoc(docRef, data);
111
+ }
112
+ async deleteDoc(path) {
113
+ const { deleteDoc, doc } = this.helpers;
114
+ const docRef = doc(this.db, path);
115
+ await deleteDoc(docRef);
116
+ }
117
+ async runTransaction(updateFunction) {
118
+ const { runTransaction } = this.helpers;
119
+ return runTransaction(this.db, updateFunction);
120
+ }
121
+ async runBatch(operations) {
122
+ const { writeBatch } = this.helpers;
123
+ const batch = writeBatch(this.db);
124
+ await Promise.all(operations.map((op) => op()));
125
+ await batch.commit();
126
+ }
127
+ writeBatch() {
128
+ const { writeBatch } = this.helpers;
129
+ const batch = writeBatch(this.db);
130
+ return batch;
131
+ }
132
+ };
133
+ var api = FirebaseAPI.getInstance();
134
+
135
+ // src/domains/assignment/utils/create-default-score.ts
136
+ var defaultScore = (props) => {
137
+ const { serverTimestamp } = api.accessHelpers();
138
+ const score = {
139
+ progress: 0,
140
+ score: 0,
141
+ startDate: serverTimestamp(),
142
+ status: "IN_PROGRESS",
143
+ submitted: false,
144
+ cards: {},
145
+ lastPlayed: serverTimestamp(),
146
+ owners: props.owners,
147
+ userId: props.userId
148
+ };
149
+ if (props.googleClassroomUserId) {
150
+ score.googleClassroomUserId = props.googleClassroomUserId;
151
+ }
152
+ if (props.courseId) {
153
+ score.courseId = props.courseId;
154
+ }
155
+ return score;
156
+ };
157
+
158
+ // src/domains/cards/card.model.ts
159
+ var RESPOND_PAGE_ACTIVITY_TYPES = [
160
+ "READ_RESPOND" /* READ_RESPOND */,
161
+ "RESPOND" /* RESPOND */,
162
+ "RESPOND_WRITE" /* RESPOND_WRITE */,
163
+ "FREE_RESPONSE" /* FREE_RESPONSE */
164
+ ];
165
+ var MULTIPLE_CHOICE_PAGE_ACTIVITY_TYPES = ["MULTIPLE_CHOICE" /* MULTIPLE_CHOICE */];
166
+ var REPEAT_PAGE_ACTIVITY_TYPES = ["READ_REPEAT" /* READ_REPEAT */, "REPEAT" /* REPEAT */];
167
+ var RESPOND_WRITE_PAGE_ACTIVITY_TYPES = [
168
+ "RESPOND_WRITE" /* RESPOND_WRITE */,
169
+ "FREE_RESPONSE" /* FREE_RESPONSE */
170
+ ];
171
+ var RESPOND_AUDIO_PAGE_ACTIVITY_TYPES = [
172
+ "RESPOND" /* RESPOND */,
173
+ "READ_RESPOND" /* READ_RESPOND */
174
+ ];
175
+
176
+ // src/domains/cards/utils/check-page-type.ts
177
+ function checkIsRepeatPage(cardType) {
178
+ if (cardType === void 0) return false;
179
+ return REPEAT_PAGE_ACTIVITY_TYPES.includes(cardType);
180
+ }
181
+ function checkIsMCPage(cardType) {
182
+ if (cardType === void 0) return false;
183
+ return MULTIPLE_CHOICE_PAGE_ACTIVITY_TYPES.includes(cardType);
184
+ }
185
+ function checkIsRespondPage(cardType) {
186
+ if (cardType === void 0) return false;
187
+ return RESPOND_PAGE_ACTIVITY_TYPES.includes(cardType);
188
+ }
189
+ function checkIsRespondWrittenPage(cardType) {
190
+ if (cardType === void 0) return false;
191
+ return RESPOND_WRITE_PAGE_ACTIVITY_TYPES.includes(cardType);
192
+ }
193
+ function checkIsRespondAudioPage(cardType) {
194
+ if (cardType === void 0) return false;
195
+ return RESPOND_AUDIO_PAGE_ACTIVITY_TYPES.includes(cardType);
196
+ }
197
+ var checkIsMediaPage = (cardType) => {
198
+ if (cardType === void 0) return false;
199
+ return cardType === "MEDIA_PAGE" /* MEDIA_PAGE */;
200
+ };
201
+ var checkIsShortAnswerPage = (cardType) => {
202
+ if (cardType === void 0) return false;
203
+ return cardType === "SHORT_ANSWER" /* SHORT_ANSWER */;
204
+ };
205
+ var checkTypePageActivity = (cardType) => {
206
+ const isRespondAudio = checkIsRespondAudioPage(cardType);
207
+ const isRespondWritten = checkIsRespondWrittenPage(cardType);
208
+ const isRespond = checkIsRespondPage(cardType);
209
+ const isMC = checkIsMCPage(cardType);
210
+ const isRepeat = checkIsRepeatPage(cardType);
211
+ const isMediaPage = checkIsMediaPage(cardType);
212
+ const isShortAnswer = checkIsShortAnswerPage(cardType);
213
+ return {
214
+ isRespondAudio,
215
+ isRespondWritten,
216
+ isRespond,
217
+ isMC,
218
+ isRepeat,
219
+ isMediaPage,
220
+ isShortAnswer
221
+ };
222
+ };
223
+
224
+ // src/domains/cards/utils/get-page-prompt.ts
225
+ function extractTextFromRichText(richText) {
226
+ if (!richText) return "";
227
+ return richText.replace(/<[^>]*>/g, "").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").trim();
228
+ }
229
+ function getPagePrompt(card) {
230
+ if (!card) return { has: false, text: "", rich_text: "", isTextEqualToRichText: false };
231
+ const { isMC, isRepeat, isRespond, isShortAnswer } = checkTypePageActivity(card == null ? void 0 : card.type);
232
+ const hidePrompt = (card == null ? void 0 : card.hidePrompt) === true;
233
+ const createReturnObject = (text, richText) => {
234
+ const plainText = text || "";
235
+ const richTextPlain = extractTextFromRichText(richText);
236
+ return {
237
+ has: true,
238
+ text: plainText,
239
+ rich_text: richText || "",
240
+ isTextEqualToRichText: plainText.trim() === richTextPlain.trim()
241
+ };
242
+ };
243
+ if (isRepeat) {
244
+ return createReturnObject(card == null ? void 0 : card.target_text, card == null ? void 0 : card.rich_text);
245
+ }
246
+ if (isRespond && !hidePrompt) {
247
+ return createReturnObject(card == null ? void 0 : card.prompt, card == null ? void 0 : card.rich_text);
248
+ }
249
+ if (isMC) {
250
+ return createReturnObject(card == null ? void 0 : card.question, card == null ? void 0 : card.rich_text);
251
+ }
252
+ if (isShortAnswer && !hidePrompt) {
253
+ return createReturnObject(card == null ? void 0 : card.prompt, card == null ? void 0 : card.rich_text);
254
+ }
255
+ return {
256
+ has: false,
257
+ text: "",
258
+ rich_text: "",
259
+ isTextEqualToRichText: false
260
+ };
261
+ }
262
+
263
+ // src/domains/cards/utils/get-completed-pages.ts
264
+ var getTotalCompletedCards = (pageScores) => {
265
+ return Object.values(pageScores != null ? pageScores : {}).reduce((acc, cardScore) => {
266
+ var _a, _b;
267
+ if (((_b = (_a = cardScore.completed) != null ? _a : cardScore.score) != null ? _b : cardScore.score === 0) && !cardScore.media_area_opened) {
268
+ acc++;
269
+ }
270
+ return acc;
271
+ }, 0);
272
+ };
273
+
274
+ // src/domains/cards/utils/get-label-page.ts
275
+ var labels = {
276
+ repeat: {
277
+ short: "Repeat",
278
+ long: "Listen & Repeat"
279
+ },
280
+ mc: {
281
+ short: "Multiple Choice",
282
+ long: "Multiple Choice"
283
+ },
284
+ mediaPage: {
285
+ short: "Media Page",
286
+ long: "Media Page"
287
+ },
288
+ shortAnswer: {
289
+ short: "Short Answer",
290
+ long: "Short Answer"
291
+ },
292
+ respondWritten: {
293
+ short: "Open Response",
294
+ long: "Written Open Response"
295
+ },
296
+ respondAudio: {
297
+ short: "Open Response",
298
+ long: "Spoken Open Response"
299
+ }
300
+ };
301
+ var getLabelPage = (pageType) => {
302
+ if (!pageType) {
303
+ return {
304
+ short: "",
305
+ long: ""
306
+ };
307
+ }
308
+ const { isRepeat, isMC, isMediaPage, isShortAnswer, isRespondWritten, isRespondAudio } = checkTypePageActivity(pageType);
309
+ if (isRepeat) {
310
+ return labels.repeat;
311
+ }
312
+ if (isMC) {
313
+ return labels.mc;
314
+ }
315
+ if (isMediaPage) {
316
+ return labels.mediaPage;
317
+ }
318
+ if (isShortAnswer) {
319
+ return labels.shortAnswer;
320
+ }
321
+ if (isRespondWritten) {
322
+ return labels.respondWritten;
323
+ }
324
+ if (isRespondAudio) {
325
+ return labels.respondAudio;
326
+ }
327
+ return {
328
+ short: "",
329
+ long: ""
330
+ };
331
+ };
332
+ export {
333
+ checkAssignmentAvailability,
334
+ checkTypePageActivity,
335
+ defaultScore,
336
+ getLabelPage,
337
+ getPagePrompt,
338
+ getTotalCompletedCards
339
+ };
340
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/domains/assignment/utils/check-assignment-availability.ts","../src/lib/firebase/api.ts","../src/domains/assignment/utils/create-default-score.ts","../src/domains/cards/card.model.ts","../src/domains/cards/utils/check-page-type.ts","../src/domains/cards/utils/get-page-prompt.ts","../src/domains/cards/utils/get-completed-pages.ts","../src/domains/cards/utils/get-label-page.ts"],"sourcesContent":["import { type CustomTimestamp } from '@core/types/firebase.types'\nimport dayjs from 'dayjs'\n\nexport const checkAssignmentAvailability = (\n scheduledTime: string | CustomTimestamp | null | undefined,\n) => {\n if (!scheduledTime) return true\n\n const scheduledDate =\n typeof scheduledTime === 'string' ? dayjs(scheduledTime) : dayjs(scheduledTime.toDate())\n\n if (!scheduledDate.isValid()) return true\n\n return dayjs().isAfter(scheduledDate)\n}\n","import { type CallableFunction, type FirestoreHelpers } from '@core/types/firebase.types'\nimport {\n type SetOptions,\n type DocumentData,\n type QueryConstraint,\n type WithFieldValue,\n type Firestore,\n} from 'firebase/firestore'\n\ninterface FirebaseConfig {\n db: Firestore\n helpers: FirestoreHelpers\n httpsCallable: (name: string) => CallableFunction\n logEvent: (name: string, data: any) => void\n}\n\ntype FirebasePath = string\n\nexport class FirebaseAPI {\n private static instance: FirebaseAPI\n private config: FirebaseConfig | null = null\n\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n private constructor() {\n // Private constructor for singleton pattern\n }\n\n static getInstance(): FirebaseAPI {\n if (!FirebaseAPI.instance) {\n FirebaseAPI.instance = new FirebaseAPI()\n }\n\n return FirebaseAPI.instance\n }\n\n initialize(config: FirebaseConfig) {\n this.config = config\n }\n\n get db() {\n if (!this.config) throw new Error('Firebase API not initialized')\n\n return this.config.db\n }\n\n get helpers() {\n if (!this.config) throw new Error('Firebase API not initialized')\n\n return this.config.helpers\n }\n\n get httpsCallable() {\n return this.config?.httpsCallable\n }\n\n logEvent(name: string, data: any) {\n this.config?.logEvent(name, data)\n }\n\n accessQueryConstraints() {\n const { query, orderBy, limit, startAt, startAfter, endAt, endBefore, where, increment } =\n this.helpers\n\n return {\n query,\n orderBy,\n limit,\n startAt,\n startAfter,\n endAt,\n endBefore,\n where,\n increment,\n }\n }\n\n accessHelpers() {\n const { doc, collection, writeBatch, serverTimestamp, setDoc } = this.helpers\n\n return {\n doc: (path: FirebasePath) => doc(this.db, path),\n collection: (path: FirebasePath) => collection(this.db, path),\n writeBatch: () => writeBatch(this.db),\n serverTimestamp: serverTimestamp,\n setDoc: setDoc,\n }\n }\n\n async getDoc<T>(path: FirebasePath) {\n const { getDoc, doc } = this.helpers\n\n const docRef = doc(this.db, path)\n const docSnap = await getDoc(docRef)\n\n const data = docSnap.exists()\n ? ({\n ...docSnap.data(),\n id: docSnap.id,\n } as T)\n : null\n\n return {\n id: docSnap.id,\n data,\n }\n }\n\n async getDocs<T>(path: FirebasePath, ...queryConstraints: QueryConstraint[]) {\n const { getDocs, query, collection } = this.helpers\n const collectionRef = collection(this.db, path)\n const q =\n queryConstraints.length > 0 ? query(collectionRef, ...queryConstraints) : collectionRef\n\n const querySnapshot = await getDocs(q)\n\n const data = querySnapshot.docs.map(doc => ({\n data: doc.data() as T,\n id: doc.id,\n }))\n\n return {\n data: data as unknown as (T & {\n id: string\n })[],\n querySnapshot,\n empty: querySnapshot.empty,\n }\n }\n\n async addDoc<T extends WithFieldValue<DocumentData>>(\n path: FirebasePath,\n data: T,\n ): Promise<{ id: string } & T> {\n const { addDoc, collection } = this.helpers\n const collectionRef = collection(this.db, path)\n const docRef = await addDoc(collectionRef, data)\n\n return {\n ...data,\n id: docRef.id,\n }\n }\n\n async setDoc<T extends WithFieldValue<DocumentData>>(\n path: FirebasePath,\n data: T,\n options: SetOptions = {},\n ): Promise<void> {\n const { setDoc, doc } = this.helpers\n const docRef = doc(this.db, path)\n\n await setDoc(docRef, data, options)\n }\n\n async updateDoc(path: FirebasePath, data: any): Promise<void> {\n const { updateDoc, doc } = this.helpers\n\n const docRef = doc(this.db, path)\n\n await updateDoc(docRef, data)\n }\n\n async deleteDoc(path: FirebasePath): Promise<void> {\n const { deleteDoc, doc } = this.helpers\n const docRef = doc(this.db, path)\n\n await deleteDoc(docRef)\n }\n\n async runTransaction<T>(updateFunction: (transaction: any) => Promise<T>): Promise<T> {\n const { runTransaction } = this.helpers\n\n return runTransaction(this.db, updateFunction)\n }\n\n async runBatch(operations: (() => Promise<void>)[]): Promise<void> {\n const { writeBatch } = this.helpers\n const batch = writeBatch(this.db)\n\n await Promise.all(operations.map(op => op()))\n await batch.commit()\n }\n\n writeBatch() {\n const { writeBatch } = this.helpers\n\n const batch = writeBatch(this.db)\n\n return batch\n }\n}\n\nexport const api = FirebaseAPI.getInstance()\n","import { api } from '@core/lib/firebase/api'\n\nimport { type Score } from '../assignment.model'\n\nexport const defaultScore = (props: {\n owners: string[]\n courseId?: string\n userId: string\n googleClassroomUserId?: string\n}): Score => {\n const { serverTimestamp } = api.accessHelpers()\n\n const score = {\n progress: 0,\n score: 0,\n startDate: serverTimestamp(),\n status: 'IN_PROGRESS',\n submitted: false,\n cards: {},\n lastPlayed: serverTimestamp(),\n owners: props.owners,\n userId: props.userId,\n } as Score\n\n if (props.googleClassroomUserId) {\n score.googleClassroomUserId = props.googleClassroomUserId\n }\n\n if (props.courseId) {\n score.courseId = props.courseId\n }\n\n return score\n}\n","import { type VerificationCardStatus } from './card.constants'\n\n/* eslint-disable @typescript-eslint/naming-convention */\nexport interface PageActivityWithId extends PageActivity {\n id: string\n}\n\nexport interface PageActivity {\n owners: string[]\n checked?: boolean\n completed?: boolean\n media_area_id?: string | null\n media_area_layout?: 'left' | 'right' | null\n score?: number\n verificationStatus?: VerificationCardStatus\n native_text?: string\n repeat?: number\n language?: string | null\n image?: {\n path?: string | null\n url?: string\n }\n audio?: {\n path?: string | null\n url?: string\n } | null\n notes?: string\n difficulty?: string\n default_language?: string\n target_text?: string\n type: ActivityPageType\n grading_criteria?: string\n scoring_type?: string\n grading_method?: 'simple' | 'rubric' | 'manual' | 'standards_based'\n feedback_types?: string[]\n rubricId?: string\n prompt?: string\n title?: string\n passing_score?: number\n maxCharacters?: number\n answer?: string[]\n choices?: {\n value: string\n option: string\n }[]\n MCQType?: string\n multipleAttemptsAllowed?: boolean\n allowRetries?: boolean\n question?: string\n respondTime?: number\n hidePrompt?: boolean\n videoUrl?: string\n link?: string\n text?: string\n isListenAloud?: boolean\n embedCode?: string\n attempt?: number\n correct?: number\n autoGrade?: boolean\n points?: number\n shuffle?: boolean\n translation?: string\n includeAIContext?: boolean\n media_area_context_ref?: string | null\n standardId?: string\n target_proficiency_level?: string\n allowTTS?: boolean\n feedback_language?: string | null\n correct_answer?: string | null\n limit_attempts?: boolean\n max_attempts?: number\n rich_text?: string\n}\n\nexport enum ActivityPageType {\n // DEFAULT = 'READ_REPEAT',\n READ_REPEAT = 'READ_REPEAT',\n VIDEO = 'VIDEO',\n TEXT = 'TEXT',\n READ_RESPOND = 'READ_RESPOND',\n FREE_RESPONSE = 'FREE_RESPONSE',\n REPEAT = 'REPEAT',\n RESPOND = 'RESPOND',\n RESPOND_WRITE = 'RESPOND_WRITE',\n TEXT_TO_SPEECH = 'TEXT_TO_SPEECH',\n MULTIPLE_CHOICE = 'MULTIPLE_CHOICE',\n PODCAST = 'PODCAST',\n MEDIA_PAGE = 'MEDIA_PAGE',\n WRITE = 'WRITE',\n SHORT_ANSWER = 'SHORT_ANSWER',\n SHORT_STORY = 'SHORT_STORY',\n SPEAK = 'SPEAK',\n CONVERSATION = 'CONVERSATION',\n CONVERSATION_WRITE = 'CONVERSATION_WRITE',\n DIALOGUE = 'DIALOGUE',\n INSTRUCTION = 'INSTRUCTION',\n LISTEN = 'LISTEN',\n READ = 'READ',\n ANSWER = 'ANSWER',\n}\n\nexport const RESPOND_PAGE_ACTIVITY_TYPES = [\n ActivityPageType.READ_RESPOND,\n ActivityPageType.RESPOND,\n ActivityPageType.RESPOND_WRITE,\n ActivityPageType.FREE_RESPONSE,\n]\n\nexport const MULTIPLE_CHOICE_PAGE_ACTIVITY_TYPES = [ActivityPageType.MULTIPLE_CHOICE]\n\nexport const REPEAT_PAGE_ACTIVITY_TYPES = [ActivityPageType.READ_REPEAT, ActivityPageType.REPEAT]\n\nexport const RESPOND_WRITE_PAGE_ACTIVITY_TYPES = [\n ActivityPageType.RESPOND_WRITE,\n ActivityPageType.FREE_RESPONSE,\n]\n\nexport const RESPOND_AUDIO_PAGE_ACTIVITY_TYPES = [\n ActivityPageType.RESPOND,\n ActivityPageType.READ_RESPOND,\n]\n","import {\n ActivityPageType,\n REPEAT_PAGE_ACTIVITY_TYPES,\n MULTIPLE_CHOICE_PAGE_ACTIVITY_TYPES,\n RESPOND_PAGE_ACTIVITY_TYPES,\n RESPOND_WRITE_PAGE_ACTIVITY_TYPES,\n RESPOND_AUDIO_PAGE_ACTIVITY_TYPES,\n} from '../card.model'\n\nfunction checkIsRepeatPage(cardType: ActivityPageType | undefined) {\n if (cardType === undefined) return false\n\n return REPEAT_PAGE_ACTIVITY_TYPES.includes(cardType)\n}\n\nfunction checkIsMCPage(cardType: ActivityPageType | undefined) {\n if (cardType === undefined) return false\n\n return MULTIPLE_CHOICE_PAGE_ACTIVITY_TYPES.includes(cardType)\n}\n\nfunction checkIsRespondPage(cardType: ActivityPageType | undefined) {\n if (cardType === undefined) return false\n\n return RESPOND_PAGE_ACTIVITY_TYPES.includes(cardType)\n}\n\nfunction checkIsRespondWrittenPage(cardType: ActivityPageType | undefined) {\n if (cardType === undefined) return false\n\n return RESPOND_WRITE_PAGE_ACTIVITY_TYPES.includes(cardType)\n}\n\nfunction checkIsRespondAudioPage(cardType: ActivityPageType | undefined) {\n if (cardType === undefined) return false\n\n return RESPOND_AUDIO_PAGE_ACTIVITY_TYPES.includes(cardType)\n}\n\nconst checkIsMediaPage = (cardType: ActivityPageType | undefined) => {\n if (cardType === undefined) return false\n\n return cardType === ActivityPageType.MEDIA_PAGE\n}\n\nconst checkIsShortAnswerPage = (cardType: ActivityPageType | undefined) => {\n if (cardType === undefined) return false\n\n return cardType === ActivityPageType.SHORT_ANSWER\n}\n\nexport const checkTypePageActivity = (cardType: ActivityPageType | undefined) => {\n const isRespondAudio = checkIsRespondAudioPage(cardType)\n const isRespondWritten = checkIsRespondWrittenPage(cardType)\n\n const isRespond = checkIsRespondPage(cardType)\n const isMC = checkIsMCPage(cardType)\n const isRepeat = checkIsRepeatPage(cardType)\n const isMediaPage = checkIsMediaPage(cardType)\n\n const isShortAnswer = checkIsShortAnswerPage(cardType)\n\n return {\n isRespondAudio,\n isRespondWritten,\n isRespond,\n isMC,\n isRepeat,\n isMediaPage,\n isShortAnswer,\n }\n}\n","import { type PageActivityWithId } from '../card.model'\n\nimport { checkTypePageActivity } from './check-page-type'\n\nfunction extractTextFromRichText(richText: string | undefined): string {\n if (!richText) return ''\n\n return richText\n .replace(/<[^>]*>/g, '')\n .replace(/&nbsp;/g, ' ')\n .replace(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .trim()\n}\n\nexport function getPagePrompt(card: PageActivityWithId | undefined) {\n if (!card) return { has: false, text: '', rich_text: '', isTextEqualToRichText: false }\n\n const { isMC, isRepeat, isRespond, isShortAnswer } = checkTypePageActivity(card?.type)\n const hidePrompt = card?.hidePrompt === true\n\n const createReturnObject = (text: string | undefined, richText: string | undefined) => {\n const plainText = text || ''\n const richTextPlain = extractTextFromRichText(richText)\n\n return {\n has: true,\n text: plainText,\n rich_text: richText || '',\n isTextEqualToRichText: plainText.trim() === richTextPlain.trim(),\n }\n }\n\n if (isRepeat) {\n return createReturnObject(card?.target_text, card?.rich_text)\n }\n\n if (isRespond && !hidePrompt) {\n return createReturnObject(card?.prompt, card?.rich_text)\n }\n\n if (isMC) {\n return createReturnObject(card?.question, card?.rich_text)\n }\n\n if (isShortAnswer && !hidePrompt) {\n return createReturnObject(card?.prompt, card?.rich_text)\n }\n\n return {\n has: false,\n text: '',\n rich_text: '',\n isTextEqualToRichText: false,\n }\n}\n","import { type Score } from '@core/domains/assignment/assignment.model'\n\nexport const getTotalCompletedCards = (pageScores: Score['cards'] | undefined) => {\n return Object.values(pageScores ?? {}).reduce((acc, cardScore) => {\n if (\n (cardScore.completed ?? cardScore.score ?? cardScore.score === 0) &&\n !cardScore.media_area_opened\n ) {\n acc++\n }\n\n return acc\n }, 0)\n}\n","import { type ActivityPageType } from '../card.model'\n\nimport { checkTypePageActivity } from './check-page-type'\n\nconst labels = {\n repeat: {\n short: 'Repeat',\n long: 'Listen & Repeat',\n },\n mc: {\n short: 'Multiple Choice',\n long: 'Multiple Choice',\n },\n mediaPage: {\n short: 'Media Page',\n long: 'Media Page',\n },\n shortAnswer: {\n short: 'Short Answer',\n long: 'Short Answer',\n },\n respondWritten: {\n short: 'Open Response',\n long: 'Written Open Response',\n },\n respondAudio: {\n short: 'Open Response',\n long: 'Spoken Open Response',\n },\n}\n\nexport const getLabelPage = (pageType: ActivityPageType | undefined) => {\n if (!pageType) {\n return {\n short: '',\n long: '',\n }\n }\n\n const { isRepeat, isMC, isMediaPage, isShortAnswer, isRespondWritten, isRespondAudio } =\n checkTypePageActivity(pageType)\n\n if (isRepeat) {\n return labels.repeat\n }\n\n if (isMC) {\n return labels.mc\n }\n\n if (isMediaPage) {\n return labels.mediaPage\n }\n\n if (isShortAnswer) {\n return labels.shortAnswer\n }\n\n if (isRespondWritten) {\n return labels.respondWritten\n }\n\n if (isRespondAudio) {\n return labels.respondAudio\n }\n\n return {\n short: '',\n long: '',\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@speakableio/core",
3
- "version": "0.1.99",
3
+ "version": "0.1.101",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Speakable-io/speakable-core.git"
@@ -61,6 +61,46 @@
61
61
  "react-native": "./dist/analytics.js",
62
62
  "default": "./dist/analytics.js",
63
63
  "types": "./dist/analytics.d.mts"
64
+ },
65
+ "./hooks": {
66
+ "import": "./dist/hooks.js",
67
+ "require": "./dist/hooks.cjs",
68
+ "browser": "./dist/hooks.js",
69
+ "react-native": "./dist/hooks.js",
70
+ "default": "./dist/hooks.js",
71
+ "types": "./dist/hooks.d.mts"
72
+ },
73
+ "./const": {
74
+ "import": "./dist/const.js",
75
+ "require": "./dist/const.cjs",
76
+ "browser": "./dist/const.js",
77
+ "react-native": "./dist/const.js",
78
+ "default": "./dist/const.js",
79
+ "types": "./dist/const.d.mts"
80
+ },
81
+ "./repos": {
82
+ "import": "./dist/repos.js",
83
+ "require": "./dist/repos.cjs",
84
+ "browser": "./dist/repos.js",
85
+ "react-native": "./dist/repos.js",
86
+ "default": "./dist/repos.js",
87
+ "types": "./dist/repos.d.mts"
88
+ },
89
+ "./models": {
90
+ "import": "./dist/models.js",
91
+ "require": "./dist/models.cjs",
92
+ "browser": "./dist/models.js",
93
+ "react-native": "./dist/models.js",
94
+ "default": "./dist/models.js",
95
+ "types": "./dist/models.d.mts"
96
+ },
97
+ "./utils": {
98
+ "import": "./dist/utils.js",
99
+ "require": "./dist/utils.cjs",
100
+ "browser": "./dist/utils.js",
101
+ "react-native": "./dist/utils.js",
102
+ "default": "./dist/utils.js",
103
+ "types": "./dist/utils.d.mts"
64
104
  }
65
105
  },
66
106
  "bugs": {
@@ -80,6 +120,21 @@
80
120
  "*": {
81
121
  "analytics": [
82
122
  "./dist/analytics.d.mts"
123
+ ],
124
+ "hooks": [
125
+ "./dist/hooks.d.mts"
126
+ ],
127
+ "const": [
128
+ "./dist/const.d.mts"
129
+ ],
130
+ "repos": [
131
+ "./dist/repos.d.mts"
132
+ ],
133
+ "models": [
134
+ "./dist/models.d.mts"
135
+ ],
136
+ "utils": [
137
+ "./dist/utils.d.mts"
83
138
  ]
84
139
  }
85
140
  },