From 9d2175485c7d3c73cf50de73f2b7cdd943583169 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 29 Mar 2022 15:53:27 -0300 Subject: [PATCH] Merge 4.26.1 into single-server (#3981) --- .gitignore | 1 + __tests__/Storyshots.test.js | 20 + android/app/build.gradle | 2 +- .../rocket/reactnative/MainApplication.java | 3 +- .../java/chat/rocket/reactnative/Ejson.java | 12 +- app/actions/actionsTypes.ts | 2 +- app/actions/login.ts | 6 + app/actions/room.ts | 2 +- app/actions/settings.ts | 10 +- app/commands.ts | 4 +- app/constants/colors.ts | 9 + app/constants/messageTypeLoad.ts | 10 +- app/constants/settings.ts | 2 +- app/containers/ActionSheet/ActionSheet.tsx | 77 +- app/containers/ActionSheet/Handle.tsx | 14 +- app/containers/ActionSheet/Item.tsx | 15 +- app/containers/ActionSheet/Provider.tsx | 30 +- app/containers/ActivityIndicator.tsx | 14 +- app/containers/Avatar/index.tsx | 15 + app/containers/Avatar/interfaces.ts | 4 +- app/containers/BackgroundContainer/index.tsx | 22 +- app/containers/Check.tsx | 12 +- app/containers/FormContainer.tsx | 46 +- app/containers/Header/index.tsx | 43 +- app/containers/HeaderButton/Common.tsx | 40 +- .../HeaderButton/HeaderButtonContainer.tsx | 4 +- .../HeaderButton/HeaderButtonItem.tsx | 34 +- .../HeaderButton/HeaderButtonItemBadge.tsx | 2 +- .../InAppNotification/NotifierComponent.tsx | 21 +- app/containers/InAppNotification/index.tsx | 10 +- app/containers/List/ListContainer.tsx | 2 +- app/containers/List/ListHeader.tsx | 23 +- app/containers/List/ListIcon.tsx | 19 +- app/containers/List/ListInfo.tsx | 20 +- app/containers/List/ListItem.tsx | 92 +- app/containers/List/ListSection.tsx | 5 +- app/containers/List/ListSeparator.tsx | 13 +- app/containers/Loading.tsx | 16 +- app/containers/LoginServices.tsx | 89 +- app/containers/MessageActions/index.tsx | 26 +- .../MessageBox/CommandsPreview/Item.tsx | 2 +- .../MessageBox/CommandsPreview/index.tsx | 3 +- .../MessageBox/getMentionRegexp.test.js | 56 ++ app/containers/MessageBox/getMentionRegexp.ts | 4 + app/containers/MessageBox/index.tsx | 36 +- app/containers/MessageErrorActions.tsx | 21 +- app/containers/Passcode/Base/Button.tsx | 6 +- app/containers/Passcode/Base/Dots.tsx | 70 +- app/containers/Passcode/Base/LockIcon.tsx | 19 +- app/containers/Passcode/Base/Locked.tsx | 26 +- app/containers/Passcode/Base/Subtitle.tsx | 20 +- app/containers/Passcode/Base/Title.tsx | 20 +- app/containers/Passcode/Base/index.tsx | 54 +- app/containers/Passcode/PasscodeChoose.tsx | 15 +- app/containers/Passcode/PasscodeEnter.tsx | 32 +- app/containers/Passcode/constants.ts | 12 +- app/containers/Passcode/utils.ts | 8 +- app/containers/ReactionsModal.tsx | 11 +- app/containers/RoomHeader/RoomHeader.tsx | 3 +- app/containers/RoomHeader/index.tsx | 6 +- app/containers/RoomTypeIcon.tsx | 10 +- app/containers/SearchBox.tsx | 103 +-- app/containers/Status/Status.tsx | 10 +- app/containers/Status/definition.ts | 9 + app/containers/Status/index.tsx | 27 +- app/containers/StatusBar.tsx | 17 +- app/containers/TextInput.tsx | 16 +- app/containers/ThreadDetails.tsx | 22 +- app/containers/Toast.tsx | 12 +- app/containers/TwoFactor/index.tsx | 62 +- app/containers/UIKit/Actions.tsx | 19 +- app/containers/UIKit/Context.tsx | 14 +- app/containers/UIKit/DatePicker.tsx | 19 +- app/containers/UIKit/Image.tsx | 35 +- app/containers/UIKit/Input.tsx | 11 +- app/containers/UIKit/MultiSelect/Items.tsx | 2 +- app/containers/UIKit/MultiSelect/index.tsx | 3 +- app/containers/UIKit/Overflow.tsx | 37 +- app/containers/UIKit/Section.tsx | 39 +- app/containers/UIKit/index.tsx | 31 +- app/containers/UIKit/interfaces.ts | 273 ++++++ app/containers/UIKit/utils.ts | 16 +- app/containers/markdown/AtMention.tsx | 15 +- app/containers/markdown/BlockQuote.tsx | 2 +- app/containers/markdown/Hashtag.tsx | 8 +- app/containers/markdown/Link.tsx | 2 +- app/containers/markdown/List.tsx | 7 +- app/containers/markdown/ListItem.tsx | 2 +- app/containers/markdown/Preview.tsx | 6 +- app/containers/markdown/Table.tsx | 6 +- app/containers/markdown/TableCell.tsx | 6 +- app/containers/markdown/TableRow.tsx | 8 +- app/containers/markdown/index.tsx | 1 + app/containers/markdown/interfaces.ts | 1 + app/containers/markdown/new/BigEmoji.tsx | 2 +- app/containers/markdown/new/Bold.tsx | 2 +- app/containers/markdown/new/Code.tsx | 8 +- app/containers/markdown/new/CodeLine.tsx | 2 +- app/containers/markdown/new/Emoji.tsx | 4 +- app/containers/markdown/new/Heading.tsx | 4 +- app/containers/markdown/new/Image.tsx | 4 +- app/containers/markdown/new/Inline.tsx | 2 +- app/containers/markdown/new/InlineCode.tsx | 8 +- app/containers/markdown/new/Italic.tsx | 2 +- app/containers/markdown/new/Link.tsx | 4 +- app/containers/markdown/new/OrderedList.tsx | 4 +- app/containers/markdown/new/Paragraph.tsx | 4 +- app/containers/markdown/new/Plain.tsx | 4 +- app/containers/markdown/new/Quote.tsx | 4 +- app/containers/markdown/new/Strike.tsx | 2 +- app/containers/markdown/new/TaskList.tsx | 4 +- app/containers/markdown/new/UnorderedList.tsx | 4 +- app/containers/markdown/new/index.tsx | 2 +- app/containers/markdown/styles.ts | 2 +- app/containers/message/Attachments.tsx | 81 +- app/containers/message/Audio.tsx | 51 +- .../CollapsibleQuote.stories.js | 34 + .../CollapsibleQuote.test.tsx | 94 +++ .../CollapsibleQuote.stories.storyshot | 3 + .../Components/CollapsibleQuote/index.tsx | 185 +++++ app/containers/message/Content.tsx | 4 +- app/containers/message/Image.tsx | 35 +- app/containers/message/Message.tsx | 22 +- app/containers/message/Reply.tsx | 66 +- app/containers/message/User.tsx | 21 +- app/containers/message/Video.tsx | 28 +- app/containers/message/index.tsx | 60 +- app/containers/message/interfaces.ts | 49 +- app/containers/message/styles.ts | 5 +- app/containers/message/utils.ts | 9 +- app/definitions/IAttachment.ts | 39 +- app/definitions/ICredentials.ts | 21 + app/definitions/IDDPMessage.ts | 7 + app/definitions/IEmoji.ts | 8 +- app/definitions/ILivechatEditView.ts | 40 + app/definitions/ILivechatVisitor.ts | 2 + app/definitions/ILoggedUser.ts | 22 +- app/definitions/IMessage.ts | 121 ++- .../IProfileViewInterfaces.ts} | 18 +- app/definitions/IRocketChat.ts | 12 +- app/definitions/IRocketChatRecord.ts | 2 +- app/definitions/IRole.ts | 11 + app/definitions/IRoom.ts | 157 +++- app/definitions/ISearch.ts | 20 + app/definitions/IServedBy.ts | 4 +- app/definitions/IServer.ts | 4 +- app/definitions/ISlashCommand.ts | 7 + app/definitions/ISpotlight.ts | 11 + app/definitions/ISubscription.ts | 113 ++- app/definitions/ITagsOmnichannel.ts | 5 - app/definitions/ITeam.ts | 10 + app/definitions/IThread.ts | 46 +- app/definitions/IThreadMessage.ts | 41 +- app/definitions/IUrl.ts | 16 +- app/definitions/IUser.ts | 34 +- app/definitions/IVisitor.ts | 24 - app/definitions/TUserStatus.ts | 3 + app/definitions/UserStatus.ts | 6 - app/definitions/index.ts | 4 + app/definitions/redux/index.ts | 11 +- app/definitions/rest/v1/channels.ts | 113 ++- app/definitions/rest/v1/chat.ts | 61 +- app/definitions/rest/v1/commands.ts | 23 + app/definitions/rest/v1/directory.ts | 13 + app/definitions/rest/v1/dm.ts | 4 +- app/definitions/rest/v1/e2e.ts | 21 + app/definitions/rest/v1/groups.ts | 89 +- app/definitions/rest/v1/im.ts | 57 +- app/definitions/rest/v1/index.ts | 16 +- app/definitions/rest/v1/invites.ts | 12 +- app/definitions/rest/v1/omnichannel.ts | 28 +- app/definitions/rest/v1/pushToken.ts | 12 + app/definitions/rest/v1/rooms.ts | 19 +- app/definitions/rest/v1/subscriptions.ts | 8 + app/definitions/rest/v1/teams.ts | 38 +- app/definitions/rest/v1/user.ts | 14 - app/definitions/rest/v1/users.ts | 44 + app/definitions/rest/v1/videoConference.ts | 5 + app/dimensions.tsx | 12 +- app/ee/omnichannel/actions/inquiry.js | 55 -- app/ee/omnichannel/actions/inquiry.ts | 84 ++ ...channelStatus.js => OmnichannelStatus.tsx} | 31 +- app/ee/omnichannel/lib/{index.js => index.ts} | 15 +- .../subscriptions/{inquiry.js => inquiry.ts} | 41 +- app/ee/omnichannel/reducers/inquiry.test.ts | 98 +++ .../reducers/{inquiry.js => inquiry.ts} | 11 +- .../selectors/{inquiry.js => inquiry.ts} | 4 +- .../{QueueListView.js => QueueListView.tsx} | 78 +- app/i18n/locales/ar.json | 8 +- app/i18n/locales/de.json | 409 +++++---- app/i18n/locales/en.json | 4 +- app/i18n/locales/fr.json | 23 +- app/i18n/locales/ja.json | 87 ++ app/i18n/locales/nl.json | 21 +- app/i18n/locales/pt-BR.json | 4 +- app/i18n/locales/ru.json | 27 +- app/i18n/locales/zh-TW.json | 1 + app/index.tsx | 21 +- app/lib/database/index.ts | 2 +- app/lib/database/model/Thread.js | 2 + app/lib/database/model/migrations.js | 9 + app/lib/database/schema/app.js | 5 +- app/lib/database/services/Message.ts | 5 +- app/lib/encryption/constants.ts | 2 +- app/lib/encryption/encryption.ts | 21 +- app/lib/encryption/room.ts | 4 +- app/lib/methods/{actions.js => actions.ts} | 78 +- .../{canOpenRoom.js => canOpenRoom.ts} | 34 +- ...erpriseModules.js => enterpriseModules.ts} | 13 +- app/lib/methods/getRoomInfo.ts | 15 +- app/lib/methods/getSingleMessage.ts | 3 +- .../{getThreadName.js => getThreadName.ts} | 15 +- app/lib/methods/getUsersPresence.ts | 2 +- app/lib/methods/helpers/buildMessage.ts | 4 +- ...ionsRooms.js => findSubscriptionsRooms.ts} | 23 +- .../helpers/mergeSubscriptionsRooms.js | 116 --- .../helpers/mergeSubscriptionsRooms.ts | 126 +++ ...ormalizeMessage.js => normalizeMessage.ts} | 16 +- app/lib/methods/loadMessagesForRoom.ts | 39 +- app/lib/methods/loadMissedMessages.ts | 4 +- app/lib/methods/loadNextMessages.ts | 16 +- ...Messages.js => loadSurroundingMessages.ts} | 28 +- app/lib/methods/loadThreadMessages.js | 76 -- app/lib/methods/loadThreadMessages.ts | 87 ++ app/lib/methods/logout.ts | 37 +- app/lib/methods/readMessages.ts | 7 +- app/lib/methods/sendFileMessage.ts | 4 +- .../{sendMessage.js => sendMessage.ts} | 84 +- .../subscriptions/{room.js => room.ts} | 94 ++- .../subscriptions/{rooms.js => rooms.ts} | 116 +-- app/lib/methods/updateMessages.ts | 34 +- .../rocketchat/methods/roomTypeToApiType.ts | 34 +- app/lib/rocketchat/methods/search.ts | 98 +++ app/lib/rocketchat/rocketchat.js | 783 +----------------- app/lib/rocketchat/services/connect.ts | 510 ++++++++++++ app/lib/rocketchat/services/restApi.ts | 544 +++++++----- app/lib/rocketchat/services/sdk.ts | 37 +- app/lib/rocketchat/services/shareExtension.ts | 92 ++ app/lib/userPreferences.ts | 33 +- app/lib/utils.ts | 6 +- app/presentation/RoomItem/RoomItem.tsx | 3 +- app/presentation/RoomItem/TypeIcon.tsx | 3 +- app/presentation/RoomItem/Wrapper.tsx | 3 +- app/presentation/RoomItem/index.tsx | 7 +- app/presentation/RoomItem/styles.ts | 5 - app/presentation/ServerItem/index.tsx | 2 +- app/presentation/TextInput.tsx | 2 +- app/reducers/activeUsers.test.ts | 3 +- app/reducers/activeUsers.ts | 5 +- app/reducers/enterpriseModules.ts | 2 +- app/reducers/login.test.ts | 11 +- app/reducers/login.ts | 12 +- app/reducers/settings.test.ts | 8 +- app/reducers/settings.ts | 12 +- app/reducers/share.ts | 16 +- app/sagas/deepLinking.js | 4 +- app/sagas/encryption.js | 8 +- app/sagas/init.js | 24 +- app/sagas/login.js | 24 +- app/sagas/rooms.js | 73 +- app/sagas/selectServer.js | 22 +- app/selectors/login.ts | 2 +- app/share.tsx | 22 +- app/stacks/MasterDetailStack/types.ts | 10 +- app/stacks/types.ts | 59 +- app/utils/debounce.ts | 2 +- app/utils/events.ts | 5 +- app/utils/fetch.ts | 16 +- app/utils/fileUpload/index.ts | 3 +- app/utils/goRoom.ts | 31 +- app/utils/isReadOnly.ts | 13 +- app/utils/localAuthentication.ts | 8 +- app/utils/openLink.ts | 2 +- app/utils/room.ts | 10 +- app/utils/sslPinning.ts | 14 +- app/utils/theme.ts | 11 + app/views/AddChannelTeamView.tsx | 4 - app/views/AttachmentView.tsx | 2 +- app/views/AuthenticationWebView.tsx | 4 +- app/views/AutoTranslateView/index.tsx | 4 +- app/views/CannedResponseDetail.tsx | 2 +- app/views/CannedResponsesListView/index.tsx | 4 +- app/views/ChangePasscodeView.tsx | 2 +- .../CreateDiscussionView/SelectChannel.tsx | 16 +- app/views/CreateDiscussionView/index.tsx | 1 - app/views/DefaultBrowserView.tsx | 8 +- app/views/DirectoryView/Options.tsx | 2 +- app/views/DirectoryView/index.tsx | 20 +- .../DiscussionsView/DiscussionDetails.tsx | 13 +- app/views/DiscussionsView/Item.tsx | 5 +- app/views/DiscussionsView/index.tsx | 12 +- app/views/DisplayPrefsView.tsx | 44 +- app/views/E2EEncryptionSecurityView.tsx | 2 +- app/views/E2ESaveYourPasswordView.tsx | 8 +- app/views/ForgotPasswordView.tsx | 2 +- app/views/ForwardLivechatView.tsx | 14 +- app/views/JitsiMeetView.tsx | 4 +- app/views/LivechatEditView.tsx | 63 +- app/views/LoginView.tsx | 4 +- app/views/MessagesView/index.tsx | 17 +- app/views/ModalBlockView.tsx | 10 +- app/views/NewMessageView.tsx | 18 +- app/views/NewServerView/index.tsx | 14 +- .../NotificationPreferencesView/index.tsx | 3 +- app/views/ProfileView/index.tsx | 10 +- app/views/ReadReceiptView/index.tsx | 18 +- app/views/RegisterView.tsx | 100 +-- .../RoomActionsView/{index.js => index.tsx} | 289 ++++--- .../RoomActionsView/{styles.js => styles.ts} | 0 ...SwitchContainer.js => SwitchContainer.tsx} | 36 +- .../RoomInfoEditView/{index.js => index.tsx} | 273 +++--- .../RoomInfoEditView/{styles.js => styles.ts} | 0 .../RoomInfoView/{Channel.js => Channel.tsx} | 12 +- app/views/RoomInfoView/CustomFields.js | 23 - app/views/RoomInfoView/CustomFields.tsx | 22 + .../RoomInfoView/{Direct.js => Direct.tsx} | 29 +- app/views/RoomInfoView/{Item.js => Item.tsx} | 25 +- .../{Livechat.js => Livechat.tsx} | 62 +- app/views/RoomInfoView/Timezone.js | 27 - app/views/RoomInfoView/Timezone.tsx | 34 + .../RoomInfoView/{index.js => index.tsx} | 159 ++-- .../RoomInfoView/{styles.js => styles.ts} | 0 .../RoomMembersView/{index.js => index.tsx} | 265 +++--- .../RoomMembersView/{styles.js => styles.ts} | 0 app/views/RoomView/{Banner.js => Banner.tsx} | 23 +- .../RoomView/{EmptyRoom.js => EmptyRoom.tsx} | 12 +- .../RoomView/{JoinCode.js => JoinCode.tsx} | 30 +- app/views/RoomView/LeftButtons.js | 56 -- app/views/RoomView/LeftButtons.tsx | 72 ++ app/views/RoomView/List/{List.js => List.tsx} | 10 +- .../{NavBottomFAB.js => NavBottomFAB.tsx} | 23 +- .../RoomView/List/{index.js => index.tsx} | 126 +-- .../RoomView/LoadMore/LoadMore.stories.js | 16 +- .../__snapshots__/LoadMore.stories.storyshot | 6 +- .../RoomView/LoadMore/{index.js => index.tsx} | 24 +- .../{ReactionPicker.js => ReactionPicker.tsx} | 36 +- .../{RightButtons.js => RightButtons.tsx} | 85 +- .../RoomView/{Separator.js => Separator.tsx} | 11 +- .../{UploadProgress.js => UploadProgress.tsx} | 73 +- app/views/RoomView/{index.js => index.tsx} | 540 +++++++----- app/views/RoomView/services/getMessageInfo.js | 41 - app/views/RoomView/services/getMessageInfo.ts | 41 + app/views/RoomView/services/getMessages.js | 10 - app/views/RoomView/services/getMessages.ts | 23 + .../RoomView/services/getMoreMessages.js | 27 - .../RoomView/services/getMoreMessages.ts | 36 + ...ThreadMessages.js => getThreadMessages.ts} | 2 +- .../RoomView/services/{index.js => index.ts} | 0 app/views/RoomView/services/readMessages.js | 5 - app/views/RoomView/services/readMessages.ts | 5 + app/views/RoomView/{styles.js => styles.ts} | 0 .../Header/{Header.js => Header.tsx} | 44 +- .../Header/{index.js => index.tsx} | 39 +- .../ListHeader/{index.js => index.tsx} | 33 +- .../{ServerDropdown.js => ServerDropdown.tsx} | 52 +- .../RoomsListView/{index.js => index.tsx} | 226 ++--- .../RoomsListView/{styles.js => styles.ts} | 2 +- app/views/ScreenLockConfigView.tsx | 8 +- app/views/ScreenLockedView.tsx | 5 +- app/views/SearchMessagesView/index.tsx | 14 +- app/views/SelectListView.tsx | 12 +- app/views/SelectServerView.tsx | 4 +- app/views/SendEmailConfirmationView.tsx | 2 +- app/views/ShareListView/index.tsx | 2 +- app/views/ShareView/index.tsx | 8 +- app/views/SidebarView/index.tsx | 3 +- app/views/StatusView.tsx | 24 +- app/views/TeamChannelsView.tsx | 24 +- app/views/ThemeView.tsx | 8 +- app/views/ThreadMessagesView/Item.tsx | 6 +- app/views/ThreadMessagesView/index.tsx | 20 +- .../UserNotificationPreferencesView/index.tsx | 28 +- app/views/WorkspaceView/index.tsx | 2 +- app/views/definition/ILivechatDepartment.ts | 15 - e2e/README.md | 2 +- e2e/data.js | 5 +- e2e/data/data.cloud.js | 5 +- e2e/e2e_account.example.js | 6 + e2e/tests/room/04-discussion.spec.js | 3 - e2e/tests/team/03-moveconvert.spec.js | 3 + ios/Podfile | 1 + ios/Podfile.lock | 22 +- ios/RocketChatRN.xcodeproj/project.pbxproj | 4 +- ios/RocketChatRN/Info.plist | 2 +- ios/ShareRocketChatRN/Info.plist | 2 +- ios/Shared/RocketChat/Storage.swift | 18 +- package.json | 19 +- patches/@types+ejson+2.1.3.patch | 13 + patches/react-native-mmkv-storage+0.3.5.patch | 157 ---- .../react-native-mmkv-storage+0.6.12.patch | 44 + patches/react-native-webview+10.3.2.patch | 26 +- patches/rn-fetch-blob+0.12.0.patch | 43 +- storybook/stories/Message.js | 18 + storybook/stories/ServerItem.js | 25 +- .../stories/__snapshots__/List.storyshot | 2 +- .../stories/__snapshots__/Message.storyshot | 74 +- .../stories/__snapshots__/RoomItem.storyshot | 22 +- .../__snapshots__/ServerItem.storyshot | 4 +- storybook/stories/index.js | 1 + yarn.lock | 116 ++- 400 files changed, 7918 insertions(+), 5349 deletions(-) create mode 100644 app/containers/MessageBox/getMentionRegexp.test.js create mode 100644 app/containers/MessageBox/getMentionRegexp.ts create mode 100644 app/containers/Status/definition.ts create mode 100644 app/containers/UIKit/interfaces.ts create mode 100644 app/containers/message/Components/CollapsibleQuote/CollapsibleQuote.stories.js create mode 100644 app/containers/message/Components/CollapsibleQuote/CollapsibleQuote.test.tsx create mode 100644 app/containers/message/Components/CollapsibleQuote/__snapshots__/CollapsibleQuote.stories.storyshot create mode 100644 app/containers/message/Components/CollapsibleQuote/index.tsx create mode 100644 app/definitions/ICredentials.ts create mode 100644 app/definitions/IDDPMessage.ts create mode 100644 app/definitions/ILivechatEditView.ts rename app/{views/ProfileView/interfaces.ts => definitions/IProfileViewInterfaces.ts} (86%) create mode 100644 app/definitions/ISearch.ts create mode 100644 app/definitions/ISpotlight.ts delete mode 100644 app/definitions/ITagsOmnichannel.ts delete mode 100644 app/definitions/IVisitor.ts create mode 100644 app/definitions/TUserStatus.ts delete mode 100644 app/definitions/UserStatus.ts create mode 100644 app/definitions/rest/v1/commands.ts create mode 100644 app/definitions/rest/v1/directory.ts create mode 100644 app/definitions/rest/v1/e2e.ts create mode 100644 app/definitions/rest/v1/pushToken.ts create mode 100644 app/definitions/rest/v1/subscriptions.ts delete mode 100644 app/definitions/rest/v1/user.ts create mode 100644 app/definitions/rest/v1/videoConference.ts delete mode 100644 app/ee/omnichannel/actions/inquiry.js create mode 100644 app/ee/omnichannel/actions/inquiry.ts rename app/ee/omnichannel/containers/{OmnichannelStatus.js => OmnichannelStatus.tsx} (68%) rename app/ee/omnichannel/lib/{index.js => index.ts} (57%) rename app/ee/omnichannel/lib/subscriptions/{inquiry.js => inquiry.ts} (64%) create mode 100644 app/ee/omnichannel/reducers/inquiry.test.ts rename app/ee/omnichannel/reducers/{inquiry.js => inquiry.ts} (74%) rename app/ee/omnichannel/selectors/{inquiry.js => inquiry.ts} (50%) rename app/ee/omnichannel/views/{QueueListView.js => QueueListView.tsx} (63%) rename app/lib/methods/{actions.js => actions.ts} (51%) rename app/lib/methods/{canOpenRoom.js => canOpenRoom.ts} (65%) rename app/lib/methods/{enterpriseModules.js => enterpriseModules.ts} (82%) rename app/lib/methods/{getThreadName.js => getThreadName.ts} (68%) rename app/lib/methods/helpers/{findSubscriptionsRooms.js => findSubscriptionsRooms.ts} (75%) delete mode 100644 app/lib/methods/helpers/mergeSubscriptionsRooms.js create mode 100644 app/lib/methods/helpers/mergeSubscriptionsRooms.ts rename app/lib/methods/helpers/{normalizeMessage.js => normalizeMessage.ts} (77%) rename app/lib/methods/{loadSurroundingMessages.js => loadSurroundingMessages.ts} (67%) delete mode 100644 app/lib/methods/loadThreadMessages.js create mode 100644 app/lib/methods/loadThreadMessages.ts rename app/lib/methods/{sendMessage.js => sendMessage.ts} (73%) rename app/lib/methods/subscriptions/{room.js => room.ts} (76%) rename app/lib/methods/subscriptions/{rooms.js => rooms.ts} (75%) create mode 100644 app/lib/rocketchat/methods/search.ts create mode 100644 app/lib/rocketchat/services/connect.ts create mode 100644 app/lib/rocketchat/services/shareExtension.ts rename app/views/RoomActionsView/{index.js => index.tsx} (84%) rename app/views/RoomActionsView/{styles.js => styles.ts} (100%) rename app/views/RoomInfoEditView/{SwitchContainer.js => SwitchContainer.tsx} (73%) rename app/views/RoomInfoEditView/{index.js => index.tsx} (76%) rename app/views/RoomInfoEditView/{styles.js => styles.ts} (100%) rename app/views/RoomInfoView/{Channel.js => Channel.tsx} (79%) delete mode 100644 app/views/RoomInfoView/CustomFields.js create mode 100644 app/views/RoomInfoView/CustomFields.tsx rename app/views/RoomInfoView/{Direct.js => Direct.tsx} (60%) rename app/views/RoomInfoView/{Item.js => Item.tsx} (67%) rename app/views/RoomInfoView/{Livechat.js => Livechat.tsx} (55%) delete mode 100644 app/views/RoomInfoView/Timezone.js create mode 100644 app/views/RoomInfoView/Timezone.tsx rename app/views/RoomInfoView/{index.js => index.tsx} (70%) rename app/views/RoomInfoView/{styles.js => styles.ts} (100%) rename app/views/RoomMembersView/{index.js => index.tsx} (74%) rename app/views/RoomMembersView/{styles.js => styles.ts} (100%) rename app/views/RoomView/{Banner.js => Banner.tsx} (79%) rename app/views/RoomView/{EmptyRoom.js => EmptyRoom.tsx} (61%) rename app/views/RoomView/{JoinCode.js => JoinCode.tsx} (78%) delete mode 100644 app/views/RoomView/LeftButtons.js create mode 100644 app/views/RoomView/LeftButtons.tsx rename app/views/RoomView/List/{List.js => List.tsx} (77%) rename app/views/RoomView/List/{NavBottomFAB.js => NavBottomFAB.tsx} (82%) rename app/views/RoomView/List/{index.js => index.tsx} (76%) rename app/views/RoomView/LoadMore/{index.js => index.tsx} (75%) rename app/views/RoomView/{ReactionPicker.js => ReactionPicker.tsx} (75%) rename app/views/RoomView/{RightButtons.js => RightButtons.tsx} (69%) rename app/views/RoomView/{Separator.js => Separator.tsx} (88%) rename app/views/RoomView/{UploadProgress.js => UploadProgress.tsx} (69%) rename app/views/RoomView/{index.js => index.tsx} (68%) delete mode 100644 app/views/RoomView/services/getMessageInfo.js create mode 100644 app/views/RoomView/services/getMessageInfo.ts delete mode 100644 app/views/RoomView/services/getMessages.js create mode 100644 app/views/RoomView/services/getMessages.ts delete mode 100644 app/views/RoomView/services/getMoreMessages.js create mode 100644 app/views/RoomView/services/getMoreMessages.ts rename app/views/RoomView/services/{getThreadMessages.js => getThreadMessages.ts} (61%) rename app/views/RoomView/services/{index.js => index.ts} (100%) delete mode 100644 app/views/RoomView/services/readMessages.js create mode 100644 app/views/RoomView/services/readMessages.ts rename app/views/RoomView/{styles.js => styles.ts} (100%) rename app/views/RoomsListView/Header/{Header.js => Header.tsx} (75%) rename app/views/RoomsListView/Header/{index.js => index.tsx} (70%) rename app/views/RoomsListView/ListHeader/{index.js => index.tsx} (72%) rename app/views/RoomsListView/{ServerDropdown.js => ServerDropdown.tsx} (83%) rename app/views/RoomsListView/{index.js => index.tsx} (82%) rename app/views/RoomsListView/{styles.js => styles.ts} (96%) delete mode 100644 app/views/definition/ILivechatDepartment.ts create mode 100644 e2e/e2e_account.example.js create mode 100644 patches/@types+ejson+2.1.3.patch delete mode 100644 patches/react-native-mmkv-storage+0.3.5.patch create mode 100644 patches/react-native-mmkv-storage+0.6.12.patch diff --git a/.gitignore b/.gitignore index 4ffd99aa9..efb752943 100644 --- a/.gitignore +++ b/.gitignore @@ -64,5 +64,6 @@ artifacts .vscode/ e2e/docker/rc_test_env/docker-compose.yml e2e/docker/data/db +e2e/e2e_account.js *.p8 \ No newline at end of file diff --git a/__tests__/Storyshots.test.js b/__tests__/Storyshots.test.js index 1315f9bb5..638fbb0ee 100644 --- a/__tests__/Storyshots.test.js +++ b/__tests__/Storyshots.test.js @@ -20,6 +20,26 @@ jest.mock('react-native-file-viewer', () => ({ jest.mock('../app/lib/database', () => jest.fn(() => null)); global.Date.now = jest.fn(() => new Date('2019-10-10').getTime()); +jest.mock('react-native-mmkv-storage', () => { + return { + Loader: jest.fn().mockImplementation(() => { + return { + setProcessingMode: jest.fn().mockImplementation(() => { + return { + withEncryption: jest.fn().mockImplementation(() => { + return { + initialize: jest.fn() + }; + }) + }; + }) + }; + }), + create: jest.fn(), + MODES: { MULTI_PROCESS: '' } + }; +}); + const converter = new Stories2SnapsConverter(); initStoryshots({ diff --git a/android/app/build.gradle b/android/app/build.gradle index 5e7aaee86..f7e9a5f4e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -144,7 +144,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode VERSIONCODE as Integer - versionName "4.25.0" + versionName "4.26.1" vectorDrawables.useSupportLibrary = true if (!isFoss) { manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java index bf7dc5ef7..c0361e78d 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java +++ b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java @@ -12,7 +12,6 @@ import com.facebook.soloader.SoLoader; import com.reactnativecommunity.viewpager.RNCViewPagerPackage; import com.facebook.react.bridge.JSIModulePackage; import com.swmansion.reanimated.ReanimatedJSIModulePackage; - import org.unimodules.adapters.react.ModuleRegistryAdapter; import org.unimodules.adapters.react.ReactModuleRegistryProvider; @@ -54,7 +53,7 @@ public class MainApplication extends Application implements ReactApplication { @Override protected JSIModulePackage getJSIModulePackage() { - return new ReanimatedJSIModulePackage(); // <- add + return new ReanimatedJSIModulePackage(); } @Override diff --git a/android/app/src/play/java/chat/rocket/reactnative/Ejson.java b/android/app/src/play/java/chat/rocket/reactnative/Ejson.java index e44c6b722..fb083df13 100644 --- a/android/app/src/play/java/chat/rocket/reactnative/Ejson.java +++ b/android/app/src/play/java/chat/rocket/reactnative/Ejson.java @@ -53,16 +53,8 @@ public class Ejson { String alias = Utils.toHex("com.MMKV.default"); // Retrieve container password - secureKeystore.getSecureKey(alias, new RNCallback() { - @Override - public void invoke(Object... args) { - String error = (String) args[0]; - if (error == null) { - String password = (String) args[1]; - mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password); - } - } - }); + String password = secureKeystore.getSecureKey(alias); + mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password); } public String getAvatarUri() { diff --git a/app/actions/actionsTypes.ts b/app/actions/actionsTypes.ts index 1367b2251..a920e27e2 100644 --- a/app/actions/actionsTypes.ts +++ b/app/actions/actionsTypes.ts @@ -11,7 +11,7 @@ function createRequestTypes(base = {}, types = defaultTypes): Record): ISetUser { }; } +export function clearUser(): Action { + return { + type: types.USER.CLEAR + }; +} + export function setLoginServices(data: Record): ISetServices { return { type: types.LOGIN.SET_SERVICES, diff --git a/app/actions/room.ts b/app/actions/room.ts index 9c817f565..f0d5ed81b 100644 --- a/app/actions/room.ts +++ b/app/actions/room.ts @@ -4,7 +4,7 @@ import { ERoomType } from '../definitions/ERoomType'; import { ROOM } from './actionsTypes'; // TYPE RETURN RELATED -type ISelected = Record; +type ISelected = string[]; export interface ITransferData { roomId: string; diff --git a/app/actions/settings.ts b/app/actions/settings.ts index 77b8dcc77..8db868889 100644 --- a/app/actions/settings.ts +++ b/app/actions/settings.ts @@ -1,26 +1,26 @@ import { Action } from 'redux'; -import { ISettings, TSettings } from '../reducers/settings'; +import { TSettingsState, TSupportedSettings, TSettingsValues } from '../reducers/settings'; import { SETTINGS } from './actionsTypes'; interface IAddSettings extends Action { - payload: ISettings; + payload: TSettingsState; } interface IUpdateSettings extends Action { - payload: { id: string; value: TSettings }; + payload: { id: TSupportedSettings; value: TSettingsValues }; } export type IActionSettings = IAddSettings & IUpdateSettings; -export function addSettings(settings: ISettings): IAddSettings { +export function addSettings(settings: TSettingsState): IAddSettings { return { type: SETTINGS.ADD, payload: settings }; } -export function updateSettings(id: string, value: TSettings): IUpdateSettings { +export function updateSettings(id: TSupportedSettings, value: TSettingsValues): IUpdateSettings { return { type: SETTINGS.UPDATE, payload: { id, value } diff --git a/app/commands.ts b/app/commands.ts index b1aee847c..0624ac3db 100644 --- a/app/commands.ts +++ b/app/commands.ts @@ -143,8 +143,8 @@ export const deleteKeyCommands = (): void => KeyCommands.deleteKeyCommands(keyCo export const KEY_COMMAND = 'KEY_COMMAND'; -interface IKeyCommandEvent extends NativeSyntheticEvent { - input: string; +export interface IKeyCommandEvent extends NativeSyntheticEvent { + input: number & string; modifierFlags: string | number; } diff --git a/app/constants/colors.ts b/app/constants/colors.ts index b28416b86..3920441a2 100644 --- a/app/constants/colors.ts +++ b/app/constants/colors.ts @@ -37,6 +37,7 @@ export const themes: any = { infoText: '#6d6d72', tintColor: '#1d74f5', tintActive: '#549df9', + tintDisabled: '#88B4F5', auxiliaryTintColor: '#6C727A', actionTintColor: '#1d74f5', separatorColor: '#cbcbcc', @@ -66,6 +67,8 @@ export const themes: any = { previewTintColor: '#ffffff', backdropOpacity: 0.3, attachmentLoadingOpacity: 0.7, + collapsibleQuoteBorder: '#CBCED1', + collapsibleChevron: '#6C727A', ...mentions }, dark: { @@ -85,6 +88,7 @@ export const themes: any = { infoText: '#6D6D72', tintColor: '#1d74f5', tintActive: '#549df9', + tintDisabled: '#88B4F5', auxiliaryTintColor: '#f9f9f9', actionTintColor: '#1d74f5', separatorColor: '#2b2b2d', @@ -114,6 +118,8 @@ export const themes: any = { previewTintColor: '#ffffff', backdropOpacity: 0.9, attachmentLoadingOpacity: 0.3, + collapsibleQuoteBorder: '#CBCED1', + collapsibleChevron: '#6C727A', ...mentions }, black: { @@ -133,6 +139,7 @@ export const themes: any = { infoText: '#6d6d72', tintColor: '#1e9bfe', tintActive: '#76b7fc', + tintDisabled: '#88B4F5', // TODO: Evaluate this with design team auxiliaryTintColor: '#f9f9f9', actionTintColor: '#1e9bfe', separatorColor: '#272728', @@ -162,6 +169,8 @@ export const themes: any = { previewTintColor: '#ffffff', backdropOpacity: 0.9, attachmentLoadingOpacity: 0.3, + collapsibleQuoteBorder: '#CBCED1', + collapsibleChevron: '#6C727A', ...mentions } }; diff --git a/app/constants/messageTypeLoad.ts b/app/constants/messageTypeLoad.ts index 4bdaa54de..6836981aa 100644 --- a/app/constants/messageTypeLoad.ts +++ b/app/constants/messageTypeLoad.ts @@ -1,5 +1,7 @@ -export const MESSAGE_TYPE_LOAD_MORE = 'load_more'; -export const MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK = 'load_previous_chunk'; -export const MESSAGE_TYPE_LOAD_NEXT_CHUNK = 'load_next_chunk'; +export enum MessageTypeLoad { + MORE = 'load_more', + PREVIOUS_CHUNK = 'load_previous_chunk', + NEXT_CHUNK = 'load_next_chunk' +} -export const MESSAGE_TYPE_ANY_LOAD = [MESSAGE_TYPE_LOAD_MORE, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK, MESSAGE_TYPE_LOAD_NEXT_CHUNK]; +export const MESSAGE_TYPE_ANY_LOAD = [MessageTypeLoad.MORE, MessageTypeLoad.PREVIOUS_CHUNK, MessageTypeLoad.NEXT_CHUNK]; diff --git a/app/constants/settings.ts b/app/constants/settings.ts index fb9c7e6b5..b84779427 100644 --- a/app/constants/settings.ts +++ b/app/constants/settings.ts @@ -206,4 +206,4 @@ export default { Canned_Responses_Enable: { type: 'valueAsBoolean' } -}; +} as const; diff --git a/app/containers/ActionSheet/ActionSheet.tsx b/app/containers/ActionSheet/ActionSheet.tsx index 11414150c..79303a593 100644 --- a/app/containers/ActionSheet/ActionSheet.tsx +++ b/app/containers/ActionSheet/ActionSheet.tsx @@ -1,30 +1,29 @@ +import { useBackHandler } from '@react-native-community/hooks'; +import * as Haptics from 'expo-haptics'; import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState } from 'react'; import { Keyboard, Text } from 'react-native'; +import { HandlerStateChangeEventPayload, State, TapGestureHandler } from 'react-native-gesture-handler'; +import Animated, { Easing, Extrapolate, interpolateNode, Value } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { State, TapGestureHandler } from 'react-native-gesture-handler'; import ScrollBottomSheet from 'react-native-scroll-bottom-sheet'; -import Animated, { Easing, Extrapolate, Value, interpolateNode } from 'react-native-reanimated'; -import * as Haptics from 'expo-haptics'; -import { useBackHandler } from '@react-native-community/hooks'; -import { Item } from './Item'; -import { Handle } from './Handle'; -import { Button } from './Button'; import { themes } from '../../constants/colors'; -import styles, { ITEM_HEIGHT } from './styles'; +import { useDimensions, useOrientation } from '../../dimensions'; +import I18n from '../../i18n'; +import { useTheme } from '../../theme'; import { isIOS, isTablet } from '../../utils/deviceInfo'; import * as List from '../List'; -import I18n from '../../i18n'; -import { IDimensionsContextProps, useDimensions, useOrientation } from '../../dimensions'; +import { Button } from './Button'; +import { Handle } from './Handle'; +import { IActionSheetItem, Item } from './Item'; +import { TActionSheetOptions, TActionSheetOptionsItem } from './Provider'; +import styles, { ITEM_HEIGHT } from './styles'; -interface IActionSheetData { - options: any; - headerHeight?: number; - hasCancel?: boolean; - customHeader: any; -} - -const getItemLayout = (data: any, index: number) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index }); +const getItemLayout = (data: TActionSheetOptionsItem[] | null | undefined, index: number) => ({ + length: ITEM_HEIGHT, + offset: ITEM_HEIGHT * index, + index +}); const HANDLE_HEIGHT = isIOS ? 40 : 56; const MAX_SNAP_HEIGHT = 16; @@ -39,16 +38,17 @@ const ANIMATION_CONFIG = { }; const ActionSheet = React.memo( - forwardRef(({ children, theme }: { children: JSX.Element; theme: string }, ref) => { - const bottomSheetRef: any = useRef(); - const [data, setData] = useState({} as IActionSheetData); + forwardRef(({ children }: { children: React.ReactElement }, ref) => { + const { theme } = useTheme(); + const bottomSheetRef = useRef>(null); + const [data, setData] = useState({} as TActionSheetOptions); const [isVisible, setVisible] = useState(false); - const { height }: Partial = useDimensions(); + const { height } = useDimensions(); const { isLandscape } = useOrientation(); const insets = useSafeAreaInsets(); const maxSnap = Math.max( - height! - + height - // Items height ITEM_HEIGHT * (data?.options?.length || 0) - // Handle height @@ -69,7 +69,7 @@ const ActionSheet = React.memo( * we'll provide more one snap * that point 50% of the whole screen */ - const snaps: any = height! - maxSnap > height! * 0.6 && !isLandscape ? [maxSnap, height! * 0.5, height] : [maxSnap, height]; + const snaps = height - maxSnap > height * 0.6 && !isLandscape ? [maxSnap, height * 0.5, height] : [maxSnap, height]; const openedSnapIndex = snaps.length > 2 ? 1 : 0; const closedSnapIndex = snaps.length - 1; @@ -79,12 +79,12 @@ const ActionSheet = React.memo( bottomSheetRef.current?.snapTo(closedSnapIndex); }; - const show = (options: any) => { + const show = (options: TActionSheetOptions) => { setData(options); toggleVisible(); }; - const onBackdropPressed = ({ nativeEvent }: any) => { + const onBackdropPressed = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => { if (nativeEvent.oldState === State.ACTIVE) { hide(); } @@ -117,7 +117,7 @@ const ActionSheet = React.memo( const renderHandle = () => ( <> - + {isValidElement(data?.customHeader) ? data.customHeader : null} ); @@ -127,21 +127,23 @@ const ActionSheet = React.memo( ) : null; - const renderItem = ({ item }: any) => ; + const renderItem = ({ item }: { item: IActionSheetItem['item'] }) => ; const animatedPosition = React.useRef(new Value(0)); - // TODO: Similar to https://github.com/wcandillon/react-native-redash/issues/307#issuecomment-827442320 const opacity = interpolateNode(animatedPosition.current, { inputRange: [0, 1], outputRange: [0, themes[theme].backdropOpacity], extrapolate: Extrapolate.CLAMP - }) as any; + }) as any; // The function's return differs from the expected type of opacity, however this problem is something related to lib, maybe when updating the types will be fixed. + + const bottomSheet = isLandscape || isTablet ? styles.bottomSheet : {}; return ( <> @@ -160,7 +162,7 @@ const ActionSheet = React.memo( ]} /> - testID='action-sheet' ref={bottomSheetRef} componentType='FlatList' @@ -169,18 +171,11 @@ const ActionSheet = React.memo( renderHandle={renderHandle} onSettle={index => index === closedSnapIndex && toggleVisible()} animatedPosition={animatedPosition.current} - containerStyle={ - [ - styles.container, - { backgroundColor: themes[theme].focusedBackground }, - (isLandscape || isTablet) && styles.bottomSheet - ] as any - } + containerStyle={{ ...styles.container, ...bottomSheet, backgroundColor: themes[theme].focusedBackground }} animationConfig={ANIMATION_CONFIG} - // FlatList props - data={data?.options} + data={data.options} renderItem={renderItem} - keyExtractor={(item: any) => item.title} + keyExtractor={item => item.title} style={{ backgroundColor: themes[theme].focusedBackground }} contentContainerStyle={styles.content} ItemSeparatorComponent={List.Separator} diff --git a/app/containers/ActionSheet/Handle.tsx b/app/containers/ActionSheet/Handle.tsx index d95262d1e..1b2b6a62c 100644 --- a/app/containers/ActionSheet/Handle.tsx +++ b/app/containers/ActionSheet/Handle.tsx @@ -3,9 +3,13 @@ import { View } from 'react-native'; import styles from './styles'; import { themes } from '../../constants/colors'; +import { useTheme } from '../../theme'; -export const Handle = React.memo(({ theme }: { theme: string }) => ( - - - -)); +export const Handle = React.memo(() => { + const { theme } = useTheme(); + return ( + + + + ); +}); diff --git a/app/containers/ActionSheet/Item.tsx b/app/containers/ActionSheet/Item.tsx index 47a3de5a7..9c281f8c1 100644 --- a/app/containers/ActionSheet/Item.tsx +++ b/app/containers/ActionSheet/Item.tsx @@ -3,23 +3,24 @@ import { Text, View } from 'react-native'; import { themes } from '../../constants/colors'; import { CustomIcon } from '../../lib/Icons'; +import { useTheme } from '../../theme'; import { Button } from './Button'; import styles from './styles'; -interface IActionSheetItem { +export interface IActionSheetItem { item: { title: string; icon: string; - danger: boolean; - testID: string; - onPress(): void; - right: Function; + danger?: boolean; + testID?: string; + onPress: () => void; + right?: Function; }; - theme: string; hide(): void; } -export const Item = React.memo(({ item, hide, theme }: IActionSheetItem) => { +export const Item = React.memo(({ item, hide }: IActionSheetItem) => { + const { theme } = useTheme(); const onPress = () => { hide(); item?.onPress(); diff --git a/app/containers/ActionSheet/Provider.tsx b/app/containers/ActionSheet/Provider.tsx index 8e786b05f..112242263 100644 --- a/app/containers/ActionSheet/Provider.tsx +++ b/app/containers/ActionSheet/Provider.tsx @@ -1,14 +1,21 @@ import React, { ForwardedRef, forwardRef, useContext, useRef } from 'react'; import ActionSheet from './ActionSheet'; -import { useTheme } from '../../theme'; +export type TActionSheetOptionsItem = { title: string; icon: string; onPress: () => void }; + +export type TActionSheetOptions = { + options: TActionSheetOptionsItem[]; + headerHeight: number; + customHeader: React.ReactElement | null; + hasCancel?: boolean; +}; interface IActionSheetProvider { - Provider: any; - Consumer: any; + showActionSheet: (item: TActionSheetOptions) => void; + hideActionSheet: () => void; } -const context: IActionSheetProvider = React.createContext({ +const context = React.createContext({ showActionSheet: () => {}, hideActionSheet: () => {} }); @@ -17,17 +24,16 @@ export const useActionSheet = () => useContext(context); const { Provider, Consumer } = context; -export const withActionSheet = (Component: any): any => - forwardRef((props: any, ref: ForwardedRef) => ( - {(contexts: any) => } +export const withActionSheet = (Component: React.ComponentType): typeof Component => + forwardRef((props: typeof React.Component, ref: ForwardedRef) => ( + {(contexts: IActionSheetProvider) => } )); -export const ActionSheetProvider = React.memo(({ children }: { children: JSX.Element | JSX.Element[] }) => { - const ref: ForwardedRef = useRef(); - const { theme }: any = useTheme(); +export const ActionSheetProvider = React.memo(({ children }: { children: React.ReactElement | React.ReactElement[] }) => { + const ref: ForwardedRef = useRef(null); const getContext = () => ({ - showActionSheet: (options: any) => { + showActionSheet: (options: TActionSheetOptions) => { ref.current?.showActionSheet(options); }, hideActionSheet: () => { @@ -37,7 +43,7 @@ export const ActionSheetProvider = React.memo(({ children }: { children: JSX.Ele return ( - + <>{children} diff --git a/app/containers/ActivityIndicator.tsx b/app/containers/ActivityIndicator.tsx index 69ef22ceb..701926416 100644 --- a/app/containers/ActivityIndicator.tsx +++ b/app/containers/ActivityIndicator.tsx @@ -1,14 +1,11 @@ import React from 'react'; import { ActivityIndicator, ActivityIndicatorProps, StyleSheet } from 'react-native'; +import { useTheme } from '../theme'; import { themes } from '../constants/colors'; -type TTheme = 'light' | 'dark' | 'black' | string; - interface IActivityIndicator extends ActivityIndicatorProps { - theme?: TTheme; absolute?: boolean; - props?: object; } const styles = StyleSheet.create({ @@ -27,8 +24,11 @@ const styles = StyleSheet.create({ } }); -const RCActivityIndicator = ({ theme = 'light', absolute, ...props }: IActivityIndicator) => ( - -); +const RCActivityIndicator = ({ absolute, ...props }: IActivityIndicator): React.ReactElement => { + const { theme } = useTheme(); + return ( + + ); +}; export default RCActivityIndicator; diff --git a/app/containers/Avatar/index.tsx b/app/containers/Avatar/index.tsx index 637596410..5b14fe154 100644 --- a/app/containers/Avatar/index.tsx +++ b/app/containers/Avatar/index.tsx @@ -37,6 +37,21 @@ class AvatarContainer extends React.Component { } } + shouldComponentUpdate(nextProps: IAvatar, nextState: { avatarETag: string }) { + const { avatarETag } = this.state; + const { text, type } = this.props; + if (nextState.avatarETag !== avatarETag) { + return true; + } + if (nextProps.text !== text) { + return true; + } + if (nextProps.type !== type) { + return true; + } + return false; + } + componentWillUnmount() { if (this.subscription?.unsubscribe) { this.subscription.unsubscribe(); diff --git a/app/containers/Avatar/interfaces.ts b/app/containers/Avatar/interfaces.ts index ddec5b276..3bc5dd85e 100644 --- a/app/containers/Avatar/interfaces.ts +++ b/app/containers/Avatar/interfaces.ts @@ -1,3 +1,5 @@ +import React from 'react'; + import { TGetCustomEmoji } from '../../definitions/IEmoji'; export interface IAvatar { @@ -9,7 +11,7 @@ export interface IAvatar { size?: number; borderRadius?: number; type?: string; - children?: JSX.Element; + children?: React.ReactElement | null; user?: { id?: string; token?: string; diff --git a/app/containers/BackgroundContainer/index.tsx b/app/containers/BackgroundContainer/index.tsx index a485611c0..c9fc70d57 100644 --- a/app/containers/BackgroundContainer/index.tsx +++ b/app/containers/BackgroundContainer/index.tsx @@ -1,13 +1,12 @@ import React from 'react'; import { ActivityIndicator, ImageBackground, StyleSheet, Text, View } from 'react-native'; -import { withTheme } from '../../theme'; +import { useTheme } from '../../theme'; import sharedStyles from '../../views/Styles'; import { themes } from '../../constants/colors'; interface IBackgroundContainer { text?: string; - theme?: string; loading?: boolean; } @@ -32,12 +31,15 @@ const styles = StyleSheet.create({ } }); -const BackgroundContainer = ({ theme, text, loading }: IBackgroundContainer) => ( - - - {text && !loading ? {text} : null} - {loading ? : null} - -); +const BackgroundContainer = ({ text, loading }: IBackgroundContainer): React.ReactElement => { + const { theme } = useTheme(); + return ( + + + {text && !loading ? {text} : null} + {loading ? : null} + + ); +}; -export default withTheme(BackgroundContainer); +export default BackgroundContainer; diff --git a/app/containers/Check.tsx b/app/containers/Check.tsx index 9ee489dfd..c51bc8b46 100644 --- a/app/containers/Check.tsx +++ b/app/containers/Check.tsx @@ -3,11 +3,8 @@ import { StyleSheet } from 'react-native'; import { CustomIcon } from '../lib/Icons'; import { themes } from '../constants/colors'; +import { useTheme } from '../theme'; -interface ICheck { - style?: object; - theme: string; -} const styles = StyleSheet.create({ icon: { width: 22, @@ -16,8 +13,9 @@ const styles = StyleSheet.create({ } }); -const Check = React.memo(({ theme, style }: ICheck) => ( - -)); +const Check = React.memo(() => { + const { theme } = useTheme(); + return ; +}); export default Check; diff --git a/app/containers/FormContainer.tsx b/app/containers/FormContainer.tsx index ff233952a..8c862b54d 100644 --- a/app/containers/FormContainer.tsx +++ b/app/containers/FormContainer.tsx @@ -5,15 +5,15 @@ import { themes } from '../constants/colors'; import sharedStyles from '../views/Styles'; import scrollPersistTaps from '../utils/scrollPersistTaps'; import KeyboardView from '../presentation/KeyboardView'; +import { useTheme } from '../theme'; import StatusBar from './StatusBar'; import AppVersion from './AppVersion'; import { isTablet } from '../utils/deviceInfo'; import SafeAreaView from './SafeAreaView'; interface IFormContainer extends ScrollViewProps { - theme: string; testID: string; - children: React.ReactNode; + children: React.ReactElement | React.ReactElement[] | null; } const styles = StyleSheet.create({ @@ -22,27 +22,31 @@ const styles = StyleSheet.create({ } }); -export const FormContainerInner = ({ children }: { children: React.ReactNode }): JSX.Element => ( +export const FormContainerInner = ({ children }: { children: (React.ReactElement | null)[] }) => ( {children} ); -const FormContainer = ({ children, theme, testID, ...props }: IFormContainer): JSX.Element => ( - - - - - {children} - - - - -); +const FormContainer = ({ children, testID, ...props }: IFormContainer) => { + const { theme } = useTheme(); + + return ( + + + + + {children} + + + + + ); +}; export default FormContainer; diff --git a/app/containers/Header/index.tsx b/app/containers/Header/index.tsx index e9f7837c6..17d888409 100644 --- a/app/containers/Header/index.tsx +++ b/app/containers/Header/index.tsx @@ -5,12 +5,11 @@ import { StyleSheet, View } from 'react-native'; import { themes } from '../../constants/colors'; import { themedHeader } from '../../utils/navigation'; import { isIOS, isTablet } from '../../utils/deviceInfo'; -import { withTheme } from '../../theme'; +import { useTheme } from '../../theme'; -// Get from https://github.com/react-navigation/react-navigation/blob/master/packages/stack/src/views/Header/HeaderSegment.tsx#L69 export const headerHeight = isIOS ? 44 : 56; -export const getHeaderHeight = (isLandscape: boolean) => { +export const getHeaderHeight = (isLandscape: boolean): number => { if (isIOS) { if (isLandscape && !isTablet) { return 32; @@ -28,7 +27,13 @@ interface IHeaderTitlePosition { numIconsRight: number; } -export const getHeaderTitlePosition = ({ insets, numIconsRight }: IHeaderTitlePosition) => ({ +export const getHeaderTitlePosition = ({ + insets, + numIconsRight +}: IHeaderTitlePosition): { + left: number; + right: number; +} => ({ left: insets.left + 60, right: insets.right + Math.max(45 * numIconsRight, 15) }); @@ -43,20 +48,22 @@ const styles = StyleSheet.create({ }); interface IHeader { - theme: string; - headerLeft(): void; - headerTitle(): void; - headerRight(): void; + headerLeft: () => React.ReactElement | null; + headerTitle: () => React.ReactElement; + headerRight: () => React.ReactElement | null; } -const Header = ({ theme, headerLeft, headerTitle, headerRight }: IHeader) => ( - - - {headerLeft ? headerLeft() : null} - {headerTitle ? headerTitle() : null} - {headerRight ? headerRight() : null} - - -); +const Header = ({ headerLeft, headerTitle, headerRight }: IHeader): React.ReactElement => { + const { theme } = useTheme(); + return ( + + + {headerLeft ? headerLeft() : null} + {headerTitle ? headerTitle() : null} + {headerRight ? headerRight() : null} + + + ); +}; -export default withTheme(Header); +export default Header; diff --git a/app/containers/HeaderButton/Common.tsx b/app/containers/HeaderButton/Common.tsx index 0877fb4c6..50206f836 100644 --- a/app/containers/HeaderButton/Common.tsx +++ b/app/containers/HeaderButton/Common.tsx @@ -6,20 +6,22 @@ import Container from './HeaderButtonContainer'; import Item from './HeaderButtonItem'; interface IHeaderButtonCommon { - navigation: any; - onPress?(): void; + navigation?: any; // TODO: Evaluate proper type + onPress?: () => void; testID?: string; } // Left -export const Drawer = React.memo(({ navigation, testID, ...props }: Partial) => ( - - navigation.toggleDrawer()} testID={testID} {...props} /> - -)); +export const Drawer = React.memo( + ({ navigation, testID, onPress = () => navigation?.toggleDrawer(), ...props }: IHeaderButtonCommon) => ( + + + + ) +); export const CloseModal = React.memo( - ({ navigation, testID, onPress = () => navigation.pop(), ...props }: IHeaderButtonCommon) => ( + ({ navigation, testID, onPress = () => navigation?.pop(), ...props }: IHeaderButtonCommon) => ( @@ -29,9 +31,9 @@ export const CloseModal = React.memo( export const CancelModal = React.memo(({ onPress, testID }: Partial) => ( {isIOS ? ( - + ) : ( - + )} )); @@ -39,22 +41,24 @@ export const CancelModal = React.memo(({ onPress, testID }: Partial) => ( - + )); -export const Download = React.memo(({ onPress, testID, ...props }: Partial) => ( +export const Download = React.memo(({ onPress, testID, ...props }: IHeaderButtonCommon) => ( - + )); -export const Preferences = React.memo(({ onPress, testID, ...props }: Partial) => ( +export const Preferences = React.memo(({ onPress, testID, ...props }: IHeaderButtonCommon) => ( - + )); -export const Legal = React.memo(({ navigation, testID }: Partial) => ( - navigation.navigate('LegalView')} testID={testID} /> -)); +export const Legal = React.memo( + ({ navigation, testID, onPress = () => navigation?.navigate('LegalView') }: IHeaderButtonCommon) => ( + + ) +); diff --git a/app/containers/HeaderButton/HeaderButtonContainer.tsx b/app/containers/HeaderButton/HeaderButtonContainer.tsx index f757d43d7..abf6db9e7 100644 --- a/app/containers/HeaderButton/HeaderButtonContainer.tsx +++ b/app/containers/HeaderButton/HeaderButtonContainer.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { StyleSheet, View } from 'react-native'; interface IHeaderButtonContainer { - children: React.ReactNode; + children?: React.ReactElement | (React.ReactElement | null)[] | null; left?: boolean; } @@ -20,7 +20,7 @@ const styles = StyleSheet.create({ } }); -const Container = ({ children, left = false }: IHeaderButtonContainer) => ( +const Container = ({ children, left = false }: IHeaderButtonContainer): React.ReactElement => ( {children} ); diff --git a/app/containers/HeaderButton/HeaderButtonItem.tsx b/app/containers/HeaderButton/HeaderButtonItem.tsx index 08f6b5a3a..987975db2 100644 --- a/app/containers/HeaderButton/HeaderButtonItem.tsx +++ b/app/containers/HeaderButton/HeaderButtonItem.tsx @@ -3,16 +3,15 @@ import { Platform, StyleSheet, Text } from 'react-native'; import Touchable from 'react-native-platform-touchable'; import { CustomIcon } from '../../lib/Icons'; -import { withTheme } from '../../theme'; +import { useTheme } from '../../theme'; import { themes } from '../../constants/colors'; import sharedStyles from '../../views/Styles'; interface IHeaderButtonItem { title?: string; iconName?: string; - onPress: (arg: T) => void; + onPress?: (arg: T) => void; testID?: string; - theme?: string; badge?(): void; } @@ -40,19 +39,22 @@ const styles = StyleSheet.create({ } }); -const Item = ({ title, iconName, onPress, testID, theme, badge }: IHeaderButtonItem) => ( - - <> - {iconName ? ( - - ) : ( - {title} - )} - {badge ? badge() : null} - - -); +const Item = ({ title, iconName, onPress, testID, badge }: IHeaderButtonItem): React.ReactElement => { + const { theme } = useTheme(); + return ( + + <> + {iconName ? ( + + ) : ( + {title} + )} + {badge ? badge() : null} + + + ); +}; Item.displayName = 'HeaderButton.Item'; -export default withTheme(Item); +export default Item; diff --git a/app/containers/HeaderButton/HeaderButtonItemBadge.tsx b/app/containers/HeaderButton/HeaderButtonItemBadge.tsx index 9634c8bde..4bd2d2682 100644 --- a/app/containers/HeaderButton/HeaderButtonItemBadge.tsx +++ b/app/containers/HeaderButton/HeaderButtonItemBadge.tsx @@ -15,6 +15,6 @@ const styles = StyleSheet.create({ } }); -export const Badge = ({ ...props }) => ; +export const Badge = ({ ...props }): React.ReactElement => ; export default Badge; diff --git a/app/containers/InAppNotification/NotifierComponent.tsx b/app/containers/InAppNotification/NotifierComponent.tsx index 4264b2eea..c0c16a9c3 100644 --- a/app/containers/InAppNotification/NotifierComponent.tsx +++ b/app/containers/InAppNotification/NotifierComponent.tsx @@ -14,9 +14,18 @@ import { ROW_HEIGHT } from '../../presentation/RoomItem'; import { goRoom } from '../../utils/goRoom'; import Navigation from '../../lib/Navigation'; import { useOrientation } from '../../dimensions'; +import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions'; -interface INotifierComponent { - notification: object; +export interface INotifierComponent { + notification: { + text: string; + payload: { + sender: { username: string }; + type: SubscriptionType; + } & Pick; + title: string; + avatar: string; + }; isMasterDetail: boolean; } @@ -67,15 +76,15 @@ const styles = StyleSheet.create({ const hideNotification = () => Notifier.hideNotification(); const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifierComponent) => { - const { theme }: any = useTheme(); + const { theme } = useTheme(); const insets = useSafeAreaInsets(); const { isLandscape } = useOrientation(); - const { text, payload }: any = notification; + const { text, payload } = notification; const { type, rid } = payload; const name = type === 'd' ? payload.sender.username : payload.name; // if sub is not on local database, title and avatar will be null, so we use payload from notification - const { title = name, avatar = name }: any = notification; + const { title = name, avatar = name } = notification; const onPress = () => { const { prid, _id } = payload; @@ -133,7 +142,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie ); }); -const mapStateToProps = (state: any) => ({ +const mapStateToProps = (state: IApplicationState) => ({ isMasterDetail: state.app.isMasterDetail }); diff --git a/app/containers/InAppNotification/index.tsx b/app/containers/InAppNotification/index.tsx index e708231b1..7e5d15889 100644 --- a/app/containers/InAppNotification/index.tsx +++ b/app/containers/InAppNotification/index.tsx @@ -3,16 +3,18 @@ import { Easing, Notifier, NotifierRoot } from 'react-native-notifier'; import { connect } from 'react-redux'; import { dequal } from 'dequal'; -import NotifierComponent from './NotifierComponent'; +import NotifierComponent, { INotifierComponent } from './NotifierComponent'; import EventEmitter from '../../utils/events'; import Navigation from '../../lib/Navigation'; import { getActiveRoute } from '../../utils/navigation'; +import { IApplicationState } from '../../definitions'; +import { IRoom } from '../../reducers/room'; export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp'; const InAppNotification = memo( - ({ rooms, appState }: { rooms: any; appState: string }) => { - const show = (notification: any) => { + ({ rooms, appState }: { rooms: IRoom['rooms']; appState: string }) => { + const show = (notification: INotifierComponent['notification']) => { if (appState !== 'foreground') { return; } @@ -46,7 +48,7 @@ const InAppNotification = memo( (prevProps, nextProps) => dequal(prevProps.rooms, nextProps.rooms) ); -const mapStateToProps = (state: any) => ({ +const mapStateToProps = (state: IApplicationState) => ({ rooms: state.room.rooms, appState: state.app.ready && state.app.foreground ? 'foreground' : 'background' }); diff --git a/app/containers/List/ListContainer.tsx b/app/containers/List/ListContainer.tsx index deb9c8a71..349c71bee 100644 --- a/app/containers/List/ListContainer.tsx +++ b/app/containers/List/ListContainer.tsx @@ -11,7 +11,7 @@ const styles = StyleSheet.create({ }); interface IListContainer { - children: React.ReactNode; + children: (React.ReactElement | null)[] | React.ReactElement | null; testID?: string; } const ListContainer = React.memo(({ children, ...props }: IListContainer) => ( diff --git a/app/containers/List/ListHeader.tsx b/app/containers/List/ListHeader.tsx index 9a0b97731..469d4cec6 100644 --- a/app/containers/List/ListHeader.tsx +++ b/app/containers/List/ListHeader.tsx @@ -4,7 +4,7 @@ import { StyleSheet, Text, View } from 'react-native'; import sharedStyles from '../../views/Styles'; import { themes } from '../../constants/colors'; import I18n from '../../i18n'; -import { withTheme } from '../../theme'; +import { useTheme } from '../../theme'; import { PADDING_HORIZONTAL } from './constants'; const styles = StyleSheet.create({ @@ -20,18 +20,21 @@ const styles = StyleSheet.create({ interface IListHeader { title: string; - theme?: string; translateTitle?: boolean; } -const ListHeader = React.memo(({ title, theme, translateTitle = true }: IListHeader) => ( - - - {translateTitle ? I18n.t(title) : title} - - -)); +const ListHeader = React.memo(({ title, translateTitle = true }: IListHeader) => { + const { theme } = useTheme(); + + return ( + + + {translateTitle ? I18n.t(title) : title} + + + ); +}); ListHeader.displayName = 'List.Header'; -export default withTheme(ListHeader); +export default ListHeader; diff --git a/app/containers/List/ListIcon.tsx b/app/containers/List/ListIcon.tsx index 71e4fbdf2..c134b1690 100644 --- a/app/containers/List/ListIcon.tsx +++ b/app/containers/List/ListIcon.tsx @@ -3,11 +3,10 @@ import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { themes } from '../../constants/colors'; import { CustomIcon } from '../../lib/Icons'; -import { withTheme } from '../../theme'; +import { useTheme } from '../../theme'; import { ICON_SIZE } from './constants'; interface IListIcon { - theme?: string; name: string; color?: string; style?: StyleProp; @@ -21,12 +20,16 @@ const styles = StyleSheet.create({ } }); -const ListIcon = React.memo(({ theme, name, color, style, testID }: IListIcon) => ( - - - -)); +const ListIcon = React.memo(({ name, color, style, testID }: IListIcon) => { + const { theme } = useTheme(); + + return ( + + + + ); +}); ListIcon.displayName = 'List.Icon'; -export default withTheme(ListIcon); +export default ListIcon; diff --git a/app/containers/List/ListInfo.tsx b/app/containers/List/ListInfo.tsx index 2bfe68e32..baac47ccc 100644 --- a/app/containers/List/ListInfo.tsx +++ b/app/containers/List/ListInfo.tsx @@ -3,7 +3,7 @@ import { StyleSheet, Text, View } from 'react-native'; import sharedStyles from '../../views/Styles'; import { themes } from '../../constants/colors'; -import { withTheme } from '../../theme'; +import { useTheme } from '../../theme'; import { PADDING_HORIZONTAL } from './constants'; import I18n from '../../i18n'; @@ -18,18 +18,20 @@ const styles = StyleSheet.create({ } }); -interface IListHeader { +interface IListInfo { info: string; - theme?: string; translateInfo?: boolean; } -const ListInfo = React.memo(({ info, theme, translateInfo = true }: IListHeader) => ( - - {translateInfo ? I18n.t(info) : info} - -)); +const ListInfo = React.memo(({ info, translateInfo = true }: IListInfo) => { + const { theme } = useTheme(); + return ( + + {translateInfo ? I18n.t(info) : info} + + ); +}); ListInfo.displayName = 'List.Info'; -export default withTheme(ListInfo); +export default ListInfo; diff --git a/app/containers/List/ListItem.tsx b/app/containers/List/ListItem.tsx index 87abfd2dd..c09266a41 100644 --- a/app/containers/List/ListItem.tsx +++ b/app/containers/List/ListItem.tsx @@ -4,11 +4,11 @@ import { I18nManager, StyleSheet, Text, View } from 'react-native'; import Touch from '../../utils/touch'; import { themes } from '../../constants/colors'; import sharedStyles from '../../views/Styles'; -import { withTheme } from '../../theme'; +import { useTheme } from '../../theme'; import I18n from '../../i18n'; import { Icon } from '.'; import { BASE_HEIGHT, ICON_SIZE, PADDING_HORIZONTAL } from './constants'; -import { withDimensions } from '../../dimensions'; +import { useDimensions } from '../../dimensions'; import { CustomIcon } from '../../lib/Icons'; const styles = StyleSheet.create({ @@ -59,13 +59,12 @@ interface IListItemContent { left?: () => JSX.Element | null; right?: () => JSX.Element | null; disabled?: boolean; + theme: string; testID?: string; - theme?: string; color?: string; translateTitle?: boolean; translateSubtitle?: boolean; showActionIndicator?: boolean; - fontScale?: number; alert?: boolean; } @@ -78,78 +77,85 @@ const Content = React.memo( left, right, color, - theme, - fontScale, alert, translateTitle = true, translateSubtitle = true, - showActionIndicator = false - }: IListItemContent) => ( - - {left ? {left()} : null} - - - - {translateTitle ? I18n.t(title) : title} - - {alert ? ( - + showActionIndicator = false, + theme + }: IListItemContent) => { + const { fontScale } = useDimensions(); + + return ( + + {left ? {left()} : null} + + + + {translateTitle ? I18n.t(title) : title} + + {alert ? ( + + ) : null} + + {subtitle ? ( + + {translateSubtitle ? I18n.t(subtitle) : subtitle} + ) : null} - {subtitle ? ( - - {translateSubtitle ? I18n.t(subtitle) : subtitle} - + {right || showActionIndicator ? ( + + {right ? right() : null} + {showActionIndicator ? : null} + ) : null} - {right || showActionIndicator ? ( - - {right ? right() : null} - {showActionIndicator ? : null} - - ) : null} - - ) + ); + } ); -interface IListButtonPress { - onPress?: Function; +interface IListButtonPress extends IListItemButton { + onPress: Function; } -interface IListItemButton extends IListButtonPress { +interface IListItemButton { title?: string; disabled?: boolean; - theme?: string; + theme: string; backgroundColor?: string; underlayColor?: string; } -const Button = React.memo(({ onPress, backgroundColor, underlayColor, ...props }: IListItemButton) => ( +const Button = React.memo(({ onPress, backgroundColor, underlayColor, ...props }: IListButtonPress) => ( onPress!(props.title)} - style={{ backgroundColor: backgroundColor || themes[props.theme!].backgroundColor }} + onPress={() => onPress(props.title)} + style={{ backgroundColor: backgroundColor || themes[props.theme].backgroundColor }} underlayColor={underlayColor} enabled={!props.disabled} - theme={props.theme!}> + theme={props.theme}> )); -interface IListItem extends IListItemContent, IListButtonPress { +interface IListItem extends Omit, Omit { backgroundColor?: string; + onPress?: Function; } -const ListItem = React.memo(({ ...props }: IListItem) => { +const ListItem = React.memo(({ ...props }: IListItem) => { + const { theme } = useTheme(); + if (props.onPress) { - return