waha-shared 1.0.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.
Files changed (135) hide show
  1. package/.DS_Store +0 -0
  2. package/README.md +10 -0
  3. package/__init__.py +0 -0
  4. package/copy.ts +37 -0
  5. package/data/README.md +45 -0
  6. package/data/__init__.py +0 -0
  7. package/data/aslTimestamps.json +192788 -0
  8. package/data/bibleAudios.json +394 -0
  9. package/data/bibleAudios.ts +4 -0
  10. package/data/bibleResources/audioBibleLicenses.json +6235 -0
  11. package/data/bibleResources/bibleBooks.json +6071 -0
  12. package/data/bibleResources/bibleBooks.ts +4 -0
  13. package/data/bibleResources/bibleChaptersList.json +1191 -0
  14. package/data/bibleResources/bibleChaptersList.ts +4 -0
  15. package/data/bibleResources/textBibleLicenses.json +14603 -0
  16. package/data/bibleStatuses.json +9196 -0
  17. package/data/bibleStatuses.ts +4 -0
  18. package/data/bibleTexts.json +527 -0
  19. package/data/bibleTexts.ts +4 -0
  20. package/data/clones.json +13 -0
  21. package/data/countryResources/areas.json +74 -0
  22. package/data/countryResources/areas.ts +4 -0
  23. package/data/countryResources/countries.json +3362 -0
  24. package/data/countryResources/countries.ts +4 -0
  25. package/data/foundationsCurriculums.json +130 -0
  26. package/data/foundationsCurriculums.ts +4 -0
  27. package/data/languageResources/countriesAndLanguages.json +11718 -0
  28. package/data/languageResources/countriesAndLanguages.ts +16 -0
  29. package/data/languageResources/crowdinLanguages.json +2172 -0
  30. package/data/languageResources/iosVoiceOverLanguages.json +64 -0
  31. package/data/languageResources/iso6933LanguageCodes.json +7927 -0
  32. package/data/languageResources/mmsLanguages.json +28164 -0
  33. package/data/languageResources/phoneLanguages.json +1532 -0
  34. package/data/languageResources/phoneLanguages.ts +14 -0
  35. package/data/languages.json +7045 -0
  36. package/data/languages.ts +4 -0
  37. package/data/mediaDurations.json +32364 -0
  38. package/data/mediaDurations.ts +4 -0
  39. package/data/notification.json +69 -0
  40. package/data/notification.ts +4 -0
  41. package/data/numeralMaps.json +26 -0
  42. package/data/numeralMaps.ts +4 -0
  43. package/data/orphanedBibleTexts.json +2747 -0
  44. package/data/questions.json +317 -0
  45. package/data/questions.ts +4 -0
  46. package/data/questionsCurriculums.json +753 -0
  47. package/data/questionsCurriculums.ts +4 -0
  48. package/data/releaseNotes.json +2381 -0
  49. package/data/releaseNotes.ts +4 -0
  50. package/data/schemas/appTranslations.schema.json +802 -0
  51. package/data/schemas/areas.schema.json +76 -0
  52. package/data/schemas/aslTimestamps.schema.json +59 -0
  53. package/data/schemas/bibleAudios.schema.json +37 -0
  54. package/data/schemas/bibleBooks.schema.json +112 -0
  55. package/data/schemas/bibleChapters.schema.json +61 -0
  56. package/data/schemas/bibleStatuses.schema.json +41 -0
  57. package/data/schemas/bibleTexts.schema.json +60 -0
  58. package/data/schemas/clones.schema.json +63 -0
  59. package/data/schemas/countries.schema.json +84 -0
  60. package/data/schemas/foundationsCurriculums.schema.json +20 -0
  61. package/data/schemas/introductionTranslations.schema.json +101 -0
  62. package/data/schemas/languages.schema.json +365 -0
  63. package/data/schemas/mediaDurations.schema.json +41 -0
  64. package/data/schemas/notification.schema.json +111 -0
  65. package/data/schemas/numeralMaps.schema.json +57 -0
  66. package/data/schemas/questionTranslations.schema.json +20 -0
  67. package/data/schemas/questions.schema.json +29 -0
  68. package/data/schemas/questionsCurriculums.schema.json +34 -0
  69. package/data/schemas/releaseNotes.schema.json +21 -0
  70. package/data/schemas/screenshots.schema.json +23 -0
  71. package/data/schemas/setTranslations.schema.json +70 -0
  72. package/data/schemas/sets.schema.json +109 -0
  73. package/data/schemas/topicsCurriculums.schema.json +20 -0
  74. package/data/screenshots.json +17 -0
  75. package/data/screenshots.ts +4 -0
  76. package/data/sets.json +12258 -0
  77. package/data/sets.ts +4 -0
  78. package/data/specialIds.json +72 -0
  79. package/data/specialIds.ts +19 -0
  80. package/data/timings/som.json +9150 -0
  81. package/data/topicsCurriculums.json +131 -0
  82. package/data/topicsCurriculums.ts +4 -0
  83. package/data/typescript/appTranslations.ts +322 -0
  84. package/data/typescript/areas.ts +45 -0
  85. package/data/typescript/aslTimestamps.ts +36 -0
  86. package/data/typescript/bibleAudioAvailableChapters.ts +35 -0
  87. package/data/typescript/bibleAudios.ts +32 -0
  88. package/data/typescript/bibleBookNames.ts +97 -0
  89. package/data/typescript/bibleBooks.ts +63 -0
  90. package/data/typescript/bibleChapters.ts +40 -0
  91. package/data/typescript/bibleStatuses.ts +54 -0
  92. package/data/typescript/bibleTextAvailableChapters.ts +35 -0
  93. package/data/typescript/bibleTexts.ts +63 -0
  94. package/data/typescript/clones.ts +35 -0
  95. package/data/typescript/countries.ts +152 -0
  96. package/data/typescript/foundationsCurriculums.ts +15 -0
  97. package/data/typescript/introductionTranslations.ts +60 -0
  98. package/data/typescript/languages.ts +369 -0
  99. package/data/typescript/mediaDurations.ts +89 -0
  100. package/data/typescript/notification.ts +189 -0
  101. package/data/typescript/numeralMaps.ts +74 -0
  102. package/data/typescript/questionTranslations.ts +6 -0
  103. package/data/typescript/questions.ts +25 -0
  104. package/data/typescript/questionsCurriculums.ts +26 -0
  105. package/data/typescript/releaseNotes.ts +10 -0
  106. package/data/typescript/screenshots.ts +52 -0
  107. package/data/typescript/setTranslations.ts +39 -0
  108. package/data/typescript/sets.ts +105 -0
  109. package/data/typescript/topicsCurriculums.ts +15 -0
  110. package/data/youtube/playlists.json +28 -0
  111. package/data/youtube/videos.json +262 -0
  112. package/data/youtube/videos.ts +2 -0
  113. package/functions/activeCampaign.ts +127 -0
  114. package/functions/bibleChapterUtils.ts +241 -0
  115. package/functions/crowdin.ts +51 -0
  116. package/functions/languages.ts +284 -0
  117. package/functions/scripturePassages.ts +368 -0
  118. package/functions/sets.ts +495 -0
  119. package/package.json +10 -0
  120. package/translations/appTranslations.json +10239 -0
  121. package/translations/appTranslations.ts +4 -0
  122. package/translations/introductionTranslations.json +422 -0
  123. package/translations/introductionTranslations.ts +4 -0
  124. package/translations/questionTranslations.json +1472 -0
  125. package/translations/questionTranslations.ts +4 -0
  126. package/translations/setTranslations.json +30257 -0
  127. package/translations/setTranslations.ts +4 -0
  128. package/translations/spokenQuestionTranslations.json +1472 -0
  129. package/types/analytics.ts +147 -0
  130. package/types/completions.ts +9 -0
  131. package/types/feedback.ts +27 -0
  132. package/types/languages.ts +37 -0
  133. package/types/notifications.ts +37 -0
  134. package/types/sets.ts +84 -0
  135. package/types/users.ts +162 -0
@@ -0,0 +1,495 @@
1
+ import mediaDurations from 'shared/data/mediaDurations'
2
+ import type { AppTranslations } from 'shared/data/typescript/appTranslations'
3
+ import { getPassagesString } from 'shared/functions/scripturePassages'
4
+ import questions from '../data/questions'
5
+ import allSets from '../data/sets'
6
+ import specialIds from '../data/specialIds'
7
+ import courseYoutubeVideos from '../data/youtube/videos'
8
+ import type { LanguageInfo, MeetTranslations } from '../types/languages'
9
+ import {
10
+ Chapter,
11
+ Content,
12
+ Lesson,
13
+ LessonInfo,
14
+ Set,
15
+ SetCategory,
16
+ SetInfo,
17
+ type Section,
18
+ } from '../types/sets'
19
+
20
+ export const firebaseUrl =
21
+ 'https://firebasestorage.googleapis.com/v0/b/waha-app-db.appspot.com/o/'
22
+
23
+ export function getSetInfo({
24
+ setId,
25
+ meetLanguageId,
26
+ sets,
27
+ t,
28
+ }: {
29
+ setId: string
30
+ meetLanguageId: string
31
+ t: MeetTranslations & AppTranslations[string]
32
+ sets: string[]
33
+ }): SetInfo | undefined {
34
+ const idComponents = setId.split('.')
35
+ const category: SetCategory =
36
+ idComponents[0] === '01'
37
+ ? 'Foundations'
38
+ : idComponents[0] === '02'
39
+ ? 'Topics'
40
+ : 'WahaTraining'
41
+
42
+ const setNumber = (
43
+ sets
44
+ .filter((set) => set.split('.')[0] === idComponents[0])
45
+ .findIndex((set) => set === setId) + 1
46
+ ).toString()
47
+
48
+ const thisSetTranslations = t.sets.find((set) => set.setId === setId)
49
+ const categoryName =
50
+ idComponents[0] === '01'
51
+ ? t.foundations
52
+ : idComponents[0] === '02'
53
+ ? t.topics
54
+ : t.training
55
+ const set = allSets.find((set) => set.setId === setId) as Set | undefined
56
+
57
+ if (!set) return
58
+
59
+ const toReturn = {
60
+ ...set,
61
+ ...thisSetTranslations,
62
+ setSubtitle:
63
+ category === 'Topics'
64
+ ? (thisSetTranslations?.setTag ?? '')
65
+ : `${categoryName} ${parseInt(setNumber)}`,
66
+ setLink: `https://web.waha.app/set?set-id=${setId}&meet-language=${meetLanguageId}`,
67
+ meetLanguageId,
68
+ setNumber,
69
+ category,
70
+ }
71
+ return toReturn
72
+ }
73
+
74
+ export function getLessonInfo({
75
+ lessonId,
76
+ meetLanguageInfo,
77
+ setInfo,
78
+ t,
79
+ }: {
80
+ lessonId: Lesson['lessonId'] | undefined
81
+ meetLanguageInfo: LanguageInfo
82
+ setInfo?: SetInfo
83
+ t: MeetTranslations & AppTranslations[string]
84
+ }): LessonInfo | undefined {
85
+ if (!lessonId) return
86
+ else if (lessonId.split('.').length !== 3) return
87
+
88
+ const [category, setNumber, lessonNumber] = lessonId.split('.')
89
+
90
+ const meetLanguageId = meetLanguageInfo.languageId
91
+
92
+ setInfo =
93
+ setInfo ??
94
+ getSetInfo({
95
+ setId: [category, setNumber].join('.'),
96
+ meetLanguageId,
97
+ sets: meetLanguageInfo.sets,
98
+ t,
99
+ })
100
+
101
+ if (!setInfo) {
102
+ console.log('Missing set info for lesson:', lessonId)
103
+ return
104
+ }
105
+
106
+ const lesson = setInfo.lessons.find((lesson) => lesson.lessonId === lessonId)
107
+
108
+ if (!lesson) return
109
+
110
+ const thisSetTranslations = t.sets.find((set) => set.setId === setInfo?.setId)
111
+ const lessonTitle = thisSetTranslations?.lessonTitles?.[lessonId]
112
+
113
+ const fellowshipQuestions =
114
+ meetLanguageInfo.questionSets.find(
115
+ (questionSet) => questionSet.questionSetId === lesson.f
116
+ )?.questions ?? []
117
+
118
+ const fellowshipQuestionsText = fellowshipQuestions.map(
119
+ (questionId) => t.questions[questionId]
120
+ )
121
+
122
+ const applicationQuestions =
123
+ meetLanguageInfo.questionSets.find(
124
+ (questionSet) => questionSet.questionSetId === lesson.a
125
+ )?.questions ?? []
126
+
127
+ const applicationQuestionsText = applicationQuestions.map(
128
+ (questionId) => t.questions[questionId]
129
+ )
130
+
131
+ const sections: Section[] = [
132
+ {
133
+ id: 'title',
134
+ isChapter: false,
135
+ header: lessonTitle ?? '',
136
+ index: 0,
137
+ chapter: null,
138
+ indexWithinChapter: 0,
139
+ text: lessonTitle ?? '',
140
+ hasBeep: false,
141
+ },
142
+ ]
143
+
144
+ fellowshipQuestions.forEach((questionId, index) => {
145
+ const hasBeep =
146
+ questions.find((question) => question.questionId === questionId)
147
+ ?.hasBeep ?? false
148
+
149
+ sections.push({
150
+ id: questionId,
151
+ isChapter: index === 0,
152
+ index: sections.length,
153
+ header: index === 0 ? t.fellowship : null,
154
+ chapter: Chapter.FELLOWSHIP,
155
+ indexWithinChapter: index,
156
+ text: fellowshipQuestionsText[index],
157
+ hasBeep,
158
+ })
159
+ })
160
+
161
+ if (specialIds.evaluationQuestionLessonIds.includes(lesson.lessonId)) {
162
+ sections.push({
163
+ id: 'eq',
164
+ isChapter: false,
165
+ index: sections.length,
166
+ chapter: Chapter.INTRODUCTION,
167
+ indexWithinChapter: 0,
168
+ header: null,
169
+ text: t.introductions.introductions.eq,
170
+ hasBeep: false,
171
+ })
172
+ } else if (specialIds.growingAsDmcSetIds.includes(setInfo.setId)) {
173
+ sections.push({
174
+ id: 'introduction',
175
+ isChapter: false,
176
+ index: sections.length,
177
+ chapter: Chapter.INTRODUCTION,
178
+ indexWithinChapter: 0,
179
+ header: t.introductions.introduction,
180
+ text: t.introductions.introductions[
181
+ lessonId as keyof typeof t.introductions.introductions
182
+ ],
183
+ hasBeep: false,
184
+ })
185
+ }
186
+
187
+ lesson.s?.forEach((passageId, index) => {
188
+ sections.push({
189
+ id: passageId,
190
+ isChapter: index === 0,
191
+ hasBeep: false,
192
+ index: sections.length,
193
+ chapter: Chapter.STORY,
194
+ indexWithinChapter: index,
195
+ text: '',
196
+ header: getPassagesString([passageId], meetLanguageInfo),
197
+ })
198
+ })
199
+
200
+ applicationQuestions.forEach((questionId, index) => {
201
+ const hasBeep =
202
+ questions.find((question) => question.questionId === questionId)
203
+ ?.hasBeep ?? false
204
+
205
+ sections.push({
206
+ id: questionId,
207
+ isChapter: index === 0,
208
+ hasBeep,
209
+ index: sections.length,
210
+ chapter: Chapter.APPLICATION,
211
+ indexWithinChapter: index,
212
+ header: index === 0 ? t.application : null,
213
+ text: questionId.includes('video')
214
+ ? t.training_video
215
+ : applicationQuestionsText[index],
216
+ })
217
+ })
218
+
219
+ const baseInfo = {
220
+ lessonIntroduction: t.introductions.introduction,
221
+ lessonLink: `https://web.waha.app/lesson?lesson-id=${lessonId}&meet-language=${meetLanguageId}`,
222
+ lessonNumber,
223
+ lessonTitle,
224
+ sections,
225
+ ...setInfo,
226
+ ...lesson,
227
+ }
228
+
229
+ if (specialIds.evaluationQuestionLessonIds.includes(lesson.lessonId)) {
230
+ const languageId = meetLanguageInfo.introBridge ?? meetLanguageId
231
+ const id = `${languageId}.eq`
232
+ const fileName = `${id}.mp3`
233
+ const path = `${languageId}/introductions/${fileName}`
234
+
235
+ return {
236
+ type: 'eq',
237
+ ...baseInfo,
238
+ eq: {
239
+ id,
240
+ languageId,
241
+ localFileName: fileName,
242
+ remoteFileName: fileName,
243
+ path,
244
+ url: firebaseUrl + encodeURIComponent(path) + `?alt=media`,
245
+ },
246
+ }
247
+ } else if (
248
+ setInfo.setId === specialIds.dmCourseSetId ||
249
+ meetLanguageId === 'ase'
250
+ ) {
251
+ let video: Content
252
+
253
+ if (meetLanguageInfo.languageId === 'ase') {
254
+ const path = `ase/full_lessons/${setInfo.setId}/ase.${lessonId}.1080.mp4`
255
+ video = {
256
+ id: `ase.${lessonId}`,
257
+ languageId: 'ase',
258
+ path,
259
+ localFileName: `ase.${lessonId}.mp4`,
260
+ remoteFileName: `ase.${lessonId}.1080.mp4`,
261
+ url: firebaseUrl + encodeURIComponent(path) + `?alt=media`,
262
+ }
263
+ } else {
264
+ const id = `${meetLanguageId}.${meetLanguageInfo.trainingVideoLanguage}.${lessonId}`
265
+ const remoteFileName = `${id}.1080.mp4`
266
+
267
+ const localFileName = `${id}.mp4`
268
+
269
+ const path = `${meetLanguageId}/full_lessons/${setInfo?.setId}/${remoteFileName}`
270
+
271
+ video = {
272
+ id,
273
+ languageId: meetLanguageInfo.trainingVideoLanguage,
274
+ path,
275
+ localFileName,
276
+ remoteFileName,
277
+ url: firebaseUrl + encodeURIComponent(path) + `?alt=media`,
278
+ }
279
+ }
280
+
281
+ let youtubeLink: string | undefined
282
+
283
+ if (video.remoteFileName && video.remoteFileName in courseYoutubeVideos)
284
+ youtubeLink =
285
+ 'https://www.youtube.com/watch?v=' +
286
+ courseYoutubeVideos[
287
+ video.remoteFileName as keyof typeof courseYoutubeVideos
288
+ ]
289
+
290
+ return {
291
+ type: 'video',
292
+ ...baseInfo,
293
+ lessonSectionHeader: thisSetTranslations?.sectionHeaders?.[lessonId],
294
+ lessonSectionBody: thisSetTranslations?.sectionBodies?.[lessonId],
295
+ video,
296
+ youtubeLink,
297
+ passagesString: getPassagesString(lesson.s, meetLanguageInfo),
298
+ }
299
+ } else {
300
+ return {
301
+ type: 'dbs',
302
+ ...baseInfo,
303
+ full: {
304
+ id: `${meetLanguageId}.${lessonId}`,
305
+ localFileName: `${meetLanguageId}.${lessonId}.mp3`,
306
+ remoteFileName: `${meetLanguageId}.${lessonId}.mp3`,
307
+ languageId: meetLanguageId,
308
+ path: `${meetLanguageId}/full_lessons/${setInfo.setId}/${meetLanguageId}.${lessonId}.mp3`,
309
+ url:
310
+ firebaseUrl +
311
+ encodeURIComponent(
312
+ `${meetLanguageId}/full_lessons/${setInfo.setId}/${meetLanguageId}.${lessonId}.mp3`
313
+ ) +
314
+ `?alt=media`,
315
+ },
316
+ dbsCast: {
317
+ remoteFileName: `${meetLanguageId}.${lessonId}.1080.mp4`,
318
+ url:
319
+ firebaseUrl +
320
+ encodeURIComponent(
321
+ `${meetLanguageId}/full_lessons/${setInfo.setId}/${meetLanguageId}.${lessonId}.1080.mp4`
322
+ ) +
323
+ `?alt=media`,
324
+ },
325
+ passagesString: getPassagesString(lesson.s, meetLanguageInfo),
326
+ }
327
+ }
328
+ }
329
+
330
+ export function convertSToString(time: number): string {
331
+ let secondsNumber: number
332
+ let minutesNumber: number
333
+ let hoursNumber: number
334
+ let secondsString: string
335
+ let minutesString: string
336
+ let hoursString: string
337
+
338
+ if (time > 0) {
339
+ // If the time is greater than an hour, we need to show the hour of the time as well.
340
+ if (time >= 3600) {
341
+ secondsNumber = Math.floor(time % 60)
342
+ minutesNumber = Math.floor((time / 60) % 60)
343
+ hoursNumber = Math.floor((time / (60 * 60)) % 60)
344
+
345
+ secondsString = secondsNumber.toString()
346
+ minutesString =
347
+ minutesNumber < 10
348
+ ? '0' + minutesNumber.toString()
349
+ : minutesNumber.toString()
350
+ hoursString =
351
+ hoursNumber < 10 ? '0' + hoursNumber.toString() : hoursNumber.toString()
352
+
353
+ return `${hoursString}:${minutesString}:${secondsString}`
354
+ } else {
355
+ secondsNumber = Math.floor(time % 60)
356
+ minutesNumber = Math.floor((time / 60) % 60)
357
+
358
+ minutesString =
359
+ minutesNumber < 10
360
+ ? '0' + minutesNumber.toString()
361
+ : minutesNumber.toString()
362
+ secondsString =
363
+ secondsNumber < 10
364
+ ? '0' + secondsNumber.toString()
365
+ : secondsNumber.toString()
366
+
367
+ return `${minutesString}:${secondsString}`
368
+ }
369
+ } // If the time is greater than the max allowed, just show the max.
370
+ else {
371
+ return '00:00'
372
+ }
373
+ }
374
+
375
+ /** Determines whether a lesson should be visible in Waha. */
376
+ export function shouldShowLesson(
377
+ lessonInfo: LessonInfo | undefined,
378
+ languageInfo: LanguageInfo
379
+ ) {
380
+ if (lessonInfo?.lessonTitle === undefined || lessonInfo.lessonTitle === '') {
381
+ console.log(
382
+ 'Missing lesson info or lessonTitle lesson:',
383
+ lessonInfo?.lessonId
384
+ )
385
+ return false
386
+ } else if (
387
+ specialIds.growingAsDmcSetIds.includes(lessonInfo.setId) &&
388
+ (lessonInfo.lessonIntroduction === undefined ||
389
+ lessonInfo.lessonIntroduction === '')
390
+ ) {
391
+ console.log('Missing intro for lesson:', lessonInfo.lessonId)
392
+ return false
393
+ } else if (
394
+ lessonInfo.type === 'eq' &&
395
+ !mediaDurations[languageInfo.introBridge ?? languageInfo.languageId]
396
+ .lessons[lessonInfo.eq.remoteFileName]
397
+ ) {
398
+ console.log('Missing remote eq file for lesson:', lessonInfo.lessonId)
399
+ return false
400
+ } else if (
401
+ lessonInfo.type === 'dbs' &&
402
+ !mediaDurations[lessonInfo.meetLanguageId].lessons[
403
+ lessonInfo.full.remoteFileName
404
+ ]
405
+ ) {
406
+ console.log('Missing remote dbs file for lesson:', lessonInfo.lessonId)
407
+ return false
408
+ } else if (
409
+ lessonInfo.type === 'video' &&
410
+ lessonInfo.video &&
411
+ !mediaDurations[lessonInfo.meetLanguageId].lessons[
412
+ lessonInfo.video.remoteFileName
413
+ ]
414
+ ) {
415
+ console.log(
416
+ 'Missing remote video for lesson:',
417
+ lessonInfo.video.remoteFileName
418
+ )
419
+ return false
420
+ } else return true
421
+ }
422
+
423
+ /**
424
+ * Calculate start times for each section based on question durations and total
425
+ * audio duration. Must be called after audio is loaded to get accurate total
426
+ * duration.
427
+ *
428
+ * @param sections - The lesson sections to calculate timings for
429
+ * @param totalDuration - Total duration of the lesson audio in seconds
430
+ * @param languageId - The language ID to look up question durations
431
+ * @returns Map of section ID to start time in seconds
432
+ */
433
+ export function calculateSectionTimings(
434
+ sections: Section[],
435
+ totalDuration: number,
436
+ languageId: string
437
+ ): Map<string, number> {
438
+ const languageDurations = mediaDurations[languageId]
439
+
440
+ const getQuestionDuration = (questionId: string): number => {
441
+ const fileName = `${languageId}.${questionId}.mp3`
442
+ return languageDurations.questions[fileName] ?? 0
443
+ }
444
+
445
+ const timings = new Map<string, number>()
446
+
447
+ // Title section always starts at 0
448
+ timings.set('title', 0)
449
+
450
+ // Calculate fellowship question timings from the start
451
+ let currentTime = 0
452
+ const fellowshipSections = sections.filter(
453
+ (s) => s.chapter === Chapter.FELLOWSHIP
454
+ )
455
+
456
+ fellowshipSections.forEach((section) => {
457
+ timings.set(section.id, currentTime)
458
+ currentTime += getQuestionDuration(section.id)
459
+ })
460
+
461
+ // Story/Scripture section starts after fellowship
462
+ const storySections = sections.filter(
463
+ (s) => s.chapter === Chapter.STORY || s.chapter === Chapter.INTRODUCTION
464
+ )
465
+ const storyStartTime = currentTime
466
+
467
+ storySections.forEach((section) => {
468
+ timings.set(section.id, storyStartTime)
469
+ })
470
+
471
+ // Calculate application question timings by working backwards from total duration
472
+ const applicationSections = sections.filter(
473
+ (s) => s.chapter === Chapter.APPLICATION
474
+ )
475
+
476
+ // Calculate total duration of all application questions
477
+ const totalApplicationDuration = applicationSections.reduce(
478
+ (sum, section) => {
479
+ return sum + getQuestionDuration(section.id)
480
+ },
481
+ 0
482
+ )
483
+
484
+ // Application section starts after story ends
485
+ // Story duration = totalDuration - fellowship duration - application duration
486
+ const applicationStartTime = totalDuration - totalApplicationDuration
487
+
488
+ let applicationTime = applicationStartTime
489
+ applicationSections.forEach((section) => {
490
+ timings.set(section.id, applicationTime)
491
+ applicationTime += getQuestionDuration(section.id)
492
+ })
493
+
494
+ return timings
495
+ }
package/package.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "waha-shared",
3
+ "version": "1.0.1",
4
+ "author": "Waha",
5
+ "description": "Shared assets for Waha's various apps",
6
+ "files": [
7
+ "**/*"
8
+ ],
9
+ "license": "MIT"
10
+ }