ordering-ui-react-native 0.12.84 → 0.12.88

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.
@@ -117,9 +117,11 @@ export const ProductOptionsUI = (props: any) => {
117
117
  const [{ auth }] = useSession();
118
118
  const { product, loading, error } = productObject;
119
119
 
120
- const { top, bottom } = useSafeAreaInsets();
121
- const { height } = useWindowDimensions();
122
- const [selOpt, setSelectedOpt] = useState(0);
120
+ const [selOpt, setSelectedOpt] = useState(null);
121
+ const [optionsLayout, setOptionsLayout] = useState<any>(null)
122
+
123
+ const extraOptions = [].concat(...product?.extras?.map((option: any) => option.options)).filter((i: any) => i.respect_to === null)
124
+
123
125
  const scrollViewRef = useRef<any>()
124
126
  const isError = (id: number) => {
125
127
  let bgColor = theme.colors.white;
@@ -133,24 +135,22 @@ export const ProductOptionsUI = (props: any) => {
133
135
  };
134
136
 
135
137
  const handleSaveProduct = () => {
136
- console.log('----- click handle ------')
137
138
  const isErrors = Object.values(errors).length > 0;
138
139
  if (!isErrors) {
139
- console.log('----- save handle ------')
140
140
  handleSave && handleSave();
141
141
  return;
142
142
  }
143
143
  };
144
144
 
145
145
  const hasRespected = (options: Array<any>, respect_id: number) => {
146
- if (respect_id == null) return;
146
+ if (respect_id === null) return;
147
147
  const option: any = options.filter(({ id }: any) => id === selOpt);
148
- if (option == undefined) return false;
149
- if (option?.suboptions?.length == 0) return false;
150
- const sel = option[0]?.suboptions?.filter(
148
+ if (option === undefined) return false;
149
+ if (option?.suboptions?.length === null) return false;
150
+ const sel = option && option[0]?.suboptions?.filter(
151
151
  ({ id }: any) => id === respect_id,
152
152
  );
153
- return sel[0]?.id !== undefined;
153
+ return sel && sel[0]?.id !== undefined;
154
154
  };
155
155
 
156
156
  const handleRedirectLogin = () => {
@@ -158,73 +158,88 @@ export const ProductOptionsUI = (props: any) => {
158
158
  navigation.navigate('Login');
159
159
  };
160
160
 
161
- const saveErrors =
162
- orderState.loading ||
163
- maxProductQuantity === 0 ||
164
- Object.keys(errors).length > 0;
161
+ const handleClickOption = (value: any) => {
162
+ setSelectedOpt(value)
163
+
164
+ const optionsArray = value
165
+ ? Object.values(optionsLayout)
166
+ .filter((opt: any) => opt.position || opt.position === 0)
167
+ .map((i: any) => i.height)
168
+ .slice(0, optionsLayout[value]?.position)
169
+ : []
170
+
171
+ scrollViewRef.current.scrollTo({
172
+ y: optionsArray.length > 0
173
+ ? optionsLayout?.header?.height + optionsArray?.reduce((acc, cur) => acc + cur)
174
+ : optionsLayout?.header?.height,
175
+ animated: true
176
+ })
177
+ }
165
178
 
166
- const ExtraOptions = ({ eID, options }: any) => (
179
+ const saveErrors = orderState.loading || maxProductQuantity === null || Object.keys(errors).length > 0;
180
+
181
+ const ExtraOptions = ({ options }: any) => (
167
182
  <ExtraOptionWrap
168
183
  horizontal
169
184
  showsHorizontalScrollIndicator={false}
170
- style={{ marginBottom: 20 }}
171
- contentContainerStyle={{ paddingHorizontal: 33 }}>
185
+ contentContainerStyle={{ paddingHorizontal: 30, paddingTop: 10 }}
186
+ >
172
187
  <>
173
188
  <TouchableOpacity
174
189
  key={`eopt_all_0`}
175
- onPress={() => setSelectedOpt(0)}
190
+ onPress={() => handleClickOption(null)}
176
191
  style={[
177
192
  styles.extraItem,
178
193
  {
179
194
  borderBottomColor:
180
- selOpt == 0 ? theme.colors.textNormal : theme.colors.border,
195
+ selOpt === null ? theme.colors.textNormal : theme.colors.border,
181
196
  },
182
197
  ]}>
183
198
  <OText
184
- color={selOpt == 0 ? theme.colors.textNormal : theme.colors.textSecondary}
185
- size={selOpt == 0 ? 14 : 12}
186
- weight={selOpt == 0 ? '600' : 'normal'}>
199
+ color={selOpt === null ? theme.colors.textNormal : theme.colors.textSecondary}
200
+ size={selOpt === null ? 14 : 12}
201
+ weight={selOpt === null ? '600' : 'normal'}>
187
202
  {t('ALL', 'All')}
188
203
  </OText>
189
204
  </TouchableOpacity>
190
205
  {product?.ingredients.length > 0 && (
191
206
  <TouchableOpacity
192
207
  key={`eopt_all_00`}
193
- onPress={() => setSelectedOpt(-1)}
208
+ onPress={() => handleClickOption('ingredients')}
194
209
  style={[
195
210
  styles.extraItem,
196
211
  {
197
212
  borderBottomColor:
198
- selOpt == -1 ? theme.colors.textNormal : theme.colors.border,
213
+ selOpt === 'ingredients' ? theme.colors.textNormal : theme.colors.border,
199
214
  },
200
215
  ]}>
201
216
  <OText
202
- color={selOpt == -1 ? theme.colors.textNormal : theme.colors.textSecondary}
203
- size={selOpt == -1 ? 14 : 12}
204
- weight={selOpt == -1 ? '600' : 'normal'}>
217
+ color={selOpt === 'ingredients' ? theme.colors.textNormal : theme.colors.textSecondary}
218
+ size={selOpt === 'ingredients' ? 14 : 12}
219
+ weight={selOpt === 'ingredients' ? '600' : 'normal'}>
205
220
  {t('INGREDIENTS', 'Ingredients')}
206
221
  </OText>
207
222
  </TouchableOpacity>
208
223
  )}
209
224
  {options.map(({ id, name, respect_to }: any) => (
210
225
  <React.Fragment key={`cont_key_${id}`}>
211
- {respect_to == null && (
226
+ {respect_to === null && (
212
227
  <TouchableOpacity
213
228
  key={`eopt_key_${id}`}
214
- onPress={() => setSelectedOpt(id)}
229
+ onPress={() => handleClickOption(`opt_${id}`)}
215
230
  style={[
216
231
  styles.extraItem,
217
232
  {
218
233
  borderBottomColor:
219
- selOpt == id ? theme.colors.textNormal : theme.colors.border,
234
+ selOpt === `opt_${id}` ? theme.colors.textNormal : theme.colors.border,
220
235
  },
221
236
  ]}>
222
237
  <OText
223
238
  color={
224
- selOpt == id ? theme.colors.textNormal : theme.colors.textSecondary
239
+ selOpt === `opt_${id}` ? theme.colors.textNormal : theme.colors.textSecondary
225
240
  }
226
- size={selOpt == id ? 14 : 12}
227
- weight={selOpt == id ? '600' : 'normal'}>
241
+ size={selOpt === `opt_${id}` ? 14 : 12}
242
+ weight={selOpt === `opt_${id}` ? '600' : 'normal'}>
228
243
  {name}
229
244
  </OText>
230
245
  </TouchableOpacity>
@@ -235,27 +250,32 @@ export const ProductOptionsUI = (props: any) => {
235
250
  </ExtraOptionWrap>
236
251
  );
237
252
 
238
- const [isKeyboardShow, setIsKeyboardShow] = useState(false)
253
+ const handleScroll = ({ nativeEvent }: any) => {
254
+ const scrollOffset = nativeEvent.contentOffset.y
255
+ const optionsArray = Object.values(optionsLayout)
256
+ .filter((opt: any) => opt.position || opt.position === 0)
257
+ .map((i: any) => ({ height: i.height, key: i.key }))
258
+
259
+ for (let i = 0; i < optionsArray.length; i++) {
260
+ const opt = optionsArray[i];
261
+ if (scrollOffset <= optionsLayout?.header?.height) {
262
+ setSelectedOpt(null)
263
+ break
264
+ } else if (scrollOffset > optionsLayout?.header?.height && scrollOffset < (optionsLayout?.header?.height + opt.height)) {
265
+ setSelectedOpt(opt.key)
266
+ break
267
+ }
268
+ }
269
+
270
+ }
239
271
 
240
272
  useEffect(() => {
241
273
  const keyboardDidShowListener = Keyboard.addListener(
242
274
  'keyboardDidShow',
243
- () => {
244
- scrollViewRef?.current && scrollViewRef?.current?.scrollToEnd()
245
- setIsKeyboardShow(true);
246
- }
247
- );
248
- const keyboardDidHideListener = Keyboard.addListener(
249
- 'keyboardDidHide',
250
- () => {
251
- setIsKeyboardShow(false);
252
- }
275
+ () => { scrollViewRef?.current && scrollViewRef?.current?.scrollToEnd()}
253
276
  );
254
277
 
255
- return () => {
256
- keyboardDidHideListener.remove();
257
- keyboardDidShowListener.remove();
258
- };
278
+ return () => { keyboardDidShowListener.remove()};
259
279
  }, []);
260
280
 
261
281
  return (
@@ -263,9 +283,17 @@ export const ProductOptionsUI = (props: any) => {
263
283
  enabled
264
284
  behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
265
285
  >
266
- <ScrollView ref={scrollViewRef}>
267
- {!error && (
268
- <>
286
+ {!error && (
287
+ <ScrollView
288
+ ref={scrollViewRef}
289
+ stickyHeaderIndices={[1]}
290
+ scrollEventThrottle={16}
291
+ contentContainerStyle={{ paddingBottom: 60 }}
292
+ // onScroll={(e: any) => handleScroll(e)}
293
+ >
294
+ <View
295
+ onLayout={(event: any) => setOptionsLayout({ ...optionsLayout, header: event.nativeEvent.layout })}
296
+ >
269
297
  <WrapHeader>
270
298
  {loading && !product ? (
271
299
  <View style={styles.productHeaderSkeleton}>
@@ -316,19 +344,19 @@ export const ProductOptionsUI = (props: any) => {
316
344
  {product?.name || productCart.name}
317
345
  </OText>
318
346
  {((product?.sku && product?.sku !== '-1' && product?.sku !== '1') || (product?.estimated_person)) && (
319
- <OText size={14} style={{ flex: I18nManager.isRTL ? 1 : 0 }} color={'#909BA9'} mBottom={7}>
320
- {
321
- ((product?.sku && product?.sku !== '-1' && product?.sku !== '1') || (productCart?.sku && productCart?.sku !== '-1' && productCart?.sku !== '1'))
322
- && <>{t('SKU', 'Sku')}{' '}{product?.sku || productCart?.sku}</>
323
- }
324
- {product?.sku && product?.sku !== '-1' && product?.sku !== '1' && product?.estimated_person && (
325
- <>&nbsp;&#183;&nbsp;</>
326
- )}
327
- {product?.estimated_person
328
- && <>{product?.estimated_person}{' '}{t('ESTIMATED_PERSONS', 'persons')}</>
329
- }
330
- </OText>
331
- )}
347
+ <OText size={14} style={{ flex: I18nManager.isRTL ? 1 : 0 }} color={'#909BA9'} mBottom={7}>
348
+ {
349
+ ((product?.sku && product?.sku !== '-1' && product?.sku !== '1') || (productCart?.sku && productCart?.sku !== '-1' && productCart?.sku !== '1'))
350
+ && <>{t('SKU', 'Sku')}{' '}{product?.sku || productCart?.sku}</>
351
+ }
352
+ {product?.sku && product?.sku !== '-1' && product?.sku !== '1' && product?.estimated_person && (
353
+ <>&nbsp;&#183;&nbsp;</>
354
+ )}
355
+ {product?.estimated_person
356
+ && <>{product?.estimated_person}{' '}{t('ESTIMATED_PERSONS', 'persons')}</>
357
+ }
358
+ </OText>
359
+ )}
332
360
  <OText size={16} lineHeight={24} color={theme.colors.textNormal}>
333
361
  {productCart.price ? parsePrice(productCart.price) : ''}
334
362
  </OText>
@@ -340,269 +368,203 @@ export const ProductOptionsUI = (props: any) => {
340
368
  {product?.description || productCart?.description}
341
369
  </OText>
342
370
  </ProductDescription>
343
- {loading && !product ? (
344
- <>
345
- {[...Array(2)].map((item, i) => (
346
- <Placeholder
347
- key={i}
348
- style={{ marginBottom: 20 }}
349
- Animation={Fade}>
350
- <PlaceholderLine
351
- height={40}
352
- style={{ flex: 1, marginTop: 10 }}
353
- />
354
- {[...Array(3)].map((item, i) => (
355
- <View
356
- key={'place_key_' + i}
357
- style={{
358
- flexDirection: 'row',
359
- justifyContent: 'space-between',
360
- }}>
361
- <PlaceholderLine
362
- height={30}
363
- width={10}
364
- style={{ marginBottom: 20 }}
365
- />
366
- <PlaceholderLine
367
- height={30}
368
- width={50}
369
- style={{ marginBottom: 20 }}
370
- />
371
- <PlaceholderLine
372
- height={30}
373
- width={30}
374
- style={{ marginBottom: 20 }}
375
- />
376
- </View>
377
- ))}
378
- </Placeholder>
379
- ))}
380
- </>
381
- ) : (
382
- <ProductEditions>
383
- {product?.extras.map((extra: any) =>
384
- <ExtraOptions key={extra.id} options={extra.options} />
385
- )}
371
+ </WrapContent>
372
+ </View>
386
373
 
387
- {selOpt == 0 ? (
388
- <>
389
- {product?.ingredients.length > 0 && (
390
- <View style={styles.optionContainer}>
391
- <SectionTitle>
392
- <OText size={16}>
393
- {t('INGREDIENTS', 'Ingredients')}
394
- </OText>
395
- </SectionTitle>
396
- <WrapperIngredients
397
- style={{
398
- backgroundColor:
399
- isSoldOut || maxProductQuantity <= 0
400
- ? 'hsl(0, 0%, 72%)'
401
- : theme.colors.white,
402
- }}>
403
- {product?.ingredients.map((ingredient: any) => (
404
- <ProductIngredient
405
- key={ingredient.id}
406
- ingredient={ingredient}
407
- state={
408
- productCart.ingredients[`id:${ingredient.id}`]
409
- }
410
- onChange={handleChangeIngredientState}
411
- />
412
- ))}
413
- </WrapperIngredients>
414
- </View>
415
- )}
416
- {product?.extras.map((extra: any) =>
417
- extra.options.map((option: any) => {
418
- const currentState =
419
- productCart.options[`id:${option.id}`] || {};
420
- return (
421
- <React.Fragment key={`popt_${option.id}`}>
422
- {showOption(option) && (
423
- <View style={styles.optionContainer}>
424
- <ProductOption
425
- option={option}
426
- currentState={currentState}
427
- error={errors[`id:${option.id}`]}>
428
- <WrapperSubOption
429
- style={{
430
- backgroundColor: isError(option.id),
431
- }}>
432
- {option.suboptions.map(
433
- (suboption: any) => {
434
- const currentState =
435
- productCart.options[
436
- `id:${option.id}`
437
- ]?.suboptions[
438
- `id:${suboption.id}`
439
- ] || {};
440
- const balance =
441
- productCart.options[
442
- `id:${option.id}`
443
- ]?.balance || 0;
444
- return (
445
- <ProductOptionSubOption
446
- key={suboption.id}
447
- onChange={
448
- handleChangeSuboptionState
449
- }
450
- balance={balance}
451
- option={option}
452
- suboption={suboption}
453
- state={currentState}
454
- disabled={
455
- isSoldOut ||
456
- maxProductQuantity <= 0
457
- }
458
- />
459
- );
460
- },
461
- )}
462
- </WrapperSubOption>
463
- </ProductOption>
464
- </View>
465
- )}
466
- </React.Fragment>
467
- );
468
- }),
469
- )}
470
- </>
471
- ) : (
472
- <>
473
- {selOpt == -1 ? (
474
- <View style={styles.optionContainer}>
475
- <SectionTitle>
476
- <OText size={16}>
477
- {t('INGREDIENTS', 'Ingredients')}
478
- </OText>
479
- </SectionTitle>
480
- <WrapperIngredients
481
- style={{
482
- backgroundColor:
483
- isSoldOut || maxProductQuantity <= 0
484
- ? 'hsl(0, 0%, 72%)'
485
- : theme.colors.white,
486
- }}>
487
- {product?.ingredients.map((ingredient: any) => (
488
- <ProductIngredient
489
- key={ingredient.id}
490
- ingredient={ingredient}
491
- state={
492
- productCart.ingredients[`id:${ingredient.id}`]
493
- }
494
- onChange={handleChangeIngredientState}
495
- />
496
- ))}
497
- </WrapperIngredients>
498
- </View>
499
- ) : (
500
- <>
501
- {product?.extras.map((extra: any) =>
502
- extra.options.map((option: any) => {
503
- if (
504
- option.id == selOpt ||
505
- (hasRespected(
506
- extra.options,
507
- option.respect_to,
508
- ) &&
509
- showOption(option))
510
- ) {
511
- const currentState =
512
- productCart.options[`id:${option.id}`] || {};
513
- return (
514
- <React.Fragment key={option.id}>
515
- {showOption(option) && (
516
- <View style={styles.optionContainer}>
517
- <ProductOption
518
- option={option}
519
- currentState={currentState}
520
- error={errors[`id:${option.id}`]}>
521
- <WrapperSubOption
522
- style={{
523
- backgroundColor: isError(
524
- option.id,
525
- ),
526
- }}>
527
- {option.suboptions.map(
528
- (suboption: any) => {
529
- const currentState =
530
- productCart.options[
531
- `id:${option.id}`
532
- ]?.suboptions[
533
- `id:${suboption.id}`
534
- ] || {};
535
- const balance =
536
- productCart.options[
537
- `id:${option.id}`
538
- ]?.balance || 0;
539
- return (
540
- <ProductOptionSubOption
541
- key={suboption.id}
542
- onChange={
543
- handleChangeSuboptionState
544
- }
545
- balance={balance}
546
- option={option}
547
- suboption={suboption}
548
- state={currentState}
549
- disabled={
550
- isSoldOut ||
551
- maxProductQuantity <= 0
552
- }
553
- />
554
- );
555
- },
556
- )}
557
- </WrapperSubOption>
558
- </ProductOption>
559
- </View>
560
- )}
561
- </React.Fragment>
562
- );
563
- }
564
- }),
565
- )}
566
- </>
374
+ <WrapContent>
375
+ {loading && !product && (
376
+ <>
377
+ {[...Array(2)].map((item, i) => (
378
+ <Placeholder
379
+ key={i}
380
+ style={{ marginBottom: 20 }}
381
+ Animation={Fade}>
382
+ <PlaceholderLine
383
+ height={40}
384
+ style={{ flex: 1, marginTop: 10 }}
385
+ />
386
+ {[...Array(3)].map((item, i) => (
387
+ <View
388
+ key={'place_key_' + i}
389
+ style={{
390
+ flexDirection: 'row',
391
+ justifyContent: 'space-between',
392
+ }}>
393
+ <PlaceholderLine
394
+ height={30}
395
+ width={10}
396
+ style={{ marginBottom: 20 }}
397
+ />
398
+ <PlaceholderLine
399
+ height={30}
400
+ width={50}
401
+ style={{ marginBottom: 20 }}
402
+ />
403
+ <PlaceholderLine
404
+ height={30}
405
+ width={30}
406
+ style={{ marginBottom: 20 }}
407
+ />
408
+ </View>
409
+ ))}
410
+ </Placeholder>
411
+ ))}
412
+ </>
413
+ )}
414
+
415
+ {!loading && product && extraOptions?.length > 0 && <ExtraOptions options={extraOptions} />}
416
+ </WrapContent>
417
+
418
+ <WrapContent>
419
+ {!loading && product && (
420
+ <ProductEditions>
421
+ <View
422
+ onLayout={(event: any) => setOptionsLayout({ ...optionsLayout, wrapper: event.nativeEvent.layout })}
423
+ >
424
+ {product?.ingredients.length > 0 && (
425
+ <View
426
+ style={styles.optionContainer}
427
+ onLayout={(event: any) => setOptionsLayout(
428
+ {
429
+ ...optionsLayout,
430
+ ingredients: {
431
+ ...event.nativeEvent.layout,
432
+ position: 0,
433
+ key: 'ingredients'
434
+ }
435
+ }
567
436
  )}
568
- </>
437
+ >
438
+ <SectionTitle>
439
+ <OText size={16}>
440
+ {t('INGREDIENTS', 'Ingredients')}
441
+ </OText>
442
+ </SectionTitle>
443
+ <WrapperIngredients
444
+ style={{
445
+ backgroundColor:
446
+ isSoldOut || maxProductQuantity <= 0
447
+ ? 'hsl(0, 0%, 72%)'
448
+ : theme.colors.white,
449
+ }}>
450
+ {product?.ingredients.map((ingredient: any) => (
451
+ <ProductIngredient
452
+ key={ingredient.id}
453
+ ingredient={ingredient}
454
+ state={
455
+ productCart.ingredients[`id:${ingredient.id}`]
456
+ }
457
+ onChange={handleChangeIngredientState}
458
+ />
459
+ ))}
460
+ </WrapperIngredients>
461
+ </View>
569
462
  )}
570
- <ProductComment>
571
- <SectionTitle>
572
- <OText size={16} weight={'600'} lineHeight={24}>
573
- {t('SPECIAL_COMMENT', 'Special comment')}
574
- </OText>
575
- </SectionTitle>
576
- <OInput
577
- multiline={true}
578
- numberOfLines={10}
579
- placeholder={t('SPECIAL_COMMENT', 'Special comment')}
580
- value={productCart.comment}
581
- onChange={(val: string) =>
582
- handleChangeCommentState({ target: { value: val } })
583
- }
584
- isDisabled={
585
- !(productCart && !isSoldOut && maxProductQuantity)
586
- }
587
- style={{
588
- height: 100,
589
- justifyContent: "flex-end",
590
- alignItems: 'flex-start',
591
- borderWidth: 1,
592
- borderRadius: 8,
593
- borderColor: theme.colors.border,
594
- }}
595
- />
596
- </ProductComment>
597
- </ProductEditions>
598
- )}
599
- </WrapContent>
600
- </>
601
- )}
602
- {error && error.length > 0 && (
603
- <NotFoundSource content={error[0]?.message || error[0]} />
604
- )}
605
- </ScrollView>
463
+ {product?.extras.map((extra: any) => extra.options.map((option: any) => {
464
+ const currentState = productCart.options[`id:${option.id}`] || {};
465
+ return (
466
+ <React.Fragment key={`popt_${option.id}`}>
467
+ {showOption(option) && (
468
+ <View
469
+ style={styles.optionContainer}
470
+ onLayout={
471
+ (event: any) => setOptionsLayout(
472
+ {
473
+ ...optionsLayout,
474
+ [`opt_${option.id}`]: {
475
+ ...event.nativeEvent.layout,
476
+ position: extraOptions.map((i: any) => i.id).indexOf(option.id) + (product?.ingredients.length > 0 ? 1 : 0),
477
+ key: `opt_${option.id}`
478
+ }
479
+ }
480
+ )
481
+ }
482
+ >
483
+ <ProductOption
484
+ option={option}
485
+ currentState={currentState}
486
+ error={errors[`id:${option.id}`]}
487
+ >
488
+ <WrapperSubOption
489
+ style={{
490
+ backgroundColor: isError(option.id),
491
+ }}>
492
+ {option.suboptions.map(
493
+ (suboption: any) => {
494
+ const currentState =
495
+ productCart.options[
496
+ `id:${option.id}`
497
+ ]?.suboptions[
498
+ `id:${suboption.id}`
499
+ ] || {};
500
+ const balance =
501
+ productCart.options[
502
+ `id:${option.id}`
503
+ ]?.balance || 0;
504
+ return (
505
+ <ProductOptionSubOption
506
+ key={suboption.id}
507
+ onChange={
508
+ handleChangeSuboptionState
509
+ }
510
+ balance={balance}
511
+ option={option}
512
+ suboption={suboption}
513
+ state={currentState}
514
+ disabled={
515
+ isSoldOut ||
516
+ maxProductQuantity <= 0
517
+ }
518
+ />
519
+ );
520
+ },
521
+ )}
522
+ </WrapperSubOption>
523
+ </ProductOption>
524
+ </View>
525
+ )}
526
+ </React.Fragment>
527
+ );
528
+ }))}
529
+ </View>
530
+
531
+ <ProductComment>
532
+ <SectionTitle>
533
+ <OText size={16} weight={'600'} lineHeight={24}>
534
+ {t('SPECIAL_COMMENT', 'Special comment')}
535
+ </OText>
536
+ </SectionTitle>
537
+ <OInput
538
+ multiline={true}
539
+ numberOfLines={10}
540
+ placeholder={t('SPECIAL_COMMENT', 'Special comment')}
541
+ value={productCart.comment}
542
+ onChange={(val: string) =>
543
+ handleChangeCommentState({ target: { value: val } })
544
+ }
545
+ isDisabled={
546
+ !(productCart && !isSoldOut && maxProductQuantity)
547
+ }
548
+ style={{
549
+ height: 100,
550
+ justifyContent: "flex-end",
551
+ alignItems: 'flex-start',
552
+ borderWidth: 1,
553
+ borderRadius: 8,
554
+ borderColor: theme.colors.border,
555
+ }}
556
+ />
557
+ </ProductComment>
558
+ </ProductEditions>
559
+ )}
560
+ </WrapContent>
561
+ </ScrollView>
562
+ )}
563
+
564
+ {error && error.length > 0 && (
565
+ <NotFoundSource content={error[0]?.message || error[0]} />
566
+ )}
567
+
606
568
  {!loading && !error && product && (
607
569
  <ProductActions>
608
570
  <OText size={16} lineHeight={24} weight={'600'}>