@ethora/chat-component-rn 26.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.
- package/LICENSE +661 -0
- package/README.md +148 -0
- package/empty-shim.js +15 -0
- package/index.js +13 -0
- package/metro.js +72 -0
- package/package.json +99 -0
- package/src/api.config.ts +34 -0
- package/src/assets/BG.png +0 -0
- package/src/assets/BG.tsx +56 -0
- package/src/assets/File.svg +4 -0
- package/src/assets/ForzaCare.png +0 -0
- package/src/assets/ForzaCare.tsx +27 -0
- package/src/assets/icons.tsx +685 -0
- package/src/assets/images.tsx +41 -0
- package/src/assets/test.xml +243 -0
- package/src/components/AuthForms/Login.tsx +295 -0
- package/src/components/AuthForms/LoginAnonym.tsx +68 -0
- package/src/components/AuthForms/Register.tsx +8 -0
- package/src/components/ContextMenu/ContextMenuComponents.tsx +87 -0
- package/src/components/DropdownMenu/DropdownMenu.tsx +244 -0
- package/src/components/ExampleComponents/CustomMessage.tsx +92 -0
- package/src/components/ExampleComponents/CustomTypingIndicatorExamples.tsx +239 -0
- package/src/components/Header/HeaderRoomList.tsx +104 -0
- package/src/components/InputComponents/AudioInput.tsx +24 -0
- package/src/components/InputComponents/AudioRecorder.tsx +100 -0
- package/src/components/InputComponents/MediaInput.tsx +114 -0
- package/src/components/InputComponents/RecordingIndicator.tsx +77 -0
- package/src/components/InputComponents/Search.tsx +135 -0
- package/src/components/InputComponents/TextInput.tsx +66 -0
- package/src/components/MainComponents/ChatHeader.tsx +163 -0
- package/src/components/MainComponents/ChatRoom.tsx +392 -0
- package/src/components/MainComponents/ChatWrapper.tsx +318 -0
- package/src/components/MainComponents/ChooseChatMessage.tsx +44 -0
- package/src/components/MainComponents/ConnectionBanner.tsx +33 -0
- package/src/components/MainComponents/EditWrapper.tsx +72 -0
- package/src/components/MainComponents/LoginWrapper.tsx +134 -0
- package/src/components/MainComponents/MediaMessage.tsx +55 -0
- package/src/components/MainComponents/MessageContainer.tsx +122 -0
- package/src/components/MainComponents/MessageList.tsx +429 -0
- package/src/components/MainComponents/NoMessagesPlaceholder.tsx +49 -0
- package/src/components/MainComponents/ProfileImagePlaceholder.tsx +143 -0
- package/src/components/MainComponents/RNStyled.tsx +34 -0
- package/src/components/MainComponents/ReduxWrapper.tsx +40 -0
- package/src/components/MainComponents/RoomList.tsx +246 -0
- package/src/components/MainComponents/Select.tsx +165 -0
- package/src/components/MainComponents/Switch.tsx +73 -0
- package/src/components/MainComponents/SystemMessage.tsx +42 -0
- package/src/components/Menu/HeaderRoomListMenu.tsx +169 -0
- package/src/components/MenuRoom/MenuRoom.tsx +46 -0
- package/src/components/MessageBubble/Avatar.tsx +84 -0
- package/src/components/MessageBubble/BottomReplyContainer.tsx +117 -0
- package/src/components/MessageBubble/CustomDivider.tsx +10 -0
- package/src/components/MessageBubble/DeletedMessage.tsx +36 -0
- package/src/components/MessageBubble/Message.tsx +400 -0
- package/src/components/MessageBubble/MessageInteractions.tsx +247 -0
- package/src/components/MessageBubble/MessageReaction.tsx +149 -0
- package/src/components/MessageBubble/MessageReply.tsx +50 -0
- package/src/components/MessageBubble/MessageTranslations.tsx +30 -0
- package/src/components/Modals/AddMembersModal/AddMembersModal.tsx +142 -0
- package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +471 -0
- package/src/components/Modals/ChatProfileModal/DeleteChatModal.tsx +91 -0
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +298 -0
- package/src/components/Modals/Modal/Modal.tsx +52 -0
- package/src/components/Modals/ModalHeaderComponent.tsx +41 -0
- package/src/components/Modals/ModalSelectMedia/ModalSelectMedia.tsx +285 -0
- package/src/components/Modals/ModalWrapper/ModalWrapper.tsx +103 -0
- package/src/components/Modals/NewChatModal/ModalContent.tsx +151 -0
- package/src/components/Modals/NewChatModal/NewChatModal.tsx +391 -0
- package/src/components/Modals/NewChatModal/UpdNewChatModal.tsx +136 -0
- package/src/components/Modals/SelectUsersModal/SelectUsersModal.tsx +134 -0
- package/src/components/Modals/SettingsModals/BlockedUsers/BlockedUsersModal.tsx +63 -0
- package/src/components/Modals/SettingsModals/DocumentShares/DocumentSharesModal.tsx +115 -0
- package/src/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.tsx +121 -0
- package/src/components/Modals/SettingsModals/ManageDataModal/StyledComponents.ts +0 -0
- package/src/components/Modals/SettingsModals/ProfileShares/ProfileShares.tsx +65 -0
- package/src/components/Modals/SettingsModals/Referrals/Referrals.tsx +90 -0
- package/src/components/Modals/SettingsModals/SharedStyledComponents.ts +74 -0
- package/src/components/Modals/SettingsModals/Visibility/RadioInput.tsx +59 -0
- package/src/components/Modals/SettingsModals/Visibility/StyledComponents.ts +32 -0
- package/src/components/Modals/SettingsModals/Visibility/VisibilityModal.tsx +146 -0
- package/src/components/Modals/UserProfileModal/EditUserModal.tsx +163 -0
- package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +410 -0
- package/src/components/Modals/UserSettingsModal/UserSettingsModal.tsx +85 -0
- package/src/components/Modals/styledModalComponents.tsx +181 -0
- package/src/components/OperationalModal/OperationalModal.tsx +78 -0
- package/src/components/RoomComponents/ChatRoomItem.tsx +219 -0
- package/src/components/RoomComponents/LastMessageItem.tsx +47 -0
- package/src/components/RoomComponents/styled/LastAudioMessage.tsx +62 -0
- package/src/components/RoomComponents/styled/LastMessageEmoji.tsx +26 -0
- package/src/components/RoomComponents/styled/LastMessageFile.tsx +54 -0
- package/src/components/RoomComponents/styled/LastMessagePhoto.tsx +54 -0
- package/src/components/RoomComponents/styled/LastMessageVideo.tsx +70 -0
- package/src/components/RoomComponents/styled/LastTextMessage.tsx +65 -0
- package/src/components/RoomComponents/styled/StyledRoomComponents.ts +53 -0
- package/src/components/SortDropDown/index.tsx +76 -0
- package/src/components/Thread/ThreadHeader.tsx +41 -0
- package/src/components/Thread/ThreadWrapper.tsx +270 -0
- package/src/components/Toast/Toast.tsx +128 -0
- package/src/components/Toast/index.ts +13 -0
- package/src/components/UsersList/StyledComponents.ts +30 -0
- package/src/components/UsersList/UsersList.tsx +112 -0
- package/src/components/styled/ActionButton.tsx +40 -0
- package/src/components/styled/AudioMessage.tsx +82 -0
- package/src/components/styled/Button.tsx +107 -0
- package/src/components/styled/ChatWrapperBox.tsx +9 -0
- package/src/components/styled/DateLabel.tsx +77 -0
- package/src/components/styled/Loader.tsx +65 -0
- package/src/components/styled/MediaFilePreview.tsx +42 -0
- package/src/components/styled/MediaModal.tsx +54 -0
- package/src/components/styled/MessageImage.tsx +54 -0
- package/src/components/styled/NewMessageLabel.tsx +50 -0
- package/src/components/styled/RoomListComponents/index.tsx +164 -0
- package/src/components/styled/SendInput.tsx +297 -0
- package/src/components/styled/StyledComponents.ts +320 -0
- package/src/components/styled/StyledInput.tsx +101 -0
- package/src/components/styled/StyledInputComponents/AudioComponents.tsx +1 -0
- package/src/components/styled/StyledInputComponents/Composing.tsx +97 -0
- package/src/components/styled/StyledInputComponents/CustomTypingIndicator.tsx +261 -0
- package/src/components/styled/StyledInputComponents/MediaComponents.tsx +90 -0
- package/src/components/styled/StyledInputComponents/StyledInputComponents.tsx +131 -0
- package/src/components/styled/StyledInputComponents/VideoComponents.tsx +49 -0
- package/src/components/styled/TreadLabel.tsx +50 -0
- package/src/components/styled/UnsupportedType.tsx +95 -0
- package/src/components/styled/VideoMessage.tsx +68 -0
- package/src/context/MessageNotificationContext.tsx +335 -0
- package/src/context/PushNotificationProvider.tsx +78 -0
- package/src/context/ToastContext.tsx +63 -0
- package/src/context/toastProvider.tsx +0 -0
- package/src/context/xmppProvider.tsx +364 -0
- package/src/helpers/block_scroll.ts +95 -0
- package/src/helpers/chatAutoEnterer.ts +46 -0
- package/src/helpers/checkUniqueUsers.tsx +31 -0
- package/src/helpers/constants/CHAT_TYPES.ts +6 -0
- package/src/helpers/constants/LANGUAGE_OPTIONS.ts +14 -0
- package/src/helpers/constants/LOCAL_STORAGE.ts +3 -0
- package/src/helpers/constants/MESSAGE_INTERACTIONS.ts +29 -0
- package/src/helpers/constants/MODAL_TYPES.ts +57 -0
- package/src/helpers/constants/PLATFORM_CONSTANTS.ts +3 -0
- package/src/helpers/createMainMessageForThread.ts +23 -0
- package/src/helpers/createMessageFromXml.ts +118 -0
- package/src/helpers/createRoomFromApi.tsx +27 -0
- package/src/helpers/createUserNameFromSetUser.ts +15 -0
- package/src/helpers/dateComparison.ts +12 -0
- package/src/helpers/debounce.ts +10 -0
- package/src/helpers/ensureScopedChatCache.ts +61 -0
- package/src/helpers/extractUniqueMembersFromRooms.ts +45 -0
- package/src/helpers/formatNumberWithCommas.ts +3 -0
- package/src/helpers/getBooleanFromString.ts +7 -0
- package/src/helpers/getDataFromXml.ts +80 -0
- package/src/helpers/getNumberFromString.ts +5 -0
- package/src/helpers/getRoomsWithRetry.ts +45 -0
- package/src/helpers/getTintedColor.ts +28 -0
- package/src/helpers/getUnnamedUsers.ts +39 -0
- package/src/helpers/handleCopyClick.ts +3 -0
- package/src/helpers/handleXmppError.ts +14 -0
- package/src/helpers/hashcolor.ts +34 -0
- package/src/helpers/historyPreloadScheduler.ts +264 -0
- package/src/helpers/initRoomsPresence.ts +32 -0
- package/src/helpers/initXmppRooms.tsx +71 -0
- package/src/helpers/insertMessageWithDelimiter.ts +96 -0
- package/src/helpers/isChatIdPresentInArray.ts +54 -0
- package/src/helpers/localStorageUser.ts +9 -0
- package/src/helpers/markdownParser.tsx +295 -0
- package/src/helpers/nanoToMs.ts +3 -0
- package/src/helpers/parseMessageBody.tsx +516 -0
- package/src/helpers/parseUrls.tsx +42 -0
- package/src/helpers/resolveInitBeforeLoadUser.ts +240 -0
- package/src/helpers/storeConsole.ts +26 -0
- package/src/helpers/transformTranslatations.ts +16 -0
- package/src/helpers/updateMessagesTillLast.tsx +104 -0
- package/src/helpers/updatedChatLastTimestamps.ts +29 -0
- package/src/helpers/validator.ts +20 -0
- package/src/helpers/walletUsername.ts +12 -0
- package/src/hooks/hooks.ts +6 -0
- package/src/hooks/useChatSettingState.tsx +38 -0
- package/src/hooks/useChatWrapperInit.ts +266 -0
- package/src/hooks/useComposing.tsx +59 -0
- package/src/hooks/useCustomTypingIndicator.tsx +193 -0
- package/src/hooks/useEventHandlers.tsx +81 -0
- package/src/hooks/useGetNewArchRoom.tsx +57 -0
- package/src/hooks/useHeapSender.tsx +88 -0
- package/src/hooks/useLocalStorage.tsx +53 -0
- package/src/hooks/useLogout.tsx +31 -0
- package/src/hooks/useMessageHeapState.tsx +11 -0
- package/src/hooks/useMessageLoaderQueue.tsx +90 -0
- package/src/hooks/useMessageQueue.ts +146 -0
- package/src/hooks/usePendingNotification.ts +49 -0
- package/src/hooks/usePrevious.tsx +15 -0
- package/src/hooks/usePushNotifications.ts +106 -0
- package/src/hooks/useQRCodeChatHandler.ts +58 -0
- package/src/hooks/useRoomInitialization.tsx +160 -0
- package/src/hooks/useRoomState.tsx +47 -0
- package/src/hooks/useRoomUrl.tsx +41 -0
- package/src/hooks/useSendMessage.tsx +306 -0
- package/src/hooks/useUnreadMessagesCounter.ts +55 -0
- package/src/hooks/useXmppInitialization.ts +148 -0
- package/src/main.ts +45 -0
- package/src/networking/api-requests/auth.api.ts +140 -0
- package/src/networking/api-requests/push.api.ts +55 -0
- package/src/networking/api-requests/roomMembers.api.ts +20 -0
- package/src/networking/api-requests/rooms.api.ts +142 -0
- package/src/networking/api-requests/user.api.ts +65 -0
- package/src/networking/apiClient.ts +149 -0
- package/src/networking/helpers/refreshChecker.ts +13 -0
- package/src/networking/stanzaHandlers.ts +418 -0
- package/src/networking/xmpp/actionSetTimestampToPrivateStore.xmpp.ts +37 -0
- package/src/networking/xmpp/allRoomPresences.xmpp.ts +10 -0
- package/src/networking/xmpp/createPrivateRoom.xmpp.ts +26 -0
- package/src/networking/xmpp/createRoom.xmpp.ts +35 -0
- package/src/networking/xmpp/createRoomPresence.xmpp.ts +74 -0
- package/src/networking/xmpp/createTimeoutPromise.xmpp.ts +15 -0
- package/src/networking/xmpp/deleteMessage.xmpp.ts +19 -0
- package/src/networking/xmpp/editMessage.xmpp.ts +46 -0
- package/src/networking/xmpp/getChatsPrivateStoreRequest.xmpp.ts +57 -0
- package/src/networking/xmpp/getHistory.xmpp.ts +140 -0
- package/src/networking/xmpp/getLastMessageArchive.xmpp.ts +24 -0
- package/src/networking/xmpp/getRoomInfo.xmpp.ts +15 -0
- package/src/networking/xmpp/getRoomMembers.xmpp.ts +13 -0
- package/src/networking/xmpp/getRooms.xmpp.ts +43 -0
- package/src/networking/xmpp/getRoomsPaged.xmpp.ts +59 -0
- package/src/networking/xmpp/handlePong.xmpp.ts +7 -0
- package/src/networking/xmpp/handleStanzas.xmpp.ts +55 -0
- package/src/networking/xmpp/inviteRoomRequest.xmpp.ts +25 -0
- package/src/networking/xmpp/leaveTheRoom.xmpp.ts +15 -0
- package/src/networking/xmpp/listenerManager.ts +116 -0
- package/src/networking/xmpp/presenceInRoom.xmpp.ts +57 -0
- package/src/networking/xmpp/roomConfig.xmpp.ts +71 -0
- package/src/networking/xmpp/sendMediaMessage.xmpp.ts +97 -0
- package/src/networking/xmpp/sendMessageReaction.xmpp.ts +41 -0
- package/src/networking/xmpp/sendPing.xmpp.ts +21 -0
- package/src/networking/xmpp/sendTextMessage.xmpp.ts +99 -0
- package/src/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.ts +44 -0
- package/src/networking/xmpp/sendTypingRequest.xmpp.ts +26 -0
- package/src/networking/xmpp/setChatsPrivateStoreRequest.xmpp.ts +48 -0
- package/src/networking/xmpp/setMeAsOwner.xmpp.ts +52 -0
- package/src/networking/xmpp/setRoomImage.xmpp.ts +26 -0
- package/src/networking/xmpp/setVCard.xmpp.ts +19 -0
- package/src/networking/xmpp/subscribeToRoomMessages.xmpp.ts +75 -0
- package/src/networking/xmppClient.ts +603 -0
- package/src/roomStore/Middleware/logoutMiddleware.tsx +24 -0
- package/src/roomStore/Middleware/newMessageMidlleware.tsx +87 -0
- package/src/roomStore/Middleware/reactionsMiddleware.tsx +77 -0
- package/src/roomStore/Middleware/testMiddleware.tsx +20 -0
- package/src/roomStore/Middleware/unreadMidlleware.tsx +58 -0
- package/src/roomStore/chatSettingsSlice.ts +178 -0
- package/src/roomStore/index.ts +68 -0
- package/src/roomStore/persistence.ts +152 -0
- package/src/roomStore/roomHeapSlice.ts +37 -0
- package/src/roomStore/roomsSlice.ts +353 -0
- package/src/services/pushNotifications.ts +170 -0
- package/src/services/pushSubscriptionService.ts +145 -0
- package/src/types/messages.ts +61 -0
- package/src/types/models/action.model.ts +30 -0
- package/src/types/models/config.model.ts +16 -0
- package/src/types/models/customComponents.model.ts +53 -0
- package/src/types/models/language.model.ts +11 -0
- package/src/types/models/media.model.ts +5 -0
- package/src/types/models/message.model.ts +69 -0
- package/src/types/models/modal.model.ts +9 -0
- package/src/types/models/room.model.ts +120 -0
- package/src/types/models/user.model.ts +90 -0
- package/src/types/models/xmpp.model.ts +155 -0
- package/src/types/types.ts +505 -0
- package/src/utils/PriorityQueue.ts +47 -0
- package/src/utils/actionHelpers.ts +40 -0
- package/src/utils/actionLogger.ts +64 -0
- package/src/utils/clientRegistry.ts +80 -0
- package/src/utils/devLogger.ts +197 -0
- package/src/utils/messageNotificationManager.ts +108 -0
- package/src/utils/resendMessage.ts +87 -0
- package/src/utils/toastEmitter.ts +27 -0
- package/src/utils/xmppMemoryMonitor.ts +63 -0
- package/src/version.ts +1 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/** @format */
|
|
2
|
+
|
|
3
|
+
import React, { FC, useMemo } from 'react';
|
|
4
|
+
import { View, StyleSheet, Image, Text } from 'react-native';
|
|
5
|
+
import { ProfileImagePlaceholder } from '../MainComponents/ProfileImagePlaceholder';
|
|
6
|
+
import { useChatSettingState } from '../../hooks/useChatSettingState';
|
|
7
|
+
import { BurgerMenuIcon } from '../../assets/icons';
|
|
8
|
+
import Button from '../styled/Button';
|
|
9
|
+
|
|
10
|
+
interface HeaderRoomListProps {
|
|
11
|
+
setDrawerOpen: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const HeaderRoomList: FC<HeaderRoomListProps> = ({ setDrawerOpen }) => {
|
|
15
|
+
const { config, user, selectedUser } = useChatSettingState();
|
|
16
|
+
|
|
17
|
+
const modalUser: any = selectedUser ?? user;
|
|
18
|
+
|
|
19
|
+
const HeaderLogo = useMemo(() => {
|
|
20
|
+
const image = config?.headerLogo;
|
|
21
|
+
|
|
22
|
+
if (image) {
|
|
23
|
+
if (typeof image === 'function') {
|
|
24
|
+
const SvgComponent = image as React.FC<React.SVGProps<SVGSVGElement>>;
|
|
25
|
+
return <SvgComponent />;
|
|
26
|
+
} else if (typeof image === 'string') {
|
|
27
|
+
return <Image source={{ uri: image }} />;
|
|
28
|
+
} else {
|
|
29
|
+
return image;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return <View />;
|
|
34
|
+
}, [config?.backgroundChat?.image]);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<View style={styles.headerContainer}>
|
|
38
|
+
{!config?.disableRoomMenu && config?.headerMenu ? (
|
|
39
|
+
<View style={styles.leftContainer}>
|
|
40
|
+
<Button
|
|
41
|
+
style={styles.menuButton}
|
|
42
|
+
color="black"
|
|
43
|
+
unstyled
|
|
44
|
+
EndIcon={<BurgerMenuIcon color={config?.colors?.primary} />}
|
|
45
|
+
onPress={() => config?.headerMenu && config?.headerMenu()}
|
|
46
|
+
/>
|
|
47
|
+
</View>
|
|
48
|
+
) : (
|
|
49
|
+
<View style={styles.leftContainer} />
|
|
50
|
+
)}
|
|
51
|
+
<View style={styles.centerContainer}>
|
|
52
|
+
{config?.headerLogo ? (
|
|
53
|
+
HeaderLogo
|
|
54
|
+
) : (
|
|
55
|
+
<Text style={{ fontWeight: 500, fontSize: 18 }}>Chats</Text>
|
|
56
|
+
)}
|
|
57
|
+
</View>
|
|
58
|
+
<View style={styles.rightContainer}>
|
|
59
|
+
<ProfileImagePlaceholder
|
|
60
|
+
icon={modalUser?.profileImage ?? null}
|
|
61
|
+
name={modalUser?.name ?? modalUser?.firstName}
|
|
62
|
+
size={30}
|
|
63
|
+
click={{
|
|
64
|
+
isClick: true,
|
|
65
|
+
onPress: setDrawerOpen,
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
</View>
|
|
69
|
+
</View>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const styles = StyleSheet.create({
|
|
74
|
+
headerContainer: {
|
|
75
|
+
flexDirection: 'row',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
paddingHorizontal: 8,
|
|
78
|
+
paddingVertical: 2,
|
|
79
|
+
paddingTop: 62, // 60px top padding + 2px for header content
|
|
80
|
+
maxHeight: 24,
|
|
81
|
+
minHeight: 24,
|
|
82
|
+
justifyContent: 'space-between',
|
|
83
|
+
},
|
|
84
|
+
menuButton: {
|
|
85
|
+
padding: 8,
|
|
86
|
+
borderRadius: 16,
|
|
87
|
+
backgroundColor: 'transparent',
|
|
88
|
+
},
|
|
89
|
+
leftContainer: {
|
|
90
|
+
flex: 1,
|
|
91
|
+
alignItems: 'flex-start',
|
|
92
|
+
},
|
|
93
|
+
centerContainer: {
|
|
94
|
+
position: 'absolute',
|
|
95
|
+
left: 0,
|
|
96
|
+
right: 0,
|
|
97
|
+
alignItems: 'center',
|
|
98
|
+
justifyContent: 'center',
|
|
99
|
+
},
|
|
100
|
+
rightContainer: {
|
|
101
|
+
flex: 1,
|
|
102
|
+
alignItems: 'flex-end',
|
|
103
|
+
},
|
|
104
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import AudioRecorder from '../InputComponents/AudioRecorder';
|
|
3
|
+
|
|
4
|
+
interface AudioInputProps {
|
|
5
|
+
isRecording: boolean;
|
|
6
|
+
setIsRecording: (value: boolean) => void;
|
|
7
|
+
handleSendClick: (audioUrl?: string) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const AudioInput: React.FC<AudioInputProps> = ({
|
|
11
|
+
isRecording,
|
|
12
|
+
setIsRecording,
|
|
13
|
+
handleSendClick,
|
|
14
|
+
}) => {
|
|
15
|
+
return (
|
|
16
|
+
<AudioRecorder
|
|
17
|
+
setIsRecording={setIsRecording}
|
|
18
|
+
isRecording={isRecording}
|
|
19
|
+
handleSendClick={handleSendClick}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default AudioInput;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React, { useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
RecordContainer,
|
|
4
|
+
Timer,
|
|
5
|
+
} from '../styled/StyledInputComponents/StyledInputComponents';
|
|
6
|
+
import { RecordIcon, RemoveIcon, SendIcon } from '../../assets/icons';
|
|
7
|
+
import Button from '../styled/Button';
|
|
8
|
+
import RecordingIndicator from './RecordingIndicator';
|
|
9
|
+
import { Platform, View } from 'react-native';
|
|
10
|
+
import AudioRecorderPlayer from 'react-native-audio-recorder-player';
|
|
11
|
+
import RNFS from 'react-native-fs';
|
|
12
|
+
|
|
13
|
+
interface AudioRecorderProps {
|
|
14
|
+
setIsRecording: (state: boolean) => void;
|
|
15
|
+
isRecording: boolean;
|
|
16
|
+
handleSendClick: (audioBlob?: any) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const AudioRecorder: React.FC<AudioRecorderProps> = ({
|
|
20
|
+
setIsRecording,
|
|
21
|
+
isRecording,
|
|
22
|
+
handleSendClick,
|
|
23
|
+
}) => {
|
|
24
|
+
const audioRecorderPlayer = useRef(new AudioRecorderPlayer()).current;
|
|
25
|
+
const [timer, setTimer] = useState('00:00');
|
|
26
|
+
const [audioPath, setAudioPath] = useState<string | null>(null);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
return () => {
|
|
30
|
+
audioRecorderPlayer.stopRecorder();
|
|
31
|
+
audioRecorderPlayer.removeRecordBackListener();
|
|
32
|
+
};
|
|
33
|
+
}, [audioRecorderPlayer]);
|
|
34
|
+
|
|
35
|
+
const startRecording = async () => {
|
|
36
|
+
try {
|
|
37
|
+
const path = Platform.select({
|
|
38
|
+
ios: `${RNFS.DocumentDirectoryPath}/recording.m4a`,
|
|
39
|
+
android: `${RNFS.CachesDirectoryPath}/recording.mp4`,
|
|
40
|
+
});
|
|
41
|
+
const uri = await audioRecorderPlayer.startRecorder(path);
|
|
42
|
+
setAudioPath(uri);
|
|
43
|
+
|
|
44
|
+
audioRecorderPlayer.addRecordBackListener((e) => {
|
|
45
|
+
const minutes = Math.floor(e.currentPosition / 60000);
|
|
46
|
+
const seconds = Math.floor((e.currentPosition % 60000) / 1000);
|
|
47
|
+
setTimer(
|
|
48
|
+
`${minutes.toString().padStart(2, '0')}:${seconds
|
|
49
|
+
.toString()
|
|
50
|
+
.padStart(2, '0')}`
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
setIsRecording(true);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error('Error starting recording:', error);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const stopRecording = async () => {
|
|
61
|
+
try {
|
|
62
|
+
await audioRecorderPlayer.stopRecorder();
|
|
63
|
+
audioRecorderPlayer.removeRecordBackListener();
|
|
64
|
+
setIsRecording(false);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('Error stopping recording:', error);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const sendAudio = () => {
|
|
71
|
+
if (audioPath) {
|
|
72
|
+
console.log('Audio sent:', audioPath);
|
|
73
|
+
handleSendClick(audioPath);
|
|
74
|
+
resetState();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const resetState = () => {
|
|
79
|
+
setAudioPath(null);
|
|
80
|
+
setTimer('00:00');
|
|
81
|
+
setIsRecording(false);
|
|
82
|
+
};
|
|
83
|
+
return isRecording ? (
|
|
84
|
+
<RecordContainer>
|
|
85
|
+
<View style={{ display: 'flex', alignItems: 'center' }}>
|
|
86
|
+
{isRecording && <RecordingIndicator />}
|
|
87
|
+
<Timer>{timer}</Timer>
|
|
88
|
+
</View>
|
|
89
|
+
|
|
90
|
+
<View style={{ display: 'flex', gap: 8 }}>
|
|
91
|
+
<Button onPress={stopRecording} EndIcon={<RemoveIcon />} unstyled />
|
|
92
|
+
<Button onPress={sendAudio} EndIcon={<SendIcon />} unstyled />
|
|
93
|
+
</View>
|
|
94
|
+
</RecordContainer>
|
|
95
|
+
) : (
|
|
96
|
+
<Button onPress={startRecording} EndIcon={<RecordIcon />} />
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default AudioRecorder;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { View, Image, TouchableOpacity } from 'react-native';
|
|
3
|
+
import Video from 'react-native-video';
|
|
4
|
+
import { AttachIcon, RemoveIcon } from '../../assets/icons';
|
|
5
|
+
import Button from '../styled/Button';
|
|
6
|
+
import { launchImageLibrary } from 'react-native-image-picker';
|
|
7
|
+
import { IConfig } from '../../types/types';
|
|
8
|
+
|
|
9
|
+
interface FilePreview {
|
|
10
|
+
uri: string;
|
|
11
|
+
name: string;
|
|
12
|
+
type: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface MediaInputProps {
|
|
16
|
+
filePreviews: FilePreview[];
|
|
17
|
+
setFilePreviews: (
|
|
18
|
+
update: FilePreview[] | ((prev: FilePreview[]) => FilePreview[])
|
|
19
|
+
) => void;
|
|
20
|
+
handleSendClick: () => void;
|
|
21
|
+
config?: IConfig;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const MediaInput: React.FC<MediaInputProps> = ({
|
|
25
|
+
filePreviews,
|
|
26
|
+
setFilePreviews,
|
|
27
|
+
handleSendClick,
|
|
28
|
+
config,
|
|
29
|
+
}) => {
|
|
30
|
+
const handleAttachClick = useCallback(() => {
|
|
31
|
+
launchImageLibrary(
|
|
32
|
+
{
|
|
33
|
+
mediaType: 'mixed',
|
|
34
|
+
selectionLimit: 5,
|
|
35
|
+
},
|
|
36
|
+
(response) => {
|
|
37
|
+
if (response.assets) {
|
|
38
|
+
const newFiles = response.assets.map((asset) => ({
|
|
39
|
+
uri: asset.uri!,
|
|
40
|
+
name: asset.fileName!,
|
|
41
|
+
type: asset.type!,
|
|
42
|
+
}));
|
|
43
|
+
setFilePreviews((prevFiles: FilePreview[]) =>
|
|
44
|
+
[...prevFiles, ...newFiles].slice(0, 5)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}, [setFilePreviews]);
|
|
50
|
+
|
|
51
|
+
const handleRemoveFile = useCallback(
|
|
52
|
+
(file: FilePreview) => {
|
|
53
|
+
setFilePreviews((prevFiles: FilePreview[]) =>
|
|
54
|
+
prevFiles.filter((f) => f.uri !== file.uri)
|
|
55
|
+
);
|
|
56
|
+
},
|
|
57
|
+
[setFilePreviews]
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const renderFilePreview = useCallback((file: FilePreview) => {
|
|
61
|
+
if (file.type?.startsWith('image/')) {
|
|
62
|
+
return (
|
|
63
|
+
<Image source={{ uri: file.uri }} style={{ width: 50, height: 50 }} />
|
|
64
|
+
);
|
|
65
|
+
} else if (file.type?.startsWith('video/')) {
|
|
66
|
+
return (
|
|
67
|
+
<Video
|
|
68
|
+
source={{ uri: file.uri }}
|
|
69
|
+
style={{ width: 50, height: 50 }}
|
|
70
|
+
controls
|
|
71
|
+
resizeMode="cover"
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<View>
|
|
80
|
+
<Button
|
|
81
|
+
onPress={handleAttachClick}
|
|
82
|
+
disabled={config?.disableMedia}
|
|
83
|
+
EndIcon={<AttachIcon />}
|
|
84
|
+
/>
|
|
85
|
+
{filePreviews.length > 0 && (
|
|
86
|
+
<View>
|
|
87
|
+
{filePreviews.map((file) => (
|
|
88
|
+
<View key={file.uri} style={{ position: 'relative', margin: 8 }}>
|
|
89
|
+
{renderFilePreview(file)}
|
|
90
|
+
<TouchableOpacity
|
|
91
|
+
onPress={() => handleRemoveFile(file)}
|
|
92
|
+
style={{
|
|
93
|
+
position: 'absolute',
|
|
94
|
+
top: 4,
|
|
95
|
+
right: 4,
|
|
96
|
+
width: 20,
|
|
97
|
+
height: 20,
|
|
98
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
99
|
+
justifyContent: 'center',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
borderRadius: 10,
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
<RemoveIcon />
|
|
105
|
+
</TouchableOpacity>
|
|
106
|
+
</View>
|
|
107
|
+
))}
|
|
108
|
+
</View>
|
|
109
|
+
)}
|
|
110
|
+
</View>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export default MediaInput;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React, {useEffect, useRef} from 'react';
|
|
2
|
+
import {View, StyleSheet, Animated} from 'react-native';
|
|
3
|
+
|
|
4
|
+
const RecordingIndicator = () => {
|
|
5
|
+
const scaleAnim = useRef(new Animated.Value(1)).current; // For scaling the circle
|
|
6
|
+
const opacityAnim = useRef(new Animated.Value(1)).current; // For pulsating effect
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
Animated.loop(
|
|
10
|
+
Animated.sequence([
|
|
11
|
+
Animated.parallel([
|
|
12
|
+
Animated.timing(scaleAnim, {
|
|
13
|
+
toValue: 1.3,
|
|
14
|
+
duration: 750,
|
|
15
|
+
useNativeDriver: true,
|
|
16
|
+
}),
|
|
17
|
+
Animated.timing(opacityAnim, {
|
|
18
|
+
toValue: 0.6,
|
|
19
|
+
duration: 750,
|
|
20
|
+
useNativeDriver: true,
|
|
21
|
+
}),
|
|
22
|
+
]),
|
|
23
|
+
Animated.parallel([
|
|
24
|
+
Animated.timing(scaleAnim, {
|
|
25
|
+
toValue: 1,
|
|
26
|
+
duration: 750,
|
|
27
|
+
useNativeDriver: true,
|
|
28
|
+
}),
|
|
29
|
+
Animated.timing(opacityAnim, {
|
|
30
|
+
toValue: 1,
|
|
31
|
+
duration: 750,
|
|
32
|
+
useNativeDriver: true,
|
|
33
|
+
}),
|
|
34
|
+
]),
|
|
35
|
+
]),
|
|
36
|
+
).start();
|
|
37
|
+
}, [scaleAnim, opacityAnim]);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<View style={styles.wrapper}>
|
|
41
|
+
<Animated.View
|
|
42
|
+
style={[
|
|
43
|
+
styles.outerCircle,
|
|
44
|
+
{
|
|
45
|
+
transform: [{scale: scaleAnim}],
|
|
46
|
+
opacity: opacityAnim,
|
|
47
|
+
},
|
|
48
|
+
]}
|
|
49
|
+
/>
|
|
50
|
+
<View style={styles.innerCircle} />
|
|
51
|
+
</View>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const styles = StyleSheet.create({
|
|
56
|
+
wrapper: {
|
|
57
|
+
justifyContent: 'center',
|
|
58
|
+
alignItems: 'center',
|
|
59
|
+
marginRight: 10,
|
|
60
|
+
},
|
|
61
|
+
outerCircle: {
|
|
62
|
+
width: 18,
|
|
63
|
+
height: 18,
|
|
64
|
+
borderRadius: 9,
|
|
65
|
+
backgroundColor: 'rgba(255, 0, 0, 0.6)',
|
|
66
|
+
position: 'absolute',
|
|
67
|
+
},
|
|
68
|
+
innerCircle: {
|
|
69
|
+
width: 16,
|
|
70
|
+
height: 16,
|
|
71
|
+
borderRadius: 8,
|
|
72
|
+
backgroundColor: 'red',
|
|
73
|
+
position: 'relative',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export default RecordingIndicator;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import styled, { css } from 'styled-components/native';
|
|
3
|
+
import {
|
|
4
|
+
TextInput,
|
|
5
|
+
View,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
TextInputProps,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
|
|
10
|
+
const shouldForwardProp = (prop: string) =>
|
|
11
|
+
prop !== 'animated' && prop !== 'expanded' && prop !== 'direction';
|
|
12
|
+
|
|
13
|
+
const SearchInputWrapper = styled.View.withConfig({ shouldForwardProp })<{
|
|
14
|
+
animated?: boolean;
|
|
15
|
+
direction?: string;
|
|
16
|
+
expanded?: boolean;
|
|
17
|
+
}>`
|
|
18
|
+
position: relative;
|
|
19
|
+
flex-direction: row;
|
|
20
|
+
align-items: center;
|
|
21
|
+
background-color: #fff;
|
|
22
|
+
border-radius: 16px;
|
|
23
|
+
height: 48px;
|
|
24
|
+
padding: 0 16px;
|
|
25
|
+
margin: 0 6px 16px 6px;
|
|
26
|
+
flex: 1;
|
|
27
|
+
${({ animated, expanded }) =>
|
|
28
|
+
animated &&
|
|
29
|
+
css`
|
|
30
|
+
justify-content: ${expanded ? 'flex-start' : 'center'};
|
|
31
|
+
transition: width 0.7s ease-in-out;
|
|
32
|
+
`};
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const SearchIcon = styled(TouchableOpacity)<{
|
|
36
|
+
animated?: boolean;
|
|
37
|
+
expanded?: boolean;
|
|
38
|
+
}>`
|
|
39
|
+
padding: 3.5px;
|
|
40
|
+
color: #999;
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const StyledInput = styled(TextInput)<{
|
|
44
|
+
animated?: boolean;
|
|
45
|
+
expanded?: boolean;
|
|
46
|
+
}>`
|
|
47
|
+
background-color: transparent;
|
|
48
|
+
border: none;
|
|
49
|
+
flex: 1;
|
|
50
|
+
font-size: 16px;
|
|
51
|
+
color: #000;
|
|
52
|
+
opacity: ${({ animated, expanded }) => (animated && !expanded ? 0 : 1)};
|
|
53
|
+
display: ${({ animated, expanded }) =>
|
|
54
|
+
animated && !expanded ? 'none' : 'flex'};
|
|
55
|
+
${({ animated }) =>
|
|
56
|
+
animated &&
|
|
57
|
+
css`
|
|
58
|
+
transition: opacity 0.7s ease-in-out;
|
|
59
|
+
`}
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
interface SearchInputProps extends TextInputProps {
|
|
63
|
+
icon?: React.ReactNode;
|
|
64
|
+
animated?: boolean;
|
|
65
|
+
direction?: 'left' | 'right';
|
|
66
|
+
placeholder?: string;
|
|
67
|
+
value: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const SearchInput: React.FC<SearchInputProps> = ({
|
|
71
|
+
icon,
|
|
72
|
+
animated = false,
|
|
73
|
+
direction = 'left',
|
|
74
|
+
placeholder,
|
|
75
|
+
...props
|
|
76
|
+
}) => {
|
|
77
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
78
|
+
const [isTyping, setIsTyping] = useState(false);
|
|
79
|
+
const inputRef = useRef<TextInput | null>(null);
|
|
80
|
+
|
|
81
|
+
const handleFocus = () => {
|
|
82
|
+
setIsExpanded(true);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const handleBlur = () => {
|
|
86
|
+
if (!isTyping) {
|
|
87
|
+
setIsExpanded(false);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const handleInput = (text: string) => {
|
|
92
|
+
setIsTyping(!!text);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (isExpanded && animated) {
|
|
97
|
+
const timeout = setTimeout(() => {
|
|
98
|
+
inputRef.current?.focus();
|
|
99
|
+
}, 250);
|
|
100
|
+
|
|
101
|
+
return () => clearTimeout(timeout);
|
|
102
|
+
}
|
|
103
|
+
}, [isExpanded, animated]);
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<SearchInputWrapper
|
|
107
|
+
animated={animated}
|
|
108
|
+
direction={direction}
|
|
109
|
+
expanded={isExpanded}
|
|
110
|
+
onTouchStart={handleFocus}
|
|
111
|
+
>
|
|
112
|
+
{icon && (
|
|
113
|
+
<SearchIcon
|
|
114
|
+
animated={animated}
|
|
115
|
+
expanded={isExpanded}
|
|
116
|
+
onPress={() => inputRef.current?.focus()}
|
|
117
|
+
>
|
|
118
|
+
{icon}
|
|
119
|
+
</SearchIcon>
|
|
120
|
+
)}
|
|
121
|
+
<StyledInput
|
|
122
|
+
ref={inputRef}
|
|
123
|
+
onBlur={handleBlur}
|
|
124
|
+
animated={animated}
|
|
125
|
+
expanded={isExpanded}
|
|
126
|
+
onChangeText={handleInput}
|
|
127
|
+
placeholder={placeholder}
|
|
128
|
+
placeholderTextColor="#999"
|
|
129
|
+
{...props}
|
|
130
|
+
/>
|
|
131
|
+
</SearchInputWrapper>
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export { SearchInput };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { SendIcon } from '../../assets/icons';
|
|
3
|
+
import { IConfig } from '../../types/types';
|
|
4
|
+
import Button from '../styled/Button';
|
|
5
|
+
import { MessageInput } from '../styled/StyledInputComponents/StyledInputComponents';
|
|
6
|
+
|
|
7
|
+
interface TextInputProps {
|
|
8
|
+
message: string;
|
|
9
|
+
setMessage: (value: string) => void;
|
|
10
|
+
handleSendClick: () => void;
|
|
11
|
+
isLoading: boolean;
|
|
12
|
+
config?: IConfig;
|
|
13
|
+
onFocus?: () => void;
|
|
14
|
+
onBlur?: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const TextInput: React.FC<TextInputProps> = ({
|
|
18
|
+
message,
|
|
19
|
+
setMessage,
|
|
20
|
+
handleSendClick,
|
|
21
|
+
config,
|
|
22
|
+
isLoading,
|
|
23
|
+
onFocus,
|
|
24
|
+
onBlur,
|
|
25
|
+
}) => {
|
|
26
|
+
const handleInputChange = useCallback(
|
|
27
|
+
(text: string) => {
|
|
28
|
+
setMessage(text);
|
|
29
|
+
},
|
|
30
|
+
[setMessage]
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const handleKeyDown = useCallback(
|
|
34
|
+
(event: { nativeEvent: { key: string } }) => {
|
|
35
|
+
if (event.nativeEvent.key === 'Enter' && message) {
|
|
36
|
+
handleSendClick();
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
[handleSendClick, message]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<MessageInput
|
|
45
|
+
color={config?.colors?.primary}
|
|
46
|
+
placeholder="Type message"
|
|
47
|
+
value={message}
|
|
48
|
+
onChangeText={handleInputChange}
|
|
49
|
+
onSubmitEditing={handleSendClick}
|
|
50
|
+
onFocus={onFocus}
|
|
51
|
+
onBlur={onBlur}
|
|
52
|
+
editable={isLoading}
|
|
53
|
+
/>
|
|
54
|
+
<Button
|
|
55
|
+
onPress={handleSendClick}
|
|
56
|
+
EndIcon={<SendIcon color={!message ? '#D4D4D8' : '#fff'} />}
|
|
57
|
+
style={{
|
|
58
|
+
borderRadius: 100,
|
|
59
|
+
backgroundColor: !message ? 'transparent' : config?.colors?.primary,
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
</>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default TextInput;
|