diff --git a/react-native/android/app/build.gradle b/react-native/android/app/build.gradle index dccdc44..f621bfa 100644 --- a/react-native/android/app/build.gradle +++ b/react-native/android/app/build.gradle @@ -79,7 +79,7 @@ android { applicationId "com.aws.swiftchat" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 8 + versionCode 10 versionName "1.5.0" ndk { //noinspection ChromeOsAbiSupport diff --git a/react-native/ios/SwiftChat.xcodeproj/project.pbxproj b/react-native/ios/SwiftChat.xcodeproj/project.pbxproj index 16b0aed..86211a4 100644 --- a/react-native/ios/SwiftChat.xcodeproj/project.pbxproj +++ b/react-native/ios/SwiftChat.xcodeproj/project.pbxproj @@ -485,7 +485,7 @@ CODE_SIGN_ENTITLEMENTS = SwiftChat/SwiftChat.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 8; + CURRENT_PROJECT_VERSION = 10; DEVELOPMENT_TEAM = BUA6W9H7T3; ENABLE_BITCODE = NO; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; @@ -526,7 +526,7 @@ CODE_SIGN_ENTITLEMENTS = SwiftChat/SwiftChat.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 8; + CURRENT_PROJECT_VERSION = 10; DEVELOPMENT_TEAM = BUA6W9H7T3; "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES; INFOPLIST_FILE = SwiftChat/Info.plist; diff --git a/react-native/src/chat/ChatScreen.tsx b/react-native/src/chat/ChatScreen.tsx index 6923d6c..e859d78 100644 --- a/react-native/src/chat/ChatScreen.tsx +++ b/react-native/src/chat/ChatScreen.tsx @@ -201,6 +201,8 @@ function ChatScreen(): React.JSX.Element { const { id } = event.params; if (sessionIdRef.current === id) { sessionIdRef.current = getSessionId() + 1; + setUsage(undefined); + bedrockMessages.current = []; clearCachedNode(); setMessages([]); } @@ -501,7 +503,9 @@ function ChatScreen(): React.JSX.Element { /> ) } - renderMessage={props => } + renderMessage={props => ( + + )} listViewProps={{ contentContainerStyle: styles.contentContainer, contentInset: { top: 2 }, diff --git a/react-native/src/chat/component/CustomFileListComponent.tsx b/react-native/src/chat/component/CustomFileListComponent.tsx index 88da348..f9f5dd4 100644 --- a/react-native/src/chat/component/CustomFileListComponent.tsx +++ b/react-native/src/chat/component/CustomFileListComponent.tsx @@ -14,6 +14,7 @@ import { ImageSource } from 'react-native-image-viewing/dist/@types'; import Share from 'react-native-share'; import FileViewer from 'react-native-file-viewer'; import { isMac } from '../../App.tsx'; +import { getFullFileUrl } from '../util/FileUtils.ts'; interface CustomFileProps { files: FileInfo[]; @@ -55,6 +56,7 @@ export const CustomFileListComponent: React.FC = ({ const renderFileItem = (file: FileInfo, fileIndex: number) => { const isImage = file.type === FileType.image; + const fullFileUrl = getFullFileUrl(file.url); const itemKey = `file-${fileIndex}-${file.url}`; return ( = ({ try { const options = { type: 'text/plain', - url: file.url, + url: fullFileUrl, showAppsToView: true, }; Share.open(options).then(); @@ -91,13 +93,13 @@ export const CustomFileListComponent: React.FC = ({ }} onPress={() => { if (isMac || file.type === FileType.document) { - openInFileViewer(file.url); + openInFileViewer(fullFileUrl); } else { const images = files .filter(item => item.type === FileType.image) - .map(item => ({ uri: item.url })); + .map(item => ({ uri: getFullFileUrl(item.url) })); const currentIndex = images.findIndex( - img => img.uri === file.url + img => img.uri === fullFileUrl ); setImageUrls(images); setIndex(currentIndex); @@ -106,7 +108,7 @@ export const CustomFileListComponent: React.FC = ({ }}> {isImage ? ( diff --git a/react-native/src/chat/component/CustomMessageComponent.tsx b/react-native/src/chat/component/CustomMessageComponent.tsx index bad67f1..e35dcfe 100644 --- a/react-native/src/chat/component/CustomMessageComponent.tsx +++ b/react-native/src/chat/component/CustomMessageComponent.tsx @@ -20,7 +20,7 @@ import { IMessage, MessageProps } from 'react-native-gifted-chat'; import { CustomMarkdownRenderer } from './CustomMarkdownRenderer.tsx'; import { MarkedStyles } from 'react-native-marked/src/theme/types.ts'; import ImageView from 'react-native-image-viewing'; -import { PressMode } from '../../types/Chat.ts'; +import { ChatStatus, PressMode } from '../../types/Chat.ts'; import { trigger } from '../util/HapticUtils.ts'; import { HapticFeedbackTypes } from 'react-native-haptic-feedback/src/types.ts'; import Clipboard from '@react-native-clipboard/clipboard'; @@ -33,8 +33,13 @@ import { isMac } from '../../App.tsx'; import { CustomTokenizer } from './CustomTokenizer.ts'; import { State, TapGestureHandler } from 'react-native-gesture-handler'; -const CustomMessageComponent: React.FC> = ({ +interface CustomMessageProps extends MessageProps { + chatStatus: ChatStatus; +} + +const CustomMessageComponent: React.FC = ({ currentMessage, + chatStatus, }) => { const [visible, setIsVisible] = useState(false); const [imgUrl, setImgUrl] = useState(''); @@ -79,7 +84,7 @@ const CustomMessageComponent: React.FC> = ({ const customTokenizer = useMemo(() => new CustomTokenizer(), []); const handleCopy = () => { if (isEdit) { - setIsEdit(false); + setIsEditValue(false); return; } Clipboard.setString(currentMessage?.text ?? ''); @@ -87,11 +92,17 @@ const CustomMessageComponent: React.FC> = ({ }; const handleEdit = () => { - setIsEdit(!isEdit); + setIsEditValue(!isEdit); }; const onDoubleTap = () => { - setIsEdit(true); + setIsEditValue(true); + }; + + const setIsEditValue = (value: boolean) => { + if (chatStatus !== ChatStatus.Running) { + setIsEdit(value); + } }; useEffect(() => { diff --git a/react-native/src/chat/util/FileUtils.ts b/react-native/src/chat/util/FileUtils.ts index 5111828..08c9d2f 100644 --- a/react-native/src/chat/util/FileUtils.ts +++ b/react-native/src/chat/util/FileUtils.ts @@ -24,11 +24,12 @@ export const saveFile = async (sourceUrl: string, fileName: string) => { if (!filesDirExists) { await RNFS.mkdir(filesDir); } - const destinationPath = await getUniqueFileName(filesDir, fileName); + const uniqueFileName = await getUniqueFileName(filesDir, fileName); + const destinationPath = `${filesDir}/${uniqueFileName}`; await RNFS.copyFile(sourceUrl, destinationPath); return Platform.OS === 'android' ? `file://${destinationPath}` - : destinationPath; + : `files/${uniqueFileName}`; } catch (error) { console.warn('Error saving file:', error); } @@ -37,7 +38,8 @@ export const saveFile = async (sourceUrl: string, fileName: string) => { export const getFileBytes = async (fileUrl: string) => { try { - return await RNFS.readFile(fileUrl, 'base64'); + const fullFileUrl = getFullFileUrl(fileUrl); + return await RNFS.readFile(fullFileUrl, 'base64'); } catch (error) { console.warn('Error reading image file:', fileUrl, error); throw error; @@ -61,7 +63,21 @@ const getUniqueFileName = async ( finalFileName = `${nameWithoutExt}(${counter})${extension}`; finalPath = `${basePath}/${finalFileName}`; } - return finalPath; + return finalFileName; +}; + +export const getFullFileUrl = (url: string) => { + if (Platform.OS === 'android') { + return url; + } else if (url.startsWith('files/')) { + return `${RNFS.DocumentDirectoryPath}/${url}`; + } else { + return ( + RNFS.DocumentDirectoryPath + + '/files' + + url.substring(url.lastIndexOf('/')) + ); + } }; const MAX_IMAGES = 20;