diff --git a/.detoxrc.js b/.detoxrc.js index a899f6a35..e10173160 100644 --- a/.detoxrc.js +++ b/.detoxrc.js @@ -19,13 +19,13 @@ module.exports = { type: 'ios.app', binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/Rocket.Chat Experimental.app', build: - 'xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build' + 'xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Debug -destination \'generic/platform=iphonesimulator\' -derivedDataPath ios/build' }, 'ios.release': { type: 'ios.app', binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/Rocket.Chat Experimental.app', build: - 'xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Release -sdk iphonesimulator -derivedDataPath ios/build' + 'xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Release -destination \'generic/platform=iphonesimulator\' -derivedDataPath ios/build' }, 'android.debug': { type: 'android.apk', diff --git a/__tests__/containers/message/__snapshots__/Message.stories.storyshot b/__tests__/containers/message/__snapshots__/Message.stories.storyshot index 6b4818857..153f8c81a 100644 --- a/__tests__/containers/message/__snapshots__/Message.stories.storyshot +++ b/__tests__/containers/message/__snapshots__/Message.stories.storyshot @@ -44,7 +44,7 @@ exports[`Storyshots Message Message 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"p exports[`Storyshots Message Message With Read Receipt 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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":16,\\"color\\":\\"#6C727A\\"},[{\\"lineHeight\\":16},{\\"paddingLeft\\":5}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"I'm fine!\\",\\"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\\":[\\"I'm fine!\\"]}]}]}]}]}]}]}]},{\\"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\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"I'm fine!\\",\\"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\\":[\\"I'm fine!\\"]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":16,\\"color\\":\\"#6C727A\\"},[{\\"lineHeight\\":16},{\\"paddingLeft\\":5}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":16,\\"color\\":\\"#1D74F5\\"},[{\\"lineHeight\\":16},{\\"paddingLeft\\":5}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"I'm fine!\\",\\"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\\":[\\"I'm fine!\\"]}]}]}]}]}]}]}]},{\\"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\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"I'm fine!\\",\\"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\\":[\\"I'm fine!\\"]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":16,\\"color\\":\\"#1D74F5\\"},[{\\"lineHeight\\":16},{\\"paddingLeft\\":5}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]}]}]}]}]}]}"`; -exports[`Storyshots Message Message With Reply 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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"I'm fine!\\",\\"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\\":[\\"I'm fine!\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"testID\\":\\"reply-I'm a very long long title and I'll break-How are you?\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":4,\\"alignSelf\\":\\"flex-start\\",\\"borderLeftWidth\\":2,\\"borderColor\\":\\"#EBECEF\\",\\"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\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"numberOfLines\\":1,\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\",\\"flexShrink\\":1},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"I'm a very long long title and I'll break\\"]}]},{\\"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\\":\\"How are you?\\",\\"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\\":[\\"How are you?\\"]}]}]}]}]}]}]}]}]},{\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"I'm fine!\\",\\"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\\":[\\"I'm fine!\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"testID\\":\\"reply-rocket.cat-How are you? :nyan_rocket:\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":4,\\"alignSelf\\":\\"flex-start\\",\\"borderLeftWidth\\":2,\\"borderColor\\":\\"#EBECEF\\",\\"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\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"numberOfLines\\":1,\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\",\\"flexShrink\\":1},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"rocket.cat\\"]}]},{\\"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\\":\\"How are you? \\",\\"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\\":[\\"How are you? \\"]},{\\"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\\":[{\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Looks cool!\\",\\"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\\":[\\"Looks cool!\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"testID\\":\\"reply-rocket.cat-\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":4,\\"alignSelf\\":\\"flex-start\\",\\"borderLeftWidth\\":2,\\"borderColor\\":\\"#EBECEF\\",\\"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\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"numberOfLines\\":1,\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\",\\"flexShrink\\":1},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"rocket.cat\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"},[{\\"color\\":\\"#6C727A\\",\\"fontSize\\":14,\\"marginBottom\\":8}]]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"What you think about this one?\\",\\"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\\":[\\"What you think about this one?\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexDirection\\":\\"column\\",\\"borderRadius\\":4,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"borderColor\\":\\"#CBCED1\\"}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://octodex.github.com/images/yaktocat.png?rc_token=abc&rc_uid=y8bd77ptZswPj3EW8\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#1F2329\\"}]},\\"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\\":\\"#6C727A\\",\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Yes, I am\\",\\"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\\":[\\"Yes, I am\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"testID\\":\\"reply-rocket.cat-\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":4,\\"alignSelf\\":\\"flex-start\\",\\"borderLeftWidth\\":2,\\"borderColor\\":\\"#EBECEF\\",\\"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\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"numberOfLines\\":1,\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\",\\"flexShrink\\":1},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"rocket.cat\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"},[{\\"color\\":\\"#6C727A\\",\\"fontSize\\":14,\\"marginBottom\\":8}]]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Are you seeing this mario \\",\\"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\\":[\\"Are you seeing this mario \\"]},{\\"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/marioparty.gif\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"contain\\"},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\" ?\\",\\"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\\":[\\" ?\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexDirection\\":\\"column\\",\\"borderRadius\\":4,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"borderColor\\":\\"#CBCED1\\"}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://octodex.github.com/images/yaktocat.png?rc_token=abc&rc_uid=y8bd77ptZswPj3EW8\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#1F2329\\"}]},\\"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\\":\\"#6C727A\\",\\"size\\":54},\\"children\\":null}]}]}]}]}]}]}]}]}]}]}]}]}"`; +exports[`Storyshots Message Message With Reply 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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"I'm fine!\\",\\"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\\":[\\"I'm fine!\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"testID\\":\\"reply-I'm a very long long title and I'll break-How are you?\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":4,\\"alignSelf\\":\\"flex-start\\",\\"borderLeftWidth\\":2,\\"borderColor\\":\\"#EBECEF\\",\\"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\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"numberOfLines\\":1,\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\",\\"flexShrink\\":1},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"I'm a very long long title and I'll break\\"]}]},{\\"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\\":\\"How are you?\\",\\"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\\":[\\"How are you?\\"]}]}]}]}]}]}]}]}]},{\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"I'm fine!\\",\\"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\\":[\\"I'm fine!\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"testID\\":\\"reply-rocket.cat-How are you? :nyan_rocket:\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":4,\\"alignSelf\\":\\"flex-start\\",\\"borderLeftWidth\\":2,\\"borderColor\\":\\"#EBECEF\\",\\"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\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"numberOfLines\\":1,\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\",\\"flexShrink\\":1},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"rocket.cat\\"]}]},{\\"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\\":\\"How are you? \\",\\"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\\":[\\"How are you? \\"]},{\\"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\\":[{\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Looks cool!\\",\\"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\\":[\\"Looks cool!\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"testID\\":\\"reply-rocket.cat-\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":4,\\"alignSelf\\":\\"flex-start\\",\\"borderLeftWidth\\":2,\\"borderColor\\":\\"#EBECEF\\",\\"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\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"numberOfLines\\":1,\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\",\\"flexShrink\\":1},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"rocket.cat\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"},[{\\"color\\":\\"#6C727A\\",\\"fontSize\\":14,\\"marginBottom\\":8}]]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"What you think about this one?\\",\\"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\\":[\\"What you think about this one?\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexDirection\\":\\"column\\",\\"borderRadius\\":4,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"borderColor\\":\\"#CBCED1\\"}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://octodex.github.com/images/yaktocat.png?rc_token=abc&rc_uid=y8bd77ptZswPj3EW8\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#E4E7EA\\"}]},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}]},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#6C727A\\",\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Yes, I am\\",\\"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\\":[\\"Yes, I am\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"testID\\":\\"reply-rocket.cat-\\",\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginVertical\\":4,\\"alignSelf\\":\\"flex-start\\",\\"borderLeftWidth\\":2,\\"borderColor\\":\\"#EBECEF\\",\\"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\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"numberOfLines\\":1,\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"500\\",\\"flexShrink\\":1},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"rocket.cat\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"},[{\\"color\\":\\"#6C727A\\",\\"fontSize\\":14,\\"marginBottom\\":8}]]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Are you seeing this mario \\",\\"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\\":[\\"Are you seeing this mario \\"]},{\\"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/marioparty.gif\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"contain\\"},\\"children\\":null}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\" ?\\",\\"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\\":[\\" ?\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"flexDirection\\":\\"column\\",\\"borderRadius\\":4,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"borderColor\\":\\"#CBCED1\\"}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://octodex.github.com/images/yaktocat.png?rc_token=abc&rc_uid=y8bd77ptZswPj3EW8\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#E4E7EA\\"}]},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}]},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#6C727A\\",\\"size\\":54},\\"children\\":null}]}]}]}]}]}]}]}]}]}]}]}]}"`; exports[`Storyshots Message Message With Thread 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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"How are you?\\",\\"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\\":[\\"How are you?\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginTop\\":8,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingHorizontal\\":12,\\"paddingVertical\\":8,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\",\\"borderRadius\\":4},{\\"backgroundColor\\":\\"#1D74F5\\"}],\\"testID\\":\\"message-thread-button-How are you?\\"},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":12,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#FFFFFF\\"}]},\\"children\\":[\\"Reply\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\"},{\\"flex\\":1,\\"marginLeft\\":12}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginRight\\":8}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":24,\\"color\\":\\"#2F343D\\"},[{\\"lineHeight\\":24},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"testID\\":\\"thread-count-1\\",\\"style\\":[{\\"fontSize\\":10,\\"marginLeft\\":2,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#6C727A\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"1\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"marginRight\\":8}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":24,\\"color\\":\\"#2F343D\\"},[{\\"lineHeight\\":24},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":10,\\"marginLeft\\":2,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"600\\"},{\\"color\\":\\"#6C727A\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"0\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":8,\\"height\\":8,\\"borderRadius\\":4,\\"marginRight\\":8},{\\"backgroundColor\\":\\"#095AD2\\"}]},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":24,\\"color\\":\\"#2F343D\\"},[{\\"lineHeight\\":24},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"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\\",\\"alignItems\\":\\"center\\",\\"marginTop\\":6,\\"marginBottom\\":12},\\"testID\\":\\"message-thread-replied-on-How are you?\\"},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#1D74F5\\"},[{\\"lineHeight\\":20},{\\"marginRight\\":10,\\"marginLeft\\":16}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"How are you?\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"},{\\"fontSize\\":16,\\"flex\\":1,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#1D74F5\\"}],\\"numberOfLines\\":1,\\"testID\\":\\"markdown-preview-How are you?\\"},\\"children\\":[\\"How are you?\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginLeft\\":4,\\"marginRight\\":4,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#6C727A\\"},[{\\"lineHeight\\":20},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":4},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"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=40\\",\\"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\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"I'm fine!\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}],\\"numberOfLines\\":1,\\"testID\\":\\"markdown-preview-I'm fine!\\"},\\"children\\":[\\"I'm fine!\\"]}]}]}]}]}]}]},{\\"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\\",\\"alignItems\\":\\"center\\",\\"marginTop\\":6,\\"marginBottom\\":12},\\"testID\\":\\"message-thread-replied-on-Thread with emoji :) :joy:\\"},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#1D74F5\\"},[{\\"lineHeight\\":20},{\\"marginRight\\":10,\\"marginLeft\\":16}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Thread with emoji🙂 😂\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"},{\\"fontSize\\":16,\\"flex\\":1,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#1D74F5\\"}],\\"numberOfLines\\":1,\\"testID\\":\\"markdown-preview-Thread with emoji🙂 😂\\"},\\"children\\":[\\"Thread with emoji🙂 😂\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginLeft\\":4,\\"marginRight\\":4,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#6C727A\\"},[{\\"lineHeight\\":20},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":4},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"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=40\\",\\"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\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"I'm fine!\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}],\\"numberOfLines\\":1,\\"testID\\":\\"markdown-preview-I'm fine!\\"},\\"children\\":[\\"I'm fine!\\"]}]}]}]}]}]}]},{\\"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\\",\\"alignItems\\":\\"center\\",\\"marginTop\\":6,\\"marginBottom\\":12},\\"testID\\":\\"message-thread-replied-on-Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\"},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#1D74F5\\"},[{\\"lineHeight\\":20},{\\"marginRight\\":10,\\"marginLeft\\":16}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"},{\\"fontSize\\":16,\\"flex\\":1,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#1D74F5\\"}],\\"numberOfLines\\":1,\\"testID\\":\\"markdown-preview-Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\"},\\"children\\":[\\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginLeft\\":4,\\"marginRight\\":4,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#6C727A\\"},[{\\"lineHeight\\":20},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":4},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"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=40\\",\\"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\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"I'm fine!\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}],\\"numberOfLines\\":1,\\"testID\\":\\"markdown-preview-I'm fine!\\"},\\"children\\":[\\"I'm fine!\\"]}]}]}]}]}]}]},{\\"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\\",\\"alignItems\\":\\"center\\",\\"marginTop\\":6,\\"marginBottom\\":12},\\"testID\\":\\"message-thread-replied-on-How are you?\\"},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#1D74F5\\"},[{\\"lineHeight\\":20},{\\"marginRight\\":10,\\"marginLeft\\":16}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"How are you?\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"},{\\"fontSize\\":16,\\"flex\\":1,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#1D74F5\\"}],\\"numberOfLines\\":1,\\"testID\\":\\"markdown-preview-How are you?\\"},\\"children\\":[\\"How are you?\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginLeft\\":4,\\"marginRight\\":4,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#6C727A\\"},[{\\"lineHeight\\":20},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":4},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"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=40\\",\\"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\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}],\\"numberOfLines\\":1,\\"testID\\":\\"markdown-preview-Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\"},\\"children\\":[\\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\"]}]}]}]}]}]}]},{\\"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\\",\\"alignItems\\":\\"center\\",\\"marginTop\\":6,\\"marginBottom\\":12},\\"testID\\":\\"message-thread-replied-on-Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\"},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#1D74F5\\"},[{\\"lineHeight\\":20},{\\"marginRight\\":10,\\"marginLeft\\":16}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"},{\\"fontSize\\":16,\\"flex\\":1,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#1D74F5\\"}],\\"numberOfLines\\":1,\\"testID\\":\\"markdown-preview-Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\"},\\"children\\":[\\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginLeft\\":4,\\"marginRight\\":4,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#6C727A\\"},[{\\"lineHeight\\":20},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":4},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"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=40\\",\\"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\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}],\\"numberOfLines\\":1,\\"testID\\":\\"markdown-preview-Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\"},\\"children\\":[\\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\\"]}]}]}]}]}]}]},{\\"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\\",\\"alignItems\\":\\"center\\",\\"marginTop\\":6,\\"marginBottom\\":12},\\"testID\\":\\"message-thread-replied-on-Thread with attachment\\"},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#1D74F5\\"},[{\\"lineHeight\\":20},{\\"marginRight\\":10,\\"marginLeft\\":16}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Thread with attachment\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"},{\\"fontSize\\":16,\\"flex\\":1,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#1D74F5\\"}],\\"numberOfLines\\":1,\\"testID\\":\\"markdown-preview-Thread with attachment\\"},\\"children\\":[\\"Thread with attachment\\"]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"marginLeft\\":4,\\"marginRight\\":4,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":20,\\"color\\":\\"#6C727A\\"},[{\\"lineHeight\\":20},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":4},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"collapsable\\":false,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"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=40\\",\\"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\\":{},\\"children\\":null}]}]}]}]}]}]}]}"`; @@ -80,8 +80,8 @@ 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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"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\\":\\"#EBECEF\\",\\"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,\\"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\\":\\"#EBECEF\\",\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"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,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"borderColor\\":\\"#CBCED1\\"}]]},\\"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\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#1F2329\\"}]},\\"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\\":\\"#6C727A\\",\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"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,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"borderColor\\":\\"#CBCED1\\"}]]},\\"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\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#1F2329\\"}]},\\"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\\":\\"#6C727A\\",\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"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,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\"},{\\"borderColor\\":\\"#CBCED1\\"}]]},\\"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 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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"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,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"borderColor\\":\\"#CBCED1\\"}]]},\\"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\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#E4E7EA\\"}]},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}]},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#6C727A\\",\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"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,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"borderColor\\":\\"#CBCED1\\"}]]},\\"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\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#E4E7EA\\"}]},\\"children\\":null},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"height\\":\\"100%\\"}],{\\"position\\":\\"absolute\\",\\"justifyContent\\":\\"center\\",\\"alignItems\\":\\"center\\"}]},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null],\\"color\\":\\"#6C727A\\",\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"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,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":\\"100%\\",\\"minHeight\\":200,\\"borderRadius\\":4,\\"borderWidth\\":1,\\"overflow\\":\\"hidden\\",\\"borderColor\\":\\"rgba(0, 0, 0, 0.1)\\"},{\\"borderColor\\":\\"#CBCED1\\"}]]},\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"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\\",\\"backgroundColor\\":\\"#1F2329\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"borderRadius\\":4,\\"height\\":150,\\"marginBottom\\":6,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"},{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#1F2329\\"}]},\\"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\\":\\"#6C727A\\",\\"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\\":\\"#6C727A\\"}]},\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"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\\",\\"backgroundColor\\":\\"#1F2329\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"borderRadius\\":4,\\"height\\":150,\\"marginBottom\\":6,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"},{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#1F2329\\"}]},\\"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\\":\\"#6C727A\\",\\"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\\":\\"#6C727A\\"}]},\\"children\\":[\\"Cancel\\"]}]}]}]}]}]}]}]}]}]}"`; +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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"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\\",\\"backgroundColor\\":\\"#1F2329\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"borderRadius\\":4,\\"height\\":150,\\"marginBottom\\":6,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"},{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#E4E7EA\\"}]},\\"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\\":\\"#6C727A\\",\\"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\\":\\"#6C727A\\"}]},\\"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,\\"testID\\":\\"username-header-diego.mello\\",\\"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\\":\\"#1F2329\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":13,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#6C727A\\"}]},\\"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\\",\\"backgroundColor\\":\\"#1F2329\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"borderRadius\\":4,\\"height\\":150,\\"marginBottom\\":6,\\"alignItems\\":\\"center\\",\\"justifyContent\\":\\"center\\"},{\\"position\\":\\"absolute\\",\\"borderWidth\\":0,\\"top\\":0,\\"left\\":0,\\"bottom\\":0,\\"right\\":0},{\\"backgroundColor\\":\\"#E4E7EA\\"}]},\\"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\\":\\"#6C727A\\",\\"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\\":\\"#6C727A\\"}]},\\"children\\":[\\"Cancel\\"]}]}]}]}]}]}]}]}]}]}"`; exports[`Storyshots Message Without Header 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\\":[{\\"flex\\":1,\\"marginLeft\\":46},false]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"Inter\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#2F343D\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Message\\",\\"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\\":[\\"Message\\"]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":null}]}]}]}]}]}]}"`; diff --git a/android/app/build.gradle b/android/app/build.gradle index 5c9807252..cd6013525 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -147,7 +147,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode VERSIONCODE as Integer - versionName "4.48.0" + versionName "4.49.0" vectorDrawables.useSupportLibrary = true if (!isFoss) { manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] diff --git a/app/containers/ImageViewer/ImageViewer.tsx b/app/containers/ImageViewer/ImageViewer.tsx index c0ff41457..c47ca1296 100644 --- a/app/containers/ImageViewer/ImageViewer.tsx +++ b/app/containers/ImageViewer/ImageViewer.tsx @@ -114,7 +114,7 @@ export const ImageViewer = ({ uri = '', imageComponentType, width, height, ...pr const { colors } = useTheme(); return ( - + { try { if (!rid) return; setRecordingAudio(false); - const fileURI = recordingRef.current?.getURI() as string; - const fileData = await getInfoAsync(fileURI); - - if (!fileData.exists) { - return; - } - - const fileInfo: IUpload = { - rid, + const fileURI = recordingRef.current?.getURI(); + const fileData = await getInfoAsync(fileURI as string); + const fileInfo = { name: `${Date.now()}${RECORDING_EXTENSION}`, + mime: 'audio/aac', type: 'audio/aac', store: 'Uploads', path: fileURI, - size: fileData.size - }; + size: fileData.exists ? fileData.size : null + } as IUpload; if (fileInfo) { if (permissionToUpload) { diff --git a/app/containers/MessageComposer/hooks/useAutoSaveDraft.ts b/app/containers/MessageComposer/hooks/useAutoSaveDraft.ts index 88bc7081b..a846237f9 100644 --- a/app/containers/MessageComposer/hooks/useAutoSaveDraft.ts +++ b/app/containers/MessageComposer/hooks/useAutoSaveDraft.ts @@ -1,24 +1,28 @@ +import { useRoute } from '@react-navigation/native'; import { useCallback, useEffect, useRef } from 'react'; +import { saveDraftMessage } from '../../../lib/methods/draftMessage'; import { useRoomContext } from '../../../views/RoomView/context'; import { useFocused } from '../context'; -import { saveDraftMessage } from '../../../lib/methods/draftMessage'; export const useAutoSaveDraft = (text = '') => { + const route = useRoute(); const { rid, tmid, action, selectedMessages } = useRoomContext(); const focused = useFocused(); const oldText = useRef(''); const intervalRef = useRef(); - const saveMessageDraft = useCallback(() => { - if (action === 'edit') return; + const mounted = useRef(true); + const saveMessageDraft = useCallback(() => { + if (route.name === 'ShareView') return; + if (action === 'edit') return; const draftMessage = selectedMessages?.length ? JSON.stringify({ quotes: selectedMessages, msg: text }) : text; - if (oldText.current !== draftMessage) { + if (oldText.current !== draftMessage || (oldText.current === '' && draftMessage === '')) { oldText.current = draftMessage; saveDraftMessage({ rid, tmid, draftMessage }); } - }, [action, rid, tmid, text, selectedMessages?.length]); + }, [action, rid, tmid, text, selectedMessages?.length, route.name]); useEffect(() => { if (focused) { @@ -29,7 +33,23 @@ export const useAutoSaveDraft = (text = '') => { return () => { clearInterval(intervalRef.current); - saveMessageDraft(); }; }, [focused, saveMessageDraft]); + + // hack to call saveMessageDraft when component is unmounted + useEffect(() => { + () => {}; + return () => { + mounted.current = false; + }; + }, []); + + useEffect( + () => () => { + if (!mounted.current) { + saveMessageDraft(); + } + }, + [saveMessageDraft] + ); }; diff --git a/app/containers/UIKit/VideoConferenceBlock/components/styles.ts b/app/containers/UIKit/VideoConferenceBlock/components/styles.ts index 523ff0029..26ab396e9 100644 --- a/app/containers/UIKit/VideoConferenceBlock/components/styles.ts +++ b/app/containers/UIKit/VideoConferenceBlock/components/styles.ts @@ -6,7 +6,7 @@ import sharedStyles from '../../../../views/Styles'; export default function useStyle() { const { colors } = useTheme(); const style = StyleSheet.create({ - container: { height: 108, flex: 1, borderWidth: 1, borderRadius: 4, marginTop: 8, borderColor: colors.surfaceHover }, + container: { height: 108, flex: 1, borderWidth: 1, borderRadius: 4, marginTop: 8, borderColor: colors.surfaceNeutral }, callInfoContainer: { flex: 1, alignItems: 'center', paddingLeft: 16, flexDirection: 'row' }, infoContainerText: { fontSize: 12, @@ -23,7 +23,7 @@ export default function useStyle() { }, callToActionContainer: { height: 48, - backgroundColor: colors.surfaceHover, + backgroundColor: colors.surfaceNeutral, flexDirection: 'row', alignItems: 'center', paddingLeft: 16 diff --git a/app/containers/message/Attachments.tsx b/app/containers/message/Attachments.tsx index d9cfcfa42..1e96dbaac 100644 --- a/app/containers/message/Attachments.tsx +++ b/app/containers/message/Attachments.tsx @@ -55,14 +55,14 @@ const AttachedActions = ({ attachment, getCustomEmoji }: { attachment: IAttachme const Attachments: React.FC = React.memo( ({ attachments, timeFormat, showAttachment, style, getCustomEmoji, isReply, author }: IMessageAttachments) => { - const { translateLanguage, isEncrypted } = useContext(MessageContext); + const { translateLanguage } = useContext(MessageContext); if (!attachments || attachments.length === 0) { return null; } const attachmentsElements = attachments.map((file: IAttachment, index: number) => { - const msg = isEncrypted ? '' : getMessageFromAttachment(file, translateLanguage); + const msg = getMessageFromAttachment(file, translateLanguage); if (file && file.image_url) { return ( - + {loading ? : } diff --git a/app/containers/message/index.tsx b/app/containers/message/index.tsx index d9e029fb8..a0572d8d8 100644 --- a/app/containers/message/index.tsx +++ b/app/containers/message/index.tsx @@ -428,8 +428,7 @@ class MessageContainer extends React.Component {/* @ts-ignore*/} diff --git a/app/containers/message/styles.ts b/app/containers/message/styles.ts index 00cfed73c..12ae4f960 100644 --- a/app/containers/message/styles.ts +++ b/app/containers/message/styles.ts @@ -103,7 +103,8 @@ export default StyleSheet.create({ minHeight: isTablet ? 300 : 200, borderRadius: 4, borderWidth: 1, - overflow: 'hidden' + overflow: 'hidden', + borderColor: 'rgba(0, 0, 0, 0.1)' }, imageBlurContainer: { height: '100%' diff --git a/app/definitions/IUpload.ts b/app/definitions/IUpload.ts index 187e34881..a2935575b 100644 --- a/app/definitions/IUpload.ts +++ b/app/definitions/IUpload.ts @@ -1,10 +1,8 @@ import Model from '@nozbe/watermelondb/Model'; -import { E2EType, MessageType } from './IMessage'; - export interface IUpload { id?: string; - rid: string; + rid?: string; path: string; name?: string; tmid?: string; @@ -16,8 +14,6 @@ export interface IUpload { error?: boolean; subscription?: { id: string }; msg?: string; - t?: MessageType; - e2e?: E2EType; } export type TUploadModel = IUpload & Model; diff --git a/app/lib/encryption/encryption.ts b/app/lib/encryption/encryption.ts index 7bbbba538..06f82d490 100644 --- a/app/lib/encryption/encryption.ts +++ b/app/lib/encryption/encryption.ts @@ -11,15 +11,7 @@ import log from '../methods/helpers/log'; import { store } from '../store/auxStore'; import { joinVectorData, randomPassword, splitVectorData, toString, utf8ToBuffer } from './utils'; import EncryptionRoom from './room'; -import { - IMessage, - ISubscription, - IUpload, - TMessageModel, - TSubscriptionModel, - TThreadMessageModel, - TThreadModel -} from '../../definitions'; +import { IMessage, ISubscription, TMessageModel, TSubscriptionModel, TThreadMessageModel, TThreadModel } from '../../definitions'; import { E2E_BANNER_TYPE, E2E_MESSAGE_TYPE, @@ -42,7 +34,6 @@ class Encryption { handshake: Function; decrypt: Function; encrypt: Function; - encryptUpload: Function; importRoomKey: Function; }; }; @@ -284,7 +275,7 @@ class Encryption { ]; toDecrypt = (await Promise.all( toDecrypt.map(async message => { - const { t, msg, tmsg, attachments } = message; + const { t, msg, tmsg } = message; let newMessage: TMessageModel = {} as TMessageModel; if (message.subscription) { const { id: rid } = message.subscription; @@ -293,8 +284,7 @@ class Encryption { t, rid, msg: msg as string, - tmsg, - attachments + tmsg }); } @@ -447,7 +437,7 @@ class Encryption { }; // Encrypt a message - encryptMessage = async (message: IMessage | IUpload) => { + encryptMessage = async (message: IMessage) => { const { rid } = message; const db = database.active; const subCollection = db.get('subscriptions'); @@ -469,10 +459,6 @@ class Encryption { } const roomE2E = await this.getRoomInstance(rid); - - if ('path' in message) { - return roomE2E.encryptUpload(message); - } return roomE2E.encrypt(message); } catch { // Subscription not found @@ -484,7 +470,7 @@ class Encryption { }; // Decrypt a message - decryptMessage = async (message: Pick) => { + decryptMessage = async (message: Pick) => { const { t, e2e } = message; // Prevent create a new instance if this room was encrypted sometime ago diff --git a/app/lib/encryption/room.ts b/app/lib/encryption/room.ts index 12bd1abed..2088e70a2 100644 --- a/app/lib/encryption/room.ts +++ b/app/lib/encryption/room.ts @@ -5,7 +5,7 @@ import ByteBuffer from 'bytebuffer'; import parse from 'url-parse'; import getSingleMessage from '../methods/getSingleMessage'; -import { IMessage, IUpload, IUser } from '../../definitions'; +import { IMessage, IUser } from '../../definitions'; import Deferred from './helpers/deferred'; import { debounce } from '../methods/helpers'; import database from '../database'; @@ -247,38 +247,8 @@ export default class EncryptionRoom { return message; }; - // Encrypt upload - encryptUpload = async (message: IUpload) => { - if (!this.ready) { - return message; - } - - try { - let description = ''; - - if (message.description) { - description = await this.encryptText(EJSON.stringify({ text: message.description })); - } - - return { - ...message, - t: E2E_MESSAGE_TYPE, - e2e: E2E_STATUS.PENDING, - description - }; - } catch { - // Do nothing - } - - return message; - }; - // Decrypt text decryptText = async (msg: string | ArrayBuffer) => { - if (!msg) { - return null; - } - msg = b64ToBuffer(msg.slice(12) as string); const [vector, cipherText] = splitVectorData(msg); @@ -309,10 +279,6 @@ export default class EncryptionRoom { tmsg = await this.decryptText(tmsg); } - if (message.attachments?.length) { - message.attachments[0].description = await this.decryptText(message.attachments[0].description as string); - } - const decryptedMessage: IMessage = { ...message, tmsg, diff --git a/app/lib/methods/helpers/emitter.ts b/app/lib/methods/helpers/emitter.ts index cc7c0ca13..3cc7f6c11 100644 --- a/app/lib/methods/helpers/emitter.ts +++ b/app/lib/methods/helpers/emitter.ts @@ -18,4 +18,5 @@ export type TKeyEmitterEvent = keyof TEmitterEvents; export const emitter = mitt(); -emitter.on('*', (type, e) => console.log(type, e)); +// uncomment the line below to log all events +// emitter.on('*', (type, e) => console.log(type, e)); diff --git a/app/lib/methods/helpers/sslPinning.ts b/app/lib/methods/helpers/sslPinning.ts index e82e659ac..abe1ecfa2 100644 --- a/app/lib/methods/helpers/sslPinning.ts +++ b/app/lib/methods/helpers/sslPinning.ts @@ -67,6 +67,7 @@ const RCSSLPinning = Platform.select({ certificate = persistCertificate(name, certificate.password); } UserPreferences.setMap(extractHostname(server), certificate); + SSLPinning?.setCertificate(server, certificate.path, certificate.password); } } }, diff --git a/app/lib/methods/sendFileMessage.ts b/app/lib/methods/sendFileMessage.ts index 8c1460752..8886f6064 100644 --- a/app/lib/methods/sendFileMessage.ts +++ b/app/lib/methods/sendFileMessage.ts @@ -4,14 +4,12 @@ import isEmpty from 'lodash/isEmpty'; import { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob'; import { Alert } from 'react-native'; -import { Encryption } from '../encryption'; import { IUpload, IUser, TUploadModel } from '../../definitions'; import i18n from '../../i18n'; import database from '../database'; import FileUpload from './helpers/fileUpload'; import { IFileUpload } from './helpers/fileUpload/interfaces'; import log from './helpers/log'; -import { E2E_MESSAGE_TYPE } from '../constants'; const uploadQueue: { [index: string]: StatefulPromise } = {}; @@ -87,8 +85,6 @@ export function sendFileMessage( } } - const encryptedFileInfo = await Encryption.encryptMessage(fileInfo); - const formData: IFileUpload[] = []; formData.push({ name: 'file', @@ -100,7 +96,7 @@ export function sendFileMessage( if (fileInfo.description) { formData.push({ name: 'description', - data: encryptedFileInfo.description + data: fileInfo.description }); } @@ -118,17 +114,6 @@ export function sendFileMessage( }); } - if (encryptedFileInfo.t === E2E_MESSAGE_TYPE) { - formData.push({ - name: 't', - data: encryptedFileInfo.t - }); - formData.push({ - name: 'e2e', - data: encryptedFileInfo.e2e - }); - } - const headers = { ...RocketChatSettings.customHeaders, 'Content-Type': 'multipart/form-data', diff --git a/app/lib/methods/sendMessage.ts b/app/lib/methods/sendMessage.ts index 9dbc6bbb3..1702cc0b1 100644 --- a/app/lib/methods/sendMessage.ts +++ b/app/lib/methods/sendMessage.ts @@ -8,7 +8,6 @@ import { Encryption } from '../encryption'; import { E2EType, IMessage, IUser, TMessageModel } from '../../definitions'; import sdk from '../services/sdk'; import { E2E_MESSAGE_TYPE, E2E_STATUS, messagesStatus } from '../constants'; -import { saveDraftMessage } from './draftMessage'; const changeMessageStatus = async (id: string, status: number, tmid?: string, message?: IMessage) => { const db = database.active; @@ -232,9 +231,6 @@ export async function sendMessage( } await sendMessageCall(message); - // clear draft message when message is sent and app is in background or closed - // do not affect the user experience when the app is in the foreground because the hook useAutoSaveDraft will handle it - saveDraftMessage({ rid, tmid, draftMessage: '' }); } catch (e) { log(e); } diff --git a/app/lib/services/connect.ts b/app/lib/services/connect.ts index 92dc4f0cf..527a9653f 100644 --- a/app/lib/services/connect.ts +++ b/app/lib/services/connect.ts @@ -9,7 +9,7 @@ import protectedFunction from '../methods/helpers/protectedFunction'; import database from '../database'; import { twoFactor } from './twoFactor'; import { store } from '../store/auxStore'; -import { loginRequest, setLoginServices, setUser } from '../../actions/login'; +import { loginRequest, logout, setLoginServices, setUser } from '../../actions/login'; import sdk from './sdk'; import I18n from '../../i18n'; import { ICredentials, ILoggedUser, STATUSES } from '../../definitions'; @@ -46,6 +46,7 @@ let usersListener: any; let notifyAllListener: any; let rolesListener: any; let notifyLoggedListener: any; +let logoutListener: any; function connect({ server, logoutOnError = false }: { server: string; logoutOnError?: boolean }): Promise { return new Promise(resolve => { @@ -89,6 +90,10 @@ function connect({ server, logoutOnError = false }: { server: string; logoutOnEr notifyLoggedListener.then(stopListener); } + if (logoutListener) { + logoutListener.then(stopListener); + } + unsubscribeRooms(); EventEmitter.emit('INQUIRY_UNSUBSCRIBE'); @@ -270,6 +275,8 @@ function connect({ server, logoutOnError = false }: { server: string; logoutOnEr }) ); + logoutListener = sdk.current.onStreamData('stream-force_logout', () => store.dispatch(logout(true))); + resolve(); }); } diff --git a/app/views/AttachmentView.tsx b/app/views/AttachmentView.tsx index 3748bfac5..28d375d83 100644 --- a/app/views/AttachmentView.tsx +++ b/app/views/AttachmentView.tsx @@ -121,19 +121,21 @@ const AttachmentView = (): React.ReactElement => { const options: StackNavigationOptions = { title: title || '', headerTitleAlign: 'center', - headerTitleStyle: { color: colors.surfaceTint }, + headerTitleStyle: { color: colors.fontDefault }, headerTintColor: colors.surfaceTint, headerTitleContainerStyle: { flex: 1, maxWidth: undefined }, headerLeftContainerStyle: { flexGrow: undefined, flexBasis: undefined }, headerRightContainerStyle: { flexGrow: undefined, flexBasis: undefined }, headerLeft: () => ( - + ), headerRight: () => Allow_Save_Media_to_Gallery && !isImageBase64(attachment.image_url) ? ( - + ) : null, - headerBackground: () => + headerBackground: () => ( + + ) }; navigation.setOptions(options); }; diff --git a/app/views/CreateChannelView/RoomSettings/SwitchItemEncrypted.test.tsx b/app/views/CreateChannelView/RoomSettings/SwitchItemEncrypted.test.tsx index 1f18b44a6..a5f536d5a 100644 --- a/app/views/CreateChannelView/RoomSettings/SwitchItemEncrypted.test.tsx +++ b/app/views/CreateChannelView/RoomSettings/SwitchItemEncrypted.test.tsx @@ -45,7 +45,7 @@ describe('SwitchItemEncrypted', () => { const component = screen.queryByTestId(testEncrypted.testSwitchID); expect(component).toBeTruthy(); }); - + it('should change value of switch', () => { render( { expect(onPressMock).toHaveReturnedWith({ value: !testEncrypted.encrypted }); } }); - + it('label when encrypted and isTeam are false and is a public channel', () => { render( { const component = screen.queryByTestId(testEncrypted.testLabelID); expect(component?.props.children).toBe(i18n.t('Channel_hint_encrypted_not_available')); }); - + it('label when encrypted and isTeam are true and is a private team', () => { testEncrypted.isTeam = true; testEncrypted.type = true; diff --git a/app/views/RoomView/index.tsx b/app/views/RoomView/index.tsx index d4db2ee0b..39ddc6167 100644 --- a/app/views/RoomView/index.tsx +++ b/app/views/RoomView/index.tsx @@ -1006,7 +1006,9 @@ class RoomView extends React.Component { const { rid } = this.state.room; const { user } = this.props; sendMessage(rid, message, this.tmid, user, tshow).then(() => { - this.setLastOpen(null); + if (this.mounted) { + this.setLastOpen(null); + } Review.pushPositiveEvent(); }); this.resetAction(); diff --git a/app/views/ShareView/Header.tsx b/app/views/ShareView/Header.tsx index 14ad49fd5..0e8113305 100644 --- a/app/views/ShareView/Header.tsx +++ b/app/views/ShareView/Header.tsx @@ -68,7 +68,7 @@ const Header = React.memo(({ room, thread }: IHeader) => { icon = 'channel-private'; } - const textColor = themes[theme].surfaceTint; + const textColor = themes[theme].fontDefault; let title; if (thread?.id) { diff --git a/app/views/ShareView/Preview.tsx b/app/views/ShareView/Preview.tsx index b9c379606..444030044 100644 --- a/app/views/ShareView/Preview.tsx +++ b/app/views/ShareView/Preview.tsx @@ -48,7 +48,7 @@ interface IIconPreview { const IconPreview = React.memo(({ iconName, title, description, theme, width, height, danger }: IIconPreview) => ( { const options: StackNavigationOptions = { headerTitle: () =>
, headerTitleAlign: 'left', - headerTintColor: themes[theme].surfaceTint + headerTintColor: themes[theme].backdropColor }; // if is share extension show default back button if (!this.isShareExtension) { options.headerLeft = () => ( - + ); } if (!attachments.length && !readOnly) { options.headerRight = () => ( - + ); } - options.headerBackground = () => ; + options.headerBackground = () => ; navigation.setOptions(options); }; @@ -257,7 +257,6 @@ class ShareView extends Component { return sendFileMessage( room.rid, { - rid: room.rid, name, description, size, @@ -351,8 +350,7 @@ class ShareView extends Component { selectedMessages, onSendMessage: this.send, onRemoveQuoteMessage: this.onRemoveQuoteMessage - }} - > + }}> { ); } return ( - + {this.renderContent()} diff --git a/ios/Experimental.xcassets/AppIcon.appiconset/1024 1.png b/ios/Experimental.xcassets/AppIcon.appiconset/1024 1.png new file mode 100644 index 000000000..f551e843f Binary files /dev/null and b/ios/Experimental.xcassets/AppIcon.appiconset/1024 1.png differ diff --git a/ios/Experimental.xcassets/AppIcon.appiconset/Contents.json b/ios/Experimental.xcassets/AppIcon.appiconset/Contents.json index 4fdf88263..cdcbf4fd0 100644 --- a/ios/Experimental.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Experimental.xcassets/AppIcon.appiconset/Contents.json @@ -149,6 +149,12 @@ "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" + }, + { + "filename" : "1024 1.png", + "idiom" : "universal", + "platform" : "watchos", + "size" : "1024x1024" } ], "info" : { diff --git a/ios/Official.xcassets/AppIcon.appiconset/1024 1.png b/ios/Official.xcassets/AppIcon.appiconset/1024 1.png new file mode 100644 index 000000000..e737a61ea Binary files /dev/null and b/ios/Official.xcassets/AppIcon.appiconset/1024 1.png differ diff --git a/ios/Official.xcassets/AppIcon.appiconset/Contents.json b/ios/Official.xcassets/AppIcon.appiconset/Contents.json index 4fdf88263..cdcbf4fd0 100644 --- a/ios/Official.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Official.xcassets/AppIcon.appiconset/Contents.json @@ -149,6 +149,12 @@ "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" + }, + { + "filename" : "1024 1.png", + "idiom" : "universal", + "platform" : "watchos", + "size" : "1024x1024" } ], "info" : { diff --git a/ios/RocketChat Watch App/ActionHandler/ErrorActionHandler.swift b/ios/RocketChat Watch App/ActionHandler/ErrorActionHandler.swift new file mode 100644 index 000000000..5a2a7984b --- /dev/null +++ b/ios/RocketChat Watch App/ActionHandler/ErrorActionHandler.swift @@ -0,0 +1,39 @@ +import Foundation + +protocol ErrorActionHandling { + func handle(error: RocketChatError) +} + +final class ErrorActionHandler { + @Dependency private var database: Database + @Dependency private var serversDB: ServersDatabase + @Dependency private var router: AppRouting + + private let server: Server + + init(server: Server) { + self.server = server + } + + private func handleOnMain(error: RocketChatError) { + switch error { + case .server(let response): + router.present(error: response) + case .unauthorized: + router.route(to: [.loading, .serverList]) { + self.database.remove() + self.serversDB.remove(self.server) + } + case .unknown: + print("Unexpected error on Client.") + } + } +} + +extension ErrorActionHandler: ErrorActionHandling { + func handle(error: RocketChatError) { + DispatchQueue.main.async { + self.handleOnMain(error: error) + } + } +} diff --git a/ios/RocketChat Watch App/AppRouter.swift b/ios/RocketChat Watch App/AppRouter.swift new file mode 100644 index 000000000..8abf1d8e1 --- /dev/null +++ b/ios/RocketChat Watch App/AppRouter.swift @@ -0,0 +1,83 @@ +import Foundation + +protocol AppRouting { + func route(to route: Route) + func present(error: ErrorResponse) + func route(to routes: [Route], completion: (() -> Void)?) +} + +final class AppRouter: ObservableObject { + @Published var error: ErrorResponse? + + @Published var server: Server? { + didSet { + if server != oldValue, let server { + registerDependencies(in: server) + } + } + } + + @Published var room: Room? + + @Storage(.currentServer) private var currentURL: URL? + + private func registerDependencies(in server: Server) { + Store.register(Database.self, factory: server.database) + Store.register(RocketChatClientProtocol.self, factory: RocketChatClient(server: server)) + Store.register(MessageSending.self, factory: MessageSender(server: server)) + Store.register(ErrorActionHandling.self, factory: ErrorActionHandler(server: server)) + Store.register(MessagesLoading.self, factory: MessagesLoader()) + Store.register(RoomsLoader.self, factory: RoomsLoader(server: server)) + } +} + +extension AppRouter: AppRouting { + func route(to route: Route) { + switch route { + case .roomList(let selectedServer): + currentURL = selectedServer.url + room = nil + server = selectedServer + case .room(let selectedServer, let selectedRoom): + currentURL = selectedServer.url + server = selectedServer + room = selectedRoom + case .serverList: + currentURL = nil + room = nil + server = nil + case .loading: + room = nil + server = nil + } + } + + func present(error: ErrorResponse) { + guard self.error == nil else { + return + } + + self.error = error + } +} + +extension AppRouter { + func route(to routes: [Route], completion: (() -> Void)? = nil) { + guard let routeTo = routes.first else { + completion?() + return + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.route(to: routeTo) + self.route(to: Array(routes[1.. URLRequest { + var request = urlRequest + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + request.addValue("application/json", forHTTPHeaderField: "Accept") + return request + } +} diff --git a/ios/RocketChat Watch App/Client/Adapters/RequestAdapter.swift b/ios/RocketChat Watch App/Client/Adapters/RequestAdapter.swift new file mode 100644 index 000000000..5ceefed87 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Adapters/RequestAdapter.swift @@ -0,0 +1,12 @@ +import Foundation + +protocol RequestAdapter { + func adapt(_ urlRequest: URLRequest) -> URLRequest + func adapt(_ url: URL) -> URL +} + +extension RequestAdapter { + func adapt(_ url: URL) -> URL { + url + } +} diff --git a/ios/RocketChat Watch App/Client/Adapters/TokenAdapter.swift b/ios/RocketChat Watch App/Client/Adapters/TokenAdapter.swift new file mode 100644 index 000000000..e96839766 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Adapters/TokenAdapter.swift @@ -0,0 +1,25 @@ +import Foundation + +struct TokenAdapter: RequestAdapter { + private let server: Server + + init(server: Server) { + self.server = server + } + + func adapt(_ url: URL) -> URL { + url.appending( + queryItems: [ + URLQueryItem(name: "rc_token", value: server.loggedUser.token), + URLQueryItem(name: "rc_uid", value: server.loggedUser.id) + ] + ) + } + + func adapt(_ urlRequest: URLRequest) -> URLRequest { + var request = urlRequest + request.addValue(server.loggedUser.id, forHTTPHeaderField: "x-user-id") + request.addValue(server.loggedUser.token, forHTTPHeaderField: "x-auth-token") + return request + } +} diff --git a/ios/RocketChat Watch App/Client/DateCodingStrategy.swift b/ios/RocketChat Watch App/Client/DateCodingStrategy.swift new file mode 100644 index 000000000..f29e2d320 --- /dev/null +++ b/ios/RocketChat Watch App/Client/DateCodingStrategy.swift @@ -0,0 +1,44 @@ +// https://stackoverflow.com/a/28016692 + +import Foundation + +extension Date.ISO8601FormatStyle { + static let iso8601withFractionalSeconds: Self = .init(includingFractionalSeconds: true) +} + +extension ParseStrategy where Self == Date.ISO8601FormatStyle { + static var iso8601withFractionalSeconds: Date.ISO8601FormatStyle { .iso8601withFractionalSeconds } +} + +extension FormatStyle where Self == Date.ISO8601FormatStyle { + static var iso8601withFractionalSeconds: Date.ISO8601FormatStyle { .iso8601withFractionalSeconds } +} + +extension Date { + init(iso8601withFractionalSeconds parseInput: ParseStrategy.ParseInput) throws { + try self.init(parseInput, strategy: .iso8601withFractionalSeconds) + } + + var iso8601withFractionalSeconds: String { + formatted(.iso8601withFractionalSeconds) + } +} + +extension String { + func iso8601withFractionalSeconds() throws -> Date { + try .init(iso8601withFractionalSeconds: self) + } +} + +extension JSONDecoder.DateDecodingStrategy { + static let iso8601withFractionalSeconds = custom { + try .init(iso8601withFractionalSeconds: $0.singleValueContainer().decode(String.self)) + } +} + +extension JSONEncoder.DateEncodingStrategy { + static let iso8601withFractionalSeconds = custom { + var container = $1.singleValueContainer() + try container.encode($0.iso8601withFractionalSeconds) + } +} diff --git a/ios/RocketChat Watch App/Client/Extensions/Data+Extensions.swift b/ios/RocketChat Watch App/Client/Extensions/Data+Extensions.swift new file mode 100644 index 000000000..517c2e63e --- /dev/null +++ b/ios/RocketChat Watch App/Client/Extensions/Data+Extensions.swift @@ -0,0 +1,9 @@ +import Foundation + +extension Data { + func decode(_ type: T.Type) throws -> T { + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601withFractionalSeconds + return try decoder.decode(T.self, from: self) + } +} diff --git a/ios/RocketChat Watch App/Client/Extensions/String+Extensions.swift b/ios/RocketChat Watch App/Client/Extensions/String+Extensions.swift new file mode 100644 index 000000000..342e9edf6 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Extensions/String+Extensions.swift @@ -0,0 +1,6 @@ +extension String { + static func random(_ count: Int) -> String { + let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + return String((0.. Self { + var components = URLComponents(url: self, resolvingAgainstBaseURL: true) + + components?.queryItems = queryItems + + return components?.url ?? self + } + + func appending(path: String) -> Self { + appendingPathComponent(path) + } +} diff --git a/ios/RocketChat Watch App/Client/FailableDecodable.swift b/ios/RocketChat Watch App/Client/FailableDecodable.swift new file mode 100644 index 000000000..c2e07097b --- /dev/null +++ b/ios/RocketChat Watch App/Client/FailableDecodable.swift @@ -0,0 +1,13 @@ +struct FailableDecodable: Codable, Hashable { + let value: Value? + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + value = try? container.decode(Value.self) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(value) + } +} diff --git a/ios/RocketChat Watch App/Client/HTTP/HTTPMethod.swift b/ios/RocketChat Watch App/Client/HTTP/HTTPMethod.swift new file mode 100644 index 000000000..4a62a7c81 --- /dev/null +++ b/ios/RocketChat Watch App/Client/HTTP/HTTPMethod.swift @@ -0,0 +1,4 @@ +enum HTTPMethod: String { + case get = "GET" + case post = "POST" +} diff --git a/ios/RocketChat Watch App/Client/HTTP/Request.swift b/ios/RocketChat Watch App/Client/HTTP/Request.swift new file mode 100644 index 000000000..00e037774 --- /dev/null +++ b/ios/RocketChat Watch App/Client/HTTP/Request.swift @@ -0,0 +1,24 @@ +import Foundation + +protocol Request { + associatedtype Response: Codable + + var path: String { get } + var method: HTTPMethod { get } + var body: Data? { get } + var queryItems: [URLQueryItem] { get } +} + +extension Request { + var method: HTTPMethod { + .get + } + + var body: Data? { + nil + } + + var queryItems: [URLQueryItem] { + [] + } +} diff --git a/ios/RocketChat Watch App/Client/Requests/HistoryRequest.swift b/ios/RocketChat Watch App/Client/Requests/HistoryRequest.swift new file mode 100644 index 000000000..741904e9e --- /dev/null +++ b/ios/RocketChat Watch App/Client/Requests/HistoryRequest.swift @@ -0,0 +1,46 @@ +import Foundation + +let HISTORY_MESSAGE_COUNT = 50 + +struct HistoryRequest: Request { + typealias Response = HistoryResponse + + let path: String + let queryItems: [URLQueryItem] + + init(roomId: String, roomType: String?, latest: Date) { + path = "api/v1/\(RoomType.from(roomType).api).history" + + queryItems = [ + URLQueryItem(name: "roomId", value: roomId), + URLQueryItem(name: "count", value: String(HISTORY_MESSAGE_COUNT)), + URLQueryItem(name: "latest", value: latest.iso8601withFractionalSeconds) + ] + } +} + +fileprivate enum RoomType: String { + case direct = "d" + case group = "p" + case channel = "c" + case livechat = "l" + + static func from(_ rawValue: String?) -> Self { + guard let rawValue, let type = RoomType(rawValue: rawValue) else { + return .channel + } + + return type + } + + var api: String { + switch self { + case .direct: + return "im" + case .group: + return "groups" + case .channel, .livechat: + return "channels" + } + } +} diff --git a/ios/RocketChat Watch App/Client/Requests/MessagesRequest.swift b/ios/RocketChat Watch App/Client/Requests/MessagesRequest.swift new file mode 100644 index 000000000..b6be1b596 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Requests/MessagesRequest.swift @@ -0,0 +1,15 @@ +import Foundation + +struct MessagesRequest: Request { + typealias Response = MessagesResponse + + let path: String = "api/v1/chat.syncMessages" + let queryItems: [URLQueryItem] + + init(lastUpdate: Date?, roomId: String) { + self.queryItems = [ + URLQueryItem(name: "roomId", value: roomId), + URLQueryItem(name: "lastUpdate", value: lastUpdate?.ISO8601Format()) + ] + } +} diff --git a/ios/RocketChat Watch App/Client/Requests/ReadRequest.swift b/ios/RocketChat Watch App/Client/Requests/ReadRequest.swift new file mode 100644 index 000000000..545f985b7 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Requests/ReadRequest.swift @@ -0,0 +1,20 @@ +import Foundation + +struct ReadRequest: Request { + typealias Response = ReadResponse + + let path: String = "api/v1/subscriptions.read" + let method: HTTPMethod = .post + + var body: Data? { + try? JSONSerialization.data(withJSONObject: [ + "rid": rid + ]) + } + + let rid: String + + init(rid: String) { + self.rid = rid + } +} diff --git a/ios/RocketChat Watch App/Client/Requests/RoomsRequest.swift b/ios/RocketChat Watch App/Client/Requests/RoomsRequest.swift new file mode 100644 index 000000000..a06f1e48d --- /dev/null +++ b/ios/RocketChat Watch App/Client/Requests/RoomsRequest.swift @@ -0,0 +1,16 @@ +import Foundation + +struct RoomsRequest: Request { + typealias Response = RoomsResponse + + let path: String = "api/v1/rooms.get" + let queryItems: [URLQueryItem] + + init(updatedSince: Date?) { + if let updatedSince { + queryItems = [URLQueryItem(name: "updatedSince", value: updatedSince.iso8601withFractionalSeconds)] + } else { + queryItems = [] + } + } +} diff --git a/ios/RocketChat Watch App/Client/Requests/SendMessageRequest.swift b/ios/RocketChat Watch App/Client/Requests/SendMessageRequest.swift new file mode 100644 index 000000000..8f5502f04 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Requests/SendMessageRequest.swift @@ -0,0 +1,29 @@ +import Foundation + +struct SendMessageRequest: Request { + typealias Response = SendMessageResponse + + let path: String = "api/v1/chat.sendMessage" + let method: HTTPMethod = .post + + var body: Data? { + try? JSONSerialization.data(withJSONObject: [ + "message": [ + "_id": id, + "rid": rid, + "msg": msg, + "tshow": false + ] + ]) + } + + let id: String + let rid: String + let msg: String + + init(id: String, rid: String, msg: String) { + self.id = id + self.rid = rid + self.msg = msg + } +} diff --git a/ios/RocketChat Watch App/Client/Requests/SubscriptionsRequest.swift b/ios/RocketChat Watch App/Client/Requests/SubscriptionsRequest.swift new file mode 100644 index 000000000..0b788b30d --- /dev/null +++ b/ios/RocketChat Watch App/Client/Requests/SubscriptionsRequest.swift @@ -0,0 +1,16 @@ +import Foundation + +struct SubscriptionsRequest: Request { + typealias Response = SubscriptionsResponse + + let path: String = "api/v1/subscriptions.get" + let queryItems: [URLQueryItem] + + init(updatedSince: Date?) { + if let updatedSince { + queryItems = [URLQueryItem(name: "updatedSince", value: updatedSince.iso8601withFractionalSeconds)] + } else { + queryItems = [] + } + } +} diff --git a/ios/RocketChat Watch App/Client/Responses/AttachmentResponse.swift b/ios/RocketChat Watch App/Client/Responses/AttachmentResponse.swift new file mode 100644 index 000000000..6be4fe708 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Responses/AttachmentResponse.swift @@ -0,0 +1,22 @@ +import Foundation + +struct AttachmentResponse: Codable, Hashable { + let title: String? + let imageURL: URL? + let audioURL: URL? + let description: String? + let dimensions: DimensionsResponse? + + enum CodingKeys: String, CodingKey { + case imageURL = "image_url" + case audioURL = "audio_url" + case title + case description + case dimensions = "image_dimensions" + } +} + +struct DimensionsResponse: Codable, Hashable { + let width: Double + let height: Double +} diff --git a/ios/RocketChat Watch App/Client/Responses/HistoryResponse.swift b/ios/RocketChat Watch App/Client/Responses/HistoryResponse.swift new file mode 100644 index 000000000..a4ef7211f --- /dev/null +++ b/ios/RocketChat Watch App/Client/Responses/HistoryResponse.swift @@ -0,0 +1,6 @@ +import Foundation + +struct HistoryResponse: Codable { + let messages: [MessageResponse] + let success: Bool +} diff --git a/ios/RocketChat Watch App/Client/Responses/MessageResponse.swift b/ios/RocketChat Watch App/Client/Responses/MessageResponse.swift new file mode 100644 index 000000000..2ea253284 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Responses/MessageResponse.swift @@ -0,0 +1,15 @@ +import Foundation + +struct MessageResponse: Codable, Hashable { + let _id: String + let rid: String + let msg: String + let u: UserResponse + let ts: Date + let attachments: [AttachmentResponse]? + let t: String? + let groupable: Bool? + let editedAt: Date? + let role: String? + let comment: String? +} diff --git a/ios/RocketChat Watch App/Client/Responses/MessagesResponse.swift b/ios/RocketChat Watch App/Client/Responses/MessagesResponse.swift new file mode 100644 index 000000000..696f360b4 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Responses/MessagesResponse.swift @@ -0,0 +1,11 @@ +import Foundation + +struct MessagesResponse: Codable { + let result: MessagesResult + let success: Bool + + struct MessagesResult: Codable { + let updated: [MessageResponse] + let deleted: [MessageResponse] + } +} diff --git a/ios/RocketChat Watch App/Client/Responses/ReadResponse.swift b/ios/RocketChat Watch App/Client/Responses/ReadResponse.swift new file mode 100644 index 000000000..57eccc7ea --- /dev/null +++ b/ios/RocketChat Watch App/Client/Responses/ReadResponse.swift @@ -0,0 +1,5 @@ +import Foundation + +struct ReadResponse: Codable { + +} diff --git a/ios/RocketChat Watch App/Client/Responses/RoomsResponse.swift b/ios/RocketChat Watch App/Client/Responses/RoomsResponse.swift new file mode 100644 index 000000000..a03692b13 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Responses/RoomsResponse.swift @@ -0,0 +1,26 @@ +import Foundation + +struct RoomsResponse: Codable { + let update: Set + let remove: Set + let success: Bool + + struct Room: Codable, Hashable { + let _id: String + let name: String? + let fname: String? + let prid: String? + let t: String? + let ts: Date? + let ro: Bool? + let _updatedAt: Date? + let encrypted: Bool? + let usernames: [String]? + let uids: [String]? + let lastMessage: FailableDecodable? + let lm: Date? + let teamMain: Bool? + let archived: Bool? + let broadcast: Bool? + } +} diff --git a/ios/RocketChat Watch App/Client/Responses/SendMessageResponse.swift b/ios/RocketChat Watch App/Client/Responses/SendMessageResponse.swift new file mode 100644 index 000000000..5ecf7aca4 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Responses/SendMessageResponse.swift @@ -0,0 +1,6 @@ +import Foundation + +struct SendMessageResponse: Codable { + let message: MessageResponse + let success: Bool +} diff --git a/ios/RocketChat Watch App/Client/Responses/SubscriptionsResponse.swift b/ios/RocketChat Watch App/Client/Responses/SubscriptionsResponse.swift new file mode 100644 index 000000000..e86c99448 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Responses/SubscriptionsResponse.swift @@ -0,0 +1,29 @@ +import Foundation + +struct SubscriptionsResponse: Codable { + let update: Set + let remove: Set + let success: Bool + + struct Subscription: Codable, Hashable { + let _id: String + let rid: String + let name: String? + let fname: String? + let t: String + let unread: Int + let alert: Bool + let lr: Date? + let open: Bool? + let _updatedAt: Date? + let hideUnreadStatus: Bool? + } +} + +extension Sequence where Element == SubscriptionsResponse.Subscription { + func find(withRoomID rid: String) -> SubscriptionsResponse.Subscription? { + first { subscription in + subscription.rid == rid + } + } +} diff --git a/ios/RocketChat Watch App/Client/Responses/UserResponse.swift b/ios/RocketChat Watch App/Client/Responses/UserResponse.swift new file mode 100644 index 000000000..981ea2111 --- /dev/null +++ b/ios/RocketChat Watch App/Client/Responses/UserResponse.swift @@ -0,0 +1,7 @@ +import Foundation + +struct UserResponse: Codable, Hashable { + let _id: String + let username: String + let name: String? +} diff --git a/ios/RocketChat Watch App/Client/RocketChatClient.swift b/ios/RocketChat Watch App/Client/RocketChatClient.swift new file mode 100644 index 000000000..574c31c42 --- /dev/null +++ b/ios/RocketChat Watch App/Client/RocketChatClient.swift @@ -0,0 +1,107 @@ +import Combine +import Foundation + +protocol RocketChatClientProtocol { + var session: URLSession { get } + + func authorizedURL(url: URL) -> URL + func getRooms(updatedSince: Date?) -> AnyPublisher + func getSubscriptions(updatedSince: Date?) -> AnyPublisher + func getHistory(rid: String, t: String, latest: Date) -> AnyPublisher + func syncMessages(rid: String, updatedSince: Date) -> AnyPublisher + func sendMessage(id: String, rid: String, msg: String) -> AnyPublisher + func sendRead(rid: String) -> AnyPublisher +} + +final class RocketChatClient: NSObject { + @Dependency private var errorActionHandler: ErrorActionHandling + + private let server: Server + + init(server: Server) { + self.server = server + } + + lazy var session = URLSession( + configuration: .default, + delegate: URLSesionClientCertificateHandling( + certificate: server.certificate, + password: server.password + ), + delegateQueue: nil + ) + + private var adapters: [RequestAdapter] { + [ + TokenAdapter(server: server), + JSONAdapter() + ] + } + + private func dataTask(for request: T) -> AnyPublisher { + let url = server.url.appending(path: request.path).appending(queryItems: request.queryItems) + + var urlRequest = adapters.reduce(URLRequest(url: url), { $1.adapt($0) }) + urlRequest.httpMethod = request.method.rawValue + urlRequest.httpBody = request.body + + return session.dataTaskPublisher(for: urlRequest) + .tryMap { data, response in + if let response = response as? HTTPURLResponse, response.statusCode == 401 { + throw RocketChatError.unauthorized + } + + if let response = try? data.decode(T.Response.self) { + return response + } + + let response = try data.decode(ErrorResponse.self) + throw RocketChatError.server(response: response) + } + .mapError { [weak self] error in + guard let error = error as? RocketChatError else { + return .unknown + } + + self?.errorActionHandler.handle(error: error) + return error + } + .eraseToAnyPublisher() + } +} + +extension RocketChatClient: RocketChatClientProtocol { + func authorizedURL(url: URL) -> URL { + adapters.reduce(server.url.appending(path: url.relativePath), { $1.adapt($0) }) + } + + func getRooms(updatedSince: Date?) -> AnyPublisher { + let request = RoomsRequest(updatedSince: updatedSince) + return dataTask(for: request) + } + + func getSubscriptions(updatedSince: Date?) -> AnyPublisher { + let request = SubscriptionsRequest(updatedSince: updatedSince) + return dataTask(for: request) + } + + func getHistory(rid: String, t: String, latest: Date) -> AnyPublisher { + let request = HistoryRequest(roomId: rid, roomType: t, latest: latest) + return dataTask(for: request) + } + + func syncMessages(rid: String, updatedSince: Date) -> AnyPublisher { + let request = MessagesRequest(lastUpdate: updatedSince, roomId: rid) + return dataTask(for: request) + } + + func sendMessage(id: String, rid: String, msg: String) -> AnyPublisher { + let request = SendMessageRequest(id: id, rid: rid, msg: msg) + return dataTask(for: request) + } + + func sendRead(rid: String) -> AnyPublisher { + let request = ReadRequest(rid: rid) + return dataTask(for: request) + } +} diff --git a/ios/RocketChat Watch App/Client/RocketChatError.swift b/ios/RocketChat Watch App/Client/RocketChatError.swift new file mode 100644 index 000000000..ab47047fe --- /dev/null +++ b/ios/RocketChat Watch App/Client/RocketChatError.swift @@ -0,0 +1,15 @@ +import Foundation + +struct ErrorResponse: Codable, Identifiable { + var id: String { + error + } + + let error: String +} + +enum RocketChatError: Error { + case server(response: ErrorResponse) + case unauthorized + case unknown +} diff --git a/ios/RocketChat Watch App/Client/URLSessionCertificateHandling.swift b/ios/RocketChat Watch App/Client/URLSessionCertificateHandling.swift new file mode 100644 index 000000000..b00cc2237 --- /dev/null +++ b/ios/RocketChat Watch App/Client/URLSessionCertificateHandling.swift @@ -0,0 +1,95 @@ +// https://medium.com/@hamidptb/implementing-mtls-on-ios-using-urlsession-and-cloudflare-890b76aca66c + +import Foundation + +final class URLSesionClientCertificateHandling: NSObject, URLSessionDelegate { + private let certificate: Data? + private let password: String? + + init(certificate: Data?, password: String?) { + self.certificate = certificate + self.password = password + } + + public func urlSession( + _: URLSession, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void + ) { + guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate else { + completionHandler(.performDefaultHandling, nil) + return + } + + guard let credential = Credentials.urlCredential(certificate: certificate, password: password) else { + completionHandler(.performDefaultHandling, nil) + return + } + + challenge.sender?.use(credential, for: challenge) + completionHandler(.useCredential, credential) + } +} + +fileprivate typealias UserCertificate = (data: Data, password: String) + +fileprivate final class Credentials { + static func urlCredential(certificate: Data?, password: String?) -> URLCredential? { + guard let certificate, let password else { return nil } + + let p12Contents = PKCS12(pkcs12Data: certificate, password: password) + + guard let identity = p12Contents.identity else { + return nil + } + + return URLCredential(identity: identity, certificates: nil, persistence: .none) + } +} + +fileprivate struct PKCS12 { + let label: String? + let keyID: NSData? + let trust: SecTrust? + let certChain: [SecTrust]? + let identity: SecIdentity? + + public init(pkcs12Data: Data, password: String) { + let importPasswordOption: NSDictionary + = [kSecImportExportPassphrase as NSString: password] + var items: CFArray? + let secError: OSStatus + = SecPKCS12Import(pkcs12Data as NSData, + importPasswordOption, &items) + guard secError == errSecSuccess else { + if secError == errSecAuthFailed { + NSLog("Incorrect password?") + } + fatalError("Error trying to import PKCS12 data") + } + guard let theItemsCFArray = items else { fatalError() } + let theItemsNSArray: NSArray = theItemsCFArray as NSArray + guard let dictArray + = theItemsNSArray as? [[String: AnyObject]] + else { + fatalError() + } + + label = dictArray.element(for: kSecImportItemLabel) + keyID = dictArray.element(for: kSecImportItemKeyID) + trust = dictArray.element(for: kSecImportItemTrust) + certChain = dictArray.element(for: kSecImportItemCertChain) + identity = dictArray.element(for: kSecImportItemIdentity) + } +} + +fileprivate extension Array where Element == [String: AnyObject] { + func element(for key: CFString) -> T? { + for dictElement in self { + if let value = dictElement[key as String] as? T { + return value + } + } + return nil + } +} diff --git a/ios/RocketChat Watch App/Database/Database.swift b/ios/RocketChat Watch App/Database/Database.swift new file mode 100644 index 000000000..8d4a5b3c0 --- /dev/null +++ b/ios/RocketChat Watch App/Database/Database.swift @@ -0,0 +1,132 @@ +import CoreData +import Foundation + +protocol ServersDatabase { + var viewContext: NSManagedObjectContext { get } + + func server(url: URL) -> Server? + func user(id: String) -> LoggedUser? + func servers() -> [Server] + + func remove(_ server: Server) + + func save() + + func process(updatedServer: WatchMessage.Server) +} + +final class DefaultDatabase: ServersDatabase { + private let container: NSPersistentContainer + + var viewContext: NSManagedObjectContext { + container.viewContext + } + + private static let model: NSManagedObjectModel = { + guard let url = Bundle.main.url(forResource: "Default", withExtension: "momd"), + let managedObjectModel = NSManagedObjectModel(contentsOf: url) else { + fatalError("Can't find Core Data Model") + } + + return managedObjectModel + }() + + init() { + container = NSPersistentContainer(name: "default", managedObjectModel: Self.model) + + container.loadPersistentStores { _, error in + if let error { fatalError("Can't load persistent stores: \(error)") } + } + + container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy + } + + func save() { + guard container.viewContext.hasChanges else { + return + } + + try? container.viewContext.save() + } + + func server(url: URL) -> Server? { + let request = Server.fetchRequest() + request.predicate = NSPredicate(format: "url == %@", url.absoluteString.removeTrailingSlash()) + + return try? viewContext.fetch(request).first + } + + func user(id: String) -> LoggedUser? { + let request = LoggedUser.fetchRequest() + request.predicate = NSPredicate(format: "id == %@", id) + + return try? viewContext.fetch(request).first + } + + func servers() -> [Server] { + let request = Server.fetchRequest() + + return (try? viewContext.fetch(request)) ?? [] + } + + func remove(_ server: Server) { + viewContext.delete(server) + + save() + } + + func process(updatedServer: WatchMessage.Server) { + if let server = server(url: updatedServer.url) { + server.url = updatedServer.url + server.name = updatedServer.name + server.iconURL = updatedServer.iconURL + server.useRealName = updatedServer.useRealName + server.loggedUser = user(from: updatedServer.loggedUser) + server.certificate = updatedServer.clientSSL?.certificate + server.password = updatedServer.clientSSL?.password + server.version = updatedServer.version + } else { + Server( + context: viewContext, + iconURL: updatedServer.iconURL, + name: updatedServer.name, + url: updatedServer.url, + useRealName: updatedServer.useRealName, + loggedUser: user(from: updatedServer.loggedUser), + certificate: updatedServer.clientSSL?.certificate, + password: updatedServer.clientSSL?.password, + version: updatedServer.version + ) + } + + save() + } + + private func user(from updatedUser: WatchMessage.Server.LoggedUser) -> LoggedUser { + if let user = user(id: updatedUser.id) { + user.name = updatedUser.name + user.username = updatedUser.username + user.token = updatedUser.token + + return user + } + + return LoggedUser( + context: viewContext, + id: updatedUser.id, + name: updatedUser.name, + token: updatedUser.token, + username: updatedUser.username + ) + } +} + +extension String { + func removeTrailingSlash() -> String { + var url = self + if (url.last == "/") { + url.removeLast() + } + return url + } +} diff --git a/ios/RocketChat Watch App/Database/Default.xcdatamodeld/Default.xcdatamodel/contents b/ios/RocketChat Watch App/Database/Default.xcdatamodeld/Default.xcdatamodel/contents new file mode 100644 index 000000000..f8d64a472 --- /dev/null +++ b/ios/RocketChat Watch App/Database/Default.xcdatamodeld/Default.xcdatamodel/contents @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ios/RocketChat Watch App/Database/Entity/Attachment.swift b/ios/RocketChat Watch App/Database/Entity/Attachment.swift new file mode 100644 index 000000000..5ee92b33b --- /dev/null +++ b/ios/RocketChat Watch App/Database/Entity/Attachment.swift @@ -0,0 +1,7 @@ +import CoreData + +extension Attachment { + var aspectRatio: Double { + return width / height + } +} diff --git a/ios/RocketChat Watch App/Database/Entity/LoggedUser.swift b/ios/RocketChat Watch App/Database/Entity/LoggedUser.swift new file mode 100644 index 000000000..5408276ea --- /dev/null +++ b/ios/RocketChat Watch App/Database/Entity/LoggedUser.swift @@ -0,0 +1,48 @@ +import CoreData + +@objc +public final class LoggedUser: NSManagedObject { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + NSFetchRequest(entityName: "LoggedUser") + } + + @NSManaged public var id: String + @NSManaged public var name: String + @NSManaged public var token: String + @NSManaged public var username: String + + @available(*, unavailable) + init() { + fatalError() + } + + @available(*, unavailable) + init(context: NSManagedObjectContext) { + fatalError() + } + + public override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) { + super.init(entity: entity, insertInto: context) + } + + @discardableResult + public init( + context: NSManagedObjectContext, + id: String, + name: String, + token: String, + username: String + ) { + let entity = NSEntityDescription.entity(forEntityName: "LoggedUser", in: context)! + super.init(entity: entity, insertInto: context) + self.id = id + self.name = name + self.token = token + self.username = username + } +} + +extension LoggedUser: Identifiable { + +} diff --git a/ios/RocketChat Watch App/Database/Entity/Room.swift b/ios/RocketChat Watch App/Database/Entity/Room.swift new file mode 100644 index 000000000..9061defb5 --- /dev/null +++ b/ios/RocketChat Watch App/Database/Entity/Room.swift @@ -0,0 +1,36 @@ +import CoreData + +extension Room { + var messagesRequest: NSFetchRequest { + let request = Message.fetchRequest() + + request.predicate = NSPredicate(format: "room == %@", self) + request.sortDescriptors = [NSSortDescriptor(keyPath: \Message.ts, ascending: true)] + + return request + } + + var lastMessage: Message? { + let request = Message.fetchRequest() + + let thisRoomPredicate = NSPredicate(format: "room == %@", self) + let nonInfoMessagePredicate = NSPredicate(format: "t == nil", self) + request.predicate = NSCompoundPredicate( + andPredicateWithSubpredicates: [thisRoomPredicate, nonInfoMessagePredicate] + ) + request.sortDescriptors = [NSSortDescriptor(keyPath: \Message.ts, ascending: false)] + request.fetchLimit = 1 + + return try? managedObjectContext?.fetch(request).first + } + + var firstMessage: Message? { + let request = Message.fetchRequest() + + request.predicate = NSPredicate(format: "room == %@", self) + request.sortDescriptors = [NSSortDescriptor(keyPath: \Message.ts, ascending: true)] + request.fetchLimit = 1 + + return try? managedObjectContext?.fetch(request).first + } +} diff --git a/ios/RocketChat Watch App/Database/Entity/Server.swift b/ios/RocketChat Watch App/Database/Entity/Server.swift new file mode 100644 index 000000000..24236cdb4 --- /dev/null +++ b/ios/RocketChat Watch App/Database/Entity/Server.swift @@ -0,0 +1,78 @@ +import CoreData + +@objc +public final class Server: NSManagedObject { + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + NSFetchRequest(entityName: "Server") + } + + @NSManaged public var iconURL: URL + @NSManaged public var name: String + @NSManaged public var updatedSince: Date? + @NSManaged public var url: URL + @NSManaged public var useRealName: Bool + @NSManaged public var loggedUser: LoggedUser + @NSManaged public var certificate: Data? + @NSManaged public var password: String? + @NSManaged public var version: String + + lazy var database: Database = RocketChatDatabase(server: self) + + @available(*, unavailable) + init() { + fatalError() + } + + @available(*, unavailable) + init(context: NSManagedObjectContext) { + fatalError() + } + + public override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) { + super.init(entity: entity, insertInto: context) + } + + @discardableResult + public init( + context: NSManagedObjectContext, + iconURL: URL, + name: String, + updatedSince: Date? = nil, + url: URL, + useRealName: Bool, + loggedUser: LoggedUser, + certificate: Data? = nil, + password: String? = nil, + version: String + ) { + let entity = NSEntityDescription.entity(forEntityName: "Server", in: context)! + super.init(entity: entity, insertInto: context) + self.iconURL = iconURL + self.name = name + self.updatedSince = updatedSince + self.url = url + self.useRealName = useRealName + self.loggedUser = loggedUser + self.certificate = certificate + self.password = password + self.version = version + } +} + +extension Server: Identifiable { + +} + +extension Server { + var roomsRequest: NSFetchRequest { + let request = Room.fetchRequest() + + let nonArchived = NSPredicate(format: "archived == false") + let open = NSPredicate(format: "open == true") + request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [nonArchived, open]) + request.sortDescriptors = [NSSortDescriptor(keyPath: \Room.ts, ascending: false)] + + return request + } +} diff --git a/ios/RocketChat Watch App/Database/Model/AttachmentModel.swift b/ios/RocketChat Watch App/Database/Model/AttachmentModel.swift new file mode 100644 index 000000000..3ebbf7eee --- /dev/null +++ b/ios/RocketChat Watch App/Database/Model/AttachmentModel.swift @@ -0,0 +1,42 @@ +import CoreData + +final class AttachmentModel { + private let context: NSManagedObjectContext + + init(context: NSManagedObjectContext) { + self.context = context + } + + func upsert(_ newAttachment: MergedRoom.Message.Attachment) -> Attachment? { + let identifier = newAttachment.imageURL ?? newAttachment.audioURL + + guard let identifier = identifier?.absoluteString ?? newAttachment.title else { + return nil + } + + let attachment = attachment(id: identifier, in: context) + + attachment.imageURL = newAttachment.imageURL + attachment.msg = newAttachment.description + attachment.width = newAttachment.dimensions?.width ?? 0 + attachment.height = newAttachment.dimensions?.height ?? 0 + + return attachment + } +} + +extension AttachmentModel { + private func attachment(id: String, in context: NSManagedObjectContext) -> Attachment { + let request = Attachment.fetchRequest() + request.predicate = NSPredicate(format: "id == %@", id) + + guard let attachment = try? context.fetch(request).first else { + let attachment = Attachment(context: context) + attachment.id = id + + return attachment + } + + return attachment + } +} diff --git a/ios/RocketChat Watch App/Database/Model/MessageModel.swift b/ios/RocketChat Watch App/Database/Model/MessageModel.swift new file mode 100644 index 000000000..a3473f023 --- /dev/null +++ b/ios/RocketChat Watch App/Database/Model/MessageModel.swift @@ -0,0 +1,62 @@ +import CoreData + +final class MessageModel { + private let context: NSManagedObjectContext + + init(context: NSManagedObjectContext) { + self.context = context + } + + func upsert(_ newMessage: MergedRoom.Message) -> Message { + let attachmentDatabase = AttachmentModel(context: context) + let userDatabase = UserModel(context: context) + + let user = userDatabase.upsert(newMessage.u) + let message = message(id: newMessage._id, in: context) + + message.status = "received" + message.id = newMessage._id + message.msg = newMessage.msg + message.ts = newMessage.ts + message.t = newMessage.t + message.groupable = newMessage.groupable ?? true + message.editedAt = newMessage.editedAt + message.role = newMessage.role + message.comment = newMessage.comment + message.user = user + + if let messageAttachments = newMessage.attachments { + for newAttachment in messageAttachments { + let attachment = attachmentDatabase.upsert(newAttachment) + + attachment?.message = message + } + } + + return message + } + + func fetch(id: String) -> Message? { + let request = Message.fetchRequest() + request.predicate = NSPredicate(format: "id == %@", id) + + return try? context.fetch(request).first + } +} + +extension MessageModel { + private func message(id: String, in context: NSManagedObjectContext) -> Message { + let request = Message.fetchRequest() + request.predicate = NSPredicate(format: "id == %@", id) + + guard let message = try? context.fetch(request).first else { + let message = Message(context: context) + message.id = id + message.ts = Date() + + return message + } + + return message + } +} diff --git a/ios/RocketChat Watch App/Database/Model/RoomModel.swift b/ios/RocketChat Watch App/Database/Model/RoomModel.swift new file mode 100644 index 000000000..f95145584 --- /dev/null +++ b/ios/RocketChat Watch App/Database/Model/RoomModel.swift @@ -0,0 +1,89 @@ +import CoreData + +final class RoomModel { + private let context: NSManagedObjectContext + + init(context: NSManagedObjectContext) { + self.context = context + } + + @discardableResult + func upsert(_ newRoom: MergedRoom) -> Room { + let room = room(id: newRoom.id, in: context) + + room.name = newRoom.name ?? room.name + room.fname = newRoom.fname ?? room.fname + room.t = newRoom.t + room.unread = Int32(newRoom.unread) + room.alert = newRoom.alert + room.lr = newRoom.lr ?? room.lr + room.open = newRoom.open ?? true + room.rid = newRoom.rid + room.hideUnreadStatus = newRoom.hideUnreadStatus ?? room.hideUnreadStatus + + room.updatedAt = newRoom.updatedAt ?? room.updatedAt + room.usernames = newRoom.usernames ?? room.usernames + room.uids = newRoom.uids ?? room.uids + room.prid = newRoom.prid ?? room.prid + room.isReadOnly = newRoom.isReadOnly ?? room.isReadOnly + room.encrypted = newRoom.encrypted ?? room.encrypted + room.teamMain = newRoom.teamMain ?? room.teamMain + room.archived = newRoom.archived ?? room.archived + room.broadcast = newRoom.broadcast ?? room.broadcast + room.ts = newRoom.ts ?? room.ts + + let messageDatabase = MessageModel(context: context) + + if let lastMessage = newRoom.lastMessage { + let message = messageDatabase.upsert(lastMessage) + message.room = room + } + + return room + } + + func delete(_ room: Room) { + context.delete(room) + } + + func fetch(ids: [String]) -> [Room] { + let request = Room.fetchRequest() + request.predicate = NSPredicate(format: "id IN %@", ids) + + return (try? context.fetch(request)) ?? [] + } + + func fetch(id: String) -> Room { + room(id: id, in: context) + } +} + +extension RoomModel { + private func room(id: String, in context: NSManagedObjectContext) -> Room { + let request = Room.fetchRequest() + request.predicate = NSPredicate(format: "id == %@", id) + + guard let room = try? context.fetch(request).first else { + let room = Room(context: context) + room.id = id + + return room + } + + return room + } + + private func room(rid: String, in context: NSManagedObjectContext) -> Room { + let request = Room.fetchRequest() + request.predicate = NSPredicate(format: "rid == %@", rid) + + guard let room = try? context.fetch(request).first else { + let room = Room(context: context) + room.rid = rid + + return room + } + + return room + } +} diff --git a/ios/RocketChat Watch App/Database/Model/UserModel.swift b/ios/RocketChat Watch App/Database/Model/UserModel.swift new file mode 100644 index 000000000..1f052cfdf --- /dev/null +++ b/ios/RocketChat Watch App/Database/Model/UserModel.swift @@ -0,0 +1,37 @@ +import CoreData + +final class UserModel { + private let context: NSManagedObjectContext + + init(context: NSManagedObjectContext) { + self.context = context + } + + func upsert(_ newUser: MergedRoom.Message.User) -> User { + let user = user(id: newUser._id, in: context) + user.name = newUser.name + user.username = newUser.username + + return user + } + + func fetch(id: String) -> User { + user(id: id, in: context) + } +} + +extension UserModel { + private func user(id: String, in context: NSManagedObjectContext) -> User { + let request = User.fetchRequest() + request.predicate = NSPredicate(format: "id == %@", id) + + guard let user = try? context.fetch(request).first else { + let user = User(context: context) + user.id = id + + return user + } + + return user + } +} diff --git a/ios/RocketChat Watch App/Database/RocketChat.xcdatamodeld/RocketChat.xcdatamodel/contents b/ios/RocketChat Watch App/Database/RocketChat.xcdatamodeld/RocketChat.xcdatamodel/contents new file mode 100644 index 000000000..806bb4dea --- /dev/null +++ b/ios/RocketChat Watch App/Database/RocketChat.xcdatamodeld/RocketChat.xcdatamodel/contents @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ios/RocketChat Watch App/Database/RocketChatDatabase.swift b/ios/RocketChat Watch App/Database/RocketChatDatabase.swift new file mode 100644 index 000000000..d41c8642b --- /dev/null +++ b/ios/RocketChat Watch App/Database/RocketChatDatabase.swift @@ -0,0 +1,316 @@ +import Combine +import CoreData + +protocol Database { + var viewContext: NSManagedObjectContext { get } + func has(context: NSManagedObjectContext) -> Bool + + func room(id: String) -> Room? + func room(rid: String) -> Room? + func remove(_ message: Message) + + func handleRoomsResponse(_ subscriptionsResponse: SubscriptionsResponse, _ roomsResponse: RoomsResponse) + func handleHistoryResponse(_ historyResponse: HistoryResponse, in roomID: String) + func handleMessagesResponse(_ messagesResponse: MessagesResponse, in roomID: String, newUpdatedSince: Date) + func handleSendMessageResponse(_ sendMessageResponse: SendMessageResponse, in roomID: String) + func handleSendMessageRequest(_ newMessage: MergedRoom.Message, in roomID: String) + func handleReadResponse(_ readResponse: ReadResponse, in roomID: String) + func handleSendMessageError(_ messageID: String) + + func remove() +} + +final class RocketChatDatabase: Database { + private let server: Server + + init(server: Server) { + self.server = server + } + + var viewContext: NSManagedObjectContext { + container.viewContext + } + + func has(context: NSManagedObjectContext) -> Bool { + context == backgroundContext + } + + private static let model: NSManagedObjectModel = { + guard let url = Bundle.main.url(forResource: "RocketChat", withExtension: "momd"), + let managedObjectModel = NSManagedObjectModel(contentsOf: url) else { + fatalError("Can't find Core Data Model") + } + + return managedObjectModel + }() + + private lazy var container: NSPersistentContainer = { + let name = server.url.host ?? "default" + + let container = NSPersistentContainer(name: name, managedObjectModel: Self.model) + + container.loadPersistentStores { _, error in + if let error { fatalError("Can't load persistent stores: \(error)") } + } + + container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy + container.viewContext.automaticallyMergesChangesFromParent = true + + return container + }() + + private lazy var backgroundContext = container.newBackgroundContext() + + func remove(_ message: Message) { + viewContext.delete(message) + + do { + try viewContext.save() + } catch { + print(error) + } + } + + func room(id: String) -> Room? { + let request = Room.fetchRequest() + request.predicate = NSPredicate(format: "id == %@", id) + + return try? viewContext.fetch(request).first + } + + func room(rid: String) -> Room? { + let request = Room.fetchRequest() + request.predicate = NSPredicate(format: "rid == %@", rid) + + return try? viewContext.fetch(request).first + } + + func remove() { + guard let url = container.persistentStoreDescriptions.first?.url else { + return + } + + do { + try container.persistentStoreCoordinator.destroyPersistentStore(at: url, type: .sqlite) + } catch { + print(error) + } + } +} + +extension RocketChatDatabase { + func handleReadResponse(_ readResponse: ReadResponse, in roomID: String) { + backgroundContext.performBackgroundTask { context in + let roomDatabase = RoomModel(context: context) + + let room = roomDatabase.fetch(id: roomID) + + room.alert = false + room.unread = 0 + + do { + try context.save() + } catch { + print(error) + } + } + } + + func handleSendMessageError(_ messageID: String) { + backgroundContext.performBackgroundTask { context in + let messageDatabase = MessageModel(context: context) + + if let message = messageDatabase.fetch(id: messageID) { + message.status = "error" + } + + do { + try context.save() + } catch { + print(error) + } + } + } + + func handleSendMessageRequest(_ newMessage: MergedRoom.Message, in roomID: String) { + backgroundContext.performBackgroundTask { context in + let roomDatabase = RoomModel(context: context) + let messageDatabase = MessageModel(context: context) + + let room = roomDatabase.fetch(id: roomID) + + let message = messageDatabase.upsert(newMessage) + + message.status = "temp" + message.room = room + + do { + try context.save() + } catch { + print(error) + } + } + } + + func handleSendMessageResponse(_ sendMessageResponse: SendMessageResponse, in roomID: String) { + let message = sendMessageResponse.message + + backgroundContext.performBackgroundTask { context in + let messageDatabase = MessageModel(context: context) + let roomDatabase = RoomModel(context: context) + + let room = roomDatabase.fetch(id: roomID) + + if let newMessage = MergedRoom.Message(from: message) { + let message = messageDatabase.upsert(newMessage) + + message.room = room + } + + do { + try context.save() + } catch { + print(error) + } + } + } + + func handleMessagesResponse(_ messagesResponse: MessagesResponse, in roomID: String, newUpdatedSince: Date) { + let messages = messagesResponse.result.updated + + backgroundContext.performBackgroundTask { context in + let messageDatabase = MessageModel(context: context) + let roomDatabase = RoomModel(context: context) + + let room = roomDatabase.fetch(id: roomID) + + for message in messages { + if let newMessage = MergedRoom.Message(from: message) { + let message = messageDatabase.upsert(newMessage) + + message.room = room + } + } + + room.updatedSince = newUpdatedSince + + do { + try context.save() + } catch { + print(error) + } + } + } + + func handleHistoryResponse(_ historyResponse: HistoryResponse, in roomID: String) { + let messages = historyResponse.messages + + backgroundContext.performBackgroundTask { context in + let messageDatabase = MessageModel(context: context) + let roomDatabase = RoomModel(context: context) + + let room = roomDatabase.fetch(id: roomID) + + room.hasMoreMessages = messages.count == HISTORY_MESSAGE_COUNT + room.synced = true + + for message in historyResponse.messages { + if let newMessage = MergedRoom.Message(from: message) { + let message = messageDatabase.upsert(newMessage) + + message.room = room + } + } + + do { + try context.save() + } catch { + print(error) + } + } + } + + func handleRoomsResponse(_ subscriptionsResponse: SubscriptionsResponse, _ roomsResponse: RoomsResponse) { + let rooms = roomsResponse.update + let subscriptions = subscriptionsResponse.update + + backgroundContext.performBackgroundTask { context in + let roomDatabase = RoomModel(context: context) + + let roomIds = rooms.filter { room in !subscriptions.contains { room._id == $0.rid } }.map { $0._id } + + let existingSubs = roomDatabase.fetch(ids: roomIds) + let mappedExistingSubs = subscriptions + existingSubs.compactMap { $0.response } + + let mergedSubscriptions = mappedExistingSubs.compactMap { subscription in + let index = rooms.firstIndex { $0._id == subscription.rid } + + guard let index else { + return MergedRoom(subscription, nil) + } + + let room = rooms[index] + return MergedRoom(subscription, room) + } + + let subsIds = mergedSubscriptions.compactMap { $0.id } + subscriptionsResponse.remove.compactMap { $0._id } + + if subsIds.count > 0 { + let existingSubscriptions = roomDatabase.fetch(ids: subsIds) + let subsToUpdate = existingSubscriptions.filter { subscription in mergedSubscriptions.contains { subscription.id == $0.id } } + let subsToCreate = mergedSubscriptions.filter { subscription in !existingSubscriptions.contains { subscription.id == $0.id } } + let subsToDelete = existingSubscriptions.filter { subscription in !mergedSubscriptions.contains { subscription.id == $0.id } } + + subsToCreate.forEach { subscription in + roomDatabase.upsert(subscription) + } + + subsToUpdate.forEach { subscription in + if let newRoom = mergedSubscriptions.first(where: { $0.id == subscription.id }) { + roomDatabase.upsert(newRoom) + } + } + + subsToDelete.forEach { subscription in + roomDatabase.delete(subscription) + } + + do { + try context.save() + } catch { + print(error) + } + } + } + } +} + +private extension Room { + var response: SubscriptionsResponse.Subscription? { + guard let id, let fname, let t, let rid else { + return nil + } + + return .init( + _id: id, + rid: rid, + name: name, + fname: fname, + t: t, + unread: Int(unread), + alert: alert, + lr: lr, + open: open, + _updatedAt: ts, + hideUnreadStatus: hideUnreadStatus + ) + } +} + +extension NSManagedObjectContext { + func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) { + perform { + block(self) + } + } +} diff --git a/ios/RocketChat Watch App/DependencyInjection/Dependency.swift b/ios/RocketChat Watch App/DependencyInjection/Dependency.swift new file mode 100644 index 000000000..eff7adc90 --- /dev/null +++ b/ios/RocketChat Watch App/DependencyInjection/Dependency.swift @@ -0,0 +1,21 @@ +@propertyWrapper +struct Dependency { + private var dependency: T + + init() { + guard let dependency = Store.resolve(T.self) else { + fatalError("No service of type \(T.self) registered!") + } + + self.dependency = dependency + } + + var wrappedValue: T { + get { + dependency + } + mutating set { + dependency = newValue + } + } +} diff --git a/ios/RocketChat Watch App/DependencyInjection/Store.swift b/ios/RocketChat Watch App/DependencyInjection/Store.swift new file mode 100644 index 000000000..eb9d67e89 --- /dev/null +++ b/ios/RocketChat Watch App/DependencyInjection/Store.swift @@ -0,0 +1,41 @@ +import Foundation + +protocol StoreInterface { + static func register(_ type: T.Type, factory: @autoclosure @escaping () -> T) + static func resolve(_ type: T.Type) -> T? +} + +final class Store: StoreInterface { + private static var factories: [ObjectIdentifier: () -> Any] = [:] + private static var cache: [ObjectIdentifier: WeakRef] = [:] + + static func register(_ type: T.Type, factory: @autoclosure @escaping () -> T) { + let identifier = ObjectIdentifier(type) + factories[identifier] = factory + cache[identifier] = nil + } + + static func resolve(_ type: T.Type) -> T? { + let identifier = ObjectIdentifier(type) + + if let dependency = cache[identifier]?.value { + return dependency as? T + } else { + let dependency = factories[identifier]?() as? T + + if let dependency { + cache[identifier] = WeakRef(value: dependency as AnyObject) + } + + return dependency + } + } +} + +private final class WeakRef { + private(set) weak var value: T? + + init(value: T) { + self.value = value + } +} diff --git a/ios/RocketChat Watch App/ExtensionDelegate.swift b/ios/RocketChat Watch App/ExtensionDelegate.swift new file mode 100644 index 000000000..609fcfa69 --- /dev/null +++ b/ios/RocketChat Watch App/ExtensionDelegate.swift @@ -0,0 +1,43 @@ +import WatchKit +import UserNotifications + +final class ExtensionDelegate: NSObject, WKApplicationDelegate { + let router = AppRouter() + let database = DefaultDatabase() + + func applicationDidFinishLaunching() { + UNUserNotificationCenter.current().delegate = self + } +} + +extension ExtensionDelegate: UNUserNotificationCenterDelegate { + func userNotificationCenter( + _ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse, + withCompletionHandler completionHandler: @escaping () -> Void + ) { + let userInfo = response.notification.request.content.userInfo + let ejson = userInfo["ejson"] as? String + let data = ejson?.data(using: .utf8) + + guard let response = try? data?.decode(NotificationResponse.self) else { return } + + deeplink(from: response) + + completionHandler() + } +} + +extension ExtensionDelegate { + private func deeplink(from response: NotificationResponse) { + guard let server = database.server(url: response.host) else { return } + guard let room = server.database.room(rid: response.rid) else { return } + + router.route(to: [.loading, .roomList(server), .room(server, room)]) + } +} + +struct NotificationResponse: Codable, Hashable { + let host: URL + let rid: String +} diff --git a/ios/RocketChat Watch App/Extensions/Binding+Extensions.swift b/ios/RocketChat Watch App/Extensions/Binding+Extensions.swift new file mode 100644 index 000000000..7116086ca --- /dev/null +++ b/ios/RocketChat Watch App/Extensions/Binding+Extensions.swift @@ -0,0 +1,24 @@ +import SwiftUI + +extension Binding where Value == Bool { + init(bindingOptional: Binding) { + self.init( + get: { + bindingOptional.wrappedValue != nil + }, + set: { newValue in + guard newValue == false else { return } + + /// We only handle `false` booleans to set our optional to `nil` + /// as we can't handle `true` for restoring the previous value. + bindingOptional.wrappedValue = nil + } + ) + } +} + +extension Binding { + func mappedToBool() -> Binding where Value == Wrapped? { + Binding(bindingOptional: self) + } +} diff --git a/ios/RocketChat Watch App/Extensions/Color+Extensions.swift b/ios/RocketChat Watch App/Extensions/Color+Extensions.swift new file mode 100644 index 000000000..95bc5c918 --- /dev/null +++ b/ios/RocketChat Watch App/Extensions/Color+Extensions.swift @@ -0,0 +1,27 @@ +import SwiftUI + +extension Color { + static var titleLabels: Color { + Color(hex: 0xF2F3F5) + } + + static var `default`: Color { + Color(hex: 0xE4E7EA) + } + + static var secondaryInfo: Color { + Color(hex: 0x9EA2A8) + } +} + +private extension Color { + init(hex: UInt, alpha: Double = 1) { + self.init( + .sRGB, + red: Double((hex >> 16) & 0xff) / 255, + green: Double((hex >> 08) & 0xff) / 255, + blue: Double((hex >> 00) & 0xff) / 255, + opacity: alpha + ) + } +} diff --git a/ios/RocketChat Watch App/Extensions/Date+Extensions.swift b/ios/RocketChat Watch App/Extensions/Date+Extensions.swift new file mode 100644 index 000000000..3d0d95ffd --- /dev/null +++ b/ios/RocketChat Watch App/Extensions/Date+Extensions.swift @@ -0,0 +1,7 @@ +import Foundation + +extension Date { + static func - (lhs: Date, rhs: Date) -> TimeInterval { + return lhs.timeIntervalSinceReferenceDate - rhs.timeIntervalSinceReferenceDate + } +} diff --git a/ios/RocketChat Watch App/Extensions/ToolbarItemPlacement+Extensions.swift b/ios/RocketChat Watch App/Extensions/ToolbarItemPlacement+Extensions.swift new file mode 100644 index 000000000..359bea7d6 --- /dev/null +++ b/ios/RocketChat Watch App/Extensions/ToolbarItemPlacement+Extensions.swift @@ -0,0 +1,11 @@ +import SwiftUI + +extension ToolbarItemPlacement { + static var `default`: Self { + if #available(watchOS 10.0, *) { + return .topBarLeading + } else { + return .automatic + } + } +} diff --git a/ios/RocketChat Watch App/Formatters/Mapper/MessageInfoMapper.swift b/ios/RocketChat Watch App/Formatters/Mapper/MessageInfoMapper.swift new file mode 100644 index 000000000..59518c8a9 --- /dev/null +++ b/ios/RocketChat Watch App/Formatters/Mapper/MessageInfoMapper.swift @@ -0,0 +1,165 @@ +import SwiftUI + +struct InfoMessage { + let msg: String + let username: String + let type: String + let role: String + let comment: String +} + +func getInfoMessage(_ infoMessage: InfoMessage) -> LocalizedStringKey { + switch infoMessage.type { + case "rm": + return "message removed" + case "uj": + return "joined the channel" + case "ujt": + return "joined this team" + case "ut": + return "joined the conversation" + case "r": + return "changed room name to: \(infoMessage.msg)" + case "ru": + return "removed \(infoMessage.msg)" + case "au": + return "added \(infoMessage.msg)" + case "user-muted": + return "muted \(infoMessage.msg)" + case "room_changed_description": + return "changed room description to: \(infoMessage.msg)" + case "room_changed_announcement": + return "changed room announcement to: \(infoMessage.msg)" + case "room_changed_topic": + return "changed room topic to: \(infoMessage.msg)" + case "room_changed_privacy": + return "changed room to \(infoMessage.msg)" + case "room_changed_avatar": + return "changed room avatar" + case "message_snippeted": + return "created a snippet" + case "room_e2e_disabled": + return "disabled E2E encryption for this room" + case "room_e2e_enabled": + return "enabled E2E encryption for this room" + case "removed-user-from-team": + return "removed @\(infoMessage.msg) from this team" + case "added-user-to-team": + return "added @\(infoMessage.msg) to this team" + case "user-added-room-to-team": + return "added #\(infoMessage.msg) to this team" + case "user-converted-to-team": + return "converted #\(infoMessage.msg) to a team" + case "user-converted-to-channel": + return "converted #\(infoMessage.msg) to channel" + case "user-deleted-room-from-team": + return "deleted #\(infoMessage.msg)" + case "user-removed-room-from-team": + return "removed #\(infoMessage.msg) from this team" + case "room-disallowed-reacting": + return "disallowed reactions" + case "room-allowed-reacting": + return "allowed reactions" + case "room-set-read-only": + return "set room to read only" + case "room-removed-read-only": + return "removed read only permission" + case "user-unmuted": + return "unmuted \(infoMessage.msg)" + case "room-archived": + return "archived room" + case "room-unarchived": + return "unarchived room" + case "subscription-role-added": + return "defined \(infoMessage.msg) as \(infoMessage.role)" + case "subscription-role-removed": + return "removed \(infoMessage.msg) as \(infoMessage.role)" + case "message_pinned": + return "Pinned a message:" + case "ul": + return "left the channel" + case "ult": + return "has left the team" + case "jitsi_call_started": + return "Call started by \(infoMessage.username)" + case "omnichannel_placed_chat_on_hold": + return "Chat on hold: \(infoMessage.comment)" + case "omnichannel_on_hold_chat_resumed": + return "On hold chat resumed: \(infoMessage.comment)" + case "command": + return "returned the chat to the queue" + case "livechat-started": + return "Chat started" + case "livechat-close": + return "Conversation closed" + case "livechat_transfer_history": + return "New chat transfer: \(infoMessage.username) returned the chat to the queue" + default: + return "Unsupported system message" + } +} + +func messageHaveAuthorName(_ messageType: String) -> Bool { + messagesWithAuthorName.contains(messageType) +} + +extension InfoMessage { + init(from message: Message) { + self.init( + msg: message.msg ?? "", + username: message.user?.username ?? "", + type: message.t ?? "", + role: message.role ?? "", + comment: message.comment ?? "" + ) + } +} + +private let messagesWithAuthorName: Set = [ + "r", + "ru", + "au", + "rm", + "uj", + "ujt", + "ut", + "ul", + "ult", + "message_pinned", + "message_snippeted", + "removed-user-from-team", + "added-user-to-team", + "user-added-room-to-team", + "user-converted-to-team", + "user-converted-to-channel", + "user-deleted-room-from-team", + "user-removed-room-from-team", + "omnichannel_placed_chat_on_hold", + "omnichannel_on_hold_chat_resumed", + "livechat_navigation_history", + "livechat_transcript_history", + "command", + "livechat-started", + "livechat-close", + "livechat_video_call", + "livechat_webrtc_video_call", + "livechat_transfer_history", + "room-archived", + "room-unarchived", + "user-muted", + "room_changed_description", + "room_changed_announcement", + "room_changed_topic", + "room_changed_privacy", + "room_changed_avatar", + "room_e2e_disabled", + "room_e2e_enabled", + "room-allowed-reacting", + "room-disallowed-reacting", + "room-set-read-only", + "room-removed-read-only", + "user-unmuted", + "room-unarchived", + "subscription-role-added", + "subscription-role-removed" +] diff --git a/ios/RocketChat Watch App/Formatters/MessageFormatter.swift b/ios/RocketChat Watch App/Formatters/MessageFormatter.swift new file mode 100644 index 000000000..a992043ee --- /dev/null +++ b/ios/RocketChat Watch App/Formatters/MessageFormatter.swift @@ -0,0 +1,88 @@ +import SwiftUI + +final class MessageFormatter { + private let message: Message + private let previousMessage: Message? + private let lastOpen: Date? + + init(message: Message, previousMessage: Message?, lastOpen: Date?) { + self.message = message + self.previousMessage = previousMessage + self.lastOpen = lastOpen + } + + func hasDateSeparator() -> Bool { + if let previousMessage, + let previousMessageTS = previousMessage.ts, + let messageTS = message.ts, + Calendar.current.isDate(previousMessageTS, inSameDayAs: messageTS) { + return false + } + return true + } + + func hasUnreadSeparator() -> Bool { + guard let messageTS = message.ts, let lastOpen else { + return false + } + + if previousMessage == nil { + return messageTS > lastOpen + } else if let previousMessage, let previousMessageTS = previousMessage.ts { + return messageTS >= lastOpen && previousMessageTS < lastOpen + } else { + return false + } + } + + func isHeader() -> Bool { + if let previousMessage, + let previousMessageTS = previousMessage.ts, + let messageTS = message.ts, + Calendar.current.isDate(previousMessageTS, inSameDayAs: messageTS), + previousMessage.user?.username == message.user?.username, + !(previousMessage.groupable == false || message.groupable == false || message.room?.broadcast == true), + messageTS - previousMessageTS < 300, + message.t != "rm", + previousMessage.t != "rm" { + return false + } + + return true + } + + func info() -> LocalizedStringKey? { + switch message.t { + case .some: + return getInfoMessage(.init(from: message)) + case .none: + return nil + } + } + + func date() -> String? { + guard let ts = message.ts else { return nil } + + let dateFormatter = DateFormatter() + + dateFormatter.locale = Locale.current + dateFormatter.timeZone = TimeZone.current + dateFormatter.timeStyle = .none + dateFormatter.dateStyle = .long + + return dateFormatter.string(from: ts) + } + + func time() -> String? { + guard let ts = message.ts else { return nil } + + let dateFormatter = DateFormatter() + + dateFormatter.locale = Locale.current + dateFormatter.timeZone = TimeZone.current + dateFormatter.timeStyle = .short + dateFormatter.dateStyle = .none + + return dateFormatter.string(from: ts) + } +} diff --git a/ios/RocketChat Watch App/Formatters/RoomFormatter.swift b/ios/RocketChat Watch App/Formatters/RoomFormatter.swift new file mode 100644 index 000000000..12c27321a --- /dev/null +++ b/ios/RocketChat Watch App/Formatters/RoomFormatter.swift @@ -0,0 +1,42 @@ +import Foundation + +final class RoomFormatter { + private let room: Room + private let server: Server + + init(room: Room, server: Server) { + self.room = room + self.server = server + } + + var title: String? { + if isGroupChat, !(room.name != nil && room.name?.isEmpty == false), let usernames = room.usernames { + return usernames + .filter { $0 == server.loggedUser.username } + .sorted() + .joined(separator: ", ") + } + + if room.t != "d" { + return room.fname ?? room.name + } + + if room.prid != nil || server.useRealName { + return room.fname ?? room.name + } + + return room.name + } + + var isGroupChat: Bool { + if let uids = room.uids, uids.count > 2 { + return true + } + + if let usernames = room.usernames, usernames.count > 2 { + return true + } + + return false + } +} diff --git a/ios/RocketChat Watch App/Loaders/CancelBag.swift b/ios/RocketChat Watch App/Loaders/CancelBag.swift new file mode 100644 index 000000000..4461ea559 --- /dev/null +++ b/ios/RocketChat Watch App/Loaders/CancelBag.swift @@ -0,0 +1,10 @@ +import Combine + +typealias CancelBag = Set + +extension CancelBag { + mutating func cancelAll() { + forEach { $0.cancel() } + removeAll() + } +} diff --git a/ios/RocketChat Watch App/Loaders/ImageLoader.swift b/ios/RocketChat Watch App/Loaders/ImageLoader.swift new file mode 100644 index 000000000..d481f6eff --- /dev/null +++ b/ios/RocketChat Watch App/Loaders/ImageLoader.swift @@ -0,0 +1,33 @@ +import Combine +import Foundation +import UIKit + +final class ImageLoader: ObservableObject { + @Dependency private var client: RocketChatClientProtocol + + @Published private(set) var image: UIImage? + + private var cancellable: AnyCancellable? + + private let url: URL + + init(url: URL) { + self.url = url + } + + deinit { + cancel() + } + + func load() { + cancellable = client.session.dataTaskPublisher(for: url) + .map { UIImage(data: $0.data) } + .replaceError(with: nil) + .receive(on: DispatchQueue.main) + .sink { [weak self] in self?.image = $0 } + } + + func cancel() { + cancellable?.cancel() + } +} diff --git a/ios/RocketChat Watch App/Loaders/Merge/MergedRoom.swift b/ios/RocketChat Watch App/Loaders/Merge/MergedRoom.swift new file mode 100644 index 000000000..af57f3be5 --- /dev/null +++ b/ios/RocketChat Watch App/Loaders/Merge/MergedRoom.swift @@ -0,0 +1,60 @@ +import Foundation + +struct MergedRoom { + let id: String + let name: String? + let fname: String? + let t: String + let unread: Int + let alert: Bool + let lr: Date? + let open: Bool? + let rid: String + let hideUnreadStatus: Bool? + + let archived: Bool? + let broadcast: Bool? + let encrypted: Bool? + let isReadOnly: Bool? + let prid: String? + let teamMain: Bool? + let ts: Date? + let uids: [String]? + let updatedAt: Date? + let usernames: [String]? + let lastMessage: Message? + let lm: Date? + + struct Message { + let _id: String + let rid: String + let msg: String + let u: User + let ts: Date + let attachments: [Attachment]? + let t: String? + let groupable: Bool? + let editedAt: Date? + let role: String? + let comment: String? + + struct User { + let _id: String + let username: String? + let name: String? + } + + struct Attachment { + let title: String? + let imageURL: URL? + let audioURL: URL? + let description: String? + let dimensions: Dimensions? + + struct Dimensions { + let width: Double + let height: Double + } + } + } +} diff --git a/ios/RocketChat Watch App/Loaders/Merge/MergedRoomMapper.swift b/ios/RocketChat Watch App/Loaders/Merge/MergedRoomMapper.swift new file mode 100644 index 000000000..da47d2412 --- /dev/null +++ b/ios/RocketChat Watch App/Loaders/Merge/MergedRoomMapper.swift @@ -0,0 +1,58 @@ +extension MergedRoom { + init(_ subscription: SubscriptionsResponse.Subscription, _ room: RoomsResponse.Room?) { + id = subscription._id + name = subscription.name ?? room?.fname + fname = subscription.fname + t = subscription.t + unread = subscription.unread + alert = subscription.alert + lr = subscription.lr + open = subscription.open + rid = subscription.rid + hideUnreadStatus = subscription.hideUnreadStatus + + if let room { + if room._updatedAt != nil { + updatedAt = room._updatedAt + lastMessage = .init(from: room.lastMessage?.value) + archived = room.archived ?? false + usernames = room.usernames + uids = room.uids + } else { + updatedAt = nil + lastMessage = nil + archived = nil + usernames = nil + uids = nil + } + + let lastRoomUpdate = room.lm ?? room.ts ?? subscription._updatedAt + + if let lr = subscription.lr, let lastRoomUpdate { + ts = max(lr, lastRoomUpdate) + } else { + ts = lastRoomUpdate + } + + isReadOnly = room.ro ?? false + broadcast = room.broadcast + encrypted = room.encrypted + teamMain = room.teamMain + prid = room.prid + lm = room.lm + } else { + updatedAt = nil + lastMessage = nil + archived = nil + usernames = nil + uids = nil + ts = nil + isReadOnly = nil + broadcast = nil + encrypted = nil + teamMain = nil + prid = nil + lm = nil + } + } +} diff --git a/ios/RocketChat Watch App/Loaders/Merge/MessageMapper.swift b/ios/RocketChat Watch App/Loaders/Merge/MessageMapper.swift new file mode 100644 index 000000000..63226a362 --- /dev/null +++ b/ios/RocketChat Watch App/Loaders/Merge/MessageMapper.swift @@ -0,0 +1,48 @@ +extension MergedRoom.Message { + init?(from newMessage: MessageResponse?) { + guard let newMessage else { + return nil + } + + _id = newMessage._id + rid = newMessage.rid + msg = newMessage.msg + u = .init(from: newMessage.u) + ts = newMessage.ts + attachments = newMessage.attachments?.map { .init(from: $0) } + t = newMessage.t + groupable = newMessage.groupable + editedAt = newMessage.editedAt + role = newMessage.role + comment = newMessage.comment + } +} + +extension MergedRoom.Message.User { + init(from user: UserResponse) { + _id = user._id + username = user.username + name = user.name + } +} + +extension MergedRoom.Message.Attachment { + init(from attachment: AttachmentResponse) { + title = attachment.title + imageURL = attachment.imageURL + audioURL = attachment.audioURL + description = attachment.description + dimensions = .init(from: attachment.dimensions) + } +} + +extension MergedRoom.Message.Attachment.Dimensions { + init?(from dimensions: DimensionsResponse?) { + guard let dimensions else { + return nil + } + + width = dimensions.width + height = dimensions.height + } +} diff --git a/ios/RocketChat Watch App/Loaders/MessageSender.swift b/ios/RocketChat Watch App/Loaders/MessageSender.swift new file mode 100644 index 000000000..d9d56e4b2 --- /dev/null +++ b/ios/RocketChat Watch App/Loaders/MessageSender.swift @@ -0,0 +1,88 @@ +import Combine +import Foundation + +protocol MessageSending { + func sendMessage(_ msg: String, in room: Room) + func resendMessage(message: Message, in room: Room) +} + +final class MessageSender { + @Dependency private var client: RocketChatClientProtocol + @Dependency private var database: Database + + private let server: Server + + init(server: Server) { + self.server = server + } + + private func sendMessageCall(_ message: MergedRoom.Message, in roomID: String) { + database.handleSendMessageRequest(message, in: roomID) + + client.sendMessage(id: message._id, rid: message.rid, msg: message.msg) + .receive(on: DispatchQueue.main) + .subscribe(Subscribers.Sink { [weak self] completion in + if case .failure = completion { + self?.database.handleSendMessageError(message._id) + } + } receiveValue: { [weak self] messageResponse in + self?.database.handleSendMessageResponse(messageResponse, in: roomID) + }) + } +} + +extension MessageSender: MessageSending { + func sendMessage(_ msg: String, in room: Room) { + guard let rid = room.rid, let roomID = room.id else { return } + + let messageID = String.random(17) + let loggedUser = server.loggedUser + + let newMessage = MergedRoom.Message( + _id: messageID, + rid: rid, + msg: msg, + u: .init( + _id: loggedUser.id, + username: loggedUser.username, + name: loggedUser.name + ), + ts: Date(), + attachments: nil, + t: nil, + groupable: true, + editedAt: nil, + role: nil, + comment: nil + ) + + sendMessageCall(newMessage, in: roomID) + } + + func resendMessage(message: Message, in room: Room) { + guard let rid = room.rid, let roomID = room.id else { return } + + guard let id = message.id, let msg = message.msg, let user = message.user, let userID = user.id else { return } + + let newMessage = MergedRoom.Message( + _id: id, + rid: rid, + msg: msg, + u: MergedRoom.Message.User( + _id: userID, + username: user.username, + name: user.name + ), + ts: Date(), + attachments: nil, + t: nil, + groupable: true, + editedAt: nil, + role: nil, + comment: nil + ) + + sendMessageCall(newMessage, in: roomID) + } +} + diff --git a/ios/RocketChat Watch App/Loaders/MessagesLoader.swift b/ios/RocketChat Watch App/Loaders/MessagesLoader.swift new file mode 100644 index 000000000..e5ac3784d --- /dev/null +++ b/ios/RocketChat Watch App/Loaders/MessagesLoader.swift @@ -0,0 +1,109 @@ +import CoreData +import Combine +import Foundation + +protocol MessagesLoading { + func start(on roomID: String) + func loadMore(from date: Date) + + func stop() +} + +final class MessagesLoader { + private var timer: Timer? + private var cancellable = CancelBag() + + @Dependency private var client: RocketChatClientProtocol + @Dependency private var database: Database + @Dependency private var serversDB: ServersDatabase + + private var roomID: String? + + private func scheduledSyncMessages(in room: Room, from date: Date) { + timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { [weak self] _ in + self?.syncMessages(in: room, from: date) + } + } + + private func syncMessages(in room: Room, from date: Date) { + guard let rid = room.rid, let roomID = room.id else { return } + + let newUpdatedSince = Date() + + client.syncMessages(rid: rid, updatedSince: date) + .receive(on: DispatchQueue.main) + .sink { [weak self] completion in + if case .failure = completion { + self?.scheduledSyncMessages(in: room, from: newUpdatedSince) + } + } receiveValue: { [weak self] messagesResponse in + self?.database.handleMessagesResponse(messagesResponse, in: roomID, newUpdatedSince: newUpdatedSince) + + self?.scheduledSyncMessages(in: room, from: newUpdatedSince) + + self?.markAsRead(in: room) + } + .store(in: &cancellable) + } + + private func loadMessages(in room: Room, from date: Date) { + guard let rid = room.rid, let roomID = room.id else { return } + + client.getHistory(rid: rid, t: room.t ?? "", latest: date) + .receive(on: DispatchQueue.main) + .sink { completion in + if case .failure(let error) = completion { + print(error) + } + } receiveValue: { [weak self] messagesResponse in + self?.database.handleHistoryResponse(messagesResponse, in: roomID) + } + .store(in: &cancellable) + } + + private func markAsRead(in room: Room) { + guard (room.unread > 0 || room.alert), let rid = room.rid, let roomID = room.id else { + return + } + + client.sendRead(rid: rid) + .receive(on: DispatchQueue.main) + .sink { completion in + if case .failure(let error) = completion { + print(error) + } + } receiveValue: { [weak self] readResponse in + self?.database.handleReadResponse(readResponse, in: roomID) + } + .store(in: &cancellable) + } +} + +extension MessagesLoader: MessagesLoading { + func start(on roomID: String) { + stop() + + self.roomID = roomID + + guard let room = database.room(id: roomID) else { return } + + if let updatedSince = room.updatedSince { + loadMessages(in: room, from: updatedSince) + syncMessages(in: room, from: updatedSince) + } else { + loadMessages(in: room, from: .now) + syncMessages(in: room, from: .now) + } + } + + func loadMore(from date: Date) { + guard let roomID, let room = database.room(id: roomID) else { return } + + loadMessages(in: room, from: date) + } + + func stop() { + timer?.invalidate() + cancellable.cancelAll() + } +} diff --git a/ios/RocketChat Watch App/Loaders/RoomsLoader.swift b/ios/RocketChat Watch App/Loaders/RoomsLoader.swift new file mode 100644 index 000000000..a40e5f439 --- /dev/null +++ b/ios/RocketChat Watch App/Loaders/RoomsLoader.swift @@ -0,0 +1,118 @@ +import CoreData +import Combine +import Foundation + +protocol RoomsLoading { + func start() + func stop() +} + +final class RoomsLoader: ObservableObject { + @Dependency private var client: RocketChatClientProtocol + @Dependency private var database: Database + @Dependency private var serversDB: ServersDatabase + + @Published private(set) var state: State + + private var timer: Timer? + private var cancellable = CancelBag() + + private let server: Server + + private var shouldUpdatedDateOnce: Bool + + init(server: Server) { + self.server = server + self.state = server.updatedSince == nil ? .loading : .loaded + + shouldUpdatedDateOnce = !(server.version >= "4") + } + + private func scheduledLoadRooms() { + timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { [weak self] _ in + self?.loadRooms() + } + } + + private func loadRooms() { + let newUpdatedSince = Date() + + let updatedSince = server.updatedSince + + Publishers.Zip( + client.getRooms(updatedSince: updatedSince), + client.getSubscriptions(updatedSince: updatedSince) + ) + .receive(on: DispatchQueue.main) + .sink { [weak self] completion in + if case .failure = completion { + if self?.state == .loading { self?.state = .error } + self?.scheduledLoadRooms() + } + } receiveValue: { roomsResponse, subscriptionsResponse in + self.database.handleRoomsResponse(subscriptionsResponse, roomsResponse) + self.updateServer(to: newUpdatedSince) + self.scheduledLoadRooms() + } + .store(in: &cancellable) + } + + /// This method updates the updateSince timestamp only once in servers with versions below 4. + /// + /// It is required due to missing events in the rooms and subscriptions + /// requests in those old versions. We get extra information + /// by passing a date that is older than the real updatedSince last timestamp. + private func updateServer(to newUpdatedSince: Date) { + if !(server.version >= "4") { + if shouldUpdatedDateOnce { + server.updatedSince = newUpdatedSince + serversDB.save() + shouldUpdatedDateOnce = false + } + } else { + server.updatedSince = newUpdatedSince + serversDB.save() + } + } + + private func observeContext() { + NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave) + .receive(on: DispatchQueue.main) + .sink { [database] notification in + if let context = notification.object as? NSManagedObjectContext { + if database.has(context: context) { + self.state = .loaded + } + } + } + .store(in: &cancellable) + } +} + +extension RoomsLoader: RoomsLoading { + func start() { + stop() + + loadRooms() + observeContext() + } + + func stop() { + timer?.invalidate() + cancellable.cancelAll() + } +} + +extension RoomsLoader { + enum State { + case loaded + case loading + case error + } +} + +private extension String { + static func >=(lhs: String, rhs: String) -> Bool { + lhs.compare(rhs, options: .numeric) == .orderedDescending || lhs.compare(rhs, options: .numeric) == .orderedSame + } +} diff --git a/ios/RocketChat Watch App/Loaders/ServersLoader.swift b/ios/RocketChat Watch App/Loaders/ServersLoader.swift new file mode 100644 index 000000000..fb6708aba --- /dev/null +++ b/ios/RocketChat Watch App/Loaders/ServersLoader.swift @@ -0,0 +1,58 @@ +import Combine +import Foundation +import WatchConnectivity + +enum ServersLoadingError: Error, Equatable { + case unactive + case unreachable + case locked + case undecodable(Error) + + static func == (lhs: ServersLoadingError, rhs: ServersLoadingError) -> Bool { + switch (lhs, rhs) { + case (.unactive, .unactive), (.unreachable, .unreachable), (.locked, .locked), (.undecodable, .undecodable): + return true + default: + return false + } + } +} + +protocol ServersLoading { + func loadServers() -> AnyPublisher +} + +final class ServersLoader: NSObject { + @Dependency private var database: ServersDatabase + + private let session: WatchSessionProtocol + + init(session: WatchSessionProtocol = RetriableWatchSession()) { + self.session = session + super.init() + } +} + +// MARK: - ServersLoading + +extension ServersLoader: ServersLoading { + func loadServers() -> AnyPublisher { + Future { [self] promise in + session.sendMessage { result in + switch result { + case .success(let message): + for server in message.servers { + DispatchQueue.main.async { + self.database.process(updatedServer: server) + } + } + + promise(.success(())) + case .failure(let error): + promise(.failure(error)) + } + } + } + .eraseToAnyPublisher() + } +} diff --git a/ios/RocketChat Watch App/Loaders/WatchSession.swift b/ios/RocketChat Watch App/Loaders/WatchSession.swift new file mode 100644 index 000000000..4989b7fa8 --- /dev/null +++ b/ios/RocketChat Watch App/Loaders/WatchSession.swift @@ -0,0 +1,98 @@ +import WatchConnectivity + +protocol WatchSessionProtocol { + func sendMessage(completionHandler: @escaping (Result) -> Void) +} + +/// Default WatchSession protocol implementation. +final class WatchSession: NSObject, WatchSessionProtocol, WCSessionDelegate { + private let session: WCSession + + init(session: WCSession) { + self.session = session + super.init() + session.delegate = self + session.activate() + } + + func sendMessage(completionHandler: @escaping (Result) -> Void) { + guard session.activationState == .activated else { + completionHandler(.failure(.unactive)) + return + } + + guard !session.iOSDeviceNeedsUnlockAfterRebootForReachability else { + completionHandler(.failure(.locked)) + return + } + + guard session.isReachable else { + completionHandler(.failure(.unreachable)) + return + } + + session.sendMessage([:]) { dictionary in + do { + let data = try JSONSerialization.data(withJSONObject: dictionary) + let message = try JSONDecoder().decode(WatchMessage.self, from: data) + + completionHandler(.success(message)) + } catch { + completionHandler(.failure(.undecodable(error))) + } + } + } + + func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { + + } +} + +/// Retry decorator for WatchSession protocol. +final class RetriableWatchSession: WatchSessionProtocol { + private let session: WatchSessionProtocol + private let retries: Int + + init(session: WatchSessionProtocol = DelayableWatchSession(session: WatchSession(session: .default)), retries: Int = 3) { + self.session = session + self.retries = retries + } + + func sendMessage(completionHandler: @escaping (Result) -> Void) { + session.sendMessage { [weak self] result in + guard let self else { + return + } + + switch result { + case .success(let message): + completionHandler(.success(message)) + case .failure where self.retries > 0: + self.session.sendMessage(completionHandler: completionHandler) + case .failure(let error): + completionHandler(.failure(error)) + } + } + } +} + +/// Delay decorator for WatchSession protocol. +final class DelayableWatchSession: WatchSessionProtocol { + private let delay: TimeInterval + private let session: WatchSessionProtocol + + init(delay: TimeInterval = 1, session: WatchSessionProtocol) { + self.delay = delay + self.session = session + } + + func sendMessage(completionHandler: @escaping (Result) -> Void) { + Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { [weak self] _ in + guard let self else { + return + } + + self.session.sendMessage(completionHandler: completionHandler) + } + } +} diff --git a/ios/RocketChat Watch App/Localizable.xcstrings b/ios/RocketChat Watch App/Localizable.xcstrings new file mode 100644 index 000000000..ab84156cb --- /dev/null +++ b/ios/RocketChat Watch App/Localizable.xcstrings @@ -0,0 +1,1126 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "%@ " : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" + } + } + } + }, + "%@ sent an attachment" : { + "extractionState" : "manual", + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ が添付ファイルを送信しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ enviou um anexo" + } + } + } + }, + "added @%@ to this team" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "このチームに @%@ を追加しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "adicionou @%@ a este time" + } + } + } + }, + "added #%@ to this team" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "このチームに #%@ を追加しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "adicionou #%@ a este time" + } + } + } + }, + "added %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ を追加しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "adicionou o usuário %@" + } + } + } + }, + "allowed reactions" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "許可されたリアクション" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "adicionou permissão de reagir" + } + } + } + }, + "archived room" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アーカイブされたルーム" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "arquivou a sala" + } + } + } + }, + "Attachment not supported." : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "添付ファイルはサポートされていません。" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anexo não suportado." + } + } + } + }, + "Call started" : { + "extractionState" : "manual", + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "通話が開始されました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chamada iniciada" + } + } + } + }, + "Call started by %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "通話開始者 %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chamada iniciada por %@" + } + } + } + }, + "Call started by: %@" : { + "extractionState" : "manual", + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "通話開始者: %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chamada iniciada por: %@" + } + } + } + }, + "changed room announcement to: %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "がルームアナウンスを次のように変更しました: %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "alterou o anúncio da sala para: %@" + } + } + } + }, + "changed room avatar" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ルームのアバターを変更しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "alterou avatar da sala" + } + } + } + }, + "changed room description to: %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "が部屋の説明を次のように変更しました: %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "alterou a descrição da sala para: %@" + } + } + } + }, + "changed room name to: %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "がルーム名を %@ に変更しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "alterou o nome da sala para: %@" + } + } + } + }, + "changed room to %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "が部屋を %@ に変更しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "mudou sala para %@" + } + } + } + }, + "changed room topic to: %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "がルームのトピックを次のように変更しました: %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "mudou tópico da sala para: %@" + } + } + } + }, + "Chat on hold: %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "チャット保留中: %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conversa em espera: %@" + } + } + } + }, + "Chat started" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "チャットが始まりました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conversa iniciada" + } + } + } + }, + "Conversation closed" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "会話は終了しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conversa encerrada" + } + } + } + }, + "converted #%@ to a team" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "が #%@ をチームに変換しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "converteu #%@ em time" + } + } + } + }, + "converted #%@ to channel" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "が #%@ をチャネルに変換しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "converteu #%@ em canal" + } + } + } + }, + "Could not connect to your iPhone." : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "iPhone に接続できませんでした。" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível conectar ao seu iPhone." + } + } + } + }, + "Could not load rooms." : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "部屋を読み込めませんでした。" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível carregar as salas." + } + } + } + }, + "Could not reach your iPhone." : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "iPhone にアクセスできませんでした。" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível acessar seu iPhone." + } + } + } + }, + "Could not read workspaces from iPhone." : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "iPhone からワークスペースを読み取ることができませんでした。" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Não foi possível ler os workspaces do iPhone." + } + } + } + }, + "created a snippet" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ がスニペットを作成しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "criou um snippet" + } + } + } + }, + "defined %@ as %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "defined %1$@ as %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "は %1$@ を %2$@ として定義しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "definiu %1$@ como %2$@" + } + } + } + }, + "Delete" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "消去" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Excluir" + } + } + } + }, + "deleted #%@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "#%@ を削除しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "#%@ apagada" + } + } + } + }, + "disabled E2E encryption for this room" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "このルームの E2E 暗号化が無効になりました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "desabilitou criptografia para essa sala" + } + } + } + }, + "disallowed reactions" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@ 許可されていないリアクション" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "removeu a permissão de reagir" + } + } + } + }, + "enabled E2E encryption for this room" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "このルームの E2E 暗号化が有効になりました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "habilitou criptografia para essa sala" + } + } + } + }, + "Encrypted message" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "暗号化されたメッセージ" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensagem criptografada" + } + } + } + }, + "has left the team" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "はチームを離れました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "saiu do time" + } + } + } + }, + "joined the channel" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "がチャンネルに参加しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "entrou no canal" + } + } + } + }, + "joined the conversation" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "さんが会話に参加しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "entrou na conversa" + } + } + } + }, + "joined this team" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "がこのチームに参加しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "entrou no time" + } + } + } + }, + "left the channel" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "がチャンネルを離れました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "saiu da conversa" + } + } + } + }, + "Load more..." : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "もっと読み込む..." + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Carregar mais..." + } + } + } + }, + "Message" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "メッセージ" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensagem" + } + } + } + }, + "message removed" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "メッセージが削除されました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "mensagem removida" + } + } + } + }, + "muted %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "をミュートしました %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "silenciou o usuário %@" + } + } + } + }, + "New chat transfer: %@ returned the chat to the queue" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "新しいチャット転送: %@ がチャットをキューに戻しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nova transferência de conversa: %@ retornou conversa para a fila" + } + } + } + }, + "No connected workspaces." : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "接続されたワークスペースがありません。" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem workspaces." + } + } + } + }, + "No message" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "メッセージなし" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nenhuma mensagem" + } + } + } + }, + "No messages" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "メッセージはありません" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem mensagens" + } + } + } + }, + "On hold chat resumed: %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "保留中のチャットが再開されました: %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Conversa em espera retomada: %@" + } + } + } + }, + "Pinned a message:" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "メッセージを固定しました:" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fixou uma mensagem:" + } + } + } + }, + "Please unlock your iPhone." : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "iPhoneのロックを解除してください。" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Por favor, desbloqueie seu iPhone." + } + } + } + }, + "Refresh" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "リフレッシュ" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atualizar" + } + } + } + }, + "removed @%@ from this team" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "がこのチームから @%@ を削除しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "removeu @%@ deste time" + } + } + } + }, + "removed #%@ from this team" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "がこのチームから #%@ を削除しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "removeu #%@ deste time" + } + } + } + }, + "removed %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "が削除されました %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "removeu %@" + } + } + } + }, + "removed %@ as %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "removed %1$@ as %2$@" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$@ を %2$@ として削除しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "removeu %1$@ como %2$@" + } + } + } + }, + "removed read only permission" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "は読み取り専用権限を削除しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "removeu permissão de escrita da sala" + } + } + } + }, + "Resend" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "再送信" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reenviar" + } + } + } + }, + "returned the chat to the queue" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "はチャットをキューに戻しました" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "retornou conversa para a fila" + } + } + } + }, + "Rooms" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "部屋" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salas" + } + } + } + }, + "set room to read only" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ルームを読み取り専用に設定します" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "adicionou permissão de escrita à sala" + } + } + } + }, + "This room is read only" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "この部屋は読み取り専用です" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Esta sala é somente leitura" + } + } + } + }, + "Try again" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "もう一度やり直してください" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tentar novamente" + } + } + } + }, + "unarchived room" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "未アーカイブルーム" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "desarquivou a sala" + } + } + } + }, + "Unexpected error." : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "予期しないエラー。" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Erro inesperado." + } + } + } + }, + "unmuted %@" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ミュート解除 %@" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "permitiu que %@ fale na sala" + } + } + } + }, + "Unread messages" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "未読メッセージ" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensagens não lidas" + } + } + } + }, + "Unsupported system message" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "サポートされていないシステム メッセージ" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mensagem de sistema não suportada" + } + } + } + }, + "Workspaces" : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ワークスペース" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Workspaces" + } + } + } + }, + "You" : { + "extractionState" : "manual", + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "あなた" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Você" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/ios/RocketChat Watch App/Preview Content/Preview Assets.xcassets/Contents.json b/ios/RocketChat Watch App/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/ios/RocketChat Watch App/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/RocketChat Watch App/RocketChatApp.swift b/ios/RocketChat Watch App/RocketChatApp.swift new file mode 100644 index 000000000..274417e7e --- /dev/null +++ b/ios/RocketChat Watch App/RocketChatApp.swift @@ -0,0 +1,23 @@ +import SwiftUI +import WatchKit + +@main +struct RocketChat_Watch_AppApp: App { + @WKApplicationDelegateAdaptor var delegate: ExtensionDelegate + + init() { + registerDependencies() + } + + private func registerDependencies() { + Store.register(AppRouting.self, factory: delegate.router) + Store.register(ServersDatabase.self, factory: delegate.database) + Store.register(ServersLoading.self, factory: ServersLoader()) + } + + var body: some Scene { + WindowGroup { + AppView(router: delegate.router) + } + } +} diff --git a/ios/RocketChat Watch App/Storage.swift b/ios/RocketChat Watch App/Storage.swift new file mode 100644 index 000000000..e79a0277b --- /dev/null +++ b/ios/RocketChat Watch App/Storage.swift @@ -0,0 +1,32 @@ +import Foundation + +enum StorageKey: String { + case currentServer = "current_server" +} + +@propertyWrapper +struct Storage { + private let key: StorageKey + private let defaultValue: T? + + init(_ key: StorageKey, defaultValue: T? = nil) { + self.key = key + self.defaultValue = defaultValue + } + + var wrappedValue: T? { + get { + guard let data = UserDefaults.standard.object(forKey: key.rawValue) as? Data else { + return defaultValue + } + + let value = try? JSONDecoder().decode(T.self, from: data) + return value ?? defaultValue + } + set { + let data = try? JSONEncoder().encode(newValue) + + UserDefaults.standard.set(data, forKey: key.rawValue) + } + } +} diff --git a/ios/RocketChat Watch App/ViewModels/MessageViewModel.swift b/ios/RocketChat Watch App/ViewModels/MessageViewModel.swift new file mode 100644 index 000000000..be521dc7c --- /dev/null +++ b/ios/RocketChat Watch App/ViewModels/MessageViewModel.swift @@ -0,0 +1,49 @@ +import SwiftUI + +final class MessageViewModel: ObservableObject { + @Published private(set) var server: Server? + @Published private(set) var message: Message + @Published private(set) var previousMessage: Message? + + private let messageFormatter: MessageFormatter + + init(message: Message, previousMessage: Message? = nil, server: Server?, lastOpen: Date?) { + self.message = message + self.previousMessage = previousMessage + self.messageFormatter = MessageFormatter( + message: message, + previousMessage: previousMessage, + lastOpen: lastOpen + ) + self.server = server + } + + var sender: String? { + server?.useRealName == true ? message.user?.name : message.user?.username + } + + var date: String? { + messageFormatter.date() + } + + var time: String? { + messageFormatter.time() + } + + var info: LocalizedStringKey? { + messageFormatter.info() + } + + var hasDateSeparator: Bool { + messageFormatter.hasDateSeparator() + } + + var hasUnreadSeparator: Bool { + messageFormatter.hasUnreadSeparator() + } + + var isHeader: Bool { + messageFormatter.isHeader() && !messageHaveAuthorName(message.t ?? "") + } + +} diff --git a/ios/RocketChat Watch App/ViewModels/RoomViewModel.swift b/ios/RocketChat Watch App/ViewModels/RoomViewModel.swift new file mode 100644 index 000000000..747c28eb3 --- /dev/null +++ b/ios/RocketChat Watch App/ViewModels/RoomViewModel.swift @@ -0,0 +1,114 @@ +import SwiftUI + +final class RoomViewModel: ObservableObject { + @Published var room: Room + @Published var server: Server + + private let formatter: RoomFormatter + + init(room: Room, server: Server) { + self.room = room + self.server = server + self.formatter = RoomFormatter(room: room, server: server) + } + + var title: String? { + formatter.title + } + + var iconName: String? { + if room.prid != nil { + return "discussions" + } else if room.teamMain == true, room.t == "p" { + return "teams-private" + } else if room.teamMain == true { + return "teams" + } else if room.t == "p" { + return "channel-private" + } else if room.t == "c" { + return "channel-public" + } else if room.t == "d", formatter.isGroupChat { + return "message" + } + + return nil + } + + var lastMessage: String { + guard let user = room.lastMessage?.user else { + return String(localized: "No message") + } + + let isLastMessageSentByMe = user.username == server.loggedUser.username + let username = isLastMessageSentByMe ? String(localized: "You") : ((server.useRealName ? user.name : user.username) ?? "") + let message = room.lastMessage?.msg ?? String(localized: "No message") + + if room.lastMessage?.t == "jitsi_call_started" { + return String(localized: "Call started by: \(username)") + } + + if room.lastMessage?.attachments?.allObjects.isEmpty == false { + return String(localized: "\(username) sent an attachment") + } + + if room.lastMessage?.t == "e2e" { + return String(localized: "Encrypted message") + } + + if room.lastMessage?.t == "videoconf" { + return String(localized: "Call started") + } + + if room.t == "d", !isLastMessageSentByMe { + return message + } + + return "\(username): \(message)" + } + + var updatedAt: String? { + guard let ts = room.ts else { + return nil + } + + let calendar = Calendar.current + let dateFormatter = DateFormatter() + dateFormatter.locale = Locale.current + dateFormatter.timeZone = TimeZone.current + + if calendar.isDateInYesterday(ts) { + return "Yesterday" + } + + if calendar.isDateInToday(ts) { + dateFormatter.timeStyle = .short + dateFormatter.dateStyle = .none + + return dateFormatter.string(from: ts) + } + + if isDateFromLastWeek(ts) { + dateFormatter.dateFormat = "EEEE" + + return dateFormatter.string(from: ts) + } + + dateFormatter.timeStyle = .none + dateFormatter.dateStyle = .short + + return dateFormatter.string(from: ts) + } + + private func isDateFromLastWeek(_ date: Date) -> Bool { + let calendar = Calendar.current + let now = Date() + + let startOfCurrentWeek = calendar.date(from: calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: now))! + + guard let startOfLastWeek = calendar.date(byAdding: .day, value: -7, to: startOfCurrentWeek) else { + return false + } + + return calendar.isDate(date, inSameDayAs: startOfLastWeek) || date > startOfLastWeek + } +} diff --git a/ios/RocketChat Watch App/Views/AttachmentView.swift b/ios/RocketChat Watch App/Views/AttachmentView.swift new file mode 100644 index 000000000..03e83f0a7 --- /dev/null +++ b/ios/RocketChat Watch App/Views/AttachmentView.swift @@ -0,0 +1,33 @@ +import SwiftUI + +struct AttachmentView: View { + @Dependency private var client: RocketChatClientProtocol + + private let attachment: Attachment + + init(attachment: Attachment) { + self.attachment = attachment + } + + var body: some View { + VStack(alignment: .leading) { + if let msg = attachment.msg { + Text(msg) + .font(.caption) + .foregroundStyle(.white) + } + if let rawURL = attachment.imageURL { + RemoteImage(url: client.authorizedURL(url: rawURL)) { + ProgressView() + } + .foregroundStyle(.secondary) + .aspectRatio(attachment.aspectRatio, contentMode: .fit) + .cornerRadius(4) + } else { + Text("Attachment not supported.") + .font(.caption.italic()) + .foregroundStyle(Color.secondaryInfo) + } + } + } +} diff --git a/ios/RocketChat Watch App/Views/ChatScrollView.swift b/ios/RocketChat Watch App/Views/ChatScrollView.swift new file mode 100644 index 000000000..7e2baf745 --- /dev/null +++ b/ios/RocketChat Watch App/Views/ChatScrollView.swift @@ -0,0 +1,28 @@ +import SwiftUI + +/// We need to reverse the scroll view to make it look like a Chat list. +/// Since we want to support older WatchOS versions, we made this wrapper to rotate the scroll view, when we can't use defaultScrollAnchor modifier. +/// It should do the trick for older WatchOS versions and have the native implementation for newer ones. +/// We hide the indicators for the flipped scroll view, since they appear reversed. +struct ChatScrollView: View { + private let content: () -> Content + + init(@ViewBuilder content: @escaping () -> Content) { + self.content = content + } + + var body: some View { + if #available(watchOS 10.0, *) { + ScrollView { + content() + } + .defaultScrollAnchor(.bottom) + } else { + ScrollView(showsIndicators: false) { + content() + .rotationEffect(.degrees(180)) + } + .rotationEffect(.degrees(180)) + } + } +} diff --git a/ios/RocketChat Watch App/Views/LazyView.swift b/ios/RocketChat Watch App/Views/LazyView.swift new file mode 100644 index 000000000..ecd6fac0b --- /dev/null +++ b/ios/RocketChat Watch App/Views/LazyView.swift @@ -0,0 +1,13 @@ +import SwiftUI + +struct LazyView: View { + private let build: () -> Content + + init(_ build: @autoclosure @escaping () -> Content) { + self.build = build + } + + var body: Content { + build() + } +} diff --git a/ios/RocketChat Watch App/Views/LoggedInView.swift b/ios/RocketChat Watch App/Views/LoggedInView.swift new file mode 100644 index 000000000..6d507a1be --- /dev/null +++ b/ios/RocketChat Watch App/Views/LoggedInView.swift @@ -0,0 +1,20 @@ +import SwiftUI + +struct LoggedInView: View { + @Dependency private var database: Database + @Dependency private var roomsLoader: RoomsLoader + + @EnvironmentObject private var router: AppRouter + + private let server: Server + + init(server: Server) { + self.server = server + } + + var body: some View { + RoomListView(server: server, roomsLoader: roomsLoader) + .environmentObject(router) + .environment(\.managedObjectContext, database.viewContext) + } +} diff --git a/ios/RocketChat Watch App/Views/MessageActionView.swift b/ios/RocketChat Watch App/Views/MessageActionView.swift new file mode 100644 index 000000000..b63e2070d --- /dev/null +++ b/ios/RocketChat Watch App/Views/MessageActionView.swift @@ -0,0 +1,34 @@ +import SwiftUI + +struct MessageActionView: View { + @Environment(\.dismiss) private var dismiss + + private let action: (MessageAction) -> Void + private let message: Message + + init(message: Message, action: @escaping (MessageAction) -> Void) { + self.action = action + self.message = message + } + + var body: some View { + VStack { + Button(action: { + dismiss() + + action(.resend(message)) + }, label: { + Text("Resend") + }) + Button(action: { + dismiss() + + action(.delete(message)) + }, label: { + Text("Delete") + .foregroundStyle(.red) + }) + } + .padding() + } +} diff --git a/ios/RocketChat Watch App/Views/MessageComposerView.swift b/ios/RocketChat Watch App/Views/MessageComposerView.swift new file mode 100644 index 000000000..f57aeeb02 --- /dev/null +++ b/ios/RocketChat Watch App/Views/MessageComposerView.swift @@ -0,0 +1,34 @@ +import SwiftUI + +struct MessageComposerView: View { + @State private var message = "" + + let room: Room + let onSend: (String) -> Void + + var body: some View { + if room.isReadOnly { + HStack { + Spacer() + Text("This room is read only") + .font(.caption.bold()) + .foregroundStyle(.white) + .multilineTextAlignment(.center) + Spacer() + } + } else { + TextField("Message", text: $message) + .submitLabel(.send) + .onSubmit(send) + } + } + + func send() { + guard !message.isEmpty else { + return + } + + onSend(message) + message = "" + } +} diff --git a/ios/RocketChat Watch App/Views/MessageListView.swift b/ios/RocketChat Watch App/Views/MessageListView.swift new file mode 100644 index 000000000..9884a9345 --- /dev/null +++ b/ios/RocketChat Watch App/Views/MessageListView.swift @@ -0,0 +1,125 @@ +import SwiftUI + +struct MessageListView: View { + private let messageComposer = "MESSAGE_COMPOSER_ID" + + @Dependency private var database: Database + @Dependency private var messagesLoader: MessagesLoading + @Dependency private var messageSender: MessageSending + + private let formatter: RoomFormatter + private let server: Server + + @ObservedObject private var room: Room + + @State private var lastOpen: Date? + @State private var info: Room? + + @Environment(\.scenePhase) private var scenePhase + + @FetchRequest private var messages: FetchedResults + + init(room: Room, server: Server) { + self.formatter = RoomFormatter(room: room, server: server) + self.room = room + self.server = server + _messages = FetchRequest(fetchRequest: room.messagesRequest, animation: .none) + _lastOpen = State(wrappedValue: room.updatedSince) + } + + var body: some View { + Group { + if messages.count == 0 { + HStack(alignment: .bottom) { + Spacer() + VStack { + Text("No messages") + .font(.caption.italic()) + .foregroundStyle(Color.secondaryInfo) + .frame(maxHeight: .infinity) + } + Spacer() + } + } + ChatScrollView { + VStack(spacing: 0) { + if room.hasMoreMessages { + Button("Load more...") { + guard let oldestMessage = room.firstMessage?.ts else { return } + + messagesLoader.loadMore(from: oldestMessage) + } + .padding(.bottom, 8) + } + + ForEach(messages.indices, id: \.self) { index in + let message = messages[index] + let previousMessage = messages.indices.contains(index - 1) ? messages[index - 1] : nil + + MessageView( + viewModel: .init( + message: message, + previousMessage: previousMessage, + server: server, + lastOpen: lastOpen + ) + ) { action in + switch action { + case .resend(let message): + messageSender.resendMessage(message: message, in: room) + + lastOpen = nil + case .delete(let message): + database.remove(message) + } + } + } + + MessageComposerView(room: room) { + messageSender.sendMessage($0, in: room) + + lastOpen = nil + } + .id(messageComposer) + .padding(.top, 8) + } + } + } + .padding([.leading, .trailing]) + .navigationDestination(for: $info) { room in + RoomInfoView(room: room) + .environment(\.managedObjectContext, database.viewContext) + } + .navigationTitle { + Text(formatter.title ?? "") + .foregroundStyle(Color.titleLabels) + .onTapGesture { + if room.t == "d" { + info = room + } + } + } + .navigationBarTitleDisplayMode(.inline) + .disabled(!room.synced) + .onAppear { + guard let roomID = room.id else { return } + + messagesLoader.start(on: roomID) + } + .onDisappear { + messagesLoader.stop() + } + .onChange(of: scenePhase) { phase in + switch phase { + case .active: + guard let roomID = room.id else { return } + + messagesLoader.start(on: roomID) + case .background, .inactive: + messagesLoader.stop() + @unknown default: + break + } + } + } +} diff --git a/ios/RocketChat Watch App/Views/MessageView.swift b/ios/RocketChat Watch App/Views/MessageView.swift new file mode 100644 index 000000000..77dafae86 --- /dev/null +++ b/ios/RocketChat Watch App/Views/MessageView.swift @@ -0,0 +1,139 @@ +import SwiftUI + +enum MessageAction { + case resend(Message) + case delete(Message) +} + +struct MessageView: View { + @Dependency private var client: RocketChatClientProtocol + + @ObservedObject private var viewModel: MessageViewModel + + @State private var message: Message? + + private let action: (MessageAction) -> Void + + init(viewModel: MessageViewModel, action: @escaping (MessageAction) -> Void) { + self.action = action + self.viewModel = viewModel + } + + @ViewBuilder + private var unreadSeparator: some View { + HStack(alignment: .center) { + Text("Unread messages") + .lineLimit(1) + .font(.footnote) + .foregroundStyle(.red) + .layoutPriority(1) + VStack(alignment: .center) { + Divider() + .overlay(.red) + } + } + } + + @ViewBuilder + private var dateSeparator: some View { + HStack(alignment: .center) { + VStack(alignment: .center) { + Divider() + .overlay(.secondary) + } + Text(viewModel.date ?? "") + .lineLimit(1) + .font(.footnote) + .foregroundStyle(.secondary) + .layoutPriority(1) + VStack(alignment: .center) { + Divider() + .overlay(.secondary) + } + } + } + + var body: some View { + HStack { + VStack(alignment: .leading, spacing: 0) { + if viewModel.hasDateSeparator { + dateSeparator + } else if viewModel.hasUnreadSeparator { + unreadSeparator + } + if viewModel.isHeader { + HStack(alignment: .center) { + Text(viewModel.sender ?? "") + .lineLimit(1) + .font(.caption.bold()) + .foregroundStyle(Color.default) + Text(viewModel.time ?? "") + .lineLimit(1) + .font(.footnote) + .foregroundStyle(.secondary) + if viewModel.message.editedAt != nil { + Image(systemName: "pencil") + .font(.caption) + .foregroundStyle(.secondary) + } + } + .padding(.bottom, 2) + .padding(.top, 6) + } + if let text = viewModel.info { + (Text("\(viewModel.sender ?? "") ").font(.caption.bold().italic()) + Text(text).font(.caption.italic())) + .foregroundStyle(Color.default) + } else if let text = viewModel.message.msg { + HStack(alignment: .top) { + Text(text) + .font(.caption) + .foregroundStyle(viewModel.message.foregroundColor) + + if viewModel.message.status == "error" { + Button( + action: { + message = viewModel.message + }, + label: { + Image(systemName: "exclamationmark.circle") + .font(.caption) + .foregroundStyle(.red) + } + ) + .buttonStyle(PlainButtonStyle()) + } + + if viewModel.message.editedAt != nil && !viewModel.isHeader { + Image(systemName: "pencil") + .font(.caption) + .foregroundStyle(.secondary) + } + } + } + if let attachments = viewModel.message.attachments?.allObjects as? Array { + ForEach(attachments) { attachment in + AttachmentView(attachment: attachment) + } + } + } + Spacer() + } + .padding(.bottom, 2) + .sheet(item: $message) { message in + MessageActionView( + message: message, + action: action + ) + } + } +} + +private extension Message { + var foregroundColor: Color { + if status == "temp" || status == "error" { + return Color.secondaryInfo + } + + return Color.default + } +} diff --git a/ios/RocketChat Watch App/Views/NavigationStackModifier.swift b/ios/RocketChat Watch App/Views/NavigationStackModifier.swift new file mode 100644 index 000000000..a6dbecfe5 --- /dev/null +++ b/ios/RocketChat Watch App/Views/NavigationStackModifier.swift @@ -0,0 +1,30 @@ +import SwiftUI + +struct NavigationStackModifier: ViewModifier { + let item: Binding + let destination: (Item) -> Destination + + func body(content: Content) -> some View { + content.background { + NavigationLink(isActive: item.mappedToBool()) { + if let item = item.wrappedValue { + destination(item) + } else { + EmptyView() + } + } label: { + EmptyView() + } + .opacity(0) + } + } +} + +public extension View { + func navigationDestination( + for binding: Binding, + @ViewBuilder destination: @escaping (Item) -> Destination + ) -> some View { + self.modifier(NavigationStackModifier(item: binding, destination: destination)) + } +} diff --git a/ios/RocketChat Watch App/Views/RemoteImage.swift b/ios/RocketChat Watch App/Views/RemoteImage.swift new file mode 100644 index 000000000..697f2ef5a --- /dev/null +++ b/ios/RocketChat Watch App/Views/RemoteImage.swift @@ -0,0 +1,27 @@ +import SwiftUI + +struct RemoteImage: View { + @StateObject private var loader: ImageLoader + private let placeholder: Placeholder + + init(url: URL, @ViewBuilder placeholder: () -> Placeholder) { + self.placeholder = placeholder() + _loader = StateObject(wrappedValue: ImageLoader(url: url)) + } + + var body: some View { + content + .onAppear(perform: loader.load) + } + + private var content: some View { + Group { + if loader.image != nil { + Image(uiImage: loader.image!) + .resizable() + } else { + placeholder + } + } + } +} diff --git a/ios/RocketChat Watch App/Views/RetryView.swift b/ios/RocketChat Watch App/Views/RetryView.swift new file mode 100644 index 000000000..b63d9a74a --- /dev/null +++ b/ios/RocketChat Watch App/Views/RetryView.swift @@ -0,0 +1,20 @@ +import SwiftUI + +struct RetryView: View { + private let label: LocalizedStringKey + private let action: () -> Void + + init(_ label: LocalizedStringKey, action: @escaping () -> Void) { + self.label = label + self.action = action + } + + var body: some View { + VStack { + Text(label) + .multilineTextAlignment(.center) + Button("Try again", action: action) + } + .padding() + } +} diff --git a/ios/RocketChat Watch App/Views/RoomInfoView.swift b/ios/RocketChat Watch App/Views/RoomInfoView.swift new file mode 100644 index 000000000..54bb690ef --- /dev/null +++ b/ios/RocketChat Watch App/Views/RoomInfoView.swift @@ -0,0 +1,25 @@ +import SwiftUI + +struct RoomInfoView: View { + @ObservedObject private var room: Room + + init(room: Room) { + self.room = room + } + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 8) { + Text(room.fname ?? "") + .font(.caption) + .fontWeight(.medium) + .foregroundStyle(Color.titleLabels) + Text(room.name ?? "") + .font(.caption2) + .fontWeight(.regular) + .foregroundStyle(Color.secondaryInfo) + Spacer() + } + } + } +} diff --git a/ios/RocketChat Watch App/Views/RoomListView.swift b/ios/RocketChat Watch App/Views/RoomListView.swift new file mode 100644 index 000000000..a8ceff4bd --- /dev/null +++ b/ios/RocketChat Watch App/Views/RoomListView.swift @@ -0,0 +1,63 @@ +import SwiftUI + +struct RoomListView: View { + @Dependency private var database: Database + + @EnvironmentObject private var router: AppRouter + + @ObservedObject private var server: Server + + @StateObject private var roomsLoader: RoomsLoader + + @Environment(\.scenePhase) private var scenePhase + + @FetchRequest private var rooms: FetchedResults + + init(server: Server, roomsLoader: RoomsLoader) { + self.server = server + _roomsLoader = StateObject(wrappedValue: roomsLoader) + _rooms = FetchRequest(fetchRequest: server.roomsRequest) + } + + var body: some View { + List(rooms, id: \.id) { room in + RoomView(viewModel: .init(room: room, server: server)) + .onTapGesture { + router.route(to: .room(server, room)) + } + } + .navigationDestination(for: $router.room) { room in + MessageListView(room: room, server: server) + .environment(\.managedObjectContext, database.viewContext) + } + .onAppear { + roomsLoader.start() + } + .onDisappear { + roomsLoader.stop() + } + .onChange(of: scenePhase) { phase in + switch phase { + case .active: + roomsLoader.start() + case .background, .inactive: + roomsLoader.stop() + @unknown default: + break + } + } + .navigationTitle("Rooms") + .navigationBarTitleDisplayMode(.inline) + .overlay { + switch roomsLoader.state { + case .loaded: + EmptyView() + case .loading: + ProgressView() + case .error: + Text("Could not load rooms.") + .multilineTextAlignment(.center) + } + } + } +} diff --git a/ios/RocketChat Watch App/Views/RoomView.swift b/ios/RocketChat Watch App/Views/RoomView.swift new file mode 100644 index 000000000..c1e46cd8e --- /dev/null +++ b/ios/RocketChat Watch App/Views/RoomView.swift @@ -0,0 +1,48 @@ +import SwiftUI + +struct RoomView: View { + @ObservedObject var viewModel: RoomViewModel + + private var isUnread: Bool { + (viewModel.room.unread > 0 || viewModel.room.alert) && viewModel.room.hideUnreadStatus != true + } + + var body: some View { + VStack(alignment: .leading) { + HStack { + if let iconName = viewModel.iconName { + Image(iconName) + .resizable() + .frame(width: 16, height: 16) + .scaledToFit() + } + Text(viewModel.title ?? "") + .lineLimit(1) + .font(.caption.weight(isUnread ? .bold : .medium)) + .foregroundStyle(Color.default) + Spacer() + Text(viewModel.updatedAt ?? "") + .lineLimit(1) + .font(.footnote.weight(isUnread ? .bold : .regular)) + .foregroundStyle(isUnread ? .blue : Color.default) + } + HStack(alignment: .top) { + Text(viewModel.lastMessage) + .lineLimit(2) + .font(.caption2) + .foregroundStyle(isUnread ? Color.titleLabels : Color.default) + Spacer() + if isUnread, viewModel.room.unread > 0 { + Text(String(viewModel.room.unread)) + .font(.footnote.bold()) + .padding(6) + .background( + Circle() + .fill(.blue) + ) + .foregroundColor(Color.default) + } + } + } + } +} diff --git a/ios/RocketChat Watch App/Views/ServerListView.swift b/ios/RocketChat Watch App/Views/ServerListView.swift new file mode 100644 index 000000000..2ba161e9c --- /dev/null +++ b/ios/RocketChat Watch App/Views/ServerListView.swift @@ -0,0 +1,116 @@ +import Combine +import CoreData +import SwiftUI + +struct ServerListView: View { + @EnvironmentObject private var router: AppRouter + + @Dependency private var serversLoader: ServersLoading + + @State private var state: ViewState = .loading + + @FetchRequest private var servers: FetchedResults + + init() { + let fetchRequest = Server.fetchRequest() + fetchRequest.sortDescriptors = [] + + _servers = FetchRequest(fetchRequest: fetchRequest) + } + + @ViewBuilder + private var serverList: some View { + List(servers.sort()) { server in + ServerView(server: server) + .onTapGesture { + router.route(to: .roomList(server)) + } + } + } + + @ViewBuilder + private var refreshLabel: some View { + if #available(watchOS 10.0, *) { + Image(systemName: "gobackward") + } else { + Text("Refresh") + } + } + + var body: some View { + VStack { + switch state { + case .loading: + ProgressView() + case .loaded where servers.isEmpty: + RetryView("No connected workspaces.", action: loadServers) + case .loaded: + serverList + case .error(let error) where error == .locked: + RetryView("Please unlock your iPhone.", action: loadServers) + case .error(let error) where error == .unactive: + RetryView("Could not connect to your iPhone.", action: loadServers) + case .error(let error) where error == .unreachable: + RetryView("Could not reach your iPhone.", action: loadServers) + case .error(let error) where error == .undecodable(error): + RetryView("Could not read workspaces from iPhone.", action: loadServers) + default: + RetryView("Unexpected error.", action: loadServers) + } + } + .navigationTitle { + Text("Workspaces").foregroundColor(.red) + } + .navigationBarTitleDisplayMode(.inline) + .navigationDestination(for: $router.server) { server in + LoggedInView(server: server) + .environmentObject(router) + } + .toolbar { + ToolbarItem(placement: .default) { + Button { + loadServers() + } label: { + refreshLabel + } + } + } + .onAppear { + loadServers() + } + } + + private func loadServers() { + state = .loading + + serversLoader.loadServers() + .receive(on: DispatchQueue.main) + .subscribe(Subscribers.Sink { completion in + if case .failure(let error) = completion { + state = .error(error) + } + } receiveValue: { _ in + state = .loaded + }) + } +} + +extension ServerListView { + enum ViewState { + case loading + case loaded + case error(ServersLoadingError) + } +} + +private extension Collection where Element == Server { + func sort() -> [Element] { + sorted { $0.host < $1.host } + } +} + +private extension Server { + var host: String { + url.host ?? "" + } +} diff --git a/ios/RocketChat Watch App/Views/ServerView.swift b/ios/RocketChat Watch App/Views/ServerView.swift new file mode 100644 index 000000000..274bab1f5 --- /dev/null +++ b/ios/RocketChat Watch App/Views/ServerView.swift @@ -0,0 +1,16 @@ +import SwiftUI + +struct ServerView: View { + @ObservedObject var server: Server + + var body: some View { + VStack(alignment: .leading) { + Text(server.name) + .font(.caption.bold()) + .foregroundStyle(Color.titleLabels) + Text(server.url.host ?? "") + .font(.caption) + .foregroundStyle(Color.default) + } + } +} diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index f24cb9212..0a6214efa 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -19,6 +19,10 @@ 1E01C82D2511337700FEF824 /* RoomKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E01C82C2511337700FEF824 /* RoomKey.swift */; }; 1E0426E6251A5467008F022C /* RoomType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E0426E5251A5467008F022C /* RoomType.swift */; }; 1E0426E7251A54B4008F022C /* RoomType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E0426E5251A5467008F022C /* RoomType.swift */; }; + 1E044F992B92798E00BCA2FD /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1E044F982B92798E00BCA2FD /* Localizable.xcstrings */; }; + 1E044F9A2B92798E00BCA2FD /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1E044F982B92798E00BCA2FD /* Localizable.xcstrings */; }; + 1E06561B2B7E91FB0081B01F /* ErrorActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E06561A2B7E91FB0081B01F /* ErrorActionHandler.swift */; }; + 1E06561D2B7E9C1C0081B01F /* MessageActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E06561C2B7E9C1C0081B01F /* MessageActionView.swift */; }; 1E068CFE24FD2DC700A0FFC1 /* AppGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E068CFD24FD2DC700A0FFC1 /* AppGroup.swift */; }; 1E068CFF24FD2DC700A0FFC1 /* AppGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E068CFD24FD2DC700A0FFC1 /* AppGroup.swift */; }; 1E068D0124FD2E0500A0FFC1 /* AppGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E068D0024FD2E0500A0FFC1 /* AppGroup.m */; }; @@ -34,18 +38,86 @@ 1E1EA8182326CD4B00E22452 /* libc.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E1EA8172326CD4B00E22452 /* libc.tbd */; }; 1E1EA81A2326CD5100E22452 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E1EA8192326CD5100E22452 /* libsqlite3.tbd */; }; 1E25743422CBA2CF005A877F /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ACD4853222860DE00442C55 /* JavaScriptCore.framework */; }; + 1E29A2CC2B5857F50093C03C /* RoomListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2CB2B5857F50093C03C /* RoomListView.swift */; }; + 1E29A2D02B58582F0093C03C /* RoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2CF2B58582F0093C03C /* RoomView.swift */; }; + 1E29A2EF2B585B070093C03C /* RocketChatClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D22B585B070093C03C /* RocketChatClient.swift */; }; + 1E29A2F02B585B070093C03C /* AttachmentResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D42B585B070093C03C /* AttachmentResponse.swift */; }; + 1E29A2F12B585B070093C03C /* SendMessageResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D52B585B070093C03C /* SendMessageResponse.swift */; }; + 1E29A2F22B585B070093C03C /* HistoryResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D62B585B070093C03C /* HistoryResponse.swift */; }; + 1E29A2F32B585B070093C03C /* MessagesResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D72B585B070093C03C /* MessagesResponse.swift */; }; + 1E29A2F42B585B070093C03C /* SubscriptionsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D82B585B070093C03C /* SubscriptionsResponse.swift */; }; + 1E29A2F52B585B070093C03C /* RoomsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D92B585B070093C03C /* RoomsResponse.swift */; }; + 1E29A2F62B585B070093C03C /* UserResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2DA2B585B070093C03C /* UserResponse.swift */; }; + 1E29A2F72B585B070093C03C /* ReadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2DB2B585B070093C03C /* ReadResponse.swift */; }; + 1E29A2F82B585B070093C03C /* MessageResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2DC2B585B070093C03C /* MessageResponse.swift */; }; + 1E29A2F92B585B070093C03C /* SubscriptionsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2DE2B585B070093C03C /* SubscriptionsRequest.swift */; }; + 1E29A2FA2B585B070093C03C /* HistoryRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2DF2B585B070093C03C /* HistoryRequest.swift */; }; + 1E29A2FB2B585B070093C03C /* MessagesRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E02B585B070093C03C /* MessagesRequest.swift */; }; + 1E29A2FC2B585B070093C03C /* SendMessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E12B585B070093C03C /* SendMessageRequest.swift */; }; + 1E29A2FD2B585B070093C03C /* RoomsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E22B585B070093C03C /* RoomsRequest.swift */; }; + 1E29A2FE2B585B070093C03C /* ReadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E32B585B070093C03C /* ReadRequest.swift */; }; + 1E29A2FF2B585B070093C03C /* TokenAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E52B585B070093C03C /* TokenAdapter.swift */; }; + 1E29A3002B585B070093C03C /* JSONAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E62B585B070093C03C /* JSONAdapter.swift */; }; + 1E29A3012B585B070093C03C /* RequestAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E72B585B070093C03C /* RequestAdapter.swift */; }; + 1E29A3022B585B070093C03C /* DateCodingStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E82B585B070093C03C /* DateCodingStrategy.swift */; }; + 1E29A3032B585B070093C03C /* FailableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E92B585B070093C03C /* FailableDecodable.swift */; }; + 1E29A3042B585B070093C03C /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EB2B585B070093C03C /* HTTPMethod.swift */; }; + 1E29A3052B585B070093C03C /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EC2B585B070093C03C /* Request.swift */; }; + 1E29A3072B585B070093C03C /* RocketChatError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EE2B585B070093C03C /* RocketChatError.swift */; }; + 1E29A30A2B585B370093C03C /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3092B585B370093C03C /* Data+Extensions.swift */; }; + 1E29A30C2B585D1D0093C03C /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A30B2B585D1D0093C03C /* String+Extensions.swift */; }; + 1E29A30E2B58608C0093C03C /* LoggedUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A30D2B58608C0093C03C /* LoggedUser.swift */; }; + 1E29A3102B5865B80093C03C /* RoomViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A30F2B5865B80093C03C /* RoomViewModel.swift */; }; + 1E29A3122B5866090093C03C /* Room.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3112B5866090093C03C /* Room.swift */; }; + 1E29A3162B5868DF0093C03C /* MessageListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3152B5868DF0093C03C /* MessageListView.swift */; }; + 1E29A3182B5868E50093C03C /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3172B5868E50093C03C /* MessageView.swift */; }; + 1E29A31A2B5868EE0093C03C /* MessageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3192B5868EE0093C03C /* MessageViewModel.swift */; }; + 1E29A31D2B5871B60093C03C /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A31C2B5871B60093C03C /* Date+Extensions.swift */; }; + 1E29A3202B5871C80093C03C /* RoomFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A31F2B5871C80093C03C /* RoomFormatter.swift */; }; + 1E29A3222B5871CE0093C03C /* MessageFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3212B5871CE0093C03C /* MessageFormatter.swift */; }; + 1E29A3242B5874FF0093C03C /* MessageComposerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3232B5874FF0093C03C /* MessageComposerView.swift */; }; 1E2F615B25128F9A00871711 /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2F615A25128F9A00871711 /* API.swift */; }; 1E2F615D25128FA300871711 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2F615C25128FA300871711 /* Response.swift */; }; 1E2F61642512955D00871711 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2F61632512955D00871711 /* HTTPMethod.swift */; }; 1E2F61662512958900871711 /* Push.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2F61652512958900871711 /* Push.swift */; }; + 1E388ABE2B934C64006FBDB0 /* ImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E388ABD2B934C64006FBDB0 /* ImageLoader.swift */; }; + 1E388ABF2B934C64006FBDB0 /* ImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E388ABD2B934C64006FBDB0 /* ImageLoader.swift */; }; + 1E388AC12B934CD4006FBDB0 /* RemoteImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E388AC02B934CD4006FBDB0 /* RemoteImage.swift */; }; + 1E388AC22B934CD4006FBDB0 /* RemoteImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E388AC02B934CD4006FBDB0 /* RemoteImage.swift */; }; 1E470E832513A71E00E3DD1D /* RocketChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E470E822513A71E00E3DD1D /* RocketChat.swift */; }; + 1E4AFC152B5AF09800E2AA7D /* Dependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4AFC142B5AF09800E2AA7D /* Dependency.swift */; }; + 1E4AFC172B5AF09C00E2AA7D /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4AFC162B5AF09C00E2AA7D /* Store.swift */; }; + 1E4AFC212B5B1AA000E2AA7D /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4AFC202B5B1AA000E2AA7D /* AppView.swift */; }; + 1E4AFC272B5B23C600E2AA7D /* RetryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4AFC262B5B23C600E2AA7D /* RetryView.swift */; }; + 1E5141182B856673007BE94A /* SSLPinning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E5141172B856673007BE94A /* SSLPinning.swift */; }; + 1E5141192B856673007BE94A /* SSLPinning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E5141172B856673007BE94A /* SSLPinning.swift */; }; + 1E51411C2B85683C007BE94A /* SSLPinning.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E51411B2B85683C007BE94A /* SSLPinning.m */; }; + 1E51411D2B85683C007BE94A /* SSLPinning.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E51411B2B85683C007BE94A /* SSLPinning.m */; }; 1E51D962251263CD00DC95DE /* MessageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E51D961251263CD00DC95DE /* MessageType.swift */; }; 1E51D965251263D600DC95DE /* NotificationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E51D964251263D600DC95DE /* NotificationType.swift */; }; + 1E54BA1A2BC9E7100073903D /* MergedRoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E54BA182BC9E6D60073903D /* MergedRoom.swift */; }; + 1E54BA1B2BC9E7110073903D /* MergedRoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E54BA182BC9E6D60073903D /* MergedRoom.swift */; }; + 1E54BA1D2BC9E8030073903D /* MessageMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E54BA1C2BC9E8030073903D /* MessageMapper.swift */; }; + 1E54BA1E2BC9E8030073903D /* MessageMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E54BA1C2BC9E8030073903D /* MessageMapper.swift */; }; + 1E54BA202BC9E9B60073903D /* MergedRoomMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E54BA1F2BC9E9B60073903D /* MergedRoomMapper.swift */; }; + 1E54BA212BC9E9B60073903D /* MergedRoomMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E54BA1F2BC9E9B60073903D /* MergedRoomMapper.swift */; }; 1E598AE42515057D002BDFBD /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E598AE32515057D002BDFBD /* Date+Extensions.swift */; }; 1E598AE725150660002BDFBD /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E598AE625150660002BDFBD /* Data+Extensions.swift */; }; 1E598AE925151A63002BDFBD /* SendMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E598AE825151A63002BDFBD /* SendMessage.swift */; }; + 1E638E992B5F0A2900E645E4 /* ChatScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E638E982B5F0A2900E645E4 /* ChatScrollView.swift */; }; + 1E638E9E2B5F0F5800E645E4 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E638E9D2B5F0F5800E645E4 /* URL+Extensions.swift */; }; 1E67380424DC529B0009E081 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E67380324DC529B0009E081 /* String+Extensions.swift */; }; + 1E675B722BAC49B000438590 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E675B712BAC49B000438590 /* Color+Extensions.swift */; }; + 1E675B732BAC49B000438590 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E675B712BAC49B000438590 /* Color+Extensions.swift */; }; 1E680ED92512990700C9257A /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E680ED82512990700C9257A /* Request.swift */; }; + 1E6BA5C62BD13DEA00B16A18 /* RoomModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6BA5C22BD13DEA00B16A18 /* RoomModel.swift */; }; + 1E6BA5C72BD13DEA00B16A18 /* RoomModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6BA5C22BD13DEA00B16A18 /* RoomModel.swift */; }; + 1E6BA5C82BD13DEA00B16A18 /* MessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6BA5C32BD13DEA00B16A18 /* MessageModel.swift */; }; + 1E6BA5C92BD13DEA00B16A18 /* MessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6BA5C32BD13DEA00B16A18 /* MessageModel.swift */; }; + 1E6BA5CA2BD13DEA00B16A18 /* AttachmentModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6BA5C42BD13DEA00B16A18 /* AttachmentModel.swift */; }; + 1E6BA5CB2BD13DEA00B16A18 /* AttachmentModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6BA5C42BD13DEA00B16A18 /* AttachmentModel.swift */; }; + 1E6BA5CC2BD13DEA00B16A18 /* UserModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6BA5C52BD13DEA00B16A18 /* UserModel.swift */; }; + 1E6BA5CD2BD13DEA00B16A18 /* UserModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6BA5C52BD13DEA00B16A18 /* UserModel.swift */; }; 1E6CC61F2513DBF400965591 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A006F13229C83B600803143 /* GoogleService-Info.plist */; }; 1E76CBC2251529560067298C /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EB8EF712510F1EE00F352B7 /* Storage.swift */; }; 1E76CBC325152A460067298C /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E67380324DC529B0009E081 /* String+Extensions.swift */; }; @@ -69,12 +141,144 @@ 1E76CBD825152C870067298C /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E680ED82512990700C9257A /* Request.swift */; }; 1E76CBD925152C8C0067298C /* Push.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2F61652512958900871711 /* Push.swift */; }; 1E76CBDA25152C8E0067298C /* SendMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E598AE825151A63002BDFBD /* SendMessage.swift */; }; + 1E8979472B6063FC001D99F0 /* WatchSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E8979462B6063FC001D99F0 /* WatchSession.swift */; }; + 1E9A71692B59B6E100477BA2 /* MessageSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A71682B59B6E100477BA2 /* MessageSender.swift */; }; + 1E9A716F2B59CBCA00477BA2 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A716E2B59CBCA00477BA2 /* AttachmentView.swift */; }; + 1E9A71712B59CC1300477BA2 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A71702B59CC1300477BA2 /* Attachment.swift */; }; + 1E9A71742B59F36E00477BA2 /* ClientSSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A71722B59F34E00477BA2 /* ClientSSL.swift */; }; + 1E9A71752B59F36E00477BA2 /* ClientSSL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A71722B59F34E00477BA2 /* ClientSSL.swift */; }; + 1E9A71772B59FCA900477BA2 /* URLSessionCertificateHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A71762B59FCA900477BA2 /* URLSessionCertificateHandling.swift */; }; + 1EB375892B55DBFB00AEC3D7 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EB375882B55DBFB00AEC3D7 /* Server.swift */; }; 1EB8EF722510F1EE00F352B7 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EB8EF712510F1EE00F352B7 /* Storage.swift */; }; + 1EC687BA2BA0FF0D00C7BAAD /* MessageInfoMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EC687B82BA0FECC00C7BAAD /* MessageInfoMapper.swift */; }; + 1EC687BB2BA0FF0D00C7BAAD /* MessageInfoMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EC687B82BA0FECC00C7BAAD /* MessageInfoMapper.swift */; }; 1EC6ACB722CB9FC300A41C61 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1EC6ACB522CB9FC300A41C61 /* MainInterface.storyboard */; }; 1EC6ACBB22CB9FC300A41C61 /* ShareRocketChatRN.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 1EC6ACB022CB9FC300A41C61 /* ShareRocketChatRN.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 1EC6ACF622CBA01500A41C61 /* ShareRocketChatRN.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC6ACF522CBA01500A41C61 /* ShareRocketChatRN.m */; }; 1ED00BB12513E04400A1331F /* ReplyNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED00BB02513E04400A1331F /* ReplyNotification.swift */; }; + 1ED033AE2B55B1CC004F4930 /* Default.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033AC2B55B1CC004F4930 /* Default.xcdatamodeld */; }; + 1ED033B02B55B25A004F4930 /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033AF2B55B25A004F4930 /* Database.swift */; }; + 1ED033B62B55B4A5004F4930 /* ServerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033B52B55B4A5004F4930 /* ServerListView.swift */; }; + 1ED033BA2B55B5F6004F4930 /* ServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033B92B55B5F6004F4930 /* ServerView.swift */; }; + 1ED033BF2B55BF94004F4930 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033BE2B55BF94004F4930 /* Storage.swift */; }; + 1ED033C42B55C65C004F4930 /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033C32B55C65C004F4930 /* AppRouter.swift */; }; + 1ED033CB2B55D4F0004F4930 /* RocketChat.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033C92B55D4F0004F4930 /* RocketChat.xcdatamodeld */; }; + 1ED033CD2B55D671004F4930 /* RocketChatDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033CC2B55D671004F4930 /* RocketChatDatabase.swift */; }; + 1ED038912B507B4C00C007D4 /* RocketChatApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038902B507B4C00C007D4 /* RocketChatApp.swift */; }; + 1ED038952B507B4D00C007D4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ED038942B507B4D00C007D4 /* Assets.xcassets */; }; + 1ED038982B507B4D00C007D4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ED038972B507B4D00C007D4 /* Preview Assets.xcassets */; }; + 1ED038A12B508FE700C007D4 /* FileManager+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038A02B508FE700C007D4 /* FileManager+Extensions.swift */; }; + 1ED038A22B508FE700C007D4 /* FileManager+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038A02B508FE700C007D4 /* FileManager+Extensions.swift */; }; + 1ED038A32B508FE700C007D4 /* FileManager+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038A02B508FE700C007D4 /* FileManager+Extensions.swift */; }; + 1ED038A52B50900800C007D4 /* Bundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038A42B50900800C007D4 /* Bundle+Extensions.swift */; }; + 1ED038A62B50900800C007D4 /* Bundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038A42B50900800C007D4 /* Bundle+Extensions.swift */; }; + 1ED038A72B50900800C007D4 /* Bundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038A42B50900800C007D4 /* Bundle+Extensions.swift */; }; + 1ED038A92B5090AD00C007D4 /* MMKV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038A82B5090AD00C007D4 /* MMKV.swift */; }; + 1ED038AA2B5090AD00C007D4 /* MMKV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038A82B5090AD00C007D4 /* MMKV.swift */; }; + 1ED038AB2B5090AD00C007D4 /* MMKV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038A82B5090AD00C007D4 /* MMKV.swift */; }; + 1ED038AD2B50927B00C007D4 /* WatermelonDB+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038AC2B50927B00C007D4 /* WatermelonDB+Extensions.swift */; }; + 1ED038AE2B50927B00C007D4 /* WatermelonDB+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038AC2B50927B00C007D4 /* WatermelonDB+Extensions.swift */; }; + 1ED038AF2B50927B00C007D4 /* WatermelonDB+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038AC2B50927B00C007D4 /* WatermelonDB+Extensions.swift */; }; + 1ED038BA2B50A1B800C007D4 /* WatchConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038B92B50A1B800C007D4 /* WatchConnection.swift */; }; + 1ED038BB2B50A1B800C007D4 /* WatchConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038B92B50A1B800C007D4 /* WatchConnection.swift */; }; + 1ED038BE2B50A1D400C007D4 /* DBServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038BD2B50A1D400C007D4 /* DBServer.swift */; }; + 1ED038BF2B50A1D400C007D4 /* DBServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038BD2B50A1D400C007D4 /* DBServer.swift */; }; + 1ED038C12B50A1E400C007D4 /* DBUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038C02B50A1E400C007D4 /* DBUser.swift */; }; + 1ED038C22B50A1E400C007D4 /* DBUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038C02B50A1E400C007D4 /* DBUser.swift */; }; + 1ED038C42B50A1F500C007D4 /* WatchMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038C32B50A1F500C007D4 /* WatchMessage.swift */; }; + 1ED038C52B50A1F500C007D4 /* WatchMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038C32B50A1F500C007D4 /* WatchMessage.swift */; }; + 1ED038C62B50A21800C007D4 /* WatchMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038C32B50A1F500C007D4 /* WatchMessage.swift */; }; + 1ED038CA2B50A58400C007D4 /* ServersLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038C92B50A58400C007D4 /* ServersLoader.swift */; }; + 1ED1EC892B867E2400F6620C /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED1EC882B867E2400F6620C /* ExtensionDelegate.swift */; }; + 1ED1EC902B86997F00F6620C /* MessageComposerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3232B5874FF0093C03C /* MessageComposerView.swift */; }; + 1ED1EC912B86997F00F6620C /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EB375882B55DBFB00AEC3D7 /* Server.swift */; }; + 1ED1EC922B86997F00F6620C /* MessageActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E06561C2B7E9C1C0081B01F /* MessageActionView.swift */; }; + 1ED1EC932B86997F00F6620C /* MessageListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3152B5868DF0093C03C /* MessageListView.swift */; }; + 1ED1EC942B86997F00F6620C /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4AFC162B5AF09C00E2AA7D /* Store.swift */; }; + 1ED1EC962B86997F00F6620C /* SubscriptionsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D82B585B070093C03C /* SubscriptionsResponse.swift */; }; + 1ED1EC972B86997F00F6620C /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED1EC882B867E2400F6620C /* ExtensionDelegate.swift */; }; + 1ED1EC982B86997F00F6620C /* SubscriptionsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2DE2B585B070093C03C /* SubscriptionsRequest.swift */; }; + 1ED1EC992B86997F00F6620C /* HistoryResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D62B585B070093C03C /* HistoryResponse.swift */; }; + 1ED1EC9A2B86997F00F6620C /* Dependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4AFC142B5AF09800E2AA7D /* Dependency.swift */; }; + 1ED1EC9B2B86997F00F6620C /* ServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033B92B55B5F6004F4930 /* ServerView.swift */; }; + 1ED1EC9C2B86997F00F6620C /* RoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2CF2B58582F0093C03C /* RoomView.swift */; }; + 1ED1EC9D2B86997F00F6620C /* RoomsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E22B585B070093C03C /* RoomsRequest.swift */; }; + 1ED1EC9E2B86997F00F6620C /* ServersLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038C92B50A58400C007D4 /* ServersLoader.swift */; }; + 1ED1EC9F2B86997F00F6620C /* MessageFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3212B5871CE0093C03C /* MessageFormatter.swift */; }; + 1ED1ECA02B86997F00F6620C /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A716E2B59CBCA00477BA2 /* AttachmentView.swift */; }; + 1ED1ECA12B86997F00F6620C /* JSONAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E62B585B070093C03C /* JSONAdapter.swift */; }; + 1ED1ECA22B86997F00F6620C /* DateCodingStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E82B585B070093C03C /* DateCodingStrategy.swift */; }; + 1ED1ECA32B86997F00F6620C /* RetryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4AFC262B5B23C600E2AA7D /* RetryView.swift */; }; + 1ED1ECA42B86997F00F6620C /* ServerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033B52B55B4A5004F4930 /* ServerListView.swift */; }; + 1ED1ECA52B86997F00F6620C /* RoomFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A31F2B5871C80093C03C /* RoomFormatter.swift */; }; + 1ED1ECA62B86997F00F6620C /* MessagesLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EDFD0F92B589B8F002FEE5F /* MessagesLoader.swift */; }; + 1ED1ECA72B86997F00F6620C /* RoomViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A30F2B5865B80093C03C /* RoomViewModel.swift */; }; + 1ED1ECA82B86997F00F6620C /* ErrorActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E06561A2B7E91FB0081B01F /* ErrorActionHandler.swift */; }; + 1ED1ECA92B86997F00F6620C /* SendMessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E12B585B070093C03C /* SendMessageRequest.swift */; }; + 1ED1ECAA2B86997F00F6620C /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A30B2B585D1D0093C03C /* String+Extensions.swift */; }; + 1ED1ECAB2B86997F00F6620C /* RocketChatDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033CC2B55D671004F4930 /* RocketChatDatabase.swift */; }; + 1ED1ECAC2B86997F00F6620C /* Room.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3112B5866090093C03C /* Room.swift */; }; + 1ED1ECAD2B86997F00F6620C /* FailableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E92B585B070093C03C /* FailableDecodable.swift */; }; + 1ED1ECAE2B86997F00F6620C /* WatchSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E8979462B6063FC001D99F0 /* WatchSession.swift */; }; + 1ED1ECAF2B86997F00F6620C /* ReadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E32B585B070093C03C /* ReadRequest.swift */; }; + 1ED1ECB02B86997F00F6620C /* MessageSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A71682B59B6E100477BA2 /* MessageSender.swift */; }; + 1ED1ECB12B86997F00F6620C /* RocketChatError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EE2B585B070093C03C /* RocketChatError.swift */; }; + 1ED1ECB22B86997F00F6620C /* RoomsLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EDFD1072B58AA77002FEE5F /* RoomsLoader.swift */; }; + 1ED1ECB32B86997F00F6620C /* SendMessageResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D52B585B070093C03C /* SendMessageResponse.swift */; }; + 1ED1ECB42B86997F00F6620C /* LoggedUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A30D2B58608C0093C03C /* LoggedUser.swift */; }; + 1ED1ECB52B86997F00F6620C /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3172B5868E50093C03C /* MessageView.swift */; }; + 1ED1ECB62B86997F00F6620C /* CancelBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EDFD1052B58A66E002FEE5F /* CancelBag.swift */; }; + 1ED1ECB72B86997F00F6620C /* TokenAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E52B585B070093C03C /* TokenAdapter.swift */; }; + 1ED1ECB82B86997F00F6620C /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EC2B585B070093C03C /* Request.swift */; }; + 1ED1ECB92B86997F00F6620C /* URLSessionCertificateHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A71762B59FCA900477BA2 /* URLSessionCertificateHandling.swift */; }; + 1ED1ECBA2B86997F00F6620C /* RocketChatClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D22B585B070093C03C /* RocketChatClient.swift */; }; + 1ED1ECBB2B86997F00F6620C /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4AFC202B5B1AA000E2AA7D /* AppView.swift */; }; + 1ED1ECBC2B86997F00F6620C /* MessagesRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E02B585B070093C03C /* MessagesRequest.swift */; }; + 1ED1ECBD2B86997F00F6620C /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A31C2B5871B60093C03C /* Date+Extensions.swift */; }; + 1ED1ECBE2B86997F00F6620C /* UserResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2DA2B585B070093C03C /* UserResponse.swift */; }; + 1ED1ECBF2B86997F00F6620C /* Default.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033AC2B55B1CC004F4930 /* Default.xcdatamodeld */; }; + 1ED1ECC02B86997F00F6620C /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033BE2B55BF94004F4930 /* Storage.swift */; }; + 1ED1ECC12B86997F00F6620C /* MessageResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2DC2B585B070093C03C /* MessageResponse.swift */; }; + 1ED1ECC22B86997F00F6620C /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2EB2B585B070093C03C /* HTTPMethod.swift */; }; + 1ED1ECC32B86997F00F6620C /* RequestAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2E72B585B070093C03C /* RequestAdapter.swift */; }; + 1ED1ECC42B86997F00F6620C /* RoomsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D92B585B070093C03C /* RoomsResponse.swift */; }; + 1ED1ECC52B86997F00F6620C /* LoggedInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EDB30F12B5B453A00532C7E /* LoggedInView.swift */; }; + 1ED1ECC62B86997F00F6620C /* MessagesResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D72B585B070093C03C /* MessagesResponse.swift */; }; + 1ED1ECC72B86997F00F6620C /* HistoryRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2DF2B585B070093C03C /* HistoryRequest.swift */; }; + 1ED1ECC82B86997F00F6620C /* WatchMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038C32B50A1F500C007D4 /* WatchMessage.swift */; }; + 1ED1ECC92B86997F00F6620C /* AttachmentResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2D42B585B070093C03C /* AttachmentResponse.swift */; }; + 1ED1ECCA2B86997F00F6620C /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E638E9D2B5F0F5800E645E4 /* URL+Extensions.swift */; }; + 1ED1ECCB2B86997F00F6620C /* RocketChatApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED038902B507B4C00C007D4 /* RocketChatApp.swift */; }; + 1ED1ECCC2B86997F00F6620C /* RoomListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2CB2B5857F50093C03C /* RoomListView.swift */; }; + 1ED1ECCD2B86997F00F6620C /* MessageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3192B5868EE0093C03C /* MessageViewModel.swift */; }; + 1ED1ECCE2B86997F00F6620C /* ChatScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E638E982B5F0A2900E645E4 /* ChatScrollView.swift */; }; + 1ED1ECCF2B86997F00F6620C /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033C32B55C65C004F4930 /* AppRouter.swift */; }; + 1ED1ECD02B86997F00F6620C /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033AF2B55B25A004F4930 /* Database.swift */; }; + 1ED1ECD12B86997F00F6620C /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9A71702B59CC1300477BA2 /* Attachment.swift */; }; + 1ED1ECD22B86997F00F6620C /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A3092B585B370093C03C /* Data+Extensions.swift */; }; + 1ED1ECD32B86997F00F6620C /* ReadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E29A2DB2B585B070093C03C /* ReadResponse.swift */; }; + 1ED1ECD42B86997F00F6620C /* RocketChat.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1ED033C92B55D4F0004F4930 /* RocketChat.xcdatamodeld */; }; + 1ED1ECD72B86997F00F6620C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ED038972B507B4D00C007D4 /* Preview Assets.xcassets */; }; + 1ED1ECD82B86997F00F6620C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ED038942B507B4D00C007D4 /* Assets.xcassets */; }; + 1ED1ECE42B8699E900F6620C /* Rocket.Chat Watch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 1ED0388E2B507B4B00C007D4 /* Rocket.Chat Watch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 1ED1ECE72B8699ED00F6620C /* Rocket.Chat Experimental Watch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 1ED1ECDD2B86997F00F6620C /* Rocket.Chat Experimental Watch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 1ED1ECEA2B869A4A00F6620C /* Official.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A14FCF3257FEB59005BDCD4 /* Official.xcassets */; }; + 1ED1ECEC2B869B1300F6620C /* Experimental.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A14FCEC257FEB3A005BDCD4 /* Experimental.xcassets */; }; 1ED59D4C22CBA77D00C54289 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 1ED59D4B22CBA77D00C54289 /* GoogleService-Info.plist */; }; + 1EDB30F22B5B453A00532C7E /* LoggedInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EDB30F12B5B453A00532C7E /* LoggedInView.swift */; }; + 1EDFD0FA2B589B8F002FEE5F /* MessagesLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EDFD0F92B589B8F002FEE5F /* MessagesLoader.swift */; }; + 1EDFD1062B58A66E002FEE5F /* CancelBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EDFD1052B58A66E002FEE5F /* CancelBag.swift */; }; + 1EDFD1082B58AA77002FEE5F /* RoomsLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EDFD1072B58AA77002FEE5F /* RoomsLoader.swift */; }; + 1EE096FA2BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096F92BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift */; }; + 1EE096FB2BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096F92BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift */; }; + 1EE096FD2BACD58300780078 /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096FC2BACD58300780078 /* LazyView.swift */; }; + 1EE096FE2BACD58300780078 /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096FC2BACD58300780078 /* LazyView.swift */; }; + 1EE097002BACD64C00780078 /* Binding+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096FF2BACD64C00780078 /* Binding+Extensions.swift */; }; + 1EE097012BACD64C00780078 /* Binding+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096FF2BACD64C00780078 /* Binding+Extensions.swift */; }; + 1EE097032BACD66900780078 /* NavigationStackModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE097022BACD66900780078 /* NavigationStackModifier.swift */; }; + 1EE097042BACD66900780078 /* NavigationStackModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE097022BACD66900780078 /* NavigationStackModifier.swift */; }; + 1EED2D332BC1E09500832666 /* RoomInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EED2D322BC1E09500832666 /* RoomInfoView.swift */; }; + 1EED2D342BC1E09500832666 /* RoomInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EED2D322BC1E09500832666 /* RoomInfoView.swift */; }; 1EF5FBD1250C109E00614FEA /* Encryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EF5FBD0250C109E00614FEA /* Encryption.swift */; }; 1EFEB5982493B6640072EDC0 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EFEB5972493B6640072EDC0 /* NotificationService.swift */; }; 1EFEB59C2493B6640072EDC0 /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 1EFEB5952493B6640072EDC0 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -159,6 +363,20 @@ remoteGlobalIDString = 1EC6ACAF22CB9FC300A41C61; remoteInfo = ShareRocketChatRN; }; + 1ED1ECE52B8699E900F6620C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 1ED0388D2B507B4B00C007D4; + remoteInfo = Rocket.Chat.Watch; + }; + 1EE79C542B8912F600CF1863 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 1ED1EC8E2B86997F00F6620C; + remoteInfo = "RocketChatRN Watch"; + }; 1EFEB59A2493B6640072EDC0 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; @@ -195,6 +413,28 @@ name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; }; + 1ED0389C2B507B4F00C007D4 /* Embed Watch Content */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; + dstSubfolderSpec = 16; + files = ( + 1ED1ECE72B8699ED00F6620C /* Rocket.Chat Experimental Watch.app in Embed Watch Content */, + ); + name = "Embed Watch Content"; + runOnlyForDeploymentPostprocessing = 0; + }; + 1ED1ECE32B8699DD00F6620C /* Embed Watch Content */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; + dstSubfolderSpec = 16; + files = ( + 1ED1ECE42B8699E900F6620C /* Rocket.Chat Watch.app in Embed Watch Content */, + ); + name = "Embed Watch Content"; + runOnlyForDeploymentPostprocessing = 0; + }; 7AAB3E48257E6A6E00707CF6 /* Embed App Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -227,6 +467,9 @@ 1E01C82A2511335A00FEF824 /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; }; 1E01C82C2511337700FEF824 /* RoomKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomKey.swift; sourceTree = ""; }; 1E0426E5251A5467008F022C /* RoomType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomType.swift; sourceTree = ""; }; + 1E044F982B92798E00BCA2FD /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + 1E06561A2B7E91FB0081B01F /* ErrorActionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorActionHandler.swift; sourceTree = ""; }; + 1E06561C2B7E9C1C0081B01F /* MessageActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActionView.swift; sourceTree = ""; }; 1E068CFD24FD2DC700A0FFC1 /* AppGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppGroup.swift; sourceTree = ""; }; 1E068D0024FD2E0500A0FFC1 /* AppGroup.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppGroup.m; sourceTree = ""; }; 1E1C2F7F250FCB69005DCE7D /* Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Database.swift; sourceTree = ""; }; @@ -239,27 +482,123 @@ 1E1EA8152326CD4500E22452 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; }; 1E1EA8172326CD4B00E22452 /* libc.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libc.tbd; path = usr/lib/libc.tbd; sourceTree = SDKROOT; }; 1E1EA8192326CD5100E22452 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; + 1E29A2CB2B5857F50093C03C /* RoomListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListView.swift; sourceTree = ""; }; + 1E29A2CF2B58582F0093C03C /* RoomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomView.swift; sourceTree = ""; }; + 1E29A2D22B585B070093C03C /* RocketChatClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RocketChatClient.swift; sourceTree = ""; }; + 1E29A2D42B585B070093C03C /* AttachmentResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentResponse.swift; sourceTree = ""; }; + 1E29A2D52B585B070093C03C /* SendMessageResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendMessageResponse.swift; sourceTree = ""; }; + 1E29A2D62B585B070093C03C /* HistoryResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HistoryResponse.swift; sourceTree = ""; }; + 1E29A2D72B585B070093C03C /* MessagesResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesResponse.swift; sourceTree = ""; }; + 1E29A2D82B585B070093C03C /* SubscriptionsResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionsResponse.swift; sourceTree = ""; }; + 1E29A2D92B585B070093C03C /* RoomsResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomsResponse.swift; sourceTree = ""; }; + 1E29A2DA2B585B070093C03C /* UserResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserResponse.swift; sourceTree = ""; }; + 1E29A2DB2B585B070093C03C /* ReadResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadResponse.swift; sourceTree = ""; }; + 1E29A2DC2B585B070093C03C /* MessageResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageResponse.swift; sourceTree = ""; }; + 1E29A2DE2B585B070093C03C /* SubscriptionsRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionsRequest.swift; sourceTree = ""; }; + 1E29A2DF2B585B070093C03C /* HistoryRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HistoryRequest.swift; sourceTree = ""; }; + 1E29A2E02B585B070093C03C /* MessagesRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesRequest.swift; sourceTree = ""; }; + 1E29A2E12B585B070093C03C /* SendMessageRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendMessageRequest.swift; sourceTree = ""; }; + 1E29A2E22B585B070093C03C /* RoomsRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomsRequest.swift; sourceTree = ""; }; + 1E29A2E32B585B070093C03C /* ReadRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadRequest.swift; sourceTree = ""; }; + 1E29A2E52B585B070093C03C /* TokenAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenAdapter.swift; sourceTree = ""; }; + 1E29A2E62B585B070093C03C /* JSONAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONAdapter.swift; sourceTree = ""; }; + 1E29A2E72B585B070093C03C /* RequestAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestAdapter.swift; sourceTree = ""; }; + 1E29A2E82B585B070093C03C /* DateCodingStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateCodingStrategy.swift; sourceTree = ""; }; + 1E29A2E92B585B070093C03C /* FailableDecodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FailableDecodable.swift; sourceTree = ""; }; + 1E29A2EB2B585B070093C03C /* HTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; + 1E29A2EC2B585B070093C03C /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; + 1E29A2EE2B585B070093C03C /* RocketChatError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RocketChatError.swift; sourceTree = ""; }; + 1E29A3092B585B370093C03C /* Data+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = ""; }; + 1E29A30B2B585D1D0093C03C /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; + 1E29A30D2B58608C0093C03C /* LoggedUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedUser.swift; sourceTree = ""; }; + 1E29A30F2B5865B80093C03C /* RoomViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomViewModel.swift; sourceTree = ""; }; + 1E29A3112B5866090093C03C /* Room.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Room.swift; sourceTree = ""; }; + 1E29A3152B5868DF0093C03C /* MessageListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListView.swift; sourceTree = ""; }; + 1E29A3172B5868E50093C03C /* MessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = ""; }; + 1E29A3192B5868EE0093C03C /* MessageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageViewModel.swift; sourceTree = ""; }; + 1E29A31C2B5871B60093C03C /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; }; + 1E29A31F2B5871C80093C03C /* RoomFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomFormatter.swift; sourceTree = ""; }; + 1E29A3212B5871CE0093C03C /* MessageFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageFormatter.swift; sourceTree = ""; }; + 1E29A3232B5874FF0093C03C /* MessageComposerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposerView.swift; sourceTree = ""; }; 1E2F615A25128F9A00871711 /* API.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = API.swift; sourceTree = ""; }; 1E2F615C25128FA300871711 /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; 1E2F61632512955D00871711 /* HTTPMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; 1E2F61652512958900871711 /* Push.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Push.swift; sourceTree = ""; }; + 1E388ABD2B934C64006FBDB0 /* ImageLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageLoader.swift; sourceTree = ""; }; + 1E388AC02B934CD4006FBDB0 /* RemoteImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteImage.swift; sourceTree = ""; }; 1E470E822513A71E00E3DD1D /* RocketChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RocketChat.swift; sourceTree = ""; }; + 1E4AFC142B5AF09800E2AA7D /* Dependency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dependency.swift; sourceTree = ""; }; + 1E4AFC162B5AF09C00E2AA7D /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = ""; }; + 1E4AFC202B5B1AA000E2AA7D /* AppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = ""; }; + 1E4AFC262B5B23C600E2AA7D /* RetryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryView.swift; sourceTree = ""; }; + 1E5141172B856673007BE94A /* SSLPinning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSLPinning.swift; sourceTree = ""; }; + 1E51411B2B85683C007BE94A /* SSLPinning.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SSLPinning.m; sourceTree = ""; }; 1E51D961251263CD00DC95DE /* MessageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageType.swift; sourceTree = ""; }; 1E51D964251263D600DC95DE /* NotificationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationType.swift; sourceTree = ""; }; + 1E54BA182BC9E6D60073903D /* MergedRoom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MergedRoom.swift; sourceTree = ""; }; + 1E54BA1C2BC9E8030073903D /* MessageMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageMapper.swift; sourceTree = ""; }; + 1E54BA1F2BC9E9B60073903D /* MergedRoomMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MergedRoomMapper.swift; sourceTree = ""; }; 1E598AE32515057D002BDFBD /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; }; 1E598AE625150660002BDFBD /* Data+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = ""; }; 1E598AE825151A63002BDFBD /* SendMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMessage.swift; sourceTree = ""; }; + 1E638E982B5F0A2900E645E4 /* ChatScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatScrollView.swift; sourceTree = ""; }; + 1E638E9D2B5F0F5800E645E4 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = ""; }; 1E6737FF24DC52660009E081 /* NotificationService-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NotificationService-Bridging-Header.h"; sourceTree = ""; }; 1E67380324DC529B0009E081 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; + 1E675B712BAC49B000438590 /* Color+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Extensions.swift"; sourceTree = ""; }; 1E680ED82512990700C9257A /* Request.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; + 1E6BA5C22BD13DEA00B16A18 /* RoomModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomModel.swift; sourceTree = ""; }; + 1E6BA5C32BD13DEA00B16A18 /* MessageModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageModel.swift; sourceTree = ""; }; + 1E6BA5C42BD13DEA00B16A18 /* AttachmentModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentModel.swift; sourceTree = ""; }; + 1E6BA5C52BD13DEA00B16A18 /* UserModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserModel.swift; sourceTree = ""; }; + 1E8979462B6063FC001D99F0 /* WatchSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchSession.swift; sourceTree = ""; }; + 1E9A71682B59B6E100477BA2 /* MessageSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSender.swift; sourceTree = ""; }; + 1E9A716E2B59CBCA00477BA2 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = ""; }; + 1E9A71702B59CC1300477BA2 /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = ""; }; + 1E9A71722B59F34E00477BA2 /* ClientSSL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientSSL.swift; sourceTree = ""; }; + 1E9A71762B59FCA900477BA2 /* URLSessionCertificateHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionCertificateHandling.swift; sourceTree = ""; }; + 1EB375882B55DBFB00AEC3D7 /* Server.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Server.swift; sourceTree = ""; }; 1EB8EF712510F1EE00F352B7 /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = ""; }; + 1EC687B82BA0FECC00C7BAAD /* MessageInfoMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageInfoMapper.swift; sourceTree = ""; }; 1EC6ACB022CB9FC300A41C61 /* ShareRocketChatRN.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareRocketChatRN.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 1EC6ACB622CB9FC300A41C61 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; 1EC6ACB822CB9FC300A41C61 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1EC6ACF522CBA01500A41C61 /* ShareRocketChatRN.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShareRocketChatRN.m; sourceTree = ""; }; 1EC6AD6022CBA20C00A41C61 /* ShareRocketChatRN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareRocketChatRN.entitlements; sourceTree = ""; }; 1ED00BB02513E04400A1331F /* ReplyNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyNotification.swift; sourceTree = ""; }; + 1ED033AD2B55B1CC004F4930 /* Default.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Default.xcdatamodel; sourceTree = ""; }; + 1ED033AF2B55B25A004F4930 /* Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Database.swift; sourceTree = ""; }; + 1ED033B52B55B4A5004F4930 /* ServerListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerListView.swift; sourceTree = ""; }; + 1ED033B92B55B5F6004F4930 /* ServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerView.swift; sourceTree = ""; }; + 1ED033BE2B55BF94004F4930 /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = ""; }; + 1ED033C32B55C65C004F4930 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = ""; }; + 1ED033CA2B55D4F0004F4930 /* RocketChat.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = RocketChat.xcdatamodel; sourceTree = ""; }; + 1ED033CC2B55D671004F4930 /* RocketChatDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RocketChatDatabase.swift; sourceTree = ""; }; + 1ED0388E2B507B4B00C007D4 /* Rocket.Chat Watch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Rocket.Chat Watch.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1ED038902B507B4C00C007D4 /* RocketChatApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RocketChatApp.swift; sourceTree = ""; }; + 1ED038942B507B4D00C007D4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 1ED038972B507B4D00C007D4 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 1ED038A02B508FE700C007D4 /* FileManager+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+Extensions.swift"; sourceTree = ""; }; + 1ED038A42B50900800C007D4 /* Bundle+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Extensions.swift"; sourceTree = ""; }; + 1ED038A82B5090AD00C007D4 /* MMKV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MMKV.swift; sourceTree = ""; }; + 1ED038AC2B50927B00C007D4 /* WatermelonDB+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WatermelonDB+Extensions.swift"; sourceTree = ""; }; + 1ED038B92B50A1B800C007D4 /* WatchConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchConnection.swift; sourceTree = ""; }; + 1ED038BD2B50A1D400C007D4 /* DBServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBServer.swift; sourceTree = ""; }; + 1ED038C02B50A1E400C007D4 /* DBUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBUser.swift; sourceTree = ""; }; + 1ED038C32B50A1F500C007D4 /* WatchMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchMessage.swift; sourceTree = ""; }; + 1ED038C92B50A58400C007D4 /* ServersLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServersLoader.swift; sourceTree = ""; }; + 1ED1EC882B867E2400F6620C /* ExtensionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = ""; }; + 1ED1ECDD2B86997F00F6620C /* Rocket.Chat Experimental Watch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Rocket.Chat Experimental Watch.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 1ED59D4B22CBA77D00C54289 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; }; + 1EDB30F12B5B453A00532C7E /* LoggedInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedInView.swift; sourceTree = ""; }; + 1EDFD0F92B589B8F002FEE5F /* MessagesLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesLoader.swift; sourceTree = ""; }; + 1EDFD1052B58A66E002FEE5F /* CancelBag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelBag.swift; sourceTree = ""; }; + 1EDFD1072B58AA77002FEE5F /* RoomsLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomsLoader.swift; sourceTree = ""; }; + 1EE096F92BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ToolbarItemPlacement+Extensions.swift"; sourceTree = ""; }; + 1EE096FC2BACD58300780078 /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = ""; }; + 1EE096FF2BACD64C00780078 /* Binding+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Extensions.swift"; sourceTree = ""; }; + 1EE097022BACD66900780078 /* NavigationStackModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationStackModifier.swift; sourceTree = ""; }; + 1EED2D322BC1E09500832666 /* RoomInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomInfoView.swift; sourceTree = ""; }; 1EF5FBD0250C109E00614FEA /* Encryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encryption.swift; sourceTree = ""; }; 1EFEB5952493B6640072EDC0 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 1EFEB5972493B6640072EDC0 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; @@ -326,6 +665,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1ED0388B2B507B4B00C007D4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1ED1ECD52B86997F00F6620C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 1EFEB5922493B6640072EDC0 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -379,6 +732,14 @@ name = RocketChatRN; sourceTree = ""; }; + 1E0656192B7E91F00081B01F /* ActionHandler */ = { + isa = PBXGroup; + children = ( + 1E06561A2B7E91FB0081B01F /* ErrorActionHandler.swift */, + ); + path = ActionHandler; + sourceTree = ""; + }; 1E068CFB24FD2DAF00A0FFC1 /* AppGroup */ = { isa = PBXGroup; children = ( @@ -400,6 +761,102 @@ path = API; sourceTree = ""; }; + 1E29A2D12B585B070093C03C /* Client */ = { + isa = PBXGroup; + children = ( + 1E29A3082B585B2F0093C03C /* Extensions */, + 1E29A2D22B585B070093C03C /* RocketChatClient.swift */, + 1E29A2D32B585B070093C03C /* Responses */, + 1E29A2DD2B585B070093C03C /* Requests */, + 1E29A2E42B585B070093C03C /* Adapters */, + 1E29A2E82B585B070093C03C /* DateCodingStrategy.swift */, + 1E29A2E92B585B070093C03C /* FailableDecodable.swift */, + 1E29A2EA2B585B070093C03C /* HTTP */, + 1E29A2EE2B585B070093C03C /* RocketChatError.swift */, + 1E9A71762B59FCA900477BA2 /* URLSessionCertificateHandling.swift */, + ); + path = Client; + sourceTree = ""; + }; + 1E29A2D32B585B070093C03C /* Responses */ = { + isa = PBXGroup; + children = ( + 1E29A2D42B585B070093C03C /* AttachmentResponse.swift */, + 1E29A2D52B585B070093C03C /* SendMessageResponse.swift */, + 1E29A2D62B585B070093C03C /* HistoryResponse.swift */, + 1E29A2D72B585B070093C03C /* MessagesResponse.swift */, + 1E29A2D82B585B070093C03C /* SubscriptionsResponse.swift */, + 1E29A2D92B585B070093C03C /* RoomsResponse.swift */, + 1E29A2DA2B585B070093C03C /* UserResponse.swift */, + 1E29A2DB2B585B070093C03C /* ReadResponse.swift */, + 1E29A2DC2B585B070093C03C /* MessageResponse.swift */, + ); + path = Responses; + sourceTree = ""; + }; + 1E29A2DD2B585B070093C03C /* Requests */ = { + isa = PBXGroup; + children = ( + 1E29A2DE2B585B070093C03C /* SubscriptionsRequest.swift */, + 1E29A2DF2B585B070093C03C /* HistoryRequest.swift */, + 1E29A2E02B585B070093C03C /* MessagesRequest.swift */, + 1E29A2E12B585B070093C03C /* SendMessageRequest.swift */, + 1E29A2E22B585B070093C03C /* RoomsRequest.swift */, + 1E29A2E32B585B070093C03C /* ReadRequest.swift */, + ); + path = Requests; + sourceTree = ""; + }; + 1E29A2E42B585B070093C03C /* Adapters */ = { + isa = PBXGroup; + children = ( + 1E29A2E52B585B070093C03C /* TokenAdapter.swift */, + 1E29A2E62B585B070093C03C /* JSONAdapter.swift */, + 1E29A2E72B585B070093C03C /* RequestAdapter.swift */, + ); + path = Adapters; + sourceTree = ""; + }; + 1E29A2EA2B585B070093C03C /* HTTP */ = { + isa = PBXGroup; + children = ( + 1E29A2EB2B585B070093C03C /* HTTPMethod.swift */, + 1E29A2EC2B585B070093C03C /* Request.swift */, + ); + path = HTTP; + sourceTree = ""; + }; + 1E29A3082B585B2F0093C03C /* Extensions */ = { + isa = PBXGroup; + children = ( + 1E29A3092B585B370093C03C /* Data+Extensions.swift */, + 1E29A30B2B585D1D0093C03C /* String+Extensions.swift */, + 1E638E9D2B5F0F5800E645E4 /* URL+Extensions.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 1E29A31B2B5871AC0093C03C /* Extensions */ = { + isa = PBXGroup; + children = ( + 1E29A31C2B5871B60093C03C /* Date+Extensions.swift */, + 1E675B712BAC49B000438590 /* Color+Extensions.swift */, + 1EE096F92BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift */, + 1EE096FF2BACD64C00780078 /* Binding+Extensions.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 1E29A31E2B5871BE0093C03C /* Formatters */ = { + isa = PBXGroup; + children = ( + 1EC687B72BA0FEBB00C7BAAD /* Mapper */, + 1E29A31F2B5871C80093C03C /* RoomFormatter.swift */, + 1E29A3212B5871CE0093C03C /* MessageFormatter.swift */, + ); + path = Formatters; + sourceTree = ""; + }; 1E2F61622512954500871711 /* Requests */ = { isa = PBXGroup; children = ( @@ -409,6 +866,36 @@ path = Requests; sourceTree = ""; }; + 1E51411A2B856820007BE94A /* SSLPinning */ = { + isa = PBXGroup; + children = ( + 1E5141172B856673007BE94A /* SSLPinning.swift */, + 1E51411B2B85683C007BE94A /* SSLPinning.m */, + ); + path = SSLPinning; + sourceTree = ""; + }; + 1E54BA132BC9E6670073903D /* Merge */ = { + isa = PBXGroup; + children = ( + 1E54BA182BC9E6D60073903D /* MergedRoom.swift */, + 1E54BA1C2BC9E8030073903D /* MessageMapper.swift */, + 1E54BA1F2BC9E9B60073903D /* MergedRoomMapper.swift */, + ); + path = Merge; + sourceTree = ""; + }; + 1E6BA5CE2BD14ABA00B16A18 /* Model */ = { + isa = PBXGroup; + children = ( + 1E6BA5C42BD13DEA00B16A18 /* AttachmentModel.swift */, + 1E6BA5C32BD13DEA00B16A18 /* MessageModel.swift */, + 1E6BA5C22BD13DEA00B16A18 /* RoomModel.swift */, + 1E6BA5C52BD13DEA00B16A18 /* UserModel.swift */, + ); + path = Model; + sourceTree = ""; + }; 1E76CBC425152A7F0067298C /* Shared */ = { isa = PBXGroup; children = ( @@ -427,6 +914,8 @@ 1E1C2F7F250FCB69005DCE7D /* Database.swift */, 1E470E822513A71E00E3DD1D /* RocketChat.swift */, 1EB8EF712510F1EE00F352B7 /* Storage.swift */, + 1ED038A82B5090AD00C007D4 /* MMKV.swift */, + 1E9A71722B59F34E00477BA2 /* ClientSSL.swift */, ); path = RocketChat; sourceTree = ""; @@ -438,6 +927,9 @@ 1E598AE32515057D002BDFBD /* Date+Extensions.swift */, 1E598AE625150660002BDFBD /* Data+Extensions.swift */, 1E67380324DC529B0009E081 /* String+Extensions.swift */, + 1ED038A02B508FE700C007D4 /* FileManager+Extensions.swift */, + 1ED038A42B50900800C007D4 /* Bundle+Extensions.swift */, + 1ED038AC2B50927B00C007D4 /* WatermelonDB+Extensions.swift */, ); path = Extensions; sourceTree = ""; @@ -458,6 +950,25 @@ path = Models; sourceTree = ""; }; + 1EB375872B55DBF400AEC3D7 /* Entity */ = { + isa = PBXGroup; + children = ( + 1EB375882B55DBFB00AEC3D7 /* Server.swift */, + 1E29A30D2B58608C0093C03C /* LoggedUser.swift */, + 1E29A3112B5866090093C03C /* Room.swift */, + 1E9A71702B59CC1300477BA2 /* Attachment.swift */, + ); + path = Entity; + sourceTree = ""; + }; + 1EC687B72BA0FEBB00C7BAAD /* Mapper */ = { + isa = PBXGroup; + children = ( + 1EC687B82BA0FECC00C7BAAD /* MessageInfoMapper.swift */, + ); + path = Mapper; + sourceTree = ""; + }; 1EC6ACB122CB9FC300A41C61 /* ShareRocketChatRN */ = { isa = PBXGroup; children = ( @@ -470,6 +981,126 @@ path = ShareRocketChatRN; sourceTree = ""; }; + 1ED033AB2B55B1C2004F4930 /* Database */ = { + isa = PBXGroup; + children = ( + 1E6BA5CE2BD14ABA00B16A18 /* Model */, + 1EB375872B55DBF400AEC3D7 /* Entity */, + 1ED033AC2B55B1CC004F4930 /* Default.xcdatamodeld */, + 1ED033AF2B55B25A004F4930 /* Database.swift */, + 1ED033C92B55D4F0004F4930 /* RocketChat.xcdatamodeld */, + 1ED033CC2B55D671004F4930 /* RocketChatDatabase.swift */, + ); + path = Database; + sourceTree = ""; + }; + 1ED033B12B55B47F004F4930 /* Views */ = { + isa = PBXGroup; + children = ( + 1ED033B52B55B4A5004F4930 /* ServerListView.swift */, + 1ED033B92B55B5F6004F4930 /* ServerView.swift */, + 1E29A2CB2B5857F50093C03C /* RoomListView.swift */, + 1E29A2CF2B58582F0093C03C /* RoomView.swift */, + 1E29A3152B5868DF0093C03C /* MessageListView.swift */, + 1E29A3172B5868E50093C03C /* MessageView.swift */, + 1E29A3232B5874FF0093C03C /* MessageComposerView.swift */, + 1E9A716E2B59CBCA00477BA2 /* AttachmentView.swift */, + 1E4AFC262B5B23C600E2AA7D /* RetryView.swift */, + 1EDB30F12B5B453A00532C7E /* LoggedInView.swift */, + 1E638E982B5F0A2900E645E4 /* ChatScrollView.swift */, + 1E06561C2B7E9C1C0081B01F /* MessageActionView.swift */, + 1E388AC02B934CD4006FBDB0 /* RemoteImage.swift */, + 1EE096FC2BACD58300780078 /* LazyView.swift */, + 1EE097022BACD66900780078 /* NavigationStackModifier.swift */, + 1EED2D322BC1E09500832666 /* RoomInfoView.swift */, + ); + path = Views; + sourceTree = ""; + }; + 1ED033B42B55B495004F4930 /* ViewModels */ = { + isa = PBXGroup; + children = ( + 1E29A30F2B5865B80093C03C /* RoomViewModel.swift */, + 1E29A3192B5868EE0093C03C /* MessageViewModel.swift */, + ); + path = ViewModels; + sourceTree = ""; + }; + 1ED0388F2B507B4C00C007D4 /* RocketChat Watch App */ = { + isa = PBXGroup; + children = ( + 1E0656192B7E91F00081B01F /* ActionHandler */, + 1EDFD0FB2B589FC4002FEE5F /* DependencyInjection */, + 1EDFD0F82B589B82002FEE5F /* Loaders */, + 1E29A31E2B5871BE0093C03C /* Formatters */, + 1ED033B42B55B495004F4930 /* ViewModels */, + 1E29A31B2B5871AC0093C03C /* Extensions */, + 1E29A2D12B585B070093C03C /* Client */, + 1ED033B12B55B47F004F4930 /* Views */, + 1ED033AB2B55B1C2004F4930 /* Database */, + 1ED038902B507B4C00C007D4 /* RocketChatApp.swift */, + 1ED033C32B55C65C004F4930 /* AppRouter.swift */, + 1E4AFC202B5B1AA000E2AA7D /* AppView.swift */, + 1ED033BE2B55BF94004F4930 /* Storage.swift */, + 1ED038942B507B4D00C007D4 /* Assets.xcassets */, + 1ED038962B507B4D00C007D4 /* Preview Content */, + 1ED1EC882B867E2400F6620C /* ExtensionDelegate.swift */, + 1E044F982B92798E00BCA2FD /* Localizable.xcstrings */, + ); + path = "RocketChat Watch App"; + sourceTree = ""; + }; + 1ED038962B507B4D00C007D4 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 1ED038972B507B4D00C007D4 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 1ED038B82B50A1A500C007D4 /* Watch */ = { + isa = PBXGroup; + children = ( + 1ED038BC2B50A1C700C007D4 /* Database */, + 1ED038B92B50A1B800C007D4 /* WatchConnection.swift */, + 1ED038C32B50A1F500C007D4 /* WatchMessage.swift */, + ); + path = Watch; + sourceTree = ""; + }; + 1ED038BC2B50A1C700C007D4 /* Database */ = { + isa = PBXGroup; + children = ( + 1ED038BD2B50A1D400C007D4 /* DBServer.swift */, + 1ED038C02B50A1E400C007D4 /* DBUser.swift */, + ); + path = Database; + sourceTree = ""; + }; + 1EDFD0F82B589B82002FEE5F /* Loaders */ = { + isa = PBXGroup; + children = ( + 1E54BA132BC9E6670073903D /* Merge */, + 1ED038C92B50A58400C007D4 /* ServersLoader.swift */, + 1EDFD0F92B589B8F002FEE5F /* MessagesLoader.swift */, + 1EDFD1052B58A66E002FEE5F /* CancelBag.swift */, + 1EDFD1072B58AA77002FEE5F /* RoomsLoader.swift */, + 1E9A71682B59B6E100477BA2 /* MessageSender.swift */, + 1E8979462B6063FC001D99F0 /* WatchSession.swift */, + 1E388ABD2B934C64006FBDB0 /* ImageLoader.swift */, + ); + path = Loaders; + sourceTree = ""; + }; + 1EDFD0FB2B589FC4002FEE5F /* DependencyInjection */ = { + isa = PBXGroup; + children = ( + 1E4AFC142B5AF09800E2AA7D /* Dependency.swift */, + 1E4AFC162B5AF09C00E2AA7D /* Store.swift */, + ); + path = DependencyInjection; + sourceTree = ""; + }; 1EFEB5962493B6640072EDC0 /* NotificationService */ = { isa = PBXGroup; children = ( @@ -525,12 +1156,15 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + 1E51411A2B856820007BE94A /* SSLPinning */, + 1ED038B82B50A1A500C007D4 /* Watch */, 1E76CBC425152A7F0067298C /* Shared */, 1E068CFB24FD2DAF00A0FFC1 /* AppGroup */, 13B07FAE1A68108700A75B9A /* RocketChatRN */, 832341AE1AAA6A7D00B99B32 /* Libraries */, 1EC6ACB122CB9FC300A41C61 /* ShareRocketChatRN */, 1EFEB5962493B6640072EDC0 /* NotificationService */, + 1ED0388F2B507B4C00C007D4 /* RocketChat Watch App */, 83CBBA001A601CBA00E9B192 /* Products */, BB4B591B5FC44CD9986DB2A6 /* Frameworks */, AF5E16F0398347E6A80C8CBE /* Resources */, @@ -538,9 +1172,9 @@ 7AC2B09613AA7C3FEBAC9F57 /* Pods */, 7890E71355E6C0A3288089E7 /* ExpoModulesProviders */, ); - indentWidth = 2; + indentWidth = 4; sourceTree = ""; - tabWidth = 2; + tabWidth = 4; }; 83CBBA001A601CBA00E9B192 /* Products */ = { isa = PBXGroup; @@ -549,6 +1183,8 @@ 1EC6ACB022CB9FC300A41C61 /* ShareRocketChatRN.appex */, 1EFEB5952493B6640072EDC0 /* NotificationService.appex */, 7AAB3E52257E6A6E00707CF6 /* Rocket.Chat.app */, + 1ED0388E2B507B4B00C007D4 /* Rocket.Chat Watch.app */, + 1ED1ECDD2B86997F00F6620C /* Rocket.Chat Experimental Watch.app */, ); name = Products; sourceTree = ""; @@ -632,15 +1268,17 @@ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 1EC6ACF422CB9FC300A41C61 /* Embed App Extensions */, 1E1EA8082326CCE300E22452 /* ShellScript */, - 7AAE9EB32891A0D20024F559 /* Upload source maps to Bugsnag */, 7F13D807CA5B7E43CE899DB3 /* [CP] Embed Pods Frameworks */, A1315A8FDA7B970DFBDB34C7 /* [CP] Copy Pods Resources */, + 1ED0389C2B507B4F00C007D4 /* Embed Watch Content */, + 7AAE9EB32891A0D20024F559 /* Upload source maps to Bugsnag */, ); buildRules = ( ); dependencies = ( 1EC6ACBA22CB9FC300A41C61 /* PBXTargetDependency */, 1EFEB59B2493B6640072EDC0 /* PBXTargetDependency */, + 1EE79C552B8912F600CF1863 /* PBXTargetDependency */, ); name = RocketChatRN; productName = "Hello World"; @@ -667,6 +1305,40 @@ productReference = 1EC6ACB022CB9FC300A41C61 /* ShareRocketChatRN.appex */; productType = "com.apple.product-type.app-extension"; }; + 1ED0388D2B507B4B00C007D4 /* Rocket.Chat.Watch */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1ED0389F2B507B4F00C007D4 /* Build configuration list for PBXNativeTarget "Rocket.Chat.Watch" */; + buildPhases = ( + 1ED0388A2B507B4B00C007D4 /* Sources */, + 1ED0388B2B507B4B00C007D4 /* Frameworks */, + 1ED0388C2B507B4B00C007D4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Rocket.Chat.Watch; + productName = "RocketChat Watch App"; + productReference = 1ED0388E2B507B4B00C007D4 /* Rocket.Chat Watch.app */; + productType = "com.apple.product-type.application"; + }; + 1ED1EC8E2B86997F00F6620C /* RocketChatRN Watch */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1ED1ECDA2B86997F00F6620C /* Build configuration list for PBXNativeTarget "RocketChatRN Watch" */; + buildPhases = ( + 1ED1EC8F2B86997F00F6620C /* Sources */, + 1ED1ECD52B86997F00F6620C /* Frameworks */, + 1ED1ECD62B86997F00F6620C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "RocketChatRN Watch"; + productName = "RocketChat Watch App"; + productReference = 1ED1ECDD2B86997F00F6620C /* Rocket.Chat Experimental Watch.app */; + productType = "com.apple.product-type.application"; + }; 1EFEB5942493B6640072EDC0 /* NotificationService */ = { isa = PBXNativeTarget; buildConfigurationList = 1EFEB5A02493B6640072EDC0 /* Build configuration list for PBXNativeTarget "NotificationService" */; @@ -698,15 +1370,17 @@ 7AAB3E46257E6A6E00707CF6 /* Bundle React Native code and images */, 7AAB3E48257E6A6E00707CF6 /* Embed App Extensions */, 7AAB3E4B257E6A6E00707CF6 /* ShellScript */, - 7A10288726B1D15200E47EF8 /* Upload source maps to Bugsnag */, 4CC99291A49CC4B7F5883239 /* [CP] Embed Pods Frameworks */, B237EE54305A08A30FCABFA0 /* [CP] Copy Pods Resources */, + 1ED1ECE32B8699DD00F6620C /* Embed Watch Content */, + 7A10288726B1D15200E47EF8 /* Upload source maps to Bugsnag */, ); buildRules = ( ); dependencies = ( 7AAB3E0E257E6A6E00707CF6 /* PBXTargetDependency */, 7AAB3E10257E6A6E00707CF6 /* PBXTargetDependency */, + 1ED1ECE62B8699E900F6620C /* PBXTargetDependency */, ); name = Rocket.Chat; productName = "Hello World"; @@ -720,7 +1394,7 @@ isa = PBXProject; attributes = { DefaultBuildSystemTypeForWorkspace = Original; - LastSwiftUpdateCheck = 1150; + LastSwiftUpdateCheck = 1500; LastUpgradeCheck = 1130; ORGANIZATIONNAME = Facebook; TargetAttributes = { @@ -753,6 +1427,15 @@ }; }; }; + 1ED0388D2B507B4B00C007D4 = { + CreatedOnToolsVersion = 15.0; + DevelopmentTeam = S6UPZG7ZR3; + ProvisioningStyle = Manual; + }; + 1ED1EC8E2B86997F00F6620C = { + DevelopmentTeam = S6UPZG7ZR3; + ProvisioningStyle = Manual; + }; 1EFEB5942493B6640072EDC0 = { CreatedOnToolsVersion = 11.5; DevelopmentTeam = S6UPZG7ZR3; @@ -772,6 +1455,8 @@ English, en, Base, + "pt-BR", + ja, ); mainGroup = 83CBB9F61A601CBA00E9B192; productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; @@ -782,6 +1467,8 @@ 1EC6ACAF22CB9FC300A41C61 /* ShareRocketChatRN */, 1EFEB5942493B6640072EDC0 /* NotificationService */, 7AAB3E0D257E6A6E00707CF6 /* Rocket.Chat */, + 1ED0388D2B507B4B00C007D4 /* Rocket.Chat.Watch */, + 1ED1EC8E2B86997F00F6620C /* RocketChatRN Watch */, ); }; /* End PBXProject section */ @@ -812,6 +1499,28 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1ED0388C2B507B4B00C007D4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1ED1ECEA2B869A4A00F6620C /* Official.xcassets in Resources */, + 1ED038982B507B4D00C007D4 /* Preview Assets.xcassets in Resources */, + 1E044F992B92798E00BCA2FD /* Localizable.xcstrings in Resources */, + 1ED038952B507B4D00C007D4 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1ED1ECD62B86997F00F6620C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1ED1ECD72B86997F00F6620C /* Preview Assets.xcassets in Resources */, + 1ED1ECD82B86997F00F6620C /* Assets.xcassets in Resources */, + 1E044F9A2B92798E00BCA2FD /* Localizable.xcstrings in Resources */, + 1ED1ECEC2B869B1300F6620C /* Experimental.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 1EFEB5932493B6640072EDC0 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1036,6 +1745,7 @@ inputFileListPaths = ( ); inputPaths = ( + "$TARGET_BUILD_DIR/$INFOPLIST_PATH", ); name = "Upload source maps to Bugsnag"; outputFileListPaths = ( @@ -1121,6 +1831,7 @@ inputFileListPaths = ( ); inputPaths = ( + "$TARGET_BUILD_DIR/$INFOPLIST_PATH", ); name = "Upload source maps to Bugsnag"; outputFileListPaths = ( @@ -1348,8 +2059,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1ED038A92B5090AD00C007D4 /* MMKV.swift in Sources */, 1E76CBCB25152C250067298C /* Sender.swift in Sources */, + 1E5141182B856673007BE94A /* SSLPinning.swift in Sources */, 1E76CBD825152C870067298C /* Request.swift in Sources */, + 1E51411C2B85683C007BE94A /* SSLPinning.m in Sources */, 1ED00BB12513E04400A1331F /* ReplyNotification.swift in Sources */, 1E76CBC2251529560067298C /* Storage.swift in Sources */, 1E76CBD925152C8C0067298C /* Push.swift in Sources */, @@ -1364,11 +2078,19 @@ 1E76CBD225152C730067298C /* Data+Extensions.swift in Sources */, 1E76CBD125152C710067298C /* Date+Extensions.swift in Sources */, 1E76CBD425152C790067298C /* Database.swift in Sources */, + 1E9A71742B59F36E00477BA2 /* ClientSSL.swift in Sources */, + 1ED038AD2B50927B00C007D4 /* WatermelonDB+Extensions.swift in Sources */, + 1ED038A52B50900800C007D4 /* Bundle+Extensions.swift in Sources */, 1E76CBC325152A460067298C /* String+Extensions.swift in Sources */, + 1ED038BA2B50A1B800C007D4 /* WatchConnection.swift in Sources */, + 1ED038A12B508FE700C007D4 /* FileManager+Extensions.swift in Sources */, 1E76CBCA25152C220067298C /* Notification.swift in Sources */, + 1ED038C12B50A1E400C007D4 /* DBUser.swift in Sources */, 1E76CBD525152C7F0067298C /* API.swift in Sources */, 1E76CBD625152C820067298C /* Response.swift in Sources */, + 1ED038BE2B50A1D400C007D4 /* DBServer.swift in Sources */, 1E068D0124FD2E0500A0FFC1 /* AppGroup.m in Sources */, + 1ED038C42B50A1F500C007D4 /* WatchMessage.swift in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, 1E76CBD025152C6E0067298C /* URL+Extensions.swift in Sources */, 1E068CFE24FD2DC700A0FFC1 /* AppGroup.swift in Sources */, @@ -1391,6 +2113,188 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1ED0388A2B507B4B00C007D4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1E54BA1D2BC9E8030073903D /* MessageMapper.swift in Sources */, + 1EE096FD2BACD58300780078 /* LazyView.swift in Sources */, + 1E29A3242B5874FF0093C03C /* MessageComposerView.swift in Sources */, + 1EB375892B55DBFB00AEC3D7 /* Server.swift in Sources */, + 1E06561D2B7E9C1C0081B01F /* MessageActionView.swift in Sources */, + 1E6BA5CC2BD13DEA00B16A18 /* UserModel.swift in Sources */, + 1EE096FA2BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift in Sources */, + 1E29A3162B5868DF0093C03C /* MessageListView.swift in Sources */, + 1E4AFC172B5AF09C00E2AA7D /* Store.swift in Sources */, + 1E29A2F42B585B070093C03C /* SubscriptionsResponse.swift in Sources */, + 1EE097002BACD64C00780078 /* Binding+Extensions.swift in Sources */, + 1ED1EC892B867E2400F6620C /* ExtensionDelegate.swift in Sources */, + 1E29A2F92B585B070093C03C /* SubscriptionsRequest.swift in Sources */, + 1E29A2F22B585B070093C03C /* HistoryResponse.swift in Sources */, + 1E4AFC152B5AF09800E2AA7D /* Dependency.swift in Sources */, + 1ED033BA2B55B5F6004F4930 /* ServerView.swift in Sources */, + 1E29A2D02B58582F0093C03C /* RoomView.swift in Sources */, + 1E29A2FD2B585B070093C03C /* RoomsRequest.swift in Sources */, + 1ED038CA2B50A58400C007D4 /* ServersLoader.swift in Sources */, + 1E29A3222B5871CE0093C03C /* MessageFormatter.swift in Sources */, + 1E9A716F2B59CBCA00477BA2 /* AttachmentView.swift in Sources */, + 1E29A3002B585B070093C03C /* JSONAdapter.swift in Sources */, + 1E29A3022B585B070093C03C /* DateCodingStrategy.swift in Sources */, + 1E4AFC272B5B23C600E2AA7D /* RetryView.swift in Sources */, + 1ED033B62B55B4A5004F4930 /* ServerListView.swift in Sources */, + 1E29A3202B5871C80093C03C /* RoomFormatter.swift in Sources */, + 1EDFD0FA2B589B8F002FEE5F /* MessagesLoader.swift in Sources */, + 1E29A3102B5865B80093C03C /* RoomViewModel.swift in Sources */, + 1E06561B2B7E91FB0081B01F /* ErrorActionHandler.swift in Sources */, + 1E29A2FC2B585B070093C03C /* SendMessageRequest.swift in Sources */, + 1E29A30C2B585D1D0093C03C /* String+Extensions.swift in Sources */, + 1EE097032BACD66900780078 /* NavigationStackModifier.swift in Sources */, + 1ED033CD2B55D671004F4930 /* RocketChatDatabase.swift in Sources */, + 1E29A3122B5866090093C03C /* Room.swift in Sources */, + 1E29A3032B585B070093C03C /* FailableDecodable.swift in Sources */, + 1E8979472B6063FC001D99F0 /* WatchSession.swift in Sources */, + 1E29A2FE2B585B070093C03C /* ReadRequest.swift in Sources */, + 1E9A71692B59B6E100477BA2 /* MessageSender.swift in Sources */, + 1E29A3072B585B070093C03C /* RocketChatError.swift in Sources */, + 1EED2D332BC1E09500832666 /* RoomInfoView.swift in Sources */, + 1EDFD1082B58AA77002FEE5F /* RoomsLoader.swift in Sources */, + 1E29A2F12B585B070093C03C /* SendMessageResponse.swift in Sources */, + 1E29A30E2B58608C0093C03C /* LoggedUser.swift in Sources */, + 1E29A3182B5868E50093C03C /* MessageView.swift in Sources */, + 1EDFD1062B58A66E002FEE5F /* CancelBag.swift in Sources */, + 1E29A2FF2B585B070093C03C /* TokenAdapter.swift in Sources */, + 1E29A3052B585B070093C03C /* Request.swift in Sources */, + 1E675B722BAC49B000438590 /* Color+Extensions.swift in Sources */, + 1E9A71772B59FCA900477BA2 /* URLSessionCertificateHandling.swift in Sources */, + 1E388AC12B934CD4006FBDB0 /* RemoteImage.swift in Sources */, + 1E388ABE2B934C64006FBDB0 /* ImageLoader.swift in Sources */, + 1E29A2EF2B585B070093C03C /* RocketChatClient.swift in Sources */, + 1E54BA202BC9E9B60073903D /* MergedRoomMapper.swift in Sources */, + 1E4AFC212B5B1AA000E2AA7D /* AppView.swift in Sources */, + 1E29A2FB2B585B070093C03C /* MessagesRequest.swift in Sources */, + 1E29A31D2B5871B60093C03C /* Date+Extensions.swift in Sources */, + 1EC687BB2BA0FF0D00C7BAAD /* MessageInfoMapper.swift in Sources */, + 1E29A2F62B585B070093C03C /* UserResponse.swift in Sources */, + 1ED033AE2B55B1CC004F4930 /* Default.xcdatamodeld in Sources */, + 1E6BA5CA2BD13DEA00B16A18 /* AttachmentModel.swift in Sources */, + 1ED033BF2B55BF94004F4930 /* Storage.swift in Sources */, + 1E29A2F82B585B070093C03C /* MessageResponse.swift in Sources */, + 1E29A3042B585B070093C03C /* HTTPMethod.swift in Sources */, + 1E29A3012B585B070093C03C /* RequestAdapter.swift in Sources */, + 1E6BA5C82BD13DEA00B16A18 /* MessageModel.swift in Sources */, + 1E29A2F52B585B070093C03C /* RoomsResponse.swift in Sources */, + 1EDB30F22B5B453A00532C7E /* LoggedInView.swift in Sources */, + 1E54BA1B2BC9E7110073903D /* MergedRoom.swift in Sources */, + 1E29A2F32B585B070093C03C /* MessagesResponse.swift in Sources */, + 1E29A2FA2B585B070093C03C /* HistoryRequest.swift in Sources */, + 1ED038C62B50A21800C007D4 /* WatchMessage.swift in Sources */, + 1E29A2F02B585B070093C03C /* AttachmentResponse.swift in Sources */, + 1E638E9E2B5F0F5800E645E4 /* URL+Extensions.swift in Sources */, + 1E6BA5C62BD13DEA00B16A18 /* RoomModel.swift in Sources */, + 1ED038912B507B4C00C007D4 /* RocketChatApp.swift in Sources */, + 1E29A2CC2B5857F50093C03C /* RoomListView.swift in Sources */, + 1E29A31A2B5868EE0093C03C /* MessageViewModel.swift in Sources */, + 1E638E992B5F0A2900E645E4 /* ChatScrollView.swift in Sources */, + 1ED033C42B55C65C004F4930 /* AppRouter.swift in Sources */, + 1ED033B02B55B25A004F4930 /* Database.swift in Sources */, + 1E9A71712B59CC1300477BA2 /* Attachment.swift in Sources */, + 1E29A30A2B585B370093C03C /* Data+Extensions.swift in Sources */, + 1E29A2F72B585B070093C03C /* ReadResponse.swift in Sources */, + 1ED033CB2B55D4F0004F4930 /* RocketChat.xcdatamodeld in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1ED1EC8F2B86997F00F6620C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1E54BA1E2BC9E8030073903D /* MessageMapper.swift in Sources */, + 1EE096FE2BACD58300780078 /* LazyView.swift in Sources */, + 1ED1EC902B86997F00F6620C /* MessageComposerView.swift in Sources */, + 1ED1EC912B86997F00F6620C /* Server.swift in Sources */, + 1ED1EC922B86997F00F6620C /* MessageActionView.swift in Sources */, + 1E6BA5CD2BD13DEA00B16A18 /* UserModel.swift in Sources */, + 1EE096FB2BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift in Sources */, + 1ED1EC932B86997F00F6620C /* MessageListView.swift in Sources */, + 1ED1EC942B86997F00F6620C /* Store.swift in Sources */, + 1ED1EC962B86997F00F6620C /* SubscriptionsResponse.swift in Sources */, + 1EE097012BACD64C00780078 /* Binding+Extensions.swift in Sources */, + 1ED1EC972B86997F00F6620C /* ExtensionDelegate.swift in Sources */, + 1ED1EC982B86997F00F6620C /* SubscriptionsRequest.swift in Sources */, + 1ED1EC992B86997F00F6620C /* HistoryResponse.swift in Sources */, + 1ED1EC9A2B86997F00F6620C /* Dependency.swift in Sources */, + 1ED1EC9B2B86997F00F6620C /* ServerView.swift in Sources */, + 1ED1EC9C2B86997F00F6620C /* RoomView.swift in Sources */, + 1ED1EC9D2B86997F00F6620C /* RoomsRequest.swift in Sources */, + 1ED1EC9E2B86997F00F6620C /* ServersLoader.swift in Sources */, + 1ED1EC9F2B86997F00F6620C /* MessageFormatter.swift in Sources */, + 1ED1ECA02B86997F00F6620C /* AttachmentView.swift in Sources */, + 1ED1ECA12B86997F00F6620C /* JSONAdapter.swift in Sources */, + 1ED1ECA22B86997F00F6620C /* DateCodingStrategy.swift in Sources */, + 1ED1ECA32B86997F00F6620C /* RetryView.swift in Sources */, + 1ED1ECA42B86997F00F6620C /* ServerListView.swift in Sources */, + 1ED1ECA52B86997F00F6620C /* RoomFormatter.swift in Sources */, + 1ED1ECA62B86997F00F6620C /* MessagesLoader.swift in Sources */, + 1ED1ECA72B86997F00F6620C /* RoomViewModel.swift in Sources */, + 1ED1ECA82B86997F00F6620C /* ErrorActionHandler.swift in Sources */, + 1ED1ECA92B86997F00F6620C /* SendMessageRequest.swift in Sources */, + 1ED1ECAA2B86997F00F6620C /* String+Extensions.swift in Sources */, + 1EE097042BACD66900780078 /* NavigationStackModifier.swift in Sources */, + 1ED1ECAB2B86997F00F6620C /* RocketChatDatabase.swift in Sources */, + 1ED1ECAC2B86997F00F6620C /* Room.swift in Sources */, + 1ED1ECAD2B86997F00F6620C /* FailableDecodable.swift in Sources */, + 1ED1ECAE2B86997F00F6620C /* WatchSession.swift in Sources */, + 1ED1ECAF2B86997F00F6620C /* ReadRequest.swift in Sources */, + 1ED1ECB02B86997F00F6620C /* MessageSender.swift in Sources */, + 1ED1ECB12B86997F00F6620C /* RocketChatError.swift in Sources */, + 1EED2D342BC1E09500832666 /* RoomInfoView.swift in Sources */, + 1ED1ECB22B86997F00F6620C /* RoomsLoader.swift in Sources */, + 1ED1ECB32B86997F00F6620C /* SendMessageResponse.swift in Sources */, + 1ED1ECB42B86997F00F6620C /* LoggedUser.swift in Sources */, + 1ED1ECB52B86997F00F6620C /* MessageView.swift in Sources */, + 1ED1ECB62B86997F00F6620C /* CancelBag.swift in Sources */, + 1ED1ECB72B86997F00F6620C /* TokenAdapter.swift in Sources */, + 1ED1ECB82B86997F00F6620C /* Request.swift in Sources */, + 1E675B732BAC49B000438590 /* Color+Extensions.swift in Sources */, + 1ED1ECB92B86997F00F6620C /* URLSessionCertificateHandling.swift in Sources */, + 1E388AC22B934CD4006FBDB0 /* RemoteImage.swift in Sources */, + 1E388ABF2B934C64006FBDB0 /* ImageLoader.swift in Sources */, + 1ED1ECBA2B86997F00F6620C /* RocketChatClient.swift in Sources */, + 1E54BA212BC9E9B60073903D /* MergedRoomMapper.swift in Sources */, + 1ED1ECBB2B86997F00F6620C /* AppView.swift in Sources */, + 1ED1ECBC2B86997F00F6620C /* MessagesRequest.swift in Sources */, + 1ED1ECBD2B86997F00F6620C /* Date+Extensions.swift in Sources */, + 1EC687BA2BA0FF0D00C7BAAD /* MessageInfoMapper.swift in Sources */, + 1ED1ECBE2B86997F00F6620C /* UserResponse.swift in Sources */, + 1ED1ECBF2B86997F00F6620C /* Default.xcdatamodeld in Sources */, + 1E6BA5CB2BD13DEA00B16A18 /* AttachmentModel.swift in Sources */, + 1ED1ECC02B86997F00F6620C /* Storage.swift in Sources */, + 1ED1ECC12B86997F00F6620C /* MessageResponse.swift in Sources */, + 1ED1ECC22B86997F00F6620C /* HTTPMethod.swift in Sources */, + 1ED1ECC32B86997F00F6620C /* RequestAdapter.swift in Sources */, + 1E6BA5C92BD13DEA00B16A18 /* MessageModel.swift in Sources */, + 1ED1ECC42B86997F00F6620C /* RoomsResponse.swift in Sources */, + 1ED1ECC52B86997F00F6620C /* LoggedInView.swift in Sources */, + 1E54BA1A2BC9E7100073903D /* MergedRoom.swift in Sources */, + 1ED1ECC62B86997F00F6620C /* MessagesResponse.swift in Sources */, + 1ED1ECC72B86997F00F6620C /* HistoryRequest.swift in Sources */, + 1ED1ECC82B86997F00F6620C /* WatchMessage.swift in Sources */, + 1ED1ECC92B86997F00F6620C /* AttachmentResponse.swift in Sources */, + 1ED1ECCA2B86997F00F6620C /* URL+Extensions.swift in Sources */, + 1E6BA5C72BD13DEA00B16A18 /* RoomModel.swift in Sources */, + 1ED1ECCB2B86997F00F6620C /* RocketChatApp.swift in Sources */, + 1ED1ECCC2B86997F00F6620C /* RoomListView.swift in Sources */, + 1ED1ECCD2B86997F00F6620C /* MessageViewModel.swift in Sources */, + 1ED1ECCE2B86997F00F6620C /* ChatScrollView.swift in Sources */, + 1ED1ECCF2B86997F00F6620C /* AppRouter.swift in Sources */, + 1ED1ECD02B86997F00F6620C /* Database.swift in Sources */, + 1ED1ECD12B86997F00F6620C /* Attachment.swift in Sources */, + 1ED1ECD22B86997F00F6620C /* Data+Extensions.swift in Sources */, + 1ED1ECD32B86997F00F6620C /* ReadResponse.swift in Sources */, + 1ED1ECD42B86997F00F6620C /* RocketChat.xcdatamodeld in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 1EFEB5912493B6640072EDC0 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1398,6 +2302,8 @@ 1E51D965251263D600DC95DE /* NotificationType.swift in Sources */, 1EF5FBD1250C109E00614FEA /* Encryption.swift in Sources */, 1E598AE42515057D002BDFBD /* Date+Extensions.swift in Sources */, + 1ED038A22B508FE700C007D4 /* FileManager+Extensions.swift in Sources */, + 1ED038AA2B5090AD00C007D4 /* MMKV.swift in Sources */, 1E01C81C2511208400FEF824 /* URL+Extensions.swift in Sources */, 1E470E832513A71E00E3DD1D /* RocketChat.swift in Sources */, 1E2F615D25128FA300871711 /* Response.swift in Sources */, @@ -1406,6 +2312,8 @@ 1E0426E7251A54B4008F022C /* RoomType.swift in Sources */, 1E1C2F80250FCB69005DCE7D /* Database.swift in Sources */, 1E67380424DC529B0009E081 /* String+Extensions.swift in Sources */, + 1ED038AE2B50927B00C007D4 /* WatermelonDB+Extensions.swift in Sources */, + 1ED038A62B50900800C007D4 /* Bundle+Extensions.swift in Sources */, 1E01C8292511304100FEF824 /* Sender.swift in Sources */, 1E51D962251263CD00DC95DE /* MessageType.swift in Sources */, 1E01C82B2511335A00FEF824 /* Message.swift in Sources */, @@ -1427,8 +2335,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1ED038AB2B5090AD00C007D4 /* MMKV.swift in Sources */, 7AAB3E15257E6A6E00707CF6 /* Sender.swift in Sources */, + 1E5141192B856673007BE94A /* SSLPinning.swift in Sources */, 7AAB3E16257E6A6E00707CF6 /* Request.swift in Sources */, + 1E51411D2B85683C007BE94A /* SSLPinning.m in Sources */, 7AAB3E17257E6A6E00707CF6 /* ReplyNotification.swift in Sources */, 7AAB3E18257E6A6E00707CF6 /* Storage.swift in Sources */, 7AAB3E19257E6A6E00707CF6 /* Push.swift in Sources */, @@ -1443,11 +2354,19 @@ 7AAB3E23257E6A6E00707CF6 /* Data+Extensions.swift in Sources */, 7AAB3E24257E6A6E00707CF6 /* Date+Extensions.swift in Sources */, 7AAB3E25257E6A6E00707CF6 /* Database.swift in Sources */, + 1E9A71752B59F36E00477BA2 /* ClientSSL.swift in Sources */, + 1ED038AF2B50927B00C007D4 /* WatermelonDB+Extensions.swift in Sources */, + 1ED038A72B50900800C007D4 /* Bundle+Extensions.swift in Sources */, 7AAB3E26257E6A6E00707CF6 /* String+Extensions.swift in Sources */, + 1ED038BB2B50A1B800C007D4 /* WatchConnection.swift in Sources */, + 1ED038A32B508FE700C007D4 /* FileManager+Extensions.swift in Sources */, 7AAB3E27257E6A6E00707CF6 /* Notification.swift in Sources */, + 1ED038C22B50A1E400C007D4 /* DBUser.swift in Sources */, 7AAB3E28257E6A6E00707CF6 /* API.swift in Sources */, 7AAB3E29257E6A6E00707CF6 /* Response.swift in Sources */, + 1ED038BF2B50A1D400C007D4 /* DBServer.swift in Sources */, 7AAB3E2A257E6A6E00707CF6 /* AppGroup.m in Sources */, + 1ED038C52B50A1F500C007D4 /* WatchMessage.swift in Sources */, 7AAB3E2B257E6A6E00707CF6 /* main.m in Sources */, 7AAB3E2C257E6A6E00707CF6 /* URL+Extensions.swift in Sources */, 7AAB3E2D257E6A6E00707CF6 /* AppGroup.swift in Sources */, @@ -1467,6 +2386,16 @@ target = 1EC6ACAF22CB9FC300A41C61 /* ShareRocketChatRN */; targetProxy = 1EC6ACB922CB9FC300A41C61 /* PBXContainerItemProxy */; }; + 1ED1ECE62B8699E900F6620C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 1ED0388D2B507B4B00C007D4 /* Rocket.Chat.Watch */; + targetProxy = 1ED1ECE52B8699E900F6620C /* PBXContainerItemProxy */; + }; + 1EE79C552B8912F600CF1863 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 1ED1EC8E2B86997F00F6620C /* RocketChatRN Watch */; + targetProxy = 1EE79C542B8912F600CF1863 /* PBXContainerItemProxy */; + }; 1EFEB59B2493B6640072EDC0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 1EFEB5942493B6640072EDC0 /* NotificationService */; @@ -1513,6 +2442,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = S6UPZG7ZR3; ENABLE_BITCODE = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; EXCLUDED_ARCHS = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1569,6 +2499,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = S6UPZG7ZR3; ENABLE_BITCODE = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; EXCLUDED_ARCHS = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1638,6 +2569,7 @@ DEVELOPMENT_TEAM = S6UPZG7ZR3; ENABLE_BITCODE = NO; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; EXCLUDED_ARCHS = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1706,6 +2638,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = S6UPZG7ZR3; ENABLE_BITCODE = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; EXCLUDED_ARCHS = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1744,6 +2677,202 @@ }; name = Release; }; + 1ED0389D2B507B4F00C007D4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_ASSET_PATHS = "\"RocketChat Watch App/Preview Content\""; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=watchos*]" = S6UPZG7ZR3; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = Rocket.Chat; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKCompanionAppBundleIdentifier = chat.rocket.ios; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; + PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.ios.watchkitapp; + PRODUCT_NAME = "Rocket.Chat Watch"; + PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.ios.watchkitapp"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=watchos*]" = "match AppStore chat.rocket.ios.watchkitapp"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 8.0; + }; + name = Debug; + }; + 1ED0389E2B507B4F00C007D4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_ASSET_PATHS = "\"RocketChat Watch App/Preview Content\""; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=watchos*]" = S6UPZG7ZR3; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = Rocket.Chat; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKCompanionAppBundleIdentifier = chat.rocket.ios; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; + PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.ios.watchkitapp; + PRODUCT_NAME = "Rocket.Chat Watch"; + PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.ios.watchkitapp"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=watchos*]" = "match AppStore chat.rocket.ios.watchkitapp"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 8.0; + }; + name = Release; + }; + 1ED1ECDB2B86997F00F6620C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_ASSET_PATHS = "\"RocketChat Watch App/Preview Content\""; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=watchos*]" = S6UPZG7ZR3; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "Rocket.Chat Experimental"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKCompanionAppBundleIdentifier = chat.rocket.reactnative; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; + PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.watchkitapp; + PRODUCT_NAME = "Rocket.Chat Experimental Watch"; + PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.reactnative.watchkitapp"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=watchos*]" = "match AppStore chat.rocket.reactnative.watchkitapp"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 8.0; + }; + name = Debug; + }; + 1ED1ECDC2B86997F00F6620C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_ASSET_PATHS = "\"RocketChat Watch App/Preview Content\""; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=watchos*]" = S6UPZG7ZR3; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "Rocket.Chat Experimental"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKCompanionAppBundleIdentifier = chat.rocket.reactnative; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; + PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.watchkitapp; + PRODUCT_NAME = "Rocket.Chat Experimental Watch"; + PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.reactnative.watchkitapp"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=watchos*]" = "match AppStore chat.rocket.reactnative.watchkitapp"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 8.0; + }; + name = Release; + }; 1EFEB59D2493B6640072EDC0 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 8D9DA7CD79909E251F469FD5 /* Pods-defaults-NotificationService.debug.xcconfig */; @@ -1761,12 +2890,13 @@ DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = S6UPZG7ZR3; ENABLE_BITCODE = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; EXCLUDED_ARCHS = ""; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = NotificationService/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.48.0; + MARKETING_VERSION = 4.49.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -1800,12 +2930,13 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = S6UPZG7ZR3; ENABLE_BITCODE = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; EXCLUDED_ARCHS = ""; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = NotificationService/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.48.0; + MARKETING_VERSION = 4.49.0; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService; @@ -1834,6 +2965,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = S6UPZG7ZR3; ENABLE_BITCODE = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1889,6 +3021,7 @@ DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = S6UPZG7ZR3; ENABLE_BITCODE = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -2051,6 +3184,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 1ED0389F2B507B4F00C007D4 /* Build configuration list for PBXNativeTarget "Rocket.Chat.Watch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1ED0389D2B507B4F00C007D4 /* Debug */, + 1ED0389E2B507B4F00C007D4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1ED1ECDA2B86997F00F6620C /* Build configuration list for PBXNativeTarget "RocketChatRN Watch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1ED1ECDB2B86997F00F6620C /* Debug */, + 1ED1ECDC2B86997F00F6620C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 1EFEB5A02493B6640072EDC0 /* Build configuration list for PBXNativeTarget "NotificationService" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2079,6 +3230,29 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCVersionGroup section */ + 1ED033AC2B55B1CC004F4930 /* Default.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 1ED033AD2B55B1CC004F4930 /* Default.xcdatamodel */, + ); + currentVersion = 1ED033AD2B55B1CC004F4930 /* Default.xcdatamodel */; + path = Default.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; + 1ED033C92B55D4F0004F4930 /* RocketChat.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 1ED033CA2B55D4F0004F4930 /* RocketChat.xcdatamodel */, + ); + currentVersion = 1ED033CA2B55D4F0004F4930 /* RocketChat.xcdatamodel */; + path = RocketChat.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ }; rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; } diff --git a/ios/RocketChatRN.xcodeproj/xcshareddata/xcschemes/RocketChat Watch.xcscheme b/ios/RocketChatRN.xcodeproj/xcshareddata/xcschemes/RocketChat Watch.xcscheme new file mode 100644 index 000000000..e9306a238 --- /dev/null +++ b/ios/RocketChatRN.xcodeproj/xcshareddata/xcschemes/RocketChat Watch.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/RocketChatRN.xcodeproj/xcshareddata/xcschemes/RocketChatRN Watch.xcscheme b/ios/RocketChatRN.xcodeproj/xcshareddata/xcschemes/RocketChatRN Watch.xcscheme new file mode 100644 index 000000000..6ba012562 --- /dev/null +++ b/ios/RocketChatRN.xcodeproj/xcshareddata/xcschemes/RocketChatRN Watch.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/RocketChatRN/AppDelegate.h b/ios/RocketChatRN/AppDelegate.h index 8d328571a..7ba85e39b 100644 --- a/ios/RocketChatRN/AppDelegate.h +++ b/ios/RocketChatRN/AppDelegate.h @@ -10,6 +10,7 @@ #import #import #import +#import // https://github.com/expo/expo/issues/17705#issuecomment-1196251146 #import "ExpoModulesCore-Swift.h" #import "RocketChatRN-Swift.h" @@ -17,5 +18,6 @@ @interface AppDelegate : EXAppDelegateWrapper @property (nonatomic, strong) UIWindow *window; +@property (nonatomic, strong) WatchConnection *watchConnection; @end diff --git a/ios/RocketChatRN/AppDelegate.mm b/ios/RocketChatRN/AppDelegate.mm index 4418704fe..ffbc15003 100644 --- a/ios/RocketChatRN/AppDelegate.mm +++ b/ios/RocketChatRN/AppDelegate.mm @@ -69,6 +69,10 @@ [MMKV initializeMMKV:nil groupDir:groupDir logLevel:MMKVLogInfo]; [RNBootSplash initWithStoryboard:@"LaunchScreen" rootView:rootView]; + + [[[SSLPinning alloc] init] migrate]; + + self.watchConnection = [[WatchConnection alloc] initWithSession:[WCSession defaultSession]]; return YES; } diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist index 6a9f66567..e23b4bb69 100644 --- a/ios/RocketChatRN/Info.plist +++ b/ios/RocketChatRN/Info.plist @@ -26,7 +26,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 4.48.0 + 4.49.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/SSLPinning/SSLPinning.m b/ios/SSLPinning/SSLPinning.m new file mode 100644 index 000000000..570f9e7be --- /dev/null +++ b/ios/SSLPinning/SSLPinning.m @@ -0,0 +1,3 @@ +#import "React/RCTBridgeModule.h" +@interface RCT_EXTERN_MODULE(SSLPinning, NSObject) +@end diff --git a/ios/SSLPinning/SSLPinning.swift b/ios/SSLPinning/SSLPinning.swift new file mode 100644 index 000000000..c14a26fb3 --- /dev/null +++ b/ios/SSLPinning/SSLPinning.swift @@ -0,0 +1,53 @@ +import WatermelonDB + +@objc(SSLPinning) +final class SSLPinning: NSObject { + private struct Constants { + static let certificateKey = "ssl_pinning_certificate" + static let passwordKey = "ssl_pinning_password" + } + + private let database = WatermelonDB.Database(name: "default") + private let mmkv = MMKV.build() + + @objc func setCertificate(_ server: String, _ path: String, _ password: String) { + guard FileManager.default.fileExists(atPath: path) else { + return + } + + guard let certificate = NSData(contentsOfFile: path) else { + return + } + + mmkv.set(Data(referencing: certificate), forKey: Constants.certificateKey.appending(server)) + mmkv.set(password, forKey: Constants.passwordKey.appending(server)) + } + + @objc func migrate() { + let serversQuery = database.query(raw: "select * from servers") as [DBServer] + + serversQuery.forEach { server in + guard let clientSSL = mmkv.clientSSL(for: server.url) else { + return + } + + setCertificate( + server.url.absoluteString.removeTrailingSlash(), + clientSSL.path, + clientSSL.password + ) + } + } + + func getCertificate(server: String) -> (certificate: Data, password: String)? { + guard let certificate = mmkv.data(forKey: Constants.certificateKey.appending(server)) else { + return nil + } + + guard let password = mmkv.string(forKey: Constants.passwordKey.appending(server)) else { + return nil + } + + return (certificate, password) + } +} diff --git a/ios/ShareRocketChatRN/Info.plist b/ios/ShareRocketChatRN/Info.plist index b84970bef..776ca2a07 100644 --- a/ios/ShareRocketChatRN/Info.plist +++ b/ios/ShareRocketChatRN/Info.plist @@ -26,7 +26,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 4.48.0 + 4.49.0 CFBundleVersion 1 KeychainGroup diff --git a/ios/Shared/Extensions/Bundle+Extensions.swift b/ios/Shared/Extensions/Bundle+Extensions.swift new file mode 100644 index 000000000..8873e7b2c --- /dev/null +++ b/ios/Shared/Extensions/Bundle+Extensions.swift @@ -0,0 +1,15 @@ +import Foundation + +extension Bundle { + func bool(forKey key: String) -> Bool { + object(forInfoDictionaryKey: key) as? Bool ?? false + } + + func string(forKey key: String) -> String { + guard let string = object(forInfoDictionaryKey: key) as? String else { + fatalError("Could not locate string for key \(key).") + } + + return string + } +} diff --git a/ios/Shared/Extensions/FileManager+Extensions.swift b/ios/Shared/Extensions/FileManager+Extensions.swift new file mode 100644 index 000000000..92f60c92c --- /dev/null +++ b/ios/Shared/Extensions/FileManager+Extensions.swift @@ -0,0 +1,13 @@ +import Foundation + +extension FileManager { + func groupDir() -> String { + let applicationGroupIdentifier = Bundle.main.string(forKey: "AppGroup") + + guard let path = containerURL(forSecurityApplicationGroupIdentifier: applicationGroupIdentifier)?.path else { + return "" + } + + return path + } +} diff --git a/ios/Shared/Extensions/WatermelonDB+Extensions.swift b/ios/Shared/Extensions/WatermelonDB+Extensions.swift new file mode 100644 index 000000000..846b8991b --- /dev/null +++ b/ios/Shared/Extensions/WatermelonDB+Extensions.swift @@ -0,0 +1,33 @@ +import WatermelonDB + +extension WatermelonDB.Database { + convenience init(name: String) { + let isOfficial = Bundle.main.bool(forKey: "IS_OFFICIAL") + let groupDir = FileManager.default.groupDir() + let path = "\(groupDir)/\(name)\(isOfficial ? "" : "-experimental").db" + + self.init(path: path) + } + + func query(raw: SQL, _ args: QueryArgs = []) -> [T] { + guard let results = try? queryRaw(raw, args) else { + return [] + } + + return results.compactMap { result in + guard let dictionary = result.resultDictionary else { + return nil + } + + guard let data = try? JSONSerialization.data(withJSONObject: dictionary) else { + return nil + } + + guard let item = try? JSONDecoder().decode(T.self, from: data) else { + return nil + } + + return item + } + } +} diff --git a/ios/Shared/RocketChat/ClientSSL.swift b/ios/Shared/RocketChat/ClientSSL.swift new file mode 100644 index 000000000..52d9f842d --- /dev/null +++ b/ios/Shared/RocketChat/ClientSSL.swift @@ -0,0 +1,29 @@ +import Foundation + +struct ClientSSL: Codable { + let path: String + let password: String +} + +extension MMKV { + func clientSSL(for url: URL) -> ClientSSL? { + let server = url.absoluteString.removeTrailingSlash() + let host = url.host ?? "" + + guard let name = string(forKey: "RC_CERTIFICATE_KEY-\(server)") else { + return nil + } + + guard let data = data(forKey: host), let certificate = try? JSONDecoder().decode(ClientSSL.self, from: data) else { + return nil + } + + return .init(path: getFilePath(forName: name), password: certificate.password) + } + + private func getFilePath(forName name: String) -> String { + let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) + let documentsDirectory = paths[0] + return documentsDirectory.path + "/" + name + } +} diff --git a/ios/Shared/RocketChat/Database.swift b/ios/Shared/RocketChat/Database.swift index 7e7928506..db493fa8a 100644 --- a/ios/Shared/RocketChat/Database.swift +++ b/ios/Shared/RocketChat/Database.swift @@ -10,60 +10,42 @@ import Foundation import WatermelonDB final class Database { - private final var database: WatermelonDB.Database? = nil - - private var directory: String? { - if let suiteName = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as? String { - if let directory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: suiteName) { - return directory.path - } - } - - return nil - } - - init(server: String) { - if let url = URL(string: server) { - if let domain = url.domain, let directory = directory { - let isOfficial = Bundle.main.object(forInfoDictionaryKey: "IS_OFFICIAL") as? Bool ?? false - self.database = WatermelonDB.Database(path: "\(directory)/\(domain)\(isOfficial ? "" : "-experimental").db") - } - } - } - - func readRoomEncryptionKey(rid: String) -> String? { - if let database = database { - if let results = try? database.queryRaw("select * from subscriptions where id == ? limit 1", [rid]) { - guard let record = results.next() else { - return nil - } - - if let room = record.resultDictionary as? [String: Any] { - if let e2eKey = room["e2e_key"] as? String { - return e2eKey - } - } - } - } - - return nil - } - - func readRoomEncrypted(rid: String) -> Bool { - if let database = database { - if let results = try? database.queryRaw("select * from subscriptions where id == ? limit 1", [rid]) { - guard let record = results.next() else { - return false - } - - if let room = record.resultDictionary as? [String: Any] { - if let encrypted = room["encrypted"] as? Bool { - return encrypted - } - } - } - } - - return false - } + private let database: WatermelonDB.Database + + init(server: String) { + let domain = URL(string: server)?.domain ?? "" + database = .init(name: domain) + } + + func readRoomEncryptionKey(rid: String) -> String? { + if let results = try? database.queryRaw("select * from subscriptions where id == ? limit 1", [rid]) { + guard let record = results.next() else { + return nil + } + + if let room = record.resultDictionary as? [String: Any] { + if let e2eKey = room["e2e_key"] as? String { + return e2eKey + } + } + } + + return nil + } + + func readRoomEncrypted(rid: String) -> Bool { + if let results = try? database.queryRaw("select * from subscriptions where id == ? limit 1", [rid]) { + guard let record = results.next() else { + return false + } + + if let room = record.resultDictionary as? [String: Any] { + if let encrypted = room["encrypted"] as? Bool { + return encrypted + } + } + } + + return false + } } diff --git a/ios/Shared/RocketChat/MMKV.swift b/ios/Shared/RocketChat/MMKV.swift new file mode 100644 index 000000000..69a1eb669 --- /dev/null +++ b/ios/Shared/RocketChat/MMKV.swift @@ -0,0 +1,40 @@ +import Foundation + +extension MMKV { + static func build() -> MMKV { + let password = SecureStorage().getSecureKey("com.MMKV.default".toHex()) + let groupDir = FileManager.default.groupDir() + + MMKV.initialize(rootDir: nil, groupDir: groupDir, logLevel: MMKVLogLevel.none) + + guard let mmkv = MMKV(mmapID: "default", cryptKey: password?.data(using: .utf8), mode: MMKVMode.multiProcess) else { + fatalError("Could not initialize MMKV instance.") + } + + return mmkv + } + + func userToken(for userId: String) -> String? { + guard let userToken = string(forKey: "reactnativemeteor_usertoken-\(userId)") else { + return nil + } + + return userToken + } + + func userId(for server: String) -> String? { + guard let userId = string(forKey: "reactnativemeteor_usertoken-\(server)") else { + return nil + } + + return userId + } + + func privateKey(for server: String) -> String? { + guard let privateKey = string(forKey: "\(server)-RC_E2E_PRIVATE_KEY") else { + return nil + } + + return privateKey + } +} diff --git a/ios/Shared/RocketChat/Storage.swift b/ios/Shared/RocketChat/Storage.swift index 7f2496897..019c56419 100644 --- a/ios/Shared/RocketChat/Storage.swift +++ b/ios/Shared/RocketChat/Storage.swift @@ -1,60 +1,24 @@ -// -// Storage.swift -// NotificationService -// -// Created by Djorkaeff Alexandre Vilela Pereira on 9/15/20. -// Copyright © 2020 Rocket.Chat. All rights reserved. -// - import Foundation struct Credentials { - let userId: String - let userToken: String + let userId: String + let userToken: String } -class Storage { - static let shared = Storage() - - final var mmkv: MMKV? = nil - - init() { - let mmapID = "default" - let instanceID = "com.MMKV.\(mmapID)" - let secureStorage = SecureStorage() - - // get mmkv instance password from keychain - var key: Data? - if let password: String = secureStorage.getSecureKey(instanceID.toHex()) { - key = password.data(using: .utf8) - } - - guard let cryptKey = key else { - return - } - - // Get App Group directory - let suiteName = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String - guard let directory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: suiteName) else { - return - } - - // Set App Group dir - MMKV.initialize(rootDir: nil, groupDir: directory.path, logLevel: MMKVLogLevel.none) - self.mmkv = MMKV(mmapID: mmapID, cryptKey: cryptKey, mode: MMKVMode.multiProcess) - } - - func getCredentials(server: String) -> Credentials? { - if let userId = self.mmkv?.string(forKey: "reactnativemeteor_usertoken-\(server)") { - if let userToken = self.mmkv?.string(forKey: "reactnativemeteor_usertoken-\(userId)") { - return Credentials(userId: userId, userToken: userToken) - } - } - - return nil - } - - func getPrivateKey(server: String) -> String? { - return self.mmkv?.string(forKey: "\(server)-RC_E2E_PRIVATE_KEY") - } +final class Storage { + static let shared = Storage() + + private let mmkv = MMKV.build() + + func getCredentials(server: String) -> Credentials? { + guard let userId = mmkv.userId(for: server), let userToken = mmkv.userToken(for: userId) else { + return nil + } + + return .init(userId: userId, userToken: userToken) + } + + func getPrivateKey(server: String) -> String? { + mmkv.privateKey(for: server) + } } diff --git a/ios/Watch/Database/DBServer.swift b/ios/Watch/Database/DBServer.swift new file mode 100644 index 000000000..245e7361a --- /dev/null +++ b/ios/Watch/Database/DBServer.swift @@ -0,0 +1,21 @@ +import Foundation + +struct DBServer: Codable { + let url: URL + let name: String + let useRealName: Int + let iconURL: URL + let version: String + + var identifier: String { + url.absoluteString.removeTrailingSlash() + } + + enum CodingKeys: String, CodingKey { + case url = "id" + case name + case useRealName = "use_real_name" + case iconURL = "icon_url" + case version + } +} diff --git a/ios/Watch/Database/DBUser.swift b/ios/Watch/Database/DBUser.swift new file mode 100644 index 000000000..4641ffebe --- /dev/null +++ b/ios/Watch/Database/DBUser.swift @@ -0,0 +1,6 @@ +import Foundation + +struct DBUser: Codable { + let name: String + let username: String +} diff --git a/ios/Watch/WatchConnection.swift b/ios/Watch/WatchConnection.swift new file mode 100644 index 000000000..defa29527 --- /dev/null +++ b/ios/Watch/WatchConnection.swift @@ -0,0 +1,92 @@ +import Foundation +import WatermelonDB +import WatchConnectivity + +@objc +final class WatchConnection: NSObject { + private let database = WatermelonDB.Database(name: "default") + private let mmkv = MMKV.build() + private let session: WCSession + + @objc init(session: WCSession) { + self.session = session + super.init() + + if WCSession.isSupported() { + session.delegate = self + session.activate() + } + } + + private func getMessage() -> WatchMessage { + let serversQuery = database.query(raw: "select * from servers") as [DBServer] + + let servers = serversQuery.compactMap { item -> WatchMessage.Server? in + guard let userId = mmkv.userId(for: item.identifier), let userToken = mmkv.userToken(for: userId) else { + return nil + } + + let clientSSL = SSLPinning().getCertificate(server: item.url.absoluteString.removeTrailingSlash()) + + let usersQuery = database.query(raw: "select * from users where token == ? limit 1", [userToken]) as [DBUser] + + guard let user = usersQuery.first else { + return nil + } + + return WatchMessage.Server( + url: item.url, + name: item.name, + iconURL: item.iconURL, + useRealName: item.useRealName == 1 ? true : false, + loggedUser: .init( + id: userId, + token: userToken, + name: user.name, + username: user.username + ), + clientSSL: clientSSL.map { + .init( + certificate: $0.certificate, + password: $0.password + ) + }, + version: item.version + ) + } + + return WatchMessage(servers: servers) + } + + private func encodedMessage() -> [String: Any] { + do { + let data = try JSONEncoder().encode(getMessage()) + + guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else { + fatalError("Could not serialize message: \(getMessage())") + } + + return dictionary + } catch { + fatalError("Could not encode message: \(getMessage())") + } + } +} + +extension WatchConnection: WCSessionDelegate { + func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { + + } + + func sessionDidBecomeInactive(_ session: WCSession) { + session.activate() + } + + func sessionDidDeactivate(_ session: WCSession) { + session.activate() + } + + func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) { + replyHandler(encodedMessage()) + } +} diff --git a/ios/Watch/WatchMessage.swift b/ios/Watch/WatchMessage.swift new file mode 100644 index 000000000..d86d9af24 --- /dev/null +++ b/ios/Watch/WatchMessage.swift @@ -0,0 +1,27 @@ +import Foundation + +struct WatchMessage: Codable { + let servers: [Server] + + struct Server: Codable { + let url: URL + let name: String + let iconURL: URL + let useRealName: Bool + let loggedUser: LoggedUser + let clientSSL: ClientSSL? + let version: String + + struct LoggedUser: Codable { + let id: String + let token: String + let name: String + let username: String + } + + struct ClientSSL: Codable { + let certificate: Data + let password: String + } + } +} diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 2a313d2cb..949428ed4 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -57,6 +57,7 @@ platform :ios do match(type: "appstore") get_provisioning_profile(app_identifier: "chat.rocket.reactnative.ShareExtension") get_provisioning_profile(app_identifier: "chat.rocket.reactnative.NotificationService") + get_provisioning_profile(app_identifier: "chat.rocket.reactnative.watchkitapp") # pem(api_key: api_key) # still uses Spaceship http://docs.fastlane.tools/actions/pem/#how-does-it-work gym( scheme: "RocketChatRN", @@ -71,7 +72,7 @@ platform :ios do match( type: "appstore", - app_identifier: ["chat.rocket.ios", "chat.rocket.ios.NotificationService", "chat.rocket.ios.Rocket-Chat-ShareExtension"], + app_identifier: ["chat.rocket.ios", "chat.rocket.ios.NotificationService", "chat.rocket.ios.Rocket-Chat-ShareExtension", "chat.rocket.ios.watchkitapp"], readonly: true, output_path: './' ) @@ -90,6 +91,13 @@ platform :ios do targets: "ShareRocketChatRN", ) + update_code_signing_settings( + profile_name: "match AppStore chat.rocket.ios.watchkitapp", + build_configurations: "Release", + code_sign_identity: "iPhone Distribution", + targets: "Rocket.Chat.Watch", + ) + gym( scheme: "RocketChat", workspace: "RocketChatRN.xcworkspace", diff --git a/package.json b/package.json index 7182e4830..4780969c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket-chat-reactnative", - "version": "4.48.0", + "version": "4.49.0", "private": true, "scripts": { "start": "react-native start", diff --git a/patches/@nozbe+watermelondb+0.23.0.patch b/patches/@nozbe+watermelondb+0.23.0.patch index da8f8509c..098217c19 100644 --- a/patches/@nozbe+watermelondb+0.23.0.patch +++ b/patches/@nozbe+watermelondb+0.23.0.patch @@ -54,3 +54,15 @@ index b4d7151..429e318 100644 let resultSet = try fmdb.executeQuery(query, values: args) return AnyIterator { +diff --git a/node_modules/@nozbe/watermelondb/native/shared/DatabasePlatform.h b/node_modules/@nozbe/watermelondb/native/shared/DatabasePlatform.h +index 6814d5f..fecded8 100644 +--- a/node_modules/@nozbe/watermelondb/native/shared/DatabasePlatform.h ++++ b/node_modules/@nozbe/watermelondb/native/shared/DatabasePlatform.h +@@ -1,6 +1,7 @@ + #pragma once + + #include ++#include + + namespace watermelondb { + namespace platform {