Merge beta into master (#2445)

* [FIX] Close tablet modal (#1773)

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

* [FIX] Setting not present (#1775)

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

* [FIX] Thread header (#1776)

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

* [FIX] Keyboard tracking loses input ref (#1784)

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

* [NEW] Mark message as unread (#1785)

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

* [IMPROVEMENT] Log server version (#1786)

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

* [IMPROVEMENT] Add loading message on long running tasks (#1798)

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

* [CHORE] Switch Apple account on Fastlane (#1810)

* [FIX] Watermelon throwing "Cannot update a record with pending updates" (#1754)

* [FIX] Detox tests (#1790)

* [CHORE] Use markdown preview on RoomView Header (#1807)

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

* [FIX] LoginSignup blink services (#1809)

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

* [IMPROVEMENT] Request user presence on demand (#1813)

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

* [FIX] Remove all invited users when create a channel (#1814)

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

* [FIX] Pop from room which you have been removed (#1819)

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

* [FIX] Room Info styles (#1820)

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

* [i18n] Add missing German keys (#1800)

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

* [FIX] Empty mentions for @all and @here when real name is enabled (#1822)

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

* [TESTS] Markdown added to Storybook (#1812)

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

* [REGRESSION] Room View header title (#1827)

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

* [FIX] Storybook snapshots (#1831)

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

* [FIX] Mentions (#1829)

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

* [FIX] Thread message not found (#1830)

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

* [FIX] Separate delete and remove channel (#1832)

* Rename to delete room

* Separate delete and remove channel

* handleRemoved -> handleRoomRemoved

* [FIX] Navigate to RoomsList & Handle tablet case

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

* [NEW] Filter system messages per room (#1815)

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

* [FIX] e2e tests (#1838)

* [FIX] Consecutive clear cache calls freezing app (#1851)

* Bump version to 4.5.1 (#1853)

* [FIX][iOS] Ignore silent mode on audio player (#1862)

* [IMPROVEMENT] Create App Group property on Info.plist (#1858)

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

* [IMPROVEMENT] Make username clickable on message (#1618)

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

* [FIX] Show proper error message on profile (#1768)

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

* [IMPROVEMENT] Show toast when a message is starred/unstarred (#1616)

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

* [FIX] Incorrect size params to avatar endpoint (#1875)

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

* [FIX] Remove unrecognized emoji flags on android  (#1887)

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

* [FIX] Remove react-native global installs (#1886)

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

* [FIX] Emojis transparent on android (#1881)

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

* Bump acorn from 5.7.3 to 5.7.4 (#1876)

Bumps [acorn](https://github.com/acornjs/acorn) from 5.7.3 to 5.7.4.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/5.7.3...5.7.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>

* Bump version to 4.6.0 (#1911)

* [FIX] Encode Image URI (#1909)

* [FIX] Encode Image URI

* [FIX] Check if Image is Valid

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

* [NEW] Adaptive Icons (#1904)

* Remove unnecessary stuff from debug build

* Adaptive icon for experimental app

* [FIX] Stop showing message on leave channel (#1896)

* [FIX] Leave room don't show 'was removed' message

* [FIX] Remove duplicated code

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

* [i18n] Added missing German translations(#1900)

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

* [FIX] Linkedin OAuth login (#1913)

* [CHORE] Fix typo in CreateChannel View (#1930)

* [FIX] Respect protocol in HTTP Auth IPs (#1933)

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

* [FIX] Use new LinkedIn OAuth url (#1935)

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

* [CHORE] Use storyboard on splash screen (#1939)

* Update react-native-bootsplash

* iOS

* Fix android

* [FIX] Check if avatar exists before create Icon (#1927)

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

* [FIX] Ignore self typing event (#1950)

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

* [FIX] Change default directory listing to Users (#1948)

* fix: change default directory listing to Users

* follow server settings

* Fix state to props

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

* [NEW] Onboarding layout (#1954)

* Onboarding texts

* OnboardingView

* FormContainer

* Minor fixes

* NewServerView

* Remove code

* Refactor

* WorkspaceView

* Stash

* Login with email working

* Login with

* Join open

* Revert "Login with"

This reverts commit d05dc507d2.

* Fix create account styles

* Register

* Refactor

* LoginServices component

* Refactor

* Multiple servers

* Remove native images

* Refactor styles

* Fix testid

* Fix add server on tablet

* i18n

* Fix close modal

* Fix TOTP

* [FIX] Registration disabled

* [FIX] Login Services separator

* Fix logos

* Fix AppVersion name

* I18n

* Minor fixes

* [FIX] Custom Fields

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

* [NEW] Create discussions (#1942)

* [WIP][NEW] Create Discussion

* [FIX] Clear multiselect & Translations

* [NEW] Create Discussion at MessageActions

* [NEW] Disabled Multiselect

* [FIX] Initial channel

* [NEW] Create discussion on MessageBox Actions

* [FIX] Crashing on edit name

* [IMPROVEMENT] New message layout

* [CHORE] Update README

* [NEW] Avatars on MultiSelect

* [FIX] Select Users

* [FIX] Add redirect and Handle tablet

* [IMPROVEMENT] Split CreateDiscussionView

* [FIX] Create a discussion inner discussion

* [FIX] Create a discussion

* [I18N] Add pt-br

* Change icons

* [FIX] Nav to discussion & header title

* Fix header

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

* [FIX] Load messages (#1910)

* Create updateLastOpen param on readMessages

* Remove InteractionManager from load messages

* [NEW] Custom Status (#1811)

* [NEW] Custom Status

* [FIX] Subscribe to changes

* [FIX] Improve code using Banner component

* [IMPROVEMENT] Toggle modal

* [NEW] Edit custom status from Sidebar

* [FIX] Modal when tablet

* [FIX] Styles

* [FIX] Switch to react-native-promp-android

* [FIX] Custom Status UI

* [TESTS] E2E Custom Status

* Fix banner

* Fix banner

* Fix subtitle

* status text

* Fix topic header

* Fix RoomActionsView topic

* Fix header alignment on Android

* [FIX] RoomInfo crashes when without statusText

* [FIX] Use users.setStatus

* [FIX] Remove customStatus of ProfileView

* [FIX] Room View Thread Header

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

* [FIX] UI issues of Create Discussion View (#1965)

* [NEW] Direct Message between multiple users (#1958)

* [WIP] DM between multiple users

* [WIP][NEW] Create new DM between multiple users

* [IMPROVEMENT] Improve createChannel Sagas

* [IMPROVEMENT] Selected Users view

* [IMPROVEMENT] Room Actions of Group DM

* [NEW] Create new DM between multiple users

* [NEW] Group DM avatar

* [FIX] Directory border

* [IMPROVEMENT] Use isGroupChat

* [CHORE] Remove legacy getRoomMemberId

* [NEW] RoomTypeIcon

* [FIX] No use legacy method on RoomInfoView

* [FIX] Blink header when create new DM

* [FIX] Only show create direct message option when allowed

* [FIX] RoomInfoView

* pt-BR

* Few fixes

* Create button name

* Show create button only after a user is selected

* Fix max users issues

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

* [FIX] Add server and hide login (#1968)

* Navigate to new server workspace from ServerDropdown if there's no token

* Hide login button based on login services and Accounts_ShowFormLogin setting

* [FIX] Lint

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

* [FIX] MultiSelect Keyboard behavior (Android) (#1969)

* fixed-modal-position

* made-changes

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

* [FIX] Bottom border style on DirectoryView (#1963)

* [FIX] Border style

* [FIX] Refactoring

* [FIX] fix color of border

* Undo

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

* [FIX] Clear settings on server change (#1967)

* [FIX] Deeplinking without RoomId (#1925)

* [FIX] Deeplinking without rid

* [FIX] Join channel

* [FIX] Deep linking without rid

* Update app/lib/methods/canOpenRoom.js

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

* [NEW] Two Factor authentication via email (#1961)

* First api call working

* [NEW] REST API Post wrapper 2FA

* [NEW] Send 2FA on Email

* [I18n] Add translations

* [NEW] Translations & Cancel totp

* [CHORE] Totp -> TwoFactor

* [NEW] Two Factor by email

* [NEW] Tablet Support

* [FIX] Text colors

* [NEW] Password 2fa

* [FIX] Encrypt password on 2FA

* [NEW] MethodCall2FA

* [FIX] Password fallback

* [FIX] Wrap all post/methodCall with 2fa

* [FIX] Wrap missed function

* few fixes

* [FIX] Use new TOTP on Login

* [improvement] 2fa methodCall

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

* [FIX] Correct message for manual approval user Registration (#1906)

* [FIX] Correct message for manual approval from admin shown on Registeration

* lint fix - added semicolon

* Updated the translations

* [FIX] Translations

* i18n to match server

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

* [FIX] Direct Message between multiple users REST (#1974)

* [FIX] Investigate app losing connection issues (#1890)

* [WIP] Reopen without timeOut & ping with 5 sec & Fix Unsubscribe

* [FIX] Remove duplicated close

* [FIX] Use no-dist lib

* [FIX] Try minor fix

* [FIX] Try reopen connection when app was put on foreground

* [FIX] Remove timeout

* [FIX] Build

* [FIX] Patch

* [FIX] Snapshot

* [IMPROVEMENT] Decrease time to reopen

* [FIX] Some fixes

* [FIX] Update sdk version

* [FIX] Subscribe Room Once

* [CHORE] Update sdk

* [FIX] Subscribe Room

* [FIX] Try to resend missed subs

* [FIX] Users never show status when start app without network

* [FIX] Subscribe to room

* [FIX] Multiple servers

* [CHORE] Update SDK

* [FIX] Don't duplicate streams on subscribeAll

* [FIX] Server version when start the app offline

* [FIX] Server version cached

* [CHORE] Remove unnecessary code

* [FIX] Offline server version

* [FIX] Subscribe before connect

* [FIX] Remove unncessary props

* [FIX] Update sdk

* [FIX] User status & Unsubscribe Typing

* [FIX] Typing at incorrect room

* [FIX] Multiple Servers

* [CHORE] Update SDK

* [REVERT] Undo some changes on SDK

* [CHORE] Update sdk to prevent incorrect subscribes

* [FIX] Prevent no reconnect

* [FIX] Remove close on open

* [FIX] Clear typing when disconnect/connect to SDK

* [CHORE] Update SDK

* [CHORE] Update SDK

* Update SDK

* fix merge develop

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

* [FIX] Single message thread inserting thread without rid (#1999)

* [FIX] ThreadMessagesView crashing on load (#1997)

* [FIX] Saml (#1996)

* [FIX] SAML incorrect close

* [FIX] Pathname

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

* [FIX] Change user own status (#1995)

* [FIX] Change user own status

* [IMPROVEMENT] Set activeUsers

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

* [FIX] Loading all updated rooms after app resume (#1998)

* [FIX] Loading all updated rooms after app resume

* Fix room date on RoomItem

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

* [FIX] Change notifications preferences (#2000)

* [FIX] Change notifications preferences

* [IMPROVEMENT] Picker View

* [I18N] Translations

* [FIX] Picker Selection

* [FIX] List border

* [FIX] Prevent crash

* [FIX] Not-Pref tablet

* [FIX] Use same style of LanguageView

* [IMPROVEMENT] Send listItem title

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

* Bump version to 4.6.1 (#2001)

* [FIX] DM header blink (#2011)

* [FIX] Split get settings into two requests (#2017)

* [FIX] Split get settings into two requests

* [FIX] Clear settings only when change server

* [IMPROVEMENT] Move the way to clear settings

* [REVERT] Revert some changes

* [FIX] Server Icon

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

* [REGRESSION] Invite Links (#2007)

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

* [FIX] Read only channel/broadcast (#1951)

* [FIX] Read only channel/broadcast

* [FIX] Roles missing

* [FIX] Check roles to readOnly

* [FIX] Can post

* [FIX] Respect post-readonly permission

* [FIX] Search a room readOnly

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

* [FIX] Cas auth (#2024)

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

* [FIX] Login TOTP Compatibility to older servers (#2018)

* [FIX] Login TOTP Compatibility to older servers

* [FIX] Android crashes if use double negation

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

* Bump version to 4.6.4 (#2029)

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

* [FIX] Lint (#2030)

* [FIX] UIKit with only one block (#2022)

* [FIX] Message with only one block

* [FIX] Update headers

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

* Bump version to 4.7.0 (#2035)

* [FIX] Action Tint Color on Black theme (#2081)

* [FIX] Prevent crash when thread is not found (#2080)

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

* [FIX] Prevent double click (#2079)

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

* [FIX] Show slash commands when disconnected (#2078)

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

* [FIX] Backhandler onboarding (#2077)

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

* [FIX] Respect UI_Allow_room_names_with_special_chars setting (#2076)

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

* [FIX] RoomsList update sometimes isn't fired (#2071)

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

* [IMPROVEMENT] Stop inserting last message as message object from rooms stream if room is focused (#2069)

* [IMPROVEMENT] No insert last message if the room is focused

* fix discussion/threads

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

* [FIX] Hide system messages (#2067)

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

* [FIX] Pending update (#2066)

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

* [FIX] Prevent crash when room.uids was not inserted yet (#2055)

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

* [FEATURE] Save video (#2063)

* added-feature-save-video

* fix sha256

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

* [FIX] Send totp-code to meteor call (#2050)

* fixed-issue

* removed-variable-name-errors

* reverted-last-commit

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

* [FIX] MessageBox mention shouldn't show group DMs (#2049)

* fixed-issue

* [FIX] Filter users only if it's not a group chat

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

* [FIX] AttachmentView (Android)(Tablet) (#2047)

* [fix]Tablet attachment View and Room Navigation

* fix weird navigation and margin bottom

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

* [FIX] Allow special chars in Filename (#2020)

* fixed-filename-issue

* improve

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

* [FIX] Recorded audio on Android doesn't play on iOS (#2073)

* react-native-video -> expo-av

* remove react-native-video

* Add audio mode

* update mocks

* [FIX] Loading bigger than play/pause

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

* [IMPROVEMENT] Message Touchable (#2082)

* [FIX] Avatar touchable

* [IMPROVEMENT] onLongPress on all Message Touchables

* [IMPROVEMENT] User & baseUrl on MessageContext

* [FIX] Context Access

* [FIX] BaseURL

* Fix User

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

* [FIX] ReactionsModal (#2085)

* [NEW] Delete Server (#1975)

* [NEW] Delete server

Co-authored-by: Bruno Dantas <oliveiradantas96@gmail.com>
Co-authored-by: Calebe Rios <calebersmendes@gmail.com>

* [FIX] Revert removed function

Co-authored-by: Bruno Dantas <oliveiradantas96@gmail.com>
Co-authored-by: Calebe Rios <calebersmendes@gmail.com>

* pods

* i18n

* Revert "pods"

This reverts commit 2854a16505.

Co-authored-by: Bruno Dantas <oliveiradantas96@gmail.com>
Co-authored-by: Calebe Rios <calebersmendes@gmail.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [IMPROVEMENT] Change server while connecting/updating (#1981)

* [IMPROVEMENT] Change server while connecting

* [FIX] Not login/reconnect to previous server

* [FIX] Abort all fetch while connecting

* [FIX] Abort sdk fetch

* [FIX] Patch-package

* Add comments

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

* [IMPROVEMENT] Keep screen awake while recording/playing some audio (#2089)

* [IMPROVEMENT] Keep screen awake while recording/playing some audio

* [FIX] Add expo-keep-awake mock

* [FIX] UIKit crashing when UIKitModal receive update event (#2088)

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

* [IMPROVEMENT] Close announcement banner (#2064)

* [NEW] Created new field in subscription table

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

* [NEW] New field added to obeserver in room view

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

* [NEW] Added icon and new design to banner

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

* [NEW] Close banner function works

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

* [IMPROVEMENT] closed banner status now update correctly

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

* improve banner style

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

* Update all dependencies (#2008)

* Android RN 62

* First steps iOS

* Second step iOS

* iOS compiling

* "New" build system

* Finish iOS

* Flipper

* Update to RN 0.62.1

* expo libs

* Hermes working

* Fix lint

* Fix android build

* Patches

* Dev patches

* Patch WatermelonDB: https://github.com/Nozbe/WatermelonDB/pull/660

* Fix jitsi

* Update several minors

* Update dev minors and lint

* react-native-keyboard-input

* Few updates

* device info

* react-native-fast-image

* Navigation bar color

* react-native-picker-select

* webview

* reactotron-react-native

* Watermelondb

* RN 0.62.2

* Few updates

* Fix selection

* update gems

* remove lib

* finishing

* tests

* Use node 10

* Re-enable app bundle

* iOS build

* Update jitsi ios

* [NEW] Passcode and biometric unlock (#2059)

* Update expo libs

* Configure expo-local-authentication

* ScreenLockedView

* Authenticate server change

* Auth on app resume

* localAuthentication util

* Add servers.lastLocalAuthenticatedSession column

* Save last session date on background

* Use our own version of app state redux

* Fix libs

* Remove inactive

* ScreenLockConfigView

* Apply on saved data

* Auto lock option label

* Starting passcode

* Basic passcode flow working

* Change passcode

* Check if biometry is enrolled

* Use fork

* Migration

* Patch expo-local-authentication

* Use async storage

* Styling

* Timer

* Refactor

* Lock orientation portrait when not on tablet

* share extension

* Deep linking

* Share extension

* Refactoring passcode

* use state

* Stash

* Refactor

* Change passcode

* Animate dots on error

* Matching passcodes

* Shake

* Remove lib

* Delete button

* Fade animation on modal

* Refactoring

* ItemInfo

* I18n

* I18n

* Remove unnecessary prop

* Save biometry column

* Raise time to lock to 30 seconds

* Vibrate on wrong confirmation passcode

* Reset attempts and save last authentication on local passcode confirmation

* Remove inline style

* Save last auth

* Fix header blink

* Change function name

* Fix android modal

* Fix vibration permission

* PasscodeEnter calls biometry

* Passcode on the state

* Biometry button on PasscodeEnter

* Show whole passcode

* Secure passcode

* Save passcode with promise to prevent empty passcodes and immediately lock

* Patch expo-local-authentication

* I18n

* Fix biometry being called every time

* Blur screen on app inactive

* Revert "Blur screen on app inactive"

This reverts commit a4ce812934.

* Remove immediately because of how Activities work on Android

* Pods

* New layout

* stash

* Layout refactored

* Fix icons

* Force set passcode from server

* Lint

* Improve permission message

* Forced passcode subtitle

* Disable based on admin's choice

* Require local authentication on login success

* Refactor

* Update tests

* Update react-native-device-info to fix notch

* Lint

* Fix modal

* Fix icons

* Fix min auto lock time

* Review

* keep enabled on mobile

* fix forced by admin when enable unlock with passcode

* use DEFAULT_AUTO_LOCK when manual enable screenLock

* fix check has passcode

* request biometry on first password

* reset auto time lock when disabled on server

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

* [FIX] Messages View (#2090)

* [FIX] Messages View

* [FIX] Opening PDF from Files View

* [FIX] Audio

* [FIX] SearchMessagesView

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

* [FIX] Big names overflow (#2072)

* [FIX] Big names overflow

* [FIX] Message time

Co-authored-by: devyaniChoubey <devyanichoubey16@gmail.com>

* [FIX] Some alignments

* fix user item overflow

* some adjustments

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

* [FIX] Avatar of message as an emoji (#2038)

* fixed-issue

* removed-hardcoded-emoji

* Merge develop

* replaced markdown with emoji componenent

* made-changes

* use avatar onPress

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

* [NEW] Livechat (#2004)

* [WIP][NEW] Livechat info/actions

* [IMPROVEMENT] RoomActionsView

* [NEW] Visitor Navigation

* [NEW] Get Department REST

* [FIX] Borders

* [IMPROVEMENT] Refactor RoomInfo View

* [FIX] Error while navigate from mention -> roomInfo

* [NEW] Livechat Fields

* [NEW] Close Livechat

* [WIP] Forward livechat

* [NEW] Return inquiry

* [WIP] Comment when close livechat

* [WIP] Improve roomInfo

* [IMPROVEMENT] Forward room

* [FIX] Department picker

* [FIX] Picker without results

* [FIX] Superfluous argument

* [FIX] Check permissions on RoomActionsView

* [FIX] Livechat permissions

* [WIP] Show edit to livechat

* [I18N] Add pt-br translations

* [WIP] Livechat Info

* [IMPROVEMENT] Livechat info

* [WIP] Livechat Edit

* [WIP] Livechat edit

* [WIP] Livechat Edit

* [WIP] Livechat edit scroll

* [FIX] Edit customFields

* [FIX] Clean livechat customField

* [FIX] Visitor Navigation

* [NEW] Next input logic LivechatEdit

* [FIX] Add livechat data to subscription

* [FIX] Revert change

* [NEW] Livechat user Status

* [WIP] Livechat tags

* [NEW] Edit livechat tags

* [FIX] Prevent some crashes

* [FIX] Forward

* [FIX] Return Livechat error

* [FIX] Prevent livechat info crash

* [IMPROVEMENT] Use input style on forward chat

* OnboardingSeparator -> OrSeparator

* [FIX] Go to next input

* [NEW] Added some icons

* [NEW] Livechat close

* [NEW] Forward Room Action

* [FIX] Livechat edit style

* [FIX] Change status logic

* [CHORE] Remove unnecessary logic

* [CHORE] Remove unnecessary code

* [CHORE] Remove unecessary case

* [FIX] Superfluous argument

* [IMPROVEMENT] Submit livechat edit

* [CHORE] Remove textInput type

* [FIX] Livechat edit

* [FIX] Livechat Edit

* [FIX] Use same effect

* [IMPROVEMENT] Tags input

* [FIX] Add empty tag

* Fix minor issues

* Fix typo

* insert livechat room data to our room object

* review

* add method calls server version

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

* [FIX] Delete Subs (#2091)

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

* [FIX] Android build (#2094)

* [FIX] Blink header DM (#2093)

* [FIX] Blink header DM

* Remove query

* [FIX] Push RoomInfoView

* remove unnecessary try/catch

* [FIX] RoomInfo > Message (Tablet)

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

* [FIX] Default biometry enabled (#2095)

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

* [IMPROVEMENT] Enable navigating to a room from auth deep linking (#2115)

* Wait for login success to navigate

* Enable auth and room deep linking at the same time

* [FIX] NewMessageView Press Item should open DM (#2116)

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

* [FIX] Roles throwing error (#2110)

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

* [FIX] Wait attach activity before changeNavigationBarColor (#2111)

* [FIX] Wait attach activity before changeNavigationBarColor

* Remove timeout and add try/catch

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

* [FIX] UIKit crash when some app send a list (#2117)

* [FIX] StoryBook

* [FIX] UIKit crash when some app send a list

* [CHORE] Update snapshot

* [CHORE] Remove token & id

* [FIX] Change bar color while no activity attached (#2130)

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

* [FIX] Screen Lock options i18n (#2120)

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

* [i18n] Added missing German translation strings (#2105)

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

* [FIX] Sometimes SDK is null when try to connect (#2131)

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

* [FIX] Autocomplete position on Android (#2106)

* [FIX] Autocomplete position on Android

* [FIX] Set selection to 0 when needed

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

* Revert "[FIX] Autocomplete position on Android (#2106)" (#2136)

This reverts commit e8c38d6f6f.

* [FIX] Here and all mentions shouldn't refer to users (#2137)

* [FIX] No send data to bugsnag if it's an aborted request (#2133)

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

* [TESTS] Update and separate E2E tests (#2126)

* Tests passing until roomslist

* create room

* roominfo

* change server

* broadcast

* profile

* custom status

* forgot password

* working

* room and onboarding

* Tests separated

* config.yml refactor

* Revert "config.yml refactor"

This reverts commit 0e984d3029.

* CI

* lint

* CI refactor

* Onboarding tests

* npx detox

* Add all tests

* Save brew cache

* mac-env executor

* detox-test command

* Update readme

* Remove folder

* [FIX] Screen Lock Time respect local value (#2141)

* [FIX] Screen Lock Time respect local value

* [FIX] Enable biometry at the first passcode change

* Bump version to 4.8.0 (#2147)

* [IMPROVEMENT] Refactor icon package (#2146)

* [IMPROVEMENT] Refactor Icon Package

* some size fixes

* [CHORE] Update WatermelonDB to 0.16.2 (#2166)

* [CHORE] Update WatermelonDB to 0.16.2

* Patch watermelon

* Markdown linting for e2e README (#2173)

* Markdown linting for e2e README

* Running a subset of tests

* [FIX] Load messages when hideSystemMessages is enabled (#2101)

* [WIP] Load messages when hideSystemMessages is enabled

* Improve method name

* Minor improvements

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

* [FIX] Respect server HideSystemMessages (#2175)

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

* [FIX] Screen Lock (#2177)

* [FIX] Screen Lock

* improve variable name

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

* [FIX] Load messages issue when trying to get ts from empty results (#2185)

* [FIX] Show registration form when add server by a invite link (#2187)

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

* [FIX] 2FA email - send code again (#2188)

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

* [CHORE] Update react-navigation to v5 (#2154)

* react-navigation v5 installed

* compiling

* Outside working

* InsideStack compiling

* Switch stack

* Starting room

* RoomView header

* SafeAreaView

* Slide from right stack animation

* stash

* Fix params

* Create channel

* inapp notification

* Custom status

* Add server working

* Refactor appStart

* Attachment

* in-app notification

* AuthLoadingView

* Remove compat

* Navigation

* Outside animations

* Fix new server icon

* block modal

* AttachmentView header

* Remove unnecessary code

* SelectedUsersView header

* StatusView

* CreateDiscussionView

* RoomInfoView

* RoomInfoEditView style

* RoomMembersView

* RoomsListView header

* RoomView header

* Share extension

* getParam

* Focus/blur

* Trying to fix inapp

* Lint

* Simpler app container

* Update libs

* Revert "Simpler app container"

This reverts commit 1e49d80bb4.

* Load messages faster

* Fix safearea on ReactionsModal

* Update safe area to v3

* lint

* Fix transition

* stash - drawer replace working

* stash - modal nav

* RoomActionsView as tablet modal

* RoomStack

* Stop showing RoomView header when there's no room

* Custom Header and different navigation based on stack

* Refactor setHeader

* MasterDetailContext

* RoomView header

* Fix isMasterDetail rule

* KeyCommands kind of working

* Create channel on tablet

* RoomView sCU

* Remove withSplit

* Settings opening as modal

* Settings

* StatusView headerLeft

* Admin panel

* TwoFactor style

* DirectoryView

* ServerDropdown and SortDropdown animations

* ThreadMessagesView

* Navigate to empty RoomView on server switch when in master detail

* ProfileView header

* Fix navigation issues

* Nav to any room info on tablet

* Room info

* Refactoring

* Fix rooms search

* Roomslist commands

* SearchMessagesView close modal

* Key commands

* Fix undefined subscription

* Disallow navigate to focused room

* isFocused state on RoomsListView

* Blur text inputs when focus is lost

* Replace animation

* Default nav theme

* Refactoring

* Always open Attachment with close modal button

* ModalContainer backdrop following themes

* Screen tracking

* Refactor get active route for in-app notification

* Only mark room as focused when in master detail layout

* Lint

* Open modals as fade from bottom on Android

* typo

* Fixing tests

* Fix in-app update

* Fixing goRoom issues

* Refactor stack names

* Fix unreadsCount

* Fix stack

* Fix header animation

* Refactor ShareNavigation

* Refactor navigation theme

* Make sure title is set

* Fix create discussion navigation

* Remove unused variable

* Create discussions from actions fixed

* Layout animation

* Screen lock on share extension

* Unnecessary change

* Admin border

* Set header after state callback

* Fix key commands on outside stack

* Fix back button pressed

* Remove layout animations from Android

* Tweak animations on Android

* Disable swipe gesture to open drawer

* Fix current item on RoomsListView

* Fix add server

* Fix drawer

* Fix broadcast

* LayoutAnimation instead of Transitions

* Fix onboarding back press

* Fix assorted tests

* Create discussion fix

* RoomInfoView header

* Drawer active item

* [NEW] Action Sheet (#2114)

* [WIP] New Action Sheet

* [NEW] Header Indicator

* [IMPROVEMENT] Header Logic

* [NEW] Use EventEmitter to show ActionSheet for while

* [FIX] Animation

* [IMPROVEMENT] Use provider

* [FIX] Add callback

* [FIX] Message Actions

* [FIX] Add MessageActions icons

* [NEW] MessageErrorActions

* [IMPROVEMENT] OnClose

* [FIX] Adjust height

* [FIX] Close/Reopen

* [CHORE] Remove react-native-action-sheet

* [CHORE] Move ActionSheet

* [FIX] Reply Message

* [IMPROVEMENT] Hide ActionSheet logic

* [WIP] Custom MessageActions Header

* [IMPROVEMENT] MessageActions Header

* [IMPROVEMENT] Enable Scroll

* [FIX] Scroll on Android

* Move to react-native-scroll-bottom-sheet

* Stash

* Refactor actions

* Revert some changes

* Trying react-native-modalize

* Back to HOC

* ActionSheet style

* HOC Header

* Reaction actionSheet

* Fix messageBox actions

* Fix add reaction

* Change to flatListProps

* fix modalize android scroll

* Use react-native-scroll-bottom-sheet

* [NEW] BottomSheet dismissable & [FIX] Android not opening

* [NEW] Show emojis based on screen width

* [WIP] Adjust to content height

* [IMPROVEMENT] Responsible

* [IMPROVEMENT] Use alert instead actionSheet at NewServerView

* [FIX] Handle tablet cases

* [IMPROVEMENT] Remove actionSheet of RoomMembersView

* [IMPROVEMENT] Min snap distance when its portrait

* [CHORE] Remove unused Components

* [IMPROVEMENT] Remove duplicated add-reaction

* [IMPROVEMENT] Refactor Icon Package

* [IMPROVEMENT] Use new icons

* [FIX] Select message at MessageActions before getOptions

* [FIX] Custom header height

* [CHORE] Remove patch & [FIX] Tablet bottom sheet

* [FIX] Use ListItem height to BottomSheet Height

* Some fixes

* [FIX] Custom MessageActions header

* [FIX] Android height adjust

* [IMPROVEMENT] Item touchable & [FIX] Respect pin permission

* [IMPROVEMENT] More than one snap point

* some size fixes

* improve code

* hide horizontal scroll indicator

* [FIX] Focus MessageBox on edit message

* [FIX] Ripple color

* [IMPROVEMENT] Backdrop must keep same opacity after 50% of the screen

* [TEST] Change animation config

* [IMPROVEMENT] BackHandler should close the ActionSheet

* [CHORE] Add react-native-safe-area-context

* [FIX] Provide a bottom padding at notch devices

* [IMPROVEMENT] Improve backdrop input/output range

* [FIX] Weird Android Snap behavior

* [PATCH] React-native-scroll-bottom-sheet

* [CI] Re-run build

* [FIX] Landscape android

* [IMPROVEMENT] Cover 50% of the screen at the landscape mode

* [FIX] Adjust emoji content to width size

* [IMPROVEMENT] Use hooks library

* [IMPROVEMENT] Close the actionSheet when orientation change

* deactivate safe-area-context for while

* [REVERT] Re-add react-native-safe-area-context (3.0.2)

* [IMPROVEMENT] Use focused background

* [TESTS] E2E Tests updated to new BottomSheet

* [NEW] Add cancel button

* [FIX] Cancel button at android

* [IMPROVEMENT] Use cancelable bottom sheet at room members view

* [IMPROVEMENT] Use better function names

* [IMPROVEMENT] Use getItemLayout

* [FIX][TEMP] Animation

* Review

* Build

* Header keyExtractor

* Rename function

* Tweak animation

* Refactoring

* useTheme

* Refactoring

* TestIDs

* Refactor

* Remove old lib

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

* [NEW] Add fastlane to android module and configure CI (#2100)

* [IMPROVEMENT] Use react-native-notifier for in-app notifications (#2139)

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

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

* [CHORE] Create DimensionsContext (#2098)

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

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

* [FIX] Remove duplicated sCU condition (#2194)

* [FIX] The auto translate toggle don't save right state (#2142)

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

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

* [CHORE] Increase "content_hash_max_items" on Watchman (#2181)

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

* [NEW] IFrame authentication (#2184)

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

* [NEW] Send multiple attachments (#2162)

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

* [NEW] Omnichannel Status Toggle (#2217)

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

* [FIX] Typing when UI_Use_Real_Name is enabled (#2216)

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

* [REGRESSION] Logout failing after #2217 (#2222)

* [CHORE] Add wrapper to make Meteor methods calls over REST (#2104)

* [WIP] Use rest instead methodCall

* [WIP] Some method calls using wrapper

* [WIP] Wrap all necessary methodCalls

* fix

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

* [IMPROVEMENT] Mark thread as read on open (#2225)

* [IMPROVEMENT] Mark a thread as read

* Use methodCallWrapper

* Check server version

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

* [FIX] Read receipt icon on action sheet (#2237)

* [FIX] Handle TypeErrors on navigationRef and draftMessage due to null properties (#2232)

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

* [FIX] Android crashing when restoring from background (#2238)

* [FIX] Send thread attachment (#2242)

* [FIX] Iframe auth Login Button (#2241)

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

* [REGRESSION] Audio Recording (#2240)

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

* [IMPROVEMENT] Expo-av audio recorder (#2195)

* [NEW] MessageBox: Expo-av audio recorder

* Refactor MessageBox to accommodate recording button even when recording
* Rename Recording.js -> RecordAudio.js as we could implement video recording in the future

* RecordAudio: Introduce cancel and send buttons

* RecordAudio: Introduce recorderBusy state, refactor MessageBox, remove useless SafeAreaView

* RecordAudio: Better audio quality 🎉, stop recording on unmount

* RecordAudio: Use FileSystem from expo-file-system instead of RNFetchBlob

* chore: flush out react-native-audio

* fix(MessageBox): bring back some missed styles during refactor

* refactor(RecordAudio): use class component

* refactor(RecordAudio): recorder busy to class property, styling changes

* recorder initialisation changes

* fix(RecordAudio): missing await in isRecordingPermissionGranted

* fix(RecordAudio): set isRecording = false on cancel/finish, refactor perms

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

* [IMPROVEMENT] Unified header UX (#2234)

* Change drawer icon

* Removed iOS variation

* Patch to react-navigation-header-buttons... easier to patch then to overwrite its behaviour :(

* Correctly position title

* Header subtitle

* Layout

* Alignment

* RoomView header

* Renamed RoomHeaderLeft to LeftButtons

* RoomView back button

* Search icon on RoomView

* Refactor

* Fix header on tablet

* Fix search messages close button on tablet

* Search key command

* Network status on RoomView header subtitle

* Update tests

* Scale content

* SearchBox cancel color

* Bump version to 4.9.0 (#2248)

* [FIX] Check for UI_Use_Real_Name when sorting rooms (#2230)

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

* [FIX] Emoji keyboard not showing custom and frequently used emojis on Share Extension (#2251)

* [FIX] Scroll to top crashing when ref is undefined (#2252)

* [FIX] Check if exists scroll ref

* Remove scrollTo

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

* [FIX] AttachmentView crashing during title decode (#2253)

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

* [FIX] Command previews crashing when API returns an error (#2254)

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

* [FIX] ThreadMessagesView throwing error when subscription wasn't found (#2255)

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

* [FIX] Register crashing when error data is undefined (#2256)

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

* [FIX] Get active route returning undefined (#2257)

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

* [FIX] ImageViewer not recognising gestures after zoomed (#2261)

* [FIX] Zoomed in images must react to gestures

* AnimatedFastImage -> AnimatedImage

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

* [FIX] Android stack animation throwing illegal node ID (#2260)

* [FIX] Navigation object undefined when tapping sidebar's user header on tablet (#2259)

* [FIX] Action sheet cutting emojis on the header (#2263)

* [FIX] Action sheet cutting emojis on the header

* fix tablet case

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

* [FIX] Mime type check crashing the app (#2264)

* [FIX] Notification preferences update crashing the app (#2262)

* [FIX] Create discussion not working from MessageActions (#2265)

* [FIX] getSettings not catching errors (#2271)

* [REGRESSION] Jitsi Call doesn't send message link (#2277)

* [FIX] Messages hidden behind MessageBox (#2281)

* fix: package.json & yarn.lock to reduce vulnerabilities (#2275)

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

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

* [i18n] Add missing german strings (#2278)

* cleaned mixed usage of "du" and "Sie"

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

* [CHORE] CircleCI Upgrades (#2269)

* Upgrade nvm

* Upgrade XCode

* Use nvm's default installed version of node (LTS)

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

* [NEW] Sign in with Apple (#2282)

* Add expo-apple-authentication

* Button

* Create new provisioning profiles

* Login with Apple

* Change provisioning profile

* Button color based on theme

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

* [FIX] MessageBox not being shown on ShareView when Preview is a video (#2283)

* Wrap video preview with ScrollView so Messagebox is shown properly

* Fix border color

* [TESTS] Test in docker (#2290)

* Add docker env with orchestration

* Update detox, update mocha config format

* One simulator at a time - mocha won't run parallel

* Docker runner, with test changes to match RC data

* Better error trapping in infrastructure control script

* Swap user provisioning from Mongo to RC API to work against existing servers

* Add docker to e2e readme

* Stop using example.com for emails

* Default detox to the tests directory

* Add working configs for both run scenarios

* Add some optional forcing of data.js version for Docker

* [CHORE] Consistent Rocket.Chat Branding (#2293)

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

* Bump version to 4.10.0 (#2305)

* [FIX] Room Info actions doesn't check permissions/settings enabled (#2292)

* [FIX] Show Call Button only when Jitsi Enabled (RoomInfoView)

* [FIX] Show user info

* [FIX] Show message button only if it's possible

* [FIX] Create direct only when needed

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

* [FIX] Read receipts on tablet (#2297)

* fixes not working read receipts on tablet (#2295)

* fixing last commit it to make it work on both tablet and phone

* fix missing close button on tablet's modal view

* Update app/views/ReadReceiptView/index.js

* remove unnecessary param

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

* [IMPROVEMENT] Clear image cache when clear cache (#2300)

* [IMPROVEMENT] Clear image cache when clear cache

* use fork instead

* react-native-fast-image -> @rocket.chat/react-native-fast-image

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

* [FIX] In-app notification tap when deep in the stack not redirecting to the room (#2302)

* [FIX] Markdown preview causes app stuck (#2303)

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

* [CHORE] Update commonmark libs (#2306)

* [CHORE] Update commonmark fork and relateds

* [CHORE] Update commonmark.js

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

* Bump lodash from 4.17.16 to 4.17.19 (#2310)

Bumps [lodash](https://github.com/lodash/lodash) from 4.17.16 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.16...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [FIX] MessagesView duplicating content (#2307)

* Fix Pagination duplicacy

* Fix MessageView Duplicate Content

* [FIX] Return fetch func

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

* Update dependencies (#2301)

* Update RN to 0.63.0-rc.1

* Updating

* Fix keyboard

* Fix native stuff on Messagebox

* Update to RN 0.63.1

* Update pods

* Update patches

* Update minors

* Majors

* Remove base-64 in favour of js-base64

* others

* Pods

* Flipper

* Yarn

* Fix setInput

* [IMPROVEMENT] Native sort and limit queries (#2249)

* Update WatermelonDB to 0.18.0

* Low onEndReachedThreshold

* Query experiment

* QUERY_SIZE

* Query or fetch data

* Reorder class functions

* Reset variables

* Hide system messages

* Change this.count behaviour

* Starting on RoomsListView

* unsubscribeQuery

* onEndReached

* Separate queries

* Reusable where clause

* Refactoring

* Refactor RoomItem to accept item as prop

* Comment RoomItem tests just so jest passes

* Fix alert and status

* onPress

* Unnecessary diff

* react-fast-compare

* Native limit on ShareListView

* Tweak item description

* Lint

* Fix on foreground crash

* Suggested changes

* [FIX] Style not being applied on RoomsListView header subtitle (#2319)

* [IMPROVEMENT] Improve our PR Template (#1893)

* [IMPROVEMENT] Improve our PR Template

* Few changes

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

* Revert "[CHORE] Update commonmark.js " (#2331)

* [TESTS] Idempotence for e2e tests (#2308)

* Idempotence for Assorted

* Idempotence for Onboarding

* Idempotence for Rooms

* Remove redundant expects

* Fixes and improvements

* Remove unneeded sleeps

* Make stable following merge

* Try solving early taps without long sleep

Try solving early taps without long sleep (cont)

Temporary CircleCI hack for quicker testing

Add screenshots to CircleCI for failed tests

Try solving early taps without long sleep (cont. 2)

Revert "Temporary CircleCI hack for quicker testing"

This reverts commit 4abef3a5827910c05b12ac8b8380275b60e8af4f.

* Fix flaky test with a fluent wait on the tap

* Add some new sleeps to workaround #2324

* Add test artifacts to gitignore

* More longpress for dodgy taps, wait for pin response

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

* [NEW] Log events from Onboarding, NewServer, Login and Register screens (#2169)

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

* Track Onboarding view

* Track NewServer view

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

* Track default login and all the oAuth options

* Track default sign up in RegisterView

* Change trackUserEvent signature and update all the files

* Track the remaining login services

* Resolve requests to improve the importing logs and events

* Leave a bugsnag breadcrumb when logging an event

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

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

* [FIX] Keyboard not focusing on TwoFactor component (#2314)

* Improve 2fa visual hierarchy

* Show 2fa keyboard by scheduling focus method after all interactions

* Remove unrelated styling improvements

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

* [IMPROVEMENT] TwoFactor component styling (#2328)

* Improve 2fa visual hierarchy

* Show 2fa keyboard by scheduling focus method after all interactions

* Remove unrelated styling improvements

* Improve 2fa component title hierarchy

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

* [FIX] Admin panel not logging in automatically (#2330)

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

* Bump codecov from 3.6.5 to 3.7.1 (#2323)

Bumps [codecov](https://github.com/codecov/codecov-node) from 3.6.5 to 3.7.1.
- [Release notes](https://github.com/codecov/codecov-node/releases)
- [Commits](https://github.com/codecov/codecov-node/compare/v3.6.5...v3.7.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Skip waiting for build processing on TestFlight upload (#2320)

* [CHORE] Update react-native-firebase (#2336)

* Remove firebase

* Install firebase/app

* Install analytics

* Crashlytics

* Android

* Fix mocks

* Edit scheme to Debug build configuration

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

* [REGRESSION] Invite links stopped working after #2154 (#2338)

* [REGRESSION] Invite links stopped working after #2154

* Clear invite links token when close NewServerView

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

* [TESTS] Fix some simple text (#2332)

* Clearer infrastructure wait message

* Use a generic mailbox rather than spamming Diego

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

* [FIX] i18n being called outside render (#2334)

* Fix I18n usage in ThemeView

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

* Fix I18n usage in NotificationPreferencesView

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

* Fix some I18n wrong uses

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

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

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

* Set title in header of room actions view items

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

* Remove unneeded spaces

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

* Set header title on constructor

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

* Remove unused navigation options

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

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

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

* Stabilise Room Actions test

* Fix Create Room test

* Be more tolerant of slow starting apps in CI

* Be more tolerant of slow running apps in CI

* Switch visibility checks ti stabilise Room Create test in CI

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

* [CHORE] Update icon names (#2318)

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

* Initial workflow for iOS detox tests

* Increase timeout

* Parallelise tests and optimise when to build

* Refine GH Actions logic

* Improve Detox App caching

* Upload failed test artifacts

* Rate limiting aware data setup

* Remove detox tests from Circle CI

* Revert "Rate limiting aware data setup"

This reverts commit d115604270.

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

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

* [WIP] Notification Service

* [WIP] Android push notification privacy

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

* [WIP] Override notification bundle

* [CHORE] Remove unnecessary import

* [WIP] Check notification Type (iOS)

* [WIP] Change to notification endpoint

* eof

* fix unwrap conditional value

* turn run request synchronous

* fix bundle info

* eof

* remove extra tab

* undo unnecessary change

* remove not working code for a while

* fix notification title

* change endpoint and received/sent data

* message-id-only working properly on android

* notification privacy working on ios

* invalidate circleCI yarn cache

* Fix provisioning profiles

* fix notification service version

* fix unwrap nil

* compatibility older servers android

* show received notification when cant fetch content from server

* undo some android changes

* prevent group & reply fallback notifications

* dont show more than one fallback notification by server

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

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

* New mention colors

* Increase letterSpacing for mentions

* Refactor

* UnreadBadge

* Add migration

* [FIX] Missing icons (#2353)

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

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

* Hide in app notification when focused on JitsiMeetView

* Hide notifications from different rooms

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

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

* Only run Flipper in debug via MainApplication is debug

* ReactNativeFlipper package rename + gradle bump

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

* Only run Flipper in debug via MainApplication is debug

* ReactNativeFlipper package rename + gradle bump

* Update Flipper to latest 0.51 for Android

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

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

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

* Track Onboarding view

* Track NewServer view

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

* Track default login and all the oAuth options

* Track default sign up in RegisterView

* Change trackUserEvent signature and update all the files

* Track the remaining login services

* track add server, change server and search

* Track SidebarView and refactor to use react-navigation

* Track profile events and handle exceptions

* Track create channel flux

* Track send message to user via NewMessageView

* Track create direct message flux

* Handle failure of create channel and group in the saga

* Track create discussion flux

* Track navigate to directory and its actions

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

* Track all channels sorting and grouping

* Resolve requests to improve the importing logs and events

* Remove unused events file

* Leave a bugsnag breadcrumb when logging an event

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

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

* Improve the logging of sidebar events

* Improve events from onboarding and newserver

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

* Improve NewMessageView events

* Improve CreateChannel events

* Improve CreateDiscussion and SelectedUsers create group events

* Improve RoomsList events and log trivial events

* Improve ProfileView events

* Remove single line function body for the sidebarNavigate

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

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

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

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

* [WIP] Jitsi Deep Links

* [WIP] Add app links

* save uniqueID servers database

* add serverInfoKey of uniqueID

* search server by call url

* open jitsi deeplink poc

* improve jitsi url

* fix

* improve comment

* add missing android scheme

* handle host not found

* Allow app links to be matched on parseDeepLinking

* Fix push notification of a call

* Minor fix

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

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

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

* Add fallbackNavigation()

* Fix insecure hosts

* [FIX] More missing icons (#2360)

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

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

* Track Onboarding view

* Track NewServer view

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

* Track default login and all the oAuth options

* Track default sign up in RegisterView

* Change trackUserEvent signature and update all the files

* Track the remaining login services

* track add server, change server and search

* Track SidebarView and refactor to use react-navigation

* Track profile events and handle exceptions

* Track create channel flux

* Track send message to user via NewMessageView

* Track create direct message flux

* Handle failure of create channel and group in the saga

* Track create discussion flux

* Track navigate to directory and its actions

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

* Track all channels sorting and grouping

* Resolve requests to improve the importing logs and events

* Remove unused events file

* Remove unused events file

* log proposed Room events

* Log proposed Message actions events

* Log EditStatus proposed events

* Log Settings proposed events

* Leave a bugsnag breadcrumb when logging an event

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

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

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

* Improve the logging of sidebar events

* Improve events from onboarding and newserver

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

* Improve NewMessageView events

* Improve CreateChannel events

* Improve CreateDiscussion and SelectedUsers create group events

* Improve RoomsList events and log trivial events

* Improve ProfileView events

* Remove single line function body for the sidebarNavigate

* Improve SettingsView events

* Log more events from ScreenLockConfigView

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

* Improve StatusView events

* Improve RoomView events

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

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

* Split RoomItem into container and component

* Refactor RoomItem

* Fix wrong status

* Tests

* Wrapper

* [NEW] Omnichannel inquiry queue (#2352)

* [WIP] Omnichannel queue

* Request inquiry when login

* Show take inquiry queued room

* Queue List as a Screen

* Poc using unread badge

* Prevent navigation to empty list

* Remove chat from queue when taked

* Fix header status on omnichannel preview room

* Fix room actions view to preview queued chat

* Use isOmnichannelPreview and dont show actions when is preview

* Filter queue chats taken by other people

* Fix room info to omnichannel preview room

* Handle show Queue

* Reset inquiry store when change server

* Improve queue logic

* Disable swipe on RoomItem when is a Queue Item

* Add unreadBadge style

* Move unread badge to presentation folder

* Cleanup inquiry reducers

* Move take saga to rocketchat function

* Remove comments

* Add relevant comments

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

* Add pt-br and improve queue empty message

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

* Add missing events

* Create selector for inquiry queue

* Minor fixes

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

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

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

* [FIX] Minor i18n issues (#2335)

* Add new translations to ptBr

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

* Fix update language in headers

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

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

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

* [NEW] Log remaining events (#2368)

* Change NAVIGATE_TO for GO_TO to reduce event size

* Log RA JitsiMeet events and join / terminate

* Log more RoomView events

* Log slash commands and handle fail

* Log RoomActions events

* Change from GO_TO to just GO

* Log RoomInfoEdit events

* Log InviteUsers and InviteUsersEdit events

* Log AutoTranslate events

* Log NotificationPreferences events

* Log remaining routes from RoomActions

* Log RoomAction toggle block user

* Fix command event

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

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

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

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

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

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

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

* [DOCS] Add Whitelabel (#2379)

* Update readme (#2381)

* Update README.md (#2378)

* Update README.md

* Update README.md

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

* [DOCS] Refactor Readme (#2382)

* Refactoring

* Detail docs

* Contributing

* Update CONTRIBUTING.md

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

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

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
Co-authored-by: Youssef Muhamad <emaildeyoussefmuhamad@gmail.com>
Co-authored-by: Prateek Jain <prateek93a@gmail.com>
Co-authored-by: phriedrich <info@phriedrich.de>
Co-authored-by: Sarthak Pranesh <41206172+sarthakpranesh@users.noreply.github.com>
Co-authored-by: devyaniChoubey <52153085+devyaniChoubey@users.noreply.github.com>
Co-authored-by: Ezequiel de Oliveira <ezequiel1de1oliveira@gmail.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: Calebe Rios <calebersmendes@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>
This commit is contained in:
Diego Mello 2020-09-01 09:22:20 -03:00 committed by GitHub
parent 0c7a9b9518
commit 6c37333dc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1605 changed files with 81637 additions and 44725 deletions

View File

@ -75,29 +75,6 @@ restore_cache: &restore-gradle-cache
name: Restore gradle cache
key: android-{{ checksum "android/build.gradle" }}-{{ checksum "android/app/build.gradle" }}
restore-brew-cache: &restore-brew-cache
name: Restore Brew cache
key: brew-{{ checksum "yarn.lock" }}-{{ checksum ".circleci/config.yml" }}
save-brew-cache: &save-brew-cache
name: Save brew cache
key: brew-{{ checksum "yarn.lock" }}-{{ checksum ".circleci/config.yml" }}
paths:
- /usr/local/Homebrew
install-apple-sim-utils: &install-apple-sim-utils
name: Install appleSimUtils
command: |
brew update
brew tap wix/brew
brew install wix/brew/applesimutils
rebuild-detox: &rebuild-detox
name: Rebuild Detox framework cache
command: |
npx detox clean-framework-cache
npx detox build-framework-cache
version: 2.1
# EXECUTORS
@ -107,35 +84,6 @@ executors:
environment:
<<: *bash-env
# COMMANDS
commands:
detox-test:
parameters:
folder:
type: string
steps:
- checkout
- attach_workspace:
at: .
- restore_cache: *restore-npm-cache-mac
- restore_cache: *restore-brew-cache
- run: *install-node
- run: *install-apple-sim-utils
- run: *install-npm-modules
- run: *rebuild-detox
- run:
name: Test
command: |
npx detox test << parameters.folder >> --configuration ios.sim.release --cleanup
# JOBS
jobs:
lint-testunit:
@ -170,57 +118,6 @@ jobs:
- save_cache: *save-npm-cache-linux
# E2E
e2e-build:
executor: mac-env
steps:
- checkout
- restore_cache: *restore-npm-cache-mac
- restore_cache: *restore-brew-cache
- run: *install-node
- run: *install-apple-sim-utils
- run: *install-npm-modules
- run: *rebuild-detox
- run:
name: Build
command: |
npx detox build --configuration ios.sim.release
- persist_to_workspace:
root: .
paths:
- ios/build/Build/Products/Release-iphonesimulator/RocketChatRN.app
- save_cache: *save-npm-cache-mac
- save_cache: *save-brew-cache
e2e-test-onboarding:
executor: mac-env
steps:
- detox-test:
folder: "./e2e/tests/onboarding"
e2e-test-room:
executor: mac-env
steps:
- detox-test:
folder: "./e2e/tests/room"
e2e-test-assorted:
executor: mac-env
steps:
- detox-test:
folder: "./e2e/tests/assorted"
# Android builds
android-build:
<<: *defaults
@ -429,23 +326,6 @@ workflows:
jobs:
- lint-testunit
- e2e-hold:
type: approval
requires:
- lint-testunit
- e2e-build:
requires:
- e2e-hold
- e2e-test-onboarding:
requires:
- e2e-build
- e2e-test-room:
requires:
- e2e-build
- e2e-test-assorted:
requires:
- e2e-build
- ios-build:
requires:
- lint-testunit

View File

@ -1,7 +1,34 @@
<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR -->
@RocketChat/ReactNative
<!-- This is a pull request template, you do not need to uncomment or remove the comments, they won't show up in the PR text. -->
<!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below -->
Closes #ISSUE_NUMBER
## Proposed changes
<!-- Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue below. -->
<!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
## Issue(s)
<!-- Link the issues being closed by or related to this PR. For example, you can use #594 if this PR closes issue number 594 -->
## How to test or reproduce
<!-- Mention how you would reproduce the bug if not mentioned on the issue page already. Also mention which screens are going to have the changes if applicable -->
## Screenshots
## Types of changes
<!-- What types of changes does your code introduce to Rocket.Chat? -->
<!-- Put an `x` in the boxes that apply -->
- [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] Improvement (non-breaking change which improves a current function)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Documentation update (if none of the other choices apply)
## Checklist
<!-- Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code. -->
- [ ] I have read the [CONTRIBUTING](https://github.com/RocketChat/Rocket.Chat/blob/develop/.github/CONTRIBUTING.md#contributing-to-rocketchat) doc
- [ ] I have signed the [CLA](https://cla-assistant.io/RocketChat/Rocket.Chat.ReactNative)
- [ ] Lint and unit tests pass locally with my changes
- [ ] I have added tests that prove my fix is effective or that my feature works (if applicable)
- [ ] I have added necessary documentation (if applicable)
- [ ] Any dependent changes have been merged and published in downstream modules
## Further comments
<!-- If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... -->

221
.github/workflows/ios_detox.yml vendored Normal file
View File

@ -0,0 +1,221 @@
name: iOS Detox
on: [pull_request]
jobs:
detox-build:
runs-on: macos-latest
timeout-minutes: 60
env:
DEVELOPER_DIR: /Applications/Xcode_11.5.app
steps:
- name: Checkout
uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Generate Detox app cache key
run: echo $(git rev-parse HEAD:app) > "./app-git-revision.txt"
- name: Cache Detox app
uses: actions/cache@v1
id: detoxappcache
with:
path: ios/build/Build/Products/Release-iphonesimulator
key: iOSDetoxRelease-v4-${{ hashFiles('yarn.lock') }}-${{ hashFiles('ios/Podfile.lock') }}-${{ hashFiles('./app-git-revision.txt') }}
- name: Node
if: steps.detoxappcache.outputs.cache-hit != 'true'
uses: actions/setup-node@v1
- name: Cache node modules
if: steps.detoxappcache.outputs.cache-hit != 'true'
uses: actions/cache@v1
id: npmcache
with:
path: node_modules
key: node-modules-${{ hashFiles('**/yarn.lock') }}
- name: Rebuild detox
if: steps.detoxappcache.outputs.cache-hit != 'true' && steps.npmcache.outputs.cache-hit == 'true'
run: yarn detox clean-framework-cache && yarn detox build-framework-cache
- name: Install Dependencies
if: steps.detoxappcache.outputs.cache-hit != 'true' && steps.npmcache.outputs.cache-hit != 'true'
run: yarn install
- run: yarn detox build e2e --configuration ios.sim.release
if: steps.detoxappcache.outputs.cache-hit != 'true'
detox-test-rooms:
needs: detox-build
runs-on: macos-latest
timeout-minutes: 60
env:
DEVELOPER_DIR: /Applications/Xcode_11.5.app
steps:
- name: Checkout
uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Generate Detox app cache key
run: echo $(git rev-parse HEAD:app) > "./app-git-revision.txt"
- name: Cache Detox app
uses: actions/cache@v1
id: detoxappcache
with:
path: ios/build/Build/Products/Release-iphonesimulator
key: iOSDetoxRelease-v4-${{ hashFiles('yarn.lock') }}-${{ hashFiles('ios/Podfile.lock') }}-${{ hashFiles('./app-git-revision.txt') }}
- name: Check for Detox app
if: steps.detoxappcache.outputs.cache-hit != 'true'
run: exit 1
- name: Node
uses: actions/setup-node@v1
- name: Cache node modules
uses: actions/cache@v1
id: npmcache
with:
path: node_modules
key: node-modules-${{ hashFiles('**/yarn.lock') }}
- name: Rebuild detox
if: steps.npmcache.outputs.cache-hit == 'true'
run: yarn detox clean-framework-cache && yarn detox build-framework-cache
- name: Install Dependencies
if: steps.npmcache.outputs.cache-hit != 'true'
run: yarn install
- run: brew tap wix/brew
- run: brew install applesimutils
- run: yarn detox test e2e/tests/room --configuration ios.sim.release --cleanup
- name: Upload test artifacts
if: ${{ failure() }}
uses: actions/upload-artifact@v2
with:
name: artifacts
path: artifacts
detox-test-assorted:
needs: detox-build
runs-on: macos-latest
timeout-minutes: 60
env:
DEVELOPER_DIR: /Applications/Xcode_11.5.app
steps:
- name: Checkout
uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Generate Detox app cache key
run: echo $(git rev-parse HEAD:app) > "./app-git-revision.txt"
- name: Cache Detox app
uses: actions/cache@v1
id: detoxappcache
with:
path: ios/build/Build/Products/Release-iphonesimulator
key: iOSDetoxRelease-v4-${{ hashFiles('yarn.lock') }}-${{ hashFiles('ios/Podfile.lock') }}-${{ hashFiles('./app-git-revision.txt') }}
- name: Check for Detox app
if: steps.detoxappcache.outputs.cache-hit != 'true'
run: exit 1
- name: Node
uses: actions/setup-node@v1
- name: Cache node modules
uses: actions/cache@v1
id: npmcache
with:
path: node_modules
key: node-modules-${{ hashFiles('**/yarn.lock') }}
- name: Rebuild detox
if: steps.npmcache.outputs.cache-hit == 'true'
run: yarn detox clean-framework-cache && yarn detox build-framework-cache
- name: Install Dependencies
if: steps.npmcache.outputs.cache-hit != 'true'
run: yarn install
- run: brew tap wix/brew
- run: brew install applesimutils
- run: yarn detox test e2e/tests/assorted --configuration ios.sim.release --cleanup
- name: Upload test artifacts
if: ${{ failure() }}
uses: actions/upload-artifact@v2
with:
name: artifacts
path: artifacts
detox-test-onboarding:
needs: detox-build
runs-on: macos-latest
timeout-minutes: 60
env:
DEVELOPER_DIR: /Applications/Xcode_11.5.app
steps:
- name: Checkout
uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Generate Detox app cache key
run: echo $(git rev-parse HEAD:app) > "./app-git-revision.txt"
- name: Cache Detox app
uses: actions/cache@v1
id: detoxappcache
with:
path: ios/build/Build/Products/Release-iphonesimulator
key: iOSDetoxRelease-v4-${{ hashFiles('yarn.lock') }}-${{ hashFiles('ios/Podfile.lock') }}-${{ hashFiles('./app-git-revision.txt') }}
- name: Check for Detox app
if: steps.detoxappcache.outputs.cache-hit != 'true'
run: exit 1
- name: Node
uses: actions/setup-node@v1
- name: Cache node modules
uses: actions/cache@v1
id: npmcache
with:
path: node_modules
key: node-modules-${{ hashFiles('**/yarn.lock') }}
- name: Rebuild detox
if: steps.npmcache.outputs.cache-hit == 'true'
run: yarn detox clean-framework-cache && yarn detox build-framework-cache
- name: Install Dependencies
if: steps.npmcache.outputs.cache-hit != 'true'
run: yarn install
- run: brew tap wix/brew
- run: brew install applesimutils
- run: yarn detox test e2e/tests/onboarding --configuration ios.sim.release --cleanup
- name: Upload test artifacts
if: ${{ failure() }}
uses: actions/upload-artifact@v2
with:
name: artifacts
path: artifacts

1
.gitignore vendored
View File

@ -58,6 +58,7 @@ buck-out/
coverage
artifacts
.vscode/
e2e/docker/rc_test_env/docker-compose.yml
e2e/docker/data/db

93
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,93 @@
# Contributing Guidelines
Great to have you here! Here are a few ways you can help make this project better!
## Setting up a development environment
Refer to [React Native environment setup](https://reactnative.dev/docs/environment-setup) to make sure everything is up and running.
Follow the `React Native CLI Quickstart` section as we don't support Expo managed flow.
*Note: you'll need a MacOS to run iOS apps*
### How to run
Clone repository and install dependencies:
```sh
git clone git@github.com:RocketChat/Rocket.Chat.ReactNative.git
cd Rocket.Chat.ReactNative
yarn
```
Run the app:
```sh
npx react-native run-ios
```
or
```sh
npx react-native run-android
```
At this point, the app should be running on the simulator or on your device!
*Note: npm won't work on this project*
## Issues needing help
Didn't find a bug or want a new feature not already reported? Check out the [help wanted](https://github.com/RocketChat/Rocket.Chat.ReactNative/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%91%8B+help+wanted%22) or the [good first issue](https://github.com/RocketChat/Rocket.Chat.ReactNative/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%8D%AD+good+first+issue%22) labels.
Can't help coding? Triaging issues is a **great** way of helping.
## Code style
We use [ESLint](https://eslint.org/) to enforce code style and best practices. We have a pre-commit hook enforcing commits to follow our lint rules.
To check for lint issues on your code, run this on your terminal:
```sh
yarn lint
```
## Tests
It's always important to ensure everything is working properly and that's why tests are great. We have unit and e2e tests on this project.
### Unit tests
We use [Jest](https://jestjs.io/) and [Storybook](https://storybook.js.org/) on our tests.
#### Storybook
Storybook is a tool for developing UI Components and has some plugins to make Jest generate snapshots of them.
[On the root of the project](https://github.com/RocketChat/Rocket.Chat.ReactNative/blob/develop/index.js#L24), comment everything leaving only the last import to Storybook left and refresh your project.
You'll see some tests like this:
<img src="https://user-images.githubusercontent.com/804994/89677725-56393200-d8c4-11ea-84b0-213be1d24e98.png" width="350" />
#### Jest
We use Jest for our unit tests and to generate Storybook snapshots. We have a pre-commit hook enforcing preventing commits that breaks any test.
To check for test issues on your code, run this on your terminal:
```sh
yarn test
```
### E2E tests
We use [Detox](https://github.com/wix/Detox) framework to end-to-end test our app and ensure everything is working properly.
[Follow this documentation to learn how to run it](https://github.com/RocketChat/Rocket.Chat.ReactNative/blob/develop/e2e).
### Pull request
As soon as your changes are ready, you can open a Pull Request.
The title of your PR should be descriptive, including either [NEW], [IMPROVEMENT] or [FIX] at the beginning, e.g. [FIX] App crashing on startup.
You may share working results prior to finishing, please include [WIP] in the title. This way anyone can look at your code: you can ask for help within the PR if you don't know how to solve a problem.
Your PR is automatically inspected by various tools, check their response and try to improve your code accordingly. Requests that fail to build or have wrong coding style won't be merged.

226
README.md
View File

@ -5,11 +5,12 @@
[![codecov](https://codecov.io/gh/RocketChat/Rocket.Chat.ReactNative/branch/master/graph/badge.svg)](https://codecov.io/gh/RocketChat/Rocket.Chat.ReactNative)
[![CodeFactor](https://www.codefactor.io/repository/github/rocketchat/rocket.chat.reactnative/badge)](https://www.codefactor.io/repository/github/rocketchat/rocket.chat.reactnative)
**Supported Server Versions:** 0.70.0+
- **Supported server versions:** 0.70.0+
- **Supported iOS versions**: 11+
- **Supported Android versions**: 5.0+
## Download
### Official apps
<a href="https://play.google.com/store/apps/details?id=chat.rocket.android">
<img alt="Download on Google Play" src="https://play.google.com/intl/en_us/badges/images/badge_new.png" height=43>
</a>
@ -17,29 +18,7 @@
<img alt="Download on App Store" src="https://user-images.githubusercontent.com/7317008/43209852-4ca39622-904b-11e8-8ce1-cdc3aee76ae9.png" height=43>
</a>
### Experimental apps
<a href="https://play.google.com/store/apps/details?id=chat.rocket.reactnative">
<img alt="Download on Google Play" src="https://play.google.com/intl/en_us/badges/images/badge_new.png" height=43>
</a>
<a href="https://itunes.apple.com/us/app/rocket-chat-experimental/id1272915472">
<img alt="Download on App Store" src="https://user-images.githubusercontent.com/7317008/43209852-4ca39622-904b-11e8-8ce1-cdc3aee76ae9.png" height=43>
</a>
## Beta Access
### TestFlight
You can signup to our TestFlight builds by accessing these links:
- Official: https://testflight.apple.com/join/3gcYeoMr
- Experimental: https://testflight.apple.com/join/7I3dLCNT.
### Google Play beta
You can subscribe to Google Play Beta program and download latest versions:
- Official: https://play.google.com/store/apps/details?id=chat.rocket.android
- Experimental: https://play.google.com/store/apps/details?id=chat.rocket.reactnative
Check [our docs](https://docs.rocket.chat/installation/mobile-and-desktop-apps#mobile-apps) for beta and Experimental versions.
## Reporting an Issue
@ -47,195 +26,16 @@ You can subscribe to Google Play Beta program and download latest versions:
Also check the [#react-native](https://open.rocket.chat/channel/react-native) community on [open.rocket.chat](https://open.rocket.chat). We'd like to help.
## Installing dependencies
## Contributing
Follow the [React Native Getting Started Guide](https://facebook.github.io/react-native/docs/getting-started.html) for detailed instructions on setting up your local machine for development.
Are you a dev and would like to help? Found a bug that you would like to report or a missing feature that you would like to work on? Great! We have written down a [Contribution guide](https://github.com/RocketChat/Rocket.Chat.ReactNative/blob/develop/CONTRIBUTING.md) so you can start easily.
## How to run
- Clone repository and install dependencies:
```bash
$ git clone git@github.com:RocketChat/Rocket.Chat.ReactNative.git
$ cd Rocket.Chat.ReactNative
$ yarn
```
## Whitelabel
Do you want to make the app run on your own server only? [Follow our whitelabel documentation.](https://docs.rocket.chat/guides/developer/mobile-apps/whitelabeling-mobile-apps)
- Run application
```bash
$ npx react-native run-ios
```
```bash
$ npx react-native run-android
```
## Engage with us
### Share your story
Wed love to hear about [your experience](https://survey.zohopublic.com/zs/e4BUFG) and potentially feature it on our [Blog](https://rocket.chat/case-studies/?utm_source=github&utm_medium=readme&utm_campaign=community).
### Running single server
If you don't need multiple servers, there is a branch `single-server` just for that.
Readme will guide you on how to config.
## Current priorities
1) Omnichannel support
2) E2E encryption
## Features
| Feature | Status |
|--------------------------------------------------------------- |-------- |
| Jitsi Integration | ✅ |
| Federation (Directory) | ✅ |
| Discussions | ✅ |
| Omnichannel | ❌ |
| Threads | ✅ |
| Record Audio | ✅ |
| Record Video | ✅ |
| Commands | ✅ |
| Draft message per room | ✅ |
| Share Extension | ✅ |
| Notifications Preferences | ✅ |
| Edited status | ✅ |
| Upload video | ✅ |
| Grouped messages | ✅ |
| Mark room as read | ✅ |
| Mark room as unread | ✅ |
| Tablet Support | ✅ |
| Read receipt | ✅ |
| Broadbast Channel | ✅ |
| Authentication via SAML | ✅ |
| Authentication via CAS | ✅ |
| Custom Fields on Signup | ✅ |
| Report message | ✅ |
| Theming | ✅ |
| Settings -> Review the App | ✅ |
| Settings -> Default Browser | ✅ |
| Admin panel | ✅ |
| Reply message from notification | ✅ |
| Unread counter banner on message list | ✅ |
| E2E Encryption | ❌ |
| Join a Protected Room | ❌ |
| Optional Analytics | ✅ |
| Settings -> About us | ❌ |
| Settings -> Contact us | ✅ |
| Settings -> Update App Icon | ❌ |
| Settings -> Share | ✅ |
| Accessibility (Medium) | ❌ |
| Accessibility (Advanced) | ❌ |
| Authentication via Meteor | ❌ |
| Authentication via Wordpress | ✅ |
| Authentication via Custom OAuth | ✅ |
| Add user to the room | ✅ |
| Send message | ✅ |
| Authentication via Email | ✅ |
| Authentication via Username | ✅ |
| Authentication via LDAP | ✅ |
| Message format: Markdown | ✅ |
| Message format: System messages (Welcome, Message removed...) | ✅ |
| Message format: links | ✅ |
| Message format: images | ✅ |
| Message format: replies | ✅ |
| Message format: alias with custom message (title & text) | ✅ |
| Messages list: day separation | ✅ |
| Messages list: load more on scroll | ✅ |
| Messages list: receive new messages via subscription | ✅ |
| Subscriptions list | ✅ |
| Segmented subscriptions list: Favorites | ✅ |
| Segmented subscriptions list: Unreads | ✅ |
| Segmented subscriptions list: DMs | ✅ |
| Segmented subscriptions list: Channels | ✅ |
| Subscriptions list: update user status via subscription | ✅ |
| Numbers os messages unread in the Subscriptions list | ✅ |
| Status change | ✅ |
| Upload image | ✅ |
| Take picture & upload it | ✅ |
| 2FA | ✅ |
| Signup | ✅ |
| Autocomplete with usernames | ✅ |
| Autocomplete with @all & @here | ✅ |
| Autocomplete room/channel name | ✅ |
| Upload audio | ✅ |
| Forgot your password | ✅ |
| Login screen: terms of service | ✅ |
| Login screen: privacy policy | ✅ |
| Authentication via Google | ✅ |
| Authentication via Facebook | ✅ |
| Authentication via Twitter | ✅ |
| Authentication via GitHub | ✅ |
| Authentication via GitLab | ✅ |
| Authentication via LinkedIn | ✅ |
| Create channel | ✅ |
| Search Local | ✅ |
| Search in the API | ✅ |
| Settings -> License | ✅ |
| Settings -> App version | ✅ |
| Autocomplete emoji | ✅ |
| Upload file (documents, PDFs, spreadsheets, zip files, etc) | ✅ |
| Copy message | ✅ |
| Pin message | ✅ |
| Unpin message | ✅ |
| Channel Info screen -> Members | ✅ |
| Channel Info screen -> Pinned | ✅ |
| Channel Info screen -> Starred | ✅ |
| Channel Info screen -> Uploads | ✅ |
| Star message | ✅ |
| Unstar message | ✅ |
| Channel Info screen -> Topic | ✅ |
| Channel Info screen -> Description | ✅ |
| Star a channel | ✅ |
| Message format: videos | ✅ |
| Message format: audios | ✅ |
| Edit message | ✅ |
| Delete a message | ✅ |
| Reply message | ✅ |
| Quote message | ✅ |
| Muted state | ✅ |
| Offline reading | ✅ |
| Offline writing | ✅ |
| Edit profile | ✅ |
| Reactions | ✅ |
| Custom emojis | ✅ |
| Accessibility (Basic) | ✅ |
| Tap notification, go to the channel | ✅ |
| Deep links: Authentication | ✅ |
| Deep links: Rooms | ✅ |
| Full name setting | ✅ |
| Read only rooms | ✅ |
| Typing status | ✅ |
| Create channel/group | ✅ |
| Disable registration setting | ✅ |
| Unread red line indicator on message list | ✅ |
| Search Messages in Channel | ✅ |
| Mentions List | ✅ |
| Attachment List | ✅ |
| Join a Room | ✅ |
## Detox (end-to-end tests)
- Build your app
```bash
$ npx detox build --configuration ios.sim.release
```
- Run tests
```bash
$ npx detox test ./e2e/tests/onboarding --configuration ios.sim.release
$ npx detox test ./e2e/tests/room --configuration ios.sim.release
$ npx detox test ./e2e/tests/assorted --configuration ios.sim.release
```
## Storybook
- Open index.js
- Uncomment following line
```bash
import './storybook';
```
- Comment out following lines
```bash
import './app/ReactotronConfig';
import { AppRegistry } from 'react-native';
import App from './app/index';
import { name as appName } from './app.json';
AppRegistry.registerComponent(appName, () => App);
```
- Start your application again
### Subscribe for Updates
Once a month our marketing team releases an email update with news about product releases, company related topics, events and use cases. [Sign Up!](https://rocket.chat/newsletter/?utm_source=github&utm_medium=readme&utm_campaign=community)

View File

@ -0,0 +1,3 @@
export default {
crashlytics: null
};

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
apply plugin: "com.android.application"
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'kotlin-android'
apply plugin: "io.fabric"
apply plugin: "com.google.firebase.firebase-perf"
apply plugin: 'com.bugsnag.android.gradle'
import com.android.build.OutputFile
@ -168,15 +168,18 @@ android {
minifyEnabled enableProguardInReleaseBuilds
setProguardFiles([getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'])
signingConfig signingConfigs.release
firebaseCrashlytics {
nativeSymbolUploadEnabled true
}
}
}
packagingOptions {
pickFirst '**/armeabi-v7a/libc++_shared.so'
pickFirst '**/x86/libc++_shared.so'
pickFirst '**/arm64-v8a/libc++_shared.so'
pickFirst '**/x86_64/libc++_shared.so'
}
// packagingOptions {
// pickFirst '**/armeabi-v7a/libc++_shared.so'
// pickFirst '**/x86/libc++_shared.so'
// pickFirst '**/arm64-v8a/libc++_shared.so'
// pickFirst '**/x86_64/libc++_shared.so'
// }
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
@ -215,11 +218,6 @@ dependencies {
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "com.google.firebase:firebase-messaging:18.0.0"
implementation "com.google.firebase:firebase-core:16.0.9"
implementation "com.google.firebase:firebase-perf:17.0.2"
implementation('com.crashlytics.sdk.android:crashlytics:2.9.9@aar') {
transitive = true
}
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
@ -251,5 +249,4 @@ task copyDownloadableDepsToLibs(type: Copy) {
into 'libs'
}
apply plugin: 'com.google.gms.google-services'
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)

View File

@ -1,8 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
xmlns:tools="http://schemas.android.com/tools"
package="chat.rocket.reactnative">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application
android:name=".MainDebugApplication"
tools:ignore="GoogleAppIndexingWarning"
tools:replace="android:name"
tools:targetApi="28" />
<application tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
</manifest>

View File

@ -0,0 +1,25 @@
package chat.rocket.reactnative;
import android.content.Context;
import com.facebook.react.ReactInstanceManager;
public class MainDebugApplication extends MainApplication {
@Override
public void onCreate() {
super.onCreate();
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
ReactNativeFlipper.initializeFlipper(context, reactInstanceManager);
}
}

View File

@ -4,7 +4,7 @@
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package com.rndiffapp;
package chat.rocket.reactnative;
import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.android.utils.FlipperUtils;

View File

@ -37,8 +37,10 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="go.rocket.chat" />
<data android:scheme="https" android:host="jitsi.rocket.chat" />
<data android:scheme="rocketchat" android:host="room" />
<data android:scheme="rocketchat" android:host="auth" />
<data android:scheme="rocketchat" android:host="jitsi.rocket.chat" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

View File

@ -0,0 +1,10 @@
package chat.rocket.reactnative;
import android.os.Bundle;
import androidx.annotation.Nullable;
public class Callback {
public void call(@Nullable Bundle bundle) {
}
}

View File

@ -14,8 +14,9 @@ import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.app.Person;
import androidx.annotation.Nullable;
import com.google.gson.*;
import com.google.gson.Gson;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
@ -33,15 +34,18 @@ import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME;
public class CustomPushNotification extends PushNotification {
public static ReactApplicationContext reactApplicationContext;
final NotificationManager notificationManager;
public CustomPushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper) {
super(context, bundle, appLifecycleFacade, appLaunchHelper, jsIoHelper);
reactApplicationContext = new ReactApplicationContext(context);
notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
}
private static Map<String, List<Bundle>> notificationMessages = new HashMap<String, List<Bundle>>();
@ -54,29 +58,39 @@ public class CustomPushNotification extends PushNotification {
@Override
public void onReceived() throws InvalidNotificationException {
final Bundle bundle = mNotificationProps.asBundle();
Bundle received = mNotificationProps.asBundle();
Ejson receivedEjson = new Gson().fromJson(received.getString("ejson", "{}"), Ejson.class);
if (receivedEjson.notificationType != null && receivedEjson.notificationType.equals("message-id-only")) {
notificationLoad(receivedEjson, new Callback() {
@Override
public void call(@Nullable Bundle bundle) {
if (bundle != null) {
mNotificationProps = createProps(bundle);
}
}
});
}
// We should re-read these values since that can be changed by notificationLoad
Bundle bundle = mNotificationProps.asBundle();
Ejson loadedEjson = new Gson().fromJson(bundle.getString("ejson", "{}"), Ejson.class);
String notId = bundle.getString("notId", "1");
String title = bundle.getString("title");
if (notificationMessages.get(notId) == null) {
notificationMessages.put(notId, new ArrayList<Bundle>());
}
Gson gson = new Gson();
Ejson ejson = gson.fromJson(bundle.getString("ejson", "{}"), Ejson.class);
boolean hasSender = ejson.sender != null;
boolean hasSender = loadedEjson.sender != null;
String title = bundle.getString("title");
bundle.putLong("time", new Date().getTime());
bundle.putString("username", hasSender ? ejson.sender.username : title);
bundle.putString("senderId", hasSender ? ejson.sender._id : "1");
bundle.putString("avatarUri", ejson.getAvatarUri());
bundle.putString("username", hasSender ? loadedEjson.sender.username : title);
bundle.putString("senderId", hasSender ? loadedEjson.sender._id : "1");
bundle.putString("avatarUri", loadedEjson.getAvatarUri());
notificationMessages.get(notId).add(bundle);
super.postNotification(Integer.parseInt(notId));
postNotification(Integer.parseInt(notId));
notifyReceivedToJS();
}
@ -96,9 +110,11 @@ public class CustomPushNotification extends PushNotification {
String notId = bundle.getString("notId", "1");
String title = bundle.getString("title");
String message = bundle.getString("message");
Boolean notificationLoaded = bundle.getBoolean("notificationLoaded", false);
Ejson ejson = new Gson().fromJson(bundle.getString("ejson", "{}"), Ejson.class);
notification
.setContentTitle(title)
.setContentTitle(title)
.setContentText(message)
.setContentIntent(intent)
.setPriority(Notification.PRIORITY_HIGH)
@ -109,10 +125,34 @@ public class CustomPushNotification extends PushNotification {
notificationColor(notification);
notificationChannel(notification);
notificationIcons(notification, bundle);
notificationStyle(notification, notificationId, bundle);
notificationReply(notification, notificationId, bundle);
notificationDismiss(notification, notificationId);
// if notificationType is null (RC < 3.5) or notificationType is different of message-id-only or notification was loaded successfully
if (ejson.notificationType == null || !ejson.notificationType.equals("message-id-only") || notificationLoaded) {
notificationStyle(notification, notificationId, bundle);
notificationReply(notification, notificationId, bundle);
// message couldn't be loaded from server (Fallback notification)
} else {
Gson gson = new Gson();
// iterate over the current notification ids to dismiss fallback notifications from same server
for (Map.Entry<String, List<Bundle>> bundleList : notificationMessages.entrySet()) {
// iterate over the notifications with this id (same host + rid)
Iterator iterator = bundleList.getValue().iterator();
while (iterator.hasNext()) {
Bundle not = (Bundle) iterator.next();
// get the notification info
Ejson notEjson = gson.fromJson(not.getString("ejson", "{}"), Ejson.class);
// if already has a notification from same server
if (ejson.serverURL().equals(notEjson.serverURL())) {
String id = not.getString("notId");
// cancel this notification
notificationManager.cancel(Integer.parseInt(id));
}
}
}
}
return notification;
}
@ -300,4 +340,7 @@ public class CustomPushNotification extends PushNotification {
notification.setDeleteIntent(dismissPendingIntent);
}
private void notificationLoad(Ejson ejson, Callback callback) {
LoadNotification.load(reactApplicationContext, ejson, callback);
}
}

View File

@ -9,6 +9,8 @@ public class Ejson {
String rid;
String type;
Sender sender;
String messageId;
String notificationType;
private String TOKEN_KEY = "reactnativemeteor_usertoken-";
private SharedPreferences sharedPreferences = RNUserDefaultsModule.getPreferences(CustomPushNotification.reactApplicationContext);

View File

@ -0,0 +1,101 @@
package chat.rocket.reactnative;
import android.os.Bundle;
import android.content.Context;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Interceptor;
import com.google.gson.Gson;
import java.io.IOException;
import com.facebook.react.bridge.ReactApplicationContext;
import chat.rocket.userdefaults.RNUserDefaultsModule;
class JsonResponse {
Data data;
class Data {
Notification notification;
class Notification {
String notId;
String title;
String text;
Payload payload;
class Payload {
String host;
String rid;
String type;
Sender sender;
String messageId;
String notificationType;
String name;
String messageType;
class Sender {
String _id;
String username;
String name;
}
}
}
}
}
public class LoadNotification {
private static int RETRY_COUNT = 0;
private static int[] TIMEOUT = new int[]{ 0, 1, 3, 5, 10 };
private static String TOKEN_KEY = "reactnativemeteor_usertoken-";
public static void load(ReactApplicationContext reactApplicationContext, final Ejson ejson, Callback callback) {
final OkHttpClient client = new OkHttpClient();
HttpUrl.Builder url = HttpUrl.parse(ejson.serverURL().concat("/api/v1/push.get")).newBuilder();
Request request = new Request.Builder()
.header("x-user-id", ejson.userId())
.header("x-auth-token", ejson.token())
.url(url.addQueryParameter("id", ejson.messageId).build())
.build();
runRequest(client, request, callback);
}
private static void runRequest(OkHttpClient client, Request request, Callback callback) {
try {
Thread.sleep(TIMEOUT[RETRY_COUNT] * 1000);
Response response = client.newCall(request).execute();
String body = response.body().string();
if (!response.isSuccessful()) {
throw new Exception("Error");
}
Gson gson = new Gson();
JsonResponse json = gson.fromJson(body, JsonResponse.class);
Bundle bundle = new Bundle();
bundle.putString("notId", json.data.notification.notId);
bundle.putString("title", json.data.notification.title);
bundle.putString("message", json.data.notification.text);
bundle.putString("ejson", gson.toJson(json.data.notification.payload));
bundle.putBoolean("notificationLoaded", true);
callback.call(bundle);
} catch (Exception e) {
if (RETRY_COUNT <= TIMEOUT.length) {
RETRY_COUNT++;
runRequest(client, request, callback);
} else {
callback.call(null);
}
}
}
}

View File

@ -29,10 +29,6 @@ import com.wix.reactnativenotifications.core.notification.INotificationsApplicat
import com.wix.reactnativenotifications.core.notification.IPushNotification;
import com.wix.reactnativekeyboardinput.KeyboardInputPackage;
import io.invertase.firebase.fabric.crashlytics.RNFirebaseCrashlyticsPackage;
import io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage;
import io.invertase.firebase.perf.RNFirebasePerformancePackage;
import com.nozbe.watermelondb.WatermelonDBPackage;
import com.reactnativecommunity.viewpager.RNCViewPagerPackage;
@ -53,9 +49,6 @@ public class MainApplication extends Application implements ReactApplication, IN
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new RNFirebaseCrashlyticsPackage());
packages.add(new RNFirebaseAnalyticsPackage());
packages.add(new RNFirebasePerformancePackage());
packages.add(new KeyboardInputPackage(MainApplication.this));
packages.add(new RNNotificationsPackage(MainApplication.this));
packages.add(new WatermelonDBPackage());
@ -88,38 +81,6 @@ public class MainApplication extends Application implements ReactApplication, IN
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("chat.rocket.reactnative");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
@Override

View File

@ -1,10 +1,10 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "28.0.3"
buildToolsVersion = "29.0.2"
minSdkVersion = 21
compileSdkVersion = 28
targetSdkVersion = 28
compileSdkVersion = 29
targetSdkVersion = 29
glideVersion = "4.9.0"
kotlin_version = "1.3.50"
supportLibVersion = "28.0.0"
@ -18,10 +18,9 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.google.gms:google-services:4.2.0'
classpath 'io.fabric.tools:gradle:1.28.1'
classpath 'com.google.firebase:perf-plugin:1.2.1'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.+'
@ -57,10 +56,10 @@ subprojects { subproject ->
afterEvaluate {
if ((subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) {
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
targetSdkVersion 28
targetSdkVersion 29
}
variantFilter { variant ->
def names = variant.flavors*.name

View File

@ -27,7 +27,7 @@ android.useAndroidX=true
android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.33.1
FLIPPER_VERSION=0.51.0
# App properties
VERSIONCODE=999999999

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

29
android/gradlew vendored
View File

@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@ -175,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

5
android/gradlew.bat vendored
View File

@ -5,7 +5,7 @@
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

View File

@ -33,6 +33,7 @@ export const ROOMS = createRequestTypes('ROOMS', [
'CLOSE_SEARCH_HEADER'
]);
export const ROOM = createRequestTypes('ROOM', ['SUBSCRIBE', 'UNSUBSCRIBE', 'LEAVE', 'DELETE', 'REMOVED', 'CLOSE', 'FORWARD', 'USER_TYPING']);
export const INQUIRY = createRequestTypes('INQUIRY', [...defaultTypes, 'SET_ENABLED', 'RESET', 'QUEUE_ADD', 'QUEUE_UPDATE', 'QUEUE_REMOVE']);
export const APP = createRequestTypes('APP', ['START', 'READY', 'INIT', 'INIT_LOCAL_SETTINGS', 'SET_MASTER_DETAIL']);
export const MESSAGES = createRequestTypes('MESSAGES', ['REPLY_BROADCAST']);
export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [...defaultTypes]);

55
app/actions/inquiry.js Normal file
View File

@ -0,0 +1,55 @@
import * as types from './actionsTypes';
export function inquirySetEnabled(enabled) {
return {
type: types.INQUIRY.SET_ENABLED,
enabled
};
}
export function inquiryReset() {
return {
type: types.INQUIRY.RESET
};
}
export function inquiryQueueAdd(inquiry) {
return {
type: types.INQUIRY.QUEUE_ADD,
inquiry
};
}
export function inquiryQueueUpdate(inquiry) {
return {
type: types.INQUIRY.QUEUE_UPDATE,
inquiry
};
}
export function inquiryQueueRemove(inquiryId) {
return {
type: types.INQUIRY.QUEUE_REMOVE,
inquiryId
};
}
export function inquiryRequest() {
return {
type: types.INQUIRY.REQUEST
};
}
export function inquirySuccess(inquiries) {
return {
type: types.INQUIRY.SUCCESS,
inquiries
};
}
export function inquiryFailure(error) {
return {
type: types.INQUIRY.FAILURE,
error
};
}

View File

@ -10,6 +10,16 @@ export const SWITCH_TRACK_COLOR = {
true: '#2de0a5'
};
const mentions = {
unreadBackground: '#414852',
mentionMeColor: '#f5455c',
mentionMeBackground: '#ffe9ec',
mentionGroupColor: '#f38c39',
mentionGroupBackground: '#fde8d7',
mentionOtherColor: '#b68d00',
mentionOtherBackground: '#fff6d6'
};
export const themes = {
light: {
backgroundColor: '#ffffff',
@ -53,7 +63,8 @@ export const themes = {
passcodeDotEmpty: '#CBCED1',
passcodeDotFull: '#6C727A',
previewBackground: '#1F2329',
previewTintColor: '#ffffff'
previewTintColor: '#ffffff',
...mentions
},
dark: {
backgroundColor: '#030b1b',
@ -97,7 +108,8 @@ export const themes = {
passcodeDotEmpty: '#CBCED1',
passcodeDotFull: '#6C727A',
previewBackground: '#030b1b',
previewTintColor: '#ffffff'
previewTintColor: '#ffffff',
...mentions
},
black: {
backgroundColor: '#000000',
@ -141,6 +153,7 @@ export const themes = {
passcodeDotEmpty: '#CBCED1',
passcodeDotFull: '#6C727A',
previewBackground: '#000000',
previewTintColor: '#ffffff'
previewTintColor: '#ffffff',
...mentions
}
};

View File

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';
import FastImage from 'react-native-fast-image';
import FastImage from '@rocket.chat/react-native-fast-image';
import Touchable from 'react-native-platform-touchable';
import { settings as RocketChatSettings } from '@rocket.chat/sdk';

View File

@ -1,5 +1,5 @@
import React from 'react';
import FastImage from 'react-native-fast-image';
import FastImage from '@rocket.chat/react-native-fast-image';
import PropTypes from 'prop-types';
const CustomEmoji = React.memo(({ baseUrl, emoji, style }) => (

View File

@ -28,7 +28,7 @@ export const CustomHeaderButtons = React.memo(props => (
export const DrawerButton = React.memo(({ navigation, testID, ...otherProps }) => (
<CustomHeaderButtons left>
<Item title='drawer' iconName='menu_hamburguer' onPress={navigation.toggleDrawer} testID={testID} {...otherProps} />
<Item title='drawer' iconName='hamburguer' onPress={navigation.toggleDrawer} testID={testID} {...otherProps} />
</CustomHeaderButtons>
));
@ -36,7 +36,7 @@ export const CloseModalButton = React.memo(({
navigation, testID, onPress = () => navigation.pop(), ...props
}) => (
<CustomHeaderButtons left>
<Item title='close' iconName='Cross' onPress={onPress} testID={testID} {...props} />
<Item title='close' iconName='close' onPress={onPress} testID={testID} {...props} />
</CustomHeaderButtons>
));
@ -44,14 +44,14 @@ export const CancelModalButton = React.memo(({ onPress, testID }) => (
<CustomHeaderButtons left>
{isIOS
? <Item title={I18n.t('Cancel')} onPress={onPress} testID={testID} />
: <Item title='close' iconName='Cross' onPress={onPress} testID={testID} />
: <Item title='close' iconName='close' onPress={onPress} testID={testID} />
}
</CustomHeaderButtons>
));
export const MoreButton = React.memo(({ onPress, testID }) => (
<CustomHeaderButtons>
<Item title='more' iconName='menu' onPress={onPress} testID={testID} />
<Item title='more' iconName='kebab' onPress={onPress} testID={testID} />
</CustomHeaderButtons>
));

View File

@ -90,6 +90,8 @@ const NotifierComponent = React.memo(({
if (isMasterDetail) {
Navigation.navigate('DrawerNavigator');
} else {
Navigation.navigate('RoomsListView');
}
goRoom({ item, isMasterDetail });
hideNotification();
@ -125,7 +127,7 @@ const NotifierComponent = React.memo(({
hitSlop={BUTTON_HIT_SLOP}
background={Touchable.SelectableBackgroundBorderless()}
>
<CustomIcon name='Cross' style={[styles.close, { color: themes[theme].titleText }]} size={20} />
<CustomIcon name='close' style={[styles.close, { color: themes[theme].titleText }]} size={20} />
</Touchable>
</View>
);

View File

@ -14,7 +14,7 @@ const InAppNotification = memo(() => {
const state = Navigation.navigationRef.current?.getRootState();
const route = getActiveRoute(state);
if (payload.rid) {
if (route?.name === 'RoomView' && route.params?.rid === payload.rid) {
if ((route?.name === 'RoomView' && route.params?.rid === payload.rid) || route?.name === 'JitsiMeetView') {
return;
}
Notifier.showNotification({

View File

@ -15,6 +15,7 @@ import OrSeparator from './OrSeparator';
import Touch from '../utils/touch';
import I18n from '../i18n';
import random from '../utils/random';
import { logEvent, events } from '../utils/log';
import RocketChat from '../lib/rocketchat';
const BUTTON_HEIGHT = 48;
@ -77,6 +78,7 @@ class LoginServices extends React.PureComponent {
}
onPressFacebook = () => {
logEvent(events.ENTER_WITH_FACEBOOK);
const { services, server } = this.props;
const { clientId } = services.facebook;
const endpoint = 'https://m.facebook.com/v2.9/dialog/oauth';
@ -88,6 +90,7 @@ class LoginServices extends React.PureComponent {
}
onPressGithub = () => {
logEvent(events.ENTER_WITH_GITHUB);
const { services, server } = this.props;
const { clientId } = services.github;
const endpoint = `https://github.com/login?client_id=${ clientId }&return_to=${ encodeURIComponent('/login/oauth/authorize') }`;
@ -99,6 +102,7 @@ class LoginServices extends React.PureComponent {
}
onPressGitlab = () => {
logEvent(events.ENTER_WITH_GITLAB);
const { services, server, Gitlab_URL } = this.props;
const { clientId } = services.gitlab;
const baseURL = Gitlab_URL ? Gitlab_URL.trim().replace(/\/*$/, '') : 'https://gitlab.com';
@ -111,6 +115,7 @@ class LoginServices extends React.PureComponent {
}
onPressGoogle = () => {
logEvent(events.ENTER_WITH_GOOGLE);
const { services, server } = this.props;
const { clientId } = services.google;
const endpoint = 'https://accounts.google.com/o/oauth2/auth';
@ -122,6 +127,7 @@ class LoginServices extends React.PureComponent {
}
onPressLinkedin = () => {
logEvent(events.ENTER_WITH_LINKEDIN);
const { services, server } = this.props;
const { clientId } = services.linkedin;
const endpoint = 'https://www.linkedin.com/oauth/v2/authorization';
@ -133,6 +139,7 @@ class LoginServices extends React.PureComponent {
}
onPressMeteor = () => {
logEvent(events.ENTER_WITH_METEOR);
const { services, server } = this.props;
const { clientId } = services['meteor-developer'];
const endpoint = 'https://www.meteor.com/oauth2/authorize';
@ -143,6 +150,7 @@ class LoginServices extends React.PureComponent {
}
onPressTwitter = () => {
logEvent(events.ENTER_WITH_TWITTER);
const { server } = this.props;
const state = this.getOAuthState();
const url = `${ server }/_oauth/twitter/?requestTokenAndRedirect=true&state=${ state }`;
@ -150,6 +158,7 @@ class LoginServices extends React.PureComponent {
}
onPressWordpress = () => {
logEvent(events.ENTER_WITH_WORDPRESS);
const { services, server } = this.props;
const { clientId, serverURL } = services.wordpress;
const endpoint = `${ serverURL }/oauth/authorize`;
@ -161,6 +170,7 @@ class LoginServices extends React.PureComponent {
}
onPressCustomOAuth = (loginService) => {
logEvent(events.ENTER_WITH_CUSTOM_OAUTH);
const { server } = this.props;
const {
serverURL, authorizePath, clientId, scope, service
@ -175,6 +185,7 @@ class LoginServices extends React.PureComponent {
}
onPressSaml = (loginService) => {
logEvent(events.ENTER_WITH_SAML);
const { server } = this.props;
const { clientConfig } = loginService;
const { provider } = clientConfig;
@ -184,6 +195,7 @@ class LoginServices extends React.PureComponent {
}
onPressCas = () => {
logEvent(events.ENTER_WITH_CAS);
const { server, CAS_login_url } = this.props;
const ssoToken = random(17);
const url = `${ CAS_login_url }?service=${ server }/_cas/${ ssoToken }`;
@ -191,6 +203,7 @@ class LoginServices extends React.PureComponent {
}
onPressAppleLogin = async() => {
logEvent(events.ENTER_WITH_APPLE);
try {
const { fullName, email, identityToken } = await AppleAuthentication.signInAsync({
requestedScopes: [
@ -201,7 +214,7 @@ class LoginServices extends React.PureComponent {
await RocketChat.loginOAuthOrSso({ fullName, email, identityToken });
} catch {
// Do nothing
logEvent(events.ENTER_WITH_APPLE_F);
}
}

View File

@ -78,7 +78,7 @@ const HeaderFooter = React.memo(({ onReaction, theme }) => (
style={[styles.headerItem, { backgroundColor: themes[theme].auxiliaryBackground }]}
theme={theme}
>
<CustomIcon name='add-reaction' size={24} color={themes[theme].bodyText} />
<CustomIcon name='reaction-add' size={24} color={themes[theme].bodyText} />
</Button>
));
HeaderFooter.propTypes = {

View File

@ -7,7 +7,7 @@ import moment from 'moment';
import RocketChat from '../../lib/rocketchat';
import database from '../../lib/database';
import I18n from '../../i18n';
import log from '../../utils/log';
import log, { logEvent } from '../../utils/log';
import Navigation from '../../lib/Navigation';
import { getMessageTranslation } from '../message/utils';
import { LISTENER } from '../Toast';
@ -15,6 +15,7 @@ import EventEmitter from '../../utils/events';
import { showConfirmationAlert } from '../../utils/info';
import { useActionSheet } from '../ActionSheet';
import Header, { HEADER_HEIGHT } from './Header';
import events from '../../utils/log/events';
const MessageActions = React.memo(forwardRef(({
room,
@ -112,11 +113,18 @@ const MessageActions = React.memo(forwardRef(({
const getPermalink = message => RocketChat.getPermalinkMessage(message);
const handleReply = message => replyInit(message, true);
const handleReply = (message) => {
logEvent(events.ROOM_MSG_ACTION_REPLY);
replyInit(message, true);
};
const handleEdit = message => editInit(message);
const handleEdit = (message) => {
logEvent(events.ROOM_MSG_ACTION_EDIT);
editInit(message);
};
const handleCreateDiscussion = (message) => {
logEvent(events.ROOM_MSG_ACTION_DISCUSSION);
const params = { message, channel: room, showCloseModal: true };
if (isMasterDetail) {
Navigation.navigate('ModalStackNavigator', { screen: 'CreateDiscussionView', params });
@ -126,6 +134,7 @@ const MessageActions = React.memo(forwardRef(({
};
const handleUnread = async(message) => {
logEvent(events.ROOM_MSG_ACTION_UNREAD);
const { id: messageId, ts } = message;
const { rid } = room;
try {
@ -144,54 +153,66 @@ const MessageActions = React.memo(forwardRef(({
Navigation.navigate('RoomsListView');
}
} catch (e) {
logEvent(events.ROOM_MSG_ACTION_UNREAD_F);
log(e);
}
};
const handlePermalink = async(message) => {
logEvent(events.ROOM_MSG_ACTION_PERMALINK);
try {
const permalink = await getPermalink(message);
Clipboard.setString(permalink);
EventEmitter.emit(LISTENER, { message: I18n.t('Permalink_copied_to_clipboard') });
} catch {
// Do nothing
logEvent(events.ROOM_MSG_ACTION_PERMALINK_F);
}
};
const handleCopy = async(message) => {
logEvent(events.ROOM_MSG_ACTION_COPY);
await Clipboard.setString(message.msg);
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
};
const handleShare = async(message) => {
logEvent(events.ROOM_MSG_ACTION_SHARE);
try {
const permalink = await getPermalink(message);
Share.share({ message: permalink });
} catch {
// Do nothing
logEvent(events.ROOM_MSG_ACTION_SHARE_F);
}
};
const handleQuote = message => replyInit(message, false);
const handleQuote = (message) => {
logEvent(events.ROOM_MSG_ACTION_QUOTE);
replyInit(message, false);
};
const handleStar = async(message) => {
logEvent(message.starred ? events.ROOM_MSG_ACTION_UNSTAR : events.ROOM_MSG_ACTION_STAR);
try {
await RocketChat.toggleStarMessage(message.id, message.starred);
EventEmitter.emit(LISTENER, { message: message.starred ? I18n.t('Message_unstarred') : I18n.t('Message_starred') });
} catch (e) {
logEvent(events.ROOM_MSG_ACTION_STAR_F);
log(e);
}
};
const handlePin = async(message) => {
logEvent(events.ROOM_MSG_ACTION_PIN);
try {
await RocketChat.togglePinMessage(message.id, message.pinned);
} catch (e) {
logEvent(events.ROOM_MSG_ACTION_PIN_F);
log(e);
}
};
const handleReaction = (shortname, message) => {
logEvent(events.ROOM_MSG_ACTION_REACTION);
if (shortname) {
onReactionPress(shortname, message.id);
} else {
@ -201,7 +222,13 @@ const MessageActions = React.memo(forwardRef(({
hideActionSheet();
};
const handleReadReceipt = message => Navigation.navigate('ReadReceiptsView', { messageId: message.id });
const handleReadReceipt = (message) => {
if (isMasterDetail) {
Navigation.navigate('ModalStackNavigator', { screen: 'ReadReceiptsView', params: { messageId: message.id } });
} else {
Navigation.navigate('ReadReceiptsView', { messageId: message.id });
}
};
const handleToggleTranslation = async(message) => {
try {
@ -228,10 +255,12 @@ const MessageActions = React.memo(forwardRef(({
};
const handleReport = async(message) => {
logEvent(events.ROOM_MSG_ACTION_REPORT);
try {
await RocketChat.reportMessage(message.id);
Alert.alert(I18n.t('Message_Reported'));
} catch (e) {
logEvent(events.ROOM_MSG_ACTION_REPORT_F);
log(e);
}
};
@ -242,8 +271,10 @@ const MessageActions = React.memo(forwardRef(({
callToAction: I18n.t('Delete'),
onPress: async() => {
try {
logEvent(events.ROOM_MSG_ACTION_DELETE);
await RocketChat.deleteMessage(message.id, message.subscription.id);
} catch (e) {
logEvent(events.ROOM_MSG_ACTION_DELETE_F);
log(e);
}
}
@ -290,7 +321,7 @@ const MessageActions = React.memo(forwardRef(({
// Create Discussion
options.push({
title: I18n.t('Start_a_Discussion'),
icon: 'chat',
icon: 'discussions',
onPress: () => handleCreateDiscussion(message)
});
@ -365,7 +396,7 @@ const MessageActions = React.memo(forwardRef(({
if (allowDelete(message)) {
options.push({
title: I18n.t('Delete'),
icon: 'trash',
icon: 'delete',
danger: true,
onPress: () => handleDelete(message)
});
@ -375,6 +406,7 @@ const MessageActions = React.memo(forwardRef(({
};
const showMessageActions = async(message) => {
logEvent(events.ROOM_SHOW_MSG_ACTIONS);
await getPermissions();
showActionSheet({
options: getOptions(message),

View File

@ -1,7 +1,7 @@
import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { TouchableOpacity } from 'react-native';
import FastImage from 'react-native-fast-image';
import FastImage from '@rocket.chat/react-native-fast-image';
import styles from '../styles';
import { CustomIcon } from '../../../lib/Icons';
@ -32,7 +32,7 @@ const Item = ({ item, theme }) => {
{ loading ? <ActivityIndicator theme={theme} /> : null }
</FastImage>
)
: <CustomIcon name='clip' size={36} color={themes[theme].actionTintColor} />
: <CustomIcon name='attach' size={36} color={themes[theme].actionTintColor} />
}
</TouchableOpacity>
);

View File

@ -10,6 +10,7 @@ import styles from './styles';
import I18n from '../../i18n';
import { themes } from '../../constants/colors';
import { CustomIcon } from '../../lib/Icons';
import { logEvent, events } from '../../utils/log';
const RECORDING_EXTENSION = '.aac';
const RECORDING_SETTINGS = {
@ -103,6 +104,7 @@ export default class RecordAudio extends React.PureComponent {
}
startRecordingAudio = async() => {
logEvent(events.ROOM_AUDIO_RECORD);
if (!this.isRecorderBusy) {
this.isRecorderBusy = true;
try {
@ -120,13 +122,14 @@ export default class RecordAudio extends React.PureComponent {
await Audio.requestPermissionsAsync();
}
} catch (error) {
// Do nothing
logEvent(events.ROOM_AUDIO_RECORD_F);
}
this.isRecorderBusy = false;
}
};
finishRecordingAudio = async() => {
logEvent(events.ROOM_AUDIO_FINISH);
if (!this.isRecorderBusy) {
const { onFinish } = this.props;
@ -147,7 +150,7 @@ export default class RecordAudio extends React.PureComponent {
onFinish(fileInfo);
} catch (error) {
// Do nothing
logEvent(events.ROOM_AUDIO_FINISH_F);
}
this.setState({ isRecording: false, recordingDurationMillis: 0 });
deactivateKeepAwake();
@ -156,12 +159,13 @@ export default class RecordAudio extends React.PureComponent {
};
cancelRecordingAudio = async() => {
logEvent(events.ROOM_AUDIO_CANCEL);
if (!this.isRecorderBusy) {
this.isRecorderBusy = true;
try {
await this.recording.stopAndUnloadAsync();
} catch (error) {
// Do nothing
logEvent(events.ROOM_AUDIO_CANCEL_F);
}
this.setState({ isRecording: false, recordingDurationMillis: 0 });
deactivateKeepAwake();
@ -182,7 +186,7 @@ export default class RecordAudio extends React.PureComponent {
accessibilityLabel={I18n.t('Send_audio_message')}
accessibilityTraits='button'
>
<CustomIcon name='mic' size={23} color={themes[theme].tintColor} />
<CustomIcon name='microphone' size={23} color={themes[theme].tintColor} />
</BorderlessButton>
);
}
@ -199,7 +203,7 @@ export default class RecordAudio extends React.PureComponent {
<CustomIcon
size={22}
color={themes[theme].dangerColor}
name='Cross'
name='close'
/>
</BorderlessButton>
<Text

View File

@ -72,7 +72,7 @@ const ReplyPreview = React.memo(({
theme={theme}
/>
</View>
<CustomIcon name='Cross' color={themes[theme].auxiliaryText} size={20} style={styles.close} onPress={close} />
<CustomIcon name='close' color={themes[theme].auxiliaryText} size={20} style={styles.close} onPress={close} />
</View>
);
}, (prevProps, nextProps) => prevProps.replying === nextProps.replying && prevProps.theme === nextProps.theme && isEqual(prevProps.message, nextProps.message));

View File

@ -8,7 +8,7 @@ const ActionsButton = React.memo(({ theme, onPress }) => (
onPress={onPress}
testID='messagebox-actions'
accessibilityLabel='Message_actions'
icon='plus'
icon='add'
theme={theme}
/>
));

View File

@ -8,7 +8,7 @@ const CancelEditingButton = React.memo(({ theme, onPress }) => (
onPress={onPress}
testID='messagebox-cancel-editing'
accessibilityLabel='Cancel_editing'
icon='Cross'
icon='close'
theme={theme}
/>
));

View File

@ -8,7 +8,7 @@ const SendButton = React.memo(({ theme, onPress }) => (
onPress={onPress}
testID='messagebox-send-message'
accessibilityLabel='Send_message'
icon='send-active'
icon='send-filled'
theme={theme}
/>
));

View File

@ -17,8 +17,8 @@ import RocketChat from '../../lib/rocketchat';
import styles from './styles';
import database from '../../lib/database';
import { emojis } from '../../emojis';
import log, { logEvent, events } from '../../utils/log';
import RecordAudio from './RecordAudio';
import log from '../../utils/log';
import I18n from '../../i18n';
import ReplyPreview from './ReplyPreview';
import debounce from '../../utils/debounce';
@ -122,6 +122,7 @@ class MessageBox extends Component {
command: {}
};
this.text = '';
this.selection = { start: 0, end: 0 };
this.focused = false;
// MessageBox Actions
@ -133,7 +134,7 @@ class MessageBox extends Component {
},
{
title: I18n.t('Take_a_video'),
icon: 'video-1',
icon: 'camera',
onPress: this.takeVideo
},
{
@ -143,12 +144,12 @@ class MessageBox extends Component {
},
{
title: I18n.t('Choose_file'),
icon: 'folder',
icon: 'attach',
onPress: this.chooseFile
},
{
title: I18n.t('Create_Discussion'),
icon: 'chat',
icon: 'discussions',
onPress: this.createDiscussion
}
];
@ -331,6 +332,10 @@ class MessageBox extends Component {
this.setInput(text);
}
onSelectionChange = (e) => {
this.selection = e.nativeEvent.selection;
}
// eslint-disable-next-line react/sort-comp
debouncedOnChangeText = debounce(async(text) => {
const { sharing } = this.props;
@ -358,9 +363,9 @@ class MessageBox extends Component {
if (!isTextEmpty) {
try {
const { start, end } = this.component?.lastNativeSelection;
const { start, end } = this.selection;
const cursor = Math.max(start, end);
const lastNativeText = this.component?.lastNativeText || '';
const lastNativeText = this.text;
// matches if text either starts with '/' or have (@,#,:) then it groups whatever comes next of mention type
let regexp = /(#|@|:|^\/)([a-z0-9._-]+)$/im;
@ -399,7 +404,7 @@ class MessageBox extends Component {
}
const { trackingType } = this.state;
const msg = this.text;
const { start, end } = this.component?.lastNativeSelection;
const { start, end } = this.selection;
const cursor = Math.max(start, end);
const regexp = /([a-z0-9._-]+)$/im;
const result = msg.substr(0, cursor).replace(regexp, '');
@ -410,7 +415,8 @@ class MessageBox extends Component {
if ((trackingType === MENTIONS_TRACKING_TYPE_COMMANDS) && item.providesPreview) {
this.setState({ showCommandPreview: true });
}
this.setInput(text);
const newCursor = cursor + mentionName.length;
this.setInput(text, { start: newCursor, end: newCursor });
this.focus();
requestAnimationFrame(() => this.stopTrackingMention());
}
@ -443,15 +449,11 @@ class MessageBox extends Component {
let newText = '';
// if messagebox has an active cursor
if (this.component?.lastNativeSelection) {
const { start, end } = this.component.lastNativeSelection;
const cursor = Math.max(start, end);
newText = `${ text.substr(0, cursor) }${ emoji }${ text.substr(cursor) }`;
} else {
// if messagebox doesn't have a cursor, just append selected emoji
newText = `${ text }${ emoji }`;
}
this.setInput(newText);
const { start, end } = this.selection;
const cursor = Math.max(start, end);
newText = `${ text.substr(0, cursor) }${ emoji }${ text.substr(cursor) }`;
const newCursor = cursor + emoji.length;
this.setInput(newText, { start: newCursor, end: newCursor });
this.setShowSend(true);
}
@ -551,11 +553,12 @@ class MessageBox extends Component {
this.setState({ commandPreview: [], showCommandPreview: true, command: {} });
}
setInput = (text) => {
setInput = (text, selection) => {
this.text = text;
if (this.component && this.component.setNativeProps) {
this.component.setNativeProps({ text });
if (selection) {
return this.component.setTextAndSelection(text, selection);
}
this.component.setNativeProps({ text });
}
setShowSend = (showSend) => {
@ -582,37 +585,41 @@ class MessageBox extends Component {
}
takePhoto = async() => {
logEvent(events.ROOM_BOX_ACTION_PHOTO);
try {
const image = await ImagePicker.openCamera(this.imagePickerConfig);
if (this.canUploadFile(image)) {
this.openShareView([image]);
}
} catch (e) {
// Do nothing
logEvent(events.ROOM_BOX_ACTION_PHOTO_F);
}
}
takeVideo = async() => {
logEvent(events.ROOM_BOX_ACTION_VIDEO);
try {
const video = await ImagePicker.openCamera(this.videoPickerConfig);
if (this.canUploadFile(video)) {
this.openShareView([video]);
}
} catch (e) {
// Do nothing
logEvent(events.ROOM_BOX_ACTION_VIDEO_F);
}
}
chooseFromLibrary = async() => {
logEvent(events.ROOM_BOX_ACTION_LIBRARY);
try {
const attachments = await ImagePicker.openPicker(this.libraryPickerConfig);
this.openShareView(attachments);
} catch (e) {
// Do nothing
logEvent(events.ROOM_BOX_ACTION_LIBRARY_F);
}
}
chooseFile = async() => {
logEvent(events.ROOM_BOX_ACTION_FILE);
try {
const res = await DocumentPicker.pick({
type: [DocumentPicker.types.allFiles]
@ -628,6 +635,7 @@ class MessageBox extends Component {
}
} catch (e) {
if (!DocumentPicker.isCancel(e)) {
logEvent(events.ROOM_BOX_ACTION_FILE_F);
log(e);
}
}
@ -645,6 +653,7 @@ class MessageBox extends Component {
}
createDiscussion = () => {
logEvent(events.ROOM_BOX_ACTION_DISCUSSION);
const { isMasterDetail } = this.props;
const params = { channel: this.room, showCloseModal: true };
if (isMasterDetail) {
@ -655,6 +664,7 @@ class MessageBox extends Component {
}
showMessageBoxActions = () => {
logEvent(events.ROOM_SHOW_BOX_ACTIONS);
const { showActionSheet } = this.props;
showActionSheet({ options: this.options });
}
@ -665,10 +675,9 @@ class MessageBox extends Component {
this.clearInput();
}
openEmoji = async() => {
await this.setState({
showEmojiKeyboard: true
});
openEmoji = () => {
logEvent(events.ROOM_OPEN_EMOJI);
this.setState({ showEmojiKeyboard: true });
}
recordingCallback = (recording) => {
@ -729,6 +738,7 @@ class MessageBox extends Component {
Q.where('id', Q.like(`${ Q.sanitizeLikeString(command) }%`))
).fetch();
if (slashCommand.length > 0) {
logEvent(events.COMMAND_RUN);
try {
const messageWithoutCommand = message.replace(/([^\s]+)/, '').trim();
const [{ appId }] = slashCommand;
@ -736,6 +746,7 @@ class MessageBox extends Component {
RocketChat.runSlashCommand(command, roomId, messageWithoutCommand, triggerId, tmid || messageTmid);
replyCancel();
} catch (e) {
logEvent(events.COMMAND_RUN_F);
log(e);
}
this.clearInput();
@ -888,6 +899,7 @@ class MessageBox extends Component {
blurOnSubmit={false}
placeholder={I18n.t('New_Message')}
onChangeText={this.onChangeText}
onSelectionChange={this.onSelectionChange}
underlineColorAndroid='transparent'
defaultValue=''
multiline

View File

@ -81,7 +81,7 @@ const MessageErrorActions = forwardRef(({ tmid }, ref) => {
},
{
title: I18n.t('Delete'),
icon: 'trash',
icon: 'delete',
danger: true,
onPress: () => handleDelete(message)
}

View File

@ -10,7 +10,7 @@ import { CustomIcon } from '../../../lib/Icons';
const LockIcon = React.memo(({ theme }) => (
<Row style={styles.row}>
<View style={styles.iconView}>
<CustomIcon name='lock' size={40} color={themes[theme].passcodeLockIcon} />
<CustomIcon name='auth' size={40} color={themes[theme].passcodeLockIcon} />
</View>
</Row>
));

View File

@ -104,7 +104,7 @@ const ModalContent = React.memo(({
<View style={styles.titleContainer}>
<CustomIcon
style={[styles.closeButton, { color: themes[props.theme].buttonText }]}
name='Cross'
name='close'
size={20}
/>
<Text style={[styles.title, { color: themes[props.theme].buttonText }]}>{I18n.t('Reactions')}</Text>

View File

@ -20,19 +20,19 @@ const RoomTypeIcon = React.memo(({
const color = themes[theme].auxiliaryText;
let icon = 'lock';
let icon = 'channel-private';
if (type === 'discussion') {
icon = 'chat';
icon = 'discussions';
} else if (type === 'c') {
icon = 'hash';
icon = 'channel-public';
} else if (type === 'd') {
if (isGroupChat) {
icon = 'team';
} else {
icon = 'at';
icon = 'mention';
}
} else if (type === 'l') {
icon = 'livechat';
icon = 'omnichannel';
}
return (

View File

@ -61,7 +61,7 @@ const SearchBox = ({
]}
>
<View style={[styles.searchBox, { backgroundColor: themes[theme].searchboxBackground }]}>
<CustomIcon name='magnifier' size={14} color={themes[theme].auxiliaryText} />
<CustomIcon name='search' size={14} color={themes[theme].auxiliaryText} />
<TextInput
ref={inputRef}
autoCapitalize='none'

View File

@ -14,7 +14,7 @@ const Status = React.memo(({
borderRadius: size,
width: size,
height: size,
backgroundColor: STATUS_COLORS[status],
backgroundColor: STATUS_COLORS[status] ?? STATUS_COLORS.offline,
borderColor: themes[theme].backgroundColor
}
]}

View File

@ -111,7 +111,7 @@ export default class RCTextInput extends React.PureComponent {
return (
<BorderlessButton onPress={this.tooglePassword} style={[styles.iconContainer, styles.iconRight]}>
<CustomIcon
name={showPassword ? 'eye' : 'eye-off'}
name={showPassword ? 'unread-on-top' : 'unread-on-top-disabled'}
testID={testID ? `${ testID }-icon-right` : null}
style={{ color: themes[theme].auxiliaryText }}
size={20}

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { View, Text } from 'react-native';
import { View, Text, InteractionManager } from 'react-native';
import _ from 'lodash';
import PropTypes from 'prop-types';
import { sha256 } from 'js-sha256';
@ -99,6 +99,7 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }) => {
<TextInput
value={code}
theme={theme}
inputRef={e => InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())}
returnKeyType='send'
autoCapitalize='none'
onChangeText={setCode}

View File

@ -14,9 +14,10 @@ export default StyleSheet.create({
borderRadius: 4
},
title: {
fontSize: 14,
fontSize: 16,
paddingBottom: 8,
...sharedStyles.textBold
...sharedStyles.textBold,
...sharedStyles.textAlignCenter
},
subtitle: {
fontSize: 14,

View File

@ -1,6 +1,6 @@
import React from 'react';
import { View, StyleSheet } from 'react-native';
import FastImage from 'react-native-fast-image';
import FastImage from '@rocket.chat/react-native-fast-image';
import PropTypes from 'prop-types';
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';

View File

@ -2,7 +2,7 @@ import React from 'react';
import { Text, View } from 'react-native';
import PropTypes from 'prop-types';
import Touchable from 'react-native-platform-touchable';
import FastImage from 'react-native-fast-image';
import FastImage from '@rocket.chat/react-native-fast-image';
import { themes } from '../../../constants/colors';
import { textParser } from '../utils';
@ -24,7 +24,7 @@ const Chip = ({
<>
{item.imageUrl ? <FastImage style={styles.chipImage} source={{ uri: item.imageUrl }} /> : null}
<Text numberOfLines={1} style={[styles.chipText, { color: themes[theme].titleText }]}>{textParser([item.text])}</Text>
<CustomIcon name='Cross' size={16} color={themes[theme].auxiliaryText} />
<CustomIcon name='close' size={16} color={themes[theme].auxiliaryText} />
</>
</Touchable>
);

View File

@ -2,7 +2,7 @@ import React from 'react';
import { Text, FlatList } from 'react-native';
import PropTypes from 'prop-types';
import Touchable from 'react-native-platform-touchable';
import FastImage from 'react-native-fast-image';
import FastImage from '@rocket.chat/react-native-fast-image';
import Separator from '../../Separator';
import Check from '../../Check';

View File

@ -82,7 +82,7 @@ export const Overflow = ({
hitSlop={BUTTON_HIT_SLOP}
style={styles.menu}
>
{!loading ? <CustomIcon size={18} name='menu' color={themes[theme].bodyText} /> : <ActivityIndicator style={styles.loading} theme={theme} />}
{!loading ? <CustomIcon size={18} name='kebab' color={themes[theme].bodyText} /> : <ActivityIndicator style={styles.loading} theme={theme} />}
</Touchable>
<Popover
isVisible={show}

View File

@ -5,30 +5,44 @@ import { Text } from 'react-native';
import { themes } from '../../constants/colors';
import styles from './styles';
import { logEvent, events } from '../../utils/log';
const AtMention = React.memo(({
mention, mentions, username, navToRoomInfo, style = [], useRealName, theme
}) => {
let mentionStyle = { ...styles.mention, color: themes[theme].buttonText };
if (mention === 'all' || mention === 'here') {
return <Text style={[mentionStyle, styles.mentionAll, ...style]}>{mention}</Text>;
return (
<Text
style={[
styles.mention,
{
color: themes[theme].mentionGroupColor,
backgroundColor: themes[theme].mentionGroupBackground
},
...style
]}
>{mention}
</Text>
);
}
let mentionStyle = {};
if (mention === username) {
mentionStyle = {
...mentionStyle,
backgroundColor: themes[theme].actionTintColor
color: themes[theme].mentionMeColor,
backgroundColor: themes[theme].mentionMeBackground
};
} else {
mentionStyle = {
...mentionStyle,
color: themes[theme].actionTintColor
color: themes[theme].mentionOtherColor,
backgroundColor: themes[theme].mentionOtherBackground
};
}
const user = mentions && mentions.length && mentions.find(m => m.username === mention);
const user = mentions?.find?.(m => m && m.username === mention);
const handlePress = () => {
logEvent(events.ROOM_MENTION_GO_USER_INFO);
const navParam = {
t: 'd',
rid: user && user._id
@ -39,7 +53,7 @@ const AtMention = React.memo(({
if (user) {
return (
<Text
style={[mentionStyle, ...style]}
style={[styles.mention, mentionStyle, ...style]}
onPress={handlePress}
>
{useRealName && user.name ? user.name : user.username}

View File

@ -21,10 +21,16 @@ const Hashtag = React.memo(({
if (channels && channels.length && channels.findIndex(channel => channel.name === hashtag) !== -1) {
return (
<Text
style={[styles.mention, ...style]}
style={[
styles.mention,
{
color: themes[theme].mentionOtherColor,
backgroundColor: themes[theme].mentionOtherBackground
},
...style]}
onPress={handlePress}
>
{hashtag}
{`#${ hashtag }`}
</Text>
);
}

View File

@ -383,9 +383,9 @@ class Markdown extends PureComponent {
m = m.replace(/^\[([\s]]*)\]\(([^)]*)\)\s/, '').trim();
if (preview) {
m = m.replace(/\n+/g, ' ');
m = shortnameToUnicode(m);
m = removeMarkdown(m);
m = m.replace(/\n+/g, ' ');
return (
<Text accessibilityLabel={m} style={[styles.text, { color: themes[theme].bodyText }, ...style]} numberOfLines={numberOfLines} testID={testID}>
{m}

View File

@ -54,13 +54,9 @@ export default StyleSheet.create({
temp: { opacity: 0.3 },
mention: {
fontSize: 16,
color: '#0072FE',
padding: 5,
...sharedStyles.textMedium,
backgroundColor: '#E8F2FF'
},
mentionAll: {
backgroundColor: '#FF5B5A'
letterSpacing: 0.5
},
paragraph: {
marginTop: 0,

View File

@ -79,7 +79,7 @@ const Button = React.memo(({
{
loading
? <ActivityIndicator style={[styles.playPauseButton, styles.audioLoading]} theme={theme} />
: <CustomIcon name={paused ? 'play' : 'pause'} size={36} color={themes[theme].tintColor} />
: <CustomIcon name={paused ? 'play-filled' : 'pause-filled'} size={36} color={themes[theme].tintColor} />
}
</Touchable>
));

View File

@ -26,7 +26,7 @@ const Broadcast = React.memo(({
testID='message-broadcast-reply'
>
<>
<CustomIcon name='back' size={20} style={styles.buttonIcon} color={themes[theme].buttonText} />
<CustomIcon name='arrow-back' size={20} style={styles.buttonIcon} color={themes[theme].buttonText} />
<Text style={[styles.buttonText, { color: themes[theme].buttonText }]}>{I18n.t('Reply')}</Text>
</>
</Touchable>

View File

@ -22,7 +22,7 @@ const CallButton = React.memo(({
hitSlop={BUTTON_HIT_SLOP}
>
<>
<CustomIcon name='video-1' size={20} style={styles.buttonIcon} color={themes[theme].buttonText} />
<CustomIcon name='camera' size={20} style={styles.buttonIcon} color={themes[theme].buttonText} />
<Text style={[styles.buttonText, { color: themes[theme].buttonText }]}>{I18n.t('Click_to_join')}</Text>
</>
</Touchable>

View File

@ -29,7 +29,7 @@ const Discussion = React.memo(({
hitSlop={BUTTON_HIT_SLOP}
>
<>
<CustomIcon name='chat' size={20} style={styles.buttonIcon} color={themes[theme].buttonText} />
<CustomIcon name='discussions' size={20} style={styles.buttonIcon} color={themes[theme].buttonText} />
<Text style={[styles.buttonText, { color: themes[theme].buttonText }]}>{buttonText}</Text>
</>
</Touchable>

View File

@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { View } from 'react-native';
import PropTypes from 'prop-types';
import FastImage from 'react-native-fast-image';
import FastImage from '@rocket.chat/react-native-fast-image';
import equal from 'deep-equal';
import { createImageProgress } from 'react-native-image-progress';
import * as Progress from 'react-native-progress';

View File

@ -23,7 +23,7 @@ const AddReaction = React.memo(({ theme }) => {
hitSlop={BUTTON_HIT_SLOP}
>
<View style={[styles.reactionContainer, { borderColor: themes[theme].borderColor }]}>
<CustomIcon name='add-reaction' size={21} color={themes[theme].tintColor} />
<CustomIcon name='reaction-add' size={21} color={themes[theme].tintColor} />
</View>
</Touchable>
);

View File

@ -3,7 +3,7 @@ import {
View, Text, StyleSheet, Clipboard
} from 'react-native';
import PropTypes from 'prop-types';
import FastImage from 'react-native-fast-image';
import FastImage from '@rocket.chat/react-native-fast-image';
import isEqual from 'lodash/isEqual';
import Touchable from './Touchable';

View File

@ -49,7 +49,7 @@ const Video = React.memo(({
background={Touchable.Ripple(themes[theme].bannerBackground)}
>
<CustomIcon
name='play'
name='play-filled'
size={54}
color={themes[theme].buttonText}
/>

View File

@ -483,6 +483,7 @@ export default {
Tags: 'Tags',
Take_a_photo: 'Take a photo',
Take_a_video: 'Take a video',
Take_it: 'Take it!',
tap_to_change_status: 'tap to change status',
Tap_to_view_servers_list: 'Tap to view servers list',
Terms_of_Service: ' Terms of Service ',
@ -621,5 +622,7 @@ export default {
Passcode_app_locked_title: 'App locked',
Passcode_app_locked_subtitle: 'Try again in {{timeLeft}} seconds',
After_seconds_set_by_admin: 'After {{seconds}} seconds (set by admin)',
Dont_activate: 'Don\'t activate now'
Dont_activate: 'Don\'t activate now',
Queued_chats: 'Queued chats',
Queue_is_empty: 'Queue is empty'
};

View File

@ -108,8 +108,10 @@ export default {
are_typing: 'estão digitando',
Are_you_sure_question_mark: 'Você tem certeza?',
Are_you_sure_you_want_to_leave_the_room: 'Tem certeza de que deseja sair da sala {{room}}?',
Audio: 'Áudio',
Authenticating: 'Autenticando',
Automatic: 'Automático',
Auto_Translate: 'Tradução automática',
Avatar_changed_successfully: 'Avatar alterado com sucesso!',
Avatar_Url: 'Avatar URL',
Away: 'Ausente',
@ -172,6 +174,7 @@ export default {
DELETE: 'EXCLUIR',
deleting_room: 'excluindo sala',
Direct_Messages: 'Mensagens Diretas',
DESKTOP_OPTIONS: 'OPÇÕES DE ÁREA DE TRABALHO',
Directory: 'Diretório',
description: 'descrição',
Description: 'Descrição',
@ -192,6 +195,7 @@ export default {
Email: 'Email',
email: 'e-mail',
Empty_title: 'Título vazio',
Enable_Auto_Translate: 'Ativar a tradução automática',
Enable_notifications: 'Habilitar notificações',
Everyone_can_access_this_channel: 'Todos podem acessar este canal',
Error_uploading: 'Erro subindo',
@ -234,6 +238,7 @@ export default {
Message_HideType_room_unarchived: 'Sala desarquivada',
IP: 'IP',
In_app: 'No app',
In_App_and_Desktop_Alert_info: 'Exibe um banner na parte superior da tela quando o aplicativo é aberto e exibe uma notificação na área de trabalho',
Invisible: 'Invisível',
Invite: 'Convidar',
is_typing: 'está digitando',
@ -299,6 +304,9 @@ export default {
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',
@ -340,6 +348,7 @@ export default {
Profile: 'Perfil',
Public_Channel: 'Canal Público',
Public: 'Público',
Push_Notifications_Alert_Info: 'Essas notificações são entregues a você quando o aplicativo não está aberto',
Quote: 'Citar',
Reactions_are_disabled: 'Reagir está desabilitado',
Reactions_are_enabled: 'Reagir está habilitado',
@ -348,6 +357,8 @@ export default {
Read_External_Permission: 'Permissão de acesso à arquivos',
Read_Only_Channel: 'Canal Somente Leitura',
Read_Only: 'Somente Leitura',
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',
@ -355,6 +366,8 @@ export default {
replies: 'respostas',
reply: 'resposta',
Reply: 'Responder',
Receive_Notification: 'Receber Notificação',
Receive_notifications_from: 'Receber notificação de {{name}}',
Resend: 'Reenviar',
Reset_password: 'Resetar senha',
resetting_password: 'redefinindo senha',
@ -411,10 +424,13 @@ export default {
Share: 'Compartilhar',
Share_Link: 'Share Link',
Show_more: 'Mostrar mais..',
Show_Unread_Counter: 'Mostrar contador não lido',
Show_Unread_Counter_Info: 'O contador não lido é exibido como um emblema à direita do canal, na lista',
Sign_in_your_server: 'Entrar no seu servidor',
Sign_Up: 'Registrar',
Some_field_is_invalid_or_empty: 'Algum campo está inválido ou vazio',
Sorting_by: 'Ordenando por {{key}}',
Sound: 'Som da notificação',
Star_room: 'Favoritar sala',
Star: 'Favorito',
Starred_Messages: 'Mensagens Favoritas',
@ -551,5 +567,7 @@ export default {
Passcode_app_locked_title: 'Aplicativo bloqueado',
Passcode_app_locked_subtitle: 'Tente novamente em {{timeLeft}} segundos',
After_seconds_set_by_admin: 'Após {{seconds}} segundos (Configurado pelo adm)',
Dont_activate: 'Não ativar agora'
Dont_activate: 'Não ativar agora',
Queued_chats: 'Bate-papos na fila',
Queue_is_empty: 'A fila está vazia'
};

View File

@ -50,6 +50,13 @@ const parseDeepLinking = (url) => {
return parseQuery(url);
}
}
const call = /^(https:\/\/)?jitsi.rocket.chat\//;
if (url.match(call)) {
url = url.replace(call, '').trim();
if (url) {
return { path: url, isCall: true };
}
}
}
return null;
};

View File

@ -25,4 +25,6 @@ export default class Server extends Model {
@field('auto_lock_time') autoLockTime;
@field('biometry') biometry;
@field('unique_id') uniqueID;
}

View File

@ -40,6 +40,8 @@ export default class Subscription extends Model {
@field('user_mentions') userMentions;
@field('group_mentions') groupMentions;
@date('room_updated_at') roomUpdatedAt;
@field('ro') ro;

View File

@ -118,6 +118,17 @@ export default schemaMigrations({
]
})
]
},
{
toVersion: 9,
steps: [
addColumns({
table: 'subscriptions',
columns: [
{ name: 'group_mentions', type: 'number', isOptional: true }
]
})
]
}
]
});

View File

@ -26,6 +26,17 @@ export default schemaMigrations({
]
})
]
},
{
toVersion: 5,
steps: [
addColumns({
table: 'servers',
columns: [
{ name: 'unique_id', type: 'string', isOptional: true }
]
})
]
}
]
});

View File

@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({
version: 8,
version: 9,
tables: [
tableSchema({
name: 'subscriptions',
@ -19,6 +19,7 @@ export default appSchema({
{ name: 'roles', type: 'string', isOptional: true },
{ name: 'unread', type: 'number' },
{ name: 'user_mentions', type: 'number' },
{ name: 'group_mentions', type: 'number' },
{ name: 'room_updated_at', type: 'number' },
{ name: 'ro', type: 'boolean' },
{ name: 'last_open', type: 'number', isOptional: true },

View File

@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({
version: 4,
version: 5,
tables: [
tableSchema({
name: 'users',
@ -28,7 +28,8 @@ export default appSchema({
{ name: 'last_local_authenticated_session', type: 'number', isOptional: true },
{ name: 'auto_lock', type: 'boolean', isOptional: true },
{ name: 'auto_lock_time', type: 'number', isOptional: true },
{ name: 'biometry', type: 'boolean', isOptional: true }
{ name: 'biometry', type: 'boolean', isOptional: true },
{ name: 'unique_id', type: 'string', isOptional: true }
]
})
]

View File

@ -1,41 +1,41 @@
import reduxStore from '../createStore';
import Navigation from '../Navigation';
import { logEvent, events } from '../../utils/log';
async function jitsiURL({ rid }) {
const { settings } = reduxStore.getState();
const { Jitsi_Enabled } = settings;
const jitsiBaseUrl = ({
Jitsi_Enabled, Jitsi_SSL, Jitsi_Domain, Jitsi_URL_Room_Prefix, uniqueID
}) => {
if (!Jitsi_Enabled) {
return '';
}
const uniqueIdentifier = uniqueID || 'undefined';
const domain = Jitsi_Domain;
const {
Jitsi_Domain, Jitsi_URL_Room_Prefix, Jitsi_SSL, Jitsi_Enabled_TokenAuth, uniqueID
} = settings;
const domain = `${ Jitsi_Domain }/`;
const prefix = Jitsi_URL_Room_Prefix;
const uniqueIdentifier = uniqueID || 'undefined';
const protocol = Jitsi_SSL ? 'https://' : 'http://';
const urlProtocol = Jitsi_SSL ? 'https://' : 'http://';
const urlDomain = `${ domain }/`;
return `${ urlProtocol }${ urlDomain }${ prefix }${ uniqueIdentifier }`;
};
async function callJitsi(rid, onlyAudio = false) {
let accessToken;
let queryString = '';
const { settings } = reduxStore.getState();
const { Jitsi_Enabled_TokenAuth } = settings;
if (Jitsi_Enabled_TokenAuth) {
try {
accessToken = await this.methodCallWrapper('jitsi:generateAccessToken', rid);
} catch (e) {
// do nothing
const accessToken = await this.methodCallWrapper('jitsi:generateAccessToken', rid);
queryString = `?jwt=${ accessToken }`;
} catch {
logEvent(events.RA_JITSI_F);
}
}
if (accessToken) {
queryString = `?jwt=${ accessToken }`;
}
return `${ protocol }${ domain }${ prefix }${ uniqueIdentifier }${ rid }${ queryString }`;
}
Navigation.navigate('JitsiMeetView', { url: `${ jitsiBaseUrl(settings) }${ rid }${ queryString }`, onlyAudio, rid });
async function callJitsi(rid, onlyAudio = false) {
logEvent(onlyAudio ? events.RA_JITSI_AUDIO : events.RA_JITSI_VIDEO);
const url = await jitsiURL.call(this, { rid });
Navigation.navigate('JitsiMeetView', { url, onlyAudio, rid });
}
export default callJitsi;

View File

@ -1,4 +1,5 @@
import database from '../database';
import store from '../createStore';
const restTypes = {
channel: 'channels', direct: 'im', group: 'groups'
@ -53,11 +54,17 @@ async function open({ type, rid, name }) {
}
}
export default async function canOpenRoom({ rid, path }) {
export default async function canOpenRoom({ rid, path, isCall }) {
try {
const db = database.active;
const subsCollection = db.collections.get('subscriptions');
const [type, name] = path.split('/');
if (isCall && !rid) {
// Extract rid from a Jitsi URL
// Eg.: [Jitsi_URL_Room_Prefix][uniqueID][rid][?jwt]
const { Jitsi_URL_Room_Prefix, uniqueID } = store.getState().settings;
rid = path.replace(`${ Jitsi_URL_Room_Prefix }${ uniqueID }`, '').replace(/\?(.*)/g, '');
}
if (rid) {
try {
@ -75,8 +82,10 @@ export default async function canOpenRoom({ rid, path }) {
}
}
const [type, name] = path.split('/');
try {
return await open.call(this, { type, rid, name });
const result = await open.call(this, { type, rid, name });
return result;
} catch (e) {
return false;
}

View File

@ -11,7 +11,7 @@ import protectedFunction from './helpers/protectedFunction';
import fetch from '../../utils/fetch';
import { DEFAULT_AUTO_LOCK } from '../../constants/localAuthentication';
const serverInfoKeys = ['Site_Name', 'UI_Use_Real_Name', 'FileUpload_MediaTypeWhiteList', 'FileUpload_MaxFileSize', 'Force_Screen_Lock', 'Force_Screen_Lock_After'];
const serverInfoKeys = ['Site_Name', 'UI_Use_Real_Name', 'FileUpload_MediaTypeWhiteList', 'FileUpload_MaxFileSize', 'Force_Screen_Lock', 'Force_Screen_Lock_After', 'uniqueID'];
// these settings are used only on onboarding process
const loginSettings = [
@ -68,6 +68,9 @@ const serverInfoUpdate = async(serverInfo, iconSetting) => {
return { ...allSettings, autoLockTime: setting.valueAsNumber };
}
}
if (setting._id === 'uniqueID') {
return { ...allSettings, uniqueID: setting.valueAsString };
}
return allSettings;
}, {});

View File

@ -0,0 +1,95 @@
import log from '../../../utils/log';
import store from '../../createStore';
import RocketChat from '../../rocketchat';
import {
inquiryRequest,
inquiryQueueAdd,
inquiryQueueUpdate,
inquiryQueueRemove
} from '../../../actions/inquiry';
const removeListener = listener => listener.stop();
let connectedListener;
let disconnectedListener;
let queueListener;
const streamTopic = 'stream-livechat-inquiry-queue-observer';
export default function subscribeInquiry() {
const handleConnection = () => {
store.dispatch(inquiryRequest());
};
const handleQueueMessageReceived = (ddpMessage) => {
const [{ type, ...sub }] = ddpMessage.fields.args;
// added can be ignored, since it is handled by 'changed' event
if (/added/.test(type)) {
return;
}
// if the sub isn't on the queue anymore
if (sub.status !== 'queued') {
// remove it from the queue
store.dispatch(inquiryQueueRemove(sub._id));
return;
}
const { queued } = store.getState().inquiry;
// check if this sub is on the current queue
const idx = queued.findIndex(item => item._id === sub._id);
if (idx >= 0) {
// if it is on the queue let's update
store.dispatch(inquiryQueueUpdate(sub));
} else {
// if it is not on the queue let's add
store.dispatch(inquiryQueueAdd(sub));
}
};
const stop = () => {
if (connectedListener) {
connectedListener.then(removeListener);
connectedListener = false;
}
if (disconnectedListener) {
disconnectedListener.then(removeListener);
disconnectedListener = false;
}
if (queueListener) {
queueListener.then(removeListener);
queueListener = false;
}
};
connectedListener = this.sdk.onStreamData('connected', handleConnection);
disconnectedListener = this.sdk.onStreamData('close', handleConnection);
queueListener = this.sdk.onStreamData(streamTopic, handleQueueMessageReceived);
try {
const { user } = store.getState().login;
RocketChat.getAgentDepartments(user.id).then((result) => {
if (result.success) {
const { departments } = result;
if (!departments.length || RocketChat.hasRole('livechat-manager')) {
this.sdk.subscribe(streamTopic, 'public').catch(e => console.log(e));
}
const departmentIds = departments.map(({ departmentId }) => departmentId);
departmentIds.forEach((departmentId) => {
// subscribe to all departments of the agent
this.sdk.subscribe(streamTopic, `department/${ departmentId }`).catch(e => console.log(e));
});
}
});
return {
stop: () => stop()
};
} catch (e) {
log(e);
return Promise.reject();
}
}

View File

@ -20,6 +20,7 @@ import {
} from '../actions/share';
import subscribeRooms from './methods/subscriptions/rooms';
import subscribeInquiry from './methods/subscriptions/inquiry';
import getUsersPresence, { getUserPresence, subscribeUsersPresence } from './methods/getUsersPresence';
import protectedFunction from './methods/helpers/protectedFunction';
@ -72,6 +73,15 @@ const RocketChat = {
}
}
},
async subscribeInquiry() {
if (!this.inquirySub) {
try {
this.inquirySub = await subscribeInquiry.call(this);
} catch (e) {
log(e);
}
}
},
canOpenRoom,
createChannel({
name, users, type, readOnly, broadcast
@ -203,6 +213,11 @@ const RocketChat = {
this.roomsSub = null;
}
if (this.inquirySub) {
this.inquirySub.stop();
this.inquirySub = null;
}
if (this.sdk) {
this.sdk.disconnect();
this.sdk = null;
@ -680,7 +695,7 @@ const RocketChat = {
c: 'channel',
d: 'direct'
}[room.t];
return `${ server }/${ roomType }/${ room.name }?msg=${ message.id }`;
return `${ server }/${ roomType }/${ this.isGroupChat(room) ? room.rid : room.name }?msg=${ message.id }`;
},
getPermalinkChannel(channel) {
const { server } = reduxStore.getState().server;
@ -816,7 +831,7 @@ const RocketChat = {
},
getAgentDepartments(uid) {
// RC 2.4.0
return this.sdk.get(`livechat/agents/${ uid }/departments`);
return this.sdk.get(`livechat/agents/${ uid }/departments?enabledDepartmentsOnly=true`);
},
getCustomFields() {
// RC 2.2.0
@ -826,6 +841,16 @@ const RocketChat = {
// RC 0.26.0
return this.methodCallWrapper('livechat:changeLivechatStatus');
},
getInquiriesQueued() {
// RC 2.4.0
return this.sdk.get('livechat/inquiries.queued');
},
takeInquiry(inquiryId) {
// this inquiry is added to the db by the subscriptions stream
// and will be removed by the queue stream
// RC 2.4.0
return this.methodCallWrapper('livechat:takeInquiry', inquiryId);
},
getUidDirectMessage(room) {
const { id: userId } = reduxStore.getState().login.user;
@ -845,6 +870,12 @@ const RocketChat = {
return other && other.length ? other[0] : me;
},
isRead(item) {
let isUnread = item.archived !== true && item.open === true; // item is not archived and not opened
isUnread = isUnread && (item.unread > 0 || item.alert === true); // either its unread count > 0 or its alert
return !isUnread;
},
isGroupChat(room) {
return (room.uids && room.uids.length > 2) || (room.usernames && room.usernames.length > 2);
},
@ -957,6 +988,14 @@ const RocketChat = {
// RC 0.47.0
return this.sdk.get('chat.getMessage', { msgId });
},
hasRole(role) {
const shareUser = reduxStore.getState().share.user;
const loginUser = reduxStore.getState().login.user;
// get user roles on the server from redux
const userRoles = (shareUser?.roles || loginUser?.roles) || [];
return userRoles.indexOf(r => r === role) > -1;
},
async hasPermission(permissions, rid) {
const db = database.active;
const subsCollection = db.collections.get('subscriptions');

File diff suppressed because one or more lines are too long

View File

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

View File

@ -6,7 +6,7 @@ export const ImageComponent = (type) => {
const { Image } = require('react-native');
Component = Image;
} else {
const FastImage = require('react-native-fast-image').default;
const FastImage = require('@rocket.chat/react-native-fast-image').default;
Component = FastImage;
}
return Component;

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { KeyboardAwareScrollView } from '@codler/react-native-keyboard-aware-scroll-view';
import scrollPersistTaps from '../utils/scrollPersistTaps';
export default class KeyboardView extends React.PureComponent {

View File

@ -107,7 +107,7 @@ export const RightActions = React.memo(({
>
<RectButton style={[styles.actionButton, { backgroundColor: themes[theme].hideBackground }]} onPress={onHidePress}>
<>
<CustomIcon size={20} name='eye-off' color={themes[theme].buttonText} />
<CustomIcon size={20} name='unread-on-top-disabled' color={themes[theme].buttonText} />
<Text style={[styles.actionText, { color: themes[theme].buttonText }]}>{I18n.t('Hide')}</Text>
</>
</RectButton>

View File

@ -0,0 +1,190 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';
import styles from './styles';
import Wrapper from './Wrapper';
import UnreadBadge from '../UnreadBadge';
import TypeIcon from './TypeIcon';
import LastMessage from './LastMessage';
import Title from './Title';
import UpdatedAt from './UpdatedAt';
import Touchable from './Touchable';
const RoomItem = ({
rid,
type,
prid,
name,
avatar,
width,
avatarSize,
baseUrl,
userId,
username,
token,
showLastMessage,
status,
useRealName,
theme,
isFocused,
isGroupChat,
isRead,
date,
accessibilityLabel,
favorite,
lastMessage,
alert,
hideUnreadStatus,
unread,
userMentions,
groupMentions,
roomUpdatedAt,
testID,
swipeEnabled,
onPress,
toggleFav,
toggleRead,
hideChannel
}) => (
<Touchable
onPress={onPress}
width={width}
favorite={favorite}
toggleFav={toggleFav}
isRead={isRead}
rid={rid}
toggleRead={toggleRead}
hideChannel={hideChannel}
testID={testID}
type={type}
theme={theme}
isFocused={isFocused}
swipeEnabled={swipeEnabled}
>
<Wrapper
accessibilityLabel={accessibilityLabel}
avatar={avatar}
avatarSize={avatarSize}
type={type}
baseUrl={baseUrl}
userId={userId}
token={token}
theme={theme}
>
{showLastMessage
? (
<>
<View style={styles.titleContainer}>
<TypeIcon
type={type}
prid={prid}
status={status}
isGroupChat={isGroupChat}
theme={theme}
/>
<Title
name={name}
theme={theme}
hideUnreadStatus={hideUnreadStatus}
alert={alert}
/>
<UpdatedAt
roomUpdatedAt={roomUpdatedAt}
date={date}
theme={theme}
hideUnreadStatus={hideUnreadStatus}
alert={alert}
/>
</View>
<View style={styles.row}>
<LastMessage
lastMessage={lastMessage}
type={type}
showLastMessage={showLastMessage}
username={username}
alert={alert && !hideUnreadStatus}
useRealName={useRealName}
theme={theme}
/>
<UnreadBadge
unread={unread}
userMentions={userMentions}
groupMentions={groupMentions}
theme={theme}
/>
</View>
</>
)
: (
<View style={[styles.titleContainer, styles.flex]}>
<TypeIcon
type={type}
prid={prid}
status={status}
isGroupChat={isGroupChat}
theme={theme}
/>
<Title
name={name}
theme={theme}
hideUnreadStatus={hideUnreadStatus}
alert={alert}
/>
<UnreadBadge
unread={unread}
userMentions={userMentions}
groupMentions={groupMentions}
theme={theme}
/>
</View>
)
}
</Wrapper>
</Touchable>
);
RoomItem.propTypes = {
rid: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
prid: PropTypes.string,
name: PropTypes.string.isRequired,
avatar: PropTypes.string.isRequired,
baseUrl: PropTypes.string.isRequired,
showLastMessage: PropTypes.bool,
userId: PropTypes.string,
username: PropTypes.string,
token: PropTypes.string,
avatarSize: PropTypes.number,
testID: PropTypes.string,
width: PropTypes.number,
status: PropTypes.string,
useRealName: PropTypes.bool,
theme: PropTypes.string,
isFocused: PropTypes.bool,
isGroupChat: PropTypes.bool,
isRead: PropTypes.bool,
date: PropTypes.string,
accessibilityLabel: PropTypes.string,
lastMessage: PropTypes.object,
favorite: PropTypes.bool,
alert: PropTypes.bool,
hideUnreadStatus: PropTypes.bool,
unread: PropTypes.number,
userMentions: PropTypes.number,
groupMentions: PropTypes.number,
roomUpdatedAt: PropTypes.instanceOf(Date),
swipeEnabled: PropTypes.bool,
toggleFav: PropTypes.func,
toggleRead: PropTypes.func,
onPress: PropTypes.func,
hideChannel: PropTypes.func
};
RoomItem.defaultProps = {
avatarSize: 48,
status: 'offline',
swipeEnabled: true
};
export default RoomItem;

View File

@ -0,0 +1,31 @@
import React from 'react';
import { Text } from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
const Title = React.memo(({
name, theme, hideUnreadStatus, alert
}) => (
<Text
style={[
styles.title,
alert && !hideUnreadStatus && styles.alert,
{ color: themes[theme].titleText }
]}
ellipsizeMode='tail'
numberOfLines={1}
>
{name}
</Text>
));
Title.propTypes = {
name: PropTypes.string,
theme: PropTypes.string,
hideUnreadStatus: PropTypes.bool,
alert: PropTypes.bool
};
export default Title;

View File

@ -26,7 +26,8 @@ class Touchable extends React.Component {
hideChannel: PropTypes.func,
children: PropTypes.element,
theme: PropTypes.string,
isFocused: PropTypes.bool
isFocused: PropTypes.bool,
swipeEnabled: PropTypes.bool
}
constructor(props) {
@ -168,7 +169,7 @@ class Touchable extends React.Component {
render() {
const {
testID, isRead, width, favorite, children, theme, isFocused
testID, isRead, width, favorite, children, theme, isFocused, swipeEnabled
} = this.props;
return (
@ -177,6 +178,7 @@ class Touchable extends React.Component {
minDeltaX={20}
onGestureEvent={this._onGestureEvent}
onHandlerStateChange={this._onHandlerStateChange}
enabled={swipeEnabled}
>
<Animated.View>
<LeftActions

View File

@ -1,44 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View, Text } from 'react-native';
import styles from './styles';
import { themes } from '../../constants/colors';
const UnreadBadge = React.memo(({
theme, unread, userMentions, type
}) => {
if (!unread || unread <= 0) {
return;
}
if (unread >= 1000) {
unread = '999+';
}
const mentioned = userMentions > 0 && type !== 'd';
return (
<View
style={[
styles.unreadNumberContainer,
{ backgroundColor: mentioned ? themes[theme].tintColor : themes[theme].borderColor }
]}
>
<Text
style={[
styles.unreadText,
{ color: mentioned ? themes[theme].buttonText : themes[theme].bodyText }
]}
>{ unread }
</Text>
</View>
);
});
UnreadBadge.propTypes = {
theme: PropTypes.string,
unread: PropTypes.number,
userMentions: PropTypes.number,
type: PropTypes.string
};
export default UnreadBadge;

View File

@ -0,0 +1,49 @@
import React from 'react';
import { Text } from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
import { capitalize } from '../../utils/room';
const UpdatedAt = React.memo(({
roomUpdatedAt, date, theme, hideUnreadStatus, alert
}) => {
if (!roomUpdatedAt) {
return null;
}
return (
<Text
style={[
styles.date,
{
color:
themes[theme]
.auxiliaryText
},
alert && !hideUnreadStatus && [
styles.updateAlert,
{
color:
themes[theme]
.tintColor
}
]
]}
ellipsizeMode='tail'
numberOfLines={1}
>
{capitalize(date)}
</Text>
);
});
UpdatedAt.propTypes = {
roomUpdatedAt: PropTypes.instanceOf(Date),
date: PropTypes.string,
theme: PropTypes.string,
hideUnreadStatus: PropTypes.bool,
alert: PropTypes.bool
};
export default UpdatedAt;

View File

@ -0,0 +1,58 @@
import React from 'react';
import { View } from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
import Avatar from '../../containers/Avatar';
const RoomItemInner = ({
accessibilityLabel,
avatar,
avatarSize,
type,
baseUrl,
userId,
token,
theme,
children
}) => (
<View
style={styles.container}
accessibilityLabel={accessibilityLabel}
>
<Avatar
text={avatar}
size={avatarSize}
type={type}
baseUrl={baseUrl}
style={styles.avatar}
userId={userId}
token={token}
/>
<View
style={[
styles.centerContainer,
{
borderColor: themes[theme].separatorColor
}
]}
>
{children}
</View>
</View>
);
RoomItemInner.propTypes = {
accessibilityLabel: PropTypes.string,
avatar: PropTypes.string,
avatarSize: PropTypes.number,
type: PropTypes.string,
baseUrl: PropTypes.string,
userId: PropTypes.string,
token: PropTypes.string,
theme: PropTypes.string,
children: PropTypes.element
};
export default RoomItemInner;

View File

@ -1,97 +1,89 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
import Avatar from '../../containers/Avatar';
import I18n from '../../i18n';
import styles, { ROW_HEIGHT } from './styles';
import UnreadBadge from './UnreadBadge';
import TypeIcon from './TypeIcon';
import LastMessage from './LastMessage';
import { capitalize, formatDate } from '../../utils/room';
import Touchable from './Touchable';
import { themes } from '../../constants/colors';
import { ROW_HEIGHT } from './styles';
import { formatDate } from '../../utils/room';
import RoomItem from './RoomItem';
export { ROW_HEIGHT };
const attrs = [
'name',
'unread',
'userMentions',
'showLastMessage',
'useRealName',
'alert',
'type',
'width',
'isRead',
'favorite',
'status',
'connected',
'theme',
'isFocused'
'isFocused',
'forceUpdate',
'showLastMessage'
];
const arePropsEqual = (oldProps, newProps) => {
const { _updatedAt: _updatedAtOld } = oldProps;
const { _updatedAt: _updatedAtNew } = newProps;
if (_updatedAtOld && _updatedAtNew && _updatedAtOld.toISOString() !== _updatedAtNew.toISOString()) {
return false;
}
return attrs.every(key => oldProps[key] === newProps[key]);
};
const arePropsEqual = (oldProps, newProps) => attrs.every(key => oldProps[key] === newProps[key]);
const RoomItem = React.memo(({
const RoomItemContainer = React.memo(({
item,
onPress,
width,
favorite,
toggleFav,
isRead,
rid,
toggleRead,
hideChannel,
testID,
unread,
userMentions,
name,
_updatedAt,
alert,
type,
avatarSize,
baseUrl,
userId,
username,
token,
id,
prid,
showLastMessage,
hideUnreadStatus,
lastMessage,
status,
avatar,
useRealName,
getUserPresence,
isGroupChat,
connected,
theme,
isFocused
isFocused,
getRoomTitle,
getRoomAvatar,
getIsGroupChat,
getIsRead,
swipeEnabled
}) => {
const [, setForceUpdate] = useState(1);
useEffect(() => {
if (connected && type === 'd' && id) {
if (connected && item.t === 'd' && id) {
getUserPresence(id);
}
}, [connected]);
const date = lastMessage && formatDate(lastMessage.ts);
useEffect(() => {
if (item?.observe) {
const observable = item.observe();
const subscription = observable?.subscribe?.(() => {
setForceUpdate(prevForceUpdate => prevForceUpdate + 1);
});
return () => {
subscription?.unsubscribe?.();
};
}
}, []);
const name = getRoomTitle(item);
const avatar = getRoomAvatar(item);
const isGroupChat = getIsGroupChat(item);
const isRead = getIsRead(item);
const _onPress = () => onPress(item);
const date = item.lastMessage?.ts && formatDate(item.lastMessage.ts);
let accessibilityLabel = name;
if (unread === 1) {
accessibilityLabel += `, ${ unread } ${ I18n.t('alert') }`;
} else if (unread > 1) {
accessibilityLabel += `, ${ unread } ${ I18n.t('alerts') }`;
if (item.unread === 1) {
accessibilityLabel += `, ${ item.unread } ${ I18n.t('alert') }`;
} else if (item.unread > 1) {
accessibilityLabel += `, ${ item.unread } ${ I18n.t('alerts') }`;
}
if (userMentions > 0) {
if (item.userMentions > 0) {
accessibilityLabel += `, ${ I18n.t('you_were_mentioned') }`;
}
@ -100,120 +92,50 @@ const RoomItem = React.memo(({
}
return (
<Touchable
onPress={onPress}
width={width}
favorite={favorite}
toggleFav={toggleFav}
<RoomItem
name={name}
avatar={avatar}
isGroupChat={isGroupChat}
isRead={isRead}
rid={rid}
onPress={_onPress}
date={date}
accessibilityLabel={accessibilityLabel}
userMentions={item.userMentions}
width={width}
favorite={item.f}
toggleFav={toggleFav}
rid={item.rid}
toggleRead={toggleRead}
hideChannel={hideChannel}
testID={testID}
type={type}
type={item.t}
theme={theme}
isFocused={isFocused}
>
<View
style={styles.container}
accessibilityLabel={accessibilityLabel}
>
<Avatar
text={avatar}
size={avatarSize}
type={type}
baseUrl={baseUrl}
style={styles.avatar}
userId={userId}
token={token}
/>
<View
style={[
styles.centerContainer,
{
borderColor: themes[theme].separatorColor
}
]}
>
<View style={styles.titleContainer}>
<TypeIcon
type={type}
prid={prid}
status={status}
isGroupChat={isGroupChat}
theme={theme}
/>
<Text
style={[
styles.title,
alert && !hideUnreadStatus && styles.alert,
{ color: themes[theme].titleText }
]}
ellipsizeMode='tail'
numberOfLines={1}
>
{name}
</Text>
{_updatedAt ? (
<Text
style={[
styles.date,
{
color:
themes[theme]
.auxiliaryText
},
alert && !hideUnreadStatus && [
styles.updateAlert,
{
color:
themes[theme]
.tintColor
}
]
]}
ellipsizeMode='tail'
numberOfLines={1}
>
{capitalize(date)}
</Text>
) : null}
</View>
<View style={styles.row}>
<LastMessage
lastMessage={lastMessage}
type={type}
showLastMessage={showLastMessage}
username={username}
alert={alert && !hideUnreadStatus}
useRealName={useRealName}
theme={theme}
/>
<UnreadBadge
unread={unread}
userMentions={userMentions}
type={type}
theme={theme}
/>
</View>
</View>
</View>
</Touchable>
size={avatarSize}
baseUrl={baseUrl}
userId={userId}
token={token}
prid={item.prid}
status={status}
hideUnreadStatus={item.hideUnreadStatus}
alert={item.alert}
roomUpdatedAt={item.roomUpdatedAt}
lastMessage={item.lastMessage}
showLastMessage={showLastMessage}
username={username}
useRealName={useRealName}
unread={item.unread}
groupMentions={item.groupMentions}
swipeEnabled={swipeEnabled}
/>
);
}, arePropsEqual);
RoomItem.propTypes = {
type: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
RoomItemContainer.propTypes = {
item: PropTypes.object.isRequired,
baseUrl: PropTypes.string.isRequired,
showLastMessage: PropTypes.bool,
_updatedAt: PropTypes.string,
lastMessage: PropTypes.object,
alert: PropTypes.bool,
unread: PropTypes.number,
userMentions: PropTypes.number,
id: PropTypes.string,
prid: PropTypes.string,
onPress: PropTypes.func,
userId: PropTypes.string,
username: PropTypes.string,
@ -221,27 +143,31 @@ RoomItem.propTypes = {
avatarSize: PropTypes.number,
testID: PropTypes.string,
width: PropTypes.number,
favorite: PropTypes.bool,
isRead: PropTypes.bool,
rid: PropTypes.string,
status: PropTypes.string,
toggleFav: PropTypes.func,
toggleRead: PropTypes.func,
hideChannel: PropTypes.func,
avatar: PropTypes.bool,
hideUnreadStatus: PropTypes.bool,
useRealName: PropTypes.bool,
getUserPresence: PropTypes.func,
connected: PropTypes.bool,
isGroupChat: PropTypes.bool,
theme: PropTypes.string,
isFocused: PropTypes.bool
isFocused: PropTypes.bool,
getRoomTitle: PropTypes.func,
getRoomAvatar: PropTypes.func,
getIsGroupChat: PropTypes.func,
getIsRead: PropTypes.func,
swipeEnabled: PropTypes.bool
};
RoomItem.defaultProps = {
RoomItemContainer.defaultProps = {
avatarSize: 48,
status: 'offline',
getUserPresence: () => {}
getUserPresence: () => {},
getRoomTitle: () => 'title',
getRoomAvatar: () => '',
getIsGroupChat: () => false,
getIsRead: () => false,
swipeEnabled: true
};
const mapStateToProps = (state, ownProps) => {
@ -260,4 +186,4 @@ const mapStateToProps = (state, ownProps) => {
};
};
export default connect(mapStateToProps)(RoomItem);
export default connect(mapStateToProps)(RoomItemContainer);

View File

@ -8,6 +8,9 @@ export const SMALL_SWIPE = ACTION_WIDTH / 2;
export const LONG_SWIPE = ACTION_WIDTH * 3;
export default StyleSheet.create({
flex: {
flex: 1
},
container: {
flexDirection: 'row',
alignItems: 'center',
@ -48,23 +51,6 @@ export default StyleSheet.create({
updateAlert: {
...sharedStyles.textSemibold
},
unreadNumberContainer: {
minWidth: 21,
height: 21,
paddingVertical: 3,
paddingHorizontal: 5,
borderRadius: 10.5,
alignItems: 'center',
justifyContent: 'center',
marginLeft: 10
},
unreadText: {
overflow: 'hidden',
fontSize: 13,
...sharedStyles.textMedium,
letterSpacing: 0.56,
textAlign: 'center'
},
status: {
marginLeft: 4,
marginRight: 7,

View File

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View, Text } from 'react-native';
import FastImage from 'react-native-fast-image';
import FastImage from '@rocket.chat/react-native-fast-image';
import Touch from '../../utils/touch';
import Check from '../../containers/Check';

View File

@ -0,0 +1,73 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View, Text, StyleSheet } from 'react-native';
import sharedStyles from '../views/Styles';
import { themes } from '../constants/colors';
const styles = StyleSheet.create({
unreadNumberContainer: {
minWidth: 21,
height: 21,
paddingVertical: 3,
paddingHorizontal: 5,
borderRadius: 10.5,
alignItems: 'center',
justifyContent: 'center',
marginLeft: 10
},
unreadText: {
overflow: 'hidden',
fontSize: 13,
...sharedStyles.textMedium,
letterSpacing: 0.56,
textAlign: 'center'
}
});
const UnreadBadge = React.memo(({
theme, unread, userMentions, groupMentions, style
}) => {
if (!unread || unread <= 0) {
return;
}
if (unread >= 1000) {
unread = '999+';
}
let backgroundColor = themes[theme].unreadBackground;
const color = themes[theme].buttonText;
if (userMentions > 0) {
backgroundColor = themes[theme].mentionMeColor;
} else if (groupMentions > 0) {
backgroundColor = themes[theme].mentionGroupColor;
}
return (
<View
style={[
styles.unreadNumberContainer,
{ backgroundColor },
style
]}
>
<Text
style={[
styles.unreadText,
{ color }
]}
>{unread}
</Text>
</View>
);
});
UnreadBadge.propTypes = {
theme: PropTypes.string,
unread: PropTypes.number,
userMentions: PropTypes.number,
groupMentions: PropTypes.number,
style: PropTypes.object
};
export default UnreadBadge;

View File

@ -1,13 +1,14 @@
import React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import {
Text, View, StyleSheet, Pressable
} from 'react-native';
import PropTypes from 'prop-types';
import Avatar from '../containers/Avatar';
import { CustomIcon } from '../lib/Icons';
import sharedStyles from '../views/Styles';
import { themes } from '../constants/colors';
import Touch from '../utils/touch';
import LongPress from '../utils/longPress';
import { isIOS } from '../utils/deviceInfo';
const styles = StyleSheet.create({
button: {
@ -43,23 +44,28 @@ const styles = StyleSheet.create({
const UserItem = ({
name, username, onPress, testID, onLongPress, style, icon, baseUrl, user, theme
}) => (
<LongPress onLongPress={onLongPress}>
<Touch
onPress={onPress}
style={{ backgroundColor: themes[theme].backgroundColor }}
testID={testID}
theme={theme}
>
<View style={[styles.container, styles.button, style]}>
<Avatar text={username} size={30} type='d' style={styles.avatar} baseUrl={baseUrl} userId={user.id} token={user.token} />
<View style={styles.textContainer}>
<Text style={[styles.name, { color: themes[theme].titleText }]} numberOfLines={1}>{name}</Text>
<Text style={[styles.username, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>@{username}</Text>
</View>
{icon ? <CustomIcon name={icon} size={22} style={[styles.icon, { color: themes[theme].actionTintColor }]} /> : null}
<Pressable
onPress={onPress}
onLongPress={onLongPress}
testID={testID}
android_ripple={{
color: themes[theme].bannerBackground
}}
style={({ pressed }) => ({
backgroundColor: isIOS && pressed
? themes[theme].bannerBackground
: 'transparent'
})}
>
<View style={[styles.container, styles.button, style]}>
<Avatar text={username} size={30} type='d' style={styles.avatar} baseUrl={baseUrl} userId={user.id} token={user.token} />
<View style={styles.textContainer}>
<Text style={[styles.name, { color: themes[theme].titleText }]} numberOfLines={1}>{name}</Text>
<Text style={[styles.username, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>@{username}</Text>
</View>
</Touch>
</LongPress>
{icon ? <CustomIcon name={icon} size={22} style={[styles.icon, { color: themes[theme].actionTintColor }]} /> : null}
</View>
</Pressable>
);
UserItem.propTypes = {

View File

@ -16,6 +16,7 @@ import activeUsers from './activeUsers';
import usersTyping from './usersTyping';
import inviteLinks from './inviteLinks';
import createDiscussion from './createDiscussion';
import inquiry from './inquiry';
export default combineReducers({
settings,
@ -34,5 +35,6 @@ export default combineReducers({
activeUsers,
usersTyping,
inviteLinks,
createDiscussion
createDiscussion,
inquiry
});

51
app/reducers/inquiry.js Normal file
View File

@ -0,0 +1,51 @@
import { INQUIRY } from '../actions/actionsTypes';
const initialState = {
enabled: false,
queued: [],
error: {}
};
export default function inquiry(state = initialState, action) {
switch (action.type) {
case INQUIRY.SUCCESS:
return {
...state,
queued: action.inquiries
};
case INQUIRY.FAILURE:
return {
...state,
error: action.error
};
case INQUIRY.SET_ENABLED:
return {
...state,
enabled: action.enabled
};
case INQUIRY.QUEUE_ADD:
return {
...state,
queued: [...state.queued, action.inquiry]
};
case INQUIRY.QUEUE_UPDATE:
return {
...state,
queued: state.queued.map((item) => {
if (item._id === action.inquiry._id) {
return action.inquiry;
}
return item;
})
};
case INQUIRY.QUEUE_REMOVE:
return {
...state,
queued: state.queued.filter(({ _id }) => _id !== action.inquiryId)
};
case INQUIRY.RESET:
return initialState;
default:
return state;
}
}

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