diff --git a/README.md b/README.md index 4d6ed53f2f..2883ffe23a 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ # Rocket.Chat React Native Mobile -[![Greenkeeper badge](https://badges.greenkeeper.io/RocketChat/Rocket.Chat.ReactNative.svg)](https://greenkeeper.io/) -[![Build Status](https://img.shields.io/travis/RocketChat/Rocket.Chat.ReactNative/master.svg)](https://travis-ci.org/RocketChat/Rocket.Chat.ReactNative) [![Project Dependencies](https://david-dm.org/RocketChat/Rocket.Chat.ReactNative.svg)](https://david-dm.org/RocketChat/Rocket.Chat.ReactNative) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/bb15e2392a71473ea59d3f634f35c54e)](https://www.codacy.com/app/RocketChat/Rocket.Chat.ReactNative?utm_source=github.com&utm_medium=referral&utm_content=RocketChat/Rocket.Chat.ReactNative&utm_campaign=badger) [![codecov](https://codecov.io/gh/RocketChat/Rocket.Chat.ReactNative/branch/master/graph/badge.svg)](https://codecov.io/gh/RocketChat/Rocket.Chat.ReactNative) [![CodeFactor](https://www.codefactor.io/repository/github/rocketchat/rocket.chat.reactnative/badge)](https://www.codefactor.io/repository/github/rocketchat/rocket.chat.reactnative) -**Supported Server Versions:** 0.66.0+ +**Supported Server Versions:** 0.70.0+ ## Download @@ -59,55 +57,53 @@ If you don't need multiple servers, there is a branch `single-server` just for t Readme will guide you on how to config. ## Current priorities -1) [NEW] Jitsi integration ([#711][i711]) -2) [NEW] Federation ([#706][i706]) -3) [NEW] Record video ([#712][i712]) -4) [NEW] Slash Commands ([#405][i405]) -5) [NEW] Share extension ([#391][i391]) - -[i711]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/711 -[i706]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/706 -[i707]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/707 -[i712]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/712 -[i708]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/708 -[i391]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/391 -[i405]: https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/405 +1) Jitsi integration +2) Notification Preferences +3) Two-way authentication +4) Authentication via SAML +5) Authentication via Custom OAuth +6) Authentication via CAS +7) Bugsnag +8) Optional Analytics +9) Typescript +10) Prettier ## Features | Feature | Status | |--------------------------------------------------------------- |-------- | | Jitsi Integration | ❌ | -| Federation (Directory) | ❌ | -| Threads | ✅ | +| Federation (Directory) | ✅ | +| Discussions | ❌ | +| Threads | ✅ | | Record Audio | ✅ | -| Record Video | ❌ | -| Commands | ❌ | +| Record Video | ✅ | +| Commands | ✅ | | Draft message per room | ✅ | -| Share Extension | ❌ | +| Share Extension | ✅ | | Notifications Preferences | ✅ | | Edited status | ✅ | -| Upload video | ❌ | +| Upload video | ✅ | | Grouped messages | ✅ | -| Mark room as read | ❌ | -| Mark room as unread | ❌ | +| Mark room as read | ✅ | +| Mark room as unread | ✅ | | Tablet Support | ❌ | -| Read receipt | ❌ | +| Read receipt | ✅ | | Broadbast Channel | ✅ | | Authentication via SAML | ❌ | | Authentication via CAS | ❌ | -| Custom Fields on Signup | ❌ | -| Report message | ❌ | +| Custom Fields on Signup | ✅ | +| Report message | ✅ | | Theming | ❌ | | Settings -> Review the App | ❌ | | Settings -> Default Browser | ❌ | | Admin panel | ✅ | | Reply message from notification | ❌ | | Unread counter banner on message list | ✅ | -| E2E | ❌ | +| E2E Encryption | ❌ | | Join a Protected Room | ❌ | | Optional Analytics | ❌ | | Settings -> About us | ❌ | -| Settings -> Contact us | ❌ | +| Settings -> Contact us | ✅ | | Settings -> Update App Icon | ❌ | | Settings -> Share | ❌ | | Accessibility (Medium) | ❌ | diff --git a/__mocks__/react-native-localize.js b/__mocks__/react-native-localize.js new file mode 100644 index 0000000000..2800ee9024 --- /dev/null +++ b/__mocks__/react-native-localize.js @@ -0,0 +1,2 @@ +export const initialConstants = null; +export const findBestAvailableLanguage = () => null; diff --git a/__mocks__/react-native-realm-path.js b/__mocks__/react-native-realm-path.js new file mode 100644 index 0000000000..5df7c3985a --- /dev/null +++ b/__mocks__/react-native-realm-path.js @@ -0,0 +1,3 @@ +export default { + realmPath: '' +}; diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap index 67f5c84e20..c2c7e00dfc 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -58,7 +58,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -180,38 +179,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Message + + + Message + - + @@ -262,7 +263,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -384,38 +384,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + - + @@ -466,7 +468,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -588,38 +589,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - … + + + … + - + @@ -652,7 +655,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -774,38 +776,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Different user + + + Different user + - + @@ -838,7 +842,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -860,38 +863,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - This is the third message + + + This is the third message + - + @@ -924,7 +929,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -946,38 +950,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - This is the second message + + + This is the second message + - + @@ -1010,7 +1016,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -1132,38 +1137,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - This is the first message + + + This is the first message + - + @@ -1214,7 +1221,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -1236,38 +1242,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - Message + + + Message + - + @@ -1318,7 +1326,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -1454,38 +1461,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Message + + + Message + - + @@ -1518,7 +1527,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -1654,38 +1662,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Message + + + Message + - + @@ -1736,7 +1746,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -1858,38 +1867,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Message + + + Message + - + @@ -1940,7 +1951,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -2062,38 +2072,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Message + + + Message + - + @@ -2144,7 +2156,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -2266,38 +2277,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Message + + + Message + - + @@ -2348,7 +2361,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -2470,122 +2482,124 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + -   - rocket.cat -   - - - - - -   - diego.mello -   - - - - - +   + rocket.cat +   + + + + + -   - all -   - - - - - +   + diego.mello +   + + + + + -   - here -   - - - - - +   + all +   + + + + + -  # - general -   + > +   + here +   + + + + + +  # + general +   + - + @@ -2636,7 +2650,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -2758,38 +2771,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - 👊🤙👏 + + + 👊🤙👏 + - + @@ -2840,7 +2855,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -2962,38 +2976,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - 👏 + + + 👏 + - + @@ -3044,7 +3060,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -3166,80 +3181,82 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - - - - + - - - - + + + + + /> + + + + + - + @@ -3290,7 +3307,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -3412,48 +3428,50 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - + > + + - + @@ -3504,7 +3522,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -3626,51 +3643,53 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - 🤙 - - + > + + 🤙 + + + - + @@ -3721,7 +3740,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -3843,54 +3861,56 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - 🤙 - - - - 🤙🤙 + > + + 🤙 + + + + 🤙🤙 + - + @@ -3941,7 +3961,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -4063,38 +4082,40 @@ exports[`Storyshots Message list 1`] = ` 10 November 2017 - - + - - Testing + + + Testing + - + @@ -4145,7 +4166,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -4267,38 +4287,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Reactions + + + Reactions + - + @@ -4763,38 +4784,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Multiple Reactions + + + Multiple Reactions + - + @@ -5622,38 +5644,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Fourth message + + + Fourth message + - + @@ -5686,7 +5710,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -5808,38 +5831,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Third message + + + Third message + - + @@ -5872,7 +5897,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -5994,38 +6018,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Second message + + + Second message + - + @@ -6058,7 +6084,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -6180,38 +6205,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - First message + + + First message + - + @@ -6262,7 +6289,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -6384,38 +6410,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Fourth message + + + Fourth message + - + @@ -6513,7 +6541,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -6635,38 +6662,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Third message + + + Third message + - + @@ -6746,7 +6775,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -6768,38 +6796,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - Second message + + + Second message + - + @@ -6832,7 +6862,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -6954,40 +6983,42 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Second message - - - - - + + + Second message + + + + + + @@ -7057,7 +7088,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -7179,38 +7209,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - First message + + + First message + - + @@ -7261,7 +7293,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -7383,6 +7414,7 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM + @@ -7623,6 +7654,7 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM + @@ -7894,6 +7925,7 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM + @@ -8137,6 +8168,7 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM + @@ -8353,6 +8384,7 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM + @@ -8645,38 +8676,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - First message + + + First message + - + @@ -8709,7 +8742,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -8731,6 +8763,7 @@ exports[`Storyshots Message list 1`] = ` ] } > + @@ -9010,6 +9042,7 @@ exports[`Storyshots Message list 1`] = ` ] } > + @@ -9257,6 +9289,7 @@ exports[`Storyshots Message list 1`] = ` ] } > + @@ -9622,38 +9654,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - I’m fine! + + + I’m fine! + - + @@ -9914,38 +9947,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - I’m fine! + + + I’m fine! + - + @@ -10237,38 +10271,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - I’m fine! + + + I’m fine! + - + @@ -10301,7 +10337,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -10323,38 +10358,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - I’m fine! + + + I’m fine! + - + @@ -10387,7 +10424,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -10509,38 +10545,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - I’m fine! + + + I’m fine! + - + @@ -10617,38 +10654,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - I’m fine! + + + I’m fine! + - + @@ -10843,38 +10881,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - How are you? + + + How are you? + - + @@ -11110,38 +11149,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - How are you? + + + How are you? + - + @@ -11407,41 +11447,43 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - I’m fine! - - - - - - + + + I’m fine! + + + + + + + @@ -11471,7 +11513,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -11623,38 +11664,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - I’m fine! + + + I’m fine! + - + @@ -11687,7 +11730,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -11839,38 +11881,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - I’m fine! + + + I’m fine! + - + @@ -11903,7 +11947,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -12055,38 +12098,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - I’m fine! + + + I’m fine! + - + @@ -12119,7 +12164,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -12271,38 +12315,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + - + @@ -12335,7 +12381,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -12487,38 +12532,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + - + @@ -12551,7 +12598,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -12703,19 +12749,21 @@ exports[`Storyshots Message list 1`] = ` ] } > - - Sent an attachment - + + + Sent an attachment + + @@ -12765,7 +12813,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -12887,38 +12934,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - How are you? + + + How are you? + - + @@ -13109,38 +13157,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - I’m fine! + + + I’m fine! + - + @@ -13173,7 +13223,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -13250,38 +13299,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + - + @@ -13314,7 +13365,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -13391,19 +13441,21 @@ exports[`Storyshots Message list 1`] = ` ] } > - + - Sent an attachment - + > + Sent an attachment + + @@ -13453,7 +13505,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -13605,38 +13656,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - I’m fine! + + + I’m fine! + - + @@ -13669,7 +13722,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -13746,40 +13798,42 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - Cool! - - - - - + + + Cool! + + + + + + @@ -13810,7 +13864,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -13887,38 +13940,40 @@ exports[`Storyshots Message list 1`] = ` ] } > - - + - - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + - + @@ -13951,7 +14006,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -14028,19 +14082,21 @@ exports[`Storyshots Message list 1`] = ` ] } > - + - Sent an attachment - + > + Sent an attachment + + @@ -14090,7 +14146,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -14361,7 +14416,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -14634,7 +14688,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -14907,7 +14960,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -15198,7 +15250,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -15320,6 +15371,7 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM + @@ -15632,51 +15683,53 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Message - - + > + + Message + + + - + @@ -15794,6 +15846,7 @@ exports[`Storyshots Message list 1`] = ` ] } > + @@ -16028,38 +16080,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Message + + + Message + - + @@ -16548,38 +16601,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Message + + + Message + - + @@ -17144,38 +17198,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Broadcasted message + + + Broadcasted message + - + @@ -17425,38 +17480,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - This message is inside an archived room + + + This message is inside an archived room + - + @@ -17481,51 +17538,7 @@ exports[`Storyshots Message list 1`] = ` > Error - - - -  - - + @@ -17657,89 +17669,93 @@ exports[`Storyshots Message list 1`] = ` > 10:00 AM - - - +  + + + + + + - - This message has error + + + This message has error + - + - - - -  - - + @@ -17773,65 +17788,163 @@ exports[`Storyshots Message list 1`] = ` } > - - - This message has error too - + diego.mello + + + 10:00 AM - - - - - - - Temp - - + +  + + + + + + + + + This message has error too + + + + + + + + + + + Temp + + @@ -17978,37 +18088,45 @@ exports[`Storyshots Message list 1`] = ` - + - - Temp message + + + Temp message + - + @@ -18059,7 +18177,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -18181,38 +18298,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Message being edited + + + Message being edited + - + @@ -18263,7 +18382,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -18403,7 +18521,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -18543,7 +18660,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -18683,7 +18799,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -18823,7 +18938,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -18963,7 +19077,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -19103,7 +19216,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -19243,7 +19355,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -19383,7 +19494,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -19523,7 +19633,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -19663,7 +19772,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -19803,7 +19911,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -19943,7 +20050,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -20083,7 +20189,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -20223,7 +20328,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -20368,7 +20472,6 @@ exports[`Storyshots Message list 1`] = ` "backgroundColor": "#ddd", }, ], - undefined, ] } > @@ -20490,38 +20593,40 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Message + + + Message + - + @@ -20572,7 +20677,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -20694,105 +20798,107 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Italic with - - asterisks + Italic with - - - or - - + > + + asterisks + + - underscores + or - - - . Bold with - - + > + + underscores + + - asterisks + . Bold with - - - or - - + > + + asterisks + + - underscores + or - - - . - - + > + + underscores + + - Strikethrough + . + + + + Strikethrough + - + @@ -20843,7 +20949,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -20965,182 +21070,184 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - + - - - H1 + + + H1 + - - - - + - - H2 + + + H2 + - - - - + - - H3 + + + H3 + - - - - + - - H4 + + + H4 + - - - - + - - H5 + + + H5 + - - - - + - - H6 + + + H6 + - + @@ -21192,7 +21299,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -21314,74 +21420,76 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Support - - Google + Support - - - - - + > + + Google + + - I\`m an inline-style link + + + + + I\`m an inline-style link + + + + https://google.com - - - https://google.com - + @@ -21432,7 +21540,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -21545,46 +21652,48 @@ exports[`Storyshots Message list 1`] = ` "color": "#9ca2a8", "fontFamily": "System", "fontSize": 12, - "fontWeight": "300", - "lineHeight": 22, - "paddingLeft": 10, - } - } - > - 10:00 AM - - - - - + + + + - + > + + + @@ -21635,7 +21744,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -21757,86 +21865,88 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - - + - - Inline - - code - - - has - - + Inline + + - back-ticks around - - - it. + > + code + + + has + + + back-ticks around + + + it. + - - - Code block + > + Code block - + + @@ -21887,7 +21997,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -22009,48 +22118,50 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - + - - - Quote + + + Quote + - + @@ -22102,7 +22213,6 @@ exports[`Storyshots Message list 1`] = ` "width": "100%", }, undefined, - undefined, ] } > @@ -22224,35 +22334,25 @@ exports[`Storyshots Message list 1`] = ` 10:00 AM - + @@ -22260,183 +22360,195 @@ exports[`Storyshots Message list 1`] = ` style={ Array [ Object { - "flex": 1, - "padding": 5, + "borderBottomWidth": 1, + "borderColor": "#000000", + "flexDirection": "row", }, ] } > - - - First Header + + + First Header + - - - - + - - Second Header + + + Second Header + - + - - - + - - - Content from cell 1 + + + Content from cell 1 + - - - - + - - Content from cell 2 + + + Content from cell 2 + - + - - - - - Content in the first column + + + Content in the first column + - - - - + - - Content in the second column + + + Content in the second column + - + diff --git a/android/app/build.gradle b/android/app/build.gradle index 148c2ad028..f8eb15bf82 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,7 +110,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode VERSIONCODE as Integer - versionName "1.16.1" + versionName "1.17.0" vectorDrawables.useSupportLibrary = true } @@ -174,6 +174,9 @@ android { dependencies { addUnimodulesDependencies() implementation "org.webkit:android-jsc:r241213" + implementation project(':rn-extensions-share') + implementation project(':rn-fetch-blob') + implementation project(':react-native-document-picker') implementation project(':react-native-firebase') implementation project(':react-native-webview') implementation project(':react-native-orientation-locker') @@ -185,7 +188,7 @@ dependencies { }) implementation project(':react-native-gesture-handler') implementation project(':react-native-image-crop-picker') - implementation project(':react-native-i18n') + implementation project(':react-native-localize') implementation project(':react-native-audio') implementation project(":reactnativekeyboardinput") implementation project(':react-native-video') diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e18141cc0f..df31796231 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,8 +5,10 @@ + + + @@ -36,6 +39,19 @@ + + + + + + + diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java index 59a66db8bb..89c788f32b 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java +++ b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.java @@ -3,6 +3,7 @@ import android.app.Application; import com.facebook.react.ReactApplication; +import io.github.elyx0.reactnativedocumentpicker.DocumentPickerPackage; import io.invertase.firebase.RNFirebasePackage; import io.invertase.firebase.fabric.crashlytics.RNFirebaseCrashlyticsPackage; import io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage; @@ -15,7 +16,7 @@ import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; -import com.AlexanderZaytsev.RNI18n.RNI18nPackage; +import com.reactcommunity.rnlocalize.RNLocalizePackage; import com.reactnative.ivpusic.imagepicker.PickerPackage; import com.brentvatne.react.ReactVideoPackage; import com.dylanvann.fastimage.FastImageViewPackage; @@ -33,6 +34,8 @@ import com.actionsheet.ActionSheetPackage; import io.realm.react.RealmReactPackage; import com.swmansion.rnscreens.RNScreensPackage; +import chat.rocket.SharePackage; +import com.RNFetchBlob.RNFetchBlobPackage; import chat.rocket.reactnative.generated.BasePackageList; @@ -60,6 +63,7 @@ public boolean getUseDeveloperSupport() { protected List getPackages() { return Arrays.asList( new MainReactPackage(), + new DocumentPickerPackage(), new RNFirebasePackage(), new RNFirebaseCrashlyticsPackage(), new RNFirebaseAnalyticsPackage(), @@ -67,6 +71,8 @@ protected List getPackages() { new RNCWebViewPackage(), new OrientationPackage(), new SplashScreenReactPackage(), + new SharePackage(), + new RNFetchBlobPackage(), new RNGestureHandlerPackage(), new RNScreensPackage(), new ActionSheetPackage(), @@ -78,7 +84,7 @@ protected List getPackages() { new ReactNativeAudioPackage(), new KeyboardInputPackage(MainApplication.this), new FastImageViewPackage(), - new RNI18nPackage(), + new RNLocalizePackage(), new RNNotificationsPackage(MainApplication.this), new ModuleRegistryAdapter(mModuleRegistryProvider) ); diff --git a/android/app/src/main/java/chat/rocket/reactnative/share/ShareActivity.java b/android/app/src/main/java/chat/rocket/reactnative/share/ShareActivity.java new file mode 100644 index 0000000000..873db3473a --- /dev/null +++ b/android/app/src/main/java/chat/rocket/reactnative/share/ShareActivity.java @@ -0,0 +1,23 @@ +package chat.rocket.reactnative.share; + +import com.facebook.react.ReactActivity; +import com.facebook.react.ReactActivityDelegate; +import com.facebook.react.ReactRootView; +import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView; + +public class ShareActivity extends ReactActivity { + @Override + protected String getMainComponentName() { + return "ShareRocketChatRN"; + } + + @Override + protected ReactActivityDelegate createReactActivityDelegate() { + return new ReactActivityDelegate(this, getMainComponentName()) { + @Override + protected ReactRootView createRootView() { + return new RNGestureHandlerEnabledRootView(ShareActivity.this); + } + }; + } +} \ No newline at end of file diff --git a/android/app/src/main/java/chat/rocket/reactnative/share/ShareApplication.java b/android/app/src/main/java/chat/rocket/reactnative/share/ShareApplication.java new file mode 100644 index 0000000000..94cd8b8610 --- /dev/null +++ b/android/app/src/main/java/chat/rocket/reactnative/share/ShareApplication.java @@ -0,0 +1,38 @@ +package chat.rocket.reactnative.share; + +import chat.rocket.reactnative.BuildConfig; + +import chat.rocket.SharePackage; + +import android.app.Application; + +import com.facebook.react.shell.MainReactPackage; +import com.facebook.react.ReactNativeHost; +import com.facebook.react.ReactApplication; +import com.facebook.react.ReactPackage; + +import java.util.Arrays; +import java.util.List; + + +public class ShareApplication extends Application implements ReactApplication { + private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { + @Override + public boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } + + @Override + protected List getPackages() { + return Arrays.asList( + new MainReactPackage(), + new SharePackage() + ); + } + }; + + @Override + public ReactNativeHost getReactNativeHost() { + return mReactNativeHost; + } +} \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 4066067afc..afec33cb3e 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ Rocket.Chat Experimental - + Rocket.Chat Experimental No Browser Found diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index a555a172ac..28c600d301 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -2,4 +2,19 @@ + + + + diff --git a/android/settings.gradle b/android/settings.gradle index e29a00fb70..73f1ee1504 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -2,6 +2,8 @@ apply from: '../node_modules/react-native-unimodules/gradle.groovy' includeUnimodulesProjects() rootProject.name = 'RocketChatRN' +include ':react-native-document-picker' +project(':react-native-document-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-document-picker/android') include ':react-native-firebase' project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-firebase/android') include ':react-native-webview' @@ -20,8 +22,8 @@ include ':react-native-gesture-handler' project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android') include ':react-native-image-crop-picker' project(':react-native-image-crop-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-crop-picker/android') -include ':react-native-i18n' -project(':react-native-i18n').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-i18n/android') +include ':react-native-localize' +project(':react-native-localize').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-localize/android') include ':react-native-fast-image' project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android') include ':react-native-audio' @@ -36,4 +38,8 @@ include ':realm' project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android') include ':reactnativenotifications' project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android') -include ':app' +include ':rn-fetch-blob' +project(':rn-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/rn-fetch-blob/android') +include ':app', ':rn-extensions-share' +project(':rn-extensions-share').projectDir = new File(rootProject.projectDir, '../node_modules/rn-extensions-share/android') + diff --git a/app.json b/app.json index 181bef9706..7b3a422dca 100644 --- a/app.json +++ b/app.json @@ -1,4 +1,5 @@ { "name": "RocketChatRN", + "share": "ShareRocketChatRN", "displayName": "RocketChatRN" } \ No newline at end of file diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index 45d2886f72..4e466bcaa9 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -14,6 +14,11 @@ export const LOGIN = createRequestTypes('LOGIN', [ 'SET_SERVICES', 'SET_PREFERENCE' ]); +export const SHARE = createRequestTypes('SHARE', [ + 'SELECT_SERVER', + 'SET_USER', + 'SET_SERVER_INFO' +]); export const USER = createRequestTypes('USER', ['SET']); export const ROOMS = createRequestTypes('ROOMS', [ ...defaultTypes, diff --git a/app/actions/share.js b/app/actions/share.js new file mode 100644 index 0000000000..56e8ced9b4 --- /dev/null +++ b/app/actions/share.js @@ -0,0 +1,15 @@ +import { SHARE } from './actionsTypes'; + +export function shareSelectServer(server) { + return { + type: SHARE.SELECT_SERVER, + server + }; +} + +export function shareSetUser(user) { + return { + type: SHARE.SET_USER, + user + }; +} diff --git a/app/constants/settings.js b/app/constants/settings.js index 4e4d993956..cf28f63641 100644 --- a/app/constants/settings.js +++ b/app/constants/settings.js @@ -68,6 +68,12 @@ export default { Threads_enabled: { type: null }, + FileUpload_MediaTypeWhiteList: { + type: 'valueAsString' + }, + FileUpload_MaxFileSize: { + type: 'valueAsNumber' + }, API_Gitlab_URL: { type: 'valueAsString' }, diff --git a/app/containers/HeaderButton.js b/app/containers/HeaderButton.js index b31fb814e8..076dd93805 100644 --- a/app/containers/HeaderButton.js +++ b/app/containers/HeaderButton.js @@ -5,6 +5,7 @@ import HeaderButtons, { HeaderButton, Item } from 'react-navigation-header-butto import { CustomIcon } from '../lib/Icons'; import { isIOS } from '../utils/deviceInfo'; import { COLOR_PRIMARY, COLOR_WHITE } from '../constants/colors'; +import I18n from '../i18n'; const color = isIOS ? COLOR_PRIMARY : COLOR_WHITE; export const headerIconSize = 23; @@ -32,6 +33,15 @@ export const CloseModalButton = React.memo(({ navigation, testID }) => ( )); +export const CloseShareExtensionButton = React.memo(({ onPress, testID }) => ( + + {isIOS + ? + : + } + +)); + export const MoreButton = React.memo(({ onPress, testID }) => ( @@ -50,6 +60,10 @@ CloseModalButton.propTypes = { navigation: PropTypes.object.isRequired, testID: PropTypes.string.isRequired }; +CloseShareExtensionButton.propTypes = { + onPress: PropTypes.func.isRequired, + testID: PropTypes.string.isRequired +}; MoreButton.propTypes = { onPress: PropTypes.func.isRequired, testID: PropTypes.string.isRequired diff --git a/app/containers/MessageActions.js b/app/containers/MessageActions.js index a3f7cbd144..68e6988994 100644 --- a/app/containers/MessageActions.js +++ b/app/containers/MessageActions.js @@ -21,6 +21,8 @@ import I18n from '../i18n'; import log from '../utils/log'; import Navigation from '../lib/Navigation'; import { getMessageTranslation } from './message/utils'; +import { LISTENER } from './Toast'; +import EventEmitter from '../utils/events'; @connect( state => ({ @@ -48,7 +50,6 @@ export default class MessageActions extends React.Component { actionsHide: PropTypes.func.isRequired, room: PropTypes.object.isRequired, actionMessage: PropTypes.object, - toast: PropTypes.element, user: PropTypes.object, deleteRequest: PropTypes.func.isRequired, editInit: PropTypes.func.isRequired, @@ -275,9 +276,9 @@ export default class MessageActions extends React.Component { } handleCopy = async() => { - const { actionMessage, toast } = this.props; + const { actionMessage } = this.props; await Clipboard.setString(actionMessage.msg); - toast.show(I18n.t('Copied_to_clipboard')); + EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') }); } handleShare = async() => { @@ -294,10 +295,10 @@ export default class MessageActions extends React.Component { } handlePermalink = async() => { - const { actionMessage, toast } = this.props; + const { actionMessage } = this.props; const permalink = await this.getPermalink(actionMessage); Clipboard.setString(permalink); - toast.show(I18n.t('Permalink_copied_to_clipboard')); + EventEmitter.emit(LISTENER, { message: I18n.t('Permalink_copied_to_clipboard') }); } handlePin = () => { diff --git a/app/containers/MessageBox/UploadModal.js b/app/containers/MessageBox/UploadModal.js index 6bb29470b5..5c8e5bb54e 100644 --- a/app/containers/MessageBox/UploadModal.js +++ b/app/containers/MessageBox/UploadModal.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import { View, Text, StyleSheet, Image, ScrollView, TouchableHighlight } from 'react-native'; +import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import Modal from 'react-native-modal'; import { responsive } from 'react-native-responsive-ui'; @@ -12,7 +13,11 @@ import Button from '../Button'; import I18n from '../../i18n'; import sharedStyles from '../../views/Styles'; import { isIOS } from '../../utils/deviceInfo'; -import { COLOR_PRIMARY, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE } from '../../constants/colors'; +import { canUploadFile } from '../../utils/media'; +import { + COLOR_PRIMARY, COLOR_BACKGROUND_CONTAINER, COLOR_WHITE, COLOR_DANGER +} from '../../constants/colors'; +import { CustomIcon } from '../../lib/Icons'; const cancelButtonColor = COLOR_BACKGROUND_CONTAINER; @@ -63,18 +68,56 @@ const styles = StyleSheet.create({ androidButtonText: { fontSize: 18, textAlign: 'center' + }, + fileIcon: { + color: COLOR_PRIMARY, + margin: 20, + flex: 1, + textAlign: 'center' + }, + errorIcon: { + color: COLOR_DANGER + }, + fileMime: { + ...sharedStyles.textColorTitle, + ...sharedStyles.textBold, + textAlign: 'center', + fontSize: 20, + marginBottom: 20 + }, + errorContainer: { + margin: 20, + flex: 1, + textAlign: 'center', + justifyContent: 'center', + alignItems: 'center' + }, + video: { + flex: 1, + borderRadius: 4, + height: 150, + backgroundColor: '#1f2329', + marginBottom: 6, + alignItems: 'center', + justifyContent: 'center' } }); @responsive +@connect(state => ({ + FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList, + FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize +})) export default class UploadModal extends Component { static propTypes = { isVisible: PropTypes.bool, file: PropTypes.object, close: PropTypes.func, submit: PropTypes.func, - window: PropTypes.object + window: PropTypes.object, + FileUpload_MediaTypeWhiteList: PropTypes.string, + FileUpload_MaxFileSize: PropTypes.number } state = { @@ -116,12 +159,79 @@ export default class UploadModal extends Component { return false; } + canUploadFile = () => { + const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize, file } = this.props; + if (!(file && file.path)) { + return true; + } + if (file.size > FileUpload_MaxFileSize) { + return false; + } + // if white list is empty, all media types are enabled + if (!FileUpload_MediaTypeWhiteList) { + return true; + } + const allowedMime = FileUpload_MediaTypeWhiteList.split(','); + if (allowedMime.includes(file.mime)) { + return true; + } + const wildCardGlob = '/*'; + const wildCards = allowedMime.filter(item => item.indexOf(wildCardGlob) > 0); + if (wildCards.includes(file.mime.replace(/(\/.*)$/, wildCardGlob))) { + return true; + } + return false; + } + submit = () => { const { file, submit } = this.props; const { name, description } = this.state; submit({ ...file, name, description }); } + renderError = () => { + const { file, FileUpload_MaxFileSize, close } = this.props; + const { window: { width } } = this.props; + const errorMessage = (FileUpload_MaxFileSize < file.size) + ? 'error-file-too-large' + : 'error-invalid-file-type'; + return ( + + + {I18n.t(errorMessage)} + + + + + { file.mime } + + { + (isIOS) + ? ( +