diff --git a/README.md b/README.md index 021859f161..1c923daf95 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ [![NPM](https://img.shields.io/npm/v/stream-chat-react-native.svg)](https://www.npmjs.com/package/stream-chat-react-native) [![Build Status](https://github.com/GetStream/stream-chat-react-native/actions/workflows/release.yml/badge.svg)](https://github.com/GetStream/stream-chat-react-native/actions) [![Component Reference](https://img.shields.io/badge/docs-component%20reference-blue.svg)](https://getstream.io/chat/docs/sdk/reactnative) -![JS Bundle Size](https://img.shields.io/badge/js_bundle_size-439%20KB-blue) +![JS Bundle Size](https://img.shields.io/badge/js_bundle_size-438.6728515625%20KB-blue) diff --git a/examples/SampleApp/.bundle/config b/examples/SampleApp/.bundle/config index d137d242ed..4c9a330d7b 100644 --- a/examples/SampleApp/.bundle/config +++ b/examples/SampleApp/.bundle/config @@ -1,2 +1,4 @@ -BUNDLE_PATH: "vendor/bundle" -BUNDLE_FORCE_RUBY_PLATFORM: 1 \ No newline at end of file +--- +BUNDLE_PATH: "/home/runner/work/stream-chat-react-native/stream-chat-react-native/examples/SampleApp/vendor/bundle" +BUNDLE_FORCE_RUBY_PLATFORM: "1" +BUNDLE_DEPLOYMENT: "true" diff --git a/examples/SampleApp/Gemfile.lock b/examples/SampleApp/Gemfile.lock index c399cce1de..ab10e3d6d2 100644 --- a/examples/SampleApp/Gemfile.lock +++ b/examples/SampleApp/Gemfile.lock @@ -163,7 +163,7 @@ GEM google-apis-firebaseappdistribution_v1 (~> 0.3.0) google-apis-firebaseappdistribution_v1alpha (~> 0.2.0) fastlane-plugin-load_json (0.0.1) - fastlane-plugin-stream_actions (0.3.65) + fastlane-plugin-stream_actions (0.3.67) xctest_list (= 1.2.1) ffi (1.17.0) fourflusher (2.3.1) @@ -320,7 +320,7 @@ DEPENDENCIES fastlane fastlane-plugin-firebase_app_distribution fastlane-plugin-load_json - fastlane-plugin-stream_actions (= 0.3.65) + fastlane-plugin-stream_actions (= 0.3.67) rubocop-performance rubocop-require_tools diff --git a/examples/SampleApp/fastlane/Fastfile b/examples/SampleApp/fastlane/Fastfile index 6e2b2169f6..b1d7416d0a 100644 --- a/examples/SampleApp/fastlane/Fastfile +++ b/examples/SampleApp/fastlane/Fastfile @@ -133,6 +133,7 @@ lane :update_img_shields_sdk_sizes do |options| update_sdk_size_in_readme( readme_path: '../../README.md', open_pr: options[:open_pr] || false, + pr_title: 'chore: update sdk size', sizes: options[:sizes] || frameworks_sizes, size_ext: sdk_size_ext ) diff --git a/examples/SampleApp/fastlane/Pluginfile b/examples/SampleApp/fastlane/Pluginfile index 96f8283318..f2703d5b51 100644 --- a/examples/SampleApp/fastlane/Pluginfile +++ b/examples/SampleApp/fastlane/Pluginfile @@ -4,4 +4,4 @@ gem 'fastlane-plugin-firebase_app_distribution' gem 'fastlane-plugin-load_json' -gem 'fastlane-plugin-stream_actions', '0.3.65' +gem 'fastlane-plugin-stream_actions', '0.3.67' diff --git a/package/src/components/Chat/hooks/__tests__/useAppSettings.test.tsx b/package/src/components/Chat/hooks/__tests__/useAppSettings.test.tsx index 3f96bdab75..c83111b9f0 100644 --- a/package/src/components/Chat/hooks/__tests__/useAppSettings.test.tsx +++ b/package/src/components/Chat/hooks/__tests__/useAppSettings.test.tsx @@ -18,7 +18,6 @@ describe('useAppSettings', () => { auto_translation_enabled: true, }), ), - userID: 'some-user-id', } as unknown as StreamChat, isOnline, false, diff --git a/package/src/components/Chat/hooks/useAppSettings.ts b/package/src/components/Chat/hooks/useAppSettings.ts index d17a3b61f0..b3e77552db 100644 --- a/package/src/components/Chat/hooks/useAppSettings.ts +++ b/package/src/components/Chat/hooks/useAppSettings.ts @@ -18,12 +18,32 @@ export const useAppSettings = < const isMounted = useIsMountedRef(); useEffect(() => { - async function enforeAppSettings() { - if (!client.userID) return; + /** + * Fetches app settings from the backend when offline support is disabled. + */ + const enforceAppSettingsWithoutOfflineSupport = async () => { + try { + const appSettings = await client.getAppSettings(); + if (isMounted.current) { + setAppSettings(appSettings); + } + } catch (error: unknown) { + if (error instanceof Error) { + console.error(`An error occurred while getting app settings: ${error}`); + } + } + }; - if (enableOfflineSupport && !initialisedDatabase) return; + /** + * Fetches app settings from the local database when offline support is enabled if internet is off else fetches from the backend. + * Note: We need to set the app settings from the local database when offline as the client will not have the app settings in memory. For this we store it for the `client.userID`. + * + * TODO: Remove client.userID usage for offline support case. + */ + const enforceAppSettingsWithOfflineSupport = async () => { + if (!client.userID) return; - if (!isOnline && enableOfflineSupport) { + if (!isOnline) { const appSettings = dbApi.getAppSettings({ currentUserId: client.userID }); setAppSettings(appSettings); return; @@ -33,17 +53,24 @@ export const useAppSettings = < const appSettings = await client.getAppSettings(); if (isMounted.current) { setAppSettings(appSettings); - enableOfflineSupport && - dbApi.upsertAppSettings({ - appSettings, - currentUserId: client.userID as string, - }); + dbApi.upsertAppSettings({ + appSettings, + currentUserId: client.userID as string, + }); } } catch (error: unknown) { if (error instanceof Error) { console.error(`An error occurred while getting app settings: ${error}`); } } + }; + + async function enforeAppSettings() { + if (enableOfflineSupport) { + await enforceAppSettingsWithOfflineSupport(); + } else { + await enforceAppSettingsWithoutOfflineSupport(); + } } enforeAppSettings(); diff --git a/package/src/components/Message/MessageSimple/utils/generateMarkdownText.ts b/package/src/components/Message/MessageSimple/utils/generateMarkdownText.ts index 91fb1010fd..2851be1376 100644 --- a/package/src/components/Message/MessageSimple/utils/generateMarkdownText.ts +++ b/package/src/components/Message/MessageSimple/utils/generateMarkdownText.ts @@ -2,10 +2,7 @@ import truncate from 'lodash/truncate'; import { parseLinksFromText } from './parseLinks'; -// If you need to use any of the special characters literally (actually searching for a "*", for instance), you must escape it by putting a backslash in front of it. -function escapeRegExp(text: string) { - return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); -} +import { escapeRegExp } from '../../../../utils/utils'; export const generateMarkdownText = (text?: string) => { if (!text) return null; diff --git a/package/src/components/Message/MessageSimple/utils/renderText.tsx b/package/src/components/Message/MessageSimple/utils/renderText.tsx index f096bb9adf..5096ea0390 100644 --- a/package/src/components/Message/MessageSimple/utils/renderText.tsx +++ b/package/src/components/Message/MessageSimple/utils/renderText.tsx @@ -23,6 +23,7 @@ import { generateMarkdownText } from './generateMarkdownText'; import type { MessageContextValue } from '../../../../contexts/messageContext/MessageContext'; import type { Colors, MarkdownStyle } from '../../../../contexts/themeContext/utils/theme'; import type { DefaultStreamChatGenerics } from '../../../../types/types'; +import { escapeRegExp } from '../../../../utils/utils'; import type { MessageType } from '../../../MessageList/hooks/useMessageList'; const defaultMarkdownStyles: MarkdownStyle = { @@ -211,10 +212,6 @@ export const renderText = < ); }; - function escapeRegExp(text: string) { - return text.replace(/[-[\]{}()*+?.,/\\^$|#]/g, '\\$&'); - } - // take the @ mentions and turn them into markdown? // translate links const { mentioned_users } = message; diff --git a/package/src/contexts/messageInputContext/MessageInputContext.tsx b/package/src/contexts/messageInputContext/MessageInputContext.tsx index 412e0c7564..d27de877fe 100644 --- a/package/src/contexts/messageInputContext/MessageInputContext.tsx +++ b/package/src/contexts/messageInputContext/MessageInputContext.tsx @@ -87,6 +87,15 @@ import { DEFAULT_BASE_CONTEXT_VALUE } from '../utils/defaultBaseContextValue'; import { getDisplayName } from '../utils/getDisplayName'; import { isTestEnvironment } from '../utils/isTestEnvironment'; +/** + * Function to escape special characters except . in a string and replace with '_' + * @param text + * @returns string + */ +function escapeRegExp(text: string) { + return text.replace(/[[\]{}()*+?,\\^$|#\s]/g, '_'); +} + export type EmojiSearchIndex = { search: (query: string) => PromiseLike> | Array | null; }; @@ -1149,6 +1158,9 @@ export const MessageInputProvider = < const uploadFile = async ({ newFile }: { newFile: FileUpload }) => { const { file, id } = newFile; + // The file name can have special characters, so we escape it. + const filename = escapeRegExp(file.name); + setFileUploads(getUploadSetStateAction(id, FileState.UPLOADING)); let response: Partial = {}; @@ -1157,17 +1169,17 @@ export const MessageInputProvider = < response = await value.doDocUploadRequest(file, channel); } else if (channel && file.uri) { uploadAbortControllerRef.current.set( - file.name, + filename, client.createAbortControllerForNextRequest(), ); // Compress images selected through file picker when uploading them if (file.mimeType?.includes('image')) { const compressedUri = await compressedImageURI(file, value.compressImageQuality); - response = await channel.sendFile(compressedUri, file.name, file.mimeType); + response = await channel.sendFile(compressedUri, filename, file.mimeType); } else { - response = await channel.sendFile(file.uri, file.name, file.mimeType); + response = await channel.sendFile(file.uri, filename, file.mimeType); } - uploadAbortControllerRef.current.delete(file.name); + uploadAbortControllerRef.current.delete(filename); } const extraData: Partial = { @@ -1181,7 +1193,7 @@ export const MessageInputProvider = < (error.name === 'AbortError' || error.name === 'CanceledError') ) { // nothing to do - uploadAbortControllerRef.current.delete(file.name); + uploadAbortControllerRef.current.delete(filename); return; } handleFileOrImageUploadError(error, false, id); @@ -1198,7 +1210,8 @@ export const MessageInputProvider = < let response = {} as SendFileAPIResponse; const uri = file.uri || ''; - const filename = file.name ?? getFileNameFromPath(uri); + // The file name can have special characters, so we escape it. + const filename = escapeRegExp(file.name ?? getFileNameFromPath(uri)); try { const compressedUri = await compressedImageURI(file, value.compressImageQuality); diff --git a/package/src/utils/utils.ts b/package/src/utils/utils.ts index 5240790493..c7b0165480 100644 --- a/package/src/utils/utils.ts +++ b/package/src/utils/utils.ts @@ -677,3 +677,12 @@ export const getDurationLabelFromDuration = (duration: number) => { return durationLabel; }; + +/** + * Utility to escape special characters in a string. + * @param text + * @returns string + */ +export function escapeRegExp(text: string) { + return text.replace(/[-[\]{}()*+?.,/\\^$|#]/g, '\\$&'); +}