diff --git a/README.md b/README.md
index aae1b8561b..b42278100f 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
[](https://www.npmjs.com/package/stream-chat-react-native)
[](https://github.com/GetStream/stream-chat-react-native/actions)
[](https://getstream.io/chat/docs/sdk/reactnative)
-
+
diff --git a/examples/ExpoMessaging/app/index.tsx b/examples/ExpoMessaging/app/index.tsx
index 14742ae6ea..e484cccbfa 100644
--- a/examples/ExpoMessaging/app/index.tsx
+++ b/examples/ExpoMessaging/app/index.tsx
@@ -1,5 +1,5 @@
import { Alert, Image, Pressable, StyleSheet, View } from 'react-native';
-import { ChannelList } from 'stream-chat-expo';
+import { ChannelList, SqliteClient } from 'stream-chat-expo';
import { useCallback, useContext, useMemo } from 'react';
import { Stack, useRouter } from 'expo-router';
import { ChannelSort } from 'stream-chat';
@@ -19,7 +19,13 @@ const LogoutButton = () => {
const onLogoutHandler = useCallback(() => {
Alert.alert('Logout', 'Are you sure you want to logout?', [
{ text: 'Cancel', style: 'cancel' },
- { text: 'Logout', onPress: logOut },
+ {
+ text: 'Logout',
+ onPress: () => {
+ SqliteClient.resetDB();
+ logOut();
+ },
+ },
]);
}, [logOut]);
diff --git a/examples/ExpoMessaging/components/ChatWrapper.tsx b/examples/ExpoMessaging/components/ChatWrapper.tsx
index 4054f9edc6..d0bf6eff10 100644
--- a/examples/ExpoMessaging/components/ChatWrapper.tsx
+++ b/examples/ExpoMessaging/components/ChatWrapper.tsx
@@ -46,7 +46,7 @@ export const ChatWrapper = ({ children }: PropsWithChildren<{}>) => {
return (
-
+
{children}
diff --git a/examples/SampleApp/src/utils/messageActions.tsx b/examples/SampleApp/src/utils/messageActions.tsx
index e462763e22..7dafc4ff79 100644
--- a/examples/SampleApp/src/utils/messageActions.tsx
+++ b/examples/SampleApp/src/utils/messageActions.tsx
@@ -2,6 +2,7 @@ import { Alert } from 'react-native';
import { StreamChat } from 'stream-chat';
import {
Colors,
+ Delete,
messageActions,
MessageActionsParams,
Time,
@@ -20,7 +21,7 @@ export function channelMessageActions({
t: TranslationContextValue['t'];
colors?: typeof Colors;
}) {
- const { dismissOverlay } = params;
+ const { dismissOverlay, deleteForMeMessage } = params;
const actions = messageActions(params);
// We cannot use the useMessageReminder hook here because it is a hook.
@@ -88,6 +89,27 @@ export function channelMessageActions({
title: reminder ? 'Remove Reminder' : 'Remind Me',
icon: ,
});
+ actions.push({
+ action: async () => {
+ Alert.alert('Delete for me', 'Are you sure you want to delete this message for me?', [
+ {
+ text: 'Cancel',
+ style: 'cancel',
+ },
+ {
+ text: 'Delete',
+ onPress: async () => {
+ await deleteForMeMessage?.action();
+ dismissOverlay();
+ },
+ style: 'destructive',
+ },
+ ]);
+ },
+ actionType: 'deleteForMe',
+ icon: ,
+ title: t('Delete for me'),
+ });
return actions;
}
diff --git a/examples/TypeScriptMessaging/ios/Podfile.lock b/examples/TypeScriptMessaging/ios/Podfile.lock
index 622d093c3b..47c9e973e8 100644
--- a/examples/TypeScriptMessaging/ios/Podfile.lock
+++ b/examples/TypeScriptMessaging/ios/Podfile.lock
@@ -3248,4 +3248,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 6b7a4b74915b42bfe4ffddaf67cbf5e7a2bfeab3
-COCOAPODS: 1.16.2
+COCOAPODS: 1.14.3
diff --git a/examples/TypeScriptMessaging/yarn.lock b/examples/TypeScriptMessaging/yarn.lock
index 46cd8ec3b9..5ae0cf23fe 100644
--- a/examples/TypeScriptMessaging/yarn.lock
+++ b/examples/TypeScriptMessaging/yarn.lock
@@ -2734,6 +2734,15 @@ available-typed-arrays@^1.0.7:
dependencies:
possible-typed-array-names "^1.0.0"
+axios@^1.12.2:
+ version "1.12.2"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.2.tgz#6c307390136cf7a2278d09cec63b136dfc6e6da7"
+ integrity sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==
+ dependencies:
+ follow-redirects "^1.15.6"
+ form-data "^4.0.4"
+ proxy-from-env "^1.1.0"
+
axios@^1.6.0:
version "1.7.9"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a"
@@ -7319,14 +7328,14 @@ stream-chat-react-native-core@8.1.0:
version "0.0.0"
uid ""
-stream-chat@^9.17.0:
- version "9.17.0"
- resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-9.17.0.tgz#540cf1ea03b08a394d6140696aae8528e9ba9ce2"
- integrity sha512-ys6K73wIVWs5+qsfPJ9wumEUtgbMXYVbH1dhmAZ1oYtQ01dY/avsvt25PYDakVjKeyrnT+y8T/xEzfeF/WDJsg==
+stream-chat@^9.23.0:
+ version "9.24.0"
+ resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-9.24.0.tgz#e6af5d4b0eb396e24e0ab7f852719581c39f18bc"
+ integrity sha512-zLtguYRqxeEc/Cjw8Zp00u/wTrqFg4gFPKdj3mvl/Jq1Pt95mY9nMc38KW0GOu/2quIAAar0NNMq8fsXl4jupQ==
dependencies:
"@types/jsonwebtoken" "^9.0.8"
"@types/ws" "^8.5.14"
- axios "^1.6.0"
+ axios "^1.12.2"
base64-js "^1.5.1"
form-data "^4.0.4"
isomorphic-ws "^5.0.0"
diff --git a/package/expo-package/src/optionalDependencies/deleteFile.ts b/package/expo-package/src/optionalDependencies/deleteFile.ts
index 694503293a..5213f53345 100644
--- a/package/expo-package/src/optionalDependencies/deleteFile.ts
+++ b/package/expo-package/src/optionalDependencies/deleteFile.ts
@@ -1,7 +1,7 @@
let FileSystem;
try {
- FileSystem = require('expo-file-system');
+ FileSystem = require('expo-file-system/legacy');
} catch (e) {
// do nothing
}
diff --git a/package/expo-package/src/optionalDependencies/pickImage.ts b/package/expo-package/src/optionalDependencies/pickImage.ts
index 25ef02f531..280710b4c2 100644
--- a/package/expo-package/src/optionalDependencies/pickImage.ts
+++ b/package/expo-package/src/optionalDependencies/pickImage.ts
@@ -1,4 +1,5 @@
import { Platform } from 'react-native';
+import { PickImageOptions } from 'stream-chat-react-native-core';
let ImagePicker;
try {
@@ -14,7 +15,7 @@ if (!ImagePicker) {
}
export const pickImage = ImagePicker
- ? async () => {
+ ? async ({ maxNumberOfFiles }: PickImageOptions = {}) => {
try {
let permissionGranted = true;
if (Platform.OS === 'ios') {
@@ -35,6 +36,7 @@ export const pickImage = ImagePicker
allowsMultipleSelection: true,
mediaTypes: ['images', 'videos'],
preferredAssetRepresentationMode: 'current',
+ selectionLimit: maxNumberOfFiles,
});
const canceled = result.canceled;
diff --git a/package/expo-package/src/optionalDependencies/saveFile.ts b/package/expo-package/src/optionalDependencies/saveFile.ts
index 3e768d2be6..c4d5070a54 100644
--- a/package/expo-package/src/optionalDependencies/saveFile.ts
+++ b/package/expo-package/src/optionalDependencies/saveFile.ts
@@ -1,7 +1,7 @@
let FileSystem;
try {
- FileSystem = require('expo-file-system');
+ FileSystem = require('expo-file-system/legacy');
} catch (e) {
// do nothing
}
diff --git a/package/native-package/src/optionalDependencies/pickImage.ts b/package/native-package/src/optionalDependencies/pickImage.ts
index 1d04e68753..dc7966dd99 100644
--- a/package/native-package/src/optionalDependencies/pickImage.ts
+++ b/package/native-package/src/optionalDependencies/pickImage.ts
@@ -1,4 +1,5 @@
import { Platform } from 'react-native';
+import { PickImageOptions } from 'stream-chat-react-native-core';
let ImagePicker;
try {
@@ -8,11 +9,12 @@ try {
}
export const pickImage = ImagePicker
- ? async () => {
+ ? async ({ maxNumberOfFiles }: PickImageOptions = {}) => {
try {
const result = await ImagePicker.launchImageLibrary({
assetRepresentationMode: 'current',
mediaType: 'mixed',
+ selectionLimit: maxNumberOfFiles,
});
const canceled = result.didCancel;
const errorCode = result.errorCode;
diff --git a/package/src/components/Channel/Channel.tsx b/package/src/components/Channel/Channel.tsx
index 9617284455..b2674cdd79 100644
--- a/package/src/components/Channel/Channel.tsx
+++ b/package/src/components/Channel/Channel.tsx
@@ -9,6 +9,7 @@ import {
Channel as ChannelClass,
ChannelState,
Channel as ChannelType,
+ DeleteMessageOptions,
EventHandler,
LocalMessage,
localMessageToNewMessagePayload,
@@ -259,7 +260,9 @@ const debounceOptions = {
};
export type ChannelPropsWithContext = Pick &
- Partial> &
+ Partial<
+ Pick
+ > &
Partial<
Pick<
AttachmentPickerProps,
@@ -324,6 +327,7 @@ export type ChannelPropsWithContext = Pick &
| 'handleBan'
| 'handleCopy'
| 'handleDelete'
+ | 'handleDeleteForMe'
| 'handleEdit'
| 'handleFlag'
| 'handleMarkUnread'
@@ -556,6 +560,7 @@ const ChannelWithContext = (props: PropsWithChildren) =
customMessageSwipeAction,
DateHeader = DateHeaderDefault,
deletedMessagesVisibilityType = 'always',
+ disableAttachmentPicker = !isImageMediaLibraryAvailable(),
disableKeyboardCompatibleView = false,
disableTypingIndicator,
dismissKeyboardOnMessageTouch = true,
@@ -582,6 +587,7 @@ const ChannelWithContext = (props: PropsWithChildren) =
handleBan,
handleCopy,
handleDelete,
+ handleDeleteForMe,
handleEdit,
handleFlag,
handleMarkUnread,
@@ -1515,7 +1521,15 @@ const ChannelWithContext = (props: PropsWithChildren) =
});
const deleteMessage: MessagesContextValue['deleteMessage'] = useStableCallback(
- async (message, hardDelete = false) => {
+ async (message, optionsOrHardDelete = false) => {
+ let options: DeleteMessageOptions = {};
+ if (typeof optionsOrHardDelete === 'boolean') {
+ options = optionsOrHardDelete ? { hardDelete: true } : {};
+ } else if (optionsOrHardDelete?.deleteForMe) {
+ options = { deleteForMe: true };
+ } else if (optionsOrHardDelete?.hardDelete) {
+ options = { hardDelete: true };
+ }
if (!channel.id) {
throw new Error('Channel has not been initialized yet');
}
@@ -1534,7 +1548,7 @@ const ChannelWithContext = (props: PropsWithChildren) =
threadInstance?.upsertReplyLocally({ message: updatedMessage });
- const data = await client.deleteMessage(message.id, hardDelete);
+ const data = await client.deleteMessage(message.id, options);
if (data?.message) {
updateMessage({ ...data.message });
@@ -1679,10 +1693,11 @@ const ChannelWithContext = (props: PropsWithChildren) =
bottomInset,
bottomSheetRef,
closePicker: () => closePicker(bottomSheetRef),
+ disableAttachmentPicker,
openPicker: () => openPicker(bottomSheetRef),
topInset,
}),
- [bottomInset, bottomSheetRef, closePicker, openPicker, topInset],
+ [bottomInset, bottomSheetRef, closePicker, openPicker, topInset, disableAttachmentPicker],
);
const ownCapabilitiesContext = useCreateOwnCapabilitiesContext({
@@ -1845,6 +1860,7 @@ const ChannelWithContext = (props: PropsWithChildren) =
handleBan,
handleCopy,
handleDelete,
+ handleDeleteForMe,
handleEdit,
handleFlag,
handleMarkUnread,
@@ -1980,7 +1996,7 @@ const ChannelWithContext = (props: PropsWithChildren) =
{children}
- {isImageMediaLibraryAvailable() && (
+ {!disableAttachmentPicker && (
)}
diff --git a/package/src/components/Channel/hooks/useCreateMessagesContext.ts b/package/src/components/Channel/hooks/useCreateMessagesContext.ts
index 71f3e02795..3ac1e7236d 100644
--- a/package/src/components/Channel/hooks/useCreateMessagesContext.ts
+++ b/package/src/components/Channel/hooks/useCreateMessagesContext.ts
@@ -33,6 +33,7 @@ export const useCreateMessagesContext = ({
handleBan,
handleCopy,
handleDelete,
+ handleDeleteForMe,
handleEdit,
handleFlag,
handleMarkUnread,
@@ -150,6 +151,7 @@ export const useCreateMessagesContext = ({
handleBan,
handleCopy,
handleDelete,
+ handleDeleteForMe,
handleEdit,
handleFlag,
handleMarkUnread,
diff --git a/package/src/components/ImageGallery/ImageGallery.tsx b/package/src/components/ImageGallery/ImageGallery.tsx
index 10b2380066..c804c0ffd9 100644
--- a/package/src/components/ImageGallery/ImageGallery.tsx
+++ b/package/src/components/ImageGallery/ImageGallery.tsx
@@ -378,7 +378,7 @@ export const ImageGallery = (props: Props) => {
);
/**
- * This transition and scaleX reverse lets use scroll left
+ * This transition and scaleX reverse lets use scroll right
*/
const pagerStyle = useAnimatedStyle(
() => ({
diff --git a/package/src/components/ImageGallery/hooks/useAnimatedGalleryStyle.tsx b/package/src/components/ImageGallery/hooks/useAnimatedGalleryStyle.tsx
index 76d2d683a9..ff9bfce0f9 100644
--- a/package/src/components/ImageGallery/hooks/useAnimatedGalleryStyle.tsx
+++ b/package/src/components/ImageGallery/hooks/useAnimatedGalleryStyle.tsx
@@ -59,17 +59,17 @@ export const useAnimatedGalleryStyle = ({
{
scale: selected ? scale.value / 8 : oneEighth,
},
- { scaleX: -1 },
+ { scaleX: 1 },
],
};
}, [previous, selected]);
const animatedStyles = useAnimatedStyle(() => {
- const xScaleOffset = -7 * screenWidth * (0.5 + index);
+ const xScaleOffset = 7 * screenWidth * (0.5 + index);
const yScaleOffset = -screenHeight * 3.5;
return {
transform: [
- { scaleX: -1 },
+ { scaleX: 1 },
{ translateY: yScaleOffset },
{
translateX: -xScaleOffset,
diff --git a/package/src/components/Message/Message.tsx b/package/src/components/Message/Message.tsx
index 1f10124150..6a677616ae 100644
--- a/package/src/components/Message/Message.tsx
+++ b/package/src/components/Message/Message.tsx
@@ -121,6 +121,7 @@ export type MessagePressableHandlerPayload = PressableHandlerPayload & {
export type MessageActionHandlers = {
copyMessage: () => void;
deleteMessage: () => void;
+ deleteForMeMessage: () => void;
editMessage: () => void;
flagMessage: () => void;
markUnread: () => Promise;
@@ -155,6 +156,7 @@ export type MessagePropsWithContext = Pick<
| 'handleBan'
| 'handleCopy'
| 'handleDelete'
+ | 'handleDeleteForMe'
| 'handleEdit'
| 'handleFlag'
| 'handleMarkUnread'
@@ -229,6 +231,7 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
handleBan,
handleCopy,
handleDelete,
+ handleDeleteForMe,
handleEdit,
handleFlag,
handleMarkUnread,
@@ -487,6 +490,7 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
const {
handleCopyMessage,
handleDeleteMessage,
+ handleDeleteForMeMessage,
handleEditMessage,
handleFlagMessage,
handleMarkUnreadMessage,
@@ -514,6 +518,7 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
banUser,
copyMessage,
deleteMessage,
+ deleteForMeMessage,
editMessage,
flagMessage,
handleReaction,
@@ -534,6 +539,7 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
handleBan,
handleCopy,
handleDelete,
+ handleDeleteForMe,
handleEdit,
handleFlag,
handleMarkUnread,
@@ -565,6 +571,7 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
: messageActionsProp({
banUser,
copyMessage,
+ deleteForMeMessage,
deleteMessage,
dismissOverlay,
editMessage,
@@ -582,10 +589,12 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
showMessageReactions,
threadReply,
unpinMessage,
+ updateMessage,
});
const actionHandlers: MessageActionHandlers = {
copyMessage: handleCopyMessage,
+ deleteForMeMessage: handleDeleteForMeMessage,
deleteMessage: handleDeleteMessage,
editMessage: handleEditMessage,
flagMessage: handleFlagMessage,
diff --git a/package/src/components/Message/hooks/useMessageActionHandlers.ts b/package/src/components/Message/hooks/useMessageActionHandlers.ts
index 3f835dca3c..872cd5e6eb 100644
--- a/package/src/components/Message/hooks/useMessageActionHandlers.ts
+++ b/package/src/components/Message/hooks/useMessageActionHandlers.ts
@@ -66,6 +66,14 @@ export const useMessageActionHandlers = ({
);
};
+ const handleDeleteForMeMessage = async () => {
+ if (!message.id) {
+ return;
+ }
+
+ await deleteMessage(message, { deleteForMe: true });
+ };
+
const handleToggleMuteUser = async () => {
if (!message.user?.id) {
return;
@@ -182,6 +190,7 @@ export const useMessageActionHandlers = ({
return {
handleCopyMessage,
+ handleDeleteForMeMessage,
handleDeleteMessage,
handleEditMessage,
handleFlagMessage,
diff --git a/package/src/components/Message/hooks/useMessageActions.tsx b/package/src/components/Message/hooks/useMessageActions.tsx
index 7f45b451ec..9f307f56fd 100644
--- a/package/src/components/Message/hooks/useMessageActions.tsx
+++ b/package/src/components/Message/hooks/useMessageActions.tsx
@@ -39,6 +39,7 @@ export type MessageActionsHookProps = Pick<
| 'handleBan'
| 'handleCopy'
| 'handleDelete'
+ | 'handleDeleteForMe'
| 'handleEdit'
| 'handleFlag'
| 'handleQuotedReply'
@@ -73,6 +74,7 @@ export const useMessageActions = ({
handleBan,
handleCopy,
handleDelete,
+ handleDeleteForMe,
handleEdit,
handleFlag,
handleMarkUnread,
@@ -101,6 +103,7 @@ export const useMessageActions = ({
const {
handleCopyMessage,
handleDeleteMessage,
+ handleDeleteForMeMessage,
handleEditMessage,
handleFlagMessage,
handleMarkUnreadMessage,
@@ -182,6 +185,19 @@ export const useMessageActions = ({
titleStyle: { color: accent_red },
};
+ const deleteForMeMessage: MessageActionType = {
+ action: () => {
+ dismissOverlay();
+ if (handleDeleteForMe) {
+ handleDeleteForMe(message);
+ }
+ handleDeleteForMeMessage();
+ },
+ actionType: 'deleteForMeMessage',
+ icon: ,
+ title: t('Delete for me'),
+ };
+
const editMessage: MessageActionType = {
action: () => {
dismissOverlay();
@@ -319,6 +335,7 @@ export const useMessageActions = ({
return {
banUser,
copyMessage,
+ deleteForMeMessage,
deleteMessage,
editMessage,
flagMessage,
diff --git a/package/src/components/Message/utils/messageActions.ts b/package/src/components/Message/utils/messageActions.ts
index 8d019238b9..d7973cc2db 100644
--- a/package/src/components/Message/utils/messageActions.ts
+++ b/package/src/components/Message/utils/messageActions.ts
@@ -1,4 +1,5 @@
import type { MessageContextValue } from '../../../contexts/messageContext/MessageContext';
+import type { MessagesContextValue } from '../../../contexts/messagesContext/MessagesContext';
import type { OwnCapabilitiesContextValue } from '../../../contexts/ownCapabilitiesContext/OwnCapabilitiesContext';
import { isClipboardAvailable } from '../../../native';
@@ -25,7 +26,10 @@ export type MessageActionsParams = {
showMessageReactions: boolean;
threadReply: MessageActionType;
unpinMessage: MessageActionType;
-} & Pick;
+ // Optional Actions
+ deleteForMeMessage?: MessageActionType;
+} & Pick &
+ Pick;
export type MessageActionsProp = (param: MessageActionsParams) => MessageActionType[];
diff --git a/package/src/components/MessageInput/AttachButton.tsx b/package/src/components/MessageInput/AttachButton.tsx
index a6e63e0f5c..a5a36efc96 100644
--- a/package/src/components/MessageInput/AttachButton.tsx
+++ b/package/src/components/MessageInput/AttachButton.tsx
@@ -4,7 +4,10 @@ import { Pressable } from 'react-native';
import { NativeAttachmentPicker } from './components/NativeAttachmentPicker';
-import { useAttachmentPickerContext } from '../../contexts/attachmentPickerContext/AttachmentPickerContext';
+import {
+ AttachmentPickerContextValue,
+ useAttachmentPickerContext,
+} from '../../contexts/attachmentPickerContext/AttachmentPickerContext';
import {
MessageInputContextValue,
useMessageInputContext,
@@ -12,22 +15,21 @@ import {
import { useTheme } from '../../contexts/themeContext/ThemeContext';
import { Attach } from '../../icons/Attach';
-import { isImageMediaLibraryAvailable } from '../../native';
-
type AttachButtonPropsWithContext = Pick<
MessageInputContextValue,
'handleAttachButtonPress' | 'toggleAttachmentPicker'
-> & {
- disabled?: boolean;
- /** Function that opens attachment options bottom sheet */
- handleOnPress?: ((event: GestureResponderEvent) => void) & (() => void);
- selectedPicker?: 'images';
-};
+> &
+ Pick & {
+ disabled?: boolean;
+ /** Function that opens attachment options bottom sheet */
+ handleOnPress?: ((event: GestureResponderEvent) => void) & (() => void);
+ };
const AttachButtonWithContext = (props: AttachButtonPropsWithContext) => {
const [showAttachButtonPicker, setShowAttachButtonPicker] = useState(false);
const [attachButtonLayoutRectangle, setAttachButtonLayoutRectangle] = useState();
const {
+ disableAttachmentPicker,
disabled = false,
handleAttachButtonPress,
handleOnPress,
@@ -73,7 +75,7 @@ const AttachButtonWithContext = (props: AttachButtonPropsWithContext) => {
handleAttachButtonPress();
return;
}
- if (isImageMediaLibraryAvailable()) {
+ if (!disableAttachmentPicker) {
toggleAttachmentPicker();
} else {
attachButtonHandler();
@@ -132,12 +134,17 @@ export type AttachButtonProps = Partial;
* UI Component for attach button in MessageInput component.
*/
export const AttachButton = (props: AttachButtonProps) => {
- const { selectedPicker } = useAttachmentPickerContext();
+ const { disableAttachmentPicker, selectedPicker } = useAttachmentPickerContext();
const { handleAttachButtonPress, toggleAttachmentPicker } = useMessageInputContext();
return (
);
diff --git a/package/src/components/MessageInput/MessageInput.tsx b/package/src/components/MessageInput/MessageInput.tsx
index 2df5981793..924828ff08 100644
--- a/package/src/components/MessageInput/MessageInput.tsx
+++ b/package/src/components/MessageInput/MessageInput.tsx
@@ -62,11 +62,7 @@ import {
} from '../../contexts/translationContext/TranslationContext';
import { useStateStore } from '../../hooks/useStateStore';
-import {
- isAudioRecorderAvailable,
- isImageMediaLibraryAvailable,
- NativeHandlers,
-} from '../../native';
+import { isAudioRecorderAvailable, NativeHandlers } from '../../native';
import { AIStates, useAIState } from '../AITypingIndicatorView';
import { AutoCompleteInput } from '../AutoCompleteInput/AutoCompleteInput';
import { CreatePoll } from '../Poll/CreatePollContent';
@@ -112,7 +108,7 @@ const styles = StyleSheet.create({
type MessageInputPropsWithContext = Pick<
AttachmentPickerContextValue,
- 'bottomInset' | 'selectedPicker'
+ 'bottomInset' | 'disableAttachmentPicker' | 'selectedPicker'
> &
Pick &
Pick &
@@ -207,6 +203,7 @@ const MessageInputWithContext = (props: MessageInputPropsWithContext) => {
cooldownEndsAt,
CooldownTimer,
CreatePollContent,
+ disableAttachmentPicker,
editing,
Input,
inputBoxRef,
@@ -564,7 +561,7 @@ const MessageInputWithContext = (props: MessageInputPropsWithContext) => {
- {isImageMediaLibraryAvailable() && selectedPicker ? (
+ {!disableAttachmentPicker && selectedPicker ? (
{
uploadNewFile,
VideoRecorderSelectorIcon,
} = useMessageInputContext();
- const { bottomInset, bottomSheetRef, selectedPicker } = useAttachmentPickerContext();
+ const { bottomInset, bottomSheetRef, disableAttachmentPicker, selectedPicker } =
+ useAttachmentPickerContext();
const messageComposer = useMessageComposer();
const editing = !!messageComposer.editedMessage;
const { clearEditingState } = useMessageComposerAPIContext();
@@ -835,6 +833,7 @@ export const MessageInput = (props: MessageInputProps) => {
CooldownTimer,
CreatePollContent,
CreatePollIcon,
+ disableAttachmentPicker,
editing,
FileSelectorIcon,
ImageSelectorIcon,
diff --git a/package/src/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.tsx b/package/src/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.tsx
index e058876ee2..7c29cc5715 100644
--- a/package/src/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.tsx
+++ b/package/src/components/MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview.tsx
@@ -49,6 +49,10 @@ export const ImageAttachmentUploadPreview = ({
setLoading(false);
}, []);
+ const onErrorHandler = useCallback(() => {
+ setLoading(false);
+ }, []);
+
return (
;
- deleteMessage: (message: LocalMessage, hardDelete?: boolean) => Promise;
+ // FIXME: Remove the signature with optionsOrHardDelete boolean with the next major release
+ deleteMessage: (
+ message: LocalMessage,
+ optionsOrHardDelete?: boolean | DeleteMessageOptions,
+ ) => Promise;
deleteReaction: (type: string, messageId: string) => Promise;
/** Should keyboard be dismissed when messaged is touched */
@@ -404,6 +409,8 @@ export type MessagesContextValue = Pick Promise;
/** Handler to access when a copy message action is invoked */
handleCopy?: (message: LocalMessage) => Promise;
+ /** Handler to access when a delete for me message action is invoked */
+ handleDeleteForMe?: (message: LocalMessage) => Promise;
/** Handler to access when a delete message action is invoked */
handleDelete?: (message: LocalMessage) => Promise;
/** Handler to access when an edit message action is invoked */
diff --git a/package/src/i18n/en.json b/package/src/i18n/en.json
index 3e9465eda0..9837a87b65 100644
--- a/package/src/i18n/en.json
+++ b/package/src/i18n/en.json
@@ -23,6 +23,7 @@
"Create Poll": "Create Poll",
"Delete": "Delete",
"Delete Message": "Delete Message",
+ "Delete for me": "Delete for me",
"Device camera is used to take photos or videos.": "Device camera is used to take photos or videos.",
"Device gallery permissions is used to take photos or videos.": "Device gallery permissions is used to take photos or videos.",
"Do you want to send a copy of this message to a moderator for further investigation?": "Do you want to send a copy of this message to a moderator for further investigation?",
diff --git a/package/src/i18n/es.json b/package/src/i18n/es.json
index 74657b6a1b..4daf4147d7 100644
--- a/package/src/i18n/es.json
+++ b/package/src/i18n/es.json
@@ -23,6 +23,7 @@
"Create Poll": "Crear encuesta",
"Delete": "Eliminar",
"Delete Message": "Eliminar mensaje",
+ "Delete for me": "Eliminar para mí",
"Device camera is used to take photos or videos.": "La cámara del dispositivo se utiliza para tomar fotografías o vídeos.",
"Device gallery permissions is used to take photos or videos.": "Los permisos de la galería del dispositivo se utilizan para tomar fotos o videos.",
"Do you want to send a copy of this message to a moderator for further investigation?": "¿Deseas enviar una copia de este mensaje a un moderador para una investigación adicional?",
diff --git a/package/src/i18n/fr.json b/package/src/i18n/fr.json
index 7ac6cfe1ea..3227ab9290 100644
--- a/package/src/i18n/fr.json
+++ b/package/src/i18n/fr.json
@@ -23,6 +23,7 @@
"Create Poll": "Créer un sondage",
"Delete": "Supprimer",
"Delete Message": "Supprimer un message",
+ "Delete for me": "Supprimer pour moi",
"Device camera is used to take photos or videos.": "L'appareil photo de l'appareil est utilisé pour prendre des photos ou des vidéos.",
"Device gallery permissions is used to take photos or videos.": "Les autorisations de la galerie de l'appareil sont utilisées pour prendre des photos ou des vidéos.",
"Do you want to send a copy of this message to a moderator for further investigation?": "Voulez-vous envoyer une copie de ce message à un modérateur pour une enquête plus approfondie?",
diff --git a/package/src/i18n/he.json b/package/src/i18n/he.json
index 2e913471ee..376f6294eb 100644
--- a/package/src/i18n/he.json
+++ b/package/src/i18n/he.json
@@ -23,6 +23,7 @@
"Create Poll": "צור סקר",
"Delete": "מחק",
"Delete Message": "מחק/י הודעה",
+ "Delete for me": "מחק עבורי",
"Device camera is used to take photos or videos.": "מצלמת המכשיר משמשת לצילום תמונות או סרטונים.",
"Device gallery permissions is used to take photos or videos.": "הרשאות גלריית המכשיר משמשות לצילום תמונות או סרטונים.",
"Do you want to send a copy of this message to a moderator for further investigation?": "האם את/ה רוצה לשלוח עותק של הודעה זו למנחה להמשך חקירה?",
diff --git a/package/src/i18n/hi.json b/package/src/i18n/hi.json
index 5f2b5bcb4a..2ea6b2a80d 100644
--- a/package/src/i18n/hi.json
+++ b/package/src/i18n/hi.json
@@ -23,6 +23,7 @@
"Create Poll": "सर्वेक्षण बनाएं",
"Delete": "हटाएं",
"Delete Message": "मैसेज को डिलीट करे",
+ "Delete for me": "मुझे हटाएं",
"Device camera is used to take photos or videos.": "डिवाइस कैमरे का उपयोग फ़ोटो या वीडियो लेने के लिए किया जाता है।",
"Device gallery permissions is used to take photos or videos.": "डिवाइस गैलरी की अनुमतियों का उपयोग फोटो या वीडियो लेने के लिए किया जाता है।",
"Do you want to send a copy of this message to a moderator for further investigation?": "क्या आप इस संदेश की एक प्रति आगे की जाँच के लिए किसी मॉडरेटर को भेजना चाहते हैं?",
diff --git a/package/src/i18n/it.json b/package/src/i18n/it.json
index 728d8a44d5..e89c76d4ba 100644
--- a/package/src/i18n/it.json
+++ b/package/src/i18n/it.json
@@ -23,6 +23,7 @@
"Create Poll": "Crea sondaggio",
"Delete": "Elimina",
"Delete Message": "Cancella il Messaggio",
+ "Delete for me": "Elimina per me",
"Device camera is used to take photos or videos.": "La fotocamera del dispositivo viene utilizzata per scattare foto o video.",
"Device gallery permissions is used to take photos or videos.": "Le autorizzazioni della galleria del dispositivo vengono utilizzate per scattare foto o video.",
"Do you want to send a copy of this message to a moderator for further investigation?": "Vuoi inviare una copia di questo messaggio a un moderatore per ulteriori indagini?",
diff --git a/package/src/i18n/ja.json b/package/src/i18n/ja.json
index 099a53591d..4394088f31 100644
--- a/package/src/i18n/ja.json
+++ b/package/src/i18n/ja.json
@@ -23,6 +23,7 @@
"Create Poll": "アンケートを作成",
"Delete": "消去",
"Delete Message": "メッセージを削除",
+ "Delete for me": "自分で削除",
"Device camera is used to take photos or videos.": "デバイスのカメラは写真やビデオの撮影に使用されます。",
"Device gallery permissions is used to take photos or videos.": "デバイスギャラリーの権限は写真やビデオを撮るために使用されます。",
"Do you want to send a copy of this message to a moderator for further investigation?": "このメッセージのコピーをモデレーターに送信して、さらに調査しますか?",
diff --git a/package/src/i18n/ko.json b/package/src/i18n/ko.json
index c297cbd67b..a9d61ef72a 100644
--- a/package/src/i18n/ko.json
+++ b/package/src/i18n/ko.json
@@ -23,6 +23,7 @@
"Create Poll": "투표 생성",
"Delete": "삭제",
"Delete Message": "메시지 삭제",
+ "Delete for me": "나 삭제",
"Device camera is used to take photos or videos.": "기기 카메라는 사진이나 동영상을 촬영하는 데 사용됩니다.",
"Device gallery permissions is used to take photos or videos.": "장치 갤러리 권한은 사진 또는 비디오를 촬영하는 데 사용됩니다.",
"Do you want to send a copy of this message to a moderator for further investigation?": "이 메시지의 복사본을 운영자에게 보내 추가 조사를합니까?",
diff --git a/package/src/i18n/nl.json b/package/src/i18n/nl.json
index 0c68500e11..eada070bd6 100644
--- a/package/src/i18n/nl.json
+++ b/package/src/i18n/nl.json
@@ -23,6 +23,7 @@
"Create Poll": "Peiling aanmaken",
"Delete": "Verwijderen",
"Delete Message": "Verwijder bericht",
+ "Delete for me": "Verwijder voor mij",
"Device camera is used to take photos or videos.": "De camera van het apparaat wordt gebruikt om foto's of video's te maken.",
"Device gallery permissions is used to take photos or videos.": "Apparaatgallerijmachtigingen worden gebruikt om foto’s of video’s te maken.",
"Do you want to send a copy of this message to a moderator for further investigation?": "Wil je een kopie van dit bericht naar een moderator sturen voor verder onderzoek?",
diff --git a/package/src/i18n/pt-br.json b/package/src/i18n/pt-br.json
index d8e975f378..4fb3d21991 100644
--- a/package/src/i18n/pt-br.json
+++ b/package/src/i18n/pt-br.json
@@ -23,6 +23,7 @@
"Create Poll": "Criar enquete",
"Delete": "Excluir",
"Delete Message": "Excluir Mensagem",
+ "Delete for me": "Excluir para mim",
"Device camera is used to take photos or videos.": "A câmera do dispositivo é usada para tirar fotos ou vídeos.",
"Device gallery permissions is used to take photos or videos.": "As permissões da galeria do dispositivo são usadas para tirar fotos ou vídeos.",
"Do you want to send a copy of this message to a moderator for further investigation?": "Deseja enviar uma cópia desta mensagem para um moderador para investigação adicional?",
diff --git a/package/src/i18n/ru.json b/package/src/i18n/ru.json
index ea29cf9806..5f4a81f7e5 100644
--- a/package/src/i18n/ru.json
+++ b/package/src/i18n/ru.json
@@ -24,6 +24,7 @@
"Create Poll": "Создать опрос",
"Delete": "удалять",
"Delete Message": "Удалить сообщение",
+ "Delete for me": "Удалить для себя",
"Device camera is used to take photos or videos.": "Камера устройства используется для съемки фотографий или видео.",
"Device gallery permissions is used to take photos or videos.": "Разрешения галереи устройства используются для съемки фото или видео.",
"Do you want to send a copy of this message to a moderator for further investigation?": "Вы хотите отправить копию этого сообщения модератору для дальнейшего изучения?",
diff --git a/package/src/i18n/tr.json b/package/src/i18n/tr.json
index 3305b84b6a..6f2b955938 100644
--- a/package/src/i18n/tr.json
+++ b/package/src/i18n/tr.json
@@ -23,6 +23,7 @@
"Create Poll": "Anket oluştur",
"Delete": "Sil",
"Delete Message": "Mesajı Sil",
+ "Delete for me": "Benim için sil",
"Device camera is used to take photos or videos.": "Cihaz kamerası fotoğraf veya video çekmek için kullanılır.",
"Device gallery permissions is used to take photos or videos.": "Cihaz galerisi izinleri fotoğraf veya video çekmek için kullanılır.",
"Do you want to send a copy of this message to a moderator for further investigation?": "Detaylı inceleme için bu mesajın kopyasını moderatöre göndermek istiyor musunuz?",
diff --git a/package/src/native.ts b/package/src/native.ts
index 7debba4ecc..f20d52b09b 100644
--- a/package/src/native.ts
+++ b/package/src/native.ts
@@ -50,7 +50,11 @@ type PickImageAssetType = {
cancelled?: boolean;
};
-type PickImage = () => Promise | never;
+export type PickImageOptions = {
+ maxNumberOfFiles?: number;
+};
+
+type PickImage = (options?: PickImageOptions) => Promise | never;
type SaveFileOptions = {
fileName: string;
diff --git a/package/src/store/SqliteClient.ts b/package/src/store/SqliteClient.ts
index 74380960c6..103eaa25e0 100644
--- a/package/src/store/SqliteClient.ts
+++ b/package/src/store/SqliteClient.ts
@@ -28,7 +28,7 @@ import type { PreparedBatchQueries, PreparedQueries, Scalar, Table } from './typ
* This way usage @op-engineering/op-sqlite package is scoped to a single class/file.
*/
export class SqliteClient {
- static dbVersion = 13;
+ static dbVersion = 14;
static dbName = DB_NAME;
static dbLocation = DB_LOCATION;
diff --git a/package/src/store/apis/softDeleteMessage.ts b/package/src/store/apis/softDeleteMessage.ts
index e88964f358..ce9c99a104 100644
--- a/package/src/store/apis/softDeleteMessage.ts
+++ b/package/src/store/apis/softDeleteMessage.ts
@@ -1,25 +1,25 @@
-import { MessageLabel } from 'stream-chat';
+import { DBDeleteMessageType, MessageLabel } from 'stream-chat';
import { createUpdateQuery } from '../sqlite-utils/createUpdateQuery';
import { SqliteClient } from '../SqliteClient';
export const softDeleteMessage = async ({
+ deleteForMe = false,
execute = true,
id,
-}: {
- id: string;
- execute?: boolean;
-}) => {
+}: DBDeleteMessageType) => {
const query = createUpdateQuery(
'messages',
{
- deletedAt: new Date().toISOString(),
+ deletedAt: deleteForMe ? undefined : new Date().toISOString(),
+ deletedForMe: deleteForMe,
type: 'deleted' as MessageLabel,
},
{ id },
);
SqliteClient.logger?.('info', 'softDeleteMessage', {
+ deleteForMe,
execute,
id,
});
diff --git a/package/src/store/mappers/mapMessageToStorable.ts b/package/src/store/mappers/mapMessageToStorable.ts
index f307355af2..ac166d7c98 100644
--- a/package/src/store/mappers/mapMessageToStorable.ts
+++ b/package/src/store/mappers/mapMessageToStorable.ts
@@ -12,6 +12,7 @@ export const mapMessageToStorable = (
cid,
created_at,
deleted_at,
+ deleted_for_me,
id,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
latest_reactions,
@@ -37,6 +38,7 @@ export const mapMessageToStorable = (
cid: cid || '',
createdAt: mapDateTimeToStorable(created_at),
deletedAt: mapDateTimeToStorable(deleted_at),
+ deletedForMe: deleted_for_me,
extraData: JSON.stringify(extraData),
id,
messageTextUpdatedAt: mapDateTimeToStorable(message_text_updated_at),
diff --git a/package/src/store/mappers/mapStorableToMessage.ts b/package/src/store/mappers/mapStorableToMessage.ts
index 30ca649ee5..e4b6459466 100644
--- a/package/src/store/mappers/mapStorableToMessage.ts
+++ b/package/src/store/mappers/mapStorableToMessage.ts
@@ -24,6 +24,7 @@ export const mapStorableToMessage = ({
const {
createdAt,
deletedAt,
+ deletedForMe,
extraData,
messageTextUpdatedAt,
poll_id,
@@ -42,6 +43,7 @@ export const mapStorableToMessage = ({
attachments: messageRow.attachments ? JSON.parse(messageRow.attachments) : [],
created_at: createdAt,
deleted_at: deletedAt,
+ deleted_for_me: deletedForMe,
latest_reactions: latestReactions,
message_text_updated_at: messageTextUpdatedAt,
own_reactions: ownReactions,
diff --git a/package/src/store/schema.ts b/package/src/store/schema.ts
index 50ec7ee2aa..21de2afed4 100644
--- a/package/src/store/schema.ts
+++ b/package/src/store/schema.ts
@@ -170,6 +170,7 @@ export const tables: Tables = {
cid: 'TEXT NOT NULL',
createdAt: 'TEXT',
deletedAt: 'TEXT',
+ deletedForMe: 'BOOLEAN DEFAULT FALSE',
extraData: 'TEXT',
id: 'TEXT',
messageTextUpdatedAt: 'TEXT',
@@ -408,6 +409,7 @@ export type Schema = {
cid: string;
createdAt: string;
deletedAt: string;
+ deletedForMe?: boolean;
extraData: string;
id: string;
messageTextUpdatedAt: string;