catchup-library-web 1.5.2 → 1.5.4

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.
@@ -20,260 +20,327 @@ const ShowBodyMediaByContentType = ({
20
20
  useState<string>("");
21
21
 
22
22
  const convertToPercentage = (size: string) => {
23
- if (size === "1/3") {
24
- return "w-small-media";
25
- } else if (size === "1/2") {
26
- return "w-medium-media";
27
- } else if (size === "1") {
28
- return "w-large-media";
23
+ switch (size) {
24
+ case "1/3":
25
+ return "w-small-media";
26
+ case "1/2":
27
+ return "w-medium-media";
28
+ case "1":
29
+ return "w-large-media";
30
+ default:
31
+ return "";
29
32
  }
30
33
  };
31
34
 
35
+ // Helper function to render special expressions with proper keys
36
+ const renderSpecialExpressions = (
37
+ inputPart: IInputPart,
38
+ partIndex: number,
39
+ parentKey: string
40
+ ) => {
41
+ return (
42
+ <span
43
+ key={`${parentKey}-expr-${partIndex}`}
44
+ className={`${inputPart.isBold ? "font-bold" : ""} ${
45
+ inputPart.isUnderline ? "underline" : ""
46
+ }`}
47
+ >
48
+ {inputPart.isEquation ? (
49
+ <span className="text-2xl">
50
+ <InlineMath math={inputPart.value} />
51
+ </span>
52
+ ) : (
53
+ inputPart.value
54
+ )}
55
+ </span>
56
+ );
57
+ };
58
+
59
+ // Helper function to ensure balanced special characters
60
+ const balanceSpecialChars = (text: string) => {
61
+ let result = text;
62
+
63
+ // Balance underscores
64
+ if ((result.split("__").length - 1) % 2 === 1) {
65
+ result = result + "__";
66
+ }
67
+
68
+ // Balance asterisks
69
+ if ((result.split("**").length - 1) % 2 === 1) {
70
+ result = result + "**";
71
+ }
72
+
73
+ // Balance backticks
74
+ if ((result.split("`").length - 1) % 2 === 1) {
75
+ result = result + "`";
76
+ }
77
+
78
+ return result;
79
+ };
80
+
81
+ // Render text content with proper formatting
82
+ const renderTextContent = (text: string, itemKey: string) => {
83
+ const balancedText = balanceSpecialChars(text);
84
+
85
+ return (
86
+ <span key={itemKey} className="text-xl whitespace-pre-wrap">
87
+ {constructInputWithSpecialExpressionList(balancedText).map(
88
+ (inputPart: IInputPart, exprIndex: number) =>
89
+ renderSpecialExpressions(inputPart, exprIndex, itemKey)
90
+ )}
91
+ </span>
92
+ );
93
+ };
94
+
95
+ // Create a fullscreen image component
96
+ const handleOpenFullScreen = (imageSource: string) => {
97
+ setSelectedFullScreenItem(imageSource);
98
+ setShowFullScreen(true);
99
+ };
100
+
32
101
  const retrieveValueParts = (value: string) => {
33
102
  let currentIndex = 0;
34
103
  const valuePartList = [];
35
104
  let copyValue = JSON.parse(JSON.stringify(value));
36
- currentIndex = 0;
37
- let checkText = "--TEXT--";
38
- while (copyValue.includes(checkText)) {
39
- const firstIndex = copyValue.indexOf(checkText);
40
- let textValue = copyValue.substring(0, firstIndex);
41
- let addition = 0;
42
- if (textValue.trim() !== "") {
43
- if ((textValue.split("__").length - 1) % 2 === 1) {
44
- textValue = textValue + "__";
45
- addition += 2;
46
- }
47
- if ((textValue.split("**").length - 1) % 2 === 1) {
48
- textValue = textValue + "**";
49
- addition += 2;
50
- }
51
- if ((textValue.split("`").length - 1) % 2 === 1) {
52
- textValue = textValue + "`";
53
- addition += 1;
105
+
106
+ // Process TEXT tags
107
+ const processTextTags = () => {
108
+ const checkText = "--TEXT--";
109
+ while (copyValue.includes(checkText)) {
110
+ const firstIndex = copyValue.indexOf(checkText);
111
+ const textBeforeTag = copyValue.substring(0, firstIndex);
112
+
113
+ // Handle text before the tag if it's not empty
114
+ if (textBeforeTag.trim() !== "") {
115
+ const balancedText = balanceSpecialChars(textBeforeTag);
116
+ currentIndex++;
117
+ const itemKey = `text-before-${index}-${currentIndex}`;
118
+ valuePartList.push(renderTextContent(balancedText, itemKey));
54
119
  }
120
+
121
+ // Process the text inside the tags
122
+ const subValue = copyValue.substring(firstIndex + checkText.length);
123
+ const secondIndex = subValue.indexOf(checkText);
124
+ if (secondIndex === -1) break; // Safety check for malformed tags
125
+
126
+ const textInsideTag = copyValue.substring(
127
+ firstIndex + checkText.length,
128
+ firstIndex + checkText.length + secondIndex
129
+ );
130
+ currentIndex++;
131
+ const itemKey = `text-inside-${index}-${currentIndex}`;
132
+
55
133
  valuePartList.push(
56
- <span
57
- key={`${index}-${currentIndex}`}
58
- className="text-xl whitespace-pre-wrap"
59
- >
60
- {constructInputWithSpecialExpressionList(textValue).map(
61
- (inputPart: IInputPart, index: number) => (
62
- <span
63
- key={index}
64
- className={`${inputPart.isBold ? "font-bold" : ""} ${
65
- inputPart.isUnderline ? "underline" : ""
66
- }`}
67
- >
68
- {inputPart.isEquation ? (
69
- <span className="text-2xl">
70
- <InlineMath math={inputPart.value} />
71
- </span>
72
- ) : (
73
- inputPart.value
74
- )}
75
- </span>
76
- )
134
+ <span key={itemKey} className="text-xl font-bold whitespace-pre-wrap">
135
+ {constructInputWithSpecialExpressionList(textInsideTag).map(
136
+ (inputPart: IInputPart, exprIndex: number) =>
137
+ renderSpecialExpressions(inputPart, exprIndex, itemKey)
77
138
  )}
78
139
  </span>
79
140
  );
141
+
142
+ // Update copyValue to process the next occurrence
143
+ copyValue = copyValue.substring(
144
+ firstIndex + checkText.length + secondIndex + checkText.length
145
+ );
146
+
147
+ // Balance backticks in remaining text if needed
148
+ if ((copyValue.split("`").length - 1) % 2 === 1) {
149
+ copyValue = "`" + copyValue;
150
+ }
80
151
  }
81
- const subValue = copyValue.substring(firstIndex + checkText.length);
82
- const secondIndex =
83
- subValue.indexOf(checkText) +
84
- checkText.length +
85
- textValue.length -
86
- addition;
87
- currentIndex++;
88
- valuePartList.push(
89
- <span
90
- key={`${index}-${currentIndex}`}
91
- className={`text-xl font-bold whitespace-pre-wrap`}
92
- >
93
- {constructInputWithSpecialExpressionList(
94
- copyValue.substring(firstIndex + checkText.length, secondIndex)
95
- ).map((inputPart: IInputPart, index: number) => (
96
- <span
97
- key={index}
98
- className={`${inputPart.isBold ? "font-bold" : ""} ${
99
- inputPart.isUnderline ? "underline" : ""
100
- }`}
152
+ };
153
+
154
+ // Process IMAGE tags
155
+ const processImageTags = () => {
156
+ const checkText = "--IMAGE--";
157
+ while (copyValue.includes(checkText)) {
158
+ const firstIndex = copyValue.indexOf(checkText);
159
+ const textBeforeTag = copyValue.substring(0, firstIndex);
160
+
161
+ // Handle text before the tag if it's not empty
162
+ if (textBeforeTag.trim() !== "") {
163
+ currentIndex++;
164
+ valuePartList.push(
165
+ <p
166
+ key={`text-before-img-${index}-${currentIndex}`}
167
+ className="text-xl"
101
168
  >
102
- {inputPart.isEquation ? (
103
- <span className="text-2xl">
104
- <InlineMath math={inputPart.value} />
105
- </span>
106
- ) : (
107
- inputPart.value
108
- )}
109
- </span>
110
- ))}
111
- </span>
112
- );
113
- copyValue = copyValue.substring(secondIndex + checkText.length);
114
- if ((copyValue.split("`").length - 1) % 2 === 1) {
115
- copyValue = "`" + copyValue;
116
- }
117
- }
118
- checkText = "--IMAGE--";
119
- while (copyValue.includes(checkText)) {
120
- const firstIndex = copyValue.indexOf(checkText);
121
- const textValue = copyValue.substring(0, firstIndex);
122
- if (textValue.trim() !== "") {
169
+ {textBeforeTag}
170
+ </p>
171
+ );
172
+ }
173
+
174
+ // Process the image source inside the tags
175
+ const subValue = copyValue.substring(firstIndex + checkText.length);
176
+ const secondIndex = subValue.indexOf(checkText);
177
+ if (secondIndex === -1) break; // Safety check for malformed tags
178
+
179
+ const imageSource = copyValue.substring(
180
+ firstIndex + checkText.length,
181
+ firstIndex + checkText.length + secondIndex
182
+ );
183
+
123
184
  currentIndex++;
124
185
  valuePartList.push(
125
- <p key={`${index}_${currentIndex}`} className="text-xl">
126
- {textValue}
127
- </p>
128
- );
129
- }
130
- const subValue = copyValue.substring(firstIndex + checkText.length);
131
- const secondIndex =
132
- subValue.indexOf(checkText) + checkText.length + textValue.length;
133
- const imageSource = copyValue.substring(
134
- firstIndex + checkText.length,
135
- secondIndex
136
- );
137
- currentIndex++;
138
- valuePartList.push(
139
- <div key={`${index}-${currentIndex}`} className="relative w-[200px]">
140
- <BaseImage
141
- src={imageSource}
142
- alt="media"
143
- size="custom"
144
- className="rounded-catchup-xlarge"
145
- />
146
186
  <div
147
- className="absolute flex items-center justify-center top-2 right-2 h-6 w-6 cursor-pointer border rounded-catchup-xlarge border-catchup-blue p-1"
148
- onClick={(event) => {
149
- setShowFullScreen(true);
150
- setSelectedFullScreenItem(imageSource);
151
- }}
187
+ key={`img-${index}-${currentIndex}`}
188
+ className="relative w-[200px]"
152
189
  >
153
190
  <BaseImage
154
- src="/icons/arrow-up.webp"
155
- alt="arrow-up"
191
+ src={imageSource}
192
+ alt="media"
156
193
  size="custom"
157
- className="w-full"
194
+ className="rounded-catchup-xlarge"
158
195
  />
196
+ <div
197
+ className="absolute flex items-center justify-center top-2 right-2 h-6 w-6 cursor-pointer border rounded-catchup-xlarge border-catchup-blue p-1"
198
+ onClick={() => handleOpenFullScreen(imageSource)}
199
+ >
200
+ <BaseImage
201
+ src="/icons/arrow-up.webp"
202
+ alt="arrow-up"
203
+ size="custom"
204
+ className="w-full"
205
+ />
206
+ </div>
159
207
  </div>
160
- </div>
161
- );
162
- copyValue = copyValue.substring(secondIndex + checkText.length);
163
- }
164
- checkText = "--VIDEO--";
165
- while (copyValue.includes(checkText)) {
166
- const firstIndex = copyValue.indexOf(checkText);
167
- const textValue = copyValue.substring(0, firstIndex);
168
- if (textValue.trim() !== "") {
208
+ );
209
+
210
+ // Update copyValue to process the next occurrence
211
+ copyValue = copyValue.substring(
212
+ firstIndex + checkText.length + secondIndex + checkText.length
213
+ );
214
+ }
215
+ };
216
+
217
+ // Process VIDEO tags
218
+ const processVideoTags = () => {
219
+ const checkText = "--VIDEO--";
220
+ while (copyValue.includes(checkText)) {
221
+ const firstIndex = copyValue.indexOf(checkText);
222
+ const textBeforeTag = copyValue.substring(0, firstIndex);
223
+
224
+ // Handle text before the tag if it's not empty
225
+ if (textBeforeTag.trim() !== "") {
226
+ currentIndex++;
227
+ valuePartList.push(
228
+ <p
229
+ key={`text-before-video-${index}-${currentIndex}`}
230
+ className="text-xl"
231
+ >
232
+ {textBeforeTag}
233
+ </p>
234
+ );
235
+ }
236
+
237
+ // Process the video source inside the tags
238
+ const subValue = copyValue.substring(firstIndex + checkText.length);
239
+ const secondIndex = subValue.indexOf(checkText);
240
+ if (secondIndex === -1) break; // Safety check for malformed tags
241
+
242
+ const videoSource = copyValue.substring(
243
+ firstIndex + checkText.length,
244
+ firstIndex + checkText.length + secondIndex
245
+ );
246
+
169
247
  currentIndex++;
170
248
  valuePartList.push(
171
- <p key={`${index}-${currentIndex}`} className="text-xl">
172
- {textValue}
173
- </p>
249
+ <video
250
+ key={`video-${index}-${currentIndex}`}
251
+ src={videoSource}
252
+ className="w-[200px] rounded-catchup-xlarge"
253
+ controls
254
+ />
255
+ );
256
+
257
+ // Update copyValue to process the next occurrence
258
+ copyValue = copyValue.substring(
259
+ firstIndex + checkText.length + secondIndex + checkText.length
174
260
  );
175
261
  }
176
- const subValue = copyValue.substring(firstIndex + checkText.length);
177
- const secondIndex =
178
- subValue.indexOf(checkText) + checkText.length + textValue.length;
179
- const videoSource = copyValue.substring(
180
- firstIndex + checkText.length,
181
- secondIndex
182
- );
183
- currentIndex++;
184
- valuePartList.push(
185
- <video
186
- key={`${index}-${currentIndex}`}
187
- src={videoSource}
188
- className={`w-[200px] rounded-catchup-xlarge`}
189
- />
190
- );
191
- copyValue = copyValue.substring(secondIndex + checkText.length);
192
- }
193
- checkText = "--AUDIO--";
194
- while (copyValue.includes(checkText)) {
195
- const firstIndex = copyValue.indexOf(checkText);
196
- const textValue = copyValue.substring(0, firstIndex);
197
- if (textValue.trim() !== "") {
262
+ };
263
+
264
+ // Process AUDIO tags
265
+ const processAudioTags = () => {
266
+ const checkText = "--AUDIO--";
267
+ while (copyValue.includes(checkText)) {
268
+ const firstIndex = copyValue.indexOf(checkText);
269
+ const textBeforeTag = copyValue.substring(0, firstIndex);
270
+
271
+ // Handle text before the tag if it's not empty
272
+ if (textBeforeTag.trim() !== "") {
273
+ currentIndex++;
274
+ valuePartList.push(
275
+ <p
276
+ key={`text-before-audio-${index}-${currentIndex}`}
277
+ className="text-xl"
278
+ >
279
+ {textBeforeTag}
280
+ </p>
281
+ );
282
+ }
283
+
284
+ // Process the audio source inside the tags
285
+ const subValue = copyValue.substring(firstIndex + checkText.length);
286
+ const secondIndex = subValue.indexOf(checkText);
287
+ if (secondIndex === -1) break; // Safety check for malformed tags
288
+
289
+ const audioSource = copyValue.substring(
290
+ firstIndex + checkText.length,
291
+ firstIndex + checkText.length + secondIndex
292
+ );
293
+
198
294
  currentIndex++;
199
295
  valuePartList.push(
200
- <p key={`${index}-${currentIndex}`} className="text-xl">
201
- {textValue}
202
- </p>
296
+ <audio
297
+ key={`audio-${index}-${currentIndex}`}
298
+ src={audioSource}
299
+ className="w-[200px] rounded-catchup-xlarge"
300
+ controls
301
+ />
302
+ );
303
+
304
+ // Update copyValue to process the next occurrence
305
+ copyValue = copyValue.substring(
306
+ firstIndex + checkText.length + secondIndex + checkText.length
203
307
  );
204
308
  }
205
- const subValue = copyValue.substring(firstIndex + checkText.length);
206
- const secondIndex =
207
- subValue.indexOf(checkText) + checkText.length + textValue.length;
208
- const audioSource = copyValue.substring(
209
- firstIndex + checkText.length,
210
- secondIndex
211
- );
212
- currentIndex++;
213
- valuePartList.push(
214
- <audio
215
- key={`${index}-${currentIndex}`}
216
- src={audioSource}
217
- className={`w-[200px] rounded-catchup-xlarge`}
218
- />
219
- );
220
- copyValue = copyValue.substring(secondIndex + checkText.length);
221
- }
309
+ };
310
+
311
+ // Process all types of tags
312
+ processTextTags();
313
+ processImageTags();
314
+ processVideoTags();
315
+ processAudioTags();
316
+
317
+ // Handle any remaining text
222
318
  if (copyValue.trim() !== "") {
223
319
  currentIndex++;
224
- if ((copyValue.split("__").length - 1) % 2 === 1) {
225
- copyValue = "__" + copyValue;
226
- }
227
- if ((copyValue.split("**").length - 1) % 2 === 1) {
228
- copyValue = "**" + copyValue;
229
- }
230
- if ((copyValue.split("`").length - 1) % 2 === 1) {
231
- copyValue = "`" + copyValue;
232
- }
320
+ copyValue = balanceSpecialChars(copyValue);
321
+
322
+ // Check for image description markup
233
323
  const regexMatchImageText = copyValue.match(/<image>([\s\S]*?)<\/image>/);
234
324
  if (regexMatchImageText) {
235
325
  const imageText = regexMatchImageText[1];
236
326
  valuePartList.push(
237
- <div className="bg-catchup-gray-50 relative px-4 py-4 rounded-catchup-small mt-2">
327
+ <div
328
+ key={`img-desc-${index}-${currentIndex}`}
329
+ className="bg-catchup-gray-50 relative px-4 py-4 rounded-catchup-small mt-2"
330
+ >
238
331
  <div className="absolute -top-3 bg-catchup-white border rounded-catchup-small px-2 left-2">
239
332
  <p className="font-bold">{i18n.t("image_description")}</p>
240
333
  </div>
241
- <span
242
- key={`${index}-${currentIndex}`}
243
- className="text-xl whitespace-pre-wrap "
244
- >
245
- {imageText}
246
- </span>
334
+ <span className="text-xl whitespace-pre-wrap">{imageText}</span>
247
335
  </div>
248
336
  );
249
337
  } else {
250
- valuePartList.push(
251
- <span
252
- key={`${index}-${currentIndex}`}
253
- className="text-xl whitespace-pre-wrap"
254
- >
255
- {constructInputWithSpecialExpressionList(copyValue).map(
256
- (inputPart: IInputPart, index: number) => (
257
- <span
258
- key={index}
259
- className={`${inputPart.isBold ? "font-bold" : ""} ${
260
- inputPart.isUnderline ? "underline" : ""
261
- }`}
262
- >
263
- {inputPart.isEquation ? (
264
- <span className="text-2xl">
265
- <InlineMath math={inputPart.value} />
266
- </span>
267
- ) : (
268
- inputPart.value
269
- )}
270
- </span>
271
- )
272
- )}
273
- </span>
274
- );
338
+ // Handle normal remaining text
339
+ const itemKey = `remaining-${index}-${currentIndex}`;
340
+ valuePartList.push(renderTextContent(copyValue, itemKey));
275
341
  }
276
342
  }
343
+
277
344
  return valuePartList;
278
345
  };
279
346
 
@@ -281,7 +348,6 @@ const ShowBodyMediaByContentType = ({
281
348
  return (
282
349
  <Modal
283
350
  isOpen={showFullScreen}
284
- onAfterOpen={() => {}}
285
351
  onRequestClose={() => {
286
352
  setShowFullScreen(false);
287
353
  setSelectedFullScreenItem("");
@@ -306,14 +372,13 @@ const ShowBodyMediaByContentType = ({
306
372
  background: "rgba(0, 0, 0, 0.6)",
307
373
  },
308
374
  }}
309
- contentLabel=""
375
+ contentLabel="Image Fullscreen View"
310
376
  >
311
377
  <div className="flex-1 flex flex-col">
312
- {/* <div className="m-4"> */}
313
378
  <div className="ml-auto px-5 py-3">
314
379
  <BaseImage
315
380
  src="/icons/cross-red.webp"
316
- alt="cross-red"
381
+ alt="close fullscreen"
317
382
  size="medium"
318
383
  onClick={() => {
319
384
  setShowFullScreen(false);
@@ -324,7 +389,7 @@ const ShowBodyMediaByContentType = ({
324
389
  <div className="flex items-center justify-center h-[500]">
325
390
  <BaseImage
326
391
  src={selectedFullScreenItem}
327
- alt="selected-fullscreen-item"
392
+ alt="fullscreen image"
328
393
  size="custom"
329
394
  className="w-full"
330
395
  />
@@ -335,66 +400,66 @@ const ShowBodyMediaByContentType = ({
335
400
  };
336
401
 
337
402
  const RenderMainContent = () => {
338
- if (type === "TEXT") {
339
- return (
340
- <div className="mb-3 flex flex-row flex-wrap items-center mx-auto w-full">
341
- <span>{retrieveValueParts(value)}</span>
342
- </div>
343
- );
344
- } else if (type === "IMAGE") {
345
- return (
346
- <div className="mb-3 flex flex-col items-center relative">
347
- <div
348
- className={`${
349
- size ? `${convertToPercentage(size)}` : ""
350
- } rounded-catchup-xlarge relative`}
351
- >
352
- <BaseImage
353
- src={value}
354
- alt="body-image"
355
- size="custom"
356
- className="w-full rounded-catchup-xlarge"
357
- />
403
+ switch (type) {
404
+ case "TEXT":
405
+ return (
406
+ <div className="mb-3 flex flex-row flex-wrap items-center mx-auto w-full">
407
+ {retrieveValueParts(value)}
408
+ </div>
409
+ );
410
+ case "IMAGE":
411
+ return (
412
+ <div className="mb-3 flex flex-col items-center relative">
358
413
  <div
359
- className="absolute flex items-center justify-center top-2 right-2 h-6 w-6 cursor-pointer border rounded-catchup-xlarge border-catchup-blue p-1"
360
- onClick={() => {
361
- setShowFullScreen(true);
362
- setSelectedFullScreenItem(value);
363
- }}
414
+ className={`${convertToPercentage(
415
+ size || ""
416
+ )} rounded-catchup-xlarge relative`}
364
417
  >
365
418
  <BaseImage
366
- src="/icons/arrow-up.webp"
367
- alt="arrow-up"
419
+ src={value}
420
+ alt="body image"
368
421
  size="custom"
369
- className="w-full"
422
+ className="w-full rounded-catchup-xlarge"
370
423
  />
424
+ <div
425
+ className="absolute flex items-center justify-center top-2 right-2 h-6 w-6 cursor-pointer border rounded-catchup-xlarge border-catchup-blue p-1"
426
+ onClick={() => handleOpenFullScreen(value)}
427
+ >
428
+ <BaseImage
429
+ src="/icons/arrow-up.webp"
430
+ alt="expand"
431
+ size="custom"
432
+ className="w-full"
433
+ />
434
+ </div>
371
435
  </div>
372
436
  </div>
373
- </div>
374
- );
375
- } else if (type === "VIDEO") {
376
- return (
377
- <div className="mb-3 flex flex-col items-center">
378
- <video
379
- src={value}
380
- className={`${
381
- size ? `${convertToPercentage(size)}` : ""
382
- } rounded-catchup-xlarge`}
383
- controls
384
- />
385
- </div>
386
- );
387
- } else if (type === "AUDIO") {
388
- return (
389
- <div className="mb-3 flex flex-col items-center">
390
- <audio src={value} controls className={`rounded-catchup-xlarge`} />
391
- </div>
392
- );
437
+ );
438
+ case "VIDEO":
439
+ return (
440
+ <div className="mb-3 flex flex-col items-center">
441
+ <video
442
+ src={value}
443
+ className={`${convertToPercentage(
444
+ size || ""
445
+ )} rounded-catchup-xlarge`}
446
+ controls
447
+ />
448
+ </div>
449
+ );
450
+ case "AUDIO":
451
+ return (
452
+ <div className="mb-3 flex flex-col items-center">
453
+ <audio src={value} controls className="rounded-catchup-xlarge" />
454
+ </div>
455
+ );
456
+ default:
457
+ return null;
393
458
  }
394
459
  };
395
460
 
396
461
  return (
397
- <div key={index} className="w-full">
462
+ <div key={`body-content-${index}`} className="w-full">
398
463
  {RenderShowFullScreenItem()}
399
464
  {RenderMainContent()}
400
465
  </div>