Merge 4.17.0 into master (#3211)

* [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)

* [IMPROVEMENT] Message attachment colors (#2860)

* Added convertStrToHex function and updated Reply component

* Removed convertStrtToHex function and added attachmentBackground

* Added color2k, removed transparent view and applied transparentize to backgroundColor

* Added stories

* Update Reply stories

* Update Reply stories

* Fix lint

* Update Reply stories

* Fix props

* Move tests to Message stories

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

* [FIX] App forgetting workspace when server is not finished added (#2798)

* [FIX] App forgetting workspace

* Added e2e tests

* Update login.js

* Update logout.js

* Reverted changes on login and share, updated init

* Update 08-persistantworkspace.spec.js

* Revert unnecessary changes

* Revert line change

* Update share.js

* Tweak tests

* Use wm shorthand

* Remove irrelevant calls to RocketChat.TOKEN_KEY

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

* [TESTS] Add E2E tests to draft message (#2960)

* [E2E TEST] Draft message

* Fix tests

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

* [TESTS] Add E2E tests to group DM (#2961)

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

* [TESTS] Add E2E tests to directory (#2964)

* [E2E TEST] Directory

* Fix tests

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

* [CHORE] Simplify server version comparison (#2922)

* Simplify server version where needed

* Added lte and gte functions and updated imports

* Updated functions names

* Update util functions

* Update util function and added methods

* Remove lt and coerce from getPermissions and mergeSubscriptionsRooms

* Fix comparison

* Update getPermissions.js

* Remove unused import

* Fix lint

* Fix lint

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

* [TESTS] Add E2E tests to discussions (#2970)

* [E2E TEST] Discussions

* fix error Cannot find UI elemen

* Fix tests

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

* [FIX] Attachment not rendering markdown (#2924)

* [FIX] Render markdown in Fields content

* Added stories

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

* [TESTS] Add e2e tests for mark message as unread (#2953)

* [E2E TEST] Add e2e tests for mark message as unread

* fixed test for draft message

* change test name

* move test to other file

* Remove unnecessary tests

* Rename file

* Update jest tests

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

* [TESTS] Add E2E tests to delete server (#2954)

* [E2E TEST] Delete server

* fixed test for delete server

* fix tests

* minor changes

* Rename file

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

* [CHORE] Refactor RoomActionsView permissions (#2872)

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

* [CHORE] Add status and teams icons (#2989)

Co-authored-by: Gerzon Z <gerzonc@icloud.com>

* [FIX] SSO not working with 2FA (TOTP) (#2978)

* Update AuthenticationWebView.js

* Updated loginTOTP

* Added validation

* Update rocketchat.js

* Update rocketchat.js

* Update rocketchat.js

* Update rocketchat.js

* Fix resolve

* Remove incognito

* Fix totp being requested on webview

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

* [IMPROVEMENT] User status icons (#2991)

* Add status and teams

* Update icons, icon size and getUsersPresence

* Minor changes

* Refactor RoomTypeIcon

* Minor tweaks

* Update unit tests

* Minor fixes

* Fix styles

* Small refactor

* Update jest

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

* [REGRESSION] Auth via deep linking not working (#3015)

* Update rocketchat and add e2e test for deep linking

* Update rocketchat and add e2e test for deep linking

* Update deeplinking e2e

* fix deep linking auth

* Test deep linking auth

* Fix deeplink to rid and add tests

* Small refactor

* Add non existing server test

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

* [FIX] Create discussion request being sent with null value on encryption param (#3033)

* [CHORE] Use JSON files for i18n (#3011)

* [IMPROVEMENT] Load only i18n files needed (#3014)

* Use json

* Load only i18n files needed

* [REGRESSION] Clear local server cache not loading rooms (#3007)

* Fix clear cache

* Write e2e tests

* Fix lint

* Fix isRTL

* [FIX] Custom OAuth and iframe login attempts being called multiple times (#3020)

* [FIX] App crashing when attachment color is an invalid HEX (#3021)

* [IMPROVEMENT] Add "Message" option to Room Info (#3029)

* [CHORE] Go to room from hashtag

* Layout tweaks

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

* [FIX] Can't change status (#3018)

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

* [FIX] Search input not using the whole header space (#3012)

* [FIX] Search input not using the whole space

* Fix on getHeaderTitlePosition

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

* [FIX] E2EE password hiding automatically (#2972)

* [FIX] E2EE password hiding automatically

* add e2e test

* fixed hiding banner

* move e2e tests to 01-e2eencryption

* remove console.log

* Fix tests

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

* [TESTS] Move threads tests to its own file (#2965)

* [E2E TEST] Move threads test to another file

* changed descirbe title

* Rearrange files

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

* [FIX] Regex typo on markdown (#2928)

* [FIX] Fix Regex Typo

* Add story for testing

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

* [FIX] Make attachment validation compatible with web client (#2927)

* [FIX] Make attachment validation compatible with web client

* Added stories

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

* [FIX] Non-reply attachments displaying time (#2902)

* Remove time if no message_link

* Fix message stories for replies

* Final stories fix

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

* [FIX] i18n not being applied on login/register labels (#2930)

* Use I18n translate in login text input label

* Add to register and add missing strings

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

* Revert "[FIX] Make attachment validation compatible with web client (#2927)" (#3036)

This reverts commit d6200745c0.

* Bump version to 4.16.0 (#3037)

* [NEW] Basic support to Teams (#3016)

* Database migration

* RoomItem icon

* Team icons

* Teams group

* Small tweak on RoomTypeIcon

* RoomView Header

* Add team's channels to RoomView header

* Starting TeamChannelsView

* Icon size

* o data found

* Update TeamChannelsView, add teams subscriptions and send params to TeamChannelsView

* Use teams.ListRooms endpoint, render rooms list, remove unused functions

* Show team main on TeamChannelsView

* Disable swipe

* Pagination working

* Fix blinking no data found

* Search working

* Refactor to use BackgroundContainer while loading

* Go to room

* Cleanup

* Go to actions

* Events

* Lint

* Add debounce to go room

* Fix for tablet

* i18n

* Small fix

* Minor refactor

* Use local data when it exists

* Show last message

* Force teams migration

* Add stories to BackgroundContainer

* Remove unused component

* Move RoomViewHeader into containers folder

* Refactoring

* Testing RoomHeader

* i18n

* Fix server endpoint version

* Fix events

Co-authored-by: Gerzon Z <gerzonzcanario@gmail.com>

* [CHORE] Refactor mention tracking logic (#2997)

* [Improvement] Improve mentions

This PR focuses on improving command, emoji, channel and user mentions.

* [Tests] Added e2e tests for mention improvement

* [Improvement] Modify slash command mention logic.
Added slash command with argument preview
Slash command should show only if the message bigins with /

* Return data on search for empty text

* Minor fixes

* Update e2e tests

* Minor fix

* [FIX] allow command mentioning in between text

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

* [FIX] Status text not being updated on sidebar (#3041)

* Update StatusView.js

* Minor tweak

* Minor tweaks

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

* [FIX] Unable to search non-latin alphabet names on members list (#3039)

* Add search by name in members list

* Update RoomMembersView search

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

* Search stops working after some time (#3044)

* Bump version to 4.17.0 (#3058)

* [CHORE] Add job to upload Experimental to Google Play production (#3050)

* [REGRESSION] SAML stopped working after #2978 (#3060)

* [REGRESSION] Room actions not loading on tablet (#3061)

* Bump version to 4.16.1 (#3063)

* [REGRESSION] Fallback language stopped working (#3072)

* [CHORE] Update Detox to 18.10.0 (#3052)

* Updated detox and 5 tests

* Update e2e cases for Detox v18, update setUserStatus and added SET_STATUS_FAIL

* Downgrade mocha

* Exclude arm64 from building and update tests cases

* Update more tests cases, add registeringUser4

* Update more test files and add room-actions-scrollview testID

* Update package.json

* Remove unused username from test file and update 08-roominfo test file

* Fixing

* Mark as unread

* Fixing flaky tests

* Minor fixes

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

* [FIX] Message author touchable taking whole space available (#3048)

Co-authored-by: Gerzon Z <gerzonc@icloud.com>

* [CHORE] Improve stories (#3028)

* [CHORE] Improve stories

* Refactor Avatar and UIKitModal

* fixed undefined 'name'

* Remove commented stories

* Remove Markdown from stories/index, update Markdown test file and remove markdown stories from Message stories

* Remove StoriesSeparator

* Refactor Markdown

* Remove commented lines of code

* Small refactor

* Re-add stories

Co-authored-by: Gerzon Z <gerzonzcanario@gmail.com>
Co-authored-by: Gerzon Z <gerzonc@icloud.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>

* Bump version to 4.17.0 (#3083)

* [REGRESSION] Fallback not working when device's language is available (#3091)

* Always add 'en' i18n

* Add tests

* Bump version to 4.16.2 (#3092)

* [FIX] Connecting stream listener not being cleared (#3008)

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

* [FIX] App making calls to DDP after socket was killed by OS (#3062)

Co-authored-by: Gerzon Z <gerzonc@icloud.com>

* [NEW] Create Team (#3082)

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

* Language update from LingoHub 🤖 (#3139)

Project Name: Rocket.Chat.ReactNative
Project Link: https://translate.lingohub.com/rocketchat/dashboard/rocket-dot-chat-dot-reactnative
User: Robot LingoHub

Easy language translations with LingoHub 🚀

Co-authored-by: Robot LingoHub <robot@lingohub.com>

* [NEW] Add/Create/Remove channel on a team (#3090)

* Added Create Team

* Added actionTypes, actions, ENG strings for Teams and updated NewMessageView

* Added createTeam sagas, createTeam reducer, new Team string and update CreateChannelView

* Remove unnecessary actionTypes, reducers and sagas, e2e tests and navigation to team view

* Minor tweaks

* Show TeamChannelsView only if joined the team

* Minor tweak

* Added AddChannelTeamView

* Added permissions, translations strings for teams,  deleteTeamRoom and addTeamRooms, AddExistingChannelView, updated CreateChannelView, TeamChannelsView

* Refactor touch component and update removeRoom and deleteRoom methods

* Minor tweaks

* Minor tweaks for removing channels and addExistingChannelView

* Added missing events and fixed channels list

* Minor tweaks for refactored touch component

* Minor tweaks

* Remove unnecesary changes, update TeamChannelsView, AddExistingChannelView, AddChannelTeamView, createChannel, goRoom and Touchable

* Add screens to ModalStack, events, autoJoin, update createChannel, addRoomsToTeam and Touchable

* Minor tweak

* Update loadMessagesForRoom.js

* Updated schema, tag component, touch, AddChannelTeamView, AddExistingChannelView, ActionSheet Item

* Fix unnecessary changes

* Add i18n, update createChannel, AddExistingChannelTeamView, AddChannelTeamView, RightButton and TeamChannelsView

* Updated styles, added tag story

* Minor tweak

* Minor tweaks

* Auto-join tweak

* Minor tweaks

* Minor tweak on search

* One way to refactor :P

* Next level refactor :)

* Fix create group dm

* Refactor renderItem

* Minor bug fixes

* Fix stories

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

* [FIX] E2E Tests not working because of ES6 import (#3147)

* Update ITeam.js

* Minor tweak

* [NEW] Leave Teams (#3116)

* Added Create Team

* Added actionTypes, actions, ENG strings for Teams and updated NewMessageView

* Added createTeam sagas, createTeam reducer, new Team string and update CreateChannelView

* Remove unnecessary actionTypes, reducers and sagas, e2e tests and navigation to team view

* Minor tweaks

* Show TeamChannelsView only if joined the team

* Minor tweak

* Added AddChannelTeamView

* Added permissions, translations strings for teams,  deleteTeamRoom and addTeamRooms, AddExistingChannelView, updated CreateChannelView, TeamChannelsView

* Refactor touch component and update removeRoom and deleteRoom methods

* Minor tweaks

* Minor tweaks for removing channels and addExistingChannelView

* Added missing events and fixed channels list

* Minor tweaks for refactored touch component

* Added SelectListView and logic for leaving team

* Minor tweak

* Minor tweak

* Minor tweaks

* Remove unnecesary changes, update TeamChannelsView, AddExistingChannelView, AddChannelTeamView, createChannel, goRoom and Touchable

* Remove unnecesary prop

* Add screens to ModalStack, events, autoJoin, update createChannel, addRoomsToTeam and Touchable

* Minor tweak

* Update loadMessagesForRoom.js

* Updated schema, tag component, touch, AddChannelTeamView, AddExistingChannelView, ActionSheet Item

* Fix unnecessary changes

* Add i18n, update createChannel, AddExistingChannelTeamView, AddChannelTeamView, RightButton and TeamChannelsView

* Updated styles, added tag story

* Minor tweak

* Minor tweaks

* Auto-join tweak

* Minor tweaks

* Minor tweak on search

* Minor refactor to ListItem, add SelectListView to ModalStack, update handleLeaveTeam

* Minor tweaks

* Update SelectListView

* Update handleLeaveTeam, remove unnecessary method, add story

* Minor tweak

* Minor visual tweaks

* Updated SelectListView, RoomActionsView, leaveTeam method and string translations

* Update SelectListVIew

* Minor tweak

* Update SelectListView

* Minor tweak

* Fix for List.Item subtitles being pushed down by title's flex

* Minor tweaks

* Update RoomActionsView

* Use showConfirmationAlert and showErrorAlert

* Lint

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

* [NEW] Jump to message (#3099)

* Scrolling

* Add loadMore button at the end of loadMessagesForRoom

* Delete dummy item on tap

* Only insert loadMore dummy if there's more data

* load surrounding messages

* fixes and load next

* First dummy and dummy-next

* Save load next messages

* Check if message exists before fetching surroundings

* Refactoring List

* Jumping to message :)

* Showing blocking loader while scrolling/fetching message

* Check if message exists on local db before inserting dummy

* Delete dummies automatically when the message sent to updateMessages again

* Minor cleanup

* Fix scroll

* Highlight message

* Jump to bottom

* Load more on scroll

* Adding stories to LoadMore

* Refactoring

* Add loading indicator to LoadMore

* Small refactor

* Add LoadMore to threads

* getMoreMessages

* chat.getThreadMessages -> getThreadMessages

* Start jumping to threads

* Add jumpToMessageId on RoomView

* Nav to correct channel

* Fix PK issue on thread_messages

* Disable jump to thread from another room

* Fix nav to thread params

* Add navToRoom

* Refactor styles

* Test notch

* Fix Android border

* Fix thread message on title

* Fix NavBottomFAB on threads

* Minor cleanup

* Workaround for readThreads being called too often

* Lint

* Update tests

* Jump from search

* Go to threads from search

* Remove getItemLayout and rely on viewable items

* Fix load older

* stash working

* Fix infinite loading

* Lower itemVisiblePercentThreshhold to 10, so very long messages behave as viewable

* Add generateLoadMoreId util

* Minor cleanup

* Jump to message from notification/deep linking

* Add getMessageInfo

* Nav to threads from other rooms

* getThreadName

* Unnecessary logic

* getRoomInfo

* Colocate getMessageInfo closer to RoomView

* Minor cleanup

* Remove search from RoomActionsView

* Minor fix for search on not joined public channels

* Jump to any link

* Fix tablets

* Jump to message from MessagesView and other bug fixes

* Fix issue on Urls

* Adds race condition to cancel jump to message if it's stuck or after 5 seconds

* Jump from message search quote

* lint

* Stop onPress

* Small refactor on load methods

* Minor fixes for loadThreadMessages

* Minor typo

* LoadMore i18n

* Minor cleanup

* [FIX] Method calls not sending date params as EJSON (#3159)

* [FIX] Read receipt not displaying full date (#3133)

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

* [NEW] Remove member from team (#3117)

* Added Create Team

* Added actionTypes, actions, ENG strings for Teams and updated NewMessageView

* Added createTeam sagas, createTeam reducer, new Team string and update CreateChannelView

* Remove unnecessary actionTypes, reducers and sagas, e2e tests and navigation to team view

* Minor tweaks

* Show TeamChannelsView only if joined the team

* Minor tweak

* Added AddChannelTeamView

* Added permissions, translations strings for teams,  deleteTeamRoom and addTeamRooms, AddExistingChannelView, updated CreateChannelView, TeamChannelsView

* Refactor touch component and update removeRoom and deleteRoom methods

* Minor tweaks

* Minor tweaks for removing channels and addExistingChannelView

* Added missing events and fixed channels list

* Minor tweaks for refactored touch component

* Added SelectListView and logic for leaving team

* Added addTeamMember and removeTeamMember

* Minor tweak

* Minor tweak

* Minor tweaks

* Remove unnecesary changes, update TeamChannelsView, AddExistingChannelView, AddChannelTeamView, createChannel, goRoom and Touchable

* Remove unnecesary prop

* Add screens to ModalStack, events, autoJoin, update createChannel, addRoomsToTeam and Touchable

* Minor tweak

* Update loadMessagesForRoom.js

* Updated schema, tag component, touch, AddChannelTeamView, AddExistingChannelView, ActionSheet Item

* Fix unnecessary changes

* Add i18n, update createChannel, AddExistingChannelTeamView, AddChannelTeamView, RightButton and TeamChannelsView

* Updated styles, added tag story

* Minor tweak

* Minor tweaks

* Auto-join tweak

* Minor tweaks

* Minor tweak on search

* Minor refactor to ListItem, add SelectListView to ModalStack, update handleLeaveTeam

* Minor tweaks

* Update SelectListView

* Update handleLeaveTeam, remove unnecessary method, add story

* Minor tweak

* Minor visual tweaks

* Update SelectListView.js

* Update RoomMembersView

* Updated SelectListView, RoomActionsView, leaveTeam method and string translations

* Update SelectListVIew

* Minor tweak

* Update SelectListView

* Minor tweak

* Minor tweaks

* Fix for List.Item subtitles being pushed down by title's flex

* Minor tweaks

* Update RoomActionsView

* Use showConfirmationAlert and showErrorAlert

* Remove addTeamMember, update removeTeamMember

* Update Alert

* Minor tweaks

* Minor tweaks

* Minor tweak

* Update showActionSheet on RoomMembersView

* Remove team main from query and move code around

* Fetch roles

* Update RoomMembersView and SelectListView

* Updated leaveTeam and handleRemoveFromTeam

* Fix validation

* Remove unnecessary function

* Added confirmationAlert for missing permissions case

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

* [FIX] Add Existing Channel screen showing discussions and channels without permission (#3151)

* [Fix] the filter to show the existing channel

* [Refactor] the function that filter to isolate it

* Refactor how to wsearch properly

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

* [FIX] Member search not trimming search text (#3129)

* Fixed logout toast bug for the iOS

* Removing callToAction and replacing with confirmationText

* Handling member search with spaces to the left and right of name/username

* Changing location of string trimmer

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

* [FIX] Discussions not subscribing properly to messages when opened from inside the room (#3149)

* [FIX] Promise at subscription Room

* E2E - Update previous roomView count after send msg in discussion

* Not needed rn

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

* [FIX] Team creation not raising error if something unexpected happens (#3152)

* [IMPROVEMENT] Add error to AddExistingChannel

* Fix the alert error when create a channel

* Fix the error alert box when create channel and teams

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

* [FIX] Check permissions on team channels action sheet (#3155)

* [IMPROVEMENT] Show only the option that user can manage in TeamChannelsView

* Refactor the showActionSheet function

* Added remove team channel permission

* Cleanup

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

* [FIX] Add channels to team's flow using different navigators (#3157)

* [FIX] the navigation to AddChannelTeamView and next screens

* Fix the order inside the NewMessageStackNavigator

* Delete spaces after arrow function in onPress

* Adjusted InsideStackNavigator to a conditional animation

* Fixed route for iPad

* Small change

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

* [IMPROVEMENT] Allow discussions to be edited (#3137)

* Refactored the filter to work the edit for channel and discussion

* Removed the filter which type of room can be edit

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

* Fix tests

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

* [NEW] Delete Teams (#3123)

* Added Create Team

* Added actionTypes, actions, ENG strings for Teams and updated NewMessageView

* Added createTeam sagas, createTeam reducer, new Team string and update CreateChannelView

* Remove unnecessary actionTypes, reducers and sagas, e2e tests and navigation to team view

* Minor tweaks

* Show TeamChannelsView only if joined the team

* Minor tweak

* Added AddChannelTeamView

* Added permissions, translations strings for teams,  deleteTeamRoom and addTeamRooms, AddExistingChannelView, updated CreateChannelView, TeamChannelsView

* Refactor touch component and update removeRoom and deleteRoom methods

* Minor tweaks

* Minor tweaks for removing channels and addExistingChannelView

* Added missing events and fixed channels list

* Minor tweaks for refactored touch component

* Added SelectListView and logic for leaving team

* Added addTeamMember and removeTeamMember

* Minor tweak

* Added deleteTeam function

* Minor tweak

* Minor tweaks

* Remove unnecesary changes, update TeamChannelsView, AddExistingChannelView, AddChannelTeamView, createChannel, goRoom and Touchable

* Remove unnecesary prop

* Add screens to ModalStack, events, autoJoin, update createChannel, addRoomsToTeam and Touchable

* Minor tweak

* Update loadMessagesForRoom.js

* Updated schema, tag component, touch, AddChannelTeamView, AddExistingChannelView, ActionSheet Item

* Fix unnecessary changes

* Add i18n, update createChannel, AddExistingChannelTeamView, AddChannelTeamView, RightButton and TeamChannelsView

* Updated styles, added tag story

* Minor tweak

* Minor tweaks

* Auto-join tweak

* Minor tweaks

* Minor tweak on search

* Minor refactor to ListItem, add SelectListView to ModalStack, update handleLeaveTeam

* Minor tweaks

* Update SelectListView

* Update handleLeaveTeam, remove unnecessary method, add story

* Minor tweak

* Minor visual tweaks

* Update SelectListView.js

* Update index.js

* Update RoomMembersView

* Updated SelectListView, RoomActionsView, leaveTeam method and string translations

* Update SelectListVIew

* Minor tweak

* Update SelectListView

* Minor tweak

* Minor tweaks

* Fix for List.Item subtitles being pushed down by title's flex

* Minor tweaks

* Update RoomActionsView

* Use showConfirmationAlert and showErrorAlert

* Remove addTeamMember, update removeTeamMember

* Update Alert

* Minor tweaks

* Minor tweaks

* Minor tweak

* Update showActionSheet on RoomMembersView

* Remove team main from query and move code around

* Fetch roles

* Update RoomMembersView and SelectListView

* Update rocketchat.js

* Updated leaveTeam and handleRemoveFromTeam

* Fix validation

* Remove unnecessary function

* Update RoomActionsView

* Update en.json

* updated deleteTeam function and permissions

* Added showConfirmationAlert

* Added string translations for teams

* Fix permission

* Minor tweaks

* Typo

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

* [FIX] Android navigation bar color when Loading modal appears (#3165)

* [FIX] Modal appearance

* Undo and only add android:navigationBarColor

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

* [FIX] Check for old servers for Teams (#3171)

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

* [NEW] Convert/Move Channel to Team (#3164)

* Added Create Team

* Added actionTypes, actions, ENG strings for Teams and updated NewMessageView

* Added createTeam sagas, createTeam reducer, new Team string and update CreateChannelView

* Remove unnecessary actionTypes, reducers and sagas, e2e tests and navigation to team view

* Minor tweaks

* Show TeamChannelsView only if joined the team

* Minor tweak

* Added AddChannelTeamView

* Added permissions, translations strings for teams,  deleteTeamRoom and addTeamRooms, AddExistingChannelView, updated CreateChannelView, TeamChannelsView

* Refactor touch component and update removeRoom and deleteRoom methods

* Minor tweaks

* Minor tweaks for removing channels and addExistingChannelView

* Added missing events and fixed channels list

* Minor tweaks for refactored touch component

* Added SelectListView and logic for leaving team

* Added addTeamMember and removeTeamMember

* Minor tweak

* Added deleteTeam function

* Minor tweak

* Minor tweaks

* Remove unnecesary changes, update TeamChannelsView, AddExistingChannelView, AddChannelTeamView, createChannel, goRoom and Touchable

* Remove unnecesary prop

* Add screens to ModalStack, events, autoJoin, update createChannel, addRoomsToTeam and Touchable

* Minor tweak

* Update loadMessagesForRoom.js

* Updated schema, tag component, touch, AddChannelTeamView, AddExistingChannelView, ActionSheet Item

* Fix unnecessary changes

* Add i18n, update createChannel, AddExistingChannelTeamView, AddChannelTeamView, RightButton and TeamChannelsView

* Updated styles, added tag story

* Minor tweak

* Minor tweaks

* Auto-join tweak

* Minor tweaks

* Minor tweak on search

* Minor refactor to ListItem, add SelectListView to ModalStack, update handleLeaveTeam

* Minor tweaks

* Update SelectListView

* Update handleLeaveTeam, remove unnecessary method, add story

* Minor tweak

* Minor visual tweaks

* Update SelectListView.js

* Update index.js

* Update RoomMembersView

* Updated SelectListView, RoomActionsView, leaveTeam method and string translations

* Update SelectListVIew

* Minor tweak

* Update SelectListView

* Minor tweak

* Minor tweaks

* Fix for List.Item subtitles being pushed down by title's flex

* Minor tweaks

* Update RoomActionsView

* Use showConfirmationAlert and showErrorAlert

* Remove addTeamMember, update removeTeamMember

* Update Alert

* Minor tweaks

* Minor tweaks

* Minor tweak

* Update showActionSheet on RoomMembersView

* Remove team main from query and move code around

* Fetch roles

* Update RoomMembersView and SelectListView

* Update rocketchat.js

* Updated leaveTeam and handleRemoveFromTeam

* Fix validation

* Remove unnecessary function

* Update RoomActionsView

* Update en.json

* updated deleteTeam function and permissions

* Added showConfirmationAlert

* Added string translations for teams

* Fix permission

* Added moveChannelToTeam and convertToTeam functionality

* Fix SelectListView RadioButton

* Fix moveToTeam

* Added searchBar to SelectListVIew

* Update RoomView , SelectListVIew and string translation for error

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

* [TEST] E2E Tests for Teams (#3178)

* Added Create Team

* Added actionTypes, actions, ENG strings for Teams and updated NewMessageView

* Added createTeam sagas, createTeam reducer, new Team string and update CreateChannelView

* Remove unnecessary actionTypes, reducers and sagas, e2e tests and navigation to team view

* Minor tweaks

* Show TeamChannelsView only if joined the team

* Minor tweak

* Added AddChannelTeamView

* Added permissions, translations strings for teams,  deleteTeamRoom and addTeamRooms, AddExistingChannelView, updated CreateChannelView, TeamChannelsView

* Refactor touch component and update removeRoom and deleteRoom methods

* Minor tweaks

* Minor tweaks for removing channels and addExistingChannelView

* Added missing events and fixed channels list

* Minor tweaks for refactored touch component

* Added SelectListView and logic for leaving team

* Added addTeamMember and removeTeamMember

* Minor tweak

* Added deleteTeam function

* Minor tweak

* Minor tweaks

* Remove unnecesary changes, update TeamChannelsView, AddExistingChannelView, AddChannelTeamView, createChannel, goRoom and Touchable

* Remove unnecesary prop

* Add screens to ModalStack, events, autoJoin, update createChannel, addRoomsToTeam and Touchable

* Minor tweak

* Update loadMessagesForRoom.js

* Updated schema, tag component, touch, AddChannelTeamView, AddExistingChannelView, ActionSheet Item

* Fix unnecessary changes

* Add i18n, update createChannel, AddExistingChannelTeamView, AddChannelTeamView, RightButton and TeamChannelsView

* Updated styles, added tag story

* Minor tweak

* Minor tweaks

* Auto-join tweak

* Minor tweaks

* Minor tweak on search

* Minor refactor to ListItem, add SelectListView to ModalStack, update handleLeaveTeam

* Minor tweaks

* Update SelectListView

* Update handleLeaveTeam, remove unnecessary method, add story

* Minor tweak

* Minor visual tweaks

* Update SelectListView.js

* Update index.js

* Update RoomMembersView

* Updated SelectListView, RoomActionsView, leaveTeam method and string translations

* Update SelectListVIew

* Minor tweak

* Update SelectListView

* Minor tweak

* Minor tweaks

* Fix for List.Item subtitles being pushed down by title's flex

* Minor tweaks

* Update RoomActionsView

* Use showConfirmationAlert and showErrorAlert

* Remove addTeamMember, update removeTeamMember

* Update Alert

* Minor tweaks

* Minor tweaks

* Minor tweak

* Update showActionSheet on RoomMembersView

* Remove team main from query and move code around

* Fetch roles

* Update RoomMembersView and SelectListView

* Update rocketchat.js

* Updated leaveTeam and handleRemoveFromTeam

* Fix validation

* Remove unnecessary function

* Update RoomActionsView

* Update en.json

* updated deleteTeam function and permissions

* Added showConfirmationAlert

* Added string translations for teams

* Fix permission

* Added moveChannelToTeam and convertToTeam functionality

* Fix SelectListView RadioButton

* Fix moveToTeam

* Added searchBar to SelectListVIew

* Update RoomView , SelectListVIew and string translation for error

* E2E for Teams

* Fix tests and cleanup

* Minor refactor

* Wrong label

* Move/convert

* Fix convert

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

* [NEW] Add Teams to Directory (#3181)

* Added Teams to DirectoryView

* Fix icon

* Minor tweaks

* add tests

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

* [CHORE] Add logEvents for Teams (#3182)

* added events for team channels view and add existing channel view

* add logevents for room actions view and room info edit view

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

* [FIX] Disable jitsi call for teams (#3183)

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

* [FIX] Show alert `Not allowed` when click on a private channel that you don't be invited before (#3177)

* [FIX] Showing only channel you joined

* [FIX] How to get the params to mnavigation to other room from TeamChannelList

* Show alert Not allowed when trying access private channel that you don't joined

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

* [IMPROVEMENT] Load team's rooms from local database on team leave (#3185)

* [IMPROVEMENT] Search team list rooms of user in watermelon db

* Minor nitpick

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

* [FIX] Option to prevent users from using Invisible status (#3186)

* [FIX] Option to prevent users from using Invisible status

* Added error to pt-BR

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

* [FIX] Item not animating on tap on team's channels view (#3187)

* [FIX] Directory sending incorrect room type (#3188)

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

* [FIX] App not showing proper alert on team leave (#3161)

* [IMPROVEMENT] refactoring how to leave team

* Fix the data passed to leaveTeam

* Fixed the lint error in i18n, the path of i18n, merged two ifs in one

* Fixed the Saga's flow when try to leave a room

* Fixed params passed to leaveRoom

* Fix the function name of leaveTeam

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

* Language update from LingoHub 🤖 (#3192)

Project Name: Rocket.Chat.ReactNative
Project Link: https://translate.lingohub.com/rocketchat/dashboard/rocket-dot-chat-dot-reactnative
User: Robot LingoHub

Easy language translations with LingoHub 🚀

Co-authored-by: Robot LingoHub <robot@lingohub.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [NEW] Support Google OAuth from external browser (#3134)

* Deep linking to the app

* Handle deep linking

* Bump version to 4.17.0 (#3093)

* Revert "[IMPROVEMENT] Load team's rooms from local database on team leave (#3185)" (#3194)

This reverts commit fa00ef92ef.

* [FIX] Teams tests (#3196)

* Make team_main not optional and fix tests

* Undo isOptional and fix query

* Comment

* [FIX] Wrong system messages being passed as parameters to room save (#3197)

* [FIX] RoomItem's long press crashing the app if prop is missing (#3199)

* Check onLongPress prop

* Add Touch stories

* [FIX] Crashing on link press (#3204)

* [FIX] Don't show Block Button inside Group DM Actions (#3195)

* [FIX] Don't show Block Button inside Group DM Actions

* Use RocketChat.isGroupChat instead of simple if condition

* Add return

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

* [TEST] Fixed E2E tests (#3201)

* [FIX] Test E2E i18n

* 01-createroom and 02-room fixed

* 03-roomactions and 04-discussions

* 05-threads and 07-markasunread from room

* Test 07-markasunread

* Set notifications 'YES' and delete true in 03-forgotpassword and 04-createuser

* Fixed the data that 02-team uses and changed the message in 07-markasunread

* Added group.alternate to data.docker and commented the test for the fallback language

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

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
Co-authored-by: Rishabh Gupta <38923768+imrishabh18@users.noreply.github.com>
Co-authored-by: Youssef Muhamad <emaildeyoussefmuhamad@gmail.com>
Co-authored-by: Prateek93a <prateek93a@gmail.com>
Co-authored-by: Ezequiel De Oliveira <ezequiel1de1oliveira@gmail.com>
Co-authored-by: Hendy Irawan <hendy@hendyirawan.com>
Co-authored-by: Alexandru Naiman <alex.naiman.4@gmail.com>
Co-authored-by: Vincenzo Esposito <aenon.esposito@gmail.com>
Co-authored-by: Dani <assgex@gmail.com>
Co-authored-by: David-Tsui <st880221@gmail.com>
Co-authored-by: Luis <ljcp28ljcp@gmail.com>
Co-authored-by: phriedrich <info@phriedrich.de>
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: 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: Guilherme Siqueira <guilhersiqueira@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: Dan Caseley <dan@caseley.me.uk>
Co-authored-by: Heng Sok <sokheng@idatahub.com>
Co-authored-by: Snyk bot <snyk-bot@snyk.io>
Co-authored-by: Rohit Verma <44283521+refactor-droidyy@users.noreply.github.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: nixxou <45721836+nixxou@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>
Co-authored-by: sadegh <sadeghmohamadnia@yahoo.com>
Co-authored-by: Noach Magedman <nmagedman@gmail.com>
Co-authored-by: lingohub[bot] <69908207+lingohub[bot]@users.noreply.github.com>
Co-authored-by: Robot LingoHub <robot@lingohub.com>
Co-authored-by: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com>
This commit is contained in:
Diego Mello 2021-06-15 17:12:55 -03:00 committed by GitHub
parent 2da0ae6820
commit b4c7870166
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
134 changed files with 23284 additions and 12772 deletions

File diff suppressed because it is too large Load Diff

View File

@ -144,7 +144,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer
versionName "4.16.2"
versionName "4.17.0"
vectorDrawables.useSupportLibrary = true
if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]

View File

@ -26,4 +26,14 @@
<item name="colorPrimaryDark">@color/splashBackground</item>
<item name="android:navigationBarColor">@color/splashBackground</item>
</style>
<!-- https://github.com/facebook/react-native/blob/d1ab03235cb4b93304150878d2b9057ab45bba77/ReactAndroid/src/main/res/views/modal/values/themes.xml#L5 -->
<style name="Theme.FullScreenDialog">
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
</resources>

View File

@ -14,9 +14,10 @@ export function createChannelSuccess(data) {
};
}
export function createChannelFailure(err) {
export function createChannelFailure(err, isTeam) {
return {
type: types.CREATE_CHANNEL.FAILURE,
err
err,
isTeam
};
}

View File

@ -14,11 +14,12 @@ export function unsubscribeRoom(rid) {
};
}
export function leaveRoom(rid, t) {
export function leaveRoom(roomType, room, selected) {
return {
type: types.ROOM.LEAVE,
rid,
t
room,
roomType,
selected
};
}

View File

@ -0,0 +1,5 @@
export const MESSAGE_TYPE_LOAD_MORE = 'load_more';
export const MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK = 'load_previous_chunk';
export const MESSAGE_TYPE_LOAD_NEXT_CHUNK = 'load_next_chunk';
export const MESSAGE_TYPE_ANY_LOAD = [MESSAGE_TYPE_LOAD_MORE, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK, MESSAGE_TYPE_LOAD_NEXT_CHUNK];

View File

@ -193,5 +193,8 @@ export default {
},
Allow_Save_Media_to_Gallery: {
type: 'valueAsBoolean'
},
Accounts_AllowInvisibleStatusOption: {
type: 'valueAsString'
}
};

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Text } from 'react-native';
import { Text, View } from 'react-native';
import { themes } from '../../constants/colors';
import { CustomIcon } from '../../lib/Icons';
@ -18,14 +18,22 @@ export const Item = React.memo(({ item, hide, theme }) => {
onPress={onPress}
style={[styles.item, { backgroundColor: themes[theme].focusedBackground }]}
theme={theme}
testID={item.testID}
>
<CustomIcon name={item.icon} size={20} color={item.danger ? themes[theme].dangerColor : themes[theme].bodyText} />
<Text
numberOfLines={1}
style={[styles.title, { color: item.danger ? themes[theme].dangerColor : themes[theme].bodyText }]}
>
{item.title}
</Text>
<View style={styles.titleContainer}>
<Text
numberOfLines={1}
style={[styles.title, { color: item.danger ? themes[theme].dangerColor : themes[theme].bodyText }]}
>
{item.title}
</Text>
</View>
{ item.right ? (
<View style={styles.rightContainer}>
{item.right ? item.right() : null}
</View>
) : null }
</Button>
);
});
@ -34,7 +42,9 @@ Item.propTypes = {
title: PropTypes.string,
icon: PropTypes.string,
danger: PropTypes.bool,
onPress: PropTypes.func
onPress: PropTypes.func,
right: PropTypes.func,
testID: PropTypes.string
}),
hide: PropTypes.func,
theme: PropTypes.string

View File

@ -22,6 +22,9 @@ export default StyleSheet.create({
content: {
paddingTop: 16
},
titleContainer: {
flex: 1
},
title: {
fontSize: 16,
marginLeft: 16,
@ -58,5 +61,8 @@ export default StyleSheet.create({
fontSize: 16,
...sharedStyles.textMedium,
...sharedStyles.textAlignCenter
},
rightContainer: {
paddingLeft: 12
}
});

View File

@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import { themes } from '../../constants/colors';
import { CustomIcon } from '../../lib/Icons';
import { withTheme } from '../../theme';
import { ICON_SIZE } from './constants';
const styles = StyleSheet.create({
icon: {
@ -17,13 +18,15 @@ const ListIcon = React.memo(({
theme,
name,
color,
style
style,
testID
}) => (
<View style={[styles.icon, style]}>
<CustomIcon
name={name}
color={color ?? themes[theme].auxiliaryText}
size={20}
size={ICON_SIZE}
testID={testID}
/>
</View>
));
@ -32,7 +35,8 @@ ListIcon.propTypes = {
theme: PropTypes.string,
name: PropTypes.string,
color: PropTypes.string,
style: PropTypes.object
style: PropTypes.object,
testID: PropTypes.string
};
ListIcon.displayName = 'List.Icon';

View File

@ -10,8 +10,9 @@ import sharedStyles from '../../views/Styles';
import { withTheme } from '../../theme';
import I18n from '../../i18n';
import { Icon } from '.';
import { BASE_HEIGHT, PADDING_HORIZONTAL } from './constants';
import { BASE_HEIGHT, ICON_SIZE, PADDING_HORIZONTAL } from './constants';
import { withDimensions } from '../../dimensions';
import { CustomIcon } from '../../lib/Icons';
const styles = StyleSheet.create({
container: {
@ -34,7 +35,15 @@ const styles = StyleSheet.create({
flex: 1,
justifyContent: 'center'
},
textAlertContainer: {
flexDirection: 'row',
alignItems: 'center'
},
alertIcon: {
paddingLeft: 4
},
title: {
flexShrink: 1,
fontSize: 16,
...sharedStyles.textRegular
},
@ -50,7 +59,7 @@ const styles = StyleSheet.create({
});
const Content = React.memo(({
title, subtitle, disabled, testID, left, right, color, theme, translateTitle, translateSubtitle, showActionIndicator, fontScale
title, subtitle, disabled, testID, left, right, color, theme, translateTitle, translateSubtitle, showActionIndicator, fontScale, alert
}) => (
<View style={[styles.container, disabled && styles.disabled, { height: BASE_HEIGHT * fontScale }]} testID={testID}>
{left
@ -61,7 +70,12 @@ const Content = React.memo(({
)
: null}
<View style={styles.textContainer}>
<Text style={[styles.title, { color: color || themes[theme].titleText }]} numberOfLines={1}>{translateTitle ? I18n.t(title) : title}</Text>
<View style={styles.textAlertContainer}>
<Text style={[styles.title, { color: color || themes[theme].titleText }]} numberOfLines={1}>{translateTitle ? I18n.t(title) : title}</Text>
{alert ? (
<CustomIcon style={[styles.alertIcon, { color: themes[theme].dangerColor }]} size={ICON_SIZE} name='info' />
) : null}
</View>
{subtitle
? <Text style={[styles.subtitle, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>{translateSubtitle ? I18n.t(subtitle) : subtitle}</Text>
: null
@ -123,7 +137,8 @@ Content.propTypes = {
translateTitle: PropTypes.bool,
translateSubtitle: PropTypes.bool,
showActionIndicator: PropTypes.bool,
fontScale: PropTypes.number
fontScale: PropTypes.number,
alert: PropTypes.bool
};
Content.defaultProps = {

View File

@ -1,2 +1,3 @@
export const PADDING_HORIZONTAL = 12;
export const BASE_HEIGHT = 46;
export const ICON_SIZE = 20;

View File

@ -1,6 +1,6 @@
import React from 'react';
import {
View, StyleSheet, Text, Animated, Easing
View, StyleSheet, Text, Animated, Easing, Linking
} from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
@ -24,6 +24,9 @@ const SERVICE_HEIGHT = 58;
const BORDER_RADIUS = 2;
const SERVICES_COLLAPSED_HEIGHT = 174;
const LOGIN_STYPE_POPUP = 'popup';
const LOGIN_STYPE_REDIRECT = 'redirect';
const styles = StyleSheet.create({
serviceButton: {
borderRadius: BORDER_RADIUS,
@ -122,9 +125,9 @@ class LoginServices extends React.PureComponent {
const endpoint = 'https://accounts.google.com/o/oauth2/auth';
const redirect_uri = `${ server }/_oauth/google?close`;
const scope = 'email';
const state = this.getOAuthState();
const state = this.getOAuthState(LOGIN_STYPE_REDIRECT);
const params = `?client_id=${ clientId }&redirect_uri=${ redirect_uri }&scope=${ scope }&state=${ state }&response_type=code`;
this.openOAuth({ url: `${ endpoint }${ params }` });
Linking.openURL(`${ endpoint }${ params }`);
}
onPressLinkedin = () => {
@ -219,9 +222,16 @@ class LoginServices extends React.PureComponent {
}
}
getOAuthState = () => {
getOAuthState = (loginStyle = LOGIN_STYPE_POPUP) => {
const credentialToken = random(43);
return Base64.encodeURI(JSON.stringify({ loginStyle: 'popup', credentialToken, isCordova: true }));
let obj = { loginStyle, credentialToken, isCordova: true };
if (loginStyle === LOGIN_STYPE_REDIRECT) {
obj = {
...obj,
redirectUrl: 'rocketchat://auth'
};
}
return Base64.encodeURI(JSON.stringify(obj));
}
openOAuth = ({ url, ssoToken, authType = 'oauth' }) => {

View File

@ -32,7 +32,7 @@ class RoomHeaderContainer extends Component {
shouldComponentUpdate(nextProps) {
const {
type, title, subtitle, status, statusText, connecting, connected, onPress, usersTyping, width, height
type, title, subtitle, status, statusText, connecting, connected, onPress, usersTyping, width, height, teamMain
} = this.props;
if (nextProps.type !== type) {
return true;
@ -67,6 +67,9 @@ class RoomHeaderContainer extends Component {
if (nextProps.onPress !== onPress) {
return true;
}
if (nextProps.teamMain !== teamMain) {
return true;
}
return false;
}

View File

@ -30,6 +30,7 @@ const RoomTypeIcon = React.memo(({
return <Status style={[iconStyle, { color: STATUS_COLORS[status] ?? STATUS_COLORS.offline }]} size={size} status={status} />;
}
// TODO: move this to a separate function
let icon = 'channel-private';
if (teamMain) {
icon = `teams${ type === 'p' ? '-private' : '' }`;

View File

@ -4,19 +4,18 @@ import { Text, Clipboard } from 'react-native';
import styles from './styles';
import { themes } from '../../constants/colors';
import openLink from '../../utils/openLink';
import { LISTENER } from '../Toast';
import EventEmitter from '../../utils/events';
import I18n from '../../i18n';
const Link = React.memo(({
children, link, theme
children, link, theme, onLinkPress
}) => {
const handlePress = () => {
if (!link) {
if (!link || !onLinkPress) {
return;
}
openLink(link, theme);
onLinkPress(link);
};
const childLength = React.Children.toArray(children).filter(o => o).length;
@ -40,7 +39,8 @@ const Link = React.memo(({
Link.propTypes = {
children: PropTypes.node,
link: PropTypes.string,
theme: PropTypes.string
theme: PropTypes.string,
onLinkPress: PropTypes.func
};
export default Link;

View File

@ -82,7 +82,8 @@ class Markdown extends PureComponent {
preview: PropTypes.bool,
theme: PropTypes.string,
testID: PropTypes.string,
style: PropTypes.array
style: PropTypes.array,
onLinkPress: PropTypes.func
};
constructor(props) {
@ -218,11 +219,12 @@ class Markdown extends PureComponent {
};
renderLink = ({ children, href }) => {
const { theme } = this.props;
const { theme, onLinkPress } = this.props;
return (
<MarkdownLink
link={href}
theme={theme}
onLinkPress={onLinkPress}
>
{children}
</MarkdownLink>

View File

@ -45,7 +45,7 @@ const Content = React.memo((props) => {
} else if (props.isEncrypted) {
content = <Text style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]}>{I18n.t('Encrypted_message')}</Text>;
} else {
const { baseUrl, user } = useContext(MessageContext);
const { baseUrl, user, onLinkPress } = useContext(MessageContext);
content = (
<Markdown
msg={props.msg}
@ -61,6 +61,7 @@ const Content = React.memo((props) => {
tmid={props.tmid}
useRealName={props.useRealName}
theme={props.theme}
onLinkPress={onLinkPress}
/>
);
}

View File

@ -19,6 +19,7 @@ import Discussion from './Discussion';
import Content from './Content';
import ReadReceipt from './ReadReceipt';
import CallButton from './CallButton';
import { themes } from '../../constants/colors';
const MessageInner = React.memo((props) => {
if (props.type === 'discussion-created') {
@ -120,6 +121,7 @@ const MessageTouchable = React.memo((props) => {
onLongPress={onLongPress}
onPress={onPress}
disabled={(props.isInfo && !props.isThreadReply) || props.archived || props.isTemp}
style={{ backgroundColor: props.highlighted ? themes[props.theme].headerBackground : null }}
>
<View>
<Message {...props} />
@ -134,7 +136,9 @@ MessageTouchable.propTypes = {
isInfo: PropTypes.bool,
isThreadReply: PropTypes.bool,
isTemp: PropTypes.bool,
archived: PropTypes.bool
archived: PropTypes.bool,
highlighted: PropTypes.bool,
theme: PropTypes.string
};
Message.propTypes = {

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { memo, useEffect, useState } from 'react';
import { View } from 'react-native';
import PropTypes from 'prop-types';
@ -8,24 +8,29 @@ import { themes } from '../../constants/colors';
import I18n from '../../i18n';
import Markdown from '../markdown';
const RepliedThread = React.memo(({
const RepliedThread = memo(({
tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted, theme
}) => {
if (!tmid || !isHeader) {
return null;
}
if (!tmsg) {
fetchThreadName(tmid, id);
const [msg, setMsg] = useState(isEncrypted ? I18n.t('Encrypted_message') : tmsg);
const fetch = async() => {
const threadName = await fetchThreadName(tmid, id);
setMsg(threadName);
};
useEffect(() => {
if (!msg) {
fetch();
}
}, []);
if (!msg) {
return null;
}
let msg = tmsg;
if (isEncrypted) {
msg = I18n.t('Encrypted_message');
}
return (
<View style={styles.repliedThread} testID={`message-thread-replied-on-${ msg }`}>
<CustomIcon name='threads' size={20} style={styles.repliedThreadIcon} color={themes[theme].tintColor} />
@ -45,23 +50,6 @@ const RepliedThread = React.memo(({
</View>
</View>
);
}, (prevProps, nextProps) => {
if (prevProps.tmid !== nextProps.tmid) {
return false;
}
if (prevProps.tmsg !== nextProps.tmsg) {
return false;
}
if (prevProps.isEncrypted !== nextProps.isEncrypted) {
return false;
}
if (prevProps.isHeader !== nextProps.isHeader) {
return false;
}
if (prevProps.theme !== nextProps.theme) {
return false;
}
return true;
});
RepliedThread.propTypes = {

View File

@ -142,10 +142,13 @@ const Reply = React.memo(({
if (!attachment) {
return null;
}
const { baseUrl, user } = useContext(MessageContext);
const { baseUrl, user, jumpToMessage } = useContext(MessageContext);
const onPress = () => {
let url = attachment.title_link || attachment.author_link;
if (attachment.message_link) {
return jumpToMessage(attachment.message_link);
}
if (!url) {
return;
}

View File

@ -80,7 +80,7 @@ const UrlContent = React.memo(({ title, description, theme }) => (
});
const Url = React.memo(({ url, index, theme }) => {
if (!url) {
if (!url || url?.ignoreParse) {
return null;
}

View File

@ -9,6 +9,7 @@ import { SYSTEM_MESSAGES, getMessageTranslation } from './utils';
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
import messagesStatus from '../../constants/messagesStatus';
import { withTheme } from '../../theme';
import openLink from '../../utils/openLink';
class MessageContainer extends React.Component {
static propTypes = {
@ -33,6 +34,7 @@ class MessageContainer extends React.Component {
autoTranslateLanguage: PropTypes.string,
status: PropTypes.number,
isIgnored: PropTypes.bool,
highlighted: PropTypes.bool,
getCustomEmoji: PropTypes.func,
onLongPress: PropTypes.func,
onReactionPress: PropTypes.func,
@ -50,7 +52,9 @@ class MessageContainer extends React.Component {
blockAction: PropTypes.func,
theme: PropTypes.string,
threadBadgeColor: PropTypes.string,
toggleFollowThread: PropTypes.func
toggleFollowThread: PropTypes.func,
jumpToMessage: PropTypes.func,
onPress: PropTypes.func
}
static defaultProps = {
@ -89,10 +93,15 @@ class MessageContainer extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
const { isManualUnignored } = this.state;
const { theme, threadBadgeColor, isIgnored } = this.props;
const {
theme, threadBadgeColor, isIgnored, highlighted
} = this.props;
if (nextProps.theme !== theme) {
return true;
}
if (nextProps.highlighted !== highlighted) {
return true;
}
if (nextProps.threadBadgeColor !== threadBadgeColor) {
return true;
}
@ -112,10 +121,15 @@ class MessageContainer extends React.Component {
}
onPress = debounce(() => {
const { onPress } = this.props;
if (this.isIgnored) {
return this.onIgnoredMessagePress();
}
if (onPress) {
return onPress();
}
const { item, isThreadRoom } = this.props;
Keyboard.dismiss();
@ -265,12 +279,69 @@ class MessageContainer extends React.Component {
}
}
onLinkPress = (link) => {
const { item, theme, jumpToMessage } = this.props;
const isMessageLink = item?.attachments?.findIndex(att => att?.message_link === link) !== -1;
if (isMessageLink) {
return jumpToMessage(link);
}
openLink(link, theme);
}
render() {
const {
item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, showAttachment, timeFormat, isReadReceiptEnabled, autoTranslateRoom, autoTranslateLanguage, navToRoomInfo, getCustomEmoji, isThreadRoom, callJitsi, blockAction, rid, theme, threadBadgeColor, toggleFollowThread
item,
user,
style,
archived,
baseUrl,
useRealName,
broadcast,
fetchThreadName,
showAttachment,
timeFormat,
isReadReceiptEnabled,
autoTranslateRoom,
autoTranslateLanguage,
navToRoomInfo,
getCustomEmoji,
isThreadRoom,
callJitsi,
blockAction,
rid,
theme,
threadBadgeColor,
toggleFollowThread,
jumpToMessage,
highlighted
} = this.props;
const {
id, msg, ts, attachments, urls, reactions, t, avatar, emoji, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels, unread, blocks, autoTranslate: autoTranslateMessage, replies
id,
msg,
ts,
attachments,
urls,
reactions,
t,
avatar,
emoji,
u,
alias,
editedBy,
role,
drid,
dcount,
dlm,
tmid,
tcount,
tlm,
tmsg,
mentions,
channels,
unread,
blocks,
autoTranslate: autoTranslateMessage,
replies
} = item;
let message = msg;
@ -294,6 +365,8 @@ class MessageContainer extends React.Component {
onEncryptedPress: this.onEncryptedPress,
onDiscussionPress: this.onDiscussionPress,
onReactionLongPress: this.onReactionLongPress,
onLinkPress: this.onLinkPress,
jumpToMessage,
threadBadgeColor,
toggleFollowThread,
replies
@ -347,6 +420,7 @@ class MessageContainer extends React.Component {
callJitsi={callJitsi}
blockAction={blockAction}
theme={theme}
highlighted={highlighted}
/>
</MessageContext.Provider>
);

5
app/definition/ITeam.js Normal file
View File

@ -0,0 +1,5 @@
// https://github.com/RocketChat/Rocket.Chat/blob/develop/definition/ITeam.ts
export const TEAM_TYPE = {
PUBLIC: 0,
PRIVATE: 1
};

View File

@ -33,7 +33,7 @@
"error-invalid-date": "التاريخ غير صالح",
"error-invalid-description": "الوصف غير صالح",
"error-invalid-domain": "عنوان الموقع غير صالح",
"error-invalid-email": "عنوان البريد اﻹلكتروني غير صالح {{emai}}",
"error-invalid-email": "عنوان البريد اﻹلكتروني غير صالح {{email}}",
"error-invalid-email-address": "عنوان البريد اﻹلكتروني غير صالح",
"error-invalid-file-height": "ارتفاع الملف غير صالح",
"error-invalid-file-type": "نوع الملف غير صالح",
@ -100,7 +100,6 @@
"announcement": "إعلان",
"Announcement": "إعلان",
"Apply_Your_Certificate": "طبق شهادتك",
"Applying_a_theme_will_change_how_the_app_looks": "سيؤدي تطبيق السمة إلى تغيير شكل التطبيق",
"ARCHIVE": "أرشفة",
"archive": "أرشفة",
"are_typing": "يكتب",
@ -184,8 +183,6 @@
"deleting_room": "حذف الغرفة",
"description": "وصف",
"Description": "وصف",
"DESKTOP_OPTIONS": "خيارات سطح المكتب",
"DESKTOP_NOTIFICATIONS": "إشعارات سطح المكتب",
"Desktop_Alert_info": "هذه الإشعارات ترسل لسطح المكتب",
"Directory": "مجلد",
"Direct_Messages": "رسالة مباشرة",
@ -213,7 +210,6 @@
"Email_Notification_Mode_Disabled": "معطل",
"Email_or_password_field_is_empty": "حقل البريد الإلكتروني أو كلمة المرور فارغ",
"Email": "البريد الإلكتروني",
"EMAIL": "البريد الإلكتروني",
"email": "البريد الإلكتروني",
"Empty_title": "عنوان فارغ",
"Enable_Auto_Translate": "تمكين الترجمة التلقائية",
@ -270,7 +266,6 @@
"I_Saved_My_E2E_Password": "قمت بحفظ كلمة المرور الطرفية",
"IP": " عنوان بروتوكول الإنترنت (الآيبي)",
"In_app": "في التطبيق",
"IN_APP_AND_DESKTOP": "داخل التطبيق وسطح المكتب",
"In_App_and_Desktop_Alert_info": "يعرض شعاراً أعلى الشاشة عندما يكون التطبيق مفتوحًا، ويعرض إشعاراً على سطح المكتب",
"Invisible": "غير مرئي",
"Invite": "دعوة",
@ -289,6 +284,7 @@
"last_message": "الرسالة الأخيرة",
"Leave_channel": "مغادرة القناة",
"leaving_room": "مغادرة الغرفة",
"Leave": "مغادرة الغرفة",
"leave": "مغادرة",
"Legal": "قانوني",
"Light": "ساطع",
@ -398,7 +394,6 @@
"Profile": "الملف الشخصي",
"Public_Channel": "قناة عامة",
"Public": "عام",
"PUSH_NOTIFICATIONS": "الإشعارات",
"Push_Notifications_Alert_Info": "يتم إرسال هذه الإشعارات إليك عندما لا يكون التطبيق مفتوحاً",
"Quote": "اقتباس",
"Reactions_are_disabled": "التفاعل معطل",
@ -446,9 +441,9 @@
"Room_Members": "أعضاء الغرفة",
"Room_name_changed": "تم تغيير اسم الغرفة إلى: {{name}} من قبل {{userBy}}",
"SAVE": "حفظ",
"Saved": "تم الحفظ",
"Save_Changes": "حفظ التغيرات",
"Save": "حفظ",
"Saved": "تم الحفظ",
"saving_preferences": "حفظ التفضيلات",
"saving_profile": "حفظ الملف الشخصي",
"saving_settings": "حفظ الإعدادات",
@ -657,5 +652,6 @@
"You_will_be_logged_out_from_other_locations": "سيتم تسجيل خروج من الأماكن الأخرى",
"Logged_out_of_other_clients_successfully": "تم تسجيل الخروج من الأماكن الأخرى بنجاح",
"Logout_failed": "فشل تسجيل الخروج!",
"Log_analytics_events": "تحليلات سجل الأحداث"
"Log_analytics_events": "تحليلات سجل الأحداث",
"invalid-room": "غرفة غير صالحة"
}

View File

@ -14,7 +14,7 @@
"error-delete-protected-role": "Eine geschützte Rolle kann nicht gelöscht werden",
"error-department-not-found": "Abteilung nicht gefunden",
"error-direct-message-file-upload-not-allowed": "Dateifreigabe in direkten Nachrichten nicht zulässig",
"error-duplicate-channel-name": "Ein Kanal mit dem Namen {{channel_name}} ist bereits vorhanden",
"error-duplicate-channel-name": "Ein Kanal mit dem Namen {{room_name}} ist bereits vorhanden",
"error-email-domain-blacklisted": "Die E-Mail-Domain wird auf die schwarze Liste gesetzt",
"error-email-send-failed": "Fehler beim Versuch, eine E-Mail zu senden: {{message}}",
"error-save-image": "Fehler beim Speichern des Bildes",
@ -33,7 +33,7 @@
"error-invalid-date": "Ungültiges Datum angegeben",
"error-invalid-description": "Ungültige Beschreibung",
"error-invalid-domain": "Ungültige Domain",
"error-invalid-email": "Ungültige E-Mail {{emai}}",
"error-invalid-email": "Ungültige E-Mail {{email}}",
"error-invalid-email-address": "Ungültige E-Mail-Adresse",
"error-invalid-file-height": "Ungültige Dateihöhe",
"error-invalid-file-type": "Ungültiger Dateityp",
@ -61,6 +61,7 @@
"error-message-editing-blocked": "Die Bearbeitung von Nachrichten ist gesperrt",
"error-message-size-exceeded": "Die Nachrichtengröße überschreitet Message_MaxAllowedSize",
"error-missing-unsubscribe-link": "Du musst den Link [abbestellen] angeben.",
"error-no-owner-channel": "Dieser Raum gehört dir nicht",
"error-no-tokens-for-this-user": "Für diesen Benutzer gibt es keine Token",
"error-not-allowed": "Nicht erlaubt",
"error-not-authorized": "Nicht berechtigt",
@ -69,7 +70,7 @@
"error-role-in-use": "Rolle kann nicht gelöscht werden, da sie gerade verwendet wird",
"error-role-name-required": "Der Rollenname ist erforderlich",
"error-the-field-is-required": "Das Feld {{field}} ist erforderlich.",
"error-too-many-requests": "Fehler, zu viele Anfragen. Du musst {{Sekunden}} Sekunden warten, bevor du es erneut versuchst.",
"error-too-many-requests": "Fehler, zu viele Anfragen. Du musst {{seconds}} Sekunden warten, bevor du es erneut versuchst.",
"error-user-is-not-activated": "Benutzer ist nicht aktiviert",
"error-user-has-no-roles": "Benutzer hat keine Rollen",
"error-user-limit-exceeded": "Die Anzahl der Benutzer, die du zu #channel_name einladen möchtest, überschreitet die vom Administrator festgelegte Grenze",
@ -78,6 +79,7 @@
"error-user-registration-disabled": "Die Benutzerregistrierung ist deaktiviert",
"error-user-registration-secret": "Die Benutzerregistrierung ist nur über eine geheime URL möglich",
"error-you-are-last-owner": "Du bist der letzte Besitzer. Bitte setze einen neuen Besitzer, bevor du den Raum verlässt.",
"error-status-not-allowed": "Unsichtbar-Status ist deaktiviert",
"Actions": "Aktionen",
"activity": "Aktivität",
"Activity": "Aktivität",
@ -90,6 +92,7 @@
"alert": "Benachrichtigung",
"alerts": "Benachrichtigungen",
"All_users_in_the_channel_can_write_new_messages": "Alle Benutzer im Kanal können neue Nachrichten schreiben",
"All_users_in_the_team_can_write_new_messages": "Alle Mitglieder eines Teams können neue Nachrichten schreiben",
"A_meaningful_name_for_the_discussion_room": "Ein aussagekräftiger Name für den Diskussionsraum",
"All": "alle",
"All_Messages": "Alle Nachrichten",
@ -117,7 +120,7 @@
"Block_user": "Benutzer blockieren",
"Browser": "Browser",
"Broadcast_channel_Description": "Nur autorisierte Benutzer können neue Nachrichten schreiben, die anderen Benutzer können jedoch antworten",
"Broadcast_Channel": "Broadcastkanal",
"Broadcast_Channel": "Broadcast-Kanal",
"Busy": "Beschäftigt",
"By_proceeding_you_are_agreeing": "Indem du fortfährst, stimmst du zu unserem",
"Cancel_editing": "Bearbeitung abbrechen",
@ -134,7 +137,7 @@
"Clear_cookies_desc": "Diese Aktion wird alle Login-Cookies löschen und erlaubt es dir, dich mit einem anderen Konto anzumelden.",
"Clear_cookies_yes": "Ja, Cookies löschen",
"Clear_cookies_no": "Nein, Cookies behalten",
"Click_to_join": "Klicken um teilzunehmen!",
"Click_to_join": "Klicken um beizutreten!",
"Close": "Schließen",
"Close_emoji_selector": "Schließe die Emoji-Auswahl",
"Closing_chat": "Chat schließen",
@ -167,10 +170,10 @@
"Create_Channel": "Kanal erstellen",
"Create_Direct_Messages": "Direkt-Nachricht erstellen",
"Create_Discussion": "Diskussion erstellen",
"Created_snippet": "Erstellt ein Snippet",
"Created_snippet": "ein Snippet erstellt",
"Create_a_new_workspace": "Erstelle einen neuen Arbeitsbereich",
"Create": "Erstellen",
"Custom_Status": "eigener Status",
"Custom_Status": "Eigener Status",
"Dark": "Dunkel",
"Dark_level": "Dunkelstufe",
"Default": "Standard",
@ -180,13 +183,15 @@
"delete": "löschen",
"Delete": "Löschen",
"DELETE": "LÖSCHEN",
"move": "verschieben",
"deleting_room": "lösche Raum",
"description": "Beschreibung",
"Description": "Beschreibung",
"Desktop_Options": "Desktop-Einstellungen",
"Desktop_Notifications": "Desktop-Benachrichtigungen",
"Desktop_Alert_info": "Diese Benachrichtigungen werden auf dem Desktop angezeigt",
"Directory": "Verzeichnis",
"Direct_Messages": "Direkte Nachrichten",
"Direct_Messages": "Direktnachrichten",
"Disable_notifications": "Benachrichtigungen deaktiveren",
"Discussions": "Diskussionen",
"Discussion_Desc": "Hilft dir die Übersicht zu behalten! Durch das Erstellen einer Diskussion wird ein Unter-Kanal im ausgewählten Raum erzeugt und beide verknüpft.",
@ -207,7 +212,7 @@
"Edit_Status": "Status ändern",
"Edit_Invite": "Einladung bearbeiten",
"End_to_end_encrypted_room": "Ende-zu-Ende-verschlüsselter Raum",
"end_to_end_encryption": "Nicht mehr Ende-zu-Ende-verschlüsseln",
"end_to_end_encryption": "Nicht mehr Ende-zu-Ende verschlüsseln",
"Email_Notification_Mode_All": "Jede Erwähnung/Direktnachricht",
"Email_Notification_Mode_Disabled": "Deaktiviert",
"Email_or_password_field_is_empty": "Das E-Mail- oder Passwortfeld ist leer",
@ -224,6 +229,7 @@
"Encryption_error_title": "Dein Verschlüsselungs-Passwort scheint falsch zu sein",
"Encryption_error_desc": "Es war nicht möglich deinen Verschlüsselungs-Key zu importieren.",
"Everyone_can_access_this_channel": "Jeder kann auf diesen Kanal zugreifen",
"Everyone_can_access_this_team": "Jeder kann auf dieses Team zugreifen",
"Error_uploading": "Fehler beim Hochladen",
"Expiration_Days": "läuft ab (Tage)",
"Favorite": "Favorisieren",
@ -268,7 +274,7 @@
"I_Saved_My_E2E_Password": "Ich habe mein Ende-zu-Ende-Passwort gesichert",
"IP": "IP",
"In_app": "In-App-Browser",
"In_App_And_Desktop": "In-app und Desktop",
"In_App_And_Desktop": "In-App und Desktop",
"In_App_and_Desktop_Alert_info": "Zeigt ein Banner oben am Bildschirm, wenn die App geöffnet ist und eine Benachrichtigung auf dem Desktop.",
"Invisible": "Unsichtbar",
"Invite": "Einladen",
@ -276,7 +282,7 @@
"is_not_a_valid_RocketChat_instance": "ist keine gültige Rocket.Chat-Instanz",
"is_typing": "schreibt",
"Invalid_or_expired_invite_token": "Ungültiger oder abgelaufener Einladungscode",
"Invalid_server_version": "Der Server, zu dem du dich verbinden möchtest, verwendet eine Version, die von der App nicht mehr unterstützt wird: {{currentVersion}}.\n\nWir benötigen Version {{MinVersion}}.",
"Invalid_server_version": "Der Server, zu dem du dich verbinden möchtest, verwendet eine Version, die von der App nicht mehr unterstützt wird: {{currentVersion}}.\n\nWir benötigen Version {{minVersion}}.",
"Invite_Link": "Einladungs-Link",
"Invite_users": "Benutzer einladen",
"Join": "Beitreten",
@ -285,16 +291,18 @@
"Join_our_open_workspace": "Tritt unserem offenen Arbeitsbereich bei",
"Join_your_workspace": "Tritt deinem Arbeitsbereich bei",
"Just_invited_people_can_access_this_channel": "Nur eingeladene Personen können auf diesen Kanal zugreifen",
"Just_invited_people_can_access_this_team": "Nur eingeladene Personen können auf das Team zugreifen",
"Language": "Sprache",
"last_message": "letzte Nachricht",
"Leave_channel": "Kanal verlassen",
"leaving_room": "Raum verlassen",
"Leave": "Raum verlassen",
"leave": "verlassen",
"Legal": "Rechtliches",
"Light": "Hell",
"License": "Lizenz",
"Livechat": "Live-Chat",
"Livechat_edit": "Livechat bearbeiten",
"Livechat_edit": "Live-Chat bearbeiten",
"Login": "Anmeldung",
"Login_error": "Deine Zugangsdaten wurden abgelehnt! Bitte versuche es erneut.",
"Login_with": "Einloggen mit",
@ -325,6 +333,7 @@
"My_servers": "Meine Server",
"N_people_reacted": "{{n}} Leute haben reagiert",
"N_users": "{{n}} Benutzer",
"N_channels": "{{n}} Kanäle",
"name": "Name",
"Name": "Name",
"Navigation_history": "Navigations-Verlauf",
@ -400,7 +409,6 @@
"Public": "Öffentlich",
"Push_Notifications": "Push-Benachrichtigungen",
"Push_Notifications_Alert_Info": "Diese Benachrichtigungen werden dir zugestellt, wenn die App nicht geöffnet ist.",
"Desktop_Alert_info": "Diese Benachrichtigungen werden auf dem Desktop angezeigt",
"Quote": "Zitat",
"Reactions_are_disabled": "Reaktionen sind deaktiviert",
"Reactions_are_enabled": "Reaktionen sind aktiviert",
@ -435,6 +443,7 @@
"Review_app_unable_store": "Kann {{store}} nicht öffnen",
"Review_this_app": "App bewerten",
"Remove": "Entfernen",
"remove": "entfernen",
"Roles": "Rollen",
"Room_actions": "Raumaktionen",
"Room_changed_announcement": "Raumansage geändert in: {{announcement}} von {{userBy}}",
@ -517,7 +526,7 @@
"Take_a_video": "Video aufnehmen",
"Take_it": "Annehmen!",
"tap_to_change_status": "Tippen um den Status zu ändern",
"Tap_to_view_servers_list": "Hier tippen, um die Serverliste anzuzeigen",
"Tap_to_view_servers_list": "Tippen, um die Serverliste anzuzeigen",
"Terms_of_Service": " Nutzungsbedingungen",
"Theme": "Erscheinungsbild",
"The_user_wont_be_able_to_type_in_roomName": "Dem Nutzer wird es nicht möglich sein in {{roomName}} zu schreiben",
@ -592,7 +601,7 @@
"You_can_search_using_RegExp_eg": "Du kannst mit RegExp suchen. z.B. `/ ^ text $ / i`",
"You_colon": "Du: ",
"you_were_mentioned": "Du wurdest erwähnt",
"You_were_removed_from_channel": "Du wurdest aus dem Kanal {{channel}} entfernt",
"You_were_removed_from_channel": "Du wurdest aus {{channel}} entfernt",
"you": "du",
"You": "Du",
"Logged_out_by_server": "Du bist vom Server abgemeldet worden. Bitte melde dich wieder an.",
@ -610,7 +619,7 @@
"You_will_unset_a_certificate_for_this_server": "Du entfernst ein Zertifikat für diesen Server",
"Change_Language": "Sprache ändern",
"Crash_report_disclaimer": "Wir verfolgen niemals den Inhalt deiner Chats. Der Crash-Report enthält nur für uns relevante Informationen um das Problem zu erkennen und zu beheben.",
"Type_message": "Type message",
"Type_message": "Nachricht schreiben",
"Room_search": "Raum-Suche",
"Room_selection": "Raum-Auswahl 1...9",
"Next_room": "Nächster Raum",
@ -623,7 +632,7 @@
"Reply_in_Thread": "Im Thread antworten",
"Server_selection": "Server-Auswahl",
"Server_selection_numbers": "Server-Auswahl 1...9",
"Add_server": "Server hinufügen",
"Add_server": "Server hinzufügen",
"New_line": "Zeilenumbruch",
"You_will_be_logged_out_of_this_application": "Du wirst in dieser Anwendung vom Server abgemeldet.",
"Clear": "Löschen",
@ -681,12 +690,9 @@
"No_threads_following": "Du folgst keinen Threads",
"No_threads_unread": "Es gibt keine ungelesenen Threads",
"Messagebox_Send_to_channel": "an Kanal senden",
"Set_as_leader": "Zum Diskussionsleiter ernennen",
"Set_as_moderator": "Zum Moderator ernennen",
"Set_as_owner": "Zum Besitzer machen",
"Remove_as_leader": "Als Diskussionsleiter entfernen",
"Remove_as_moderator": "Moderatorenrechte entfernen",
"Remove_as_owner": "Als Eigentümer entfernen",
"Leader": "Leiter",
"Moderator": "Moderator",
"Owner": "Eigentümer",
"Remove_from_room": "Aus dem Raum entfernen",
"Ignore": "Ignorieren",
"Unignore": "Nicht mehr ignorieren",
@ -704,5 +710,56 @@
"Direct_message": "Direktnachricht",
"Message_Ignored": "Nachricht ignoriert. Antippen um sie zu zeigen.",
"Enter_workspace_URL": "Arbeitsbereich-URL",
"Workspace_URL_Example": "z.B. https://rocketchat.deine-firma.de"
"Workspace_URL_Example": "z.B. https://rocketchat.deine-firma.de",
"This_room_encryption_has_been_enabled_by__username_": "Die Verschlüsselung dieses Raums wurde von {{username}} aktiviert",
"This_room_encryption_has_been_disabled_by__username_": "Die Verschlüsselung dieses Raums wurde von {{username}} deaktiviert",
"Teams": "Teams",
"No_team_channels_found": "Keine Kanäle gefunden",
"Team_not_found": "Team nicht gefunden",
"Create_Team": "Team erstellen",
"Team_Name": "Team-Name",
"Private_Team": "Privates Team",
"Read_Only_Team": "Nur-Lesen-Team",
"Broadcast_Team": "Broadcast-Team",
"creating_team": "Team erstellen",
"team-name-already-exists": "Ein Team mit diesem Namen existiert bereits",
"Add_Channel_to_Team": "Kanal zum Team hinzufügen",
"Create_New": "Neu erstellen",
"Add_Existing": "Vorhandenes hinzufügen",
"Add_Existing_Channel": "Vorhandenen Kanal hinzufügen",
"Remove_from_Team": "Aus Team entfernen",
"Auto-join": "Automatischer Beitritt",
"Remove_Team_Room_Warning": "Möchten du diesen Kanal aus dem Team entfernen? Der Kanal wird zurück in den Arbeitsbereich verschoben.",
"Confirmation": "Bestätigung",
"invalid-room": "Ungültiger Raum",
"You_are_leaving_the_team": "Du verlässt das Team '{{team}}'",
"Leave_Team": "Team verlassen",
"Select_Team": "Team auswählen",
"Select_Team_Channels": "Wähle die Kanäle des Teams aus, die du verlassen möchtest.",
"Cannot_leave": "Verlassen nicht möglich",
"Cannot_remove": "Kann nicht entfernt werden",
"Cannot_delete": "Kann nicht gelöscht werden",
"Last_owner_team_room": "Du bist der letzte Eigentümer des Kanals. Wenn du das Team verlässt, bleibt der Kanal innerhalb des Teams aber du verwaltest ihn von außen.",
"last-owner-can-not-be-removed": "Letzter Besitzer kann nicht entfernt werden",
"Remove_User_Teams": "Wähle die Kanäle aus, aus denen der Benutzer entfernt werden soll.",
"Delete_Team": "Team löschen",
"Select_channels_to_delete": "Dies kann nicht rückgängig gemacht werden. Wenn du ein Team löschst, werden alle Chat-Inhalte und und Einstellungen gelöscht.\n\nWähle die Kanäle, die du löschen möchtest. Diejenigen, die du behalten möchtest, werden in deinem Arbeitsbereich verfügbar sein. Beachte, das öffentliche Kanäle öffentlich bleiben und für jeden sichtbar sein werden.",
"You_are_deleting_the_team": "Du löschst dieses Team",
"Removing_user_from_this_team": "Du entfernst {{user}} aus diesem Team",
"Remove_User_Team_Channels": "Wähle die Kanäle aus, aus denen der Benutzer entfernt werden soll.",
"Remove_Member": "Mitglied entfernen",
"leaving_team": "Team verlassen",
"removing_team": "Aus dem Team entfernen",
"moving_channel_to_team": "Kanal zu Team verschieben",
"deleting_team": "Team löschen",
"member-does-not-exist": "Mitglied existiert nicht",
"Convert": "Konvertieren",
"Convert_to_Team": "Zu Team konvertieren",
"Convert_to_Team_Warning": "Dies kann nicht rückgängig gemacht werden. Sobald du einen Kanal in ein Team umgewandelt hast, kannst du ihn nicht mehr zurück in einen Kanal verwandeln.",
"Move_to_Team": "Zu Team hinzufügen",
"Move_Channel_Paragraph": "Das Verschieben eines Kanals innerhalb eines Teams bedeutet, dass dieser Kanal im Kontext des Teams hinzugefügt wird, jedoch haben alle Mitglieder des Kanals, die nicht Mitglied des jeweiligen Teams sind, weiterhin Zugriff auf diesen Kanal, werden aber nicht als Teammitglieder hinzugefügt \n\nDie gesamte Verwaltung des Kanals wird weiterhin von den Eigentümern dieses Kanals vorgenommen.\n\nTeammitglieder und sogar Teameigentümer, die nicht Mitglied dieses Kanals sind, können keinen Zugriff auf den Inhalt des Kanals haben \n\nBitte beachte, dass der Besitzer des Teams in der Lage ist, Mitglieder aus dem Kanal zu entfernen.",
"Move_to_Team_Warning": "Nachdem du die vorherigen Anleitungen zu diesem Verhalten gelesen hast, möchtest du diesen Kanal immer noch in das ausgewählte Team verschieben?",
"Load_More": "Mehr laden",
"Load_Newer": "Neuere laden",
"Load_Older": "Ältere laden"
}

View File

@ -14,7 +14,7 @@
"error-delete-protected-role": "Cannot delete a protected role",
"error-department-not-found": "Department not found",
"error-direct-message-file-upload-not-allowed": "File sharing not allowed in direct messages",
"error-duplicate-channel-name": "A channel with name {{channel_name}} exists",
"error-duplicate-channel-name": "A channel with name {{room_name}} exists",
"error-email-domain-blacklisted": "The email domain is blacklisted",
"error-email-send-failed": "Error trying to send email: {{message}}",
"error-save-image": "Error while saving image",
@ -33,7 +33,7 @@
"error-invalid-date": "Invalid date provided.",
"error-invalid-description": "Invalid description",
"error-invalid-domain": "Invalid domain",
"error-invalid-email": "Invalid email {{emai}}",
"error-invalid-email": "Invalid email {{email}}",
"error-invalid-email-address": "Invalid email address",
"error-invalid-file-height": "Invalid file height",
"error-invalid-file-type": "Invalid file type",
@ -61,6 +61,7 @@
"error-message-editing-blocked": "Message editing is blocked",
"error-message-size-exceeded": "Message size exceeds Message_MaxAllowedSize",
"error-missing-unsubscribe-link": "You must provide the [unsubscribe] link.",
"error-no-owner-channel": "You don't own the channel",
"error-no-tokens-for-this-user": "There are no tokens for this user",
"error-not-allowed": "Not allowed",
"error-not-authorized": "Not authorized",
@ -78,6 +79,7 @@
"error-user-registration-disabled": "User registration is disabled",
"error-user-registration-secret": "User registration is only allowed via Secret URL",
"error-you-are-last-owner": "You are the last owner. Please set new owner before leaving the room.",
"error-status-not-allowed": "Invisible status is disabled",
"Actions": "Actions",
"activity": "activity",
"Activity": "Activity",
@ -90,6 +92,7 @@
"alert": "alert",
"alerts": "alerts",
"All_users_in_the_channel_can_write_new_messages": "All users in the channel can write new messages",
"All_users_in_the_team_can_write_new_messages": "All users in the team can write new messages",
"A_meaningful_name_for_the_discussion_room": "A meaningful name for the discussion room",
"All": "All",
"All_Messages": "All Messages",
@ -180,6 +183,7 @@
"delete": "delete",
"Delete": "Delete",
"DELETE": "DELETE",
"move": "move",
"deleting_room": "deleting room",
"description": "description",
"Description": "Description",
@ -225,6 +229,7 @@
"Encryption_error_title": "Your encryption password seems wrong",
"Encryption_error_desc": "It wasn't possible to decode your encryption key to be imported.",
"Everyone_can_access_this_channel": "Everyone can access this channel",
"Everyone_can_access_this_team": "Everyone can access this team",
"Error_uploading": "Error uploading",
"Expiration_Days": "Expiration (Days)",
"Favorite": "Favorite",
@ -286,10 +291,12 @@
"Join_our_open_workspace": "Join our open workspace",
"Join_your_workspace": "Join your workspace",
"Just_invited_people_can_access_this_channel": "Just invited people can access this channel",
"Just_invited_people_can_access_this_team": "Just invited people can access this team",
"Language": "Language",
"last_message": "last message",
"Leave_channel": "Leave channel",
"leaving_room": "leaving room",
"Leave": "Leave",
"leave": "leave",
"Legal": "Legal",
"Light": "Light",
@ -326,6 +333,7 @@
"My_servers": "My servers",
"N_people_reacted": "{{n}} people reacted",
"N_users": "{{n}} users",
"N_channels": "{{n}} channels",
"name": "name",
"Name": "Name",
"Navigation_history": "Navigation history",
@ -435,6 +443,7 @@
"Review_app_unable_store": "Unable to open {{store}}",
"Review_this_app": "Review this app",
"Remove": "Remove",
"remove": "remove",
"Roles": "Roles",
"Room_actions": "Room actions",
"Room_changed_announcement": "Room announcement changed to: {{announcement}} by {{userBy}}",
@ -681,12 +690,9 @@
"No_threads_following": "You are not following any threads",
"No_threads_unread": "There are no unread threads",
"Messagebox_Send_to_channel": "Send to channel",
"Set_as_leader": "Set as leader",
"Set_as_moderator": "Set as moderator",
"Set_as_owner": "Set as owner",
"Remove_as_leader": "Remove as leader",
"Remove_as_moderator": "Remove as moderator",
"Remove_as_owner": "Remove as owner",
"Leader": "Leader",
"Moderator": "Moderator",
"Owner": "Owner",
"Remove_from_room": "Remove from room",
"Ignore": "Ignore",
"Unignore": "Unignore",
@ -709,5 +715,52 @@
"This_room_encryption_has_been_disabled_by__username_": "This room's encryption has been disabled by {{username}}",
"Teams": "Teams",
"No_team_channels_found": "No channels found",
"Team_not_found": "Team not found"
"Team_not_found": "Team not found",
"Create_Team": "Create Team",
"Team_Name": "Team Name",
"Private_Team": "Private Team",
"Read_Only_Team": "Read Only Team",
"Broadcast_Team": "Broadcast Team",
"creating_team": "creating team",
"team-name-already-exists": "A team with that name already exists",
"Add_Channel_to_Team": "Add Channel to Team",
"Left_The_Team_Successfully": "Left the team successfully",
"Create_New": "Create New",
"Add_Existing": "Add Existing",
"Add_Existing_Channel": "Add Existing Channel",
"Remove_from_Team": "Remove from Team",
"Auto-join": "Auto-join",
"Remove_Team_Room_Warning": "Woud you like to remove this channel from the team? The channel will be moved back to the workspace",
"Confirmation": "Confirmation",
"invalid-room": "Invalid room",
"You_are_leaving_the_team": "You are leaving the team '{{team}}'",
"Leave_Team": "Leave Team",
"Select_Team": "Select Team",
"Select_Team_Channels": "Select the Team's channels you would like to leave.",
"Cannot_leave": "Cannot leave",
"Cannot_remove": "Cannot remove",
"Cannot_delete": "Cannot delete",
"Last_owner_team_room": "You are the last owner of this channel. Once you leave the team, the channel will be kept inside the team but you will be managing it from outside.",
"last-owner-can-not-be-removed": "Last owner cannot be removed",
"Remove_User_Teams": "Select channels you want the user to be removed from.",
"Delete_Team": "Delete Team",
"Select_channels_to_delete": "This can't be undone. Once you delete a team, all chat content and configuration will be deleted. \n\nSelect the channels you would like to delete. The ones you decide to keep will be available on your workspace. Notice that public channels will still be public and visible to everyone.",
"You_are_deleting_the_team": "You are deleting this team.",
"Removing_user_from_this_team": "You are removing {{user}} from this team",
"Remove_User_Team_Channels": "Select the channels you want the user to be removed from.",
"Remove_Member": "Remove Member",
"leaving_team": "leaving team",
"removing_team": "removing from team",
"moving_channel_to_team": "moving channel to team",
"deleting_team": "deleting team",
"member-does-not-exist": "Member does not exist",
"Convert": "Convert",
"Convert_to_Team": "Convert to Team",
"Convert_to_Team_Warning": "This can't be undone. Once you convert a channel to a team, you can not turn it back to a channel.",
"Move_to_Team": "Move to Team",
"Move_Channel_Paragraph": "Moving a channel inside a team means that this channel will be added in the teams context, however, all channels members, which are not members of the respective team, will still have access to this channel, but will not be added as teams members. \n\nAll channels management will still be made by the owners of this channel.\n\nTeams members and even teams owners, if not a member of this channel, can not have access to the channels content. \n\nPlease notice that the Teams owner will be able remove members from the Channel.",
"Move_to_Team_Warning": "After reading the previous intructions about this behavior, do you still want to move this channel to the selected team?",
"Load_More": "Load More",
"Load_Newer": "Load Newer",
"Load_Older": "Load Older"
}

View File

@ -13,7 +13,7 @@
"error-delete-protected-role": "No se puede eliminar un rol protegido",
"error-department-not-found": "Departamento no encontrado",
"error-direct-message-file-upload-not-allowed": "No se permite compartir archivos en mensajes directos",
"error-duplicate-channel-name": "Ya existe un canal con nombre {{channel_name}}",
"error-duplicate-channel-name": "Ya existe un canal con nombre {{room_name}}",
"error-email-domain-blacklisted": "El dominio del correo electrónico está en la lista negra",
"error-email-send-failed": "Error al enviar el correo electrónico: {{message}}",
"error-field-unavailable": "{{field}} ya está en uso :(",
@ -25,12 +25,12 @@
"error-invalid-asset": "El archivo archivo no es correcto",
"error-invalid-channel": "El canal no es correcto.",
"error-invalid-channel-start-with-chars": "Canal incorrecto. Debe comenzar con @ o #",
"error-invalid-custom-field": "Invalid custom field",
"error-invalid-custom-field-name": "Nombre inválido para el campo personalizado. Utilice sólo letras, números, guiones o guión bajo",
"error-invalid-custom-field": "Campo personalizado no válido",
"error-invalid-custom-field-name": "Nombre no válido para el campo personalizado. Utilice sólo letras, números, guiones o guión bajo",
"error-invalid-date": "La fecha proporcionada no es correcta.",
"error-invalid-description": "La descipción no es correcta",
"error-invalid-description": "La descripción no es correcta",
"error-invalid-domain": "El dominio no es correcto",
"error-invalid-email": "El email {{emai}} no es correcto",
"error-invalid-email": "El email {{email}} no es correcto",
"error-invalid-email-address": "La dirección de correo no es correcta",
"error-invalid-file-height": "La altura de la imagen no es correcta",
"error-invalid-file-type": "El formato del archivo no es correcto",
@ -44,10 +44,10 @@
"error-invalid-redirectUri": "La URL de redirección no es correcta.",
"error-invalid-role": "El rol no es correcto",
"error-invalid-room": "La sala no es correcta",
"error-invalid-room-name": "No se puede asignar el nombre {{name}} a una sala.",
"error-invalid-room-type": "No se puede asginar el tipo {{type}} a una sala.",
"error-invalid-room-name": "No se puede asignar el nombre {{room_name}} a una sala.",
"error-invalid-room-type": "No se puede asignar el tipo {{type}} a una sala.",
"error-invalid-settings": "La configuración proporcionada no es correcta",
"error-invalid-subscription": "La subscripción no es correcta",
"error-invalid-subscription": "La suscripción no es correcta",
"error-invalid-token": "El token no es correcto",
"error-invalid-triggerWords": "El triggerWords no es correcto",
"error-invalid-urls": "Las URLs no son correctas",
@ -62,25 +62,24 @@
"error-not-allowed": "No permitido",
"error-not-authorized": "No autorizado",
"error-push-disabled": "El Push está desactivado",
"error-remove-last-owner": "El usuario el único propietario existente. Debes establecer un nuevo propietario antes de eliminarlo.",
"error-remove-last-owner": "El usuario es el único propietario existente. Debes establecer un nuevo propietario antes de eliminarlo.",
"error-role-in-use": "No puedes eliminar el rol dado que está en uso",
"error-role-name-required": "Debes indicar el nombre del rol",
"error-the-field-is-required": "El campo {{field}} es obligatorio.",
"error-too-many-requests": "Hemos recibido demasiadas peticiones. Debes esperar {{seconds}} segundos antes de continuar. Por favor, sé paciente.",
"error-too-many-requests": "Error, demasiadas peticiones. Debes esperar {{seconds}} segundos antes de continuar. Por favor, sé paciente.",
"error-user-is-not-activated": "El usuario no está activo",
"error-user-has-no-roles": "El usuario no tiene roles",
"error-user-limit-exceeded": "El número de usuarios que quieres invitiar al canal #channel_name supera el límite establecido por el adminitrador.",
"error-user-limit-exceeded": "El número de usuarios que quieres invitar al canal #channel_name supera el límite establecido por el administrador.",
"error-user-not-in-room": "El usuario no está en la sala",
"error-user-registration-custom-field": "error-user-registration-custom-field",
"error-user-registration-disabled": "El registro de usuario está deshabilitador",
"error-user-registration-disabled": "El registro de usuario está deshabilitado",
"error-user-registration-secret": "El registro de usuarios sólo está permitido por URL secretas",
"error-you-are-last-owner": "El usuario el único propietario existente. Debes establecer un nuevo propietario antes de abandonar la sala.",
"error-you-are-last-owner": "Eres el único propietario existente. Debes establecer un nuevo propietario antes de abandonar la sala.",
"Actions": "Acciones",
"activity": "actividad",
"Activity": "Actividad",
"Add_Reaction": "Reaccionar",
"Add_Reaction": "Añadir reacción",
"Add_Server": "Añadir servidor",
"Add_user": "Añadir usuario",
"Admin_Panel": "Panel de Control",
"Alert": "Alerta",
"alert": "alerta",
@ -90,27 +89,27 @@
"All_Messages": "Todos los mensajes",
"Allow_Reactions": "Permitir reacciones",
"Alphabetical": "Alfabético",
"and_more": "más",
"and_more": "y más",
"and": "y",
"announcement": "anuncio",
"Announcement": "Anuncio",
"Apply_Your_Certificate": "Applica tu Certificación",
"Apply_Your_Certificate": "Aplica tu certificado",
"ARCHIVE": "FICHERO",
"archive": "Fichero",
"are_typing": "escribiendo",
"archive": "fichero",
"are_typing": "están escribiendo",
"Are_you_sure_question_mark": "¿Estás seguro?",
"Are_you_sure_you_want_to_leave_the_room": "¿Deseas salir de la sala {{room}}?",
"Audio": "Audio",
"Authenticating": "Autenticando",
"Automatic": "Automático",
"Auto_Translate": "Auto-Translate",
"Avatar_changed_successfully": "Has cambiado tu Avatar!",
"Auto_Translate": "Traducción automática",
"Avatar_changed_successfully": "¡Avatar modificado correctamente!",
"Avatar_Url": "URL del Avatar",
"Away": "Ausente",
"Back": "Volver",
"Black": "Black",
"Black": "Negro",
"Block_user": "Bloquear usuario",
"Broadcast_channel_Description": "Sólo los usuario permitidos pueden escribir nuevos mensajes, el resto podrán responder sobre los mismos.",
"Broadcast_channel_Description": "Sólo los usuarios autorizados pueden escribir nuevos mensajes, el resto podrán responder sobre los mismos.",
"Broadcast_Channel": "Canal de Transmisión",
"Busy": "Ocupado",
"By_proceeding_you_are_agreeing": "Al proceder estarás de acuerdo",
@ -122,35 +121,35 @@
"Channel_Name": "Nombre sala",
"Channels": "Salas",
"Chats": "Chats",
"Call_already_ended": "La llamada ya ha finalizado!",
"Click_to_join": "Unirme!",
"Call_already_ended": "¡!La llamada ya ha finalizado!",
"Click_to_join": "¡Unirme!",
"Close": "Cerrar",
"Close_emoji_selector": "Cerrar selector de emojis",
"Choose": "Seleccionar",
"Choose_from_library": "Seleccionar desde Galería",
"Choose_file": "Seleccionar Archivo",
"Choose_from_library": "Seleccionar desde galería",
"Choose_file": "Seleccionar archivo",
"Code": "Código",
"Collaborative": "Colaborativo",
"Confirm": "Confirmar",
"Connect": "Conectar",
"Connected": "Conectado",
"connecting_server": "conectando a servidor",
"connecting_server": "conectando al servidor",
"Connecting": "Conectando...",
"Contact_us": "Contactar",
"Contact_us": "Contacta con nosotros",
"Contact_your_server_admin": "Contacta con el administrador.",
"Continue_with": "Continuar con",
"Copied_to_clipboard": "Copiado al portapapeles!",
"Copied_to_clipboard": "¡Copiado al portapapeles!",
"Copy": "Copiar",
"Permalink": "Enlace permanente",
"Certificate_password": "Contraseña del certificado",
"Whats_the_password_for_your_certificate": "¿Cuál es la contraseña de tu cerficiado?",
"Whats_the_password_for_your_certificate": "¿Cuál es la contraseña de tu certificado?",
"Create_account": "Crear una cuenta",
"Create_Channel": "Crear Sala",
"Created_snippet": "crear snippet",
"Create_a_new_workspace": "Crear un Workspace",
"Create_Channel": "Crear sala",
"Created_snippet": "crear mensaje en bloque",
"Create_a_new_workspace": "Crear un nuevo espacio de trabajo",
"Create": "Crear",
"Dark": "Óscuro",
"Dark_level": "Nivel",
"Dark": "Oscuro",
"Dark_level": "Nivel de oscuridad",
"Default": "Por defecto",
"Delete_Room_Warning": "Eliminar a un usuario causará la eliminación de todos los mensajes creados por dicho usuario. Esta operación no se puede deshacer.",
"delete": "eliminar",
@ -159,9 +158,9 @@
"deleting_room": "eliminando sala",
"description": "descripción",
"Description": "Descripción",
"Desktop_Options": "Opciones De Escritorio",
"Desktop_Options": "Opciones de escritorio",
"Directory": "Directorio",
"Direct_Messages": "Mensajes directo",
"Direct_Messages": "Mensajes directos",
"Disable_notifications": "Desactivar notificaciones",
"Discussions": "Conversaciones",
"Dont_Have_An_Account": "¿Todavía no tienes una cuenta?",
@ -170,7 +169,7 @@
"edit": "editar",
"edited": "editado",
"Edit": "Editar",
"Email_or_password_field_is_empty": "El email o la contraseña están vacios",
"Email_or_password_field_is_empty": "El email o la contraseña están vacíos",
"Email": "E-mail",
"email": "e-mail",
"Enable_Auto_Translate": "Permitir Auto-Translate",
@ -185,9 +184,9 @@
"Finish_recording": "Finalizar grabación",
"Following_thread": "Siguiendo hilo",
"For_your_security_you_must_enter_your_current_password_to_continue": "Por seguridad, debes introducir tu contraseña para continuar",
"Forgot_password_If_this_email_is_registered": "Si este email está registrado, te enviaremos las instrucciones para resetear tu contraseña.Si no recibes un email en un rato, vuelve aquí e inténtalo de nuevo.",
"Forgot_password": "Restablecer mi contraseña",
"Forgot_Password": "Restabler mi Contraseña",
"Forgot_password_If_this_email_is_registered": "Si este email está registrado, te enviaremos las instrucciones para resetear tu contraseña. Si no recibes un email en breve, vuelve aquí e inténtalo de nuevo.",
"Forgot_password": "¿Ha olvidado su contraseña?",
"Forgot_Password": "Olvidé la contraseña",
"Full_table": "Click para ver la tabla completa",
"Group_by_favorites": "Agrupar por favoritos",
"Group_by_type": "Agrupar por tipo",
@ -195,29 +194,29 @@
"Has_joined_the_channel": "se ha unido al canal",
"Has_joined_the_conversation": "se ha unido a la conversación",
"Has_left_the_channel": "ha dejado el canal",
"In_App_And_Desktop": "In-app and Desktop",
"In_App_and_Desktop_Alert_info": "Muestra un banner en la parte superior de la pantalla cuando la aplicación está abierta y muestra una notificación en el escritorio",
"In_App_And_Desktop": "En la aplicación y en el escritorio",
"In_App_and_Desktop_Alert_info": "Muestra un banner en la parte superior de la pantalla cuando la aplicación esté abierta y muestra una notificación en el escritorio",
"Invisible": "Invisible",
"Invite": "Invitar",
"is_a_valid_RocketChat_instance": "es una instancia válida Rocket.Chat",
"is_not_a_valid_RocketChat_instance": "no es una instancia válida Rocket.Chat",
"is_a_valid_RocketChat_instance": "es una instancia válida de Rocket.Chat",
"is_not_a_valid_RocketChat_instance": "no es una instancia válida de Rocket.Chat",
"is_typing": "escribiendo",
"Invalid_server_version": "El servidor que intentas conectar está usando una versión que ya no es soportada por la aplicación : {{currentVersion}}. Requerimos una versión {{minVersion}}.",
"Invalid_server_version": "El servidor que intentas conectar está usando una versión que ya no es soportada por la aplicación : {{currentVersion}}. Se requiere una versión {{minVersion}}.",
"Join": "Conectar",
"Just_invited_people_can_access_this_channel": "Sólo gente invitada puede acceder a este canal.",
"Language": "Idioma",
"last_message": "último mensaje",
"Leave_channel": "Abandonar canal",
"Leave_channel": "Abandonar el canal",
"leaving_room": "abandonando sala",
"leave": "abandonar",
"Legal": "Legal",
"Light": "Claro",
"License": "Licencia",
"Livechat": "Livechat",
"Login": "Acceder",
"Livechat": "LiveChat",
"Login": "Inicio de sesión",
"Login_error": "¡Sus credenciales fueron rechazadas! Por favor, inténtelo de nuevo.",
"Login_with": "Acceder con",
"Logout": "Salir",
"Login_with": "Iniciar sesión con",
"Logout": "Cerrar sesión",
"members": "miembros",
"Members": "Miembros",
"Mentioned_Messages": "Mensajes mencionados",
@ -249,19 +248,19 @@
"No_pinned_messages": "No hay mensajes fijados",
"No_results_found": "No hay resultados",
"No_starred_messages": "No hay mensajes destacados",
"No_thread_messages": "No hay hilots",
"No_thread_messages": "No hay hilos",
"No_Message": "Sin mensajes",
"No_messages_yet": "No hay todavía mensajes",
"No_messages_yet": "No hay mensajes todavía",
"No_Reactions": "No hay reacciones",
"No_Read_Receipts": "No hay confirmaciones de lectura",
"Not_logged": "No logueado",
"Not_logged": "No ha iniciado sesión",
"Not_RC_Server": "Esto no es un servidor de Rocket.Chat.\n{{contact}}",
"Nothing": "Nada",
"Nothing_to_save": "No hay nada para guardar!",
"Notify_active_in_this_room": "Notificar usuarios activos en esta sala",
"Nothing_to_save": "¡No hay nada por guardar!",
"Notify_active_in_this_room": "Notificar a los usuarios activos en esta sala",
"Notify_all_in_this_room": "Notificar a todos en esta sala",
"Notifications": "Notificaciones",
"Notification_Duration": "Duración notificación",
"Notification_Duration": "Duración de la notificación",
"Notification_Preferences": "Configuración de notificaciones",
"Offline": "Sin conexión",
"Oops": "Oops!",
@ -271,28 +270,28 @@
"Open_emoji_selector": "Abrir selector de emojis",
"Open_Source_Communication": "Comunicación Open Source",
"Password": "Contraseña",
"Permalink_copied_to_clipboard": "Enlace permanente copiado al portapapeles!",
"Permalink_copied_to_clipboard": "¡Enlace permanente copiado al portapapeles!",
"Pin": "Fijar",
"Pinned_Messages": "Mensajes fijados",
"pinned": "fijado",
"Pinned": "Fijado",
"Please_enter_your_password": "Por favor introduce tu contraseña",
"Preferences": "Configuración",
"Preferences_saved": "Configuración guardada!",
"Privacy_Policy": "Política de Privacidad",
"Please_enter_your_password": "Por favor introduce la contraseña",
"Preferences": "Preferencias",
"Preferences_saved": "¡Preferencias guardadas!",
"Privacy_Policy": "Política de privacidad",
"Private_Channel": "Canal privado",
"Private_Groups": "Grupos privados",
"Private": "Privado",
"Processing": "Procesando...",
"Profile_saved_successfully": "Perfil guardado correctamente!",
"Profile_saved_successfully": "¡Perfil guardado correctamente!",
"Profile": "Perfil",
"Public_Channel": "Canal público",
"Public": "Público",
"Push_Notifications": "Push Notifications",
"Push_Notifications": "Notificaciones Push",
"Push_Notifications_Alert_Info": "Estas notificaciones se le entregan cuando la aplicación no está abierta",
"Quote": "Citar",
"Reactions_are_disabled": "Las reacciones están desactivadas",
"Reactions_are_enabled": "Las reacciones están habilitadas",
"Reactions_are_enabled": "Las reacciones están activadas",
"Reactions": "Reacciones",
"Read": "Leer",
"Read_Only_Channel": "Canal de sólo lectura",
@ -324,12 +323,12 @@
"Room_Info": "Información de la sala",
"Room_Members": "Miembros de la sala",
"Room_name_changed": "El nombre de la sala cambió a: {{name}} por {{userBy}}",
"SAVE": "SAVE",
"SAVE": "GUARDAR",
"Save_Changes": "Guardar cambios",
"Save": "Guardar",
"saving_preferences": "guardando preferencias",
"saving_profile": "guardando perfil",
"saving_settings": "guardando confiración",
"saving_settings": "guardando configuración",
"Search_Messages": "Buscar mensajes",
"Search": "Buscar",
"Search_by": "Buscar por",
@ -350,14 +349,14 @@
"Server_version": "Versión servidor: {{version}}",
"Set_username_subtitle": "El nombre de usuario se utiliza para permitir que otros le mencionen en los mensajes",
"Settings": "Configuración",
"Settings_succesfully_changed": "Configuración cambiada correctamente!",
"Settings_succesfully_changed": "¡Configuración cambiada correctamente!",
"Share": "Compartir",
"Share_this_app": "Compartir esta App",
"Show_Unread_Counter": "Mostrar contador No leídos",
"Share_this_app": "Compartir esta aplicación",
"Show_Unread_Counter": "Mostrar contador de no leídos",
"Show_Unread_Counter_Info": "El contador de no leídos se muestra como una insignia a la derecha del canal, en la lista",
"Sign_in_your_server": "Accede a tu servidor",
"Sign_Up": "Acceder",
"Some_field_is_invalid_or_empty": "Algún campo es incorrecto o vacío",
"Sign_Up": "Registrarse",
"Some_field_is_invalid_or_empty": "Algún campo no es correcto o está vacío",
"Sorting_by": "Ordenado por {{key}}",
"Sound": "Sonido",
"Star_room": "Destacar sala",
@ -365,18 +364,18 @@
"Starred_Messages": "Mensajes destacados",
"starred": "destacado",
"Starred": "Destacado",
"Start_of_conversation": "Comiezo de la conversación",
"Start_of_conversation": "Comienzo de la conversación",
"Started_discussion": "Comenzar una conversación:",
"Started_call": "Llamada iniciada por {{userBy}}",
"Submit": "Enviar",
"Table": "Tabla",
"Take_a_photo": "Enviar Foto",
"Take_a_video": "Enviar Vídeo",
"Take_a_photo": "Enviar una foto",
"Take_a_video": "Enviar un vídeo",
"tap_to_change_status": "pulsa para cambiar el estado",
"Tap_to_view_servers_list": "Pulsa para ver la lista de servidores",
"Terms_of_Service": "Términos de servicio",
"Theme": "Tema",
"There_was_an_error_while_action": "Ha habido un error mientras {{action}}!",
"There_was_an_error_while_action": "¡Ha habido un error mientras {{action}}!",
"This_room_is_blocked": "La sala está bloqueada",
"This_room_is_read_only": "Esta sala es de sólo lectura",
"Thread": "Hilo",
@ -389,21 +388,21 @@
"Try_again": "Intentar de nuevo",
"Two_Factor_Authentication": "Autenticación de doble factor",
"Type_the_channel_name_here": "Escribe el nombre del canal aquí",
"unarchive": "reactivar",
"UNARCHIVE": "UNARCHIVE",
"unarchive": "desarchivar",
"UNARCHIVE": "DESARCHIVAR",
"Unblock_user": "Desbloquear usuario",
"Unfavorite": "Quitar Favorito",
"Unfollowed_thread": "Dejar de seguir el Hilo",
"Unfavorite": "Quitar favorito",
"Unfollowed_thread": "Dejar de seguir el hilo",
"Unmute": "Desmutear",
"unmuted": "Desmuteado",
"Unpin": "Quitar estado Fijado",
"unread_messages": "marcar como No leído",
"Unread": "Marcar como No leído",
"Unread_on_top": "Mensajes No leídos en la parte superior",
"Unstar": "Quitar Destacado",
"Unpin": "Quitar estado fijado",
"unread_messages": "marcar como no leído",
"Unread": "Marcar como no leído",
"Unread_on_top": "Mensajes no leídos en la parte superior",
"Unstar": "Quitar destacado",
"Updating": "Actualizando...",
"Uploading": "Subiendo",
"Upload_file_question_mark": "Subir fichero?",
"Upload_file_question_mark": "¿Subir fichero?",
"Users": "Usuarios",
"User_added_by": "Usuario {{userAdded}} añadido por {{userBy}}",
"User_has_been_key": "El usuario ha sido {{key}}",
@ -424,9 +423,9 @@
"Welcome": "Bienvenido",
"Whats_your_2fa": "¿Cuál es tu código 2FA?",
"Without_Servers": "Sin servidores",
"Yes_action_it": "Sí, {{action}}!",
"Yes_action_it": "Sí, ¡{{action}}!",
"Yesterday": "Ayer",
"You_are_in_preview_mode": "Estás en modo Vista Previa",
"You_are_in_preview_mode": "Estás en modo vista previa",
"You_are_offline": "Estás desconectado",
"You_can_search_using_RegExp_eg": "Puedes usar expresiones regulares. Por ejemplo, `/^text$/i`",
"You_colon": "Tú: ",
@ -436,7 +435,7 @@
"You_need_to_access_at_least_one_RocketChat_server_to_share_something": "Necesita acceder al menos a un servidor Rocket.Chat para compartir algo.",
"Your_certificate": "Tu certificado",
"Version_no": "Versión: {{version}}",
"You_will_not_be_able_to_recover_this_message": "No podrás recuperar este mensaje!",
"You_will_not_be_able_to_recover_this_message": "¡No podrás recuperar este mensaje!",
"Change_Language": "Cambiar idioma",
"Crash_report_disclaimer": "Nunca rastreamos el contenido de sus conversaciones. El informe del error sólo contiene información relevante para nosotros con el fin de identificar los problemas y solucionarlos.",
"Type_message": "Escribir mensaje",

View File

@ -3,45 +3,45 @@
"1_user": "1 utilisateur",
"error-action-not-allowed": "{{action}} n'est pas autorisé",
"error-application-not-found": "Application non trouvée",
"error-archived-duplicate-name": "Il y a un canal archivé avec nom {{room_name}}",
"error-avatar-invalid-url": "URL d'avatar invalide: {{url}}",
"error-archived-duplicate-name": "Il y a un canal archivé avec le nom {{room_name}}",
"error-avatar-invalid-url": "URL d'avatar invalide : {{url}}",
"error-avatar-url-handling": "Erreur lors de la gestion du paramètre d'avatar à partir d'une URL ({{url}}) pour {{username}}",
"error-cant-invite-for-direct-room": "Impossible d'inviter l'utilisateur aux salles direct",
"error-cant-invite-for-direct-room": "Impossible d'inviter l'utilisateur aux salons directs",
"error-could-not-change-email": "Impossible de changer l'adresse e-mail",
"error-could-not-change-name": "Impossible de changer le nom",
"error-could-not-change-username": "Impossible de changer le nom d'utilisateur",
"error-could-not-change-status": "Impossible de changer le statut",
"error-delete-protected-role": "Impossible de supprimer un rôle protégé",
"error-department-not-found": "Département introuvable",
"error-direct-message-file-upload-not-allowed": "Le partage de fichiers n'est pas autorisé dans les messages directs",
"error-duplicate-channel-name": "un canal avec nom {{channel_name}} existe",
"error-direct-message-file-upload-not-allowed": "Partage de fichiers non autorisé dans les messages privés",
"error-duplicate-channel-name": "Un canal avec nom {{room_name}} existe",
"error-email-domain-blacklisted": "Le domaine de messagerie est sur liste noire",
"error-email-send-failed": "Erreur lors de la tentative d'envoi d'un courrier électronique: {{message}}",
"error-save-image": "Erreur en sauvegardant l'image",
"error-save-video": "Erreur en sauvegardant la video",
"error-email-send-failed": "Erreur lors de la tentative d'envoi de l'e-mail : {{message}}",
"error-save-image": "Erreur lors de l'enregistrement de l'image",
"error-save-video": "Erreur en sauvegardant la vidéo",
"error-field-unavailable": "{{field}} est déjà utilisé: (",
"error-file-too-large": "Le fichier est trop volumineux",
"error-importer-not-defined": "L'importateur n'a pas été défini correctement, il manque la classe import.",
"error-input-is-not-a-valid-field": "{{input}} N'est pas valide {{field}}",
"error-invalid-actionlink": "Lien d'action invalide",
"error-invalid-arguments": "Invalid arguments",
"error-invalid-asset": "élément incorrect",
"error-file-too-large": "Le fichier est trop grand",
"error-importer-not-defined": "L'importateur n'a pas été défini correctement, il manque la classe Import.",
"error-input-is-not-a-valid-field": "{{input}} n'est pas un {{field}} valide",
"error-invalid-actionlink": "Lien d'action non valide",
"error-invalid-arguments": "Arguments non valides",
"error-invalid-asset": "Elément non valide",
"error-invalid-channel": "Canal invalide.",
"error-invalid-channel-start-with-chars": "Canal invalide. Commence par @ ou #",
"error-invalid-custom-field": "Champ personnalisé incorrect",
"error-invalid-custom-field-name": "Nom de champ personnalisé non valide. Utilisez uniquement des lettres, des chiffres, des traits d'union et de soulignement.",
"error-invalid-date": "Date fournie invalide.",
"error-invalid-channel-start-with-chars": "Canal non valide. Commencez par @ ou #",
"error-invalid-custom-field": "Champ personnalisé non valide",
"error-invalid-custom-field-name": "Nom de champ personnalisé non valide. Utilisez uniquement des lettres, des chiffres, des traits d'union et des traits de soulignement.",
"error-invalid-date": "Date fournie non valide.",
"error-invalid-description": "Description invalide",
"error-invalid-domain": "Domaine invalide",
"error-invalid-email": "Adresse e-mail non valide {{emai}}",
"error-invalid-email": "E-mail {{email}} invalide",
"error-invalid-email-address": "Adresse e-mail invalide",
"error-invalid-file-height": "Hauteur de fichier non valide",
"error-invalid-file-type": "Type de fichier invalide",
"error-invalid-file-width": "Largeur de fichier invalide",
"error-invalid-from-address": "Vous avez informé une adresse FROM invalide.",
"error-invalid-file-width": "Largeur de fichier non valide",
"error-invalid-from-address": "Vous avez renseigné une adresse FROM invalide.",
"error-invalid-integration": "Intégration invalide",
"error-invalid-message": "Message invalide",
"error-invalid-method": "Méthode invalide",
"error-invalid-method": "Méthode non valide",
"error-invalid-name": "Nom incorrect",
"error-invalid-password": "Mot de passe incorrect",
"error-invalid-redirectUri": "RedirectUri invalide",
@ -50,47 +50,50 @@
"error-invalid-room-name": "{{room_name}} n'est pas un nom de salon valide",
"error-invalid-room-type": "{{type}} n'est pas un type de salon valide.",
"error-invalid-settings": "Paramètres fournis non valides",
"error-invalid-subscription": "Subscription invalide",
"error-invalid-subscription": "Abonnement invalide",
"error-invalid-token": "Jeton invalide",
"error-invalid-triggerWords": "Mots déclencheurs invalides",
"error-invalid-urls": "URL non valides",
"error-invalid-user": "Utilisateur invalide",
"error-invalid-username": "Nom d'utilisateur invalide",
"error-invalid-webhook-response": "L'URL webhook a répondu avec un statut autre que 200",
"error-invalid-webhook-response": "L'URL du webhook a répondu avec un statut autre que 200",
"error-message-deleting-blocked": "La suppression du message est bloquée",
"error-message-editing-blocked": "La modification du message est bloquée",
"error-message-size-exceeded": "La taille du message dépasse Message_MaxAllowedSize",
"error-missing-unsubscribe-link": "Vous devez fournir le [unsubscribe] lien.",
"error-missing-unsubscribe-link": "Vous devez fournir le lien [unsubscribe].",
"error-no-owner-channel": "Vous n'êtes pas propriétaire du canal",
"error-no-tokens-for-this-user": "Il n'y a pas de jetons pour cet utilisateur",
"error-not-allowed": "Non autorisé",
"error-not-authorized": "Non autorisé",
"error-not-allowed": "Interdit",
"error-not-authorized": "Pas autorisé",
"error-push-disabled": "Push est désactivé",
"error-remove-last-owner": "Ceci est le dernier propriétaire. Veuillez définir un nouveau propriétaire avant de supprimer celui-ci.",
"error-role-in-use": "Impossible de supprimer le rôle car est utilisé",
"error-remove-last-owner": "C'est le dernier propriétaire. Veuillez définir un nouveau propriétaire avant de supprimer celui-ci.",
"error-role-in-use": "Impossible de supprimer le rôle car il est en cours d'utilisation",
"error-role-name-required": "Le nom du rôle est requis",
"error-the-field-is-required": "Le champ {{field}} est requis.",
"error-too-many-requests": "Erreur, trop de demandes. Ralentissez, s'il vous plaît. Vous devez attendre {{seconds}} secondes avant d'essayer à nouveau.",
"error-too-many-requests": "Erreur, trop de demandes. Ralentissez, s'il vous plaît. Vous devez attendre {{seconds}} secondes avant de réessayer.",
"error-user-is-not-activated": "L'utilisateur n'est pas activé",
"error-user-has-no-roles": "L'utilisateur ne dispose pas d'un rôle",
"error-user-has-no-roles": "L'utilisateur n'a aucun rôle",
"error-user-limit-exceeded": "Le nombre d'utilisateurs que vous essayez d'inviter à #channel_name dépasse la limite définie par l'administrateur",
"error-user-not-in-room": "L'utilisateur n'est pas dans cette salle",
"error-user-not-in-room": "L'utilisateur n'est pas dans ce salon",
"error-user-registration-custom-field": "error-user-registration-custom-field",
"error-user-registration-disabled": "L'enregistrement de l'utilisateur est désactivé",
"error-user-registration-secret": "Enregistrement de l'utilisateur est autorisée uniquement via l'URL secret",
"error-you-are-last-owner": "Vous êtes le dernier propriétaire. Veuillez définir un nouveau propriétaire avant de quitter la salle.",
"error-user-registration-secret": "L'enregistrement de l'utilisateur n'est autorisé que via l'URL secrète",
"error-you-are-last-owner": "Vous êtes le dernier propriétaire. Veuillez définir un nouveau propriétaire avant de quitter le salon.",
"error-status-not-allowed": "Le statut invisible est désactivé",
"Actions": "Actions",
"activity": "activité",
"Activity": "Activité",
"Add_Reaction": "Ajouter une réaction",
"Add_Server": "Ajouter un serveur",
"Add_users": "Ajouter des utilisateurs",
"Admin_Panel": "Panneau d'Administration",
"Admin_Panel": "Panneau d'administration",
"Agent": "Agent",
"Alert": "Alerte",
"alert": "alerte",
"alerts": "alertes",
"All_users_in_the_channel_can_write_new_messages": "Tous les utilisateurs du canal peuvent écrire de nouveaux messages",
"A_meaningful_name_for_the_discussion_room": "Un nom explicite pour la salle de discussion",
"All_users_in_the_team_can_write_new_messages": "Tous les utilisateurs de l'équipe peuvent écrire de nouveaux messages",
"A_meaningful_name_for_the_discussion_room": "Un nom significatif pour le salon de discussion",
"All": "Tout",
"All_Messages": "Tous les messages",
"Allow_Reactions": "Autoriser les réactions",
@ -99,113 +102,137 @@
"and": "et",
"announcement": "annonce",
"Announcement": "Annonce",
"Apply_Your_Certificate": "Valider le Certificat",
"Apply_Your_Certificate": "Appliquer votre certificat",
"ARCHIVE": "ARCHIVER",
"archive": "archiver",
"are_typing": "sont en train d'écrire",
"Are_you_sure_question_mark": "Êtes-vous sûr ?",
"Are_you_sure_you_want_to_leave_the_room": "Êtes-vous sûr de vouloir quitter le salon {{room}}?",
"Are_you_sure_you_want_to_leave_the_room": "Êtes-vous sûr de vouloir quitter le salon {{room}} ?",
"Audio": "Audio",
"Authenticating": "Authentifier",
"Authenticating": "Authentification",
"Automatic": "Automatique",
"Auto_Translate": "Traduction-Auto",
"Avatar_changed_successfully": "Avatar changé avec succès!",
"Auto_Translate": "Traduction automatique",
"Avatar_changed_successfully": "Avatar changé avec succès !",
"Avatar_Url": "URL de l'avatar",
"Away": "absent",
"Back": "Arrière",
"Away": "Absent",
"Back": "Retour",
"Black": "Noir",
"Block_user": "Bloquer l'Utilisateur",
"Block_user": "Bloquer l'utilisateur",
"Browser": "Navigateur",
"Broadcast_channel_Description": "Seuls les utilisateurs autorisés peuvent écrire de nouveaux messages, mais les autres utilisateurs pourront répondre.",
"Broadcast_Channel": "Canal de diffusion",
"Busy": "Occupé",
"By_proceeding_you_are_agreeing": "En procédant, vous acceptez nos",
"By_proceeding_you_are_agreeing": "En poursuivant, vous acceptez nos",
"Cancel_editing": "Annuler la modification",
"Cancel_recording": "Annuler l'enregistrement",
"Cancel": "Annuler",
"changing_avatar": "changer d'avatar",
"creating_channel": "créer un canal",
"creating_channel": "création d'un canal",
"creating_invite": "création d'une invitation",
"Channel_Name": "Nom du canal",
"Channels": "Canaux",
"Chats": "Chats",
"Call_already_ended": "L'appel a déjà terminé !",
"Click_to_join": "Cliquez pour rejoindre!",
"Call_already_ended": "Appel déjà terminé !",
"Clear_cookies_alert": "Voulez-vous effacer tous les cookies ?",
"Clear_cookies_desc": "Cette action effacera tous les cookies de connexion ce qui vous permettra de vous connecter à d'autres comptes.",
"Clear_cookies_yes": "Oui, effacez les cookies",
"Clear_cookies_no": "Non, gardez les cookies",
"Click_to_join": "Cliquez pour rejoindre !",
"Close": "Fermer",
"Close_emoji_selector": "Fermer le sélecteur d'emoji",
"Closing_chat": "Fermeture du Salon de discussion",
"Close_emoji_selector": "Fermer le sélecteur d'émoji",
"Closing_chat": "Fermeture du chat",
"Change_language_loading": "Changement de la langue.",
"Chat_closed_by_agent": "Le salon de discussion a été fermé",
"Chat_closed_by_agent": "Chat fermé par l'agent",
"Choose": "Choisir",
"Choose_from_library": "Choisissez parmi la bibliothèque",
"Choose_file": "Choisir un fichier",
"Choose_where_you_want_links_be_opened": "Choisissez ou vous souhaitez ouvrir vos liens",
"Choose_from_library": "Choisissez dans la bibliothèque",
"Choose_file": "Choisir le fichier",
"Choose_where_you_want_links_be_opened": "Choisissez oµ vous souhaitez ouvrir les liens",
"Code": "Code",
"Code_or_password_invalid": "Code ou mot de passe invalide",
"Collaborative": "Collaborative",
"Collaborative": "Collaboratif",
"Confirm": "Confirmer",
"Connect": "Se connecter",
"Connect": "Connecter",
"Connected": "Connecté",
"connecting_server": "connexion en cours au serveur",
"Connecting": "Connexion ...",
"Contact_us": "Contactez nous",
"Contact_your_server_admin": "Contactez l'administrateur de votre serveur.",
"Connecting": "Connexion...",
"Contact_us": "Contactez-nous",
"Contact_your_server_admin": "Contactez votre administrateur de serveur.",
"Continue_with": "Continuer avec",
"Copied_to_clipboard": "Copié dans le presse-papier!",
"Copied_to_clipboard": "Copié dans le presse-papier !",
"Copy": "Copier",
"Conversation": "Conversation",
"Permalink": "Lien permanent",
"Certificate_password": "Mot de passe du Certificat",
"Clear_cache": "Effacer le cache local",
"Certificate_password": "Mot de passe du certificat",
"Clear_cache": "Effacer le cache du serveur local",
"Clear_cache_loading": "Effacement du cache.",
"Whats_the_password_for_your_certificate": "Quel est le mot de passe du Certificat ?",
"Whats_the_password_for_your_certificate": "Quel est le mot de passe de votre certificat ?",
"Create_account": "Créer un compte",
"Create_Channel": "Créer un canal",
"Create_Direct_Messages": "Créer un message direct",
"Create_Discussion": "Créer une Discussion",
"Create_Direct_Messages": "Créer des messages directs",
"Create_Discussion": "Créer une discussion",
"Created_snippet": "créé un extrait",
"Create_a_new_workspace": "Créer un nouvel espace de travail",
"Create": "Créer",
"Custom_Status": "Statut Personnalisé",
"Custom_Status": "Statut personnalisé",
"Dark": "Sombre",
"Dark_level": "Niveau d'assombrissement",
"Dark_level": "Niveau d'obscurité",
"Default": "Défaut",
"Default_browser": "Navigateur par défaut",
"Delete_Room_Warning": "Supprimer une salle supprimera tous les messages postés dans la salle. Ça ne peut pas être annulé.",
"Delete_Room_Warning": "Supprimer une salon supprimera tous les messages publiés dans le salon. Ça ne peut pas être annulé.",
"Department": "Département",
"delete": "supprimer",
"Delete": "Supprimer",
"DELETE": "SUPPRIMER",
"deleting_room": "effacement de la salle",
"move": "déplacer",
"deleting_room": "suppression du salon",
"description": "la description",
"Description": "La description",
"Desktop_Options": "Desktop Options",
"Desktop_Options": "Options de bureau",
"Desktop_Notifications": "Notifications de bureau",
"Desktop_Alert_info": "Ces notifications sont transmises sur le bureau",
"Directory": "Répertoire",
"Direct_Messages": "Messages directs",
"Disable_notifications": "Désactiver les notifications",
"Discussions": "Discussions",
"Discussion_Desc": "Aide à garder un aperçu de ce qui se passe! En créant une discussion, un sous-canal de celui que vous avez sélectionné est créé et les deux sont liés.",
"Discussion_Desc": "Aide à garder une vue d'ensemble sur ce qui se passe ! En créant une discussion, un sous-canal de celui que vous avez sélectionné est créé et les deux sont liés.",
"Discussion_name": "Nom de la discussion",
"Done": "Fait",
"Dont_Have_An_Account": "Vous n'avez pas de compte?",
"Do_you_have_an_account": "Avez-vous un compte?",
"Do_you_have_a_certificate": "Avez-vous un certificat?",
"Do_you_really_want_to_key_this_room_question_mark": "Voulez-vous vraiment {{key}} cette salle?",
"Dont_Have_An_Account": "Vous n'avez pas de compte ?",
"Do_you_have_an_account": "Avez-vous un compte ?",
"Do_you_have_a_certificate": "Avez-vous un certificat ?",
"Do_you_really_want_to_key_this_room_question_mark": "Voulez-vous vraiment {{key}} ce salon ?",
"E2E_Encryption": "Cryptage E2E",
"E2E_How_It_Works_info1": "Vous pouvez désormais créer des groupes privés et des messages directs chiffrés. Vous pouvez également modifier les groupes privés ou DM existants pour les crypter.",
"E2E_How_It_Works_info2": "Il s'agit du *chiffrement de bout en bout*, la clé permettant de coder/décoder vos messages ne sera pas enregistrée sur le serveur. C'est pourquoi *vous devez stocker ce mot de passe à un endroit sûr* auquel vous pourrez accéder plus tard si vous en avez besoin.",
"E2E_How_It_Works_info3": "Si vous continuez, un mot de passe E2E sera automatiquement généré.",
"E2E_How_It_Works_info4": "Vous pouvez également configurer un nouveau mot de passe pour votre clé de cryptage à tout moment à partir de n'importe quel navigateur dans lequel vous avez entré le mot de passe E2E existant.",
"edit": "modifier",
"edited": "édité",
"edited": "modifié",
"Edit": "Modifier",
"Edit_Status": "Modifier le Statut",
"Edit_Status": "Modifier le statut",
"Edit_Invite": "Modifier l'invitation",
"End_to_end_encrypted_room": "Salon crypté de bout en bout",
"end_to_end_encryption": "chiffrement de bout en bout",
"Email_Notification_Mode_All": "Chaque mention/MD",
"Email_Notification_Mode_Disabled": "Désactivé",
"Email_or_password_field_is_empty": "Le champ e-mail ou mot de passe est vide",
"Email": "E-mail",
"email": "e-mail",
"Empty_title": "Titre vide",
"Enable_Auto_Translate": "Activer la traduction-auto",
"Enable_Auto_Translate": "Activer la traduction automatique",
"Enable_notifications": "Activer les notifications",
"Encrypted": "Crypté",
"Encrypted_message": "Message crypté",
"Enter_Your_E2E_Password": "Entrez votre mot de passe E2E",
"Enter_Your_Encryption_Password_desc1": "Cela vous permettra d'accéder à vos groupes privés cryptés et à vos messages directs.",
"Enter_Your_Encryption_Password_desc2": "Vous devez entrer le mot de passe pour coder/décoder les messages à chaque endroit où vous utilisez le chat.",
"Encryption_error_title": "Votre mot de passe de cryptage semble erroné",
"Encryption_error_desc": "Il n'a pas été possible de décoder votre clé de cryptage pour être importé.",
"Everyone_can_access_this_channel": "Tout le monde peut accéder à ce canal",
"Error_uploading": "Erreur lors du téléchargement",
"Everyone_can_access_this_team": "Tout le monde peut accéder à cette équipe",
"Error_uploading": "Erreur lors de l'envoi",
"Expiration_Days": "Expiration (Jours)",
"Favorite": "Favoris",
"Favorite": "Favori",
"Favorites": "Favoris",
"Files": "Fichiers",
"File_description": "Description du fichier",
@ -214,38 +241,40 @@
"Following_thread": "Suivre le fil",
"For_your_security_you_must_enter_your_current_password_to_continue": "Pour votre sécurité, vous devez entrer votre mot de passe actuel pour continuer.",
"Forgot_password_If_this_email_is_registered": "Si cet e-mail est enregistré, nous vous enverrons des instructions pour réinitialiser votre mot de passe. Si vous ne recevez pas d'e-mail sous peu, veuillez revenir et réessayer.",
"Forgot_password": "Mot de passe oublié",
"Forgot_password": "Mot de passe oublié ?",
"Forgot_Password": "Mot de passe oublié",
"Forward": "Faire suivre",
"Forward_Chat": "Faire suivre le canal de discussion",
"Forward_to_department": "Faire suivre au département",
"Forward_to_user": "Faire suivre a l'utilisateur",
"Full_table": "Cliquez pour voir la table complète",
"Forward": "Transmettre",
"Forward_Chat": "Transmettre la conversation",
"Forward_to_department": "Transmettre au département",
"Forward_to_user": "Transmettre à l'utilisateur",
"Full_table": "Cliquez pour voir le tableau complet",
"Generate_New_Link": "Générer un nouveau lien",
"Group_by_favorites": "Grouper par favoris",
"Group_by_type": "Grouper par type",
"Hide": "Cacher",
"Has_joined_the_channel": "a rejoint le canal",
"Has_joined_the_conversation": "a rejoint la conversation",
"Has_left_the_channel": "a quitté la chaîne",
"Has_left_the_channel": "a quitté le canal",
"Hide_System_Messages": "Masquer les messages système",
"Hide_type_messages": "Masquer les messages \"{{type}}\"",
"How_It_Works": "Comment cela fonctionne",
"Message_HideType_uj": "L'utilisateur a rejoint",
"Message_HideType_ul": "L'utilisateur est parti",
"Message_HideType_ru": "Utilisateur éjecté",
"Message_HideType_ru": "Utilisateur supprimé",
"Message_HideType_au": "Utilisateur ajouté",
"Message_HideType_mute_unmute": "Utilisateur rendu muet / a retrouvé la parole",
"Message_HideType_r": "Le nom du salon a été changé",
"Message_HideType_ut": "L'Utilisateur a rejoint la conversation",
"Message_HideType_r": "Nom du salon modifié",
"Message_HideType_ut": "L'utilisateur a rejoint la conversation",
"Message_HideType_wm": "Bienvenue",
"Message_HideType_rm": "Message supprimé",
"Message_HideType_subscription_role_added": "a été défini avec ce Rôle",
"Message_HideType_subscription_role_removed": "Ce Rôle n'est plus défini",
"Message_HideType_room_archived": "Salon Archivé",
"Message_HideType_room_unarchived": "Salon Désarchivé",
"Message_HideType_subscription_role_added": "Rôle assigné",
"Message_HideType_subscription_role_removed": "Le rôle n'est plus défini",
"Message_HideType_room_archived": "Salon archivé",
"Message_HideType_room_unarchived": "Salon désarchivé",
"I_Saved_My_E2E_Password": "J'ai enregistré mon mot de passe E2E",
"IP": "IP",
"In_app": "In-app",
"In_App_And_Desktop": "In-app et Bureau",
"In_app": "Dans l'app",
"In_App_And_Desktop": "Dans l'application et sur le bureau",
"In_App_and_Desktop_Alert_info": "Affiche une bannière en haut de l'écran lorsque l'application est ouverte et affiche une notification sur le bureau",
"Invisible": "Invisible",
"Invite": "Inviter",
@ -253,25 +282,29 @@
"is_not_a_valid_RocketChat_instance": "n'est pas une instance valide de Rocket.Chat",
"is_typing": "est en train d'écrire",
"Invalid_or_expired_invite_token": "Jeton d'invitation non valide ou expiré",
"Invalid_server_version": "Le serveur que vous essayez de connecter utilise une version qui n'est plus prise en charge par l'application: {{currentVersion}}.\n\nNous exigeons la version {{minVersion}}",
"Invalid_server_version": "Le serveur auquel vous essayez de vous connecter utilise une version qui n'est plus prise en charge par l'application : {{currentVersion}}.\n\nNous exigeons la version {{minVersion}}",
"Invite_Link": "Lien d'invitation",
"Invite_users": "Inviter utilisateur",
"Invite_users": "Inviter des utilisateurs",
"Join": "Rejoindre",
"Join_Code": "Code d'adhésion",
"Insert_Join_Code": "Insérer le code d'adhésion",
"Join_our_open_workspace": "Rejoignez notre espace de travail ouvert",
"Join_your_workspace": "Rejoignez votre espace de travail",
"Just_invited_people_can_access_this_channel": "Seuls les invités peuvent accéder à ce canal",
"Just_invited_people_can_access_this_channel": "Seuls les personnes invitées peuvent accéder à ce canal",
"Just_invited_people_can_access_this_team": "Seules les personnes invitées peuvent accéder à cette équipe",
"Language": "Langue",
"last_message": "Dernier message",
"last_message": "dernier message",
"Leave_channel": "Quitter le canal",
"leaving_room": "En quittent le canal",
"leaving_room": "quittant le salon",
"Leave": "Quitter",
"leave": "quitter",
"Legal": "Légale",
"Light": "Lumière",
"Legal": "Légal",
"Light": "Clair",
"License": "Licence",
"Livechat": "Livechat",
"Livechat_edit": "Livechat modification",
"Livechat": "Chat en direct",
"Livechat_edit": "Modifier le chat en direct",
"Login": "Connexion",
"Login_error": "Vos identifiants ont été rejetés! Veuillez réessayer.",
"Login_error": "Vos identifiants ont été rejetés ! Veuillez réessayer.",
"Login_with": "Se connecter avec",
"Logging_out": "Déconnexion.",
"Logout": "Se déconnecter",
@ -282,7 +315,7 @@
"Mentioned_Messages": "Messages mentionnés",
"mentioned": "mentionné",
"Mentions": "Mentions",
"Message_accessibility": "message de {{user}} à {{time}}: {{message}}",
"Message_accessibility": "Message de {{user}} à {{time}} : {{message}}",
"Message_actions": "Actions de message",
"Message_pinned": "Message épinglé",
"Message_removed": "Message supprimé",
@ -293,13 +326,14 @@
"Message": "Message",
"Messages": "Messages",
"Message_Reported": "Message signalé",
"Microphone_Permission_Message": "Rocket.Chat doit avoir accès à votre microphone pour pouvoir envoyer un message audio.",
"Microphone_Permission_Message": "Rocket.Chat a besoin d'accéder à votre microphone pour que vous puissiez envoyer un message audio.",
"Microphone_Permission": "Permission de microphone",
"Mute": "Rendre muet",
"muted": "Rendu muet",
"muted": "muet",
"My_servers": "Mes serveurs",
"N_people_reacted": "{{n}} personnes ont réagi",
"N_users": "{{n}} utilisateurs",
"N_channels": "{{n}} canaux",
"name": "nom",
"Name": "Nom",
"Navigation_history": "Historique de navigation",
@ -313,26 +347,28 @@
"No_mentioned_messages": "Aucun message mentionné",
"No_pinned_messages": "Aucun message épinglé",
"No_results_found": "Aucun résultat trouvé",
"No_starred_messages": "Pas de messages suivis",
"No_thread_messages": "Aucun fil de discussion",
"No_starred_messages": "Aucun message suivi",
"No_thread_messages": "Aucun message de fil de discussion",
"No_label_provided": "Aucun {{label}} fourni.",
"No_Message": "Aucun message",
"No_messages_yet": "Pas encore de messages",
"No_Reactions": "Aucune réaction",
"No_Read_Receipts": "Pas d'accusé de lecture",
"No_Read_Receipts": "Aucun accusé de lecture",
"Not_logged": "Non connecté",
"Not_RC_Server": "Ce n'est pas un serveur Rocket.Chat.\n{{contact}}",
"Nothing": "Rien",
"Nothing_to_save": "Rien à enregistrer!",
"Notify_active_in_this_room": "Notifier les utilisateurs actifs dans cette salle",
"Notify_all_in_this_room": "Notifier tous dans cette salle",
"Nothing_to_save": "Rien à enregistrer !",
"Notify_active_in_this_room": "Notifier les utilisateurs actifs dans ce salon",
"Notify_all_in_this_room": "Avertir tout le monde dans ce salon",
"Notifications": "Notifications",
"Notification_Duration": "Durée de Notification",
"Notification_Preferences": "Préférences de Notification",
"No_available_agents_to_transfer": "Aucun agent disponible à qui transférer",
"Notification_Duration": "Durée des notifications",
"Notification_Preferences": "Préférences de notification",
"No_available_agents_to_transfer": "Aucun agent disponible pour le transfert",
"Offline": "Hors ligne",
"Oops": "Oops!",
"Omnichannel": "Omnichannel",
"Oops": "Oups !",
"Omnichannel": "Omnicanal",
"Open_Livechats": "Discussions en cours",
"Omnichannel_enable_alert": "Vous n'êtes pas disponible sur Omnicanal. Souhaitez-vous être disponible ?",
"Onboarding_description": "Un espace de travail est l'espace de collaboration de votre équipe ou organisation. Demandez à l'administrateur de l'espace de travail l'adresse pour rejoindre ou créez-en une pour votre équipe.",
"Onboarding_join_workspace": "Rejoindre un espace de travail",
"Onboarding_subtitle": "Au-delà de la collaboration d'équipe",
@ -343,15 +379,15 @@
"Onboarding_more_options": "Plus d'options",
"Online": "En ligne",
"Only_authorized_users_can_write_new_messages": "Seuls les utilisateurs autorisés peuvent écrire de nouveaux messages.",
"Open_emoji_selector": "Ouvrir sélecteur emoji",
"Open_emoji_selector": "Ouvrir le sélecteur d'émoji",
"Open_Source_Communication": "Communication Open Source",
"Open_your_authentication_app_and_enter_the_code": "Ouvrez votre application d'authentification et entrez le code.",
"OR": "OU",
"OS": "OS",
"Overwrites_the_server_configuration_and_use_room_config": "Écrase la configuration du serveur et utilise la configuration du salon",
"Password": "Mot de passe",
"Parent_channel_or_group": "Chaîne ou groupe parent",
"Permalink_copied_to_clipboard": "Lien permanent copié dans le presse-papier!",
"Parent_channel_or_group": "Canal ou groupe parent",
"Permalink_copied_to_clipboard": "Lien permanent copié dans le presse-papiers !",
"Phone": "Téléphone",
"Pin": "Épingler",
"Pinned_Messages": "Messages épinglés",
@ -359,20 +395,20 @@
"Pinned": "Épinglé",
"Please_add_a_comment": "Veuillez ajouter un commentaire",
"Please_enter_your_password": "Veuillez entrer votre mot de passe",
"Please_wait": "Attendez s'il vous plaît",
"Please_wait": "Veuillez patienter.",
"Preferences": "Préférences",
"Preferences_saved": "Préférences sauvegardées!",
"Preferences_saved": "Préférences sauvegardées !",
"Privacy_Policy": " Politique de confidentialité",
"Private_Channel": "Canal privé",
"Private_Groups": "Groupes privés",
"Private": "Privé",
"Processing": "En traitement...",
"Profile_saved_successfully": "Profil enregistré avec succès!",
"Processing": "Traitement...",
"Profile_saved_successfully": "Profil enregistré avec succès !",
"Profile": "Profil",
"Public_Channel": "Canal Public",
"Public_Channel": "Canal public",
"Public": "Public",
"Push_Notifications": "Notifications Push",
"Push_Notifications_Alert_Info": "Ces notifications vous sont livrées lorsque l'application n'est pas ouverte",
"Push_Notifications_Alert_Info": "Ces notifications vous sont envoyées lorsque l'application n'est pas ouverte",
"Quote": "Citation",
"Reactions_are_disabled": "Les réactions sont désactivées",
"Reactions_are_enabled": "Les réactions sont activées",
@ -380,61 +416,68 @@
"Read": "Lecture",
"Read_External_Permission_Message": "Rocket.Chat doit accéder aux photos, aux médias et aux fichiers sur votre appareil",
"Read_External_Permission": "Permission de lecture des fichiers",
"Read_Only_Channel": "Chaîne en lecture seule",
"Read_Only_Channel": "Canal en lecture seule",
"Read_Only": "Lecture seule",
"Read_Receipt": "Accusé de réception",
"Receive_Group_Mentions": "Recevoir des mentions de groupe",
"Receive_Group_Mentions_Info": "Recevoir les mentions @all et @here",
"Receive_Group_Mentions_Info": "Recevoir des mentions @all et @here",
"Register": "S'inscrire",
"Repeat_Password": "Répéter le mot de passe",
"Replied_on": "Répondu le:",
"Replied_on": "A répondu le :",
"replies": "réponses",
"reply": "répondre",
"Reply": "Répondre",
"Report": "Signaler",
"Receive_Notification": "Recevoir une notification",
"Receive_notifications_from": "Recevoir les notifications de {{name}}",
"Receive_notifications_from": "Recevoir des notifications de {{name}}",
"Resend": "Renvoyer",
"Reset_password": "Réinitialiser le mot de passe",
"resetting_password": "réinitialisation du mot de passe",
"RESET": "RÉINITIALISER",
"Return": "Retour",
"Review_app_title": "Appréciez-vous cette application?",
"Review_app_title": "Appréciez-vous cette application ?",
"Review_app_desc": "Donnez-nous 5 étoiles sur {{store}}",
"Review_app_yes": "Bien sur!",
"Review_app_yes": "Bien sûr !",
"Review_app_no": "Non",
"Review_app_later": "plus tard",
"Review_app_later": "Peut-être plus tard",
"Review_app_unable_store": "Impossible d'ouvrir {{store}}",
"Review_this_app": "Donnez votre avis sur cette application",
"Remove": "Retirer",
"Remove": "Supprimer",
"remove": "supprimer",
"Roles": "Rôles",
"Room_actions": "Actions de canal",
"Room_changed_announcement": "Annonce de canal est changée en: {{announcement}} par {{userBy}}",
"Room_changed_description": "Description de canal est changée en: {{description}} par {{userBy}}",
"Room_changed_privacy": "Type de canal est changé en: {{type}} par {{userBy}}",
"Room_changed_topic": "Le sujet de canal est changé en: {{topic}} par {{userBy}}",
"Room_Files": "Fichiers de canal",
"Room_Info_Edit": "Infos sur le canal Modifier",
"Room_Info": "Info sur le canal",
"Room_Members": "Membres de canal",
"Room_name_changed": "Nom de canal est changé en: {{name}} par {{userBy}}",
"SAVE": "ENREGISTRER",
"Room_actions": "Actions du salon",
"Room_changed_announcement": "Annonce du salon changé en : {{announcement}} par {{userBy}}",
"Room_changed_avatar": "Avatar du salon modifié par {{userBy}}",
"Room_changed_description": "Description du salon changé en : {{description}} par {{userBy}}",
"Room_changed_privacy": "Type de salon changé en : {{type}} par {{userBy}}",
"Room_changed_topic": "Le sujet de salon est changé en : {{topic}} par {{userBy}}",
"Room_Files": "Fichiers du salon",
"Room_Info_Edit": "Modifier les informations du salon",
"Room_Info": "Info sur le salon",
"Room_Members": "Membres du salon",
"Room_name_changed": "Nom de salon changé en : {{name}} par {{userBy}}",
"SAVE": "SAUVEGARDER",
"Save_Changes": "Sauvegarder les modifications",
"Save": "Sauvegarder",
"Saved": "Sauvé",
"saving_preferences": "sauvegardant les préférences",
"Saved": "Enregistré",
"saving_preferences": "enregistrement des préférences",
"saving_profile": "enregistrement du profil",
"saving_settings": "enregistrement des paramètres",
"saved_to_gallery": "Sauvé dans la galerie",
"saved_to_gallery": "Enregistré dans la galerie",
"Save_Your_E2E_Password": "Enregistrez votre mot de passe E2E",
"Save_Your_Encryption_Password": "Enregistrez votre mot de passe de cryptage",
"Save_Your_Encryption_Password_warning": "Ce mot de passe n'est stocké nulle part, enregistrez-le donc soigneusement ailleurs.",
"Save_Your_Encryption_Password_info": "Si vous perdez le mot de passe, il n'y a aucun moyen de le récupérer et vous perdrez l'accès à vos messages.",
"Search_Messages": "Rechercher des messages",
"Search": "Recherche",
"Search_by": "Recherche par",
"Search_by": "Rechercher par",
"Search_global_users": "Rechercher des utilisateurs mondiaux",
"Search_global_users_description": "Si vous activez, vous pouvez rechercher n'importe quel utilisateur d'autres sociétés ou serveurs.",
"Seconds": "{{second}} secondes",
"Security_and_privacy": "Sécurité et vie privée",
"Select_Avatar": "Sélectionnez un avatar",
"Select_Server": "Sélectionnez un serveur",
"Select_Users": "Sélectionner des utilisateurs",
"Select_Users": "Sélectionner les utilisateurs",
"Select_a_Channel": "Sélectionnez un canal",
"Select_a_Department": "Sélectionnez un département",
"Select_an_option": "Sélectionnez une option",
@ -449,50 +492,50 @@
"Sent_an_attachment": "Envoyé une pièce jointe",
"Server": "Serveur",
"Servers": "Serveurs",
"Server_version": "Version du serveur: {{version}}",
"Server_version": "Version du serveur : {{version}}",
"Set_username_subtitle": "Le nom d'utilisateur est utilisé pour permettre aux autres de vous mentionner dans les messages",
"Set_custom_status": "Définir un statut personnalisé",
"Set_custom_status": "Définir le statut personnalisé",
"Set_status": "Définir le statut",
"Status_saved_successfully": "Statut enregistré avec succès!",
"Status_saved_successfully": "Statut enregistré avec succès !",
"Settings": "Paramètres",
"Settings_succesfully_changed": "Paramètres modifiés avec succès!",
"Settings_succesfully_changed": "Paramètres modifiés avec succès !",
"Share": "Partager",
"Share_Link": "Partager le lien",
"Share_this_app": "Partager cette application",
"Show_more": "Afficher plus..",
"Show_Unread_Counter": "Afficher le compteur non lu",
"Show_Unread_Counter_Info": "Le compteur non-lu est affiché sous forme de badge à droite de la chaîne, dans la liste",
"Show_Unread_Counter_Info": "Le compteur non lu est affiché sous forme de badge à droite du canal, dans la liste",
"Sign_in_your_server": "Connectez-vous à votre serveur",
"Sign_Up": "S'inscrire",
"Some_field_is_invalid_or_empty": "Certains champs sont invalides ou vides",
"Sorting_by": "Tri par {{key}}",
"Sound": "Son",
"Star_room": "Favoriser canal",
"Star": "Favoris",
"Starred_Messages": "Les messages favorisé",
"starred": "favorisé",
"Starred": "Favorisé",
"Star_room": "Canal favoris",
"Star": "Mettre en favoris",
"Starred_Messages": "Les messages favoris",
"starred": "favoris",
"Starred": "Favoris",
"Start_of_conversation": "Début de conversation",
"Start_a_Discussion": "Lancer une discussion",
"Started_discussion": "A commencé une discussion:",
"Started_discussion": "A commencé une discussion :",
"Started_call": "Appel lancé par {{userBy}}",
"Submit": "Soumettre",
"Table": "Table",
"Table": "Tableau",
"Tags": "Mots clés",
"Take_a_photo": "Prendre une photo",
"Take_a_video": "Prendre une vidéo",
"Take_it": "Prends-le!",
"tap_to_change_status": "Appuyez pour changer de statut",
"Take_it": "Prends-le !",
"tap_to_change_status": "appuyez pour changer de statut",
"Tap_to_view_servers_list": "Appuyez pour afficher la liste des serveurs",
"Terms_of_Service": " Conditions d'utilisation ",
"Theme": "Thème",
"The_user_wont_be_able_to_type_in_roomName": "L'utilisateur ne pourra pas écrire dans {{roomName}}",
"The_user_will_be_able_to_type_in_roomName": "L'utilisateur pourra écrire dans {{roomName}}",
"There_was_an_error_while_action": "Il y avait une erreur en {{action}}!",
"This_room_is_blocked": "Cette canal est bloquée",
"This_room_is_read_only": "Cette canal est en lecture seule",
"Thread": "Fil de discutions",
"Threads": "Fils de discutions",
"There_was_an_error_while_action": "Une erreur s'est produite lors de {{action}} !",
"This_room_is_blocked": "Ce salon est bloqué",
"This_room_is_read_only": "Ce salon est en lecture seule",
"Thread": "Fil de discussion",
"Threads": "Fils de discussions",
"Timezone": "Fuseau horaire",
"To": "A",
"topic": "sujet",
@ -506,101 +549,103 @@
"Unblock_user": "Débloquer l'utilisateur",
"Unfavorite": "Supprimer des favoris",
"Unfollowed_thread": "Ne plus suivre ce fil",
"Unmute": "Rendre La parole",
"unmuted": "Rendu la parole",
"Unmute": "Rendre la parole",
"unmuted": "rendu la parole",
"Unpin": "Détacher",
"unread_messages": "non lus",
"unread_messages": "non lu",
"Unread": "Non lu",
"Unread_on_top": "Non lu sur le dessus",
"Unstar": "Unstar",
"Unread_on_top": "Non lu en haut",
"Unstar": "Enlever des favoris",
"Updating": "Mise à jour...",
"Uploading": "Téléchargement",
"Upload_file_question_mark": "Télécharger le fichier?",
"Uploading": "Envoyer",
"Upload_file_question_mark": "Téléverser un fichier ?",
"User": "Utilisateur",
"Users": "Utilisateurs",
"User_added_by": "L'utilisateur {{userAdded}} a été ajouté par {{userBy}}",
"User_added_by": "Utilisateur {{userAdded}} ajouté par {{userBy}}",
"User_Info": "Info d'utilisateur",
"User_has_been_key": "L'utilisateur a été {{key}}",
"User_is_no_longer_role_by_": "{{user}} n'est plus {{role}} par {{userBy}}",
"User_muted_by": "L'utilisateur {{userMuted}} a été rendu muet par {{userBy}}",
"User_removed_by": "L'utilisateur {{userRemoved}} a été retiré par {{userBy}}",
"User_sent_an_attachment": "{{user}} envoyé une pièce jointe",
"User_unmuted_by": "L'utilisateur {{userBy}} a rendu la parole a {{userUnmuted}}",
"User_was_set_role_by_": "{{user}} l'utilisateur a été défini {{role}} par {{userBy}}",
"User_removed_by": "Utilisateur {{userRemoved}} supprimé par {{userBy}}",
"User_sent_an_attachment": "{{user}} a envoyé une pièce jointe",
"User_unmuted_by": "L'utilisateur {{userBy}} a rendu la parole à {{userUnmuted}}",
"User_was_set_role_by_": "{{user}} a été défini {{role}} par {{userBy}}",
"Username_is_empty": "Nom d'utilisateur est vide",
"Username": "Nom d'utilisateur",
"Username_or_email": "Nom d'utilisateur ou address e-mail",
"Username_or_email": "Nom d'utilisateur ou e-mail",
"Uses_server_configuration": "Utilise la configuration du serveur",
"Validating": "Validation",
"Registration_Succeeded": "Inscription réussie!",
"Registration_Succeeded": "Inscription réussie !",
"Verify": "Vérifier",
"Verify_email_title": "Inscription réussie!",
"Verify_email_title": "Inscription réussie !",
"Verify_email_desc": "Nous vous avons envoyé un e-mail pour confirmer votre inscription. Si vous ne recevez pas d'e-mail sous peu, veuillez revenir et réessayer.",
"Verify_your_email_for_the_code_we_sent": "Vérifiez votre e-mail pour le code que nous avons envoyé",
"Video_call": "Appel vidéo",
"View_Original": "Voir l'original",
"Voice_call": "Appel vocal",
"Waiting_for_network": "En attente du réseau ...",
"Waiting_for_network": "En attente du réseau...",
"Websocket_disabled": "Le Websocket est désactivé pour ce serveur.\n{{contact}}",
"Welcome": "Bienvenue",
"What_are_you_doing_right_now": "Qu'es ce que vous faites actuellement?",
"Whats_your_2fa": "Quel est votre code 2FA?",
"What_are_you_doing_right_now": "Que fais-tu en ce moment ?",
"Whats_your_2fa": "Quel est votre code 2FA ?",
"Without_Servers": "Sans serveurs",
"Workspaces": "Espaces de travail",
"Would_you_like_to_return_the_inquiry": "Souhaitez-vous retourner la demande?",
"Would_you_like_to_return_the_inquiry": "Souhaitez-vous retourner la demande ?",
"Write_External_Permission_Message": "Rocket.Chat a besoin d'accéder à votre galerie pour que vous puissiez enregistrer des images.",
"Write_External_Permission": "Autorisation de la galerie",
"Yes": "Oui",
"Yes_action_it": "Oui, {{action}} le!",
"Yes_action_it": "Oui, {{action}} le !",
"Yesterday": "Hier",
"You_are_in_preview_mode": "Vous êtes en mode de prévisualisation",
"You_are_in_preview_mode": "Vous êtes en mode aperçu",
"You_are_offline": "Vous êtes hors ligne",
"You_can_search_using_RegExp_eg": "Vous pouvez rechercher à l'aide de RegExp. e.g. `/^text$/i`",
"You_can_search_using_RegExp_eg": "Vous pouvez utiliser RegExp., par exemple `/^texte$/i`",
"You_colon": "Vous: ",
"you_were_mentioned": "vous avez été mentionné",
"You_were_removed_from_channel": "Vous avez été retiré de{{channel}}",
"You_were_removed_from_channel": "Vous avez été retiré de {{channel}}",
"you": "vous",
"You": "Vous",
"Logged_out_by_server": "Vous avez été déconnecté par le serveur. Veuillez vous reconnecter.",
"Logged_out_by_server": "Vous avez été déconnecté du serveur. Veuillez vous reconnecter.",
"You_need_to_access_at_least_one_RocketChat_server_to_share_something": "Vous devez accéder à au moins un serveur Rocket.Chat pour partager quelque chose.",
"Your_certificate": "Votre Certificat",
"You_need_to_verifiy_your_email_address_to_get_notications": "Vous devez vérifier votre adresse e-mail pour recevoir des notifications",
"Your_certificate": "Votre certificat",
"Your_invite_link_will_expire_after__usesLeft__uses": "Votre lien d'invitation expirera après {{usesLeft}} utilisations.",
"Your_invite_link_will_expire_on__date__or_after__usesLeft__uses": "Votre lien d'invitation expirera le {{date}} ou après {{usesLeft}} utilisations.",
"Your_invite_link_will_expire_on__date__": "Votre lien d'invitation expirera le {{date}}.",
"Your_invite_link_will_never_expire": "Votre lien d'invitation n'expirera jamais.",
"Your_workspace": "Votre espace de travail",
"Version_no": "Version: {{version}}",
"You_will_not_be_able_to_recover_this_message": "Vous ne pourrez pas récupérer ce message!",
"You_will_unset_a_certificate_for_this_server": "Vous allez annuler un certificat pour ce serveur",
"Change_Language": "Changer la Langue",
"Crash_report_disclaimer": "Nous ne suivons jamais le contenu de vos chats. Le rapport de plantage ne contient que des informations pertinentes pour nous afin d'identifier les problèmes et de les résoudre.",
"Type_message": "Écrire un message",
"Room_search": "Recherche de salon",
"Room_selection": "Sélection du Salon 1...9",
"Next_room": "Salon Suivant",
"Previous_room": "Salon Précédent",
"Your_password_is": "Votre mot de passe est",
"Version_no": "Version : {{version}}",
"You_will_not_be_able_to_recover_this_message": "Vous ne pourrez pas récupérer ce message !",
"You_will_unset_a_certificate_for_this_server": "Vous allez supprimer un certificat pour ce serveur",
"Change_Language": "Changer la langue",
"Crash_report_disclaimer": "Nous ne suivons jamais le contenu de vos chats. Le rapport d'incident et les évènements d'analyse ne contiennent que des informations pertinentes pour nous afin d'identifier et de résoudre les problèmes.",
"Type_message": "Tapez le message",
"Room_search": "Recherche de salons",
"Room_selection": "Sélection de salon 1...9",
"Next_room": "Salon suivant",
"Previous_room": "Salon précédent",
"New_room": "Nouveau salon",
"Upload_room": "Envoyer sur un salon",
"Search_messages": "Recherche de messages",
"Scroll_messages": "Défiler messages",
"Upload_room": "Envoyer dans un salon",
"Search_messages": "Rechercher des messages",
"Scroll_messages": "Faire défiler les messages",
"Reply_latest": "Répondre au dernier",
"Reply_in_Thread": "Répondre dans le fil",
"Server_selection": "Sélection du serveur",
"Server_selection_numbers": "Sélection du Serveur 1...9",
"Add_server": "Ajouter serveur",
"Server_selection_numbers": "Sélection du serveur 1...9",
"Add_server": "Ajouter un serveur",
"New_line": "Nouvelle ligne",
"You_will_be_logged_out_of_this_application": "Vous serez déconnecté de cette application.",
"Clear": "Effacer",
"This_will_clear_all_your_offline_data": "Cela effacera toutes vos données hors-ligne.",
"This_will_clear_all_your_offline_data": "Cela effacera toutes vos données hors ligne.",
"This_will_remove_all_data_from_this_server": "Cela supprimera toutes les données de ce serveur.",
"Mark_unread": "Marquer comme non lu",
"Wait_activation_warning": "Avant de pouvoir vous connecter, votre compte doit être activé manuellement par un administrateur.",
"Screen_lock": "Verrouillage d'écran",
"Local_authentication_biometry_title": "Authentifier",
"Local_authentication_biometry_fallback": "Utiliser le mot de passe",
"Local_authentication_unlock_option": "Déverrouiller avec mot de passe",
"Local_authentication_change_passcode": "Changer le code",
"Local_authentication_info": "Remarque: si vous oubliez le code, vous devrez supprimer et réinstaller l'application.",
"Local_authentication_biometry_fallback": "Utiliser le code d'accès",
"Local_authentication_unlock_option": "Déverrouiller avec le code d'accès",
"Local_authentication_change_passcode": "Changer le code d'accès",
"Local_authentication_info": "Remarque : si vous oubliez le code d'accès, vous devrez supprimer et réinstaller l'application.",
"Local_authentication_facial_recognition": "reconnaissance faciale",
"Local_authentication_fingerprint": "empreinte digitale",
"Local_authentication_unlock_with_label": "Déverrouiller avec {{label}}",
@ -609,15 +654,112 @@
"Local_authentication_auto_lock_900": "Après 15 minutes",
"Local_authentication_auto_lock_1800": "Après 30 minutes",
"Local_authentication_auto_lock_3600": "Après 1 heure",
"Passcode_enter_title": "Entrez votre mot de passe",
"Passcode_choose_title": "Choisissez votre nouveau mot de passe",
"Passcode_choose_confirm_title": "Confirmez votre nouveau mot de passe",
"Passcode_choose_error": "Les codes secrets ne correspondent pas. Réessayer.",
"Passcode_enter_title": "Entrez votre code d'accès",
"Passcode_choose_title": "Choisissez votre nouveau code d'accès",
"Passcode_choose_confirm_title": "Confirmez votre nouveau code d'accès",
"Passcode_choose_error": "Les codes d'accès ne correspondent pas. Réessayer.",
"Passcode_choose_force_set": "Code d'accès requis par l'administrateur",
"Passcode_app_locked_title": "App verrouillée",
"Passcode_app_locked_subtitle": "Réessayez dans {{timeLeft}} secondes",
"After_seconds_set_by_admin": "Après {{seconds}} secondes (défini par l'administrateur)",
"Dont_activate": "Ne pas activer maintenant",
"Queued_chats": "Discussions en file d'attente",
"Queue_is_empty": "La file d'attente est vide"
"Queue_is_empty": "La file d'attente est vide",
"Logout_from_other_logged_in_locations": "Déconnexion des autres emplacements connectés",
"You_will_be_logged_out_from_other_locations": "Vous serez déconnecté des autres emplacements.",
"Logged_out_of_other_clients_successfully": "Déconnexion réussie des autres clients",
"Logout_failed": "Echec de la déconnexion !",
"Log_analytics_events": "Journal des événements d'analyse",
"E2E_encryption_change_password_title": "Changer le mot de passe de cryptage",
"E2E_encryption_change_password_description": "Vous pouvez désormais créer des groupes privés et des messages directs chiffrés. Vous pouvez également modifier les groupes privés ou DM existants pour les crypter.\nIl s'agit du chiffrement de bout en bout, la clé permettant de coder/décoder vos messages ne sera pas enregistrée sur le serveur. Pour cette raison, vous devez stocker ce mot de passe à un endroit sûr. Vous devrez le saisir sur les autres appareils sur lesquels vous souhaitez utiliser le cryptage E2E.",
"E2E_encryption_change_password_error": "Erreur lors de la modification du mot de passe de la clé E2E",
"E2E_encryption_change_password_success": "Le mot de passe de la clé E2E a été changé avec succès !",
"E2E_encryption_change_password_message": "Assurez-vous de l'avoir enregistré soigneusement ailleurs.",
"E2E_encryption_change_password_confirmation": "Oui, changez-le",
"E2E_encryption_reset_title": "Réinitialiser la clé E2E",
"E2E_encryption_reset_description": "Cette option supprimera la clé E2E actuelle et vous déconnectera.\nLorsque vous vous reconnecterez, Rocket.Chat générera une nouvelle clé et restaurera votre accès aux salons cryptés qui a un ou plusieurs membres en ligne.\nEn raison de la nature du cryptage E2E, Rocket.Chat ne pourra pas restaurer l'accès à un salon crypté qui n'a aucun membre en ligne.",
"E2E_encryption_reset_button": "Réinitialiser la clé E2E",
"E2E_encryption_reset_error": "Erreur lors de la réinitialisation de la clé E2E !",
"E2E_encryption_reset_message": "Vous allez être déconnecté.",
"E2E_encryption_reset_confirmation": "Oui, réinitialisez-le",
"Following": "Suivant",
"Threads_displaying_all": "Tout afficher",
"Threads_displaying_following": "Affichage suivant",
"Threads_displaying_unread": "Affichage non lu",
"No_threads": "Il n'y a pas de fils",
"No_threads_following": "Vous ne suivez aucun fil de discussion",
"No_threads_unread": "Il n'y a pas de fils non lus",
"Messagebox_Send_to_channel": "Envoyer au canal",
"Leader": "Leader",
"Moderator": "Modérateur",
"Owner": "Propriétaire",
"Remove_from_room": "Retirer du salon",
"Ignore": "Ignorer",
"Unignore": "Ne pas ignorer",
"User_has_been_ignored": "L'utilisateur a été ignoré",
"User_has_been_unignored": "L'utilisateur n'est plus ignoré",
"User_has_been_removed_from_s": "L'utilisateur a été retiré de {{s}}",
"User__username__is_now_a_leader_of__room_name_": "L'utilisateur {{username}} est désormais un leader de {{room_name}}",
"User__username__is_now_a_moderator_of__room_name_": "L'utilisateur {{username}} est désormais un modérateur de {{room_name}}",
"User__username__is_now_a_owner_of__room_name_": "L'utilisateur {{username}} est désormais un propriétaire de {{room_name}}",
"User__username__removed_from__room_name__leaders": "L'utilisateur {{username}} a été supprimé des leaders de {{room_name}}",
"User__username__removed_from__room_name__moderators": "L'utilisateur {{username}} a été supprimé des modérateurs de {{room_name}}",
"User__username__removed_from__room_name__owners": "L'utilisateur {{username}} a été supprimé des propriétaires de {{room_name}}",
"The_user_will_be_removed_from_s": "L'utilisateur sera supprimé de {{s}}",
"Yes_remove_user": "Oui, supprimez l'utilisateur !",
"Direct_message": "Message direct",
"Message_Ignored": "Message ignoré. Touchez pour l'afficher.",
"Enter_workspace_URL": "Entrez l'URL de l'espace de travail",
"Workspace_URL_Example": "Ex. votre-société.rocket.chat",
"This_room_encryption_has_been_enabled_by__username_": "Le cryptage de ce salon a été activé par {{username}}",
"This_room_encryption_has_been_disabled_by__username_": "Le cryptage de ce salon a été désactivé par {{username}}",
"Teams": "Equipes",
"No_team_channels_found": "Aucun canal trouvé",
"Team_not_found": "Equipe non trouvée",
"Create_Team": "Créer une équipe",
"Team_Name": "Nom de l'équipe",
"Private_Team": "Equipe privée",
"Read_Only_Team": "Equipe en lecture seule",
"Broadcast_Team": "Equipe de diffusion",
"creating_team": "création de l'équipe",
"team-name-already-exists": "Une équipe portant ce nom existe déjà",
"Add_Channel_to_Team": "Ajouter un canal à l'équipe",
"Create_New": "Créer un nouveau",
"Add_Existing": "Ajouter existant",
"Add_Existing_Channel": "Ajouter un canal existant",
"Remove_from_Team": "Retirer de l'équipe",
"Auto-join": "Rejoindre automatiquement",
"Remove_Team_Room_Warning": "Souhaitez-vous supprimer ce canal de l'équipe ? Le canal sera déplacé vers l'espace de travail",
"Confirmation": "Confirmation",
"invalid-room": "Salon invalide",
"You_are_leaving_the_team": "Vous quittez l'équipe '{{team}}'",
"Leave_Team": "Quitter l'équipe",
"Select_Team": "Sélectionnez l'équipe",
"Select_Team_Channels": "Sélectionnez les canaux de l'équipe que vous souhaitez quitter.",
"Cannot_leave": "Ne peut pas partir",
"Cannot_remove": "Impossible d'enlever",
"Cannot_delete": "Impossible de supprimer",
"Last_owner_team_room": "Vous êtes le dernier propriétaire de ce canal. Une fois que vous quittez l'équipe, le canal sera conservé au sein de l'équipe mais vous le gérerez de l'extérieur.",
"last-owner-can-not-be-removed": "Le dernier propriétaire ne peut pas être supprimé",
"Remove_User_Teams": "Sélectionnez les canaux dont vous souhaitez supprimer l'utilisateur.",
"Delete_Team": "Supprimer l'équipe",
"Select_channels_to_delete": "Ceci ne peut pas être annulé. Une fois que vous supprimez une équipe, tout le contenu et la configuration du chat seront supprimés.\n\nSélectionnez les canaux que vous souhaitez supprimer. Ceux que vous décidez de conserver seront disponible dans votre espace de travail. Notez que les canaux publics seront toujours publics et visibles par tous.",
"You_are_deleting_the_team": "Vous supprimez cette équipe.",
"Removing_user_from_this_team": "Vous supprimez {{user}} de cette équipe",
"Remove_User_Team_Channels": "Sélectionnez les canaux dont vous souhaitez supprimer l'utilisateur.",
"Remove_Member": "Supprimer un membre",
"leaving_team": "quitter l'équipe",
"removing_team": "retirer de l'équipe",
"moving_channel_to_team": "transfert de canal à l'équipe",
"deleting_team": "suppression de l'équipe",
"member-does-not-exist": "Le membre n'existe pas",
"Convert": "Convertir",
"Convert_to_Team": "Convertir en équipe",
"Convert_to_Team_Warning": "Ceci ne peut pas être annulé. Une fois que vous avez converti un canal en équipe, vous ne pouvez pas le retransformer en canal.",
"Move_to_Team": "Déplacer vers l'équipe",
"Move_Channel_Paragraph": "Le déplacement d'un canal dans une équipe signifie que ce canal sera ajouté dans le contexte d'équipe. Cependant, tous les membres du canal, qui ne sont pas membres de l'équipe respective, auront toujours accès à ce canal, mais ne seront pas ajoutés comme membres de l'équipe.\n\nLa gestion de tout le canal sera toujours assurée par les propriétaires de ce canal.\n\nLes membres de l'équipe et même les propriétaires de l'équipe, s'ils ne sont pas membres de ce canal, ne peuvent pas avoir accès au contenu du canal.\n\nVeuillez noter que le propriétaire de l'équipe pourra supprimer des membres du canal.",
"Move_to_Team_Warning": "Après avoir lu les instructions précédentes sur ce comportement, voulez-vous toujours déplacer ce canal vers l'équipe sélectionnée ?",
"Load_More": "Charger plus",
"Load_Newer": "Charger plus récent",
"Load_Older": "Charger plus ancien"
}

View File

@ -33,7 +33,7 @@
"error-invalid-date": "Data fornita non valida.",
"error-invalid-description": "Descrizione non valida",
"error-invalid-domain": "Dominio non valido",
"error-invalid-email": "E-mail {{emai}} non valida",
"error-invalid-email": "E-mail {{email}} non valida",
"error-invalid-email-address": "Indirizzo e-mail non valido",
"error-invalid-file-height": "Altezza del file non valida",
"error-invalid-file-type": "Tipo di file non valido",
@ -157,8 +157,8 @@
"Continue_with": "Continua con",
"Copied_to_clipboard": "Copiato negli appunti!",
"Copy": "Copia",
"Permalink": "Permalink",
"Conversation": "Conversazione",
"Permalink": "Permalink",
"Certificate_password": "Password certificato",
"Clear_cache": "Cancella la cache locale",
"Clear_cache_loading": "Cancellando la cache.",
@ -290,11 +290,11 @@
"last_message": "ultimo messaggio",
"Leave_channel": "Abbandona canale",
"leaving_room": "abbandonando stanza",
"Leave": "Lasciare il canale",
"leave": "abbandona",
"Legal": "Informazioni",
"Light": "Chiaro",
"License": "Licenza",
"Livechat": "Livechat",
"Livechat_edit": "Modifica Livechat",
"Login": "Accedi",
"Login_error": "Le tue credenziali sono state rifiutate! Prova di nuovo.",
@ -681,12 +681,6 @@
"No_threads_following": "Non stai seguendo alcun thread",
"No_threads_unread": "Non ci sono thread non letti",
"Messagebox_Send_to_channel": "Invia sul canale",
"Set_as_leader": "Rendi leader",
"Set_as_moderator": "Rendi moderatore",
"Set_as_owner": "Rendi proprietario",
"Remove_as_leader": "Rimuovi come leader",
"Remove_as_moderator": "Rimuovi come moderatore",
"Remove_as_owner": "Rimuovi come proprietario",
"Remove_from_room": "Rimuovi dalla stanza",
"Ignore": "Ignora",
"Unignore": "Non ignorare",
@ -704,5 +698,6 @@
"Direct_message": "Messaggio diretto",
"Message_Ignored": "Messaggio ignorato. Tocca per visualizzarlo.",
"Enter_workspace_URL": "Inserisci la url del workspace",
"Workspace_URL_Example": "Es. tua-azienda.rocket.chat"
"Workspace_URL_Example": "Es. tua-azienda.rocket.chat",
"invalid-room": "Canale non valido"
}

View File

@ -31,7 +31,7 @@
"error-invalid-date": "不正な日時です",
"error-invalid-description": "不正な詳細です",
"error-invalid-domain": "不正なドメインです",
"error-invalid-email": "不正なメールアドレスです。 {{emai}}",
"error-invalid-email": "不正なメールアドレスです。 {{email}}",
"error-invalid-email-address": "不正なメールアドレスです",
"error-invalid-file-height": "ファイルの高さが不正です",
"error-invalid-file-type": "ファイルの種類が不正です",
@ -179,7 +179,6 @@
"Email": "メールアドレス",
"email": "メールアドレス",
"Enable_Auto_Translate": "自動翻訳を有効にする",
"Enable_markdown": "マークダウンを有効にする",
"Enable_notifications": "通知を有効にする",
"Everyone_can_access_this_channel": "全員このチャンネルにアクセスできます",
"Error_uploading": "アップロードエラー",
@ -220,6 +219,7 @@
"last_message": "最後のメッセージ",
"Leave_channel": "チャンネルを退出",
"leaving_room": "チャンネルを退出",
"Leave": "ルームを退出",
"leave": "退出",
"Legal": "法的項目",
"Light": "ライト",
@ -432,7 +432,7 @@
"Users": "ユーザー",
"User_added_by": "{{userBy}} が {{userAdded}} を追加しました",
"User_Info": "ユーザー情報",
"User_has_been_key": "ユーザーは{{ key }}",
"User_has_been_key": "ユーザーは{{key}}",
"User_is_no_longer_role_by_": "{{userBy}} は {{user}} のロール {{role}} を削除しました。",
"User_muted_by": "{{userBy}} は {{userMuted}} をミュートしました。",
"User_removed_by": "{{userBy}} は {{userRemoved}} を退出させました。",
@ -488,5 +488,6 @@
"New_line": "新しい行",
"You_will_be_logged_out_of_this_application": "アプリからログアウトします。",
"Clear": "クリア",
"This_will_clear_all_your_offline_data": "オフラインデータをすべて削除します。"
"This_will_clear_all_your_offline_data": "オフラインデータをすべて削除します。",
"invalid-room": "無効なルーム"
}

File diff suppressed because it is too large Load Diff

View File

@ -62,27 +62,20 @@
"error-no-tokens-for-this-user": "Não existem tokens para este usuário",
"error-not-allowed": "Não permitido",
"error-not-authorized": "Não autorizado",
"error-password-policy-not-met": "A senha não atende a política do servidor",
"error-password-policy-not-met-maxLength": "A senha não está de acordo com a política de comprimento máximo do servidor (senha muito longa)",
"error-password-policy-not-met-minLength": "A senha não está de acordo com a política de comprimento mínimo do servidor (senha muito curta)",
"error-password-policy-not-met-oneLowercase": "A senha não está de acordo com a política do servidor de pelo menos um caractere minúsculo.",
"error-password-policy-not-met-oneNumber": "A senha não está de acordo com a política do servidor, de pelo menos um caractere numérico.",
"error-password-policy-not-met-oneSpecial": "A senha não está de acordo com a política do servidor, de pelo menos um caractere especial.",
"error-password-policy-not-met-oneUppercase": "A senha não está de acordo com a política do servidor, de pelo menos um caractere maiúsculo.",
"error-password-policy-not-met-repeatingCharacters": "A senha não está de acordo com a política do servidor, relativamente aos caracteres proibidos repetidos (existem vários caracteres proibidos próximos uns dos outros)",
"error-push-disabled": "Notificações push desativadas",
"error-remove-last-owner": "Este é o último proprietário. Por favor, defina um novo proprietário antes de remover este.",
"error-role-in-use": "Não é possível remover o papel pois ele está em uso",
"error-role-name-required": "Nome do papel é obrigatório",
"error-the-field-is-required": "O campo {{field}} é obrigatório.",
"error-too-many-requests": "Erro, muitas solicitações. Por favor, diminua a velocidade. Você deve esperar {{seconds}} segundos antes de tentar novamente.",
"error-user-has-no-roles": "O usuário não possui permissões",
"error-user-is-not-activated": "O usuário não está ativo",
"error-user-has-no-roles": "O usuário não possui permissões",
"error-user-limit-exceeded": "O número de usuários que você está tentando convidar para #channel_name excede o limite determindado pelo administrador",
"error-user-not-in-room": "O usuário não está nesta sala",
"error-user-registration-disabled": "O registro do usuário está desativado",
"error-user-registration-secret": "O registro de usuário é permitido somente via URL secreta",
"error-you-are-last-owner": "Você é o último proprietário da sala. Por favor defina um novo proprietário antes de sair.",
"error-status-not-allowed": "O status invisível está desativado",
"Actions": "Ações",
"activity": "atividade",
"Activity": "Atividade",
@ -102,6 +95,7 @@
"and": "e",
"announcement": "anúncio",
"Announcement": "Anúncio",
"Apply_Your_Certificate": "Aplicar certificado",
"ARCHIVE": "ARQUIVAR",
"archive": "arquivar",
"are_typing": "estão digitando",
@ -131,10 +125,7 @@
"Channel_Name": "Nome do Canal",
"Channels": "Canais",
"Chats": "Conversas",
"Change_Language": "Alterar idioma",
"Change_language_loading": "Alterando idioma.",
"Call_already_ended": "A chamada já terminou!",
"Clear_cache_loading": "Limpando cache.",
"Clear_cookies_alert": "Você quer limpar seus cookies?",
"Clear_cookies_desc": "Esta ação limpará todos os cookies de login permitindo que você faça login em outras contas.",
"Clear_cookies_yes": "Sim, limpar cookies",
@ -143,8 +134,9 @@
"Close": "Fechar",
"Close_emoji_selector": "Fechar seletor de emojis",
"Closing_chat": "Fechando conversa",
"Choose": "Escolher",
"Change_language_loading": "Alterando idioma.",
"Chat_closed_by_agent": "Conversa fechada por agente",
"Choose": "Escolher",
"Choose_from_library": "Escolha da biblioteca",
"Choose_file": "Enviar arquivo",
"Choose_where_you_want_links_be_opened": "Escolha onde deseja que os links sejam abertos",
@ -154,15 +146,16 @@
"Confirm": "Confirmar",
"Connect": "Conectar",
"Connected": "Conectado",
"Conversation": "Conversação",
"connecting_server": "conectando no servidor",
"Connecting": "Conectando...",
"Contact_us": "Entre em contato",
"Continue_with": "Entrar com",
"Contact_your_server_admin": "Contate o administrador do servidor.",
"Continue_with": "Entrar com",
"Copied_to_clipboard": "Copiado para a área de transferência!",
"Copy": "Copiar",
"Conversation": "Conversação",
"Permalink": "Link-Permanente",
"Clear_cache_loading": "Limpando cache.",
"Create_account": "Criar conta",
"Create_Channel": "Criar Canal",
"Create_Direct_Messages": "Criar Mensagens Diretas",
@ -172,19 +165,21 @@
"Create": "Criar",
"Dark": "Escuro",
"Dark_level": "Nível escuro",
"Default": "Padrão",
"Default_browser": "Navegador padrão",
"Delete_Room_Warning": "A exclusão de uma sala irá apagar todas as mensagens postadas na sala. Isso não pode ser desfeito.",
"Department": "Departamento",
"delete": "excluir",
"Delete": "Excluir",
"DELETE": "EXCLUIR",
"deleting_room": "excluindo sala",
"Direct_Messages": "Mensagens Diretas",
"description": "descrição",
"Description": "Descrição",
"Desktop_Options": "Opções De Área De Trabalho",
"Desktop_Notifications": "Notificações da Área de Trabalho",
"Desktop_Alert_info": "Essas notificações são entregues a você na área de trabalho",
"Directory": "Diretório",
"description": "descrição",
"Description": "Descrição",
"Direct_Messages": "Mensagens Diretas",
"Disable_notifications": "Desabilitar notificações",
"Discussions": "Discussões",
"Discussion_Desc": "Ajude a manter uma visão geral sobre o que está acontecendo! Ao criar uma discussão, um sub-canal do que você selecionou é criado e os dois são vinculados.",
@ -192,6 +187,7 @@
"Done": "Pronto",
"Dont_Have_An_Account": "Não tem uma conta?",
"Do_you_have_an_account": "Você tem uma conta?",
"Do_you_have_a_certificate": "Você tem um certificado?",
"Do_you_really_want_to_key_this_room_question_mark": "Você quer realmente {{key}} esta sala?",
"E2E_Encryption": "Encriptação ponta a ponta",
"E2E_How_It_Works_info1": "Agora você pode criar grupos privados criptografados e mensagens diretas. Você também pode alterar grupos privados existentes ou DMs para criptografados.",
@ -201,16 +197,16 @@
"edit": "editar",
"edited": "editado",
"Edit": "Editar",
"Edit_Invite": "Editar convite",
"Edit_Status": "Editar Status",
"Edit_Invite": "Editar convite",
"End_to_end_encrypted_room": "Sala criptografada de ponta a ponta",
"end_to_end_encryption": "criptografia de ponta a ponta",
"Email_Notification_Mode_All": "Cada Menção / Mensagem Direta",
"Email_Notification_Mode_Disabled": "Desativado",
"Email_or_password_field_is_empty": "Email ou senha estão vazios",
"Email": "E-mail",
"email": "e-mail",
"Empty_title": "Título vazio",
"Email_Notification_Mode_All": "Cada Menção / Mensagem Direta",
"Email_Notification_Mode_Disabled": "Desativado",
"Enable_Auto_Translate": "Ativar a tradução automática",
"Enable_notifications": "Habilitar notificações",
"Encrypted": "Criptografado",
@ -223,6 +219,7 @@
"Everyone_can_access_this_channel": "Todos podem acessar este canal",
"Error_uploading": "Erro subindo",
"Expiration_Days": "Expira em (dias)",
"Favorite": "Adicionar aos Favoritos",
"Favorites": "Favoritos",
"Files": "Arquivos",
"File_description": "Descrição do arquivo",
@ -241,6 +238,7 @@
"Generate_New_Link": "Gerar novo convite",
"Group_by_favorites": "Agrupar favoritos",
"Group_by_type": "Agrupar por tipo",
"Hide": "Ocultar",
"Has_joined_the_channel": "entrou no canal",
"Has_joined_the_conversation": "entrou na conversa",
"Has_left_the_channel": "saiu da conversa",
@ -279,6 +277,7 @@
"last_message": "última mensagem",
"Leave_channel": "Sair do canal",
"leaving_room": "saindo do canal",
"Leave": "Sair da sala",
"leave": "sair",
"Legal": "Legal",
"Light": "Claro",
@ -286,8 +285,8 @@
"Login": "Entrar",
"Login_error": "Suas credenciais foram rejeitadas. Tente novamente por favor!",
"Login_with": "Login with",
"Logout": "Sair",
"Logging_out": "Saindo.",
"Logout": "Sair",
"Max_number_of_uses": "Número máximo de usos",
"Max_number_of_users_allowed_is_number": "Número máximo de usuários é {{maxUsers}}",
"Members": "Membros",
@ -300,6 +299,7 @@
"Message_removed": "Mensagem removida",
"message": "mensagem",
"messages": "mensagens",
"Message": "Mensagem",
"Messages": "Mensagens",
"Microphone_Permission_Message": "Rocket.Chat precisa de acesso ao seu microfone para enviar mensagens de áudio.",
"Microphone_Permission": "Acesso ao Microfone",
@ -311,7 +311,6 @@
"Name": "Nome",
"Navigation_history": "Histórico de navegação",
"Never": "Nunca",
"New_in_RocketChat_question_mark": "Novo no Rocket.Chat?",
"New_Message": "Nova Mensagem",
"New_Password": "Nova Senha",
"Next": "Próximo",
@ -326,19 +325,20 @@
"No_Message": "Não há mensagens",
"No_messages_yet": "Não há mensagens ainda",
"No_Reactions": "Sem reações",
"Not_RC_Server": "Este não é um servidor Rocket.Chat.\n{{contact}}",
"Nothing": "Nada",
"Nothing_to_save": "Nada para salvar!",
"Notify_active_in_this_room": "Notificar usuários ativos nesta sala",
"Notify_all_in_this_room": "Notificar todos nesta sala",
"Notifications": "Notificações",
"Notification_Duration": "Duração da notificação",
"Notification_Preferences": "Preferências de notificação",
"Not_RC_Server": "Este não é um servidor Rocket.Chat.\n{{contact}}",
"No_available_agents_to_transfer": "Nenhum agente disponível para transferência",
"Offline": "Offline",
"Oops": "Ops!",
"Omnichannel": "Omnichannel",
"Open_Livechats": "Bate-papos em Andamento",
"Omnichannel_enable_alert": "Você não está disponível no Omnichannel. Você quer ficar disponível?",
"Oops": "Ops!",
"Onboarding_description": "Workspace é o espaço de colaboração do seu time ou organização. Peça um convite ou o endereço ao seu administrador ou crie uma workspace para o seu time.",
"Onboarding_join_workspace": "Entre numa workspace",
"Onboarding_subtitle": "Além da colaboração em equipe",
@ -358,13 +358,14 @@
"Password": "Senha",
"Parent_channel_or_group": "Canal ou grupo pai",
"Permalink_copied_to_clipboard": "Link-permanente copiado para a área de transferência!",
"Phone": "Telefone",
"Pin": "Fixar",
"Pinned_Messages": "Mensagens Fixadas",
"pinned": "fixada",
"Pinned": "Mensagens Fixadas",
"Please_wait": "Por favor, aguarde.",
"Please_enter_your_password": "Por favor, digite sua senha",
"Please_add_a_comment": "Por favor, adicione um comentário",
"Please_enter_your_password": "Por favor, digite sua senha",
"Please_wait": "Por favor, aguarde.",
"Preferences": "Preferências",
"Preferences_saved": "Preferências salvas!",
"Privacy_Policy": " Política de Privacidade",
@ -386,15 +387,16 @@
"Read_External_Permission": "Permissão de acesso à arquivos",
"Read_Only_Channel": "Canal Somente Leitura",
"Read_Only": "Somente Leitura",
"Read_Receipt": "Lida por",
"Receive_Group_Mentions": "Receber menções de grupo",
"Receive_Group_Mentions_Info": "Receber menções @all e @here",
"Register": "Registrar",
"Read_Receipt": "Lida por",
"Repeat_Password": "Repetir Senha",
"Replied_on": "Respondido em:",
"replies": "respostas",
"reply": "resposta",
"Reply": "Responder",
"Report": "Reportar",
"Receive_Notification": "Receber Notificação",
"Receive_notifications_from": "Receber notificação de {{name}}",
"Resend": "Reenviar",
@ -424,6 +426,7 @@
"SAVE": "SALVAR",
"Save_Changes": "Salvar Alterações",
"Save": "Salvar",
"Saved": "Salvo",
"saving_preferences": "salvando preferências",
"saving_profile": "salvando perfil",
"saving_settings": "salvando configurações",
@ -472,12 +475,14 @@
"starred": "favoritou",
"Starred": "Mensagens Favoritas",
"Start_of_conversation": "Início da conversa",
"Started_call": "Chamada iniciada por {{userBy}}",
"Start_a_Discussion": "Iniciar uma Discussão",
"Started_discussion": "Iniciou uma discussão:",
"Started_call": "Chamada iniciada por {{userBy}}",
"Submit": "Enviar",
"Table": "Tabela",
"Take_a_photo": "Tirar uma foto",
"Take_a_video": "Gravar um vídeo",
"Take_it": "Pegue!",
"Terms_of_Service": " Termos de Serviço ",
"Theme": "Tema",
"The_user_wont_be_able_to_type_in_roomName": "O usuário não poderá digitar em {{roomName}}",
@ -491,12 +496,14 @@
"To": "Para",
"topic": "tópico",
"Topic": "Tópico",
"Translate": "Traduzir",
"Try_again": "Tentar novamente",
"Two_Factor_Authentication": "Autenticação de dois fatores",
"Type_the_channel_name_here": "Digite o nome do canal",
"unarchive": "desarquivar",
"UNARCHIVE": "DESARQUIVAR",
"Unblock_user": "Desbloquear usuário",
"Unfavorite": "Remover dos Favoritos",
"Unfollowed_thread": "Parou de seguir tópico",
"Unmute": "Permitir que o usuário fale",
"unmuted": "permitiu que o usuário fale",
@ -511,6 +518,7 @@
"User": "Usuário",
"Users": "Usuários",
"User_added_by": "Usuário {{userAdded}} adicionado por {{userBy}}",
"User_Info": "Informações do usuário",
"User_has_been_key": "Usuário foi {{key}}",
"User_is_no_longer_role_by_": "{{user}} não pertence mais à {{role}} por {{userBy}}",
"User_muted_by": "User {{userMuted}} muted por {{userBy}}",
@ -527,25 +535,31 @@
"Verify_email_desc": "Nós lhe enviamos um e-mail para confirmar o seu registro. Se você não receber um e-mail em breve, por favor retorne e tente novamente.",
"Verify_your_email_for_the_code_we_sent": "Verifique em seu e-mail o código que enviamos",
"Video_call": "Chamada de vídeo",
"View_Original": "Visualizar original",
"Voice_call": "Chamada de voz",
"Waiting_for_network": "Aguardando rede...",
"Websocket_disabled": "Websocket está desativado para esse servidor.\n{{contact}}",
"Welcome": "Bem vindo",
"Whats_your_2fa": "Qual seu código de autenticação?",
"What_are_you_doing_right_now": "O que você está fazendo agora?",
"Whats_your_2fa": "Qual seu código de autenticação?",
"Without_Servers": "Sem Servidores",
"Workspaces": "Workspaces",
"Would_you_like_to_return_the_inquiry": "Deseja retornar a consulta?",
"Write_External_Permission_Message": "Rocket.Chat precisa de acesso à sua galeria para salvar imagens",
"Write_External_Permission": "Acesso à Galeria",
"Yes": "Sim",
"Yes_action_it": "Sim, {{action}}!",
"Yesterday": "Ontem",
"You_are_in_preview_mode": "Está é uma prévia do canal",
"You_are_offline": "Você está offline",
"You_can_search_using_RegExp_eg": "Você pode usar expressões regulares, por exemplo `/^text$/i`",
"You_need_to_verifiy_your_email_address_to_get_notications": "Você precisa confirmar seu endereço de e-mail para obter notificações",
"You_colon": "Você: ",
"you_were_mentioned": "você foi mencionado",
"You_were_removed_from_channel": "Você foi removido de {{channel}}",
"you": "você",
"You": "Você",
"You_need_to_verifiy_your_email_address_to_get_notications": "Você precisa confirmar seu endereço de e-mail para obter notificações",
"Your_certificate": "Seu certificado",
"Your_invite_link_will_expire_after__usesLeft__uses": "Seu link de convite irá vencer depois de {{usesLeft}} usos.",
"Your_invite_link_will_expire_on__date__or_after__usesLeft__uses": "Seu link de convite irá vencer em {{date}} ou depois de {{usesLeft}} usos.",
"Your_invite_link_will_expire_on__date__": "Seu link de convite irá vencer em {{date}}.",
@ -553,10 +567,7 @@
"Your_workspace": "Sua workspace",
"You_will_not_be_able_to_recover_this_message": "Você não será capaz de recuperar essa mensagem!",
"You_will_unset_a_certificate_for_this_server": "Você cancelará a configuração de um certificado para este servidor",
"Would_you_like_to_return_the_inquiry": "Deseja retornar a consulta?",
"Write_External_Permission_Message": "Rocket.Chat precisa de acesso à sua galeria para salvar imagens",
"Write_External_Permission": "Acesso à Galeria",
"Yes": "Sim",
"Change_Language": "Alterar idioma",
"Crash_report_disclaimer": "Nós não rastreamos o conteúdo das suas conversas. O relatório de erros e os eventos do analytics apenas contém informações relevantes para identificarmos problemas e corrigí-los.",
"Type_message": "Digitar mensagem",
"Room_search": "Busca de sala",
@ -568,6 +579,7 @@
"Search_messages": "Buscar mensagens",
"Scroll_messages": "Rolar mensagens",
"Reply_latest": "Responder para última mensagem",
"Reply_in_Thread": "Responder por Tópico",
"Server_selection": "Seleção de servidor",
"Server_selection_numbers": "Selecionar servidor 1...9",
"Add_server": "Adicionar servidor",
@ -628,12 +640,6 @@
"No_threads_following": "Você não está seguindo tópicos",
"No_threads_unread": "Não há tópicos não lidos",
"Messagebox_Send_to_channel": "Mostrar no canal",
"Set_as_leader": "Definir como líder",
"Set_as_moderator": "Definir como moderador",
"Set_as_owner": "Definir como proprietário",
"Remove_as_leader": "Remover como líder",
"Remove_as_moderator": "Remover como moderador",
"Remove_as_owner": "Remover como owner",
"Remove_from_room": "Remover do canal",
"Ignore": "Ignorar",
"Unignore": "Deixar de ignorar",
@ -654,10 +660,10 @@
"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}}",
"Apply_Your_Certificate": "Aplicar certificado",
"Do_you_have_a_certificate": "Você tem um certificado?",
"Your_certificate": "Seu certificado",
"Teams": "Times",
"No_team_channels_found": "Nenhum canal encontrado",
"Team_not_found": "Time não encontrado"
"Team_not_found": "Time não encontrado",
"Private_Team": "Equipe Privada",
"Add_Existing_Channel": "Adicionar Canal Existente",
"invalid-room": "Sala inválida"
}

View File

@ -30,7 +30,7 @@
"error-invalid-date": "Data inválida fornecida.",
"error-invalid-description": "Descrição inválida",
"error-invalid-domain": "Domínio inválido",
"error-invalid-email": "E-mail inválido {{emai}}",
"error-invalid-email": "E-mail inválido {{email}}",
"error-invalid-email-address": "Endereço de e-mail invalido",
"error-invalid-file-height": "Altura de ficheiro inválida",
"error-invalid-file-type": "Tipo de ficheiro inválido",
@ -137,14 +137,14 @@
"delete": "apagar",
"Delete": "Apagar",
"DELETE": "APAGAR",
"deleting_room": "apagando sala",
"description": "descrição",
"Description": "Descrição",
"Disable_notifications": "Desactivar notificações",
"Direct_Messages": "Mensagens Directas",
"Disable_notifications": "Desactivar notificações",
"Dont_Have_An_Account": "Não tem uma conta?",
"Do_you_really_want_to_key_this_room_question_mark": "Você quer mesmo {{key}} esta sala?",
"edit": "editar",
"deleting_room": "apagando sala",
"Edit": "Editar",
"Email_or_password_field_is_empty": "O campo de e-mail ou palavra-passe está vazio",
"Email": "E-mail",

View File

@ -29,7 +29,7 @@
"error-invalid-channel": "Недействительный канал.",
"error-invalid-channel-start-with-chars": "Недействительный канал. Начните с @ или #",
"error-invalid-custom-field": "Неверное настраиваемое поле",
"error-invalid-custom-field-name": "Неверное имя настраиваемого поля. Используйте только буквы, цифры, дефисы и символы подчеркивания.",
"error-invalid-custom-field-name": "Неверное имя настраиваемого поля. Используйте только буквы, цифры, дефис и символ подчеркивания.",
"error-invalid-date": "Указана недопустимая дата.",
"error-invalid-description": "Недопустимое описание",
"error-invalid-domain": "Недопустимый домен",
@ -46,9 +46,9 @@
"error-invalid-password": "Неверный пароль",
"error-invalid-redirectUri": "Недопустимый redirectUri",
"error-invalid-role": "Недопустимая роль",
"error-invalid-room": "Недопустимый канал",
"error-invalid-room-name": "{{room_name}} не является допустимым именем канала",
"error-invalid-room-type": "{{type}} не является допустимым типом канала.",
"error-invalid-room": "Недопустимый чат",
"error-invalid-room-name": "{{room_name}} не является допустимым именем чата",
"error-invalid-room-type": "{{type}} не является допустимым типом чата.",
"error-invalid-settings": "Недопустимые параметры",
"error-invalid-subscription": "Недействительная подписка",
"error-invalid-token": "Недопустимый токен",
@ -61,6 +61,7 @@
"error-message-editing-blocked": "Правка сообщений заблокирована",
"error-message-size-exceeded": "Размер сообщения превышает максимально разрешенный",
"error-missing-unsubscribe-link": "Вы должны указать ссылку [отписаться].",
"error-no-owner-channel": "Вы не являетесь владельцем данного чата",
"error-no-tokens-for-this-user": "Для этого пользователя нет токенов",
"error-not-allowed": "Не допускается",
"error-not-authorized": "Не разрешено",
@ -77,7 +78,8 @@
"error-user-registration-custom-field": "error-user-registration-custom-field",
"error-user-registration-disabled": "Регистрация пользователей отключена",
"error-user-registration-secret": "Регистрация пользователей разрешена только через секретный URL",
"error-you-are-last-owner": "Вы последний владелец. Пожалуйста, установите нового владельца, прежде чем покинуть комнату.",
"error-you-are-last-owner": "Вы последний владелец. Пожалуйста, назначьте нового владельца, прежде чем покинуть чат.",
"error-status-not-allowed": "Статус Невидимый отключён",
"Actions": "Действия",
"activity": "активности",
"Activity": "По активности",
@ -90,6 +92,7 @@
"alert": "оповещение",
"alerts": "оповещения",
"All_users_in_the_channel_can_write_new_messages": "Все пользователи канала могут писать новые сообщения",
"All_users_in_the_team_can_write_new_messages": "Все пользователи в Команде могут писать новые сообщения",
"A_meaningful_name_for_the_discussion_room": "Осмысленное имя для обсуждения",
"All": "Все",
"All_Messages": "Все сообщения",
@ -180,6 +183,7 @@
"delete": "удалить",
"Delete": "Удалить",
"DELETE": "УДАЛИТЬ",
"move": "переместить",
"deleting_room": "удаление чата",
"description": "описание",
"Description": "Описание",
@ -225,6 +229,7 @@
"Encryption_error_title": "Введен не верный пароль шифрования",
"Encryption_error_desc": "Невозможно расшифровать ваш ключ шифрования, чтобы импортировать его",
"Everyone_can_access_this_channel": "Каждый может получить доступ к этому каналу",
"Everyone_can_access_this_team": "Каждый может получить доступ к этой Команде",
"Error_uploading": "Ошибка загрузки",
"Expiration_Days": "Срок действия (Дни)",
"Favorite": "Избранное",
@ -281,13 +286,17 @@
"Invite_Link": "Ссылка Приглашения",
"Invite_users": "Приглашение пользователей",
"Join": "Присоединиться",
"Join_Code": "Код присоединения",
"Insert_Join_Code": "Вставить код присоединения",
"Join_our_open_workspace": "Присоединиться к нашему открытому серверу",
"Join_your_workspace": "Присоединиться к вашему серверу",
"Just_invited_people_can_access_this_channel": "Только приглашенные люди могут получить доступ к этому каналу",
"Just_invited_people_can_access_this_team": "Только приглашенные пользователи могут получить доступ к этой Команде",
"Language": "Язык",
"last_message": "последнее сообщение",
"Leave_channel": "Покинуть канал",
"leaving_room": "покинуть комнату",
"Leave": "Покинуть комнату",
"leave": "покинуть",
"Legal": "Правовые аспекты",
"Light": "Светлая",
@ -322,8 +331,9 @@
"Mute": "Заглушить",
"muted": "Заглушен",
"My_servers": "Мои серверы",
"N_person_reacted": "{{n}} людей отреагировало",
"N_people_reacted": "отреагировало {{n}} человек",
"N_users": "{{n}} пользователи",
"N_channels": "{{n}} каналов",
"name": "имя",
"Name": "Имя",
"Navigation_history": "История навигации",
@ -433,6 +443,7 @@
"Review_app_unable_store": "Невозможно открыть {{store}}",
"Review_this_app": "Оценить это приложение",
"Remove": "Удалить",
"remove": "удалить",
"Roles": "Роли",
"Room_actions": "Действия с чатом",
"Room_changed_announcement": "Объявление чата было изменено на: {{announcement}} пользователем {{userBy}}",
@ -679,12 +690,9 @@
"No_threads_following": "Нет тредов, за которыми вы следите",
"No_threads_unread": "Непрочитанных тредов нет",
"Messagebox_Send_to_channel": "Отправить в чат",
"Set_as_leader": "Назначить лидером",
"Set_as_moderator": "Назначить модератором",
"Set_as_owner": "Назначить владельцем",
"Remove_as_leader": "Удалить из лидеров",
"Remove_as_moderator": "Удалить из модераторов",
"Remove_as_owner": "Удалить из владельцев",
"Leader": "Лидер",
"Moderator": "Модератор",
"Owner": "Владелец",
"Remove_from_room": "Удалить из чата",
"Ignore": "Игнориновать",
"Unignore": "Прекратить игнорировать",
@ -704,5 +712,54 @@
"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}}"
"This_room_encryption_has_been_disabled_by__username_": "Шифрование для этого чата выключено {{username}}",
"Teams": "Команды",
"No_team_channels_found": "Каналы не найдены",
"Team_not_found": "Команда не найдена",
"Create_Team": "Создать Команду",
"Team_Name": "Имя Команды",
"Private_Team": "Приватная Команда",
"Read_Only_Team": "Команда только для чтения",
"Broadcast_Team": "Широковещательная Команда",
"creating_team": "создание Команды",
"team-name-already-exists": "Команда с таким названием уже существует",
"Add_Channel_to_Team": "Добавить канал в Команду",
"Create_New": "Создать",
"Add_Existing": "Добавить существующее",
"Add_Existing_Channel": "Добавить существующий канал",
"Remove_from_Team": "Удалить из Команды",
"Auto-join": "Автодобавление",
"Remove_Team_Room_Warning": "Хотите ли вы удалить этот канал из Команды? Канал будет перемещен обратно в рабочее пространство",
"Confirmation": "Подтверждение",
"invalid-room": "Такого канала не существует",
"You_are_leaving_the_team": "Вы покидаете Команду '{{team}}'",
"Leave_Team": "Покинуть команду",
"Select_Team": "Выберите Команду",
"Select_Team_Channels": "Выберите каналы Команды, которые вы хотите покинуть.",
"Cannot_leave": "Невозможно выйти",
"Cannot_remove": "Невозможно удалить",
"Cannot_delete": "Невозможно удалить",
"Last_owner_team_room": "Вы последний владелец этого чата. Как только вы покинете Команду, чат будет храниться внутри нее, но вы будете управлять ею снаружи.",
"last-owner-can-not-be-removed": "Последний владелец не может быть удален",
"Remove_User_Teams": "Выберите каналы, из которых вы хотите удалить пользователя.",
"Delete_Team": "Удалить Команду",
"Select_channels_to_delete": "Это нельзя отменить. После удаления Команды все содержимое чата и конфигурация будут удалены \n\nВыберите каналы, которые вы хотите удалить. Те, которые вы решите оставить, будут доступны в вашем рабочем пространстве. Обратите внимание, что публичные каналы по-прежнему будут открытыми и видимыми для всех.",
"You_are_deleting_the_team": "Вы удаляете эту Команду.",
"Removing_user_from_this_team": "Вы удаляете {{user}} из этой Команды",
"Remove_User_Team_Channels": "Выберите каналы, из которых вы хотите удалить пользователя.",
"Remove_Member": "Удалить участника",
"leaving_team": "выход из Команды",
"removing_team": "удаление из Команды",
"moving_channel_to_team": "перемещение канала в Команду",
"deleting_team": "удаление Команды",
"member-does-not-exist": "Участник не существует",
"Convert": "Конвертировать",
"Convert_to_Team": "Конвертировать в команду",
"Convert_to_Team_Warning": "Это нельзя отменить. После преобразования канала в Команду, вы не сможете преобразовать его обратно в канал.",
"Move_to_Team": "Перенести в команду",
"Move_Channel_Paragraph": "Перемещение канала внутрь Команды означает, что этот канал будет добавлен в контекст Команды, однако все участники канала, которые не являются членами соответствующей Команды, по-прежнему будут иметь доступ к этому каналу, но не будут добавлены как участники Команды \n\nВсе управление каналом по-прежнему будет осуществляться владельцами этого канала.\n\nЧлены Команды и даже владельцы Команды, если они не являются членами этого канала, не могут иметь доступ к содержимому канала \n\nОбратите внимание, что владелец Команды сможет удалять участников с канала.",
"Move_to_Team_Warning": "После прочтения предыдущих инструкций об этом поведении, вы все еще хотите переместить этот канал в выбранную Команду?",
"Load_More": "Загрузить еще",
"Load_Newer": "Загрузить более позднее",
"Load_Older": "Загрузить более раннее"
}

View File

@ -290,6 +290,7 @@
"last_message": "son ileti",
"Leave_channel": "Kanaldan ayrıl",
"leaving_room": "odadan ayrılıyor",
"Leave": "Odadan ayrıl",
"leave": "ayrıl",
"Legal": "Yasal",
"Light": "Açık",
@ -440,7 +441,6 @@
"Room_changed_announcement": "Oda duyurusu, {{userBy}} tarafından {{announcement}} olarak değiştirildi",
"Room_changed_avatar": "Oda profil fotoğrafı {{userBy}} tarafından değiştirildi",
"Room_changed_description": "Oda açıklaması, {{userBy}} tarafından {{description}} olarak değiştirildi",
"Room_changed_privacy": "Oda açıklaması, {{userBy}} tarafından {{description}} olarak değiştirildi",
"Room_changed_topic": "Oda konusu, {{userBy}} tarafından {{topic}} olarak değiştirildi",
"Room_Files": "Oda Dosyaları",
"Room_Info_Edit": "Oda Bilgilerini Düzenle",
@ -565,7 +565,6 @@
"Username": "Kullanıcı adı",
"Username_or_email": "Kullanıcı adı ya da e-posta",
"Uses_server_configuration": "Sunucu yapılandırmasını kullanır",
"Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture": "Genellikle tartışma, \"Nasıl resim yüklerim?\" gibi bir soruyla başlar.",
"Validating": "Doğrulanıyor",
"Registration_Succeeded": "Kayıt Başarılı!",
"Verify": "Onayla",
@ -600,7 +599,6 @@
"You_need_to_access_at_least_one_RocketChat_server_to_share_something": "Bir şeyler paylaşmak için Rocket.Chat sunucusuna erişmeniz gerekir.",
"You_need_to_verifiy_your_email_address_to_get_notications": "Bildirim almak için e-posta adresinizi doğrulamanız gerekiyor",
"Your_certificate": "Sertifikanız",
"Your_message": "İletiınız",
"Your_invite_link_will_expire_after__usesLeft__uses": "Davet bağlantınızın geçerliliği {{usesLeft}} kullanımdan sonra sona erecek.",
"Your_invite_link_will_expire_on__date__or_after__usesLeft__uses": "Davet bağlantınızın geçerliliği {{date}} tarihinde veya {{usesLeft}} kullanımdan sonra sona erecek.",
"Your_invite_link_will_expire_on__date__": "Davet bağlantınızın geçerlilik süresi {{date}} tarihinde sona erecek.",
@ -683,28 +681,23 @@
"No_threads_following": "Herhangi bir konuyu takip etmiyorsunuz",
"No_threads_unread": "Okunmamış konu yok",
"Messagebox_Send_to_channel": "Kanala gönder",
"Set_as_leader": "Lider olarak ayarla",
"Set_as_moderator": "Moderatör olarak ayarla",
"Set_as_owner": "Sahip olarak ayarla",
"Remove_as_leader": "Lider olarak kaldır",
"Remove_as_moderator": "Moderatör olarak kaldır",
"Remove_as_owner": "Sahip olarak kaldır",
"Remove_from_room": "Odadan çıkar",
"Ignore": "Yok say",
"Unignore": "Yok sayma",
"User_has_been_ignored": "Kullanıcı yok sayıldı.",
"User_has_been_unignored": "Kullanıcı artık yok sayılmıyor.",
"User_has_been_removed_from_s": "Kullanıcı {{s}} alanından kaldırıldı.",
"User__username__is_now_a_leader_of__room_name_": "{{Username}} kullanıcısı artık {{room_name}} lideridir.",
"User__username__is_now_a_moderator_of__room_name_": "{{Username}} kullanıcısı artık bir {{room_name}} moderatörüdür.",
"User__username__is_now_a_owner_of__room_name_": "{{Username}} kullanıcısı artık {{room_name}} adlı odanın sahibidir.",
"User__username__removed_from__room_name__leaders": "{{Username}} adlı kullanıcı, {{room_name}} liderlerinden kaldırıldı.",
"User__username__removed_from__room_name__moderators": "{{Username}} adlı kullanıcı, {{room_name}} moderatörlerinden kaldırıldı.",
"User__username__removed_from__room_name__owners": "{{Username}} adlı kullanıcı, {{room_name}} sahiplerinden kaldırıldı.",
"User__username__is_now_a_leader_of__room_name_": "{{username}} kullanıcısı artık {{room_name}} lideridir.",
"User__username__is_now_a_moderator_of__room_name_": "{{username}} kullanıcısı artık bir {{room_name}} moderatörüdür.",
"User__username__is_now_a_owner_of__room_name_": "{{username}} kullanıcısı artık {{room_name}} adlı odanın sahibidir.",
"User__username__removed_from__room_name__leaders": "{{username}} adlı kullanıcı, {{room_name}} liderlerinden kaldırıldı.",
"User__username__removed_from__room_name__moderators": "{{username}} adlı kullanıcı, {{room_name}} moderatörlerinden kaldırıldı.",
"User__username__removed_from__room_name__owners": "{{username}} adlı kullanıcı, {{room_name}} sahiplerinden kaldırıldı.",
"The_user_will_be_removed_from_s": "Kullanıcı, {{s}} alanından kaldırılacak!",
"Yes_remove_user": "Evet, kullanıcıyı kaldır!",
"Direct_message": "Özel ileti",
"Message_Ignored": "İleti yok sayıldı. Görüntülemek için dokunun.",
"Enter_workspace_URL": "Çalışma alanı URL'nizi girin",
"Workspace_URL_Example": "Örn. sirketiniz.rocket.chat"
"Workspace_URL_Example": "Örn. sirketiniz.rocket.chat",
"invalid-room": "Geçersiz oda"
}

View File

@ -33,7 +33,7 @@
"error-invalid-date": "无效的日期",
"error-invalid-description": "无效的描述",
"error-invalid-domain": "无效的域名",
"error-invalid-email": "无效的电子邮件{{emai}}",
"error-invalid-email": "无效的电子邮件{{email}}",
"error-invalid-email-address": "无效的邮件地址",
"error-invalid-file-height": "无效的文件长度",
"error-invalid-file-type": "无效的文件类型",
@ -278,11 +278,11 @@
"is_typing": "正在输入",
"Invalid_or_expired_invite_token": "无效或到期的邀请 token",
"Invalid_server_version": "此 App 版本已不支援您正在连线之服务器版本。当前版本: {{currentVersion}}.\\n\\n最低版本要求: {{minVersion}}",
"Join_your_workspace": "加入您的工作区",
"Invite_Link": "邀请链接",
"Invite_users": "邀请用戶",
"Join": "加入",
"Join_our_open_workspace": "加入开放工作区",
"Join_your_workspace": "加入您的工作区",
"Just_invited_people_can_access_this_channel": "仅有被邀请人能进入这个频道",
"Language": "语言",
"last_message": "最后一条信息",
@ -300,7 +300,7 @@
"Logging_out": "正在登出",
"Logout": "注销",
"Max_number_of_uses": "最大使用次数",
"Max_number_of_users_allowed_is_number": "允许使用者上限数量",
"Max_number_of_users_allowed_is_number": "允许使用者上限数量{{maxUsers}}",
"members": "成员",
"Members": "成员",
"Mentioned_Messages": "被提及的信息",
@ -444,7 +444,7 @@
"Room_Info_Edit": "聊天室信息编辑",
"Room_Info": "聊天室信息",
"Room_Members": "聊天室成员",
"Room_name_changed": "{{userBy}} 将聊天室名称改为:{{{name}}",
"Room_name_changed": "{{userBy}} 将聊天室名称改为:{{name}}",
"SAVE": "保存",
"Save_Changes": "保存更改",
"Save": "保存",

View File

@ -278,16 +278,17 @@
"is_typing": "正在輸入",
"Invalid_or_expired_invite_token": "無效或到期的邀請 token",
"Invalid_server_version": "此 App 版本已不支援您正在連線之伺服器版本。當前版本: {{currentVersion}}.\\n\\n最低版本要求: {{minVersion}}",
"Join_your_workspace": "加入您的工作區",
"Invite_Link": "邀請連結",
"Invite_users": "邀請使用者",
"Join": "加入",
"Join_our_open_workspace": "加入開放工作區",
"Join_your_workspace": "加入您的工作區",
"Just_invited_people_can_access_this_channel": "僅有受邀者能存取此頻道",
"Language": "語言",
"last_message": "最後一則訊息",
"Leave_channel": "離開頻道",
"leaving_room": "離開聊天室",
"Leave": "離開",
"leave": "離開",
"Legal": "合法",
"Light": "淺色",
@ -300,7 +301,7 @@
"Logging_out": "正在登出",
"Logout": "登出",
"Max_number_of_uses": "最大使用次數",
"Max_number_of_users_allowed_is_number": "允許使用者上限數量",
"Max_number_of_users_allowed_is_number": "允許使用者上限數量 {{maxUsers}}",
"members": "成員",
"Members": "成員",
"Mentioned_Messages": "被提及的訊息",
@ -444,7 +445,7 @@
"Room_Info_Edit": "修改聊天室資訊",
"Room_Info": "聊天室資訊",
"Room_Members": "聊天室成員",
"Room_name_changed": "{{userBy}} 將聊天室名稱改為:{{{name}}",
"Room_name_changed": "{{userBy}} 將聊天室名稱改為:{{name}}",
"SAVE": "儲存",
"Save_Changes": "儲存更改",
"Save": "儲存",
@ -678,5 +679,7 @@
"No_threads": "當前沒有討論串",
"No_threads_following": "當前沒有正在追蹤的討論",
"No_threads_unread": "當前沒有未讀的討論",
"Messagebox_Send_to_channel": "發送至頻道"
"Messagebox_Send_to_channel": "發送至頻道",
"Confirmation": "確認",
"invalid-room": "無效的房間"
}

View File

@ -5,8 +5,10 @@ import {
import { sanitizer } from '../utils';
export const TABLE_NAME = 'messages';
export default class Message extends Model {
static table = 'messages';
static table = TABLE_NAME;
static associations = {
subscriptions: { type: 'belongs_to', key: 'rid' }

View File

@ -4,8 +4,10 @@ import {
} from '@nozbe/watermelondb/decorators';
import { sanitizer } from '../utils';
export const TABLE_NAME = 'subscriptions';
export default class Subscription extends Model {
static table = 'subscriptions';
static table = TABLE_NAME;
static associations = {
messages: { type: 'has_many', foreignKey: 'rid' },

View File

@ -5,8 +5,10 @@ import {
import { sanitizer } from '../utils';
export const TABLE_NAME = 'threads';
export default class Thread extends Model {
static table = 'threads';
static table = TABLE_NAME;
static associations = {
subscriptions: { type: 'belongs_to', key: 'rid' }

View File

@ -5,8 +5,10 @@ import {
import { sanitizer } from '../utils';
export const TABLE_NAME = 'thread_messages';
export default class ThreadMessage extends Model {
static table = 'thread_messages';
static table = TABLE_NAME;
static associations = {
subscriptions: { type: 'belongs_to', key: 'subscription_id' }

View File

@ -59,7 +59,7 @@ export default appSchema({
{ name: 'e2e_key_id', type: 'string', isOptional: true },
{ name: 'avatar_etag', type: 'string', isOptional: true },
{ name: 'team_id', type: 'string', isIndexed: true },
{ name: 'team_main', type: 'boolean', isOptional: true }
{ name: 'team_main', type: 'boolean', isOptional: true } // Use `Q.notEq(true)` to get false or null
]
}),
tableSchema({

View File

@ -0,0 +1,15 @@
import database from '..';
import { TABLE_NAME } from '../model/Message';
const getCollection = db => db.get(TABLE_NAME);
export const getMessageById = async(messageId) => {
const db = database.active;
const messageCollection = getCollection(db);
try {
const result = await messageCollection.find(messageId);
return result;
} catch (error) {
return null;
}
};

View File

@ -0,0 +1,15 @@
import database from '..';
import { TABLE_NAME } from '../model/Subscription';
const getCollection = db => db.get(TABLE_NAME);
export const getSubscriptionByRoomId = async(rid) => {
const db = database.active;
const subCollection = getCollection(db);
try {
const result = await subCollection.find(rid);
return result;
} catch (error) {
return null;
}
};

View File

@ -0,0 +1,15 @@
import database from '..';
import { TABLE_NAME } from '../model/Thread';
const getCollection = db => db.get(TABLE_NAME);
export const getThreadById = async(tmid) => {
const db = database.active;
const threadCollection = getCollection(db);
try {
const result = await threadCollection.find(tmid);
return result;
} catch (error) {
return null;
}
};

View File

@ -0,0 +1,15 @@
import database from '..';
import { TABLE_NAME } from '../model/ThreadMessage';
const getCollection = db => db.get(TABLE_NAME);
export const getThreadMessageById = async(messageId) => {
const db = database.active;
const threadMessageCollection = getCollection(db);
try {
const result = await threadMessageCollection.find(messageId);
return result;
} catch (error) {
return null;
}
};

View File

@ -13,19 +13,25 @@ const PERMISSIONS = [
'add-user-to-any-c-room',
'add-user-to-any-p-room',
'add-user-to-joined-room',
'add-team-channel',
'archive-room',
'auto-translate',
'create-invite-links',
'create-team',
'delete-c',
'delete-message',
'delete-p',
'delete-team',
'edit-message',
'edit-room',
'edit-team-member',
'edit-team-channel',
'force-delete-message',
'mute-user',
'pin-message',
'post-readonly',
'remove-user',
'remove-team-channel',
'set-leader',
'set-moderator',
'set-owner',
@ -38,7 +44,9 @@ const PERMISSIONS = [
'view-privileged-setting',
'view-room-administration',
'view-statistics',
'view-user-administration'
'view-user-administration',
'view-all-teams',
'view-all-team-channels'
];
export async function setPermissions() {

View File

@ -0,0 +1,29 @@
import { getSubscriptionByRoomId } from '../database/services/Subscription';
import RocketChat from '../rocketchat';
const getRoomInfo = async(rid) => {
let result;
result = await getSubscriptionByRoomId(rid);
if (result) {
return {
rid,
name: result.name,
fname: result.fname,
t: result.t
};
}
result = await RocketChat.getRoomInfo(rid);
if (result?.success) {
return {
rid,
name: result.room.name,
fname: result.room.fname,
t: result.room.t
};
}
return null;
};
export default getRoomInfo;

View File

@ -0,0 +1,15 @@
import RocketChat from '../rocketchat';
const getSingleMessage = messageId => new Promise(async(resolve, reject) => {
try {
const result = await RocketChat.getSingleMessage(messageId);
if (result.success) {
return resolve(result.message);
}
return reject();
} catch (e) {
return reject();
}
});
export default getSingleMessage;

View File

@ -0,0 +1,49 @@
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import database from '../database';
import { getMessageById } from '../database/services/Message';
import { getThreadById } from '../database/services/Thread';
import log from '../../utils/log';
import getSingleMessage from './getSingleMessage';
import { Encryption } from '../encryption';
const buildThreadName = thread => thread.msg || thread?.attachments?.[0]?.title;
const getThreadName = async(rid, tmid, messageId) => {
let tmsg;
try {
const db = database.active;
const threadCollection = db.get('threads');
const messageRecord = await getMessageById(messageId);
const threadRecord = await getThreadById(tmid);
if (threadRecord) {
tmsg = buildThreadName(threadRecord);
await db.action(async() => {
await messageRecord?.update((m) => {
m.tmsg = tmsg;
});
});
} else {
let thread = await getSingleMessage(tmid);
thread = await Encryption.decryptMessage(thread);
tmsg = buildThreadName(thread);
await db.action(async() => {
await db.batch(
threadCollection?.prepareCreate((t) => {
t._raw = sanitizedRaw({ id: thread._id }, threadCollection.schema);
t.subscription.id = rid;
Object.assign(t, thread);
}),
messageRecord?.prepareUpdate((m) => {
m.tmsg = tmsg;
})
);
});
}
} catch (e) {
log(e);
}
return tmsg;
};
export default getThreadName;

View File

@ -1,8 +1,15 @@
import moment from 'moment';
import { MESSAGE_TYPE_LOAD_MORE } from '../../constants/messageTypeLoad';
import log from '../../utils/log';
import { getMessageById } from '../database/services/Message';
import updateMessages from './updateMessages';
import { generateLoadMoreId } from '../utils';
const COUNT = 50;
async function load({ rid: roomId, latest, t }) {
let params = { roomId, count: 50 };
let params = { roomId, count: COUNT };
if (latest) {
params = { ...params, latest: new Date(latest).toISOString() };
}
@ -24,9 +31,20 @@ export default function loadMessagesForRoom(args) {
return new Promise(async(resolve, reject) => {
try {
const data = await load.call(this, args);
if (data && data.length) {
await updateMessages({ rid: args.rid, update: data });
if (data?.length) {
const lastMessage = data[data.length - 1];
const lastMessageRecord = await getMessageById(lastMessage._id);
if (!lastMessageRecord && data.length === COUNT) {
const loadMoreItem = {
_id: generateLoadMoreId(lastMessage._id),
rid: lastMessage.rid,
ts: moment(lastMessage.ts).subtract(1, 'millisecond'),
t: MESSAGE_TYPE_LOAD_MORE,
msg: lastMessage.msg
};
data.push(loadMoreItem);
}
await updateMessages({ rid: args.rid, update: data, loaderItem: args.loaderItem });
return resolve(data);
} else {
return resolve([]);

View File

@ -0,0 +1,42 @@
import EJSON from 'ejson';
import moment from 'moment';
import orderBy from 'lodash/orderBy';
import log from '../../utils/log';
import updateMessages from './updateMessages';
import { getMessageById } from '../database/services/Message';
import { MESSAGE_TYPE_LOAD_NEXT_CHUNK } from '../../constants/messageTypeLoad';
import { generateLoadMoreId } from '../utils';
const COUNT = 50;
export default function loadNextMessages(args) {
return new Promise(async(resolve, reject) => {
try {
const data = await this.methodCallWrapper('loadNextMessages', args.rid, args.ts, COUNT);
let messages = EJSON.fromJSONValue(data?.messages);
messages = orderBy(messages, 'ts');
if (messages?.length) {
const lastMessage = messages[messages.length - 1];
const lastMessageRecord = await getMessageById(lastMessage._id);
if (!lastMessageRecord && messages.length === COUNT) {
const loadMoreItem = {
_id: generateLoadMoreId(lastMessage._id),
rid: lastMessage.rid,
tmid: args.tmid,
ts: moment(lastMessage.ts).add(1, 'millisecond'),
t: MESSAGE_TYPE_LOAD_NEXT_CHUNK
};
messages.push(loadMoreItem);
}
await updateMessages({ rid: args.rid, update: messages, loaderItem: args.loaderItem });
return resolve(messages);
} else {
return resolve([]);
}
} catch (e) {
log(e);
reject(e);
}
});
}

View File

@ -0,0 +1,65 @@
import EJSON from 'ejson';
import moment from 'moment';
import orderBy from 'lodash/orderBy';
import log from '../../utils/log';
import updateMessages from './updateMessages';
import { getMessageById } from '../database/services/Message';
import { MESSAGE_TYPE_LOAD_NEXT_CHUNK, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK } from '../../constants/messageTypeLoad';
import { generateLoadMoreId } from '../utils';
const COUNT = 50;
export default function loadSurroundingMessages({ messageId, rid }) {
return new Promise(async(resolve, reject) => {
try {
const data = await this.methodCallWrapper('loadSurroundingMessages', { _id: messageId, rid }, COUNT);
let messages = EJSON.fromJSONValue(data?.messages);
messages = orderBy(messages, 'ts');
const message = messages.find(m => m._id === messageId);
const { tmid } = message;
if (messages?.length) {
if (data?.moreBefore) {
const firstMessage = messages[0];
const firstMessageRecord = await getMessageById(firstMessage._id);
if (!firstMessageRecord) {
const loadMoreItem = {
_id: generateLoadMoreId(firstMessage._id),
rid: firstMessage.rid,
tmid,
ts: moment(firstMessage.ts).subtract(1, 'millisecond'),
t: MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK,
msg: firstMessage.msg
};
messages.unshift(loadMoreItem);
}
}
if (data?.moreAfter) {
const lastMessage = messages[messages.length - 1];
const lastMessageRecord = await getMessageById(lastMessage._id);
if (!lastMessageRecord) {
const loadMoreItem = {
_id: generateLoadMoreId(lastMessage._id),
rid: lastMessage.rid,
tmid,
ts: moment(lastMessage.ts).add(1, 'millisecond'),
t: MESSAGE_TYPE_LOAD_NEXT_CHUNK,
msg: lastMessage.msg
};
messages.push(loadMoreItem);
}
}
await updateMessages({ rid, update: messages });
return resolve(messages);
} else {
return resolve([]);
}
} catch (e) {
log(e);
reject(e);
}
});
}

View File

@ -1,5 +1,6 @@
import { Q } from '@nozbe/watermelondb';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import EJSON from 'ejson';
import buildMessage from './helpers/buildMessage';
import database from '../database';
@ -7,30 +8,27 @@ import log from '../../utils/log';
import protectedFunction from './helpers/protectedFunction';
import { Encryption } from '../encryption';
async function load({ tmid, offset }) {
async function load({ tmid }) {
try {
// RC 1.0
const result = await this.sdk.get('chat.getThreadMessages', {
tmid, count: 50, offset, sort: { ts: -1 }, query: { _hidden: { $ne: true } }
});
if (!result || !result.success) {
const result = await this.methodCallWrapper('getThreadMessages', { tmid });
if (!result) {
return [];
}
return result.messages;
return EJSON.fromJSONValue(result);
} catch (error) {
console.log(error);
return [];
}
}
export default function loadThreadMessages({ tmid, rid, offset = 0 }) {
export default function loadThreadMessages({ tmid, rid }) {
return new Promise(async(resolve, reject) => {
try {
let data = await load.call(this, { tmid, offset });
let data = await load.call(this, { tmid });
if (data && data.length) {
try {
data = data.map(m => buildMessage(m));
data = data.filter(m => m.tmid).map(m => buildMessage(m));
data = await Encryption.decryptMessages(data);
const db = database.active;
const threadMessagesCollection = db.get('thread_messages');

View File

@ -159,7 +159,7 @@ export default class RoomSubscription {
updateMessage = message => (
new Promise(async(resolve) => {
if (this.rid !== message.rid) {
return;
return resolve();
}
const db = database.active;

View File

@ -6,8 +6,12 @@ import log from '../../utils/log';
import database from '../database';
import protectedFunction from './helpers/protectedFunction';
import { Encryption } from '../encryption';
import { MESSAGE_TYPE_ANY_LOAD } from '../../constants/messageTypeLoad';
import { generateLoadMoreId } from '../utils';
export default function updateMessages({ rid, update = [], remove = [] }) {
export default function updateMessages({
rid, update = [], remove = [], loaderItem
}) {
try {
if (!((update && update.length) || (remove && remove.length))) {
return;
@ -30,7 +34,13 @@ export default function updateMessages({ rid, update = [], remove = [] }) {
const threadCollection = db.get('threads');
const threadMessagesCollection = db.get('thread_messages');
const allMessagesRecords = await msgCollection
.query(Q.where('rid', rid), Q.where('id', Q.oneOf(messagesIds)))
.query(
Q.where('rid', rid),
Q.or(
Q.where('id', Q.oneOf(messagesIds)),
Q.where('t', Q.oneOf(MESSAGE_TYPE_ANY_LOAD))
)
)
.fetch();
const allThreadsRecords = await threadCollection
.query(Q.where('rid', rid), Q.where('id', Q.oneOf(messagesIds)))
@ -55,6 +65,9 @@ export default function updateMessages({ rid, update = [], remove = [] }) {
let threadMessagesToCreate = allThreadMessages.filter(i1 => !allThreadMessagesRecords.find(i2 => i1._id === i2.id));
let threadMessagesToUpdate = allThreadMessagesRecords.filter(i1 => allThreadMessages.find(i2 => i1.id === i2._id));
// filter loaders to delete
let loadersToDelete = allMessagesRecords.filter(i1 => update.find(i2 => i1.id === generateLoadMoreId(i2._id)));
// Create
msgsToCreate = msgsToCreate.map(message => msgCollection.prepareCreate(protectedFunction((m) => {
m._raw = sanitizedRaw({ id: message._id }, msgCollection.schema);
@ -121,6 +134,12 @@ export default function updateMessages({ rid, update = [], remove = [] }) {
threadMessagesToDelete = threadMessagesToDelete.map(tm => tm.prepareDestroyPermanently());
}
// Delete loaders
loadersToDelete = loadersToDelete.map(m => m.prepareDestroyPermanently());
if (loaderItem) {
loadersToDelete.push(loaderItem.prepareDestroyPermanently());
}
const allRecords = [
...msgsToCreate,
...msgsToUpdate,
@ -130,7 +149,8 @@ export default function updateMessages({ rid, update = [], remove = [] }) {
...threadsToDelete,
...threadMessagesToCreate,
...threadMessagesToUpdate,
...threadMessagesToDelete
...threadMessagesToDelete,
...loadersToDelete
];
try {

View File

@ -1,4 +1,5 @@
import { InteractionManager } from 'react-native';
import EJSON from 'ejson';
import {
Rocketchat as RocketchatClient,
settings as RocketChatSettings
@ -41,6 +42,8 @@ import canOpenRoom from './methods/canOpenRoom';
import triggerBlockAction, { triggerSubmitView, triggerCancel } from './methods/actions';
import loadMessagesForRoom from './methods/loadMessagesForRoom';
import loadSurroundingMessages from './methods/loadSurroundingMessages';
import loadNextMessages from './methods/loadNextMessages';
import loadMissedMessages from './methods/loadMissedMessages';
import loadThreadMessages from './methods/loadThreadMessages';
@ -60,6 +63,7 @@ import UserPreferences from './userPreferences';
import { Encryption } from './encryption';
import EventEmitter from '../utils/events';
import { sanitizeLikeString } from './database/utils';
import { TEAM_TYPE } from '../definition/ITeam';
const TOKEN_KEY = 'reactnativemeteor_usertoken';
const CURRENT_SERVER = 'currentServer';
@ -94,10 +98,19 @@ const RocketChat = {
},
canOpenRoom,
createChannel({
name, users, type, readOnly, broadcast, encrypted
name, users, type, readOnly, broadcast, encrypted, teamId
}) {
// RC 0.51.0
return this.methodCallWrapper(type ? 'createPrivateGroup' : 'createChannel', name, users, readOnly, {}, { broadcast, encrypted });
const params = {
name,
members: users,
readOnly,
extraData: {
broadcast,
encrypted,
...(teamId && { teamId })
}
};
return this.post(type ? 'groups.create' : 'channels.create', params);
},
async getWebsocketInfo({ server }) {
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
@ -196,6 +209,10 @@ const RocketChat = {
clearTimeout(this.connectTimeout);
}
if (this.connectingListener) {
this.connectingListener.then(this.stopListener);
}
if (this.connectedListener) {
this.connectedListener.then(this.stopListener);
}
@ -243,7 +260,7 @@ const RocketChat = {
sdkConnect();
this.connectedListener = this.sdk.onStreamData('connecting', () => {
this.connectingListener = this.sdk.onStreamData('connecting', () => {
reduxStore.dispatch(connectRequest());
});
@ -610,6 +627,8 @@ const RocketChat = {
},
loadMissedMessages,
loadMessagesForRoom,
loadSurroundingMessages,
loadNextMessages,
loadThreadMessages,
sendMessage,
getRooms,
@ -643,7 +662,8 @@ const RocketChat = {
avatarETag: sub.avatarETag,
t: sub.t,
encrypted: sub.encrypted,
lastMessage: sub.lastMessage
lastMessage: sub.lastMessage,
...(sub.teamId && { teamId: sub.teamId })
}));
return data;
@ -728,7 +748,74 @@ const RocketChat = {
prid, pmid, t_name, reply, users, encrypted
});
},
createTeam({
name, users, type, readOnly, broadcast, encrypted
}) {
const params = {
name,
users,
type: type ? TEAM_TYPE.PRIVATE : TEAM_TYPE.PUBLIC,
room: {
readOnly,
extraData: {
broadcast,
encrypted
}
}
};
// RC 3.13.0
return this.post('teams.create', params);
},
addRoomsToTeam({ teamId, rooms }) {
// RC 3.13.0
return this.post('teams.addRooms', { teamId, rooms });
},
removeTeamRoom({ roomId, teamId }) {
// RC 3.13.0
return this.post('teams.removeRoom', { roomId, teamId });
},
leaveTeam({ teamName, rooms }) {
// RC 3.13.0
return this.post('teams.leave', { teamName, rooms });
},
removeTeamMember({
teamId, teamName, userId, rooms
}) {
// RC 3.13.0
return this.post('teams.removeMember', {
teamId, teamName, userId, rooms
});
},
updateTeamRoom({ roomId, isDefault }) {
// RC 3.13.0
return this.post('teams.updateRoom', { roomId, isDefault });
},
deleteTeam({ teamId, roomsToRemove }) {
// RC 3.13.0
return this.post('teams.delete', { teamId, roomsToRemove });
},
teamListRoomsOfUser({ teamId, userId }) {
// RC 3.13.0
return this.sdk.get('teams.listRoomsOfUser', { teamId, userId });
},
getTeamInfo({ teamId }) {
// RC 3.13.0
return this.sdk.get('teams.info', { teamId });
},
convertChannelToTeam({ rid, name, type }) {
const params = {
...(type === 'c'
? {
channelId: rid,
channelName: name
}
: {
roomId: rid,
roomName: name
})
};
return this.sdk.post(type === 'c' ? 'channels.convertToTeam' : 'groups.convertToTeam', params);
},
joinRoom(roomId, joinCode, type) {
// TODO: join code
// RC 0.48.0
@ -890,9 +977,15 @@ const RocketChat = {
methodCallWrapper(method, ...params) {
const { API_Use_REST_For_DDP_Calls } = reduxStore.getState().settings;
if (API_Use_REST_For_DDP_Calls) {
return this.post(`method.call/${ method }`, { message: JSON.stringify({ method, params }) });
return this.post(`method.call/${ method }`, { message: EJSON.stringify({ method, params }) });
}
return this.methodCall(method, ...params);
const parsedParams = params.map((param) => {
if (param instanceof Date) {
return { $date: new Date(param).getTime() };
}
return param;
});
return this.methodCall(method, ...parsedParams);
},
getUserRoles() {
@ -1136,7 +1229,7 @@ const RocketChat = {
methodCall(...args) {
return new Promise(async(resolve, reject) => {
try {
const result = await this.sdk.methodCall(...args, this.code || '');
const result = await this.sdk?.methodCall(...args, this.code || '');
return resolve(result);
} catch (e) {
if (e.error && (e.error === 'totp-required' || e.error === 'totp-invalid')) {

View File

@ -20,3 +20,5 @@ export const methods = {
};
export const compareServerVersion = (currentServerVersion, versionToCompare, func) => currentServerVersion && func(coerce(currentServerVersion), versionToCompare);
export const generateLoadMoreId = id => `load-more-${ id }`;

View File

@ -10,7 +10,7 @@ export const onNotification = (notification) => {
if (data) {
try {
const {
rid, name, sender, type, host, messageType
rid, name, sender, type, host, messageType, messageId
} = EJSON.parse(data.ejson);
const types = {
@ -24,6 +24,7 @@ export const onNotification = (notification) => {
const params = {
host,
rid,
messageId,
path: `${ types[type] }/${ roomName }`,
isCall: messageType === 'jitsi_call_started'
};

View File

@ -18,7 +18,7 @@ const DirectoryItemLabel = React.memo(({ text, theme }) => {
});
const DirectoryItem = ({
title, description, avatar, onPress, testID, style, rightLabel, type, rid, theme
title, description, avatar, onPress, testID, style, rightLabel, type, rid, theme, teamMain
}) => (
<Touch
onPress={onPress}
@ -36,7 +36,7 @@ const DirectoryItem = ({
/>
<View style={styles.directoryItemTextContainer}>
<View style={styles.directoryItemTextTitle}>
<RoomTypeIcon type={type} theme={theme} />
<RoomTypeIcon type={type} teamMain={teamMain} theme={theme} />
<Text style={[styles.directoryItemName, { color: themes[theme].titleText }]} numberOfLines={1}>{title}</Text>
</View>
{ description ? <Text style={[styles.directoryItemUsername, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>{description}</Text> : null }
@ -56,7 +56,8 @@ DirectoryItem.propTypes = {
style: PropTypes.any,
rightLabel: PropTypes.string,
rid: PropTypes.string,
theme: PropTypes.string
theme: PropTypes.string,
teamMain: PropTypes.bool
};
DirectoryItemLabel.propTypes = {

View File

@ -10,6 +10,8 @@ import LastMessage from './LastMessage';
import Title from './Title';
import UpdatedAt from './UpdatedAt';
import Touchable from './Touchable';
import Tag from './Tag';
import I18n from '../../i18n';
const RoomItem = ({
rid,
@ -42,13 +44,16 @@ const RoomItem = ({
testID,
swipeEnabled,
onPress,
onLongPress,
toggleFav,
toggleRead,
hideChannel,
teamMain
teamMain,
autoJoin
}) => (
<Touchable
onPress={onPress}
onLongPress={onLongPress}
width={width}
favorite={favorite}
toggleFav={toggleFav}
@ -88,6 +93,9 @@ const RoomItem = ({
hideUnreadStatus={hideUnreadStatus}
alert={alert}
/>
{
autoJoin ? <Tag testID='auto-join-tag' name={I18n.t('Auto-join')} /> : null
}
<UpdatedAt
date={date}
theme={theme}
@ -132,6 +140,9 @@ const RoomItem = ({
hideUnreadStatus={hideUnreadStatus}
alert={alert}
/>
{
autoJoin ? <Tag name={I18n.t('Auto-join')} /> : null
}
<UnreadBadge
unread={unread}
userMentions={userMentions}
@ -181,7 +192,9 @@ RoomItem.propTypes = {
toggleFav: PropTypes.func,
toggleRead: PropTypes.func,
onPress: PropTypes.func,
hideChannel: PropTypes.func
onLongPress: PropTypes.func,
hideChannel: PropTypes.func,
autoJoin: PropTypes.bool
};
RoomItem.defaultProps = {

View File

@ -0,0 +1,32 @@
import React from 'react';
import { Text, View } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../../constants/colors';
import { useTheme } from '../../theme';
import styles from './styles';
const Tag = React.memo(({ name, testID }) => {
const { theme } = useTheme();
return (
<View style={[styles.tagContainer, { backgroundColor: themes[theme].borderColor }]}>
<Text
style={[
styles.tagText, { color: themes[theme].infoText }
]}
numberOfLines={1}
testID={testID}
>
{name}
</Text>
</View>
);
});
Tag.propTypes = {
name: PropTypes.string,
testID: PropTypes.string
};
export default Tag;

View File

@ -1,7 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Animated } from 'react-native';
import { PanGestureHandler, State } from 'react-native-gesture-handler';
import {
LongPressGestureHandler, PanGestureHandler, State
} from 'react-native-gesture-handler';
import Touch from '../../utils/touch';
import {
@ -17,6 +19,7 @@ class Touchable extends React.Component {
static propTypes = {
type: PropTypes.string.isRequired,
onPress: PropTypes.func,
onLongPress: PropTypes.func,
testID: PropTypes.string,
width: PropTypes.number,
favorite: PropTypes.bool,
@ -59,6 +62,12 @@ class Touchable extends React.Component {
}
}
onLongPressHandlerStateChange = ({ nativeEvent }) => {
if (nativeEvent.state === State.ACTIVE) {
this.onLongPress();
}
}
_handleRelease = (nativeEvent) => {
const { translationX } = nativeEvent;
@ -203,54 +212,70 @@ class Touchable extends React.Component {
}
};
onLongPress = () => {
const { rowState } = this.state;
const { onLongPress } = this.props;
if (rowState !== 0) {
this.close();
return;
}
if (onLongPress) {
onLongPress();
}
};
render() {
const {
testID, isRead, width, favorite, children, theme, isFocused, swipeEnabled
} = this.props;
return (
<PanGestureHandler
minDeltaX={20}
onGestureEvent={this._onGestureEvent}
onHandlerStateChange={this._onHandlerStateChange}
enabled={swipeEnabled}
>
<LongPressGestureHandler onHandlerStateChange={this.onLongPressHandlerStateChange}>
<Animated.View>
<LeftActions
transX={this.transXReverse}
isRead={isRead}
width={width}
onToggleReadPress={this.onToggleReadPress}
theme={theme}
/>
<RightActions
transX={this.transXReverse}
favorite={favorite}
width={width}
toggleFav={this.toggleFav}
onHidePress={this.onHidePress}
theme={theme}
/>
<Animated.View
style={{
transform: [{ translateX: this.transX }]
}}
<PanGestureHandler
minDeltaX={20}
onGestureEvent={this._onGestureEvent}
onHandlerStateChange={this._onHandlerStateChange}
enabled={swipeEnabled}
>
<Touch
onPress={this.onPress}
theme={theme}
testID={testID}
style={{
backgroundColor: isFocused ? themes[theme].chatComponentBackground : themes[theme].backgroundColor
}}
>
{children}
</Touch>
</Animated.View>
</Animated.View>
<Animated.View>
<LeftActions
transX={this.transXReverse}
isRead={isRead}
width={width}
onToggleReadPress={this.onToggleReadPress}
theme={theme}
/>
<RightActions
transX={this.transXReverse}
favorite={favorite}
width={width}
toggleFav={this.toggleFav}
onHidePress={this.onHidePress}
theme={theme}
/>
<Animated.View
style={{
transform: [{ translateX: this.transX }]
}}
>
<Touch
onPress={this.onPress}
theme={theme}
testID={testID}
style={{
backgroundColor: isFocused ? themes[theme].chatComponentBackground : themes[theme].backgroundColor
}}
>
{children}
</Touch>
</Animated.View>
</Animated.View>
</PanGestureHandler>
</PanGestureHandler>
</Animated.View>
</LongPressGestureHandler>
);
}
}

View File

@ -16,7 +16,8 @@ const attrs = [
'theme',
'isFocused',
'forceUpdate',
'showLastMessage'
'showLastMessage',
'autoJoin'
];
class RoomItemContainer extends React.Component {
@ -25,6 +26,7 @@ class RoomItemContainer extends React.Component {
showLastMessage: PropTypes.bool,
id: PropTypes.string,
onPress: PropTypes.func,
onLongPress: PropTypes.func,
username: PropTypes.string,
avatarSize: PropTypes.number,
width: PropTypes.number,
@ -41,7 +43,8 @@ class RoomItemContainer extends React.Component {
getRoomAvatar: PropTypes.func,
getIsGroupChat: PropTypes.func,
getIsRead: PropTypes.func,
swipeEnabled: PropTypes.bool
swipeEnabled: PropTypes.bool,
autoJoin: PropTypes.bool
};
static defaultProps = {
@ -112,6 +115,13 @@ class RoomItemContainer extends React.Component {
return onPress(item);
}
onLongPress = () => {
const { item, onLongPress } = this.props;
if (onLongPress) {
return onLongPress(item);
}
}
render() {
const {
item,
@ -129,7 +139,8 @@ class RoomItemContainer extends React.Component {
showLastMessage,
username,
useRealName,
swipeEnabled
swipeEnabled,
autoJoin
} = this.props;
const name = getRoomTitle(item);
const testID = `rooms-list-view-item-${ name }`;
@ -160,6 +171,7 @@ class RoomItemContainer extends React.Component {
isGroupChat={this.isGroupChat}
isRead={isRead}
onPress={this.onPress}
onLongPress={this.onLongPress}
date={date}
accessibilityLabel={accessibilityLabel}
width={width}
@ -189,6 +201,7 @@ class RoomItemContainer extends React.Component {
tunreadGroup={item.tunreadGroup}
swipeEnabled={swipeEnabled}
teamMain={item.teamMain}
autoJoin={autoJoin}
/>
);
}

View File

@ -96,5 +96,16 @@ export default StyleSheet.create({
height: '100%',
alignItems: 'center',
justifyContent: 'center'
},
tagContainer: {
alignSelf: 'center',
alignItems: 'center',
borderRadius: 4,
marginHorizontal: 4
},
tagText: {
fontSize: 13,
paddingHorizontal: 4,
...sharedStyles.textSemibold
}
});

View File

@ -22,7 +22,7 @@ export default function(state = initialState, action) {
case ROOM.LEAVE:
return {
...state,
rid: action.rid,
rid: action.room.rid,
isDeleting: true
};
case ROOM.DELETE:

View File

@ -21,6 +21,10 @@ const createGroupChat = function createGroupChat() {
return RocketChat.createGroupChat();
};
const createTeam = function createTeam(data) {
return RocketChat.createTeam(data);
};
const handleRequest = function* handleRequest({ data }) {
try {
const auth = yield select(state => state.login.isAuthenticated);
@ -29,11 +33,33 @@ const handleRequest = function* handleRequest({ data }) {
}
let sub;
if (data.group) {
if (data.isTeam) {
const {
type,
readOnly,
broadcast,
encrypted
} = data;
logEvent(events.CT_CREATE, {
type,
readOnly,
broadcast,
encrypted
});
const result = yield call(createTeam, data);
sub = {
rid: result?.team?.roomId,
...result.team,
t: result.team.type ? 'p' : 'c'
};
} else if (data.group) {
logEvent(events.SELECTED_USERS_CREATE_GROUP);
const result = yield call(createGroupChat);
if (result.success) {
({ room: sub } = result);
sub = {
rid: result.room?._id,
...result.room
};
}
} else {
const {
@ -48,9 +74,13 @@ const handleRequest = function* handleRequest({ data }) {
broadcast,
encrypted
});
sub = yield call(createChannel, data);
const result = yield call(createChannel, data);
sub = {
rid: result?.channel?._id || result?.group?._id,
...result?.channel,
...result?.group
};
}
try {
const db = database.active;
const subCollection = db.get('subscriptions');
@ -63,11 +93,10 @@ const handleRequest = function* handleRequest({ data }) {
} catch {
// do nothing
}
yield put(createChannelSuccess(sub));
} catch (err) {
logEvent(events[data.group ? 'SELECTED_USERS_CREATE_GROUP_F' : 'CR_CREATE_F']);
yield put(createChannelFailure(err));
yield put(createChannelFailure(err, data.isTeam));
}
};
@ -79,10 +108,10 @@ const handleSuccess = function* handleSuccess({ data }) {
goRoom({ item: data, isMasterDetail });
};
const handleFailure = function handleFailure({ err }) {
const handleFailure = function handleFailure({ err, isTeam }) {
setTimeout(() => {
const msg = err.reason || I18n.t('There_was_an_error_while_action', { action: I18n.t('creating_channel') });
showErrorAlert(msg);
const msg = err.data.errorType ? I18n.t(err.data.errorType, { room_name: err.data.details.channel_name }) : err.reason || I18n.t('There_was_an_error_while_action', { action: isTeam ? I18n.t('creating_team') : I18n.t('creating_channel') });
showErrorAlert(msg, isTeam ? I18n.t('Create_Team') : I18n.t('Create_Channel'));
}, 300);
};

View File

@ -16,6 +16,7 @@ import {
import { localAuthenticate } from '../utils/localAuthentication';
import { goRoom } from '../utils/goRoom';
import { loginRequest } from '../actions/login';
import log from '../utils/log';
const roomTypes = {
channel: 'c', direct: 'd', group: 'p', channels: 'l'
@ -60,18 +61,19 @@ const navigate = function* navigate({ params }) {
const isMasterDetail = yield select(state => state.app.isMasterDetail);
const focusedRooms = yield select(state => state.room.rooms);
const jumpToMessageId = params.messageId;
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 });
yield goRoom({ item, isMasterDetail, jumpToMessageId });
} else {
popToRoot({ isMasterDetail });
yield goRoom({ item, isMasterDetail });
yield goRoom({ item, isMasterDetail, jumpToMessageId });
}
} else {
popToRoot({ isMasterDetail });
yield goRoom({ item, isMasterDetail });
yield goRoom({ item, isMasterDetail, jumpToMessageId });
}
if (params.isCall) {
@ -92,6 +94,15 @@ const fallbackNavigation = function* fallbackNavigation() {
yield put(appInit());
};
const handleOAuth = function* handleOAuth({ params }) {
const { credentialToken, credentialSecret } = params;
try {
yield RocketChat.loginOAuthOrSso({ oauth: { credentialToken, credentialSecret } });
} catch (e) {
log(e);
}
};
const handleOpen = function* handleOpen({ params }) {
const serversDB = database.servers;
const serversCollection = serversDB.get('servers');
@ -107,6 +118,11 @@ const handleOpen = function* handleOpen({ params }) {
});
}
if (params.type === 'oauth') {
yield handleOAuth({ params });
return;
}
// If there's no host on the deep link params and the app is opened, just call appInit()
if (!host) {
yield fallbackNavigation();

View File

@ -4,6 +4,7 @@ import {
takeLatest, take, select, delay, race, put
} from 'redux-saga/effects';
import EventEmitter from '../utils/events';
import Navigation from '../lib/Navigation';
import * as types from '../actions/actionsTypes';
import { removedRoom } from '../actions/room';
@ -11,6 +12,7 @@ import RocketChat from '../lib/rocketchat';
import log, { logEvent, events } from '../utils/log';
import I18n from '../i18n';
import { showErrorAlert } from '../utils/info';
import { LISTENER } from '../containers/Toast';
const watchUserTyping = function* watchUserTyping({ rid, status }) {
const auth = yield select(state => state.login.isAuthenticated);
@ -30,13 +32,18 @@ const watchUserTyping = function* watchUserTyping({ rid, status }) {
}
};
const handleRemovedRoom = function* handleRemovedRoom() {
const handleRemovedRoom = function* handleRemovedRoom(roomType) {
const isMasterDetail = yield select(state => state.app.isMasterDetail);
if (isMasterDetail) {
yield Navigation.navigate('DrawerNavigator');
} else {
yield Navigation.navigate('RoomsListView');
}
if (roomType === 'team') {
EventEmitter.emit(LISTENER, { message: I18n.t('Left_The_Team_Successfully') });
}
// types.ROOM.REMOVE is triggered by `subscriptions-changed` with `removed` arg
const { timeout } = yield race({
deleteFinished: take(types.ROOM.REMOVED),
@ -47,17 +54,26 @@ const handleRemovedRoom = function* handleRemovedRoom() {
}
};
const handleLeaveRoom = function* handleLeaveRoom({ rid, t }) {
const handleLeaveRoom = function* handleLeaveRoom({ room, roomType, selected }) {
logEvent(events.RA_LEAVE);
try {
const result = yield RocketChat.leaveRoom(rid, t);
if (result.success) {
yield handleRemovedRoom();
let result = {};
if (roomType === 'channel') {
result = yield RocketChat.leaveRoom(room.rid, room.t);
} else if (roomType === 'team') {
result = yield RocketChat.leaveTeam({ teamName: room.name, ...(selected && { rooms: selected }) });
}
if (result?.success) {
yield handleRemovedRoom(roomType);
}
} catch (e) {
logEvent(events.RA_LEAVE_F);
if (e.data && e.data.errorType === 'error-you-are-last-owner') {
Alert.alert(I18n.t('Oops'), I18n.t(e.data.errorType));
} else if (e?.data?.error === 'last-owner-can-not-be-removed') {
Alert.alert(I18n.t('Oops'), I18n.t(e.data.error));
} else {
Alert.alert(I18n.t('Oops'), I18n.t('There_was_an_error_while_action', { action: I18n.t('leaving_room') }));
}

View File

@ -71,6 +71,9 @@ import ShareView from '../views/ShareView';
import CreateDiscussionView from '../views/CreateDiscussionView';
import QueueListView from '../ee/omnichannel/views/QueueListView';
import AddChannelTeamView from '../views/AddChannelTeamView';
import AddExistingChannelView from '../views/AddExistingChannelView';
import SelectListView from '../views/SelectListView';
// ChatsStackNavigator
const ChatsStack = createStackNavigator();
@ -91,6 +94,11 @@ const ChatsStackNavigator = () => {
component={RoomActionsView}
options={RoomActionsView.navigationOptions}
/>
<ChatsStack.Screen
name='SelectListView'
component={SelectListView}
options={SelectListView.navigationOptions}
/>
<ChatsStack.Screen
name='RoomInfoView'
component={RoomInfoView}
@ -174,6 +182,21 @@ const ChatsStackNavigator = () => {
component={TeamChannelsView}
options={TeamChannelsView.navigationOptions}
/>
<ChatsStack.Screen
name='CreateChannelView'
component={CreateChannelView}
options={CreateChannelView.navigationOptions}
/>
<ChatsStack.Screen
name='AddChannelTeamView'
component={AddChannelTeamView}
options={AddChannelTeamView.navigationOptions}
/>
<ChatsStack.Screen
name='AddExistingChannelView'
component={AddExistingChannelView}
options={AddExistingChannelView.navigationOptions}
/>
<ChatsStack.Screen
name='MarkdownTableView'
component={MarkdownTableView}

View File

@ -61,6 +61,9 @@ import { setKeyCommands, deleteKeyCommands } from '../../commands';
import ShareView from '../../views/ShareView';
import QueueListView from '../../ee/omnichannel/views/QueueListView';
import AddChannelTeamView from '../../views/AddChannelTeamView';
import AddExistingChannelView from '../../views/AddExistingChannelView';
import SelectListView from '../../views/SelectListView';
// ChatsStackNavigator
const ChatsStack = createStackNavigator();
@ -117,6 +120,11 @@ const ModalStackNavigator = React.memo(({ navigation }) => {
component={RoomInfoView}
options={RoomInfoView.navigationOptions}
/>
<ModalStack.Screen
name='SelectListView'
component={SelectListView}
options={SelectListView.navigationOptions}
/>
<ModalStack.Screen
name='RoomInfoEditView'
component={RoomInfoEditView}
@ -141,6 +149,16 @@ const ModalStackNavigator = React.memo(({ navigation }) => {
component={InviteUsersView}
options={InviteUsersView.navigationOptions}
/>
<ModalStack.Screen
name='AddChannelTeamView'
component={AddChannelTeamView}
options={AddChannelTeamView.navigationOptions}
/>
<ModalStack.Screen
name='AddExistingChannelView'
component={AddExistingChannelView}
options={AddExistingChannelView.navigationOptions}
/>
<ModalStack.Screen
name='InviteUsersEditView'
component={InviteUsersEditView}

View File

@ -14,7 +14,6 @@ const navigate = ({ item, isMasterDetail, ...props }) => {
t: item.t,
prid: item.prid,
room: item,
search: item.search,
visitor: item.visitor,
roomUserId: RocketChat.getUidDirectMessage(item),
...props

View File

@ -88,6 +88,7 @@ export default {
// NEW MESSAGE VIEW
NEW_MSG_CREATE_CHANNEL: 'new_msg_create_channel',
NEW_MSG_CREATE_TEAM: 'new_msg_create_team',
NEW_MSG_CREATE_GROUP_CHAT: 'new_msg_create_group_chat',
NEW_MSG_CREATE_DISCUSSION: 'new_msg_create_discussion',
NEW_MSG_CHAT_WITH_USER: 'new_msg_chat_with_user',
@ -98,14 +99,22 @@ export default {
SELECTED_USERS_CREATE_GROUP: 'selected_users_create_group',
SELECTED_USERS_CREATE_GROUP_F: 'selected_users_create_group_f',
// ADD EXISTING CHANNEL VIEW
AEC_ADD_CHANNEL: 'aec_add_channel',
AEC_REMOVE_CHANNEL: 'aec_remove_channel',
// CREATE CHANNEL VIEW
CR_CREATE: 'cr_create',
CT_CREATE: 'ct_create',
CR_CREATE_F: 'cr_create_f',
CT_CREATE_F: 'ct_create_f',
CR_TOGGLE_TYPE: 'cr_toggle_type',
CR_TOGGLE_READ_ONLY: 'cr_toggle_read_only',
CR_TOGGLE_BROADCAST: 'cr_toggle_broadcast',
CR_TOGGLE_ENCRYPTED: 'cr_toggle_encrypted',
CR_REMOVE_USER: 'cr_remove_user',
CT_ADD_ROOM_TO_TEAM: 'ct_add_room_to_team',
CT_ADD_ROOM_TO_TEAM_F: 'ct_add_room_to_team_f',
// CREATE DISCUSSION VIEW
CD_CREATE: 'cd_create',
@ -246,6 +255,13 @@ export default {
RA_TOGGLE_BLOCK_USER_F: 'ra_toggle_block_user_f',
RA_TOGGLE_ENCRYPTED: 'ra_toggle_encrypted',
RA_TOGGLE_ENCRYPTED_F: 'ra_toggle_encrypted_f',
RA_LEAVE_TEAM: 'ra_leave_team',
RA_LEAVE_TEAM_F: 'ra_leave_team_f',
RA_CONVERT_TO_TEAM: 'ra_convert_to_team',
RA_CONVERT_TO_TEAM_F: 'ra_convert_to_team_f',
RA_MOVE_TO_TEAM: 'ra_move_to_team',
RA_MOVE_TO_TEAM_F: 'ra_move_to_team_f',
RA_SEARCH_TEAM: 'ra_search_team',
// ROOM INFO VIEW
RI_GO_RI_EDIT: 'ri_go_ri_edit',
@ -265,6 +281,8 @@ export default {
RI_EDIT_TOGGLE_ARCHIVE_F: 'ri_edit_toggle_archive_f',
RI_EDIT_DELETE: 'ri_edit_delete',
RI_EDIT_DELETE_F: 'ri_edit_delete_f',
RI_EDIT_DELETE_TEAM: 'ri_edit_delete_team',
RI_EDIT_DELETE_TEAM_F: 'ri_edit_delete_team_f',
// JITSI MEET VIEW
JM_CONFERENCE_JOIN: 'jm_conference_join',
@ -318,5 +336,9 @@ export default {
TC_SEARCH: 'tc_search',
TC_CANCEL_SEARCH: 'tc_cancel_search',
TC_GO_ACTIONS: 'tc_go_actions',
TC_GO_ROOM: 'tc_go_room'
TC_GO_ROOM: 'tc_go_room',
TC_DELETE_ROOM: 'tc_delete_room',
TC_DELETE_ROOM_F: 'tc_delete_room_f',
TC_TOGGLE_AUTOJOIN: 'tc_toggle_autojoin',
TC_TOGGLE_AUTOJOIN_F: 'tc_toggle_autojoin_f'
};

View File

@ -27,10 +27,10 @@ export const MessageTypeValues = [
value: 'rm',
text: 'Message_HideType_rm'
}, {
value: 'subscription_role_added',
value: 'subscription-role-added',
text: 'Message_HideType_subscription_role_added'
}, {
value: 'subscription_role_removed',
value: 'subscription-role-removed',
text: 'Message_HideType_subscription_role_removed'
}, {
value: 'room_archived',

View File

@ -45,3 +45,5 @@ export const getBadgeColor = ({ subscription, messageId, theme }) => {
};
export const makeThreadName = messageRecord => messageRecord.msg || messageRecord?.attachments[0]?.title;
export const isTeamRoom = ({ teamId, joined }) => teamId && joined;

View File

@ -0,0 +1,75 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import * as List from '../containers/List';
import StatusBar from '../containers/StatusBar';
import { useTheme } from '../theme';
import * as HeaderButton from '../containers/HeaderButton';
import SafeAreaView from '../containers/SafeAreaView';
import I18n from '../i18n';
const setHeader = (navigation, isMasterDetail) => {
const options = {
headerTitle: I18n.t('Add_Channel_to_Team')
};
if (isMasterDetail) {
options.headerLeft = () => <HeaderButton.CloseModal navigation={navigation} />;
}
navigation.setOptions(options);
};
const AddChannelTeamView = ({
navigation, route, isMasterDetail
}) => {
const { teamId, teamChannels } = route.params;
const { theme } = useTheme();
useEffect(() => {
setHeader(navigation, isMasterDetail);
}, []);
return (
<SafeAreaView testID='add-channel-team-view'>
<StatusBar />
<List.Container>
<List.Separator />
<List.Item
title='Create_New'
onPress={() => (isMasterDetail
? navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView', { teamId }) })
: navigation.navigate('SelectedUsersView', { nextAction: () => navigation.navigate('ChatsStackNavigator', { screen: 'CreateChannelView', params: { teamId } }) }))
}
testID='add-channel-team-view-create-channel'
left={() => <List.Icon name='team' />}
right={() => <List.Icon name='chevron-right' />}
theme={theme}
/>
<List.Separator />
<List.Item
title='Add_Existing'
onPress={() => navigation.navigate('AddExistingChannelView', { teamId, teamChannels })}
testID='add-channel-team-view-add-existing'
left={() => <List.Icon name='channel-public' />}
right={() => <List.Icon name='chevron-right' />}
theme={theme}
/>
<List.Separator />
</List.Container>
</SafeAreaView>
);
};
AddChannelTeamView.propTypes = {
route: PropTypes.object,
navigation: PropTypes.object,
isMasterDetail: PropTypes.bool
};
const mapStateToProps = state => ({
isMasterDetail: state.app.isMasterDetail
});
export default connect(mapStateToProps)(AddChannelTeamView);

View File

@ -0,0 +1,215 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
View, FlatList
} from 'react-native';
import { connect } from 'react-redux';
import { Q } from '@nozbe/watermelondb';
import * as List from '../containers/List';
import database from '../lib/database';
import RocketChat from '../lib/rocketchat';
import I18n from '../i18n';
import log, { events, logEvent } from '../utils/log';
import SearchBox from '../containers/SearchBox';
import * as HeaderButton from '../containers/HeaderButton';
import StatusBar from '../containers/StatusBar';
import { themes } from '../constants/colors';
import { withTheme } from '../theme';
import SafeAreaView from '../containers/SafeAreaView';
import Loading from '../containers/Loading';
import { animateNextTransition } from '../utils/layoutAnimation';
import { goRoom } from '../utils/goRoom';
import { showErrorAlert } from '../utils/info';
import debounce from '../utils/debounce';
const QUERY_SIZE = 50;
class AddExistingChannelView extends React.Component {
static propTypes = {
navigation: PropTypes.object,
route: PropTypes.object,
theme: PropTypes.string,
isMasterDetail: PropTypes.bool,
addTeamChannelPermission: PropTypes.array
};
constructor(props) {
super(props);
this.query();
this.teamId = props.route?.params?.teamId;
this.state = {
search: [],
channels: [],
selected: [],
loading: false
};
this.setHeader();
}
setHeader = () => {
const { navigation, isMasterDetail } = this.props;
const { selected } = this.state;
const options = {
headerTitle: I18n.t('Add_Existing_Channel')
};
if (isMasterDetail) {
options.headerLeft = () => <HeaderButton.CloseModal navigation={navigation} />;
}
options.headerRight = () => selected.length > 0 && (
<HeaderButton.Container>
<HeaderButton.Item title={I18n.t('Next')} onPress={this.submit} testID='add-existing-channel-view-submit' />
</HeaderButton.Container>
);
navigation.setOptions(options);
}
query = async(stringToSearch = '') => {
try {
const { addTeamChannelPermission } = this.props;
const db = database.active;
const channels = await db.collections
.get('subscriptions')
.query(
Q.where('team_id', ''),
Q.where('t', Q.oneOf(['c', 'p'])),
Q.where('name', Q.like(`%${ stringToSearch }%`)),
Q.experimentalTake(QUERY_SIZE),
Q.experimentalSortBy('room_updated_at', Q.desc)
)
.fetch();
const asyncFilter = async(channelsArray) => {
const results = await Promise.all(channelsArray.map(async(channel) => {
if (channel.prid) {
return false;
}
const permissions = await RocketChat.hasPermission([addTeamChannelPermission], channel.rid);
if (!permissions[0]) {
return false;
}
return true;
}));
return channelsArray.filter((_v, index) => results[index]);
};
const channelFiltered = await asyncFilter(channels);
this.setState({ channels: channelFiltered });
} catch (e) {
log(e);
}
}
onSearchChangeText = debounce((text) => {
this.query(text);
}, 300)
dismiss = () => {
const { navigation } = this.props;
return navigation.pop();
}
submit = async() => {
const { selected } = this.state;
const { isMasterDetail } = this.props;
this.setState({ loading: true });
try {
logEvent(events.CT_ADD_ROOM_TO_TEAM);
const result = await RocketChat.addRoomsToTeam({ rooms: selected, teamId: this.teamId });
if (result.success) {
this.setState({ loading: false });
goRoom({ item: result, isMasterDetail });
}
} catch (e) {
logEvent(events.CT_ADD_ROOM_TO_TEAM_F);
showErrorAlert(I18n.t(e.data.error), I18n.t('Add_Existing_Channel'), () => {});
this.setState({ loading: false });
}
}
renderHeader = () => {
const { theme } = this.props;
return (
<View style={{ backgroundColor: themes[theme].auxiliaryBackground }}>
<SearchBox onChangeText={text => this.onSearchChangeText(text)} testID='add-existing-channel-view-search' />
</View>
);
}
isChecked = (rid) => {
const { selected } = this.state;
return selected.includes(rid);
}
toggleChannel = (rid) => {
const { selected } = this.state;
animateNextTransition();
if (!this.isChecked(rid)) {
logEvent(events.AEC_ADD_CHANNEL);
this.setState({ selected: [...selected, rid] }, () => this.setHeader());
} else {
logEvent(events.AEC_REMOVE_CHANNEL);
const filterSelected = selected.filter(el => el !== rid);
this.setState({ selected: filterSelected }, () => this.setHeader());
}
}
renderItem = ({ item }) => {
const isChecked = this.isChecked(item.rid);
// TODO: reuse logic inside RoomTypeIcon
const icon = item.t === 'p' && !item.teamId ? 'channel-private' : 'channel-public';
return (
<List.Item
title={RocketChat.getRoomTitle(item)}
translateTitle={false}
onPress={() => this.toggleChannel(item.rid)}
testID={`add-existing-channel-view-item-${ item.name }`}
left={() => <List.Icon name={icon} />}
right={() => (isChecked ? <List.Icon name='check' /> : null)}
/>
);
}
renderList = () => {
const { search, channels } = this.state;
const { theme } = this.props;
return (
<FlatList
data={search.length > 0 ? search : channels}
extraData={this.state}
keyExtractor={item => item._id}
ListHeaderComponent={this.renderHeader}
renderItem={this.renderItem}
ItemSeparatorComponent={List.Separator}
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }}
keyboardShouldPersistTaps='always'
/>
);
}
render() {
const { loading } = this.state;
return (
<SafeAreaView testID='add-existing-channel-view'>
<StatusBar />
{this.renderList()}
<Loading visible={loading} />
</SafeAreaView>
);
}
}
const mapStateToProps = state => ({
isMasterDetail: state.app.isMasterDetail,
addTeamChannelPermission: state.permissions['add-team-channel']
});
export default connect(mapStateToProps)(withTheme(AddExistingChannelView));

View File

@ -68,12 +68,9 @@ const styles = StyleSheet.create({
});
class CreateChannelView extends React.Component {
static navigationOptions = () => ({
title: I18n.t('Create_Channel')
});
static propTypes = {
navigation: PropTypes.object,
route: PropTypes.object,
baseUrl: PropTypes.string,
create: PropTypes.func.isRequired,
removeUser: PropTypes.func.isRequired,
@ -86,15 +83,24 @@ class CreateChannelView extends React.Component {
id: PropTypes.string,
token: PropTypes.string
}),
theme: PropTypes.string
theme: PropTypes.string,
teamId: PropTypes.string
};
state = {
channelName: '',
type: true,
readOnly: false,
encrypted: false,
broadcast: false
constructor(props) {
super(props);
const { route } = this.props;
const isTeam = route?.params?.isTeam || false;
this.teamId = route?.params?.teamId;
this.state = {
channelName: '',
type: true,
readOnly: false,
encrypted: false,
broadcast: false,
isTeam
};
this.setHeader();
}
shouldComponentUpdate(nextProps, nextState) {
@ -134,6 +140,15 @@ class CreateChannelView extends React.Component {
return false;
}
setHeader = () => {
const { navigation } = this.props;
const { isTeam } = this.state;
navigation.setOptions({
title: isTeam ? I18n.t('Create_Team') : I18n.t('Create_Channel')
});
}
toggleRightButton = (channelName) => {
const { navigation } = this.props;
navigation.setOptions({
@ -152,9 +167,11 @@ class CreateChannelView extends React.Component {
submit = () => {
const {
channelName, type, readOnly, broadcast, encrypted
channelName, type, readOnly, broadcast, encrypted, isTeam
} = this.state;
const { users: usersProps, isFetching, create } = this.props;
const {
users: usersProps, isFetching, create
} = this.props;
if (!channelName.trim() || isFetching) {
return;
@ -163,9 +180,9 @@ class CreateChannelView extends React.Component {
// transform users object into array of usernames
const users = usersProps.map(user => user.name);
// create channel
// create channel or team
create({
name: channelName, users, type, readOnly, broadcast, encrypted
name: channelName, users, type, readOnly, broadcast, encrypted, isTeam, teamId: this.teamId
});
Review.pushPositiveEvent();
@ -196,11 +213,12 @@ class CreateChannelView extends React.Component {
}
renderType() {
const { type } = this.state;
const { type, isTeam } = this.state;
return this.renderSwitch({
id: 'type',
value: type,
label: 'Private_Channel',
label: isTeam ? 'Private_Team' : 'Private_Channel',
onValueChange: (value) => {
logEvent(events.CR_TOGGLE_TYPE);
// If we set the channel as public, encrypted status should be false
@ -210,11 +228,12 @@ class CreateChannelView extends React.Component {
}
renderReadOnly() {
const { readOnly, broadcast } = this.state;
const { readOnly, broadcast, isTeam } = this.state;
return this.renderSwitch({
id: 'readonly',
value: readOnly,
label: 'Read_Only_Channel',
label: isTeam ? 'Read_Only_Team' : 'Read_Only_Channel',
onValueChange: (value) => {
logEvent(events.CR_TOGGLE_READ_ONLY);
this.setState({ readOnly: value });
@ -244,11 +263,12 @@ class CreateChannelView extends React.Component {
}
renderBroadcast() {
const { broadcast, readOnly } = this.state;
const { broadcast, readOnly, isTeam } = this.state;
return this.renderSwitch({
id: 'broadcast',
value: broadcast,
label: 'Broadcast_Channel',
label: isTeam ? 'Broadcast_Team' : 'Broadcast_Channel',
onValueChange: (value) => {
logEvent(events.CR_TOGGLE_BROADCAST);
this.setState({
@ -301,8 +321,10 @@ class CreateChannelView extends React.Component {
}
render() {
const { channelName } = this.state;
const { users, isFetching, theme } = this.props;
const { channelName, isTeam } = this.state;
const {
users, isFetching, theme
} = this.props;
const userCount = users.length;
return (
@ -318,10 +340,10 @@ class CreateChannelView extends React.Component {
<TextInput
autoFocus
style={[styles.input, { backgroundColor: themes[theme].backgroundColor }]}
label={I18n.t('Channel_Name')}
label={isTeam ? I18n.t('Team_Name') : I18n.t('Channel_Name')}
value={channelName}
onChangeText={this.onChangeText}
placeholder={I18n.t('Channel_Name')}
placeholder={isTeam ? I18n.t('Team_Name') : I18n.t('Channel_Name')}
returnKeyType='done'
testID='create-channel-name'
autoCorrect={false}

View File

@ -64,6 +64,11 @@ export default class DirectoryOptions extends PureComponent {
icon = 'channel-public';
}
if (itemType === 'teams') {
text = 'Teams';
icon = 'teams';
}
return (
<Touch
onPress={() => changeType(itemType)}
@ -105,6 +110,7 @@ export default class DirectoryOptions extends PureComponent {
</Touch>
{this.renderItem('channels')}
{this.renderItem('users')}
{this.renderItem('teams')}
{isFederationEnabled
? (
<>

View File

@ -121,6 +121,8 @@ class DirectoryView extends React.Component {
logEvent(events.DIRECTORY_SEARCH_USERS);
} else if (type === 'channels') {
logEvent(events.DIRECTORY_SEARCH_CHANNELS);
} else if (type === 'teams') {
logEvent(events.DIRECTORY_SEARCH_TEAMS);
}
}
@ -149,10 +151,14 @@ class DirectoryView extends React.Component {
if (result.success) {
this.goRoom({ rid: result.room._id, name: item.username, t: 'd' });
}
} else {
} else if (['p', 'c'].includes(item.t) && !item.teamMain) {
const { room } = await RocketChat.getRoomInfo(item._id);
this.goRoom({
rid: item._id, name: item.name, joinCodeRequired: room.joinCodeRequired, t: 'c', search: true
rid: item._id, name: item.name, joinCodeRequired: room.joinCodeRequired, t: item.t, search: true
});
} else {
this.goRoom({
rid: item._id, name: item.name, t: item.t, search: true, teamMain: item.teamMain, teamId: item.teamId
});
}
}
@ -160,6 +166,19 @@ class DirectoryView extends React.Component {
renderHeader = () => {
const { type } = this.state;
const { theme } = this.props;
let text = 'Users';
let icon = 'user';
if (type === 'channels') {
text = 'Channels';
icon = 'channel-public';
}
if (type === 'teams') {
text = 'Teams';
icon = 'teams';
}
return (
<>
<SearchBox
@ -174,8 +193,8 @@ class DirectoryView extends React.Component {
theme={theme}
>
<View style={[sharedStyles.separatorVertical, styles.toggleDropdownContainer, { borderColor: themes[theme].separatorColor }]}>
<CustomIcon style={[styles.toggleDropdownIcon, { color: themes[theme].tintColor }]} size={20} name={type === 'users' ? 'user' : 'channel-public'} />
<Text style={[styles.toggleDropdownText, { color: themes[theme].tintColor }]}>{type === 'users' ? I18n.t('Users') : I18n.t('Channels')}</Text>
<CustomIcon style={[styles.toggleDropdownIcon, { color: themes[theme].tintColor }]} size={20} name={icon} />
<Text style={[styles.toggleDropdownText, { color: themes[theme].tintColor }]}>{I18n.t(text)}</Text>
<CustomIcon name='chevron-down' size={20} style={[styles.toggleDropdownArrow, { color: themes[theme].auxiliaryTintColor }]} />
</View>
</Touch>
@ -217,12 +236,25 @@ class DirectoryView extends React.Component {
/>
);
}
if (type === 'teams') {
return (
<DirectoryItem
avatar={item.name}
description={item.name}
rightLabel={I18n.t('N_channels', { n: item.roomsCount })}
type={item.t}
teamMain={item.teamMain}
{...commonProps}
/>
);
}
return (
<DirectoryItem
avatar={item.name}
description={item.topic}
rightLabel={I18n.t('N_users', { n: item.usersCount })}
type='c'
type={item.t}
{...commonProps}
/>
);

View File

@ -16,6 +16,7 @@ import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login';
import { withActionSheet } from '../../containers/ActionSheet';
import SafeAreaView from '../../containers/SafeAreaView';
import getThreadName from '../../lib/methods/getThreadName';
class MessagesView extends React.Component {
static propTypes = {
@ -26,7 +27,8 @@ class MessagesView extends React.Component {
customEmojis: PropTypes.object,
theme: PropTypes.string,
showActionSheet: PropTypes.func,
useRealName: PropTypes.bool
useRealName: PropTypes.bool,
isMasterDetail: PropTypes.bool
}
constructor(props) {
@ -81,6 +83,32 @@ class MessagesView extends React.Component {
navigation.navigate('RoomInfoView', navParam);
}
jumpToMessage = async({ item }) => {
const { navigation, isMasterDetail } = this.props;
let params = {
rid: this.rid,
jumpToMessageId: item._id,
t: this.t,
room: this.room
};
if (item.tmid) {
if (isMasterDetail) {
navigation.navigate('DrawerNavigator');
} else {
navigation.pop(2);
}
params = {
...params,
tmid: item.tmid,
name: await getThreadName(this.rid, item.tmid, item._id),
t: 'thread'
};
navigation.push('RoomView', params);
} else {
navigation.navigate('RoomView', params);
}
}
defineMessagesViewContent = (name) => {
const {
user, baseUrl, theme, useRealName
@ -93,11 +121,13 @@ class MessagesView extends React.Component {
timeFormat: 'MMM Do YYYY, h:mm:ss a',
isEdited: !!item.editedAt,
isHeader: true,
isThreadRoom: true,
attachments: item.attachments || [],
useRealName,
showAttachment: this.showAttachment,
getCustomEmoji: this.getCustomEmoji,
navToRoomInfo: this.navToRoomInfo
navToRoomInfo: this.navToRoomInfo,
onPress: () => this.jumpToMessage({ item })
});
return ({
@ -315,7 +345,8 @@ const mapStateToProps = state => ({
baseUrl: state.server.server,
user: getUserSelector(state),
customEmojis: state.customEmojis,
useRealName: state.settings.UI_Use_Real_Name
useRealName: state.settings.UI_Use_Real_Name,
isMasterDetail: state.app.isMasterDetail
});
export default connect(mapStateToProps)(withTheme(withActionSheet(MessagesView)));

View File

@ -25,6 +25,7 @@ import Navigation from '../lib/Navigation';
import { createChannelRequest } from '../actions/createChannel';
import { goRoom } from '../utils/goRoom';
import SafeAreaView from '../containers/SafeAreaView';
import { compareServerVersion, methods } from '../lib/utils';
const QUERY_SIZE = 50;
@ -60,10 +61,11 @@ class NewMessageView extends React.Component {
id: PropTypes.string,
token: PropTypes.string
}),
createChannel: PropTypes.func,
create: PropTypes.func,
maxUsers: PropTypes.number,
theme: PropTypes.string,
isMasterDetail: PropTypes.bool
isMasterDetail: PropTypes.bool,
serverVersion: PropTypes.string
};
constructor(props) {
@ -116,11 +118,17 @@ class NewMessageView extends React.Component {
navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView') });
}
createTeam = () => {
logEvent(events.NEW_MSG_CREATE_TEAM);
const { navigation } = this.props;
navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView', { isTeam: true }) });
}
createGroupChat = () => {
logEvent(events.NEW_MSG_CREATE_GROUP_CHAT);
const { createChannel, maxUsers, navigation } = this.props;
const { create, maxUsers, navigation } = this.props;
navigation.navigate('SelectedUsersViewCreateChannel', {
nextAction: () => createChannel({ group: true }),
nextAction: () => create({ group: true }),
buttonText: I18n.t('Create'),
maxUsers
});
@ -160,7 +168,7 @@ class NewMessageView extends React.Component {
}
renderHeader = () => {
const { maxUsers, theme } = this.props;
const { maxUsers, theme, serverVersion } = this.props;
return (
<View style={{ backgroundColor: themes[theme].auxiliaryBackground }}>
<SearchBox onChangeText={text => this.onSearchChangeText(text)} testID='new-message-view-search' />
@ -172,6 +180,13 @@ class NewMessageView extends React.Component {
testID: 'new-message-view-create-channel',
first: true
})}
{compareServerVersion(serverVersion, '3.13.0', methods.greaterThanOrEqualTo)
? (this.renderButton({
onPress: this.createTeam,
title: I18n.t('Create_Team'),
icon: 'teams',
testID: 'new-message-view-create-team'
})) : null}
{maxUsers > 2 ? this.renderButton({
onPress: this.createGroupChat,
title: I18n.t('Create_Direct_Messages'),
@ -246,6 +261,7 @@ class NewMessageView extends React.Component {
}
const mapStateToProps = state => ({
serverVersion: state.server.version,
isMasterDetail: state.app.isMasterDetail,
baseUrl: state.server.server,
maxUsers: state.settings.DirectMesssage_maxUsers || 1,
@ -253,7 +269,7 @@ const mapStateToProps = state => ({
});
const mapDispatchToProps = dispatch => ({
createChannel: params => dispatch(createChannelRequest(params))
create: params => dispatch(createChannelRequest(params))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewMessageView));

View File

@ -30,7 +30,7 @@ class ReadReceiptView extends React.Component {
static propTypes = {
route: PropTypes.object,
Message_TimeFormat: PropTypes.string,
Message_TimeAndDateFormat: PropTypes.string,
theme: PropTypes.string
}
@ -94,8 +94,8 @@ class ReadReceiptView extends React.Component {
}
renderItem = ({ item }) => {
const { Message_TimeFormat, theme } = this.props;
const time = moment(item.ts).format(Message_TimeFormat);
const { theme, Message_TimeAndDateFormat } = this.props;
const time = moment(item.ts).format(Message_TimeAndDateFormat);
if (!item?.user?.username) {
return null;
}
@ -156,7 +156,7 @@ class ReadReceiptView extends React.Component {
}
const mapStateToProps = state => ({
Message_TimeFormat: state.settings.Message_TimeFormat
Message_TimeAndDateFormat: state.settings.Message_TimeAndDateFormat
});
export default connect(mapStateToProps)(withTheme(ReadReceiptView));

View File

@ -1,15 +1,18 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
View, Text, Alert, Share, Switch
View, Text, Share, Switch
} from 'react-native';
import { connect } from 'react-redux';
import isEmpty from 'lodash/isEmpty';
import { compareServerVersion, methods } from '../../lib/utils';
import { Q } from '@nozbe/watermelondb';
import { compareServerVersion, methods } from '../../lib/utils';
import Touch from '../../utils/touch';
import { setLoading as setLoadingAction } from '../../actions/selectedUsers';
import { leaveRoom as leaveRoomAction, closeRoom as closeRoomAction } from '../../actions/room';
import {
leaveRoom as leaveRoomAction, closeRoom as closeRoomAction
} from '../../actions/room';
import styles from './styles';
import sharedStyles from '../Styles';
import Avatar from '../../containers/Avatar';
@ -60,7 +63,9 @@ class RoomActionsView extends React.Component {
editRoomPermission: PropTypes.array,
toggleRoomE2EEncryptionPermission: PropTypes.array,
viewBroadcastMemberListPermission: PropTypes.array,
transferLivechatGuestPermission: PropTypes.array
transferLivechatGuestPermission: PropTypes.array,
createTeamPermission: PropTypes.array,
addTeamChannelPermission: PropTypes.array
}
constructor(props) {
@ -82,7 +87,9 @@ class RoomActionsView extends React.Component {
canForwardGuest: false,
canReturnQueue: false,
canEdit: false,
canToggleEncryption: false
canToggleEncryption: false,
canCreateTeam: false,
canAddChannelToTeam: false
};
if (room && room.observe && room.rid) {
this.roomObservable = room.observe();
@ -131,9 +138,11 @@ class RoomActionsView extends React.Component {
const canEdit = await this.canEdit();
const canToggleEncryption = await this.canToggleEncryption();
const canViewMembers = await this.canViewMembers();
const canCreateTeam = await this.canCreateTeam();
const canAddChannelToTeam = await this.canAddChannelToTeam();
this.setState({
canAutoTranslate, canAddUser, canInviteUser, canEdit, canToggleEncryption, canViewMembers
canAutoTranslate, canAddUser, canInviteUser, canEdit, canToggleEncryption, canViewMembers, canCreateTeam, canAddChannelToTeam
});
// livechat permissions
@ -209,6 +218,26 @@ class RoomActionsView extends React.Component {
return canEdit;
}
canCreateTeam = async() => {
const { room } = this.state;
const { createTeamPermission } = this.props;
const { rid } = room;
const permissions = await RocketChat.hasPermission([createTeamPermission], rid);
const canCreateTeam = permissions[0];
return canCreateTeam;
}
canAddChannelToTeam = async() => {
const { room } = this.state;
const { addTeamChannelPermission } = this.props;
const { rid } = room;
const permissions = await RocketChat.hasPermission([addTeamChannelPermission], rid);
const canAddChannelToTeam = permissions[0];
return canAddChannelToTeam;
}
canToggleEncryption = async() => {
const { room } = this.state;
const { toggleRoomE2EEncryptionPermission } = this.props;
@ -395,21 +424,162 @@ class RoomActionsView extends React.Component {
const { room } = this.state;
const { leaveRoom } = this.props;
Alert.alert(
I18n.t('Are_you_sure_question_mark'),
I18n.t('Are_you_sure_you_want_to_leave_the_room', { room: RocketChat.getRoomTitle(room) }),
[
{
text: I18n.t('Cancel'),
style: 'cancel'
},
{
text: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
style: 'destructive',
onPress: () => leaveRoom(room.rid, room.t)
}
]
);
showConfirmationAlert({
message: I18n.t('Are_you_sure_you_want_to_leave_the_room', { room: RocketChat.getRoomTitle(room) }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
onPress: () => leaveRoom('channel', room)
});
}
leaveTeam = async() => {
const { room } = this.state;
const { navigation, leaveRoom } = this.props;
try {
const result = await RocketChat.teamListRoomsOfUser({ teamId: room.teamId, userId: room.u._id });
if (result.rooms?.length) {
const teamChannels = result.rooms.map(r => ({
rid: r._id,
name: r.name,
teamId: r.teamId,
alert: r.isLastOwner
}));
navigation.navigate('SelectListView', {
title: 'Leave_Team',
data: teamChannels,
infoText: 'Select_Team_Channels',
nextAction: data => leaveRoom('team', room, data),
showAlert: () => showErrorAlert(I18n.t('Last_owner_team_room'), I18n.t('Cannot_leave'))
});
} else {
showConfirmationAlert({
message: I18n.t('You_are_leaving_the_team', { team: RocketChat.getRoomTitle(room) }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
onPress: () => leaveRoom('team', room)
});
}
} catch (e) {
showConfirmationAlert({
message: I18n.t('You_are_leaving_the_team', { team: RocketChat.getRoomTitle(room) }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
onPress: () => leaveRoom('team', room)
});
}
}
handleConvertToTeam = async() => {
logEvent(events.RA_CONVERT_TO_TEAM);
try {
const { room } = this.state;
const { navigation } = this.props;
const result = await RocketChat.convertChannelToTeam({ rid: room.rid, name: room.name, type: room.t });
if (result.success) {
navigation.navigate('RoomView');
}
} catch (e) {
logEvent(events.RA_CONVERT_TO_TEAM_F);
log(e);
}
}
convertToTeam = () => {
showConfirmationAlert({
title: I18n.t('Confirmation'),
message: I18n.t('Convert_to_Team_Warning'),
confirmationText: I18n.t('Convert'),
onPress: () => this.handleConvertToTeam()
});
}
handleMoveToTeam = async(selected) => {
logEvent(events.RA_MOVE_TO_TEAM);
try {
const { room } = this.state;
const { navigation } = this.props;
const result = await RocketChat.addRoomsToTeam({ teamId: selected?.[0], rooms: [room.rid] });
if (result.success) {
navigation.navigate('RoomView');
}
} catch (e) {
logEvent(events.RA_MOVE_TO_TEAM_F);
log(e);
showErrorAlert(I18n.t('There_was_an_error_while_action', { action: I18n.t('moving_channel_to_team') }));
}
}
moveToTeam = async() => {
try {
const { navigation } = this.props;
const db = database.active;
const subCollection = db.get('subscriptions');
const teamRooms = await subCollection.query(
Q.where('team_main', true)
);
if (teamRooms.length) {
const data = teamRooms.map(team => ({
rid: team.teamId,
t: team.t,
name: team.name
}));
navigation.navigate('SelectListView', {
title: 'Move_to_Team',
infoText: 'Move_Channel_Paragraph',
nextAction: () => {
navigation.push('SelectListView', {
title: 'Select_Team',
data,
isRadio: true,
isSearch: true,
onSearch: onChangeText => this.searchTeam(onChangeText),
nextAction: selected => showConfirmationAlert({
title: I18n.t('Confirmation'),
message: I18n.t('Move_to_Team_Warning'),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('move') }),
onPress: () => this.handleMoveToTeam(selected)
})
});
}
});
}
} catch (e) {
log(e);
}
}
searchTeam = async(onChangeText) => {
logEvent(events.RA_SEARCH_TEAM);
try {
const { addTeamChannelPermission, createTeamPermission } = this.props;
const QUERY_SIZE = 50;
const db = database.active;
const teams = await db.collections
.get('subscriptions')
.query(
Q.where('team_main', true),
Q.where('name', Q.like(`%${ onChangeText }%`)),
Q.experimentalTake(QUERY_SIZE),
Q.experimentalSortBy('room_updated_at', Q.desc)
);
const asyncFilter = async(teamArray) => {
const results = await Promise.all(teamArray.map(async(team) => {
const permissions = await RocketChat.hasPermission([addTeamChannelPermission, createTeamPermission], team.rid);
if (!permissions[0]) {
return false;
}
return true;
}));
return teamArray.filter((_v, index) => results[index]);
};
const teamsFiltered = await asyncFilter(teams);
return teamsFiltered;
} catch (e) {
log(e);
}
}
renderRoomInfo = () => {
@ -486,7 +656,7 @@ class RoomActionsView extends React.Component {
renderJitsi = () => {
const { room } = this.state;
const { jitsiEnabled } = this.props;
if (!jitsiEnabled) {
if (!jitsiEnabled || room.teamMain) {
return null;
}
return (
@ -544,7 +714,7 @@ class RoomActionsView extends React.Component {
return null;
}
if (t === 'd') {
if (t === 'd' && !RocketChat.isGroupChat(room)) {
return (
<List.Section>
<List.Separator />
@ -568,9 +738,9 @@ class RoomActionsView extends React.Component {
<List.Section>
<List.Separator />
<List.Item
title='Leave_channel'
title='Leave'
onPress={() => this.onPressTouchable({
event: this.leaveChannel
event: room.teamMain ? this.leaveTeam : this.leaveChannel
})}
testID='room-actions-leave-channel'
left={() => <List.Icon name='logout' color={themes[theme].dangerColor} />}
@ -581,6 +751,52 @@ class RoomActionsView extends React.Component {
</List.Section>
);
}
return null;
}
teamChannelActions = (t, room) => {
const { canEdit, canCreateTeam, canAddChannelToTeam } = this.state;
const canConvertToTeam = canEdit && canCreateTeam && !room.teamMain;
const canMoveToTeam = canEdit && canAddChannelToTeam && !room.teamId;
return (
<>
{['c', 'p'].includes(t) && canConvertToTeam
? (
<>
<List.Item
title='Convert_to_Team'
onPress={() => this.onPressTouchable({
event: this.convertToTeam
})}
testID='room-actions-convert-to-team'
left={() => <List.Icon name='teams' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['c', 'p'].includes(t) && canMoveToTeam
? (
<>
<List.Item
title='Move_to_Team'
onPress={() => this.onPressTouchable({
event: this.moveToTeam
})}
testID='room-actions-move-to-team'
left={() => <List.Icon name='channel-move-to-team' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
</>
);
}
render() {
@ -588,7 +804,7 @@ class RoomActionsView extends React.Component {
room, membersCount, canViewMembers, canAddUser, canInviteUser, joined, canAutoTranslate, canForwardGuest, canReturnQueue
} = this.state;
const {
rid, t, encrypted
rid, t
} = room;
const isGroupChat = RocketChat.isGroupChat(room);
@ -713,24 +929,6 @@ class RoomActionsView extends React.Component {
)
: null}
{['c', 'p', 'd'].includes(t)
? (
<>
<List.Item
title='Search'
onPress={() => this.onPressTouchable({
route: 'SearchMessagesView',
params: { rid, encrypted }
})}
testID='room-actions-search'
left={() => <List.Icon name='search' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['c', 'p', 'd'].includes(t)
? (
<>
@ -802,6 +1000,8 @@ class RoomActionsView extends React.Component {
)
: null}
{ this.teamChannelActions(t, room) }
{['l'].includes(t) && !this.isOmnichannelPreview
? (
<>
@ -880,6 +1080,7 @@ const mapStateToProps = state => ({
jitsiEnabled: state.settings.Jitsi_Enabled || false,
encryptionEnabled: state.encryption.enabled,
serverVersion: state.server.version,
isMasterDetail: state.app.isMasterDetail,
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'],
@ -887,11 +1088,13 @@ const mapStateToProps = state => ({
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']
transferLivechatGuestPermission: state.permissions['transfer-livechat-guest'],
createTeamPermission: state.permissions['create-team'],
addTeamChannelPermission: state.permissions['add-team-channel']
});
const mapDispatchToProps = dispatch => ({
leaveRoom: (rid, t) => dispatch(leaveRoomAction(rid, t)),
leaveRoom: (roomType, room, selected) => dispatch(leaveRoomAction(roomType, room, selected)),
closeRoom: rid => dispatch(closeRoomAction(rid)),
setLoadingInvite: loading => dispatch(setLoadingAction(loading))
});

View File

@ -8,15 +8,16 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import ImagePicker from 'react-native-image-crop-picker';
import { dequal } from 'dequal';
import isEmpty from 'lodash/isEmpty';
import { compareServerVersion, methods } from '../../lib/utils';
import { Q } from '@nozbe/watermelondb';
import { compareServerVersion, methods } from '../../lib/utils';
import database from '../../lib/database';
import { deleteRoom as deleteRoomAction } from '../../actions/room';
import KeyboardView from '../../presentation/KeyboardView';
import sharedStyles from '../Styles';
import styles from './styles';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { showErrorAlert } from '../../utils/info';
import { showConfirmationAlert, showErrorAlert } from '../../utils/info';
import { LISTENER } from '../../containers/Toast';
import EventEmitter from '../../utils/events';
import RocketChat from '../../lib/rocketchat';
@ -41,6 +42,7 @@ const PERMISSION_ARCHIVE = 'archive-room';
const PERMISSION_UNARCHIVE = 'unarchive-room';
const PERMISSION_DELETE_C = 'delete-c';
const PERMISSION_DELETE_P = 'delete-p';
const PERMISSION_DELETE_TEAM = 'delete-team';
class RoomInfoEditView extends React.Component {
static navigationOptions = () => ({
@ -48,6 +50,7 @@ class RoomInfoEditView extends React.Component {
})
static propTypes = {
navigation: PropTypes.object,
route: PropTypes.object,
deleteRoom: PropTypes.func,
serverVersion: PropTypes.string,
@ -58,7 +61,9 @@ class RoomInfoEditView extends React.Component {
archiveRoomPermission: PropTypes.array,
unarchiveRoomPermission: PropTypes.array,
deleteCPermission: PropTypes.array,
deletePPermission: PropTypes.array
deletePPermission: PropTypes.array,
deleteTeamPermission: PropTypes.array,
isMasterDetail: PropTypes.bool
};
constructor(props) {
@ -100,7 +105,8 @@ class RoomInfoEditView extends React.Component {
archiveRoomPermission,
unarchiveRoomPermission,
deleteCPermission,
deletePPermission
deletePPermission,
deleteTeamPermission
} = this.props;
const rid = route.params?.rid;
if (!rid) {
@ -122,7 +128,8 @@ class RoomInfoEditView extends React.Component {
archiveRoomPermission,
unarchiveRoomPermission,
deleteCPermission,
deletePPermission
deletePPermission,
...(this.room.teamMain ? [deleteTeamPermission] : [])
], rid);
this.setState({
@ -132,7 +139,8 @@ class RoomInfoEditView extends React.Component {
[PERMISSION_ARCHIVE]: result[2],
[PERMISSION_UNARCHIVE]: result[3],
[PERMISSION_DELETE_C]: result[4],
[PERMISSION_DELETE_P]: result[5]
[PERMISSION_DELETE_P]: result[5],
...(this.room.teamMain && { [PERMISSION_DELETE_TEAM]: result[6] })
}
});
} catch (e) {
@ -284,6 +292,74 @@ class RoomInfoEditView extends React.Component {
}, 100);
}
handleDeleteTeam = async(selected) => {
logEvent(events.RI_EDIT_DELETE_TEAM);
const { navigation, isMasterDetail } = this.props;
const { room } = this.state;
try {
const result = await RocketChat.deleteTeam({ teamId: room.teamId, ...(selected && { roomsToRemove: selected }) });
if (result.success) {
if (isMasterDetail) {
navigation.navigate('DrawerNavigator');
} else {
navigation.navigate('RoomsListView');
}
}
} catch (e) {
logEvent(events.RI_EDIT_DELETE_TEAM_F);
log(e);
showErrorAlert(
e.data.error
? I18n.t(e.data.error)
: I18n.t('There_was_an_error_while_action', { action: I18n.t('deleting_team') }),
I18n.t('Cannot_delete')
);
}
}
deleteTeam = async() => {
const { room } = this.state;
const { navigation } = this.props;
try {
const db = database.active;
const subCollection = db.get('subscriptions');
const teamChannels = await subCollection.query(
Q.where('team_id', room.teamId),
Q.where('team_main', Q.notEq(true))
);
if (teamChannels.length) {
navigation.navigate('SelectListView', {
title: 'Delete_Team',
data: teamChannels,
infoText: 'Select_channels_to_delete',
nextAction: (selected) => {
showConfirmationAlert({
message: I18n.t('You_are_deleting_the_team', { team: RocketChat.getRoomTitle(room) }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
onPress: () => this.handleDeleteTeam(selected)
});
}
});
} else {
showConfirmationAlert({
message: I18n.t('You_are_deleting_the_team', { team: RocketChat.getRoomTitle(room) }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
onPress: () => this.handleDeleteTeam()
});
}
} catch (e) {
log(e);
showErrorAlert(
e.data.error
? I18n.t(e.data.error)
: I18n.t('There_was_an_error_while_action', { action: I18n.t('deleting_team') }),
I18n.t('Cannot_delete')
);
}
}
delete = () => {
const { room } = this.state;
const { deleteRoom } = this.props;
@ -339,9 +415,16 @@ class RoomInfoEditView extends React.Component {
hasDeletePermission = () => {
const { room, permissions } = this.state;
return (
room.t === 'p' ? permissions[PERMISSION_DELETE_P] : permissions[PERMISSION_DELETE_C]
);
if (room.teamMain) {
return permissions[PERMISSION_DELETE_TEAM];
}
if (room.t === 'p') {
return permissions[PERMISSION_DELETE_P];
}
return permissions[PERMISSION_DELETE_C];
}
hasArchivePermission = () => {
@ -513,9 +596,9 @@ class RoomInfoEditView extends React.Component {
<SwitchContainer
value={t}
leftLabelPrimary={I18n.t('Public')}
leftLabelSecondary={I18n.t('Everyone_can_access_this_channel')}
leftLabelSecondary={room.teamMain ? I18n.t('Everyone_can_access_this_team') : I18n.t('Everyone_can_access_this_channel')}
rightLabelPrimary={I18n.t('Private')}
rightLabelSecondary={I18n.t('Just_invited_people_can_access_this_channel')}
rightLabelSecondary={room.teamMain ? I18n.t('Just_invited_people_can_access_this_team') : I18n.t('Just_invited_people_can_access_this_channel')}
onValueChange={this.toggleRoomType}
theme={theme}
testID='room-info-edit-view-t'
@ -523,7 +606,7 @@ class RoomInfoEditView extends React.Component {
<SwitchContainer
value={ro}
leftLabelPrimary={I18n.t('Collaborative')}
leftLabelSecondary={I18n.t('All_users_in_the_channel_can_write_new_messages')}
leftLabelSecondary={room.teamMain ? I18n.t('All_users_in_the_team_can_write_new_messages') : I18n.t('All_users_in_the_channel_can_write_new_messages')}
rightLabelPrimary={I18n.t('Read_Only')}
rightLabelSecondary={I18n.t('Only_authorized_users_can_write_new_messages')}
onValueChange={this.toggleReadOnly}
@ -647,7 +730,7 @@ class RoomInfoEditView extends React.Component {
{ borderColor: dangerColor },
!this.hasDeletePermission() && sharedStyles.opacity5
]}
onPress={this.delete}
onPress={room.teamMain ? this.deleteTeam : this.delete}
disabled={!this.hasDeletePermission()}
testID='room-info-edit-view-delete'
>
@ -678,7 +761,9 @@ const mapStateToProps = state => ({
archiveRoomPermission: state.permissions[PERMISSION_ARCHIVE],
unarchiveRoomPermission: state.permissions[PERMISSION_UNARCHIVE],
deleteCPermission: state.permissions[PERMISSION_DELETE_C],
deletePPermission: state.permissions[PERMISSION_DELETE_P]
deletePPermission: state.permissions[PERMISSION_DELETE_P],
deleteTeamPermission: state.permissions[PERMISSION_DELETE_TEAM],
isMasterDetail: state.app.isMasterDetail
});
const mapDispatchToProps = dispatch => ({

View File

@ -214,7 +214,7 @@ class RoomInfoView extends React.Component {
}
const permissions = await RocketChat.hasPermission([editRoomPermission], room.rid);
if (permissions[0] && !room.prid) {
if (permissions[0]) {
this.setState({ showEdit: true }, () => this.setHeader());
}
}

View File

@ -23,9 +23,10 @@ import { withTheme } from '../../theme';
import { themes } from '../../constants/colors';
import { getUserSelector } from '../../selectors/login';
import { withActionSheet } from '../../containers/ActionSheet';
import { showConfirmationAlert } from '../../utils/info';
import { showConfirmationAlert, showErrorAlert } from '../../utils/info';
import SafeAreaView from '../../containers/SafeAreaView';
import { goRoom } from '../../utils/goRoom';
import { CustomIcon } from '../../lib/Icons';
const PAGE_SIZE = 25;
@ -34,6 +35,9 @@ const PERMISSION_SET_LEADER = 'set-leader';
const PERMISSION_SET_OWNER = 'set-owner';
const PERMISSION_SET_MODERATOR = 'set-moderator';
const PERMISSION_REMOVE_USER = 'remove-user';
const PERMISSION_EDIT_TEAM_MEMBER = 'edit-team-member';
const PERMISION_VIEW_ALL_TEAMS = 'view-all-teams';
const PERMISSION_VIEW_ALL_TEAM_CHANNELS = 'view-all-team-channels';
class RoomMembersView extends React.Component {
static propTypes = {
@ -55,7 +59,10 @@ class RoomMembersView extends React.Component {
setLeaderPermission: PropTypes.array,
setOwnerPermission: PropTypes.array,
setModeratorPermission: PropTypes.array,
removeUserPermission: PropTypes.array
removeUserPermission: PropTypes.array,
editTeamMemberPermission: PropTypes.array,
viewAllTeamChannelsPermission: PropTypes.array,
viewAllTeamsPermission: PropTypes.array
}
constructor(props) {
@ -94,10 +101,11 @@ class RoomMembersView extends React.Component {
const { room } = this.state;
const {
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission, editTeamMemberPermission, viewAllTeamChannelsPermission, viewAllTeamsPermission
} = this.props;
const result = await RocketChat.hasPermission([
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission, ...(room.teamMain ? [editTeamMemberPermission, viewAllTeamChannelsPermission, viewAllTeamsPermission] : [])
], room.rid);
this.permissions = {
@ -105,7 +113,12 @@ class RoomMembersView extends React.Component {
[PERMISSION_SET_LEADER]: result[1],
[PERMISSION_SET_OWNER]: result[2],
[PERMISSION_SET_MODERATOR]: result[3],
[PERMISSION_REMOVE_USER]: result[4]
[PERMISSION_REMOVE_USER]: result[4],
...(room.teamMain ? {
[PERMISSION_EDIT_TEAM_MEMBER]: result[5],
[PERMISSION_VIEW_ALL_TEAM_CHANNELS]: result[6],
[PERMISION_VIEW_ALL_TEAMS]: result[7]
} : {})
};
const hasSinglePermission = Object.values(this.permissions).some(p => !!p);
@ -137,6 +150,7 @@ class RoomMembersView extends React.Component {
onSearchChangeText = protectedFunction((text) => {
const { members } = this.state;
let membersFiltered = [];
text = text.trim();
if (members && members.length > 0 && text) {
membersFiltered = members.filter(m => m.username.toLowerCase().match(text.toLowerCase()) || m.name.toLowerCase().match(text.toLowerCase()));
@ -163,9 +177,80 @@ class RoomMembersView extends React.Component {
}
}
handleRemoveFromTeam = async(selectedUser) => {
try {
const { navigation } = this.props;
const { room } = this.state;
const result = await RocketChat.teamListRoomsOfUser({ teamId: room.teamId, userId: selectedUser._id });
if (result.rooms?.length) {
const teamChannels = result.rooms.map(r => ({
rid: r._id,
name: r.name,
teamId: r.teamId,
alert: r.isLastOwner
}));
navigation.navigate('SelectListView', {
title: 'Remove_Member',
infoText: 'Remove_User_Team_Channels',
data: teamChannels,
nextAction: selected => this.removeFromTeam(selectedUser, selected),
showAlert: () => showErrorAlert(I18n.t('Last_owner_team_room'), I18n.t('Cannot_remove'))
});
} else {
showConfirmationAlert({
message: I18n.t('Removing_user_from_this_team', { user: selectedUser.username }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('remove') }),
onPress: () => this.removeFromTeam(selectedUser)
});
}
} catch (e) {
showConfirmationAlert({
message: I18n.t('Removing_user_from_this_team', { user: selectedUser.username }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('remove') }),
onPress: () => this.removeFromTeam(selectedUser)
});
}
}
removeFromTeam = async(selectedUser, selected) => {
try {
const { members, membersFiltered, room } = this.state;
const { navigation } = this.props;
const userId = selectedUser._id;
const result = await RocketChat.removeTeamMember({
teamId: room.teamId,
teamName: room.name,
userId,
...(selected && { rooms: selected })
});
if (result.success) {
const message = I18n.t('User_has_been_removed_from_s', { s: RocketChat.getRoomTitle(room) });
EventEmitter.emit(LISTENER, { message });
const newMembers = members.filter(member => member._id !== userId);
const newMembersFiltered = membersFiltered.filter(member => member._id !== userId);
this.setState({
members: newMembers,
membersFiltered: newMembersFiltered
});
navigation.navigate('RoomMembersView');
}
} catch (e) {
log(e);
showErrorAlert(
e.data.error
? I18n.t(e.data.error)
: I18n.t('There_was_an_error_while_action', { action: I18n.t('removing_team') }),
I18n.t('Cannot_remove')
);
}
}
onPressUser = (selectedUser) => {
const { room } = this.state;
const { showActionSheet, user } = this.props;
const { showActionSheet, user, theme } = this.props;
const options = [{
icon: 'message',
@ -173,39 +258,6 @@ class RoomMembersView extends React.Component {
onPress: () => this.navToDirectMessage(selectedUser)
}];
// Owner
if (this.permissions['set-owner']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isOwner = userRoleResult?.roles.includes('owner');
options.push({
icon: 'shield-check',
title: I18n.t(isOwner ? 'Remove_as_owner' : 'Set_as_owner'),
onPress: () => this.handleOwner(selectedUser, !isOwner)
});
}
// Leader
if (this.permissions['set-leader']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isLeader = userRoleResult?.roles.includes('leader');
options.push({
icon: 'shield-alt',
title: I18n.t(isLeader ? 'Remove_as_leader' : 'Set_as_leader'),
onPress: () => this.handleLeader(selectedUser, !isLeader)
});
}
// Moderator
if (this.permissions['set-moderator']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isModerator = userRoleResult?.roles.includes('moderator');
options.push({
icon: 'shield',
title: I18n.t(isModerator ? 'Remove_as_moderator' : 'Set_as_moderator'),
onPress: () => this.handleModerator(selectedUser, !isModerator)
});
}
// Ignore
if (selectedUser._id !== user.id) {
const { ignored } = room;
@ -213,7 +265,8 @@ class RoomMembersView extends React.Component {
options.push({
icon: 'ignore',
title: I18n.t(isIgnored ? 'Unignore' : 'Ignore'),
onPress: () => this.handleIgnore(selectedUser, !isIgnored)
onPress: () => this.handleIgnore(selectedUser, !isIgnored),
testID: 'action-sheet-ignore-user'
});
}
@ -232,12 +285,63 @@ class RoomMembersView extends React.Component {
confirmationText: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
onPress: () => this.handleMute(selectedUser)
});
}
},
testID: 'action-sheet-mute-user'
});
}
// Owner
if (this.permissions['set-owner']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isOwner = userRoleResult?.roles.includes('owner');
options.push({
icon: 'shield-check',
title: I18n.t('Owner'),
onPress: () => this.handleOwner(selectedUser, !isOwner),
right: () => <CustomIcon testID={isOwner ? 'action-sheet-set-owner-checked' : 'action-sheet-set-owner-unchecked'} name={isOwner ? 'checkbox-checked' : 'checkbox-unchecked'} size={20} color={isOwner ? themes[theme].tintActive : themes[theme].auxiliaryTintColor} />,
testID: 'action-sheet-set-owner'
});
}
// Leader
if (this.permissions['set-leader']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isLeader = userRoleResult?.roles.includes('leader');
options.push({
icon: 'shield-alt',
title: I18n.t('Leader'),
onPress: () => this.handleLeader(selectedUser, !isLeader),
right: () => <CustomIcon testID={isLeader ? 'action-sheet-set-leader-checked' : 'action-sheet-set-leader-unchecked'} name={isLeader ? 'checkbox-checked' : 'checkbox-unchecked'} size={20} color={isLeader ? themes[theme].tintActive : themes[theme].auxiliaryTintColor} />,
testID: 'action-sheet-set-leader'
});
}
// Moderator
if (this.permissions['set-moderator']) {
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
const isModerator = userRoleResult?.roles.includes('moderator');
options.push({
icon: 'shield',
title: I18n.t('Moderator'),
onPress: () => this.handleModerator(selectedUser, !isModerator),
right: () => <CustomIcon testID={isModerator ? 'action-sheet-set-moderator-checked' : 'action-sheet-set-moderator-unchecked'} name={isModerator ? 'checkbox-checked' : 'checkbox-unchecked'} size={20} color={isModerator ? themes[theme].tintActive : themes[theme].auxiliaryTintColor} />,
testID: 'action-sheet-set-moderator'
});
}
// Remove from team
if (this.permissions['edit-team-member']) {
options.push({
icon: 'logout',
danger: true,
title: I18n.t('Remove_from_Team'),
onPress: () => this.handleRemoveFromTeam(selectedUser),
testID: 'action-sheet-remove-from-team'
});
}
// Remove from room
if (this.permissions['remove-user']) {
if (this.permissions['remove-user'] && !room.teamMain) {
options.push({
icon: 'logout',
title: I18n.t('Remove_from_room'),
@ -248,7 +352,8 @@ class RoomMembersView extends React.Component {
confirmationText: I18n.t('Yes_remove_user'),
onPress: () => this.handleRemoveUserFromRoom(selectedUser)
});
}
},
testID: 'action-sheet-remove-from-room'
});
}
@ -477,7 +582,10 @@ const mapStateToProps = state => ({
setLeaderPermission: state.permissions[PERMISSION_SET_LEADER],
setOwnerPermission: state.permissions[PERMISSION_SET_OWNER],
setModeratorPermission: state.permissions[PERMISSION_SET_MODERATOR],
removeUserPermission: state.permissions[PERMISSION_REMOVE_USER]
removeUserPermission: state.permissions[PERMISSION_REMOVE_USER],
editTeamMemberPermission: state.permissions[PERMISSION_EDIT_TEAM_MEMBER],
viewAllTeamChannelsPermission: state.permissions[PERMISSION_VIEW_ALL_TEAM_CHANNELS],
viewAllTeamsPermission: state.permissions[PERMISION_VIEW_ALL_TEAMS]
});
export default connect(mapStateToProps)(withActionSheet(withTheme(RoomMembersView)));

View File

@ -0,0 +1,42 @@
import React from 'react';
import { FlatList, StyleSheet } from 'react-native';
import Animated from 'react-native-reanimated';
import PropTypes from 'prop-types';
import { isIOS } from '../../../utils/deviceInfo';
import scrollPersistTaps from '../../../utils/scrollPersistTaps';
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
const styles = StyleSheet.create({
list: {
flex: 1
},
contentContainer: {
paddingTop: 10
}
});
const List = ({ listRef, ...props }) => (
<AnimatedFlatList
testID='room-view-messages'
ref={listRef}
keyExtractor={item => item.id}
contentContainerStyle={styles.contentContainer}
style={styles.list}
inverted
removeClippedSubviews={isIOS}
initialNumToRender={7}
onEndReachedThreshold={0.5}
maxToRenderPerBatch={5}
windowSize={10}
{...props}
{...scrollPersistTaps}
/>
);
List.propTypes = {
listRef: PropTypes.object
};
export default List;

View File

@ -0,0 +1,75 @@
import React, { useCallback, useState } from 'react';
import { View, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import Animated, {
call, cond, greaterOrEq, useCode
} from 'react-native-reanimated';
import { themes } from '../../../constants/colors';
import { CustomIcon } from '../../../lib/Icons';
import { useTheme } from '../../../theme';
import Touch from '../../../utils/touch';
import { hasNotch } from '../../../utils/deviceInfo';
const SCROLL_LIMIT = 200;
const SEND_TO_CHANNEL_HEIGHT = 40;
const styles = StyleSheet.create({
container: {
position: 'absolute',
right: 15
},
button: {
borderRadius: 25
},
content: {
width: 50,
height: 50,
borderRadius: 25,
borderWidth: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
const NavBottomFAB = ({ y, onPress, isThread }) => {
const { theme } = useTheme();
const [show, setShow] = useState(false);
const handleOnPress = useCallback(() => onPress());
const toggle = v => setShow(v);
useCode(() => cond(greaterOrEq(y, SCROLL_LIMIT),
call([y], () => toggle(true)),
call([y], () => toggle(false))),
[y]);
if (!show) {
return null;
}
let bottom = hasNotch ? 100 : 60;
if (isThread) {
bottom += SEND_TO_CHANNEL_HEIGHT;
}
return (
<Animated.View style={[styles.container, { bottom }]}>
<Touch
onPress={handleOnPress}
theme={theme}
style={[styles.button, { backgroundColor: themes[theme].backgroundColor }]}
>
<View style={[styles.content, { borderColor: themes[theme].borderColor }]}>
<CustomIcon name='chevron-down' color={themes[theme].auxiliaryTintColor} size={36} />
</View>
</Touch>
</Animated.View>
);
};
NavBottomFAB.propTypes = {
y: Animated.Value,
onPress: PropTypes.func,
isThread: PropTypes.bool
};
export default NavBottomFAB;

View File

@ -1,30 +1,39 @@
import React from 'react';
import { FlatList, RefreshControl } from 'react-native';
import { RefreshControl } from 'react-native';
import PropTypes from 'prop-types';
import { Q } from '@nozbe/watermelondb';
import moment from 'moment';
import { dequal } from 'dequal';
import { Value, event } from 'react-native-reanimated';
import styles from './styles';
import database from '../../lib/database';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import RocketChat from '../../lib/rocketchat';
import log from '../../utils/log';
import EmptyRoom from './EmptyRoom';
import { isIOS } from '../../utils/deviceInfo';
import { animateNextTransition } from '../../utils/layoutAnimation';
import ActivityIndicator from '../../containers/ActivityIndicator';
import { themes } from '../../constants/colors';
import database from '../../../lib/database';
import RocketChat from '../../../lib/rocketchat';
import log from '../../../utils/log';
import EmptyRoom from '../EmptyRoom';
import { animateNextTransition } from '../../../utils/layoutAnimation';
import ActivityIndicator from '../../../containers/ActivityIndicator';
import { themes } from '../../../constants/colors';
import List from './List';
import NavBottomFAB from './NavBottomFAB';
import debounce from '../../../utils/debounce';
const QUERY_SIZE = 50;
class List extends React.Component {
const onScroll = ({ y }) => event(
[
{
nativeEvent: {
contentOffset: { y }
}
}
],
{ useNativeDriver: true }
);
class ListContainer extends React.Component {
static propTypes = {
onEndReached: PropTypes.func,
renderFooter: PropTypes.func,
renderRow: PropTypes.func,
rid: PropTypes.string,
t: PropTypes.string,
tmid: PropTypes.string,
theme: PropTypes.string,
loading: PropTypes.bool,
@ -36,34 +45,28 @@ class List extends React.Component {
showMessageInMainThread: PropTypes.bool
};
// this.state.loading works for this.onEndReached and RoomView.init
static getDerivedStateFromProps(props, state) {
if (props.loading !== state.loading) {
return {
loading: props.loading
};
}
return null;
}
constructor(props) {
super(props);
console.time(`${ this.constructor.name } init`);
console.time(`${ this.constructor.name } mount`);
this.count = 0;
this.needsFetch = false;
this.mounted = false;
this.animated = false;
this.jumping = false;
this.state = {
loading: true,
end: false,
messages: [],
refreshing: false
refreshing: false,
highlightedMessage: null
};
this.y = new Value(0);
this.onScroll = onScroll({ y: this.y });
this.query();
this.unsubscribeFocus = props.navigation.addListener('focus', () => {
this.animated = true;
});
this.viewabilityConfig = {
itemVisiblePercentThreshold: 10
};
console.timeEnd(`${ this.constructor.name } init`);
}
@ -73,17 +76,17 @@ class List extends React.Component {
}
shouldComponentUpdate(nextProps, nextState) {
const { loading, end, refreshing } = this.state;
const { refreshing, highlightedMessage } = this.state;
const {
hideSystemMessages, theme, tunread, ignored
hideSystemMessages, theme, tunread, ignored, loading
} = this.props;
if (theme !== nextProps.theme) {
return true;
}
if (loading !== nextState.loading) {
if (loading !== nextProps.loading) {
return true;
}
if (end !== nextState.end) {
if (highlightedMessage !== nextState.highlightedMessage) {
return true;
}
if (refreshing !== nextState.refreshing) {
@ -116,32 +119,14 @@ class List extends React.Component {
if (this.unsubscribeFocus) {
this.unsubscribeFocus();
}
this.clearHighlightedMessageTimeout();
console.countReset(`${ this.constructor.name }.render calls`);
}
fetchData = async() => {
const {
loading, end, messages, latest = messages[messages.length - 1]?.ts
} = this.state;
if (loading || end) {
return;
}
this.setState({ loading: true });
const { rid, t, tmid } = this.props;
try {
let result;
if (tmid) {
// `offset` is `messages.length - 1` because we append thread start to `messages` obj
result = await RocketChat.loadThreadMessages({ tmid, rid, offset: messages.length - 1 });
} else {
result = await RocketChat.loadMessagesForRoom({ rid, t, latest });
}
this.setState({ end: result.length < QUERY_SIZE, loading: false, latest: result[result.length - 1]?.ts }, () => this.loadMoreMessages(result));
} catch (e) {
this.setState({ loading: false });
log(e);
clearHighlightedMessageTimeout = () => {
if (this.highlightedMessageTimeout) {
clearTimeout(this.highlightedMessageTimeout);
this.highlightedMessageTimeout = false;
}
}
@ -198,9 +183,6 @@ class List extends React.Component {
this.unsubscribeMessages();
this.messagesSubscription = this.messagesObservable
.subscribe((messages) => {
if (messages.length <= this.count) {
this.needsFetch = true;
}
if (tmid && this.thread) {
messages = [...messages, this.thread];
}
@ -211,6 +193,7 @@ class List extends React.Component {
} else {
this.state.messages = messages;
}
// TODO: move it away from here
this.readThreads();
});
}
@ -221,7 +204,7 @@ class List extends React.Component {
this.query();
}
readThreads = async() => {
readThreads = debounce(async() => {
const { tmid } = this.props;
if (tmid) {
@ -231,39 +214,9 @@ class List extends React.Component {
// Do nothing
}
}
}
}, 300)
onEndReached = async() => {
if (this.needsFetch) {
this.needsFetch = false;
await this.fetchData();
}
this.query();
}
loadMoreMessages = (result) => {
const { end } = this.state;
if (end) {
return;
}
// handle servers with version < 3.0.0
let { hideSystemMessages = [] } = this.props;
if (!Array.isArray(hideSystemMessages)) {
hideSystemMessages = [];
}
if (!hideSystemMessages.length) {
return;
}
const hasReadableMessages = result.filter(message => !message.t || (message.t && !hideSystemMessages.includes(message.t))).length > 0;
// if this batch doesn't contain any messages that will be displayed, we'll request a new batch
if (!hasReadableMessages) {
this.onEndReached();
}
}
onEndReached = () => this.query()
onRefresh = () => this.setState({ refreshing: true }, async() => {
const { messages } = this.state;
@ -272,7 +225,7 @@ class List extends React.Component {
if (messages.length) {
try {
if (tmid) {
await RocketChat.loadThreadMessages({ tmid, rid, offset: messages.length - 1 });
await RocketChat.loadThreadMessages({ tmid, rid });
} else {
await RocketChat.loadMissedMessages({ rid, lastOpen: moment().subtract(7, 'days').toDate() });
}
@ -284,7 +237,6 @@ class List extends React.Component {
this.setState({ refreshing: false });
})
// eslint-disable-next-line react/sort-comp
update = () => {
if (this.animated) {
animateNextTransition();
@ -306,9 +258,53 @@ class List extends React.Component {
return null;
}
handleScrollToIndexFailed = (params) => {
const { listRef } = this.props;
listRef.current.getNode().scrollToIndex({ index: params.highestMeasuredFrameIndex, animated: false });
}
jumpToMessage = messageId => new Promise(async(resolve) => {
this.jumping = true;
const { messages } = this.state;
const { listRef } = this.props;
const index = messages.findIndex(item => item.id === messageId);
if (index > -1) {
listRef.current.getNode().scrollToIndex({ index, viewPosition: 0.5 });
await new Promise(res => setTimeout(res, 300));
if (!this.viewableItems.map(vi => vi.key).includes(messageId)) {
if (!this.jumping) {
return resolve();
}
await setTimeout(() => resolve(this.jumpToMessage(messageId)), 300);
return;
}
this.setState({ highlightedMessage: messageId });
this.clearHighlightedMessageTimeout();
this.highlightedMessageTimeout = setTimeout(() => {
this.setState({ highlightedMessage: null });
}, 10000);
await setTimeout(() => resolve(), 300);
} else {
listRef.current.getNode().scrollToIndex({ index: messages.length - 1, animated: false });
if (!this.jumping) {
return resolve();
}
await setTimeout(() => resolve(this.jumpToMessage(messageId)), 300);
}
});
// this.jumping is checked in between operations to make sure we're not stuck
cancelJumpToMessage = () => {
this.jumping = false;
}
jumpToBottom = () => {
const { listRef } = this.props;
listRef.current.getNode().scrollToOffset({ offset: -100 });
}
renderFooter = () => {
const { loading } = this.state;
const { rid, theme } = this.props;
const { rid, theme, loading } = this.props;
if (loading && rid) {
return <ActivityIndicator theme={theme} />;
}
@ -316,36 +312,34 @@ class List extends React.Component {
}
renderItem = ({ item, index }) => {
const { messages } = this.state;
const { messages, highlightedMessage } = this.state;
const { renderRow } = this.props;
return renderRow(item, messages[index + 1]);
return renderRow(item, messages[index + 1], highlightedMessage);
}
onViewableItemsChanged = ({ viewableItems }) => {
this.viewableItems = viewableItems;
}
render() {
console.count(`${ this.constructor.name }.render calls`);
const { rid, listRef } = this.props;
const { rid, tmid, listRef } = this.props;
const { messages, refreshing } = this.state;
const { theme } = this.props;
return (
<>
<EmptyRoom rid={rid} length={messages.length} mounted={this.mounted} theme={theme} />
<FlatList
testID='room-view-messages'
ref={listRef}
keyExtractor={item => item.id}
<List
onScroll={this.onScroll}
scrollEventThrottle={16}
listRef={listRef}
data={messages}
extraData={this.state}
renderItem={this.renderItem}
contentContainerStyle={styles.contentContainer}
style={styles.list}
inverted
removeClippedSubviews={isIOS}
initialNumToRender={7}
onEndReached={this.onEndReached}
onEndReachedThreshold={0.5}
maxToRenderPerBatch={5}
windowSize={10}
ListFooterComponent={this.renderFooter}
onScrollToIndexFailed={this.handleScrollToIndexFailed}
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
refreshControl={(
<RefreshControl
refreshing={refreshing}
@ -353,11 +347,11 @@ class List extends React.Component {
tintColor={themes[theme].auxiliaryText}
/>
)}
{...scrollPersistTaps}
/>
<NavBottomFAB y={this.y} onPress={this.jumpToBottom} isThread={!!tmid} />
</>
);
}
}
export default List;
export default ListContainer;

View File

@ -0,0 +1,62 @@
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions, react/prop-types, react/destructuring-assignment */
import React from 'react';
import { ScrollView } from 'react-native';
import { storiesOf } from '@storybook/react-native';
import LoadMore from './index';
import { longText } from '../../../../storybook/utils';
import { ThemeContext } from '../../../theme';
import {
Message, StoryProvider, MessageDecorator
} from '../../../../storybook/stories/Message';
import { themes } from '../../../constants/colors';
import { MESSAGE_TYPE_LOAD_MORE, MESSAGE_TYPE_LOAD_NEXT_CHUNK, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK } from '../../../constants/messageTypeLoad';
const stories = storiesOf('LoadMore', module);
// FIXME: for some reason, this promise never resolves on Storybook (it works on the app, so maybe the issue isn't on the component)
const load = () => new Promise(res => setTimeout(res, 1000));
stories.add('basic', () => (
<>
<LoadMore load={load} />
<LoadMore load={load} runOnRender />
<LoadMore load={load} type={MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK} />
<LoadMore load={load} type={MESSAGE_TYPE_LOAD_NEXT_CHUNK} />
</>
));
const ThemeStory = ({ theme }) => (
<ThemeContext.Provider
value={{ theme }}
>
<ScrollView style={{ backgroundColor: themes[theme].backgroundColor }}>
<LoadMore load={load} type={MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK} />
<Message msg='Hey!' theme={theme} />
<Message msg={longText} theme={theme} isHeader={false} />
<Message msg='Older message' theme={theme} isHeader={false} />
<LoadMore load={load} type={MESSAGE_TYPE_LOAD_NEXT_CHUNK} />
<LoadMore load={load} type={MESSAGE_TYPE_LOAD_MORE} />
<Message msg={longText} theme={theme} />
<Message msg='This is the third message' isHeader={false} theme={theme} />
<Message msg='This is the second message' isHeader={false} theme={theme} />
<Message msg='This is the first message' theme={theme} />
</ScrollView>
</ThemeContext.Provider>
);
stories
.addDecorator(StoryProvider)
.addDecorator(MessageDecorator)
.add('light theme', () => <ThemeStory theme='light' />);
stories
.addDecorator(StoryProvider)
.addDecorator(MessageDecorator)
.add('dark theme', () => <ThemeStory theme='dark' />);
stories
.addDecorator(StoryProvider)
.addDecorator(MessageDecorator)
.add('black theme', () => <ThemeStory theme='black' />);

View File

@ -0,0 +1,76 @@
import React, { useEffect, useCallback, useState } from 'react';
import { Text, StyleSheet, ActivityIndicator } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../../../constants/colors';
import { MESSAGE_TYPE_LOAD_NEXT_CHUNK, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK } from '../../../constants/messageTypeLoad';
import { useTheme } from '../../../theme';
import Touch from '../../../utils/touch';
import sharedStyles from '../../Styles';
import I18n from '../../../i18n';
const styles = StyleSheet.create({
button: {
paddingVertical: 16,
alignItems: 'center',
justifyContent: 'center'
},
text: {
fontSize: 16,
...sharedStyles.textMedium
}
});
const LoadMore = ({ load, type, runOnRender }) => {
const { theme } = useTheme();
const [loading, setLoading] = useState(false);
const handleLoad = useCallback(async() => {
try {
if (loading) {
return;
}
setLoading(true);
await load();
} finally {
setLoading(false);
}
}, [loading]);
useEffect(() => {
if (runOnRender) {
handleLoad();
}
}, []);
let text = 'Load_More';
if (type === MESSAGE_TYPE_LOAD_NEXT_CHUNK) {
text = 'Load_Newer';
}
if (type === MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK) {
text = 'Load_Older';
}
return (
<Touch
onPress={handleLoad}
style={styles.button}
theme={theme}
enabled={!loading}
>
{
loading
? <ActivityIndicator color={themes[theme].auxiliaryText} />
: <Text style={[styles.text, { color: themes[theme].titleText }]}>{I18n.t(text)}</Text>
}
</Touch>
);
};
LoadMore.propTypes = {
load: PropTypes.func,
type: PropTypes.string,
runOnRender: PropTypes.bool
};
export default LoadMore;

View File

@ -7,6 +7,7 @@ import * as HeaderButton from '../../containers/HeaderButton';
import database from '../../lib/database';
import { getUserSelector } from '../../selectors/login';
import { logEvent, events } from '../../utils/log';
import { isTeamRoom } from '../../utils/room';
class RightButtonsContainer extends Component {
static propTypes = {
@ -15,10 +16,11 @@ class RightButtonsContainer extends Component {
rid: PropTypes.string,
t: PropTypes.string,
tmid: PropTypes.string,
teamId: PropTypes.bool,
teamId: PropTypes.string,
navigation: PropTypes.object,
isMasterDetail: PropTypes.bool,
toggleFollowThread: PropTypes.func
toggleFollowThread: PropTypes.func,
joined: PropTypes.bool
};
constructor(props) {
@ -57,6 +59,10 @@ class RightButtonsContainer extends Component {
const {
isFollowingThread, tunread, tunreadUser, tunreadGroup
} = this.state;
const { teamId } = this.props;
if (nextProps.teamId !== teamId) {
return true;
}
if (nextState.isFollowingThread !== isFollowingThread) {
return true;
}
@ -140,12 +146,12 @@ class RightButtonsContainer extends Component {
goSearchView = () => {
logEvent(events.ROOM_GO_SEARCH);
const {
rid, navigation, isMasterDetail
rid, t, navigation, isMasterDetail
} = this.props;
if (isMasterDetail) {
navigation.navigate('ModalStackNavigator', { screen: 'SearchMessagesView', params: { rid, showCloseModal: true } });
} else {
navigation.navigate('SearchMessagesView', { rid });
navigation.navigate('SearchMessagesView', { rid, t });
}
}
@ -163,7 +169,7 @@ class RightButtonsContainer extends Component {
isFollowingThread, tunread, tunreadUser, tunreadGroup
} = this.state;
const {
t, tmid, threadsEnabled, teamId
t, tmid, threadsEnabled, teamId, joined
} = this.props;
if (t === 'l') {
return null;
@ -181,7 +187,7 @@ class RightButtonsContainer extends Component {
}
return (
<HeaderButton.Container>
{teamId ? (
{isTeamRoom({ teamId, joined }) ? (
<HeaderButton.Item
iconName='channel-public'
onPress={this.goTeamChannels}

View File

@ -2,8 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Text, View, InteractionManager } from 'react-native';
import { connect } from 'react-redux';
import parse from 'url-parse';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import moment from 'moment';
import * as Haptics from 'expo-haptics';
import { Q } from '@nozbe/watermelondb';
@ -17,7 +17,6 @@ import {
import List from './List';
import database from '../../lib/database';
import RocketChat from '../../lib/rocketchat';
import { Encryption } from '../../lib/encryption';
import Message from '../../containers/message';
import MessageActions from '../../containers/MessageActions';
import MessageErrorActions from '../../containers/MessageErrorActions';
@ -35,10 +34,13 @@ import RightButtons from './RightButtons';
import StatusBar from '../../containers/StatusBar';
import Separator from './Separator';
import { themes } from '../../constants/colors';
import { MESSAGE_TYPE_ANY_LOAD, MESSAGE_TYPE_LOAD_MORE } from '../../constants/messageTypeLoad';
import debounce from '../../utils/debounce';
import ReactionsModal from '../../containers/ReactionsModal';
import { LISTENER } from '../../containers/Toast';
import { getBadgeColor, isBlocked, makeThreadName } from '../../utils/room';
import {
getBadgeColor, isBlocked, makeThreadName, isTeamRoom
} from '../../utils/room';
import { isReadOnly } from '../../utils/isReadOnly';
import { isIOS, isTablet } from '../../utils/deviceInfo';
import { showErrorAlert } from '../../utils/info';
@ -62,6 +64,12 @@ import { getHeaderTitlePosition } from '../../containers/Header';
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
import { takeInquiry } from '../../ee/omnichannel/lib';
import Loading from '../../containers/Loading';
import LoadMore from './LoadMore';
import RoomServices from './services';
import { goRoom } from '../../utils/goRoom';
import getThreadName from '../../lib/methods/getThreadName';
import getRoomInfo from '../../lib/methods/getRoomInfo';
const stateAttrsUpdate = [
'joined',
@ -74,9 +82,10 @@ const stateAttrsUpdate = [
'replying',
'reacting',
'readOnly',
'member'
'member',
'showingBlockingLoader'
];
const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'tunread', 'muted', 'ignored', 'jitsiTimeout', 'announcement', 'sysMes', 'topic', 'name', 'fname', 'roles', 'bannerClosed', 'visitor', 'joinCodeRequired'];
const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'tunread', 'muted', 'ignored', 'jitsiTimeout', 'announcement', 'sysMes', 'topic', 'name', 'fname', 'roles', 'bannerClosed', 'visitor', 'joinCodeRequired', 'teamMain', 'teamId'];
class RoomView extends React.Component {
static propTypes = {
@ -115,11 +124,11 @@ class RoomView extends React.Component {
const selectedMessage = props.route.params?.message;
const name = props.route.params?.name;
const fname = props.route.params?.fname;
const search = props.route.params?.search;
const prid = props.route.params?.prid;
const room = props.route.params?.room ?? {
rid: this.rid, t: this.t, name, fname, prid
};
this.jumpToMessageId = props.route.params?.jumpToMessageId;
const roomUserId = props.route.params?.roomUserId ?? RocketChat.getUidDirectMessage(room);
this.state = {
joined: true,
@ -131,6 +140,7 @@ class RoomView extends React.Component {
selectedMessage: selectedMessage || {},
canAutoTranslate: false,
loading: true,
showingBlockingLoader: false,
editing: false,
replying: !!selectedMessage,
replyWithMention: false,
@ -149,13 +159,10 @@ class RoomView extends React.Component {
this.setReadOnly();
if (search) {
this.updateRoom();
}
this.messagebox = React.createRef();
this.list = React.createRef();
this.joinCode = React.createRef();
this.flatList = React.createRef();
this.mounted = false;
// we don't need to subscribe to threads
@ -179,6 +186,9 @@ class RoomView extends React.Component {
EventEmitter.addEventListener('connected', this.handleConnected);
}
}
if (this.jumpToMessageId) {
this.jumpToMessage(this.jumpToMessageId);
}
if (isIOS && this.rid) {
this.updateUnreadCount();
}
@ -193,7 +203,9 @@ class RoomView extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
const { state } = this;
const { roomUpdate, member } = state;
const { appState, theme, insets } = this.props;
const {
appState, theme, insets, route
} = this.props;
if (theme !== nextProps.theme) {
return true;
}
@ -210,12 +222,19 @@ class RoomView extends React.Component {
if (!dequal(nextProps.insets, insets)) {
return true;
}
if (!dequal(nextProps.route?.params, route?.params)) {
return true;
}
return roomAttrsUpdate.some(key => !dequal(nextState.roomUpdate[key], roomUpdate[key]));
}
componentDidUpdate(prevProps, prevState) {
const { roomUpdate } = this.state;
const { appState, insets } = this.props;
const { appState, insets, route } = this.props;
if (route?.params?.jumpToMessageId !== prevProps.route?.params?.jumpToMessageId) {
this.jumpToMessage(route?.params?.jumpToMessageId);
}
if (appState === 'foreground' && appState !== prevProps.appState && this.rid) {
// Fire List.query() just to keep observables working
@ -235,7 +254,10 @@ class RoomView extends React.Component {
this.setHeader();
}
}
if (((roomUpdate.fname !== prevState.roomUpdate.fname) || (roomUpdate.name !== prevState.roomUpdate.name)) && !this.tmid) {
if ((roomUpdate.teamMain !== prevState.roomUpdate.teamMain) || (roomUpdate.teamId !== prevState.roomUpdate.teamId)) {
this.setHeader();
}
if (((roomUpdate.fname !== prevState.roomUpdate.fname) || (roomUpdate.name !== prevState.roomUpdate.name) || (roomUpdate.teamMain !== prevState.roomUpdate.teamMain) || (roomUpdate.teamId !== prevState.roomUpdate.teamId)) && !this.tmid) {
this.setHeader();
}
if (insets.left !== prevProps.insets.left || insets.right !== prevProps.insets.right) {
@ -301,7 +323,7 @@ class RoomView extends React.Component {
setHeader = () => {
const {
room, unreadsCount, roomUserId
room, unreadsCount, roomUserId, joined
} = this.state;
const {
navigation, isMasterDetail, theme, baseUrl, user, insets, route
@ -331,7 +353,7 @@ class RoomView extends React.Component {
let numIconsRight = 2;
if (tmid) {
numIconsRight = 1;
} else if (teamId) {
} else if (isTeamRoom({ teamId, joined })) {
numIconsRight = 3;
}
const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight });
@ -380,6 +402,8 @@ class RoomView extends React.Component {
rid={rid}
tmid={tmid}
teamId={teamId}
teamMain={teamMain}
joined={joined}
t={t}
navigation={navigation}
toggleFollowThread={this.toggleFollowThread}
@ -413,34 +437,15 @@ class RoomView extends React.Component {
this.setState({ readOnly });
}
updateRoom = async() => {
const db = database.active;
try {
const subCollection = db.get('subscriptions');
const sub = await subCollection.find(this.rid);
const { room } = await RocketChat.getRoomInfo(this.rid);
await db.action(async() => {
await sub.update((s) => {
Object.assign(s, room);
});
});
} catch {
// do nothing
}
}
init = async() => {
try {
this.setState({ loading: true });
const { room, joined } = this.state;
if (this.tmid) {
await this.getThreadMessages();
await RoomServices.getThreadMessages(this.tmid, this.rid);
} else {
const newLastOpen = new Date();
await this.getMessages(room);
await RoomServices.getMessages(room);
// if room is joined
if (joined) {
@ -449,7 +454,7 @@ class RoomView extends React.Component {
} else {
this.setLastOpen(null);
}
RocketChat.readMessages(room.rid, newLastOpen, true).catch(e => console.log(e));
RoomServices.readMessages(room.rid, newLastOpen, true).catch(e => console.log(e));
}
}
@ -656,26 +661,69 @@ class RoomView extends React.Component {
});
};
onThreadPress = debounce(async(item) => {
const { roomUserId } = this.state;
const { navigation } = this.props;
if (item.tmid) {
if (!item.tmsg) {
await this.fetchThreadName(item.tmid, item.id);
}
let name = item.tmsg;
if (item.t === E2E_MESSAGE_TYPE && item.e2e !== E2E_STATUS.DONE) {
name = I18n.t('Encrypted_message');
}
navigation.push('RoomView', {
rid: item.subscription.id, tmid: item.tmid, name, t: 'thread', roomUserId
});
} else if (item.tlm) {
navigation.push('RoomView', {
rid: item.subscription.id, tmid: item.id, name: makeThreadName(item), t: 'thread', roomUserId
});
onThreadPress = debounce(item => this.navToThread(item), 1000, true)
shouldNavigateToRoom = (message) => {
if (message.tmid && message.tmid === this.tmid) {
return false;
}
}, 1000, true)
if (!message.tmid && message.rid === this.rid) {
return false;
}
return true;
}
jumpToMessageByUrl = async(messageUrl) => {
if (!messageUrl) {
return;
}
try {
this.setState({ showingBlockingLoader: true });
const parsedUrl = parse(messageUrl, true);
const messageId = parsedUrl.query.msg;
await this.jumpToMessage(messageId);
this.setState({ showingBlockingLoader: false });
} catch (e) {
this.setState({ showingBlockingLoader: false });
log(e);
}
}
jumpToMessage = async(messageId) => {
try {
this.setState({ showingBlockingLoader: true });
const message = await RoomServices.getMessageInfo(messageId);
if (!message) {
return;
}
if (this.shouldNavigateToRoom(message)) {
if (message.rid !== this.rid) {
this.navToRoom(message);
} else {
this.navToThread(message);
}
} else {
/**
* if it's from server, we don't have it saved locally and so we fetch surroundings
* we test if it's not from threads because we're fetching from threads currently with `getThreadMessages`
*/
if (message.fromServer && !message.tmid) {
await RocketChat.loadSurroundingMessages({ messageId, rid: this.rid });
}
await Promise.race([
this.list.current.jumpToMessage(message.id),
new Promise(res => setTimeout(res, 5000))
]);
this.list.current.cancelJumpToMessage();
}
} catch (e) {
log(e);
} finally {
this.setState({ showingBlockingLoader: false });
}
}
replyBroadcast = (message) => {
const { replyBroadcast } = this.props;
@ -714,17 +762,6 @@ class RoomView extends React.Component {
});
};
getMessages = () => {
const { room } = this.state;
if (room.lastOpen) {
return RocketChat.loadMissedMessages(room);
} else {
return RocketChat.loadMessagesForRoom(room);
}
}
getThreadMessages = () => RocketChat.loadThreadMessages({ tmid: this.tmid, rid: this.rid })
getCustomEmoji = (name) => {
const { customEmojis } = this.props;
const emoji = customEmojis[name];
@ -763,45 +800,7 @@ class RoomView extends React.Component {
}
}
// eslint-disable-next-line react/sort-comp
fetchThreadName = async(tmid, messageId) => {
try {
const db = database.active;
const threadCollection = db.get('threads');
const messageCollection = db.get('messages');
const messageRecord = await messageCollection.find(messageId);
let threadRecord;
try {
threadRecord = await threadCollection.find(tmid);
} catch (error) {
console.log('Thread not found. We have to search for it.');
}
if (threadRecord) {
await db.action(async() => {
await messageRecord.update((m) => {
m.tmsg = threadRecord.msg || (threadRecord.attachments && threadRecord.attachments.length && threadRecord.attachments[0].title);
});
});
} else {
let { message: thread } = await RocketChat.getSingleMessage(tmid);
thread = await Encryption.decryptMessage(thread);
await db.action(async() => {
await db.batch(
threadCollection.prepareCreate((t) => {
t._raw = sanitizedRaw({ id: thread._id }, threadCollection.schema);
t.subscription.id = this.rid;
Object.assign(t, thread);
}),
messageRecord.prepareUpdate((m) => {
m.tmsg = thread.msg || (thread.attachments && thread.attachments.length && thread.attachments[0].title);
})
);
});
}
} catch (e) {
// log(e);
}
}
getThreadName = (tmid, messageId) => getThreadName(this.rid, tmid, messageId)
toggleFollowThread = async(isFollowingThread, tmid) => {
try {
@ -832,6 +831,38 @@ class RoomView extends React.Component {
}
}
navToThread = async(item) => {
const { roomUserId } = this.state;
const { navigation } = this.props;
if (item.tmid) {
let name = item.tmsg;
if (!name) {
name = await this.getThreadName(item.tmid, item.id);
}
if (item.t === E2E_MESSAGE_TYPE && item.e2e !== E2E_STATUS.DONE) {
name = I18n.t('Encrypted_message');
}
return navigation.push('RoomView', {
rid: this.rid, tmid: item.tmid, name, t: 'thread', roomUserId, jumpToMessageId: item.id
});
}
if (item.tlm) {
return navigation.push('RoomView', {
rid: this.rid, tmid: item.id, name: makeThreadName(item), t: 'thread', roomUserId
});
}
}
navToRoom = async(message) => {
const { navigation, isMasterDetail } = this.props;
const roomInfo = await getRoomInfo(message.rid);
return goRoom({
item: roomInfo, isMasterDetail, navigationMethod: navigation.push, jumpToMessageId: message.id
});
}
callJitsi = () => {
const { room } = this.state;
const { jitsiTimeout } = room;
@ -896,7 +927,11 @@ class RoomView extends React.Component {
return room?.ignored?.includes?.(message?.u?._id) ?? false;
}
renderItem = (item, previousItem) => {
onLoadMoreMessages = loaderItem => RoomServices.getMoreMessages({
rid: this.rid, tmid: this.tmid, t: this.t, loaderItem
})
renderItem = (item, previousItem, highlightedMessage) => {
const { room, lastOpen, canAutoTranslate } = this.state;
const {
user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, Message_Read_Receipt_Enabled, theme
@ -916,48 +951,55 @@ class RoomView extends React.Component {
}
}
const message = (
<Message
item={item}
user={user}
rid={room.rid}
archived={room.archived}
broadcast={room.broadcast}
status={item.status}
isThreadRoom={!!this.tmid}
isIgnored={this.isIgnored(item)}
previousItem={previousItem}
fetchThreadName={this.fetchThreadName}
onReactionPress={this.onReactionPress}
onReactionLongPress={this.onReactionLongPress}
onLongPress={this.onMessageLongPress}
onEncryptedPress={this.onEncryptedPress}
onDiscussionPress={this.onDiscussionPress}
onThreadPress={this.onThreadPress}
showAttachment={this.showAttachment}
reactionInit={this.onReactionInit}
replyBroadcast={this.replyBroadcast}
errorActionsShow={this.errorActionsShow}
baseUrl={baseUrl}
Message_GroupingPeriod={Message_GroupingPeriod}
timeFormat={Message_TimeFormat}
useRealName={useRealName}
isReadReceiptEnabled={Message_Read_Receipt_Enabled}
autoTranslateRoom={canAutoTranslate && room.autoTranslate}
autoTranslateLanguage={room.autoTranslateLanguage}
navToRoomInfo={this.navToRoomInfo}
getCustomEmoji={this.getCustomEmoji}
callJitsi={this.callJitsi}
blockAction={this.blockAction}
threadBadgeColor={this.getBadgeColor(item?.id)}
toggleFollowThread={this.toggleFollowThread}
/>
);
let content = null;
if (MESSAGE_TYPE_ANY_LOAD.includes(item.t)) {
content = <LoadMore load={() => this.onLoadMoreMessages(item)} type={item.t} runOnRender={item.t === MESSAGE_TYPE_LOAD_MORE && !previousItem} />;
} else {
content = (
<Message
item={item}
user={user}
rid={room.rid}
archived={room.archived}
broadcast={room.broadcast}
status={item.status}
isThreadRoom={!!this.tmid}
isIgnored={this.isIgnored(item)}
previousItem={previousItem}
fetchThreadName={this.getThreadName}
onReactionPress={this.onReactionPress}
onReactionLongPress={this.onReactionLongPress}
onLongPress={this.onMessageLongPress}
onEncryptedPress={this.onEncryptedPress}
onDiscussionPress={this.onDiscussionPress}
onThreadPress={this.onThreadPress}
showAttachment={this.showAttachment}
reactionInit={this.onReactionInit}
replyBroadcast={this.replyBroadcast}
errorActionsShow={this.errorActionsShow}
baseUrl={baseUrl}
Message_GroupingPeriod={Message_GroupingPeriod}
timeFormat={Message_TimeFormat}
useRealName={useRealName}
isReadReceiptEnabled={Message_Read_Receipt_Enabled}
autoTranslateRoom={canAutoTranslate && room.autoTranslate}
autoTranslateLanguage={room.autoTranslateLanguage}
navToRoomInfo={this.navToRoomInfo}
getCustomEmoji={this.getCustomEmoji}
callJitsi={this.callJitsi}
blockAction={this.blockAction}
threadBadgeColor={this.getBadgeColor(item?.id)}
toggleFollowThread={this.toggleFollowThread}
jumpToMessage={this.jumpToMessageByUrl}
highlighted={highlightedMessage === item.id}
/>
);
}
if (showUnreadSeparator || dateSeparator) {
return (
<>
{message}
{content}
<Separator
ts={dateSeparator}
unread={showUnreadSeparator}
@ -967,7 +1009,7 @@ class RoomView extends React.Component {
);
}
return message;
return content;
}
renderFooter = () => {
@ -1053,12 +1095,10 @@ class RoomView extends React.Component {
);
}
setListRef = ref => this.flatList = ref;
render() {
console.count(`${ this.constructor.name }.render calls`);
const {
room, reactionsModalVisible, selectedMessage, loading, reacting
room, reactionsModalVisible, selectedMessage, loading, reacting, showingBlockingLoader
} = this.state;
const {
user, baseUrl, theme, navigation, Hide_System_Messages, width, height
@ -1083,7 +1123,7 @@ class RoomView extends React.Component {
/>
<List
ref={this.list}
listRef={this.setListRef}
listRef={this.flatList}
rid={rid}
t={t}
tmid={this.tmid}
@ -1123,6 +1163,7 @@ class RoomView extends React.Component {
t={t}
theme={theme}
/>
<Loading visible={showingBlockingLoader} />
</SafeAreaView>
);
}

View File

@ -0,0 +1,41 @@
import { getMessageById } from '../../../lib/database/services/Message';
import { getThreadMessageById } from '../../../lib/database/services/ThreadMessage';
import getSingleMessage from '../../../lib/methods/getSingleMessage';
const getMessageInfo = async(messageId) => {
let result;
result = await getMessageById(messageId);
if (result) {
return {
id: result.id,
rid: result.subscription.id,
tmid: result.tmid,
msg: result.msg
};
}
result = await getThreadMessageById(messageId);
if (result) {
return {
id: result.id,
rid: result.subscription.id,
tmid: result.rid,
msg: result.msg
};
}
result = await getSingleMessage(messageId);
if (result) {
return {
id: result._id,
rid: result.rid,
tmid: result.tmid,
msg: result.msg,
fromServer: true
};
}
return null;
};
export default getMessageInfo;

View File

@ -0,0 +1,10 @@
import RocketChat from '../../../lib/rocketchat';
const getMessages = (room) => {
if (room.lastOpen) {
return RocketChat.loadMissedMessages(room);
} else {
return RocketChat.loadMessagesForRoom(room);
}
};
export default getMessages;

View File

@ -0,0 +1,19 @@
import { MESSAGE_TYPE_LOAD_MORE, MESSAGE_TYPE_LOAD_NEXT_CHUNK, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK } from '../../../constants/messageTypeLoad';
import RocketChat from '../../../lib/rocketchat';
const getMoreMessages = ({
rid, t, tmid, loaderItem
}) => {
if ([MESSAGE_TYPE_LOAD_MORE, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK].includes(loaderItem.t)) {
return RocketChat.loadMessagesForRoom({
rid, t, latest: loaderItem.ts, loaderItem
});
}
if (loaderItem.t === MESSAGE_TYPE_LOAD_NEXT_CHUNK) {
return RocketChat.loadNextMessages({
rid, tmid, ts: loaderItem.ts, loaderItem
});
}
};
export default getMoreMessages;

View File

@ -0,0 +1,6 @@
import RocketChat from '../../../lib/rocketchat';
// unlike getMessages, sync isn't required for threads, because loadMissedMessages does it already
const getThreadMessages = (tmid, rid) => RocketChat.loadThreadMessages({ tmid, rid });
export default getThreadMessages;

View File

@ -0,0 +1,13 @@
import getMessages from './getMessages';
import getMoreMessages from './getMoreMessages';
import getThreadMessages from './getThreadMessages';
import readMessages from './readMessages';
import getMessageInfo from './getMessageInfo';
export default {
getMessages,
getMoreMessages,
getThreadMessages,
readMessages,
getMessageInfo
};

Some files were not shown because too many files have changed in this diff Show More