@speakableio/core 0.1.0 → 0.1.2

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.js CHANGED
@@ -1,36 +1,290 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }// src/providers/SpeakableProvider.tsx
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/providers/SpeakableProvider.tsx
2
2
  var _react = require('react');
3
3
 
4
+ // src/utils/error-handler.ts
5
+ var ServiceError = class extends Error {
6
+ constructor(message, originalError, code) {
7
+ super(message);
8
+ this.originalError = originalError;
9
+ this.code = code;
10
+ this.name = "ServiceError";
11
+ }
12
+ };
13
+ function withErrorHandler(fn, serviceName) {
14
+ return async (...args) => {
15
+ try {
16
+ return await fn(...args);
17
+ } catch (error) {
18
+ if (error instanceof Error && "code" in error) {
19
+ const firebaseError = error;
20
+ throw new ServiceError(
21
+ `Error in ${serviceName}: ${firebaseError.message}`,
22
+ error,
23
+ firebaseError.code
24
+ );
25
+ }
26
+ if (error instanceof Error) {
27
+ throw new ServiceError(`Error in ${serviceName}: ${error.message}`, error);
28
+ }
29
+ throw new ServiceError(`Unknown error in ${serviceName}`, error);
30
+ }
31
+ };
32
+ }
33
+
34
+ // src/lib/firebase/api.ts
35
+ var FirebaseAPI = class _FirebaseAPI {
36
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
37
+ constructor() {
38
+ this.config = null;
39
+ }
40
+ static getInstance() {
41
+ if (!_FirebaseAPI.instance) {
42
+ _FirebaseAPI.instance = new _FirebaseAPI();
43
+ }
44
+ return _FirebaseAPI.instance;
45
+ }
46
+ initialize(config) {
47
+ this.config = config;
48
+ }
49
+ get db() {
50
+ if (!this.config) throw new Error("FirebaseAPI not initialized");
51
+ return this.config.db;
52
+ }
53
+ get helpers() {
54
+ if (!this.config) throw new Error("FirebaseAPI not initialized");
55
+ return this.config.helpers;
56
+ }
57
+ accessQueryConstraints() {
58
+ const { query, orderBy, limit, startAt, startAfter, endAt, endBefore } = this.helpers;
59
+ return {
60
+ query,
61
+ orderBy,
62
+ limit,
63
+ startAt,
64
+ startAfter,
65
+ endAt,
66
+ endBefore
67
+ };
68
+ }
69
+ async getDoc(path) {
70
+ const { getDoc, doc } = this.helpers;
71
+ const docRef = doc(this.db, path);
72
+ const docSnap = await getDoc(docRef);
73
+ return {
74
+ id: docSnap.id,
75
+ data: docSnap.exists() ? docSnap.data() : null
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
+ id: doc.id,
85
+ data: doc.data()
86
+ }));
87
+ return {
88
+ data,
89
+ querySnapshot
90
+ };
91
+ }
92
+ async addDoc(path, data) {
93
+ const { addDoc, collection } = this.helpers;
94
+ const collectionRef = collection(this.db, path);
95
+ const docRef = await addDoc(collectionRef, data);
96
+ return docRef.id;
97
+ }
98
+ async setDoc(path, data) {
99
+ const { setDoc, doc } = this.helpers;
100
+ const docRef = doc(this.db, path);
101
+ await setDoc(docRef, data);
102
+ }
103
+ async updateDoc(path, data) {
104
+ const { updateDoc, doc } = this.helpers;
105
+ const docRef = doc(this.db, path);
106
+ await updateDoc(docRef, data);
107
+ }
108
+ async deleteDoc(path) {
109
+ const { deleteDoc, doc } = this.helpers;
110
+ const docRef = doc(this.db, path);
111
+ await deleteDoc(docRef);
112
+ }
113
+ async runTransaction(updateFunction) {
114
+ const { runTransaction } = this.helpers;
115
+ return runTransaction(this.db, updateFunction);
116
+ }
117
+ async runBatch(operations) {
118
+ const { writeBatch } = this.helpers;
119
+ const batch = writeBatch(this.db);
120
+ await Promise.all(operations.map((op) => op()));
121
+ await batch.commit();
122
+ }
123
+ };
124
+ var api = FirebaseAPI.getInstance();
125
+
126
+ // src/domains/assignment/assignment.constants.ts
127
+ var ASSIGNMENT_ANALYTICS_TYPES = [
128
+ "macro" /* Macro */,
129
+ "gradebook" /* Gradebook */,
130
+ "cards" /* Cards */,
131
+ "student" /* Student */,
132
+ "student_summary" /* StudentSummary */
133
+ ];
134
+ var ASSIGNMENTS_COLLECTION = "assignments";
135
+ var ANALYTICS_SUBCOLLECTION = "analytics";
136
+ var SCORES_SUBCOLLECTION = "scores";
137
+ var refsAssignmentFiresotre = {
138
+ allAssignments: () => ASSIGNMENTS_COLLECTION,
139
+ assignment: (id) => `${ASSIGNMENTS_COLLECTION}/${id}`,
140
+ assignmentAllAnalytics: (id) => `${ASSIGNMENTS_COLLECTION}/${id}/${ANALYTICS_SUBCOLLECTION}`,
141
+ assignmentAnalytics: (id, type) => `${ASSIGNMENTS_COLLECTION}/${id}/${ANALYTICS_SUBCOLLECTION}/${type}`,
142
+ assignmentScores: (id, userId) => `${ASSIGNMENTS_COLLECTION}/${id}/${SCORES_SUBCOLLECTION}/${userId}`
143
+ };
144
+
145
+ // src/domains/assignment/services/get-assignments-score.service.ts
146
+ var _getAssignmentScores = async ({
147
+ assignmentId,
148
+ analyticType = "macro" /* Macro */,
149
+ studentId,
150
+ currentUserId
151
+ }) => {
152
+ if (analyticType === "student" /* Student */) {
153
+ const path = refsAssignmentFiresotre.assignmentScores(assignmentId, currentUserId);
154
+ const response = await api.getDoc(path);
155
+ return { scores: response.data, id: assignmentId };
156
+ }
157
+ if (analyticType === "student_summary" /* StudentSummary */ && studentId) {
158
+ const path = refsAssignmentFiresotre.assignmentScores(assignmentId, studentId);
159
+ const response = await api.getDoc(path);
160
+ return { scores: response.data, id: assignmentId };
161
+ }
162
+ if (analyticType !== "all" /* All */ && ASSIGNMENT_ANALYTICS_TYPES.includes(analyticType)) {
163
+ const ref = refsAssignmentFiresotre.assignmentAnalytics(assignmentId, analyticType);
164
+ const docData = await api.getDoc(ref);
165
+ return { scores: docData.data, id: assignmentId };
166
+ } else if (analyticType === "all" /* All */) {
167
+ const ref = refsAssignmentFiresotre.assignmentAllAnalytics(assignmentId);
168
+ const response = await api.getDocs(ref);
169
+ const data = response.data.reduce((acc, curr) => {
170
+ acc[curr.id] = curr;
171
+ return acc;
172
+ }, {});
173
+ return { scores: data, id: assignmentId };
174
+ }
175
+ };
176
+ var getAssignmentScores = withErrorHandler(_getAssignmentScores, "getAssignmentScores");
177
+
178
+ // src/domains/assignment/services/attach-score-assignment.service.ts
179
+ var _attachScoresAssignment = async ({
180
+ assignments,
181
+ analyticType,
182
+ studentId,
183
+ currentUserId
184
+ }) => {
185
+ const scoresPromises = assignments.map((a) => {
186
+ return getAssignmentScores({
187
+ assignmentId: a.id,
188
+ analyticType,
189
+ studentId,
190
+ currentUserId
191
+ });
192
+ });
193
+ const scores = await Promise.all(scoresPromises);
194
+ const scoresObject = scores.reduce((acc, curr) => {
195
+ acc[curr.id] = curr.scores;
196
+ return acc;
197
+ }, {});
198
+ const assignmentsWithScores = assignments.map((a) => {
199
+ return {
200
+ ...a,
201
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/ban-ts-comment
202
+ // @ts-ignore
203
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
204
+ scores: _nullishCoalesce(scoresObject[a.id], () => ( null))
205
+ };
206
+ });
207
+ return assignmentsWithScores;
208
+ };
209
+ var attachScoresAssignment = withErrorHandler(
210
+ _attachScoresAssignment,
211
+ "attachScoresAssignment"
212
+ );
213
+
214
+ // src/domains/assignment/services/get-all-assignment.service.ts
215
+ async function _getAllAssignments() {
216
+ const path = refsAssignmentFiresotre.allAssignments();
217
+ const response = await api.getDocs(path);
218
+ return response.data;
219
+ }
220
+ var getAllAssignments = withErrorHandler(_getAllAssignments, "getAllAssignments");
221
+
222
+ // src/domains/assignment/utils/check-assignment-availability.ts
223
+ var _dayjs = require('dayjs'); var _dayjs2 = _interopRequireDefault(_dayjs);
224
+ var checkAssignmentAvailability = (scheduledTime) => {
225
+ if (!scheduledTime) return true;
226
+ const scheduledDate = typeof scheduledTime === "string" ? _dayjs2.default.call(void 0, scheduledTime) : _dayjs2.default.call(void 0, scheduledTime.toDate());
227
+ if (!scheduledDate.isValid()) return true;
228
+ return _dayjs2.default.call(void 0, ).isAfter(scheduledDate);
229
+ };
230
+
231
+ // src/domains/assignment/services/get-assignment.service.ts
232
+ async function _getAssignment(params) {
233
+ const path = refsAssignmentFiresotre.assignment(params.assignmentId);
234
+ const response = await api.getDoc(path);
235
+ if (!response.data) return null;
236
+ const assignment = response.data;
237
+ const isAvailable = checkAssignmentAvailability(assignment.scheduledTime);
238
+ const assignmentWithId = {
239
+ ...assignment,
240
+ isAvailable,
241
+ id: params.assignmentId,
242
+ scheduledTime: _nullishCoalesce(assignment.scheduledTime, () => ( null))
243
+ };
244
+ if (params.analyticType) {
245
+ const assignmentsWithScores = await attachScoresAssignment({
246
+ assignments: [assignmentWithId],
247
+ analyticType: params.analyticType,
248
+ currentUserId: params.currentUserId
249
+ });
250
+ return assignmentsWithScores.length > 0 ? assignmentsWithScores[0] : assignmentWithId;
251
+ }
252
+ return assignmentWithId;
253
+ }
254
+ var getAssignment = withErrorHandler(_getAssignment, "getAssignment");
255
+
4
256
  // src/domains/assignment/assignment.repo.ts
5
- var createAssignmentRepo = (db, h) => {
6
- const { doc, collection, getDoc, getDocs } = h;
257
+ var createAssignmentRepo = () => {
7
258
  return {
8
- async get(id) {
9
- const snap = await getDoc(doc(db, `assignments/${id}`));
10
- if (!snap.exists()) return null;
11
- const data = snap.data();
12
- return { id, ...data };
13
- },
14
- async list() {
15
- const col = collection(db, "assignments");
16
- const snap = await getDocs(col);
17
- return snap.docs.map((d) => ({ id: d.id, ...d.data() }));
18
- }
259
+ getAssignment,
260
+ attachScoresAssignment,
261
+ getAssignmentScores,
262
+ getAllAssignments
19
263
  };
20
264
  };
21
265
 
22
- // src/domains/assignment/hooks.ts
266
+ // src/domains/assignment/assignment.hooks.ts
23
267
  var _reactquery = require('@tanstack/react-query');
24
268
  var assignmentQueryKeys = {
25
269
  all: ["assignments"],
26
270
  byId: (id) => [...assignmentQueryKeys.all, id],
27
271
  list: () => [...assignmentQueryKeys.all, "list"]
28
272
  };
29
- function useAssignment(id) {
30
- const { fs } = useFs();
273
+ function useAssignment({
274
+ assignmentId,
275
+ enabled = true,
276
+ analyticType,
277
+ userId
278
+ }) {
279
+ const { speakableApi } = useSpeakableApi();
31
280
  return _reactquery.useQuery.call(void 0, {
32
- queryKey: assignmentQueryKeys.byId(id),
33
- queryFn: () => fs.assignmentRepo.get(id)
281
+ queryKey: assignmentQueryKeys.byId(assignmentId),
282
+ queryFn: () => speakableApi.assignmentRepo.getAssignment({
283
+ assignmentId,
284
+ analyticType,
285
+ currentUserId: userId
286
+ }),
287
+ enabled
34
288
  });
35
289
  }
36
290
 
@@ -38,8 +292,12 @@ function useAssignment(id) {
38
292
  async function createFsClient(db, platform) {
39
293
  const dbAsFirestore = db;
40
294
  const helpers = platform === "web" ? await Promise.resolve().then(() => _interopRequireWildcard(require("firebase/firestore"))) : await Promise.resolve().then(() => _interopRequireWildcard(require("@react-native-firebase/firestore")));
295
+ api.initialize({
296
+ db: dbAsFirestore,
297
+ helpers
298
+ });
41
299
  return {
42
- assignmentRepo: createAssignmentRepo(dbAsFirestore, helpers)
300
+ assignmentRepo: createAssignmentRepo()
43
301
  };
44
302
  }
45
303
 
@@ -52,27 +310,27 @@ function SpeakableProvider({
52
310
  children,
53
311
  queryClient
54
312
  }) {
55
- const [fs, setFs] = _react.useState.call(void 0, null);
313
+ const [speakableApi, setSpeakableApi] = _react.useState.call(void 0, null);
56
314
  _react.useEffect.call(void 0, () => {
57
315
  createFsClient(db, platform).then((repos) => {
58
- setFs(repos);
316
+ setSpeakableApi(repos);
59
317
  });
60
318
  }, [db, platform]);
61
- if (!fs) return null;
319
+ if (!speakableApi) return null;
62
320
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
63
321
  FsCtx.Provider,
64
322
  {
65
323
  value: {
66
- fs,
324
+ speakableApi,
67
325
  queryClient
68
326
  },
69
327
  children
70
328
  }
71
329
  );
72
330
  }
73
- function useFs() {
331
+ function useSpeakableApi() {
74
332
  const ctx = _react.useContext.call(void 0, FsCtx);
75
- if (!ctx) throw new Error("useFs must be used within a SpeakableProvider");
333
+ if (!ctx) throw new Error("useSpeakableApi must be used within a SpeakableProvider");
76
334
  return ctx;
77
335
  }
78
336
 
@@ -83,4 +341,4 @@ function useFs() {
83
341
 
84
342
 
85
343
 
86
- exports.FsCtx = FsCtx; exports.SpeakableProvider = SpeakableProvider; exports.assignmentQueryKeys = assignmentQueryKeys; exports.createAssignmentRepo = createAssignmentRepo; exports.createFsClient = createFsClient; exports.useAssignment = useAssignment; exports.useFs = useFs;
344
+ exports.FsCtx = FsCtx; exports.SpeakableProvider = SpeakableProvider; exports.assignmentQueryKeys = assignmentQueryKeys; exports.createAssignmentRepo = createAssignmentRepo; exports.createFsClient = createFsClient; exports.useAssignment = useAssignment; exports.useSpeakableApi = useSpeakableApi;