@tradly/asset 1.0.25 → 1.0.27
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/README.md +812 -57
- package/dist/core/MediaApiService.js +7 -6
- package/dist/esm/core/MediaApiService.js +7 -6
- package/dist/esm/native/FileUpload.native.js +90 -13
- package/dist/esm/native/ImagesSkeleton.native.js +2 -2
- package/dist/native/FileUpload.native.js +90 -13
- package/dist/native/ImagesSkeleton.native.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# @tradly/asset
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
videos, files) with Tradly authentication support.
|
|
3
|
+
A cross-platform React component package for uploading and selecting media
|
|
4
|
+
(images, videos, files) with Tradly authentication support. Works seamlessly on
|
|
5
|
+
both Web (React) and Mobile (React Native).
|
|
5
6
|
|
|
6
7
|
## Features
|
|
7
8
|
|
|
@@ -100,6 +101,11 @@ function MyComponent() {
|
|
|
100
101
|
import React, { useState } from "react";
|
|
101
102
|
import { View, Button } from "react-native";
|
|
102
103
|
import { MediaPopup, MediaApiService } from "@tradly/asset";
|
|
104
|
+
import {
|
|
105
|
+
CameraIcon,
|
|
106
|
+
VideoIcon,
|
|
107
|
+
UploadIcon,
|
|
108
|
+
} from "@tradly/asset/native/Icons.native";
|
|
103
109
|
import { launchImageLibrary } from "react-native-image-picker";
|
|
104
110
|
|
|
105
111
|
function MyComponent() {
|
|
@@ -121,6 +127,10 @@ function MyComponent() {
|
|
|
121
127
|
const picker = (options) => {
|
|
122
128
|
return new Promise((resolve) => {
|
|
123
129
|
launchImageLibrary(options, (response) => {
|
|
130
|
+
if (response.didCancel) {
|
|
131
|
+
resolve([]);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
124
134
|
resolve(response.assets || []);
|
|
125
135
|
});
|
|
126
136
|
});
|
|
@@ -137,9 +147,14 @@ function MyComponent() {
|
|
|
137
147
|
isOpen={isOpen}
|
|
138
148
|
onClose={() => setIsOpen(false)}
|
|
139
149
|
onSelect={handleSelect}
|
|
140
|
-
options={["image", "video"]}
|
|
150
|
+
options={["image", "video", "file"]}
|
|
141
151
|
apiService={apiService}
|
|
142
152
|
picker={picker} // REQUIRED: Your file picker function
|
|
153
|
+
icons={{
|
|
154
|
+
image: <CameraIcon />,
|
|
155
|
+
video: <VideoIcon />,
|
|
156
|
+
default: <UploadIcon />,
|
|
157
|
+
}}
|
|
143
158
|
/>
|
|
144
159
|
</View>
|
|
145
160
|
);
|
|
@@ -222,17 +237,76 @@ You can customize picker options based on the accept type:
|
|
|
222
237
|
```jsx
|
|
223
238
|
<MediaPopup
|
|
224
239
|
picker={yourPickerFunction}
|
|
225
|
-
pickerOptions={(accept) =>
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
240
|
+
pickerOptions={(accept) => {
|
|
241
|
+
const isImage = accept?.includes("image");
|
|
242
|
+
const isVideo = accept?.includes("video");
|
|
243
|
+
const isFile = accept?.includes("file");
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
mediaType: isImage ? "photo" : isVideo ? "video" : "mixed",
|
|
247
|
+
quality: 0.9,
|
|
248
|
+
allowsMultiple: true,
|
|
249
|
+
// Add any other options your picker library supports
|
|
250
|
+
};
|
|
251
|
+
}}
|
|
231
252
|
apiService={apiService}
|
|
232
253
|
// ... other props
|
|
233
254
|
/>
|
|
234
255
|
```
|
|
235
256
|
|
|
257
|
+
#### File Picker Response Format
|
|
258
|
+
|
|
259
|
+
The picker function should return an array of file objects. The package supports
|
|
260
|
+
multiple formats:
|
|
261
|
+
|
|
262
|
+
**react-native-image-picker format:**
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
{
|
|
266
|
+
assets: [
|
|
267
|
+
{
|
|
268
|
+
uri: "file:///path/to/file.jpg",
|
|
269
|
+
fileName: "image.jpg",
|
|
270
|
+
mimeType: "image/jpeg",
|
|
271
|
+
fileSize: 12345,
|
|
272
|
+
},
|
|
273
|
+
];
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**expo-image-picker format:**
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
{
|
|
281
|
+
assets: [
|
|
282
|
+
{
|
|
283
|
+
uri: "file:///path/to/file.jpg",
|
|
284
|
+
fileName: "image.jpg",
|
|
285
|
+
mimeType: "image/jpeg",
|
|
286
|
+
},
|
|
287
|
+
];
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Custom format:**
|
|
292
|
+
|
|
293
|
+
```javascript
|
|
294
|
+
[
|
|
295
|
+
{
|
|
296
|
+
uri: "file:///path/to/file.jpg",
|
|
297
|
+
name: "image.jpg", // or fileName
|
|
298
|
+
type: "image/jpeg", // or mimeType
|
|
299
|
+
},
|
|
300
|
+
];
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The package will automatically:
|
|
304
|
+
|
|
305
|
+
- Extract `uri` from `uri` or `path`
|
|
306
|
+
- Extract file name from `fileName`, `name`, or parse from URI
|
|
307
|
+
- Detect MIME type from `mimeType`, `type`, or file extension
|
|
308
|
+
- Handle missing properties with smart fallbacks
|
|
309
|
+
|
|
236
310
|
### React Native UI Differences
|
|
237
311
|
|
|
238
312
|
- **Bottom Sheet**: Media gallery opens as a bottom sheet (slides up from
|
|
@@ -242,6 +316,11 @@ You can customize picker options based on the accept type:
|
|
|
242
316
|
`TouchableOpacity`, `FlatList`, `Image`)
|
|
243
317
|
- **File Upload**: Requires `picker` prop to be provided
|
|
244
318
|
- **Theme System**: Full theme support for easy dark/light mode customization
|
|
319
|
+
- **Icons**: Support for custom icons per media type (image, video, file)
|
|
320
|
+
- **File Handling**: Automatically handles `file://` URIs and converts them to
|
|
321
|
+
blobs for S3 upload
|
|
322
|
+
- **MIME Type Detection**: Comprehensive MIME type detection from file
|
|
323
|
+
extensions and picker results
|
|
245
324
|
|
|
246
325
|
### React Native Theme & Customization
|
|
247
326
|
|
|
@@ -268,68 +347,313 @@ const lightTheme = createTheme({ mode: "light" });
|
|
|
268
347
|
/>;
|
|
269
348
|
```
|
|
270
349
|
|
|
271
|
-
####
|
|
350
|
+
#### Theme Examples
|
|
351
|
+
|
|
352
|
+
**Example 1: Complete Custom Theme**
|
|
272
353
|
|
|
273
354
|
```jsx
|
|
274
355
|
import { createTheme } from "@tradly/asset/native/theme";
|
|
275
356
|
|
|
276
357
|
const customTheme = createTheme({
|
|
277
|
-
mode: "dark",
|
|
358
|
+
mode: "dark",
|
|
278
359
|
colors: {
|
|
279
360
|
// Primary colors
|
|
280
361
|
primary: "#6366F1",
|
|
281
|
-
|
|
282
|
-
|
|
362
|
+
primaryLight: "#818CF8",
|
|
363
|
+
primaryDark: "#4F46E5",
|
|
283
364
|
|
|
284
365
|
// Backgrounds
|
|
285
366
|
background: "#0F172A",
|
|
286
367
|
backgroundSecondary: "#1E293B",
|
|
368
|
+
backgroundTertiary: "#334155",
|
|
287
369
|
|
|
288
370
|
// Text
|
|
289
371
|
text: "#F1F5F9",
|
|
290
372
|
textSecondary: "#CBD5E1",
|
|
373
|
+
textTertiary: "#94A3B8",
|
|
291
374
|
|
|
292
375
|
// Tabs
|
|
293
376
|
tabActive: "#6366F1",
|
|
294
377
|
tabInactive: "#94A3B8",
|
|
378
|
+
tabIndicator: "#6366F1",
|
|
295
379
|
tabBackground: "#0F172A",
|
|
296
380
|
|
|
297
|
-
//
|
|
298
|
-
|
|
381
|
+
// Upload
|
|
382
|
+
uploadBorder: "#6366F1",
|
|
383
|
+
uploadBackground: "#1E293B",
|
|
384
|
+
uploadIconBackground: "#6366F1",
|
|
385
|
+
uploadText: "#F1F5F9",
|
|
299
386
|
|
|
300
387
|
// Pagination
|
|
388
|
+
paginationBackground: "#1E293B",
|
|
301
389
|
paginationButtonActive: "#6366F1",
|
|
302
390
|
paginationTextActive: "#FFFFFF",
|
|
303
391
|
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
392
|
+
// And all other colors...
|
|
393
|
+
},
|
|
394
|
+
spacing: {
|
|
395
|
+
xs: 4,
|
|
396
|
+
sm: 8,
|
|
397
|
+
md: 12,
|
|
398
|
+
lg: 16,
|
|
399
|
+
xl: 20,
|
|
400
|
+
xxl: 24,
|
|
309
401
|
},
|
|
310
|
-
|
|
402
|
+
radius: {
|
|
403
|
+
sm: 6,
|
|
404
|
+
md: 8,
|
|
405
|
+
lg: 12,
|
|
406
|
+
xl: 16,
|
|
407
|
+
xxl: 20,
|
|
408
|
+
},
|
|
409
|
+
bottomSheetHeight: 0.92,
|
|
311
410
|
});
|
|
312
411
|
|
|
313
412
|
<MediaPopup theme={customTheme} ... />
|
|
314
413
|
```
|
|
315
414
|
|
|
316
|
-
|
|
415
|
+
**Example 2: Minimal Override (Only Colors)**
|
|
317
416
|
|
|
318
|
-
|
|
417
|
+
```jsx
|
|
418
|
+
// Only override specific colors, rest uses defaults
|
|
419
|
+
const minimalTheme = createTheme({
|
|
420
|
+
colors: {
|
|
421
|
+
primary: "#FF6B6B",
|
|
422
|
+
tabActive: "#FF6B6B",
|
|
423
|
+
tabIndicator: "#FF6B6B",
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
```
|
|
319
427
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
- **
|
|
428
|
+
#### Complete Theme Reference
|
|
429
|
+
|
|
430
|
+
**All Available Theme Properties:**
|
|
431
|
+
|
|
432
|
+
| Category | Property | Type | Description | Default (Light) |
|
|
433
|
+
| ----------------------- | ------------------------ | -------- | --------------------- | ----------------- |
|
|
434
|
+
| **Colors - Primary** | `primary` | `string` | Main brand color | `#3B3269` |
|
|
435
|
+
| | `primaryLight` | `string` | Lighter variant | `#5A4A8A` |
|
|
436
|
+
| | `primaryDark` | `string` | Darker variant | `#2A1F4A` |
|
|
437
|
+
| **Colors - Background** | `background` | `string` | Main background | `#FFFFFF` |
|
|
438
|
+
| | `backgroundSecondary` | `string` | Secondary background | `#F5F5F5` |
|
|
439
|
+
| | `backgroundTertiary` | `string` | Tertiary background | `#E5E5E5` |
|
|
440
|
+
| **Colors - Text** | `text` | `string` | Primary text | `#000000` |
|
|
441
|
+
| | `textSecondary` | `string` | Secondary text | `#4F4F4F` |
|
|
442
|
+
| | `textTertiary` | `string` | Tertiary text | `#6B7280` |
|
|
443
|
+
| | `textInverse` | `string` | Inverse text | `#FFFFFF` |
|
|
444
|
+
| **Colors - Border** | `border` | `string` | Default border | `#E5E5E5` |
|
|
445
|
+
| | `borderLight` | `string` | Light border | `#F0F0F0` |
|
|
446
|
+
| | `borderDark` | `string` | Dark border | `#D1D5DB` |
|
|
447
|
+
| **Colors - Overlay** | `overlay` | `string` | Modal overlay | `rgba(0,0,0,0.5)` |
|
|
448
|
+
| **Colors - Tabs** | `tabActive` | `string` | Active tab text | `#3B3269` |
|
|
449
|
+
| | `tabInactive` | `string` | Inactive tab text | `#4F4F4F` |
|
|
450
|
+
| | `tabIndicator` | `string` | Tab indicator line | `#3B3269` |
|
|
451
|
+
| | `tabBackground` | `string` | Tab container | `#FFFFFF` |
|
|
452
|
+
| **Colors - Items** | `itemBackground` | `string` | Media item background | `#FFFFFF` |
|
|
453
|
+
| | `itemShadow` | `string` | Item shadow color | `#000000` |
|
|
454
|
+
| **Colors - Pagination** | `paginationBackground` | `string` | Container background | `#F5F5F5` |
|
|
455
|
+
| | `paginationButton` | `string` | Button background | `#FFFFFF` |
|
|
456
|
+
| | `paginationButtonActive` | `string` | Active button | `#3B3269` |
|
|
457
|
+
| | `paginationText` | `string` | Text color | `#6B7280` |
|
|
458
|
+
| | `paginationTextActive` | `string` | Active text | `#FFFFFF` |
|
|
459
|
+
| | `paginationBorder` | `string` | Border color | `#D1D5DB` |
|
|
460
|
+
| **Colors - Upload** | `uploadBorder` | `string` | Button border | `#3B3269` |
|
|
461
|
+
| | `uploadBackground` | `string` | Button background | `#FFFFFF` |
|
|
462
|
+
| | `uploadIconBackground` | `string` | Icon container | `#3B3269` |
|
|
463
|
+
| | `uploadText` | `string` | Button text | `#000000` |
|
|
464
|
+
| **Colors - Loading** | `loadingBackground` | `string` | Skeleton background | `#E5E5E5` |
|
|
465
|
+
| | `loadingText` | `string` | Loading text | `#666666` |
|
|
466
|
+
| **Spacing** | `xs` | `number` | Extra small (4px) | `4` |
|
|
467
|
+
| | `sm` | `number` | Small (8px) | `8` |
|
|
468
|
+
| | `md` | `number` | Medium (12px) | `12` |
|
|
469
|
+
| | `lg` | `number` | Large (16px) | `16` |
|
|
470
|
+
| | `xl` | `number` | Extra large (20px) | `20` |
|
|
471
|
+
| | `xxl` | `number` | 2X extra large (24px) | `24` |
|
|
472
|
+
| **Radius** | `sm` | `number` | Small radius (6px) | `6` |
|
|
473
|
+
| | `md` | `number` | Medium radius (8px) | `8` |
|
|
474
|
+
| | `lg` | `number` | Large radius (12px) | `12` |
|
|
475
|
+
| | `xl` | `number` | Extra large (16px) | `16` |
|
|
476
|
+
| | `xxl` | `number` | 2X extra large (20px) | `20` |
|
|
477
|
+
| **Typography** | `title.fontSize` | `number` | Title font size | `24` |
|
|
478
|
+
| | `title.fontWeight` | `string` | Title weight | `"bold"` |
|
|
479
|
+
| | `subtitle.fontSize` | `number` | Subtitle size | `18` |
|
|
480
|
+
| | `subtitle.fontWeight` | `string` | Subtitle weight | `"600"` |
|
|
481
|
+
| | `body.fontSize` | `number` | Body font size | `16` |
|
|
482
|
+
| | `body.fontWeight` | `string` | Body weight | `"500"` |
|
|
483
|
+
| | `caption.fontSize` | `number` | Caption size | `14` |
|
|
484
|
+
| | `caption.fontWeight` | `string` | Caption weight | `"400"` |
|
|
485
|
+
| **Other** | `bottomSheetHeight` | `number` | Sheet height (0-1) | `0.9` |
|
|
486
|
+
| | `mode` | `string` | `"light"` or `"dark"` | `"light"` |
|
|
487
|
+
|
|
488
|
+
#### Complete Theme Customization
|
|
489
|
+
|
|
490
|
+
The theme system supports comprehensive customization of colors, spacing,
|
|
491
|
+
typography, and more:
|
|
492
|
+
|
|
493
|
+
**Colors:**
|
|
494
|
+
|
|
495
|
+
```jsx
|
|
496
|
+
const customTheme = createTheme({
|
|
497
|
+
mode: "light", // or "dark"
|
|
498
|
+
colors: {
|
|
499
|
+
// Primary colors
|
|
500
|
+
primary: "#3B3269", // Main brand color
|
|
501
|
+
primaryLight: "#5A4A8A", // Lighter variant
|
|
502
|
+
primaryDark: "#2A1F4A", // Darker variant
|
|
503
|
+
|
|
504
|
+
// Background colors
|
|
505
|
+
background: "#FFFFFF", // Main background
|
|
506
|
+
backgroundSecondary: "#F5F5F5", // Secondary background
|
|
507
|
+
backgroundTertiary: "#E5E5E5", // Tertiary background
|
|
508
|
+
|
|
509
|
+
// Text colors
|
|
510
|
+
text: "#000000", // Primary text
|
|
511
|
+
textSecondary: "#4F4F4F", // Secondary text
|
|
512
|
+
textTertiary: "#6B7280", // Tertiary text
|
|
513
|
+
textInverse: "#FFFFFF", // Inverse text (for dark backgrounds)
|
|
514
|
+
|
|
515
|
+
// Border colors
|
|
516
|
+
border: "#E5E5E5", // Default border
|
|
517
|
+
borderLight: "#F0F0F0", // Light border
|
|
518
|
+
borderDark: "#D1D5DB", // Dark border
|
|
519
|
+
|
|
520
|
+
// Overlay
|
|
521
|
+
overlay: "rgba(0, 0, 0, 0.5)", // Modal overlay
|
|
522
|
+
|
|
523
|
+
// Tab colors
|
|
524
|
+
tabActive: "#3B3269", // Active tab text
|
|
525
|
+
tabInactive: "#4F4F4F", // Inactive tab text
|
|
526
|
+
tabIndicator: "#3B3269", // Tab indicator line
|
|
527
|
+
tabBackground: "#FFFFFF", // Tab container background
|
|
528
|
+
|
|
529
|
+
// Media item colors
|
|
530
|
+
itemBackground: "#FFFFFF", // Image/video item background
|
|
531
|
+
itemShadow: "#000000", // Item shadow color
|
|
532
|
+
|
|
533
|
+
// Pagination colors
|
|
534
|
+
paginationBackground: "#F5F5F5", // Pagination container
|
|
535
|
+
paginationButton: "#FFFFFF", // Page button background
|
|
536
|
+
paginationButtonActive: "#3B3269", // Active page button
|
|
537
|
+
paginationText: "#6B7280", // Page number text
|
|
538
|
+
paginationTextActive: "#FFFFFF", // Active page text
|
|
539
|
+
paginationBorder: "#D1D5DB", // Pagination border
|
|
540
|
+
|
|
541
|
+
// Upload button colors
|
|
542
|
+
uploadBorder: "#3B3269", // Upload button border
|
|
543
|
+
uploadBackground: "#FFFFFF", // Upload button background
|
|
544
|
+
uploadIconBackground: "#3B3269", // Icon container background
|
|
545
|
+
uploadText: "#000000", // Upload button text
|
|
546
|
+
|
|
547
|
+
// Loading state colors
|
|
548
|
+
loadingBackground: "#E5E5E5", // Loading skeleton background
|
|
549
|
+
loadingText: "#666666", // Loading text color
|
|
550
|
+
},
|
|
551
|
+
// Spacing (in pixels)
|
|
552
|
+
spacing: {
|
|
553
|
+
xs: 4, // Extra small
|
|
554
|
+
sm: 8, // Small
|
|
555
|
+
md: 12, // Medium
|
|
556
|
+
lg: 16, // Large
|
|
557
|
+
xl: 20, // Extra large
|
|
558
|
+
xxl: 24, // 2X extra large
|
|
559
|
+
},
|
|
560
|
+
// Border radius (in pixels)
|
|
561
|
+
radius: {
|
|
562
|
+
sm: 6, // Small radius
|
|
563
|
+
md: 8, // Medium radius
|
|
564
|
+
lg: 12, // Large radius
|
|
565
|
+
xl: 16, // Extra large radius
|
|
566
|
+
xxl: 20, // 2X extra large radius
|
|
567
|
+
},
|
|
568
|
+
// Typography
|
|
569
|
+
typography: {
|
|
570
|
+
title: {
|
|
571
|
+
fontSize: 24,
|
|
572
|
+
fontWeight: "bold",
|
|
573
|
+
},
|
|
574
|
+
subtitle: {
|
|
575
|
+
fontSize: 18,
|
|
576
|
+
fontWeight: "600",
|
|
577
|
+
},
|
|
578
|
+
body: {
|
|
579
|
+
fontSize: 16,
|
|
580
|
+
fontWeight: "500",
|
|
581
|
+
},
|
|
582
|
+
caption: {
|
|
583
|
+
fontSize: 14,
|
|
584
|
+
fontWeight: "400",
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
// Bottom sheet height (0.85 = 85%, 0.9 = 90%, etc.)
|
|
588
|
+
bottomSheetHeight: 0.9,
|
|
589
|
+
});
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
**Partial Theme Override:**
|
|
593
|
+
|
|
594
|
+
You can override only specific parts of the theme - other values will use
|
|
595
|
+
defaults:
|
|
596
|
+
|
|
597
|
+
```jsx
|
|
598
|
+
// Only change primary color and background
|
|
599
|
+
const brandTheme = createTheme({
|
|
600
|
+
colors: {
|
|
601
|
+
primary: "#6366F1",
|
|
602
|
+
tabActive: "#6366F1",
|
|
603
|
+
tabIndicator: "#6366F1",
|
|
604
|
+
background: "#F8F9FA",
|
|
605
|
+
},
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
// Only change spacing
|
|
609
|
+
const compactTheme = createTheme({
|
|
610
|
+
spacing: {
|
|
611
|
+
md: 8, // Reduce medium spacing
|
|
612
|
+
lg: 12, // Reduce large spacing
|
|
613
|
+
},
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
// Only change bottom sheet height
|
|
617
|
+
const tallTheme = createTheme({
|
|
618
|
+
bottomSheetHeight: 0.95, // 95% of screen
|
|
619
|
+
});
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
#### Dark Mode Example
|
|
623
|
+
|
|
624
|
+
```jsx
|
|
625
|
+
import { createTheme } from "@tradly/asset/native/theme";
|
|
626
|
+
|
|
627
|
+
const darkTheme = createTheme({
|
|
628
|
+
mode: "dark",
|
|
629
|
+
colors: {
|
|
630
|
+
primary: "#6366F1",
|
|
631
|
+
background: "#0F172A",
|
|
632
|
+
text: "#F1F5F9",
|
|
633
|
+
tabActive: "#6366F1",
|
|
634
|
+
tabInactive: "#94A3B8",
|
|
635
|
+
// ... customize other colors
|
|
636
|
+
},
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
<MediaPopup theme={darkTheme} ... />
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
#### Brand Color Customization
|
|
643
|
+
|
|
644
|
+
```jsx
|
|
645
|
+
const brandTheme = createTheme({
|
|
646
|
+
colors: {
|
|
647
|
+
// Your brand colors
|
|
648
|
+
primary: "#FF6B6B",
|
|
649
|
+
tabActive: "#FF6B6B",
|
|
650
|
+
tabIndicator: "#FF6B6B",
|
|
651
|
+
uploadBorder: "#FF6B6B",
|
|
652
|
+
uploadIconBackground: "#FF6B6B",
|
|
653
|
+
paginationButtonActive: "#FF6B6B",
|
|
654
|
+
},
|
|
655
|
+
});
|
|
656
|
+
```
|
|
333
657
|
|
|
334
658
|
#### Customizing Bottom Sheet Height
|
|
335
659
|
|
|
@@ -343,20 +667,30 @@ All colors are customizable via the theme object:
|
|
|
343
667
|
|
|
344
668
|
#### Individual Component Styling
|
|
345
669
|
|
|
346
|
-
You can
|
|
670
|
+
You can override individual component styles in addition to theme:
|
|
347
671
|
|
|
348
672
|
```jsx
|
|
349
673
|
<MediaPopup
|
|
350
674
|
theme={darkTheme}
|
|
351
|
-
// Override specific styles
|
|
675
|
+
// Override specific styles (these override theme values)
|
|
352
676
|
containerStyle={{ padding: 24 }}
|
|
353
677
|
headerStyle={{ paddingBottom: 16 }}
|
|
354
|
-
titleStyle={{ fontSize: 28 }}
|
|
678
|
+
titleStyle={{ fontSize: 28, color: "#FFFFFF" }}
|
|
355
679
|
closeButtonStyle={{ padding: 12 }}
|
|
680
|
+
closeButtonTextStyle={{ color: "#FFFFFF" }}
|
|
681
|
+
// Tab styles
|
|
356
682
|
tabListStyle={{ paddingHorizontal: 8 }}
|
|
357
683
|
tabButtonStyle={{ paddingVertical: 16 }}
|
|
358
684
|
tabButtonActiveStyle={{ backgroundColor: "#F0F0F0" }}
|
|
359
685
|
tabButtonTextActiveStyle={{ fontWeight: "700" }}
|
|
686
|
+
tabButtonTextInactiveStyle={{ color: "#999999" }}
|
|
687
|
+
tabIndicatorStyle={{ height: 3 }}
|
|
688
|
+
// Gallery styles
|
|
689
|
+
gridStyle={{ padding: 8 }}
|
|
690
|
+
imageItemStyle={{ borderRadius: 12 }}
|
|
691
|
+
videoItemStyle={{ borderRadius: 12 }}
|
|
692
|
+
fileItemStyle={{ borderRadius: 12 }}
|
|
693
|
+
paginationContainerStyle={{ padding: 16 }}
|
|
360
694
|
// ... and many more style props
|
|
361
695
|
/>
|
|
362
696
|
```
|
|
@@ -372,6 +706,43 @@ upload logic is handled internally:
|
|
|
372
706
|
2. **Upload files to S3** using the signed URLs
|
|
373
707
|
3. **Save media metadata** to your API
|
|
374
708
|
|
|
709
|
+
**React Native File Upload:**
|
|
710
|
+
|
|
711
|
+
The package automatically handles React Native file formats:
|
|
712
|
+
|
|
713
|
+
- Converts `file://` URIs to blobs for S3 upload
|
|
714
|
+
- Handles `content://` URIs (Android)
|
|
715
|
+
- Supports multiple file picker libraries
|
|
716
|
+
- Automatic MIME type detection from file extensions
|
|
717
|
+
- Proper file name cleaning (removes spaces)
|
|
718
|
+
|
|
719
|
+
**File Format Support:**
|
|
720
|
+
|
|
721
|
+
The package accepts files in these formats:
|
|
722
|
+
|
|
723
|
+
- **react-native-image-picker**: `{ uri, fileName, mimeType, ... }`
|
|
724
|
+
- **expo-image-picker**: `{ uri, fileName, mimeType, ... }`
|
|
725
|
+
- **Custom pickers**: `{ uri, name, type, ... }` or
|
|
726
|
+
`{ uri, fileName, mimeType, ... }`
|
|
727
|
+
|
|
728
|
+
The package will automatically:
|
|
729
|
+
|
|
730
|
+
- Extract file name from `fileName`, `name`, or URI
|
|
731
|
+
- Detect MIME type from `mimeType`, `type`, or file extension
|
|
732
|
+
- Handle file URIs (`file://`, `content://`) for upload
|
|
733
|
+
- Clean file names (removes spaces for S3 compatibility)
|
|
734
|
+
- Convert file URIs to blobs for S3 PUT requests
|
|
735
|
+
|
|
736
|
+
**Upload Process:**
|
|
737
|
+
|
|
738
|
+
1. **File Selection**: User selects files via picker
|
|
739
|
+
2. **File Processing**: Package converts picker results to standardized format
|
|
740
|
+
3. **MIME Type Detection**: Automatically detects MIME type from extension or
|
|
741
|
+
picker result
|
|
742
|
+
4. **S3 Signed URL**: Gets signed URL from Tradly API
|
|
743
|
+
5. **File Upload**: Fetches file from URI, converts to blob, uploads to S3
|
|
744
|
+
6. **Metadata Save**: Saves media metadata to Tradly API
|
|
745
|
+
|
|
375
746
|
Configuration:
|
|
376
747
|
|
|
377
748
|
```jsx
|
|
@@ -393,15 +764,22 @@ const apiService = new MediaApiService({
|
|
|
393
764
|
|
|
394
765
|
### Using Individual Components
|
|
395
766
|
|
|
396
|
-
You can
|
|
767
|
+
You can use individual gallery components if you need more control:
|
|
768
|
+
|
|
769
|
+
**Web:**
|
|
397
770
|
|
|
398
771
|
```jsx
|
|
399
|
-
import {
|
|
772
|
+
import {
|
|
773
|
+
ImagesGallery,
|
|
774
|
+
VideosGallery,
|
|
775
|
+
FilesGallery,
|
|
776
|
+
MediaApiService,
|
|
777
|
+
} from "@tradly/asset";
|
|
400
778
|
|
|
401
779
|
function CustomGallery() {
|
|
402
780
|
const apiService = new MediaApiService({
|
|
403
781
|
authKey: "your-auth-key",
|
|
404
|
-
|
|
782
|
+
bearerToken: "your-bearer-token",
|
|
405
783
|
});
|
|
406
784
|
|
|
407
785
|
return (
|
|
@@ -414,6 +792,52 @@ function CustomGallery() {
|
|
|
414
792
|
}
|
|
415
793
|
```
|
|
416
794
|
|
|
795
|
+
**React Native:**
|
|
796
|
+
|
|
797
|
+
```jsx
|
|
798
|
+
import {
|
|
799
|
+
ImagesGallery,
|
|
800
|
+
VideosGallery,
|
|
801
|
+
FilesGallery,
|
|
802
|
+
MediaApiService,
|
|
803
|
+
} from "@tradly/asset";
|
|
804
|
+
import {
|
|
805
|
+
CameraIcon,
|
|
806
|
+
VideoIcon,
|
|
807
|
+
UploadIcon,
|
|
808
|
+
} from "@tradly/asset/native/Icons.native";
|
|
809
|
+
import { launchImageLibrary } from "react-native-image-picker";
|
|
810
|
+
|
|
811
|
+
function CustomGallery() {
|
|
812
|
+
const apiService = new MediaApiService({
|
|
813
|
+
authKey: "your-auth-key",
|
|
814
|
+
bearerToken: "your-bearer-token",
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
const picker = (options) => {
|
|
818
|
+
return new Promise((resolve) => {
|
|
819
|
+
launchImageLibrary(options, (response) => {
|
|
820
|
+
if (response.didCancel) {
|
|
821
|
+
resolve([]);
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
resolve(response.assets || []);
|
|
825
|
+
});
|
|
826
|
+
});
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
return (
|
|
830
|
+
<FilesGallery
|
|
831
|
+
update_data={(url) => console.log("Selected:", url)}
|
|
832
|
+
closePopup={() => console.log("Closed")}
|
|
833
|
+
apiService={apiService}
|
|
834
|
+
picker={picker}
|
|
835
|
+
icons={{ default: <UploadIcon /> }}
|
|
836
|
+
/>
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
```
|
|
840
|
+
|
|
417
841
|
### Error Handling
|
|
418
842
|
|
|
419
843
|
```jsx
|
|
@@ -472,6 +896,7 @@ apiService.setBearerToken("new-bearer-token");
|
|
|
472
896
|
| `title` | `string` | `'Media Gallery'` | Popup title | Web & Native |
|
|
473
897
|
| `picker` | `function` | - | **Required for React Native**: File picker function | Native only |
|
|
474
898
|
| `pickerOptions` | `function` | - | Optional: Function to generate picker options | Native only |
|
|
899
|
+
| `icons` | `object` | - | Icon components: `{ image, video, default }` | Native only |
|
|
475
900
|
| `theme` | `object` | - | Theme object for colors/spacing (see Theme docs) | Native only |
|
|
476
901
|
| `bottomSheetHeight` | `number` | `0.9` | Bottom sheet height (0.85 = 85%, 0.9 = 90%, etc.) | Native only |
|
|
477
902
|
|
|
@@ -483,6 +908,81 @@ apiService.setBearerToken("new-bearer-token");
|
|
|
483
908
|
`{ uri: string, type?: string, fileName?: string }`
|
|
484
909
|
- Example: `picker={(options) => launchImageLibrary(options, callback)}`
|
|
485
910
|
|
|
911
|
+
**Icons Configuration (React Native):**
|
|
912
|
+
|
|
913
|
+
You can provide different icons for each media type:
|
|
914
|
+
|
|
915
|
+
```jsx
|
|
916
|
+
import {
|
|
917
|
+
CameraIcon,
|
|
918
|
+
VideoIcon,
|
|
919
|
+
UploadIcon,
|
|
920
|
+
} from "@tradly/asset/native/Icons.native";
|
|
921
|
+
|
|
922
|
+
<MediaPopup
|
|
923
|
+
icons={{
|
|
924
|
+
image: <CameraIcon />, // Icon for image uploads
|
|
925
|
+
video: <VideoIcon />, // Icon for video uploads
|
|
926
|
+
default: <UploadIcon />, // Icon for file uploads (PDFs, docs, etc.)
|
|
927
|
+
}}
|
|
928
|
+
// ... other props
|
|
929
|
+
/>;
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
If `icons` prop is not provided, the upload button will show only text (no
|
|
933
|
+
icon).
|
|
934
|
+
|
|
935
|
+
**Available Icons:**
|
|
936
|
+
|
|
937
|
+
The package includes built-in icons that you can use:
|
|
938
|
+
|
|
939
|
+
```jsx
|
|
940
|
+
import {
|
|
941
|
+
CameraIcon, // For image uploads
|
|
942
|
+
VideoIcon, // For video uploads
|
|
943
|
+
UploadIcon, // For file uploads (default)
|
|
944
|
+
CloseIcon, // For close button (used internally)
|
|
945
|
+
} from "@tradly/asset/native/Icons.native";
|
|
946
|
+
|
|
947
|
+
<MediaPopup
|
|
948
|
+
icons={{
|
|
949
|
+
image: <CameraIcon />,
|
|
950
|
+
video: <VideoIcon />,
|
|
951
|
+
default: <UploadIcon />,
|
|
952
|
+
}}
|
|
953
|
+
/>;
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
You can also use custom icon components from libraries like
|
|
957
|
+
`react-native-vector-icons` or `lucide-react-native`:
|
|
958
|
+
|
|
959
|
+
```jsx
|
|
960
|
+
import { Camera, Video, Upload } from "lucide-react-native";
|
|
961
|
+
|
|
962
|
+
<MediaPopup
|
|
963
|
+
icons={{
|
|
964
|
+
image: (
|
|
965
|
+
<Camera
|
|
966
|
+
size={20}
|
|
967
|
+
color="#3B3269"
|
|
968
|
+
/>
|
|
969
|
+
),
|
|
970
|
+
video: (
|
|
971
|
+
<Video
|
|
972
|
+
size={20}
|
|
973
|
+
color="#3B3269"
|
|
974
|
+
/>
|
|
975
|
+
),
|
|
976
|
+
default: (
|
|
977
|
+
<Upload
|
|
978
|
+
size={20}
|
|
979
|
+
color="#3B3269"
|
|
980
|
+
/>
|
|
981
|
+
),
|
|
982
|
+
}}
|
|
983
|
+
/>;
|
|
984
|
+
```
|
|
985
|
+
|
|
486
986
|
## Styling
|
|
487
987
|
|
|
488
988
|
### Web (React)
|
|
@@ -573,20 +1073,23 @@ using style props:
|
|
|
573
1073
|
- `tabIndicatorStyle` - Tab indicator line
|
|
574
1074
|
- `tabPanelStyle` - Tab panel container
|
|
575
1075
|
|
|
576
|
-
**ImagesGallery/VideosGallery:**
|
|
1076
|
+
**ImagesGallery/VideosGallery/FilesGallery:**
|
|
577
1077
|
|
|
578
1078
|
- `containerStyle` - Gallery container
|
|
579
1079
|
- `gridStyle` - Media grid layout
|
|
580
|
-
- `imageItemStyle` / `videoItemStyle` - Media item styles
|
|
1080
|
+
- `imageItemStyle` / `videoItemStyle` / `fileItemStyle` - Media item styles
|
|
581
1081
|
- `paginationContainerStyle` - Pagination container
|
|
582
1082
|
|
|
583
1083
|
**FileUpload:**
|
|
584
1084
|
|
|
585
1085
|
- `containerStyle` - Upload container
|
|
586
1086
|
- `buttonStyle` - Upload button
|
|
587
|
-
- `iconContainerStyle` - Icon container
|
|
1087
|
+
- `iconContainerStyle` - Icon container (only shown if icon is provided)
|
|
588
1088
|
- `titleStyle` - Upload title text
|
|
589
1089
|
- `loadingStyle` - Loading state container
|
|
1090
|
+
- `icon` - Single icon (backward compatibility, overridden by `icons` prop)
|
|
1091
|
+
- `icons` - Object with `{ image, video, default }` icon components (React
|
|
1092
|
+
Native)
|
|
590
1093
|
|
|
591
1094
|
**Pagination:**
|
|
592
1095
|
|
|
@@ -648,6 +1151,12 @@ For detailed styling documentation with examples, see
|
|
|
648
1151
|
**React Native:**
|
|
649
1152
|
|
|
650
1153
|
```jsx
|
|
1154
|
+
import {
|
|
1155
|
+
CameraIcon,
|
|
1156
|
+
VideoIcon,
|
|
1157
|
+
UploadIcon,
|
|
1158
|
+
} from "@tradly/asset/native/Icons.native";
|
|
1159
|
+
|
|
651
1160
|
<MediaPopup
|
|
652
1161
|
isOpen={isOpen}
|
|
653
1162
|
onClose={() => setIsOpen(false)}
|
|
@@ -655,9 +1164,25 @@ For detailed styling documentation with examples, see
|
|
|
655
1164
|
options={["image", "video", "file"]}
|
|
656
1165
|
apiService={apiService}
|
|
657
1166
|
picker={picker} // Required
|
|
658
|
-
|
|
1167
|
+
icons={{
|
|
1168
|
+
image: <CameraIcon />,
|
|
1169
|
+
video: <VideoIcon />,
|
|
1170
|
+
default: <UploadIcon />, // For file uploads
|
|
1171
|
+
}}
|
|
1172
|
+
/>;
|
|
659
1173
|
```
|
|
660
1174
|
|
|
1175
|
+
### File Type Gallery
|
|
1176
|
+
|
|
1177
|
+
The `file` option displays non-image, non-video files (PDFs, documents,
|
|
1178
|
+
archives, etc.):
|
|
1179
|
+
|
|
1180
|
+
- **Supported File Types**: PDF, Word, Excel, PowerPoint, ZIP, RAR, audio files,
|
|
1181
|
+
text files, and more
|
|
1182
|
+
- **File Icons**: Automatically displays appropriate icons based on MIME type
|
|
1183
|
+
- **File Information**: Shows file name and extension
|
|
1184
|
+
- **Upload Support**: Full upload functionality for all file types
|
|
1185
|
+
|
|
661
1186
|
## Platform-Specific Notes
|
|
662
1187
|
|
|
663
1188
|
### Web (React)
|
|
@@ -666,30 +1191,118 @@ For detailed styling documentation with examples, see
|
|
|
666
1191
|
- Tailwind CSS for styling
|
|
667
1192
|
- HTML file input for file selection
|
|
668
1193
|
- Canvas API for image compression
|
|
1194
|
+
- Direct File/Blob objects for upload
|
|
669
1195
|
|
|
670
1196
|
### React Native
|
|
671
1197
|
|
|
672
|
-
- Uses `Modal` component for bottom sheet
|
|
1198
|
+
- Uses `Modal` component for bottom sheet (slides up from bottom)
|
|
673
1199
|
- React Native `StyleSheet` for styling
|
|
674
1200
|
- Requires file picker library (configurable)
|
|
675
1201
|
- Uses `FlatList` for efficient media rendering
|
|
676
1202
|
- Supports both iOS and Android
|
|
1203
|
+
- Handles `file://` and `content://` URIs
|
|
1204
|
+
- Converts file URIs to blobs for S3 upload
|
|
1205
|
+
- Comprehensive MIME type detection
|
|
1206
|
+
- File name cleaning (removes spaces)
|
|
1207
|
+
- 3-column grid layout (optimized for mobile)
|
|
1208
|
+
- Customizable icons per media type
|
|
677
1209
|
|
|
678
|
-
##
|
|
1210
|
+
## File Types Support
|
|
679
1211
|
|
|
680
|
-
|
|
1212
|
+
The package supports three media types:
|
|
681
1213
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
1214
|
+
- **`image`**: Images (JPEG, PNG, GIF, WebP, SVG, HEIC, etc.)
|
|
1215
|
+
- **`video`**: Videos (MP4, MOV, AVI, WebM, etc.)
|
|
1216
|
+
- **`file`**: Other files (PDF, Word, Excel, PowerPoint, ZIP, audio files, text
|
|
1217
|
+
files, etc.)
|
|
1218
|
+
|
|
1219
|
+
Each type has its own gallery component:
|
|
1220
|
+
|
|
1221
|
+
- `ImagesGallery` - For images
|
|
1222
|
+
- `VideosGallery` - For videos
|
|
1223
|
+
- `FilesGallery` - For non-image, non-video files
|
|
1224
|
+
|
|
1225
|
+
The package automatically uses the correct gallery based on the `options` prop.
|
|
1226
|
+
|
|
1227
|
+
## MIME Type Detection
|
|
1228
|
+
|
|
1229
|
+
The package includes comprehensive MIME type detection:
|
|
1230
|
+
|
|
1231
|
+
- **From Picker Result**: Uses `mimeType` or `type` from picker response
|
|
1232
|
+
- **From File Extension**: Automatically detects MIME type from file extension
|
|
1233
|
+
- **Fallback**: Defaults to `application/octet-stream` if detection fails
|
|
1234
|
+
|
|
1235
|
+
Supported file types include:
|
|
1236
|
+
|
|
1237
|
+
- **Images**: jpg, jpeg, png, gif, webp, svg, bmp, ico, heic, heif, tiff, avif
|
|
1238
|
+
- **Videos**: mp4, mov, avi, wmv, flv, mkv, webm, mpeg, mpg, m4v, 3gp
|
|
1239
|
+
- **Audio**: mp3, wav, ogg, m4a, aac, flac
|
|
1240
|
+
- **Documents**: pdf, doc, docx, xls, xlsx, ppt, pptx
|
|
1241
|
+
- **Archives**: zip, rar, 7z, tar, gz
|
|
1242
|
+
- **Text**: txt, html, css, js, json, xml, php
|
|
1243
|
+
|
|
1244
|
+
## How It Works
|
|
1245
|
+
|
|
1246
|
+
The package automatically detects the platform and uses the appropriate
|
|
1247
|
+
components:
|
|
1248
|
+
|
|
1249
|
+
- **Web bundlers** (Webpack, Vite, etc.) automatically use `.web.jsx` files
|
|
1250
|
+
- **React Native bundlers** (Metro) automatically use `.native.jsx` files
|
|
1251
|
+
|
|
1252
|
+
You don't need to import different files - the package handles platform
|
|
1253
|
+
detection automatically:
|
|
1254
|
+
|
|
1255
|
+
```jsx
|
|
1256
|
+
// Same import works for both web and React Native
|
|
1257
|
+
import { MediaPopup, MediaApiService } from "@tradly/asset";
|
|
686
1258
|
```
|
|
687
1259
|
|
|
688
|
-
|
|
689
|
-
|
|
1260
|
+
The package uses platform-specific implementations under the hood:
|
|
1261
|
+
|
|
1262
|
+
- Web: Uses React DOM, Tailwind CSS, HTML file inputs
|
|
1263
|
+
- React Native: Uses React Native primitives, StyleSheet, configurable file
|
|
1264
|
+
pickers
|
|
1265
|
+
|
|
1266
|
+
## Troubleshooting
|
|
1267
|
+
|
|
1268
|
+
### React Native File Upload Issues
|
|
1269
|
+
|
|
1270
|
+
**Problem**: Files not uploading from React Native
|
|
1271
|
+
|
|
1272
|
+
**Solutions**:
|
|
690
1273
|
|
|
691
|
-
|
|
692
|
-
|
|
1274
|
+
1. Ensure `picker` prop is provided and returns files in correct format
|
|
1275
|
+
2. Check that file URIs are accessible (`file://` or `content://`)
|
|
1276
|
+
3. Verify MIME type is being detected correctly (check console logs)
|
|
1277
|
+
4. Ensure proper permissions are granted for file access
|
|
1278
|
+
|
|
1279
|
+
**Problem**: MIME type not detected properly
|
|
1280
|
+
|
|
1281
|
+
**Solutions**:
|
|
1282
|
+
|
|
1283
|
+
1. The package automatically detects MIME type from:
|
|
1284
|
+
- Picker result (`mimeType` or `type` property)
|
|
1285
|
+
- File extension (comprehensive mapping included)
|
|
1286
|
+
2. If detection fails, it defaults to `application/octet-stream`
|
|
1287
|
+
3. Check console logs to see detected MIME type
|
|
1288
|
+
|
|
1289
|
+
**Problem**: Bottom sheet not showing properly
|
|
1290
|
+
|
|
1291
|
+
**Solutions**:
|
|
1292
|
+
|
|
1293
|
+
1. Adjust `bottomSheetHeight` prop (default: 0.9 = 90%)
|
|
1294
|
+
2. Check container padding in theme
|
|
1295
|
+
3. Ensure `overflow: 'hidden'` is applied (already included)
|
|
1296
|
+
|
|
1297
|
+
### Grid Layout Issues
|
|
1298
|
+
|
|
1299
|
+
**Problem**: Items showing 2 per row instead of 3
|
|
1300
|
+
|
|
1301
|
+
**Solutions**:
|
|
1302
|
+
|
|
1303
|
+
1. Check container padding matches between skeleton and gallery
|
|
1304
|
+
2. Verify `ITEM_SIZE` calculation accounts for all padding/margins
|
|
1305
|
+
3. Ensure `FlatList` with `numColumns={3}` is used (not `flexWrap`)
|
|
693
1306
|
|
|
694
1307
|
## License
|
|
695
1308
|
|
|
@@ -699,3 +1312,145 @@ MIT
|
|
|
699
1312
|
|
|
700
1313
|
For issues and questions, please open an issue on the repository.
|
|
701
1314
|
|
|
1315
|
+
## Quick Reference
|
|
1316
|
+
|
|
1317
|
+
### Complete React Native Example
|
|
1318
|
+
|
|
1319
|
+
```jsx
|
|
1320
|
+
import React, { useState } from "react";
|
|
1321
|
+
import { View, Button, Alert } from "react-native";
|
|
1322
|
+
import { MediaPopup, MediaApiService } from "@tradly/asset";
|
|
1323
|
+
import {
|
|
1324
|
+
CameraIcon,
|
|
1325
|
+
VideoIcon,
|
|
1326
|
+
UploadIcon,
|
|
1327
|
+
} from "@tradly/asset/native/Icons.native";
|
|
1328
|
+
import { launchImageLibrary } from "react-native-image-picker";
|
|
1329
|
+
import { Platform } from "react-native";
|
|
1330
|
+
|
|
1331
|
+
function App() {
|
|
1332
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
1333
|
+
const [selectedMedia, setSelectedMedia] = useState(null);
|
|
1334
|
+
|
|
1335
|
+
const apiService = new MediaApiService({
|
|
1336
|
+
authKey: "your-tradly-auth-key",
|
|
1337
|
+
bearerToken: "your-bearer-token",
|
|
1338
|
+
environment: "dev", // Auto-detected from process.env.ENVIRONMENT
|
|
1339
|
+
});
|
|
1340
|
+
|
|
1341
|
+
const handleSelect = (mediaUrl) => {
|
|
1342
|
+
setSelectedMedia(mediaUrl);
|
|
1343
|
+
setIsOpen(false);
|
|
1344
|
+
Alert.alert("Selected", mediaUrl);
|
|
1345
|
+
};
|
|
1346
|
+
|
|
1347
|
+
const handleError = (error) => {
|
|
1348
|
+
console.error("Error:", error);
|
|
1349
|
+
Alert.alert("Error", error.message || "Something went wrong");
|
|
1350
|
+
};
|
|
1351
|
+
|
|
1352
|
+
// File picker configuration
|
|
1353
|
+
const picker = (options) => {
|
|
1354
|
+
return new Promise((resolve) => {
|
|
1355
|
+
launchImageLibrary(options, (response) => {
|
|
1356
|
+
if (response.didCancel) {
|
|
1357
|
+
resolve([]);
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
if (response.errorMessage) {
|
|
1361
|
+
Alert.alert("Error", response.errorMessage);
|
|
1362
|
+
resolve([]);
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
resolve(response.assets || []);
|
|
1366
|
+
});
|
|
1367
|
+
});
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
// Custom picker options
|
|
1371
|
+
const pickerOptions = (accept) => {
|
|
1372
|
+
const isImage = accept?.includes("image");
|
|
1373
|
+
const isVideo = accept?.includes("video");
|
|
1374
|
+
const isFile = accept?.includes("file");
|
|
1375
|
+
|
|
1376
|
+
return {
|
|
1377
|
+
mediaType: isImage ? "photo" : isVideo ? "video" : "mixed",
|
|
1378
|
+
quality: 0.8,
|
|
1379
|
+
allowsMultiple: true,
|
|
1380
|
+
selectionLimit: 10,
|
|
1381
|
+
};
|
|
1382
|
+
};
|
|
1383
|
+
|
|
1384
|
+
return (
|
|
1385
|
+
<View style={{ flex: 1, justifyContent: "center", padding: 20 }}>
|
|
1386
|
+
<Button
|
|
1387
|
+
title="Open Media Gallery"
|
|
1388
|
+
onPress={() => setIsOpen(true)}
|
|
1389
|
+
/>
|
|
1390
|
+
|
|
1391
|
+
<MediaPopup
|
|
1392
|
+
isOpen={isOpen}
|
|
1393
|
+
onClose={() => setIsOpen(false)}
|
|
1394
|
+
onSelect={handleSelect}
|
|
1395
|
+
onError={handleError}
|
|
1396
|
+
options={["image", "video", "file"]}
|
|
1397
|
+
apiService={apiService}
|
|
1398
|
+
picker={picker}
|
|
1399
|
+
pickerOptions={pickerOptions}
|
|
1400
|
+
icons={{
|
|
1401
|
+
image: <CameraIcon />,
|
|
1402
|
+
video: <VideoIcon />,
|
|
1403
|
+
default: <UploadIcon />,
|
|
1404
|
+
}}
|
|
1405
|
+
bottomSheetHeight={0.9}
|
|
1406
|
+
title="Select Media"
|
|
1407
|
+
/>
|
|
1408
|
+
</View>
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
export default App;
|
|
1413
|
+
```
|
|
1414
|
+
|
|
1415
|
+
### Complete Web Example
|
|
1416
|
+
|
|
1417
|
+
```jsx
|
|
1418
|
+
import React, { useState } from "react";
|
|
1419
|
+
import { MediaPopup, MediaApiService } from "@tradly/asset";
|
|
1420
|
+
|
|
1421
|
+
function App() {
|
|
1422
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
1423
|
+
const [selectedMedia, setSelectedMedia] = useState(null);
|
|
1424
|
+
|
|
1425
|
+
const apiService = new MediaApiService({
|
|
1426
|
+
authKey: "your-tradly-auth-key",
|
|
1427
|
+
bearerToken: "your-bearer-token",
|
|
1428
|
+
// environment is auto-detected from process.env.ENVIRONMENT
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
const handleSelect = (mediaUrl) => {
|
|
1432
|
+
setSelectedMedia(mediaUrl);
|
|
1433
|
+
setIsOpen(false);
|
|
1434
|
+
console.log("Selected:", mediaUrl);
|
|
1435
|
+
};
|
|
1436
|
+
|
|
1437
|
+
return (
|
|
1438
|
+
<div>
|
|
1439
|
+
<button onClick={() => setIsOpen(true)}>
|
|
1440
|
+
Open Media Gallery
|
|
1441
|
+
</button>
|
|
1442
|
+
|
|
1443
|
+
<MediaPopup
|
|
1444
|
+
isOpen={isOpen}
|
|
1445
|
+
onClose={() => setIsOpen(false)}
|
|
1446
|
+
onSelect={handleSelect}
|
|
1447
|
+
options={["image", "video", "file"]}
|
|
1448
|
+
apiService={apiService}
|
|
1449
|
+
/>
|
|
1450
|
+
</div>
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
export default App;
|
|
1455
|
+
```
|
|
1456
|
+
|