From 0d7af4088a434b1e852aca101094153de62a3745 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Mon, 9 Oct 2023 13:18:02 -0300 Subject: [PATCH 01/12] fix(Android): Remove RefreshControl from messages list (#5251) --- app/views/RoomView/List/components/List.tsx | 2 +- .../RoomView/List/components/NavBottomFAB.tsx | 12 +---- .../List/components/RefreshControl.tsx | 38 -------------- app/views/RoomView/List/components/index.ts | 1 - app/views/RoomView/List/index.tsx | 49 ++++++++----------- app/views/RoomView/index.tsx | 2 +- 6 files changed, 24 insertions(+), 80 deletions(-) delete mode 100644 app/views/RoomView/List/components/RefreshControl.tsx diff --git a/app/views/RoomView/List/components/List.tsx b/app/views/RoomView/List/components/List.tsx index 22c07570f..1976075c5 100644 --- a/app/views/RoomView/List/components/List.tsx +++ b/app/views/RoomView/List/components/List.tsx @@ -43,7 +43,7 @@ export const List = ({ listRef, jumpToBottom, isThread, ...props }: IListProps) keyExtractor={item => item.id} contentContainerStyle={styles.contentContainer} style={styles.list} - inverted={isIOS} + inverted removeClippedSubviews={isIOS} initialNumToRender={7} onEndReachedThreshold={0.5} diff --git a/app/views/RoomView/List/components/NavBottomFAB.tsx b/app/views/RoomView/List/components/NavBottomFAB.tsx index a0d9a65c6..9ecb6681e 100644 --- a/app/views/RoomView/List/components/NavBottomFAB.tsx +++ b/app/views/RoomView/List/components/NavBottomFAB.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { StyleSheet, View, Platform } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { CustomIcon } from '../../../../containers/CustomIcon'; import { useTheme } from '../../../../theme'; @@ -43,15 +43,7 @@ const NavBottomFAB = ({ style={[ styles.container, { - ...Platform.select({ - ios: { - bottom: 100 + (isThread ? 40 : 0) - }, - android: { - top: 15, - scaleY: -1 - } - }) + bottom: 100 + (isThread ? 40 : 0) } ]} testID='nav-jump-to-bottom' diff --git a/app/views/RoomView/List/components/RefreshControl.tsx b/app/views/RoomView/List/components/RefreshControl.tsx deleted file mode 100644 index ef72298ab..000000000 --- a/app/views/RoomView/List/components/RefreshControl.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import { RefreshControl as RNRefreshControl, RefreshControlProps, StyleSheet } from 'react-native'; - -import { useTheme } from '../../../../theme'; -import { isAndroid } from '../../../../lib/methods/helpers'; - -const style = StyleSheet.create({ - container: { - flex: 1 - }, - inverted: { - scaleY: -1 - } -}); - -interface IRefreshControl extends RefreshControlProps { - children: React.ReactElement; -} - -export const RefreshControl = ({ children, onRefresh, refreshing }: IRefreshControl): React.ReactElement => { - const { colors } = useTheme(); - if (isAndroid) { - return ( - - {children} - - ); - } - - const refreshControl = ; - - return React.cloneElement(children, { refreshControl }); -}; diff --git a/app/views/RoomView/List/components/index.ts b/app/views/RoomView/List/components/index.ts index 7f17ff1d9..c45143de1 100644 --- a/app/views/RoomView/List/components/index.ts +++ b/app/views/RoomView/List/components/index.ts @@ -1,4 +1,3 @@ export * from './NavBottomFAB'; -export * from './RefreshControl'; export * from './EmptyRoom'; export * from './List'; diff --git a/app/views/RoomView/List/index.tsx b/app/views/RoomView/List/index.tsx index 6846f208f..c38080468 100644 --- a/app/views/RoomView/List/index.tsx +++ b/app/views/RoomView/List/index.tsx @@ -1,21 +1,12 @@ import React, { forwardRef, useImperativeHandle } from 'react'; -import { View, Platform, StyleSheet } from 'react-native'; +import { RefreshControl } from 'react-native'; import ActivityIndicator from '../../../containers/ActivityIndicator'; import { useMessages, useRefresh, useScroll } from './hooks'; -import { useDebounce } from '../../../lib/methods/helpers'; -import { RefreshControl, EmptyRoom, List } from './components'; +import { isIOS, useDebounce } from '../../../lib/methods/helpers'; +import { EmptyRoom, List } from './components'; import { IListContainerProps, IListContainerRef, IListProps } from './definitions'; - -const styles = StyleSheet.create({ - inverted: { - ...Platform.select({ - android: { - scaleY: -1 - } - }) - } -}); +import { useTheme } from '../../../theme'; const ListContainer = forwardRef( ({ rid, tmid, renderRow, showMessageInMainThread, serverVersion, hideSystemMessages, listRef, loading }, ref) => { @@ -26,6 +17,7 @@ const ListContainer = forwardRef( serverVersion, hideSystemMessages }); + const { colors } = useTheme(); const [refreshing, refresh] = useRefresh({ rid, tmid, messagesLength: messages.length }); const { jumpToBottom, @@ -52,26 +44,25 @@ const ListContainer = forwardRef( return null; }; - const renderItem: IListProps['renderItem'] = ({ item, index }) => ( - {renderRow(item, messages[index + 1], highlightedMessageId)} - ); + const renderItem: IListProps['renderItem'] = ({ item, index }) => renderRow(item, messages[index + 1], highlightedMessageId); return ( <> - - - + : undefined + } + /> ); } diff --git a/app/views/RoomView/index.tsx b/app/views/RoomView/index.tsx index 62338423f..4aea96c2c 100644 --- a/app/views/RoomView/index.tsx +++ b/app/views/RoomView/index.tsx @@ -1361,8 +1361,8 @@ class RoomView extends React.Component { if (showUnreadSeparator || dateSeparator) { return ( <> - {content} + ); } From 62559c852b45e3f30962c592c326b473823c03ca Mon Sep 17 00:00:00 2001 From: Gleidson Daniel Silva Date: Mon, 9 Oct 2023 17:24:07 -0300 Subject: [PATCH 02/12] regression: disable mediaPlaybackRequiresUserAction on Android (#5263) --- app/views/JitsiMeetView/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/JitsiMeetView/index.tsx b/app/views/JitsiMeetView/index.tsx index 9f3b26c10..00c7997f1 100644 --- a/app/views/JitsiMeetView/index.tsx +++ b/app/views/JitsiMeetView/index.tsx @@ -86,6 +86,7 @@ const JitsiMeetView = (): React.ReactElement => { domStorageEnabled allowsInlineMediaPlayback mediaCapturePermissionGrantType={'grant'} + mediaPlaybackRequiresUserAction={isIOS} /> ); From 6a02fde4539892042e409bdf6e7c6fc38a257f3c Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 10 Oct 2023 13:43:05 -0300 Subject: [PATCH 03/12] chore: Fix CI yarn cache not working (#5265) --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index e12bd923b..0e3aaeaa5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17704,7 +17704,7 @@ react-native-easy-grid@^0.2.2: dependencies: lodash "^4.17.15" -react-native-easy-toast@^1.2.0: +react-native-easy-toast@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/react-native-easy-toast/-/react-native-easy-toast-1.2.0.tgz#0f70bcb99e3306cda4800c244bfb4a67d42276ed" integrity sha512-UtpxnRn1ME+035Uey4VR+9K0P4aVoTcWNOx5QkioWBe3LBKMPb/kZjrQ1LtvWzOyeGP4TeTUTtMX3IOPWv5MtA== From 74cd85a069769a5d7a55fcd44a0c8eb467167750 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com> Date: Wed, 11 Oct 2023 11:23:11 -0300 Subject: [PATCH 04/12] feat: image base64 (#5264) * improvement: handle base64 * update tests * minor tweak * remove imageprocess and remove the save to gallery from image base64 * Update app/containers/message/Image.tsx * Update app/views/AttachmentView.tsx * Update app/views/AttachmentView.tsx * Update app/views/AttachmentView.tsx * Update app/containers/message/Image.tsx * chore: change file name and fix implementation * chore: remove useless const * chore: update regex * chore: update name * Update app/lib/methods/isImageBase64.ts * Update app/lib/methods/isImageBase64.ts * early return --------- Co-authored-by: Gleidson Daniel Silva Co-authored-by: Diego Mello --- .../__snapshots__/Message.stories.storyshot | 2 +- app/containers/message/Image.tsx | 8 +++++++- app/containers/message/Message.stories.tsx | 12 ++++++++++++ app/lib/methods/handleMediaDownload.ts | 7 +++---- app/lib/methods/helpers/formatAttachmentUrl.ts | 6 +++++- app/lib/methods/index.ts | 1 + app/lib/methods/isImageBase64.test.ts | 16 ++++++++++++++++ app/lib/methods/isImageBase64.ts | 8 ++++++++ app/views/AttachmentView.tsx | 4 +++- 9 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 app/lib/methods/isImageBase64.test.ts create mode 100644 app/lib/methods/isImageBase64.ts diff --git a/__tests__/containers/message/__snapshots__/Message.stories.storyshot b/__tests__/containers/message/__snapshots__/Message.stories.storyshot index 76b5b01b0..ea77bfa7d 100644 --- a/__tests__/containers/message/__snapshots__/Message.stories.storyshot +++ b/__tests__/containers/message/__snapshots__/Message.stories.storyshot @@ -78,7 +78,7 @@ exports[`Storyshots Message With Audio 1`] = `"{\\"type\\":\\"RCTScrollView\\",\ exports[`Storyshots Message With File 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":{\\"backgroundColor\\":\\"#ffffff\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4},{\\"marginTop\\":4}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=72\\",\\"headers\\":{\\"User-Agent\\":\\"RC Mobile; ios unknown; vunknown (unknown)\\"},\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"justifyContent\\":\\"space-between\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexShrink\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"flexShrink\\":1,\\"fontSize\\":16,\\"lineHeight\\":22,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"testID\\":\\"reply-undefined-File.pdf\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":4,\\"alignSelf\\":\\"flex-start\\",\\"borderLeftWidth\\":2,\\"marginBottom\\":4,\\"borderColor\\":\\"#e1e5e8\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"borderRadius\\":4,\\"flexDirection\\":\\"column\\",\\"paddingVertical\\":4,\\"paddingLeft\\":8}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginBottom\\":8}},\\"children\\":null},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2f343d\\"},{\\"color\\":\\"#6C727A\\",\\"fontSize\\":14}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"File.pdf\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"File.pdf\\"]}]}]}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2f343d\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"This is a description \\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"This is a description \\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":20,\\"height\\":20}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/emoji-custom/nyan_rocket.png\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"contain\\"},\\"children\\":null}]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},false]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"testID\\":\\"reply-undefined-File.pdf\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":4,\\"alignSelf\\":\\"flex-start\\",\\"borderLeftWidth\\":2,\\"marginBottom\\":4,\\"borderColor\\":\\"#e1e5e8\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"borderRadius\\":4,\\"flexDirection\\":\\"column\\",\\"paddingVertical\\":4,\\"paddingLeft\\":8}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginBottom\\":8}},\\"children\\":null},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2f343d\\"},{\\"color\\":\\"#6C727A\\",\\"fontSize\\":14}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"File.pdf\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"File.pdf\\"]}]}]}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2f343d\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"This is a description \\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"This is a description \\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":20,\\"height\\":20}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/emoji-custom/nyan_rocket.png\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"contain\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]}]}]}]}]}]}"`; -exports[`Storyshots Message With Image 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":{\\"backgroundColor\\":\\"#ffffff\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4},{\\"marginTop\\":4}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=72\\",\\"headers\\":{\\"User-Agent\\":\\"RC Mobile; ios unknown; vunknown (unknown)\\"},\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"justifyContent\\":\\"space-between\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexShrink\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"flexShrink\\":1,\\"fontSize\\":16,\\"lineHeight\\":22,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2f343d\\"},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"This is a description\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"This is a description\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexDirection\\":\\"column\\",\\"borderRadius\\":4,\\"overflow\\":\\"visible\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"borderColor\\":\\"#e1e5e8\\"}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/dummypath?rc_token=abc&rc_uid=y8bd77ptZswPj3EW8\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]},{\\"type\\":\\"BlurView\\",\\"props\\":{\\"style\\":[{\\"backgroundColor\\":\\"transparent\\"},[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0}]],\\"blurType\\":\\"dark\\",\\"blurAmount\\":2},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}]},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#9ca2a8\\",\\"size\\":54},\\"children\\":null}]}]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4},{\\"marginTop\\":4}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=72\\",\\"headers\\":{\\"User-Agent\\":\\"RC Mobile; ios unknown; vunknown (unknown)\\"},\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"justifyContent\\":\\"space-between\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexShrink\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"flexShrink\\":1,\\"fontSize\\":16,\\"lineHeight\\":22,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2f343d\\"},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"This is a description \\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"This is a description \\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":20,\\"height\\":20}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/emoji-custom/nyan_rocket.png\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"contain\\"},\\"children\\":null}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexDirection\\":\\"column\\",\\"borderRadius\\":4,\\"overflow\\":\\"visible\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"borderColor\\":\\"#e1e5e8\\"}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/dummypath?rc_token=abc&rc_uid=y8bd77ptZswPj3EW8\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]},{\\"type\\":\\"BlurView\\",\\"props\\":{\\"style\\":[{\\"backgroundColor\\":\\"transparent\\"},[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0}]],\\"blurType\\":\\"dark\\",\\"blurAmount\\":2},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}]},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#9ca2a8\\",\\"size\\":54},\\"children\\":null}]}]}]}]}]}]}]}]}]}]}"`; +exports[`Storyshots Message With Image 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":{\\"backgroundColor\\":\\"#ffffff\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4},{\\"marginTop\\":4}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=72\\",\\"headers\\":{\\"User-Agent\\":\\"RC Mobile; ios unknown; vunknown (unknown)\\"},\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"justifyContent\\":\\"space-between\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexShrink\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"flexShrink\\":1,\\"fontSize\\":16,\\"lineHeight\\":22,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2f343d\\"},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"This is a description\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"This is a description\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexDirection\\":\\"column\\",\\"borderRadius\\":4,\\"overflow\\":\\"visible\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"borderColor\\":\\"#e1e5e8\\"}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/dummypath?rc_token=abc&rc_uid=y8bd77ptZswPj3EW8\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]},{\\"type\\":\\"BlurView\\",\\"props\\":{\\"style\\":[{\\"backgroundColor\\":\\"transparent\\"},[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0}]],\\"blurType\\":\\"dark\\",\\"blurAmount\\":2},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}]},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#9ca2a8\\",\\"size\\":54},\\"children\\":null}]}]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4},{\\"marginTop\\":4}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=72\\",\\"headers\\":{\\"User-Agent\\":\\"RC Mobile; ios unknown; vunknown (unknown)\\"},\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"justifyContent\\":\\"space-between\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexShrink\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"flexShrink\\":1,\\"fontSize\\":16,\\"lineHeight\\":22,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2f343d\\"},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"This is a description \\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"This is a description \\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":20,\\"height\\":20}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/emoji-custom/nyan_rocket.png\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"contain\\"},\\"children\\":null}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexDirection\\":\\"column\\",\\"borderRadius\\":4,\\"overflow\\":\\"visible\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"borderColor\\":\\"#e1e5e8\\"}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/dummypath?rc_token=abc&rc_uid=y8bd77ptZswPj3EW8\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]},{\\"type\\":\\"BlurView\\",\\"props\\":{\\"style\\":[{\\"backgroundColor\\":\\"transparent\\"},[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0}]],\\"blurType\\":\\"dark\\",\\"blurAmount\\":2},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}]},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#9ca2a8\\",\\"size\\":54},\\"children\\":null}]}]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4},{\\"marginTop\\":4}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=72\\",\\"headers\\":{\\"User-Agent\\":\\"RC Mobile; ios unknown; vunknown (unknown)\\"},\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"justifyContent\\":\\"space-between\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexShrink\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"flexShrink\\":1,\\"fontSize\\":16,\\"lineHeight\\":22,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2f343d\\"},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"This is a description for Base64 Attachment \\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"This is a description for Base64 Attachment \\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":20,\\"height\\":20}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/emoji-custom/nyan_rocket.png\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"contain\\"},\\"children\\":null}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexDirection\\":\\"column\\",\\"borderRadius\\":4,\\"overflow\\":\\"visible\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"borderColor\\":\\"#e1e5e8\\"}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"data:image/png;base64,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\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]}]}]}]}]}]}]}]}"`; exports[`Storyshots Message With Video 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":{\\"backgroundColor\\":\\"#ffffff\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4},{\\"marginTop\\":4}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=72\\",\\"headers\\":{\\"User-Agent\\":\\"RC Mobile; ios unknown; vunknown (unknown)\\"},\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"justifyContent\\":\\"space-between\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexShrink\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"flexShrink\\":1,\\"fontSize\\":16,\\"lineHeight\\":22,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2f343d\\"},null]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"This is a description \\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"This is a description \\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":20,\\"height\\":20}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/emoji-custom/nyan_rocket.png\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"contain\\"},\\"children\\":null}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"borderRadius\\":4,\\"height\\":150,\\"marginBottom\\":6,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\",\\"overflow\\":\\"visible\\",\\"backgroundColor\\":\\"#1f2329\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"BlurView\\",\\"props\\":{\\"style\\":[{\\"backgroundColor\\":\\"transparent\\"},[{\\"flex\\":1,\\"borderRadius\\":4,\\"height\\":150,\\"marginBottom\\":6,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"},{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0}]],\\"blurType\\":\\"dark\\",\\"blurAmount\\":2},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"borderRadius\\":4,\\"height\\":150,\\"marginBottom\\":6,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"},{\\"position\\":\\"absolute\\",\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}]},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#9ca2a8\\",\\"size\\":54},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"top\\":8,\\"right\\":8}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\",\\"fontSize\\":12},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"Cancel\\"]}]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4},{\\"marginTop\\":4}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=72\\",\\"headers\\":{\\"User-Agent\\":\\"RC Mobile; ios unknown; vunknown (unknown)\\"},\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"justifyContent\\":\\"space-between\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityState\\":{\\"disabled\\":false},\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexShrink\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"flexShrink\\":1,\\"fontSize\\":16,\\"lineHeight\\":22,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#0d0e12\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"borderRadius\\":4,\\"height\\":150,\\"marginBottom\\":6,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\",\\"overflow\\":\\"visible\\",\\"backgroundColor\\":\\"#1f2329\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"BlurView\\",\\"props\\":{\\"style\\":[{\\"backgroundColor\\":\\"transparent\\"},[{\\"flex\\":1,\\"borderRadius\\":4,\\"height\\":150,\\"marginBottom\\":6,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"},{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0}]],\\"blurType\\":\\"dark\\",\\"blurAmount\\":2},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"borderRadius\\":4,\\"height\\":150,\\"marginBottom\\":6,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"},{\\"position\\":\\"absolute\\",\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}]},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#9ca2a8\\",\\"size\\":54},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"top\\":8,\\"right\\":8}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\",\\"fontSize\\":12},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"Cancel\\"]}]}]}]}]}]}]}]}]}]}"`; diff --git a/app/containers/message/Image.tsx b/app/containers/message/Image.tsx index 61bfe1f3b..43fd1428c 100644 --- a/app/containers/message/Image.tsx +++ b/app/containers/message/Image.tsx @@ -13,6 +13,7 @@ import BlurComponent from './Components/BlurComponent'; import MessageContext from './Context'; import Touchable from './Touchable'; import styles from './styles'; +import { isImageBase64 } from '../../lib/methods'; interface IMessageButton { children: React.ReactElement; @@ -110,7 +111,12 @@ const ImageContainer = ({ await handleAutoDownload(); } }; - handleCache(); + if (isImageBase64(imgUrlToCache)) { + setLoading(false); + setCached(true); + } else { + handleCache(); + } }, []); if (!img) { diff --git a/app/containers/message/Message.stories.tsx b/app/containers/message/Message.stories.tsx index 33994b5e3..d59daa02b 100644 --- a/app/containers/message/Message.stories.tsx +++ b/app/containers/message/Message.stories.tsx @@ -356,6 +356,9 @@ export const DateAndUnreadSeparators = () => ( ); +const base64 = + 'data:image/png;base64,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'; + export const WithImage = () => ( <> ( } ]} /> + ); diff --git a/app/lib/methods/handleMediaDownload.ts b/app/lib/methods/handleMediaDownload.ts index 9fb3c5c9b..21beb2792 100644 --- a/app/lib/methods/handleMediaDownload.ts +++ b/app/lib/methods/handleMediaDownload.ts @@ -199,8 +199,7 @@ export function downloadMediaFile({ try { const path = getFilePath({ type, mimeType, urlToCache: downloadUrl }); if (!path) { - reject(); - return; + return reject(); } downloadKey = mediaDownloadKey(downloadUrl); downloadQueue[downloadKey] = FileSystem.createDownloadResumable(downloadUrl, path); @@ -208,9 +207,9 @@ export function downloadMediaFile({ if (result?.uri) { return resolve(result.uri); } - reject(); + return reject(); } catch { - reject(); + return reject(); } finally { delete downloadQueue[downloadKey]; } diff --git a/app/lib/methods/helpers/formatAttachmentUrl.ts b/app/lib/methods/helpers/formatAttachmentUrl.ts index de8c204f6..55055f4dc 100644 --- a/app/lib/methods/helpers/formatAttachmentUrl.ts +++ b/app/lib/methods/helpers/formatAttachmentUrl.ts @@ -1,6 +1,7 @@ import { URL } from 'react-native-url-polyfill'; import { LOCAL_DOCUMENT_DIRECTORY } from '../handleMediaDownload'; +import { isImageBase64 } from '../isImageBase64'; function setParamInUrl({ url, token, userId }: { url: string; token: string; userId: string }) { const urlObj = new URL(url); @@ -10,7 +11,10 @@ function setParamInUrl({ url, token, userId }: { url: string; token: string; use } export const formatAttachmentUrl = (attachmentUrl: string | undefined, userId: string, token: string, server: string): string => { - if (LOCAL_DOCUMENT_DIRECTORY && attachmentUrl?.startsWith(LOCAL_DOCUMENT_DIRECTORY)) { + if ( + (attachmentUrl && isImageBase64(attachmentUrl)) || + (LOCAL_DOCUMENT_DIRECTORY && attachmentUrl?.startsWith(LOCAL_DOCUMENT_DIRECTORY)) + ) { return attachmentUrl; } if (attachmentUrl && attachmentUrl.startsWith('http')) { diff --git a/app/lib/methods/index.ts b/app/lib/methods/index.ts index a1ac3ae1a..d86b7ea14 100644 --- a/app/lib/methods/index.ts +++ b/app/lib/methods/index.ts @@ -38,3 +38,4 @@ export * from './parseSettings'; export * from './subscribeRooms'; export * from './serializeAsciiUrl'; export * from './isRoomFederated'; +export * from './isImageBase64'; diff --git a/app/lib/methods/isImageBase64.test.ts b/app/lib/methods/isImageBase64.test.ts new file mode 100644 index 000000000..69f34b51d --- /dev/null +++ b/app/lib/methods/isImageBase64.test.ts @@ -0,0 +1,16 @@ +import { isImageBase64 } from './isImageBase64'; + +// We aren't testing the content, only the header +const base64 = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKkAAADcCAIAAACEWBYKAAAAA3NCSVQICAjb4U/gAAAgAElEQVR4nO29aZAc13WoeXOtrMza967qHd1ooAGRBLQNTcnUxmHMmPo3CjvGEdZo7PAP/fDzs2fscbw/Hr14EYrwNhMeWZbMkCY0YZJiaB4FkwxRFkiAIgACDZAAutFbVXft+55Vua/z40g9EJaWWN0Al8rvR0d1Va735M177jnnnoPZto0cxhL8/b4Ah/cNR/bjiyP78cWR/fjiyH58cWQ/vjiyH18c2Y8vjuzHF0f244sj+/HFkf344sh+fHFkP76MLntLkg/xOhwePiPK3pKV0vLTpf/+fxy8fp4/e07e2PpN9lJ3c3d+s5P9lcOKkt5q37GN3myZojjadTrsw4iyx90M8z8'; + +describe('Test the isImageBase64', () => { + it.each([ + ['test', false], + ['/file-upload/oTQmb2zRCsYF4pdHv/help-image-url.png', false], + [base64, true] + ])('return properly the boolean', (data, res) => { + const result = isImageBase64(data); + expect(result).toBe(res); + }); +}); diff --git a/app/lib/methods/isImageBase64.ts b/app/lib/methods/isImageBase64.ts new file mode 100644 index 000000000..3ec0e5b3a --- /dev/null +++ b/app/lib/methods/isImageBase64.ts @@ -0,0 +1,8 @@ +const imageBase64RegExp = new RegExp(/^data:image\/([a-zA-Z]*);base64,([^\"]*)$/); + +export function isImageBase64(data?: string): boolean { + if (!data) { + return false; + } + return imageBase64RegExp.test(data); +} diff --git a/app/views/AttachmentView.tsx b/app/views/AttachmentView.tsx index d77bfbd8e..89fd71f15 100644 --- a/app/views/AttachmentView.tsx +++ b/app/views/AttachmentView.tsx @@ -8,6 +8,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { shallowEqual } from 'react-redux'; import RNFetchBlob from 'rn-fetch-blob'; +import { isImageBase64 } from '../lib/methods'; import RCActivityIndicator from '../containers/ActivityIndicator'; import * as HeaderButton from '../containers/HeaderButton'; import { ImageViewer } from '../containers/ImageViewer'; @@ -109,6 +110,7 @@ const AttachmentView = (): React.ReactElement => { const setHeader = () => { let { title } = attachment; + try { if (title) { title = decodeURI(title); @@ -128,7 +130,7 @@ const AttachmentView = (): React.ReactElement => { ), headerRight: () => - Allow_Save_Media_to_Gallery ? ( + Allow_Save_Media_to_Gallery && !isImageBase64(attachment.image_url) ? ( ) : null, headerBackground: () => ( From 231057af10c9243960e7f1e40a3d3916f143e969 Mon Sep 17 00:00:00 2001 From: Gleidson Daniel Silva Date: Fri, 13 Oct 2023 16:34:59 -0300 Subject: [PATCH 05/12] regression(Android): Poor performance in messages list (#5267) * fix: remove RefreshControl from RoomView * fix: add container to verify android or ios --- app/views/RoomView/List/components/List.tsx | 2 +- .../RoomView/List/components/NavBottomFAB.tsx | 12 ++++- app/views/RoomView/List/hooks/index.ts | 1 - app/views/RoomView/List/hooks/useRefresh.ts | 27 ---------- app/views/RoomView/List/index.tsx | 53 +++++++++++-------- app/views/RoomView/index.tsx | 2 +- 6 files changed, 44 insertions(+), 53 deletions(-) delete mode 100644 app/views/RoomView/List/hooks/useRefresh.ts diff --git a/app/views/RoomView/List/components/List.tsx b/app/views/RoomView/List/components/List.tsx index 1976075c5..22c07570f 100644 --- a/app/views/RoomView/List/components/List.tsx +++ b/app/views/RoomView/List/components/List.tsx @@ -43,7 +43,7 @@ export const List = ({ listRef, jumpToBottom, isThread, ...props }: IListProps) keyExtractor={item => item.id} contentContainerStyle={styles.contentContainer} style={styles.list} - inverted + inverted={isIOS} removeClippedSubviews={isIOS} initialNumToRender={7} onEndReachedThreshold={0.5} diff --git a/app/views/RoomView/List/components/NavBottomFAB.tsx b/app/views/RoomView/List/components/NavBottomFAB.tsx index 9ecb6681e..a0d9a65c6 100644 --- a/app/views/RoomView/List/components/NavBottomFAB.tsx +++ b/app/views/RoomView/List/components/NavBottomFAB.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { StyleSheet, View } from 'react-native'; +import { StyleSheet, View, Platform } from 'react-native'; import { CustomIcon } from '../../../../containers/CustomIcon'; import { useTheme } from '../../../../theme'; @@ -43,7 +43,15 @@ const NavBottomFAB = ({ style={[ styles.container, { - bottom: 100 + (isThread ? 40 : 0) + ...Platform.select({ + ios: { + bottom: 100 + (isThread ? 40 : 0) + }, + android: { + top: 15, + scaleY: -1 + } + }) } ]} testID='nav-jump-to-bottom' diff --git a/app/views/RoomView/List/hooks/index.ts b/app/views/RoomView/List/hooks/index.ts index 12e970eca..0e9e991f8 100644 --- a/app/views/RoomView/List/hooks/index.ts +++ b/app/views/RoomView/List/hooks/index.ts @@ -1,3 +1,2 @@ export * from './useMessages'; -export * from './useRefresh'; export * from './useScroll'; diff --git a/app/views/RoomView/List/hooks/useRefresh.ts b/app/views/RoomView/List/hooks/useRefresh.ts deleted file mode 100644 index 79b744d13..000000000 --- a/app/views/RoomView/List/hooks/useRefresh.ts +++ /dev/null @@ -1,27 +0,0 @@ -import moment from 'moment'; -import { useState } from 'react'; - -import log from '../../../../lib/methods/helpers/log'; -import { loadMissedMessages, loadThreadMessages } from '../../../../lib/methods'; - -export const useRefresh = ({ rid, tmid, messagesLength }: { rid: string; tmid?: string; messagesLength: number }) => { - const [refreshing, setRefreshing] = useState(false); - - const refresh = async () => { - if (messagesLength) { - setRefreshing(true); - try { - if (tmid) { - await loadThreadMessages({ tmid, rid }); - } else { - await loadMissedMessages({ rid, lastOpen: moment().subtract(7, 'days').toDate() }); - } - } catch (e) { - log(e); - } - setRefreshing(false); - } - }; - - return [refreshing, refresh] as const; -}; diff --git a/app/views/RoomView/List/index.tsx b/app/views/RoomView/List/index.tsx index c38080468..a4c666be5 100644 --- a/app/views/RoomView/List/index.tsx +++ b/app/views/RoomView/List/index.tsx @@ -1,12 +1,24 @@ import React, { forwardRef, useImperativeHandle } from 'react'; -import { RefreshControl } from 'react-native'; +import { Platform, StyleSheet, View } from 'react-native'; import ActivityIndicator from '../../../containers/ActivityIndicator'; -import { useMessages, useRefresh, useScroll } from './hooks'; -import { isIOS, useDebounce } from '../../../lib/methods/helpers'; +import { isAndroid, useDebounce } from '../../../lib/methods/helpers'; import { EmptyRoom, List } from './components'; import { IListContainerProps, IListContainerRef, IListProps } from './definitions'; -import { useTheme } from '../../../theme'; +import { useMessages, useScroll } from './hooks'; + +const styles = StyleSheet.create({ + inverted: { + ...Platform.select({ + android: { + scaleY: -1 + } + }) + } +}); + +const Container = ({ children }: { children: React.ReactElement }) => + isAndroid ? {children} : <>{children}; const ListContainer = forwardRef( ({ rid, tmid, renderRow, showMessageInMainThread, serverVersion, hideSystemMessages, listRef, loading }, ref) => { @@ -17,8 +29,6 @@ const ListContainer = forwardRef( serverVersion, hideSystemMessages }); - const { colors } = useTheme(); - const [refreshing, refresh] = useRefresh({ rid, tmid, messagesLength: messages.length }); const { jumpToBottom, jumpToMessage, @@ -44,25 +54,26 @@ const ListContainer = forwardRef( return null; }; - const renderItem: IListProps['renderItem'] = ({ item, index }) => renderRow(item, messages[index + 1], highlightedMessageId); + const renderItem: IListProps['renderItem'] = ({ item, index }) => ( + {renderRow(item, messages[index + 1], highlightedMessageId)} + ); return ( <> - : undefined - } - /> + + + ); } diff --git a/app/views/RoomView/index.tsx b/app/views/RoomView/index.tsx index 4aea96c2c..62338423f 100644 --- a/app/views/RoomView/index.tsx +++ b/app/views/RoomView/index.tsx @@ -1361,8 +1361,8 @@ class RoomView extends React.Component { if (showUnreadSeparator || dateSeparator) { return ( <> - {content} + {content} ); } From 199331f8f6bec7079781eaa3b0732aad1f1b25d3 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com> Date: Mon, 16 Oct 2023 17:55:38 -0300 Subject: [PATCH 06/12] fix: check the permissions properly to create a discussion and reply in dm (#5217) * fix: check the permissions properly to create a discussion * add the permission start-discussion-other-user and fix the permission at messagebox --------- Co-authored-by: Gleidson Daniel Silva --- app/containers/MessageActions/index.tsx | 31 ++++++++++++------ app/containers/MessageBox/index.tsx | 43 ++++++++++++++++++------- app/lib/methods/getPermissions.ts | 1 + 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/app/containers/MessageActions/index.tsx b/app/containers/MessageActions/index.tsx index 9ee4be937..2f9864659 100644 --- a/app/containers/MessageActions/index.tsx +++ b/app/containers/MessageActions/index.tsx @@ -44,6 +44,7 @@ export interface IMessageActionsProps { deleteOwnMessagePermission?: string[]; pinMessagePermission?: string[]; createDirectMessagePermission?: string[]; + createDiscussionOtherUserPermission?: string[]; } export interface IMessageActions { @@ -76,6 +77,7 @@ const MessageActions = React.memo( deleteOwnMessagePermission, pinMessagePermission, createDirectMessagePermission, + createDiscussionOtherUserPermission, serverVersion }, ref @@ -85,7 +87,9 @@ const MessageActions = React.memo( hasDeletePermission: false, hasForceDeletePermission: false, hasPinPermission: false, - hasDeleteOwnPermission: false + hasDeleteOwnPermission: false, + hasCreateDirectMessagePermission: false, + hasCreateDiscussionOtherUserPermission: false }; const { showActionSheet, hideActionSheet } = useActionSheet(); @@ -96,7 +100,9 @@ const MessageActions = React.memo( deleteMessagePermission, forceDeleteMessagePermission, pinMessagePermission, - deleteOwnMessagePermission + deleteOwnMessagePermission, + createDirectMessagePermission, + createDiscussionOtherUserPermission ]; const result = await hasPermission(permission, room.rid); permissions = { @@ -104,7 +110,9 @@ const MessageActions = React.memo( hasDeletePermission: result[1], hasForceDeletePermission: result[2], hasPinPermission: result[3], - hasDeleteOwnPermission: result[4] + hasDeleteOwnPermission: result[4], + hasCreateDirectMessagePermission: result[5], + hasCreateDiscussionOtherUserPermission: result[6] }; } catch { // Do nothing @@ -385,7 +393,7 @@ const MessageActions = React.memo( } // Reply in DM - if (room.t !== 'd' && room.t !== 'l' && createDirectMessagePermission && !videoConfBlock) { + if (room.t !== 'd' && room.t !== 'l' && permissions.hasCreateDirectMessagePermission && !videoConfBlock) { options.push({ title: I18n.t('Reply_in_direct_message'), icon: 'arrow-back', @@ -394,11 +402,13 @@ const MessageActions = React.memo( } // Create Discussion - options.push({ - title: I18n.t('Start_a_Discussion'), - icon: 'discussions', - onPress: () => handleCreateDiscussion(message) - }); + if (permissions.hasCreateDiscussionOtherUserPermission) { + options.push({ + title: I18n.t('Start_a_Discussion'), + icon: 'discussions', + onPress: () => handleCreateDiscussion(message) + }); + } if (compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '6.2.0') && !videoConfBlock) { options.push({ @@ -541,7 +551,8 @@ const mapStateToProps = (state: IApplicationState) => ({ deleteOwnMessagePermission: state.permissions['delete-own-message'], forceDeleteMessagePermission: state.permissions['force-delete-message'], pinMessagePermission: state.permissions['pin-message'], - createDirectMessagePermission: state.permissions['create-d'] + createDirectMessagePermission: state.permissions['create-d'], + createDiscussionOtherUserPermission: state.permissions['start-discussion-other-user'] }); export default connect(mapStateToProps, null, null, { forwardRef: true })(MessageActions); diff --git a/app/containers/MessageBox/index.tsx b/app/containers/MessageBox/index.tsx index cbf4dfd94..695d8572d 100644 --- a/app/containers/MessageBox/index.tsx +++ b/app/containers/MessageBox/index.tsx @@ -112,6 +112,7 @@ export interface IMessageBoxProps extends IBaseScreen void | null; serverVersion: string; } @@ -130,6 +131,7 @@ interface IMessageBoxState { tshow: boolean; mentionLoading: boolean; permissionToUpload: boolean; + hasCreateDiscussionPermission: boolean; showEmojiSearchbar: boolean; } @@ -184,7 +186,8 @@ class MessageBox extends Component { tshow: this.sendThreadToChannel, mentionLoading: false, permissionToUpload: true, - showEmojiSearchbar: false + showEmojiSearchbar: false, + hasCreateDiscussionPermission: false }; this.text = ''; this.selection = { start: 0, end: 0 }; @@ -325,6 +328,7 @@ class MessageBox extends Component { mentionLoading, trackingType, permissionToUpload, + hasCreateDiscussionPermission, showEmojiSearchbar } = this.state; @@ -337,6 +341,7 @@ class MessageBox extends Component { theme, usedCannedResponse, uploadFilePermission, + createDiscussionPermission, goToCannedResponses } = this.props; if (nextProps.theme !== theme) { @@ -378,6 +383,9 @@ class MessageBox extends Component { if (nextState.permissionToUpload !== permissionToUpload) { return true; } + if (nextState.hasCreateDiscussionPermission !== hasCreateDiscussionPermission) { + return true; + } if (!dequal(nextState.mentions, mentions)) { return true; } @@ -390,6 +398,9 @@ class MessageBox extends Component { if (!dequal(nextProps.uploadFilePermission, uploadFilePermission)) { return true; } + if (!dequal(nextProps.createDiscussionPermission, createDiscussionPermission)) { + return true; + } if (nextProps.usedCannedResponse !== usedCannedResponse) { return true; } @@ -400,13 +411,18 @@ class MessageBox extends Component { } componentDidUpdate(prevProps: IMessageBoxProps) { - const { uploadFilePermission, goToCannedResponses, replyWithMention, threadsEnabled } = this.props; + const { uploadFilePermission, goToCannedResponses, replyWithMention, threadsEnabled, createDiscussionPermission } = + this.props; if (prevProps.replyWithMention !== replyWithMention) { if (threadsEnabled && replyWithMention) { this.setState({ tshow: this.sendThreadToChannel }); } } - if (!dequal(prevProps.uploadFilePermission, uploadFilePermission) || prevProps.goToCannedResponses !== goToCannedResponses) { + if ( + !dequal(prevProps.uploadFilePermission, uploadFilePermission) || + !dequal(prevProps.createDiscussionPermission, createDiscussionPermission) || + prevProps.goToCannedResponses !== goToCannedResponses + ) { this.setOptions(); } } @@ -441,7 +457,7 @@ class MessageBox extends Component { } setOptions = async () => { - const { uploadFilePermission, rid } = this.props; + const { uploadFilePermission, rid, createDiscussionPermission } = this.props; // Servers older than 4.2 if (!uploadFilePermission) { @@ -449,8 +465,8 @@ class MessageBox extends Component { return; } - const permissionToUpload = await hasPermission([uploadFilePermission], rid); - this.setState({ permissionToUpload: permissionToUpload[0] }); + const permissions = await hasPermission([uploadFilePermission, createDiscussionPermission], rid); + this.setState({ permissionToUpload: permissions[0], hasCreateDiscussionPermission: permissions[1] }); }; onChangeText: any = (text: string): void => { @@ -877,7 +893,7 @@ class MessageBox extends Component { showMessageBoxActions = () => { logEvent(events.ROOM_SHOW_BOX_ACTIONS); - const { permissionToUpload } = this.state; + const { permissionToUpload, hasCreateDiscussionPermission } = this.state; const { showActionSheet, goToCannedResponses } = this.props; const options: TActionSheetOptionsItem[] = []; @@ -913,11 +929,13 @@ class MessageBox extends Component { ); } - options.push({ - title: I18n.t('Create_Discussion'), - icon: 'discussions', - onPress: this.createDiscussion - }); + if (hasCreateDiscussionPermission) { + options.push({ + title: I18n.t('Create_Discussion'), + icon: 'discussions', + onPress: this.createDiscussion + }); + } this.closeEmojiAndAction(showActionSheet, { options }); }; @@ -1316,6 +1334,7 @@ const mapStateToProps = (state: IApplicationState) => ({ FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize, Message_AudioRecorderEnabled: state.settings.Message_AudioRecorderEnabled, uploadFilePermission: state.permissions['mobile-upload-file'], + createDiscussionPermission: state.permissions['start-discussion'], serverVersion: state.server.version }); diff --git a/app/lib/methods/getPermissions.ts b/app/lib/methods/getPermissions.ts index aab7a432c..cd741ee62 100644 --- a/app/lib/methods/getPermissions.ts +++ b/app/lib/methods/getPermissions.ts @@ -23,6 +23,7 @@ export const SUPPORTED_PERMISSIONS = [ 'create-p', 'create-d', 'start-discussion', + 'start-discussion-other-user', 'create-team', 'delete-c', 'delete-message', From 3cf08c29e180b64742f2dac81f498449e091de40 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:28:02 -0300 Subject: [PATCH 07/12] feat: add the support for CDN_PREFIX (#5266) * add the support for cdn_prefix * add cdn-prefix to video and audio too * refactor where call the store to cdn * fix e2e test * add connect redux --- app/containers/Avatar/Avatar.tsx | 6 ++++-- app/containers/Avatar/AvatarContainer.tsx | 6 ++++-- app/containers/Avatar/interfaces.ts | 1 + app/containers/message/Audio.tsx | 15 ++++++++++----- app/lib/constants/defaultSettings.ts | 3 +++ app/lib/methods/helpers/formatAttachmentUrl.ts | 5 +++++ app/lib/methods/helpers/getAvatarUrl.ts | 7 ++++++- e2e/tests/room/02-room.spec.ts | 2 ++ e2e/tests/room/05-threads.spec.ts | 3 +++ e2e/tests/room/11-autoTranslate.spec.ts | 4 +++- 10 files changed, 41 insertions(+), 11 deletions(-) diff --git a/app/containers/Avatar/Avatar.tsx b/app/containers/Avatar/Avatar.tsx index 60bbfe14f..7d10b2936 100644 --- a/app/containers/Avatar/Avatar.tsx +++ b/app/containers/Avatar/Avatar.tsx @@ -30,7 +30,8 @@ const Avatar = React.memo( borderRadius = 4, type = SubscriptionType.DIRECT, avatarExternalProviderUrl, - roomAvatarExternalProviderUrl + roomAvatarExternalProviderUrl, + cdnPrefix }: IAvatar) => { if ((!text && !avatar && !emoji && !rid) || !server) { return null; @@ -61,7 +62,8 @@ const Avatar = React.memo( rid, blockUnauthenticatedAccess, avatarExternalProviderUrl, - roomAvatarExternalProviderUrl + roomAvatarExternalProviderUrl, + cdnPrefix }); } diff --git a/app/containers/Avatar/AvatarContainer.tsx b/app/containers/Avatar/AvatarContainer.tsx index acfb0978a..e8e6a0754 100644 --- a/app/containers/Avatar/AvatarContainer.tsx +++ b/app/containers/Avatar/AvatarContainer.tsx @@ -32,9 +32,10 @@ const AvatarContainer = ({ shallowEqual ); - const { avatarExternalProviderUrl, roomAvatarExternalProviderUrl } = useSelector((state: IApplicationState) => ({ + const { avatarExternalProviderUrl, roomAvatarExternalProviderUrl, cdnPrefix } = useSelector((state: IApplicationState) => ({ avatarExternalProviderUrl: state.settings.Accounts_AvatarExternalProviderUrl as string, - roomAvatarExternalProviderUrl: state.settings.Accounts_RoomAvatarExternalProviderUrl as string + roomAvatarExternalProviderUrl: state.settings.Accounts_RoomAvatarExternalProviderUrl as string, + cdnPrefix: state.settings.CDN_PREFIX as string })); const blockUnauthenticatedAccess = useSelector( (state: IApplicationState) => @@ -67,6 +68,7 @@ const AvatarContainer = ({ roomAvatarExternalProviderUrl={roomAvatarExternalProviderUrl} avatarETag={avatarETag} serverVersion={serverVersion} + cdnPrefix={cdnPrefix} /> ); }; diff --git a/app/containers/Avatar/interfaces.ts b/app/containers/Avatar/interfaces.ts index 6ae020202..625ae4b87 100644 --- a/app/containers/Avatar/interfaces.ts +++ b/app/containers/Avatar/interfaces.ts @@ -24,4 +24,5 @@ export interface IAvatar { serverVersion?: string | null; avatarExternalProviderUrl?: string; roomAvatarExternalProviderUrl?: string; + cdnPrefix?: string; } diff --git a/app/containers/message/Audio.tsx b/app/containers/message/Audio.tsx index 315a26442..7f6afdf54 100644 --- a/app/containers/message/Audio.tsx +++ b/app/containers/message/Audio.tsx @@ -6,6 +6,7 @@ import moment from 'moment'; import { dequal } from 'dequal'; import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake'; import { Sound } from 'expo-av/build/Audio/Sound'; +import { connect } from 'react-redux'; import Touchable from './Touchable'; import Markdown from '../markdown'; @@ -17,7 +18,7 @@ import MessageContext from './Context'; import ActivityIndicator from '../ActivityIndicator'; import { withDimensions } from '../../dimensions'; import { TGetCustomEmoji } from '../../definitions/IEmoji'; -import { IAttachment, IUserMessage } from '../../definitions'; +import { IApplicationState, IAttachment, IUserMessage } from '../../definitions'; import { TSupportedThemes, useTheme } from '../../theme'; import { downloadMediaFile, getMediaCache } from '../../lib/methods/handleMediaDownload'; import EventEmitter from '../../lib/methods/helpers/events'; @@ -41,6 +42,7 @@ interface IMessageAudioProps { scale?: number; author?: IUserMessage; msg?: string; + cdnPrefix?: string; } interface IMessageAudioState { @@ -208,13 +210,12 @@ class MessageAudio extends React.Component { - const { file } = this.props; + const { file, cdnPrefix } = this.props; // @ts-ignore can't use declare to type this const { baseUrl } = this.context; - let url = file.audio_url; if (url && !url.startsWith('http')) { - url = `${baseUrl}${file.audio_url}`; + url = `${cdnPrefix || baseUrl}${file.audio_url}`; } return url; }; @@ -393,4 +394,8 @@ class MessageAudio extends React.Component ({ + cdnPrefix: state.settings.CDN_PREFIX as string +}); + +export default connect(mapStateToProps)(withDimensions(MessageAudio)); diff --git a/app/lib/constants/defaultSettings.ts b/app/lib/constants/defaultSettings.ts index 164832190..4551f6319 100644 --- a/app/lib/constants/defaultSettings.ts +++ b/app/lib/constants/defaultSettings.ts @@ -246,5 +246,8 @@ export const defaultSettings = { Omnichannel_call_provider: { type: 'valueAsBoolean' }, + CDN_PREFIX: { + type: 'valueAsString' + }, ...deprecatedSettings } as const; diff --git a/app/lib/methods/helpers/formatAttachmentUrl.ts b/app/lib/methods/helpers/formatAttachmentUrl.ts index 55055f4dc..143188594 100644 --- a/app/lib/methods/helpers/formatAttachmentUrl.ts +++ b/app/lib/methods/helpers/formatAttachmentUrl.ts @@ -2,6 +2,7 @@ import { URL } from 'react-native-url-polyfill'; import { LOCAL_DOCUMENT_DIRECTORY } from '../handleMediaDownload'; import { isImageBase64 } from '../isImageBase64'; +import { store } from '../../store/auxStore'; function setParamInUrl({ url, token, userId }: { url: string; token: string; userId: string }) { const urlObj = new URL(url); @@ -23,5 +24,9 @@ export const formatAttachmentUrl = (attachmentUrl: string | undefined, userId: s } return setParamInUrl({ url: attachmentUrl, token, userId }); } + const cdnPrefix = store?.getState().settings.CDN_PREFIX as string; + if (cdnPrefix) { + server = cdnPrefix.trim().replace(/\/+$/, ''); + } return setParamInUrl({ url: `${server}${attachmentUrl}`, token, userId }); }; diff --git a/app/lib/methods/helpers/getAvatarUrl.ts b/app/lib/methods/helpers/getAvatarUrl.ts index 476b54896..1702369ef 100644 --- a/app/lib/methods/helpers/getAvatarUrl.ts +++ b/app/lib/methods/helpers/getAvatarUrl.ts @@ -22,7 +22,8 @@ export const getAvatarURL = ({ blockUnauthenticatedAccess, serverVersion, avatarExternalProviderUrl, - roomAvatarExternalProviderUrl + roomAvatarExternalProviderUrl, + cdnPrefix }: IAvatar): string => { let room; if (type === SubscriptionType.DIRECT) { @@ -48,6 +49,10 @@ export const getAvatarURL = ({ query += `&etag=${avatarETag}`; } + if (cdnPrefix) { + server = cdnPrefix.trim().replace(/\/+$/, ''); + } + if (avatar) { if (avatar.startsWith('http')) { return avatar; diff --git a/e2e/tests/room/02-room.spec.ts b/e2e/tests/room/02-room.spec.ts index 8a863f9de..a7f091106 100644 --- a/e2e/tests/room/02-room.spec.ts +++ b/e2e/tests/room/02-room.spec.ts @@ -478,6 +478,8 @@ describe('Room screen', () => { .toExist() .withTimeout(2000); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); + // Fix android flaky test. Close the action sheet, then re-open again + await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); await element(by.id('action-sheet')).swipe('up', 'fast', 0.5); await sleep(300); // wait for animation await waitFor(element(by[textMatcher]('Delete'))) diff --git a/e2e/tests/room/05-threads.spec.ts b/e2e/tests/room/05-threads.spec.ts index 9f46eceab..5ef800674 100644 --- a/e2e/tests/room/05-threads.spec.ts +++ b/e2e/tests/room/05-threads.spec.ts @@ -237,6 +237,9 @@ describe('Threads', () => { .withTimeout(5000); await element(by.id(`message-thread-button-${thread}`)).tap(); await tryTapping(element(by[textMatcher]('replied')).atIndex(0), 2000, true); + // Fix android flaky test. Close the action sheet, then re-open again + await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); + await sleep(1000); // wait for animation await element(by.id('action-sheet')).swipe('up', 'fast', 0.5); await sleep(300); // wait for animation await element(by[textMatcher]('Delete')).atIndex(0).tap(); diff --git a/e2e/tests/room/11-autoTranslate.spec.ts b/e2e/tests/room/11-autoTranslate.spec.ts index 44fa2d81d..814ad5f37 100644 --- a/e2e/tests/room/11-autoTranslate.spec.ts +++ b/e2e/tests/room/11-autoTranslate.spec.ts @@ -137,9 +137,10 @@ describe('Auto Translate', () => { // verify default language is checked await waitFor(element(by.id(`auto-translate-view-${languages.default}`))) - .toBeVisible() + .toExist() .whileElement(by.id('auto-translate-view')) .scroll(750, 'down'); + await element(by.id('auto-translate-view')).swipe('up', 'slow', 0.5); await waitForVisible(`auto-translate-view-${languages.default}-check`); // enable translated language @@ -219,6 +220,7 @@ describe('Auto Translate', () => { }); it(`should don't see action to View original when disable auto translate`, async () => { + await searchMessage(oldMessage[languages.default] as string, textMatcher); // will scroll the messages list to the last one await waitForVisibleTextMatcher(oldMessage[languages.default] as string, textMatcher); await tryTapping(element(by[textMatcher](oldMessage[languages.default] as string)).atIndex(0), 2000, true); From c0660db06d2d3ac8ac45bcae0ab32cbf96cd2670 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:13:42 -0300 Subject: [PATCH 08/12] chore: migrate AuthenticationWebView to hooks (#5054) * chore: migrate AuthenticationWebView to hooks * minor tweak * remove navigation, tweak at useRoute --------- Co-authored-by: GleidsonDaniel --- app/lib/methods/helpers/debounce.ts | 6 +- app/stacks/OutsideStack.tsx | 6 +- app/views/AuthenticationWebView.tsx | 181 ++++++++++------------------ 3 files changed, 71 insertions(+), 122 deletions(-) diff --git a/app/lib/methods/helpers/debounce.ts b/app/lib/methods/helpers/debounce.ts index 5061e322b..4a58b6d76 100644 --- a/app/lib/methods/helpers/debounce.ts +++ b/app/lib/methods/helpers/debounce.ts @@ -1,4 +1,4 @@ -import { useDebouncedCallback } from 'use-debounce'; +import { useDebouncedCallback, Options } from 'use-debounce'; export function debounce(func: Function, wait?: number, immediate?: boolean) { let timeout: ReturnType | null; @@ -24,6 +24,6 @@ export function debounce(func: Function, wait?: number, immediate?: boolean) { return _debounce; } -export function useDebounce(func: (...args: any) => any, wait?: number): (...args: any[]) => void { - return useDebouncedCallback(func, wait || 1000); +export function useDebounce(func: (...args: any) => any, wait?: number, options?: Options): (...args: any[]) => void { + return useDebouncedCallback(func, wait || 1000, options); } diff --git a/app/stacks/OutsideStack.tsx b/app/stacks/OutsideStack.tsx index ec19c2319..40a3c9cb1 100644 --- a/app/stacks/OutsideStack.tsx +++ b/app/stacks/OutsideStack.tsx @@ -50,11 +50,7 @@ const OutsideStackModal = () => { screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...ModalAnimation, presentation: 'transparentModal' }} > - + ); }; diff --git a/app/views/AuthenticationWebView.tsx b/app/views/AuthenticationWebView.tsx index 894c524b9..4874f13a1 100644 --- a/app/views/AuthenticationWebView.tsx +++ b/app/views/AuthenticationWebView.tsx @@ -1,20 +1,20 @@ -import React from 'react'; -import { WebView, WebViewNavigation } from 'react-native-webview'; -import { connect } from 'react-redux'; -import parse from 'url-parse'; -import { StackNavigationProp } from '@react-navigation/stack'; -import { WebViewMessage } from 'react-native-webview/lib/WebViewTypes'; import { RouteProp } from '@react-navigation/core'; +import { useNavigation, useRoute } from '@react-navigation/native'; +import { StackNavigationProp } from '@react-navigation/stack'; +import React, { useLayoutEffect, useState } from 'react'; +import { WebView, WebViewNavigation } from 'react-native-webview'; +import { WebViewMessage } from 'react-native-webview/lib/WebViewTypes'; +import parse from 'url-parse'; -import { OutsideModalParamList } from '../stacks/types'; -import StatusBar from '../containers/StatusBar'; import ActivityIndicator from '../containers/ActivityIndicator'; -import { TSupportedThemes, withTheme } from '../theme'; -import { userAgent } from '../lib/constants'; -import { debounce } from '../lib/methods/helpers'; import * as HeaderButton from '../containers/HeaderButton'; +import StatusBar from '../containers/StatusBar'; +import { ICredentials } from '../definitions'; +import { userAgent } from '../lib/constants'; +import { useAppSelector } from '../lib/hooks'; +import { useDebounce } from '../lib/methods/helpers'; import { Services } from '../lib/services'; -import { IApplicationState, ICredentials } from '../definitions'; +import { OutsideModalParamList } from '../stacks/types'; // iframe uses a postMessage to send the token to the client // We'll handle this sending the token to the hash of the window.location @@ -40,95 +40,56 @@ window.addEventListener('popstate', function() { }); `; -interface INavigationOption { - navigation: StackNavigationProp; - route: RouteProp; -} +const AuthenticationWebView = () => { + const [logging, setLogging] = useState(false); + const [loading, setLoading] = useState(false); -interface IAuthenticationWebView extends INavigationOption { - server: string; - Accounts_Iframe_api_url: string; - Accounts_Iframe_api_method: string; - theme: TSupportedThemes; -} + const navigation = useNavigation>(); + const { + params: { authType, url, ssoToken } + } = useRoute>(); -interface IState { - logging: boolean; - loading: boolean; -} + const { Accounts_Iframe_api_method, Accounts_Iframe_api_url, server } = useAppSelector(state => ({ + server: state.server.server, + Accounts_Iframe_api_url: state.settings.Accounts_Iframe_api_url as string, + Accounts_Iframe_api_method: state.settings.Accounts_Iframe_api_method as string + })); -class AuthenticationWebView extends React.PureComponent { - private oauthRedirectRegex: RegExp; - private iframeRedirectRegex: RegExp; + const oauthRedirectRegex = new RegExp(`(?=.*(${server}))(?=.*(credentialToken))(?=.*(credentialSecret))`, 'g'); + const iframeRedirectRegex = new RegExp(`(?=.*(${server}))(?=.*(event|loginToken|token))`, 'g'); - static navigationOptions = ({ route, navigation }: INavigationOption) => { - const { authType } = route.params; - return { - headerLeft: () => , - title: ['saml', 'cas', 'iframe'].includes(authType) ? 'SSO' : 'OAuth' - }; - }; + // Force 3s delay so the server has time to evaluate the token + const debouncedLogin = useDebounce((params: ICredentials) => login(params), 3000); - constructor(props: IAuthenticationWebView) { - super(props); - this.state = { - logging: false, - loading: false - }; - this.oauthRedirectRegex = new RegExp(`(?=.*(${props.server}))(?=.*(credentialToken))(?=.*(credentialSecret))`, 'g'); - this.iframeRedirectRegex = new RegExp(`(?=.*(${props.server}))(?=.*(event|loginToken|token))`, 'g'); - } - - componentWillUnmount() { - if (this.debouncedLogin && this.debouncedLogin.stop) { - this.debouncedLogin.stop(); - } - } - - dismiss = () => { - const { navigation } = this.props; - navigation.pop(); - }; - - login = (params: ICredentials) => { - const { logging } = this.state; + const login = (params: ICredentials) => { if (logging) { return; } - - this.setState({ logging: true }); - + setLogging(true); try { Services.loginOAuthOrSso(params); } catch (e) { console.warn(e); } - this.setState({ logging: false }); - this.dismiss(); + setLogging(false); + navigation.pop(); }; - // Force 3s delay so the server has time to evaluate the token - debouncedLogin = debounce((params: ICredentials) => this.login(params), 3000); - - tryLogin = debounce( + const tryLogin = useDebounce( async () => { - const { Accounts_Iframe_api_url, Accounts_Iframe_api_method } = this.props; const data = await fetch(Accounts_Iframe_api_url, { method: Accounts_Iframe_api_method }).then(response => response.json()); const resume = data?.login || data?.loginToken; if (resume) { - this.login({ resume }); + login({ resume }); } }, 3000, - true + { leading: true } ); - onNavigationStateChange = (webViewState: WebViewNavigation | WebViewMessage) => { + const onNavigationStateChange = (webViewState: WebViewNavigation | WebViewMessage) => { const url = decodeURIComponent(webViewState.url); - const { route } = this.props; - const { authType } = route.params; if (authType === 'saml' || authType === 'cas') { - const { ssoToken } = route.params; const parsedUrl = parse(url, true); // ticket -> cas / validate & saml_idp_credentialToken -> saml if (parsedUrl.pathname?.includes('validate') || parsedUrl.query?.ticket || parsedUrl.query?.saml_idp_credentialToken) { @@ -140,28 +101,28 @@ class AuthenticationWebView extends React.PureComponent - - this.onNavigationStateChange(nativeEvent)} - onNavigationStateChange={this.onNavigationStateChange} - injectedJavaScript={isIframe ? injectedJavaScript : undefined} - onLoadStart={() => { - this.setState({ loading: true }); - }} - onLoadEnd={() => { - this.setState({ loading: false }); - }} - /> - {loading ? : null} - - ); - } -} + useLayoutEffect(() => { + navigation.setOptions({ + headerLeft: () => , + title: ['saml', 'cas', 'iframe'].includes(authType) ? 'SSO' : 'OAuth' + }); + }, [authType, navigation]); -const mapStateToProps = (state: IApplicationState) => ({ - server: state.server.server, - Accounts_Iframe_api_url: state.settings.Accounts_Iframe_api_url as string, - Accounts_Iframe_api_method: state.settings.Accounts_Iframe_api_method as string -}); + return ( + <> + + onNavigationStateChange(nativeEvent)} + onNavigationStateChange={onNavigationStateChange} + injectedJavaScript={isIframe ? injectedJavaScript : undefined} + onLoadStart={() => setLoading(true)} + onLoadEnd={() => setLoading(false)} + /> + {loading ? : null} + + ); +}; -export default connect(mapStateToProps)(withTheme(AuthenticationWebView)); +export default AuthenticationWebView; From 3f2e5ced197e9a56f70e106e72671d54213e4c16 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:23:12 -0300 Subject: [PATCH 09/12] chore: migrate LoginView to hooks (#5092) * chore: migrate LoginView to hooks * minor tweak * minor tweak * fix ref --------- Co-authored-by: GleidsonDaniel --- .../TextInput/ControlledFormTextInput.tsx | 6 +- app/views/LoginView.tsx | 264 ------------------ app/views/LoginView/UserForm.tsx | 171 ++++++++++++ app/views/LoginView/index.tsx | 40 +++ app/views/LoginView/styles.ts | 36 +++ 5 files changed, 251 insertions(+), 266 deletions(-) delete mode 100644 app/views/LoginView.tsx create mode 100644 app/views/LoginView/UserForm.tsx create mode 100644 app/views/LoginView/index.tsx create mode 100644 app/views/LoginView/styles.ts diff --git a/app/containers/TextInput/ControlledFormTextInput.tsx b/app/containers/TextInput/ControlledFormTextInput.tsx index 5b69b0e20..d2aa1d446 100644 --- a/app/containers/TextInput/ControlledFormTextInput.tsx +++ b/app/containers/TextInput/ControlledFormTextInput.tsx @@ -3,7 +3,7 @@ import { Control, Controller } from 'react-hook-form'; import { FormTextInput, IRCTextInputProps } from './FormTextInput'; -interface IControlledFormTextInputProps extends IRCTextInputProps { +interface IControlledFormTextInputProps extends Omit { control: Control; name: string; } @@ -12,6 +12,8 @@ export const ControlledFormTextInput = ({ control, name, ...props }: IControlled } + render={({ field: { onChange, value, ref } }) => ( + + )} /> ); diff --git a/app/views/LoginView.tsx b/app/views/LoginView.tsx deleted file mode 100644 index b43830f73..000000000 --- a/app/views/LoginView.tsx +++ /dev/null @@ -1,264 +0,0 @@ -import { dequal } from 'dequal'; -import React from 'react'; -import { Alert, Keyboard, StyleSheet, Text, View, TextInput as RNTextInput } from 'react-native'; -import { connect } from 'react-redux'; - -import { loginRequest } from '../actions/login'; -import { themes } from '../lib/constants'; -import Button from '../containers/Button'; -import FormContainer, { FormContainerInner } from '../containers/FormContainer'; -import * as HeaderButton from '../containers/HeaderButton'; -import LoginServices from '../containers/LoginServices'; -import { FormTextInput } from '../containers/TextInput'; -import { IApplicationState, IBaseScreen } from '../definitions'; -import I18n from '../i18n'; -import { OutsideParamList } from '../stacks/types'; -import { withTheme } from '../theme'; -import sharedStyles from './Styles'; -import UGCRules from '../containers/UserGeneratedContentRules'; - -const styles = StyleSheet.create({ - registerDisabled: { - ...sharedStyles.textRegular, - ...sharedStyles.textAlignCenter, - fontSize: 16 - }, - title: { - ...sharedStyles.textBold, - fontSize: 22 - }, - inputContainer: { - marginVertical: 16 - }, - bottomContainer: { - flexDirection: 'column', - alignItems: 'center' - }, - bottomContainerText: { - ...sharedStyles.textRegular, - fontSize: 13 - }, - bottomContainerTextBold: { - ...sharedStyles.textSemibold, - fontSize: 13 - }, - loginButton: { - marginTop: 16 - }, - ugcContainer: { - marginTop: 32 - } -}); - -interface ILoginViewProps extends IBaseScreen { - Site_Name: string; - Accounts_RegistrationForm: string; - Accounts_RegistrationForm_LinkReplacementText: string; - Accounts_EmailOrUsernamePlaceholder: string; - Accounts_PasswordPlaceholder: string; - Accounts_PasswordReset: boolean; - Accounts_ShowFormLogin: boolean; - isFetching: boolean; - error: { - error: string; - }; - failure: boolean; - loginRequest: Function; - inviteLinkToken: string; -} - -interface ILoginViewState { - user: string; - password: string; -} - -class LoginView extends React.Component { - private passwordInput: RNTextInput | null | undefined; - - static navigationOptions = ({ route, navigation }: ILoginViewProps) => ({ - title: route?.params?.title ?? 'Rocket.Chat', - headerRight: () => - }); - - constructor(props: ILoginViewProps) { - super(props); - this.state = { - user: props.route.params?.username ?? '', - password: '' - }; - } - - UNSAFE_componentWillReceiveProps(nextProps: ILoginViewProps) { - const { error } = this.props; - if (nextProps.failure && !dequal(error, nextProps.error)) { - if (nextProps.error?.error === 'error-invalid-email') { - this.resendEmailConfirmation(); - } else { - Alert.alert(I18n.t('Oops'), I18n.t('Login_error')); - } - } - } - - get showRegistrationButton() { - const { Accounts_RegistrationForm, inviteLinkToken } = this.props; - return Accounts_RegistrationForm === 'Public' || (Accounts_RegistrationForm === 'Secret URL' && inviteLinkToken?.length); - } - - login = () => { - const { navigation, Site_Name } = this.props; - navigation.navigate('LoginView', { title: Site_Name }); - }; - - register = () => { - const { navigation, Site_Name } = this.props; - navigation.navigate('RegisterView', { title: Site_Name }); - }; - - forgotPassword = () => { - const { navigation, Site_Name } = this.props; - navigation.navigate('ForgotPasswordView', { title: Site_Name }); - }; - - resendEmailConfirmation = () => { - const { user } = this.state; - const { navigation } = this.props; - navigation.navigate('SendEmailConfirmationView', { user }); - }; - - valid = () => { - const { user, password } = this.state; - return user.trim() && password.trim(); - }; - - submit = () => { - if (!this.valid()) { - return; - } - - const { user, password } = this.state; - const { dispatch } = this.props; - Keyboard.dismiss(); - dispatch(loginRequest({ user, password })); - }; - - renderUserForm = () => { - const { user } = this.state; - const { - Accounts_EmailOrUsernamePlaceholder, - Accounts_PasswordPlaceholder, - Accounts_PasswordReset, - Accounts_RegistrationForm_LinkReplacementText, - isFetching, - theme, - Accounts_ShowFormLogin - } = this.props; - - if (!Accounts_ShowFormLogin) { - return null; - } - - return ( - <> - {I18n.t('Login')} - this.setState({ user: value })} - onSubmitEditing={() => { - this.passwordInput?.focus(); - }} - testID='login-view-email' - textContentType='username' - autoComplete='username' - value={user} - /> - { - this.passwordInput = e; - }} - placeholder={Accounts_PasswordPlaceholder || I18n.t('Password')} - returnKeyType='send' - secureTextEntry - onSubmitEditing={this.submit} - onChangeText={(value: string) => this.setState({ password: value })} - testID='login-view-password' - textContentType='password' - autoComplete='password' - /> -