@sendbird/uikit-react-native 2.4.0 → 2.4.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 (40) hide show
  1. package/lib/commonjs/components/ChannelInput/SendInput.js +3 -3
  2. package/lib/commonjs/components/ChannelInput/SendInput.js.map +1 -1
  3. package/lib/commonjs/containers/SendbirdUIKitContainer.js +19 -1
  4. package/lib/commonjs/containers/SendbirdUIKitContainer.js.map +1 -1
  5. package/lib/commonjs/platform/createFileService.expo.js +6 -14
  6. package/lib/commonjs/platform/createFileService.expo.js.map +1 -1
  7. package/lib/commonjs/platform/createFileService.native.js +65 -37
  8. package/lib/commonjs/platform/createFileService.native.js.map +1 -1
  9. package/lib/commonjs/utils/normalizeFile.js +41 -0
  10. package/lib/commonjs/utils/normalizeFile.js.map +1 -0
  11. package/lib/commonjs/version.js +1 -1
  12. package/lib/commonjs/version.js.map +1 -1
  13. package/lib/module/components/ChannelInput/SendInput.js +3 -3
  14. package/lib/module/components/ChannelInput/SendInput.js.map +1 -1
  15. package/lib/module/containers/SendbirdUIKitContainer.js +20 -2
  16. package/lib/module/containers/SendbirdUIKitContainer.js.map +1 -1
  17. package/lib/module/platform/createFileService.expo.js +7 -15
  18. package/lib/module/platform/createFileService.expo.js.map +1 -1
  19. package/lib/module/platform/createFileService.native.js +64 -36
  20. package/lib/module/platform/createFileService.native.js.map +1 -1
  21. package/lib/module/utils/normalizeFile.js +33 -0
  22. package/lib/module/utils/normalizeFile.js.map +1 -0
  23. package/lib/module/version.js +1 -1
  24. package/lib/module/version.js.map +1 -1
  25. package/lib/typescript/src/containers/SendbirdUIKitContainer.d.ts +1 -1
  26. package/lib/typescript/src/utils/normalizeFile.d.ts +4 -0
  27. package/lib/typescript/src/version.d.ts +1 -1
  28. package/package.json +5 -5
  29. package/src/components/ChannelInput/SendInput.tsx +3 -3
  30. package/src/containers/SendbirdUIKitContainer.tsx +20 -1
  31. package/src/platform/createFileService.expo.ts +5 -10
  32. package/src/platform/createFileService.native.ts +53 -28
  33. package/src/utils/normalizeFile.ts +32 -0
  34. package/src/version.ts +1 -1
  35. package/lib/commonjs/utils/fileTypeGuard.js +0 -26
  36. package/lib/commonjs/utils/fileTypeGuard.js.map +0 -1
  37. package/lib/module/utils/fileTypeGuard.js +0 -18
  38. package/lib/module/utils/fileTypeGuard.js.map +0 -1
  39. package/lib/typescript/src/utils/fileTypeGuard.d.ts +0 -4
  40. package/src/utils/fileTypeGuard.ts +0 -10
@@ -1,8 +1,10 @@
1
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
2
+
1
3
  import { Platform } from 'react-native';
2
- import { getFileExtension, getFileType, normalizeFileName } from '@sendbird/uikit-utils';
4
+ import { getFileExtension, getFileExtensionFromMime, getFileExtensionFromUri, getFileType, normalizeFileName } from '@sendbird/uikit-utils';
3
5
  import SBUError from '../libs/SBUError';
4
- import fileTypeGuard from '../utils/fileTypeGuard';
5
6
  import nativePermissionGranted from '../utils/nativePermissionGranted';
7
+ import normalizeFile from '../utils/normalizeFile';
6
8
 
7
9
  function getAndroidStoragePermissionsByAPILevel(permissionModule) {
8
10
  if (Platform.OS !== 'android') return [];
@@ -38,6 +40,42 @@ const createNativeFileService = _ref => {
38
40
  });
39
41
 
40
42
  class NativeFileService {
43
+ constructor() {
44
+ _defineProperty(this, "buildDownloadPath", async options => {
45
+ const dirname = Platform.select({
46
+ android: fsModule.Dirs.CacheDir,
47
+ default: fsModule.Dirs.DocumentDir
48
+ });
49
+ const context = {
50
+ dirname,
51
+ filename: options.fileName
52
+ };
53
+ const extension = getFileExtension(options.fileName) || getFileExtensionFromMime(options.fileType) || getFileExtension(options.fileUrl) || (await getFileExtensionFromUri(options.fileUrl));
54
+ if (extension) context.filename = normalizeFileName(context.filename, extension);
55
+ return {
56
+ path: `${context.dirname}/${context.filename}`,
57
+ ...context
58
+ };
59
+ });
60
+
61
+ _defineProperty(this, "downloadFile", async options => {
62
+ const {
63
+ path,
64
+ filename
65
+ } = await this.buildDownloadPath(options);
66
+ await fsModule.FileSystem.fetch(options.fileUrl, {
67
+ path
68
+ });
69
+ return {
70
+ downloadedPath: path,
71
+ file: {
72
+ name: filename,
73
+ type: getFileType(getFileExtension(path))
74
+ }
75
+ };
76
+ });
77
+ }
78
+
41
79
  async hasCameraPermission() {
42
80
  const status = await permissionModule.checkMultiple(cameraPermissions);
43
81
  return nativePermissionGranted(status);
@@ -113,7 +151,7 @@ const createNativeFileService = _ref => {
113
151
  type,
114
152
  uri
115
153
  } = ((_response$assets = response.assets) === null || _response$assets === void 0 ? void 0 : _response$assets[0]) ?? {};
116
- return fileTypeGuard({
154
+ return normalizeFile({
117
155
  uri,
118
156
  size,
119
157
  name,
@@ -168,20 +206,20 @@ const createNativeFileService = _ref => {
168
206
  return null;
169
207
  }
170
208
 
171
- return (response.assets || []).slice(0, selectionLimit).map(_ref2 => {
209
+ return Promise.all((response.assets || []).slice(0, selectionLimit).map(_ref2 => {
172
210
  let {
173
211
  fileName: name,
174
212
  fileSize: size,
175
213
  type,
176
214
  uri
177
215
  } = _ref2;
178
- return fileTypeGuard({
216
+ return normalizeFile({
179
217
  uri,
180
218
  size,
181
219
  name,
182
220
  type
183
221
  });
184
- });
222
+ }));
185
223
  }
186
224
 
187
225
  async openDocument(options) {
@@ -192,7 +230,7 @@ const createNativeFileService = _ref => {
192
230
  name,
193
231
  type
194
232
  } = await documentPickerModule.pickSingle();
195
- return fileTypeGuard({
233
+ return normalizeFile({
196
234
  uri,
197
235
  size,
198
236
  name,
@@ -217,46 +255,36 @@ const createNativeFileService = _ref => {
217
255
  if (!granted) throw new Error('Permission not granted');
218
256
  }
219
257
 
220
- const basePath = Platform.select({
221
- android: fsModule.Dirs.CacheDir,
222
- default: fsModule.Dirs.DocumentDir
223
- });
224
- let downloadPath = `${basePath}/${options.fileName}`;
225
-
226
- if (!getFileExtension(options.fileName)) {
227
- const extensionFromUrl = getFileExtension(options.fileUrl);
228
-
229
- if (getFileType(extensionFromUrl).match(/image|video/)) {
230
- downloadPath += extensionFromUrl;
258
+ const {
259
+ downloadedPath,
260
+ file
261
+ } = await this.downloadFile(options);
262
+
263
+ if (Platform.OS === 'ios') {
264
+ if (file.type === 'image' || file.type === 'video') {
265
+ const mediaTypeMap = {
266
+ 'image': 'photo',
267
+ 'video': 'video'
268
+ };
269
+ const mediaType = mediaTypeMap[file.type];
270
+ await mediaLibraryModule.save(downloadedPath, {
271
+ type: mediaType
272
+ });
231
273
  }
232
274
  }
233
275
 
234
- await fsModule.FileSystem.fetch(options.fileUrl, {
235
- path: downloadPath
236
- });
237
- const fileType = getFileType(getFileExtension(options.fileUrl));
238
-
239
- if (Platform.OS === 'ios' && (fileType === 'image' || fileType === 'video')) {
240
- const type = {
241
- 'image': 'photo',
242
- 'video': 'video'
243
- }[fileType];
244
- await mediaLibraryModule.save(downloadPath, {
245
- type
246
- });
247
- }
248
-
249
276
  if (Platform.OS === 'android') {
250
- const dirType = {
277
+ const externalDirMap = {
251
278
  'file': 'downloads',
252
279
  'audio': 'audio',
253
280
  'image': 'images',
254
281
  'video': 'video'
255
282
  };
256
- await fsModule.FileSystem.cpExternal(downloadPath, normalizeFileName(options.fileName, getFileExtension(options.fileUrl)), dirType[fileType]);
283
+ const externalDir = externalDirMap[file.type];
284
+ await fsModule.FileSystem.cpExternal(downloadedPath, file.name, externalDir);
257
285
  }
258
286
 
259
- return downloadPath;
287
+ return downloadedPath;
260
288
  }
261
289
 
262
290
  }
@@ -1 +1 @@
1
- {"version":3,"names":["Platform","getFileExtension","getFileType","normalizeFileName","SBUError","fileTypeGuard","nativePermissionGranted","getAndroidStoragePermissionsByAPILevel","permissionModule","OS","Version","PERMISSIONS","ANDROID","READ_MEDIA_AUDIO","READ_MEDIA_IMAGES","READ_MEDIA_VIDEO","READ_EXTERNAL_STORAGE","WRITE_EXTERNAL_STORAGE","createNativeFileService","imagePickerModule","documentPickerModule","mediaLibraryModule","fsModule","cameraPermissions","select","ios","IOS","CAMERA","MICROPHONE","android","default","mediaLibraryPermissions","MEDIA_LIBRARY","PHOTO_LIBRARY","NativeFileService","hasCameraPermission","status","checkMultiple","requestCameraPermission","requestMultiple","hasMediaLibraryPermission","__DEV__","requestMediaLibraryPermission","openCamera","options","hasPermission","granted","onOpenFailure","PERMISSIONS_DENIED","response","launchCamera","presentationStyle","cameraType","mediaType","didCancel","errorCode","DEVICE_UNAVAILABLE","Error","errorMessage","fileName","name","fileSize","size","type","uri","assets","openMediaLibrary","selectionLimit","launchImageLibrary","slice","map","openDocument","pickSingle","e","isCancel","isInProgress","UNKNOWN","save","basePath","Dirs","CacheDir","DocumentDir","downloadPath","extensionFromUrl","fileUrl","match","FileSystem","fetch","path","fileType","dirType","cpExternal"],"sources":["createFileService.native.ts"],"sourcesContent":["import type { CameraRoll } from '@react-native-camera-roll/camera-roll';\nimport { Platform } from 'react-native';\nimport type * as DocumentPicker from 'react-native-document-picker';\nimport type * as FileAccess from 'react-native-file-access';\nimport type * as ImagePicker from 'react-native-image-picker';\nimport type * as Permissions from 'react-native-permissions';\nimport type { Permission } from 'react-native-permissions';\n\nimport { getFileExtension, getFileType, normalizeFileName } from '@sendbird/uikit-utils';\n\nimport SBUError from '../libs/SBUError';\nimport fileTypeGuard from '../utils/fileTypeGuard';\nimport nativePermissionGranted from '../utils/nativePermissionGranted';\nimport type {\n FilePickerResponse,\n FileServiceInterface,\n OpenCameraOptions,\n OpenDocumentOptions,\n OpenMediaLibraryOptions,\n SaveOptions,\n} from './types';\n\nfunction getAndroidStoragePermissionsByAPILevel(permissionModule: typeof Permissions): Permission[] {\n if (Platform.OS !== 'android') return [];\n\n if (Platform.Version > 32) {\n return [\n permissionModule.PERMISSIONS.ANDROID.READ_MEDIA_AUDIO,\n permissionModule.PERMISSIONS.ANDROID.READ_MEDIA_IMAGES,\n permissionModule.PERMISSIONS.ANDROID.READ_MEDIA_VIDEO,\n ];\n }\n\n if (Platform.Version > 28) {\n return [permissionModule.PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE];\n }\n\n return [\n permissionModule.PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,\n permissionModule.PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE,\n ];\n}\n\nconst createNativeFileService = ({\n imagePickerModule,\n documentPickerModule,\n permissionModule,\n mediaLibraryModule,\n fsModule,\n}: {\n imagePickerModule: typeof ImagePicker;\n documentPickerModule: typeof DocumentPicker;\n permissionModule: typeof Permissions;\n mediaLibraryModule: typeof CameraRoll;\n fsModule: typeof FileAccess;\n}): FileServiceInterface => {\n const cameraPermissions: Permission[] = Platform.select({\n ios: [permissionModule.PERMISSIONS.IOS.CAMERA, permissionModule.PERMISSIONS.IOS.MICROPHONE],\n android: [permissionModule.PERMISSIONS.ANDROID.CAMERA],\n default: [],\n });\n const mediaLibraryPermissions: Permission[] = Platform.select({\n ios: [permissionModule.PERMISSIONS.IOS.MEDIA_LIBRARY, permissionModule.PERMISSIONS.IOS.PHOTO_LIBRARY],\n android: getAndroidStoragePermissionsByAPILevel(permissionModule),\n default: [],\n });\n\n class NativeFileService implements FileServiceInterface {\n async hasCameraPermission(): Promise<boolean> {\n const status = await permissionModule.checkMultiple(cameraPermissions);\n return nativePermissionGranted(status);\n }\n async requestCameraPermission(): Promise<boolean> {\n const status = await permissionModule.requestMultiple(cameraPermissions);\n return nativePermissionGranted(status);\n }\n async hasMediaLibraryPermission(): Promise<boolean> {\n const status = await permissionModule.checkMultiple(mediaLibraryPermissions);\n if (\n __DEV__ &&\n Platform.OS === 'ios' &&\n status['ios.permission.MEDIA_LIBRARY'] === 'unavailable' &&\n status['ios.permission.PHOTO_LIBRARY'] === 'granted'\n ) {\n return true;\n }\n return nativePermissionGranted(status);\n }\n async requestMediaLibraryPermission(): Promise<boolean> {\n const status = await permissionModule.requestMultiple(mediaLibraryPermissions);\n return nativePermissionGranted(status);\n }\n\n async openCamera(options?: OpenCameraOptions): Promise<FilePickerResponse> {\n const hasPermission = await this.hasCameraPermission();\n if (!hasPermission) {\n const granted = await this.requestCameraPermission();\n if (!granted) {\n options?.onOpenFailure?.(SBUError.PERMISSIONS_DENIED);\n return null;\n }\n }\n\n const response = await imagePickerModule.launchCamera({\n presentationStyle: 'fullScreen',\n cameraType: options?.cameraType ?? 'back',\n mediaType: (() => {\n switch (options?.mediaType) {\n case 'photo':\n return 'photo';\n case 'video':\n return 'video';\n case 'all':\n return 'mixed';\n default:\n return 'photo';\n }\n })(),\n });\n if (response.didCancel) return null;\n if (response.errorCode === 'camera_unavailable') {\n options?.onOpenFailure?.(SBUError.DEVICE_UNAVAILABLE, new Error(response.errorMessage));\n return null;\n }\n\n const { fileName: name, fileSize: size, type, uri } = response.assets?.[0] ?? {};\n return fileTypeGuard({ uri, size, name, type });\n }\n async openMediaLibrary(options?: OpenMediaLibraryOptions): Promise<FilePickerResponse[] | null> {\n /**\n * NOTE: options.selectionLimit {@link https://github.com/react-native-image-picker/react-native-image-picker#options}\n * We do not support 0 (any number of files)\n **/\n const selectionLimit = options?.selectionLimit || 1;\n const hasPermission = await this.hasMediaLibraryPermission();\n if (!hasPermission) {\n const granted = await this.requestMediaLibraryPermission();\n if (!granted) {\n options?.onOpenFailure?.(SBUError.PERMISSIONS_DENIED);\n return null;\n }\n }\n\n const response = await imagePickerModule.launchImageLibrary({\n presentationStyle: 'fullScreen',\n selectionLimit,\n mediaType: (() => {\n switch (options?.mediaType) {\n case 'photo':\n return 'photo';\n case 'video':\n return 'video';\n case 'all':\n return 'mixed';\n default:\n return 'photo';\n }\n })(),\n });\n if (response.didCancel) return null;\n if (response.errorCode === 'camera_unavailable') {\n options?.onOpenFailure?.(SBUError.DEVICE_UNAVAILABLE, new Error(response.errorMessage));\n return null;\n }\n\n return (response.assets || [])\n .slice(0, selectionLimit)\n .map(({ fileName: name, fileSize: size, type, uri }) => fileTypeGuard({ uri, size, name, type }));\n }\n async openDocument(options?: OpenDocumentOptions): Promise<FilePickerResponse> {\n try {\n const { uri, size, name, type } = await documentPickerModule.pickSingle();\n return fileTypeGuard({ uri, size, name, type });\n } catch (e) {\n if (!documentPickerModule.isCancel(e) && documentPickerModule.isInProgress(e)) {\n options?.onOpenFailure?.(SBUError.UNKNOWN, e);\n }\n return null;\n }\n }\n async save(options: SaveOptions): Promise<string> {\n const hasPermission = await this.hasMediaLibraryPermission();\n if (!hasPermission) {\n const granted = await this.requestMediaLibraryPermission();\n if (!granted) throw new Error('Permission not granted');\n }\n\n const basePath = Platform.select({ android: fsModule.Dirs.CacheDir, default: fsModule.Dirs.DocumentDir });\n let downloadPath = `${basePath}/${options.fileName}`;\n if (!getFileExtension(options.fileName)) {\n const extensionFromUrl = getFileExtension(options.fileUrl);\n if (getFileType(extensionFromUrl).match(/image|video/)) {\n downloadPath += extensionFromUrl;\n }\n }\n\n await fsModule.FileSystem.fetch(options.fileUrl, { path: downloadPath });\n const fileType = getFileType(getFileExtension(options.fileUrl));\n\n if (Platform.OS === 'ios' && (fileType === 'image' || fileType === 'video')) {\n const type = ({ 'image': 'photo', 'video': 'video' } as const)[fileType];\n await mediaLibraryModule.save(downloadPath, { type });\n }\n\n if (Platform.OS === 'android') {\n const dirType = { 'file': 'downloads', 'audio': 'audio', 'image': 'images', 'video': 'video' } as const;\n await fsModule.FileSystem.cpExternal(\n downloadPath,\n normalizeFileName(options.fileName, getFileExtension(options.fileUrl)),\n dirType[fileType],\n );\n }\n return downloadPath;\n }\n }\n\n return new NativeFileService();\n};\n\nexport default createNativeFileService;\n"],"mappings":"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"}
1
+ {"version":3,"names":["Platform","getFileExtension","getFileExtensionFromMime","getFileExtensionFromUri","getFileType","normalizeFileName","SBUError","nativePermissionGranted","normalizeFile","getAndroidStoragePermissionsByAPILevel","permissionModule","OS","Version","PERMISSIONS","ANDROID","READ_MEDIA_AUDIO","READ_MEDIA_IMAGES","READ_MEDIA_VIDEO","READ_EXTERNAL_STORAGE","WRITE_EXTERNAL_STORAGE","createNativeFileService","imagePickerModule","documentPickerModule","mediaLibraryModule","fsModule","cameraPermissions","select","ios","IOS","CAMERA","MICROPHONE","android","default","mediaLibraryPermissions","MEDIA_LIBRARY","PHOTO_LIBRARY","NativeFileService","options","dirname","Dirs","CacheDir","DocumentDir","context","filename","fileName","extension","fileType","fileUrl","path","buildDownloadPath","FileSystem","fetch","downloadedPath","file","name","type","hasCameraPermission","status","checkMultiple","requestCameraPermission","requestMultiple","hasMediaLibraryPermission","__DEV__","requestMediaLibraryPermission","openCamera","hasPermission","granted","onOpenFailure","PERMISSIONS_DENIED","response","launchCamera","presentationStyle","cameraType","mediaType","didCancel","errorCode","DEVICE_UNAVAILABLE","Error","errorMessage","fileSize","size","uri","assets","openMediaLibrary","selectionLimit","launchImageLibrary","Promise","all","slice","map","openDocument","pickSingle","e","isCancel","isInProgress","UNKNOWN","save","downloadFile","mediaTypeMap","externalDirMap","externalDir","cpExternal"],"sources":["createFileService.native.ts"],"sourcesContent":["import type { CameraRoll } from '@react-native-camera-roll/camera-roll';\nimport { Platform } from 'react-native';\nimport type * as DocumentPicker from 'react-native-document-picker';\nimport type * as FileAccess from 'react-native-file-access';\nimport type * as ImagePicker from 'react-native-image-picker';\nimport type * as Permissions from 'react-native-permissions';\nimport type { Permission } from 'react-native-permissions';\n\nimport {\n getFileExtension,\n getFileExtensionFromMime,\n getFileExtensionFromUri,\n getFileType,\n normalizeFileName,\n} from '@sendbird/uikit-utils';\n\nimport SBUError from '../libs/SBUError';\nimport nativePermissionGranted from '../utils/nativePermissionGranted';\nimport normalizeFile from '../utils/normalizeFile';\nimport type {\n FilePickerResponse,\n FileServiceInterface,\n OpenCameraOptions,\n OpenDocumentOptions,\n OpenMediaLibraryOptions,\n SaveOptions,\n} from './types';\n\nfunction getAndroidStoragePermissionsByAPILevel(permissionModule: typeof Permissions): Permission[] {\n if (Platform.OS !== 'android') return [];\n\n if (Platform.Version > 32) {\n return [\n permissionModule.PERMISSIONS.ANDROID.READ_MEDIA_AUDIO,\n permissionModule.PERMISSIONS.ANDROID.READ_MEDIA_IMAGES,\n permissionModule.PERMISSIONS.ANDROID.READ_MEDIA_VIDEO,\n ];\n }\n\n if (Platform.Version > 28) {\n return [permissionModule.PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE];\n }\n\n return [\n permissionModule.PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,\n permissionModule.PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE,\n ];\n}\n\nconst createNativeFileService = ({\n imagePickerModule,\n documentPickerModule,\n permissionModule,\n mediaLibraryModule,\n fsModule,\n}: {\n imagePickerModule: typeof ImagePicker;\n documentPickerModule: typeof DocumentPicker;\n permissionModule: typeof Permissions;\n mediaLibraryModule: typeof CameraRoll;\n fsModule: typeof FileAccess;\n}): FileServiceInterface => {\n const cameraPermissions: Permission[] = Platform.select({\n ios: [permissionModule.PERMISSIONS.IOS.CAMERA, permissionModule.PERMISSIONS.IOS.MICROPHONE],\n android: [permissionModule.PERMISSIONS.ANDROID.CAMERA],\n default: [],\n });\n const mediaLibraryPermissions: Permission[] = Platform.select({\n ios: [permissionModule.PERMISSIONS.IOS.MEDIA_LIBRARY, permissionModule.PERMISSIONS.IOS.PHOTO_LIBRARY],\n android: getAndroidStoragePermissionsByAPILevel(permissionModule),\n default: [],\n });\n\n class NativeFileService implements FileServiceInterface {\n async hasCameraPermission(): Promise<boolean> {\n const status = await permissionModule.checkMultiple(cameraPermissions);\n return nativePermissionGranted(status);\n }\n async requestCameraPermission(): Promise<boolean> {\n const status = await permissionModule.requestMultiple(cameraPermissions);\n return nativePermissionGranted(status);\n }\n async hasMediaLibraryPermission(): Promise<boolean> {\n const status = await permissionModule.checkMultiple(mediaLibraryPermissions);\n if (\n __DEV__ &&\n Platform.OS === 'ios' &&\n status['ios.permission.MEDIA_LIBRARY'] === 'unavailable' &&\n status['ios.permission.PHOTO_LIBRARY'] === 'granted'\n ) {\n return true;\n }\n return nativePermissionGranted(status);\n }\n async requestMediaLibraryPermission(): Promise<boolean> {\n const status = await permissionModule.requestMultiple(mediaLibraryPermissions);\n return nativePermissionGranted(status);\n }\n\n async openCamera(options?: OpenCameraOptions): Promise<FilePickerResponse> {\n const hasPermission = await this.hasCameraPermission();\n if (!hasPermission) {\n const granted = await this.requestCameraPermission();\n if (!granted) {\n options?.onOpenFailure?.(SBUError.PERMISSIONS_DENIED);\n return null;\n }\n }\n\n const response = await imagePickerModule.launchCamera({\n presentationStyle: 'fullScreen',\n cameraType: options?.cameraType ?? 'back',\n mediaType: (() => {\n switch (options?.mediaType) {\n case 'photo':\n return 'photo';\n case 'video':\n return 'video';\n case 'all':\n return 'mixed';\n default:\n return 'photo';\n }\n })(),\n });\n if (response.didCancel) return null;\n if (response.errorCode === 'camera_unavailable') {\n options?.onOpenFailure?.(SBUError.DEVICE_UNAVAILABLE, new Error(response.errorMessage));\n return null;\n }\n\n const { fileName: name, fileSize: size, type, uri } = response.assets?.[0] ?? {};\n return normalizeFile({ uri, size, name, type });\n }\n async openMediaLibrary(options?: OpenMediaLibraryOptions): Promise<FilePickerResponse[] | null> {\n /**\n * NOTE: options.selectionLimit {@link https://github.com/react-native-image-picker/react-native-image-picker#options}\n * We do not support 0 (any number of files)\n **/\n const selectionLimit = options?.selectionLimit || 1;\n const hasPermission = await this.hasMediaLibraryPermission();\n if (!hasPermission) {\n const granted = await this.requestMediaLibraryPermission();\n if (!granted) {\n options?.onOpenFailure?.(SBUError.PERMISSIONS_DENIED);\n return null;\n }\n }\n\n const response = await imagePickerModule.launchImageLibrary({\n presentationStyle: 'fullScreen',\n selectionLimit,\n mediaType: (() => {\n switch (options?.mediaType) {\n case 'photo':\n return 'photo';\n case 'video':\n return 'video';\n case 'all':\n return 'mixed';\n default:\n return 'photo';\n }\n })(),\n });\n if (response.didCancel) return null;\n if (response.errorCode === 'camera_unavailable') {\n options?.onOpenFailure?.(SBUError.DEVICE_UNAVAILABLE, new Error(response.errorMessage));\n return null;\n }\n\n return Promise.all(\n (response.assets || [])\n .slice(0, selectionLimit)\n .map(({ fileName: name, fileSize: size, type, uri }) => normalizeFile({ uri, size, name, type })),\n );\n }\n async openDocument(options?: OpenDocumentOptions): Promise<FilePickerResponse> {\n try {\n const { uri, size, name, type } = await documentPickerModule.pickSingle();\n return normalizeFile({ uri, size, name, type });\n } catch (e) {\n if (!documentPickerModule.isCancel(e) && documentPickerModule.isInProgress(e)) {\n options?.onOpenFailure?.(SBUError.UNKNOWN, e);\n }\n return null;\n }\n }\n async save(options: SaveOptions): Promise<string> {\n const hasPermission = await this.hasMediaLibraryPermission();\n if (!hasPermission) {\n const granted = await this.requestMediaLibraryPermission();\n if (!granted) throw new Error('Permission not granted');\n }\n\n const { downloadedPath, file } = await this.downloadFile(options);\n\n if (Platform.OS === 'ios') {\n if (file.type === 'image' || file.type === 'video') {\n const mediaTypeMap = { 'image': 'photo', 'video': 'video' } as const;\n const mediaType = mediaTypeMap[file.type];\n await mediaLibraryModule.save(downloadedPath, { type: mediaType });\n }\n }\n\n if (Platform.OS === 'android') {\n const externalDirMap = { 'file': 'downloads', 'audio': 'audio', 'image': 'images', 'video': 'video' } as const;\n const externalDir = externalDirMap[file.type];\n await fsModule.FileSystem.cpExternal(downloadedPath, file.name, externalDir);\n }\n\n return downloadedPath;\n }\n\n private buildDownloadPath = async (options: SaveOptions) => {\n const dirname = Platform.select({ android: fsModule.Dirs.CacheDir, default: fsModule.Dirs.DocumentDir });\n const context = { dirname, filename: options.fileName };\n const extension =\n getFileExtension(options.fileName) ||\n getFileExtensionFromMime(options.fileType) ||\n getFileExtension(options.fileUrl) ||\n (await getFileExtensionFromUri(options.fileUrl));\n\n if (extension) context.filename = normalizeFileName(context.filename, extension);\n\n return { path: `${context.dirname}/${context.filename}`, ...context };\n };\n\n private downloadFile = async (options: SaveOptions) => {\n const { path, filename } = await this.buildDownloadPath(options);\n await fsModule.FileSystem.fetch(options.fileUrl, { path });\n return {\n downloadedPath: path,\n file: {\n name: filename,\n type: getFileType(getFileExtension(path)),\n } as const,\n };\n };\n }\n\n return new NativeFileService();\n};\n\nexport default createNativeFileService;\n"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { getFileExtension, getFileExtensionFromMime, getFileExtensionFromUri, getMimeFromFileExtension, normalizeFileName } from '@sendbird/uikit-utils';
2
+
3
+ const normalizeFile = async _ref => {
4
+ let {
5
+ uri,
6
+ size,
7
+ name,
8
+ type
9
+ } = _ref;
10
+ // URI is required property
11
+ if (!uri) return null;
12
+ let filename = name || String(Date.now());
13
+ let filetype = type || '';
14
+ const extension = getFileExtension(filename) || getFileExtensionFromMime(filetype) || (await getFileExtensionFromUri(uri));
15
+
16
+ if (extension) {
17
+ filename = normalizeFileName(filename, extension);
18
+
19
+ if (!filetype) {
20
+ filetype = getMimeFromFileExtension(extension);
21
+ }
22
+ }
23
+
24
+ return {
25
+ uri,
26
+ name: filename,
27
+ type: filetype,
28
+ size: size ?? 0
29
+ };
30
+ };
31
+
32
+ export default normalizeFile;
33
+ //# sourceMappingURL=normalizeFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["getFileExtension","getFileExtensionFromMime","getFileExtensionFromUri","getMimeFromFileExtension","normalizeFileName","normalizeFile","uri","size","name","type","filename","String","Date","now","filetype","extension"],"sources":["normalizeFile.ts"],"sourcesContent":["import type { PartialNullable } from '@sendbird/uikit-utils';\nimport {\n getFileExtension,\n getFileExtensionFromMime,\n getFileExtensionFromUri,\n getMimeFromFileExtension,\n normalizeFileName,\n} from '@sendbird/uikit-utils';\n\nimport type { FilePickerResponse, FileType } from '../platform/types';\n\nconst normalizeFile = async ({ uri, size, name, type }: PartialNullable<FileType>): Promise<FilePickerResponse> => {\n // URI is required property\n if (!uri) return null;\n\n let filename = name || String(Date.now());\n let filetype = type || '';\n\n const extension =\n getFileExtension(filename) || getFileExtensionFromMime(filetype) || (await getFileExtensionFromUri(uri));\n\n if (extension) {\n filename = normalizeFileName(filename, extension);\n if (!filetype) {\n filetype = getMimeFromFileExtension(extension);\n }\n }\n\n return { uri, name: filename, type: filetype, size: size ?? 0 };\n};\n\nexport default normalizeFile;\n"],"mappings":"AACA,SACEA,gBADF,EAEEC,wBAFF,EAGEC,uBAHF,EAIEC,wBAJF,EAKEC,iBALF,QAMO,uBANP;;AAUA,MAAMC,aAAa,GAAG,cAA6F;EAAA,IAAtF;IAAEC,GAAF;IAAOC,IAAP;IAAaC,IAAb;IAAmBC;EAAnB,CAAsF;EACjH;EACA,IAAI,CAACH,GAAL,EAAU,OAAO,IAAP;EAEV,IAAII,QAAQ,GAAGF,IAAI,IAAIG,MAAM,CAACC,IAAI,CAACC,GAAL,EAAD,CAA7B;EACA,IAAIC,QAAQ,GAAGL,IAAI,IAAI,EAAvB;EAEA,MAAMM,SAAS,GACbf,gBAAgB,CAACU,QAAD,CAAhB,IAA8BT,wBAAwB,CAACa,QAAD,CAAtD,KAAqE,MAAMZ,uBAAuB,CAACI,GAAD,CAAlG,CADF;;EAGA,IAAIS,SAAJ,EAAe;IACbL,QAAQ,GAAGN,iBAAiB,CAACM,QAAD,EAAWK,SAAX,CAA5B;;IACA,IAAI,CAACD,QAAL,EAAe;MACbA,QAAQ,GAAGX,wBAAwB,CAACY,SAAD,CAAnC;IACD;EACF;;EAED,OAAO;IAAET,GAAF;IAAOE,IAAI,EAAEE,QAAb;IAAuBD,IAAI,EAAEK,QAA7B;IAAuCP,IAAI,EAAEA,IAAI,IAAI;EAArD,CAAP;AACD,CAlBD;;AAoBA,eAAeF,aAAf"}
@@ -1,3 +1,3 @@
1
- const VERSION = '2.4.0';
1
+ const VERSION = '2.4.1';
2
2
  export default VERSION;
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["VERSION"],"sources":["version.ts"],"sourcesContent":["const VERSION = '2.4.0';\nexport default VERSION;\n"],"mappings":"AAAA,MAAMA,OAAO,GAAG,OAAhB;AACA,eAAeA,OAAf"}
1
+ {"version":3,"names":["VERSION"],"sources":["version.ts"],"sourcesContent":["const VERSION = '2.4.1';\nexport default VERSION;\n"],"mappings":"AAAA,MAAMA,OAAO,GAAG,OAAhB;AACA,eAAeA,OAAf"}
@@ -8,7 +8,7 @@ import type { StringSet } from '../localization/StringSet.type';
8
8
  import type { ClipboardServiceInterface, FileServiceInterface, MediaServiceInterface, NotificationServiceInterface } from '../platform/types';
9
9
  import type { ErrorBoundaryProps, LocalCacheStorage } from '../types';
10
10
  export declare const SendbirdUIKit: Readonly<{
11
- VERSION: "2.4.0";
11
+ VERSION: "2.4.1";
12
12
  PLATFORM: string;
13
13
  DEFAULT: {
14
14
  AUTO_PUSH_TOKEN_REGISTRATION: boolean;
@@ -0,0 +1,4 @@
1
+ import type { PartialNullable } from '@sendbird/uikit-utils';
2
+ import type { FilePickerResponse, FileType } from '../platform/types';
3
+ declare const normalizeFile: ({ uri, size, name, type }: PartialNullable<FileType>) => Promise<FilePickerResponse>;
4
+ export default normalizeFile;
@@ -1,2 +1,2 @@
1
- declare const VERSION = "2.4.0";
1
+ declare const VERSION = "2.4.1";
2
2
  export default VERSION;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendbird/uikit-react-native",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "description": "react-native-uikit",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -42,9 +42,9 @@
42
42
  "access": "public"
43
43
  },
44
44
  "dependencies": {
45
- "@sendbird/uikit-chat-hooks": "2.4.0",
46
- "@sendbird/uikit-react-native-foundation": "2.4.0",
47
- "@sendbird/uikit-utils": "2.4.0"
45
+ "@sendbird/uikit-chat-hooks": "2.4.1",
46
+ "@sendbird/uikit-react-native-foundation": "2.4.1",
47
+ "@sendbird/uikit-utils": "2.4.1"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@bam.tech/react-native-image-resizer": "^3.0.4",
@@ -182,5 +182,5 @@
182
182
  "readmeFile": "./README.md",
183
183
  "displayName": "@sendbird/uikit-react-native"
184
184
  },
185
- "gitHead": "05ec076e3a2433089586870c9a7e7bf7639b06be"
185
+ "gitHead": "9ba7a005c941a3edd5b0f78d88d8cd1df6c280fc"
186
186
  }
@@ -120,7 +120,7 @@ const SendInput = forwardRef<RNTextInput, SendInputProps>(function SendInput(
120
120
  // Image compression
121
121
  if (
122
122
  isImage(mediaFile.uri, mediaFile.type) &&
123
- shouldCompressImage(mediaFile.uri, features.imageCompressionEnabled)
123
+ shouldCompressImage(mediaFile.type, features.imageCompressionEnabled)
124
124
  ) {
125
125
  await SBUUtils.safeRun(async () => {
126
126
  const compressed = await mediaService.compressImage({
@@ -170,7 +170,7 @@ const SendInput = forwardRef<RNTextInput, SendInputProps>(function SendInput(
170
170
  // Image compression
171
171
  if (
172
172
  isImage(mediaFile.uri, mediaFile.type) &&
173
- shouldCompressImage(mediaFile.uri, features.imageCompressionEnabled)
173
+ shouldCompressImage(mediaFile.type, features.imageCompressionEnabled)
174
174
  ) {
175
175
  await SBUUtils.safeRun(async () => {
176
176
  const compressed = await mediaService.compressImage({
@@ -203,7 +203,7 @@ const SendInput = forwardRef<RNTextInput, SendInputProps>(function SendInput(
203
203
  // Image compression
204
204
  if (
205
205
  isImage(documentFile.uri, documentFile.type) &&
206
- shouldCompressImage(documentFile.uri, features.imageCompressionEnabled)
206
+ shouldCompressImage(documentFile.type, features.imageCompressionEnabled)
207
207
  ) {
208
208
  await SBUUtils.safeRun(async () => {
209
209
  const compressed = await mediaService.compressImage({
@@ -21,7 +21,7 @@ import type {
21
21
  SendbirdMember,
22
22
  SendbirdUser,
23
23
  } from '@sendbird/uikit-utils';
24
- import { useIsFirstMount } from '@sendbird/uikit-utils';
24
+ import { NOOP, useIsFirstMount } from '@sendbird/uikit-utils';
25
25
 
26
26
  import { LocalizationContext, LocalizationProvider } from '../contexts/LocalizationCtx';
27
27
  import { PlatformServiceProvider } from '../contexts/PlatformServiceCtx';
@@ -287,9 +287,28 @@ const initializeSendbird = (
287
287
  }
288
288
 
289
289
  if (NetInfo?.addEventListener) {
290
+ try {
291
+ // NOTE: For removing buggy behavior of NetInfo.addEventListener
292
+ // When you first add an event listener, it is assumed that the initialization of the internal event detector is done simultaneously.
293
+ // In other words, when you call the first event listener two events are triggered immediately
294
+ // - the one that is called when adding the event listener
295
+ // - and the internal initialization event
296
+ NetInfo.addEventListener(NOOP)();
297
+ } catch {}
298
+
290
299
  const listener = (callback: () => void, callbackType: 'online' | 'offline') => {
300
+ let callCount = 0;
291
301
  const unsubscribe = NetInfo.addEventListener((state) => {
292
302
  const online = Boolean(state.isConnected) || Boolean(state.isInternetReachable);
303
+
304
+ // NOTE: When NetInfo.addEventListener is called
305
+ // the event is immediately triggered regardless of whether the event actually occurred.
306
+ // This is why it filters the first event.
307
+ if (callCount === 0) {
308
+ callCount++;
309
+ return;
310
+ }
311
+
293
312
  if (online && callbackType === 'online') callback();
294
313
  if (!online && callbackType === 'offline') callback();
295
314
  });
@@ -3,12 +3,12 @@ import type * as ExpoFs from 'expo-file-system';
3
3
  import type * as ExpoImagePicker from 'expo-image-picker';
4
4
  import type * as ExpoMediaLibrary from 'expo-media-library';
5
5
 
6
- import { getFileExtension, getFileType } from '@sendbird/uikit-utils';
6
+ import { getFileType } from '@sendbird/uikit-utils';
7
7
 
8
8
  import SBUError from '../libs/SBUError';
9
9
  import type { ExpoMediaLibraryPermissionResponse, ExpoPermissionResponse } from '../utils/expoPermissionGranted';
10
10
  import expoPermissionGranted from '../utils/expoPermissionGranted';
11
- import fileTypeGuard from '../utils/fileTypeGuard';
11
+ import normalizeFile from '../utils/normalizeFile';
12
12
  import type {
13
13
  FilePickerResponse,
14
14
  FileServiceInterface,
@@ -80,10 +80,8 @@ const createExpoFileService = ({
80
80
 
81
81
  const { uri } = response;
82
82
  const { size } = await fsModule.getInfoAsync(response.uri);
83
- const ext = getFileExtension(uri);
84
- const type = getFileType(ext);
85
83
 
86
- return fileTypeGuard({ uri, size, type: `${type}/${ext.slice(1)}`, name: Date.now() + ext });
84
+ return normalizeFile({ uri, size });
87
85
  }
88
86
  async openMediaLibrary(options: OpenMediaLibraryOptions) {
89
87
  const hasPermission = await this.hasMediaLibraryPermission('read');
@@ -111,11 +109,8 @@ const createExpoFileService = ({
111
109
  });
112
110
  if (response.cancelled) return null;
113
111
  const { uri } = response;
114
-
115
112
  const { size } = await fsModule.getInfoAsync(uri);
116
- const ext = getFileExtension(uri);
117
- const type = getFileType(ext);
118
- return [fileTypeGuard({ uri, size, type: `${type}/${ext.slice(1)}`, name: Date.now() + ext })];
113
+ return [await normalizeFile({ uri, size })];
119
114
  }
120
115
 
121
116
  async openDocument(options?: OpenDocumentOptions): Promise<FilePickerResponse> {
@@ -123,7 +118,7 @@ const createExpoFileService = ({
123
118
  const response = await documentPickerModule.getDocumentAsync({ type: '*/*' });
124
119
  if (response.type === 'cancel') return null;
125
120
  const { mimeType, uri, size, name } = response;
126
- return fileTypeGuard({ uri, size, name, type: mimeType });
121
+ return normalizeFile({ uri, size, name, type: mimeType });
127
122
  } catch (e) {
128
123
  options?.onOpenFailure?.(SBUError.UNKNOWN, e);
129
124
  return null;
@@ -6,11 +6,17 @@ import type * as ImagePicker from 'react-native-image-picker';
6
6
  import type * as Permissions from 'react-native-permissions';
7
7
  import type { Permission } from 'react-native-permissions';
8
8
 
9
- import { getFileExtension, getFileType, normalizeFileName } from '@sendbird/uikit-utils';
9
+ import {
10
+ getFileExtension,
11
+ getFileExtensionFromMime,
12
+ getFileExtensionFromUri,
13
+ getFileType,
14
+ normalizeFileName,
15
+ } from '@sendbird/uikit-utils';
10
16
 
11
17
  import SBUError from '../libs/SBUError';
12
- import fileTypeGuard from '../utils/fileTypeGuard';
13
18
  import nativePermissionGranted from '../utils/nativePermissionGranted';
19
+ import normalizeFile from '../utils/normalizeFile';
14
20
  import type {
15
21
  FilePickerResponse,
16
22
  FileServiceInterface,
@@ -124,7 +130,7 @@ const createNativeFileService = ({
124
130
  }
125
131
 
126
132
  const { fileName: name, fileSize: size, type, uri } = response.assets?.[0] ?? {};
127
- return fileTypeGuard({ uri, size, name, type });
133
+ return normalizeFile({ uri, size, name, type });
128
134
  }
129
135
  async openMediaLibrary(options?: OpenMediaLibraryOptions): Promise<FilePickerResponse[] | null> {
130
136
  /**
@@ -163,14 +169,16 @@ const createNativeFileService = ({
163
169
  return null;
164
170
  }
165
171
 
166
- return (response.assets || [])
167
- .slice(0, selectionLimit)
168
- .map(({ fileName: name, fileSize: size, type, uri }) => fileTypeGuard({ uri, size, name, type }));
172
+ return Promise.all(
173
+ (response.assets || [])
174
+ .slice(0, selectionLimit)
175
+ .map(({ fileName: name, fileSize: size, type, uri }) => normalizeFile({ uri, size, name, type })),
176
+ );
169
177
  }
170
178
  async openDocument(options?: OpenDocumentOptions): Promise<FilePickerResponse> {
171
179
  try {
172
180
  const { uri, size, name, type } = await documentPickerModule.pickSingle();
173
- return fileTypeGuard({ uri, size, name, type });
181
+ return normalizeFile({ uri, size, name, type });
174
182
  } catch (e) {
175
183
  if (!documentPickerModule.isCancel(e) && documentPickerModule.isInProgress(e)) {
176
184
  options?.onOpenFailure?.(SBUError.UNKNOWN, e);
@@ -185,33 +193,50 @@ const createNativeFileService = ({
185
193
  if (!granted) throw new Error('Permission not granted');
186
194
  }
187
195
 
188
- const basePath = Platform.select({ android: fsModule.Dirs.CacheDir, default: fsModule.Dirs.DocumentDir });
189
- let downloadPath = `${basePath}/${options.fileName}`;
190
- if (!getFileExtension(options.fileName)) {
191
- const extensionFromUrl = getFileExtension(options.fileUrl);
192
- if (getFileType(extensionFromUrl).match(/image|video/)) {
193
- downloadPath += extensionFromUrl;
194
- }
195
- }
196
-
197
- await fsModule.FileSystem.fetch(options.fileUrl, { path: downloadPath });
198
- const fileType = getFileType(getFileExtension(options.fileUrl));
196
+ const { downloadedPath, file } = await this.downloadFile(options);
199
197
 
200
- if (Platform.OS === 'ios' && (fileType === 'image' || fileType === 'video')) {
201
- const type = ({ 'image': 'photo', 'video': 'video' } as const)[fileType];
202
- await mediaLibraryModule.save(downloadPath, { type });
198
+ if (Platform.OS === 'ios') {
199
+ if (file.type === 'image' || file.type === 'video') {
200
+ const mediaTypeMap = { 'image': 'photo', 'video': 'video' } as const;
201
+ const mediaType = mediaTypeMap[file.type];
202
+ await mediaLibraryModule.save(downloadedPath, { type: mediaType });
203
+ }
203
204
  }
204
205
 
205
206
  if (Platform.OS === 'android') {
206
- const dirType = { 'file': 'downloads', 'audio': 'audio', 'image': 'images', 'video': 'video' } as const;
207
- await fsModule.FileSystem.cpExternal(
208
- downloadPath,
209
- normalizeFileName(options.fileName, getFileExtension(options.fileUrl)),
210
- dirType[fileType],
211
- );
207
+ const externalDirMap = { 'file': 'downloads', 'audio': 'audio', 'image': 'images', 'video': 'video' } as const;
208
+ const externalDir = externalDirMap[file.type];
209
+ await fsModule.FileSystem.cpExternal(downloadedPath, file.name, externalDir);
212
210
  }
213
- return downloadPath;
211
+
212
+ return downloadedPath;
214
213
  }
214
+
215
+ private buildDownloadPath = async (options: SaveOptions) => {
216
+ const dirname = Platform.select({ android: fsModule.Dirs.CacheDir, default: fsModule.Dirs.DocumentDir });
217
+ const context = { dirname, filename: options.fileName };
218
+ const extension =
219
+ getFileExtension(options.fileName) ||
220
+ getFileExtensionFromMime(options.fileType) ||
221
+ getFileExtension(options.fileUrl) ||
222
+ (await getFileExtensionFromUri(options.fileUrl));
223
+
224
+ if (extension) context.filename = normalizeFileName(context.filename, extension);
225
+
226
+ return { path: `${context.dirname}/${context.filename}`, ...context };
227
+ };
228
+
229
+ private downloadFile = async (options: SaveOptions) => {
230
+ const { path, filename } = await this.buildDownloadPath(options);
231
+ await fsModule.FileSystem.fetch(options.fileUrl, { path });
232
+ return {
233
+ downloadedPath: path,
234
+ file: {
235
+ name: filename,
236
+ type: getFileType(getFileExtension(path)),
237
+ } as const,
238
+ };
239
+ };
215
240
  }
216
241
 
217
242
  return new NativeFileService();
@@ -0,0 +1,32 @@
1
+ import type { PartialNullable } from '@sendbird/uikit-utils';
2
+ import {
3
+ getFileExtension,
4
+ getFileExtensionFromMime,
5
+ getFileExtensionFromUri,
6
+ getMimeFromFileExtension,
7
+ normalizeFileName,
8
+ } from '@sendbird/uikit-utils';
9
+
10
+ import type { FilePickerResponse, FileType } from '../platform/types';
11
+
12
+ const normalizeFile = async ({ uri, size, name, type }: PartialNullable<FileType>): Promise<FilePickerResponse> => {
13
+ // URI is required property
14
+ if (!uri) return null;
15
+
16
+ let filename = name || String(Date.now());
17
+ let filetype = type || '';
18
+
19
+ const extension =
20
+ getFileExtension(filename) || getFileExtensionFromMime(filetype) || (await getFileExtensionFromUri(uri));
21
+
22
+ if (extension) {
23
+ filename = normalizeFileName(filename, extension);
24
+ if (!filetype) {
25
+ filetype = getMimeFromFileExtension(extension);
26
+ }
27
+ }
28
+
29
+ return { uri, name: filename, type: filetype, size: size ?? 0 };
30
+ };
31
+
32
+ export default normalizeFile;
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
- const VERSION = '2.4.0';
1
+ const VERSION = '2.4.1';
2
2
  export default VERSION;