stream-chat-react-native-core 6.7.3-beta.2 → 6.7.3-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/Attachment/AudioAttachment.js +3 -1
- package/lib/commonjs/components/Attachment/AudioAttachment.js.map +1 -1
- package/lib/commonjs/components/Channel/Channel.js +273 -281
- package/lib/commonjs/components/Channel/Channel.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useMessageListPagination.js +133 -147
- package/lib/commonjs/components/Channel/hooks/useMessageListPagination.js.map +1 -1
- package/lib/commonjs/components/KeyboardCompatibleView/KeyboardCompatibleView.js +7 -12
- package/lib/commonjs/components/KeyboardCompatibleView/KeyboardCompatibleView.js.map +1 -1
- package/lib/commonjs/components/MessageList/MessageList.js +167 -179
- package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
- package/lib/commonjs/components/MessageList/hooks/useMessageList.js +60 -37
- package/lib/commonjs/components/MessageList/hooks/useMessageList.js.map +1 -1
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +450 -459
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/commonjs/contexts/messagesContext/MessagesContext.js.map +1 -1
- package/lib/commonjs/hooks/index.js +11 -0
- package/lib/commonjs/hooks/index.js.map +1 -1
- package/lib/commonjs/hooks/useAudioPlayer.js +1 -1
- package/lib/commonjs/hooks/useAudioPlayer.js.map +1 -1
- package/lib/commonjs/hooks/useStableCallback.js +13 -0
- package/lib/commonjs/hooks/useStableCallback.js.map +1 -0
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Attachment/AudioAttachment.js +3 -1
- package/lib/module/components/Attachment/AudioAttachment.js.map +1 -1
- package/lib/module/components/Channel/Channel.js +273 -281
- package/lib/module/components/Channel/Channel.js.map +1 -1
- package/lib/module/components/Channel/hooks/useMessageListPagination.js +133 -147
- package/lib/module/components/Channel/hooks/useMessageListPagination.js.map +1 -1
- package/lib/module/components/KeyboardCompatibleView/KeyboardCompatibleView.js +7 -12
- package/lib/module/components/KeyboardCompatibleView/KeyboardCompatibleView.js.map +1 -1
- package/lib/module/components/MessageList/MessageList.js +167 -179
- package/lib/module/components/MessageList/MessageList.js.map +1 -1
- package/lib/module/components/MessageList/hooks/useMessageList.js +60 -37
- package/lib/module/components/MessageList/hooks/useMessageList.js.map +1 -1
- package/lib/module/contexts/messageInputContext/MessageInputContext.js +450 -459
- package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/module/contexts/messagesContext/MessagesContext.js.map +1 -1
- package/lib/module/hooks/index.js +11 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/useAudioPlayer.js +1 -1
- package/lib/module/hooks/useAudioPlayer.js.map +1 -1
- package/lib/module/hooks/useStableCallback.js +13 -0
- package/lib/module/hooks/useStableCallback.js.map +1 -0
- package/lib/module/native.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/Attachment/AudioAttachment.d.ts.map +1 -1
- package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useMessageListPagination.d.ts +3 -3
- package/lib/typescript/components/Channel/hooks/useMessageListPagination.d.ts.map +1 -1
- package/lib/typescript/components/KeyboardCompatibleView/KeyboardCompatibleView.d.ts +3 -0
- package/lib/typescript/components/KeyboardCompatibleView/KeyboardCompatibleView.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/MessageList.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/hooks/useMessageList.d.ts +4 -0
- package/lib/typescript/components/MessageList/hooks/useMessageList.d.ts.map +1 -1
- package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts.map +1 -1
- package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts +1 -1
- package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts.map +1 -1
- package/lib/typescript/hooks/index.d.ts +1 -0
- package/lib/typescript/hooks/index.d.ts.map +1 -1
- package/lib/typescript/hooks/useStableCallback.d.ts +26 -0
- package/lib/typescript/hooks/useStableCallback.d.ts.map +1 -0
- package/lib/typescript/native.d.ts +3 -1
- package/lib/typescript/native.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Attachment/AudioAttachment.tsx +2 -0
- package/src/components/Channel/Channel.tsx +424 -408
- package/src/components/Channel/hooks/useMessageListPagination.tsx +152 -147
- package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx +6 -4
- package/src/components/MessageList/MessageList.tsx +147 -112
- package/src/components/MessageList/hooks/useMessageList.ts +69 -38
- package/src/contexts/messageInputContext/MessageInputContext.tsx +293 -267
- package/src/contexts/messageInputContext/__tests__/pickFile.test.tsx +2 -1
- package/src/contexts/messagesContext/MessagesContext.tsx +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAudioPlayer.ts +1 -1
- package/src/hooks/useStableCallback.ts +37 -0
- package/src/native.ts +8 -1
- package/src/version.json +1 -1
|
@@ -4,6 +4,7 @@ import React, {
|
|
|
4
4
|
useCallback,
|
|
5
5
|
useContext,
|
|
6
6
|
useEffect,
|
|
7
|
+
useMemo,
|
|
7
8
|
useRef,
|
|
8
9
|
useState,
|
|
9
10
|
} from 'react';
|
|
@@ -610,19 +611,19 @@ export const MessageInputProvider = <
|
|
|
610
611
|
text,
|
|
611
612
|
} = useMessageDetailsForState<StreamChatGenerics>(editing, initialValue);
|
|
612
613
|
const { endsAt: cooldownEndsAt, start: startCooldown } = useCooldown<StreamChatGenerics>();
|
|
613
|
-
const { onChangeText } = value;
|
|
614
|
+
const { onChangeText, emojiSearchIndex, autoCompleteTriggerSettings } = value;
|
|
614
615
|
|
|
615
616
|
const threadId = thread?.id;
|
|
616
617
|
useEffect(() => {
|
|
617
618
|
setSendThreadMessageInChannel(false);
|
|
618
619
|
}, [threadId]);
|
|
619
620
|
|
|
620
|
-
const appendText = (newText: string) => {
|
|
621
|
+
const appendText = useStableCallback((newText: string) => {
|
|
621
622
|
setText((prevText) => `${prevText}${newText}`);
|
|
622
|
-
};
|
|
623
|
+
});
|
|
623
624
|
|
|
624
625
|
/** Checks if the message is valid or not. Accordingly we can enable/disable send button */
|
|
625
|
-
const isValidMessage = () => {
|
|
626
|
+
const isValidMessage = useStableCallback(() => {
|
|
626
627
|
if (text && text.trim()) {
|
|
627
628
|
return true;
|
|
628
629
|
}
|
|
@@ -656,7 +657,7 @@ export const MessageInputProvider = <
|
|
|
656
657
|
}
|
|
657
658
|
|
|
658
659
|
return false;
|
|
659
|
-
};
|
|
660
|
+
});
|
|
660
661
|
|
|
661
662
|
const onChange = useCallback(
|
|
662
663
|
(newText: string) => {
|
|
@@ -676,24 +677,24 @@ export const MessageInputProvider = <
|
|
|
676
677
|
[channel, channelCapabities.sendTypingEvents, isOnline, setText, thread?.id, onChangeText],
|
|
677
678
|
);
|
|
678
679
|
|
|
679
|
-
const openCommandsPicker = () => {
|
|
680
|
+
const openCommandsPicker = useStableCallback(() => {
|
|
680
681
|
appendText('/');
|
|
681
682
|
if (inputBoxRef.current) {
|
|
682
683
|
inputBoxRef.current.focus();
|
|
683
684
|
}
|
|
684
|
-
};
|
|
685
|
+
});
|
|
685
686
|
|
|
686
|
-
const openMentionsPicker = () => {
|
|
687
|
+
const openMentionsPicker = useStableCallback(() => {
|
|
687
688
|
appendText('@');
|
|
688
689
|
if (inputBoxRef.current) {
|
|
689
690
|
inputBoxRef.current.focus();
|
|
690
691
|
}
|
|
691
|
-
};
|
|
692
|
+
});
|
|
692
693
|
|
|
693
694
|
/**
|
|
694
695
|
* Function for capturing a photo and uploading it
|
|
695
696
|
*/
|
|
696
|
-
const takeAndUploadImage = async (mediaType?: MediaTypes) => {
|
|
697
|
+
const takeAndUploadImage = useStableCallback(async (mediaType?: MediaTypes) => {
|
|
697
698
|
setSelectedPicker(undefined);
|
|
698
699
|
closePicker();
|
|
699
700
|
const photo = await NativeHandlers.takePhoto({
|
|
@@ -717,12 +718,12 @@ export const MessageInputProvider = <
|
|
|
717
718
|
await uploadNewFile({ ...photo, mimeType: photo.type, type: FileTypes.Video });
|
|
718
719
|
}
|
|
719
720
|
}
|
|
720
|
-
};
|
|
721
|
+
});
|
|
721
722
|
|
|
722
723
|
/**
|
|
723
724
|
* Function for picking a photo from native image picker and uploading it
|
|
724
725
|
*/
|
|
725
|
-
const pickAndUploadImageFromNativePicker = async () => {
|
|
726
|
+
const pickAndUploadImageFromNativePicker = useStableCallback(async () => {
|
|
726
727
|
const result = await NativeHandlers.pickImage();
|
|
727
728
|
if (result.askToOpenSettings) {
|
|
728
729
|
Alert.alert(
|
|
@@ -755,7 +756,7 @@ export const MessageInputProvider = <
|
|
|
755
756
|
}
|
|
756
757
|
});
|
|
757
758
|
}
|
|
758
|
-
};
|
|
759
|
+
});
|
|
759
760
|
|
|
760
761
|
/**
|
|
761
762
|
* Function to open the attachment picker if the MediaLibary is installed.
|
|
@@ -785,11 +786,11 @@ export const MessageInputProvider = <
|
|
|
785
786
|
}
|
|
786
787
|
}, [closeAttachmentPicker, openAttachmentPicker, selectedPicker]);
|
|
787
788
|
|
|
788
|
-
const onSelectItem = (item: UserResponse<StreamChatGenerics>) => {
|
|
789
|
+
const onSelectItem = useStableCallback((item: UserResponse<StreamChatGenerics>) => {
|
|
789
790
|
setMentionedUsers((prevMentionedUsers) => [...prevMentionedUsers, item.id]);
|
|
790
|
-
};
|
|
791
|
+
});
|
|
791
792
|
|
|
792
|
-
const pickFile = async () => {
|
|
793
|
+
const pickFile = useStableCallback(async () => {
|
|
793
794
|
if (!isDocumentPickerAvailable()) {
|
|
794
795
|
console.log(
|
|
795
796
|
'The file picker is not installed. Check our Getting Started documentation to install it.',
|
|
@@ -818,7 +819,7 @@ export const MessageInputProvider = <
|
|
|
818
819
|
await uploadNewFile(asset);
|
|
819
820
|
});
|
|
820
821
|
}
|
|
821
|
-
};
|
|
822
|
+
});
|
|
822
823
|
|
|
823
824
|
const removeFile = useCallback(
|
|
824
825
|
(id: string) => {
|
|
@@ -840,242 +841,257 @@ export const MessageInputProvider = <
|
|
|
840
841
|
[imageUploads, setImageUploads, setNumberOfUploads],
|
|
841
842
|
);
|
|
842
843
|
|
|
843
|
-
const resetInput = (
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
setFileUploads([]);
|
|
853
|
-
setGiphyActive(false);
|
|
854
|
-
setShowMoreOptions(true);
|
|
855
|
-
setImageUploads([]);
|
|
856
|
-
setMentionedUsers([]);
|
|
857
|
-
setNumberOfUploads(
|
|
858
|
-
(prevNumberOfUploads) => prevNumberOfUploads - (pendingAttachments?.length || 0),
|
|
859
|
-
);
|
|
860
|
-
setText('');
|
|
861
|
-
if (value.editing) {
|
|
862
|
-
value.clearEditingState();
|
|
863
|
-
}
|
|
864
|
-
};
|
|
844
|
+
const resetInput = useStableCallback(
|
|
845
|
+
(pendingAttachments: Attachment<StreamChatGenerics>[] = []) => {
|
|
846
|
+
/**
|
|
847
|
+
* If the MediaLibrary is available, reset the selected files and images
|
|
848
|
+
*/
|
|
849
|
+
if (isImageMediaLibraryAvailable()) {
|
|
850
|
+
setSelectedFiles([]);
|
|
851
|
+
setSelectedImages([]);
|
|
852
|
+
}
|
|
865
853
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
854
|
+
setFileUploads([]);
|
|
855
|
+
setGiphyActive(false);
|
|
856
|
+
setShowMoreOptions(true);
|
|
857
|
+
setImageUploads([]);
|
|
858
|
+
setMentionedUsers([]);
|
|
859
|
+
setNumberOfUploads(
|
|
860
|
+
(prevNumberOfUploads) => prevNumberOfUploads - (pendingAttachments?.length || 0),
|
|
861
|
+
);
|
|
862
|
+
setText('');
|
|
863
|
+
if (value.editing) {
|
|
864
|
+
value.clearEditingState();
|
|
865
|
+
}
|
|
866
|
+
},
|
|
867
|
+
);
|
|
879
868
|
|
|
880
|
-
const
|
|
881
|
-
|
|
869
|
+
const mapImageUploadToAttachment = useStableCallback(
|
|
870
|
+
(image: ImageUpload): Attachment<StreamChatGenerics> => {
|
|
871
|
+
const mime_type: string | boolean = lookup(image.file.name as string);
|
|
872
|
+
const name = image.file.name as string;
|
|
882
873
|
return {
|
|
883
|
-
fallback:
|
|
884
|
-
image_url:
|
|
885
|
-
mime_type:
|
|
886
|
-
|
|
874
|
+
fallback: name,
|
|
875
|
+
image_url: image.url,
|
|
876
|
+
mime_type: mime_type ? mime_type : undefined,
|
|
877
|
+
original_height: image.height,
|
|
878
|
+
original_width: image.width,
|
|
879
|
+
originalImage: image.file,
|
|
887
880
|
type: FileTypes.Image,
|
|
888
881
|
};
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
882
|
+
},
|
|
883
|
+
);
|
|
884
|
+
|
|
885
|
+
const mapFileUploadToAttachment = useStableCallback(
|
|
886
|
+
(file: FileUpload): Attachment<StreamChatGenerics> => {
|
|
887
|
+
if (file.type === FileTypes.Image) {
|
|
888
|
+
return {
|
|
889
|
+
fallback: file.file.name,
|
|
890
|
+
image_url: file.url,
|
|
891
|
+
mime_type: file.file.mimeType,
|
|
892
|
+
originalFile: file.file,
|
|
893
|
+
type: FileTypes.Image,
|
|
894
|
+
};
|
|
895
|
+
} else if (file.type === FileTypes.Audio) {
|
|
896
|
+
return {
|
|
897
|
+
asset_url: file.url || file.file.uri,
|
|
898
|
+
duration: file.file.duration,
|
|
899
|
+
file_size: file.file.size,
|
|
900
|
+
mime_type: file.file.mimeType,
|
|
901
|
+
originalFile: file.file,
|
|
902
|
+
title: file.file.name,
|
|
903
|
+
type: FileTypes.Audio,
|
|
904
|
+
};
|
|
905
|
+
} else if (file.type === FileTypes.Video) {
|
|
906
|
+
return {
|
|
907
|
+
asset_url: file.url || file.file.uri,
|
|
908
|
+
duration: file.file.duration,
|
|
909
|
+
file_size: file.file.size,
|
|
910
|
+
mime_type: file.file.mimeType,
|
|
911
|
+
originalFile: file.file,
|
|
912
|
+
thumb_url: file.thumb_url,
|
|
913
|
+
title: file.file.name,
|
|
914
|
+
type: FileTypes.Video,
|
|
915
|
+
};
|
|
916
|
+
} else if (file.type === FileTypes.VoiceRecording) {
|
|
917
|
+
return {
|
|
918
|
+
asset_url: file.url || file.file.uri,
|
|
919
|
+
duration: file.file.duration,
|
|
920
|
+
file_size: file.file.size,
|
|
921
|
+
mime_type: file.file.mimeType,
|
|
922
|
+
originalFile: file.file,
|
|
923
|
+
title: file.file.name,
|
|
924
|
+
type: FileTypes.VoiceRecording,
|
|
925
|
+
waveform_data: file.file.waveform_data,
|
|
926
|
+
};
|
|
927
|
+
} else {
|
|
928
|
+
return {
|
|
929
|
+
asset_url: file.url || file.file.uri,
|
|
930
|
+
file_size: file.file.size,
|
|
931
|
+
mime_type: file.file.mimeType,
|
|
932
|
+
originalFile: file.file,
|
|
933
|
+
title: file.file.name,
|
|
934
|
+
type: FileTypes.File,
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
},
|
|
938
|
+
);
|
|
932
939
|
|
|
933
940
|
// TODO: Figure out why this is async, as it doesn't await any promise.
|
|
934
|
-
const sendMessage =
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
941
|
+
const sendMessage = useStableCallback(
|
|
942
|
+
async ({
|
|
943
|
+
customMessageData,
|
|
944
|
+
}: {
|
|
945
|
+
customMessageData?: Partial<Message<StreamChatGenerics>>;
|
|
946
|
+
} = {}) => {
|
|
947
|
+
if (sending.current) {
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
const linkInfos = parseLinksFromText(text);
|
|
943
951
|
|
|
944
|
-
|
|
945
|
-
|
|
952
|
+
if (!channelCapabities.sendLinks && linkInfos.length > 0) {
|
|
953
|
+
Alert.alert(
|
|
954
|
+
t('Links are disabled'),
|
|
955
|
+
t('Sending links is not allowed in this conversation'),
|
|
956
|
+
);
|
|
946
957
|
|
|
947
|
-
|
|
948
|
-
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
949
960
|
|
|
950
|
-
|
|
961
|
+
sending.current = true;
|
|
951
962
|
|
|
952
|
-
|
|
963
|
+
startCooldown();
|
|
953
964
|
|
|
954
|
-
|
|
955
|
-
|
|
965
|
+
const prevText = giphyEnabled && giphyActive ? `/giphy ${text}` : text;
|
|
966
|
+
setText('');
|
|
956
967
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
if (enableOfflineSupport) {
|
|
960
|
-
if (image.state === FileState.NOT_SUPPORTED) {
|
|
961
|
-
return;
|
|
962
|
-
}
|
|
963
|
-
attachments.push(mapImageUploadToAttachment(image));
|
|
964
|
-
continue;
|
|
968
|
+
if (inputBoxRef.current) {
|
|
969
|
+
inputBoxRef.current.clear();
|
|
965
970
|
}
|
|
966
971
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
972
|
+
const attachments = [] as Attachment<StreamChatGenerics>[];
|
|
973
|
+
for (const image of imageUploads) {
|
|
974
|
+
if (enableOfflineSupport) {
|
|
975
|
+
if (image.state === FileState.NOT_SUPPORTED) {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
attachments.push(mapImageUploadToAttachment(image));
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
970
981
|
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
if (value.sendImageAsync) {
|
|
974
|
-
/**
|
|
975
|
-
* If user hit send before image uploaded, push ID into a queue to later
|
|
976
|
-
* be matched with the successful CDN response
|
|
977
|
-
*/
|
|
978
|
-
setAsyncIds((prevAsyncIds) => [...prevAsyncIds, image.id]);
|
|
979
|
-
} else {
|
|
980
|
-
sending.current = false;
|
|
981
|
-
return setText(prevText);
|
|
982
|
+
if ((!image || image.state === FileState.UPLOAD_FAILED) && !enableOfflineSupport) {
|
|
983
|
+
continue;
|
|
982
984
|
}
|
|
983
|
-
}
|
|
984
985
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
986
|
+
if (image.state === FileState.UPLOADING) {
|
|
987
|
+
// TODO: show error to user that they should wait until image is uploaded
|
|
988
|
+
if (value.sendImageAsync) {
|
|
989
|
+
/**
|
|
990
|
+
* If user hit send before image uploaded, push ID into a queue to later
|
|
991
|
+
* be matched with the successful CDN response
|
|
992
|
+
*/
|
|
993
|
+
setAsyncIds((prevAsyncIds) => [...prevAsyncIds, image.id]);
|
|
994
|
+
} else {
|
|
995
|
+
sending.current = false;
|
|
996
|
+
return setText(prevText);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// To get the mime type of the image from the file name and send it as an response for an image
|
|
1001
|
+
if (image.state === FileState.UPLOADED || image.state === FileState.FINISHED) {
|
|
1002
|
+
attachments.push(mapImageUploadToAttachment(image));
|
|
1003
|
+
}
|
|
988
1004
|
}
|
|
989
|
-
}
|
|
990
1005
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1006
|
+
for (const file of fileUploads) {
|
|
1007
|
+
if (enableOfflineSupport) {
|
|
1008
|
+
if (file.state === FileState.NOT_SUPPORTED) {
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
attachments.push(mapFileUploadToAttachment(file));
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
if (!file || file.state === FileState.UPLOAD_FAILED) {
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
if (file.state === FileState.UPLOADING) {
|
|
1020
|
+
// TODO: show error to user that they should wait until image is uploaded
|
|
1021
|
+
sending.current = false;
|
|
994
1022
|
return;
|
|
995
1023
|
}
|
|
996
|
-
attachments.push(mapFileUploadToAttachment(file));
|
|
997
|
-
continue;
|
|
998
|
-
}
|
|
999
1024
|
|
|
1000
|
-
|
|
1001
|
-
|
|
1025
|
+
if (file.state === FileState.UPLOADED || file.state === FileState.FINISHED) {
|
|
1026
|
+
attachments.push(mapFileUploadToAttachment(file));
|
|
1027
|
+
}
|
|
1002
1028
|
}
|
|
1003
1029
|
|
|
1004
|
-
if
|
|
1005
|
-
|
|
1030
|
+
// Disallow sending message if its empty.
|
|
1031
|
+
if (!prevText && attachments.length === 0 && !customMessageData?.poll_id) {
|
|
1006
1032
|
sending.current = false;
|
|
1007
1033
|
return;
|
|
1008
1034
|
}
|
|
1009
1035
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
// Disallow sending message if its empty.
|
|
1016
|
-
if (!prevText && attachments.length === 0 && !customMessageData?.poll_id) {
|
|
1017
|
-
sending.current = false;
|
|
1018
|
-
return;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
const message = value.editing;
|
|
1022
|
-
if (message && message.type !== 'error') {
|
|
1023
|
-
const updatedMessage = {
|
|
1024
|
-
...message,
|
|
1025
|
-
attachments,
|
|
1026
|
-
mentioned_users: mentionedUsers,
|
|
1027
|
-
quoted_message: undefined,
|
|
1028
|
-
text: prevText,
|
|
1029
|
-
...customMessageData,
|
|
1030
|
-
} as Parameters<StreamChat<StreamChatGenerics>['updateMessage']>[0];
|
|
1031
|
-
|
|
1032
|
-
// TODO: Remove this line and show an error when submit fails
|
|
1033
|
-
value.clearEditingState();
|
|
1034
|
-
|
|
1035
|
-
const updateMessagePromise = value
|
|
1036
|
-
.editMessage(
|
|
1037
|
-
// @ts-ignore
|
|
1038
|
-
removeReservedFields(updatedMessage),
|
|
1039
|
-
)
|
|
1040
|
-
.then(value.clearEditingState);
|
|
1041
|
-
resetInput(attachments);
|
|
1042
|
-
logChatPromiseExecution(updateMessagePromise, 'update message');
|
|
1043
|
-
|
|
1044
|
-
sending.current = false;
|
|
1045
|
-
} else {
|
|
1046
|
-
try {
|
|
1047
|
-
/**
|
|
1048
|
-
* If the message is bounced by moderation, we firstly remove the message from message list and then send a new message.
|
|
1049
|
-
*/
|
|
1050
|
-
if (message && isBouncedMessage(message as MessageType<StreamChatGenerics>)) {
|
|
1051
|
-
await removeMessage(message);
|
|
1052
|
-
}
|
|
1053
|
-
value.sendMessage({
|
|
1036
|
+
const message = value.editing;
|
|
1037
|
+
if (message && message.type !== 'error') {
|
|
1038
|
+
const updatedMessage = {
|
|
1039
|
+
...message,
|
|
1054
1040
|
attachments,
|
|
1055
|
-
mentioned_users:
|
|
1056
|
-
|
|
1057
|
-
parent_id: thread?.id,
|
|
1058
|
-
quoted_message_id: value.quotedMessage ? value.quotedMessage.id : undefined,
|
|
1059
|
-
show_in_channel: sendThreadMessageInChannel || undefined,
|
|
1041
|
+
mentioned_users: mentionedUsers,
|
|
1042
|
+
quoted_message: undefined,
|
|
1060
1043
|
text: prevText,
|
|
1061
1044
|
...customMessageData,
|
|
1062
|
-
} as
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1045
|
+
} as Parameters<StreamChat<StreamChatGenerics>['updateMessage']>[0];
|
|
1046
|
+
|
|
1047
|
+
// TODO: Remove this line and show an error when submit fails
|
|
1048
|
+
value.clearEditingState();
|
|
1049
|
+
|
|
1050
|
+
const updateMessagePromise = value
|
|
1051
|
+
.editMessage(
|
|
1052
|
+
// @ts-ignore
|
|
1053
|
+
removeReservedFields(updatedMessage),
|
|
1054
|
+
)
|
|
1055
|
+
.then(value.clearEditingState);
|
|
1056
|
+
logChatPromiseExecution(updateMessagePromise, 'update message');
|
|
1066
1057
|
resetInput(attachments);
|
|
1067
|
-
|
|
1058
|
+
|
|
1068
1059
|
sending.current = false;
|
|
1069
|
-
|
|
1070
|
-
|
|
1060
|
+
} else {
|
|
1061
|
+
try {
|
|
1062
|
+
/**
|
|
1063
|
+
* If the message is bounced by moderation, we firstly remove the message from message list and then send a new message.
|
|
1064
|
+
*/
|
|
1065
|
+
if (message && isBouncedMessage(message as MessageType<StreamChatGenerics>)) {
|
|
1066
|
+
await removeMessage(message);
|
|
1067
|
+
}
|
|
1068
|
+
value.sendMessage({
|
|
1069
|
+
attachments,
|
|
1070
|
+
mentioned_users: uniq(mentionedUsers),
|
|
1071
|
+
/** Parent message id - in case of thread */
|
|
1072
|
+
parent_id: thread?.id,
|
|
1073
|
+
quoted_message_id: value.quotedMessage ? value.quotedMessage.id : undefined,
|
|
1074
|
+
show_in_channel: sendThreadMessageInChannel || undefined,
|
|
1075
|
+
text: prevText,
|
|
1076
|
+
...customMessageData,
|
|
1077
|
+
} as unknown as StreamMessage<StreamChatGenerics>);
|
|
1078
|
+
|
|
1079
|
+
value.clearQuotedMessageState();
|
|
1080
|
+
sending.current = false;
|
|
1081
|
+
resetInput(attachments);
|
|
1082
|
+
} catch (_error) {
|
|
1083
|
+
sending.current = false;
|
|
1084
|
+
if (value.quotedMessage && typeof value.quotedMessage !== 'boolean') {
|
|
1085
|
+
value.setQuotedMessageState(value.quotedMessage);
|
|
1086
|
+
}
|
|
1087
|
+
setText(prevText.slice(giphyEnabled && giphyActive ? 7 : 0)); // 7 because of '/giphy ' length
|
|
1088
|
+
console.log('Failed to send message');
|
|
1071
1089
|
}
|
|
1072
|
-
setText(prevText.slice(giphyEnabled && giphyActive ? 7 : 0)); // 7 because of '/giphy ' length
|
|
1073
|
-
console.log('Failed to send message');
|
|
1074
1090
|
}
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1091
|
+
},
|
|
1092
|
+
);
|
|
1077
1093
|
|
|
1078
|
-
const sendMessageAsync = (id: string) => {
|
|
1094
|
+
const sendMessageAsync = useStableCallback((id: string) => {
|
|
1079
1095
|
const image = asyncUploads[id];
|
|
1080
1096
|
if (!image || image.state === FileState.UPLOAD_FAILED) {
|
|
1081
1097
|
return;
|
|
@@ -1111,31 +1127,31 @@ export const MessageInputProvider = <
|
|
|
1111
1127
|
console.log('Failed');
|
|
1112
1128
|
}
|
|
1113
1129
|
}
|
|
1114
|
-
};
|
|
1130
|
+
});
|
|
1115
1131
|
|
|
1116
|
-
const setInputBoxRef = (ref: TextInput | null) => {
|
|
1132
|
+
const setInputBoxRef = useStableCallback((ref: TextInput | null) => {
|
|
1117
1133
|
inputBoxRef.current = ref;
|
|
1118
1134
|
if (value.setInputRef) {
|
|
1119
1135
|
value.setInputRef(ref);
|
|
1120
1136
|
}
|
|
1121
|
-
};
|
|
1137
|
+
});
|
|
1122
1138
|
|
|
1123
|
-
const
|
|
1139
|
+
const triggerSettings = useMemo(() => {
|
|
1124
1140
|
try {
|
|
1125
1141
|
let triggerSettings: TriggerSettings<StreamChatGenerics> = {};
|
|
1126
1142
|
if (channel) {
|
|
1127
|
-
if (
|
|
1128
|
-
triggerSettings =
|
|
1143
|
+
if (autoCompleteTriggerSettings) {
|
|
1144
|
+
triggerSettings = autoCompleteTriggerSettings({
|
|
1129
1145
|
channel,
|
|
1130
1146
|
client,
|
|
1131
|
-
emojiSearchIndex
|
|
1147
|
+
emojiSearchIndex,
|
|
1132
1148
|
onMentionSelectItem: onSelectItem,
|
|
1133
1149
|
});
|
|
1134
1150
|
} else {
|
|
1135
1151
|
triggerSettings = ACITriggerSettings<StreamChatGenerics>({
|
|
1136
1152
|
channel,
|
|
1137
1153
|
client,
|
|
1138
|
-
emojiSearchIndex
|
|
1154
|
+
emojiSearchIndex,
|
|
1139
1155
|
onMentionSelectItem: onSelectItem,
|
|
1140
1156
|
});
|
|
1141
1157
|
}
|
|
@@ -1145,11 +1161,11 @@ export const MessageInputProvider = <
|
|
|
1145
1161
|
console.warn('Error in getting trigger settings', error);
|
|
1146
1162
|
throw error;
|
|
1147
1163
|
}
|
|
1148
|
-
};
|
|
1164
|
+
}, [channel, client, onSelectItem, autoCompleteTriggerSettings, emojiSearchIndex]);
|
|
1149
1165
|
|
|
1150
|
-
const triggerSettings = getTriggerSettings();
|
|
1166
|
+
// const triggerSettings = getTriggerSettings();
|
|
1151
1167
|
|
|
1152
|
-
const updateMessage = async () => {
|
|
1168
|
+
const updateMessage = useStableCallback(async () => {
|
|
1153
1169
|
try {
|
|
1154
1170
|
if (value.editing) {
|
|
1155
1171
|
await client.updateMessage({
|
|
@@ -1164,51 +1180,54 @@ export const MessageInputProvider = <
|
|
|
1164
1180
|
} catch (error) {
|
|
1165
1181
|
console.log(error);
|
|
1166
1182
|
}
|
|
1167
|
-
};
|
|
1183
|
+
});
|
|
1168
1184
|
|
|
1169
1185
|
const regexCondition = /File (extension \.\w{2,4}|type \S+) is not supported/;
|
|
1170
1186
|
|
|
1171
|
-
const getUploadSetStateAction =
|
|
1187
|
+
const getUploadSetStateAction = useStableCallback(
|
|
1172
1188
|
<UploadType extends ImageUpload | FileUpload>(
|
|
1173
1189
|
id: string,
|
|
1174
1190
|
fileState: FileStateValue,
|
|
1175
1191
|
extraData: Partial<UploadType> = {},
|
|
1176
1192
|
): React.SetStateAction<UploadType[]> =>
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1193
|
+
(prevUploads: UploadType[]) =>
|
|
1194
|
+
prevUploads.map((prevUpload) => {
|
|
1195
|
+
if (prevUpload.id === id) {
|
|
1196
|
+
return {
|
|
1197
|
+
...prevUpload,
|
|
1198
|
+
...extraData,
|
|
1199
|
+
state: fileState,
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
return prevUpload;
|
|
1203
|
+
}),
|
|
1204
|
+
);
|
|
1188
1205
|
|
|
1189
|
-
const handleFileOrImageUploadError = (
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
if (
|
|
1194
|
-
|
|
1195
|
-
|
|
1206
|
+
const handleFileOrImageUploadError = useStableCallback(
|
|
1207
|
+
(error: unknown, isImageError: boolean, id: string) => {
|
|
1208
|
+
if (isImageError) {
|
|
1209
|
+
setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads - 1);
|
|
1210
|
+
if (error instanceof Error) {
|
|
1211
|
+
if (regexCondition.test(error.message)) {
|
|
1212
|
+
return setImageUploads(getUploadSetStateAction(id, FileState.NOT_SUPPORTED));
|
|
1213
|
+
}
|
|
1196
1214
|
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1215
|
+
return setImageUploads(getUploadSetStateAction(id, FileState.UPLOAD_FAILED));
|
|
1216
|
+
}
|
|
1217
|
+
} else {
|
|
1218
|
+
setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads - 1);
|
|
1201
1219
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1220
|
+
if (error instanceof Error) {
|
|
1221
|
+
if (regexCondition.test(error.message)) {
|
|
1222
|
+
return setFileUploads(getUploadSetStateAction(id, FileState.NOT_SUPPORTED));
|
|
1223
|
+
}
|
|
1224
|
+
return setFileUploads(getUploadSetStateAction(id, FileState.UPLOAD_FAILED));
|
|
1205
1225
|
}
|
|
1206
|
-
return setFileUploads(getUploadSetStateAction(id, FileState.UPLOAD_FAILED));
|
|
1207
1226
|
}
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1227
|
+
},
|
|
1228
|
+
);
|
|
1210
1229
|
|
|
1211
|
-
const uploadFile = async ({ newFile }: { newFile: FileUpload }) => {
|
|
1230
|
+
const uploadFile = useStableCallback(async ({ newFile }: { newFile: FileUpload }) => {
|
|
1212
1231
|
const { file, id } = newFile;
|
|
1213
1232
|
|
|
1214
1233
|
// The file name can have special characters, so we escape it.
|
|
@@ -1251,9 +1270,9 @@ export const MessageInputProvider = <
|
|
|
1251
1270
|
}
|
|
1252
1271
|
handleFileOrImageUploadError(error, false, id);
|
|
1253
1272
|
}
|
|
1254
|
-
};
|
|
1273
|
+
});
|
|
1255
1274
|
|
|
1256
|
-
const uploadImage = async ({ newImage }: { newImage: ImageUpload }) => {
|
|
1275
|
+
const uploadImage = useStableCallback(async ({ newImage }: { newImage: ImageUpload }) => {
|
|
1257
1276
|
const { file, id } = newImage || {};
|
|
1258
1277
|
|
|
1259
1278
|
if (!file) {
|
|
@@ -1334,9 +1353,9 @@ export const MessageInputProvider = <
|
|
|
1334
1353
|
}
|
|
1335
1354
|
handleFileOrImageUploadError(error, true, id);
|
|
1336
1355
|
}
|
|
1337
|
-
};
|
|
1356
|
+
});
|
|
1338
1357
|
|
|
1339
|
-
const uploadNewFile = async (file: File) => {
|
|
1358
|
+
const uploadNewFile = useStableCallback(async (file: File) => {
|
|
1340
1359
|
try {
|
|
1341
1360
|
const id: string = generateRandomId();
|
|
1342
1361
|
const fileConfig = getFileUploadConfig();
|
|
@@ -1382,9 +1401,9 @@ export const MessageInputProvider = <
|
|
|
1382
1401
|
} catch (error) {
|
|
1383
1402
|
console.log('Error uploading file', error);
|
|
1384
1403
|
}
|
|
1385
|
-
};
|
|
1404
|
+
});
|
|
1386
1405
|
|
|
1387
|
-
const uploadNewImage = async (image: Partial<Asset>) => {
|
|
1406
|
+
const uploadNewImage = useStableCallback(async (image: Partial<Asset>) => {
|
|
1388
1407
|
try {
|
|
1389
1408
|
const id = generateRandomId();
|
|
1390
1409
|
const imageUploadConfig = getImageUploadConfig();
|
|
@@ -1430,15 +1449,15 @@ export const MessageInputProvider = <
|
|
|
1430
1449
|
} catch (error) {
|
|
1431
1450
|
console.log('Error uploading image', error);
|
|
1432
1451
|
}
|
|
1433
|
-
};
|
|
1452
|
+
});
|
|
1434
1453
|
|
|
1435
|
-
const openPollCreationDialog = () => {
|
|
1454
|
+
const openPollCreationDialog = useStableCallback(() => {
|
|
1436
1455
|
if (openPollCreationDialogFromContext) {
|
|
1437
1456
|
openPollCreationDialogFromContext({ sendMessage });
|
|
1438
1457
|
return;
|
|
1439
1458
|
}
|
|
1440
1459
|
defaultOpenPollCreationDialog();
|
|
1441
|
-
};
|
|
1460
|
+
});
|
|
1442
1461
|
|
|
1443
1462
|
const messageInputContext = useCreateMessageInputContext({
|
|
1444
1463
|
appendText,
|
|
@@ -1523,3 +1542,10 @@ export const useMessageInputContext = <
|
|
|
1523
1542
|
|
|
1524
1543
|
return contextValue;
|
|
1525
1544
|
};
|
|
1545
|
+
|
|
1546
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
1547
|
+
const useStableCallback = <T extends Function>(callback: T): T => {
|
|
1548
|
+
const ref = useRef<T>(callback);
|
|
1549
|
+
ref.current = callback;
|
|
1550
|
+
return useCallback(((...args: unknown[]) => ref.current(...args)) as unknown as T, []);
|
|
1551
|
+
};
|