Merge 4.15.0 into single-server (#2986)
* [FIX] RoomItem using deprecated animated event signature (#2771) * [FIX] Server autocomplete text breaking line (#2774) * [FIX] ServerDropdown flashing bigger server icon (#2775) * [FIX] ServerDropdown flashing bigger server icon * Remove unused logo and update image path where needed * Minor tweak Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Rooms list not being updated on some cases (#2765) * Request subscriptions on RoomsListView.constructor * Removes opened rooms from last message persisting * Change server reducer * Prevent undefined ids causing query error * [FIX] Share Extension hitting memory limit on iOS (#2788) * [FIX] Disallow swipe to dismiss on share extension * Limit query to 20 and clean up props * Remove rn-extension-share branch pointer * Test new branch * Remove branch * [IMPROVEMENT] Threads layout tweaks (#2686) * improvement: Thread Details * fix: re-render Thread Messages Item * fix: update snapshots * improve: thread details component * fix: cast replies length * improvement: format date of threads * improvement: thread details styles * fix: wrap text * tests: update snapshot * improvement: use same date format for all dates * Icon size 24 * Remove date * Remove prop drill * Badge position * Badge container tweak * Fix inline style * Move ThreadDetails to containers * Update stories * Fix lint * Remove wrong prop Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] Remove some migrations (#2792) * Remove force rooms refresh * Remove MMKV migration * Bump version to 4.14.0 (#2797) * [FIX] Messagebox tracking lost on pop gesture navigation (#2799) * Use setTimeout instead of InteractionManager * Update tracking lib * [FIX] Back button closing activity when on root stack screen (#2804) * Make hardware back button to behave as home button on root screens * Remove unnecessary code * Remove handleBackPress from OnboardingView * Fix lint * [i18n] Add missing German strings (#2715) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [NEW] Encrypted Discussions (#2813) * I18n key fix * Add encrypted switch * Remove unused i18n keys * Add enabled to encryption reducer * Show encrypted option on CreateDiscussionView only when e2e encryption is properly set * Add localSearch and use it on search * Use encrypted from parent channel * Fix method calls as rest api with 2fa enabled * Fix logout after reset keys * Use encryption reducer instead of lib directly to check render * Check for room type logic to display encryption option on create discussion * Check toggle-room-e2e-encryption permission on RoomActionsView * Check for encryption status instead of setting on server * Fix * Disable switch instead of hide it * Fix spotlight for DMs * Fix server test * [FIX] Messagebox missing style for text color (#2786) * Changing auxilaryTintColor * Changed Placeholder color to BodyText color * added color prop * eslint changes * used array for styles Co-authored-by: Diego Mello <diegolmello@gmail.com> * [I18N] Update arabic (#2696) * Update ar.js * Update ar.js Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Workspace input without i18n (#2689) * [FIX] Translation of strings in Login page * Strings are added for translation. fixes: #2620 * Add pt-BR Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Spotlight returning duplicated entries (#2805) * Update rocketchat.js * Updated search function * Minor improvements * Remove atIndex * Add remove logic to remove duplicate data from response Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] Refactor ServerItem (#2778) * Updated ServerDropdown and ServerItem * Added ServerItem stories * Update ServerDropdown.js * Updated ServerItem stories * Updated ServerItem stories and ServerItem component * Updated SelectServerView, ServerItem and ServerItem stories * Updated ServerItem stories * Updated ServerItem stories * Update tests Co-authored-by: Diego Mello <diegolmello@gmail.com> * [DOCS] Updated Quick Start docs link in e2e/readme (#2802) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [I18N] Add Turkish (#2793) * Turkish language support added * Update tr.js Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Lint on #2793 (#2818) * [I18N] Add missing german strings (#2689) (#2820) * [I18N] Add missing italian strings (#2817) * [FIX] Server version becoming null on server change (#2821) * [FIX] Wrong styling on E2E encryption banner (#2767) * [FIX] Wrong styling on E2E encryption banner * [FIX] Wrong styling on E2E encryption banner * [FIX] Wrong styling on E2E encryption banner * [FIX] Wrong styling on E2E encryption banner (#2767) * Updated SortDropdown, ListHeader, ListItem and added stories for List.Item * Updated SortDropdown * Removed unused component * Updated List.Item and stories * Reverted unnecessary changes and updated ListItem stories * Fix minor indentation * Stop breaking Touch's default underlay color * Fix indentation * Remove falsy comparison from render * Fix left icon * Use List.Item on OmnichannelStatus * Add missing separator * Lint * Fix sort dropdown * Remove unnecessary styles * Fix detox Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] App Store using Experimental's app id (#2826) * [FIX] Wrong username on push notifications (#2825) * [FIX] Share extension memory issues on iOS (#2845) * Remove unnecessary class prop * Stop rendering servers when there's only one * Map and alloc only necessary columns from query * Fetch servers count instead of all servers records * Fetch only needed servers * Separators * Remove renderContent * Minor fix * Refactor query * Smaller avatars in memory * Fix getItemLayout * Add topic * Load less pods * tests * Import only used functions from lodash * Fix pods * Import only used functions from semver * Fix media sharing * Update pods * Disables preview and thumb on iOS * Update expo-video-thumbnail * Unnecessary change * [FIX] Logout from other locations not prompting confirmation option (#2854) * Fixed logout toast bug for the iOS * Removing callToAction and replacing with confirmationText Co-authored-by: Diego Mello <diegolmello@gmail.com> * Bump version to 4.14.1 (#2859) * [IMPROVEMENT] Check for focused rooms on in-app notifications (#2857) * Update InAppNotification and room reducer * Update InAppNotification This reverts commit 60330a1e04cfe8d2e5aa311f367083d831682c49. * Stop subscribing to threads * Remove ref * Fix prop-types Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Real name being ignored in SearchMessagesView (#2838) Co-authored-by: Gerzon Z <gerzonc@icloud.com> Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] Remove unnecessary share reducer calls (#2861) * Remove unnecesary share reducer calls * Update Avatar Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Breadcrumbs exceeding characters limit (#2862) * [FIX] breadcrumbs exceeding * fix.breadcrumbs-exceeding-change-events Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] App compressing videos on iOS (#2915) * Update index.js * Update index.js * [FIX] Real name setting ignored on reply preview (#2908) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Reply component sending unused prop to Description (#2900) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] BackdropOpacity based on themes (#2863) * Added backdropOpacity based on theme * Updated ActionSheet, ReactionsModal, ReactionPicker and Sidebar * Updated MultiSelect Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Webview not falling back to default auth challenge when no cert is provided (#2918) * [FIX] Android - fallback to default auth challenge handling when no cert is provided * If a certificate auth challenge is requested on Android the webview will hang if no certificate is loaded. To prevent this, fallback to default Android behavior and cancel the challenge with request.cancel() * No client certificate case defaults to super implementation * Update react-native-webview * Downgrade to previous dependency version Co-authored-by: Diego Mello <diegolmello@gmail.com> Co-authored-by: Gerzon Z <gerzonc@icloud.com> Co-authored-by: Jan Garaj <jan.garaj@gmail.com> * [FIX] Support Jitsi_URL_Room_Hash (#2905) * [FIX] Temp attachment files not being flushed after saved to gallery (#2871) * Update AttachmentView.js * Update AttachmentView.js * Update AttachmentView.js * Update AttachmentView.js Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] Update iOS profiles for Experimental app (#2933) * [IMPROVE] Deleted thread reply redirects to thread (#2840) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Thread showing typing indicator from main room (#2869) * [FIX] Remove typing indicator from thread's header * remove unnecessary props and change usersTyping condition Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] DM rooms show typing status from last group room (#2878) * [FIX] DM rooms show typing status from last group room * Undo changes * Check if current typing is from focused room before dispatching to redux Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Can't copy or edit media's description (#2885) * [FIX] Image descriptions issues * shorten the condition string * fix selectedMessage state Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] RightButtonsContainer re-render check not returning default value (#2899) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] Remove InteractionManager blocks (#2906) * [FIX] Remove InteractionManager blocks * Minor fix Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] App not sending second argument for EventEmitter.removeListener on some places (#2909) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Temp message ignoring real name (#2919) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] System message of e2e encryption is missing (#2888) * [FIX] System message of e2e encryption missing * add new encryption string * add to stories * Add pt-BR * Move stories Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] Add permissions to Redux (#2914) * [FIX] Add permissions to Redux store * add only permissions being used in the app * add clear permissions reducer * call RocketChat.hasPermission from reducer * add server version comparison on getPermissions * refactor hasPermission function * refactor hasPermission function * remove uncomment code * use Q.experimentalSortBy() * add coerce function * Change Rocketchat.hasPermission * Apply on isReadOnly * Apply to RoomInfoEditView * Apply to RoomInfoView and RoomInfoEditView * canAutoTranslate * Unnecessary clear permissions * Revert getUpdatedSince * Naming fix Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] Add hold step for ios and android build experimental (#2943) * [CHORE] Add hold step for ios-build-experimental and android-build-experimental * Android hold step * add ios hold step Co-authored-by: Diego Mello <diegolmello@gmail.com> * [IMPROVEMENT] Remove lodash.isEqual (#2893) * Added dequal and react-fast-compare as substitutes to lodash.isEqual * Update ReplyPreview.js * Remove react-fast-compare * Removed deep-equal and upgrade babel-eslint dev dependency * Fix avatar * Fix Messagebox * Fix CreateDiscussionView * ModalBlockView * NewMessageView * ProfileView * RoomInfoEditView * ServerDropdown * Return local search as object instead of observable * SelectedUsersView Co-authored-by: Diego Mello <diegolmello@gmail.com> * [I18N] Add missing Russian strings (#2946) * [i18n] Add missing Russian strings * Couple fixes * Fix Direct_message Translate Direct_message as already has been translated Co-authored-by: Diego Mello <diegolmello@gmail.com> * [CHORE] Use shortcut syntax for get collections (#2932) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Use List.Separator in all places (#2931) * [FIX] Use List.Separator in all places * add List.Separator * change List.Separator Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Limit new message list query size to 50 (#2947) * Limit query to 50 * Remove observable * [FIX] Support chats order for older versions of the server (#2934) * Update mergeSubscriptionsRooms.js * Update mergeSubscriptionsRooms.js * Update mergeSubscriptionsRooms.js * Minor refactor Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Reactions modal's backdrop color too light (#2949) Co-authored-by: Diego Mello <diegolmello@gmail.com> * Bump version to 4.15.0 (#2950) * [FIX] Share extension not working correctly on Official app (#2963) * [FIX] Cannot read property 'some' of undefined on hasPermission (#2966) Co-authored-by: Diego Mello <diegolmello@gmail.com> * [FIX] Deep linking and other connectivity issues (#2894) * Navigate from push notification only if necessary * Use JS SDK branch * Stop reconnecting if it's already connected * Fix RoomsListView forever loading after tapping push notification of another server * Execute fewer operations on app/index * Remove roomsRequest call from onForeground * Apply check and reopen * Stop opening in-app notification when the app is on backgorund * Connecting tweaks * Fix deep linking not working if the app is on background * Force reset yarn cache * Upgrade JS SDK * Remove listener on unmount * Fix resume on Android after back button is pressed * Fix local authentication resume * Fix back button android * Change JS SDK branch * [FIX] Messagebox's placeholder color is too bright (#2968) Co-authored-by: Gerzon Z <gerzonzcanario@gmail.com> Co-authored-by: Gerzon Z <gerzonc@icloud.com> Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> Co-authored-by: phriedrich <info@phriedrich.de> Co-authored-by: yash-rajpal <58601732+yash-rajpal@users.noreply.github.com> Co-authored-by: Fazil Boudjelal <fazildiablou@hotmail.fr> Co-authored-by: Sumukha Hegde <SUMUKHA214@GMAIL.COM> Co-authored-by: Hakan YILMAZ <mukerrem.yilmaz@hotmail.com> Co-authored-by: Vincenzo Esposito <aenon.esposito@gmail.com> Co-authored-by: Arkadyuti Bandyopadhyay <bandyopadhyayarkadyuti@gmail.com> Co-authored-by: Anant Bhasin <38764067+aKn1ghtOut@users.noreply.github.com> Co-authored-by: Gung Wah <41157464+kresnaputra@users.noreply.github.com> Co-authored-by: Billy Newman <newmanw10@gmail.com> Co-authored-by: Jan Garaj <jan.garaj@gmail.com> Co-authored-by: ankar84 <ankar84@gmail.com>
This commit is contained in:
parent
322dabc762
commit
a6e937f247
|
@ -237,9 +237,13 @@ commands:
|
|||
if [[ $CIRCLE_JOB == "ios-build-official" ]]; then
|
||||
/usr/libexec/PlistBuddy -c "Set BugsnagAPIKey $BUGSNAG_KEY_OFFICIAL" ./RocketChatRN/Info.plist
|
||||
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL YES" ./RocketChatRN/Info.plist
|
||||
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL YES" ./ShareRocketChatRN/Info.plist
|
||||
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL YES" ./NotificationService/Info.plist
|
||||
else
|
||||
/usr/libexec/PlistBuddy -c "Set BugsnagAPIKey $BUGSNAG_KEY" ./RocketChatRN/Info.plist
|
||||
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL NO" ./RocketChatRN/Info.plist
|
||||
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL NO" ./ShareRocketChatRN/Info.plist
|
||||
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL NO" ./NotificationService/Info.plist
|
||||
fi
|
||||
|
||||
if [[ $APP_STORE_CONNECT_API_KEY ]]; then
|
||||
|
@ -416,9 +420,13 @@ workflows:
|
|||
- lint-testunit
|
||||
|
||||
# iOS Experimental
|
||||
- ios-build-experimental:
|
||||
- ios-hold-build-experimental:
|
||||
type: approval
|
||||
requires:
|
||||
- lint-testunit
|
||||
- ios-build-experimental:
|
||||
requires:
|
||||
- ios-hold-build-experimental
|
||||
- ios-hold-testflight-experimental:
|
||||
type: approval
|
||||
requires:
|
||||
|
@ -444,9 +452,13 @@ workflows:
|
|||
- ios-hold-testflight-official
|
||||
|
||||
# Android Experimental
|
||||
- android-build-experimental:
|
||||
- android-hold-build-experimental:
|
||||
type: approval
|
||||
requires:
|
||||
- lint-testunit
|
||||
- android-build-experimental:
|
||||
requires:
|
||||
- android-hold-build-experimental
|
||||
- android-hold-google-play-beta-experimental:
|
||||
type: approval
|
||||
requires:
|
||||
|
|
|
@ -6,7 +6,7 @@ module.exports = {
|
|||
}
|
||||
}
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"parser": "@babel/eslint-parser",
|
||||
"extends": "airbnb",
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
|
@ -21,7 +21,8 @@ module.exports = {
|
|||
"react",
|
||||
"jsx-a11y",
|
||||
"import",
|
||||
"react-native"
|
||||
"react-native",
|
||||
"@babel"
|
||||
],
|
||||
"env": {
|
||||
"browser": true,
|
||||
|
@ -148,7 +149,8 @@ module.exports = {
|
|||
"react/jsx-curly-newline": [0],
|
||||
"react/state-in-constructor": [0],
|
||||
"no-async-promise-executor": [0],
|
||||
"max-classes-per-file": [0]
|
||||
"max-classes-per-file": [0],
|
||||
"no-multiple-empty-lines": [0]
|
||||
},
|
||||
"globals": {
|
||||
"__DEV__": true
|
||||
|
|
|
@ -44404,6 +44404,309 @@ exports[`Storyshots Message list message 1`] = `
|
|||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontSize": 20,
|
||||
"fontWeight": "300",
|
||||
"marginLeft": 10,
|
||||
"marginVertical": 30,
|
||||
},
|
||||
Object {
|
||||
"color": "#0d0e12",
|
||||
},
|
||||
Object {
|
||||
"marginBottom": 0,
|
||||
"marginTop": 30,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
Toggle e2e encryption
|
||||
</Text>
|
||||
<View
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flexDirection": "column",
|
||||
"paddingHorizontal": 14,
|
||||
"paddingVertical": 4,
|
||||
"width": "100%",
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"borderRadius": 2,
|
||||
"height": 20,
|
||||
"width": 20,
|
||||
},
|
||||
Object {
|
||||
"marginLeft": 16,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"overflow": "hidden",
|
||||
},
|
||||
Object {
|
||||
"borderRadius": 2,
|
||||
"height": 20,
|
||||
"width": 20,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<FastImageView
|
||||
resizeMode="cover"
|
||||
source={
|
||||
Object {
|
||||
"headers": undefined,
|
||||
"priority": "high",
|
||||
"uri": "https://open.rocket.chat/avatar/diego.mello?format=png&size=20",
|
||||
}
|
||||
}
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
"marginLeft": 46,
|
||||
},
|
||||
Object {
|
||||
"marginLeft": 10,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Text
|
||||
accessibilityLabel="This room's encryption has been disabled by diego.mello"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"fontFamily": "System",
|
||||
"fontSize": 16,
|
||||
"fontStyle": "italic",
|
||||
"fontWeight": "400",
|
||||
"textAlign": "left",
|
||||
},
|
||||
Object {
|
||||
"color": "#9ca2a8",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
This room's encryption has been disabled by diego.mello
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flexDirection": "column",
|
||||
"paddingHorizontal": 14,
|
||||
"paddingVertical": 4,
|
||||
"width": "100%",
|
||||
},
|
||||
undefined,
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"borderRadius": 2,
|
||||
"height": 20,
|
||||
"width": 20,
|
||||
},
|
||||
Object {
|
||||
"marginLeft": 16,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
focusable={true}
|
||||
onClick={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"overflow": "hidden",
|
||||
},
|
||||
Object {
|
||||
"borderRadius": 2,
|
||||
"height": 20,
|
||||
"width": 20,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<FastImageView
|
||||
resizeMode="cover"
|
||||
source={
|
||||
Object {
|
||||
"headers": undefined,
|
||||
"priority": "high",
|
||||
"uri": "https://open.rocket.chat/avatar/diego.mello?format=png&size=20",
|
||||
}
|
||||
}
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"flex": 1,
|
||||
"marginLeft": 46,
|
||||
},
|
||||
Object {
|
||||
"marginLeft": 10,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Text
|
||||
accessibilityLabel="This room's encryption has been enabled by diego.mello"
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"fontFamily": "System",
|
||||
"fontSize": 16,
|
||||
"fontStyle": "italic",
|
||||
"fontWeight": "400",
|
||||
"textAlign": "left",
|
||||
},
|
||||
Object {
|
||||
"color": "#9ca2a8",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
This room's encryption has been enabled by diego.mello
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
|
|
|
@ -144,7 +144,7 @@ android {
|
|||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode VERSIONCODE as Integer
|
||||
versionName "4.14.1"
|
||||
versionName "4.15.0"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
if (!isFoss) {
|
||||
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
||||
|
|
|
@ -70,3 +70,5 @@ export const SETTINGS = createRequestTypes('SETTINGS', ['CLEAR', 'ADD']);
|
|||
export const APP_STATE = createRequestTypes('APP_STATE', ['FOREGROUND', 'BACKGROUND']);
|
||||
export const ENTERPRISE_MODULES = createRequestTypes('ENTERPRISE_MODULES', ['CLEAR', 'SET']);
|
||||
export const ENCRYPTION = createRequestTypes('ENCRYPTION', ['INIT', 'STOP', 'DECODE_KEY', 'SET', 'SET_BANNER']);
|
||||
|
||||
export const PERMISSIONS = createRequestTypes('PERMISSIONS', ['SET']);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import * as types from './actionsTypes';
|
||||
|
||||
export function setPermissions(permissions) {
|
||||
return {
|
||||
type: types.PERMISSIONS.SET,
|
||||
permissions
|
||||
};
|
||||
}
|
|
@ -63,6 +63,7 @@ export const themes = {
|
|||
passcodeDotFull: '#6C727A',
|
||||
previewBackground: '#1F2329',
|
||||
previewTintColor: '#ffffff',
|
||||
backdropOpacity: 0.3,
|
||||
...mentions
|
||||
},
|
||||
dark: {
|
||||
|
@ -109,6 +110,7 @@ export const themes = {
|
|||
passcodeDotFull: '#6C727A',
|
||||
previewBackground: '#030b1b',
|
||||
previewTintColor: '#ffffff',
|
||||
backdropOpacity: 0.9,
|
||||
...mentions
|
||||
},
|
||||
black: {
|
||||
|
@ -155,6 +157,7 @@ export const themes = {
|
|||
passcodeDotFull: '#6C727A',
|
||||
previewBackground: '#000000',
|
||||
previewTintColor: '#ffffff',
|
||||
backdropOpacity: 0.9,
|
||||
...mentions
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { getBundleId, isIOS } from '../utils/deviceInfo';
|
||||
|
||||
import { appStoreID as APP_STORE_ID } from '../../app.json';
|
||||
const APP_STORE_ID = '1148741252';
|
||||
|
||||
export const PLAY_MARKET_LINK = `https://play.google.com/store/apps/details?id=${ getBundleId }`;
|
||||
export const FDROID_MARKET_LINK = 'https://f-droid.org/en/packages/chat.rocket.android';
|
||||
|
|
|
@ -101,6 +101,9 @@ export default {
|
|||
Jitsi_Enabled_TokenAuth: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_URL_Room_Hash: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Jitsi_URL_Room_Prefix: {
|
||||
type: 'valueAsString'
|
||||
},
|
||||
|
|
|
@ -147,7 +147,7 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
|
|||
const animatedPosition = React.useRef(new Value(0));
|
||||
const opacity = interpolate(animatedPosition.current, {
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 0.7],
|
||||
outputRange: [0, themes[theme].backdropOpacity],
|
||||
extrapolate: Extrapolate.CLAMP
|
||||
});
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import isEqual from 'react-fast-compare';
|
||||
|
||||
import database from '../../lib/database';
|
||||
import { getUserSelector } from '../../selectors/login';
|
||||
|
@ -34,7 +33,8 @@ class AvatarContainer extends React.Component {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!isEqual(prevProps, this.props)) {
|
||||
const { text, type } = this.props;
|
||||
if (prevProps.text !== text || prevProps.type !== type) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
|
@ -52,8 +52,8 @@ class AvatarContainer extends React.Component {
|
|||
|
||||
init = async() => {
|
||||
const db = database.active;
|
||||
const usersCollection = db.collections.get('users');
|
||||
const subsCollection = db.collections.get('subscriptions');
|
||||
const usersCollection = db.get('users');
|
||||
const subsCollection = db.get('subscriptions');
|
||||
|
||||
let record;
|
||||
try {
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
|||
import { View } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import ScrollableTabView from 'react-native-scrollable-tab-view';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
import { connect } from 'react-redux';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||
|
@ -67,7 +67,7 @@ class EmojiPicker extends Component {
|
|||
if (nextState.width !== width) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextState.frequentlyUsed, frequentlyUsed)) {
|
||||
if (!dequal(nextState.frequentlyUsed, frequentlyUsed)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -95,7 +95,7 @@ class EmojiPicker extends Component {
|
|||
// eslint-disable-next-line react/sort-comp
|
||||
_addFrequentlyUsed = protectedFunction(async(emoji) => {
|
||||
const db = database.active;
|
||||
const freqEmojiCollection = db.collections.get('frequently_used_emojis');
|
||||
const freqEmojiCollection = db.get('frequently_used_emojis');
|
||||
let freqEmojiRecord;
|
||||
try {
|
||||
freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
|
||||
|
@ -120,7 +120,7 @@ class EmojiPicker extends Component {
|
|||
|
||||
updateFrequentlyUsed = async() => {
|
||||
const db = database.active;
|
||||
const frequentlyUsedRecords = await db.collections.get('frequently_used_emojis').query().fetch();
|
||||
const frequentlyUsedRecords = await db.get('frequently_used_emojis').query().fetch();
|
||||
let frequentlyUsed = orderBy(frequentlyUsedRecords, ['count'], ['desc']);
|
||||
frequentlyUsed = frequentlyUsed.map((item) => {
|
||||
if (item.isCustom) {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import React, { memo, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { NotifierRoot, Notifier, Easing } from 'react-native-notifier';
|
||||
import { connect } from 'react-redux';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import NotifierComponent from './NotifierComponent';
|
||||
import EventEmitter from '../../utils/events';
|
||||
|
@ -8,13 +11,17 @@ import { getActiveRoute } from '../../utils/navigation';
|
|||
|
||||
export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp';
|
||||
|
||||
const InAppNotification = memo(() => {
|
||||
const InAppNotification = memo(({ rooms, appState }) => {
|
||||
const show = (notification) => {
|
||||
if (appState !== 'foreground') {
|
||||
return;
|
||||
}
|
||||
|
||||
const { payload } = notification;
|
||||
const state = Navigation.navigationRef.current?.getRootState();
|
||||
const route = getActiveRoute(state);
|
||||
if (payload.rid) {
|
||||
if ((route?.name === 'RoomView' && route.params?.rid === payload.rid) || route?.name === 'JitsiMeetView') {
|
||||
if (rooms.includes(payload.rid) || route?.name === 'JitsiMeetView') {
|
||||
return;
|
||||
}
|
||||
Notifier.showNotification({
|
||||
|
@ -28,13 +35,23 @@ const InAppNotification = memo(() => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
EventEmitter.addEventListener(INAPP_NOTIFICATION_EMITTER, show);
|
||||
const listener = EventEmitter.addEventListener(INAPP_NOTIFICATION_EMITTER, show);
|
||||
return () => {
|
||||
EventEmitter.removeListener(INAPP_NOTIFICATION_EMITTER);
|
||||
EventEmitter.removeListener(INAPP_NOTIFICATION_EMITTER, listener);
|
||||
};
|
||||
}, []);
|
||||
}, [rooms]);
|
||||
|
||||
return <NotifierRoot />;
|
||||
}, (prevProps, nextProps) => dequal(prevProps.rooms, nextProps.rooms));
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
rooms: state.room.rooms,
|
||||
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
|
||||
});
|
||||
|
||||
export default InAppNotification;
|
||||
InAppNotification.propTypes = {
|
||||
rooms: PropTypes.array,
|
||||
appState: PropTypes.string
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(InAppNotification);
|
||||
|
|
|
@ -96,7 +96,7 @@ const Header = React.memo(({
|
|||
const setEmojis = async() => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const freqEmojiCollection = db.collections.get('frequently_used_emojis');
|
||||
const freqEmojiCollection = db.get('frequently_used_emojis');
|
||||
let freqEmojis = await freqEmojiCollection.query().fetch();
|
||||
|
||||
const isLandscape = width > height;
|
||||
|
|
|
@ -34,20 +34,24 @@ const MessageActions = React.memo(forwardRef(({
|
|||
Message_AllowPinning,
|
||||
Message_AllowStarring,
|
||||
Message_Read_Receipt_Store_Users,
|
||||
isMasterDetail
|
||||
isMasterDetail,
|
||||
editMessagePermission,
|
||||
deleteMessagePermission,
|
||||
forceDeleteMessagePermission,
|
||||
pinMessagePermission
|
||||
}, ref) => {
|
||||
let permissions = {};
|
||||
const { showActionSheet, hideActionSheet } = useActionSheet();
|
||||
|
||||
const getPermissions = async() => {
|
||||
try {
|
||||
const permission = ['edit-message', 'delete-message', 'force-delete-message', 'pin-message'];
|
||||
const permission = [editMessagePermission, deleteMessagePermission, forceDeleteMessagePermission, pinMessagePermission];
|
||||
const result = await RocketChat.hasPermission(permission, room.rid);
|
||||
permissions = {
|
||||
hasEditPermission: result[permission[0]],
|
||||
hasDeletePermission: result[permission[1]],
|
||||
hasForceDeletePermission: result[permission[2]],
|
||||
hasPinPermission: result[permission[3]]
|
||||
hasEditPermission: result[0],
|
||||
hasDeletePermission: result[1],
|
||||
hasForceDeletePermission: result[2],
|
||||
hasPinPermission: result[3]
|
||||
};
|
||||
} catch {
|
||||
// Do nothing
|
||||
|
@ -141,7 +145,7 @@ const MessageActions = React.memo(forwardRef(({
|
|||
const db = database.active;
|
||||
const result = await RocketChat.markAsUnread({ messageId });
|
||||
if (result.success) {
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const subCollection = db.get('subscriptions');
|
||||
const subRecord = await subCollection.find(rid);
|
||||
await db.action(async() => {
|
||||
try {
|
||||
|
@ -171,7 +175,7 @@ const MessageActions = React.memo(forwardRef(({
|
|||
|
||||
const handleCopy = async(message) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_COPY);
|
||||
await Clipboard.setString(message.msg);
|
||||
await Clipboard.setString(message?.attachments?.[0]?.description || message.msg);
|
||||
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
|
||||
};
|
||||
|
||||
|
@ -440,7 +444,11 @@ MessageActions.propTypes = {
|
|||
Message_AllowPinning: PropTypes.bool,
|
||||
Message_AllowStarring: PropTypes.bool,
|
||||
Message_Read_Receipt_Store_Users: PropTypes.bool,
|
||||
server: PropTypes.string
|
||||
server: PropTypes.string,
|
||||
editMessagePermission: PropTypes.array,
|
||||
deleteMessagePermission: PropTypes.array,
|
||||
forceDeleteMessagePermission: PropTypes.array,
|
||||
pinMessagePermission: PropTypes.array
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
@ -452,7 +460,11 @@ const mapStateToProps = state => ({
|
|||
Message_AllowPinning: state.settings.Message_AllowPinning,
|
||||
Message_AllowStarring: state.settings.Message_AllowStarring,
|
||||
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users,
|
||||
isMasterDetail: state.app.isMasterDetail
|
||||
isMasterDetail: state.app.isMasterDetail,
|
||||
editMessagePermission: state.permissions['edit-message'],
|
||||
deleteMessagePermission: state.permissions['delete-message'],
|
||||
forceDeleteMessagePermission: state.permissions['force-delete-message'],
|
||||
pinMessagePermission: state.permissions['pin-message']
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, null, null, { forwardRef: true })(MessageActions);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { FlatList } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import Item from './Item';
|
||||
import styles from '../styles';
|
||||
|
@ -31,7 +31,7 @@ const CommandsPreview = React.memo(({ theme, commandPreview, showCommandPreview
|
|||
if (prevProps.showCommandPreview !== nextProps.showCommandPreview) {
|
||||
return false;
|
||||
}
|
||||
if (!equal(prevProps.commandPreview, nextProps.commandPreview)) {
|
||||
if (!dequal(prevProps.commandPreview, nextProps.commandPreview)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { FlatList, View } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import styles from '../styles';
|
||||
import MentionItem from './MentionItem';
|
||||
|
@ -30,7 +30,7 @@ const Mentions = React.memo(({ mentions, trackingType, theme }) => {
|
|||
if (prevProps.trackingType !== nextProps.trackingType) {
|
||||
return false;
|
||||
}
|
||||
if (!equal(prevProps.mentions, nextProps.mentions)) {
|
||||
if (!dequal(prevProps.mentions, nextProps.mentions)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -3,7 +3,6 @@ import { View, Text, StyleSheet } from 'react-native';
|
|||
import PropTypes from 'prop-types';
|
||||
import moment from 'moment';
|
||||
import { connect } from 'react-redux';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import Markdown from '../markdown';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
|
@ -43,7 +42,7 @@ const styles = StyleSheet.create({
|
|||
});
|
||||
|
||||
const ReplyPreview = React.memo(({
|
||||
message, Message_TimeFormat, baseUrl, username, replying, getCustomEmoji, close, theme
|
||||
message, Message_TimeFormat, baseUrl, username, replying, getCustomEmoji, close, theme, useRealName
|
||||
}) => {
|
||||
if (!replying) {
|
||||
return null;
|
||||
|
@ -59,7 +58,7 @@ const ReplyPreview = React.memo(({
|
|||
>
|
||||
<View style={[styles.messageContainer, { backgroundColor: themes[theme].chatComponentBackground }]}>
|
||||
<View style={styles.header}>
|
||||
<Text style={[styles.username, { color: themes[theme].tintColor }]}>{message.u?.username}</Text>
|
||||
<Text style={[styles.username, { color: themes[theme].tintColor }]}>{useRealName ? message.u?.name : message.u?.username}</Text>
|
||||
<Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
||||
</View>
|
||||
<Markdown
|
||||
|
@ -75,7 +74,7 @@ const ReplyPreview = React.memo(({
|
|||
<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));
|
||||
}, (prevProps, nextProps) => prevProps.replying === nextProps.replying && prevProps.theme === nextProps.theme && prevProps.message.id === nextProps.message.id);
|
||||
|
||||
ReplyPreview.propTypes = {
|
||||
replying: PropTypes.bool,
|
||||
|
@ -85,12 +84,14 @@ ReplyPreview.propTypes = {
|
|||
baseUrl: PropTypes.string.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
getCustomEmoji: PropTypes.func,
|
||||
theme: PropTypes.string
|
||||
theme: PropTypes.string,
|
||||
useRealName: PropTypes.bool
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
||||
baseUrl: state.server.server
|
||||
baseUrl: state.server.server,
|
||||
useRealName: state.settings.UI_Use_Real_Name
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(ReplyPreview);
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
import { connect } from 'react-redux';
|
||||
import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard';
|
||||
import ImagePicker from 'react-native-image-crop-picker';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
|
||||
|
@ -63,6 +63,7 @@ const imagePickerConfig = {
|
|||
|
||||
const libraryPickerConfig = {
|
||||
multiple: true,
|
||||
compressVideoPreset: 'Passthrough',
|
||||
mediaType: 'any'
|
||||
};
|
||||
|
||||
|
@ -189,8 +190,8 @@ class MessageBox extends Component {
|
|||
} = this.props;
|
||||
let msg;
|
||||
try {
|
||||
const threadsCollection = db.collections.get('threads');
|
||||
const subsCollection = db.collections.get('subscriptions');
|
||||
const threadsCollection = db.get('threads');
|
||||
const subsCollection = db.get('subscriptions');
|
||||
try {
|
||||
this.room = await subsCollection.find(rid);
|
||||
} catch (error) {
|
||||
|
@ -270,7 +271,7 @@ class MessageBox extends Component {
|
|||
} = this.state;
|
||||
|
||||
const {
|
||||
roomType, replying, editing, isFocused, message, theme, children
|
||||
roomType, replying, editing, isFocused, message, theme
|
||||
} = this.props;
|
||||
if (nextProps.theme !== theme) {
|
||||
return true;
|
||||
|
@ -299,16 +300,13 @@ class MessageBox extends Component {
|
|||
if (nextState.tshow !== tshow) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextState.mentions, mentions)) {
|
||||
if (!dequal(nextState.mentions, mentions)) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextState.commandPreview, commandPreview)) {
|
||||
if (!dequal(nextState.commandPreview, commandPreview)) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextProps.message, message)) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextProps.children, children)) {
|
||||
if (!dequal(nextProps.message?.id, message?.id)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -366,7 +364,7 @@ class MessageBox extends Component {
|
|||
const slashCommand = text.match(/^\/([a-z0-9._-]+) (.+)/im);
|
||||
if (slashCommand) {
|
||||
const [, name, params] = slashCommand;
|
||||
const commandsCollection = db.collections.get('slash_commands');
|
||||
const commandsCollection = db.get('slash_commands');
|
||||
try {
|
||||
const command = await commandsCollection.find(name);
|
||||
if (command.providesPreview) {
|
||||
|
@ -507,7 +505,7 @@ class MessageBox extends Component {
|
|||
getEmojis = debounce(async(keyword) => {
|
||||
const db = database.active;
|
||||
if (keyword) {
|
||||
const customEmojisCollection = db.collections.get('custom_emojis');
|
||||
const customEmojisCollection = db.get('custom_emojis');
|
||||
const likeString = sanitizeLikeString(keyword);
|
||||
let customEmojis = await customEmojisCollection.query(
|
||||
Q.where('name', Q.like(`${ likeString }%`))
|
||||
|
@ -521,7 +519,7 @@ class MessageBox extends Component {
|
|||
|
||||
getSlashCommands = debounce(async(keyword) => {
|
||||
const db = database.active;
|
||||
const commandsCollection = db.collections.get('slash_commands');
|
||||
const commandsCollection = db.get('slash_commands');
|
||||
const likeString = sanitizeLikeString(keyword);
|
||||
const commands = await commandsCollection.query(
|
||||
Q.where('id', Q.like(`${ likeString }%`))
|
||||
|
@ -753,7 +751,7 @@ class MessageBox extends Component {
|
|||
// Slash command
|
||||
if (message[0] === MENTIONS_TRACKING_TYPE_COMMANDS) {
|
||||
const db = database.active;
|
||||
const commandsCollection = db.collections.get('slash_commands');
|
||||
const commandsCollection = db.get('slash_commands');
|
||||
const command = message.replace(/ .*/, '').slice(1);
|
||||
const likeString = sanitizeLikeString(command);
|
||||
const slashCommand = await commandsCollection.query(
|
||||
|
@ -941,7 +939,7 @@ class MessageBox extends Component {
|
|||
keyboardType='twitter'
|
||||
blurOnSubmit={false}
|
||||
placeholder={I18n.t('New_Message')}
|
||||
placeholderTextColor={themes[theme].auxiliaryTintColor}
|
||||
placeholderTextColor={themes[theme].auxiliaryText}
|
||||
onChangeText={this.onChangeText}
|
||||
onSelectionChange={this.onSelectionChange}
|
||||
underlineColorAndroid='transparent'
|
||||
|
|
|
@ -19,8 +19,8 @@ const MessageErrorActions = forwardRef(({ tmid }, ref) => {
|
|||
try {
|
||||
const db = database.active;
|
||||
const deleteBatch = [];
|
||||
const msgCollection = db.collections.get('messages');
|
||||
const threadCollection = db.collections.get('threads');
|
||||
const msgCollection = db.get('messages');
|
||||
const threadCollection = db.get('threads');
|
||||
|
||||
// Delete the object (it can be Message or ThreadMessage instance)
|
||||
deleteBatch.push(message.prepareDestroyPermanently());
|
||||
|
|
|
@ -28,7 +28,7 @@ class Toast extends React.Component {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
EventEmitter.addEventListener(LISTENER, this.showToast);
|
||||
this.listener = EventEmitter.addEventListener(LISTENER, this.showToast);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
|
@ -40,7 +40,7 @@ class Toast extends React.Component {
|
|||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EventEmitter.removeListener(LISTENER);
|
||||
EventEmitter.removeListener(LISTENER, this.listener);
|
||||
}
|
||||
|
||||
getToastRef = toast => this.toast = toast;
|
||||
|
|
|
@ -58,9 +58,9 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }) => {
|
|||
const showTwoFactor = args => setData(args);
|
||||
|
||||
useEffect(() => {
|
||||
EventEmitter.addEventListener(TWO_FACTOR, showTwoFactor);
|
||||
const listener = EventEmitter.addEventListener(TWO_FACTOR, showTwoFactor);
|
||||
|
||||
return () => EventEmitter.removeListener(TWO_FACTOR);
|
||||
return () => EventEmitter.removeListener(TWO_FACTOR, listener);
|
||||
}, []);
|
||||
|
||||
const onCancel = () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
View, Text, TouchableWithoutFeedback, Modal, KeyboardAvoidingView, Animated, Easing
|
||||
View, Text, TouchableWithoutFeedback, Modal, KeyboardAvoidingView, Animated, Easing, StyleSheet
|
||||
} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
||||
|
@ -172,7 +172,7 @@ export const MultiSelect = React.memo(({
|
|||
>
|
||||
<TouchableWithoutFeedback onPress={onHide}>
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.backdrop, { backgroundColor: themes[theme].backdropColor }]} />
|
||||
<View style={{ ...StyleSheet.absoluteFill, opacity: themes[theme].backdropOpacity, backgroundColor: themes[theme].backdropColor }} />
|
||||
<KeyboardAvoidingView style={styles.keyboardView} behavior={behavior}>
|
||||
<Animated.View style={[styles.animatedContent, { transform: [{ translateY }] }]}>
|
||||
{showContent ? renderContent() : null}
|
||||
|
|
|
@ -8,10 +8,6 @@ export default StyleSheet.create({
|
|||
alignItems: 'center',
|
||||
justifyContent: 'flex-end'
|
||||
},
|
||||
backdrop: {
|
||||
...StyleSheet.absoluteFill,
|
||||
opacity: 0.3
|
||||
},
|
||||
modal: {
|
||||
height: 300,
|
||||
width: '100%',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { dequal } from 'dequal';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Image from './Image';
|
||||
|
@ -28,7 +28,7 @@ const Attachments = React.memo(({
|
|||
// eslint-disable-next-line react/no-array-index-key
|
||||
return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
||||
});
|
||||
}, (prevProps, nextProps) => isEqual(prevProps.attachments, nextProps.attachments) && prevProps.theme === nextProps.theme);
|
||||
}, (prevProps, nextProps) => dequal(prevProps.attachments, nextProps.attachments) && prevProps.theme === nextProps.theme);
|
||||
|
||||
Attachments.propTypes = {
|
||||
attachments: PropTypes.array,
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
import { Audio } from 'expo-av';
|
||||
import Slider from '@react-native-community/slider';
|
||||
import moment from 'moment';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
|
||||
|
||||
import Touchable from './Touchable';
|
||||
|
@ -150,7 +150,7 @@ class MessageAudio extends React.Component {
|
|||
if (nextState.paused !== paused) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextProps.file, file)) {
|
||||
if (!dequal(nextProps.file, file)) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.loading !== loading) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import I18n from '../../i18n';
|
||||
import styles from './styles';
|
||||
|
@ -108,10 +108,10 @@ const Content = React.memo((props) => {
|
|||
if (prevProps.isIgnored !== nextProps.isIgnored) {
|
||||
return false;
|
||||
}
|
||||
if (!equal(prevProps.mentions, nextProps.mentions)) {
|
||||
if (!dequal(prevProps.mentions, nextProps.mentions)) {
|
||||
return false;
|
||||
}
|
||||
if (!equal(prevProps.channels, nextProps.channels)) {
|
||||
if (!dequal(prevProps.channels, nextProps.channels)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useContext } from 'react';
|
|||
import { View } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
import { createImageProgress } from 'react-native-image-progress';
|
||||
import * as Progress from 'react-native-progress';
|
||||
|
||||
|
@ -66,7 +66,7 @@ const ImageContainer = React.memo(({
|
|||
<MessageImage img={img} theme={theme} />
|
||||
</Button>
|
||||
);
|
||||
}, (prevProps, nextProps) => equal(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme);
|
||||
}, (prevProps, nextProps) => dequal(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme);
|
||||
|
||||
ImageContainer.propTypes = {
|
||||
file: PropTypes.object,
|
||||
|
|
|
@ -119,7 +119,7 @@ const MessageTouchable = React.memo((props) => {
|
|||
<Touchable
|
||||
onLongPress={onLongPress}
|
||||
onPress={onPress}
|
||||
disabled={props.isInfo || props.archived || props.isTemp}
|
||||
disabled={(props.isInfo && !props.isThreadReply) || props.archived || props.isTemp}
|
||||
>
|
||||
<View>
|
||||
<Message {...props} />
|
||||
|
@ -132,6 +132,7 @@ MessageTouchable.displayName = 'MessageTouchable';
|
|||
MessageTouchable.propTypes = {
|
||||
hasError: PropTypes.bool,
|
||||
isInfo: PropTypes.bool,
|
||||
isThreadReply: PropTypes.bool,
|
||||
isTemp: PropTypes.bool,
|
||||
archived: PropTypes.bool
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useContext } from 'react';
|
|||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import moment from 'moment';
|
||||
import isEqual from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import Touchable from './Touchable';
|
||||
import Markdown from '../markdown';
|
||||
|
@ -125,7 +125,7 @@ const Fields = React.memo(({ attachment, theme }) => {
|
|||
))}
|
||||
</View>
|
||||
);
|
||||
}, (prevProps, nextProps) => isEqual(prevProps.attachment.fields, nextProps.attachment.fields) && prevProps.theme === nextProps.theme);
|
||||
}, (prevProps, nextProps) => dequal(prevProps.attachment.fields, nextProps.attachment.fields) && prevProps.theme === nextProps.theme);
|
||||
|
||||
const Reply = React.memo(({
|
||||
attachment, timeFormat, index, getCustomEmoji, theme
|
||||
|
@ -172,7 +172,6 @@ const Reply = React.memo(({
|
|||
/>
|
||||
<Description
|
||||
attachment={attachment}
|
||||
timeFormat={timeFormat}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
theme={theme}
|
||||
/>
|
||||
|
@ -188,7 +187,7 @@ const Reply = React.memo(({
|
|||
/>
|
||||
</>
|
||||
);
|
||||
}, (prevProps, nextProps) => isEqual(prevProps.attachment, nextProps.attachment) && prevProps.theme === nextProps.theme);
|
||||
}, (prevProps, nextProps) => dequal(prevProps.attachment, nextProps.attachment) && prevProps.theme === nextProps.theme);
|
||||
|
||||
Reply.propTypes = {
|
||||
attachment: PropTypes.object,
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import Touchable from './Touchable';
|
||||
import openLink from '../../utils/openLink';
|
||||
|
@ -112,7 +112,7 @@ const Url = React.memo(({ url, index, theme }) => {
|
|||
</>
|
||||
</Touchable>
|
||||
);
|
||||
}, (oldProps, newProps) => isEqual(oldProps.url, newProps.url) && oldProps.theme === newProps.theme);
|
||||
}, (oldProps, newProps) => dequal(oldProps.url, newProps.url) && oldProps.theme === newProps.theme);
|
||||
|
||||
const Urls = React.memo(({ urls, theme }) => {
|
||||
if (!urls || urls.length === 0) {
|
||||
|
@ -122,7 +122,7 @@ const Urls = React.memo(({ urls, theme }) => {
|
|||
return urls.map((url, index) => (
|
||||
<Url url={url} key={url.url} index={index} theme={theme} />
|
||||
));
|
||||
}, (oldProps, newProps) => isEqual(oldProps.urls, newProps.urls) && oldProps.theme === newProps.theme);
|
||||
}, (oldProps, newProps) => dequal(oldProps.urls, newProps.urls) && oldProps.theme === newProps.theme);
|
||||
|
||||
UrlImage.propTypes = {
|
||||
image: PropTypes.string
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import isEqual from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import Touchable from './Touchable';
|
||||
import Markdown from '../markdown';
|
||||
|
@ -57,7 +57,7 @@ const Video = React.memo(({
|
|||
<Markdown msg={file.description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||
</>
|
||||
);
|
||||
}, (prevProps, nextProps) => isEqual(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme);
|
||||
}, (prevProps, nextProps) => dequal(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme);
|
||||
|
||||
Video.propTypes = {
|
||||
file: PropTypes.object,
|
||||
|
|
|
@ -37,7 +37,9 @@ export const SYSTEM_MESSAGES = [
|
|||
'room_changed_privacy',
|
||||
'room_changed_avatar',
|
||||
'message_snippeted',
|
||||
'thread-created'
|
||||
'thread-created',
|
||||
'room_e2e_enabled',
|
||||
'room_e2e_disabled'
|
||||
];
|
||||
|
||||
export const SYSTEM_MESSAGE_TYPES = {
|
||||
|
@ -100,6 +102,10 @@ export const getInfoMessage = ({
|
|||
return I18n.t('Room_changed_avatar', { userBy: username });
|
||||
} else if (type === 'message_snippeted') {
|
||||
return I18n.t('Created_snippet');
|
||||
} else if (type === 'room_e2e_disabled') {
|
||||
return I18n.t('This_room_encryption_has_been_disabled_by__username_', { username });
|
||||
} else if (type === 'room_e2e_enabled') {
|
||||
return I18n.t('This_room_encryption_has_been_enabled_by__username_', { username });
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { FlatList } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import isEqual from 'react-fast-compare';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import I18n from '../../../i18n';
|
||||
import RoomItem, { ROW_HEIGHT } from '../../../presentation/RoomItem';
|
||||
|
@ -56,7 +56,7 @@ class QueueListView extends React.Component {
|
|||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
const { queued } = this.props;
|
||||
if (!isEqual(nextProps.queued, queued)) {
|
||||
if (!dequal(nextProps.queued, queued)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -704,5 +704,7 @@ export default {
|
|||
Direct_message: 'Direct message',
|
||||
Message_Ignored: 'Message ignored. Tap to display it.',
|
||||
Enter_workspace_URL: 'Enter workspace URL',
|
||||
Workspace_URL_Example: 'Ex. your-company.rocket.chat'
|
||||
Workspace_URL_Example: 'Ex. your-company.rocket.chat',
|
||||
This_room_encryption_has_been_enabled_by__username_: 'This room\'s encryption has been enabled by {{username}}',
|
||||
This_room_encryption_has_been_disabled_by__username_: 'This room\'s encryption has been disabled by {{username}}'
|
||||
};
|
||||
|
|
|
@ -651,5 +651,7 @@ export default {
|
|||
Direct_message: 'Mensagem direta',
|
||||
Message_Ignored: 'Mensagem ignorada. Toque para mostrar.',
|
||||
Enter_workspace_URL: 'Digite a URL da sua workspace',
|
||||
Workspace_URL_Example: 'Ex. sua-empresa.rocket.chat'
|
||||
Workspace_URL_Example: 'Ex. sua-empresa.rocket.chat',
|
||||
This_room_encryption_has_been_enabled_by__username_: 'A criptografia para essa sala foi habilitada por {{username}}',
|
||||
This_room_encryption_has_been_disabled_by__username_: 'A criptografia para essa sala foi desabilitada por {{username}}'
|
||||
};
|
||||
|
|
|
@ -678,5 +678,31 @@ export default {
|
|||
No_threads: 'Тредов нет',
|
||||
No_threads_following: 'Нет тредов, за которыми вы следите',
|
||||
No_threads_unread: 'Непрочитанных тредов нет',
|
||||
Messagebox_Send_to_channel: 'Отправить в чат'
|
||||
Messagebox_Send_to_channel: 'Отправить в чат',
|
||||
Set_as_leader: 'Назначить лидером',
|
||||
Set_as_moderator: 'Назначить модератором',
|
||||
Set_as_owner: 'Назначить владельцем',
|
||||
Remove_as_leader: 'Удалить из лидеров',
|
||||
Remove_as_moderator: 'Удалить из модераторов',
|
||||
Remove_as_owner: 'Удалить из владельцев',
|
||||
Remove_from_room: 'Удалить из чата',
|
||||
Ignore: 'Игнориновать',
|
||||
Unignore: 'Прекратить игнорировать',
|
||||
User_has_been_ignored: 'Пользователь теперь игнорируется',
|
||||
User_has_been_unignored: 'Пользователь больше не игнорируется',
|
||||
User_has_been_removed_from_s: 'Пользователь удален из {{s}}',
|
||||
User__username__is_now_a_leader_of__room_name_: 'Пользователь {{username}} больше не лидер в чате {{room_name}}',
|
||||
User__username__is_now_a_moderator_of__room_name_: 'Пользователь {{username}} больше не модератор в чате {{room_name}}',
|
||||
User__username__is_now_a_owner_of__room_name_: 'Пользователь {{username}} больше не владелец в чате {{room_name}}',
|
||||
User__username__removed_from__room_name__leaders: 'Пользователь {{username}} удален из {{room_name}} лидеров',
|
||||
User__username__removed_from__room_name__moderators: 'Пользователь {{username}} удален из {{room_name}} модераторов',
|
||||
User__username__removed_from__room_name__owners: 'Пользователь {{username}} удален из {{room_name}} владельцев',
|
||||
The_user_will_be_removed_from_s: 'Пользователь будет удален из {{s}}',
|
||||
Yes_remove_user: 'Да, удалить пользователя!',
|
||||
Direct_message: 'Личное сообщение',
|
||||
Message_Ignored: 'Сообщение игнорируется. Тапните по нему, чтобы отобразить его.',
|
||||
Enter_workspace_URL: 'Введите URL вашего рабочего пространства',
|
||||
Workspace_URL_Example: 'Например, your-company.rocket.chat',
|
||||
This_room_encryption_has_been_enabled_by__username_: 'Шифрование для этого чата включено {{username}}',
|
||||
This_room_encryption_has_been_disabled_by__username_: 'Шифрование для этого чата выключено {{username}}'
|
||||
};
|
||||
|
|
21
app/index.js
21
app/index.js
|
@ -112,16 +112,25 @@ export default class Root extends React.Component {
|
|||
|
||||
init = async() => {
|
||||
UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then(this.setTheme);
|
||||
const [notification, deepLinking] = await Promise.all([initializePushNotifications(), Linking.getInitialURL()]);
|
||||
const parsedDeepLinkingURL = parseDeepLinking(deepLinking);
|
||||
store.dispatch(appInitLocalSettings());
|
||||
|
||||
// Open app from push notification
|
||||
const notification = await initializePushNotifications();
|
||||
if (notification) {
|
||||
onNotification(notification);
|
||||
} else if (parsedDeepLinkingURL) {
|
||||
store.dispatch(deepLinkingOpen(parsedDeepLinkingURL));
|
||||
} else {
|
||||
store.dispatch(appInit());
|
||||
return;
|
||||
}
|
||||
|
||||
// Open app from deep linking
|
||||
const deepLinking = await Linking.getInitialURL();
|
||||
const parsedDeepLinkingURL = parseDeepLinking(deepLinking);
|
||||
if (parsedDeepLinkingURL) {
|
||||
store.dispatch(deepLinkingOpen(parsedDeepLinkingURL));
|
||||
return;
|
||||
}
|
||||
|
||||
// Open app from app icon
|
||||
store.dispatch(appInit());
|
||||
}
|
||||
|
||||
getMasterDetail = (width) => {
|
||||
|
|
|
@ -229,9 +229,9 @@ class Encryption {
|
|||
decryptPendingMessages = async(roomId) => {
|
||||
const db = database.active;
|
||||
|
||||
const messagesCollection = db.collections.get('messages');
|
||||
const threadsCollection = db.collections.get('threads');
|
||||
const threadMessagesCollection = db.collections.get('thread_messages');
|
||||
const messagesCollection = db.get('messages');
|
||||
const threadsCollection = db.get('threads');
|
||||
const threadMessagesCollection = db.get('thread_messages');
|
||||
|
||||
// e2e status is null or 'pending' and message type is 'e2e'
|
||||
const whereClause = [
|
||||
|
@ -286,7 +286,7 @@ class Encryption {
|
|||
// after initialize the encryption client
|
||||
decryptPendingSubscriptions = async() => {
|
||||
const db = database.active;
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const subCollection = db.get('subscriptions');
|
||||
try {
|
||||
// Find all rooms that can have a lastMessage encrypted
|
||||
// If we select only encrypted rooms we can miss some room that changed their encrypted status
|
||||
|
@ -347,7 +347,7 @@ class Encryption {
|
|||
|
||||
const { rid } = subscription;
|
||||
const db = database.active;
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const subCollection = db.get('subscriptions');
|
||||
|
||||
let subRecord;
|
||||
try {
|
||||
|
@ -400,7 +400,7 @@ class Encryption {
|
|||
encryptMessage = async(message) => {
|
||||
const { rid } = message;
|
||||
const db = database.active;
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const subCollection = db.get('subscriptions');
|
||||
|
||||
try {
|
||||
// Find the subscription
|
||||
|
|
|
@ -49,7 +49,7 @@ export default class EncryptionRoom {
|
|||
}
|
||||
|
||||
const db = database.active;
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const subCollection = db.get('subscriptions');
|
||||
try {
|
||||
// Find the subscription
|
||||
const subscription = await subCollection.find(this.roomId);
|
||||
|
|
|
@ -2,7 +2,7 @@ import reduxStore from '../createStore';
|
|||
import Navigation from '../Navigation';
|
||||
import { logEvent, events } from '../../utils/log';
|
||||
|
||||
async function jitsiURL({ rid }) {
|
||||
async function jitsiURL({ room }) {
|
||||
const { settings } = reduxStore.getState();
|
||||
const { Jitsi_Enabled } = settings;
|
||||
|
||||
|
@ -11,31 +11,37 @@ async function jitsiURL({ rid }) {
|
|||
}
|
||||
|
||||
const {
|
||||
Jitsi_Domain, Jitsi_URL_Room_Prefix, Jitsi_SSL, Jitsi_Enabled_TokenAuth, uniqueID
|
||||
Jitsi_Domain, Jitsi_URL_Room_Prefix, Jitsi_SSL, Jitsi_Enabled_TokenAuth, uniqueID, Jitsi_URL_Room_Hash
|
||||
} = settings;
|
||||
|
||||
const domain = `${ Jitsi_Domain }/`;
|
||||
const prefix = Jitsi_URL_Room_Prefix;
|
||||
const uniqueIdentifier = uniqueID || 'undefined';
|
||||
const protocol = Jitsi_SSL ? 'https://' : 'http://';
|
||||
|
||||
let queryString = '';
|
||||
if (Jitsi_Enabled_TokenAuth) {
|
||||
try {
|
||||
const accessToken = await this.methodCallWrapper('jitsi:generateAccessToken', rid);
|
||||
const accessToken = await this.methodCallWrapper('jitsi:generateAccessToken', room?.rid);
|
||||
queryString = `?jwt=${ accessToken }`;
|
||||
} catch {
|
||||
logEvent(events.RA_JITSI_F);
|
||||
}
|
||||
}
|
||||
|
||||
return `${ protocol }${ domain }${ prefix }${ uniqueIdentifier }${ rid }${ queryString }`;
|
||||
let rname;
|
||||
if (Jitsi_URL_Room_Hash) {
|
||||
rname = uniqueID + room?.rid;
|
||||
} else {
|
||||
rname = encodeURIComponent(room.t === 'd' ? room?.usernames?.join?.(' x ') : room?.name);
|
||||
}
|
||||
|
||||
return `${ protocol }${ domain }${ prefix }${ rname }${ queryString }`;
|
||||
}
|
||||
|
||||
async function callJitsi(rid, onlyAudio = false) {
|
||||
async function callJitsi(room, onlyAudio = false) {
|
||||
logEvent(onlyAudio ? events.RA_JITSI_AUDIO : events.RA_JITSI_VIDEO);
|
||||
const url = await jitsiURL.call(this, { rid });
|
||||
Navigation.navigate('JitsiMeetView', { url, onlyAudio, rid });
|
||||
const url = await jitsiURL.call(this, { room });
|
||||
Navigation.navigate('JitsiMeetView', { url, onlyAudio, rid: room?.rid });
|
||||
}
|
||||
|
||||
export default callJitsi;
|
||||
|
|
|
@ -57,7 +57,7 @@ async function open({ type, rid, name }) {
|
|||
export default async function canOpenRoom({ rid, path, isCall }) {
|
||||
try {
|
||||
const db = database.active;
|
||||
const subsCollection = db.collections.get('subscriptions');
|
||||
const subsCollection = db.get('subscriptions');
|
||||
|
||||
if (isCall && !rid) {
|
||||
// Extract rid from a Jitsi URL
|
||||
|
@ -75,7 +75,8 @@ export default async function canOpenRoom({ rid, path, isCall }) {
|
|||
name: room.name,
|
||||
fname: room.fname,
|
||||
prid: room.prid,
|
||||
uids: room.uids
|
||||
uids: room.uids,
|
||||
usernames: room.usernames
|
||||
};
|
||||
} catch (e) {
|
||||
// Do nothing
|
||||
|
|
|
@ -13,7 +13,7 @@ export async function setEnterpriseModules() {
|
|||
try {
|
||||
const { server: serverId } = reduxStore.getState().server;
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.collections.get('servers');
|
||||
const serversCollection = serversDB.get('servers');
|
||||
let server;
|
||||
try {
|
||||
server = await serversCollection.find(serverId);
|
||||
|
@ -39,7 +39,7 @@ export function getEnterpriseModules() {
|
|||
const enterpriseModules = await this.methodCallWrapper('license:getModules');
|
||||
if (enterpriseModules) {
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.collections.get('servers');
|
||||
const serversCollection = serversDB.get('servers');
|
||||
const server = await serversCollection.find(serverId);
|
||||
await serversDB.action(async() => {
|
||||
await server.update((s) => {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { InteractionManager } from 'react-native';
|
||||
import lt from 'semver/functions/lt';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||
|
@ -21,7 +20,7 @@ const updateEmojis = async({ update = [], remove = [], allRecords }) => {
|
|||
return;
|
||||
}
|
||||
const db = database.active;
|
||||
const emojisCollection = db.collections.get('custom_emojis');
|
||||
const emojisCollection = db.get('custom_emojis');
|
||||
let emojisToCreate = [];
|
||||
let emojisToUpdate = [];
|
||||
let emojisToDelete = [];
|
||||
|
@ -63,7 +62,7 @@ const updateEmojis = async({ update = [], remove = [], allRecords }) => {
|
|||
|
||||
export async function setCustomEmojis() {
|
||||
const db = database.active;
|
||||
const emojisCollection = db.collections.get('custom_emojis');
|
||||
const emojisCollection = db.get('custom_emojis');
|
||||
const allEmojis = await emojisCollection.query().fetch();
|
||||
const parsed = allEmojis.reduce((ret, item) => {
|
||||
ret[item.name] = {
|
||||
|
@ -86,7 +85,7 @@ export function getCustomEmojis() {
|
|||
try {
|
||||
const serverVersion = reduxStore.getState().server.version;
|
||||
const db = database.active;
|
||||
const emojisCollection = db.collections.get('custom_emojis');
|
||||
const emojisCollection = db.get('custom_emojis');
|
||||
const allRecords = await emojisCollection.query().fetch();
|
||||
const updatedSince = await getUpdatedSince(allRecords);
|
||||
|
||||
|
@ -95,7 +94,6 @@ export function getCustomEmojis() {
|
|||
// RC 0.61.0
|
||||
const result = await this.sdk.get('emoji-custom');
|
||||
|
||||
InteractionManager.runAfterInteractions(async() => {
|
||||
let { emojis } = result;
|
||||
emojis = emojis.filter(emoji => !updatedSince || emoji._updatedAt > updatedSince);
|
||||
const changedEmojis = await updateEmojis({ update: emojis, allRecords });
|
||||
|
@ -106,7 +104,6 @@ export function getCustomEmojis() {
|
|||
setCustomEmojis();
|
||||
}
|
||||
return resolve();
|
||||
});
|
||||
} else {
|
||||
const params = {};
|
||||
if (updatedSince) {
|
||||
|
@ -120,7 +117,6 @@ export function getCustomEmojis() {
|
|||
return resolve();
|
||||
}
|
||||
|
||||
InteractionManager.runAfterInteractions(async() => {
|
||||
const { emojis } = result;
|
||||
const { update, remove } = emojis;
|
||||
const changedEmojis = await updateEmojis({ update, remove, allRecords });
|
||||
|
@ -130,7 +126,6 @@ export function getCustomEmojis() {
|
|||
if (changedEmojis) {
|
||||
setCustomEmojis();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
|
|
|
@ -1,12 +1,55 @@
|
|||
import { InteractionManager } from 'react-native';
|
||||
import lt from 'semver/functions/lt';
|
||||
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import coerce from 'semver/functions/coerce';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
|
||||
import database from '../database';
|
||||
import log from '../../utils/log';
|
||||
import reduxStore from '../createStore';
|
||||
import protectedFunction from './helpers/protectedFunction';
|
||||
import { setPermissions as setPermissionsAction } from '../../actions/permissions';
|
||||
|
||||
const PERMISSIONS = [
|
||||
'add-user-to-any-c-room',
|
||||
'add-user-to-any-p-room',
|
||||
'add-user-to-joined-room',
|
||||
'archive-room',
|
||||
'auto-translate',
|
||||
'create-invite-links',
|
||||
'delete-c',
|
||||
'delete-message',
|
||||
'delete-p',
|
||||
'edit-message',
|
||||
'edit-room',
|
||||
'force-delete-message',
|
||||
'mute-user',
|
||||
'pin-message',
|
||||
'post-readonly',
|
||||
'remove-user',
|
||||
'set-leader',
|
||||
'set-moderator',
|
||||
'set-owner',
|
||||
'set-react-when-readonly',
|
||||
'set-readonly',
|
||||
'toggle-room-e2e-encryption',
|
||||
'transfer-livechat-guest',
|
||||
'unarchive-room',
|
||||
'view-broadcast-member-list',
|
||||
'view-privileged-setting',
|
||||
'view-room-administration',
|
||||
'view-statistics',
|
||||
'view-user-administration'
|
||||
];
|
||||
|
||||
export async function setPermissions() {
|
||||
const db = database.active;
|
||||
const permissionsCollection = db.collections.get('permissions');
|
||||
const allPermissions = await permissionsCollection.query(Q.where('id', Q.oneOf(PERMISSIONS))).fetch();
|
||||
const parsed = allPermissions.reduce((acc, item) => ({ ...acc, [item.id]: item.roles }), {});
|
||||
|
||||
reduxStore.dispatch(setPermissionsAction(parsed));
|
||||
}
|
||||
|
||||
const getUpdatedSince = (allRecords) => {
|
||||
try {
|
||||
|
@ -26,7 +69,7 @@ const updatePermissions = async({ update = [], remove = [], allRecords }) => {
|
|||
return;
|
||||
}
|
||||
const db = database.active;
|
||||
const permissionsCollection = db.collections.get('permissions');
|
||||
const permissionsCollection = db.get('permissions');
|
||||
|
||||
// filter permissions
|
||||
let permissionsToCreate = [];
|
||||
|
@ -65,33 +108,35 @@ const updatePermissions = async({ update = [], remove = [], allRecords }) => {
|
|||
await db.action(async() => {
|
||||
await db.batch(...batch);
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
export default function() {
|
||||
export function getPermissions() {
|
||||
return new Promise(async(resolve) => {
|
||||
try {
|
||||
const serverVersion = reduxStore.getState().server.version;
|
||||
const db = database.active;
|
||||
const permissionsCollection = db.collections.get('permissions');
|
||||
const permissionsCollection = db.get('permissions');
|
||||
const allRecords = await permissionsCollection.query().fetch();
|
||||
|
||||
// if server version is lower than 0.73.0, fetches from old api
|
||||
if (serverVersion && lt(serverVersion, '0.73.0')) {
|
||||
if (serverVersion && lt(coerce(serverVersion), '0.73.0')) {
|
||||
// RC 0.66.0
|
||||
const result = await this.sdk.get('permissions.list');
|
||||
if (!result.success) {
|
||||
return resolve();
|
||||
}
|
||||
InteractionManager.runAfterInteractions(async() => {
|
||||
await updatePermissions({ update: result.permissions, allRecords });
|
||||
const changePermissions = await updatePermissions({ update: result.permissions, allRecords });
|
||||
if (changePermissions) {
|
||||
setPermissions();
|
||||
}
|
||||
return resolve();
|
||||
});
|
||||
} else {
|
||||
const params = {};
|
||||
const updatedSince = await getUpdatedSince(allRecords);
|
||||
const updatedSince = getUpdatedSince(allRecords);
|
||||
if (updatedSince) {
|
||||
params.updatedSince = updatedSince;
|
||||
}
|
||||
|
@ -102,10 +147,11 @@ export default function() {
|
|||
return resolve();
|
||||
}
|
||||
|
||||
InteractionManager.runAfterInteractions(async() => {
|
||||
await updatePermissions({ update: result.update, remove: result.delete, allRecords });
|
||||
const changePermissions = await updatePermissions({ update: result.update, remove: result.delete, allRecords });
|
||||
if (changePermissions) {
|
||||
setPermissions();
|
||||
}
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { InteractionManager } from 'react-native';
|
||||
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||
|
||||
import database from '../database';
|
||||
|
@ -19,9 +18,8 @@ export default function() {
|
|||
const { roles } = result;
|
||||
|
||||
if (roles && roles.length) {
|
||||
InteractionManager.runAfterInteractions(async() => {
|
||||
await db.action(async() => {
|
||||
const rolesCollections = db.collections.get('roles');
|
||||
const rolesCollections = db.get('roles');
|
||||
const allRolesRecords = await rolesCollections.query().fetch();
|
||||
|
||||
// filter roles
|
||||
|
@ -55,7 +53,6 @@ export default function() {
|
|||
return allRecords.length;
|
||||
});
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
|
|
|
@ -44,7 +44,7 @@ const loginSettings = [
|
|||
const serverInfoUpdate = async(serverInfo, iconSetting) => {
|
||||
const serversDB = database.servers;
|
||||
const serverId = reduxStore.getState().server.server;
|
||||
const serversCollection = serversDB.collections.get('servers');
|
||||
const serversCollection = serversDB.get('servers');
|
||||
const server = await serversCollection.find(serverId);
|
||||
|
||||
let info = serverInfo.reduce((allSettings, setting) => {
|
||||
|
@ -118,7 +118,7 @@ export async function getLoginSettings({ server }) {
|
|||
|
||||
export async function setSettings() {
|
||||
const db = database.active;
|
||||
const settingsCollection = db.collections.get('settings');
|
||||
const settingsCollection = db.get('settings');
|
||||
const settingsRecords = await settingsCollection.query().fetch();
|
||||
const parsed = Object.values(settingsRecords).map(item => ({
|
||||
_id: item.id,
|
||||
|
@ -157,7 +157,7 @@ export default async function() {
|
|||
}
|
||||
|
||||
await db.action(async() => {
|
||||
const settingsCollection = db.collections.get('settings');
|
||||
const settingsCollection = db.get('settings');
|
||||
const allSettingsRecords = await settingsCollection
|
||||
.query(Q.where('id', Q.oneOf(filteredSettingsIds)))
|
||||
.fetch();
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { InteractionManager } from 'react-native';
|
||||
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||
|
||||
import database from '../database';
|
||||
|
@ -20,9 +19,8 @@ export default function() {
|
|||
const { commands } = result;
|
||||
|
||||
if (commands && commands.length) {
|
||||
InteractionManager.runAfterInteractions(async() => {
|
||||
await db.action(async() => {
|
||||
const slashCommandsCollection = db.collections.get('slash_commands');
|
||||
const slashCommandsCollection = db.get('slash_commands');
|
||||
const allSlashCommandsRecords = await slashCommandsCollection.query().fetch();
|
||||
|
||||
// filter slash commands
|
||||
|
@ -61,7 +59,6 @@ export default function() {
|
|||
}
|
||||
return allRecords.length;
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
|
|
|
@ -72,7 +72,7 @@ export default async function getUsersPresence() {
|
|||
ids = [];
|
||||
|
||||
const db = database.active;
|
||||
const userCollection = db.collections.get('users');
|
||||
const userCollection = db.get('users');
|
||||
users.forEach(async(user) => {
|
||||
try {
|
||||
const userRecord = await userCollection.find(user._id);
|
||||
|
|
|
@ -5,7 +5,7 @@ import database from '../../database';
|
|||
export default async(subscriptions = [], rooms = []) => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const subCollection = db.get('subscriptions');
|
||||
|
||||
const roomIds = rooms.filter(r => !subscriptions.find(s => s.rid === r._id)).map(r => r._id);
|
||||
let existingSubs = await subCollection.query(Q.where('rid', Q.oneOf(roomIds))).fetch();
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import EJSON from 'ejson';
|
||||
import { lt, coerce } from 'semver';
|
||||
|
||||
import normalizeMessage from './normalizeMessage';
|
||||
import findSubscriptionsRooms from './findSubscriptionsRooms';
|
||||
import { Encryption } from '../../encryption';
|
||||
import reduxStore from '../../createStore';
|
||||
// TODO: delete and update
|
||||
|
||||
export const merge = (subscription, room) => {
|
||||
const serverVersion = reduxStore.getState().server.version;
|
||||
subscription = EJSON.fromJSONValue(subscription);
|
||||
room = EJSON.fromJSONValue(room);
|
||||
|
||||
|
@ -25,9 +28,15 @@ export const merge = (subscription, room) => {
|
|||
subscription.usernames = room.usernames;
|
||||
subscription.uids = room.uids;
|
||||
}
|
||||
if (serverVersion && lt(coerce(serverVersion), '3.7.0')) {
|
||||
const updatedAt = room?._updatedAt ? new Date(room._updatedAt) : null;
|
||||
const lastMessageTs = subscription?.lastMessage?.ts ? new Date(subscription.lastMessage.ts) : null;
|
||||
subscription.roomUpdatedAt = Math.max(updatedAt, lastMessageTs);
|
||||
} else {
|
||||
// https://github.com/RocketChat/Rocket.Chat/blob/develop/app/ui-sidenav/client/roomList.js#L180
|
||||
const lastRoomUpdate = room.lm || subscription.ts || subscription._updatedAt;
|
||||
subscription.roomUpdatedAt = subscription.lr ? Math.max(new Date(subscription.lr), new Date(lastRoomUpdate)) : lastRoomUpdate;
|
||||
}
|
||||
subscription.ro = room.ro;
|
||||
subscription.broadcast = room.broadcast;
|
||||
subscription.encrypted = room.encrypted;
|
||||
|
|
|
@ -5,7 +5,7 @@ import updateMessages from './updateMessages';
|
|||
const getLastUpdate = async(rid) => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const subsCollection = db.collections.get('subscriptions');
|
||||
const subsCollection = db.get('subscriptions');
|
||||
const sub = await subsCollection.find(rid);
|
||||
return sub.lastOpen.toISOString();
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { InteractionManager } from 'react-native';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||
|
||||
|
@ -30,12 +29,11 @@ export default function loadThreadMessages({ tmid, rid, offset = 0 }) {
|
|||
let data = await load.call(this, { tmid, offset });
|
||||
|
||||
if (data && data.length) {
|
||||
InteractionManager.runAfterInteractions(async() => {
|
||||
try {
|
||||
data = data.map(m => buildMessage(m));
|
||||
data = await Encryption.decryptMessages(data);
|
||||
const db = database.active;
|
||||
const threadMessagesCollection = db.collections.get('thread_messages');
|
||||
const threadMessagesCollection = db.get('thread_messages');
|
||||
const allThreadMessagesRecords = await threadMessagesCollection.query(Q.where('rid', tmid)).fetch();
|
||||
let threadMessagesToCreate = data.filter(i1 => !allThreadMessagesRecords.find(i2 => i1._id === i2.id));
|
||||
let threadMessagesToUpdate = allThreadMessagesRecords.filter(i1 => data.find(i2 => i1.id === i2._id));
|
||||
|
@ -67,7 +65,6 @@ export default function loadThreadMessages({ tmid, rid, offset = 0 }) {
|
|||
log(e);
|
||||
}
|
||||
return resolve(data);
|
||||
});
|
||||
} else {
|
||||
return resolve([]);
|
||||
}
|
||||
|
|
|
@ -42,12 +42,12 @@ async function removeServerData({ server }) {
|
|||
const serversDB = database.servers;
|
||||
const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
||||
|
||||
const usersCollection = serversDB.collections.get('users');
|
||||
const usersCollection = serversDB.get('users');
|
||||
if (userId) {
|
||||
const userRecord = await usersCollection.find(userId);
|
||||
batch.push(userRecord.prepareDestroyPermanently());
|
||||
}
|
||||
const serverCollection = serversDB.collections.get('servers');
|
||||
const serverCollection = serversDB.get('servers');
|
||||
const serverRecord = await serverCollection.find(server);
|
||||
batch.push(serverRecord.prepareDestroyPermanently());
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import log from '../../utils/log';
|
|||
export default async function readMessages(rid, ls, updateLastOpen = false) {
|
||||
try {
|
||||
const db = database.active;
|
||||
const subscription = await db.collections.get('subscriptions').find(rid);
|
||||
const subscription = await db.get('subscriptions').find(rid);
|
||||
|
||||
// RC 0.61.0
|
||||
await this.sdk.post('subscriptions.read', { rid });
|
||||
|
|
|
@ -40,7 +40,7 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) {
|
|||
fileInfo.rid = rid;
|
||||
|
||||
const db = database.active;
|
||||
const uploadsCollection = db.collections.get('uploads');
|
||||
const uploadsCollection = db.get('uploads');
|
||||
let uploadRecord;
|
||||
try {
|
||||
uploadRecord = await uploadsCollection.find(fileInfo.path);
|
||||
|
|
|
@ -9,8 +9,8 @@ import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../encryption/constants';
|
|||
|
||||
const changeMessageStatus = async(id, tmid, status, message) => {
|
||||
const db = database.active;
|
||||
const msgCollection = db.collections.get('messages');
|
||||
const threadMessagesCollection = db.collections.get('thread_messages');
|
||||
const msgCollection = db.get('messages');
|
||||
const threadMessagesCollection = db.get('thread_messages');
|
||||
const successBatch = [];
|
||||
const messageRecord = await msgCollection.find(id);
|
||||
successBatch.push(
|
||||
|
@ -89,10 +89,10 @@ export async function resendMessage(message, tmid) {
|
|||
export default async function(rid, msg, tmid, user, tshow) {
|
||||
try {
|
||||
const db = database.active;
|
||||
const subsCollection = db.collections.get('subscriptions');
|
||||
const msgCollection = db.collections.get('messages');
|
||||
const threadCollection = db.collections.get('threads');
|
||||
const threadMessagesCollection = db.collections.get('thread_messages');
|
||||
const subsCollection = db.get('subscriptions');
|
||||
const msgCollection = db.get('messages');
|
||||
const threadCollection = db.get('threads');
|
||||
const threadMessagesCollection = db.get('thread_messages');
|
||||
const messageId = random(17);
|
||||
const batch = [];
|
||||
|
||||
|
@ -151,7 +151,8 @@ export default async function(rid, msg, tmid, user, tshow) {
|
|||
tm.status = messagesStatus.TEMP;
|
||||
tm.u = {
|
||||
_id: user.id || '1',
|
||||
username: user.username
|
||||
username: user.username,
|
||||
name: user.name
|
||||
};
|
||||
tm.t = message.t;
|
||||
if (message.t === E2E_MESSAGE_TYPE) {
|
||||
|
@ -175,7 +176,8 @@ export default async function(rid, msg, tmid, user, tshow) {
|
|||
m.status = messagesStatus.TEMP;
|
||||
m.u = {
|
||||
_id: user.id || '1',
|
||||
username: user.username
|
||||
username: user.username,
|
||||
name: user.name
|
||||
};
|
||||
if (tmid && tMessageRecord) {
|
||||
m.tmid = tmid;
|
||||
|
|
|
@ -90,6 +90,10 @@ export default class RoomSubscription {
|
|||
if (ev === 'typing') {
|
||||
const { user } = reduxStore.getState().login;
|
||||
const { UI_Use_Real_Name } = reduxStore.getState().settings;
|
||||
const { rooms } = reduxStore.getState().room;
|
||||
if (rooms[0] !== _rid) {
|
||||
return;
|
||||
}
|
||||
const [name, typing] = ddpMessage.fields.args;
|
||||
const key = UI_Use_Real_Name ? 'name' : 'username';
|
||||
if (name !== user[key]) {
|
||||
|
@ -105,9 +109,9 @@ export default class RoomSubscription {
|
|||
try {
|
||||
const { _id } = ddpMessage.fields.args[0];
|
||||
const db = database.active;
|
||||
const msgCollection = db.collections.get('messages');
|
||||
const threadsCollection = db.collections.get('threads');
|
||||
const threadMessagesCollection = db.collections.get('thread_messages');
|
||||
const msgCollection = db.get('messages');
|
||||
const threadsCollection = db.get('threads');
|
||||
const threadMessagesCollection = db.get('thread_messages');
|
||||
let deleteMessage;
|
||||
let deleteThread;
|
||||
let deleteThreadMessage;
|
||||
|
@ -159,9 +163,9 @@ export default class RoomSubscription {
|
|||
}
|
||||
|
||||
const db = database.active;
|
||||
const msgCollection = db.collections.get('messages');
|
||||
const threadsCollection = db.collections.get('threads');
|
||||
const threadMessagesCollection = db.collections.get('thread_messages');
|
||||
const msgCollection = db.get('messages');
|
||||
const threadsCollection = db.get('threads');
|
||||
const threadMessagesCollection = db.get('thread_messages');
|
||||
|
||||
// Decrypt the message if necessary
|
||||
message = await Encryption.decryptMessage(message);
|
||||
|
|
|
@ -32,8 +32,8 @@ const WINDOW_TIME = 500;
|
|||
const createOrUpdateSubscription = async(subscription, room) => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const roomsCollection = db.collections.get('rooms');
|
||||
const subCollection = db.get('subscriptions');
|
||||
const roomsCollection = db.get('rooms');
|
||||
|
||||
if (!subscription) {
|
||||
try {
|
||||
|
@ -185,7 +185,7 @@ const createOrUpdateSubscription = async(subscription, room) => {
|
|||
const { rooms } = store.getState().room;
|
||||
if (tmp.lastMessage && !rooms.includes(tmp.rid)) {
|
||||
const lastMessage = buildMessage(tmp.lastMessage);
|
||||
const messagesCollection = db.collections.get('messages');
|
||||
const messagesCollection = db.get('messages');
|
||||
let messageRecord;
|
||||
try {
|
||||
messageRecord = await messagesCollection.find(lastMessage._id);
|
||||
|
@ -281,7 +281,7 @@ export default function subscribeRooms() {
|
|||
if (/subscriptions/.test(ev)) {
|
||||
if (type === 'removed') {
|
||||
try {
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const subCollection = db.get('subscriptions');
|
||||
const sub = await subCollection.find(data.rid);
|
||||
const messages = await sub.messages.fetch();
|
||||
const threads = await sub.threads.fetch();
|
||||
|
@ -335,7 +335,7 @@ export default function subscribeRooms() {
|
|||
}
|
||||
};
|
||||
try {
|
||||
const msgCollection = db.collections.get('messages');
|
||||
const msgCollection = db.get('messages');
|
||||
await db.action(async() => {
|
||||
await msgCollection.create(protectedFunction((m) => {
|
||||
m._raw = sanitizedRaw({ id: message._id }, msgCollection.schema);
|
||||
|
@ -407,7 +407,7 @@ export default function subscribeRooms() {
|
|||
};
|
||||
|
||||
connectedListener = this.sdk.onStreamData('connected', handleConnection);
|
||||
disconnectedListener = this.sdk.onStreamData('close', handleConnection);
|
||||
// disconnectedListener = this.sdk.onStreamData('close', handleConnection);
|
||||
streamListener = this.sdk.onStreamData('stream-notify-user', handleStreamMessageReceived);
|
||||
|
||||
try {
|
||||
|
|
|
@ -16,7 +16,7 @@ export default function updateMessages({ rid, update = [], remove = [] }) {
|
|||
return db.action(async() => {
|
||||
// Decrypt these messages
|
||||
update = await Encryption.decryptMessages(update);
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const subCollection = db.get('subscriptions');
|
||||
let sub;
|
||||
try {
|
||||
sub = await subCollection.find(rid);
|
||||
|
@ -26,9 +26,9 @@ export default function updateMessages({ rid, update = [], remove = [] }) {
|
|||
}
|
||||
|
||||
const messagesIds = [...update.map(m => m._id), ...remove.map(m => m._id)];
|
||||
const msgCollection = db.collections.get('messages');
|
||||
const threadCollection = db.collections.get('threads');
|
||||
const threadMessagesCollection = db.collections.get('thread_messages');
|
||||
const msgCollection = db.get('messages');
|
||||
const threadCollection = db.get('threads');
|
||||
const threadMessagesCollection = db.get('thread_messages');
|
||||
const allMessagesRecords = await msgCollection
|
||||
.query(Q.where('rid', rid), Q.where('id', Q.oneOf(messagesIds)))
|
||||
.fetch();
|
||||
|
|
|
@ -32,7 +32,7 @@ import readMessages from './methods/readMessages';
|
|||
import getSettings, { getLoginSettings, setSettings } from './methods/getSettings';
|
||||
|
||||
import getRooms from './methods/getRooms';
|
||||
import getPermissions from './methods/getPermissions';
|
||||
import { setPermissions, getPermissions } from './methods/getPermissions';
|
||||
import { getCustomEmojis, setCustomEmojis } from './methods/getCustomEmojis';
|
||||
import {
|
||||
getEnterpriseModules, setEnterpriseModules, hasLicense, isOmnichannelModuleAvailable
|
||||
|
@ -70,7 +70,6 @@ const CERTIFICATE_KEY = 'RC_CERTIFICATE_KEY';
|
|||
export const THEME_PREFERENCES_KEY = 'RC_THEME_PREFERENCES_KEY';
|
||||
export const CRASH_REPORT_KEY = 'RC_CRASH_REPORT_KEY';
|
||||
export const ANALYTICS_EVENTS_KEY = 'RC_ANALYTICS_EVENTS_KEY';
|
||||
const returnAnArray = obj => obj || [];
|
||||
const MIN_ROCKETCHAT_VERSION = '0.70.0';
|
||||
|
||||
const STATUSES = ['offline', 'online', 'away', 'busy'];
|
||||
|
@ -178,9 +177,16 @@ const RocketChat = {
|
|||
}
|
||||
this.controller = new AbortController();
|
||||
},
|
||||
checkAndReopen() {
|
||||
return this?.sdk?.checkAndReopen();
|
||||
},
|
||||
connect({ server, user, logoutOnError = false }) {
|
||||
return new Promise((resolve) => {
|
||||
if (!this.sdk || this.sdk.client.host !== server) {
|
||||
if (this?.sdk?.client?.host === server) {
|
||||
return resolve();
|
||||
} else {
|
||||
this.sdk?.disconnect?.();
|
||||
this.sdk = null;
|
||||
database.setActiveDB(server);
|
||||
}
|
||||
reduxStore.dispatch(connectRequest());
|
||||
|
@ -209,11 +215,6 @@ const RocketChat = {
|
|||
|
||||
EventEmitter.emit('INQUIRY_UNSUBSCRIBE');
|
||||
|
||||
if (this.sdk) {
|
||||
this.sdk.disconnect();
|
||||
this.sdk = null;
|
||||
}
|
||||
|
||||
if (this.code) {
|
||||
this.code = null;
|
||||
}
|
||||
|
@ -241,6 +242,10 @@ const RocketChat = {
|
|||
|
||||
sdkConnect();
|
||||
|
||||
this.connectedListener = this.sdk.onStreamData('connecting', () => {
|
||||
reduxStore.dispatch(connectRequest());
|
||||
});
|
||||
|
||||
this.connectedListener = this.sdk.onStreamData('connected', () => {
|
||||
reduxStore.dispatch(connectSuccess());
|
||||
});
|
||||
|
@ -276,7 +281,7 @@ const RocketChat = {
|
|||
} else if (/updateAvatar/.test(eventName)) {
|
||||
const { username, etag } = ddpMessage.fields.args[0];
|
||||
const db = database.active;
|
||||
const userCollection = db.collections.get('users');
|
||||
const userCollection = db.get('users');
|
||||
try {
|
||||
const [userRecord] = await userCollection.query(Q.where('username', Q.eq(username))).fetch();
|
||||
await db.action(async() => {
|
||||
|
@ -290,7 +295,7 @@ const RocketChat = {
|
|||
} else if (/Users:NameChanged/.test(eventName)) {
|
||||
const userNameChanged = ddpMessage.fields.args[0];
|
||||
const db = database.active;
|
||||
const userCollection = db.collections.get('users');
|
||||
const userCollection = db.get('users');
|
||||
try {
|
||||
const userRecord = await userCollection.find(userNameChanged._id);
|
||||
await db.action(async() => {
|
||||
|
@ -334,7 +339,7 @@ const RocketChat = {
|
|||
// set Server
|
||||
const currentServer = { server };
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.collections.get('servers');
|
||||
const serversCollection = serversDB.get('servers');
|
||||
try {
|
||||
const serverRecord = await serversCollection.find(server);
|
||||
currentServer.version = serverRecord.version;
|
||||
|
@ -349,7 +354,7 @@ const RocketChat = {
|
|||
// set Settings
|
||||
const settings = ['Accounts_AvatarBlockUnauthenticatedAccess'];
|
||||
const db = database.active;
|
||||
const settingsCollection = db.collections.get('settings');
|
||||
const settingsCollection = db.get('settings');
|
||||
const settingsRecords = await settingsCollection.query(Q.where('id', Q.oneOf(settings))).fetch();
|
||||
const parsed = Object.values(settingsRecords).map(item => ({
|
||||
_id: item.id,
|
||||
|
@ -363,7 +368,7 @@ const RocketChat = {
|
|||
|
||||
// set User info
|
||||
const userId = await UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
||||
const userCollections = serversDB.collections.get('users');
|
||||
const userCollections = serversDB.get('users');
|
||||
let user = null;
|
||||
if (userId) {
|
||||
const userRecord = await userCollections.find(userId);
|
||||
|
@ -545,7 +550,7 @@ const RocketChat = {
|
|||
try {
|
||||
const serversDB = database.servers;
|
||||
await serversDB.action(async() => {
|
||||
const serverCollection = serversDB.collections.get('servers');
|
||||
const serverCollection = serversDB.get('servers');
|
||||
const serverRecord = await serverCollection.find(server);
|
||||
await serverRecord.update((s) => {
|
||||
s.roomsUpdatedAt = null;
|
||||
|
@ -605,7 +610,7 @@ const RocketChat = {
|
|||
}
|
||||
const db = database.active;
|
||||
const likeString = sanitizeLikeString(searchText);
|
||||
let data = await db.collections.get('subscriptions').query(
|
||||
let data = await db.get('subscriptions').query(
|
||||
Q.or(
|
||||
Q.where('name', Q.like(`%${ likeString }%`)),
|
||||
Q.where('fname', Q.like(`%${ likeString }%`))
|
||||
|
@ -621,19 +626,15 @@ const RocketChat = {
|
|||
|
||||
data = data.slice(0, 7);
|
||||
|
||||
data = data.map((sub) => {
|
||||
if (sub.t !== 'd') {
|
||||
return {
|
||||
data = data.map(sub => ({
|
||||
rid: sub.rid,
|
||||
name: sub.name,
|
||||
fname: sub.fname,
|
||||
avatarETag: sub.avatarETag,
|
||||
t: sub.t,
|
||||
encrypted: sub.encrypted
|
||||
};
|
||||
}
|
||||
return sub;
|
||||
});
|
||||
encrypted: sub.encrypted,
|
||||
lastMessage: sub.lastMessage
|
||||
}));
|
||||
|
||||
return data;
|
||||
},
|
||||
|
@ -740,6 +741,7 @@ const RocketChat = {
|
|||
getLoginSettings,
|
||||
setSettings,
|
||||
getPermissions,
|
||||
setPermissions,
|
||||
getCustomEmojis,
|
||||
setCustomEmojis,
|
||||
getEnterpriseModules,
|
||||
|
@ -796,7 +798,7 @@ const RocketChat = {
|
|||
async getRoom(rid) {
|
||||
try {
|
||||
const db = database.active;
|
||||
const room = await db.collections.get('subscriptions').find(rid);
|
||||
const room = await db.get('subscriptions').find(rid);
|
||||
return Promise.resolve(room);
|
||||
} catch (error) {
|
||||
return Promise.reject(new Error('Room not found'));
|
||||
|
@ -1172,10 +1174,13 @@ const RocketChat = {
|
|||
// RC 0.65.0
|
||||
return this.sdk.get(`${ this.roomTypeToApiType(type) }.roles`, { roomId });
|
||||
},
|
||||
/**
|
||||
* Permissions: array of permissions' roles from redux. Example: [['owner', 'admin'], ['leader']]
|
||||
* Returns an array of boolean for each permission from permissions arg
|
||||
*/
|
||||
async hasPermission(permissions, rid) {
|
||||
const db = database.active;
|
||||
const subsCollection = db.collections.get('subscriptions');
|
||||
const permissionsCollection = db.collections.get('permissions');
|
||||
const subsCollection = db.get('subscriptions');
|
||||
let roomRoles = [];
|
||||
try {
|
||||
// get the room from database
|
||||
|
@ -1184,31 +1189,16 @@ const RocketChat = {
|
|||
roomRoles = room.roles || [];
|
||||
} catch (error) {
|
||||
console.log('hasPermission -> Room not found');
|
||||
return permissions.reduce((result, permission) => {
|
||||
result[permission] = false;
|
||||
return result;
|
||||
}, {});
|
||||
return permissions.map(() => false);
|
||||
}
|
||||
// get permissions from database
|
||||
|
||||
try {
|
||||
const permissionsFiltered = await permissionsCollection.query(Q.where('id', Q.oneOf(permissions))).fetch();
|
||||
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) || [];
|
||||
// merge both roles
|
||||
const mergedRoles = [...new Set([...roomRoles, ...userRoles])];
|
||||
|
||||
// return permissions in object format
|
||||
// e.g. { 'edit-room': true, 'set-readonly': false }
|
||||
return permissions.reduce((result, permission) => {
|
||||
result[permission] = false;
|
||||
const permissionFound = permissionsFiltered.find(p => p.id === permission);
|
||||
if (permissionFound) {
|
||||
result[permission] = returnAnArray(permissionFound.roles).some(r => mergedRoles.includes(r));
|
||||
}
|
||||
return result;
|
||||
}, {});
|
||||
return permissions.map(permission => permission?.some(r => mergedRoles.includes(r) ?? false));
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
|
@ -1438,17 +1428,15 @@ const RocketChat = {
|
|||
query, count, offset, sort
|
||||
});
|
||||
},
|
||||
async canAutoTranslate() {
|
||||
const db = database.active;
|
||||
canAutoTranslate() {
|
||||
try {
|
||||
const AutoTranslate_Enabled = reduxStore.getState().settings && reduxStore.getState().settings.AutoTranslate_Enabled;
|
||||
const { AutoTranslate_Enabled } = reduxStore.getState().settings;
|
||||
if (!AutoTranslate_Enabled) {
|
||||
return false;
|
||||
}
|
||||
const permissionsCollection = db.collections.get('permissions');
|
||||
const autoTranslatePermission = await permissionsCollection.find('auto-translate');
|
||||
const userRoles = (reduxStore.getState().login.user && reduxStore.getState().login.user.roles) || [];
|
||||
return autoTranslatePermission.roles.some(role => userRoles.includes(role));
|
||||
const autoTranslatePermission = reduxStore.getState().permissions['auto-translate'];
|
||||
const userRoles = (reduxStore.getState().login?.user?.roles) ?? [];
|
||||
return autoTranslatePermission?.some(role => userRoles.includes(role));
|
||||
} catch (e) {
|
||||
log(e);
|
||||
return false;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import I18n from '../../i18n';
|
||||
import styles from './styles';
|
||||
|
@ -45,7 +45,7 @@ const formatMsg = ({
|
|||
return `${ prefix }${ lastMessage.msg }`;
|
||||
};
|
||||
|
||||
const arePropsEqual = (oldProps, newProps) => isEqual(oldProps, newProps);
|
||||
const arePropsEqual = (oldProps, newProps) => dequal(oldProps, newProps);
|
||||
|
||||
const LastMessage = React.memo(({
|
||||
lastMessage, type, showLastMessage, username, alert, useRealName, theme
|
||||
|
|
|
@ -18,6 +18,7 @@ import inviteLinks from './inviteLinks';
|
|||
import createDiscussion from './createDiscussion';
|
||||
import enterpriseModules from './enterpriseModules';
|
||||
import encryption from './encryption';
|
||||
import permissions from './permissions';
|
||||
|
||||
import inquiry from '../ee/omnichannel/reducers/inquiry';
|
||||
|
||||
|
@ -41,5 +42,6 @@ export default combineReducers({
|
|||
createDiscussion,
|
||||
inquiry,
|
||||
enterpriseModules,
|
||||
encryption
|
||||
encryption,
|
||||
permissions
|
||||
});
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { PERMISSIONS } from '../actions/actionsTypes';
|
||||
|
||||
const initialState = {
|
||||
permissions: {}
|
||||
};
|
||||
|
||||
export default function permissions(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case PERMISSIONS.SET:
|
||||
return action.permissions;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ const handleRequest = function* handleRequest({ data }) {
|
|||
broadcast,
|
||||
encrypted
|
||||
} = data;
|
||||
logEvent(events.CREATE_CHANNEL_CREATE, {
|
||||
logEvent(events.CR_CREATE, {
|
||||
type: type ? 'private' : 'public',
|
||||
readOnly,
|
||||
broadcast,
|
||||
|
@ -53,7 +53,7 @@ const handleRequest = function* handleRequest({ data }) {
|
|||
|
||||
try {
|
||||
const db = database.active;
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const subCollection = db.get('subscriptions');
|
||||
yield db.action(async() => {
|
||||
await subCollection.create((s) => {
|
||||
s._raw = sanitizedRaw({ id: sub.rid }, subCollection.schema);
|
||||
|
@ -66,7 +66,7 @@ const handleRequest = function* handleRequest({ data }) {
|
|||
|
||||
yield put(createChannelSuccess(sub));
|
||||
} catch (err) {
|
||||
logEvent(events[data.group ? 'SELECTED_USERS_CREATE_GROUP_F' : 'CREATE_CHANNEL_CREATE_F']);
|
||||
logEvent(events[data.group ? 'SELECTED_USERS_CREATE_GROUP_F' : 'CR_CREATE_F']);
|
||||
yield put(createChannelFailure(err));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ const create = function* create(data) {
|
|||
};
|
||||
|
||||
const handleRequest = function* handleRequest({ data }) {
|
||||
logEvent(events.CREATE_DISCUSSION_CREATE);
|
||||
logEvent(events.CD_CREATE);
|
||||
try {
|
||||
const auth = yield select(state => state.login.isAuthenticated);
|
||||
if (!auth) {
|
||||
|
@ -27,7 +27,7 @@ const handleRequest = function* handleRequest({ data }) {
|
|||
|
||||
try {
|
||||
const db = database.active;
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const subCollection = db.get('subscriptions');
|
||||
yield db.action(async() => {
|
||||
await subCollection.create((s) => {
|
||||
s._raw = sanitizedRaw({ id: sub.rid }, subCollection.schema);
|
||||
|
@ -39,11 +39,11 @@ const handleRequest = function* handleRequest({ data }) {
|
|||
}
|
||||
yield put(createDiscussionSuccess(sub));
|
||||
} else {
|
||||
logEvent(events.CREATE_DISCUSSION_CREATE_F);
|
||||
logEvent(events.CD_CREATE_F);
|
||||
yield put(createDiscussionFailure(result));
|
||||
}
|
||||
} catch (err) {
|
||||
logEvent(events.CREATE_DISCUSSION_CREATE_F);
|
||||
logEvent(events.CD_CREATE_F);
|
||||
yield put(createDiscussionFailure(err));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -31,6 +31,14 @@ const handleInviteLink = function* handleInviteLink({ params, requireLogin = fal
|
|||
}
|
||||
};
|
||||
|
||||
const popToRoot = function popToRoot({ isMasterDetail }) {
|
||||
if (isMasterDetail) {
|
||||
Navigation.navigate('DrawerNavigator');
|
||||
} else {
|
||||
Navigation.navigate('RoomsListView');
|
||||
}
|
||||
};
|
||||
|
||||
const navigate = function* navigate({ params }) {
|
||||
yield put(appStart({ root: ROOT_INSIDE }));
|
||||
if (params.path) {
|
||||
|
@ -38,22 +46,31 @@ const navigate = function* navigate({ params }) {
|
|||
if (type !== 'invite') {
|
||||
const room = yield RocketChat.canOpenRoom(params);
|
||||
if (room) {
|
||||
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
||||
if (isMasterDetail) {
|
||||
Navigation.navigate('DrawerNavigator');
|
||||
} else {
|
||||
Navigation.navigate('RoomsListView');
|
||||
}
|
||||
const item = {
|
||||
name,
|
||||
t: roomTypes[type],
|
||||
roomUserId: RocketChat.getUidDirectMessage(room),
|
||||
...room
|
||||
};
|
||||
|
||||
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
||||
const focusedRooms = yield select(state => state.room.rooms);
|
||||
|
||||
if (focusedRooms.includes(room.rid)) {
|
||||
// if there's one room on the list or last room is the one
|
||||
if (focusedRooms.length === 1 || focusedRooms[0] === room.rid) {
|
||||
yield goRoom({ item, isMasterDetail });
|
||||
} else {
|
||||
popToRoot({ isMasterDetail });
|
||||
yield goRoom({ item, isMasterDetail });
|
||||
}
|
||||
} else {
|
||||
popToRoot({ isMasterDetail });
|
||||
yield goRoom({ item, isMasterDetail });
|
||||
}
|
||||
|
||||
if (params.isCall) {
|
||||
RocketChat.callJitsi(item.rid);
|
||||
RocketChat.callJitsi(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -72,7 +89,7 @@ const fallbackNavigation = function* fallbackNavigation() {
|
|||
|
||||
const handleOpen = function* handleOpen({ params }) {
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.collections.get('servers');
|
||||
const serversCollection = serversDB.get('servers');
|
||||
|
||||
let { host } = params;
|
||||
if (params.isCall && !host) {
|
||||
|
@ -121,10 +138,10 @@ const handleOpen = function* handleOpen({ params }) {
|
|||
} else {
|
||||
// search if deep link's server already exists
|
||||
try {
|
||||
const servers = yield serversCollection.find(host);
|
||||
if (servers && user) {
|
||||
const hostServerRecord = yield serversCollection.find(host);
|
||||
if (hostServerRecord && user) {
|
||||
yield localAuthenticate(host);
|
||||
yield put(selectServerRequest(host));
|
||||
yield put(selectServerRequest(host, hostServerRecord.version, true, true));
|
||||
yield take(types.LOGIN.SUCCESS);
|
||||
yield navigate({ params });
|
||||
return;
|
||||
|
|
|
@ -30,7 +30,7 @@ const handleEncryptionInit = function* handleEncryptionInit() {
|
|||
|
||||
// Fetch server info to check E2E enable
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.collections.get('servers');
|
||||
const serversCollection = serversDB.get('servers');
|
||||
let serverInfo;
|
||||
try {
|
||||
serverInfo = yield serversCollection.find(server);
|
||||
|
|
|
@ -42,7 +42,7 @@ const restore = function* restore() {
|
|||
// yield put(appStart({ root: ROOT_OUTSIDE }));
|
||||
} else {
|
||||
const serversDB = database.servers;
|
||||
const serverCollections = serversDB.collections.get('servers');
|
||||
const serverCollections = serversDB.get('servers');
|
||||
|
||||
let serverObj;
|
||||
try {
|
||||
|
|
|
@ -60,7 +60,7 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE
|
|||
|
||||
// Saves username on server history
|
||||
const serversDB = database.servers;
|
||||
const serversHistoryCollection = serversDB.collections.get('servers_history');
|
||||
const serversHistoryCollection = serversDB.get('servers_history');
|
||||
yield serversDB.action(async() => {
|
||||
try {
|
||||
const serversHistory = await serversHistoryCollection.query(Q.where('url', server)).fetch();
|
||||
|
@ -148,7 +148,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
|
|||
moment.locale(toMomentLocale(user.language));
|
||||
|
||||
const serversDB = database.servers;
|
||||
const usersCollection = serversDB.collections.get('users');
|
||||
const usersCollection = serversDB.get('users');
|
||||
const u = {
|
||||
token: user.token,
|
||||
username: user.username,
|
||||
|
@ -224,10 +224,9 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
|
|||
// EventEmitter.emit('NewServer', { server });
|
||||
yield put(serverRequest(appConfig.server));
|
||||
} else {
|
||||
yield put(serverRequest(appConfig.server));
|
||||
// const serversDB = database.servers;
|
||||
// // all servers
|
||||
// const serversCollection = serversDB.collections.get('servers');
|
||||
// const serversCollection = serversDB.get('servers');
|
||||
// const servers = yield serversCollection.query().fetch();
|
||||
|
||||
// // see if there're other logged in servers and selects first one
|
||||
|
@ -241,8 +240,8 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
|
|||
// }
|
||||
// }
|
||||
// }
|
||||
// // if there's no servers, go outside
|
||||
// yield put(appStart({ root: ROOT_OUTSIDE }));
|
||||
// if there's no servers, go outside
|
||||
yield put(appStart({ root: ROOT_OUTSIDE }));
|
||||
}
|
||||
} catch (e) {
|
||||
yield put(appStart({ root: ROOT_OUTSIDE }));
|
||||
|
|
|
@ -12,7 +12,7 @@ const handleReplyBroadcast = function* handleReplyBroadcast({ message }) {
|
|||
try {
|
||||
const db = database.active;
|
||||
const { username } = message.u;
|
||||
const subsCollection = db.collections.get('subscriptions');
|
||||
const subsCollection = db.get('subscriptions');
|
||||
const subscriptions = yield subsCollection.query(Q.where('name', username)).fetch();
|
||||
|
||||
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
||||
|
|
|
@ -15,7 +15,7 @@ import protectedFunction from '../lib/methods/helpers/protectedFunction';
|
|||
|
||||
const updateRooms = function* updateRooms({ server, newRoomsUpdatedAt }) {
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.collections.get('servers');
|
||||
const serversCollection = serversDB.get('servers');
|
||||
try {
|
||||
const serverRecord = yield serversCollection.find(server);
|
||||
|
||||
|
@ -39,7 +39,7 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) {
|
|||
if (params.allData) {
|
||||
yield put(roomsRefresh());
|
||||
} else {
|
||||
const serversCollection = serversDB.collections.get('servers');
|
||||
const serversCollection = serversDB.get('servers');
|
||||
try {
|
||||
const serverRecord = yield serversCollection.find(server);
|
||||
({ roomsUpdatedAt } = serverRecord);
|
||||
|
@ -51,8 +51,8 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) {
|
|||
const { subscriptions } = yield mergeSubscriptionsRooms(subscriptionsResult, roomsResult);
|
||||
|
||||
const db = database.active;
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const messagesCollection = db.collections.get('messages');
|
||||
const subCollection = db.get('subscriptions');
|
||||
const messagesCollection = db.get('messages');
|
||||
|
||||
const subsIds = subscriptions.map(sub => sub.rid).concat(roomsResult.remove.map(room => room._id));
|
||||
if (subsIds.length) {
|
||||
|
|
|
@ -46,7 +46,7 @@ const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
|
|||
}
|
||||
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.collections.get('servers');
|
||||
const serversCollection = serversDB.get('servers');
|
||||
yield serversDB.action(async() => {
|
||||
try {
|
||||
const serverRecord = await serversCollection.find(server);
|
||||
|
@ -78,7 +78,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
|
|||
const serversDB = database.servers;
|
||||
yield UserPreferences.setStringAsync(RocketChat.CURRENT_SERVER, server);
|
||||
const userId = yield UserPreferences.getStringAsync(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
||||
const userCollections = serversDB.collections.get('users');
|
||||
const userCollections = serversDB.get('users');
|
||||
let user = null;
|
||||
if (userId) {
|
||||
try {
|
||||
|
@ -124,6 +124,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
|
|||
// and block the selectServerSuccess raising multiples errors
|
||||
RocketChat.setSettings();
|
||||
RocketChat.setCustomEmojis();
|
||||
RocketChat.setPermissions();
|
||||
RocketChat.setEnterpriseModules();
|
||||
|
||||
let serverInfo;
|
||||
|
@ -151,7 +152,7 @@ const handleServerRequest = function* handleServerRequest({ server, username, fr
|
|||
|
||||
const serverInfo = yield getServerInfo({ server });
|
||||
const serversDB = database.servers;
|
||||
const serversHistoryCollection = serversDB.collections.get('servers_history');
|
||||
const serversHistoryCollection = serversDB.get('servers_history');
|
||||
|
||||
if (serverInfo) {
|
||||
yield RocketChat.getLoginServices(server);
|
||||
|
|
|
@ -12,13 +12,14 @@ const appHasComeBackToForeground = function* appHasComeBackToForeground() {
|
|||
if (appRoot === ROOT_OUTSIDE) {
|
||||
return;
|
||||
}
|
||||
const auth = yield select(state => state.login.isAuthenticated);
|
||||
if (!auth) {
|
||||
const login = yield select(state => state.login);
|
||||
const server = yield select(state => state.server);
|
||||
if (!login.isAuthenticated || login.isFetching || server.connecting || server.loading || server.changingServer) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const server = yield select(state => state.server.server);
|
||||
yield localAuthenticate(server);
|
||||
yield localAuthenticate(server.server);
|
||||
RocketChat.checkAndReopen();
|
||||
setBadgeCount();
|
||||
return yield RocketChat.setUserPresenceOnline();
|
||||
} catch (e) {
|
||||
|
@ -31,14 +32,6 @@ const appHasComeBackToBackground = function* appHasComeBackToBackground() {
|
|||
if (appRoot === ROOT_OUTSIDE) {
|
||||
return;
|
||||
}
|
||||
const auth = yield select(state => state.login.isAuthenticated);
|
||||
if (!auth) {
|
||||
return;
|
||||
}
|
||||
const localAuthenticated = yield select(state => state.login.isLocalAuthenticated);
|
||||
if (!localAuthenticated) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const server = yield select(state => state.server.server);
|
||||
yield saveLastLocalAuthenticationSession(server);
|
||||
|
|
|
@ -31,6 +31,7 @@ import PickerView from '../views/PickerView';
|
|||
import ThreadMessagesView from '../views/ThreadMessagesView';
|
||||
import MarkdownTableView from '../views/MarkdownTableView';
|
||||
import ReadReceiptsView from '../views/ReadReceiptView';
|
||||
import { themes } from '../constants/colors';
|
||||
|
||||
// Profile Stack
|
||||
import ProfileView from '../views/ProfileView';
|
||||
|
@ -280,19 +281,24 @@ const AdminPanelStackNavigator = () => {
|
|||
|
||||
// DrawerNavigator
|
||||
const Drawer = createDrawerNavigator();
|
||||
const DrawerNavigator = () => (
|
||||
const DrawerNavigator = () => {
|
||||
const { theme } = React.useContext(ThemeContext);
|
||||
|
||||
return (
|
||||
<Drawer.Navigator
|
||||
drawerContent={({ navigation, state }) => <Sidebar navigation={navigation} state={state} />}
|
||||
drawerPosition={I18nManager.isRTL ? 'right' : 'left'}
|
||||
screenOptions={{ swipeEnabled: false }}
|
||||
drawerType='back'
|
||||
overlayColor={`rgba(0,0,0,${ themes[theme].backdropOpacity })`}
|
||||
>
|
||||
<Drawer.Screen name='ChatsStackNavigator' component={ChatsStackNavigator} />
|
||||
<Drawer.Screen name='ProfileStackNavigator' component={ProfileStackNavigator} />
|
||||
<Drawer.Screen name='SettingsStackNavigator' component={SettingsStackNavigator} />
|
||||
<Drawer.Screen name='AdminPanelStackNavigator' component={AdminPanelStackNavigator} />
|
||||
</Drawer.Navigator>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
// NewMessageStackNavigator
|
||||
const NewMessageStack = createStackNavigator();
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import RocketChat from '../lib/rocketchat';
|
||||
import reduxStore from '../lib/createStore';
|
||||
|
||||
const canPost = async({ rid }) => {
|
||||
try {
|
||||
const permission = await RocketChat.hasPermission(['post-readonly'], rid);
|
||||
return permission && permission['post-readonly'];
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
return false;
|
||||
const canPostReadOnly = async({ rid }) => {
|
||||
// TODO: this is not reactive. If this permission changes, the component won't be updated
|
||||
const postReadOnlyPermission = reduxStore.getState().permissions['post-readonly'];
|
||||
const permission = await RocketChat.hasPermission([postReadOnlyPermission], rid);
|
||||
return permission[0];
|
||||
};
|
||||
|
||||
const isMuted = (room, user) => room && room.muted && room.muted.find && !!room.muted.find(m => m === user.username);
|
||||
|
@ -20,7 +18,7 @@ export const isReadOnly = async(room, user) => {
|
|||
return true;
|
||||
}
|
||||
if (room?.ro) {
|
||||
const allowPost = await canPost(room);
|
||||
const allowPost = await canPostReadOnly(room);
|
||||
if (allowPost) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import { setLocalAuthenticated } from '../actions/login';
|
|||
|
||||
export const saveLastLocalAuthenticationSession = async(server, serverRecord) => {
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.collections.get('servers');
|
||||
const serversCollection = serversDB.get('servers');
|
||||
await serversDB.action(async() => {
|
||||
try {
|
||||
if (!serverRecord) {
|
||||
|
@ -91,7 +91,7 @@ export const checkHasPasscode = async({ force = true, serverRecord }) => {
|
|||
|
||||
export const localAuthenticate = async(server) => {
|
||||
const serversDB = database.servers;
|
||||
const serversCollection = serversDB.collections.get('servers');
|
||||
const serversCollection = serversDB.get('servers');
|
||||
|
||||
let serverRecord;
|
||||
try {
|
||||
|
@ -102,9 +102,6 @@ export const localAuthenticate = async(server) => {
|
|||
|
||||
// if screen lock is enabled
|
||||
if (serverRecord?.autoLock) {
|
||||
// set isLocalAuthenticated to false
|
||||
store.dispatch(setLocalAuthenticated(false));
|
||||
|
||||
// Make sure splash screen has been hidden
|
||||
RNBootSplash.hide();
|
||||
|
||||
|
@ -118,6 +115,9 @@ export const localAuthenticate = async(server) => {
|
|||
|
||||
// if last authenticated session is older than configured auto lock time, authentication is required
|
||||
if (diffToLastSession >= serverRecord?.autoLockTime) {
|
||||
// set isLocalAuthenticated to false
|
||||
store.dispatch(setLocalAuthenticated(false));
|
||||
|
||||
let hasBiometry = false;
|
||||
|
||||
// if biometry is enabled on the app
|
||||
|
|
|
@ -5,9 +5,8 @@ export default {
|
|||
ONBOARD_CREATE_NEW_WORKSPACE_F: 'onboard_create_new_workspace_f',
|
||||
|
||||
// NEW SERVER VIEW
|
||||
NEWSERVER_CONNECT_TO_WORKSPACE: 'newserver_connect_to_workspace',
|
||||
NEWSERVER_CONNECT_TO_WORKSPACE_F: 'newserver_connect_to_workspace_f',
|
||||
NEWSERVER_JOIN_OPEN_WORKSPACE: 'newserver_join_open_workspace',
|
||||
NS_CONNECT_TO_WORKSPACE: 'ns_connect_to_workspace',
|
||||
NS_JOIN_OPEN_WORKSPACE: 'ns_join_open_workspace',
|
||||
|
||||
// LOGIN VIEW
|
||||
LOGIN_DEFAULT_LOGIN: 'login_default_login',
|
||||
|
@ -99,20 +98,20 @@ export default {
|
|||
SELECTED_USERS_CREATE_GROUP_F: 'selected_users_create_group_f',
|
||||
|
||||
// CREATE CHANNEL VIEW
|
||||
CREATE_CHANNEL_CREATE: 'create_channel_create',
|
||||
CREATE_CHANNEL_CREATE_F: 'create_channel_create_f',
|
||||
CREATE_CHANNEL_TOGGLE_TYPE: 'create_channel_toggle_type',
|
||||
CREATE_CHANNEL_TOGGLE_READ_ONLY: 'create_channel_toggle_read_only',
|
||||
CREATE_CHANNEL_TOGGLE_BROADCAST: 'create_channel_toggle_broadcast',
|
||||
CREATE_CHANNEL_TOGGLE_ENCRYPTED: 'create_channel_toggle_encrypted',
|
||||
CREATE_CHANNEL_REMOVE_USER: 'create_channel_remove_user',
|
||||
CR_CREATE: 'cr_create',
|
||||
CR_CREATE_F: 'cr_create_f',
|
||||
CR_TOGGLE_TYPE: 'cr_toggle_type',
|
||||
CR_TOGGLE_READ_ONLY: 'cr_toggle_read_only',
|
||||
CR_TOGGLE_BROADCAST: 'cr_toggle_broadcast',
|
||||
CR_TOGGLE_ENCRYPTED: 'cr_toggle_encrypted',
|
||||
CR_REMOVE_USER: 'cr_remove_user',
|
||||
|
||||
// CREATE DISCUSSION VIEW
|
||||
CREATE_DISCUSSION_CREATE: 'create_discussion_create',
|
||||
CREATE_DISCUSSION_CREATE_F: 'create_discussion_create_f',
|
||||
CREATE_DISCUSSION_SELECT_CHANNEL: 'create_discussion_select_channel',
|
||||
CREATE_DISCUSSION_SELECT_USERS: 'create_discussion_select_users',
|
||||
CREATE_DISCUSSION_TOGGLE_ENCRY: 'create_discussion_toggle_encry',
|
||||
CD_CREATE: 'cd_create',
|
||||
CD_CREATE_F: 'cd_create_f',
|
||||
CD_SELECT_CHANNEL: 'cd_select_channel',
|
||||
CD_SELECT_USERS: 'cd_select_users',
|
||||
CD_TOGGLE_ENCRY: 'cd_toggle_encry',
|
||||
|
||||
// PROFILE VIEW
|
||||
PROFILE_PICK_AVATAR: 'profile_pick_avatar',
|
||||
|
@ -122,8 +121,9 @@ export default {
|
|||
PROFILE_SAVE_AVATAR_F: 'profile_save_avatar_f',
|
||||
PROFILE_SAVE_CHANGES: 'profile_save_changes',
|
||||
PROFILE_SAVE_CHANGES_F: 'profile_save_changes_f',
|
||||
PROFILE_LOGOUT_OTHER_LOCATIONS: 'profile_logout_other_locations',
|
||||
PROFILE_LOGOUT_OTHER_LOCATIONS_F: 'profile_logout_other_locations_f',
|
||||
// PROFILE LOGOUT
|
||||
PL_OTHER_LOCATIONS: 'pl_other_locations',
|
||||
PL_OTHER_LOCATIONS_F: 'pl_other_locations_f',
|
||||
|
||||
// SETTINGS VIEW
|
||||
SE_CONTACT_US: 'se_contact_us',
|
||||
|
@ -297,8 +297,6 @@ export default {
|
|||
NP_AUDIONOTIFICATIONS_F: 'np_audio_notifications_f',
|
||||
NP_AUDIONOTIFICATIONVALUE: 'np_audio_notification_value',
|
||||
NP_AUDIONOTIFICATIONVALUE_F: 'np_audio_notification_value_f',
|
||||
NP_DESKTOPNOTIFICATIONDURATION: 'np_desktopnotificationduration',
|
||||
NP_DESKTOPNOTIFICATIONDURATION_F: 'np_desktopnotificationduration_f',
|
||||
NP_EMAILNOTIFICATIONS: 'np_email_notifications',
|
||||
NP_EMAILNOTIFICATIONS_F: 'np_email_notifications_f',
|
||||
|
||||
|
|
|
@ -121,8 +121,9 @@ class AttachmentView extends React.Component {
|
|||
const extension = image_url ? `.${ mime.extension(image_type) || 'jpg' }` : `.${ mime.extension(video_type) || 'mp4' }`;
|
||||
const documentDir = `${ RNFetchBlob.fs.dirs.DocumentDir }/`;
|
||||
const path = `${ documentDir + SHA256(url) + extension }`;
|
||||
const file = await RNFetchBlob.config({ path }).fetch('GET', mediaAttachment).then(res => res.path());
|
||||
await CameraRoll.save(file, { album: 'Rocket.Chat' });
|
||||
const file = await RNFetchBlob.config({ path }).fetch('GET', mediaAttachment);
|
||||
await CameraRoll.save(path, { album: 'Rocket.Chat' });
|
||||
await file.flush();
|
||||
EventEmitter.emit(LISTENER, { message: I18n.t('saved_to_gallery') });
|
||||
} catch (e) {
|
||||
EventEmitter.emit(LISTENER, { message: I18n.t(image_url ? 'error-save-image' : 'error-save-video') });
|
||||
|
|
|
@ -63,12 +63,12 @@ const ChangePasscodeView = React.memo(({ theme }) => {
|
|||
if (!isTablet) {
|
||||
Orientation.lockToPortrait();
|
||||
}
|
||||
EventEmitter.addEventListener(CHANGE_PASSCODE_EMITTER, showChangePasscode);
|
||||
const listener = EventEmitter.addEventListener(CHANGE_PASSCODE_EMITTER, showChangePasscode);
|
||||
return (() => {
|
||||
if (!isTablet) {
|
||||
Orientation.unlockAllOrientations();
|
||||
}
|
||||
EventEmitter.removeListener(CHANGE_PASSCODE_EMITTER);
|
||||
EventEmitter.removeListener(CHANGE_PASSCODE_EMITTER, listener);
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ import PropTypes from 'prop-types';
|
|||
import {
|
||||
View, Text, Switch, ScrollView, StyleSheet, FlatList
|
||||
} from 'react-native';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
import * as List from '../containers/List';
|
||||
|
||||
import TextInput from '../presentation/TextInput';
|
||||
import Loading from '../containers/Loading';
|
||||
|
@ -31,12 +32,6 @@ const styles = StyleSheet.create({
|
|||
list: {
|
||||
width: '100%'
|
||||
},
|
||||
separator: {
|
||||
marginLeft: 60
|
||||
},
|
||||
formSeparator: {
|
||||
marginLeft: 15
|
||||
},
|
||||
input: {
|
||||
height: 54,
|
||||
paddingHorizontal: 18,
|
||||
|
@ -133,7 +128,7 @@ class CreateChannelView extends React.Component {
|
|||
if (nextProps.encryptionEnabled !== encryptionEnabled) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextProps.users, users)) {
|
||||
if (!dequal(nextProps.users, users)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -177,7 +172,7 @@ class CreateChannelView extends React.Component {
|
|||
}
|
||||
|
||||
removeUser = (user) => {
|
||||
logEvent(events.CREATE_CHANNEL_REMOVE_USER);
|
||||
logEvent(events.CR_REMOVE_USER);
|
||||
const { removeUser } = this.props;
|
||||
removeUser(user);
|
||||
}
|
||||
|
@ -207,7 +202,7 @@ class CreateChannelView extends React.Component {
|
|||
value: type,
|
||||
label: 'Private_Channel',
|
||||
onValueChange: (value) => {
|
||||
logEvent(events.CREATE_CHANNEL_TOGGLE_TYPE);
|
||||
logEvent(events.CR_TOGGLE_TYPE);
|
||||
// If we set the channel as public, encrypted status should be false
|
||||
this.setState(({ encrypted }) => ({ type: value, encrypted: value && encrypted }));
|
||||
}
|
||||
|
@ -221,7 +216,7 @@ class CreateChannelView extends React.Component {
|
|||
value: readOnly,
|
||||
label: 'Read_Only_Channel',
|
||||
onValueChange: (value) => {
|
||||
logEvent(events.CREATE_CHANNEL_TOGGLE_READ_ONLY);
|
||||
logEvent(events.CR_TOGGLE_READ_ONLY);
|
||||
this.setState({ readOnly: value });
|
||||
},
|
||||
disabled: broadcast
|
||||
|
@ -241,7 +236,7 @@ class CreateChannelView extends React.Component {
|
|||
value: encrypted,
|
||||
label: 'Encrypted',
|
||||
onValueChange: (value) => {
|
||||
logEvent(events.CREATE_CHANNEL_TOGGLE_ENCRYPTED);
|
||||
logEvent(events.CR_TOGGLE_ENCRYPTED);
|
||||
this.setState({ encrypted: value });
|
||||
},
|
||||
disabled: !type
|
||||
|
@ -255,7 +250,7 @@ class CreateChannelView extends React.Component {
|
|||
value: broadcast,
|
||||
label: 'Broadcast_Channel',
|
||||
onValueChange: (value) => {
|
||||
logEvent(events.CREATE_CHANNEL_TOGGLE_BROADCAST);
|
||||
logEvent(events.CR_TOGGLE_BROADCAST);
|
||||
this.setState({
|
||||
broadcast: value,
|
||||
readOnly: value ? true : readOnly
|
||||
|
@ -264,13 +259,6 @@ class CreateChannelView extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
renderSeparator = () => <View style={[sharedStyles.separator, styles.separator]} />
|
||||
|
||||
renderFormSeparator = () => {
|
||||
const { theme } = this.props;
|
||||
return <View style={[sharedStyles.separator, styles.formSeparator, { backgroundColor: themes[theme].separatorColor }]} />;
|
||||
}
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const { baseUrl, user, theme } = this.props;
|
||||
|
||||
|
@ -305,7 +293,7 @@ class CreateChannelView extends React.Component {
|
|||
}
|
||||
]}
|
||||
renderItem={this.renderItem}
|
||||
ItemSeparatorComponent={this.renderSeparator}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
enableEmptySections
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
|
@ -341,13 +329,13 @@ class CreateChannelView extends React.Component {
|
|||
theme={theme}
|
||||
underlineColorAndroid='transparent'
|
||||
/>
|
||||
{this.renderFormSeparator()}
|
||||
<List.Separator />
|
||||
{this.renderType()}
|
||||
{this.renderFormSeparator()}
|
||||
<List.Separator />
|
||||
{this.renderReadOnly()}
|
||||
{this.renderFormSeparator()}
|
||||
<List.Separator />
|
||||
{this.renderEncrypted()}
|
||||
{this.renderFormSeparator()}
|
||||
<List.Separator />
|
||||
{this.renderBroadcast()}
|
||||
</View>
|
||||
<View style={styles.invitedHeader}>
|
||||
|
|
|
@ -22,7 +22,7 @@ const SelectUsers = ({
|
|||
const getUsers = debounce(async(keyword = '') => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const usersCollection = db.collections.get('users');
|
||||
const usersCollection = db.get('users');
|
||||
const res = await RocketChat.search({ text: keyword, filterRooms: false });
|
||||
let items = [...users.filter(u => selected.includes(u.name)), ...res.filter(r => !users.find(u => u.name === r.name))];
|
||||
const records = await usersCollection.query(Q.where('username', Q.oneOf(items.map(u => u.name)))).fetch();
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ScrollView, Text, Switch } from 'react-native';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import Loading from '../../containers/Loading';
|
||||
import KeyboardView from '../../presentation/KeyboardView';
|
||||
|
@ -63,11 +62,12 @@ class CreateChannelView extends React.Component {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const { channel, name } = this.state;
|
||||
const {
|
||||
loading, failure, error, result, isMasterDetail
|
||||
} = this.props;
|
||||
|
||||
if (!isEqual(this.state, prevState)) {
|
||||
if (channel?.rid !== prevState.channel?.rid || name !== prevState.name) {
|
||||
this.setHeader();
|
||||
}
|
||||
|
||||
|
@ -136,17 +136,17 @@ class CreateChannelView extends React.Component {
|
|||
};
|
||||
|
||||
selectChannel = ({ value }) => {
|
||||
logEvent(events.CREATE_DISCUSSION_SELECT_CHANNEL);
|
||||
logEvent(events.CD_SELECT_CHANNEL);
|
||||
this.setState({ channel: value, encrypted: value?.encrypted });
|
||||
}
|
||||
|
||||
selectUsers = ({ value }) => {
|
||||
logEvent(events.CREATE_DISCUSSION_SELECT_USERS);
|
||||
logEvent(events.CD_SELECT_USERS);
|
||||
this.setState({ users: value });
|
||||
}
|
||||
|
||||
onEncryptedChange = (value) => {
|
||||
logEvent(events.CREATE_DISCUSSION_TOGGLE_ENCRY);
|
||||
logEvent(events.CD_TOGGLE_ENCRY);
|
||||
this.setState({ encrypted: value });
|
||||
}
|
||||
|
||||
|
@ -222,7 +222,7 @@ const mapStateToProps = state => ({
|
|||
loading: state.createDiscussion.isFetching,
|
||||
result: state.createDiscussion.result,
|
||||
blockUnauthenticatedAccess: state.settings.Accounts_AvatarBlockUnauthenticatedAccess ?? true,
|
||||
serverVersion: state.share.server.version || state.server.version,
|
||||
serverVersion: state.server.version,
|
||||
isMasterDetail: state.app.isMasterDetail,
|
||||
encryptionEnabled: state.encryption.enabled
|
||||
});
|
||||
|
|
|
@ -84,13 +84,13 @@ export default class DirectoryOptions extends PureComponent {
|
|||
inputRange: [0, 1],
|
||||
outputRange: [-326, 0]
|
||||
});
|
||||
const backdropOpacity = this.animatedValue.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 0.3]
|
||||
});
|
||||
const {
|
||||
globalUsers, toggleWorkspace, isFederationEnabled, theme
|
||||
} = this.props;
|
||||
const backdropOpacity = this.animatedValue.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, themes[theme].backdropOpacity]
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<TouchableWithoutFeedback onPress={this.close}>
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
View, FlatList, Text
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import * as List from '../../containers/List';
|
||||
|
||||
import Touch from '../../utils/touch';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
|
@ -182,11 +183,6 @@ class DirectoryView extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderSeparator = () => {
|
||||
const { theme } = this.props;
|
||||
return <View style={[sharedStyles.separator, styles.separator, { backgroundColor: themes[theme].separatorColor }]} />;
|
||||
}
|
||||
|
||||
renderItem = ({ item, index }) => {
|
||||
const { data, type } = this.state;
|
||||
const { baseUrl, user, theme } = this.props;
|
||||
|
@ -251,7 +247,7 @@ class DirectoryView extends React.Component {
|
|||
keyExtractor={item => item._id}
|
||||
ListHeaderComponent={this.renderHeader}
|
||||
renderItem={this.renderItem}
|
||||
ItemSeparatorComponent={this.renderSeparator}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
keyboardShouldPersistTaps='always'
|
||||
ListFooterComponent={loading ? <ActivityIndicator theme={theme} /> : null}
|
||||
onEndReached={() => this.load({})}
|
||||
|
|
|
@ -94,7 +94,7 @@ class LanguageView extends React.Component {
|
|||
setUser({ language: params.language });
|
||||
|
||||
const serversDB = database.servers;
|
||||
const usersCollection = serversDB.collections.get('users');
|
||||
const usersCollection = serversDB.get('users');
|
||||
await serversDB.action(async() => {
|
||||
try {
|
||||
const userRecord = await usersCollection.find(user.id);
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
Text, View, StyleSheet, Keyboard, Alert
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import sharedStyles from './Styles';
|
||||
import Button from '../containers/Button';
|
||||
|
@ -82,7 +82,7 @@ class LoginView extends React.Component {
|
|||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
const { error } = this.props;
|
||||
if (nextProps.failure && !equal(error, nextProps.error)) {
|
||||
if (nextProps.failure && !dequal(error, nextProps.error)) {
|
||||
Alert.alert(I18n.t('Oops'), I18n.t('Login_error'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { FlatList, View, Text } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import styles from './styles';
|
||||
import Message from '../../containers/message';
|
||||
|
@ -57,7 +57,7 @@ class MessagesView extends React.Component {
|
|||
if (nextState.loading !== loading) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextState.messages, messages)) {
|
||||
if (!dequal(nextState.messages, messages)) {
|
||||
return true;
|
||||
}
|
||||
if (fileLoading !== nextState.fileLoading) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { connect } from 'react-redux';
|
||||
import { KeyboardAwareScrollView } from '@codler/react-native-keyboard-aware-scroll-view';
|
||||
|
||||
|
@ -94,17 +93,6 @@ class ModalBlockView extends React.Component {
|
|||
EventEmitter.addEventListener(viewId, this.handleUpdate);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if (!isEqual(nextProps, this.props)) {
|
||||
return true;
|
||||
}
|
||||
if (!isEqual(nextState, this.state)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { navigation, route } = this.props;
|
||||
const oldData = prevProps.route.params?.data ?? {};
|
||||
|
|
|
@ -4,9 +4,8 @@ import {
|
|||
View, StyleSheet, FlatList, Text
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import equal from 'deep-equal';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import * as List from '../containers/List';
|
||||
|
||||
import Touch from '../utils/touch';
|
||||
import database from '../lib/database';
|
||||
|
@ -27,10 +26,9 @@ import { createChannelRequest } from '../actions/createChannel';
|
|||
import { goRoom } from '../utils/goRoom';
|
||||
import SafeAreaView from '../containers/SafeAreaView';
|
||||
|
||||
const QUERY_SIZE = 50;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
separator: {
|
||||
marginLeft: 60
|
||||
},
|
||||
button: {
|
||||
height: 46,
|
||||
flexDirection: 'row',
|
||||
|
@ -77,40 +75,20 @@ class NewMessageView extends React.Component {
|
|||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const { search, chats } = this.state;
|
||||
const { theme } = this.props;
|
||||
if (nextProps.theme !== theme) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextState.search, search)) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextState.chats, chats)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.querySubscription && this.querySubscription.unsubscribe) {
|
||||
this.querySubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/sort-comp
|
||||
init = async() => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const observable = await db.collections
|
||||
const chats = await db.collections
|
||||
.get('subscriptions')
|
||||
.query(Q.where('t', 'd'))
|
||||
.observeWithColumns(['room_updated_at']);
|
||||
.query(
|
||||
Q.where('t', 'd'),
|
||||
Q.experimentalTake(QUERY_SIZE),
|
||||
Q.experimentalSortBy('room_updated_at', Q.desc)
|
||||
)
|
||||
.fetch();
|
||||
|
||||
this.querySubscription = observable.subscribe((data) => {
|
||||
const chats = orderBy(data, ['roomUpdatedAt'], ['desc']);
|
||||
this.setState({ chats });
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
|
@ -211,10 +189,6 @@ class NewMessageView extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderSeparator = () => {
|
||||
const { theme } = this.props;
|
||||
return <View style={[sharedStyles.separator, styles.separator, { backgroundColor: themes[theme].separatorColor }]} />;
|
||||
}
|
||||
|
||||
renderItem = ({ item, index }) => {
|
||||
const { search, chats } = this.state;
|
||||
|
@ -254,7 +228,7 @@ class NewMessageView extends React.Component {
|
|||
keyExtractor={item => item._id}
|
||||
ListHeaderComponent={this.renderHeader}
|
||||
renderItem={this.renderItem}
|
||||
ItemSeparatorComponent={this.renderSeparator}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
|
|
|
@ -132,7 +132,7 @@ class NewServerView extends React.Component {
|
|||
queryServerHistory = async(text) => {
|
||||
const db = database.servers;
|
||||
try {
|
||||
const serversHistoryCollection = db.collections.get('servers_history');
|
||||
const serversHistoryCollection = db.get('servers_history');
|
||||
let whereClause = [
|
||||
Q.where('username', Q.notEq(null)),
|
||||
Q.experimentalSortBy('updated_at', Q.desc),
|
||||
|
@ -174,7 +174,7 @@ class NewServerView extends React.Component {
|
|||
}
|
||||
|
||||
submit = async({ fromServerHistory = false, username }) => {
|
||||
logEvent(events.NEWSERVER_CONNECT_TO_WORKSPACE);
|
||||
logEvent(events.NS_CONNECT_TO_WORKSPACE);
|
||||
const { text, certificate } = this.state;
|
||||
const { connectServer } = this.props;
|
||||
|
||||
|
@ -199,7 +199,7 @@ class NewServerView extends React.Component {
|
|||
}
|
||||
|
||||
connectOpen = () => {
|
||||
logEvent(events.NEWSERVER_JOIN_OPEN_WORKSPACE);
|
||||
logEvent(events.NS_JOIN_OPEN_WORKSPACE);
|
||||
this.setState({ connectingOpen: true });
|
||||
const { connectServer } = this.props;
|
||||
connectServer('https://open.rocket.chat');
|
||||
|
|
|
@ -6,7 +6,7 @@ import prompt from 'react-native-prompt-android';
|
|||
import SHA256 from 'js-sha256';
|
||||
import ImagePicker from 'react-native-image-crop-picker';
|
||||
import RNPickerSelect from 'react-native-picker-select';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { dequal } from 'dequal';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
import Touch from '../../utils/touch';
|
||||
|
@ -91,21 +91,11 @@ class ProfileView extends React.Component {
|
|||
* it's resetting the avatar right after
|
||||
* select some image from gallery.
|
||||
*/
|
||||
if (!isEqual(omit(user, ['status']), omit(nextProps.user, ['status']))) {
|
||||
if (!dequal(omit(user, ['status']), omit(nextProps.user, ['status']))) {
|
||||
this.init(nextProps.user);
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if (!isEqual(nextState, this.state)) {
|
||||
return true;
|
||||
}
|
||||
if (!isEqual(nextProps, this.props)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
setAvatar = (avatar) => {
|
||||
const { Accounts_AllowUserAvatarChange } = this.props;
|
||||
|
||||
|
@ -434,7 +424,7 @@ class ProfileView extends React.Component {
|
|||
}
|
||||
|
||||
logoutOtherLocations = () => {
|
||||
logEvent(events.PROFILE_LOGOUT_OTHER_LOCATIONS);
|
||||
logEvent(events.PL_OTHER_LOCATIONS);
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('You_will_be_logged_out_from_other_locations'),
|
||||
confirmationText: I18n.t('Logout'),
|
||||
|
@ -443,7 +433,7 @@ class ProfileView extends React.Component {
|
|||
await RocketChat.logoutOtherLocations();
|
||||
EventEmitter.emit(LISTENER, { message: I18n.t('Logged_out_of_other_clients_successfully') });
|
||||
} catch {
|
||||
logEvent(events.PROFILE_LOGOUT_OTHER_LOCATIONS_F);
|
||||
logEvent(events.PL_OTHER_LOCATIONS_F);
|
||||
EventEmitter.emit(LISTENER, { message: I18n.t('Logout_failed') });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FlatList, View, Text } from 'react-native';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
import moment from 'moment';
|
||||
import { connect } from 'react-redux';
|
||||
import * as List from '../../containers/List';
|
||||
|
||||
import Avatar from '../../containers/Avatar';
|
||||
import styles from './styles';
|
||||
|
@ -55,7 +56,7 @@ class ReadReceiptView extends React.Component {
|
|||
if (nextState.loading !== loading) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextState.receipts, receipts)) {
|
||||
if (!dequal(nextState.receipts, receipts)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -121,11 +122,6 @@ class ReadReceiptView extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderSeparator = () => {
|
||||
const { theme } = this.props;
|
||||
return <View style={[styles.separator, { backgroundColor: themes[theme].separatorColor }]} />;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { receipts, loading } = this.state;
|
||||
const { theme } = this.props;
|
||||
|
@ -143,7 +139,7 @@ class ReadReceiptView extends React.Component {
|
|||
<FlatList
|
||||
data={receipts}
|
||||
renderItem={this.renderItem}
|
||||
ItemSeparatorComponent={this.renderSeparator}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
style={[
|
||||
styles.list,
|
||||
{
|
||||
|
|
|
@ -53,7 +53,15 @@ class RoomActionsView extends React.Component {
|
|||
closeRoom: PropTypes.func,
|
||||
theme: PropTypes.string,
|
||||
fontScale: PropTypes.number,
|
||||
serverVersion: PropTypes.string
|
||||
serverVersion: PropTypes.string,
|
||||
addUserToJoinedRoomPermission: PropTypes.array,
|
||||
addUserToAnyCRoomPermission: PropTypes.array,
|
||||
addUserToAnyPRoomPermission: PropTypes.array,
|
||||
createInviteLinksPermission: PropTypes.array,
|
||||
editRoomPermission: PropTypes.array,
|
||||
toggleRoomE2EEncryptionPermission: PropTypes.array,
|
||||
viewBroadcastMemberListPermission: PropTypes.array,
|
||||
transferLivechatGuestPermission: PropTypes.array
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -118,7 +126,7 @@ class RoomActionsView extends React.Component {
|
|||
this.updateRoomMember();
|
||||
}
|
||||
|
||||
const canAutoTranslate = await RocketChat.canAutoTranslate();
|
||||
const canAutoTranslate = RocketChat.canAutoTranslate();
|
||||
this.setState({ canAutoTranslate });
|
||||
|
||||
this.canAddUser();
|
||||
|
@ -159,60 +167,62 @@ class RoomActionsView extends React.Component {
|
|||
|
||||
canAddUser = async() => {
|
||||
const { room, joined } = this.state;
|
||||
const { addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission } = this.props;
|
||||
const { rid, t } = room;
|
||||
let canAdd = false;
|
||||
let canAddUser = false;
|
||||
|
||||
const userInRoom = joined;
|
||||
const permissions = await RocketChat.hasPermission(['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room'], rid);
|
||||
const permissions = await RocketChat.hasPermission([addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission], rid);
|
||||
|
||||
if (permissions) {
|
||||
if (userInRoom && permissions['add-user-to-joined-room']) {
|
||||
canAdd = true;
|
||||
if (userInRoom && permissions[0]) {
|
||||
canAddUser = true;
|
||||
}
|
||||
if (t === 'c' && permissions['add-user-to-any-c-room']) {
|
||||
canAdd = true;
|
||||
if (t === 'c' && permissions[1]) {
|
||||
canAddUser = true;
|
||||
}
|
||||
if (t === 'p' && permissions['add-user-to-any-p-room']) {
|
||||
canAdd = true;
|
||||
if (t === 'p' && permissions[2]) {
|
||||
canAddUser = true;
|
||||
}
|
||||
}
|
||||
this.setState({ canAddUser: canAdd });
|
||||
this.setState({ canAddUser });
|
||||
}
|
||||
|
||||
canInviteUser = async() => {
|
||||
const { room } = this.state;
|
||||
const { createInviteLinksPermission } = this.props;
|
||||
const { rid } = room;
|
||||
const permissions = await RocketChat.hasPermission(['create-invite-links'], rid);
|
||||
const permissions = await RocketChat.hasPermission([createInviteLinksPermission], rid);
|
||||
|
||||
const canInviteUser = permissions && permissions['create-invite-links'];
|
||||
const canInviteUser = permissions[0];
|
||||
this.setState({ canInviteUser });
|
||||
}
|
||||
|
||||
canEdit = async() => {
|
||||
const { room } = this.state;
|
||||
const { editRoomPermission } = this.props;
|
||||
const { rid } = room;
|
||||
const permissions = await RocketChat.hasPermission(['edit-room'], rid);
|
||||
const permissions = await RocketChat.hasPermission([editRoomPermission], rid);
|
||||
|
||||
const canEdit = permissions && permissions['edit-room'];
|
||||
const canEdit = permissions[0];
|
||||
this.setState({ canEdit });
|
||||
}
|
||||
|
||||
canToggleEncryption = async() => {
|
||||
const { room } = this.state;
|
||||
const { toggleRoomE2EEncryptionPermission } = this.props;
|
||||
const { rid } = room;
|
||||
const permissions = await RocketChat.hasPermission(['toggle-room-e2e-encryption'], rid);
|
||||
const permissions = await RocketChat.hasPermission([toggleRoomE2EEncryptionPermission], rid);
|
||||
|
||||
const canToggleEncryption = permissions && permissions['toggle-room-e2e-encryption'];
|
||||
const canToggleEncryption = permissions[0];
|
||||
this.setState({ canToggleEncryption });
|
||||
}
|
||||
|
||||
canViewMembers = async() => {
|
||||
const { room } = this.state;
|
||||
const { viewBroadcastMemberListPermission } = this.props;
|
||||
const { rid, t, broadcast } = room;
|
||||
if (broadcast) {
|
||||
const viewBroadcastMemberListPermission = 'view-broadcast-member-list';
|
||||
const permissions = await RocketChat.hasPermission([viewBroadcastMemberListPermission], rid);
|
||||
if (!permissions[viewBroadcastMemberListPermission]) {
|
||||
if (!permissions[0]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -226,16 +236,10 @@ class RoomActionsView extends React.Component {
|
|||
|
||||
canForwardGuest = async() => {
|
||||
const { room } = this.state;
|
||||
const { transferLivechatGuestPermission } = this.props;
|
||||
const { rid } = room;
|
||||
let result = true;
|
||||
|
||||
const transferLivechatGuest = 'transfer-livechat-guest';
|
||||
const permissions = await RocketChat.hasPermission([transferLivechatGuest], rid);
|
||||
if (!permissions[transferLivechatGuest]) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
this.setState({ canForwardGuest: result });
|
||||
const permissions = await RocketChat.hasPermission([transferLivechatGuestPermission], rid);
|
||||
this.setState({ canForwardGuest: permissions[0] });
|
||||
}
|
||||
|
||||
canReturnQueue = async() => {
|
||||
|
@ -482,7 +486,7 @@ class RoomActionsView extends React.Component {
|
|||
<List.Separator />
|
||||
<List.Item
|
||||
title='Voice_call'
|
||||
onPress={() => RocketChat.callJitsi(room?.rid, true)}
|
||||
onPress={() => RocketChat.callJitsi(room, true)}
|
||||
testID='room-actions-voice'
|
||||
left={() => <List.Icon name='phone' />}
|
||||
showActionIndicator
|
||||
|
@ -490,7 +494,7 @@ class RoomActionsView extends React.Component {
|
|||
<List.Separator />
|
||||
<List.Item
|
||||
title='Video_call'
|
||||
onPress={() => RocketChat.callJitsi(room?.rid)}
|
||||
onPress={() => RocketChat.callJitsi(room)}
|
||||
testID='room-actions-video'
|
||||
left={() => <List.Icon name='camera' />}
|
||||
showActionIndicator
|
||||
|
@ -866,7 +870,15 @@ class RoomActionsView extends React.Component {
|
|||
const mapStateToProps = state => ({
|
||||
jitsiEnabled: state.settings.Jitsi_Enabled || false,
|
||||
encryptionEnabled: state.encryption.enabled,
|
||||
serverVersion: state.server.version
|
||||
serverVersion: state.server.version,
|
||||
addUserToJoinedRoomPermission: state.permissions['add-user-to-joined-room'],
|
||||
addUserToAnyCRoomPermission: state.permissions['add-user-to-any-c-room'],
|
||||
addUserToAnyPRoomPermission: state.permissions['add-user-to-any-p-room'],
|
||||
createInviteLinksPermission: state.permissions['create-invite-links'],
|
||||
editRoomPermission: state.permissions['edit-room'],
|
||||
toggleRoomE2EEncryptionPermission: state.permissions['toggle-room-e2e-encryption'],
|
||||
viewBroadcastMemberListPermission: state.permissions['view-broadcast-member-list'],
|
||||
transferLivechatGuestPermission: state.permissions['transfer-livechat-guest']
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
|
|
@ -4,15 +4,13 @@ import {
|
|||
Text, View, ScrollView, TouchableOpacity, Keyboard, Alert
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import equal from 'deep-equal';
|
||||
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
||||
import ImagePicker from 'react-native-image-crop-picker';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { dequal } from 'dequal';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import lt from 'semver/functions/lt';
|
||||
import coerce from 'semver/functions/coerce';
|
||||
|
||||
|
||||
import database from '../../lib/database';
|
||||
import { deleteRoom as deleteRoomAction } from '../../actions/room';
|
||||
import KeyboardView from '../../presentation/KeyboardView';
|
||||
|
@ -44,14 +42,6 @@ const PERMISSION_ARCHIVE = 'archive-room';
|
|||
const PERMISSION_UNARCHIVE = 'unarchive-room';
|
||||
const PERMISSION_DELETE_C = 'delete-c';
|
||||
const PERMISSION_DELETE_P = 'delete-p';
|
||||
const PERMISSIONS_ARRAY = [
|
||||
PERMISSION_SET_READONLY,
|
||||
PERMISSION_SET_REACT_WHEN_READONLY,
|
||||
PERMISSION_ARCHIVE,
|
||||
PERMISSION_UNARCHIVE,
|
||||
PERMISSION_DELETE_C,
|
||||
PERMISSION_DELETE_P
|
||||
];
|
||||
|
||||
class RoomInfoEditView extends React.Component {
|
||||
static navigationOptions = () => ({
|
||||
|
@ -63,7 +53,13 @@ class RoomInfoEditView extends React.Component {
|
|||
deleteRoom: PropTypes.func,
|
||||
serverVersion: PropTypes.string,
|
||||
encryptionEnabled: PropTypes.bool,
|
||||
theme: PropTypes.string
|
||||
theme: PropTypes.string,
|
||||
setReadOnlyPermission: PropTypes.array,
|
||||
setReactWhenReadOnlyPermission: PropTypes.array,
|
||||
archiveRoomPermission: PropTypes.array,
|
||||
unarchiveRoomPermission: PropTypes.array,
|
||||
deleteCPermission: PropTypes.array,
|
||||
deletePPermission: PropTypes.array
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
@ -90,16 +86,6 @@ class RoomInfoEditView extends React.Component {
|
|||
this.loadRoom();
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if (!equal(nextState, this.state)) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextProps, this.props)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.querySubscription && this.querySubscription.unsubscribe) {
|
||||
this.querySubscription.unsubscribe();
|
||||
|
@ -108,14 +94,22 @@ class RoomInfoEditView extends React.Component {
|
|||
|
||||
// eslint-disable-next-line react/sort-comp
|
||||
loadRoom = async() => {
|
||||
const { route } = this.props;
|
||||
const {
|
||||
route,
|
||||
setReadOnlyPermission,
|
||||
setReactWhenReadOnlyPermission,
|
||||
archiveRoomPermission,
|
||||
unarchiveRoomPermission,
|
||||
deleteCPermission,
|
||||
deletePPermission
|
||||
} = this.props;
|
||||
const rid = route.params?.rid;
|
||||
if (!rid) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const db = database.active;
|
||||
const sub = await db.collections.get('subscriptions').find(rid);
|
||||
const sub = await db.get('subscriptions').find(rid);
|
||||
const observable = sub.observe();
|
||||
|
||||
this.querySubscription = observable.subscribe((data) => {
|
||||
|
@ -123,8 +117,25 @@ class RoomInfoEditView extends React.Component {
|
|||
this.init(this.room);
|
||||
});
|
||||
|
||||
const permissions = await RocketChat.hasPermission(PERMISSIONS_ARRAY, rid);
|
||||
this.setState({ permissions });
|
||||
const result = await RocketChat.hasPermission([
|
||||
setReadOnlyPermission,
|
||||
setReactWhenReadOnlyPermission,
|
||||
archiveRoomPermission,
|
||||
unarchiveRoomPermission,
|
||||
deleteCPermission,
|
||||
deletePPermission
|
||||
], rid);
|
||||
|
||||
this.setState({
|
||||
permissions: {
|
||||
[PERMISSION_SET_READONLY]: result[0],
|
||||
[PERMISSION_SET_REACT_WHEN_READONLY]: result[1],
|
||||
[PERMISSION_ARCHIVE]: result[2],
|
||||
[PERMISSION_UNARCHIVE]: result[3],
|
||||
[PERMISSION_DELETE_C]: result[4],
|
||||
[PERMISSION_DELETE_P]: result[5]
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
|
@ -179,7 +190,7 @@ class RoomInfoEditView extends React.Component {
|
|||
&& room.t === 'p' === t
|
||||
&& room.ro === ro
|
||||
&& room.reactWhenReadOnly === reactWhenReadOnly
|
||||
&& isEqual(room.sysMes, systemMessages)
|
||||
&& dequal(room.sysMes, systemMessages)
|
||||
&& enableSysMes === (room.sysMes && room.sysMes.length > 0)
|
||||
&& room.encrypted === encrypted
|
||||
&& isEmpty(avatar)
|
||||
|
@ -239,7 +250,7 @@ class RoomInfoEditView extends React.Component {
|
|||
params.reactWhenReadOnly = reactWhenReadOnly;
|
||||
}
|
||||
|
||||
if (!isEqual(room.sysMes, systemMessages)) {
|
||||
if (!dequal(room.sysMes, systemMessages)) {
|
||||
params.systemMessages = systemMessages;
|
||||
}
|
||||
|
||||
|
@ -666,8 +677,14 @@ class RoomInfoEditView extends React.Component {
|
|||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
serverVersion: state.share.server.version || state.server.version,
|
||||
encryptionEnabled: state.encryption.enabled
|
||||
serverVersion: state.server.version,
|
||||
encryptionEnabled: state.encryption.enabled,
|
||||
setReadOnlyPermission: state.permissions[PERMISSION_SET_READONLY],
|
||||
setReactWhenReadOnlyPermission: state.permissions[PERMISSION_SET_REACT_WHEN_READONLY],
|
||||
archiveRoomPermission: state.permissions[PERMISSION_ARCHIVE],
|
||||
unarchiveRoomPermission: state.permissions[PERMISSION_UNARCHIVE],
|
||||
deleteCPermission: state.permissions[PERMISSION_DELETE_C],
|
||||
deletePPermission: state.permissions[PERMISSION_DELETE_P]
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
|
|
@ -31,7 +31,6 @@ import SafeAreaView from '../../containers/SafeAreaView';
|
|||
import { goRoom } from '../../utils/goRoom';
|
||||
import Navigation from '../../lib/Navigation';
|
||||
|
||||
const PERMISSION_EDIT_ROOM = 'edit-room';
|
||||
const getRoomTitle = (room, type, name, username, statusText, theme) => (type === 'd'
|
||||
? (
|
||||
<>
|
||||
|
@ -55,7 +54,8 @@ class RoomInfoView extends React.Component {
|
|||
rooms: PropTypes.array,
|
||||
theme: PropTypes.string,
|
||||
isMasterDetail: PropTypes.bool,
|
||||
jitsiEnabled: PropTypes.bool
|
||||
jitsiEnabled: PropTypes.bool,
|
||||
editRoomPermission: PropTypes.array
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -136,7 +136,7 @@ class RoomInfoView extends React.Component {
|
|||
getRoleDescription = async(id) => {
|
||||
const db = database.active;
|
||||
try {
|
||||
const rolesCollection = db.collections.get('roles');
|
||||
const rolesCollection = db.get('roles');
|
||||
const role = await rolesCollection.find(id);
|
||||
if (role) {
|
||||
return role.description;
|
||||
|
@ -193,7 +193,7 @@ class RoomInfoView extends React.Component {
|
|||
|
||||
loadRoom = async() => {
|
||||
const { room: roomState } = this.state;
|
||||
const { route } = this.props;
|
||||
const { route, editRoomPermission } = this.props;
|
||||
let room = route.params?.room;
|
||||
if (room && room.observe) {
|
||||
this.roomObservable = room.observe();
|
||||
|
@ -213,8 +213,8 @@ class RoomInfoView extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
const permissions = await RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid);
|
||||
if (permissions[PERMISSION_EDIT_ROOM] && !room.prid) {
|
||||
const permissions = await RocketChat.hasPermission([editRoomPermission], room.rid);
|
||||
if (permissions[0] && !room.prid) {
|
||||
this.setState({ showEdit: true }, () => this.setHeader());
|
||||
}
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ class RoomInfoView extends React.Component {
|
|||
|
||||
videoCall = () => {
|
||||
const { room } = this.state;
|
||||
RocketChat.callJitsi(room.rid);
|
||||
RocketChat.callJitsi(room);
|
||||
}
|
||||
|
||||
renderAvatar = (room, roomUser) => {
|
||||
|
@ -369,7 +369,8 @@ class RoomInfoView extends React.Component {
|
|||
const mapStateToProps = state => ({
|
||||
rooms: state.room.rooms,
|
||||
isMasterDetail: state.app.isMasterDetail,
|
||||
jitsiEnabled: state.settings.Jitsi_Enabled || false
|
||||
jitsiEnabled: state.settings.Jitsi_Enabled || false,
|
||||
editRoomPermission: state.permissions['edit-room']
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withTheme(RoomInfoView));
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FlatList, View } from 'react-native';
|
||||
import { FlatList } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import * as List from '../../containers/List';
|
||||
|
||||
import styles from './styles';
|
||||
import UserItem from '../../presentation/UserItem';
|
||||
|
@ -28,6 +29,12 @@ import { goRoom } from '../../utils/goRoom';
|
|||
|
||||
const PAGE_SIZE = 25;
|
||||
|
||||
const PERMISSION_MUTE_USER = 'mute-user';
|
||||
const PERMISSION_SET_LEADER = 'set-leader';
|
||||
const PERMISSION_SET_OWNER = 'set-owner';
|
||||
const PERMISSION_SET_MODERATOR = 'set-moderator';
|
||||
const PERMISSION_REMOVE_USER = 'remove-user';
|
||||
|
||||
class RoomMembersView extends React.Component {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
|
@ -43,7 +50,12 @@ class RoomMembersView extends React.Component {
|
|||
showActionSheet: PropTypes.func,
|
||||
theme: PropTypes.string,
|
||||
isMasterDetail: PropTypes.bool,
|
||||
useRealName: PropTypes.bool
|
||||
useRealName: PropTypes.bool,
|
||||
muteUserPermission: PropTypes.array,
|
||||
setLeaderPermission: PropTypes.array,
|
||||
setOwnerPermission: PropTypes.array,
|
||||
setModeratorPermission: PropTypes.array,
|
||||
removeUserPermission: PropTypes.array
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -81,7 +93,20 @@ class RoomMembersView extends React.Component {
|
|||
this.fetchMembers();
|
||||
|
||||
const { room } = this.state;
|
||||
this.permissions = await RocketChat.hasPermission(['mute-user', 'set-leader', 'set-owner', 'set-moderator', 'remove-user'], room.rid);
|
||||
const {
|
||||
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
|
||||
} = this.props;
|
||||
const result = await RocketChat.hasPermission([
|
||||
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
|
||||
], room.rid);
|
||||
|
||||
this.permissions = {
|
||||
[PERMISSION_MUTE_USER]: result[0],
|
||||
[PERMISSION_SET_LEADER]: result[1],
|
||||
[PERMISSION_SET_OWNER]: result[2],
|
||||
[PERMISSION_SET_MODERATOR]: result[3],
|
||||
[PERMISSION_REMOVE_USER]: result[4]
|
||||
};
|
||||
|
||||
const hasSinglePermission = Object.values(this.permissions).some(p => !!p);
|
||||
if (hasSinglePermission) {
|
||||
|
@ -122,7 +147,7 @@ class RoomMembersView extends React.Component {
|
|||
navToDirectMessage = async(item) => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const subsCollection = db.collections.get('subscriptions');
|
||||
const subsCollection = db.get('subscriptions');
|
||||
const query = await subsCollection.query(Q.where('name', item.username)).fetch();
|
||||
if (query.length) {
|
||||
const [room] = query;
|
||||
|
@ -395,11 +420,6 @@ class RoomMembersView extends React.Component {
|
|||
<SearchBox onChangeText={text => this.onSearchChangeText(text)} testID='room-members-view-search' />
|
||||
)
|
||||
|
||||
renderSeparator = () => {
|
||||
const { theme } = this.props;
|
||||
return <View style={[styles.separator, { backgroundColor: themes[theme].separatorColor }]} />;
|
||||
}
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const { baseUrl, user, theme } = this.props;
|
||||
|
||||
|
@ -429,7 +449,7 @@ class RoomMembersView extends React.Component {
|
|||
renderItem={this.renderItem}
|
||||
style={[styles.list, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
keyExtractor={item => item._id}
|
||||
ItemSeparatorComponent={this.renderSeparator}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
ListHeaderComponent={this.renderSearchBar}
|
||||
ListFooterComponent={() => {
|
||||
if (isLoading) {
|
||||
|
@ -452,7 +472,12 @@ const mapStateToProps = state => ({
|
|||
baseUrl: state.server.server,
|
||||
user: getUserSelector(state),
|
||||
isMasterDetail: state.app.isMasterDetail,
|
||||
useRealName: state.settings.UI_Use_Real_Name
|
||||
useRealName: state.settings.UI_Use_Real_Name,
|
||||
muteUserPermission: state.permissions[PERMISSION_MUTE_USER],
|
||||
setLeaderPermission: state.permissions[PERMISSION_SET_LEADER],
|
||||
setOwnerPermission: state.permissions[PERMISSION_SET_OWNER],
|
||||
setModeratorPermission: state.permissions[PERMISSION_SET_MODERATOR],
|
||||
removeUserPermission: state.permissions[PERMISSION_REMOVE_USER]
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withActionSheet(withTheme(RoomMembersView)));
|
||||
|
|
|
@ -168,7 +168,7 @@ const Header = React.memo(({
|
|||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
<SubTitle usersTyping={usersTyping} subtitle={subtitle} theme={theme} renderFunc={renderFunc} />
|
||||
<SubTitle usersTyping={tmid ? [] : usersTyping} subtitle={subtitle} theme={theme} renderFunc={renderFunc} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import isEqual from 'react-fast-compare';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import * as HeaderButton from '../../../containers/HeaderButton';
|
||||
import database from '../../../lib/database';
|
||||
|
@ -35,7 +35,7 @@ class RightButtonsContainer extends Component {
|
|||
const db = database.active;
|
||||
if (tmid) {
|
||||
try {
|
||||
const threadRecord = await db.collections.get('messages').find(tmid);
|
||||
const threadRecord = await db.get('messages').find(tmid);
|
||||
this.observeThread(threadRecord);
|
||||
} catch (e) {
|
||||
console.log('Can\'t find message to observe.');
|
||||
|
@ -43,7 +43,7 @@ class RightButtonsContainer extends Component {
|
|||
}
|
||||
if (rid) {
|
||||
try {
|
||||
const subCollection = db.collections.get('subscriptions');
|
||||
const subCollection = db.get('subscriptions');
|
||||
const subRecord = await subCollection.find(rid);
|
||||
this.observeSubscription(subRecord);
|
||||
} catch (e) {
|
||||
|
@ -59,15 +59,16 @@ class RightButtonsContainer extends Component {
|
|||
if (nextState.isFollowingThread !== isFollowingThread) {
|
||||
return true;
|
||||
}
|
||||
if (!isEqual(nextState.tunread, tunread)) {
|
||||
if (!dequal(nextState.tunread, tunread)) {
|
||||
return true;
|
||||
}
|
||||
if (!isEqual(nextState.tunreadUser, tunreadUser)) {
|
||||
if (!dequal(nextState.tunreadUser, tunreadUser)) {
|
||||
return true;
|
||||
}
|
||||
if (!isEqual(nextState.tunreadGroup, tunreadGroup)) {
|
||||
if (!dequal(nextState.tunreadGroup, tunreadGroup)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import equal from 'deep-equal';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import Header from './Header';
|
||||
import LeftButtons from './LeftButtons';
|
||||
|
@ -65,7 +65,7 @@ class RoomHeaderView extends Component {
|
|||
if (nextProps.height !== height) {
|
||||
return true;
|
||||
}
|
||||
if (!equal(nextProps.usersTyping, usersTyping)) {
|
||||
if (!dequal(nextProps.usersTyping, usersTyping)) {
|
||||
return true;
|
||||
}
|
||||
if (nextProps.goRoomActionsView !== goRoomActionsView) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue