Merge 4.15.0 into master (#2984)

* [FIX] MessagesView title not working (#2294)

* Set title in header of room actions view items

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Remove unneeded spaces

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Set header title on constructor

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Remove unused navigation options

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [TESTS] Stabilise Room Actions test (#2333)

* Stabilise Room Actions test

* Fix Create Room test

* Be more tolerant of slow starting apps in CI

* Be more tolerant of slow running apps in CI

* Switch visibility checks ti stabilise Room Create test in CI

* Move slow simulator readiness waiting to initial navigateToX methods rather than repeatedly in tests without description of purpose

* [CHORE] Update icon names (#2318)

* [CHORE] Move Detox to Github Actions (#2340)

* Initial workflow for iOS detox tests

* Increase timeout

* Parallelise tests and optimise when to build

* Refine GH Actions logic

* Improve Detox App caching

* Upload failed test artifacts

* Rate limiting aware data setup

* Remove detox tests from Circle CI

* Revert "Rate limiting aware data setup"

This reverts commit d115604270f719de775018b9b06e89f2bfdc2dc7.

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [NEW] Push notification data privacy (#2213)

* [WIP] Notification Service

* [WIP] Android push notification privacy

* [WIP] Retry request when it fails (iOS)

* [WIP] Override notification bundle

* [CHORE] Remove unnecessary import

* [WIP] Check notification Type (iOS)

* [WIP] Change to notification endpoint

* eof

* fix unwrap conditional value

* turn run request synchronous

* fix bundle info

* eof

* remove extra tab

* undo unnecessary change

* remove not working code for a while

* fix notification title

* change endpoint and received/sent data

* message-id-only working properly on android

* notification privacy working on ios

* invalidate circleCI yarn cache

* Fix provisioning profiles

* fix notification service version

* fix unwrap nil

* compatibility older servers android

* show received notification when cant fetch content from server

* undo some android changes

* prevent group & reply fallback notifications

* dont show more than one fallback notification by server

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Apply new mention colors (#2351)

* New mention colors

* Increase letterSpacing for mentions

* Refactor

* UnreadBadge

* Add migration

* [FIX] Missing icons (#2353)

* [FIX] Long press gestures not working properly on Android (#2354)

* [FIX] In-app notification showing while in a Jitsi call (#2345)

* Hide in app notification when focused on JitsiMeetView

* Hide notifications from different rooms

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Only run Flipper in debug via MainApplication is debug (#2347)

* Only run Flipper in debug via MainApplication is debug

* ReactNativeFlipper package rename + gradle bump

* [CHORE] Update Flipper to 0.51.0 (#2356)

* Only run Flipper in debug via MainApplication is debug

* ReactNativeFlipper package rename + gradle bump

* Update Flipper to latest 0.51 for Android

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [NEW] Log events from RoomsList, SideDrawer and Profile (#2190)

* Create method to track user event to isolate the logic to improve future refactoring

* Track Onboarding view

* Track NewServer view

* Refactor track method due to firebase already send the current screen

* Track default login and all the oAuth options

* Track default sign up in RegisterView

* Change trackUserEvent signature and update all the files

* Track the remaining login services

* track add server, change server and search

* Track SidebarView and refactor to use react-navigation

* Track profile events and handle exceptions

* Track create channel flux

* Track send message to user via NewMessageView

* Track create direct message flux

* Handle failure of create channel and group in the saga

* Track create discussion flux

* Track navigate to directory and its actions

* Track read, favorite and hide a channel, handling its errors

* Track all channels sorting and grouping

* Resolve requests to improve the importing logs and events

* Remove unused events file

* Leave a bugsnag breadcrumb when logging an event

* Move all logEvent to the top of code block and log remaining fail events

* Move all the non-logic-dependent logEvent to the top of code block

* Improve the logging of sidebar events

* Improve events from onboarding and newserver

* Improve events from login and register view, and log enter with apple

* Improve NewMessageView events

* Improve CreateChannel events

* Improve CreateDiscussion and SelectedUsers create group events

* Improve RoomsList events and log trivial events

* Improve ProfileView events

* Remove single line function body for the sidebarNavigate

* Navigate to Status and AdminPanel View using the defined sidebarNavigate method

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Add missing keys to push get payload (#2358)

* [IMPROVEMENT] Add deep link to Jitsi calls (#2223)

* [WIP] Jitsi Deep Links

* [WIP] Add app links

* save uniqueID servers database

* add serverInfoKey of uniqueID

* search server by call url

* open jitsi deeplink poc

* improve jitsi url

* fix

* improve comment

* add missing android scheme

* handle host not found

* Allow app links to be matched on parseDeepLinking

* Fix push notification of a call

* Minor fix

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] App hanging on splash screen when deep link params are wrong (#2359)

* Add rule when there's no host on the deep link params

* Add fallbackNavigation()

* Fix insecure hosts

* [FIX] More missing icons (#2360)

* [NEW] Log events from Room, Settings and Edit status (#2206)

* Create method to track user event to isolate the logic to improve future refactoring

* Track Onboarding view

* Track NewServer view

* Refactor track method due to firebase already send the current screen

* Track default login and all the oAuth options

* Track default sign up in RegisterView

* Change trackUserEvent signature and update all the files

* Track the remaining login services

* track add server, change server and search

* Track SidebarView and refactor to use react-navigation

* Track profile events and handle exceptions

* Track create channel flux

* Track send message to user via NewMessageView

* Track create direct message flux

* Handle failure of create channel and group in the saga

* Track create discussion flux

* Track navigate to directory and its actions

* Track read, favorite and hide a channel, handling its errors

* Track all channels sorting and grouping

* Resolve requests to improve the importing logs and events

* Remove unused events file

* Remove unused events file

* log proposed Room events

* Log proposed Message actions events

* Log EditStatus proposed events

* Log Settings proposed events

* Leave a bugsnag breadcrumb when logging an event

* Move all logEvent to the top of code block and log remaining fail events

* Move all the non-logic-dependent logEvent to the top of code block

* Move all non-logic and non-data dependent logEvent to the top of code block

* Improve the logging of sidebar events

* Improve events from onboarding and newserver

* Improve events from login and register view, and log enter with apple

* Improve NewMessageView events

* Improve CreateChannel events

* Improve CreateDiscussion and SelectedUsers create group events

* Improve RoomsList events and log trivial events

* Improve ProfileView events

* Remove single line function body for the sidebarNavigate

* Improve SettingsView events

* Log more events from ScreenLockConfigView

* Navigate to Status and AdminPanel View using the defined sidebarNavigate method

* Improve StatusView events

* Improve RoomView events

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Vertically centralize RoomItem when `Store_Last_Message` is disabled (#2363)

* Split RoomItem into container and component

* Refactor RoomItem

* Fix wrong status

* Tests

* Wrapper

* [NEW] Omnichannel inquiry queue (#2352)

* [WIP] Omnichannel queue

* Request inquiry when login

* Show take inquiry queued room

* Queue List as a Screen

* Poc using unread badge

* Prevent navigation to empty list

* Remove chat from queue when taked

* Fix header status on omnichannel preview room

* Fix room actions view to preview queued chat

* Use isOmnichannelPreview and dont show actions when is preview

* Filter queue chats taken by other people

* Fix room info to omnichannel preview room

* Handle show Queue

* Reset inquiry store when change server

* Improve queue logic

* Disable swipe on RoomItem when is a Queue Item

* Add unreadBadge style

* Move unread badge to presentation folder

* Cleanup inquiry reducers

* Move take saga to rocketchat function

* Remove comments

* Add relevant comments

* Subscribe to public stream if is livechat manager or doesnt have departments

* Add pt-br and improve queue empty message

* Fix take when dont have view-livechat-manager permission

* Add missing events

* Create selector for inquiry queue

* Minor fixes

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Wrap logEvent in a try/catch (#2361)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Minor i18n issues (#2335)

* Add new translations to ptBr

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Fix update language in headers

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Use parsed EJSON info on load notification (#2370)

* [NEW] Log remaining events (#2368)

* Change NAVIGATE_TO for GO_TO to reduce event size

* Log RA JitsiMeet events and join / terminate

* Log more RoomView events

* Log slash commands and handle fail

* Log RoomActions events

* Change from GO_TO to just GO

* Log RoomInfoEdit events

* Log InviteUsers and InviteUsersEdit events

* Log AutoTranslate events

* Log NotificationPreferences events

* Log remaining routes from RoomActions

* Log RoomAction toggle block user

* Fix command event

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] WorkspaceView not looking for the correct image path (#2376)

Co-authored-by: Gabriel Henriques <gabriel.henriques@rocket.chat>

* [FIX] Android targeting wrong SDK version (#2375)

* [FIX] Mentions crashing without username (#2374)

* [FIX] Missing delete icon on MessageErrorActions (#2373)

* [FIX] Quote not working on Group DM (#2372)

* [DOCS] Add Whitelabel (#2379)

* Update readme (#2381)

* Update README.md (#2378)

* Update README.md

* Update README.md

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [DOCS] Refactor Readme (#2382)

* Refactoring

* Detail docs

* Contributing

* Update CONTRIBUTING.md

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

* Merge beta into master (#2388)

* Sync develop on master (#275)

* Create LICENSE

* Sync master (#721)

* Merge 1.13.0 into Master (#936)

* fix last messages (#239)

* fix last messages

* Room actions (#231)

* Layout


* Empty starred list


* Favorite room

* Pinned messages

* fix last messages

* fix date on pinned messages

* fix package

* [NEW] OAuth (#241)

* Layout

* tmp

* test iscordova

* Webview redirecting

* Open and Close login actions

* Login services saved on redux

* OAuth Github

* Server regex fix

* OAuth modal style

* - Twitter login
- Remove services from redux
- Open login saga fix

* - Facebook login
- Fixed user agent
- Reactions fix
- Message url unique key fix

* Google login

* Email keyboard removed from messagebox

* - Login buttons refactored
- RoomList header

* Layout improvements

* Meteor login redirect_uri changed

* fix

* Random credentialToken state

* [NEW] Room actions: Mentioned messages and Room Members (#242)

* Mentioned messages

* Starred and pinned actions debounce

* Room members

* Open room on member touch

* [WIP] Improves (#245)

*  hotfix for ios

*  hotfix for ios

* Update config.yml

* Workaround for RN 0.54 on iOS (#246)

* Update iOS to RN 0.54 (#248)

* Update iOS to RN 0.54

* [WIP] Audio message functionality (#247)

* [NEW] Add module react-native-audio

* [WIP] Audio message basic UI

* [NEW] Record audio message

* Use cordova repository to get certificates

* Icon 1024

* [NEW] Room actions: block user, snippet messages, room files and leave room (#250)

* - Block user
- Load room members async
- fixed reactive change of room's read only flag

* Snippet messages

* - Room files
- Dismiss Video component on back button press
- Improvements on Image component

* Improvement on Video component

* Leave room

* Missing message types

* lint

* Reactotron working (#249)

* [NEW] Room info and Room info edit (#254)

* - Block user
- Load room members async
- fixed reactive change of room's read only flag

* Snippet messages

* - Room files
- Dismiss Video component on back button press
- Improvements on Image component

* Improvement on Video component

* Leave room

* Missing message types

* lint

* - Room info (read only)
- Missing message types

* Room info scroll

* - Tap on room header opens room info
- Layout tweaks

* - Room info edit
- iOS Toast fixed

* - Style not implemented actions as disabled

* Edit room permission

* - Save all room settings in a single call
- Implemented roomType and readOnly

* - Allow reacting when room is read only

* Message type added: room_changed_privacy

* Erase room

* Created TextInput and SwitchContainer components for reuse and readability

* - hasPermission method

* - Archive/Unarchive room
- Set Join Code

* Twitter keyboard type on iOS

* Archived room

* reactWhenReadOnly permission on message

* Active users refactored

* User roles

* - Subscribe to roles (in order to get role description info: e.g. 'core-team' to 'Rocket.Chat Team')
- Save roles to realm (for offline access)
- Save roles to redux (and get data from realm on app init)

* Lint

* code style

* password show/hide feature

* fix show/hide password

* password show/hide

* Crashlytics (#258)

* Fabric iOS

* Fabric configured on iOS and Android

* login tracked

* more logs

* fix reaction

* CI fix

* Bug fixes (#261)

* Layout fixes

* RoomsListView's SafeAreaView

* Unhandled promise rejection fix

* Prevent navigation from opening scenes twice

* Create channel fixes

* Create LICENSE

* Beta (#265)

* Fabric iOS

* Fabric configured on iOS and Android

* - react-native-fabric configured

- login tracked

* README updated

* Run scripts from README updated

* README scripts

* get rooms and messages by rest

* user status

* more improves

* more improves

* send pong on timeout

* fix some methods

* more tests

* rest messages

* Room actions (#266)

* Toggle notifications

* Search messages

* Invite users

* Mute/Unmute users in room

* rocket.cat messages

* Room topic layout fixed

* Starred messages loading onEndReached

* Room actions onEndReached

* Unnecessary login request

* Login loading

* Login services fixed

* User presence layout

* ïmproves on room actions view

* Removed unnecessary data from SelectedUsersView

* load few messages on open room, search message improve

* fix loading messages forever

* Removed state from search

* Custom message time format

* secureTextEntry layout

* Reduce android app size

* Roles subscription fix

* Public routes navigation

* fix reconnect

* - New login/register, login, register

* proguard

* Login flux

* App init/restore

* Android layout fixes

* Multiple meteor connection requests fixed

* Nested attachments

* Nested attachments

* fix check status

* New login layout (#269)

* Public routes navigation

* New login/register, login, register

* Multiple meteor connection requests fixed

* Nested attachments

* Button component

* TextInput android layout fixed

* Register fixed

* Thinner close modal button

* Requests /me after login only one time

* Static images moved

* fix reconnect

* fix ddp

* fix custom emoji

* New message layout (#273)

* Grouping messages

* Message layout

* Users typing animation

* Image  attachment layout

* Fabric and image fix (#284)

* Fixed images not showing

* Keyboard libs updated

* Fabric fix and location removed (#286)


* Proguard disabled

* message with list + links fixed (#288)

* Better image cache component (#292)

* react-native-img-cache removed

* Improve list render

* Support <http://link/Text> inside markdown

* Deep linking (#291)

* deep linking

* Basic deep link working

* Deep link routing

* Multiple servers working

* Send user to the room

* Avatar initials and room type icon (#298)

* Deep linking fix and more (#294)

* Fix - Any https link was deep linking to RocketChat

* Keyboard dismiss after add new server

* Room info bug fix

* Opacity animation

* Navigation when adding server fixed

* Throttle for unnecessary render on receiving several messages

* Search inputs without autocorrect and autocapitalize

* Search messages fixed

* Messagebox unnecessary render and spotlight fixed

* react-native-keyboard-input updated

* Lint

* Tests updated

* Update all dependencies (#299)

* Update react-navigation to the latest version 🚀 (#293)

* fix(package): update react-navigation to version 2.0.0

* Code updated to support breaking changes of react-navigation

* Detox tests E2E (#283)

* RoomsListView re-render (#304)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
- [x] Removed unnecessary re-renders on RoomsListView

* [NEW] Broadcast channels (#301)

* Broadcast channels

* e2e tests

* New markdown (#306)

Our current markdown is causing a lot of issues on Android devices, since it wraps everything inside a Text component.
On Android, Text doesn't support View as a child.
This PR adds react-native-markdown-renderer, that uses View as wrapper and may be better.

* Fixed audio recording issues (#310)

* Fix for "java.lang.IllegalArgumentException: unexpected url" (#313)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
User was able to add an invalid instance of Rocket.Chat by pressing submit button instead of "Connect" button.

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* I18n (#312)

* Unread and date separator layout improved (#319)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
- [x] Unread and date separator layout
- [x] "Start of conversation"/"Loading messages" label

![screen shot 2018-05-30 at 18 10 43](https://user-images.githubusercontent.com/804994/40747867-0424964a-6435-11e8-9293-31cc43c110ab.png)
![screen shot 2018-05-30 at 18 09 05](https://user-images.githubusercontent.com/804994/40747868-04484784-6435-11e8-8c31-92e0776276f0.png)



<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* [FIX] iOS Universal links (#318)

* [NEW] Drawer (#322)

* [FIX] invalid user muted value

* Ddp fixes (#324)

* [NEW] User Profile (#323)

* Drawer layout

* Drawer changes

* Profile

* Profile avatar

* Set language

* Tests

* Custom fields

* Readme updated

* fix invalid user muted value

* Fix for "Cannot add a child that doesn't have a YogaNode to a parent without a measure function! (Trying to add a 'RCTVirtualText' to a 'RCTView')"

* Settings/Permissions improvements (#325)

* Changed the way we read RocketChat settings since setting.type won't be returned from server anymore

* Permissions

* Unnecessary action sheet render

* Update gradle and targetSdkVersion (#328)

* Changed the way we read RocketChat settings since setting.type won't be returned from server anymore

* Permissions

* Unnecessary action sheet render

* Update gradle

* Switched testServer to use blob

* RoomsListHeader search fixed

* Runs loadMessagesForRoom only if room has at least 20 rows

* - Logout if user's token expired
- Removed update avatar logic
- Profile dialog border on android

* - Animations disabled
- CircleCI set

* Tests updated

* "eventType argument is required" fix

* Switch push notification lib (#346)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #342 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* Allow x-instance-id and X-Instance-ID header (#354)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #137 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
Some server configurations may send x-instance-id header with different case.

* Image upload improvements (#368)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
- [x] Crop image
- [x] Type image description (like web)
- [x] Show upload progress
- [x] "Try again" in case of error
- [x] Cancel upload while in progress
- [x] [Android] Zoom on photos

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
![image](https://user-images.githubusercontent.com/804994/42526934-a12da304-844d-11e8-8668-f3d69369726a.png)
![image](https://user-images.githubusercontent.com/804994/42527829-297945fe-8450-11e8-9f0e-9e668dd33043.png)

* [NEW] Room Loading(#372)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* [FIX] Empty room name for livechat (#375)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #320 
Closes #209 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* [NEW] Reply preview (#374)

* Updated to React Native 0.56

* Reply Preview

* [FIX] Close websocket (#379)

* Fixed a bug when closing websocket

* removeListener fixed

* [I18N] Russian translation (#381)

[I18N] Russian translation file

* [NEW] Icon (#383)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
![image](https://user-images.githubusercontent.com/804994/43228416-d8af49d6-9037-11e8-8830-a1803932c7fd.png)

* [FIX] Android 8 notifications (#382)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #380 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* Added CocoaPods to manage react-native-image-crop-picker (#373)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
react-native-image-crop-picker raised an error when uploading to TestFlight.
The lib highly recommends CocoaPods for production builds.

* Added single-server to readme (#390)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #386 
Closes #295 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* Improve RoomsList render time (#384)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
- [x] Added FlatList.getItemLayout() to improve list render time
- [x] Some texts were breaking lines at sidebar
- [x] Removed onPress from links at RoomsListView
- [x] Added eslint rule to prevent unused styles
- [x] Fixed auto focus bug at CreateChannel and NewServer
- [x] Fix change server bug
- [x] Fixed a bug when resuming in ListServer
- [x] I18n fixed
- [x] Fixed a bug on actionsheet ref not being created
- [x] Reply wasn't showing on Android
- [x] Use Notification.Builder.setColor/getColor only after Android SDK 23
- [x] Listen to app state only when inside app
- [x] Switched register push token position in order to improve login performance
- [x] When deep link changes server, it doesn't refresh rooms list
- [x] Added SafeAreaView in all views to improve iPhone X experience
- [x] Subpath regex #388

* [NEW] Empty room background (#412)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #398 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
![aug-09-2018 11-35-32](https://user-images.githubusercontent.com/804994/43906080-cbfadf92-9bc8-11e8-9ac9-44f43d3af023.gif)
![aug-09-2018 11-35-16](https://user-images.githubusercontent.com/804994/43906082-cc19411c-9bc8-11e8-9892-c65c86951a91.gif)
![image](https://user-images.githubusercontent.com/804994/43911366-ad830cd0-9bd5-11e8-8913-6a7e87a2206c.png)

* Add roadmap (#406)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #45 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* [NEW] Onboarding (#407)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #392 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
![aug-07-2018 17-03-50](https://user-images.githubusercontent.com/804994/43799447-f62074dc-9a63-11e8-8aac-bf2c4c5a8a2b.gif)
![aug-07-2018 17-03-35](https://user-images.githubusercontent.com/804994/43799446-f5f84a70-9a63-11e8-8947-265113ae9bf4.gif)
![aug-07-2018 17-03-13](https://user-images.githubusercontent.com/804994/43799445-f5d70ee6-9a63-11e8-94a9-f49c7d69fbba.gif)

* [NEW] Updated Logo on Splash screen (#409)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #399 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
![aug-07-2018 17-39-44](https://user-images.githubusercontent.com/804994/43801415-739a0cca-9a69-11e8-8bec-d65f751e6a28.gif)
![aug-07-2018 17-31-12](https://user-images.githubusercontent.com/804994/43801416-73d19bd6-9a69-11e8-90ac-bbc7ddeed938.gif)

* [FIX] Only single attachment rendered (#417)

* [NEW] Rooms list layout (#413)

* RoomsListView layout

* Rooms list layout

* Sort component

* Header icons

* Default header colors

* Add server dropdown

* Close sort dropdown if server dropdown will open

* UserItem

* Room type icon

* Search working

* Tests updated

* Android layout

* Using realm queries instead of array iterates

* Animation duration

* Fixed render bug

* [NEW] Create channel layout (#420)

* RoomsListView layout

* Rooms list layout

* Sort component

* Header icons

* Default header colors

* Add server dropdown

* Close sort dropdown if server dropdown will open

* UserItem

* Room type icon

* Search working

* Tests updated

* Android layout

* Using realm queries instead of array iterates

* Animation duration

* Fixed render bug

* - NewMessageView
- backButtonTitle always empty
- SearchBox created

* New create channel layout

* Search refactored

* loginSuccess dismiss modal

* Tests working

* [FIX] Open unsupported videos on browser (#422)

* 1.1

* Sort/group rooms local only (#425)

* Update android api from ci

* Sort local only

* [FIX] Missing current server (#427)

* server.current removed

* Increased area of touch on header

* Hide search when sort dropdown is tapped

* default server icon url

* 1.1.1

* [NEW] Experimental Icon (#430)

* [NEW] Message layout (#426)

* message container/component

* Separator component

* Reply

* Url

* tests updated

* Minor changes

* Audio component

* Broadcast button

* Minor touches

* Reply preview

* Edited

* Minor bug fixes

* - Update roadmap
- Bump version to 1.2

* Onboarding styles fix

* [FIX] Drawer navigation won't refresh chats (#432)

* Avoid errors on Audio/Image/Video (#443)

* Bump version to 1.2.1 (#444)

* Stop supporting Android 4.4 and lower (#447)

* Several fixes for 1.2.1 (#448)

* Fix user.roles

* Better onLongPress handle on messages

* Indicator position

* Fix role undefined in system messages

* Add baseUrl in case of file attachments

* Join room fixed

* RoomView params

* Broadcast fixes

* Add server layout changes

* Use native images

* Subscribe to not joined channels

* Fix alerts without i18n

* Tests updated

* Bump version to 1.2.2 (#449)

* [NEW] Use community JSC for Android (#450)

* [NEW] Use community JSC for Android

* Quick fix on unread chats

* [NEW] Show app version (#454)

* [NEW] Portuguese translation (#452)

* [NEW] Portuguese translation

* Remove servers from sidebar

* Update dependencies (#431)

* Update dependencies

* Lint and test

* Added react-native fork

* rn 57

* Lint and tests updated

* Update xcode on circleci

* Use legacy build system

* Update tests

* Use inline requires (#459)

* Update dependencies

* Lint and test

* Added react-native fork

* rn 57

* Lint and tests updated

* Update xcode on circleci

* Use legacy build system

* Update tests

* Inline requires

* Fix eslint and remove temp gradle

* Unnecessary renders

* Update isNotch and Readme

* Tests updated

* Bump version to 1.3.0 (#461)

* Better touch handling on rooms list (#462)

* Use react-native-gesture-handler at RoomItem

* Fixed info message author

* Edit message render improvement

* Fix ws to http replace

* Bump version to 1.3.1 (#463)

* Composer layout tweaked (#464)

* Composer layout tweaked

* Fix localization error

* Bump version to 1.3.2

* [FIX] Handle deleted messages (#466)

* [FIX] Handle deleted messages

* Fix rest error

* Fix some connection issues

* [FIX] Search rooms (#468)

* Bump version to 1.3.3 (#469)

* Connecting to DDP badge (#471)

* Display custom fields on user info (#476)

* Render custom fields on user info

* renderCustomFields fix

* Display custom fields in user info

* Fix lint error

* [FIX] DDP badge wasn't hiding on fast connections (#477)

* Use Rocket.Chat JS SDK (#481)

* JS SDK

* API working

* Multiple servers

* Bump version to 1.4.0 (#482)

* [FIX] 2FA and LDAP (#488)

* [FIX] Unread rooms group order (#487)

* Use grouping setting on temp messages (#486)

* [FIX] Delete room error (#485)

* Rename to Rocket.Chat Experimental (#483)

* Update dependencies (#484)

* Bump version to 1.4.0 (#482)

* test

* one more test

* Fix build

* Regression: Wait for unmount to delete database after logout (#489)

* Bump version to 1.4.1 (#490)

* Regression: Crash on Android search (#492)

* Bump version to 1.4.2 (#493)

* Update Rocket.Chat.js.SDK (#494)

* Bump version to v1.4.3 (#495)

* [FIX] OAuth (#496)

* Smaller header icons inside the room (#499)

* [FIX] Logout (#497)

* [FIX] Logout

* Removed realm instances on rooms list

* Bump version to 1.4.4 (#498)

* Update navigation library (#501)

* v2

* Working on Android 0.57.3

* Drawer working

* Removing v1 navigator

* - Splash screen
- Icons changed

* Deeplink

* Remove EventEmitter from CreateChannelView

* Android search

* Android notifications

* OAuth

* Fix search props

* Lint and tests fixed

* Fix android build

* Improvements on iPhone X* usage

* Fix detox

* Fix android build

* Room.f added to RoomView.shouldComponentUpdate

* Animations on RoomsListView and RoomView

* Fix topbar buttons on Android

* Bump version to 1.5.0 (#503)

* Check $FABRIC_KEY availability in CircleCI (#506)

* Check $FABRIC_KEY in CircleCI

* Remove config scripts

* Check $FABRIC_KEY availability in CircleCI for iOS (#507)

* [I18n] Add Simplified Chinese(zh-CN) locale (#505)

* [FIX] iOS pop gesture not working properly (#509)

* Check if lastMessage has an attachment and show "User sent an attachment" at RoomsList (#510)

* [FIX] Messages not being loaded properly (#513)

* Fetch avatar initials from server (#512)

* Fix iOS pop gesture and open sidemenu gesture (#511)

* Bump version to 1.5.1 (#516)

* [NEW] Room header layout (#521)

* Clear iOS notification on resume/open (#520)

* [FIX] Flashing avatars on Android after #512 (#519)

* [FIX] App connects to previous server instead of the recent added (#518)

* [FIX] Room view header crashes when destructuring reducer (#523)

* [FIX] Dismiss keyboard on room close (#530)

* [FIX] Composer composer's send icon slowness (#528)

* [WIP] New Authentication layout (#536)

New Authentication layout

* Regression: Resend messages with error (#532)

* DDP Connection badge animation changed (#533)

* [FIX] Upload buttons on Android (#541)

* Bump version to 1.6.0 (#543)

* I18n: Add missing translation of simplified Chinese (#539)

* Update dependencies (#544)

* AndroidManifest changes

* Regression: Deep linking stopped working after react-native-navigation update (#549)

* [FIX] Android stuck on splash screen after hardware back button is pressed (#550)

* [FIX] Android stuck on splash screen after hardware button is pressed

* Fix empty user at asyncstorage

* Remove unused subscribe

* [FIX] x-instance-id header prop is case insensitive (#551)

* Bump version to 1.6.1 (#553)

* [FIX] x-instance-id header prop is case insensitive

* Use Rest API calls (#558)

* Chats: Don't show group header if none of the filters is selected (#560)

* [CHORE] Update Xcode image version on CircleCI (#561)

* Bump version to 1.7.0 (#562)

* [FIX] Load messages on notification tap (#564)

* Use Rest API pt 2 (#568)

* Room files

* Pinned messages

* Starred messages

* Mentioned messages

* Search messages

* Bug fixes

* Profile

* Livechat

* Block/unblock user

* Erase room

* Archive room

* Remove unused method

* Bug fix

* [CHORE] Add hold step on CircleCI before TestFlight (#572)

* [FIX] GET /info to check if it's a valid server instead of x-instance-id (#573)

* Bump version to 1.7.1 (#574)

* Unnecessary re-renders removed (#570)

* shouldComponentUpdate

* Rooms list shouldcomponentupdate

* RoomView shouldComponentUpdate

* Messagebox and Message shouldComponentUpdate

* EmojiPicker shouldComponentUpdate

* RoomActions shouldComponentUpdate

* Room info shouldComponentUpdate

* Update RNN

* Use only one Flatlist if none group filter is selected

* Update fix

* shouldComponentUpdate

* Bug fixes

* ListView changes

* Bug fix

* render list bug fix

* Changes on public channels

* - RoomView saga leak removed
- Join room e2e tests added

* Rest versions

* Method call versions

* Min RocketChat version alert

* Update dependencies (#587)

* [FIX] Better message actions (#567)

* [FIX] Back button press on message actions (#592)

* Bump version to 1.8.0 (#595)

* [FIX] LDAP login (#596)

* Create class to manage navigation (#594)

* Add Navigation class

* Place Drawer.js logic inside of Navigation

* Load less views at startup

* [FIX] v1.8.0 (#599)

* Downgrade react-native-fast-image

* Update iOS permission usage descriptions

* [FIX] Delete upload item

* Update JS SDK version (#602)

* Add Icons class (#611)

Creates Icons class to manage when to load icons from native side or react-native-vector-icons.
It also fixes `react-native run-android` #517

* Updating room indicator (#609)

Shows "Updating..." when requesting rooms from Rest API.

* [FIX] Load avatar on servers that prevent unauthenticated avatar access (#604)

App would show an empty space on servers that require authentication on avatar access

* [FIX] 2FA login in a server with LDAP enabled (#612)

* [FIX] Start loop searching for rooms updates only when connection goes down and SDK has userId (#613)

* Allow to create empty channel (#615)

* [FIX] Reply title should break text (#616)

* Bump version to 1.9.0 (#617)

* [FIX] SDK issues (#621)

* Remove listeners from room
* Properly close connections on change server
* Minor layout change on connecting badge

* [CHORE] Add TestFlight invite and update Readme (#623)

* [FIX] npm -> yarn dependencies migration (#622)

* I18n: Add French (#629)

* [FIX] Remove rooms listener (#630)

* [CHORE] Update issue template (#638)

* I18n: Add German (#641)

* Bump version to 1.10.0 (#644)

* [FIX] Prevent mass is typing dispatchs (#651)

* [FIX] Handle database errors properly (#650)

* [FIX] Change actions labels (#654)

* [FIX] Room members filter (#655)

* [FIX] uploadProgress is not a function (#656)

* [FIX] Slow messagebox (#658)

* Remove drawer (#653)

* Remove drawer (layout needs to be changed in future releases, though)
* Don't navigate outside on logout if there's other logged server
* Update react-native-navigation

* Message button (#660)

* Remove touchable opacity when scrolling messages
* Tap on disable messages closes keyboard
* Unify vibration
* Vibrate only on Android

* [FIX] Fetch rooms date (#662)

* [FIX] Select emoji error (#666)

* Update Realm to 2.24 (#667)

* Update React Native to 0.58.6 (#668)

* [FIX] Fix some language issues in German language (#664)

* New icons (#643)

* New Icons

* Remove unused assets

* Change send icon

* Layout tweaks

* Refactor Status

* Styles changed

* User layout fix

* Separator layout changes

* Sidebar status layout fix

* Fix Message.onLongPress issue

* Fix code markdown
Closes https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/625

* Status lint

* Fix tests

* Navigation debounce

* RoomActions icons

* Space between components

* Group text

* Update tests

* [CHORE] Remove .debug suffix on Android (#681)

* [FIX] Fix null native Messagebox component object (#680)

* Fix null native Messagebox component object

* [iOS] Fix header alignment

* Remove unused files

* Switch to react-navigation (#687)

* Update readme (#714)

* Bump to 1.10.1 (#731)

* [FIX] Deep linking between multiple logged servers (#730)

* Fix handle invisible status (#692)

* I18n: Add Portuguese (Portugal) (#722)

* [FIX] Show ActivityIndicator in RoomMembersView (#686)

* Bump version to 1.11.0 (#761)

* Migrate from GCM to FCM (#760)

* [NEW] Scrollable room name feature (#756)

* [NEW] Scroll down floating button (#735)

* [CHORE] Added Storybook documentation (#757)

* Use FlatList in RoomView (#762)

* [FIX] iOS requiring location permission (#768)

* Room item layout (#771)

* [NEW] Draft message per room (#772)

* [FIX] Add Realm.safeAddListener (#785)

* [CHORE] Remove tvOS target (#779)

* [NEW] Discussions (#696)

* Bump version to 1.12.0 (#804)

* [NEW] Threads (#798)

* RoomsListView improvements (#819)

* [FIX] Giphy not showing (#810)

* [FIX] Apply emojify on empty texts (#824)

* Lock drawer when stack is not on root screen (#825)

* Room item layout (#835)

* [FIX] Threads (#838)

Closes #826
Closes #827
Closes #828
Closes #829
Closes #830
Closes #831
Closes #832
Closes #833

* [FIX] Smaller thread title (#846)

* [FIX] Smaller thread title

* Remove markdown notation from thread title

* On message press debounce

* Align vertical thread title

* [Regression] Search stopped working on Android after LastMessage refactor (#851)

* Load legal pages from web (#849)

* Update fetch permissions api (#850)

* Update custom emojis endpoint (#852)

* Update emoji endpoint

* Use React.memo on Markdown

* Support RC versions lower than 0.75.0

* Realm migration

* Fetch roles from rest api (#853)

* Fetch roles from rest api

* Fix RoomInfoView role get

* Remove roles from redux

* Bump version to 1.13 (#857)

* Active users improvements (#855)

* Remove connection badge (#862)

* Connecting indicator on RoomsListView header

* Connecting indicator on RoomView header

* Remove ConnectionBadge

* Show updating on RoomView load messages

* Update dependencies (#863)

* Minor updates

* Update jsc-android

* Update react-native-modal

* Minor updates

* Update react-native-fast-image

* Minor dev updates

* Few major updates

* Update react-native-keyboard-aware-scroll-view

* Update pods

* Update android-support

* Update tests

* Remove duplicated getRoleDescription function (#866)

* [FIX] Load local URL image (#871)

* [FIX] Toggle/follow thread icon (#867)

* Tweaks on sequential threads messages layout (#858)

* Tweaks on sequential threads messages

* Update tests

* Fix quote

* Prevent from deleting thread start message when positioned inside the thread

* Remove thread listener from RightButtons

* Fix error on thread start parse

* Stop parsing threads on render

* Check replied thread only if necessary

* Fix messages don't displaying

* Fix threads e2e

* RoomsListView.updateState slice

* Stop fetching hidden messages on threads

* Set initialNumToRender to 5

* [FIX] Check if room is mounted before setting state (#864)

* Tweaks on sequential threads messages

* Update tests

* Fix quote

* Prevent from deleting thread start message when positioned inside the thread

* Remove thread listener from RightButtons

* Fix error on thread start parse

* Stop parsing threads on render

* Check replied thread only if necessary

* Fix messages don't displaying

* Fix threads e2e

* RoomsListView.updateState slice

* Stop fetching hidden messages on threads

* Check if RoomView is mounted before rendering

* Refactor navigation events on RoomsListView

* Fix lint

* Fix listener

* [FIX] Typing not getting cleared after popping a room (#873)

* [CHORE] Remove e2e tests from CI (#875)

* [FIX] Remove listeners on RoomView header unmount (#874)

* [RELEASE] Merge beta into master (#1055)

* Bump version to 1.16.0 (#1014)

* [IMPROVEMENT] Share credentials with Rocket.Chat.iOS (#982)

*  Create user table

*  Introduce user table

* 🔥 Remove unused table

*  Add userdefaults to storage data

* 💚 Fix android build

*  Get credentials from iOS native client

* 🔥 Remove unused code

*  Revert sign xcode

* 🐛 Fix first login-logout

* 🎨 Use constants to UserDefaults Keys

* 🐛 Fix clear server-user-info on logout

* 🐛 Fix filter null value

* 🚑 Remove user object in logout

*  Fix get servers from native-client

* 🚑 Fix error on change server

* [FIX] Don't run UserDefaults credentials on Android (#1015)

* 🐛 Fix native credentials (android)

* Fix migration loop

* [IMPROVEMENT] Hide frequently used emoji tab when empty (#792)

* [IMPROVEMENT] Bigger emoji in emoji only messages (#793)

* issue #725: bigger emoji in emoji only message

* issue-725/add storybook for Message/Emoji

* issue-725: update storybook/Message jest snapshot

* comment storybook import

* allow spaces and line breaks in emoji only message

* merge develop

* revert unnecessary spacing

* [FIX] Empty message if contains only a link (#787)

* Fix empty message if contains only a link

* 🐛 Fix empty space

* [IMPROVEMENT] Refactor empty space regex on quote (#1017)

* 🎨 Improve regex to empty space on quote

* 🎨 Improve on regex to empty space on quote

* [NEW] Custom fields on signup (#1013)

* added custom feilds on registration

* added flag as leftIcon and removed lable

* added try and catch

* typo

* [CHORE] Renew provisioning profiles (#1020)

* [NEW] Auto-translate (#1012)

* Update realm

* View original and translate working

* Read AutoTranslate_Enabled setting

* RocketChat.canAutoTranslate()

* AutoTranslateView

* Save language

* Auto-translate switch

* Translate message

* [IMPROVEMENT] Use haptics rather than vibration (#1016)

* Install expo-haptics

* Use expo-haptics rather than RN's Vibration module

* [IMPROVEMENT] Use Rest API for file upload (#1005)

* removed rn-fetch-blob and use native XMLHttpRequest instead

* removed unnessary changes

* fix android bug

* fix android bug

* added tmid support

* fix bug

* fixed isssue with cacel model

* fix problems with audio

* done requested changes

* fix bug with android

* [CHORE] [CI] [TESTS] update detox to make ci pass (#1026)

* feat: update detox to 12.11.3 to make CI pass

* ci: comment all jobs but leave e2e-test job

* commit to rerun IC e2e-test job

* ci: uncomment all CI jobs

* [NEW] Room swipe actions: mark as read/unread, hide, fav (#976)

* added unread and fav feature

* changed the layout

* fix jest

* done requested changes

* added requested changes

* [FIX] Android build (#1027)

* [FIX] Android build

* CircleCI error

* [FIX] iOS share credentials build (#1028)

* [FIX] iOS share credentials build

* Use `hasMigration` as a string

* [CI] Restore cache on CI (#1029)

* feat: add fastlane save\restore cache config; comment not needed jobs;

* install fastlane using 'bundle install'

* install fastlane using 'sudo bundle install'

* uncomment ios build commands

* run set up google services in ios folder

* add working_directory: ios to ios-build steps

* remove 'cd ios' from Fastlane build step

* add save\restore cache for npm modules

* group save_cache steps

* cache fastlane in ios-testflight job

* uncomment previously commented jobs\steps

* fix: add missing colon

* use key for caching: node-modules-{{ checksum ".circleci/config.yml" }}-{{ checksum "yarn.lock" }}

* add names for save\restore steps

* ci: add `default` step with `working_directory: ~/repo` to ios-build job

* return back caching npm: `node-v1-{{ checksum "package.json" }}-{{ arch }}`

* fix: add missing curly braces

* save\restore cache in e2e-test job; remove {{arch}} from cache names

* add names to restore_cache steps in android-build job

* add names to save_cache steps in android-build job

* add names to all save\restore steps; change checksum package.json to yarn.lock

* change `npm` to `NPM` in steps naming

* remove {{ checksum circle ci }} from android-build job and fix naming of steps

* [FIX] Rooms swipes (#1034)

* Regression: on press style feedback

* Action button styles

* Fix animations

* Styles changed

* Update subscription without having to wait for socket

* Calculate width on RoomsListView instead

* [FIX] Decrease bigger emoji size to 30 (#1031)

* [FIX] Append server URL on avatar if necessary (#1038)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Bump version to 1.16.1 (#1045)

* [FIX] Set UserDefaults AppGroup on notification tap (#1047)

* [FIX] Auto-translate messages as they arrive

* Fix favorite button

* [RELEASE] Merge beta into master (#1082)

* Bump version to 1.16.0 (#1014)

* [IMPROVEMENT] Share credentials with Rocket.Chat.iOS (#982)

*  Create user table

*  Introduce user table

* 🔥 Remove unused table

*  Add userdefaults to storage data

* 💚 Fix android build

*  Get credentials from iOS native client

* 🔥 Remove unused code

*  Revert sign xcode

* 🐛 Fix first login-logout

* 🎨 Use constants to UserDefaults Keys

* 🐛 Fix clear server-user-info on logout

* 🐛 Fix filter null value

* 🚑 Remove user object in logout

*  Fix get servers from native-client

* 🚑 Fix error on change server

* [FIX] Don't run UserDefaults credentials on Android (#1015)

* 🐛 Fix native credentials (android)

* Fix migration loop

* [IMPROVEMENT] Hide frequently used emoji tab when empty (#792)

* [IMPROVEMENT] Bigger emoji in emoji only messages (#793)

* issue #725: bigger emoji in emoji only message

* issue-725/add storybook for Message/Emoji

* issue-725: update storybook/Message jest snapshot

* comment storybook import

* allow spaces and line breaks in emoji only message

* merge develop

* revert unnecessary spacing

* [FIX] Empty message if contains only a link (#787)

* Fix empty message if contains only a link

* 🐛 Fix empty space

* [IMPROVEMENT] Refactor empty space regex on quote (#1017)

* 🎨 Improve regex to empty space on quote

* 🎨 Improve on regex to empty space on quote

* [NEW] Custom fields on signup (#1013)

* added custom feilds on registration

* added flag as leftIcon and removed lable

* added try and catch

* typo

* [CHORE] Renew provisioning profiles (#1020)

* [NEW] Auto-translate (#1012)

* Update realm

* View original and translate working

* Read AutoTranslate_Enabled setting

* RocketChat.canAutoTranslate()

* AutoTranslateView

* Save language

* Auto-translate switch

* Translate message

* [IMPROVEMENT] Use haptics rather than vibration (#1016)

* Install expo-haptics

* Use expo-haptics rather than RN's Vibration module

* [IMPROVEMENT] Use Rest API for file upload (#1005)

* removed rn-fetch-blob and use native XMLHttpRequest instead

* removed unnessary changes

* fix android bug

* fix android bug

* added tmid support

* fix bug

* fixed isssue with cacel model

* fix problems with audio

* done requested changes

* fix bug with android

* [CHORE] [CI] [TESTS] update detox to make ci pass (#1026)

* feat: update detox to 12.11.3 to make CI pass

* ci: comment all jobs but leave e2e-test job

* commit to rerun IC e2e-test job

* ci: uncomment all CI jobs

* [NEW] Room swipe actions: mark as read/unread, hide, fav (#976)

* added unread and fav feature

* changed the layout

* fix jest

* done requested changes

* added requested changes

* [FIX] Android build (#1027)

* [FIX] Android build

* CircleCI error

* [FIX] iOS share credentials build (#1028)

* [FIX] iOS share credentials build

* Use `hasMigration` as a string

* [CI] Restore cache on CI (#1029)

* feat: add fastlane save\restore cache config; comment not needed jobs;

* install fastlane using 'bundle install'

* install fastlane using 'sudo bundle install'

* uncomment ios build commands

* run set up google services in ios folder

* add working_directory: ios to ios-build steps

* remove 'cd ios' from Fastlane build step

* add save\restore cache for npm modules

* group save_cache steps

* cache fastlane in ios-testflight job

* uncomment previously commented jobs\steps

* fix: add missing colon

* use key for caching: node-modules-{{ checksum ".circleci/config.yml" }}-{{ checksum "yarn.lock" }}

* add names for save\restore steps

* ci: add `default` step with `working_directory: ~/repo` to ios-build job

* return back caching npm: `node-v1-{{ checksum "package.json" }}-{{ arch }}`

* fix: add missing curly braces

* save\restore cache in e2e-test job; remove {{arch}} from cache names

* add names to restore_cache steps in android-build job

* add names to save_cache steps in android-build job

* add names to all save\restore steps; change checksum package.json to yarn.lock

* change `npm` to `NPM` in steps naming

* remove {{ checksum circle ci }} from android-build job and fix naming of steps

* [FIX] Rooms swipes (#1034)

* Regression: on press style feedback

* Action button styles

* Fix animations

* Styles changed

* Update subscription without having to wait for socket

* Calculate width on RoomsListView instead

* [FIX] Decrease bigger emoji size to 30 (#1031)

* [FIX] Append server URL on avatar if necessary (#1038)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Bump version to 1.16.1 (#1045)

* [FIX] Set UserDefaults AppGroup on notification tap (#1047)

* [FIX] Auto-translate messages as they arrive

* Fix favorite button

* [FIX] Swipe animations (#1044)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Fix favorite button

* [FIX] Auto-translate messages as they arrive (#1049)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* [FIX] Auto-translate messages as they arrive

* [i18n] Add missing de translations (#1040)

* [CHORE] Switch to react-native-localize (#1043)

* Bump version to 1.17.0 (#1057)

* Load views as needed (#1056)

* [IMPROVEMENT] Change "resend" icon position (#1048)

* [NEW] Video support (#801)

* [NEW] File upload (#882)

* [NEW] Share extension (#942)

* [FIX] Share extension CI build (#1060)

* Change bundleID

* Provisioning

* get provisioning profile

* [IMPROVEMENT] Reusable toast (#1065)

* [FIX] Moment locales (#1066)

* [FIX] Share Extension issues (#1064)

* [FIX] Empty white list enables all media types upload (#1077)

* Merge branch 'master' into develop (#1079)

* [FIX] Empty white list enables all media types upload (#1080)

* Create utils to media (canUpload)

* Fix variable name

* [CHORE] Update README (#1081)

* [RELEASE] Merge beta into master (#1088)

* Bump version to 1.16.0 (#1014)

* [IMPROVEMENT] Share credentials with Rocket.Chat.iOS (#982)

*  Create user table

*  Introduce user table

* 🔥 Remove unused table

*  Add userdefaults to storage data

* 💚 Fix android build

*  Get credentials from iOS native client

* 🔥 Remove unused code

*  Revert sign xcode

* 🐛 Fix first login-logout

* 🎨 Use constants to UserDefaults Keys

* 🐛 Fix clear server-user-info on logout

* 🐛 Fix filter null value

* 🚑 Remove user object in logout

*  Fix get servers from native-client

* 🚑 Fix error on change server

* [FIX] Don't run UserDefaults credentials on Android (#1015)

* 🐛 Fix native credentials (android)

* Fix migration loop

* [IMPROVEMENT] Hide frequently used emoji tab when empty (#792)

* [IMPROVEMENT] Bigger emoji in emoji only messages (#793)

* issue #725: bigger emoji in emoji only message

* issue-725/add storybook for Message/Emoji

* issue-725: update storybook/Message jest snapshot

* comment storybook import

* allow spaces and line breaks in emoji only message

* merge develop

* revert unnecessary spacing

* [FIX] Empty message if contains only a link (#787)

* Fix empty message if contains only a link

* 🐛 Fix empty space

* [IMPROVEMENT] Refactor empty space regex on quote (#1017)

* 🎨 Improve regex to empty space on quote

* 🎨 Improve on regex to empty space on quote

* [NEW] Custom fields on signup (#1013)

* added custom feilds on registration

* added flag as leftIcon and removed lable

* added try and catch

* typo

* [CHORE] Renew provisioning profiles (#1020)

* [NEW] Auto-translate (#1012)

* Update realm

* View original and translate working

* Read AutoTranslate_Enabled setting

* RocketChat.canAutoTranslate()

* AutoTranslateView

* Save language

* Auto-translate switch

* Translate message

* [IMPROVEMENT] Use haptics rather than vibration (#1016)

* Install expo-haptics

* Use expo-haptics rather than RN's Vibration module

* [IMPROVEMENT] Use Rest API for file upload (#1005)

* removed rn-fetch-blob and use native XMLHttpRequest instead

* removed unnessary changes

* fix android bug

* fix android bug

* added tmid support

* fix bug

* fixed isssue with cacel model

* fix problems with audio

* done requested changes

* fix bug with android

* [CHORE] [CI] [TESTS] update detox to make ci pass (#1026)

* feat: update detox to 12.11.3 to make CI pass

* ci: comment all jobs but leave e2e-test job

* commit to rerun IC e2e-test job

* ci: uncomment all CI jobs

* [NEW] Room swipe actions: mark as read/unread, hide, fav (#976)

* added unread and fav feature

* changed the layout

* fix jest

* done requested changes

* added requested changes

* [FIX] Android build (#1027)

* [FIX] Android build

* CircleCI error

* [FIX] iOS share credentials build (#1028)

* [FIX] iOS share credentials build

* Use `hasMigration` as a string

* [CI] Restore cache on CI (#1029)

* feat: add fastlane save\restore cache config; comment not needed jobs;

* install fastlane using 'bundle install'

* install fastlane using 'sudo bundle install'

* uncomment ios build commands

* run set up google services in ios folder

* add working_directory: ios to ios-build steps

* remove 'cd ios' from Fastlane build step

* add save\restore cache for npm modules

* group save_cache steps

* cache fastlane in ios-testflight job

* uncomment previously commented jobs\steps

* fix: add missing colon

* use key for caching: node-modules-{{ checksum ".circleci/config.yml" }}-{{ checksum "yarn.lock" }}

* add names for save\restore steps

* ci: add `default` step with `working_directory: ~/repo` to ios-build job

* return back caching npm: `node-v1-{{ checksum "package.json" }}-{{ arch }}`

* fix: add missing curly braces

* save\restore cache in e2e-test job; remove {{arch}} from cache names

* add names to restore_cache steps in android-build job

* add names to save_cache steps in android-build job

* add names to all save\restore steps; change checksum package.json to yarn.lock

* change `npm` to `NPM` in steps naming

* remove {{ checksum circle ci }} from android-build job and fix naming of steps

* [FIX] Rooms swipes (#1034)

* Regression: on press style feedback

* Action button styles

* Fix animations

* Styles changed

* Update subscription without having to wait for socket

* Calculate width on RoomsListView instead

* [FIX] Decrease bigger emoji size to 30 (#1031)

* [FIX] Append server URL on avatar if necessary (#1038)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Bump version to 1.16.1 (#1045)

* [FIX] Set UserDefaults AppGroup on notification tap (#1047)

* [FIX] Auto-translate messages as they arrive

* Fix favorite button

* [FIX] Swipe animations (#1044)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Fix favorite button

* [FIX] Auto-translate messages as they arrive (#1049)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* [FIX] Auto-translate messages as they arrive

* [i18n] Add missing de translations (#1040)

* [CHORE] Switch to react-native-localize (#1043)

* Bump version to 1.17.0 (#1057)

* Load views as needed (#1056)

* [IMPROVEMENT] Change "resend" icon position (#1048)

* [NEW] Video support (#801)

* [NEW] File upload (#882)

* [NEW] Share extension (#942)

* [FIX] Share extension CI build (#1060)

* Change bundleID

* Provisioning

* get provisioning profile

* [IMPROVEMENT] Reusable toast (#1065)

* [FIX] Moment locales (#1066)

* [FIX] Share Extension issues (#1064)

* [FIX] Empty white list enables all media types upload (#1077)

* Merge branch 'master' into develop (#1079)

* [FIX] Empty white list enables all media types upload (#1080)

* Create utils to media (canUpload)

* Fix variable name

* [CHORE] Update README (#1081)

* [FIX] Media share type (#1086)

* [RELEASE] Merge beta into master (#1142)

* [RELEASE] Merge beta into master (#1174)

* [RELEASE] Merge beta into master (#1282)

* Merge beta into master (#1461)

* Merge beta into master (#1637)

* Merge beta into master (#1759)

* Merge beta into master (#1897)

* [FIX] Close SortDropdown on sort select (#1230)

* [FIX] Cancel upload and check failed upload (#1232)

* [FIX] Slash commands not cleaning is typing and not using state (#1233)

* [FIX] Dispatch roomsRequest on app foreground event even if not connected (#1234)

* [CHORE] Update react-native-jitsi-meet (#1235)

* [FIX] Regex on run slash command (#1223)

* Update React Native to 0.61.1 (#1236)

* Update React Native to 0.61.1

* Update patch to SSL Pinning

* Revert storybook

* [CHORE] Update react-native-safe-area-view (#1219)

* [FIX] Try/catch JSON.parse XHR response (#1238)

* [FIX] Change messagebox icon immediate on change text (#1241)

* [FIX] Update last open on message stream received (#1240)

* [FIX] Remove animation from RoomsListView.willFocus (#1239)

* [FIX] Delete message on thread (#1214)

* [REGRESSION] Markdown text (#1242)

* [FIX] Jest (#1243)

* [FIX] Avatar shown when useRealName is activated (#1162)

* Fix avatar when use real name

* Wrong indentation

* [DOCS] Add SECURITY.md (#1244)

* [CHORE] Update react-native-reanimated to 1.3.0 (#1246)

* [FIX] Run credentials migration only once (#1245)

* [CHORE] Update react-native-jitsi-meet to 2.0.1 (#1249)

* [FIX] Messagebox onChangeText issues (#1252)

* Stop ongoing debounces on messagebox unmount

* Immediately change send icon, but keep debouncing others

* Make CustomEmoji stateless function

* Fix mentions keyExtractor

* [FIX] Room subscription issues (#1255)

* [FIX] Reaction press (#1258)

* [FIX] Channel avatars not showing after application unloads (#1264)

* Revert react-native-safe-area-view (#1265)

* [FIX] Remove console on production mode (#1268)

* [FIX] Messages preview issues (#1257)

* [FIX] Select user from native credentials (#1266)

* [FIX] Some issues on preview message (#1271)

* [FIX] Audio player track and thumb not rendering on Android (#1273)

* [FIX] Record audio message throws exception when FileSystem.getInfoAsync is called (#1272)

* [FIX] China shouldn't use CallKit (#1274)

* [FIX] Watermelon batches (#1277)

* Bump version to 1.20.1 (#1285)

* [CHORE] Remove memoize-one (#1284)

* [FIX] End Jitsi call on unmount (#1291)

* [FIX] Allow self-signed certificates (#1310)

* [FIX] Set User-Agent  (#1318)

* Set User-Agent Fetch & Websocket & XHR

* Set User-Agent

* Custom User Agent on fetch/websocket

* Fix names

* Use DeviceInfo

* fix server with subpath (#1322)

* [FIX] Server with https:\\ instead of https:// (#1320)

* [FIX] Server dropdown not closing after changing stack (#1299)

* [FIX] Invalid server version (#1319)

* [IMPROVEMENT] Respect "Hide counter" preference (#1306)

* [FIX] Pass isFocused as a function to Messagebox (#1309)

* [CHORE] Remove icons folder (#1290)

* [CHORE] Refactor RoomItem touchable (#1331)

* [FIX] Unnecessary rerender on RoomItem when status is undefined (#1336)

* [UPDATE DEPS] react-navigation and react-navigation-stack (#1337)

* [FIX] Avatars not loading on share extension when Accounts_AvatarBlockUnauthenticatedAccess is enabled (#1339)

* Bump version to 1.20.2 (#1340)

* [FIX] Remove some unnecessary re-renders on Messagebox (#1341)

* [REGRESSION] Use LayoutAnimation instead of Transition API (#1338)

* [FIX] Remove setState from notifications view causing watermelon object to be updated outside an action (#1342)

* [IMPROVEMENT] Save last message as message when subscription is updated (#1344)

* [UPDATE DEPS] Update RN to 0.61.3 (#1345)

* [DOCS] Update Readme (#1346)

* [CHORE] Remove react-native-scrollable-tab-view fork (#1352)

* [FIX] URL preview (#1360)

* [REGRESSION] Decrease list view memory size (#1361)

* [FIX] Paste (#1350)

* [CHORE] Update gems (#1365)

* Bump version to 1.20.3 (#1366)

* [FIX] Use Ruby 2.4 on TestFlight upload (#1368)

* [FIX] Parse Urls (#1371)

* [FIX] Parse image URL only if it's not empty (#1372)

* [FIX] Load messages issues (#1373)

* Bump version to 1.21.0 (#1376)

* [FIX] Crowd login (#1381)

* [FIX] Clicking user avatar in thread previews crashes app (#1363)

* [IMPROVEMENT] Error messages on connect (#1379)

* [FIX] ProfileView input navigation error when custom fields aren't set (#1383)

* [FIX] Batch server deletion on logout (#1382)

* Bump app to 1.22.0 (#1387)

* [FIX] Server Version (#1392)

* Update patch and minor deps (#1386)

* [FIX] Crash when open thread (#1395)

* Bump version to 1.23.0 (#1394)

* [I18N] Update ru.js (#1384)

* [FIX] CAS building wrong URL (#1362)

* [FIX] Delete messages (#1399)

* [FIX] In-app notification showing wrong content on channels (#1400)

* Bump version to 1.24.0 (#1404)

* [FIX] Prevent server with whitespace (#1402)

* [IMPROVEMENT] Keyboard and content type on login (#1403)

* [FIX] Messages stop loading (#1410)

* [NEW] Tablet support (#1300)

* [IMPROVEMENT] Authentication via deep linking (#1418)

* [IMPROVEMENT] Markdown performance when identifying emoji only content (#1422)

* [FIX] BackHandler remove random failing on development (#1423)

* Bump version to 1.25.0 (#1424)

* [CHORE] Update CI Xcode Image (#1430)

* [FIX] Rooms grouping not working properly (#1435)

* [FIX] Take a video (#1437)

* [NEW] Themes (#1298)

* [FIX] Share extension doesn't reconnect to previous selected server on Android (#1429)

* [FIX] Init local settings on notification tap (#1438)

* Bump version to 1.26.0 (#1450)

* [FIX] Emoji parser not working on Hermes  (#1445)

* [NEW] Enable Hermes (#1446)

* [FIX] Automatic theme repeating (#1457)

* [CHORE] Sync Experimental and Official app versions (#1458)

* [DOCS] Update readme (#1459)

* [FIX] Messages being sent but showing as temp status (#1469)

* [FIX] Missing messages after reconnect (#1470)

* [FIX] Few fixes on themes (#1477)

* [I18N] Missing German translations (#1465)

* Missing German translation

* adding a missing space behind colon

* added a missing space after colon

* and another attempt to finally fix this – got confused by all the branches

* some smaller fixes for the translation

* better wording

* fixed another typo

* [FIX] Crash while displaying the attached image with http on file name (#1401)

* [IMPROVEMENT] Tap app and server version to copy to clipboard (#1425)

* [NEW] Reply notification (#1448)

* [FIX] Incorrect background color login on iPad (#1480)

* [FIX] Prevent multiple tap on send (Share Extension) (#1481)

* [NEW] Image Viewer (#1479)

* [DOCS] Update Readme (#1485)

* [FIX] Jitsi with Hermes Enabled (#1523)

* [FIX] Draft messages not working with themed Messagebox (#1525)

* [FIX] Go to direct message from members list (#1519)

* [FIX] Make SAML wait for idp token instead of creating it on client (#1527)

* [FIX] Server Test Push Notification (#1508)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Update to new server response (#1509)

* [FIX] Insert messages with blank users (#1529)

* Bump version to 4.2.1 (#1530)

* [FIX] Error when normalizing empty messages (#1532)

* [REGRESSION] CAS (#1570)

* Bump version to 4.2.2 (#1571)

* [FIX] Add username block condition to prevent error (#1585)

* Bump version to 4.2.3

* Bump version to 4.2.4

* Bump version to 4.3.0 (#1630)

* [FIX] Channels doesn't load (#1586)

* [FIX] Channels doesn't load

* [FIX] Update roomsUpdatedAt when subscriptions.length is 0

* [FIX] Remove unnecessary changes

* [FIX] Improve the code

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Make SAML to work on Rocket.Chat < 2.3.0 (#1629)

* [NEW] Invite links (#1534)

* [FIX] Set the http-agent to the form that Rocket.Chat requires for logging (#1482)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] "Following thread" and "Unfollowed Thread" is hardcoded and not translated (#1625)

* [FIX] Disable reset button if form didn't changed (#1569)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Header title of RoomInfoView (#1553)

* [I18N] Gallery Permissions DE (#1542)

* [FIX] Not allow to send messages to archived room (#1623)

* [FIX] Profile fields automatically reset (#1502)

* [FIX] Show attachment on ThreadMessagesView (#1493)

* [NEW] Wordpress auth (#1633)

* [CHORE] Add Start Packager script (#1639)

* [CHORE] Update RN to 0.61.5 (#1638)

* [CHORE] Update RN to 0.61.5

* [CHORE] Update react-native patch

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

* Bump version to 4.3.1 (#1641)

* [FIX] Change force logout rule (#1640)

* Bump version to 4.4.0 (#1643)

* [IMPROVEMENT] Use MessagingStyle on Android Notification (#1575)

* [NEW] Request review (#1627)

* [NEW] Pull to refresh RoomView (#1657)

* [FIX] Unsubscribe from room (#1655)

* [FIX] Server with subdirs (#1646)

* [NEW] Clear cache (#1660)

* [IMPROVEMENT] Memoize and batch subscriptions updates (#1642)

* [FIX] Disallow empty sharing (#1664)

* [REGRESSION] Use HTTPS links for sharing and markets protocol for review (#1663)

* [FIX] In some cases, share extension doesn't load images (#1649)

* [i18n] DE translations for new invite function and some minor fixes (#1631)

* [FIX] Remove duplicate jetify step (#1628)

minor: also remove 'cd' calls

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [REGRESSION] Read messages (#1666)

* [i18n] German translations missing (#1670)

* [FIX] Notifications crash on older Android Versions (#1672)

* [i18n] Added Dutch translation (#1676)

* [NEW] Omnichannel Beta (#1674)

* [NEW] Confirm logout/clear cache (#1688)

* [I18N] Add es-ES language  (#1495)

* [NEW] UiKit Beta (#1497)

* [IMPROVEMENT] Use reselect (#1696)

* [FIX] Notification in Android API level less than 24 (#1692)

* [IMPROVEMENT] Send tmid on slash commands and media (#1698)

* [FIX] Unhandled action on UIKit (#1703)

* [NEW] Pull to refresh RoomsList (#1701)

* [IMPROVEMENT] Reset app when language is changed (#1702)

* [FIX] Small fixes on UIKit (#1709)

* [FIX] Spotlight (#1719)

* [CHORE] Update react-native-image-crop-picker (#1712)

* [FIX] Messages Overlapping (Android) and MessageBox Scroll (iOS) (#1720)

* [REGRESSION] Remove @ and # from mention (#1721)

* [NEW] Direct message from user info (#1516)

* [FIX] Delete slash commands (#1723)

* [IMPROVEMENT] Hold URL to copy (#1684)

* [FIX] Different sourcemaps generation for Hermes (#1724)

* [FIX] Different sourcemaps generation for Hermes

* Upload sourcemaps after build

* [REVERT] Show emoji keyboard on Android (#1738)

* [FIX] Stop logging react-native-image-crop-picker (#1745)

* [FIX] Prevent toast ref error (#1744)

* [FIX] Prevent reaction map error (#1743)

* [FIX] Add missing calls to user info (#1741)

* [FIX] Catch room unsubscribe error (#1739)

* [i18n] Missing German keys (#1735)

* [FIX] Missing i18n on MessagesView title (#1733)

* [FIX]  UIKit Modal: Weird behavior on Android Tablet (#1742)

* [i18n] Missing key on German (#1747)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [i18n] Add Italian (#1736)

…

* [CHORE] Improve ISSUE_TEMPLATE.md (#2390)

* Bump version to 4.11.0 (#2392)

* [i18n] Update fr.js (#2380)

* Update fr.js

* Update fr.js

* Update fr.js

* Update fr.js

* Update fr.js

* Update fr.js

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Add to internal lane instead of alpha (#2400)

* [CHORE] Remove Google Services files from repo (#2405)

* Android

* iOS

* [FIX] Fix broken StatusView on tablet (#2407)

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* [FIX] REST for method calls not raising errors (#2408)

* [FIX] REST for Method calls not raising erorrs

* Remove unnecessary lint disable

* [NEW] Encrypt user credentials and preferences (#2247)

* install react-native-mmkv-storage

* wip ios migration

* change all js rn-user-defaults -> react-native-mmkv-storage

* remove all rn-user-defaults native references (iOS)

* android migration from rn-user-defaults to react-native-mmkv-storage

* ios app group accessible mmkv

* handle get errors

* remove access of credentials from legacy native apps

* remove data of user defaults

* remove no longer necessary import

* js mmkv encryption

* run migration only once

* reply from notification android

* fix app group key access at native level ios

* encrypt user credentials using a specific key

* ios encrypt with random key

* use a random key at the first encryption

* encrypt migrated data on js land

* remove unused function

* reply notifications ios should be working

* use fix instanceID

* android ejson retrieve encrypted data

* remove encryption migrated data for a while

* encryption working between app and share extension

* fix patch react-native-notifications

* ssl pinning working using mmkv encrypted data

* improve react-native-notifications

* run encrypt migration data only once

* fix build

* fix patches magic string

* fix mmkv id

* mmkv -> userPreferences

* fix instance id on android migration

* cast our oldest sharedPreferences string into an object

* revert log remove

* create currentServer Rocket.Chat key

* wrap mmkv api class

* change the get logic

* move userPreferences to lib

* move encrypt migrated data to userPreferences class

* check if the new object is new before insert

* invalidate ci yarn cache

* fix sort migration from android shared preferences

* fix splashscreen forever

* invalidate yarn cache

* invalidate yarn cache

* fix patch

* Minor change

* fix android notifications looking for wrong mmkv instance

* Fix some issues on iOS mmkv native access

* Remove unnecessary code

* Fix notification reply and ssl pinning

* WIP NotificationService use MMKV credentials

* Add KeychainGroup

* Notification idOnly get credentials from mmkv

* Some fixes

* Invalidate yarn cache

* Pods

* Use MMKVAppExtension on NotificationService

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Use font icons on login services (#2412)

* Replace font

* Use CustomIcon

* Remove native assets

* [FIX] SharedPreferences data migration (#2413)

* [IMPROVEMENT] Move directory to header (#2414)

* [FIX] Android crashing on receive a notification (#2415)

* [NEW] User notification preferences (#2403)

* Button to preferences view

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Create screen to preferences and listItem to notifications

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Refactoring NotificationPreferencesView

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* List notification preferences

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Adding translations to labels

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* SetUserPreferences api call

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Saving new user preference in API

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Fix lint

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Add in-app notification test

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Fix in app mentions preference

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Improve object in testInAppNotification

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Removing improper options for NotificationpreferencesView

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Adding API version

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Use redux in UserNotificationPrefView

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Remove in app test

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Use components from another view

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Removing verification for testing in-app notifications

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Move to ProfileView

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Verify Enterprise status on Omnichannel (#2399)

* Add enterpriseModules on Redux

* Fetch enterprise modules and put on redux

* hasLicense

* Clear modules

* Hide omnichannel rooms

* Minor refactor

* Hide omnichannel toggle

* Check license on user status

* Apply on search

* lint

* Look for 'livechat-enterprise'

* One module is enough to enable the features

* Unhide omnichannel rooms

* Sort tweaks

* Move omnichannel toggle to RoomsListView

* Remove omnichannel toggle from SettingsView

* Fix toggle

* Ask to enable omnichannel

* Lint

* Fix issues found on review

* [FIX] Change some icons (#2419)

* [FIX] User Preferences (#2418)

* [FIX] User Preferences

* PreferencesView -> UserPreferencesView

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Customize Sign in with Apple button (#2420)

* [Snyk] Security upgrade lodash from 4.17.19 to 4.17.20 (#2416)

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-LODASH-590103

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [NEW] Add to F-Droid (#2171)

* create play and foss build

* update package.json to generate each build

* check1

* requested changes

* initial commit

* Update config.yml

* minor changes

* remove bugsnag from foss build

* remove bugsnag tasks from foss job

* fix stuck screen

* fixes

* update

* fix lint

* finalise 🚀

* requested changes

* share app for fdroid

* update

* use negation for builds

* requested change

* update share app

* fix issues due to latest sync

* add extra line

* fix lint

* update

* update

* fix bugsnag issue

* Update config.yml

* Fix store url

* Foss release instead of debug

* Add hold for foss

* Fix build

* requested changes

* update name and icons

* update

* fix

* Revert "Bump version to 4.11.0 (#2392)"

This reverts commit ea287980d9.

* finalise

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Commit pods after #2171 (#2424)

* [IMPROVEMENT] Add subscription and room events on the same batch queue (#2423)

* [IMPROVEMENT] Add subscription and room events on the same batch queue

* Send both params

* Unused var

* [IMPROVEMENT] Show "Chats in Progress" group (#2425)

* [NEW] Logout from other logged in locations (#2386)

* Logout from other logged in locations

* Add UI feedback for the request result

* Refactor request to use the proper REST API

* Change backgroundColor

* I18n

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] App can't reopening a room in some cases (#2429)

* [FIX] Logout from custom oauth (#2377)

* New field in table of users

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Saving when the user logged in with email and password

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Saving login method info

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Ask for the user to clear cookies

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Fix lint

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Removing loginMethod from redux and add I18n

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Using async/await instead of then/catch

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Fix lint

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Pods

* Added dismissText on showConfirmationAlert

* Fix iOS

* Rename function

* I18n tweaks

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Wrong date and time shown in file section (#2409)

* Adding missing prop to item object

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Resolving the missing date in the files section in a more elegant way

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Using ts attribute always inside of an item object

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Verify useRealName setting on files screen (#2427)

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Apply theme on Directory description (#2428)

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Wrong merge resolution after #2171 (#2431)

* [FIX] Upload to internal looking for the wrong path after #2171 (#2432)

* [FIX] Detox tests (#2433)

* Spotlight issues

* Fix room tests

* Fix roomactions tests

* [FIX] Crashlytics reportError not working after #2171 (#2436)

* [FIX] Logout from custom oauth when using password (#2435)

* [FIX] Logout from custom oauth when using password

* Remove an useless const

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Move toggle and inquiry to Enterprise Edition license (#2426)

* [IMPROVEMENT] Move toggle and inquiry to Enterprise

* Move inquiry stream to ee

* Emit inquiry subscribe

* imports to ee last

* Add readme to ee

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

* [CHORE] App Group path as a iOS constant (#2439)

* [FIX] Chrome debugging

* Remove rn-fetch-blob

* [CHORE] Use Rocket.Chat JS SDK's official repo (#2440)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Disable HTTP for production on Android (#2357)

* Only enable HTTP and user CAs on debug builds and

* Allow User CAs in prod

* Add config on debug

* Add lint

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [NEW] E2E Encryption (#2394)

* Add E2EKey to Subscription Model

* Install react-native-simple-crypto

* Install bytebuffer

* Add translations

* CreateChannel Encrypted toggle

* Request E2E_Enabled setting

* Add some E2E API methods

* POC E2E Encryption

* Garbage remove

* Remove keys cleaner

* Android cast JWK -> PKCS1

* Initialize E2E when Login Success

* Add some translations

* Add e2e property to Message model

* Send Encrypted messages

* (iOS) PKCS1 -> JWK & e2e.setUserPublicAndPrivateKeys

* (Android) PKCS1 -> JWK & e2e.setUserPublicAndPrivateKeys

* Create an encrypted channel

* Fix app crashing on RoomsList

* Create room key

* Set Room E2E Key (Android)

* Edit room encrypted

* Show encrypted icon on messages

* logEvents

* Decrypt pending subscriptions & messages

* Handle user cancel e2e password entry

* E2ESavePasswordView

* Update Snapshot

* Add encrypted props to message on Send

* Thread messages encryption

* E2E -> Encryption

* Share Extension: Share encrypted text

* (POC) Search messages on Encrypted room

* Provide room key to new users

* Request roomKey on stream-notify-room-users

* Add e2eKeyId to Room Model

* (WIP) E2E Encryption Screens

* Remove encryption subscription file

* Move E2E_Enable to Server Model

* Encryption List Banner

* Move Encryption init to Sagas

* Show banner only when enabled

* Use RocketChat/react-native-simple-crypto

* Search on WM only when is an Encrypted channel

* (WIP) Encryption Banner

* Encryption banner

* Patch -> Fork

* Improve send encrypted message

* Update simple-crypto

* Not decrypt already decrypted messages

* Add comments

* Change eslint disable to inline

* Improve code

* Remove comment

* Some fixes

* (WIP) Encryption Screens

* Improve sub find

* Resend an encrypted message

* Fix comment

* Code improvements

* Hide e2e buttons on features if it is not enabled

* InApp notifications of a encrypted room

* Encryption stop logic

* Edit encrypted message

* DB batch on decryptPending

* Encryption ready client

* Comments

* Handle getRoomInstance errors

* Multiple messages decrypt

* Remove unnecessary try/catch

* Fix decrypt all messages history

* Just add a questionmark

* Fix some subscriptions missing decrypt

* Disable request key logic

* Fix unicode emojis

* Fix e2ekey request

* roomId -> subscription

* Decrypt subscription after merge

* E2ERoom -> EncryptionRoom

* Fix infinite loading

* Handle import key errors

* Handle request key errors

* Move e2eRequestRoomKey to Rocket.Chat

* WIP handshake when key should be requested

* Add search messages explanation

* Remove some TODO and update comments

* Improvements

* Dont show message hash to user

* Handle key request & prevent multiple calls

* Request E2EKey on decryptSubscription that doesn't exists on database yet

* Insert decrypted subscription

* Fix crash after login

* Decrypt sub when receive the key

* Decrypt pending messages of a room

* Encrypted as a switch

* Buffer to Base64 URI Safe

* Add a relevant comment

* Prevent import key without a privateKey

* Prevent create a new instance when client is not ready

* Update simple-crypto & remove replace trick

* More comments

* Remove useless comment

* Remove useless try/catch

* I18n all E2E screens

* E2ESavePassword -> E2ESaveYourPassword

* Prevent multiple views on message when is not encrypted

* Fix encryption toggle not working sometimes

* follow some suggestions

* dont rotate icons

* remove unnecessary condition

* remove unreachable event

* create channel comment

* disable no-bitwise rule for entire file

* loadKeys -> persistKeys

* getMasterKey -> generateMasterKey

* explicit difference between E2EKey & e2eKeyId

* roomId -> rid

* group columns

* Remove server selector

* missing log events

* remove comment

* use stored public key

* update simple-crypto & remove base64-js patch

* add some logs

* remove unreachable condition

* log errors

* handle errors on provide key directly on subscription

* Downgrade RocketChat/react-native-simple-crypto

* improve get room instance

* migration of older apps

* check encrypted status before send a message

* wait client ready

* use our own base64-js

* add more jest tests

* explain return

* remove unncessary stop

* thrown error to caller

* remove superfluous checks

* use Encryption property

* change ready state logic

* ready -> establishing

* encryption.room -> encryptionRoom

* EncryptionRoom -> Room

* add documentation

* wait establishing before provide a room key

* remove superfluous condition

* improve error handling logic

* fallback e2ekey set

* remove no longer necessary check

* remove e.g.

* improve getRoomInstance

* import from index

* use batch

* fix a comment

* decrypt tmsg

* dont show hash when message is encrypted

* Fix detox

* Apply suggestions from code review

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Update run-ios and run-android scripts (#2450)

* [IMPROVEMENT] Show errors on server enter (#2449)

* Catching errors

* [IMPROVEMENT] Show errors on server enter

* "Not rc server" instead of "invalid or insecure url" msg

* [NEW] Show server history (#2421)

* Add dropdown

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Adding new table to serverSchema

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Saving if not exists

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* list of visited servers finished

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Fix lint

Signed-off-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* Rename ServerLinks to ServersHistory

* Refactor

* Save username

* Sort servers desc

* ServerInput

* Item

* Refactor

* Layout tweaks

* Layout

* query by text

* Small refactor

* Redirecting to login

* Save username for oauth

* Fix keyboard persist

* Add tests

* Unnecessary yield

* Stop rendering FlatList logic when there's no servers on history

* Dismiss keyboard and autocomplete when tapped outside server TextInput

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [NEW] Toggle analytics events (#2422)

* Create flow to toggle analytics events on memory

* Persist toggle analytics events

* Update crash report to contemplate analytics events

* Minor tweaks

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Local database searches using non-latin characters (#2462)

* [FIX] Local database searches using non-latin characters

* Add tests

* [FIX] Read receipt crashing in some cases (#2464)

* [IMPROVEMENT] Add "Allow_Save_Media_to_Gallery" setting (#2459)

* [IMPROVEMENT] Add "Allow_Save_Media_to_Gallery" setting

* Default true for old servers

* [FIX] Jitsi breaking changes (#2468)

* [FIX] Jitsi breaking changes

* Update yarn cache

* Update WatermelonDB to 0.19.0 (#2469)

* [FIX] Jitsi breaking changes

* Update yarn cache

* Update watermelon to 0.19

* [FIX] SanitizeLikeString util crashes for empty strings (#2471)

* [i18n] Add Traditional Chinese (zh_TW)  (#2465)

* I18n: Add Traditional Chinese language file(zh_TW)

* Minor fixes

* I18n: Add missing translation and fix some weird words

* fix escape char

* Fix minor issues

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [I18n] Improve Simplified Chinese (zh_CN) (#2466)

* I18n: Improve Simplified Chinese(zh_CN) language file

* I18n: Add missing translation

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Simplify i18n files (#2472)

* [FIX] Remove assets from share extension on iOS (#2473)

* [CHORE] Change database location to Experimental Apps (#2483)

* change database location of experimental apps

* fix migration from older versions

* [FIX] WatermelonDB caching Date as String (#2484)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [NEW] E2E Encryption push (Android) (#2481)

* poc push encryption android

* eof

* format code

* react-native-simple-crypto update

* prevent find sub twice

* remove storage and use ejson storage

* invalidate yarn cache

* Bump crypto and fix db path

* Fix google-services path

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Language set by web client (#2488)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [i18n] Improve Chinese translation (zh-TW, zh-CN) (#2486)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [i18n] Add missing italian strings (#2487)

fix some existing ones too

* [NEW] E2E Encryption push (iOS) (#2463)

* link pods to notification service

* push encryption poc

* decrypt room key poc

* read user key from mmkv and cast into a pkcs

* push decrypt poc (iOS)

* expose needed watermelon methods

* watermelon -> database

* indent & simple-crypto update

* string extensions

* storage

* toBase64 -> toData

* remove a forced unwrap

* remove unused import

* database driver

* improvement

* folder structure & watermelon bridge

* more improvement stuff

* watermelon -> database

* reuse database instance

* improvement

* database fix: bypass watermelon cache

* some code improvements

* encryption instances

* start api stuff

* network layer

* improve notification service

* improve folder structure

* watermelon patch

* retry fetch logic

* rocketchat class

* fix try to decrypt without a roomKey

* fallback to original content that is translated

* some fixes to rocketchat logic

* merge develop

* remove unnecessary extension

* [CHORE] Improve reply notification code (iOS)

* undo sign changes

* remove mocked value

* import direct from library

* send message request

* reply notification with encrypted message working properly

* revert apple sign

* fix api onerror

* trick to display sender name on group notifications

* revert data.host change

* fix some multithread issues

* use sendername sent by server

* small improvement

* Bump crypto lib

* Update ios/NotificationService/NotificationService.swift

* add experimental string

* remove trailing slash

* remove trailing slash on reply

* fix decrypt messages

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [REGRESSION] HTTP Basic Auth (#2490)

* [FIX] Logout when install fresh Official and Experimental iOS app (#2493)

* [FIX] Show images in iOS 14 (#2494)

* [DOCS] Add Reactotron (#2498)

* Update about the inspection tool for our app.

Information about the Reactotron tool was missing in the contribution file.

* Update CONTRIBUTING.md

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [REGRESSION] SSL Pinning stopped working after #2449 (#2510)

* [CHORE] Reset yarn cache (#2512)

* [FIX] Fastlane iOS (#2513)

* [IMPROVEMENT] Add F-Droid modules as AdditionalModules (#2530)

* [IMPROVEMENT] Add F-Droid modules as AdditionalModules

* Fix missing import

* [CHORE] Use App Store Connect API Key (#2549)

* [CHORE] Use App Store Connect API Key

* Update bundle

* rollback keychain

* Remove keychain

* Keychain is actually needed

* Update gitignore

* [FIX] Failing iOS build on fork PR (#2558)

* Fix fastlane build for a fork PR

* Change the iOS fastlane command to build_fork

* [FIX] Avatar cache invalidation (#2311)

* [WIP] Avatar cache invalidation

* [WIP] Avatar container

* [IMPROVEMENT] Avatar container

* [CHORE] Improve code

* Allow static image on Avatar

* Fix avatar changing while change username (#1583)

Co-authored-by: Prateek93a <prateek93a@gmail.com>

* Add default props to properly update on Sidebar and ProfileView

* Fix subscribing on the wrong moment

* Storyshots update

* RoomItem using Avatar Component

* use iife to unsubscribe from user

* Use component on avatar container

* RoomItem as a React.Component

* Move servers models to servers folder

* Avatar -> AvatarContainer

* Users indexed fields

* Initialize author and check if u is present

* Not was found -> User not found (turn comments more relevant)

* RoomItemInner -> Wrapper

* Revert Avatar Touchable logic

* Revert responsability of LeftButton on Tablet Mode

* Prevent setState on constructor

* Run avatarURL only when its not static

* Add streams RC Version

* Move entire add user logic to result.success

* Reorder init on RoomItem

* onPress as a class function

* Fix roomItem using same username

* Add avatar Stories

* Fix pick an image from gallery on ProfileView

* get avatar etag on select users of create discussion

* invalidate ci cache

* Fix migration

* Fix sidebar avatar not updating

Co-authored-by: Prateek93a <prateek93a@gmail.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [NEW] Channel avatars (#2504)

* [WIP] Avatar cache invalidation

* [WIP] Avatar container

* [IMPROVEMENT] Avatar container

* [CHORE] Improve code

* Allow static image on Avatar

* Fix avatar changing while change username (#1583)

Co-authored-by: Prateek93a <prateek93a@gmail.com>

* Add default props to properly update on Sidebar and ProfileView

* Fix subscribing on the wrong moment

* Storyshots update

* RoomItem using Avatar Component

* use iife to unsubscribe from user

* Use component on avatar container

* RoomItem as a React.Component

* Move servers models to servers folder

* Avatar -> AvatarContainer

* Users indexed fields

* Initialize author and check if u is present

* Not was found -> User not found (turn comments more relevant)

* RoomItemInner -> Wrapper

* Revert Avatar Touchable logic

* Revert responsability of LeftButton on Tablet Mode

* Prevent setState on constructor

* Run avatarURL only when its not static

* Add streams RC Version

* Move entire add user logic to result.success

* Reorder init on RoomItem

* onPress as a class function

* Fix roomItem using same username

* Add avatar Stories

* Fix pick an image from gallery on ProfileView

* Format Avatar URL to use RoomId.

Co-authored-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* edit room avatar

* invalidate cache of room images

* reinit avatar if something change

* read avatar cache on search

* room avatar changed system message

* add avatar by rid test

* update snapshot

* etag cache on select channel

* reset room avatar

* increase caching to have a better image quality

* fix lgtm warn

* invalidate ci cache

* get avatar etag on select users of create discussion

* invalidate ci cache

* Fix migration

* Fix sidebar avatar not updating

* Remove outdated comment

* Tests

Co-authored-by: Prateek93a <prateek93a@gmail.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>
Co-authored-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>

* [IMPROVEMENT] List Component (#2506)

* List.Item

* section

* Start removing theme as prop

* Remove StatusBar theme prop

* SafeAreaView theme prop

* Minor fixes

* List.Container

* Add translateTitle and translateSubtitle props

* Storybook

* Show action indicator

* Header

* Info

* Theme stories

* FlatList

* DisplayName

* Fix settings

* FlatList tweaks

* ThemeView

* Screen Lock Config

* DefaultBrowserView

* PickerView and User Prefs

* Notification Prefs

* StatusView

* Auto Translate

* InviteUsersEdit

* Visitor

* Minor fixes

* Remove Separator

* Remove iteminfo

* Font scale

* Legal

* Jitsi and e2e

* Block

* search, star, etc

* auto translate and notifications

* RoomInfo

* Refactor RoomActions

* lint

* Remove DisclosureIndicator

* padding horizontal 12

* Detox

* Tests

* Address review comments

* Fix vertical scroll

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

* [FIX] App always sends auth for Avatar requests (#2517)

* [FIX] Sending auth for Avatar requests when not necessary

* fix storybook

* Fix ShareListView not updating avatars

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] iOS uploads always cropping as squares (#2516)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Mentions layout without background (#2559)

* [IMPROVEMENT] Mentions layout without background

* Fix RoomItem

* Fix tests

* [IMPROVEMENT] Support badge number on header buttons (#2566)

* Beginning header buttons refactor

* Add HeaderButtons

* item with title

* Refactor

* Remove lib

* Refactor

* Update snapshot

* Refactor

* Update tests

* Lint

* [NEW] Threads (#2567)

* [IMPROVEMENT] Mentions layout without background

* Fix RoomItem

* Fix tests

* Smaller messagebox

* Messagebox colors tweak

* Beginning header buttons refactor

* Add HeaderButtons

* item with title

* Refactor

* Remove lib

* Refactor

* Update snapshot

* Send to channel on messagebox

* Add tshow

* Add showMessageInMainThread to login.user reducer

* Filter threads on main channel based on user setting

* Send tshow

* Add tunread

* Move unread colors logic away from UnreadBadge component so it can be used on other components

* Export UnreadBadge on index

* Add empty test

* Refactor

* Update tests

* Lint

* Thread unread user and group on RoomItem

* Thread badge working

* Started ThreadMessagesView.Item

* Fix separator

* Reactivity working

* Lint

* custom emojis aren't necessary

* Basic filter layout

* Filtering layout

* Refactor

* apply filter

* DropdownItemHeader

* default all

* few fixes

* No data found

* Fixes list performance issues

* Use locale on date formats

* Fixed minor styles

* Thread badge

* Refactor getBadgeColor

* Fix send to channel background color

* starting search threads

* Fix lint and tests

* Bump to 4.12.0 just for testing :)

* Search input layout

* query

* starting threads header

* fix unnecessary tlm on tmid messages

* Fix thread header

* lint

* Fix thread header on ShareView

* Add e2e tests

* Fix subscriptions sort

* Update stories and minor fixes

* Fix button sizes on Messagebox

* Remove comment

* Unnecessary conditional

* Add showMessageInMainThread to user collection

* Fix thread header

* Fix thread messages not working on tablet

* Reset Messagebox.tshow after sending a message

* Allow to send to channel when replying to a thread from main channel

* Unnecessary theme prop

* Address comments

* Remove re-render

* Fix scroll indicator bug

* Fix style

* Minor i18n fix

* Fix dropdown height

* I18n ptbr

* I18n

* [IMPROVEMENT] Android push notification as a heads-up notification (#2507)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Add `Change Encryption Password` and `Reset E2E Key` (#2542)

* init

* Basic tests passing

* Add SecurityPrivacyView

* List.Item

* section

* Start removing theme as prop

* Remove StatusBar theme prop

* SafeAreaView theme prop

* Minor fixes

* List.Container

* Add translateTitle and translateSubtitle props

* Storybook

* Show action indicator

* Header

* Info

* Theme stories

* FlatList

* DisplayName

* Fix settings

* FlatList tweaks

* ThemeView

* Screen Lock Config

* DefaultBrowserView

* PickerView and User Prefs

* Notification Prefs

* StatusView

* Auto Translate

* InviteUsersEdit

* Visitor

* Minor fixes

* Remove Separator

* Remove iteminfo

* Font scale

* Legal

* Jitsi and e2e

* Block

* search, star, etc

* auto translate and notifications

* RoomInfo

* Refactor RoomActions

* lint

* Remove DisclosureIndicator

* padding horizontal 12

* Detox

* Tests

* SecurityPrivacy

* E2E encryption sec view

* stash

* Reset own key

* Reset key

* Change password

* Hide content

* Small refactor

* Fix tests

* Tests passing

* Change test order

* add pt-br

* Address review comments

* tests

* Missing i18n ptbr

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

* [IMPROVEMENT] Branding update (#2580)

* iOS native icons

* Android native icons

* Foss native icons

* Experimental icon iOS

* Experimental

* Notification icon

* Splash screen

* Splash screen iOS

* Blue notification text

* Fix iOS Launch Screen Icon

* Experimental and foss

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Update Xcode to 12.1.0 (#2592)

* [CHORE] Update Xcode to 12.1.0

* Remove alpha from Xcode App Store Icon

* [IMPROVEMENT] Auto search when text changes in directory textfield (#2547)

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Rooms header overlapping right icons (#2503)

takes into account long names on small screen which led to overlapping title and right buttons on the header bar

* [IMPROVEMENT] Jitsi lean (#2534)

* 2.10.2

* update jitsi sdk

* use our own react-native-jitsi-meet

* use own android jitsi sdk

* remove jsc reference

* use self-builded ios sdk

* update react-native-jitsi-meet

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [i18n] German word fix (#2598)

Report in German means "The Report" not "to report". Therefor "Melden" ist better suited here.

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [i18n] Improve Chinese translation (#2570)

* [FIX] App crashing when notification is received/replied (Android) (#2602)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Update react native CLI to support white labeling with XCode 12 (#2560)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [i18n] Add missing German strings (#2571)

* adding missing German strings

* resolving conflict

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [REGRESSION] Avatars doesn't show up on older servers (< 3.6.0) (#2603)

* [REGRESSION] Avatars doesn't show up on older servers (< 3.6.0)

* fix: snapshots tests failing

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Missing locales in moment helper (#2562)

* [i18n] Add missing Russian strings (#2555)

* Added waiting for network string translate

* [i18n] Add missing russian strings

* Some E2E strings

* [i18n] Add missing russian strings

* Some grammatical changes and translate optimizations

* Add english strings

* Final translate

Co-authored-by: Карлан Антон Андреевич <KarlanAA@global.bcs>
Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] 'Send to channel' when replying as a quote (#2606)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Android notification on Dark Theme using Official main color (#2604)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Storybook not able to import Avatar (#2607)

* [FIX] Storybook not able to import Avatar

* Fix lint

* Mock Date.now

* Fix RU translation

* isLegacy -> serverVersion

* Remove change avatar from room info edit for servers below 3.6

* Mock for storyshots only

* lint

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [BUG] App isn't showing message for PDF/file uploads (#2584)

* Fixed the issue #2531

In app/containers/message/Reply.js added a View Contaier around the
Attachment Touchable and Added a Markdown attribute with msg set to
description of attachment to display the message if any.

* Added the condition to check if File Description Exists

Added an if statement to check if file description exists and if yes
then add a markdown with value msg equal to the description.

Also tested using 'yarn test -u' to add/update the tests.

* Made the requested Changes

Removed the condition to check for attachment description.
Added the `markdown` inside the touchable and wrapped `attachmentContainer` and the `markdown` inside a `<>` component

* Added file not showing message issue code in this branch

* Fixed the mistake in return

* fix

* Add tests

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Header title positioning not changing according to the number of icons (#2608)

* [DOCS] Update Android Supported versions (#2611)

* [i18n] Improve Russian translation (#2609)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] User notification preferences throwing an error when select default Email option (#2615)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] MomentJS crashing on Spanish language (#2616)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] AllowBackup manifest attribute causing unexpected behaviour on login (#2617)

* [FIX] Search messages crashing when show a thread message (#2618)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] F-Droid build for store (#2557)

* [FIX] F-Droid build for store

* Trying to make Override custom push notifications on play build only

* Use play sourceSets

* Change version code

* Fix react-native-config-reader

* [FIX] F-Droid build for store

* Trying to make Override custom push notifications on play build only

* Use play sourceSets

* Change version code

* Fix react-native-config-reader

* Remove react-native-device-info Google dependencies / Use LIBRE_BUILD of react-native-jitsi-meet

* Invalidate CI Cache

* Set specific jitsi-meet-sdk

* Specify 2.10.0-libre

* jitsi-meet using an url based on play build

* update react-native-jitsi-meet

* react-native-device-info foss

* undo some unnecessary changes

* Fix notifications

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

* Merge beta into master (#2621)

* Sync develop on master (#275)

* Create LICENSE

* Sync master (#721)

* Merge 1.13.0 into Master (#936)

* fix last messages (#239)

* fix last messages

* Room actions (#231)

* Layout


* Empty starred list


* Favorite room

* Pinned messages

* fix last messages

* fix date on pinned messages

* fix package

* [NEW] OAuth (#241)

* Layout

* tmp

* test iscordova

* Webview redirecting

* Open and Close login actions

* Login services saved on redux

* OAuth Github

* Server regex fix

* OAuth modal style

* - Twitter login
- Remove services from redux
- Open login saga fix

* - Facebook login
- Fixed user agent
- Reactions fix
- Message url unique key fix

* Google login

* Email keyboard removed from messagebox

* - Login buttons refactored
- RoomList header

* Layout improvements

* Meteor login redirect_uri changed

* fix

* Random credentialToken state

* [NEW] Room actions: Mentioned messages and Room Members (#242)

* Mentioned messages

* Starred and pinned actions debounce

* Room members

* Open room on member touch

* [WIP] Improves (#245)

*  hotfix for ios

*  hotfix for ios

* Update config.yml

* Workaround for RN 0.54 on iOS (#246)

* Update iOS to RN 0.54 (#248)

* Update iOS to RN 0.54

* [WIP] Audio message functionality (#247)

* [NEW] Add module react-native-audio

* [WIP] Audio message basic UI

* [NEW] Record audio message

* Use cordova repository to get certificates

* Icon 1024

* [NEW] Room actions: block user, snippet messages, room files and leave room (#250)

* - Block user
- Load room members async
- fixed reactive change of room's read only flag

* Snippet messages

* - Room files
- Dismiss Video component on back button press
- Improvements on Image component

* Improvement on Video component

* Leave room

* Missing message types

* lint

* Reactotron working (#249)

* [NEW] Room info and Room info edit (#254)

* - Block user
- Load room members async
- fixed reactive change of room's read only flag

* Snippet messages

* - Room files
- Dismiss Video component on back button press
- Improvements on Image component

* Improvement on Video component

* Leave room

* Missing message types

* lint

* - Room info (read only)
- Missing message types

* Room info scroll

* - Tap on room header opens room info
- Layout tweaks

* - Room info edit
- iOS Toast fixed

* - Style not implemented actions as disabled

* Edit room permission

* - Save all room settings in a single call
- Implemented roomType and readOnly

* - Allow reacting when room is read only

* Message type added: room_changed_privacy

* Erase room

* Created TextInput and SwitchContainer components for reuse and readability

* - hasPermission method

* - Archive/Unarchive room
- Set Join Code

* Twitter keyboard type on iOS

* Archived room

* reactWhenReadOnly permission on message

* Active users refactored

* User roles

* - Subscribe to roles (in order to get role description info: e.g. 'core-team' to 'Rocket.Chat Team')
- Save roles to realm (for offline access)
- Save roles to redux (and get data from realm on app init)

* Lint

* code style

* password show/hide feature

* fix show/hide password

* password show/hide

* Crashlytics (#258)

* Fabric iOS

* Fabric configured on iOS and Android

* login tracked

* more logs

* fix reaction

* CI fix

* Bug fixes (#261)

* Layout fixes

* RoomsListView's SafeAreaView

* Unhandled promise rejection fix

* Prevent navigation from opening scenes twice

* Create channel fixes

* Create LICENSE

* Beta (#265)

* Fabric iOS

* Fabric configured on iOS and Android

* - react-native-fabric configured

- login tracked

* README updated

* Run scripts from README updated

* README scripts

* get rooms and messages by rest

* user status

* more improves

* more improves

* send pong on timeout

* fix some methods

* more tests

* rest messages

* Room actions (#266)

* Toggle notifications

* Search messages

* Invite users

* Mute/Unmute users in room

* rocket.cat messages

* Room topic layout fixed

* Starred messages loading onEndReached

* Room actions onEndReached

* Unnecessary login request

* Login loading

* Login services fixed

* User presence layout

* ïmproves on room actions view

* Removed unnecessary data from SelectedUsersView

* load few messages on open room, search message improve

* fix loading messages forever

* Removed state from search

* Custom message time format

* secureTextEntry layout

* Reduce android app size

* Roles subscription fix

* Public routes navigation

* fix reconnect

* - New login/register, login, register

* proguard

* Login flux

* App init/restore

* Android layout fixes

* Multiple meteor connection requests fixed

* Nested attachments

* Nested attachments

* fix check status

* New login layout (#269)

* Public routes navigation

* New login/register, login, register

* Multiple meteor connection requests fixed

* Nested attachments

* Button component

* TextInput android layout fixed

* Register fixed

* Thinner close modal button

* Requests /me after login only one time

* Static images moved

* fix reconnect

* fix ddp

* fix custom emoji

* New message layout (#273)

* Grouping messages

* Message layout

* Users typing animation

* Image  attachment layout

* Fabric and image fix (#284)

* Fixed images not showing

* Keyboard libs updated

* Fabric fix and location removed (#286)


* Proguard disabled

* message with list + links fixed (#288)

* Better image cache component (#292)

* react-native-img-cache removed

* Improve list render

* Support <http://link/Text> inside markdown

* Deep linking (#291)

* deep linking

* Basic deep link working

* Deep link routing

* Multiple servers working

* Send user to the room

* Avatar initials and room type icon (#298)

* Deep linking fix and more (#294)

* Fix - Any https link was deep linking to RocketChat

* Keyboard dismiss after add new server

* Room info bug fix

* Opacity animation

* Navigation when adding server fixed

* Throttle for unnecessary render on receiving several messages

* Search inputs without autocorrect and autocapitalize

* Search messages fixed

* Messagebox unnecessary render and spotlight fixed

* react-native-keyboard-input updated

* Lint

* Tests updated

* Update all dependencies (#299)

* Update react-navigation to the latest version 🚀 (#293)

* fix(package): update react-navigation to version 2.0.0

* Code updated to support breaking changes of react-navigation

* Detox tests E2E (#283)

* RoomsListView re-render (#304)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
- [x] Removed unnecessary re-renders on RoomsListView

* [NEW] Broadcast channels (#301)

* Broadcast channels

* e2e tests

* New markdown (#306)

Our current markdown is causing a lot of issues on Android devices, since it wraps everything inside a Text component.
On Android, Text doesn't support View as a child.
This PR adds react-native-markdown-renderer, that uses View as wrapper and may be better.

* Fixed audio recording issues (#310)

* Fix for "java.lang.IllegalArgumentException: unexpected url" (#313)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
User was able to add an invalid instance of Rocket.Chat by pressing submit button instead of "Connect" button.

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* I18n (#312)

* Unread and date separator layout improved (#319)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
- [x] Unread and date separator layout
- [x] "Start of conversation"/"Loading messages" label

![screen shot 2018-05-30 at 18 10 43](https://user-images.githubusercontent.com/804994/40747867-0424964a-6435-11e8-9293-31cc43c110ab.png)
![screen shot 2018-05-30 at 18 09 05](https://user-images.githubusercontent.com/804994/40747868-04484784-6435-11e8-8c31-92e0776276f0.png)



<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* [FIX] iOS Universal links (#318)

* [NEW] Drawer (#322)

* [FIX] invalid user muted value

* Ddp fixes (#324)

* [NEW] User Profile (#323)

* Drawer layout

* Drawer changes

* Profile

* Profile avatar

* Set language

* Tests

* Custom fields

* Readme updated

* fix invalid user muted value

* Fix for "Cannot add a child that doesn't have a YogaNode to a parent without a measure function! (Trying to add a 'RCTVirtualText' to a 'RCTView')"

* Settings/Permissions improvements (#325)

* Changed the way we read RocketChat settings since setting.type won't be returned from server anymore

* Permissions

* Unnecessary action sheet render

* Update gradle and targetSdkVersion (#328)

* Changed the way we read RocketChat settings since setting.type won't be returned from server anymore

* Permissions

* Unnecessary action sheet render

* Update gradle

* Switched testServer to use blob

* RoomsListHeader search fixed

* Runs loadMessagesForRoom only if room has at least 20 rows

* - Logout if user's token expired
- Removed update avatar logic
- Profile dialog border on android

* - Animations disabled
- CircleCI set

* Tests updated

* "eventType argument is required" fix

* Switch push notification lib (#346)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #342 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* Allow x-instance-id and X-Instance-ID header (#354)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #137 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
Some server configurations may send x-instance-id header with different case.

* Image upload improvements (#368)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
- [x] Crop image
- [x] Type image description (like web)
- [x] Show upload progress
- [x] "Try again" in case of error
- [x] Cancel upload while in progress
- [x] [Android] Zoom on photos

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
![image](https://user-images.githubusercontent.com/804994/42526934-a12da304-844d-11e8-8668-f3d69369726a.png)
![image](https://user-images.githubusercontent.com/804994/42527829-297945fe-8450-11e8-9f0e-9e668dd33043.png)

* [NEW] Room Loading(#372)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* [FIX] Empty room name for livechat (#375)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #320 
Closes #209 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* [NEW] Reply preview (#374)

* Updated to React Native 0.56

* Reply Preview

* [FIX] Close websocket (#379)

* Fixed a bug when closing websocket

* removeListener fixed

* [I18N] Russian translation (#381)

[I18N] Russian translation file

* [NEW] Icon (#383)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
![image](https://user-images.githubusercontent.com/804994/43228416-d8af49d6-9037-11e8-8830-a1803932c7fd.png)

* [FIX] Android 8 notifications (#382)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #380 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* Added CocoaPods to manage react-native-image-crop-picker (#373)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
react-native-image-crop-picker raised an error when uploading to TestFlight.
The lib highly recommends CocoaPods for production builds.

* Added single-server to readme (#390)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #386 
Closes #295 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* Improve RoomsList render time (#384)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
- [x] Added FlatList.getItemLayout() to improve list render time
- [x] Some texts were breaking lines at sidebar
- [x] Removed onPress from links at RoomsListView
- [x] Added eslint rule to prevent unused styles
- [x] Fixed auto focus bug at CreateChannel and NewServer
- [x] Fix change server bug
- [x] Fixed a bug when resuming in ListServer
- [x] I18n fixed
- [x] Fixed a bug on actionsheet ref not being created
- [x] Reply wasn't showing on Android
- [x] Use Notification.Builder.setColor/getColor only after Android SDK 23
- [x] Listen to app state only when inside app
- [x] Switched register push token position in order to improve login performance
- [x] When deep link changes server, it doesn't refresh rooms list
- [x] Added SafeAreaView in all views to improve iPhone X experience
- [x] Subpath regex #388

* [NEW] Empty room background (#412)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #398 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
![aug-09-2018 11-35-32](https://user-images.githubusercontent.com/804994/43906080-cbfadf92-9bc8-11e8-9ac9-44f43d3af023.gif)
![aug-09-2018 11-35-16](https://user-images.githubusercontent.com/804994/43906082-cc19411c-9bc8-11e8-9892-c65c86951a91.gif)
![image](https://user-images.githubusercontent.com/804994/43911366-ad830cd0-9bd5-11e8-8913-6a7e87a2206c.png)

* Add roadmap (#406)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #45 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->

* [NEW] Onboarding (#407)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #392 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
![aug-07-2018 17-03-50](https://user-images.githubusercontent.com/804994/43799447-f62074dc-9a63-11e8-8aac-bf2c4c5a8a2b.gif)
![aug-07-2018 17-03-35](https://user-images.githubusercontent.com/804994/43799446-f5f84a70-9a63-11e8-8947-265113ae9bf4.gif)
![aug-07-2018 17-03-13](https://user-images.githubusercontent.com/804994/43799445-f5d70ee6-9a63-11e8-94a9-f49c7d69fbba.gif)

* [NEW] Updated Logo on Splash screen (#409)

<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative

<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #399 

<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
![aug-07-2018 17-39-44](https://user-images.githubusercontent.com/804994/43801415-739a0cca-9a69-11e8-8bec-d65f751e6a28.gif)
![aug-07-2018 17-31-12](https://user-images.githubusercontent.com/804994/43801416-73d19bd6-9a69-11e8-90ac-bbc7ddeed938.gif)

* [FIX] Only single attachment rendered (#417)

* [NEW] Rooms list layout (#413)

* RoomsListView layout

* Rooms list layout

* Sort component

* Header icons

* Default header colors

* Add server dropdown

* Close sort dropdown if server dropdown will open

* UserItem

* Room type icon

* Search working

* Tests updated

* Android layout

* Using realm queries instead of array iterates

* Animation duration

* Fixed render bug

* [NEW] Create channel layout (#420)

* RoomsListView layout

* Rooms list layout

* Sort component

* Header icons

* Default header colors

* Add server dropdown

* Close sort dropdown if server dropdown will open

* UserItem

* Room type icon

* Search working

* Tests updated

* Android layout

* Using realm queries instead of array iterates

* Animation duration

* Fixed render bug

* - NewMessageView
- backButtonTitle always empty
- SearchBox created

* New create channel layout

* Search refactored

* loginSuccess dismiss modal

* Tests working

* [FIX] Open unsupported videos on browser (#422)

* 1.1

* Sort/group rooms local only (#425)

* Update android api from ci

* Sort local only

* [FIX] Missing current server (#427)

* server.current removed

* Increased area of touch on header

* Hide search when sort dropdown is tapped

* default server icon url

* 1.1.1

* [NEW] Experimental Icon (#430)

* [NEW] Message layout (#426)

* message container/component

* Separator component

* Reply

* Url

* tests updated

* Minor changes

* Audio component

* Broadcast button

* Minor touches

* Reply preview

* Edited

* Minor bug fixes

* - Update roadmap
- Bump version to 1.2

* Onboarding styles fix

* [FIX] Drawer navigation won't refresh chats (#432)

* Avoid errors on Audio/Image/Video (#443)

* Bump version to 1.2.1 (#444)

* Stop supporting Android 4.4 and lower (#447)

* Several fixes for 1.2.1 (#448)

* Fix user.roles

* Better onLongPress handle on messages

* Indicator position

* Fix role undefined in system messages

* Add baseUrl in case of file attachments

* Join room fixed

* RoomView params

* Broadcast fixes

* Add server layout changes

* Use native images

* Subscribe to not joined channels

* Fix alerts without i18n

* Tests updated

* Bump version to 1.2.2 (#449)

* [NEW] Use community JSC for Android (#450)

* [NEW] Use community JSC for Android

* Quick fix on unread chats

* [NEW] Show app version (#454)

* [NEW] Portuguese translation (#452)

* [NEW] Portuguese translation

* Remove servers from sidebar

* Update dependencies (#431)

* Update dependencies

* Lint and test

* Added react-native fork

* rn 57

* Lint and tests updated

* Update xcode on circleci

* Use legacy build system

* Update tests

* Use inline requires (#459)

* Update dependencies

* Lint and test

* Added react-native fork

* rn 57

* Lint and tests updated

* Update xcode on circleci

* Use legacy build system

* Update tests

* Inline requires

* Fix eslint and remove temp gradle

* Unnecessary renders

* Update isNotch and Readme

* Tests updated

* Bump version to 1.3.0 (#461)

* Better touch handling on rooms list (#462)

* Use react-native-gesture-handler at RoomItem

* Fixed info message author

* Edit message render improvement

* Fix ws to http replace

* Bump version to 1.3.1 (#463)

* Composer layout tweaked (#464)

* Composer layout tweaked

* Fix localization error

* Bump version to 1.3.2

* [FIX] Handle deleted messages (#466)

* [FIX] Handle deleted messages

* Fix rest error

* Fix some connection issues

* [FIX] Search rooms (#468)

* Bump version to 1.3.3 (#469)

* Connecting to DDP badge (#471)

* Display custom fields on user info (#476)

* Render custom fields on user info

* renderCustomFields fix

* Display custom fields in user info

* Fix lint error

* [FIX] DDP badge wasn't hiding on fast connections (#477)

* Use Rocket.Chat JS SDK (#481)

* JS SDK

* API working

* Multiple servers

* Bump version to 1.4.0 (#482)

* [FIX] 2FA and LDAP (#488)

* [FIX] Unread rooms group order (#487)

* Use grouping setting on temp messages (#486)

* [FIX] Delete room error (#485)

* Rename to Rocket.Chat Experimental (#483)

* Update dependencies (#484)

* Bump version to 1.4.0 (#482)

* test

* one more test

* Fix build

* Regression: Wait for unmount to delete database after logout (#489)

* Bump version to 1.4.1 (#490)

* Regression: Crash on Android search (#492)

* Bump version to 1.4.2 (#493)

* Update Rocket.Chat.js.SDK (#494)

* Bump version to v1.4.3 (#495)

* [FIX] OAuth (#496)

* Smaller header icons inside the room (#499)

* [FIX] Logout (#497)

* [FIX] Logout

* Removed realm instances on rooms list

* Bump version to 1.4.4 (#498)

* Update navigation library (#501)

* v2

* Working on Android 0.57.3

* Drawer working

* Removing v1 navigator

* - Splash screen
- Icons changed

* Deeplink

* Remove EventEmitter from CreateChannelView

* Android search

* Android notifications

* OAuth

* Fix search props

* Lint and tests fixed

* Fix android build

* Improvements on iPhone X* usage

* Fix detox

* Fix android build

* Room.f added to RoomView.shouldComponentUpdate

* Animations on RoomsListView and RoomView

* Fix topbar buttons on Android

* Bump version to 1.5.0 (#503)

* Check $FABRIC_KEY availability in CircleCI (#506)

* Check $FABRIC_KEY in CircleCI

* Remove config scripts

* Check $FABRIC_KEY availability in CircleCI for iOS (#507)

* [I18n] Add Simplified Chinese(zh-CN) locale (#505)

* [FIX] iOS pop gesture not working properly (#509)

* Check if lastMessage has an attachment and show "User sent an attachment" at RoomsList (#510)

* [FIX] Messages not being loaded properly (#513)

* Fetch avatar initials from server (#512)

* Fix iOS pop gesture and open sidemenu gesture (#511)

* Bump version to 1.5.1 (#516)

* [NEW] Room header layout (#521)

* Clear iOS notification on resume/open (#520)

* [FIX] Flashing avatars on Android after #512 (#519)

* [FIX] App connects to previous server instead of the recent added (#518)

* [FIX] Room view header crashes when destructuring reducer (#523)

* [FIX] Dismiss keyboard on room close (#530)

* [FIX] Composer composer's send icon slowness (#528)

* [WIP] New Authentication layout (#536)

New Authentication layout

* Regression: Resend messages with error (#532)

* DDP Connection badge animation changed (#533)

* [FIX] Upload buttons on Android (#541)

* Bump version to 1.6.0 (#543)

* I18n: Add missing translation of simplified Chinese (#539)

* Update dependencies (#544)

* AndroidManifest changes

* Regression: Deep linking stopped working after react-native-navigation update (#549)

* [FIX] Android stuck on splash screen after hardware back button is pressed (#550)

* [FIX] Android stuck on splash screen after hardware button is pressed

* Fix empty user at asyncstorage

* Remove unused subscribe

* [FIX] x-instance-id header prop is case insensitive (#551)

* Bump version to 1.6.1 (#553)

* [FIX] x-instance-id header prop is case insensitive

* Use Rest API calls (#558)

* Chats: Don't show group header if none of the filters is selected (#560)

* [CHORE] Update Xcode image version on CircleCI (#561)

* Bump version to 1.7.0 (#562)

* [FIX] Load messages on notification tap (#564)

* Use Rest API pt 2 (#568)

* Room files

* Pinned messages

* Starred messages

* Mentioned messages

* Search messages

* Bug fixes

* Profile

* Livechat

* Block/unblock user

* Erase room

* Archive room

* Remove unused method

* Bug fix

* [CHORE] Add hold step on CircleCI before TestFlight (#572)

* [FIX] GET /info to check if it's a valid server instead of x-instance-id (#573)

* Bump version to 1.7.1 (#574)

* Unnecessary re-renders removed (#570)

* shouldComponentUpdate

* Rooms list shouldcomponentupdate

* RoomView shouldComponentUpdate

* Messagebox and Message shouldComponentUpdate

* EmojiPicker shouldComponentUpdate

* RoomActions shouldComponentUpdate

* Room info shouldComponentUpdate

* Update RNN

* Use only one Flatlist if none group filter is selected

* Update fix

* shouldComponentUpdate

* Bug fixes

* ListView changes

* Bug fix

* render list bug fix

* Changes on public channels

* - RoomView saga leak removed
- Join room e2e tests added

* Rest versions

* Method call versions

* Min RocketChat version alert

* Update dependencies (#587)

* [FIX] Better message actions (#567)

* [FIX] Back button press on message actions (#592)

* Bump version to 1.8.0 (#595)

* [FIX] LDAP login (#596)

* Create class to manage navigation (#594)

* Add Navigation class

* Place Drawer.js logic inside of Navigation

* Load less views at startup

* [FIX] v1.8.0 (#599)

* Downgrade react-native-fast-image

* Update iOS permission usage descriptions

* [FIX] Delete upload item

* Update JS SDK version (#602)

* Add Icons class (#611)

Creates Icons class to manage when to load icons from native side or react-native-vector-icons.
It also fixes `react-native run-android` #517

* Updating room indicator (#609)

Shows "Updating..." when requesting rooms from Rest API.

* [FIX] Load avatar on servers that prevent unauthenticated avatar access (#604)

App would show an empty space on servers that require authentication on avatar access

* [FIX] 2FA login in a server with LDAP enabled (#612)

* [FIX] Start loop searching for rooms updates only when connection goes down and SDK has userId (#613)

* Allow to create empty channel (#615)

* [FIX] Reply title should break text (#616)

* Bump version to 1.9.0 (#617)

* [FIX] SDK issues (#621)

* Remove listeners from room
* Properly close connections on change server
* Minor layout change on connecting badge

* [CHORE] Add TestFlight invite and update Readme (#623)

* [FIX] npm -> yarn dependencies migration (#622)

* I18n: Add French (#629)

* [FIX] Remove rooms listener (#630)

* [CHORE] Update issue template (#638)

* I18n: Add German (#641)

* Bump version to 1.10.0 (#644)

* [FIX] Prevent mass is typing dispatchs (#651)

* [FIX] Handle database errors properly (#650)

* [FIX] Change actions labels (#654)

* [FIX] Room members filter (#655)

* [FIX] uploadProgress is not a function (#656)

* [FIX] Slow messagebox (#658)

* Remove drawer (#653)

* Remove drawer (layout needs to be changed in future releases, though)
* Don't navigate outside on logout if there's other logged server
* Update react-native-navigation

* Message button (#660)

* Remove touchable opacity when scrolling messages
* Tap on disable messages closes keyboard
* Unify vibration
* Vibrate only on Android

* [FIX] Fetch rooms date (#662)

* [FIX] Select emoji error (#666)

* Update Realm to 2.24 (#667)

* Update React Native to 0.58.6 (#668)

* [FIX] Fix some language issues in German language (#664)

* New icons (#643)

* New Icons

* Remove unused assets

* Change send icon

* Layout tweaks

* Refactor Status

* Styles changed

* User layout fix

* Separator layout changes

* Sidebar status layout fix

* Fix Message.onLongPress issue

* Fix code markdown
Closes https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/625

* Status lint

* Fix tests

* Navigation debounce

* RoomActions icons

* Space between components

* Group text

* Update tests

* [CHORE] Remove .debug suffix on Android (#681)

* [FIX] Fix null native Messagebox component object (#680)

* Fix null native Messagebox component object

* [iOS] Fix header alignment

* Remove unused files

* Switch to react-navigation (#687)

* Update readme (#714)

* Bump to 1.10.1 (#731)

* [FIX] Deep linking between multiple logged servers (#730)

* Fix handle invisible status (#692)

* I18n: Add Portuguese (Portugal) (#722)

* [FIX] Show ActivityIndicator in RoomMembersView (#686)

* Bump version to 1.11.0 (#761)

* Migrate from GCM to FCM (#760)

* [NEW] Scrollable room name feature (#756)

* [NEW] Scroll down floating button (#735)

* [CHORE] Added Storybook documentation (#757)

* Use FlatList in RoomView (#762)

* [FIX] iOS requiring location permission (#768)

* Room item layout (#771)

* [NEW] Draft message per room (#772)

* [FIX] Add Realm.safeAddListener (#785)

* [CHORE] Remove tvOS target (#779)

* [NEW] Discussions (#696)

* Bump version to 1.12.0 (#804)

* [NEW] Threads (#798)

* RoomsListView improvements (#819)

* [FIX] Giphy not showing (#810)

* [FIX] Apply emojify on empty texts (#824)

* Lock drawer when stack is not on root screen (#825)

* Room item layout (#835)

* [FIX] Threads (#838)

Closes #826
Closes #827
Closes #828
Closes #829
Closes #830
Closes #831
Closes #832
Closes #833

* [FIX] Smaller thread title (#846)

* [FIX] Smaller thread title

* Remove markdown notation from thread title

* On message press debounce

* Align vertical thread title

* [Regression] Search stopped working on Android after LastMessage refactor (#851)

* Load legal pages from web (#849)

* Update fetch permissions api (#850)

* Update custom emojis endpoint (#852)

* Update emoji endpoint

* Use React.memo on Markdown

* Support RC versions lower than 0.75.0

* Realm migration

* Fetch roles from rest api (#853)

* Fetch roles from rest api

* Fix RoomInfoView role get

* Remove roles from redux

* Bump version to 1.13 (#857)

* Active users improvements (#855)

* Remove connection badge (#862)

* Connecting indicator on RoomsListView header

* Connecting indicator on RoomView header

* Remove ConnectionBadge

* Show updating on RoomView load messages

* Update dependencies (#863)

* Minor updates

* Update jsc-android

* Update react-native-modal

* Minor updates

* Update react-native-fast-image

* Minor dev updates

* Few major updates

* Update react-native-keyboard-aware-scroll-view

* Update pods

* Update android-support

* Update tests

* Remove duplicated getRoleDescription function (#866)

* [FIX] Load local URL image (#871)

* [FIX] Toggle/follow thread icon (#867)

* Tweaks on sequential threads messages layout (#858)

* Tweaks on sequential threads messages

* Update tests

* Fix quote

* Prevent from deleting thread start message when positioned inside the thread

* Remove thread listener from RightButtons

* Fix error on thread start parse

* Stop parsing threads on render

* Check replied thread only if necessary

* Fix messages don't displaying

* Fix threads e2e

* RoomsListView.updateState slice

* Stop fetching hidden messages on threads

* Set initialNumToRender to 5

* [FIX] Check if room is mounted before setting state (#864)

* Tweaks on sequential threads messages

* Update tests

* Fix quote

* Prevent from deleting thread start message when positioned inside the thread

* Remove thread listener from RightButtons

* Fix error on thread start parse

* Stop parsing threads on render

* Check replied thread only if necessary

* Fix messages don't displaying

* Fix threads e2e

* RoomsListView.updateState slice

* Stop fetching hidden messages on threads

* Check if RoomView is mounted before rendering

* Refactor navigation events on RoomsListView

* Fix lint

* Fix listener

* [FIX] Typing not getting cleared after popping a room (#873)

* [CHORE] Remove e2e tests from CI (#875)

* [FIX] Remove listeners on RoomView header unmount (#874)

* [RELEASE] Merge beta into master (#1055)

* Bump version to 1.16.0 (#1014)

* [IMPROVEMENT] Share credentials with Rocket.Chat.iOS (#982)

*  Create user table

*  Introduce user table

* 🔥 Remove unused table

*  Add userdefaults to storage data

* 💚 Fix android build

*  Get credentials from iOS native client

* 🔥 Remove unused code

*  Revert sign xcode

* 🐛 Fix first login-logout

* 🎨 Use constants to UserDefaults Keys

* 🐛 Fix clear server-user-info on logout

* 🐛 Fix filter null value

* 🚑 Remove user object in logout

*  Fix get servers from native-client

* 🚑 Fix error on change server

* [FIX] Don't run UserDefaults credentials on Android (#1015)

* 🐛 Fix native credentials (android)

* Fix migration loop

* [IMPROVEMENT] Hide frequently used emoji tab when empty (#792)

* [IMPROVEMENT] Bigger emoji in emoji only messages (#793)

* issue #725: bigger emoji in emoji only message

* issue-725/add storybook for Message/Emoji

* issue-725: update storybook/Message jest snapshot

* comment storybook import

* allow spaces and line breaks in emoji only message

* merge develop

* revert unnecessary spacing

* [FIX] Empty message if contains only a link (#787)

* Fix empty message if contains only a link

* 🐛 Fix empty space

* [IMPROVEMENT] Refactor empty space regex on quote (#1017)

* 🎨 Improve regex to empty space on quote

* 🎨 Improve on regex to empty space on quote

* [NEW] Custom fields on signup (#1013)

* added custom feilds on registration

* added flag as leftIcon and removed lable

* added try and catch

* typo

* [CHORE] Renew provisioning profiles (#1020)

* [NEW] Auto-translate (#1012)

* Update realm

* View original and translate working

* Read AutoTranslate_Enabled setting

* RocketChat.canAutoTranslate()

* AutoTranslateView

* Save language

* Auto-translate switch

* Translate message

* [IMPROVEMENT] Use haptics rather than vibration (#1016)

* Install expo-haptics

* Use expo-haptics rather than RN's Vibration module

* [IMPROVEMENT] Use Rest API for file upload (#1005)

* removed rn-fetch-blob and use native XMLHttpRequest instead

* removed unnessary changes

* fix android bug

* fix android bug

* added tmid support

* fix bug

* fixed isssue with cacel model

* fix problems with audio

* done requested changes

* fix bug with android

* [CHORE] [CI] [TESTS] update detox to make ci pass (#1026)

* feat: update detox to 12.11.3 to make CI pass

* ci: comment all jobs but leave e2e-test job

* commit to rerun IC e2e-test job

* ci: uncomment all CI jobs

* [NEW] Room swipe actions: mark as read/unread, hide, fav (#976)

* added unread and fav feature

* changed the layout

* fix jest

* done requested changes

* added requested changes

* [FIX] Android build (#1027)

* [FIX] Android build

* CircleCI error

* [FIX] iOS share credentials build (#1028)

* [FIX] iOS share credentials build

* Use `hasMigration` as a string

* [CI] Restore cache on CI (#1029)

* feat: add fastlane save\restore cache config; comment not needed jobs;

* install fastlane using 'bundle install'

* install fastlane using 'sudo bundle install'

* uncomment ios build commands

* run set up google services in ios folder

* add working_directory: ios to ios-build steps

* remove 'cd ios' from Fastlane build step

* add save\restore cache for npm modules

* group save_cache steps

* cache fastlane in ios-testflight job

* uncomment previously commented jobs\steps

* fix: add missing colon

* use key for caching: node-modules-{{ checksum ".circleci/config.yml" }}-{{ checksum "yarn.lock" }}

* add names for save\restore steps

* ci: add `default` step with `working_directory: ~/repo` to ios-build job

* return back caching npm: `node-v1-{{ checksum "package.json" }}-{{ arch }}`

* fix: add missing curly braces

* save\restore cache in e2e-test job; remove {{arch}} from cache names

* add names to restore_cache steps in android-build job

* add names to save_cache steps in android-build job

* add names to all save\restore steps; change checksum package.json to yarn.lock

* change `npm` to `NPM` in steps naming

* remove {{ checksum circle ci }} from android-build job and fix naming of steps

* [FIX] Rooms swipes (#1034)

* Regression: on press style feedback

* Action button styles

* Fix animations

* Styles changed

* Update subscription without having to wait for socket

* Calculate width on RoomsListView instead

* [FIX] Decrease bigger emoji size to 30 (#1031)

* [FIX] Append server URL on avatar if necessary (#1038)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Bump version to 1.16.1 (#1045)

* [FIX] Set UserDefaults AppGroup on notification tap (#1047)

* [FIX] Auto-translate messages as they arrive

* Fix favorite button

* [RELEASE] Merge beta into master (#1082)

* Bump version to 1.16.0 (#1014)

* [IMPROVEMENT] Share credentials with Rocket.Chat.iOS (#982)

*  Create user table

*  Introduce user table

* 🔥 Remove unused table

*  Add userdefaults to storage data

* 💚 Fix android build

*  Get credentials from iOS native client

* 🔥 Remove unused code

*  Revert sign xcode

* 🐛 Fix first login-logout

* 🎨 Use constants to UserDefaults Keys

* 🐛 Fix clear server-user-info on logout

* 🐛 Fix filter null value

* 🚑 Remove user object in logout

*  Fix get servers from native-client

* 🚑 Fix error on change server

* [FIX] Don't run UserDefaults credentials on Android (#1015)

* 🐛 Fix native credentials (android)

* Fix migration loop

* [IMPROVEMENT] Hide frequently used emoji tab when empty (#792)

* [IMPROVEMENT] Bigger emoji in emoji only messages (#793)

* issue #725: bigger emoji in emoji only message

* issue-725/add storybook for Message/Emoji

* issue-725: update storybook/Message jest snapshot

* comment storybook import

* allow spaces and line breaks in emoji only message

* merge develop

* revert unnecessary spacing

* [FIX] Empty message if contains only a link (#787)

* Fix empty message if contains only a link

* 🐛 Fix empty space

* [IMPROVEMENT] Refactor empty space regex on quote (#1017)

* 🎨 Improve regex to empty space on quote

* 🎨 Improve on regex to empty space on quote

* [NEW] Custom fields on signup (#1013)

* added custom feilds on registration

* added flag as leftIcon and removed lable

* added try and catch

* typo

* [CHORE] Renew provisioning profiles (#1020)

* [NEW] Auto-translate (#1012)

* Update realm

* View original and translate working

* Read AutoTranslate_Enabled setting

* RocketChat.canAutoTranslate()

* AutoTranslateView

* Save language

* Auto-translate switch

* Translate message

* [IMPROVEMENT] Use haptics rather than vibration (#1016)

* Install expo-haptics

* Use expo-haptics rather than RN's Vibration module

* [IMPROVEMENT] Use Rest API for file upload (#1005)

* removed rn-fetch-blob and use native XMLHttpRequest instead

* removed unnessary changes

* fix android bug

* fix android bug

* added tmid support

* fix bug

* fixed isssue with cacel model

* fix problems with audio

* done requested changes

* fix bug with android

* [CHORE] [CI] [TESTS] update detox to make ci pass (#1026)

* feat: update detox to 12.11.3 to make CI pass

* ci: comment all jobs but leave e2e-test job

* commit to rerun IC e2e-test job

* ci: uncomment all CI jobs

* [NEW] Room swipe actions: mark as read/unread, hide, fav (#976)

* added unread and fav feature

* changed the layout

* fix jest

* done requested changes

* added requested changes

* [FIX] Android build (#1027)

* [FIX] Android build

* CircleCI error

* [FIX] iOS share credentials build (#1028)

* [FIX] iOS share credentials build

* Use `hasMigration` as a string

* [CI] Restore cache on CI (#1029)

* feat: add fastlane save\restore cache config; comment not needed jobs;

* install fastlane using 'bundle install'

* install fastlane using 'sudo bundle install'

* uncomment ios build commands

* run set up google services in ios folder

* add working_directory: ios to ios-build steps

* remove 'cd ios' from Fastlane build step

* add save\restore cache for npm modules

* group save_cache steps

* cache fastlane in ios-testflight job

* uncomment previously commented jobs\steps

* fix: add missing colon

* use key for caching: node-modules-{{ checksum ".circleci/config.yml" }}-{{ checksum "yarn.lock" }}

* add names for save\restore steps

* ci: add `default` step with `working_directory: ~/repo` to ios-build job

* return back caching npm: `node-v1-{{ checksum "package.json" }}-{{ arch }}`

* fix: add missing curly braces

* save\restore cache in e2e-test job; remove {{arch}} from cache names

* add names to restore_cache steps in android-build job

* add names to save_cache steps in android-build job

* add names to all save\restore steps; change checksum package.json to yarn.lock

* change `npm` to `NPM` in steps naming

* remove {{ checksum circle ci }} from android-build job and fix naming of steps

* [FIX] Rooms swipes (#1034)

* Regression: on press style feedback

* Action button styles

* Fix animations

* Styles changed

* Update subscription without having to wait for socket

* Calculate width on RoomsListView instead

* [FIX] Decrease bigger emoji size to 30 (#1031)

* [FIX] Append server URL on avatar if necessary (#1038)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Bump version to 1.16.1 (#1045)

* [FIX] Set UserDefaults AppGroup on notification tap (#1047)

* [FIX] Auto-translate messages as they arrive

* Fix favorite button

* [FIX] Swipe animations (#1044)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Fix favorite button

* [FIX] Auto-translate messages as they arrive (#1049)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* [FIX] Auto-translate messages as they arrive

* [i18n] Add missing de translations (#1040)

* [CHORE] Switch to react-native-localize (#1043)

* Bump version to 1.17.0 (#1057)

* Load views as needed (#1056)

* [IMPROVEMENT] Change "resend" icon position (#1048)

* [NEW] Video support (#801)

* [NEW] File upload (#882)

* [NEW] Share extension (#942)

* [FIX] Share extension CI build (#1060)

* Change bundleID

* Provisioning

* get provisioning profile

* [IMPROVEMENT] Reusable toast (#1065)

* [FIX] Moment locales (#1066)

* [FIX] Share Extension issues (#1064)

* [FIX] Empty white list enables all media types upload (#1077)

* Merge branch 'master' into develop (#1079)

* [FIX] Empty white list enables all media types upload (#1080)

* Create utils to media (canUpload)

* Fix variable name

* [CHORE] Update README (#1081)

* [RELEASE] Merge beta into master (#1088)

* Bump version to 1.16.0 (#1014)

* [IMPROVEMENT] Share credentials with Rocket.Chat.iOS (#982)

*  Create user table

*  Introduce user table

* 🔥 Remove unused table

*  Add userdefaults to storage data

* 💚 Fix android build

*  Get credentials from iOS native client

* 🔥 Remove unused code

*  Revert sign xcode

* 🐛 Fix first login-logout

* 🎨 Use constants to UserDefaults Keys

* 🐛 Fix clear server-user-info on logout

* 🐛 Fix filter null value

* 🚑 Remove user object in logout

*  Fix get servers from native-client

* 🚑 Fix error on change server

* [FIX] Don't run UserDefaults credentials on Android (#1015)

* 🐛 Fix native credentials (android)

* Fix migration loop

* [IMPROVEMENT] Hide frequently used emoji tab when empty (#792)

* [IMPROVEMENT] Bigger emoji in emoji only messages (#793)

* issue #725: bigger emoji in emoji only message

* issue-725/add storybook for Message/Emoji

* issue-725: update storybook/Message jest snapshot

* comment storybook import

* allow spaces and line breaks in emoji only message

* merge develop

* revert unnecessary spacing

* [FIX] Empty message if contains only a link (#787)

* Fix empty message if contains only a link

* 🐛 Fix empty space

* [IMPROVEMENT] Refactor empty space regex on quote (#1017)

* 🎨 Improve regex to empty space on quote

* 🎨 Improve on regex to empty space on quote

* [NEW] Custom fields on signup (#1013)

* added custom feilds on registration

* added flag as leftIcon and removed lable

* added try and catch

* typo

* [CHORE] Renew provisioning profiles (#1020)

* [NEW] Auto-translate (#1012)

* Update realm

* View original and translate working

* Read AutoTranslate_Enabled setting

* RocketChat.canAutoTranslate()

* AutoTranslateView

* Save language

* Auto-translate switch

* Translate message

* [IMPROVEMENT] Use haptics rather than vibration (#1016)

* Install expo-haptics

* Use expo-haptics rather than RN's Vibration module

* [IMPROVEMENT] Use Rest API for file upload (#1005)

* removed rn-fetch-blob and use native XMLHttpRequest instead

* removed unnessary changes

* fix android bug

* fix android bug

* added tmid support

* fix bug

* fixed isssue with cacel model

* fix problems with audio

* done requested changes

* fix bug with android

* [CHORE] [CI] [TESTS] update detox to make ci pass (#1026)

* feat: update detox to 12.11.3 to make CI pass

* ci: comment all jobs but leave e2e-test job

* commit to rerun IC e2e-test job

* ci: uncomment all CI jobs

* [NEW] Room swipe actions: mark as read/unread, hide, fav (#976)

* added unread and fav feature

* changed the layout

* fix jest

* done requested changes

* added requested changes

* [FIX] Android build (#1027)

* [FIX] Android build

* CircleCI error

* [FIX] iOS share credentials build (#1028)

* [FIX] iOS share credentials build

* Use `hasMigration` as a string

* [CI] Restore cache on CI (#1029)

* feat: add fastlane save\restore cache config; comment not needed jobs;

* install fastlane using 'bundle install'

* install fastlane using 'sudo bundle install'

* uncomment ios build commands

* run set up google services in ios folder

* add working_directory: ios to ios-build steps

* remove 'cd ios' from Fastlane build step

* add save\restore cache for npm modules

* group save_cache steps

* cache fastlane in ios-testflight job

* uncomment previously commented jobs\steps

* fix: add missing colon

* use key for caching: node-modules-{{ checksum ".circleci/config.yml" }}-{{ checksum "yarn.lock" }}

* add names for save\restore steps

* ci: add `default` step with `working_directory: ~/repo` to ios-build job

* return back caching npm: `node-v1-{{ checksum "package.json" }}-{{ arch }}`

* fix: add missing curly braces

* save\restore cache in e2e-test job; remove {{arch}} from cache names

* add names to restore_cache steps in android-build job

* add names to save_cache steps in android-build job

* add names to all save\restore steps; change checksum package.json to yarn.lock

* change `npm` to `NPM` in steps naming

* remove {{ checksum circle ci }} from android-build job and fix naming of steps

* [FIX] Rooms swipes (#1034)

* Regression: on press style feedback

* Action button styles

* Fix animations

* Styles changed

* Update subscription without having to wait for socket

* Calculate width on RoomsListView instead

* [FIX] Decrease bigger emoji size to 30 (#1031)

* [FIX] Append server URL on avatar if necessary (#1038)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Bump version to 1.16.1 (#1045)

* [FIX] Set UserDefaults AppGroup on notification tap (#1047)

* [FIX] Auto-translate messages as they arrive

* Fix favorite button

* [FIX] Swipe animations (#1044)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Fix favorite button

* [FIX] Auto-translate messages as they arrive (#1049)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* [FIX] Auto-translate messages as they arrive

* [i18n] Add missing de translations (#1040)

* [CHORE] Switch to react-native-localize (#1043)

* Bump version to 1.17.0 (#1057)

* Load views as needed (#1056)

* [IMPROVEMENT] Change "resend" icon position (#1048)

* [NEW] Video support (#801)

* [NEW] File upload (#882)

* [NEW] Share extension (#942)

* [FIX] Share extension CI build (#1060)

* Change bundleID

* Provisioning

* get provisioning profile

* [IMPROVEMENT] Reusable toast (#1065)

* [FIX] Moment locales (#1066)

* [FIX] Share Extension issues (#1064)

* [FIX] Empty white list enables all media types upload (#1077)

* Merge branch 'master' into develop (#1079)

* [FIX] Empty white list enables all media types upload (#1080)

* Create utils to media (canUpload)

* Fix variable name

* [CHORE] Update README (#1081)

* [FIX] Media share type (#1086)

* [RELEASE] Merge beta into master (#1142)

* [RELEASE] Merge beta into master (#1174)

* [RELEASE] Merge beta into master (#1282)

* Merge beta into master (#1461)

* Merge beta into master (#1637)

* Merge beta into master (#1759)

* Merge beta into master (#1897)

* [FIX] Close SortDropdown on sort select (#1230)

* [FIX] Cancel upload and check failed upload (#1232)

* [FIX] Slash commands not cleaning is typing and not using state (#1233)

* [FIX] Dispatch roomsRequest on app foreground event even if not connected (#1234)

* [CHORE] Update react-native-jitsi-meet (#1235)

* [FIX] Regex on run slash command (#1223)

* Update React Native to 0.61.1 (#1236)

* Update React Native to 0.61.1

* Update patch to SSL Pinning

* Revert storybook

* [CHORE] Update react-native-safe-area-view (#1219)

* [FIX] Try/catch JSON.parse XHR response (#1238)

* [FIX] Change messagebox icon immediate on change text (#1241)

* [FIX] Update last open on message stream received (#1240)

* [FIX] Remove animation from RoomsListView.willFocus (#1239)

* [FIX] Delete message on thread (#1214)

* [REGRESSION] Markdown text (#1242)

* [FIX] Jest (#1243)

* [FIX] Avatar shown when useRealName is activated (#1162)

* Fix avatar when use real name

* Wrong indentation

* [DOCS] Add SECURITY.md (#1244)

* [CHORE] Update react-native-reanimated to 1.3.0 (#1246)

* [FIX] Run credentials migration only once (#1245)

* [CHORE] Update react-native-jitsi-meet to 2.0.1 (#1249)

* [FIX] Messagebox onChangeText issues (#1252)

* Stop ongoing debounces on messagebox unmount

* Immediately change send icon, but keep debouncing others

* Make CustomEmoji stateless function

* Fix mentions keyExtractor

* [FIX] Room subscription issues (#1255)

* [FIX] Reaction press (#1258)

* [FIX] Channel avatars not showing after application unloads (#1264)

* Revert react-native-safe-area-view (#1265)

* [FIX] Remove console on production mode (#1268)

* [FIX] Messages preview issues (#1257)

* [FIX] Select user from native credentials (#1266)

* [FIX] Some issues on preview message (#1271)

* [FIX] Audio player track and thumb not rendering on Android (#1273)

* [FIX] Record audio message throws exception when FileSystem.getInfoAsync is called (#1272)

* [FIX] China shouldn't use CallKit (#1274)

* [FIX] Watermelon batches (#1277)

* Bump version to 1.20.1 (#1285)

* [CHORE] Remove memoize-one (#1284)

* [FIX] End Jitsi call on unmount (#1291)

* [FIX] Allow self-signed certificates (#1310)

* [FIX] Set User-Agent  (#1318)

* Set User-Agent Fetch & Websocket & XHR

* Set User-Agent

* Custom User Agent on fetch/websocket

* Fix names

* Use DeviceInfo

* fix server with subpath (#1322)

* [FIX] Server with https:\\ instead of https:// (#1320)

* [FIX] Server dropdown not closing after changing stack (#1299)

* [FIX] Invalid server version (#1319)

* [IMPROVEMENT] Respect "Hide counter" preference (#1306)

* [FIX] Pass isFocused as a function to Messagebox (#1309)

* [CHORE] Remove icons folder (#1290)

* [CHORE] Refactor RoomItem touchable (#1331)

* [FIX] Unnecessary rerender on RoomItem when status is undefined (#1336)

* [UPDATE DEPS] react-navigation and react-navigation-stack (#1337)

* [FIX] Avatars not loading on share extension when Accounts_AvatarBlockUnauthenticatedAccess is enabled (#1339)

* Bump version to 1.20.2 (#1340)

* [FIX] Remove some unnecessary re-renders on Messagebox (#1341)

* [REGRESSION] Use LayoutAnimation instead of Transition API (#1338)

* [FIX] Remove setState from notifications view causing watermelon object to be updated outside an action (#1342)

* [IMPROVEMENT] Save last message as message when subscription is updated (#1344)

* [UPDATE DEPS] Update RN to 0.61.3 (#1345)

* [DOCS] Update Readme (#1346)

* [CHORE] Remove react-native-scrollable-tab-view fork (#1352)

* [FIX] URL preview (#1360)

* [REGRESSION] Decrease list view memory size (#1361)

* [FIX] Paste (#1350)

* [CHORE] Update gems (#1365)

* Bump version to 1.20.3 (#1366)

* [FIX] Use Ruby 2.4 on TestFlight upload (#1368)

* [FIX] Parse Urls (#1371)

* [FIX] Parse image URL only if it's not empty (#1372)

* [FIX] Load messages issues (#1373)

* Bump version to 1.21.0 (#1376)

* [FIX] Crowd login (#1381)

* [FIX] Clicking user avatar in thread previews crashes app (#1363)

* [IMPROVEMENT] Error messages on connect (#1379)

* [FIX] ProfileView input navigation error when custom fields aren't set (#1383)

* [FIX] Batch server deletion on logout (#1382)

* Bump app to 1.22.0 (#1387)

* [FIX] Server Version (#1392)

* Update patch and minor deps (#1386)

* [FIX] Crash when open thread (#1395)

* Bump version to 1.23.0 (#1394)

* [I18N] Update ru.js (#1384)

* [FIX] CAS building wrong URL (#1362)

* [FIX] Delete messages (#1399)

* [FIX] In-app notification showing wrong content on channels (#1400)

* Bump version to 1.24.0 (#1404)

* [FIX] Prevent server with whitespace (#1402)

* [IMPROVEMENT] Keyboard and content type on login (#1403)

* [FIX] Messages stop loading (#1410)

* [NEW] Tablet support (#1300)

* [IMPROVEMENT] Authentication via deep linking (#1418)

* [IMPROVEMENT] Markdown performance when identifying emoji only content (#1422)

* [FIX] BackHandler remove random failing on development (#1423)

* Bump version to 1.25.0 (#1424)

* [CHORE] Update CI Xcode Image (#1430)

* [FIX] Rooms grouping not working properly (#1435)

* [FIX] Take a video (#1437)

* [NEW] Themes (#1298)

* [FIX] Share extension doesn't reconnect to previous selected server on Android (#1429)

* [FIX] Init local settings on notification tap (#1438)

* Bump version to 1.26.0 (#1450)

* [FIX] Emoji parser not working on Hermes  (#1445)

* [NEW] Enable Hermes (#1446)

* [FIX] Automatic theme repeating (#1457)

* [CHORE] Sync Experimental and Official app versions (#1458)

* [DOCS] Update readme (#1459)

* [FIX] Messages being sent but showing as temp status (#1469)

* [FIX] Missing messages after reconnect (#1470)

* [FIX] Few fixes on themes (#1477)

* [I18N] Missing German translations (#1465)

* Missing German translation

* adding a missing space behind colon

* added a missing space after colon

* and another attempt to finally fix this – got confused by all the branches

* some smaller fixes for the translation

* better wording

* fixed another typo

* [FIX] Crash while displaying the attached image with http on file name (#1401)

* [IMPROVEMENT] Tap app and server version to copy to clipboard (#1425)

* [NEW] Reply notification (#1448)

* [FIX] Incorrect background color login on iPad (#1480)

* [FIX] Prevent multiple tap on send (Share Extension) (#1481)

* [NEW] Image Viewer (#1479)

* [DOCS] Update Readme (#1485)

* [FIX] Jitsi with Hermes Enabled (#1523)

* [FIX] Draft messages not working with themed Messagebox (#1525)

* [FIX] Go to direct message from members list (#1519)

* [FIX] Make SAML wait for idp token instead of creating it on client (#1527)

* [FIX] Server Test Push Notification (#1508)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Update to new server response (#1509)

* [FIX] Insert messages with blank users (#1529)

* Bump version to 4.2.1 (#1530)

* [FIX] Error when normalizing empty messages (#1532)

* [REGRESSION] CAS (#1570)

* Bump version to 4.2.2 (#1571)

* [FIX] Add username block condition to prevent error (#1585)

* Bump version to 4.2.3

* Bump version to 4.2.4

* Bump version to 4.3.0 (#1630)

* [FIX] Channels doesn't load (#1586)

* [FIX] Channels doesn't load

* [FIX] Update roomsUpdatedAt when subscriptions.length is 0

* [FIX] Remove unnecessary changes

* [FIX] Improve the code

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Make SAML to work on Rocket.Chat < 2.3.0 (#1629)

* [NEW] Invite links (#1534)

* [FIX] Set the http-agent to the form that Rocket.Chat requires for logging (#1482)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] "Following thread" and "Unfollowed Thread" is hardcoded and not translated (#1625)

* [FIX] Disable reset button if form didn't changed (#1569)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Header title of RoomInfoView (#1553)

* [I18N] Gallery Permissions DE (#1542)

* [FIX] Not allow to send messages to archived room (#1623)

* [FIX] Profile fields automatically reset (#1502)

* [FIX] Show attachment on ThreadMessagesView (#1493)

* [NEW] Wordpress auth (#1633)

* [CHORE] Add Start Packager script (#1639)

* [CHORE] Update RN to 0.61.5 (#1638)

* [CHORE] Update RN to 0.61.5

* [CHORE] Update react-native patch

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

* Bump version to 4.3.1 (#1641)

* [FIX] Change force logout rule (#1640)

* Bump version to 4.4.0 (#1643)

* [IMPROVEMENT] Use MessagingStyle on Android Notification (#1575)

* [NEW] Request review (#1627)

* [NEW] Pull to refresh RoomView (#1657)

* [FIX] Unsubscribe from room (#1655)

* [FIX] Server with subdirs (#1646)

* [NEW] Clear cache (#1660)

* [IMPROVEMENT] Memoize and batch subscriptions updates (#1642)

* [FIX] Disallow empty sharing (#1664)

* [REGRESSION] Use HTTPS links for sharing and markets protocol for review (#1663)

* [FIX] In some cases, share extension doesn't load images (#1649)

* [i18n] DE translations for new invite function and some minor fixes (#1631)

* [FIX] Remove duplicate jetify step (#1628)

minor: also remove 'cd' calls

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [REGRESSION] Read messages (#1666)

* [i18n] German translations missing (#1670)

* [FIX] Notifications crash on older Android Versions (#1672)

* [i18n] Added Dutch translation (#1676)

* [NEW] Omnichannel Beta (#1674)

* [NEW] Confirm logout/clear cache (#1688)

* [I18N] Add es-ES language  (#1495)

* [NEW] UiKit Beta (#1497)

* [IMPROVEMENT] Use reselect (#1696)

* [FIX] Notification in Android API level less than 24 (#1692)

* [IMPROVEMENT] Send tmid on slash commands and media (#1698)

* [FIX] Unhandled action on UIKit (#1703)

* [NEW] Pull to refresh RoomsList (#1701)

* [IMPROVEMENT] Reset app when language is changed (#1702)

* [FIX] Small fixes on UIKit (#1709)

* [FIX] Spotlight (#1719)

* [CHORE] Update react-native-image-crop-picker (#1712)

* [FIX] Messages Overlapping (Android) and MessageBox Scroll (iOS) (#1720)

* [REGRESSION] Remove @ and # from mention (#1721)

* [NEW] Direct message from user info (#1516)

* [FIX] Delete slash commands (#1723)

* [IMPROVEMENT] Hold URL to copy (#1684)

* [FIX] Different sourcemaps generation for Hermes (#1724)

* [FIX] Different sourcemaps generation for Hermes

* Upload sourcemaps after build

* [REVERT] Show emoji keyboard on Android (#1738)

* [FIX] Stop logging react-native-image-crop-picker (#1745)

* [FIX] Prevent toast ref error (#1744)

* [FIX] Prevent reaction map error (#1743)

* [FIX] Add missing calls to user info (#1741)

* [FIX] Catch room unsubscribe error (#1739)

* [i18n] Missing German keys (#1735)

* [FIX] Missing i18n on MessagesView title (#1733)

* [FIX]  UIKit Modal: Weird behavior on Android Tablet (#1742)

* [i18n] Missing key on German (#1747)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [i18n] Add Italian (#1736)

…

* [FIX] Notification stream throwing an error when there isn't a message on payload (#2637)

* [FIX] Threads not being updated and other related issues (#2636)

* Fix parent title on thread header breaking lines

* Fix https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/2519

* Fix thread badge not being updated

* [FIX] Minor room header issues (#2630)

* Add hitSlop to RoomView header

* Use 1 icon padding for threads header

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

* [FIX] Whitelabel unable to find package name (#2626)

* Fixes #2625

* Fixes #2614

* Apply resValue on defaultConfig and undo unnecessary changes

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [i18n] Add missing German strings (#2619)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Model columns misplaced (#2640)

* [FIX] Connect a null server (#2639)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Jitsi users unable to unmute (#2623)

* [FIX] Jitsi users being muted always

* fix: bundle is invalid

* Update jitsi meet sdk with ui improvements

* Update JitsiMeetSDK ios

* Centralize toolbox android

* Fix images on Jitsi

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* Bump version to 4.12.1 (#2641)

* [FIX] Share extension and save image not working on Android 10 (#2651)

* Bump version to 4.13.0 (#2657)

* [FIX] Update Loading logo (#2658)

* [NEW] Support client certificates for SSL (two-way authentication) (Android) (#2624)

* wip: Android SSL Pinning

* Use own SSLPinningModule

* wip: Use Rocket.Chat own react-native

* wip: Fresco Images using custom OkHttpClient

* wip: react-native-webview onReceivedClientCertRequest

* feat: Save Images of a SSL Pinning protected server

* chore: SSLPinning package as a interface to iOS & Android implementations

* chore: update glide

* feat: load images under a client ssl certificate protected server

* chore: remove patch

* feat: Audio & Video under a SSL Client protected server

* fix: Unpin certificate when change server

* feat: Fast Image as a patch

* chore: update fast-image

* Fix merge

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [NEW] Channel actions (#2644)

* room roles

* handle owner

* endpoints

* Leader and Moderator

* Remove user from room

* stash ignore

* Add subscription.ignored column

* ignore user

* Fix icons

* I18n

* Minor i18n fixes

* Direct Message and open action sheet after a normal tap

* Fix icon

* stash isIgnored

* isManualUnignored message

* Fix update

* Ignored

* Mute, moderator, leader, owner, remove from room

* ignore

* Tests

* pt-BR

* Update pods

* Apply requested changes

* Add RC version on requests

* [NEW] Support RTL (#2656)

* wip: RTL (iOS)

* wip: RTL (Android)

* wip: reload bundle when change between RTL languages

* fix: Stack Animation on Android

* fix: update snapshot

* fix: Swipe Room Actions in RTL mode

* fix: snapshots

* Move isRTL to i18n

* Fix styling

* Update tests

* Update pods

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Initial users' status is not fetched (#2664)

* [FIX] Messages overlapping and emoji keyboard not opening (#2670)

* Replace keyboard libs for react-native-ui-lib

* Apply Jitsi branch

* Require keyboard on bundle

* Update ui-lib

* chore: update deps

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

* [CHORE] Force normalized params for 2FA (#2683)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Thread message flickering while thread parent isn't found (#2676)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Light theme not working on Android with Dark Theme set (#2675)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] App not prompting join code for password protected channels (#2514)

* Adding joinCode parameter

Co-authored-by: Vitor Leal <vitor_leal2201@hotmail.com>
Co-authored-by: Fernando Aguilar <fernando.aguilar@hotmail.com.br>

* Insert join code input

Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com>

* Add joinCode field on db

Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com>

* Add label i18 pt-br and en-us

Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com>

* Add insert join code text

Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com>

* Fix atribute name

Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com>

* Add join text

Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com>

Co-authored-by: Daniel Maike <danmke@hotmail.com>
Co-authored-by: Fernando Aguilar <fernando.aguilar@hotmail.com.br>

* Fix attributes joinCode, joinCodeRequired and pass attribute param in navigation

Signed-off-by: Daniel Maike <danmke@hotmail.com>

Co-authored-by: Vitor Leal <vitor_leal2201@hotmail.com>

* Fixing attribute joinCodeRequired pass to goRoom

Signed-off-by: Daniel Maike <danmke@hotmail.com>

* Changed textinput style

Signed-off-by: Daniel Maike <danmke@hotmail.com>

Co-authored-by: Vitor Leal <vitor_leal2201@hotmail.com>

* Delete not necessary attribute

Signed-off-by: Daniel Maike <danmke@hotmail.com>

* Fixing input style

Co-authored-by: Vitor Leal <vitor_leal2201@hotmail.com>

* Undo unncessary changes

* use a join code modal

* tests: e2e tests to join protected channel

* fix: undo unnecessary change

* tests: cancel join code

* Remove some tests

* Minor fixes

Co-authored-by: Vitor Leal <vitor_leal2201@hotmail.com>
Co-authored-by: Fernando Aguilar <fernando.aguilar@hotmail.com.br>
Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
Co-authored-by: youssef-md <emaildeyoussefmuhamad@gmail.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [I18n] Add Arabic (#2537)

* Arabic language setup

* Added arabic translation

* Arabic translation Proofreading

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [I18N] Add missing zh_TW and zh_CN strings (#2680)

* feat(i18n): add some missing strings and improve some translation

* fix: add missing server version

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Add username on status messages (#2553)

* 1689 - missing user name for status messages

* 1689 - missing user name for status messages. Fixed broken e2e test "should pin message".

* Minor tweak

* Remove center style

* Small refactor on User

* Remove toLowerCase

* Update tests

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Filenames are incorrect in non-latin alphabets on upload (#2671)

* fix: filename on react-native-image-crop-picker

* fix: use rn-fetch-blob to upload files

* fix: FileUpload as a service

* fix: cancel upload on iOS

* fix: file upload from share extension

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Ease white labelling for Android (#2685)

* improve white labelling for Android

* Move application ID to gradle properties

* Fix CI

* Point foss sufix to main app

* Use npx on android-whitelabel script

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Chats order (#2688)

* Persist highest value on subscription.roomUpdatedAt

* Update tests

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

* [REGRESSION] Re-enable Jitsi Chat (#2687)

* Fix main jitsi

* Fix iOS

* Clear build.gradle cache

* Don't restore gradle

* cache is back

* Use master

* Point to react-native-jitsi-meet#master

* [CHORE] Build official apps on CI (#2701)

* Duplicated target and changed Bridging Header

* Display name

* Unnecessary dumb swift file removed

* Buildable name

* Reorder Info.plist

* Rename Official target's bundle id

* Ignore .mobileprovision

* Fix provisioning of official app

* Starting signing

* stash fastfile

* starting official ci iOS

* Uncomment Fastfile keychain

* Fix CI config

* allowProvisioningUpdates

* Changing AppIcon and Splash Screen

* Remove unnecessary folder inside of Images.xcassets

* Reorder notificationservice and shareextension plists

* Fix signing

* Manual signing style for official

* Split official signing

* Update project provisioning

* Use ENV as profile

* Output match

* Keys

* TestFlight refactor

* Setting up android

* android-official-play-build job

* Start removing unnecessary fastlane tasks on Android

* Trying to refactor Android jobs

* android-env

* Remove foss build for now

* Fork

* Fix if conditions

* Fix push

* ios-build command

* Rename Android builds

* Upload dSYMs

* Refactoring workflow

* Reorder upload-to-testflight

* upload-to-google-play-beta command

* Fix ci

* Fix android fork build

* Fix keystore

* Fix options on fastlane android

* Fix keystore

* Check isOfficial on iOS

* Check isOfficial on db

* Remove unused imports

* Database names on Android

* Tag fix

* Minor fixes

* Set IS_OFFICIAL on CI

* Fix detox

* follow review suggestions

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>

* [i18n] Update fr (#2697)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [i18n] Update fr (#2705)

Typo

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Empty space on Messagebox (#2704)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Yarn android scripts (#2716)

* [CHORE] Rename Experimental iOS lane (#2717)

* Move build_fork to the end

* Rename release to build_experimental

* [IMPROVEMENT] Use class variable instead of state for List's animated (#2718)

* [FIX] Bottom sheet being hidden sometimes (#2722)

* [IMPROVEMENT] Match background and text mention colors (#2723)

* [FIX] App freezing if Markdown preview contains sequential empty spaces (#2726)

* Remove sequential empty spaces from Markdown preview

* Use Markdown preview on RepliedThread

* [FIX] Official app without sharedUserId (#2734)

* [CHORE] Update React Native to 0.63.4 (#2737)

* Bump version to 4.13.1 (#2739)

* [REGRESSION] Multiple uploads not working on iOS (#2738)

* Update React Native to 0.63.4

* Fix multiple uploads not working on iOS

* [FIX] Unable to save attachment on iOS (#2743)

* Fix rn-fetch-blob's document dir without forward slash

* Update camera roll

* [FIX] Generate Jitsi access token when making a call (#2694)

fixes: #2693

 # Please enter the commit message for your changes. Lines starting

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Jitsi notification delay (#2668)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Channels list not following the same sorting logic from web client (#2763)

* [FIX] Pods lost on Official target (#2764)

* [FIX] RoomItem using deprecated animated event signature (#2771)

* [FIX] Server autocomplete text breaking line (#2774)

* [FIX] ServerDropdown flashing bigger server icon (#2775)

* [FIX] ServerDropdown flashing bigger server icon

* Remove unused logo and update image path where needed

* Minor tweak

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Rooms list not being updated on some cases (#2765)

* Request subscriptions on RoomsListView.constructor

* Removes opened rooms from last message persisting

* Change server reducer

* Prevent undefined ids causing query error

* [FIX] Share Extension hitting memory limit on iOS (#2788)

* [FIX] Disallow swipe to dismiss on share extension

* Limit query to 20 and clean up props

* Remove rn-extension-share branch pointer

* Test new branch

* Remove branch

* [IMPROVEMENT] Threads layout tweaks (#2686)

* improvement: Thread Details

* fix: re-render Thread Messages Item

* fix: update snapshots

* improve: thread details component

* fix: cast replies length

* improvement: format date of threads

* improvement: thread details styles

* fix: wrap text

* tests: update snapshot

* improvement: use same date format for all dates

* Icon size 24

* Remove date

* Remove prop drill

* Badge position

* Badge container tweak

* Fix inline style

* Move ThreadDetails to containers

* Update stories

* Fix lint

* Remove wrong prop

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Remove some migrations (#2792)

* Remove force rooms refresh

* Remove MMKV migration

* Bump version to 4.14.0 (#2797)

* [FIX] Messagebox tracking lost on pop gesture navigation (#2799)

* Use setTimeout instead of InteractionManager

* Update tracking lib

* [FIX] Back button closing activity when on root stack screen (#2804)

* Make hardware back button to behave as home button on root screens

* Remove unnecessary code

* Remove handleBackPress from OnboardingView

* Fix lint

* [i18n] Add missing German strings (#2715)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [NEW] Encrypted Discussions  (#2813)

* I18n key fix

* Add encrypted switch

* Remove unused i18n keys

* Add enabled to encryption reducer

* Show encrypted option on CreateDiscussionView only when e2e encryption is properly set

* Add localSearch and use it on search

* Use encrypted from parent channel

* Fix method calls as rest api with 2fa enabled

* Fix logout after reset keys

* Use encryption reducer instead of lib directly to check render

* Check for room type logic to display encryption option on create discussion

* Check toggle-room-e2e-encryption permission on RoomActionsView

* Check for encryption status instead of setting on server

* Fix

* Disable switch instead of hide it

* Fix spotlight for DMs

* Fix server test

* [FIX] Messagebox missing style for text color (#2786)

* Changing auxilaryTintColor

* Changed Placeholder color to BodyText color

* added color prop

* eslint changes

* used array for styles

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [I18N] Update arabic (#2696)

* Update ar.js

* Update ar.js

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Workspace input without i18n (#2689)

* [FIX] Translation of strings in Login page

* Strings are added for translation.

fixes: #2620

* Add pt-BR

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Spotlight returning duplicated entries (#2805)

* Update rocketchat.js

* Updated search function

* Minor improvements

* Remove atIndex

* Add remove logic to remove duplicate data from response

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Refactor ServerItem (#2778)

* Updated ServerDropdown and ServerItem

* Added ServerItem stories

* Update ServerDropdown.js

* Updated ServerItem stories

* Updated ServerItem stories and ServerItem component

* Updated SelectServerView, ServerItem and ServerItem stories

* Updated ServerItem stories

* Updated ServerItem stories

* Update tests

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [DOCS] Updated Quick Start docs link in e2e/readme (#2802)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [I18N] Add Turkish (#2793)

* Turkish language support added

* Update tr.js

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Lint on #2793 (#2818)

* [I18N] Add missing german strings (#2689) (#2820)

* [I18N] Add missing italian strings (#2817)

* [FIX] Server version becoming null on server change (#2821)

* [FIX] Wrong styling on E2E encryption banner (#2767)

* [FIX] Wrong styling on E2E encryption banner

* [FIX] Wrong styling on E2E encryption banner

* [FIX] Wrong styling on E2E encryption banner

* [FIX] Wrong styling on E2E encryption banner (#2767)

* Updated SortDropdown, ListHeader, ListItem and added stories for List.Item

* Updated SortDropdown

* Removed unused component

* Updated List.Item and stories

* Reverted unnecessary changes and updated ListItem stories

* Fix minor indentation

* Stop breaking Touch's default underlay color

* Fix indentation

* Remove falsy comparison from render

* Fix left icon

* Use List.Item on OmnichannelStatus

* Add missing separator

* Lint

* Fix sort dropdown

* Remove unnecessary styles

* Fix detox

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] App Store using Experimental's app id (#2826)

* [FIX] Wrong username on push notifications (#2825)

* [FIX] Share extension memory issues on iOS (#2845)

* Remove unnecessary class prop

* Stop rendering servers when there's only one

* Map and alloc only necessary columns from query

* Fetch servers count instead of all servers records

* Fetch only needed servers

* Separators

* Remove renderContent

* Minor fix

* Refactor query

* Smaller avatars in memory

* Fix getItemLayout

* Add topic

* Load less pods

* tests

* Import only used functions from lodash

* Fix pods

* Import only used functions from semver

* Fix media sharing

* Update pods

* Disables preview and thumb on iOS

* Update expo-video-thumbnail

* Unnecessary change

* [FIX] Logout from other locations not prompting confirmation option (#2854)

* Fixed logout toast bug for the iOS

* Removing callToAction and replacing with confirmationText

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* Bump version to 4.14.1 (#2859)

* [IMPROVEMENT] Check for focused rooms on in-app notifications (#2857)

* Update InAppNotification and room reducer

* Update InAppNotification

This reverts commit 60330a1e04cfe8d2e5aa311f367083d831682c49.

* Stop subscribing to threads

* Remove ref

* Fix prop-types

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Real name being ignored in SearchMessagesView (#2838)

Co-authored-by: Gerzon Z <gerzonc@icloud.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Remove unnecessary share reducer calls (#2861)

* Remove unnecesary share reducer calls

* Update Avatar

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Breadcrumbs exceeding characters limit (#2862)

* [FIX] breadcrumbs exceeding

* fix.breadcrumbs-exceeding-change-events

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] App compressing videos on iOS (#2915)

* Update index.js

* Update index.js

* [FIX] Real name setting ignored on reply preview (#2908)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Reply component sending unused prop to Description (#2900)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] BackdropOpacity based on themes (#2863)

* Added backdropOpacity based on theme

* Updated ActionSheet, ReactionsModal, ReactionPicker and Sidebar

* Updated MultiSelect

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Webview not falling back to default auth challenge when no cert is provided (#2918)

* [FIX] Android - fallback to default auth challenge handling when no cert is provided

* If a certificate auth challenge is requested on Android the webview will hang if no certificate is loaded.
  To prevent this, fallback to default Android behavior and cancel the challenge with request.cancel()

* No client certificate case defaults to super implementation

* Update react-native-webview

* Downgrade to previous dependency version

Co-authored-by: Diego Mello <diegolmello@gmail.com>
Co-authored-by: Gerzon Z <gerzonc@icloud.com>
Co-authored-by: Jan Garaj <jan.garaj@gmail.com>

* [FIX] Support Jitsi_URL_Room_Hash (#2905)

* [FIX] Temp attachment files not being flushed after saved to gallery (#2871)

* Update AttachmentView.js

* Update AttachmentView.js

* Update AttachmentView.js

* Update AttachmentView.js

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Update iOS profiles for Experimental app (#2933)

* [IMPROVE] Deleted thread reply redirects to thread (#2840)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Thread showing typing indicator from main room (#2869)

* [FIX] Remove typing indicator from thread's header

* remove unnecessary props and change usersTyping condition

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] DM rooms show typing status from last group room (#2878)

* [FIX] DM rooms show typing status from last group room

* Undo changes

* Check if current typing is from focused room before dispatching to redux

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Can't copy or edit media's description (#2885)

* [FIX] Image descriptions issues

* shorten the condition string

* fix selectedMessage state

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] RightButtonsContainer re-render check not returning default value (#2899)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Remove InteractionManager blocks (#2906)

* [FIX] Remove InteractionManager blocks

* Minor fix

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] App not sending second argument for EventEmitter.removeListener on some places (#2909)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Temp message ignoring real name (#2919)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] System message of e2e encryption is missing (#2888)

* [FIX] System message of e2e encryption missing

* add new encryption string

* add to stories

* Add pt-BR

* Move stories

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Add permissions to Redux (#2914)

* [FIX] Add permissions to Redux store

* add only permissions being used in the app

* add clear permissions reducer

* call RocketChat.hasPermission from reducer

* add server version comparison on getPermissions

* refactor hasPermission function

* refactor hasPermission function

* remove uncomment code

* use Q.experimentalSortBy()

* add coerce function

* Change Rocketchat.hasPermission

* Apply on isReadOnly

* Apply to RoomInfoEditView

* Apply to RoomInfoView and RoomInfoEditView

* canAutoTranslate

* Unnecessary clear permissions

* Revert getUpdatedSince

* Naming fix

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Add hold step for ios and android build experimental (#2943)

* [CHORE] Add hold step for ios-build-experimental and android-build-experimental

* Android hold step

* add ios hold step

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Remove lodash.isEqual (#2893)

* Added dequal and react-fast-compare as substitutes to lodash.isEqual

* Update ReplyPreview.js

* Remove react-fast-compare

* Removed deep-equal and upgrade babel-eslint dev dependency

* Fix avatar

* Fix Messagebox

* Fix CreateDiscussionView

* ModalBlockView

* NewMessageView

* ProfileView

* RoomInfoEditView

* ServerDropdown

* Return local search as object instead of observable

* SelectedUsersView

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [I18N] Add missing Russian strings (#2946)

* [i18n] Add missing Russian strings

* Couple fixes

* Fix Direct_message

Translate Direct_message as already has been translated

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Use shortcut syntax for get collections (#2932)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Use List.Separator in all places (#2931)

* [FIX] Use List.Separator in all places

* add List.Separator

* change List.Separator

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Limit new message list query size to 50 (#2947)

* Limit query to 50

* Remove observable

* [FIX] Support chats order for older versions of the server (#2934)

* Update mergeSubscriptionsRooms.js

* Update mergeSubscriptionsRooms.js

* Update mergeSubscriptionsRooms.js

* Minor refactor

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Reactions modal's backdrop color too light (#2949)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* Bump version to 4.15.0 (#2950)

* [FIX] Share extension not working correctly on Official app (#2963)

* [FIX] Cannot read property 'some' of undefined on hasPermission (#2966)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Deep linking and other connectivity issues (#2894)

* Navigate from push notification only if necessary

* Use JS SDK branch

* Stop reconnecting if it's already connected

* Fix RoomsListView forever loading after tapping push notification of another server

* Execute fewer operations on app/index

* Remove roomsRequest call from onForeground

* Apply check and reopen

* Stop opening in-app notification when the app is on backgorund

* Connecting tweaks

* Fix deep linking not working if the app is on background

* Force reset yarn cache

* Upgrade JS SDK

* Remove listener on unmount

* Fix resume on Android after back button is pressed

* Fix local authentication resume

* Fix back button android

* Change JS SDK branch

* [FIX] Messagebox's placeholder color is too bright (#2968)

Co-authored-by: Ezequiel de Oliveira <ezequiel1de1oliveira@gmail.com>
Co-authored-by: Dan Caseley <dan@caseley.me.uk>
Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
Co-authored-by: Youssef Muhamad <emaildeyoussefmuhamad@gmail.com>
Co-authored-by: Graham Smith <graham@wiseman-designs.com>
Co-authored-by: Gabriel Henriques <gabriel.henriques@rocket.chat>
Co-authored-by: Júlia Grala <30629556+juliagrala@users.noreply.github.com>
Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com>
Co-authored-by: Prateek Jain <44807945+Prateek93a@users.noreply.github.com>
Co-authored-by: Lucas Siqueira <lucassiqzro@gmail.com>
Co-authored-by: Calebe Rios <calebersmendes@gmail.com>
Co-authored-by: Pitstopper <18574776+Pitstopper@users.noreply.github.com>
Co-authored-by: phriedrich <info@phriedrich.de>
Co-authored-by: Guilherme Siqueira <guilhersiqueira@gmail.com>
Co-authored-by: Prateek Jain <prateek93a@gmail.com>
Co-authored-by: devyaniChoubey <52153085+devyaniChoubey@users.noreply.github.com>
Co-authored-by: Bernard Seow <ssbing99@gmail.com>
Co-authored-by: Hiroki Ishiura <ishiura@ja2.so-net.ne.jp>
Co-authored-by: Exordian <jakob.englisch@gmail.com>
Co-authored-by: Daanchaam <daanhendriks97@gmail.com>
Co-authored-by: Iván Álvarez <ialvarezpereira@gmail.com>
Co-authored-by: Sarthak Pranesh <41206172+sarthakpranesh@users.noreply.github.com>
Co-authored-by: Michele Pellegrini <pellettiero@users.noreply.github.com>
Co-authored-by: Tanmoy Bhowmik <tanmoy.openroot@gmail.com>
Co-authored-by: Hibikine Kage <14365761+hibikine@users.noreply.github.com>
Co-authored-by: Neil Agarwal <neil@neilagarwal.me>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Govind Dixit <GOVINDDIXIT93@GMAIL.COM>
Co-authored-by: Zhaubassarova Aruzhan <49000079+azhaubassar@users.noreply.github.com>
Co-authored-by: Aroo <azhaubassar@gmail.com>
Co-authored-by: Sarthak Pranesh <sarthak.pranesh2018@vitstudent.ac.in>
Co-authored-by: Siddharth Padhi <padhisiddharth31@gmail.com>
Co-authored-by: Bruno Dantas <oliveiradantas96@gmail.com>
Co-authored-by: devyaniChoubey <devyanichoubey16@gmail.com>
Co-authored-by: Heng Sok <sokheng@idatahub.com>
Co-authored-by: Snyk bot <snyk-bot@snyk.io>
Co-authored-by: nixxou <45721836+nixxou@users.noreply.github.com>
Co-authored-by: David-Tsui <st880221@gmail.com>
Co-authored-by: Vincenzo Esposito <aenon.esposito@gmail.com>
Co-authored-by: Rishabh Gupta <38923768+imrishabh18@users.noreply.github.com>
Co-authored-by: Hendy Irawan <hendy@hendyirawan.com>
Co-authored-by: Alexandru Naiman <alex.naiman.4@gmail.com>
Co-authored-by: Dani <assgex@gmail.com>
Co-authored-by: Luis <ljcp28ljcp@gmail.com>
Co-authored-by: zaphod534 <32894570+zaphod534@users.noreply.github.com>
Co-authored-by: ankar84 <ankar84@gmail.com>
Co-authored-by: Карлан Антон Андреевич <KarlanAA@global.bcs>
Co-authored-by: Saket Mahajan <saketmahajan99@gmail.com>
Co-authored-by: Rohit Verma <44283521+refactor-droidyy@users.noreply.github.com>
Co-authored-by: Marco Jakobs <mj@jacotec.de>
Co-authored-by: Daniel Maike <danmke@hotmail.com>
Co-authored-by: Vitor Leal <vitor_leal2201@hotmail.com>
Co-authored-by: Fernando Aguilar <fernando.aguilar@hotmail.com.br>
Co-authored-by: Abdullah Alhamoud <10301923+abalhamoud@users.noreply.github.com>
Co-authored-by: Dave Koo <dkoo761@gmail.com>
Co-authored-by: Fazil Boudjelal <fazildiablou@hotmail.fr>
Co-authored-by: Lucas Dousse <Cormoran96@users.noreply.github.com>
Co-authored-by: Sumukha Hegde <SUMUKHA214@GMAIL.COM>
Co-authored-by: Gerzon Z <gerzonzcanario@gmail.com>
Co-authored-by: Gerzon Z <gerzonc@icloud.com>
Co-authored-by: yash-rajpal <58601732+yash-rajpal@users.noreply.github.com>
Co-authored-by: Hakan YILMAZ <mukerrem.yilmaz@hotmail.com>
Co-authored-by: Arkadyuti Bandyopadhyay <bandyopadhyayarkadyuti@gmail.com>
Co-authored-by: Anant Bhasin <38764067+aKn1ghtOut@users.noreply.github.com>
Co-authored-by: Gung Wah <41157464+kresnaputra@users.noreply.github.com>
Co-authored-by: Billy Newman <newmanw10@gmail.com>
Co-authored-by: Jan Garaj <jan.garaj@gmail.com>
This commit is contained in:
Diego Mello 2021-03-15 14:36:40 -03:00 committed by GitHub
parent 197b78e8f0
commit f5b013f4e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
128 changed files with 1342 additions and 1013 deletions

View File

@ -237,9 +237,13 @@ commands:
if [[ $CIRCLE_JOB == "ios-build-official" ]]; then if [[ $CIRCLE_JOB == "ios-build-official" ]]; then
/usr/libexec/PlistBuddy -c "Set BugsnagAPIKey $BUGSNAG_KEY_OFFICIAL" ./RocketChatRN/Info.plist /usr/libexec/PlistBuddy -c "Set BugsnagAPIKey $BUGSNAG_KEY_OFFICIAL" ./RocketChatRN/Info.plist
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL YES" ./RocketChatRN/Info.plist /usr/libexec/PlistBuddy -c "Set IS_OFFICIAL YES" ./RocketChatRN/Info.plist
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL YES" ./ShareRocketChatRN/Info.plist
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL YES" ./NotificationService/Info.plist
else else
/usr/libexec/PlistBuddy -c "Set BugsnagAPIKey $BUGSNAG_KEY" ./RocketChatRN/Info.plist /usr/libexec/PlistBuddy -c "Set BugsnagAPIKey $BUGSNAG_KEY" ./RocketChatRN/Info.plist
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL NO" ./RocketChatRN/Info.plist /usr/libexec/PlistBuddy -c "Set IS_OFFICIAL NO" ./RocketChatRN/Info.plist
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL NO" ./ShareRocketChatRN/Info.plist
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL NO" ./NotificationService/Info.plist
fi fi
if [[ $APP_STORE_CONNECT_API_KEY ]]; then if [[ $APP_STORE_CONNECT_API_KEY ]]; then
@ -416,9 +420,13 @@ workflows:
- lint-testunit - lint-testunit
# iOS Experimental # iOS Experimental
- ios-hold-build-experimental:
type: approval
requires:
- lint-testunit
- ios-build-experimental: - ios-build-experimental:
requires: requires:
- lint-testunit - ios-hold-build-experimental
- ios-hold-testflight-experimental: - ios-hold-testflight-experimental:
type: approval type: approval
requires: requires:
@ -444,9 +452,13 @@ workflows:
- ios-hold-testflight-official - ios-hold-testflight-official
# Android Experimental # Android Experimental
- android-hold-build-experimental:
type: approval
requires:
- lint-testunit
- android-build-experimental: - android-build-experimental:
requires: requires:
- lint-testunit - android-hold-build-experimental
- android-hold-google-play-beta-experimental: - android-hold-google-play-beta-experimental:
type: approval type: approval
requires: requires:

View File

@ -6,7 +6,7 @@ module.exports = {
} }
} }
}, },
"parser": "babel-eslint", "parser": "@babel/eslint-parser",
"extends": "airbnb", "extends": "airbnb",
"parserOptions": { "parserOptions": {
"sourceType": "module", "sourceType": "module",
@ -21,7 +21,8 @@ module.exports = {
"react", "react",
"jsx-a11y", "jsx-a11y",
"import", "import",
"react-native" "react-native",
"@babel"
], ],
"env": { "env": {
"browser": true, "browser": true,
@ -148,7 +149,8 @@ module.exports = {
"react/jsx-curly-newline": [0], "react/jsx-curly-newline": [0],
"react/state-in-constructor": [0], "react/state-in-constructor": [0],
"no-async-promise-executor": [0], "no-async-promise-executor": [0],
"max-classes-per-file": [0] "max-classes-per-file": [0],
"no-multiple-empty-lines": [0]
}, },
"globals": { "globals": {
"__DEV__": true "__DEV__": true

View File

@ -44404,6 +44404,309 @@ exports[`Storyshots Message list message 1`] = `
</View> </View>
</View> </View>
</View> </View>
<Text
style={
Array [
Object {
"fontSize": 20,
"fontWeight": "300",
"marginLeft": 10,
"marginVertical": 30,
},
Object {
"color": "#0d0e12",
},
Object {
"marginBottom": 0,
"marginTop": 30,
},
]
}
>
Toggle e2e encryption
</Text>
<View
accessible={true}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"opacity": 1,
}
}
>
<View>
<View
style={
Array [
Object {
"flexDirection": "column",
"paddingHorizontal": 14,
"paddingVertical": 4,
"width": "100%",
},
undefined,
]
}
>
<View
style={
Object {
"flexDirection": "row",
}
}
>
<View
style={
Array [
Object {
"borderRadius": 2,
"height": 20,
"width": 20,
},
Object {
"marginLeft": 16,
},
]
}
>
<View
accessible={true}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"opacity": 1,
}
}
>
<View
style={
Array [
Object {
"overflow": "hidden",
},
Object {
"borderRadius": 2,
"height": 20,
"width": 20,
},
]
}
>
<FastImageView
resizeMode="cover"
source={
Object {
"headers": undefined,
"priority": "high",
"uri": "https://open.rocket.chat/avatar/diego.mello?format=png&size=20",
}
}
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
/>
</View>
</View>
</View>
<View
style={
Array [
Object {
"flex": 1,
"marginLeft": 46,
},
Object {
"marginLeft": 10,
},
]
}
>
<Text
accessibilityLabel="This room's encryption has been disabled by diego.mello"
style={
Array [
Object {
"backgroundColor": "transparent",
"fontFamily": "System",
"fontSize": 16,
"fontStyle": "italic",
"fontWeight": "400",
"textAlign": "left",
},
Object {
"color": "#9ca2a8",
},
]
}
>
This room's encryption has been disabled by diego.mello
</Text>
</View>
</View>
</View>
</View>
</View>
<View
accessible={true}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"opacity": 1,
}
}
>
<View>
<View
style={
Array [
Object {
"flexDirection": "column",
"paddingHorizontal": 14,
"paddingVertical": 4,
"width": "100%",
},
undefined,
]
}
>
<View
style={
Object {
"flexDirection": "row",
}
}
>
<View
style={
Array [
Object {
"borderRadius": 2,
"height": 20,
"width": 20,
},
Object {
"marginLeft": 16,
},
]
}
>
<View
accessible={true}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"opacity": 1,
}
}
>
<View
style={
Array [
Object {
"overflow": "hidden",
},
Object {
"borderRadius": 2,
"height": 20,
"width": 20,
},
]
}
>
<FastImageView
resizeMode="cover"
source={
Object {
"headers": undefined,
"priority": "high",
"uri": "https://open.rocket.chat/avatar/diego.mello?format=png&size=20",
}
}
style={
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
/>
</View>
</View>
</View>
<View
style={
Array [
Object {
"flex": 1,
"marginLeft": 46,
},
Object {
"marginLeft": 10,
},
]
}
>
<Text
accessibilityLabel="This room's encryption has been enabled by diego.mello"
style={
Array [
Object {
"backgroundColor": "transparent",
"fontFamily": "System",
"fontSize": 16,
"fontStyle": "italic",
"fontWeight": "400",
"textAlign": "left",
},
Object {
"color": "#9ca2a8",
},
]
}
>
This room's encryption has been enabled by diego.mello
</Text>
</View>
</View>
</View>
</View>
</View>
<Text <Text
style={ style={
Array [ Array [

View File

@ -144,7 +144,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer versionCode VERSIONCODE as Integer
versionName "4.14.1" versionName "4.15.0"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
if (!isFoss) { if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]

View File

@ -29,7 +29,7 @@
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:exported="true" android:exported="true"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTop" android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" /> <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />

View File

@ -70,3 +70,5 @@ export const SETTINGS = createRequestTypes('SETTINGS', ['CLEAR', 'ADD']);
export const APP_STATE = createRequestTypes('APP_STATE', ['FOREGROUND', 'BACKGROUND']); export const APP_STATE = createRequestTypes('APP_STATE', ['FOREGROUND', 'BACKGROUND']);
export const ENTERPRISE_MODULES = createRequestTypes('ENTERPRISE_MODULES', ['CLEAR', 'SET']); export const ENTERPRISE_MODULES = createRequestTypes('ENTERPRISE_MODULES', ['CLEAR', 'SET']);
export const ENCRYPTION = createRequestTypes('ENCRYPTION', ['INIT', 'STOP', 'DECODE_KEY', 'SET', 'SET_BANNER']); export const ENCRYPTION = createRequestTypes('ENCRYPTION', ['INIT', 'STOP', 'DECODE_KEY', 'SET', 'SET_BANNER']);
export const PERMISSIONS = createRequestTypes('PERMISSIONS', ['SET']);

View File

@ -0,0 +1,8 @@
import * as types from './actionsTypes';
export function setPermissions(permissions) {
return {
type: types.PERMISSIONS.SET,
permissions
};
}

View File

@ -63,6 +63,7 @@ export const themes = {
passcodeDotFull: '#6C727A', passcodeDotFull: '#6C727A',
previewBackground: '#1F2329', previewBackground: '#1F2329',
previewTintColor: '#ffffff', previewTintColor: '#ffffff',
backdropOpacity: 0.3,
...mentions ...mentions
}, },
dark: { dark: {
@ -109,6 +110,7 @@ export const themes = {
passcodeDotFull: '#6C727A', passcodeDotFull: '#6C727A',
previewBackground: '#030b1b', previewBackground: '#030b1b',
previewTintColor: '#ffffff', previewTintColor: '#ffffff',
backdropOpacity: 0.9,
...mentions ...mentions
}, },
black: { black: {
@ -155,6 +157,7 @@ export const themes = {
passcodeDotFull: '#6C727A', passcodeDotFull: '#6C727A',
previewBackground: '#000000', previewBackground: '#000000',
previewTintColor: '#ffffff', previewTintColor: '#ffffff',
backdropOpacity: 0.9,
...mentions ...mentions
} }
}; };

View File

@ -101,6 +101,9 @@ export default {
Jitsi_Enabled_TokenAuth: { Jitsi_Enabled_TokenAuth: {
type: 'valueAsBoolean' type: 'valueAsBoolean'
}, },
Jitsi_URL_Room_Hash: {
type: 'valueAsBoolean'
},
Jitsi_URL_Room_Prefix: { Jitsi_URL_Room_Prefix: {
type: 'valueAsString' type: 'valueAsString'
}, },

View File

@ -147,7 +147,7 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
const animatedPosition = React.useRef(new Value(0)); const animatedPosition = React.useRef(new Value(0));
const opacity = interpolate(animatedPosition.current, { const opacity = interpolate(animatedPosition.current, {
inputRange: [0, 1], inputRange: [0, 1],
outputRange: [0, 0.7], outputRange: [0, themes[theme].backdropOpacity],
extrapolate: Extrapolate.CLAMP extrapolate: Extrapolate.CLAMP
}); });

View File

@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import isEqual from 'react-fast-compare';
import database from '../../lib/database'; import database from '../../lib/database';
import { getUserSelector } from '../../selectors/login'; import { getUserSelector } from '../../selectors/login';
@ -34,7 +33,8 @@ class AvatarContainer extends React.Component {
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if (!isEqual(prevProps, this.props)) { const { text, type } = this.props;
if (prevProps.text !== text || prevProps.type !== type) {
this.init(); this.init();
} }
} }
@ -52,8 +52,8 @@ class AvatarContainer extends React.Component {
init = async() => { init = async() => {
const db = database.active; const db = database.active;
const usersCollection = db.collections.get('users'); const usersCollection = db.get('users');
const subsCollection = db.collections.get('subscriptions'); const subsCollection = db.get('subscriptions');
let record; let record;
try { try {

View File

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import { View } from 'react-native'; import { View } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ScrollableTabView from 'react-native-scrollable-tab-view'; import ScrollableTabView from 'react-native-scrollable-tab-view';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import orderBy from 'lodash/orderBy'; import orderBy from 'lodash/orderBy';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
@ -67,7 +67,7 @@ class EmojiPicker extends Component {
if (nextState.width !== width) { if (nextState.width !== width) {
return true; return true;
} }
if (!equal(nextState.frequentlyUsed, frequentlyUsed)) { if (!dequal(nextState.frequentlyUsed, frequentlyUsed)) {
return true; return true;
} }
return false; return false;
@ -95,7 +95,7 @@ class EmojiPicker extends Component {
// eslint-disable-next-line react/sort-comp // eslint-disable-next-line react/sort-comp
_addFrequentlyUsed = protectedFunction(async(emoji) => { _addFrequentlyUsed = protectedFunction(async(emoji) => {
const db = database.active; const db = database.active;
const freqEmojiCollection = db.collections.get('frequently_used_emojis'); const freqEmojiCollection = db.get('frequently_used_emojis');
let freqEmojiRecord; let freqEmojiRecord;
try { try {
freqEmojiRecord = await freqEmojiCollection.find(emoji.content); freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
@ -120,7 +120,7 @@ class EmojiPicker extends Component {
updateFrequentlyUsed = async() => { updateFrequentlyUsed = async() => {
const db = database.active; const db = database.active;
const frequentlyUsedRecords = await db.collections.get('frequently_used_emojis').query().fetch(); const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch();
let frequentlyUsed = orderBy(frequentlyUsedRecords, ['count'], ['desc']); let frequentlyUsed = orderBy(frequentlyUsedRecords, ['count'], ['desc']);
frequentlyUsed = frequentlyUsed.map((item) => { frequentlyUsed = frequentlyUsed.map((item) => {
if (item.isCustom) { if (item.isCustom) {

View File

@ -1,5 +1,8 @@
import React, { memo, useEffect } from 'react'; import React, { memo, useEffect } from 'react';
import PropTypes from 'prop-types';
import { NotifierRoot, Notifier, Easing } from 'react-native-notifier'; import { NotifierRoot, Notifier, Easing } from 'react-native-notifier';
import { connect } from 'react-redux';
import { dequal } from 'dequal';
import NotifierComponent from './NotifierComponent'; import NotifierComponent from './NotifierComponent';
import EventEmitter from '../../utils/events'; import EventEmitter from '../../utils/events';
@ -8,13 +11,17 @@ import { getActiveRoute } from '../../utils/navigation';
export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp'; export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp';
const InAppNotification = memo(() => { const InAppNotification = memo(({ rooms, appState }) => {
const show = (notification) => { const show = (notification) => {
if (appState !== 'foreground') {
return;
}
const { payload } = notification; const { payload } = notification;
const state = Navigation.navigationRef.current?.getRootState(); const state = Navigation.navigationRef.current?.getRootState();
const route = getActiveRoute(state); const route = getActiveRoute(state);
if (payload.rid) { if (payload.rid) {
if ((route?.name === 'RoomView' && route.params?.rid === payload.rid) || route?.name === 'JitsiMeetView') { if (rooms.includes(payload.rid) || route?.name === 'JitsiMeetView') {
return; return;
} }
Notifier.showNotification({ Notifier.showNotification({
@ -28,13 +35,23 @@ const InAppNotification = memo(() => {
}; };
useEffect(() => { useEffect(() => {
EventEmitter.addEventListener(INAPP_NOTIFICATION_EMITTER, show); const listener = EventEmitter.addEventListener(INAPP_NOTIFICATION_EMITTER, show);
return () => { return () => {
EventEmitter.removeListener(INAPP_NOTIFICATION_EMITTER); EventEmitter.removeListener(INAPP_NOTIFICATION_EMITTER, listener);
}; };
}, []); }, [rooms]);
return <NotifierRoot />; return <NotifierRoot />;
}, (prevProps, nextProps) => dequal(prevProps.rooms, nextProps.rooms));
const mapStateToProps = state => ({
rooms: state.room.rooms,
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
}); });
export default InAppNotification; InAppNotification.propTypes = {
rooms: PropTypes.array,
appState: PropTypes.string
};
export default connect(mapStateToProps)(InAppNotification);

View File

@ -96,7 +96,7 @@ const Header = React.memo(({
const setEmojis = async() => { const setEmojis = async() => {
try { try {
const db = database.active; const db = database.active;
const freqEmojiCollection = db.collections.get('frequently_used_emojis'); const freqEmojiCollection = db.get('frequently_used_emojis');
let freqEmojis = await freqEmojiCollection.query().fetch(); let freqEmojis = await freqEmojiCollection.query().fetch();
const isLandscape = width > height; const isLandscape = width > height;

View File

@ -34,20 +34,24 @@ const MessageActions = React.memo(forwardRef(({
Message_AllowPinning, Message_AllowPinning,
Message_AllowStarring, Message_AllowStarring,
Message_Read_Receipt_Store_Users, Message_Read_Receipt_Store_Users,
isMasterDetail isMasterDetail,
editMessagePermission,
deleteMessagePermission,
forceDeleteMessagePermission,
pinMessagePermission
}, ref) => { }, ref) => {
let permissions = {}; let permissions = {};
const { showActionSheet, hideActionSheet } = useActionSheet(); const { showActionSheet, hideActionSheet } = useActionSheet();
const getPermissions = async() => { const getPermissions = async() => {
try { try {
const permission = ['edit-message', 'delete-message', 'force-delete-message', 'pin-message']; const permission = [editMessagePermission, deleteMessagePermission, forceDeleteMessagePermission, pinMessagePermission];
const result = await RocketChat.hasPermission(permission, room.rid); const result = await RocketChat.hasPermission(permission, room.rid);
permissions = { permissions = {
hasEditPermission: result[permission[0]], hasEditPermission: result[0],
hasDeletePermission: result[permission[1]], hasDeletePermission: result[1],
hasForceDeletePermission: result[permission[2]], hasForceDeletePermission: result[2],
hasPinPermission: result[permission[3]] hasPinPermission: result[3]
}; };
} catch { } catch {
// Do nothing // Do nothing
@ -141,7 +145,7 @@ const MessageActions = React.memo(forwardRef(({
const db = database.active; const db = database.active;
const result = await RocketChat.markAsUnread({ messageId }); const result = await RocketChat.markAsUnread({ messageId });
if (result.success) { if (result.success) {
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
const subRecord = await subCollection.find(rid); const subRecord = await subCollection.find(rid);
await db.action(async() => { await db.action(async() => {
try { try {
@ -171,7 +175,7 @@ const MessageActions = React.memo(forwardRef(({
const handleCopy = async(message) => { const handleCopy = async(message) => {
logEvent(events.ROOM_MSG_ACTION_COPY); logEvent(events.ROOM_MSG_ACTION_COPY);
await Clipboard.setString(message.msg); await Clipboard.setString(message?.attachments?.[0]?.description || message.msg);
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') }); EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
}; };
@ -440,7 +444,11 @@ MessageActions.propTypes = {
Message_AllowPinning: PropTypes.bool, Message_AllowPinning: PropTypes.bool,
Message_AllowStarring: PropTypes.bool, Message_AllowStarring: PropTypes.bool,
Message_Read_Receipt_Store_Users: PropTypes.bool, Message_Read_Receipt_Store_Users: PropTypes.bool,
server: PropTypes.string server: PropTypes.string,
editMessagePermission: PropTypes.array,
deleteMessagePermission: PropTypes.array,
forceDeleteMessagePermission: PropTypes.array,
pinMessagePermission: PropTypes.array
}; };
const mapStateToProps = state => ({ const mapStateToProps = state => ({
@ -452,7 +460,11 @@ const mapStateToProps = state => ({
Message_AllowPinning: state.settings.Message_AllowPinning, Message_AllowPinning: state.settings.Message_AllowPinning,
Message_AllowStarring: state.settings.Message_AllowStarring, Message_AllowStarring: state.settings.Message_AllowStarring,
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users, Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users,
isMasterDetail: state.app.isMasterDetail isMasterDetail: state.app.isMasterDetail,
editMessagePermission: state.permissions['edit-message'],
deleteMessagePermission: state.permissions['delete-message'],
forceDeleteMessagePermission: state.permissions['force-delete-message'],
pinMessagePermission: state.permissions['pin-message']
}); });
export default connect(mapStateToProps, null, null, { forwardRef: true })(MessageActions); export default connect(mapStateToProps, null, null, { forwardRef: true })(MessageActions);

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { FlatList } from 'react-native'; import { FlatList } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import Item from './Item'; import Item from './Item';
import styles from '../styles'; import styles from '../styles';
@ -31,7 +31,7 @@ const CommandsPreview = React.memo(({ theme, commandPreview, showCommandPreview
if (prevProps.showCommandPreview !== nextProps.showCommandPreview) { if (prevProps.showCommandPreview !== nextProps.showCommandPreview) {
return false; return false;
} }
if (!equal(prevProps.commandPreview, nextProps.commandPreview)) { if (!dequal(prevProps.commandPreview, nextProps.commandPreview)) {
return false; return false;
} }
return true; return true;

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { FlatList, View } from 'react-native'; import { FlatList, View } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import styles from '../styles'; import styles from '../styles';
import MentionItem from './MentionItem'; import MentionItem from './MentionItem';
@ -30,7 +30,7 @@ const Mentions = React.memo(({ mentions, trackingType, theme }) => {
if (prevProps.trackingType !== nextProps.trackingType) { if (prevProps.trackingType !== nextProps.trackingType) {
return false; return false;
} }
if (!equal(prevProps.mentions, nextProps.mentions)) { if (!dequal(prevProps.mentions, nextProps.mentions)) {
return false; return false;
} }
return true; return true;

View File

@ -3,7 +3,6 @@ import { View, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import moment from 'moment'; import moment from 'moment';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import isEqual from 'lodash/isEqual';
import Markdown from '../markdown'; import Markdown from '../markdown';
import { CustomIcon } from '../../lib/Icons'; import { CustomIcon } from '../../lib/Icons';
@ -43,7 +42,7 @@ const styles = StyleSheet.create({
}); });
const ReplyPreview = React.memo(({ const ReplyPreview = React.memo(({
message, Message_TimeFormat, baseUrl, username, replying, getCustomEmoji, close, theme message, Message_TimeFormat, baseUrl, username, replying, getCustomEmoji, close, theme, useRealName
}) => { }) => {
if (!replying) { if (!replying) {
return null; return null;
@ -59,7 +58,7 @@ const ReplyPreview = React.memo(({
> >
<View style={[styles.messageContainer, { backgroundColor: themes[theme].chatComponentBackground }]}> <View style={[styles.messageContainer, { backgroundColor: themes[theme].chatComponentBackground }]}>
<View style={styles.header}> <View style={styles.header}>
<Text style={[styles.username, { color: themes[theme].tintColor }]}>{message.u?.username}</Text> <Text style={[styles.username, { color: themes[theme].tintColor }]}>{useRealName ? message.u?.name : message.u?.username}</Text>
<Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text> <Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
</View> </View>
<Markdown <Markdown
@ -75,7 +74,7 @@ const ReplyPreview = React.memo(({
<CustomIcon name='close' color={themes[theme].auxiliaryText} size={20} style={styles.close} onPress={close} /> <CustomIcon name='close' color={themes[theme].auxiliaryText} size={20} style={styles.close} onPress={close} />
</View> </View>
); );
}, (prevProps, nextProps) => prevProps.replying === nextProps.replying && prevProps.theme === nextProps.theme && isEqual(prevProps.message, nextProps.message)); }, (prevProps, nextProps) => prevProps.replying === nextProps.replying && prevProps.theme === nextProps.theme && prevProps.message.id === nextProps.message.id);
ReplyPreview.propTypes = { ReplyPreview.propTypes = {
replying: PropTypes.bool, replying: PropTypes.bool,
@ -85,12 +84,14 @@ ReplyPreview.propTypes = {
baseUrl: PropTypes.string.isRequired, baseUrl: PropTypes.string.isRequired,
username: PropTypes.string.isRequired, username: PropTypes.string.isRequired,
getCustomEmoji: PropTypes.func, getCustomEmoji: PropTypes.func,
theme: PropTypes.string theme: PropTypes.string,
useRealName: PropTypes.bool
}; };
const mapStateToProps = state => ({ const mapStateToProps = state => ({
Message_TimeFormat: state.settings.Message_TimeFormat, Message_TimeFormat: state.settings.Message_TimeFormat,
baseUrl: state.server.server baseUrl: state.server.server,
useRealName: state.settings.UI_Use_Real_Name
}); });
export default connect(mapStateToProps)(ReplyPreview); export default connect(mapStateToProps)(ReplyPreview);

View File

@ -6,7 +6,7 @@ import {
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard'; import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard';
import ImagePicker from 'react-native-image-crop-picker'; import ImagePicker from 'react-native-image-crop-picker';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import DocumentPicker from 'react-native-document-picker'; import DocumentPicker from 'react-native-document-picker';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler'; import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
@ -63,6 +63,7 @@ const imagePickerConfig = {
const libraryPickerConfig = { const libraryPickerConfig = {
multiple: true, multiple: true,
compressVideoPreset: 'Passthrough',
mediaType: 'any' mediaType: 'any'
}; };
@ -189,8 +190,8 @@ class MessageBox extends Component {
} = this.props; } = this.props;
let msg; let msg;
try { try {
const threadsCollection = db.collections.get('threads'); const threadsCollection = db.get('threads');
const subsCollection = db.collections.get('subscriptions'); const subsCollection = db.get('subscriptions');
try { try {
this.room = await subsCollection.find(rid); this.room = await subsCollection.find(rid);
} catch (error) { } catch (error) {
@ -270,7 +271,7 @@ class MessageBox extends Component {
} = this.state; } = this.state;
const { const {
roomType, replying, editing, isFocused, message, theme, children roomType, replying, editing, isFocused, message, theme
} = this.props; } = this.props;
if (nextProps.theme !== theme) { if (nextProps.theme !== theme) {
return true; return true;
@ -299,16 +300,13 @@ class MessageBox extends Component {
if (nextState.tshow !== tshow) { if (nextState.tshow !== tshow) {
return true; return true;
} }
if (!equal(nextState.mentions, mentions)) { if (!dequal(nextState.mentions, mentions)) {
return true; return true;
} }
if (!equal(nextState.commandPreview, commandPreview)) { if (!dequal(nextState.commandPreview, commandPreview)) {
return true; return true;
} }
if (!equal(nextProps.message, message)) { if (!dequal(nextProps.message?.id, message?.id)) {
return true;
}
if (!equal(nextProps.children, children)) {
return true; return true;
} }
return false; return false;
@ -366,7 +364,7 @@ class MessageBox extends Component {
const slashCommand = text.match(/^\/([a-z0-9._-]+) (.+)/im); const slashCommand = text.match(/^\/([a-z0-9._-]+) (.+)/im);
if (slashCommand) { if (slashCommand) {
const [, name, params] = slashCommand; const [, name, params] = slashCommand;
const commandsCollection = db.collections.get('slash_commands'); const commandsCollection = db.get('slash_commands');
try { try {
const command = await commandsCollection.find(name); const command = await commandsCollection.find(name);
if (command.providesPreview) { if (command.providesPreview) {
@ -507,7 +505,7 @@ class MessageBox extends Component {
getEmojis = debounce(async(keyword) => { getEmojis = debounce(async(keyword) => {
const db = database.active; const db = database.active;
if (keyword) { if (keyword) {
const customEmojisCollection = db.collections.get('custom_emojis'); const customEmojisCollection = db.get('custom_emojis');
const likeString = sanitizeLikeString(keyword); const likeString = sanitizeLikeString(keyword);
let customEmojis = await customEmojisCollection.query( let customEmojis = await customEmojisCollection.query(
Q.where('name', Q.like(`${ likeString }%`)) Q.where('name', Q.like(`${ likeString }%`))
@ -521,7 +519,7 @@ class MessageBox extends Component {
getSlashCommands = debounce(async(keyword) => { getSlashCommands = debounce(async(keyword) => {
const db = database.active; const db = database.active;
const commandsCollection = db.collections.get('slash_commands'); const commandsCollection = db.get('slash_commands');
const likeString = sanitizeLikeString(keyword); const likeString = sanitizeLikeString(keyword);
const commands = await commandsCollection.query( const commands = await commandsCollection.query(
Q.where('id', Q.like(`${ likeString }%`)) Q.where('id', Q.like(`${ likeString }%`))
@ -753,7 +751,7 @@ class MessageBox extends Component {
// Slash command // Slash command
if (message[0] === MENTIONS_TRACKING_TYPE_COMMANDS) { if (message[0] === MENTIONS_TRACKING_TYPE_COMMANDS) {
const db = database.active; const db = database.active;
const commandsCollection = db.collections.get('slash_commands'); const commandsCollection = db.get('slash_commands');
const command = message.replace(/ .*/, '').slice(1); const command = message.replace(/ .*/, '').slice(1);
const likeString = sanitizeLikeString(command); const likeString = sanitizeLikeString(command);
const slashCommand = await commandsCollection.query( const slashCommand = await commandsCollection.query(
@ -941,7 +939,7 @@ class MessageBox extends Component {
keyboardType='twitter' keyboardType='twitter'
blurOnSubmit={false} blurOnSubmit={false}
placeholder={I18n.t('New_Message')} placeholder={I18n.t('New_Message')}
placeholderTextColor={themes[theme].auxiliaryTintColor} placeholderTextColor={themes[theme].auxiliaryText}
onChangeText={this.onChangeText} onChangeText={this.onChangeText}
onSelectionChange={this.onSelectionChange} onSelectionChange={this.onSelectionChange}
underlineColorAndroid='transparent' underlineColorAndroid='transparent'

View File

@ -19,8 +19,8 @@ const MessageErrorActions = forwardRef(({ tmid }, ref) => {
try { try {
const db = database.active; const db = database.active;
const deleteBatch = []; const deleteBatch = [];
const msgCollection = db.collections.get('messages'); const msgCollection = db.get('messages');
const threadCollection = db.collections.get('threads'); const threadCollection = db.get('threads');
// Delete the object (it can be Message or ThreadMessage instance) // Delete the object (it can be Message or ThreadMessage instance)
deleteBatch.push(message.prepareDestroyPermanently()); deleteBatch.push(message.prepareDestroyPermanently());

View File

@ -28,7 +28,7 @@ class Toast extends React.Component {
} }
componentDidMount() { componentDidMount() {
EventEmitter.addEventListener(LISTENER, this.showToast); this.listener = EventEmitter.addEventListener(LISTENER, this.showToast);
} }
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
@ -40,7 +40,7 @@ class Toast extends React.Component {
} }
componentWillUnmount() { componentWillUnmount() {
EventEmitter.removeListener(LISTENER); EventEmitter.removeListener(LISTENER, this.listener);
} }
getToastRef = toast => this.toast = toast; getToastRef = toast => this.toast = toast;

View File

@ -58,9 +58,9 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }) => {
const showTwoFactor = args => setData(args); const showTwoFactor = args => setData(args);
useEffect(() => { useEffect(() => {
EventEmitter.addEventListener(TWO_FACTOR, showTwoFactor); const listener = EventEmitter.addEventListener(TWO_FACTOR, showTwoFactor);
return () => EventEmitter.removeListener(TWO_FACTOR); return () => EventEmitter.removeListener(TWO_FACTOR, listener);
}, []); }, []);
const onCancel = () => { const onCancel = () => {

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { import {
View, Text, TouchableWithoutFeedback, Modal, KeyboardAvoidingView, Animated, Easing View, Text, TouchableWithoutFeedback, Modal, KeyboardAvoidingView, Animated, Easing, StyleSheet
} from 'react-native'; } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit'; import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
@ -172,7 +172,7 @@ export const MultiSelect = React.memo(({
> >
<TouchableWithoutFeedback onPress={onHide}> <TouchableWithoutFeedback onPress={onHide}>
<View style={styles.container}> <View style={styles.container}>
<View style={[styles.backdrop, { backgroundColor: themes[theme].backdropColor }]} /> <View style={{ ...StyleSheet.absoluteFill, opacity: themes[theme].backdropOpacity, backgroundColor: themes[theme].backdropColor }} />
<KeyboardAvoidingView style={styles.keyboardView} behavior={behavior}> <KeyboardAvoidingView style={styles.keyboardView} behavior={behavior}>
<Animated.View style={[styles.animatedContent, { transform: [{ translateY }] }]}> <Animated.View style={[styles.animatedContent, { transform: [{ translateY }] }]}>
{showContent ? renderContent() : null} {showContent ? renderContent() : null}

View File

@ -8,10 +8,6 @@ export default StyleSheet.create({
alignItems: 'center', alignItems: 'center',
justifyContent: 'flex-end' justifyContent: 'flex-end'
}, },
backdrop: {
...StyleSheet.absoluteFill,
opacity: 0.3
},
modal: { modal: {
height: 300, height: 300,
width: '100%', width: '100%',

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import isEqual from 'lodash/isEqual'; import { dequal } from 'dequal';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Image from './Image'; import Image from './Image';
@ -28,7 +28,7 @@ const Attachments = React.memo(({
// eslint-disable-next-line react/no-array-index-key // eslint-disable-next-line react/no-array-index-key
return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} theme={theme} />; return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} theme={theme} />;
}); });
}, (prevProps, nextProps) => isEqual(prevProps.attachments, nextProps.attachments) && prevProps.theme === nextProps.theme); }, (prevProps, nextProps) => dequal(prevProps.attachments, nextProps.attachments) && prevProps.theme === nextProps.theme);
Attachments.propTypes = { Attachments.propTypes = {
attachments: PropTypes.array, attachments: PropTypes.array,

View File

@ -6,7 +6,7 @@ import {
import { Audio } from 'expo-av'; import { Audio } from 'expo-av';
import Slider from '@react-native-community/slider'; import Slider from '@react-native-community/slider';
import moment from 'moment'; import moment from 'moment';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake'; import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
import Touchable from './Touchable'; import Touchable from './Touchable';
@ -150,7 +150,7 @@ class MessageAudio extends React.Component {
if (nextState.paused !== paused) { if (nextState.paused !== paused) {
return true; return true;
} }
if (!equal(nextProps.file, file)) { if (!dequal(nextProps.file, file)) {
return true; return true;
} }
if (nextState.loading !== loading) { if (nextState.loading !== loading) {

View File

@ -1,7 +1,7 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import I18n from '../../i18n'; import I18n from '../../i18n';
import styles from './styles'; import styles from './styles';
@ -108,10 +108,10 @@ const Content = React.memo((props) => {
if (prevProps.isIgnored !== nextProps.isIgnored) { if (prevProps.isIgnored !== nextProps.isIgnored) {
return false; return false;
} }
if (!equal(prevProps.mentions, nextProps.mentions)) { if (!dequal(prevProps.mentions, nextProps.mentions)) {
return false; return false;
} }
if (!equal(prevProps.channels, nextProps.channels)) { if (!dequal(prevProps.channels, nextProps.channels)) {
return false; return false;
} }
return true; return true;

View File

@ -2,7 +2,7 @@ import React, { useContext } from 'react';
import { View } from 'react-native'; import { View } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import FastImage from '@rocket.chat/react-native-fast-image'; import FastImage from '@rocket.chat/react-native-fast-image';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import { createImageProgress } from 'react-native-image-progress'; import { createImageProgress } from 'react-native-image-progress';
import * as Progress from 'react-native-progress'; import * as Progress from 'react-native-progress';
@ -66,7 +66,7 @@ const ImageContainer = React.memo(({
<MessageImage img={img} theme={theme} /> <MessageImage img={img} theme={theme} />
</Button> </Button>
); );
}, (prevProps, nextProps) => equal(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme); }, (prevProps, nextProps) => dequal(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme);
ImageContainer.propTypes = { ImageContainer.propTypes = {
file: PropTypes.object, file: PropTypes.object,

View File

@ -119,7 +119,7 @@ const MessageTouchable = React.memo((props) => {
<Touchable <Touchable
onLongPress={onLongPress} onLongPress={onLongPress}
onPress={onPress} onPress={onPress}
disabled={props.isInfo || props.archived || props.isTemp} disabled={(props.isInfo && !props.isThreadReply) || props.archived || props.isTemp}
> >
<View> <View>
<Message {...props} /> <Message {...props} />
@ -132,6 +132,7 @@ MessageTouchable.displayName = 'MessageTouchable';
MessageTouchable.propTypes = { MessageTouchable.propTypes = {
hasError: PropTypes.bool, hasError: PropTypes.bool,
isInfo: PropTypes.bool, isInfo: PropTypes.bool,
isThreadReply: PropTypes.bool,
isTemp: PropTypes.bool, isTemp: PropTypes.bool,
archived: PropTypes.bool archived: PropTypes.bool
}; };

View File

@ -2,7 +2,7 @@ import React, { useContext } from 'react';
import { View, Text, StyleSheet } from 'react-native'; import { View, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import moment from 'moment'; import moment from 'moment';
import isEqual from 'deep-equal'; import { dequal } from 'dequal';
import Touchable from './Touchable'; import Touchable from './Touchable';
import Markdown from '../markdown'; import Markdown from '../markdown';
@ -125,7 +125,7 @@ const Fields = React.memo(({ attachment, theme }) => {
))} ))}
</View> </View>
); );
}, (prevProps, nextProps) => isEqual(prevProps.attachment.fields, nextProps.attachment.fields) && prevProps.theme === nextProps.theme); }, (prevProps, nextProps) => dequal(prevProps.attachment.fields, nextProps.attachment.fields) && prevProps.theme === nextProps.theme);
const Reply = React.memo(({ const Reply = React.memo(({
attachment, timeFormat, index, getCustomEmoji, theme attachment, timeFormat, index, getCustomEmoji, theme
@ -172,7 +172,6 @@ const Reply = React.memo(({
/> />
<Description <Description
attachment={attachment} attachment={attachment}
timeFormat={timeFormat}
getCustomEmoji={getCustomEmoji} getCustomEmoji={getCustomEmoji}
theme={theme} theme={theme}
/> />
@ -188,7 +187,7 @@ const Reply = React.memo(({
/> />
</> </>
); );
}, (prevProps, nextProps) => isEqual(prevProps.attachment, nextProps.attachment) && prevProps.theme === nextProps.theme); }, (prevProps, nextProps) => dequal(prevProps.attachment, nextProps.attachment) && prevProps.theme === nextProps.theme);
Reply.propTypes = { Reply.propTypes = {
attachment: PropTypes.object, attachment: PropTypes.object,

View File

@ -4,7 +4,7 @@ import {
} from 'react-native'; } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import FastImage from '@rocket.chat/react-native-fast-image'; import FastImage from '@rocket.chat/react-native-fast-image';
import isEqual from 'lodash/isEqual'; import { dequal } from 'dequal';
import Touchable from './Touchable'; import Touchable from './Touchable';
import openLink from '../../utils/openLink'; import openLink from '../../utils/openLink';
@ -112,7 +112,7 @@ const Url = React.memo(({ url, index, theme }) => {
</> </>
</Touchable> </Touchable>
); );
}, (oldProps, newProps) => isEqual(oldProps.url, newProps.url) && oldProps.theme === newProps.theme); }, (oldProps, newProps) => dequal(oldProps.url, newProps.url) && oldProps.theme === newProps.theme);
const Urls = React.memo(({ urls, theme }) => { const Urls = React.memo(({ urls, theme }) => {
if (!urls || urls.length === 0) { if (!urls || urls.length === 0) {
@ -122,7 +122,7 @@ const Urls = React.memo(({ urls, theme }) => {
return urls.map((url, index) => ( return urls.map((url, index) => (
<Url url={url} key={url.url} index={index} theme={theme} /> <Url url={url} key={url.url} index={index} theme={theme} />
)); ));
}, (oldProps, newProps) => isEqual(oldProps.urls, newProps.urls) && oldProps.theme === newProps.theme); }, (oldProps, newProps) => dequal(oldProps.urls, newProps.urls) && oldProps.theme === newProps.theme);
UrlImage.propTypes = { UrlImage.propTypes = {
image: PropTypes.string image: PropTypes.string

View File

@ -1,7 +1,7 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
import isEqual from 'deep-equal'; import { dequal } from 'dequal';
import Touchable from './Touchable'; import Touchable from './Touchable';
import Markdown from '../markdown'; import Markdown from '../markdown';
@ -57,7 +57,7 @@ const Video = React.memo(({
<Markdown msg={file.description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} /> <Markdown msg={file.description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
</> </>
); );
}, (prevProps, nextProps) => isEqual(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme); }, (prevProps, nextProps) => dequal(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme);
Video.propTypes = { Video.propTypes = {
file: PropTypes.object, file: PropTypes.object,

View File

@ -37,7 +37,9 @@ export const SYSTEM_MESSAGES = [
'room_changed_privacy', 'room_changed_privacy',
'room_changed_avatar', 'room_changed_avatar',
'message_snippeted', 'message_snippeted',
'thread-created' 'thread-created',
'room_e2e_enabled',
'room_e2e_disabled'
]; ];
export const SYSTEM_MESSAGE_TYPES = { export const SYSTEM_MESSAGE_TYPES = {
@ -100,6 +102,10 @@ export const getInfoMessage = ({
return I18n.t('Room_changed_avatar', { userBy: username }); return I18n.t('Room_changed_avatar', { userBy: username });
} else if (type === 'message_snippeted') { } else if (type === 'message_snippeted') {
return I18n.t('Created_snippet'); return I18n.t('Created_snippet');
} else if (type === 'room_e2e_disabled') {
return I18n.t('This_room_encryption_has_been_disabled_by__username_', { username });
} else if (type === 'room_e2e_enabled') {
return I18n.t('This_room_encryption_has_been_enabled_by__username_', { username });
} }
return ''; return '';
}; };

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FlatList } from 'react-native'; import { FlatList } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import isEqual from 'react-fast-compare'; import { dequal } from 'dequal';
import I18n from '../../../i18n'; import I18n from '../../../i18n';
import RoomItem, { ROW_HEIGHT } from '../../../presentation/RoomItem'; import RoomItem, { ROW_HEIGHT } from '../../../presentation/RoomItem';
@ -56,7 +56,7 @@ class QueueListView extends React.Component {
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
const { queued } = this.props; const { queued } = this.props;
if (!isEqual(nextProps.queued, queued)) { if (!dequal(nextProps.queued, queued)) {
return true; return true;
} }

View File

@ -704,5 +704,7 @@ export default {
Direct_message: 'Direct message', Direct_message: 'Direct message',
Message_Ignored: 'Message ignored. Tap to display it.', Message_Ignored: 'Message ignored. Tap to display it.',
Enter_workspace_URL: 'Enter workspace URL', Enter_workspace_URL: 'Enter workspace URL',
Workspace_URL_Example: 'Ex. your-company.rocket.chat' Workspace_URL_Example: 'Ex. your-company.rocket.chat',
This_room_encryption_has_been_enabled_by__username_: 'This room\'s encryption has been enabled by {{username}}',
This_room_encryption_has_been_disabled_by__username_: 'This room\'s encryption has been disabled by {{username}}'
}; };

View File

@ -651,5 +651,7 @@ export default {
Direct_message: 'Mensagem direta', Direct_message: 'Mensagem direta',
Message_Ignored: 'Mensagem ignorada. Toque para mostrar.', Message_Ignored: 'Mensagem ignorada. Toque para mostrar.',
Enter_workspace_URL: 'Digite a URL da sua workspace', Enter_workspace_URL: 'Digite a URL da sua workspace',
Workspace_URL_Example: 'Ex. sua-empresa.rocket.chat' Workspace_URL_Example: 'Ex. sua-empresa.rocket.chat',
This_room_encryption_has_been_enabled_by__username_: 'A criptografia para essa sala foi habilitada por {{username}}',
This_room_encryption_has_been_disabled_by__username_: 'A criptografia para essa sala foi desabilitada por {{username}}'
}; };

View File

@ -678,5 +678,31 @@ export default {
No_threads: 'Тредов нет', No_threads: 'Тредов нет',
No_threads_following: 'Нет тредов, за которыми вы следите', No_threads_following: 'Нет тредов, за которыми вы следите',
No_threads_unread: 'Непрочитанных тредов нет', No_threads_unread: 'Непрочитанных тредов нет',
Messagebox_Send_to_channel: 'Отправить в чат' Messagebox_Send_to_channel: 'Отправить в чат',
Set_as_leader: 'Назначить лидером',
Set_as_moderator: 'Назначить модератором',
Set_as_owner: 'Назначить владельцем',
Remove_as_leader: 'Удалить из лидеров',
Remove_as_moderator: 'Удалить из модераторов',
Remove_as_owner: 'Удалить из владельцев',
Remove_from_room: 'Удалить из чата',
Ignore: 'Игнориновать',
Unignore: 'Прекратить игнорировать',
User_has_been_ignored: 'Пользователь теперь игнорируется',
User_has_been_unignored: 'Пользователь больше не игнорируется',
User_has_been_removed_from_s: 'Пользователь удален из {{s}}',
User__username__is_now_a_leader_of__room_name_: 'Пользователь {{username}} больше не лидер в чате {{room_name}}',
User__username__is_now_a_moderator_of__room_name_: 'Пользователь {{username}} больше не модератор в чате {{room_name}}',
User__username__is_now_a_owner_of__room_name_: 'Пользователь {{username}} больше не владелец в чате {{room_name}}',
User__username__removed_from__room_name__leaders: 'Пользователь {{username}} удален из {{room_name}} лидеров',
User__username__removed_from__room_name__moderators: 'Пользователь {{username}} удален из {{room_name}} модераторов',
User__username__removed_from__room_name__owners: 'Пользователь {{username}} удален из {{room_name}} владельцев',
The_user_will_be_removed_from_s: 'Пользователь будет удален из {{s}}',
Yes_remove_user: 'Да, удалить пользователя!',
Direct_message: 'Личное сообщение',
Message_Ignored: 'Сообщение игнорируется. Тапните по нему, чтобы отобразить его.',
Enter_workspace_URL: 'Введите URL вашего рабочего пространства',
Workspace_URL_Example: 'Например, your-company.rocket.chat',
This_room_encryption_has_been_enabled_by__username_: 'Шифрование для этого чата включено {{username}}',
This_room_encryption_has_been_disabled_by__username_: 'Шифрование для этого чата выключено {{username}}'
}; };

View File

@ -112,16 +112,25 @@ export default class Root extends React.Component {
init = async() => { init = async() => {
UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then(this.setTheme); UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then(this.setTheme);
const [notification, deepLinking] = await Promise.all([initializePushNotifications(), Linking.getInitialURL()]);
const parsedDeepLinkingURL = parseDeepLinking(deepLinking);
store.dispatch(appInitLocalSettings()); store.dispatch(appInitLocalSettings());
// Open app from push notification
const notification = await initializePushNotifications();
if (notification) { if (notification) {
onNotification(notification); onNotification(notification);
} else if (parsedDeepLinkingURL) { return;
store.dispatch(deepLinkingOpen(parsedDeepLinkingURL));
} else {
store.dispatch(appInit());
} }
// Open app from deep linking
const deepLinking = await Linking.getInitialURL();
const parsedDeepLinkingURL = parseDeepLinking(deepLinking);
if (parsedDeepLinkingURL) {
store.dispatch(deepLinkingOpen(parsedDeepLinkingURL));
return;
}
// Open app from app icon
store.dispatch(appInit());
} }
getMasterDetail = (width) => { getMasterDetail = (width) => {

View File

@ -229,9 +229,9 @@ class Encryption {
decryptPendingMessages = async(roomId) => { decryptPendingMessages = async(roomId) => {
const db = database.active; const db = database.active;
const messagesCollection = db.collections.get('messages'); const messagesCollection = db.get('messages');
const threadsCollection = db.collections.get('threads'); const threadsCollection = db.get('threads');
const threadMessagesCollection = db.collections.get('thread_messages'); const threadMessagesCollection = db.get('thread_messages');
// e2e status is null or 'pending' and message type is 'e2e' // e2e status is null or 'pending' and message type is 'e2e'
const whereClause = [ const whereClause = [
@ -286,7 +286,7 @@ class Encryption {
// after initialize the encryption client // after initialize the encryption client
decryptPendingSubscriptions = async() => { decryptPendingSubscriptions = async() => {
const db = database.active; const db = database.active;
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
try { try {
// Find all rooms that can have a lastMessage encrypted // Find all rooms that can have a lastMessage encrypted
// If we select only encrypted rooms we can miss some room that changed their encrypted status // If we select only encrypted rooms we can miss some room that changed their encrypted status
@ -347,7 +347,7 @@ class Encryption {
const { rid } = subscription; const { rid } = subscription;
const db = database.active; const db = database.active;
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
let subRecord; let subRecord;
try { try {
@ -400,7 +400,7 @@ class Encryption {
encryptMessage = async(message) => { encryptMessage = async(message) => {
const { rid } = message; const { rid } = message;
const db = database.active; const db = database.active;
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
try { try {
// Find the subscription // Find the subscription

View File

@ -49,7 +49,7 @@ export default class EncryptionRoom {
} }
const db = database.active; const db = database.active;
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
try { try {
// Find the subscription // Find the subscription
const subscription = await subCollection.find(this.roomId); const subscription = await subCollection.find(this.roomId);

View File

@ -2,7 +2,7 @@ import reduxStore from '../createStore';
import Navigation from '../Navigation'; import Navigation from '../Navigation';
import { logEvent, events } from '../../utils/log'; import { logEvent, events } from '../../utils/log';
async function jitsiURL({ rid }) { async function jitsiURL({ room }) {
const { settings } = reduxStore.getState(); const { settings } = reduxStore.getState();
const { Jitsi_Enabled } = settings; const { Jitsi_Enabled } = settings;
@ -11,31 +11,37 @@ async function jitsiURL({ rid }) {
} }
const { const {
Jitsi_Domain, Jitsi_URL_Room_Prefix, Jitsi_SSL, Jitsi_Enabled_TokenAuth, uniqueID Jitsi_Domain, Jitsi_URL_Room_Prefix, Jitsi_SSL, Jitsi_Enabled_TokenAuth, uniqueID, Jitsi_URL_Room_Hash
} = settings; } = settings;
const domain = `${ Jitsi_Domain }/`; const domain = `${ Jitsi_Domain }/`;
const prefix = Jitsi_URL_Room_Prefix; const prefix = Jitsi_URL_Room_Prefix;
const uniqueIdentifier = uniqueID || 'undefined';
const protocol = Jitsi_SSL ? 'https://' : 'http://'; const protocol = Jitsi_SSL ? 'https://' : 'http://';
let queryString = ''; let queryString = '';
if (Jitsi_Enabled_TokenAuth) { if (Jitsi_Enabled_TokenAuth) {
try { try {
const accessToken = await this.methodCallWrapper('jitsi:generateAccessToken', rid); const accessToken = await this.methodCallWrapper('jitsi:generateAccessToken', room?.rid);
queryString = `?jwt=${ accessToken }`; queryString = `?jwt=${ accessToken }`;
} catch { } catch {
logEvent(events.RA_JITSI_F); logEvent(events.RA_JITSI_F);
} }
} }
return `${ protocol }${ domain }${ prefix }${ uniqueIdentifier }${ rid }${ queryString }`; let rname;
if (Jitsi_URL_Room_Hash) {
rname = uniqueID + room?.rid;
} else {
rname = encodeURIComponent(room.t === 'd' ? room?.usernames?.join?.(' x ') : room?.name);
}
return `${ protocol }${ domain }${ prefix }${ rname }${ queryString }`;
} }
async function callJitsi(rid, onlyAudio = false) { async function callJitsi(room, onlyAudio = false) {
logEvent(onlyAudio ? events.RA_JITSI_AUDIO : events.RA_JITSI_VIDEO); logEvent(onlyAudio ? events.RA_JITSI_AUDIO : events.RA_JITSI_VIDEO);
const url = await jitsiURL.call(this, { rid }); const url = await jitsiURL.call(this, { room });
Navigation.navigate('JitsiMeetView', { url, onlyAudio, rid }); Navigation.navigate('JitsiMeetView', { url, onlyAudio, rid: room?.rid });
} }
export default callJitsi; export default callJitsi;

View File

@ -57,7 +57,7 @@ async function open({ type, rid, name }) {
export default async function canOpenRoom({ rid, path, isCall }) { export default async function canOpenRoom({ rid, path, isCall }) {
try { try {
const db = database.active; const db = database.active;
const subsCollection = db.collections.get('subscriptions'); const subsCollection = db.get('subscriptions');
if (isCall && !rid) { if (isCall && !rid) {
// Extract rid from a Jitsi URL // Extract rid from a Jitsi URL
@ -75,7 +75,8 @@ export default async function canOpenRoom({ rid, path, isCall }) {
name: room.name, name: room.name,
fname: room.fname, fname: room.fname,
prid: room.prid, prid: room.prid,
uids: room.uids uids: room.uids,
usernames: room.usernames
}; };
} catch (e) { } catch (e) {
// Do nothing // Do nothing

View File

@ -13,7 +13,7 @@ export async function setEnterpriseModules() {
try { try {
const { server: serverId } = reduxStore.getState().server; const { server: serverId } = reduxStore.getState().server;
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
let server; let server;
try { try {
server = await serversCollection.find(serverId); server = await serversCollection.find(serverId);
@ -39,7 +39,7 @@ export function getEnterpriseModules() {
const enterpriseModules = await this.methodCallWrapper('license:getModules'); const enterpriseModules = await this.methodCallWrapper('license:getModules');
if (enterpriseModules) { if (enterpriseModules) {
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
const server = await serversCollection.find(serverId); const server = await serversCollection.find(serverId);
await serversDB.action(async() => { await serversDB.action(async() => {
await server.update((s) => { await server.update((s) => {

View File

@ -1,4 +1,3 @@
import { InteractionManager } from 'react-native';
import lt from 'semver/functions/lt'; import lt from 'semver/functions/lt';
import orderBy from 'lodash/orderBy'; import orderBy from 'lodash/orderBy';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
@ -21,7 +20,7 @@ const updateEmojis = async({ update = [], remove = [], allRecords }) => {
return; return;
} }
const db = database.active; const db = database.active;
const emojisCollection = db.collections.get('custom_emojis'); const emojisCollection = db.get('custom_emojis');
let emojisToCreate = []; let emojisToCreate = [];
let emojisToUpdate = []; let emojisToUpdate = [];
let emojisToDelete = []; let emojisToDelete = [];
@ -63,7 +62,7 @@ const updateEmojis = async({ update = [], remove = [], allRecords }) => {
export async function setCustomEmojis() { export async function setCustomEmojis() {
const db = database.active; const db = database.active;
const emojisCollection = db.collections.get('custom_emojis'); const emojisCollection = db.get('custom_emojis');
const allEmojis = await emojisCollection.query().fetch(); const allEmojis = await emojisCollection.query().fetch();
const parsed = allEmojis.reduce((ret, item) => { const parsed = allEmojis.reduce((ret, item) => {
ret[item.name] = { ret[item.name] = {
@ -86,7 +85,7 @@ export function getCustomEmojis() {
try { try {
const serverVersion = reduxStore.getState().server.version; const serverVersion = reduxStore.getState().server.version;
const db = database.active; const db = database.active;
const emojisCollection = db.collections.get('custom_emojis'); const emojisCollection = db.get('custom_emojis');
const allRecords = await emojisCollection.query().fetch(); const allRecords = await emojisCollection.query().fetch();
const updatedSince = await getUpdatedSince(allRecords); const updatedSince = await getUpdatedSince(allRecords);
@ -95,18 +94,16 @@ export function getCustomEmojis() {
// RC 0.61.0 // RC 0.61.0
const result = await this.sdk.get('emoji-custom'); const result = await this.sdk.get('emoji-custom');
InteractionManager.runAfterInteractions(async() => { let { emojis } = result;
let { emojis } = result; emojis = emojis.filter(emoji => !updatedSince || emoji._updatedAt > updatedSince);
emojis = emojis.filter(emoji => !updatedSince || emoji._updatedAt > updatedSince); const changedEmojis = await updateEmojis({ update: emojis, allRecords });
const changedEmojis = await updateEmojis({ update: emojis, allRecords });
// `setCustomEmojis` is fired on selectServer // `setCustomEmojis` is fired on selectServer
// We run it again only if emojis were changed // We run it again only if emojis were changed
if (changedEmojis) { if (changedEmojis) {
setCustomEmojis(); setCustomEmojis();
} }
return resolve(); return resolve();
});
} else { } else {
const params = {}; const params = {};
if (updatedSince) { if (updatedSince) {
@ -120,17 +117,15 @@ export function getCustomEmojis() {
return resolve(); return resolve();
} }
InteractionManager.runAfterInteractions(async() => { const { emojis } = result;
const { emojis } = result; const { update, remove } = emojis;
const { update, remove } = emojis; const changedEmojis = await updateEmojis({ update, remove, allRecords });
const changedEmojis = await updateEmojis({ update, remove, allRecords });
// `setCustomEmojis` is fired on selectServer // `setCustomEmojis` is fired on selectServer
// We run it again only if emojis were changed // We run it again only if emojis were changed
if (changedEmojis) { if (changedEmojis) {
setCustomEmojis(); setCustomEmojis();
} }
});
} }
} catch (e) { } catch (e) {
log(e); log(e);

View File

@ -1,12 +1,55 @@
import { InteractionManager } from 'react-native';
import lt from 'semver/functions/lt'; import lt from 'semver/functions/lt';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { Q } from '@nozbe/watermelondb';
import coerce from 'semver/functions/coerce';
import orderBy from 'lodash/orderBy'; import orderBy from 'lodash/orderBy';
import database from '../database'; import database from '../database';
import log from '../../utils/log'; import log from '../../utils/log';
import reduxStore from '../createStore'; import reduxStore from '../createStore';
import protectedFunction from './helpers/protectedFunction'; import protectedFunction from './helpers/protectedFunction';
import { setPermissions as setPermissionsAction } from '../../actions/permissions';
const PERMISSIONS = [
'add-user-to-any-c-room',
'add-user-to-any-p-room',
'add-user-to-joined-room',
'archive-room',
'auto-translate',
'create-invite-links',
'delete-c',
'delete-message',
'delete-p',
'edit-message',
'edit-room',
'force-delete-message',
'mute-user',
'pin-message',
'post-readonly',
'remove-user',
'set-leader',
'set-moderator',
'set-owner',
'set-react-when-readonly',
'set-readonly',
'toggle-room-e2e-encryption',
'transfer-livechat-guest',
'unarchive-room',
'view-broadcast-member-list',
'view-privileged-setting',
'view-room-administration',
'view-statistics',
'view-user-administration'
];
export async function setPermissions() {
const db = database.active;
const permissionsCollection = db.collections.get('permissions');
const allPermissions = await permissionsCollection.query(Q.where('id', Q.oneOf(PERMISSIONS))).fetch();
const parsed = allPermissions.reduce((acc, item) => ({ ...acc, [item.id]: item.roles }), {});
reduxStore.dispatch(setPermissionsAction(parsed));
}
const getUpdatedSince = (allRecords) => { const getUpdatedSince = (allRecords) => {
try { try {
@ -26,7 +69,7 @@ const updatePermissions = async({ update = [], remove = [], allRecords }) => {
return; return;
} }
const db = database.active; const db = database.active;
const permissionsCollection = db.collections.get('permissions'); const permissionsCollection = db.get('permissions');
// filter permissions // filter permissions
let permissionsToCreate = []; let permissionsToCreate = [];
@ -65,33 +108,35 @@ const updatePermissions = async({ update = [], remove = [], allRecords }) => {
await db.action(async() => { await db.action(async() => {
await db.batch(...batch); await db.batch(...batch);
}); });
return true;
} catch (e) { } catch (e) {
log(e); log(e);
} }
}; };
export default function() { export function getPermissions() {
return new Promise(async(resolve) => { return new Promise(async(resolve) => {
try { try {
const serverVersion = reduxStore.getState().server.version; const serverVersion = reduxStore.getState().server.version;
const db = database.active; const db = database.active;
const permissionsCollection = db.collections.get('permissions'); const permissionsCollection = db.get('permissions');
const allRecords = await permissionsCollection.query().fetch(); const allRecords = await permissionsCollection.query().fetch();
// if server version is lower than 0.73.0, fetches from old api // if server version is lower than 0.73.0, fetches from old api
if (serverVersion && lt(serverVersion, '0.73.0')) { if (serverVersion && lt(coerce(serverVersion), '0.73.0')) {
// RC 0.66.0 // RC 0.66.0
const result = await this.sdk.get('permissions.list'); const result = await this.sdk.get('permissions.list');
if (!result.success) { if (!result.success) {
return resolve(); return resolve();
} }
InteractionManager.runAfterInteractions(async() => { const changePermissions = await updatePermissions({ update: result.permissions, allRecords });
await updatePermissions({ update: result.permissions, allRecords }); if (changePermissions) {
return resolve(); setPermissions();
}); }
return resolve();
} else { } else {
const params = {}; const params = {};
const updatedSince = await getUpdatedSince(allRecords); const updatedSince = getUpdatedSince(allRecords);
if (updatedSince) { if (updatedSince) {
params.updatedSince = updatedSince; params.updatedSince = updatedSince;
} }
@ -102,10 +147,11 @@ export default function() {
return resolve(); return resolve();
} }
InteractionManager.runAfterInteractions(async() => { const changePermissions = await updatePermissions({ update: result.update, remove: result.delete, allRecords });
await updatePermissions({ update: result.update, remove: result.delete, allRecords }); if (changePermissions) {
return resolve(); setPermissions();
}); }
return resolve();
} }
} catch (e) { } catch (e) {
log(e); log(e);

View File

@ -1,4 +1,3 @@
import { InteractionManager } from 'react-native';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import database from '../database'; import database from '../database';
@ -19,43 +18,41 @@ export default function() {
const { roles } = result; const { roles } = result;
if (roles && roles.length) { if (roles && roles.length) {
InteractionManager.runAfterInteractions(async() => { await db.action(async() => {
await db.action(async() => { const rolesCollections = db.get('roles');
const rolesCollections = db.collections.get('roles'); const allRolesRecords = await rolesCollections.query().fetch();
const allRolesRecords = await rolesCollections.query().fetch();
// filter roles // filter roles
let rolesToCreate = roles.filter(i1 => !allRolesRecords.find(i2 => i1._id === i2.id)); let rolesToCreate = roles.filter(i1 => !allRolesRecords.find(i2 => i1._id === i2.id));
let rolesToUpdate = allRolesRecords.filter(i1 => roles.find(i2 => i1.id === i2._id)); let rolesToUpdate = allRolesRecords.filter(i1 => roles.find(i2 => i1.id === i2._id));
// Create // Create
rolesToCreate = rolesToCreate.map(role => rolesCollections.prepareCreate(protectedFunction((r) => { rolesToCreate = rolesToCreate.map(role => rolesCollections.prepareCreate(protectedFunction((r) => {
r._raw = sanitizedRaw({ id: role._id }, rolesCollections.schema); r._raw = sanitizedRaw({ id: role._id }, rolesCollections.schema);
Object.assign(r, role); Object.assign(r, role);
}))); })));
// Update // Update
rolesToUpdate = rolesToUpdate.map((role) => { rolesToUpdate = rolesToUpdate.map((role) => {
const newRole = roles.find(r => r._id === role.id); const newRole = roles.find(r => r._id === role.id);
return role.prepareUpdate(protectedFunction((r) => { return role.prepareUpdate(protectedFunction((r) => {
Object.assign(r, newRole); Object.assign(r, newRole);
})); }));
});
const allRecords = [
...rolesToCreate,
...rolesToUpdate
];
try {
await db.batch(...allRecords);
} catch (e) {
log(e);
}
return allRecords.length;
}); });
return resolve();
const allRecords = [
...rolesToCreate,
...rolesToUpdate
];
try {
await db.batch(...allRecords);
} catch (e) {
log(e);
}
return allRecords.length;
}); });
return resolve();
} }
} catch (e) { } catch (e) {
log(e); log(e);

View File

@ -44,7 +44,7 @@ const loginSettings = [
const serverInfoUpdate = async(serverInfo, iconSetting) => { const serverInfoUpdate = async(serverInfo, iconSetting) => {
const serversDB = database.servers; const serversDB = database.servers;
const serverId = reduxStore.getState().server.server; const serverId = reduxStore.getState().server.server;
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
const server = await serversCollection.find(serverId); const server = await serversCollection.find(serverId);
let info = serverInfo.reduce((allSettings, setting) => { let info = serverInfo.reduce((allSettings, setting) => {
@ -118,7 +118,7 @@ export async function getLoginSettings({ server }) {
export async function setSettings() { export async function setSettings() {
const db = database.active; const db = database.active;
const settingsCollection = db.collections.get('settings'); const settingsCollection = db.get('settings');
const settingsRecords = await settingsCollection.query().fetch(); const settingsRecords = await settingsCollection.query().fetch();
const parsed = Object.values(settingsRecords).map(item => ({ const parsed = Object.values(settingsRecords).map(item => ({
_id: item.id, _id: item.id,
@ -157,7 +157,7 @@ export default async function() {
} }
await db.action(async() => { await db.action(async() => {
const settingsCollection = db.collections.get('settings'); const settingsCollection = db.get('settings');
const allSettingsRecords = await settingsCollection const allSettingsRecords = await settingsCollection
.query(Q.where('id', Q.oneOf(filteredSettingsIds))) .query(Q.where('id', Q.oneOf(filteredSettingsIds)))
.fetch(); .fetch();

View File

@ -1,4 +1,3 @@
import { InteractionManager } from 'react-native';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import database from '../database'; import database from '../database';
@ -20,47 +19,45 @@ export default function() {
const { commands } = result; const { commands } = result;
if (commands && commands.length) { if (commands && commands.length) {
InteractionManager.runAfterInteractions(async() => { await db.action(async() => {
await db.action(async() => { const slashCommandsCollection = db.get('slash_commands');
const slashCommandsCollection = db.collections.get('slash_commands'); const allSlashCommandsRecords = await slashCommandsCollection.query().fetch();
const allSlashCommandsRecords = await slashCommandsCollection.query().fetch();
// filter slash commands // filter slash commands
let slashCommandsToCreate = commands.filter(i1 => !allSlashCommandsRecords.find(i2 => i1.command === i2.id)); let slashCommandsToCreate = commands.filter(i1 => !allSlashCommandsRecords.find(i2 => i1.command === i2.id));
let slashCommandsToUpdate = allSlashCommandsRecords.filter(i1 => commands.find(i2 => i1.id === i2.command)); let slashCommandsToUpdate = allSlashCommandsRecords.filter(i1 => commands.find(i2 => i1.id === i2.command));
let slashCommandsToDelete = allSlashCommandsRecords let slashCommandsToDelete = allSlashCommandsRecords
.filter(i1 => !slashCommandsToCreate.find(i2 => i2.command === i1.id) && !slashCommandsToUpdate.find(i2 => i2.id === i1.id)); .filter(i1 => !slashCommandsToCreate.find(i2 => i2.command === i1.id) && !slashCommandsToUpdate.find(i2 => i2.id === i1.id));
// Create // Create
slashCommandsToCreate = slashCommandsToCreate.map(command => slashCommandsCollection.prepareCreate(protectedFunction((s) => { slashCommandsToCreate = slashCommandsToCreate.map(command => slashCommandsCollection.prepareCreate(protectedFunction((s) => {
s._raw = sanitizedRaw({ id: command.command }, slashCommandsCollection.schema); s._raw = sanitizedRaw({ id: command.command }, slashCommandsCollection.schema);
Object.assign(s, command); Object.assign(s, command);
}))); })));
// Update // Update
slashCommandsToUpdate = slashCommandsToUpdate.map((command) => { slashCommandsToUpdate = slashCommandsToUpdate.map((command) => {
const newCommand = commands.find(s => s.command === command.id); const newCommand = commands.find(s => s.command === command.id);
return command.prepareUpdate(protectedFunction((s) => { return command.prepareUpdate(protectedFunction((s) => {
Object.assign(s, newCommand); Object.assign(s, newCommand);
})); }));
});
// Delete
slashCommandsToDelete = slashCommandsToDelete.map(command => command.prepareDestroyPermanently());
const allRecords = [
...slashCommandsToCreate,
...slashCommandsToUpdate,
...slashCommandsToDelete
];
try {
await db.batch(...allRecords);
} catch (e) {
log(e);
}
return allRecords.length;
}); });
// Delete
slashCommandsToDelete = slashCommandsToDelete.map(command => command.prepareDestroyPermanently());
const allRecords = [
...slashCommandsToCreate,
...slashCommandsToUpdate,
...slashCommandsToDelete
];
try {
await db.batch(...allRecords);
} catch (e) {
log(e);
}
return allRecords.length;
}); });
} }
} catch (e) { } catch (e) {

View File

@ -72,7 +72,7 @@ export default async function getUsersPresence() {
ids = []; ids = [];
const db = database.active; const db = database.active;
const userCollection = db.collections.get('users'); const userCollection = db.get('users');
users.forEach(async(user) => { users.forEach(async(user) => {
try { try {
const userRecord = await userCollection.find(user._id); const userRecord = await userCollection.find(user._id);

View File

@ -5,7 +5,7 @@ import database from '../../database';
export default async(subscriptions = [], rooms = []) => { export default async(subscriptions = [], rooms = []) => {
try { try {
const db = database.active; const db = database.active;
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
const roomIds = rooms.filter(r => !subscriptions.find(s => s.rid === r._id)).map(r => r._id); const roomIds = rooms.filter(r => !subscriptions.find(s => s.rid === r._id)).map(r => r._id);
let existingSubs = await subCollection.query(Q.where('rid', Q.oneOf(roomIds))).fetch(); let existingSubs = await subCollection.query(Q.where('rid', Q.oneOf(roomIds))).fetch();

View File

@ -1,11 +1,14 @@
import EJSON from 'ejson'; import EJSON from 'ejson';
import { lt, coerce } from 'semver';
import normalizeMessage from './normalizeMessage'; import normalizeMessage from './normalizeMessage';
import findSubscriptionsRooms from './findSubscriptionsRooms'; import findSubscriptionsRooms from './findSubscriptionsRooms';
import { Encryption } from '../../encryption'; import { Encryption } from '../../encryption';
import reduxStore from '../../createStore';
// TODO: delete and update // TODO: delete and update
export const merge = (subscription, room) => { export const merge = (subscription, room) => {
const serverVersion = reduxStore.getState().server.version;
subscription = EJSON.fromJSONValue(subscription); subscription = EJSON.fromJSONValue(subscription);
room = EJSON.fromJSONValue(room); room = EJSON.fromJSONValue(room);
@ -25,9 +28,15 @@ export const merge = (subscription, room) => {
subscription.usernames = room.usernames; subscription.usernames = room.usernames;
subscription.uids = room.uids; subscription.uids = room.uids;
} }
// https://github.com/RocketChat/Rocket.Chat/blob/develop/app/ui-sidenav/client/roomList.js#L180 if (serverVersion && lt(coerce(serverVersion), '3.7.0')) {
const lastRoomUpdate = room.lm || subscription.ts || subscription._updatedAt; const updatedAt = room?._updatedAt ? new Date(room._updatedAt) : null;
subscription.roomUpdatedAt = subscription.lr ? Math.max(new Date(subscription.lr), new Date(lastRoomUpdate)) : lastRoomUpdate; const lastMessageTs = subscription?.lastMessage?.ts ? new Date(subscription.lastMessage.ts) : null;
subscription.roomUpdatedAt = Math.max(updatedAt, lastMessageTs);
} else {
// https://github.com/RocketChat/Rocket.Chat/blob/develop/app/ui-sidenav/client/roomList.js#L180
const lastRoomUpdate = room.lm || subscription.ts || subscription._updatedAt;
subscription.roomUpdatedAt = subscription.lr ? Math.max(new Date(subscription.lr), new Date(lastRoomUpdate)) : lastRoomUpdate;
}
subscription.ro = room.ro; subscription.ro = room.ro;
subscription.broadcast = room.broadcast; subscription.broadcast = room.broadcast;
subscription.encrypted = room.encrypted; subscription.encrypted = room.encrypted;

View File

@ -5,7 +5,7 @@ import updateMessages from './updateMessages';
const getLastUpdate = async(rid) => { const getLastUpdate = async(rid) => {
try { try {
const db = database.active; const db = database.active;
const subsCollection = db.collections.get('subscriptions'); const subsCollection = db.get('subscriptions');
const sub = await subsCollection.find(rid); const sub = await subsCollection.find(rid);
return sub.lastOpen.toISOString(); return sub.lastOpen.toISOString();
} catch (e) { } catch (e) {

View File

@ -1,4 +1,3 @@
import { InteractionManager } from 'react-native';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
@ -30,44 +29,42 @@ export default function loadThreadMessages({ tmid, rid, offset = 0 }) {
let data = await load.call(this, { tmid, offset }); let data = await load.call(this, { tmid, offset });
if (data && data.length) { if (data && data.length) {
InteractionManager.runAfterInteractions(async() => { try {
try { data = data.map(m => buildMessage(m));
data = data.map(m => buildMessage(m)); data = await Encryption.decryptMessages(data);
data = await Encryption.decryptMessages(data); const db = database.active;
const db = database.active; const threadMessagesCollection = db.get('thread_messages');
const threadMessagesCollection = db.collections.get('thread_messages'); const allThreadMessagesRecords = await threadMessagesCollection.query(Q.where('rid', tmid)).fetch();
const allThreadMessagesRecords = await threadMessagesCollection.query(Q.where('rid', tmid)).fetch(); let threadMessagesToCreate = data.filter(i1 => !allThreadMessagesRecords.find(i2 => i1._id === i2.id));
let threadMessagesToCreate = data.filter(i1 => !allThreadMessagesRecords.find(i2 => i1._id === i2.id)); let threadMessagesToUpdate = allThreadMessagesRecords.filter(i1 => data.find(i2 => i1.id === i2._id));
let threadMessagesToUpdate = allThreadMessagesRecords.filter(i1 => data.find(i2 => i1.id === i2._id));
threadMessagesToCreate = threadMessagesToCreate.map(threadMessage => threadMessagesCollection.prepareCreate(protectedFunction((tm) => { threadMessagesToCreate = threadMessagesToCreate.map(threadMessage => threadMessagesCollection.prepareCreate(protectedFunction((tm) => {
tm._raw = sanitizedRaw({ id: threadMessage._id }, threadMessagesCollection.schema); tm._raw = sanitizedRaw({ id: threadMessage._id }, threadMessagesCollection.schema);
Object.assign(tm, threadMessage); Object.assign(tm, threadMessage);
tm.subscription.id = rid; tm.subscription.id = rid;
tm.rid = threadMessage.tmid;
delete threadMessage.tmid;
})));
threadMessagesToUpdate = threadMessagesToUpdate.map((threadMessage) => {
const newThreadMessage = data.find(t => t._id === threadMessage.id);
return threadMessage.prepareUpdate(protectedFunction((tm) => {
Object.assign(tm, newThreadMessage);
tm.rid = threadMessage.tmid; tm.rid = threadMessage.tmid;
delete threadMessage.tmid; delete threadMessage.tmid;
}))); }));
});
threadMessagesToUpdate = threadMessagesToUpdate.map((threadMessage) => { await db.action(async() => {
const newThreadMessage = data.find(t => t._id === threadMessage.id); await db.batch(
return threadMessage.prepareUpdate(protectedFunction((tm) => { ...threadMessagesToCreate,
Object.assign(tm, newThreadMessage); ...threadMessagesToUpdate
tm.rid = threadMessage.tmid; );
delete threadMessage.tmid; });
})); } catch (e) {
}); log(e);
}
await db.action(async() => { return resolve(data);
await db.batch(
...threadMessagesToCreate,
...threadMessagesToUpdate
);
});
} catch (e) {
log(e);
}
return resolve(data);
});
} else { } else {
return resolve([]); return resolve([]);
} }

View File

@ -42,12 +42,12 @@ async function removeServerData({ server }) {
const serversDB = database.servers; const serversDB = database.servers;
const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`); const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
const usersCollection = serversDB.collections.get('users'); const usersCollection = serversDB.get('users');
if (userId) { if (userId) {
const userRecord = await usersCollection.find(userId); const userRecord = await usersCollection.find(userId);
batch.push(userRecord.prepareDestroyPermanently()); batch.push(userRecord.prepareDestroyPermanently());
} }
const serverCollection = serversDB.collections.get('servers'); const serverCollection = serversDB.get('servers');
const serverRecord = await serverCollection.find(server); const serverRecord = await serverCollection.find(server);
batch.push(serverRecord.prepareDestroyPermanently()); batch.push(serverRecord.prepareDestroyPermanently());

View File

@ -4,7 +4,7 @@ import log from '../../utils/log';
export default async function readMessages(rid, ls, updateLastOpen = false) { export default async function readMessages(rid, ls, updateLastOpen = false) {
try { try {
const db = database.active; const db = database.active;
const subscription = await db.collections.get('subscriptions').find(rid); const subscription = await db.get('subscriptions').find(rid);
// RC 0.61.0 // RC 0.61.0
await this.sdk.post('subscriptions.read', { rid }); await this.sdk.post('subscriptions.read', { rid });

View File

@ -40,7 +40,7 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) {
fileInfo.rid = rid; fileInfo.rid = rid;
const db = database.active; const db = database.active;
const uploadsCollection = db.collections.get('uploads'); const uploadsCollection = db.get('uploads');
let uploadRecord; let uploadRecord;
try { try {
uploadRecord = await uploadsCollection.find(fileInfo.path); uploadRecord = await uploadsCollection.find(fileInfo.path);

View File

@ -9,8 +9,8 @@ import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../encryption/constants';
const changeMessageStatus = async(id, tmid, status, message) => { const changeMessageStatus = async(id, tmid, status, message) => {
const db = database.active; const db = database.active;
const msgCollection = db.collections.get('messages'); const msgCollection = db.get('messages');
const threadMessagesCollection = db.collections.get('thread_messages'); const threadMessagesCollection = db.get('thread_messages');
const successBatch = []; const successBatch = [];
const messageRecord = await msgCollection.find(id); const messageRecord = await msgCollection.find(id);
successBatch.push( successBatch.push(
@ -89,10 +89,10 @@ export async function resendMessage(message, tmid) {
export default async function(rid, msg, tmid, user, tshow) { export default async function(rid, msg, tmid, user, tshow) {
try { try {
const db = database.active; const db = database.active;
const subsCollection = db.collections.get('subscriptions'); const subsCollection = db.get('subscriptions');
const msgCollection = db.collections.get('messages'); const msgCollection = db.get('messages');
const threadCollection = db.collections.get('threads'); const threadCollection = db.get('threads');
const threadMessagesCollection = db.collections.get('thread_messages'); const threadMessagesCollection = db.get('thread_messages');
const messageId = random(17); const messageId = random(17);
const batch = []; const batch = [];
@ -151,7 +151,8 @@ export default async function(rid, msg, tmid, user, tshow) {
tm.status = messagesStatus.TEMP; tm.status = messagesStatus.TEMP;
tm.u = { tm.u = {
_id: user.id || '1', _id: user.id || '1',
username: user.username username: user.username,
name: user.name
}; };
tm.t = message.t; tm.t = message.t;
if (message.t === E2E_MESSAGE_TYPE) { if (message.t === E2E_MESSAGE_TYPE) {
@ -175,7 +176,8 @@ export default async function(rid, msg, tmid, user, tshow) {
m.status = messagesStatus.TEMP; m.status = messagesStatus.TEMP;
m.u = { m.u = {
_id: user.id || '1', _id: user.id || '1',
username: user.username username: user.username,
name: user.name
}; };
if (tmid && tMessageRecord) { if (tmid && tMessageRecord) {
m.tmid = tmid; m.tmid = tmid;

View File

@ -90,6 +90,10 @@ export default class RoomSubscription {
if (ev === 'typing') { if (ev === 'typing') {
const { user } = reduxStore.getState().login; const { user } = reduxStore.getState().login;
const { UI_Use_Real_Name } = reduxStore.getState().settings; const { UI_Use_Real_Name } = reduxStore.getState().settings;
const { rooms } = reduxStore.getState().room;
if (rooms[0] !== _rid) {
return;
}
const [name, typing] = ddpMessage.fields.args; const [name, typing] = ddpMessage.fields.args;
const key = UI_Use_Real_Name ? 'name' : 'username'; const key = UI_Use_Real_Name ? 'name' : 'username';
if (name !== user[key]) { if (name !== user[key]) {
@ -105,9 +109,9 @@ export default class RoomSubscription {
try { try {
const { _id } = ddpMessage.fields.args[0]; const { _id } = ddpMessage.fields.args[0];
const db = database.active; const db = database.active;
const msgCollection = db.collections.get('messages'); const msgCollection = db.get('messages');
const threadsCollection = db.collections.get('threads'); const threadsCollection = db.get('threads');
const threadMessagesCollection = db.collections.get('thread_messages'); const threadMessagesCollection = db.get('thread_messages');
let deleteMessage; let deleteMessage;
let deleteThread; let deleteThread;
let deleteThreadMessage; let deleteThreadMessage;
@ -159,9 +163,9 @@ export default class RoomSubscription {
} }
const db = database.active; const db = database.active;
const msgCollection = db.collections.get('messages'); const msgCollection = db.get('messages');
const threadsCollection = db.collections.get('threads'); const threadsCollection = db.get('threads');
const threadMessagesCollection = db.collections.get('thread_messages'); const threadMessagesCollection = db.get('thread_messages');
// Decrypt the message if necessary // Decrypt the message if necessary
message = await Encryption.decryptMessage(message); message = await Encryption.decryptMessage(message);

View File

@ -32,8 +32,8 @@ const WINDOW_TIME = 500;
const createOrUpdateSubscription = async(subscription, room) => { const createOrUpdateSubscription = async(subscription, room) => {
try { try {
const db = database.active; const db = database.active;
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
const roomsCollection = db.collections.get('rooms'); const roomsCollection = db.get('rooms');
if (!subscription) { if (!subscription) {
try { try {
@ -185,7 +185,7 @@ const createOrUpdateSubscription = async(subscription, room) => {
const { rooms } = store.getState().room; const { rooms } = store.getState().room;
if (tmp.lastMessage && !rooms.includes(tmp.rid)) { if (tmp.lastMessage && !rooms.includes(tmp.rid)) {
const lastMessage = buildMessage(tmp.lastMessage); const lastMessage = buildMessage(tmp.lastMessage);
const messagesCollection = db.collections.get('messages'); const messagesCollection = db.get('messages');
let messageRecord; let messageRecord;
try { try {
messageRecord = await messagesCollection.find(lastMessage._id); messageRecord = await messagesCollection.find(lastMessage._id);
@ -281,7 +281,7 @@ export default function subscribeRooms() {
if (/subscriptions/.test(ev)) { if (/subscriptions/.test(ev)) {
if (type === 'removed') { if (type === 'removed') {
try { try {
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
const sub = await subCollection.find(data.rid); const sub = await subCollection.find(data.rid);
const messages = await sub.messages.fetch(); const messages = await sub.messages.fetch();
const threads = await sub.threads.fetch(); const threads = await sub.threads.fetch();
@ -335,7 +335,7 @@ export default function subscribeRooms() {
} }
}; };
try { try {
const msgCollection = db.collections.get('messages'); const msgCollection = db.get('messages');
await db.action(async() => { await db.action(async() => {
await msgCollection.create(protectedFunction((m) => { await msgCollection.create(protectedFunction((m) => {
m._raw = sanitizedRaw({ id: message._id }, msgCollection.schema); m._raw = sanitizedRaw({ id: message._id }, msgCollection.schema);
@ -407,7 +407,7 @@ export default function subscribeRooms() {
}; };
connectedListener = this.sdk.onStreamData('connected', handleConnection); connectedListener = this.sdk.onStreamData('connected', handleConnection);
disconnectedListener = this.sdk.onStreamData('close', handleConnection); // disconnectedListener = this.sdk.onStreamData('close', handleConnection);
streamListener = this.sdk.onStreamData('stream-notify-user', handleStreamMessageReceived); streamListener = this.sdk.onStreamData('stream-notify-user', handleStreamMessageReceived);
try { try {

View File

@ -16,7 +16,7 @@ export default function updateMessages({ rid, update = [], remove = [] }) {
return db.action(async() => { return db.action(async() => {
// Decrypt these messages // Decrypt these messages
update = await Encryption.decryptMessages(update); update = await Encryption.decryptMessages(update);
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
let sub; let sub;
try { try {
sub = await subCollection.find(rid); sub = await subCollection.find(rid);
@ -26,9 +26,9 @@ export default function updateMessages({ rid, update = [], remove = [] }) {
} }
const messagesIds = [...update.map(m => m._id), ...remove.map(m => m._id)]; const messagesIds = [...update.map(m => m._id), ...remove.map(m => m._id)];
const msgCollection = db.collections.get('messages'); const msgCollection = db.get('messages');
const threadCollection = db.collections.get('threads'); const threadCollection = db.get('threads');
const threadMessagesCollection = db.collections.get('thread_messages'); const threadMessagesCollection = db.get('thread_messages');
const allMessagesRecords = await msgCollection const allMessagesRecords = await msgCollection
.query(Q.where('rid', rid), Q.where('id', Q.oneOf(messagesIds))) .query(Q.where('rid', rid), Q.where('id', Q.oneOf(messagesIds)))
.fetch(); .fetch();

View File

@ -32,7 +32,7 @@ import readMessages from './methods/readMessages';
import getSettings, { getLoginSettings, setSettings } from './methods/getSettings'; import getSettings, { getLoginSettings, setSettings } from './methods/getSettings';
import getRooms from './methods/getRooms'; import getRooms from './methods/getRooms';
import getPermissions from './methods/getPermissions'; import { setPermissions, getPermissions } from './methods/getPermissions';
import { getCustomEmojis, setCustomEmojis } from './methods/getCustomEmojis'; import { getCustomEmojis, setCustomEmojis } from './methods/getCustomEmojis';
import { import {
getEnterpriseModules, setEnterpriseModules, hasLicense, isOmnichannelModuleAvailable getEnterpriseModules, setEnterpriseModules, hasLicense, isOmnichannelModuleAvailable
@ -70,7 +70,6 @@ const CERTIFICATE_KEY = 'RC_CERTIFICATE_KEY';
export const THEME_PREFERENCES_KEY = 'RC_THEME_PREFERENCES_KEY'; export const THEME_PREFERENCES_KEY = 'RC_THEME_PREFERENCES_KEY';
export const CRASH_REPORT_KEY = 'RC_CRASH_REPORT_KEY'; export const CRASH_REPORT_KEY = 'RC_CRASH_REPORT_KEY';
export const ANALYTICS_EVENTS_KEY = 'RC_ANALYTICS_EVENTS_KEY'; export const ANALYTICS_EVENTS_KEY = 'RC_ANALYTICS_EVENTS_KEY';
const returnAnArray = obj => obj || [];
const MIN_ROCKETCHAT_VERSION = '0.70.0'; const MIN_ROCKETCHAT_VERSION = '0.70.0';
const STATUSES = ['offline', 'online', 'away', 'busy']; const STATUSES = ['offline', 'online', 'away', 'busy'];
@ -178,9 +177,16 @@ const RocketChat = {
} }
this.controller = new AbortController(); this.controller = new AbortController();
}, },
checkAndReopen() {
return this?.sdk?.checkAndReopen();
},
connect({ server, user, logoutOnError = false }) { connect({ server, user, logoutOnError = false }) {
return new Promise((resolve) => { return new Promise((resolve) => {
if (!this.sdk || this.sdk.client.host !== server) { if (this?.sdk?.client?.host === server) {
return resolve();
} else {
this.sdk?.disconnect?.();
this.sdk = null;
database.setActiveDB(server); database.setActiveDB(server);
} }
reduxStore.dispatch(connectRequest()); reduxStore.dispatch(connectRequest());
@ -209,11 +215,6 @@ const RocketChat = {
EventEmitter.emit('INQUIRY_UNSUBSCRIBE'); EventEmitter.emit('INQUIRY_UNSUBSCRIBE');
if (this.sdk) {
this.sdk.disconnect();
this.sdk = null;
}
if (this.code) { if (this.code) {
this.code = null; this.code = null;
} }
@ -241,6 +242,10 @@ const RocketChat = {
sdkConnect(); sdkConnect();
this.connectedListener = this.sdk.onStreamData('connecting', () => {
reduxStore.dispatch(connectRequest());
});
this.connectedListener = this.sdk.onStreamData('connected', () => { this.connectedListener = this.sdk.onStreamData('connected', () => {
reduxStore.dispatch(connectSuccess()); reduxStore.dispatch(connectSuccess());
}); });
@ -276,7 +281,7 @@ const RocketChat = {
} else if (/updateAvatar/.test(eventName)) { } else if (/updateAvatar/.test(eventName)) {
const { username, etag } = ddpMessage.fields.args[0]; const { username, etag } = ddpMessage.fields.args[0];
const db = database.active; const db = database.active;
const userCollection = db.collections.get('users'); const userCollection = db.get('users');
try { try {
const [userRecord] = await userCollection.query(Q.where('username', Q.eq(username))).fetch(); const [userRecord] = await userCollection.query(Q.where('username', Q.eq(username))).fetch();
await db.action(async() => { await db.action(async() => {
@ -290,7 +295,7 @@ const RocketChat = {
} else if (/Users:NameChanged/.test(eventName)) { } else if (/Users:NameChanged/.test(eventName)) {
const userNameChanged = ddpMessage.fields.args[0]; const userNameChanged = ddpMessage.fields.args[0];
const db = database.active; const db = database.active;
const userCollection = db.collections.get('users'); const userCollection = db.get('users');
try { try {
const userRecord = await userCollection.find(userNameChanged._id); const userRecord = await userCollection.find(userNameChanged._id);
await db.action(async() => { await db.action(async() => {
@ -334,7 +339,7 @@ const RocketChat = {
// set Server // set Server
const currentServer = { server }; const currentServer = { server };
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
try { try {
const serverRecord = await serversCollection.find(server); const serverRecord = await serversCollection.find(server);
currentServer.version = serverRecord.version; currentServer.version = serverRecord.version;
@ -349,7 +354,7 @@ const RocketChat = {
// set Settings // set Settings
const settings = ['Accounts_AvatarBlockUnauthenticatedAccess']; const settings = ['Accounts_AvatarBlockUnauthenticatedAccess'];
const db = database.active; const db = database.active;
const settingsCollection = db.collections.get('settings'); const settingsCollection = db.get('settings');
const settingsRecords = await settingsCollection.query(Q.where('id', Q.oneOf(settings))).fetch(); const settingsRecords = await settingsCollection.query(Q.where('id', Q.oneOf(settings))).fetch();
const parsed = Object.values(settingsRecords).map(item => ({ const parsed = Object.values(settingsRecords).map(item => ({
_id: item.id, _id: item.id,
@ -363,7 +368,7 @@ const RocketChat = {
// set User info // set User info
const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`); const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
const userCollections = serversDB.collections.get('users'); const userCollections = serversDB.get('users');
let user = null; let user = null;
if (userId) { if (userId) {
const userRecord = await userCollections.find(userId); const userRecord = await userCollections.find(userId);
@ -545,7 +550,7 @@ const RocketChat = {
try { try {
const serversDB = database.servers; const serversDB = database.servers;
await serversDB.action(async() => { await serversDB.action(async() => {
const serverCollection = serversDB.collections.get('servers'); const serverCollection = serversDB.get('servers');
const serverRecord = await serverCollection.find(server); const serverRecord = await serverCollection.find(server);
await serverRecord.update((s) => { await serverRecord.update((s) => {
s.roomsUpdatedAt = null; s.roomsUpdatedAt = null;
@ -605,7 +610,7 @@ const RocketChat = {
} }
const db = database.active; const db = database.active;
const likeString = sanitizeLikeString(searchText); const likeString = sanitizeLikeString(searchText);
let data = await db.collections.get('subscriptions').query( let data = await db.get('subscriptions').query(
Q.or( Q.or(
Q.where('name', Q.like(`%${ likeString }%`)), Q.where('name', Q.like(`%${ likeString }%`)),
Q.where('fname', Q.like(`%${ likeString }%`)) Q.where('fname', Q.like(`%${ likeString }%`))
@ -621,19 +626,15 @@ const RocketChat = {
data = data.slice(0, 7); data = data.slice(0, 7);
data = data.map((sub) => { data = data.map(sub => ({
if (sub.t !== 'd') { rid: sub.rid,
return { name: sub.name,
rid: sub.rid, fname: sub.fname,
name: sub.name, avatarETag: sub.avatarETag,
fname: sub.fname, t: sub.t,
avatarETag: sub.avatarETag, encrypted: sub.encrypted,
t: sub.t, lastMessage: sub.lastMessage
encrypted: sub.encrypted }));
};
}
return sub;
});
return data; return data;
}, },
@ -740,6 +741,7 @@ const RocketChat = {
getLoginSettings, getLoginSettings,
setSettings, setSettings,
getPermissions, getPermissions,
setPermissions,
getCustomEmojis, getCustomEmojis,
setCustomEmojis, setCustomEmojis,
getEnterpriseModules, getEnterpriseModules,
@ -796,7 +798,7 @@ const RocketChat = {
async getRoom(rid) { async getRoom(rid) {
try { try {
const db = database.active; const db = database.active;
const room = await db.collections.get('subscriptions').find(rid); const room = await db.get('subscriptions').find(rid);
return Promise.resolve(room); return Promise.resolve(room);
} catch (error) { } catch (error) {
return Promise.reject(new Error('Room not found')); return Promise.reject(new Error('Room not found'));
@ -1172,10 +1174,13 @@ const RocketChat = {
// RC 0.65.0 // RC 0.65.0
return this.sdk.get(`${ this.roomTypeToApiType(type) }.roles`, { roomId }); return this.sdk.get(`${ this.roomTypeToApiType(type) }.roles`, { roomId });
}, },
/**
* Permissions: array of permissions' roles from redux. Example: [['owner', 'admin'], ['leader']]
* Returns an array of boolean for each permission from permissions arg
*/
async hasPermission(permissions, rid) { async hasPermission(permissions, rid) {
const db = database.active; const db = database.active;
const subsCollection = db.collections.get('subscriptions'); const subsCollection = db.get('subscriptions');
const permissionsCollection = db.collections.get('permissions');
let roomRoles = []; let roomRoles = [];
try { try {
// get the room from database // get the room from database
@ -1184,31 +1189,16 @@ const RocketChat = {
roomRoles = room.roles || []; roomRoles = room.roles || [];
} catch (error) { } catch (error) {
console.log('hasPermission -> Room not found'); console.log('hasPermission -> Room not found');
return permissions.reduce((result, permission) => { return permissions.map(() => false);
result[permission] = false;
return result;
}, {});
} }
// get permissions from database
try { try {
const permissionsFiltered = await permissionsCollection.query(Q.where('id', Q.oneOf(permissions))).fetch();
const shareUser = reduxStore.getState().share.user; const shareUser = reduxStore.getState().share.user;
const loginUser = reduxStore.getState().login.user; const loginUser = reduxStore.getState().login.user;
// get user roles on the server from redux // get user roles on the server from redux
const userRoles = (shareUser?.roles || loginUser?.roles) || []; const userRoles = (shareUser?.roles || loginUser?.roles) || [];
// merge both roles
const mergedRoles = [...new Set([...roomRoles, ...userRoles])]; const mergedRoles = [...new Set([...roomRoles, ...userRoles])];
return permissions.map(permission => permission?.some(r => mergedRoles.includes(r) ?? false));
// return permissions in object format
// e.g. { 'edit-room': true, 'set-readonly': false }
return permissions.reduce((result, permission) => {
result[permission] = false;
const permissionFound = permissionsFiltered.find(p => p.id === permission);
if (permissionFound) {
result[permission] = returnAnArray(permissionFound.roles).some(r => mergedRoles.includes(r));
}
return result;
}, {});
} catch (e) { } catch (e) {
log(e); log(e);
} }
@ -1438,17 +1428,15 @@ const RocketChat = {
query, count, offset, sort query, count, offset, sort
}); });
}, },
async canAutoTranslate() { canAutoTranslate() {
const db = database.active;
try { try {
const AutoTranslate_Enabled = reduxStore.getState().settings && reduxStore.getState().settings.AutoTranslate_Enabled; const { AutoTranslate_Enabled } = reduxStore.getState().settings;
if (!AutoTranslate_Enabled) { if (!AutoTranslate_Enabled) {
return false; return false;
} }
const permissionsCollection = db.collections.get('permissions'); const autoTranslatePermission = reduxStore.getState().permissions['auto-translate'];
const autoTranslatePermission = await permissionsCollection.find('auto-translate'); const userRoles = (reduxStore.getState().login?.user?.roles) ?? [];
const userRoles = (reduxStore.getState().login.user && reduxStore.getState().login.user.roles) || []; return autoTranslatePermission?.some(role => userRoles.includes(role));
return autoTranslatePermission.roles.some(role => userRoles.includes(role));
} catch (e) { } catch (e) {
log(e); log(e);
return false; return false;

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import isEqual from 'lodash/isEqual'; import { dequal } from 'dequal';
import I18n from '../../i18n'; import I18n from '../../i18n';
import styles from './styles'; import styles from './styles';
@ -45,7 +45,7 @@ const formatMsg = ({
return `${ prefix }${ lastMessage.msg }`; return `${ prefix }${ lastMessage.msg }`;
}; };
const arePropsEqual = (oldProps, newProps) => isEqual(oldProps, newProps); const arePropsEqual = (oldProps, newProps) => dequal(oldProps, newProps);
const LastMessage = React.memo(({ const LastMessage = React.memo(({
lastMessage, type, showLastMessage, username, alert, useRealName, theme lastMessage, type, showLastMessage, username, alert, useRealName, theme

View File

@ -18,6 +18,7 @@ import inviteLinks from './inviteLinks';
import createDiscussion from './createDiscussion'; import createDiscussion from './createDiscussion';
import enterpriseModules from './enterpriseModules'; import enterpriseModules from './enterpriseModules';
import encryption from './encryption'; import encryption from './encryption';
import permissions from './permissions';
import inquiry from '../ee/omnichannel/reducers/inquiry'; import inquiry from '../ee/omnichannel/reducers/inquiry';
@ -41,5 +42,6 @@ export default combineReducers({
createDiscussion, createDiscussion,
inquiry, inquiry,
enterpriseModules, enterpriseModules,
encryption encryption,
permissions
}); });

View File

@ -0,0 +1,14 @@
import { PERMISSIONS } from '../actions/actionsTypes';
const initialState = {
permissions: {}
};
export default function permissions(state = initialState, action) {
switch (action.type) {
case PERMISSIONS.SET:
return action.permissions;
default:
return state;
}
}

View File

@ -42,7 +42,7 @@ const handleRequest = function* handleRequest({ data }) {
broadcast, broadcast,
encrypted encrypted
} = data; } = data;
logEvent(events.CREATE_CHANNEL_CREATE, { logEvent(events.CR_CREATE, {
type: type ? 'private' : 'public', type: type ? 'private' : 'public',
readOnly, readOnly,
broadcast, broadcast,
@ -53,7 +53,7 @@ const handleRequest = function* handleRequest({ data }) {
try { try {
const db = database.active; const db = database.active;
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
yield db.action(async() => { yield db.action(async() => {
await subCollection.create((s) => { await subCollection.create((s) => {
s._raw = sanitizedRaw({ id: sub.rid }, subCollection.schema); s._raw = sanitizedRaw({ id: sub.rid }, subCollection.schema);
@ -66,7 +66,7 @@ const handleRequest = function* handleRequest({ data }) {
yield put(createChannelSuccess(sub)); yield put(createChannelSuccess(sub));
} catch (err) { } catch (err) {
logEvent(events[data.group ? 'SELECTED_USERS_CREATE_GROUP_F' : 'CREATE_CHANNEL_CREATE_F']); logEvent(events[data.group ? 'SELECTED_USERS_CREATE_GROUP_F' : 'CR_CREATE_F']);
yield put(createChannelFailure(err)); yield put(createChannelFailure(err));
} }
}; };

View File

@ -14,7 +14,7 @@ const create = function* create(data) {
}; };
const handleRequest = function* handleRequest({ data }) { const handleRequest = function* handleRequest({ data }) {
logEvent(events.CREATE_DISCUSSION_CREATE); logEvent(events.CD_CREATE);
try { try {
const auth = yield select(state => state.login.isAuthenticated); const auth = yield select(state => state.login.isAuthenticated);
if (!auth) { if (!auth) {
@ -27,7 +27,7 @@ const handleRequest = function* handleRequest({ data }) {
try { try {
const db = database.active; const db = database.active;
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
yield db.action(async() => { yield db.action(async() => {
await subCollection.create((s) => { await subCollection.create((s) => {
s._raw = sanitizedRaw({ id: sub.rid }, subCollection.schema); s._raw = sanitizedRaw({ id: sub.rid }, subCollection.schema);
@ -39,11 +39,11 @@ const handleRequest = function* handleRequest({ data }) {
} }
yield put(createDiscussionSuccess(sub)); yield put(createDiscussionSuccess(sub));
} else { } else {
logEvent(events.CREATE_DISCUSSION_CREATE_F); logEvent(events.CD_CREATE_F);
yield put(createDiscussionFailure(result)); yield put(createDiscussionFailure(result));
} }
} catch (err) { } catch (err) {
logEvent(events.CREATE_DISCUSSION_CREATE_F); logEvent(events.CD_CREATE_F);
yield put(createDiscussionFailure(err)); yield put(createDiscussionFailure(err));
} }
}; };

View File

@ -31,6 +31,14 @@ const handleInviteLink = function* handleInviteLink({ params, requireLogin = fal
} }
}; };
const popToRoot = function popToRoot({ isMasterDetail }) {
if (isMasterDetail) {
Navigation.navigate('DrawerNavigator');
} else {
Navigation.navigate('RoomsListView');
}
};
const navigate = function* navigate({ params }) { const navigate = function* navigate({ params }) {
yield put(appStart({ root: ROOT_INSIDE })); yield put(appStart({ root: ROOT_INSIDE }));
if (params.path) { if (params.path) {
@ -38,22 +46,31 @@ const navigate = function* navigate({ params }) {
if (type !== 'invite') { if (type !== 'invite') {
const room = yield RocketChat.canOpenRoom(params); const room = yield RocketChat.canOpenRoom(params);
if (room) { if (room) {
const isMasterDetail = yield select(state => state.app.isMasterDetail);
if (isMasterDetail) {
Navigation.navigate('DrawerNavigator');
} else {
Navigation.navigate('RoomsListView');
}
const item = { const item = {
name, name,
t: roomTypes[type], t: roomTypes[type],
roomUserId: RocketChat.getUidDirectMessage(room), roomUserId: RocketChat.getUidDirectMessage(room),
...room ...room
}; };
yield goRoom({ item, isMasterDetail });
const isMasterDetail = yield select(state => state.app.isMasterDetail);
const focusedRooms = yield select(state => state.room.rooms);
if (focusedRooms.includes(room.rid)) {
// if there's one room on the list or last room is the one
if (focusedRooms.length === 1 || focusedRooms[0] === room.rid) {
yield goRoom({ item, isMasterDetail });
} else {
popToRoot({ isMasterDetail });
yield goRoom({ item, isMasterDetail });
}
} else {
popToRoot({ isMasterDetail });
yield goRoom({ item, isMasterDetail });
}
if (params.isCall) { if (params.isCall) {
RocketChat.callJitsi(item.rid); RocketChat.callJitsi(item);
} }
} }
} else { } else {
@ -72,7 +89,7 @@ const fallbackNavigation = function* fallbackNavigation() {
const handleOpen = function* handleOpen({ params }) { const handleOpen = function* handleOpen({ params }) {
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
let { host } = params; let { host } = params;
if (params.isCall && !host) { if (params.isCall && !host) {
@ -121,10 +138,10 @@ const handleOpen = function* handleOpen({ params }) {
} else { } else {
// search if deep link's server already exists // search if deep link's server already exists
try { try {
const servers = yield serversCollection.find(host); const hostServerRecord = yield serversCollection.find(host);
if (servers && user) { if (hostServerRecord && user) {
yield localAuthenticate(host); yield localAuthenticate(host);
yield put(selectServerRequest(host)); yield put(selectServerRequest(host, hostServerRecord.version, true, true));
yield take(types.LOGIN.SUCCESS); yield take(types.LOGIN.SUCCESS);
yield navigate({ params }); yield navigate({ params });
return; return;

View File

@ -30,7 +30,7 @@ const handleEncryptionInit = function* handleEncryptionInit() {
// Fetch server info to check E2E enable // Fetch server info to check E2E enable
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
let serverInfo; let serverInfo;
try { try {
serverInfo = yield serversCollection.find(server); serverInfo = yield serversCollection.find(server);

View File

@ -38,7 +38,7 @@ const restore = function* restore() {
yield put(appStart({ root: ROOT_OUTSIDE })); yield put(appStart({ root: ROOT_OUTSIDE }));
} else { } else {
const serversDB = database.servers; const serversDB = database.servers;
const serverCollections = serversDB.collections.get('servers'); const serverCollections = serversDB.get('servers');
let serverObj; let serverObj;
try { try {

View File

@ -57,7 +57,7 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE
// Saves username on server history // Saves username on server history
const serversDB = database.servers; const serversDB = database.servers;
const serversHistoryCollection = serversDB.collections.get('servers_history'); const serversHistoryCollection = serversDB.get('servers_history');
yield serversDB.action(async() => { yield serversDB.action(async() => {
try { try {
const serversHistory = await serversHistoryCollection.query(Q.where('url', server)).fetch(); const serversHistory = await serversHistoryCollection.query(Q.where('url', server)).fetch();
@ -145,7 +145,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
moment.locale(toMomentLocale(user.language)); moment.locale(toMomentLocale(user.language));
const serversDB = database.servers; const serversDB = database.servers;
const usersCollection = serversDB.collections.get('users'); const usersCollection = serversDB.get('users');
const u = { const u = {
token: user.token, token: user.token,
username: user.username, username: user.username,
@ -222,7 +222,7 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
} else { } else {
const serversDB = database.servers; const serversDB = database.servers;
// all servers // all servers
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
const servers = yield serversCollection.query().fetch(); const servers = yield serversCollection.query().fetch();
// see if there're other logged in servers and selects first one // see if there're other logged in servers and selects first one

View File

@ -12,7 +12,7 @@ const handleReplyBroadcast = function* handleReplyBroadcast({ message }) {
try { try {
const db = database.active; const db = database.active;
const { username } = message.u; const { username } = message.u;
const subsCollection = db.collections.get('subscriptions'); const subsCollection = db.get('subscriptions');
const subscriptions = yield subsCollection.query(Q.where('name', username)).fetch(); const subscriptions = yield subsCollection.query(Q.where('name', username)).fetch();
const isMasterDetail = yield select(state => state.app.isMasterDetail); const isMasterDetail = yield select(state => state.app.isMasterDetail);

View File

@ -15,7 +15,7 @@ import protectedFunction from '../lib/methods/helpers/protectedFunction';
const updateRooms = function* updateRooms({ server, newRoomsUpdatedAt }) { const updateRooms = function* updateRooms({ server, newRoomsUpdatedAt }) {
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
try { try {
const serverRecord = yield serversCollection.find(server); const serverRecord = yield serversCollection.find(server);
@ -39,7 +39,7 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) {
if (params.allData) { if (params.allData) {
yield put(roomsRefresh()); yield put(roomsRefresh());
} else { } else {
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
try { try {
const serverRecord = yield serversCollection.find(server); const serverRecord = yield serversCollection.find(server);
({ roomsUpdatedAt } = serverRecord); ({ roomsUpdatedAt } = serverRecord);
@ -51,8 +51,8 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) {
const { subscriptions } = yield mergeSubscriptionsRooms(subscriptionsResult, roomsResult); const { subscriptions } = yield mergeSubscriptionsRooms(subscriptionsResult, roomsResult);
const db = database.active; const db = database.active;
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
const messagesCollection = db.collections.get('messages'); const messagesCollection = db.get('messages');
const subsIds = subscriptions.map(sub => sub.rid).concat(roomsResult.remove.map(room => room._id)); const subsIds = subscriptions.map(sub => sub.rid).concat(roomsResult.remove.map(room => room._id));
if (subsIds.length) { if (subsIds.length) {

View File

@ -46,7 +46,7 @@ const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
} }
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
yield serversDB.action(async() => { yield serversDB.action(async() => {
try { try {
const serverRecord = await serversCollection.find(server); const serverRecord = await serversCollection.find(server);
@ -78,7 +78,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
const serversDB = database.servers; const serversDB = database.servers;
yield UserPreferences.setStringAsync(RocketChat.CURRENT_SERVER, server); yield UserPreferences.setStringAsync(RocketChat.CURRENT_SERVER, server);
const userId = yield UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`); const userId = yield UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
const userCollections = serversDB.collections.get('users'); const userCollections = serversDB.get('users');
let user = null; let user = null;
if (userId) { if (userId) {
try { try {
@ -124,6 +124,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
// and block the selectServerSuccess raising multiples errors // and block the selectServerSuccess raising multiples errors
RocketChat.setSettings(); RocketChat.setSettings();
RocketChat.setCustomEmojis(); RocketChat.setCustomEmojis();
RocketChat.setPermissions();
RocketChat.setEnterpriseModules(); RocketChat.setEnterpriseModules();
let serverInfo; let serverInfo;
@ -151,7 +152,7 @@ const handleServerRequest = function* handleServerRequest({ server, username, fr
const serverInfo = yield getServerInfo({ server }); const serverInfo = yield getServerInfo({ server });
const serversDB = database.servers; const serversDB = database.servers;
const serversHistoryCollection = serversDB.collections.get('servers_history'); const serversHistoryCollection = serversDB.get('servers_history');
if (serverInfo) { if (serverInfo) {
yield RocketChat.getLoginServices(server); yield RocketChat.getLoginServices(server);

View File

@ -12,13 +12,14 @@ const appHasComeBackToForeground = function* appHasComeBackToForeground() {
if (appRoot === ROOT_OUTSIDE) { if (appRoot === ROOT_OUTSIDE) {
return; return;
} }
const auth = yield select(state => state.login.isAuthenticated); const login = yield select(state => state.login);
if (!auth) { const server = yield select(state => state.server);
if (!login.isAuthenticated || login.isFetching || server.connecting || server.loading || server.changingServer) {
return; return;
} }
try { try {
const server = yield select(state => state.server.server); yield localAuthenticate(server.server);
yield localAuthenticate(server); RocketChat.checkAndReopen();
setBadgeCount(); setBadgeCount();
return yield RocketChat.setUserPresenceOnline(); return yield RocketChat.setUserPresenceOnline();
} catch (e) { } catch (e) {
@ -31,14 +32,6 @@ const appHasComeBackToBackground = function* appHasComeBackToBackground() {
if (appRoot === ROOT_OUTSIDE) { if (appRoot === ROOT_OUTSIDE) {
return; return;
} }
const auth = yield select(state => state.login.isAuthenticated);
if (!auth) {
return;
}
const localAuthenticated = yield select(state => state.login.isLocalAuthenticated);
if (!localAuthenticated) {
return;
}
try { try {
const server = yield select(state => state.server.server); const server = yield select(state => state.server.server);
yield saveLastLocalAuthenticationSession(server); yield saveLastLocalAuthenticationSession(server);

View File

@ -31,6 +31,7 @@ import PickerView from '../views/PickerView';
import ThreadMessagesView from '../views/ThreadMessagesView'; import ThreadMessagesView from '../views/ThreadMessagesView';
import MarkdownTableView from '../views/MarkdownTableView'; import MarkdownTableView from '../views/MarkdownTableView';
import ReadReceiptsView from '../views/ReadReceiptView'; import ReadReceiptsView from '../views/ReadReceiptView';
import { themes } from '../constants/colors';
// Profile Stack // Profile Stack
import ProfileView from '../views/ProfileView'; import ProfileView from '../views/ProfileView';
@ -280,19 +281,24 @@ const AdminPanelStackNavigator = () => {
// DrawerNavigator // DrawerNavigator
const Drawer = createDrawerNavigator(); const Drawer = createDrawerNavigator();
const DrawerNavigator = () => ( const DrawerNavigator = () => {
<Drawer.Navigator const { theme } = React.useContext(ThemeContext);
drawerContent={({ navigation, state }) => <Sidebar navigation={navigation} state={state} />}
drawerPosition={I18nManager.isRTL ? 'right' : 'left'} return (
screenOptions={{ swipeEnabled: false }} <Drawer.Navigator
drawerType='back' drawerContent={({ navigation, state }) => <Sidebar navigation={navigation} state={state} />}
> drawerPosition={I18nManager.isRTL ? 'right' : 'left'}
<Drawer.Screen name='ChatsStackNavigator' component={ChatsStackNavigator} /> screenOptions={{ swipeEnabled: false }}
<Drawer.Screen name='ProfileStackNavigator' component={ProfileStackNavigator} /> drawerType='back'
<Drawer.Screen name='SettingsStackNavigator' component={SettingsStackNavigator} /> overlayColor={`rgba(0,0,0,${ themes[theme].backdropOpacity })`}
<Drawer.Screen name='AdminPanelStackNavigator' component={AdminPanelStackNavigator} /> >
</Drawer.Navigator> <Drawer.Screen name='ChatsStackNavigator' component={ChatsStackNavigator} />
); <Drawer.Screen name='ProfileStackNavigator' component={ProfileStackNavigator} />
<Drawer.Screen name='SettingsStackNavigator' component={SettingsStackNavigator} />
<Drawer.Screen name='AdminPanelStackNavigator' component={AdminPanelStackNavigator} />
</Drawer.Navigator>
);
};
// NewMessageStackNavigator // NewMessageStackNavigator
const NewMessageStack = createStackNavigator(); const NewMessageStack = createStackNavigator();

View File

@ -1,13 +1,11 @@
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import reduxStore from '../lib/createStore';
const canPost = async({ rid }) => { const canPostReadOnly = async({ rid }) => {
try { // TODO: this is not reactive. If this permission changes, the component won't be updated
const permission = await RocketChat.hasPermission(['post-readonly'], rid); const postReadOnlyPermission = reduxStore.getState().permissions['post-readonly'];
return permission && permission['post-readonly']; const permission = await RocketChat.hasPermission([postReadOnlyPermission], rid);
} catch { return permission[0];
// do nothing
}
return false;
}; };
const isMuted = (room, user) => room && room.muted && room.muted.find && !!room.muted.find(m => m === user.username); const isMuted = (room, user) => room && room.muted && room.muted.find && !!room.muted.find(m => m === user.username);
@ -20,7 +18,7 @@ export const isReadOnly = async(room, user) => {
return true; return true;
} }
if (room?.ro) { if (room?.ro) {
const allowPost = await canPost(room); const allowPost = await canPostReadOnly(room);
if (allowPost) { if (allowPost) {
return false; return false;
} }

View File

@ -17,7 +17,7 @@ import { setLocalAuthenticated } from '../actions/login';
export const saveLastLocalAuthenticationSession = async(server, serverRecord) => { export const saveLastLocalAuthenticationSession = async(server, serverRecord) => {
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
await serversDB.action(async() => { await serversDB.action(async() => {
try { try {
if (!serverRecord) { if (!serverRecord) {
@ -91,7 +91,7 @@ export const checkHasPasscode = async({ force = true, serverRecord }) => {
export const localAuthenticate = async(server) => { export const localAuthenticate = async(server) => {
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.get('servers');
let serverRecord; let serverRecord;
try { try {
@ -102,9 +102,6 @@ export const localAuthenticate = async(server) => {
// if screen lock is enabled // if screen lock is enabled
if (serverRecord?.autoLock) { if (serverRecord?.autoLock) {
// set isLocalAuthenticated to false
store.dispatch(setLocalAuthenticated(false));
// Make sure splash screen has been hidden // Make sure splash screen has been hidden
RNBootSplash.hide(); RNBootSplash.hide();
@ -118,6 +115,9 @@ export const localAuthenticate = async(server) => {
// if last authenticated session is older than configured auto lock time, authentication is required // if last authenticated session is older than configured auto lock time, authentication is required
if (diffToLastSession >= serverRecord?.autoLockTime) { if (diffToLastSession >= serverRecord?.autoLockTime) {
// set isLocalAuthenticated to false
store.dispatch(setLocalAuthenticated(false));
let hasBiometry = false; let hasBiometry = false;
// if biometry is enabled on the app // if biometry is enabled on the app

View File

@ -5,9 +5,8 @@ export default {
ONBOARD_CREATE_NEW_WORKSPACE_F: 'onboard_create_new_workspace_f', ONBOARD_CREATE_NEW_WORKSPACE_F: 'onboard_create_new_workspace_f',
// NEW SERVER VIEW // NEW SERVER VIEW
NEWSERVER_CONNECT_TO_WORKSPACE: 'newserver_connect_to_workspace', NS_CONNECT_TO_WORKSPACE: 'ns_connect_to_workspace',
NEWSERVER_CONNECT_TO_WORKSPACE_F: 'newserver_connect_to_workspace_f', NS_JOIN_OPEN_WORKSPACE: 'ns_join_open_workspace',
NEWSERVER_JOIN_OPEN_WORKSPACE: 'newserver_join_open_workspace',
// LOGIN VIEW // LOGIN VIEW
LOGIN_DEFAULT_LOGIN: 'login_default_login', LOGIN_DEFAULT_LOGIN: 'login_default_login',
@ -99,20 +98,20 @@ export default {
SELECTED_USERS_CREATE_GROUP_F: 'selected_users_create_group_f', SELECTED_USERS_CREATE_GROUP_F: 'selected_users_create_group_f',
// CREATE CHANNEL VIEW // CREATE CHANNEL VIEW
CREATE_CHANNEL_CREATE: 'create_channel_create', CR_CREATE: 'cr_create',
CREATE_CHANNEL_CREATE_F: 'create_channel_create_f', CR_CREATE_F: 'cr_create_f',
CREATE_CHANNEL_TOGGLE_TYPE: 'create_channel_toggle_type', CR_TOGGLE_TYPE: 'cr_toggle_type',
CREATE_CHANNEL_TOGGLE_READ_ONLY: 'create_channel_toggle_read_only', CR_TOGGLE_READ_ONLY: 'cr_toggle_read_only',
CREATE_CHANNEL_TOGGLE_BROADCAST: 'create_channel_toggle_broadcast', CR_TOGGLE_BROADCAST: 'cr_toggle_broadcast',
CREATE_CHANNEL_TOGGLE_ENCRYPTED: 'create_channel_toggle_encrypted', CR_TOGGLE_ENCRYPTED: 'cr_toggle_encrypted',
CREATE_CHANNEL_REMOVE_USER: 'create_channel_remove_user', CR_REMOVE_USER: 'cr_remove_user',
// CREATE DISCUSSION VIEW // CREATE DISCUSSION VIEW
CREATE_DISCUSSION_CREATE: 'create_discussion_create', CD_CREATE: 'cd_create',
CREATE_DISCUSSION_CREATE_F: 'create_discussion_create_f', CD_CREATE_F: 'cd_create_f',
CREATE_DISCUSSION_SELECT_CHANNEL: 'create_discussion_select_channel', CD_SELECT_CHANNEL: 'cd_select_channel',
CREATE_DISCUSSION_SELECT_USERS: 'create_discussion_select_users', CD_SELECT_USERS: 'cd_select_users',
CREATE_DISCUSSION_TOGGLE_ENCRY: 'create_discussion_toggle_encry', CD_TOGGLE_ENCRY: 'cd_toggle_encry',
// PROFILE VIEW // PROFILE VIEW
PROFILE_PICK_AVATAR: 'profile_pick_avatar', PROFILE_PICK_AVATAR: 'profile_pick_avatar',
@ -122,8 +121,9 @@ export default {
PROFILE_SAVE_AVATAR_F: 'profile_save_avatar_f', PROFILE_SAVE_AVATAR_F: 'profile_save_avatar_f',
PROFILE_SAVE_CHANGES: 'profile_save_changes', PROFILE_SAVE_CHANGES: 'profile_save_changes',
PROFILE_SAVE_CHANGES_F: 'profile_save_changes_f', PROFILE_SAVE_CHANGES_F: 'profile_save_changes_f',
PROFILE_LOGOUT_OTHER_LOCATIONS: 'profile_logout_other_locations', // PROFILE LOGOUT
PROFILE_LOGOUT_OTHER_LOCATIONS_F: 'profile_logout_other_locations_f', PL_OTHER_LOCATIONS: 'pl_other_locations',
PL_OTHER_LOCATIONS_F: 'pl_other_locations_f',
// SETTINGS VIEW // SETTINGS VIEW
SE_CONTACT_US: 'se_contact_us', SE_CONTACT_US: 'se_contact_us',
@ -297,8 +297,6 @@ export default {
NP_AUDIONOTIFICATIONS_F: 'np_audio_notifications_f', NP_AUDIONOTIFICATIONS_F: 'np_audio_notifications_f',
NP_AUDIONOTIFICATIONVALUE: 'np_audio_notification_value', NP_AUDIONOTIFICATIONVALUE: 'np_audio_notification_value',
NP_AUDIONOTIFICATIONVALUE_F: 'np_audio_notification_value_f', NP_AUDIONOTIFICATIONVALUE_F: 'np_audio_notification_value_f',
NP_DESKTOPNOTIFICATIONDURATION: 'np_desktopnotificationduration',
NP_DESKTOPNOTIFICATIONDURATION_F: 'np_desktopnotificationduration_f',
NP_EMAILNOTIFICATIONS: 'np_email_notifications', NP_EMAILNOTIFICATIONS: 'np_email_notifications',
NP_EMAILNOTIFICATIONS_F: 'np_email_notifications_f', NP_EMAILNOTIFICATIONS_F: 'np_email_notifications_f',

View File

@ -121,8 +121,9 @@ class AttachmentView extends React.Component {
const extension = image_url ? `.${ mime.extension(image_type) || 'jpg' }` : `.${ mime.extension(video_type) || 'mp4' }`; const extension = image_url ? `.${ mime.extension(image_type) || 'jpg' }` : `.${ mime.extension(video_type) || 'mp4' }`;
const documentDir = `${ RNFetchBlob.fs.dirs.DocumentDir }/`; const documentDir = `${ RNFetchBlob.fs.dirs.DocumentDir }/`;
const path = `${ documentDir + SHA256(url) + extension }`; const path = `${ documentDir + SHA256(url) + extension }`;
const file = await RNFetchBlob.config({ path }).fetch('GET', mediaAttachment).then(res => res.path()); const file = await RNFetchBlob.config({ path }).fetch('GET', mediaAttachment);
await CameraRoll.save(file, { album: 'Rocket.Chat' }); await CameraRoll.save(path, { album: 'Rocket.Chat' });
await file.flush();
EventEmitter.emit(LISTENER, { message: I18n.t('saved_to_gallery') }); EventEmitter.emit(LISTENER, { message: I18n.t('saved_to_gallery') });
} catch (e) { } catch (e) {
EventEmitter.emit(LISTENER, { message: I18n.t(image_url ? 'error-save-image' : 'error-save-video') }); EventEmitter.emit(LISTENER, { message: I18n.t(image_url ? 'error-save-image' : 'error-save-video') });

View File

@ -63,12 +63,12 @@ const ChangePasscodeView = React.memo(({ theme }) => {
if (!isTablet) { if (!isTablet) {
Orientation.lockToPortrait(); Orientation.lockToPortrait();
} }
EventEmitter.addEventListener(CHANGE_PASSCODE_EMITTER, showChangePasscode); const listener = EventEmitter.addEventListener(CHANGE_PASSCODE_EMITTER, showChangePasscode);
return (() => { return (() => {
if (!isTablet) { if (!isTablet) {
Orientation.unlockAllOrientations(); Orientation.unlockAllOrientations();
} }
EventEmitter.removeListener(CHANGE_PASSCODE_EMITTER); EventEmitter.removeListener(CHANGE_PASSCODE_EMITTER, listener);
}); });
}, []); }, []);

View File

@ -4,7 +4,8 @@ import PropTypes from 'prop-types';
import { import {
View, Text, Switch, ScrollView, StyleSheet, FlatList View, Text, Switch, ScrollView, StyleSheet, FlatList
} from 'react-native'; } from 'react-native';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import * as List from '../containers/List';
import TextInput from '../presentation/TextInput'; import TextInput from '../presentation/TextInput';
import Loading from '../containers/Loading'; import Loading from '../containers/Loading';
@ -31,12 +32,6 @@ const styles = StyleSheet.create({
list: { list: {
width: '100%' width: '100%'
}, },
separator: {
marginLeft: 60
},
formSeparator: {
marginLeft: 15
},
input: { input: {
height: 54, height: 54,
paddingHorizontal: 18, paddingHorizontal: 18,
@ -133,7 +128,7 @@ class CreateChannelView extends React.Component {
if (nextProps.encryptionEnabled !== encryptionEnabled) { if (nextProps.encryptionEnabled !== encryptionEnabled) {
return true; return true;
} }
if (!equal(nextProps.users, users)) { if (!dequal(nextProps.users, users)) {
return true; return true;
} }
return false; return false;
@ -177,7 +172,7 @@ class CreateChannelView extends React.Component {
} }
removeUser = (user) => { removeUser = (user) => {
logEvent(events.CREATE_CHANNEL_REMOVE_USER); logEvent(events.CR_REMOVE_USER);
const { removeUser } = this.props; const { removeUser } = this.props;
removeUser(user); removeUser(user);
} }
@ -207,7 +202,7 @@ class CreateChannelView extends React.Component {
value: type, value: type,
label: 'Private_Channel', label: 'Private_Channel',
onValueChange: (value) => { onValueChange: (value) => {
logEvent(events.CREATE_CHANNEL_TOGGLE_TYPE); logEvent(events.CR_TOGGLE_TYPE);
// If we set the channel as public, encrypted status should be false // If we set the channel as public, encrypted status should be false
this.setState(({ encrypted }) => ({ type: value, encrypted: value && encrypted })); this.setState(({ encrypted }) => ({ type: value, encrypted: value && encrypted }));
} }
@ -221,7 +216,7 @@ class CreateChannelView extends React.Component {
value: readOnly, value: readOnly,
label: 'Read_Only_Channel', label: 'Read_Only_Channel',
onValueChange: (value) => { onValueChange: (value) => {
logEvent(events.CREATE_CHANNEL_TOGGLE_READ_ONLY); logEvent(events.CR_TOGGLE_READ_ONLY);
this.setState({ readOnly: value }); this.setState({ readOnly: value });
}, },
disabled: broadcast disabled: broadcast
@ -241,7 +236,7 @@ class CreateChannelView extends React.Component {
value: encrypted, value: encrypted,
label: 'Encrypted', label: 'Encrypted',
onValueChange: (value) => { onValueChange: (value) => {
logEvent(events.CREATE_CHANNEL_TOGGLE_ENCRYPTED); logEvent(events.CR_TOGGLE_ENCRYPTED);
this.setState({ encrypted: value }); this.setState({ encrypted: value });
}, },
disabled: !type disabled: !type
@ -255,7 +250,7 @@ class CreateChannelView extends React.Component {
value: broadcast, value: broadcast,
label: 'Broadcast_Channel', label: 'Broadcast_Channel',
onValueChange: (value) => { onValueChange: (value) => {
logEvent(events.CREATE_CHANNEL_TOGGLE_BROADCAST); logEvent(events.CR_TOGGLE_BROADCAST);
this.setState({ this.setState({
broadcast: value, broadcast: value,
readOnly: value ? true : readOnly readOnly: value ? true : readOnly
@ -264,13 +259,6 @@ class CreateChannelView extends React.Component {
}); });
} }
renderSeparator = () => <View style={[sharedStyles.separator, styles.separator]} />
renderFormSeparator = () => {
const { theme } = this.props;
return <View style={[sharedStyles.separator, styles.formSeparator, { backgroundColor: themes[theme].separatorColor }]} />;
}
renderItem = ({ item }) => { renderItem = ({ item }) => {
const { baseUrl, user, theme } = this.props; const { baseUrl, user, theme } = this.props;
@ -305,7 +293,7 @@ class CreateChannelView extends React.Component {
} }
]} ]}
renderItem={this.renderItem} renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator} ItemSeparatorComponent={List.Separator}
enableEmptySections enableEmptySections
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
/> />
@ -341,13 +329,13 @@ class CreateChannelView extends React.Component {
theme={theme} theme={theme}
underlineColorAndroid='transparent' underlineColorAndroid='transparent'
/> />
{this.renderFormSeparator()} <List.Separator />
{this.renderType()} {this.renderType()}
{this.renderFormSeparator()} <List.Separator />
{this.renderReadOnly()} {this.renderReadOnly()}
{this.renderFormSeparator()} <List.Separator />
{this.renderEncrypted()} {this.renderEncrypted()}
{this.renderFormSeparator()} <List.Separator />
{this.renderBroadcast()} {this.renderBroadcast()}
</View> </View>
<View style={styles.invitedHeader}> <View style={styles.invitedHeader}>

View File

@ -22,7 +22,7 @@ const SelectUsers = ({
const getUsers = debounce(async(keyword = '') => { const getUsers = debounce(async(keyword = '') => {
try { try {
const db = database.active; const db = database.active;
const usersCollection = db.collections.get('users'); const usersCollection = db.get('users');
const res = await RocketChat.search({ text: keyword, filterRooms: false }); const res = await RocketChat.search({ text: keyword, filterRooms: false });
let items = [...users.filter(u => selected.includes(u.name)), ...res.filter(r => !users.find(u => u.name === r.name))]; let items = [...users.filter(u => selected.includes(u.name)), ...res.filter(r => !users.find(u => u.name === r.name))];
const records = await usersCollection.query(Q.where('username', Q.oneOf(items.map(u => u.name)))).fetch(); const records = await usersCollection.query(Q.where('username', Q.oneOf(items.map(u => u.name)))).fetch();

View File

@ -2,7 +2,6 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ScrollView, Text, Switch } from 'react-native'; import { ScrollView, Text, Switch } from 'react-native';
import isEqual from 'lodash/isEqual';
import Loading from '../../containers/Loading'; import Loading from '../../containers/Loading';
import KeyboardView from '../../presentation/KeyboardView'; import KeyboardView from '../../presentation/KeyboardView';
@ -63,11 +62,12 @@ class CreateChannelView extends React.Component {
} }
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, prevState) {
const { channel, name } = this.state;
const { const {
loading, failure, error, result, isMasterDetail loading, failure, error, result, isMasterDetail
} = this.props; } = this.props;
if (!isEqual(this.state, prevState)) { if (channel?.rid !== prevState.channel?.rid || name !== prevState.name) {
this.setHeader(); this.setHeader();
} }
@ -136,17 +136,17 @@ class CreateChannelView extends React.Component {
}; };
selectChannel = ({ value }) => { selectChannel = ({ value }) => {
logEvent(events.CREATE_DISCUSSION_SELECT_CHANNEL); logEvent(events.CD_SELECT_CHANNEL);
this.setState({ channel: value, encrypted: value?.encrypted }); this.setState({ channel: value, encrypted: value?.encrypted });
} }
selectUsers = ({ value }) => { selectUsers = ({ value }) => {
logEvent(events.CREATE_DISCUSSION_SELECT_USERS); logEvent(events.CD_SELECT_USERS);
this.setState({ users: value }); this.setState({ users: value });
} }
onEncryptedChange = (value) => { onEncryptedChange = (value) => {
logEvent(events.CREATE_DISCUSSION_TOGGLE_ENCRY); logEvent(events.CD_TOGGLE_ENCRY);
this.setState({ encrypted: value }); this.setState({ encrypted: value });
} }
@ -222,7 +222,7 @@ const mapStateToProps = state => ({
loading: state.createDiscussion.isFetching, loading: state.createDiscussion.isFetching,
result: state.createDiscussion.result, result: state.createDiscussion.result,
blockUnauthenticatedAccess: state.settings.Accounts_AvatarBlockUnauthenticatedAccess ?? true, blockUnauthenticatedAccess: state.settings.Accounts_AvatarBlockUnauthenticatedAccess ?? true,
serverVersion: state.share.server.version || state.server.version, serverVersion: state.server.version,
isMasterDetail: state.app.isMasterDetail, isMasterDetail: state.app.isMasterDetail,
encryptionEnabled: state.encryption.enabled encryptionEnabled: state.encryption.enabled
}); });

View File

@ -84,13 +84,13 @@ export default class DirectoryOptions extends PureComponent {
inputRange: [0, 1], inputRange: [0, 1],
outputRange: [-326, 0] outputRange: [-326, 0]
}); });
const backdropOpacity = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 0.3]
});
const { const {
globalUsers, toggleWorkspace, isFederationEnabled, theme globalUsers, toggleWorkspace, isFederationEnabled, theme
} = this.props; } = this.props;
const backdropOpacity = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, themes[theme].backdropOpacity]
});
return ( return (
<> <>
<TouchableWithoutFeedback onPress={this.close}> <TouchableWithoutFeedback onPress={this.close}>

View File

@ -4,6 +4,7 @@ import {
View, FlatList, Text View, FlatList, Text
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import * as List from '../../containers/List';
import Touch from '../../utils/touch'; import Touch from '../../utils/touch';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
@ -182,11 +183,6 @@ class DirectoryView extends React.Component {
); );
} }
renderSeparator = () => {
const { theme } = this.props;
return <View style={[sharedStyles.separator, styles.separator, { backgroundColor: themes[theme].separatorColor }]} />;
}
renderItem = ({ item, index }) => { renderItem = ({ item, index }) => {
const { data, type } = this.state; const { data, type } = this.state;
const { baseUrl, user, theme } = this.props; const { baseUrl, user, theme } = this.props;
@ -251,7 +247,7 @@ class DirectoryView extends React.Component {
keyExtractor={item => item._id} keyExtractor={item => item._id}
ListHeaderComponent={this.renderHeader} ListHeaderComponent={this.renderHeader}
renderItem={this.renderItem} renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator} ItemSeparatorComponent={List.Separator}
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
ListFooterComponent={loading ? <ActivityIndicator theme={theme} /> : null} ListFooterComponent={loading ? <ActivityIndicator theme={theme} /> : null}
onEndReached={() => this.load({})} onEndReached={() => this.load({})}

View File

@ -94,7 +94,7 @@ class LanguageView extends React.Component {
setUser({ language: params.language }); setUser({ language: params.language });
const serversDB = database.servers; const serversDB = database.servers;
const usersCollection = serversDB.collections.get('users'); const usersCollection = serversDB.get('users');
await serversDB.action(async() => { await serversDB.action(async() => {
try { try {
const userRecord = await usersCollection.find(user.id); const userRecord = await usersCollection.find(user.id);

View File

@ -4,7 +4,7 @@ import {
Text, View, StyleSheet, Keyboard, Alert Text, View, StyleSheet, Keyboard, Alert
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import Button from '../containers/Button'; import Button from '../containers/Button';
@ -82,7 +82,7 @@ class LoginView extends React.Component {
UNSAFE_componentWillReceiveProps(nextProps) { UNSAFE_componentWillReceiveProps(nextProps) {
const { error } = this.props; const { error } = this.props;
if (nextProps.failure && !equal(error, nextProps.error)) { if (nextProps.failure && !dequal(error, nextProps.error)) {
Alert.alert(I18n.t('Oops'), I18n.t('Login_error')); Alert.alert(I18n.t('Oops'), I18n.t('Login_error'));
} }
} }

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FlatList, View, Text } from 'react-native'; import { FlatList, View, Text } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import styles from './styles'; import styles from './styles';
import Message from '../../containers/message'; import Message from '../../containers/message';
@ -57,7 +57,7 @@ class MessagesView extends React.Component {
if (nextState.loading !== loading) { if (nextState.loading !== loading) {
return true; return true;
} }
if (!equal(nextState.messages, messages)) { if (!dequal(nextState.messages, messages)) {
return true; return true;
} }
if (fileLoading !== nextState.fileLoading) { if (fileLoading !== nextState.fileLoading) {

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { StyleSheet, View } from 'react-native'; import { StyleSheet, View } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import isEqual from 'lodash/isEqual';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { KeyboardAwareScrollView } from '@codler/react-native-keyboard-aware-scroll-view'; import { KeyboardAwareScrollView } from '@codler/react-native-keyboard-aware-scroll-view';
@ -94,17 +93,6 @@ class ModalBlockView extends React.Component {
EventEmitter.addEventListener(viewId, this.handleUpdate); EventEmitter.addEventListener(viewId, this.handleUpdate);
} }
shouldComponentUpdate(nextProps, nextState) {
if (!isEqual(nextProps, this.props)) {
return true;
}
if (!isEqual(nextState, this.state)) {
return true;
}
return false;
}
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { navigation, route } = this.props; const { navigation, route } = this.props;
const oldData = prevProps.route.params?.data ?? {}; const oldData = prevProps.route.params?.data ?? {};

View File

@ -4,9 +4,8 @@ import {
View, StyleSheet, FlatList, Text View, StyleSheet, FlatList, Text
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import equal from 'deep-equal';
import orderBy from 'lodash/orderBy';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import * as List from '../containers/List';
import Touch from '../utils/touch'; import Touch from '../utils/touch';
import database from '../lib/database'; import database from '../lib/database';
@ -27,10 +26,9 @@ import { createChannelRequest } from '../actions/createChannel';
import { goRoom } from '../utils/goRoom'; import { goRoom } from '../utils/goRoom';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
const QUERY_SIZE = 50;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
separator: {
marginLeft: 60
},
button: { button: {
height: 46, height: 46,
flexDirection: 'row', flexDirection: 'row',
@ -77,40 +75,20 @@ class NewMessageView extends React.Component {
}; };
} }
shouldComponentUpdate(nextProps, nextState) {
const { search, chats } = this.state;
const { theme } = this.props;
if (nextProps.theme !== theme) {
return true;
}
if (!equal(nextState.search, search)) {
return true;
}
if (!equal(nextState.chats, chats)) {
return true;
}
return false;
}
componentWillUnmount() {
if (this.querySubscription && this.querySubscription.unsubscribe) {
this.querySubscription.unsubscribe();
}
}
// eslint-disable-next-line react/sort-comp // eslint-disable-next-line react/sort-comp
init = async() => { init = async() => {
try { try {
const db = database.active; const db = database.active;
const observable = await db.collections const chats = await db.collections
.get('subscriptions') .get('subscriptions')
.query(Q.where('t', 'd')) .query(
.observeWithColumns(['room_updated_at']); Q.where('t', 'd'),
Q.experimentalTake(QUERY_SIZE),
Q.experimentalSortBy('room_updated_at', Q.desc)
)
.fetch();
this.querySubscription = observable.subscribe((data) => { this.setState({ chats });
const chats = orderBy(data, ['roomUpdatedAt'], ['desc']);
this.setState({ chats });
});
} catch (e) { } catch (e) {
log(e); log(e);
} }
@ -211,10 +189,6 @@ class NewMessageView extends React.Component {
); );
} }
renderSeparator = () => {
const { theme } = this.props;
return <View style={[sharedStyles.separator, styles.separator, { backgroundColor: themes[theme].separatorColor }]} />;
}
renderItem = ({ item, index }) => { renderItem = ({ item, index }) => {
const { search, chats } = this.state; const { search, chats } = this.state;
@ -254,7 +228,7 @@ class NewMessageView extends React.Component {
keyExtractor={item => item._id} keyExtractor={item => item._id}
ListHeaderComponent={this.renderHeader} ListHeaderComponent={this.renderHeader}
renderItem={this.renderItem} renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator} ItemSeparatorComponent={List.Separator}
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }} contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }}
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
/> />

View File

@ -132,7 +132,7 @@ class NewServerView extends React.Component {
queryServerHistory = async(text) => { queryServerHistory = async(text) => {
const db = database.servers; const db = database.servers;
try { try {
const serversHistoryCollection = db.collections.get('servers_history'); const serversHistoryCollection = db.get('servers_history');
let whereClause = [ let whereClause = [
Q.where('username', Q.notEq(null)), Q.where('username', Q.notEq(null)),
Q.experimentalSortBy('updated_at', Q.desc), Q.experimentalSortBy('updated_at', Q.desc),
@ -174,7 +174,7 @@ class NewServerView extends React.Component {
} }
submit = async({ fromServerHistory = false, username }) => { submit = async({ fromServerHistory = false, username }) => {
logEvent(events.NEWSERVER_CONNECT_TO_WORKSPACE); logEvent(events.NS_CONNECT_TO_WORKSPACE);
const { text, certificate } = this.state; const { text, certificate } = this.state;
const { connectServer } = this.props; const { connectServer } = this.props;
@ -199,7 +199,7 @@ class NewServerView extends React.Component {
} }
connectOpen = () => { connectOpen = () => {
logEvent(events.NEWSERVER_JOIN_OPEN_WORKSPACE); logEvent(events.NS_JOIN_OPEN_WORKSPACE);
this.setState({ connectingOpen: true }); this.setState({ connectingOpen: true });
const { connectServer } = this.props; const { connectServer } = this.props;
connectServer('https://open.rocket.chat'); connectServer('https://open.rocket.chat');

View File

@ -6,7 +6,7 @@ import prompt from 'react-native-prompt-android';
import SHA256 from 'js-sha256'; import SHA256 from 'js-sha256';
import ImagePicker from 'react-native-image-crop-picker'; import ImagePicker from 'react-native-image-crop-picker';
import RNPickerSelect from 'react-native-picker-select'; import RNPickerSelect from 'react-native-picker-select';
import isEqual from 'lodash/isEqual'; import { dequal } from 'dequal';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import Touch from '../../utils/touch'; import Touch from '../../utils/touch';
@ -91,21 +91,11 @@ class ProfileView extends React.Component {
* it's resetting the avatar right after * it's resetting the avatar right after
* select some image from gallery. * select some image from gallery.
*/ */
if (!isEqual(omit(user, ['status']), omit(nextProps.user, ['status']))) { if (!dequal(omit(user, ['status']), omit(nextProps.user, ['status']))) {
this.init(nextProps.user); this.init(nextProps.user);
} }
} }
shouldComponentUpdate(nextProps, nextState) {
if (!isEqual(nextState, this.state)) {
return true;
}
if (!isEqual(nextProps, this.props)) {
return true;
}
return false;
}
setAvatar = (avatar) => { setAvatar = (avatar) => {
const { Accounts_AllowUserAvatarChange } = this.props; const { Accounts_AllowUserAvatarChange } = this.props;
@ -434,7 +424,7 @@ class ProfileView extends React.Component {
} }
logoutOtherLocations = () => { logoutOtherLocations = () => {
logEvent(events.PROFILE_LOGOUT_OTHER_LOCATIONS); logEvent(events.PL_OTHER_LOCATIONS);
showConfirmationAlert({ showConfirmationAlert({
message: I18n.t('You_will_be_logged_out_from_other_locations'), message: I18n.t('You_will_be_logged_out_from_other_locations'),
confirmationText: I18n.t('Logout'), confirmationText: I18n.t('Logout'),
@ -443,7 +433,7 @@ class ProfileView extends React.Component {
await RocketChat.logoutOtherLocations(); await RocketChat.logoutOtherLocations();
EventEmitter.emit(LISTENER, { message: I18n.t('Logged_out_of_other_clients_successfully') }); EventEmitter.emit(LISTENER, { message: I18n.t('Logged_out_of_other_clients_successfully') });
} catch { } catch {
logEvent(events.PROFILE_LOGOUT_OTHER_LOCATIONS_F); logEvent(events.PL_OTHER_LOCATIONS_F);
EventEmitter.emit(LISTENER, { message: I18n.t('Logout_failed') }); EventEmitter.emit(LISTENER, { message: I18n.t('Logout_failed') });
} }
} }

View File

@ -1,9 +1,10 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FlatList, View, Text } from 'react-native'; import { FlatList, View, Text } from 'react-native';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import moment from 'moment'; import moment from 'moment';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import * as List from '../../containers/List';
import Avatar from '../../containers/Avatar'; import Avatar from '../../containers/Avatar';
import styles from './styles'; import styles from './styles';
@ -55,7 +56,7 @@ class ReadReceiptView extends React.Component {
if (nextState.loading !== loading) { if (nextState.loading !== loading) {
return true; return true;
} }
if (!equal(nextState.receipts, receipts)) { if (!dequal(nextState.receipts, receipts)) {
return true; return true;
} }
return false; return false;
@ -121,11 +122,6 @@ class ReadReceiptView extends React.Component {
); );
} }
renderSeparator = () => {
const { theme } = this.props;
return <View style={[styles.separator, { backgroundColor: themes[theme].separatorColor }]} />;
}
render() { render() {
const { receipts, loading } = this.state; const { receipts, loading } = this.state;
const { theme } = this.props; const { theme } = this.props;
@ -143,7 +139,7 @@ class ReadReceiptView extends React.Component {
<FlatList <FlatList
data={receipts} data={receipts}
renderItem={this.renderItem} renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator} ItemSeparatorComponent={List.Separator}
style={[ style={[
styles.list, styles.list,
{ {

View File

@ -53,7 +53,15 @@ class RoomActionsView extends React.Component {
closeRoom: PropTypes.func, closeRoom: PropTypes.func,
theme: PropTypes.string, theme: PropTypes.string,
fontScale: PropTypes.number, fontScale: PropTypes.number,
serverVersion: PropTypes.string serverVersion: PropTypes.string,
addUserToJoinedRoomPermission: PropTypes.array,
addUserToAnyCRoomPermission: PropTypes.array,
addUserToAnyPRoomPermission: PropTypes.array,
createInviteLinksPermission: PropTypes.array,
editRoomPermission: PropTypes.array,
toggleRoomE2EEncryptionPermission: PropTypes.array,
viewBroadcastMemberListPermission: PropTypes.array,
transferLivechatGuestPermission: PropTypes.array
} }
constructor(props) { constructor(props) {
@ -118,7 +126,7 @@ class RoomActionsView extends React.Component {
this.updateRoomMember(); this.updateRoomMember();
} }
const canAutoTranslate = await RocketChat.canAutoTranslate(); const canAutoTranslate = RocketChat.canAutoTranslate();
this.setState({ canAutoTranslate }); this.setState({ canAutoTranslate });
this.canAddUser(); this.canAddUser();
@ -159,60 +167,62 @@ class RoomActionsView extends React.Component {
canAddUser = async() => { canAddUser = async() => {
const { room, joined } = this.state; const { room, joined } = this.state;
const { addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission } = this.props;
const { rid, t } = room; const { rid, t } = room;
let canAdd = false; let canAddUser = false;
const userInRoom = joined; const userInRoom = joined;
const permissions = await RocketChat.hasPermission(['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room'], rid); const permissions = await RocketChat.hasPermission([addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission], rid);
if (permissions) { if (userInRoom && permissions[0]) {
if (userInRoom && permissions['add-user-to-joined-room']) { canAddUser = true;
canAdd = true;
}
if (t === 'c' && permissions['add-user-to-any-c-room']) {
canAdd = true;
}
if (t === 'p' && permissions['add-user-to-any-p-room']) {
canAdd = true;
}
} }
this.setState({ canAddUser: canAdd }); if (t === 'c' && permissions[1]) {
canAddUser = true;
}
if (t === 'p' && permissions[2]) {
canAddUser = true;
}
this.setState({ canAddUser });
} }
canInviteUser = async() => { canInviteUser = async() => {
const { room } = this.state; const { room } = this.state;
const { createInviteLinksPermission } = this.props;
const { rid } = room; const { rid } = room;
const permissions = await RocketChat.hasPermission(['create-invite-links'], rid); const permissions = await RocketChat.hasPermission([createInviteLinksPermission], rid);
const canInviteUser = permissions && permissions['create-invite-links']; const canInviteUser = permissions[0];
this.setState({ canInviteUser }); this.setState({ canInviteUser });
} }
canEdit = async() => { canEdit = async() => {
const { room } = this.state; const { room } = this.state;
const { editRoomPermission } = this.props;
const { rid } = room; const { rid } = room;
const permissions = await RocketChat.hasPermission(['edit-room'], rid); const permissions = await RocketChat.hasPermission([editRoomPermission], rid);
const canEdit = permissions && permissions['edit-room']; const canEdit = permissions[0];
this.setState({ canEdit }); this.setState({ canEdit });
} }
canToggleEncryption = async() => { canToggleEncryption = async() => {
const { room } = this.state; const { room } = this.state;
const { toggleRoomE2EEncryptionPermission } = this.props;
const { rid } = room; const { rid } = room;
const permissions = await RocketChat.hasPermission(['toggle-room-e2e-encryption'], rid); const permissions = await RocketChat.hasPermission([toggleRoomE2EEncryptionPermission], rid);
const canToggleEncryption = permissions && permissions['toggle-room-e2e-encryption']; const canToggleEncryption = permissions[0];
this.setState({ canToggleEncryption }); this.setState({ canToggleEncryption });
} }
canViewMembers = async() => { canViewMembers = async() => {
const { room } = this.state; const { room } = this.state;
const { viewBroadcastMemberListPermission } = this.props;
const { rid, t, broadcast } = room; const { rid, t, broadcast } = room;
if (broadcast) { if (broadcast) {
const viewBroadcastMemberListPermission = 'view-broadcast-member-list';
const permissions = await RocketChat.hasPermission([viewBroadcastMemberListPermission], rid); const permissions = await RocketChat.hasPermission([viewBroadcastMemberListPermission], rid);
if (!permissions[viewBroadcastMemberListPermission]) { if (!permissions[0]) {
return false; return false;
} }
} }
@ -226,16 +236,10 @@ class RoomActionsView extends React.Component {
canForwardGuest = async() => { canForwardGuest = async() => {
const { room } = this.state; const { room } = this.state;
const { transferLivechatGuestPermission } = this.props;
const { rid } = room; const { rid } = room;
let result = true; const permissions = await RocketChat.hasPermission([transferLivechatGuestPermission], rid);
this.setState({ canForwardGuest: permissions[0] });
const transferLivechatGuest = 'transfer-livechat-guest';
const permissions = await RocketChat.hasPermission([transferLivechatGuest], rid);
if (!permissions[transferLivechatGuest]) {
result = false;
}
this.setState({ canForwardGuest: result });
} }
canReturnQueue = async() => { canReturnQueue = async() => {
@ -482,7 +486,7 @@ class RoomActionsView extends React.Component {
<List.Separator /> <List.Separator />
<List.Item <List.Item
title='Voice_call' title='Voice_call'
onPress={() => RocketChat.callJitsi(room?.rid, true)} onPress={() => RocketChat.callJitsi(room, true)}
testID='room-actions-voice' testID='room-actions-voice'
left={() => <List.Icon name='phone' />} left={() => <List.Icon name='phone' />}
showActionIndicator showActionIndicator
@ -490,7 +494,7 @@ class RoomActionsView extends React.Component {
<List.Separator /> <List.Separator />
<List.Item <List.Item
title='Video_call' title='Video_call'
onPress={() => RocketChat.callJitsi(room?.rid)} onPress={() => RocketChat.callJitsi(room)}
testID='room-actions-video' testID='room-actions-video'
left={() => <List.Icon name='camera' />} left={() => <List.Icon name='camera' />}
showActionIndicator showActionIndicator
@ -866,7 +870,15 @@ class RoomActionsView extends React.Component {
const mapStateToProps = state => ({ const mapStateToProps = state => ({
jitsiEnabled: state.settings.Jitsi_Enabled || false, jitsiEnabled: state.settings.Jitsi_Enabled || false,
encryptionEnabled: state.encryption.enabled, encryptionEnabled: state.encryption.enabled,
serverVersion: state.server.version serverVersion: state.server.version,
addUserToJoinedRoomPermission: state.permissions['add-user-to-joined-room'],
addUserToAnyCRoomPermission: state.permissions['add-user-to-any-c-room'],
addUserToAnyPRoomPermission: state.permissions['add-user-to-any-p-room'],
createInviteLinksPermission: state.permissions['create-invite-links'],
editRoomPermission: state.permissions['edit-room'],
toggleRoomE2EEncryptionPermission: state.permissions['toggle-room-e2e-encryption'],
viewBroadcastMemberListPermission: state.permissions['view-broadcast-member-list'],
transferLivechatGuestPermission: state.permissions['transfer-livechat-guest']
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({

View File

@ -4,15 +4,13 @@ import {
Text, View, ScrollView, TouchableOpacity, Keyboard, Alert Text, View, ScrollView, TouchableOpacity, Keyboard, Alert
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import equal from 'deep-equal';
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit'; import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import ImagePicker from 'react-native-image-crop-picker'; import ImagePicker from 'react-native-image-crop-picker';
import isEqual from 'lodash/isEqual'; import { dequal } from 'dequal';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import lt from 'semver/functions/lt'; import lt from 'semver/functions/lt';
import coerce from 'semver/functions/coerce'; import coerce from 'semver/functions/coerce';
import database from '../../lib/database'; import database from '../../lib/database';
import { deleteRoom as deleteRoomAction } from '../../actions/room'; import { deleteRoom as deleteRoomAction } from '../../actions/room';
import KeyboardView from '../../presentation/KeyboardView'; import KeyboardView from '../../presentation/KeyboardView';
@ -44,14 +42,6 @@ const PERMISSION_ARCHIVE = 'archive-room';
const PERMISSION_UNARCHIVE = 'unarchive-room'; const PERMISSION_UNARCHIVE = 'unarchive-room';
const PERMISSION_DELETE_C = 'delete-c'; const PERMISSION_DELETE_C = 'delete-c';
const PERMISSION_DELETE_P = 'delete-p'; const PERMISSION_DELETE_P = 'delete-p';
const PERMISSIONS_ARRAY = [
PERMISSION_SET_READONLY,
PERMISSION_SET_REACT_WHEN_READONLY,
PERMISSION_ARCHIVE,
PERMISSION_UNARCHIVE,
PERMISSION_DELETE_C,
PERMISSION_DELETE_P
];
class RoomInfoEditView extends React.Component { class RoomInfoEditView extends React.Component {
static navigationOptions = () => ({ static navigationOptions = () => ({
@ -63,7 +53,13 @@ class RoomInfoEditView extends React.Component {
deleteRoom: PropTypes.func, deleteRoom: PropTypes.func,
serverVersion: PropTypes.string, serverVersion: PropTypes.string,
encryptionEnabled: PropTypes.bool, encryptionEnabled: PropTypes.bool,
theme: PropTypes.string theme: PropTypes.string,
setReadOnlyPermission: PropTypes.array,
setReactWhenReadOnlyPermission: PropTypes.array,
archiveRoomPermission: PropTypes.array,
unarchiveRoomPermission: PropTypes.array,
deleteCPermission: PropTypes.array,
deletePPermission: PropTypes.array
}; };
constructor(props) { constructor(props) {
@ -90,16 +86,6 @@ class RoomInfoEditView extends React.Component {
this.loadRoom(); this.loadRoom();
} }
shouldComponentUpdate(nextProps, nextState) {
if (!equal(nextState, this.state)) {
return true;
}
if (!equal(nextProps, this.props)) {
return true;
}
return false;
}
componentWillUnmount() { componentWillUnmount() {
if (this.querySubscription && this.querySubscription.unsubscribe) { if (this.querySubscription && this.querySubscription.unsubscribe) {
this.querySubscription.unsubscribe(); this.querySubscription.unsubscribe();
@ -108,14 +94,22 @@ class RoomInfoEditView extends React.Component {
// eslint-disable-next-line react/sort-comp // eslint-disable-next-line react/sort-comp
loadRoom = async() => { loadRoom = async() => {
const { route } = this.props; const {
route,
setReadOnlyPermission,
setReactWhenReadOnlyPermission,
archiveRoomPermission,
unarchiveRoomPermission,
deleteCPermission,
deletePPermission
} = this.props;
const rid = route.params?.rid; const rid = route.params?.rid;
if (!rid) { if (!rid) {
return; return;
} }
try { try {
const db = database.active; const db = database.active;
const sub = await db.collections.get('subscriptions').find(rid); const sub = await db.get('subscriptions').find(rid);
const observable = sub.observe(); const observable = sub.observe();
this.querySubscription = observable.subscribe((data) => { this.querySubscription = observable.subscribe((data) => {
@ -123,8 +117,25 @@ class RoomInfoEditView extends React.Component {
this.init(this.room); this.init(this.room);
}); });
const permissions = await RocketChat.hasPermission(PERMISSIONS_ARRAY, rid); const result = await RocketChat.hasPermission([
this.setState({ permissions }); setReadOnlyPermission,
setReactWhenReadOnlyPermission,
archiveRoomPermission,
unarchiveRoomPermission,
deleteCPermission,
deletePPermission
], rid);
this.setState({
permissions: {
[PERMISSION_SET_READONLY]: result[0],
[PERMISSION_SET_REACT_WHEN_READONLY]: result[1],
[PERMISSION_ARCHIVE]: result[2],
[PERMISSION_UNARCHIVE]: result[3],
[PERMISSION_DELETE_C]: result[4],
[PERMISSION_DELETE_P]: result[5]
}
});
} catch (e) { } catch (e) {
log(e); log(e);
} }
@ -179,7 +190,7 @@ class RoomInfoEditView extends React.Component {
&& room.t === 'p' === t && room.t === 'p' === t
&& room.ro === ro && room.ro === ro
&& room.reactWhenReadOnly === reactWhenReadOnly && room.reactWhenReadOnly === reactWhenReadOnly
&& isEqual(room.sysMes, systemMessages) && dequal(room.sysMes, systemMessages)
&& enableSysMes === (room.sysMes && room.sysMes.length > 0) && enableSysMes === (room.sysMes && room.sysMes.length > 0)
&& room.encrypted === encrypted && room.encrypted === encrypted
&& isEmpty(avatar) && isEmpty(avatar)
@ -239,7 +250,7 @@ class RoomInfoEditView extends React.Component {
params.reactWhenReadOnly = reactWhenReadOnly; params.reactWhenReadOnly = reactWhenReadOnly;
} }
if (!isEqual(room.sysMes, systemMessages)) { if (!dequal(room.sysMes, systemMessages)) {
params.systemMessages = systemMessages; params.systemMessages = systemMessages;
} }
@ -666,8 +677,14 @@ class RoomInfoEditView extends React.Component {
} }
const mapStateToProps = state => ({ const mapStateToProps = state => ({
serverVersion: state.share.server.version || state.server.version, serverVersion: state.server.version,
encryptionEnabled: state.encryption.enabled encryptionEnabled: state.encryption.enabled,
setReadOnlyPermission: state.permissions[PERMISSION_SET_READONLY],
setReactWhenReadOnlyPermission: state.permissions[PERMISSION_SET_REACT_WHEN_READONLY],
archiveRoomPermission: state.permissions[PERMISSION_ARCHIVE],
unarchiveRoomPermission: state.permissions[PERMISSION_UNARCHIVE],
deleteCPermission: state.permissions[PERMISSION_DELETE_C],
deletePPermission: state.permissions[PERMISSION_DELETE_P]
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({

View File

@ -31,7 +31,6 @@ import SafeAreaView from '../../containers/SafeAreaView';
import { goRoom } from '../../utils/goRoom'; import { goRoom } from '../../utils/goRoom';
import Navigation from '../../lib/Navigation'; import Navigation from '../../lib/Navigation';
const PERMISSION_EDIT_ROOM = 'edit-room';
const getRoomTitle = (room, type, name, username, statusText, theme) => (type === 'd' const getRoomTitle = (room, type, name, username, statusText, theme) => (type === 'd'
? ( ? (
<> <>
@ -55,7 +54,8 @@ class RoomInfoView extends React.Component {
rooms: PropTypes.array, rooms: PropTypes.array,
theme: PropTypes.string, theme: PropTypes.string,
isMasterDetail: PropTypes.bool, isMasterDetail: PropTypes.bool,
jitsiEnabled: PropTypes.bool jitsiEnabled: PropTypes.bool,
editRoomPermission: PropTypes.array
} }
constructor(props) { constructor(props) {
@ -136,7 +136,7 @@ class RoomInfoView extends React.Component {
getRoleDescription = async(id) => { getRoleDescription = async(id) => {
const db = database.active; const db = database.active;
try { try {
const rolesCollection = db.collections.get('roles'); const rolesCollection = db.get('roles');
const role = await rolesCollection.find(id); const role = await rolesCollection.find(id);
if (role) { if (role) {
return role.description; return role.description;
@ -193,7 +193,7 @@ class RoomInfoView extends React.Component {
loadRoom = async() => { loadRoom = async() => {
const { room: roomState } = this.state; const { room: roomState } = this.state;
const { route } = this.props; const { route, editRoomPermission } = this.props;
let room = route.params?.room; let room = route.params?.room;
if (room && room.observe) { if (room && room.observe) {
this.roomObservable = room.observe(); this.roomObservable = room.observe();
@ -213,8 +213,8 @@ class RoomInfoView extends React.Component {
} }
} }
const permissions = await RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid); const permissions = await RocketChat.hasPermission([editRoomPermission], room.rid);
if (permissions[PERMISSION_EDIT_ROOM] && !room.prid) { if (permissions[0] && !room.prid) {
this.setState({ showEdit: true }, () => this.setHeader()); this.setState({ showEdit: true }, () => this.setHeader());
} }
} }
@ -276,7 +276,7 @@ class RoomInfoView extends React.Component {
videoCall = () => { videoCall = () => {
const { room } = this.state; const { room } = this.state;
RocketChat.callJitsi(room.rid); RocketChat.callJitsi(room);
} }
renderAvatar = (room, roomUser) => { renderAvatar = (room, roomUser) => {
@ -369,7 +369,8 @@ class RoomInfoView extends React.Component {
const mapStateToProps = state => ({ const mapStateToProps = state => ({
rooms: state.room.rooms, rooms: state.room.rooms,
isMasterDetail: state.app.isMasterDetail, isMasterDetail: state.app.isMasterDetail,
jitsiEnabled: state.settings.Jitsi_Enabled || false jitsiEnabled: state.settings.Jitsi_Enabled || false,
editRoomPermission: state.permissions['edit-room']
}); });
export default connect(mapStateToProps)(withTheme(RoomInfoView)); export default connect(mapStateToProps)(withTheme(RoomInfoView));

View File

@ -1,8 +1,9 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FlatList, View } from 'react-native'; import { FlatList } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import * as List from '../../containers/List';
import styles from './styles'; import styles from './styles';
import UserItem from '../../presentation/UserItem'; import UserItem from '../../presentation/UserItem';
@ -28,6 +29,12 @@ import { goRoom } from '../../utils/goRoom';
const PAGE_SIZE = 25; const PAGE_SIZE = 25;
const PERMISSION_MUTE_USER = 'mute-user';
const PERMISSION_SET_LEADER = 'set-leader';
const PERMISSION_SET_OWNER = 'set-owner';
const PERMISSION_SET_MODERATOR = 'set-moderator';
const PERMISSION_REMOVE_USER = 'remove-user';
class RoomMembersView extends React.Component { class RoomMembersView extends React.Component {
static propTypes = { static propTypes = {
navigation: PropTypes.object, navigation: PropTypes.object,
@ -43,7 +50,12 @@ class RoomMembersView extends React.Component {
showActionSheet: PropTypes.func, showActionSheet: PropTypes.func,
theme: PropTypes.string, theme: PropTypes.string,
isMasterDetail: PropTypes.bool, isMasterDetail: PropTypes.bool,
useRealName: PropTypes.bool useRealName: PropTypes.bool,
muteUserPermission: PropTypes.array,
setLeaderPermission: PropTypes.array,
setOwnerPermission: PropTypes.array,
setModeratorPermission: PropTypes.array,
removeUserPermission: PropTypes.array
} }
constructor(props) { constructor(props) {
@ -81,7 +93,20 @@ class RoomMembersView extends React.Component {
this.fetchMembers(); this.fetchMembers();
const { room } = this.state; const { room } = this.state;
this.permissions = await RocketChat.hasPermission(['mute-user', 'set-leader', 'set-owner', 'set-moderator', 'remove-user'], room.rid); const {
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
} = this.props;
const result = await RocketChat.hasPermission([
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
], room.rid);
this.permissions = {
[PERMISSION_MUTE_USER]: result[0],
[PERMISSION_SET_LEADER]: result[1],
[PERMISSION_SET_OWNER]: result[2],
[PERMISSION_SET_MODERATOR]: result[3],
[PERMISSION_REMOVE_USER]: result[4]
};
const hasSinglePermission = Object.values(this.permissions).some(p => !!p); const hasSinglePermission = Object.values(this.permissions).some(p => !!p);
if (hasSinglePermission) { if (hasSinglePermission) {
@ -122,7 +147,7 @@ class RoomMembersView extends React.Component {
navToDirectMessage = async(item) => { navToDirectMessage = async(item) => {
try { try {
const db = database.active; const db = database.active;
const subsCollection = db.collections.get('subscriptions'); const subsCollection = db.get('subscriptions');
const query = await subsCollection.query(Q.where('name', item.username)).fetch(); const query = await subsCollection.query(Q.where('name', item.username)).fetch();
if (query.length) { if (query.length) {
const [room] = query; const [room] = query;
@ -395,11 +420,6 @@ class RoomMembersView extends React.Component {
<SearchBox onChangeText={text => this.onSearchChangeText(text)} testID='room-members-view-search' /> <SearchBox onChangeText={text => this.onSearchChangeText(text)} testID='room-members-view-search' />
) )
renderSeparator = () => {
const { theme } = this.props;
return <View style={[styles.separator, { backgroundColor: themes[theme].separatorColor }]} />;
}
renderItem = ({ item }) => { renderItem = ({ item }) => {
const { baseUrl, user, theme } = this.props; const { baseUrl, user, theme } = this.props;
@ -429,7 +449,7 @@ class RoomMembersView extends React.Component {
renderItem={this.renderItem} renderItem={this.renderItem}
style={[styles.list, { backgroundColor: themes[theme].backgroundColor }]} style={[styles.list, { backgroundColor: themes[theme].backgroundColor }]}
keyExtractor={item => item._id} keyExtractor={item => item._id}
ItemSeparatorComponent={this.renderSeparator} ItemSeparatorComponent={List.Separator}
ListHeaderComponent={this.renderSearchBar} ListHeaderComponent={this.renderSearchBar}
ListFooterComponent={() => { ListFooterComponent={() => {
if (isLoading) { if (isLoading) {
@ -452,7 +472,12 @@ const mapStateToProps = state => ({
baseUrl: state.server.server, baseUrl: state.server.server,
user: getUserSelector(state), user: getUserSelector(state),
isMasterDetail: state.app.isMasterDetail, isMasterDetail: state.app.isMasterDetail,
useRealName: state.settings.UI_Use_Real_Name useRealName: state.settings.UI_Use_Real_Name,
muteUserPermission: state.permissions[PERMISSION_MUTE_USER],
setLeaderPermission: state.permissions[PERMISSION_SET_LEADER],
setOwnerPermission: state.permissions[PERMISSION_SET_OWNER],
setModeratorPermission: state.permissions[PERMISSION_SET_MODERATOR],
removeUserPermission: state.permissions[PERMISSION_REMOVE_USER]
}); });
export default connect(mapStateToProps)(withActionSheet(withTheme(RoomMembersView))); export default connect(mapStateToProps)(withActionSheet(withTheme(RoomMembersView)));

View File

@ -168,7 +168,7 @@ const Header = React.memo(({
theme={theme} theme={theme}
/> />
</View> </View>
<SubTitle usersTyping={usersTyping} subtitle={subtitle} theme={theme} renderFunc={renderFunc} /> <SubTitle usersTyping={tmid ? [] : usersTyping} subtitle={subtitle} theme={theme} renderFunc={renderFunc} />
</TouchableOpacity> </TouchableOpacity>
); );
}); });

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import isEqual from 'react-fast-compare'; import { dequal } from 'dequal';
import * as HeaderButton from '../../../containers/HeaderButton'; import * as HeaderButton from '../../../containers/HeaderButton';
import database from '../../../lib/database'; import database from '../../../lib/database';
@ -35,7 +35,7 @@ class RightButtonsContainer extends Component {
const db = database.active; const db = database.active;
if (tmid) { if (tmid) {
try { try {
const threadRecord = await db.collections.get('messages').find(tmid); const threadRecord = await db.get('messages').find(tmid);
this.observeThread(threadRecord); this.observeThread(threadRecord);
} catch (e) { } catch (e) {
console.log('Can\'t find message to observe.'); console.log('Can\'t find message to observe.');
@ -43,7 +43,7 @@ class RightButtonsContainer extends Component {
} }
if (rid) { if (rid) {
try { try {
const subCollection = db.collections.get('subscriptions'); const subCollection = db.get('subscriptions');
const subRecord = await subCollection.find(rid); const subRecord = await subCollection.find(rid);
this.observeSubscription(subRecord); this.observeSubscription(subRecord);
} catch (e) { } catch (e) {
@ -59,15 +59,16 @@ class RightButtonsContainer extends Component {
if (nextState.isFollowingThread !== isFollowingThread) { if (nextState.isFollowingThread !== isFollowingThread) {
return true; return true;
} }
if (!isEqual(nextState.tunread, tunread)) { if (!dequal(nextState.tunread, tunread)) {
return true; return true;
} }
if (!isEqual(nextState.tunreadUser, tunreadUser)) { if (!dequal(nextState.tunreadUser, tunreadUser)) {
return true; return true;
} }
if (!isEqual(nextState.tunreadGroup, tunreadGroup)) { if (!dequal(nextState.tunreadGroup, tunreadGroup)) {
return true; return true;
} }
return false;
} }
componentWillUnmount() { componentWillUnmount() {

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import equal from 'deep-equal'; import { dequal } from 'dequal';
import Header from './Header'; import Header from './Header';
import LeftButtons from './LeftButtons'; import LeftButtons from './LeftButtons';
@ -65,7 +65,7 @@ class RoomHeaderView extends Component {
if (nextProps.height !== height) { if (nextProps.height !== height) {
return true; return true;
} }
if (!equal(nextProps.usersTyping, usersTyping)) { if (!dequal(nextProps.usersTyping, usersTyping)) {
return true; return true;
} }
if (nextProps.goRoomActionsView !== goRoomActionsView) { if (nextProps.goRoomActionsView !== goRoomActionsView) {

Some files were not shown because too many files have changed in this diff Show More