diff --git a/.circleci/config.yml b/.circleci/config.yml
index c2195c3b2..3e3a66b87 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -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-hold-build-experimental:
+ type: approval
+ requires:
+ - lint-testunit
- ios-build-experimental:
requires:
- - lint-testunit
+ - ios-hold-build-experimental
- ios-hold-testflight-experimental:
type: approval
requires:
@@ -444,9 +452,13 @@ workflows:
- ios-hold-testflight-official
# Android Experimental
+ - android-hold-build-experimental:
+ type: approval
+ requires:
+ - lint-testunit
- android-build-experimental:
requires:
- - lint-testunit
+ - android-hold-build-experimental
- android-hold-google-play-beta-experimental:
type: approval
requires:
diff --git a/.eslintrc.js b/.eslintrc.js
index 44e4eaf4c..add372a0b 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -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
diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap
index 17bfb4dd2..0eb72257c 100644
--- a/__tests__/__snapshots__/Storyshots.test.js.snap
+++ b/__tests__/__snapshots__/Storyshots.test.js.snap
@@ -44404,6 +44404,309 @@ exports[`Storyshots Message list message 1`] = `
+
+ Toggle e2e encryption
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This room's encryption has been disabled by diego.mello
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This room's encryption has been enabled by diego.mello
+
+
+
+
+
+
diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js
index 0d18c9445..1fd8679bc 100644
--- a/app/actions/actionsTypes.js
+++ b/app/actions/actionsTypes.js
@@ -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']);
diff --git a/app/actions/permissions.js b/app/actions/permissions.js
new file mode 100644
index 000000000..88179c34f
--- /dev/null
+++ b/app/actions/permissions.js
@@ -0,0 +1,8 @@
+import * as types from './actionsTypes';
+
+export function setPermissions(permissions) {
+ return {
+ type: types.PERMISSIONS.SET,
+ permissions
+ };
+}
diff --git a/app/constants/colors.js b/app/constants/colors.js
index b1fdcd7f3..f29f03d1d 100644
--- a/app/constants/colors.js
+++ b/app/constants/colors.js
@@ -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
}
};
diff --git a/app/constants/settings.js b/app/constants/settings.js
index 359dff092..9f0df8865 100644
--- a/app/constants/settings.js
+++ b/app/constants/settings.js
@@ -101,6 +101,9 @@ export default {
Jitsi_Enabled_TokenAuth: {
type: 'valueAsBoolean'
},
+ Jitsi_URL_Room_Hash: {
+ type: 'valueAsBoolean'
+ },
Jitsi_URL_Room_Prefix: {
type: 'valueAsString'
},
diff --git a/app/containers/ActionSheet/ActionSheet.js b/app/containers/ActionSheet/ActionSheet.js
index 81b3496ae..af98a75ce 100644
--- a/app/containers/ActionSheet/ActionSheet.js
+++ b/app/containers/ActionSheet/ActionSheet.js
@@ -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
});
diff --git a/app/containers/Avatar/index.js b/app/containers/Avatar/index.js
index 73314a17d..4d7c6d7a5 100644
--- a/app/containers/Avatar/index.js
+++ b/app/containers/Avatar/index.js
@@ -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 {
diff --git a/app/containers/EmojiPicker/index.js b/app/containers/EmojiPicker/index.js
index 65a973715..89446c067 100644
--- a/app/containers/EmojiPicker/index.js
+++ b/app/containers/EmojiPicker/index.js
@@ -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) {
diff --git a/app/containers/InAppNotification/index.js b/app/containers/InAppNotification/index.js
index dd6573f33..7d22b127a 100644
--- a/app/containers/InAppNotification/index.js
+++ b/app/containers/InAppNotification/index.js
@@ -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 ;
+}, (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);
diff --git a/app/containers/MessageActions/Header.js b/app/containers/MessageActions/Header.js
index ab011e445..78aa9ae3d 100644
--- a/app/containers/MessageActions/Header.js
+++ b/app/containers/MessageActions/Header.js
@@ -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;
diff --git a/app/containers/MessageActions/index.js b/app/containers/MessageActions/index.js
index 0ce32bb71..cf0ba6918 100644
--- a/app/containers/MessageActions/index.js
+++ b/app/containers/MessageActions/index.js
@@ -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);
diff --git a/app/containers/MessageBox/CommandsPreview/index.js b/app/containers/MessageBox/CommandsPreview/index.js
index 1b6a27acd..1bb03ca8b 100644
--- a/app/containers/MessageBox/CommandsPreview/index.js
+++ b/app/containers/MessageBox/CommandsPreview/index.js
@@ -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;
diff --git a/app/containers/MessageBox/Mentions/index.js b/app/containers/MessageBox/Mentions/index.js
index 37c30c30b..fb845b2ad 100644
--- a/app/containers/MessageBox/Mentions/index.js
+++ b/app/containers/MessageBox/Mentions/index.js
@@ -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;
diff --git a/app/containers/MessageBox/ReplyPreview.js b/app/containers/MessageBox/ReplyPreview.js
index e1fcbcca7..eee81236d 100644
--- a/app/containers/MessageBox/ReplyPreview.js
+++ b/app/containers/MessageBox/ReplyPreview.js
@@ -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(({
>
- {message.u?.username}
+ {useRealName ? message.u?.name : message.u?.username}
{time}
);
-}, (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);
diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js
index 12fad3f15..9a075e31a 100644
--- a/app/containers/MessageBox/index.js
+++ b/app/containers/MessageBox/index.js
@@ -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'
diff --git a/app/containers/MessageErrorActions.js b/app/containers/MessageErrorActions.js
index c9975b0e3..e84a99048 100644
--- a/app/containers/MessageErrorActions.js
+++ b/app/containers/MessageErrorActions.js
@@ -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());
diff --git a/app/containers/Toast.js b/app/containers/Toast.js
index eed7cfd7c..4b982fb49 100644
--- a/app/containers/Toast.js
+++ b/app/containers/Toast.js
@@ -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;
diff --git a/app/containers/TwoFactor/index.js b/app/containers/TwoFactor/index.js
index c6ad5467a..deb5c25db 100644
--- a/app/containers/TwoFactor/index.js
+++ b/app/containers/TwoFactor/index.js
@@ -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 = () => {
diff --git a/app/containers/UIKit/MultiSelect/index.js b/app/containers/UIKit/MultiSelect/index.js
index b88330e4e..246a44ec0 100644
--- a/app/containers/UIKit/MultiSelect/index.js
+++ b/app/containers/UIKit/MultiSelect/index.js
@@ -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(({
>
-
+
{showContent ? renderContent() : null}
diff --git a/app/containers/UIKit/MultiSelect/styles.js b/app/containers/UIKit/MultiSelect/styles.js
index f261932c3..8bc2c2124 100644
--- a/app/containers/UIKit/MultiSelect/styles.js
+++ b/app/containers/UIKit/MultiSelect/styles.js
@@ -8,10 +8,6 @@ export default StyleSheet.create({
alignItems: 'center',
justifyContent: 'flex-end'
},
- backdrop: {
- ...StyleSheet.absoluteFill,
- opacity: 0.3
- },
modal: {
height: 300,
width: '100%',
diff --git a/app/containers/message/Attachments.js b/app/containers/message/Attachments.js
index 3d4ff48b4..0d068e9fd 100644
--- a/app/containers/message/Attachments.js
+++ b/app/containers/message/Attachments.js
@@ -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 ;
});
-}, (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,
diff --git a/app/containers/message/Audio.js b/app/containers/message/Audio.js
index fad195890..e22dff408 100644
--- a/app/containers/message/Audio.js
+++ b/app/containers/message/Audio.js
@@ -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) {
diff --git a/app/containers/message/Content.js b/app/containers/message/Content.js
index c963fed5c..2f29bf4ac 100644
--- a/app/containers/message/Content.js
+++ b/app/containers/message/Content.js
@@ -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;
diff --git a/app/containers/message/Image.js b/app/containers/message/Image.js
index 1471b449e..6f466b3b3 100644
--- a/app/containers/message/Image.js
+++ b/app/containers/message/Image.js
@@ -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(({
);
-}, (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,
diff --git a/app/containers/message/Message.js b/app/containers/message/Message.js
index bb59dbee0..104244740 100644
--- a/app/containers/message/Message.js
+++ b/app/containers/message/Message.js
@@ -119,7 +119,7 @@ const MessageTouchable = React.memo((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
};
diff --git a/app/containers/message/Reply.js b/app/containers/message/Reply.js
index fba4c0fbd..88c748baa 100644
--- a/app/containers/message/Reply.js
+++ b/app/containers/message/Reply.js
@@ -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 }) => {
))}
);
-}, (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(({
/>
@@ -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,
diff --git a/app/containers/message/Urls.js b/app/containers/message/Urls.js
index 946433d43..742b2f478 100644
--- a/app/containers/message/Urls.js
+++ b/app/containers/message/Urls.js
@@ -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 }) => {
>
);
-}, (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) => (
));
-}, (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
diff --git a/app/containers/message/Video.js b/app/containers/message/Video.js
index 2e9ef6c2a..fed0c67f2 100644
--- a/app/containers/message/Video.js
+++ b/app/containers/message/Video.js
@@ -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(({
>
);
-}, (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,
diff --git a/app/containers/message/utils.js b/app/containers/message/utils.js
index 325d3d764..22f4d90ca 100644
--- a/app/containers/message/utils.js
+++ b/app/containers/message/utils.js
@@ -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 '';
};
diff --git a/app/ee/omnichannel/views/QueueListView.js b/app/ee/omnichannel/views/QueueListView.js
index 34a307699..a261d481e 100644
--- a/app/ee/omnichannel/views/QueueListView.js
+++ b/app/ee/omnichannel/views/QueueListView.js
@@ -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;
}
diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js
index c35da810b..02adcb460 100644
--- a/app/i18n/locales/en.js
+++ b/app/i18n/locales/en.js
@@ -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}}'
};
diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js
index 3819a3b3b..e3dc8e106 100644
--- a/app/i18n/locales/pt-BR.js
+++ b/app/i18n/locales/pt-BR.js
@@ -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}}'
};
diff --git a/app/i18n/locales/ru.js b/app/i18n/locales/ru.js
index 4d37232bf..fcbcd5e47 100644
--- a/app/i18n/locales/ru.js
+++ b/app/i18n/locales/ru.js
@@ -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}}'
};
diff --git a/app/index.js b/app/index.js
index 8bf338dad..048323a5b 100644
--- a/app/index.js
+++ b/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) => {
diff --git a/app/lib/encryption/encryption.js b/app/lib/encryption/encryption.js
index 972f2c588..e4a7f2c14 100644
--- a/app/lib/encryption/encryption.js
+++ b/app/lib/encryption/encryption.js
@@ -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
diff --git a/app/lib/encryption/room.js b/app/lib/encryption/room.js
index 835bd9ab5..0aa1b932e 100644
--- a/app/lib/encryption/room.js
+++ b/app/lib/encryption/room.js
@@ -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);
diff --git a/app/lib/methods/callJitsi.js b/app/lib/methods/callJitsi.js
index 7410ed8bd..7275ef812 100644
--- a/app/lib/methods/callJitsi.js
+++ b/app/lib/methods/callJitsi.js
@@ -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;
diff --git a/app/lib/methods/canOpenRoom.js b/app/lib/methods/canOpenRoom.js
index b85728727..c233899b8 100644
--- a/app/lib/methods/canOpenRoom.js
+++ b/app/lib/methods/canOpenRoom.js
@@ -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
diff --git a/app/lib/methods/enterpriseModules.js b/app/lib/methods/enterpriseModules.js
index 5cb68e7a9..35c6672d2 100644
--- a/app/lib/methods/enterpriseModules.js
+++ b/app/lib/methods/enterpriseModules.js
@@ -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) => {
diff --git a/app/lib/methods/getCustomEmojis.js b/app/lib/methods/getCustomEmojis.js
index 0037168df..31108fb62 100644
--- a/app/lib/methods/getCustomEmojis.js
+++ b/app/lib/methods/getCustomEmojis.js
@@ -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,18 +94,16 @@ 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 });
+ let { emojis } = result;
+ emojis = emojis.filter(emoji => !updatedSince || emoji._updatedAt > updatedSince);
+ const changedEmojis = await updateEmojis({ update: emojis, allRecords });
- // `setCustomEmojis` is fired on selectServer
- // We run it again only if emojis were changed
- if (changedEmojis) {
- setCustomEmojis();
- }
- return resolve();
- });
+ // `setCustomEmojis` is fired on selectServer
+ // We run it again only if emojis were changed
+ if (changedEmojis) {
+ setCustomEmojis();
+ }
+ return resolve();
} else {
const params = {};
if (updatedSince) {
@@ -120,17 +117,15 @@ export function getCustomEmojis() {
return resolve();
}
- InteractionManager.runAfterInteractions(async() => {
- const { emojis } = result;
- const { update, remove } = emojis;
- const changedEmojis = await updateEmojis({ update, remove, allRecords });
+ const { emojis } = result;
+ const { update, remove } = emojis;
+ const changedEmojis = await updateEmojis({ update, remove, allRecords });
- // `setCustomEmojis` is fired on selectServer
- // We run it again only if emojis were changed
- if (changedEmojis) {
- setCustomEmojis();
- }
- });
+ // `setCustomEmojis` is fired on selectServer
+ // We run it again only if emojis were changed
+ if (changedEmojis) {
+ setCustomEmojis();
+ }
}
} catch (e) {
log(e);
diff --git a/app/lib/methods/getPermissions.js b/app/lib/methods/getPermissions.js
index 249441582..15ee221b8 100644
--- a/app/lib/methods/getPermissions.js
+++ b/app/lib/methods/getPermissions.js
@@ -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 });
- return resolve();
- });
+ 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 });
- return resolve();
- });
+ const changePermissions = await updatePermissions({ update: result.update, remove: result.delete, allRecords });
+ if (changePermissions) {
+ setPermissions();
+ }
+ return resolve();
}
} catch (e) {
log(e);
diff --git a/app/lib/methods/getRoles.js b/app/lib/methods/getRoles.js
index 3856d64f4..df22d382b 100644
--- a/app/lib/methods/getRoles.js
+++ b/app/lib/methods/getRoles.js
@@ -1,4 +1,3 @@
-import { InteractionManager } from 'react-native';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import database from '../database';
@@ -19,43 +18,41 @@ export default function() {
const { roles } = result;
if (roles && roles.length) {
- InteractionManager.runAfterInteractions(async() => {
- await db.action(async() => {
- const rolesCollections = db.collections.get('roles');
- const allRolesRecords = await rolesCollections.query().fetch();
+ await db.action(async() => {
+ const rolesCollections = db.get('roles');
+ const allRolesRecords = await rolesCollections.query().fetch();
- // filter roles
- let rolesToCreate = roles.filter(i1 => !allRolesRecords.find(i2 => i1._id === i2.id));
- let rolesToUpdate = allRolesRecords.filter(i1 => roles.find(i2 => i1.id === i2._id));
+ // filter roles
+ let rolesToCreate = roles.filter(i1 => !allRolesRecords.find(i2 => i1._id === i2.id));
+ let rolesToUpdate = allRolesRecords.filter(i1 => roles.find(i2 => i1.id === i2._id));
- // Create
- rolesToCreate = rolesToCreate.map(role => rolesCollections.prepareCreate(protectedFunction((r) => {
- r._raw = sanitizedRaw({ id: role._id }, rolesCollections.schema);
- Object.assign(r, role);
- })));
+ // Create
+ rolesToCreate = rolesToCreate.map(role => rolesCollections.prepareCreate(protectedFunction((r) => {
+ r._raw = sanitizedRaw({ id: role._id }, rolesCollections.schema);
+ Object.assign(r, role);
+ })));
- // Update
- rolesToUpdate = rolesToUpdate.map((role) => {
- const newRole = roles.find(r => r._id === role.id);
- return role.prepareUpdate(protectedFunction((r) => {
- Object.assign(r, newRole);
- }));
- });
-
- const allRecords = [
- ...rolesToCreate,
- ...rolesToUpdate
- ];
-
- try {
- await db.batch(...allRecords);
- } catch (e) {
- log(e);
- }
- return allRecords.length;
+ // Update
+ rolesToUpdate = rolesToUpdate.map((role) => {
+ const newRole = roles.find(r => r._id === role.id);
+ return role.prepareUpdate(protectedFunction((r) => {
+ Object.assign(r, newRole);
+ }));
});
- return resolve();
+
+ const allRecords = [
+ ...rolesToCreate,
+ ...rolesToUpdate
+ ];
+
+ try {
+ await db.batch(...allRecords);
+ } catch (e) {
+ log(e);
+ }
+ return allRecords.length;
});
+ return resolve();
}
} catch (e) {
log(e);
diff --git a/app/lib/methods/getSettings.js b/app/lib/methods/getSettings.js
index f433f4a9a..6935631ef 100644
--- a/app/lib/methods/getSettings.js
+++ b/app/lib/methods/getSettings.js
@@ -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();
diff --git a/app/lib/methods/getSlashCommands.js b/app/lib/methods/getSlashCommands.js
index 729e8b4a9..c37e077f4 100644
--- a/app/lib/methods/getSlashCommands.js
+++ b/app/lib/methods/getSlashCommands.js
@@ -1,4 +1,3 @@
-import { InteractionManager } from 'react-native';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import database from '../database';
@@ -20,47 +19,45 @@ 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 allSlashCommandsRecords = await slashCommandsCollection.query().fetch();
+ await db.action(async() => {
+ const slashCommandsCollection = db.get('slash_commands');
+ const allSlashCommandsRecords = await slashCommandsCollection.query().fetch();
- // filter slash commands
- let slashCommandsToCreate = commands.filter(i1 => !allSlashCommandsRecords.find(i2 => i1.command === i2.id));
- let slashCommandsToUpdate = allSlashCommandsRecords.filter(i1 => commands.find(i2 => i1.id === i2.command));
- let slashCommandsToDelete = allSlashCommandsRecords
- .filter(i1 => !slashCommandsToCreate.find(i2 => i2.command === i1.id) && !slashCommandsToUpdate.find(i2 => i2.id === i1.id));
+ // filter slash commands
+ let slashCommandsToCreate = commands.filter(i1 => !allSlashCommandsRecords.find(i2 => i1.command === i2.id));
+ let slashCommandsToUpdate = allSlashCommandsRecords.filter(i1 => commands.find(i2 => i1.id === i2.command));
+ let slashCommandsToDelete = allSlashCommandsRecords
+ .filter(i1 => !slashCommandsToCreate.find(i2 => i2.command === i1.id) && !slashCommandsToUpdate.find(i2 => i2.id === i1.id));
- // Create
- slashCommandsToCreate = slashCommandsToCreate.map(command => slashCommandsCollection.prepareCreate(protectedFunction((s) => {
- s._raw = sanitizedRaw({ id: command.command }, slashCommandsCollection.schema);
- Object.assign(s, command);
- })));
+ // Create
+ slashCommandsToCreate = slashCommandsToCreate.map(command => slashCommandsCollection.prepareCreate(protectedFunction((s) => {
+ s._raw = sanitizedRaw({ id: command.command }, slashCommandsCollection.schema);
+ Object.assign(s, command);
+ })));
- // Update
- slashCommandsToUpdate = slashCommandsToUpdate.map((command) => {
- const newCommand = commands.find(s => s.command === command.id);
- return command.prepareUpdate(protectedFunction((s) => {
- Object.assign(s, newCommand);
- }));
- });
-
- // Delete
- slashCommandsToDelete = slashCommandsToDelete.map(command => command.prepareDestroyPermanently());
-
- const allRecords = [
- ...slashCommandsToCreate,
- ...slashCommandsToUpdate,
- ...slashCommandsToDelete
- ];
-
- try {
- await db.batch(...allRecords);
- } catch (e) {
- log(e);
- }
- return allRecords.length;
+ // Update
+ slashCommandsToUpdate = slashCommandsToUpdate.map((command) => {
+ const newCommand = commands.find(s => s.command === command.id);
+ return command.prepareUpdate(protectedFunction((s) => {
+ Object.assign(s, newCommand);
+ }));
});
+
+ // Delete
+ slashCommandsToDelete = slashCommandsToDelete.map(command => command.prepareDestroyPermanently());
+
+ const allRecords = [
+ ...slashCommandsToCreate,
+ ...slashCommandsToUpdate,
+ ...slashCommandsToDelete
+ ];
+
+ try {
+ await db.batch(...allRecords);
+ } catch (e) {
+ log(e);
+ }
+ return allRecords.length;
});
}
} catch (e) {
diff --git a/app/lib/methods/getUsersPresence.js b/app/lib/methods/getUsersPresence.js
index 68d9b1eaa..83e5ce815 100644
--- a/app/lib/methods/getUsersPresence.js
+++ b/app/lib/methods/getUsersPresence.js
@@ -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);
diff --git a/app/lib/methods/helpers/findSubscriptionsRooms.js b/app/lib/methods/helpers/findSubscriptionsRooms.js
index f1714932d..a64a88940 100644
--- a/app/lib/methods/helpers/findSubscriptionsRooms.js
+++ b/app/lib/methods/helpers/findSubscriptionsRooms.js
@@ -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();
diff --git a/app/lib/methods/helpers/mergeSubscriptionsRooms.js b/app/lib/methods/helpers/mergeSubscriptionsRooms.js
index 1bc3f18b2..1e5afa3b2 100644
--- a/app/lib/methods/helpers/mergeSubscriptionsRooms.js
+++ b/app/lib/methods/helpers/mergeSubscriptionsRooms.js
@@ -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;
}
- // 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;
+ 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;
diff --git a/app/lib/methods/loadMissedMessages.js b/app/lib/methods/loadMissedMessages.js
index 82c2e1bda..d1acaf0b4 100644
--- a/app/lib/methods/loadMissedMessages.js
+++ b/app/lib/methods/loadMissedMessages.js
@@ -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) {
diff --git a/app/lib/methods/loadThreadMessages.js b/app/lib/methods/loadThreadMessages.js
index 4dc8a05fd..de6f244c6 100644
--- a/app/lib/methods/loadThreadMessages.js
+++ b/app/lib/methods/loadThreadMessages.js
@@ -1,4 +1,3 @@
-import { InteractionManager } from 'react-native';
import { Q } from '@nozbe/watermelondb';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
@@ -30,44 +29,42 @@ export default function loadThreadMessages({ tmid, rid, offset = 0 }) {
let data = await load.call(this, { tmid, offset });
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 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));
+ try {
+ data = data.map(m => buildMessage(m));
+ data = await Encryption.decryptMessages(data);
+ const db = database.active;
+ 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));
- threadMessagesToCreate = threadMessagesToCreate.map(threadMessage => threadMessagesCollection.prepareCreate(protectedFunction((tm) => {
- tm._raw = sanitizedRaw({ id: threadMessage._id }, threadMessagesCollection.schema);
- Object.assign(tm, threadMessage);
- tm.subscription.id = rid;
+ threadMessagesToCreate = threadMessagesToCreate.map(threadMessage => threadMessagesCollection.prepareCreate(protectedFunction((tm) => {
+ tm._raw = sanitizedRaw({ id: threadMessage._id }, threadMessagesCollection.schema);
+ Object.assign(tm, threadMessage);
+ tm.subscription.id = rid;
+ tm.rid = threadMessage.tmid;
+ delete threadMessage.tmid;
+ })));
+
+ threadMessagesToUpdate = threadMessagesToUpdate.map((threadMessage) => {
+ const newThreadMessage = data.find(t => t._id === threadMessage.id);
+ return threadMessage.prepareUpdate(protectedFunction((tm) => {
+ Object.assign(tm, newThreadMessage);
tm.rid = threadMessage.tmid;
delete threadMessage.tmid;
- })));
+ }));
+ });
- threadMessagesToUpdate = threadMessagesToUpdate.map((threadMessage) => {
- const newThreadMessage = data.find(t => t._id === threadMessage.id);
- return threadMessage.prepareUpdate(protectedFunction((tm) => {
- Object.assign(tm, newThreadMessage);
- tm.rid = threadMessage.tmid;
- delete threadMessage.tmid;
- }));
- });
-
- await db.action(async() => {
- await db.batch(
- ...threadMessagesToCreate,
- ...threadMessagesToUpdate
- );
- });
- } catch (e) {
- log(e);
- }
- return resolve(data);
- });
+ await db.action(async() => {
+ await db.batch(
+ ...threadMessagesToCreate,
+ ...threadMessagesToUpdate
+ );
+ });
+ } catch (e) {
+ log(e);
+ }
+ return resolve(data);
} else {
return resolve([]);
}
diff --git a/app/lib/methods/logout.js b/app/lib/methods/logout.js
index 0a1923de0..bb6950835 100644
--- a/app/lib/methods/logout.js
+++ b/app/lib/methods/logout.js
@@ -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());
diff --git a/app/lib/methods/readMessages.js b/app/lib/methods/readMessages.js
index e813a68b4..137612648 100644
--- a/app/lib/methods/readMessages.js
+++ b/app/lib/methods/readMessages.js
@@ -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 });
diff --git a/app/lib/methods/sendFileMessage.js b/app/lib/methods/sendFileMessage.js
index 923da1723..435e377cb 100644
--- a/app/lib/methods/sendFileMessage.js
+++ b/app/lib/methods/sendFileMessage.js
@@ -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);
diff --git a/app/lib/methods/sendMessage.js b/app/lib/methods/sendMessage.js
index a9a86ae60..9b1bc6d10 100644
--- a/app/lib/methods/sendMessage.js
+++ b/app/lib/methods/sendMessage.js
@@ -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;
diff --git a/app/lib/methods/subscriptions/room.js b/app/lib/methods/subscriptions/room.js
index 71ac3dfee..885c2469a 100644
--- a/app/lib/methods/subscriptions/room.js
+++ b/app/lib/methods/subscriptions/room.js
@@ -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);
diff --git a/app/lib/methods/subscriptions/rooms.js b/app/lib/methods/subscriptions/rooms.js
index 42918da13..b79e422a6 100644
--- a/app/lib/methods/subscriptions/rooms.js
+++ b/app/lib/methods/subscriptions/rooms.js
@@ -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 {
diff --git a/app/lib/methods/updateMessages.js b/app/lib/methods/updateMessages.js
index 0dc63e9ba..5f0db0f66 100644
--- a/app/lib/methods/updateMessages.js
+++ b/app/lib/methods/updateMessages.js
@@ -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();
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index 4f9952b80..693c5eb78 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -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 {
- rid: sub.rid,
- name: sub.name,
- fname: sub.fname,
- avatarETag: sub.avatarETag,
- t: sub.t,
- encrypted: sub.encrypted
- };
- }
- return sub;
- });
+ data = data.map(sub => ({
+ rid: sub.rid,
+ name: sub.name,
+ fname: sub.fname,
+ avatarETag: sub.avatarETag,
+ t: sub.t,
+ 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;
diff --git a/app/presentation/RoomItem/LastMessage.js b/app/presentation/RoomItem/LastMessage.js
index b29cb6cd1..25fc2c08d 100644
--- a/app/presentation/RoomItem/LastMessage.js
+++ b/app/presentation/RoomItem/LastMessage.js
@@ -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
diff --git a/app/reducers/index.js b/app/reducers/index.js
index 6211ccb6f..dfee5f3eb 100644
--- a/app/reducers/index.js
+++ b/app/reducers/index.js
@@ -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
});
diff --git a/app/reducers/permissions.js b/app/reducers/permissions.js
new file mode 100644
index 000000000..1b3a14ec2
--- /dev/null
+++ b/app/reducers/permissions.js
@@ -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;
+ }
+}
diff --git a/app/sagas/createChannel.js b/app/sagas/createChannel.js
index ca4bcb9fe..613aedec2 100644
--- a/app/sagas/createChannel.js
+++ b/app/sagas/createChannel.js
@@ -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));
}
};
diff --git a/app/sagas/createDiscussion.js b/app/sagas/createDiscussion.js
index a185e6671..746ab1583 100644
--- a/app/sagas/createDiscussion.js
+++ b/app/sagas/createDiscussion.js
@@ -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));
}
};
diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js
index caf2a46f9..21e0695fb 100644
--- a/app/sagas/deepLinking.js
+++ b/app/sagas/deepLinking.js
@@ -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
};
- yield goRoom({ item, isMasterDetail });
+
+ const isMasterDetail = yield select(state => state.app.isMasterDetail);
+ const focusedRooms = yield select(state => state.room.rooms);
+
+ if (focusedRooms.includes(room.rid)) {
+ // if there's one room on the list or last room is the one
+ if (focusedRooms.length === 1 || focusedRooms[0] === room.rid) {
+ yield goRoom({ item, isMasterDetail });
+ } else {
+ popToRoot({ isMasterDetail });
+ yield goRoom({ item, isMasterDetail });
+ }
+ } else {
+ popToRoot({ isMasterDetail });
+ yield goRoom({ item, isMasterDetail });
+ }
if (params.isCall) {
- 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;
diff --git a/app/sagas/encryption.js b/app/sagas/encryption.js
index f28c77834..395923a43 100644
--- a/app/sagas/encryption.js
+++ b/app/sagas/encryption.js
@@ -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);
diff --git a/app/sagas/init.js b/app/sagas/init.js
index 5c0cb7286..03a6c5a78 100644
--- a/app/sagas/init.js
+++ b/app/sagas/init.js
@@ -38,7 +38,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 {
diff --git a/app/sagas/login.js b/app/sagas/login.js
index cf487eed4..b5c267263 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -57,7 +57,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();
@@ -145,7 +145,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,
@@ -222,7 +222,7 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
} else {
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
diff --git a/app/sagas/messages.js b/app/sagas/messages.js
index 2879b2b64..2674be42e 100644
--- a/app/sagas/messages.js
+++ b/app/sagas/messages.js
@@ -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);
diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js
index 94670ea1f..f963a2948 100644
--- a/app/sagas/rooms.js
+++ b/app/sagas/rooms.js
@@ -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) {
diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js
index 87fb47549..d367274a3 100644
--- a/app/sagas/selectServer.js
+++ b/app/sagas/selectServer.js
@@ -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);
diff --git a/app/sagas/state.js b/app/sagas/state.js
index 8b072c0a9..5b86af2ef 100644
--- a/app/sagas/state.js
+++ b/app/sagas/state.js
@@ -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);
diff --git a/app/stacks/InsideStack.js b/app/stacks/InsideStack.js
index 75d01708e..a0af96177 100644
--- a/app/stacks/InsideStack.js
+++ b/app/stacks/InsideStack.js
@@ -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 = () => (
- }
- drawerPosition={I18nManager.isRTL ? 'right' : 'left'}
- screenOptions={{ swipeEnabled: false }}
- drawerType='back'
- >
-
-
-
-
-
-);
+const DrawerNavigator = () => {
+ const { theme } = React.useContext(ThemeContext);
+
+ return (
+ }
+ drawerPosition={I18nManager.isRTL ? 'right' : 'left'}
+ screenOptions={{ swipeEnabled: false }}
+ drawerType='back'
+ overlayColor={`rgba(0,0,0,${ themes[theme].backdropOpacity })`}
+ >
+
+
+
+
+
+ );
+};
// NewMessageStackNavigator
const NewMessageStack = createStackNavigator();
diff --git a/app/utils/isReadOnly.js b/app/utils/isReadOnly.js
index 7d35a2836..f6f1937f6 100644
--- a/app/utils/isReadOnly.js
+++ b/app/utils/isReadOnly.js
@@ -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;
}
diff --git a/app/utils/localAuthentication.js b/app/utils/localAuthentication.js
index 2eb9c7d29..6e6d40dfb 100644
--- a/app/utils/localAuthentication.js
+++ b/app/utils/localAuthentication.js
@@ -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
diff --git a/app/utils/log/events.js b/app/utils/log/events.js
index 4895ad8f2..2e1e8f9a4 100644
--- a/app/utils/log/events.js
+++ b/app/utils/log/events.js
@@ -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',
diff --git a/app/views/AttachmentView.js b/app/views/AttachmentView.js
index 3f4adfec7..c07ed71b9 100644
--- a/app/views/AttachmentView.js
+++ b/app/views/AttachmentView.js
@@ -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') });
diff --git a/app/views/ChangePasscodeView.js b/app/views/ChangePasscodeView.js
index 308e2d782..535ba71d4 100644
--- a/app/views/ChangePasscodeView.js
+++ b/app/views/ChangePasscodeView.js
@@ -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);
});
}, []);
diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js
index 70982094d..9d1e450ff 100644
--- a/app/views/CreateChannelView.js
+++ b/app/views/CreateChannelView.js
@@ -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 = () =>
-
- renderFormSeparator = () => {
- const { theme } = this.props;
- return ;
- }
-
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()}
+
{this.renderType()}
- {this.renderFormSeparator()}
+
{this.renderReadOnly()}
- {this.renderFormSeparator()}
+
{this.renderEncrypted()}
- {this.renderFormSeparator()}
+
{this.renderBroadcast()}
diff --git a/app/views/CreateDiscussionView/SelectUsers.js b/app/views/CreateDiscussionView/SelectUsers.js
index a88773ab0..8500ee15c 100644
--- a/app/views/CreateDiscussionView/SelectUsers.js
+++ b/app/views/CreateDiscussionView/SelectUsers.js
@@ -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();
diff --git a/app/views/CreateDiscussionView/index.js b/app/views/CreateDiscussionView/index.js
index 43329f102..2970ba016 100644
--- a/app/views/CreateDiscussionView/index.js
+++ b/app/views/CreateDiscussionView/index.js
@@ -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
});
diff --git a/app/views/DirectoryView/Options.js b/app/views/DirectoryView/Options.js
index ab69b11f4..ad8de1da1 100644
--- a/app/views/DirectoryView/Options.js
+++ b/app/views/DirectoryView/Options.js
@@ -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 (
<>
diff --git a/app/views/DirectoryView/index.js b/app/views/DirectoryView/index.js
index 8be78712d..afb1214f3 100644
--- a/app/views/DirectoryView/index.js
+++ b/app/views/DirectoryView/index.js
@@ -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 ;
- }
-
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 ? : null}
onEndReached={() => this.load({})}
diff --git a/app/views/LanguageView/index.js b/app/views/LanguageView/index.js
index 435762d21..b2e6e1079 100644
--- a/app/views/LanguageView/index.js
+++ b/app/views/LanguageView/index.js
@@ -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);
diff --git a/app/views/LoginView.js b/app/views/LoginView.js
index bdffd103b..8e9791d36 100644
--- a/app/views/LoginView.js
+++ b/app/views/LoginView.js
@@ -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'));
}
}
diff --git a/app/views/MessagesView/index.js b/app/views/MessagesView/index.js
index 678d28f29..fc840b2d6 100644
--- a/app/views/MessagesView/index.js
+++ b/app/views/MessagesView/index.js
@@ -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) {
diff --git a/app/views/ModalBlockView.js b/app/views/ModalBlockView.js
index 2d72fa339..85eee8492 100644
--- a/app/views/ModalBlockView.js
+++ b/app/views/ModalBlockView.js
@@ -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 ?? {};
diff --git a/app/views/NewMessageView.js b/app/views/NewMessageView.js
index be47513bc..7e2aa2323 100644
--- a/app/views/NewMessageView.js
+++ b/app/views/NewMessageView.js
@@ -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 });
- });
+ this.setState({ chats });
} catch (e) {
log(e);
}
@@ -211,10 +189,6 @@ class NewMessageView extends React.Component {
);
}
- renderSeparator = () => {
- const { theme } = this.props;
- return ;
- }
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'
/>
diff --git a/app/views/NewServerView/index.js b/app/views/NewServerView/index.js
index 192468ed3..27422dec8 100644
--- a/app/views/NewServerView/index.js
+++ b/app/views/NewServerView/index.js
@@ -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');
diff --git a/app/views/ProfileView/index.js b/app/views/ProfileView/index.js
index f4faeec1c..7e97422df 100644
--- a/app/views/ProfileView/index.js
+++ b/app/views/ProfileView/index.js
@@ -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') });
}
}
diff --git a/app/views/ReadReceiptView/index.js b/app/views/ReadReceiptView/index.js
index b7456c77a..f41d7c873 100644
--- a/app/views/ReadReceiptView/index.js
+++ b/app/views/ReadReceiptView/index.js
@@ -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 ;
- }
-
render() {
const { receipts, loading } = this.state;
const { theme } = this.props;
@@ -143,7 +139,7 @@ class ReadReceiptView extends React.Component {
{
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 (t === 'c' && permissions['add-user-to-any-c-room']) {
- canAdd = true;
- }
- if (t === 'p' && permissions['add-user-to-any-p-room']) {
- canAdd = true;
- }
+ if (userInRoom && permissions[0]) {
+ canAddUser = true;
}
- this.setState({ canAddUser: canAdd });
+ if (t === 'c' && permissions[1]) {
+ canAddUser = true;
+ }
+ if (t === 'p' && permissions[2]) {
+ canAddUser = true;
+ }
+ 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 {
RocketChat.callJitsi(room?.rid, true)}
+ onPress={() => RocketChat.callJitsi(room, true)}
testID='room-actions-voice'
left={() => }
showActionIndicator
@@ -490,7 +494,7 @@ class RoomActionsView extends React.Component {
RocketChat.callJitsi(room?.rid)}
+ onPress={() => RocketChat.callJitsi(room)}
testID='room-actions-video'
left={() => }
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 => ({
diff --git a/app/views/RoomInfoEditView/index.js b/app/views/RoomInfoEditView/index.js
index 58664c6f2..04472c82e 100644
--- a/app/views/RoomInfoEditView/index.js
+++ b/app/views/RoomInfoEditView/index.js
@@ -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 => ({
diff --git a/app/views/RoomInfoView/index.js b/app/views/RoomInfoView/index.js
index fd61e0eae..a18476d34 100644
--- a/app/views/RoomInfoView/index.js
+++ b/app/views/RoomInfoView/index.js
@@ -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));
diff --git a/app/views/RoomMembersView/index.js b/app/views/RoomMembersView/index.js
index 269814669..4caa03381 100644
--- a/app/views/RoomMembersView/index.js
+++ b/app/views/RoomMembersView/index.js
@@ -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 {
this.onSearchChangeText(text)} testID='room-members-view-search' />
)
- renderSeparator = () => {
- const { theme } = this.props;
- return ;
- }
-
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)));
diff --git a/app/views/RoomView/Header/Header.js b/app/views/RoomView/Header/Header.js
index 637d57c58..410ca1bf5 100644
--- a/app/views/RoomView/Header/Header.js
+++ b/app/views/RoomView/Header/Header.js
@@ -168,7 +168,7 @@ const Header = React.memo(({
theme={theme}
/>
-
+
);
});
diff --git a/app/views/RoomView/Header/RightButtons.js b/app/views/RoomView/Header/RightButtons.js
index 6580351c8..37939b1e3 100644
--- a/app/views/RoomView/Header/RightButtons.js
+++ b/app/views/RoomView/Header/RightButtons.js
@@ -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() {
diff --git a/app/views/RoomView/Header/index.js b/app/views/RoomView/Header/index.js
index 33663bbe4..05a5f2e12 100644
--- a/app/views/RoomView/Header/index.js
+++ b/app/views/RoomView/Header/index.js
@@ -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) {
diff --git a/app/views/RoomView/List.js b/app/views/RoomView/List.js
index 4db965d97..41d424fa3 100644
--- a/app/views/RoomView/List.js
+++ b/app/views/RoomView/List.js
@@ -3,7 +3,7 @@ import { FlatList, RefreshControl } from 'react-native';
import PropTypes from 'prop-types';
import { Q } from '@nozbe/watermelondb';
import moment from 'moment';
-import isEqual from 'lodash/isEqual';
+import { dequal } from 'dequal';
import styles from './styles';
import database from '../../lib/database';
@@ -89,13 +89,13 @@ class List extends React.Component {
if (refreshing !== nextState.refreshing) {
return true;
}
- if (!isEqual(hideSystemMessages, nextProps.hideSystemMessages)) {
+ if (!dequal(hideSystemMessages, nextProps.hideSystemMessages)) {
return true;
}
- if (!isEqual(tunread, nextProps.tunread)) {
+ if (!dequal(tunread, nextProps.tunread)) {
return true;
}
- if (!isEqual(ignored, nextProps.ignored)) {
+ if (!dequal(ignored, nextProps.ignored)) {
return true;
}
return false;
@@ -103,7 +103,7 @@ class List extends React.Component {
componentDidUpdate(prevProps) {
const { hideSystemMessages } = this.props;
- if (!isEqual(hideSystemMessages, prevProps.hideSystemMessages)) {
+ if (!dequal(hideSystemMessages, prevProps.hideSystemMessages)) {
this.reload();
}
}
diff --git a/app/views/RoomView/ReactionPicker.js b/app/views/RoomView/ReactionPicker.js
index 556d18256..9e9c470f8 100644
--- a/app/views/RoomView/ReactionPicker.js
+++ b/app/views/RoomView/ReactionPicker.js
@@ -7,6 +7,8 @@ import Modal from 'react-native-modal';
import EmojiPicker from '../../containers/EmojiPicker';
import styles from './styles';
import { isAndroid } from '../../utils/deviceInfo';
+import { themes } from '../../constants/colors';
+import { withTheme } from '../../theme';
const margin = isAndroid ? 40 : 20;
const maxSize = 400;
@@ -20,7 +22,8 @@ class ReactionPicker extends React.Component {
reactionClose: PropTypes.func,
onEmojiSelected: PropTypes.func,
width: PropTypes.number,
- height: PropTypes.number
+ height: PropTypes.number,
+ theme: PropTypes.string
};
shouldComponentUpdate(nextProps) {
@@ -38,7 +41,7 @@ class ReactionPicker extends React.Component {
render() {
const {
- width, height, show, baseUrl, reactionClose, isMasterDetail
+ width, height, show, baseUrl, reactionClose, isMasterDetail, theme
} = this.props;
let widthStyle = width - margin;
@@ -58,6 +61,7 @@ class ReactionPicker extends React.Component {
onBackButtonPress={reactionClose}
animationIn='fadeIn'
animationOut='fadeOut'
+ backdropOpacity={themes[theme].backdropOpacity}
>
({
isMasterDetail: state.app.isMasterDetail
});
-export default connect(mapStateToProps)(ReactionPicker);
+export default connect(mapStateToProps)(withTheme(ReactionPicker));
diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js
index 38a39d979..2ee84ae25 100644
--- a/app/views/RoomView/index.js
+++ b/app/views/RoomView/index.js
@@ -7,7 +7,7 @@ import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import moment from 'moment';
import * as Haptics from 'expo-haptics';
import { Q } from '@nozbe/watermelondb';
-import isEqual from 'lodash/isEqual';
+import { dequal } from 'dequal';
import { withSafeAreaInsets } from 'react-native-safe-area-context';
import Touch from '../../utils/touch';
@@ -155,7 +155,9 @@ class RoomView extends React.Component {
this.list = React.createRef();
this.joinCode = React.createRef();
this.mounted = false;
- if (this.rid) {
+
+ // we don't need to subscribe to threads
+ if (this.rid && !this.tmid) {
this.sub = new RoomClass(this.rid);
}
console.timeEnd(`${ this.constructor.name } init`);
@@ -168,7 +170,7 @@ class RoomView extends React.Component {
const { isAuthenticated } = this.props;
this.setHeader();
if (this.rid) {
- this.sub.subscribe();
+ this.sub?.subscribe?.();
if (isAuthenticated) {
this.init();
} else {
@@ -203,10 +205,10 @@ class RoomView extends React.Component {
if (stateUpdated) {
return true;
}
- if (!isEqual(nextProps.insets, insets)) {
+ if (!dequal(nextProps.insets, insets)) {
return true;
}
- return roomAttrsUpdate.some(key => !isEqual(nextState.roomUpdate[key], roomUpdate[key]));
+ return roomAttrsUpdate.some(key => !dequal(nextState.roomUpdate[key], roomUpdate[key]));
}
componentDidUpdate(prevProps, prevState) {
@@ -227,7 +229,7 @@ class RoomView extends React.Component {
}
// If it's a livechat room
if (this.t === 'l') {
- if (!isEqual(prevState.roomUpdate.visitor, roomUpdate.visitor)) {
+ if (!dequal(prevState.roomUpdate.visitor, roomUpdate.visitor)) {
this.setHeader();
}
}
@@ -249,7 +251,7 @@ class RoomView extends React.Component {
let obj;
if (this.tmid) {
try {
- const threadsCollection = db.collections.get('threads');
+ const threadsCollection = db.get('threads');
obj = await threadsCollection.find(this.tmid);
} catch (e) {
// Do nothing
@@ -398,7 +400,7 @@ class RoomView extends React.Component {
const db = database.active;
try {
- const subCollection = db.collections.get('subscriptions');
+ const subCollection = db.get('subscriptions');
const sub = await subCollection.find(this.rid);
const { room } = await RocketChat.getRoomInfo(this.rid);
@@ -434,10 +436,7 @@ class RoomView extends React.Component {
}
}
- // We run `canAutoTranslate` again in order to refetch auto translate permission
- // in case of a missing connection or poor connection on room open
- const canAutoTranslate = await RocketChat.canAutoTranslate();
-
+ const canAutoTranslate = RocketChat.canAutoTranslate();
const member = await this.getRoomMember();
this.setState({ canAutoTranslate, member, loading: false });
@@ -476,7 +475,7 @@ class RoomView extends React.Component {
findAndObserveRoom = async(rid) => {
try {
const db = database.active;
- const subCollection = await db.collections.get('subscriptions');
+ const subCollection = await db.get('subscriptions');
const room = await subCollection.find(rid);
this.setState({ room });
if (!this.tmid) {
@@ -531,7 +530,14 @@ class RoomView extends React.Component {
}
onEditInit = (message) => {
- this.setState({ selectedMessage: message, editing: true });
+ const newMessage = {
+ id: message.id,
+ subscription: {
+ id: message.subscription.id
+ },
+ msg: message?.attachments?.[0]?.description || message.msg
+ };
+ this.setState({ selectedMessage: newMessage, editing: true });
}
onEditCancel = () => {
@@ -744,8 +750,8 @@ class RoomView extends React.Component {
fetchThreadName = async(tmid, messageId) => {
try {
const db = database.active;
- const threadCollection = db.collections.get('threads');
- const messageCollection = db.collections.get('messages');
+ const threadCollection = db.get('threads');
+ const messageCollection = db.get('messages');
const messageRecord = await messageCollection.find(messageId);
let threadRecord;
try {
@@ -815,7 +821,7 @@ class RoomView extends React.Component {
if (jitsiTimeout < Date.now()) {
showErrorAlert(I18n.t('Call_already_ended'));
} else {
- RocketChat.callJitsi(this.rid);
+ RocketChat.callJitsi(room);
}
};
@@ -1082,6 +1088,7 @@ class RoomView extends React.Component {
reactionClose={this.onReactionClose}
width={width}
height={height}
+ theme={theme}
/>
{
- const { theme } = this.props;
- return ;
- }
-
renderServer = ({ item }) => {
const { server, theme } = this.props;
@@ -225,7 +202,7 @@ class ServerDropdown extends Component {
});
const backdropOpacity = this.animatedValue.interpolate({
inputRange: [0, 1],
- outputRange: [0, 0.6]
+ outputRange: [0, themes[theme].backdropOpacity]
});
return (
<>
@@ -266,7 +243,7 @@ class ServerDropdown extends Component {
data={servers}
keyExtractor={item => item.id}
renderItem={this.renderServer}
- ItemSeparatorComponent={this.renderSeparator}
+ ItemSeparatorComponent={List.Separator}
keyboardShouldPersistTaps='always'
/>
diff --git a/app/views/RoomsListView/SortDropdown/index.js b/app/views/RoomsListView/SortDropdown/index.js
index cb85edcd5..06a398a82 100644
--- a/app/views/RoomsListView/SortDropdown/index.js
+++ b/app/views/RoomsListView/SortDropdown/index.js
@@ -124,13 +124,13 @@ class Sort extends PureComponent {
inputRange: [0, 1],
outputRange: [-326, heightDestination]
});
- const backdropOpacity = this.animatedValue.interpolate({
- inputRange: [0, 1],
- outputRange: [0, 0.3]
- });
const {
sortBy, groupByType, showFavorites, showUnread, theme
} = this.props;
+ const backdropOpacity = this.animatedValue.interpolate({
+ inputRange: [0, 1],
+ outputRange: [0, themes[theme].backdropOpacity]
+ });
return (
<>
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index 6ec23f29c..996847c91 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -9,7 +9,7 @@ import {
RefreshControl
} from 'react-native';
import { connect } from 'react-redux';
-import isEqual from 'react-fast-compare';
+import { dequal } from 'dequal';
import Orientation from 'react-native-orientation-locker';
import { Q } from '@nozbe/watermelondb';
import { withSafeAreaInsets } from 'react-native-safe-area-context';
@@ -89,7 +89,6 @@ const shouldUpdateProps = [
'showUnread',
'useRealName',
'StoreLastMessage',
- 'appState',
'theme',
'isMasterDetail',
'refreshing',
@@ -126,7 +125,6 @@ class RoomsListView extends React.Component {
showUnread: PropTypes.bool,
refreshing: PropTypes.bool,
StoreLastMessage: PropTypes.bool,
- appState: PropTypes.string,
theme: PropTypes.string,
toggleSortDropdown: PropTypes.func,
openSearchHeader: PropTypes.func,
@@ -135,7 +133,6 @@ class RoomsListView extends React.Component {
roomsRequest: PropTypes.func,
closeServerDropdown: PropTypes.func,
useRealName: PropTypes.bool,
- connected: PropTypes.bool,
isMasterDetail: PropTypes.bool,
rooms: PropTypes.array,
width: PropTypes.number,
@@ -222,7 +219,7 @@ class RoomsListView extends React.Component {
}
// Compare changes only once
- const chatsNotEqual = !isEqual(nextState.chatsUpdate, chatsUpdate);
+ const chatsNotEqual = !dequal(nextState.chatsUpdate, chatsUpdate);
// If they aren't equal, set to update if focused
if (chatsNotEqual) {
@@ -253,13 +250,13 @@ class RoomsListView extends React.Component {
if (nextProps.width !== width) {
return true;
}
- if (!isEqual(nextState.search, search)) {
+ if (!dequal(nextState.search, search)) {
return true;
}
- if (!isEqual(nextProps.rooms, rooms)) {
+ if (!dequal(nextProps.rooms, rooms)) {
return true;
}
- if (!isEqual(nextProps.insets, insets)) {
+ if (!dequal(nextProps.insets, insets)) {
return true;
}
// If it's focused and there are changes, update
@@ -276,9 +273,6 @@ class RoomsListView extends React.Component {
groupByType,
showFavorites,
showUnread,
- appState,
- connected,
- roomsRequest,
rooms,
isMasterDetail,
insets
@@ -294,15 +288,9 @@ class RoomsListView extends React.Component {
)
) {
this.getSubscriptions();
- } else if (
- appState === 'foreground'
- && appState !== prevProps.appState
- && connected
- ) {
- roomsRequest();
}
// Update current item in case of another action triggers an update on rooms reducer
- if (isMasterDetail && item?.rid !== rooms[0] && !isEqual(rooms, prevProps.rooms)) {
+ if (isMasterDetail && item?.rid !== rooms[0] && !dequal(rooms, prevProps.rooms)) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({ item: { rid: rooms[0] } });
}
@@ -319,6 +307,9 @@ class RoomsListView extends React.Component {
if (this.unsubscribeBlur) {
this.unsubscribeBlur();
}
+ if (this.backHandler && this.backHandler.remove) {
+ this.backHandler.remove();
+ }
if (isTablet) {
EventEmitter.removeListener(KEY_COMMAND, this.handleCommands);
}
@@ -620,7 +611,7 @@ class RoomsListView extends React.Component {
const db = database.active;
const result = await RocketChat.toggleFavorite(rid, !favorite);
if (result.success) {
- const subCollection = db.collections.get('subscriptions');
+ const subCollection = db.get('subscriptions');
await db.action(async() => {
try {
const subRecord = await subCollection.find(rid);
@@ -644,7 +635,7 @@ class RoomsListView extends React.Component {
const db = database.active;
const result = await RocketChat.toggleRead(isRead, rid);
if (result.success) {
- const subCollection = db.collections.get('subscriptions');
+ const subCollection = db.get('subscriptions');
await db.action(async() => {
try {
const subRecord = await subCollection.find(rid);
@@ -668,7 +659,7 @@ class RoomsListView extends React.Component {
const db = database.active;
const result = await RocketChat.hideRoom(rid, type);
if (result.success) {
- const subCollection = db.collections.get('subscriptions');
+ const subCollection = db.get('subscriptions');
await db.action(async() => {
try {
const subRecord = await subCollection.find(rid);
@@ -1018,7 +1009,6 @@ const mapStateToProps = state => ({
isMasterDetail: state.app.isMasterDetail,
server: state.server.server,
changingServer: state.server.changingServer,
- connected: state.server.connected,
searchText: state.rooms.searchText,
loadingServer: state.server.loading,
showServerDropdown: state.rooms.showServerDropdown,
@@ -1029,7 +1019,6 @@ const mapStateToProps = state => ({
showFavorites: state.sortPreferences.showFavorites,
showUnread: state.sortPreferences.showUnread,
useRealName: state.settings.UI_Use_Real_Name,
- appState: state.app.ready && state.app.foreground ? 'foreground' : 'background',
StoreLastMessage: state.settings.Store_Last_Message,
rooms: state.room.rooms,
queueSize: getInquiryQueueSelector(state).length,
diff --git a/app/views/ScreenLockConfigView.js b/app/views/ScreenLockConfigView.js
index bf0f6a15c..294a854a6 100644
--- a/app/views/ScreenLockConfigView.js
+++ b/app/views/ScreenLockConfigView.js
@@ -71,7 +71,7 @@ class ScreenLockConfigView extends React.Component {
init = async() => {
const { server } = this.props;
const serversDB = database.servers;
- const serversCollection = serversDB.collections.get('servers');
+ const serversCollection = serversDB.get('servers');
try {
this.serverRecord = await serversCollection.find(server);
this.setState({
diff --git a/app/views/ScreenLockedView.js b/app/views/ScreenLockedView.js
index 883584056..2b036779c 100644
--- a/app/views/ScreenLockedView.js
+++ b/app/views/ScreenLockedView.js
@@ -31,12 +31,12 @@ const ScreenLockedView = ({ theme }) => {
if (!isTablet) {
Orientation.lockToPortrait();
}
- EventEmitter.addEventListener(LOCAL_AUTHENTICATE_EMITTER, showScreenLock);
+ const listener = EventEmitter.addEventListener(LOCAL_AUTHENTICATE_EMITTER, showScreenLock);
return (() => {
if (!isTablet) {
Orientation.unlockAllOrientations();
}
- EventEmitter.removeListener(LOCAL_AUTHENTICATE_EMITTER);
+ EventEmitter.removeListener(LOCAL_AUTHENTICATE_EMITTER, listener);
});
}, []);
diff --git a/app/views/SearchMessagesView/index.js b/app/views/SearchMessagesView/index.js
index 18380e55f..e11a5b83d 100644
--- a/app/views/SearchMessagesView/index.js
+++ b/app/views/SearchMessagesView/index.js
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { View, FlatList, Text } from 'react-native';
import { Q } from '@nozbe/watermelondb';
import { connect } from 'react-redux';
-import equal from 'deep-equal';
+import { dequal } from 'dequal';
import RCTextInput from '../../containers/TextInput';
import ActivityIndicator from '../../containers/ActivityIndicator';
@@ -42,7 +42,8 @@ class SearchMessagesView extends React.Component {
user: PropTypes.object,
baseUrl: PropTypes.string,
customEmojis: PropTypes.object,
- theme: PropTypes.string
+ theme: PropTypes.string,
+ useRealName: PropTypes.bool
}
constructor(props) {
@@ -68,7 +69,7 @@ class SearchMessagesView extends React.Component {
if (nextState.searchText !== searchText) {
return true;
}
- if (!equal(nextState.messages, messages)) {
+ if (!dequal(nextState.messages, messages)) {
return true;
}
return false;
@@ -83,7 +84,7 @@ class SearchMessagesView extends React.Component {
// If it's a encrypted, room we'll search only on the local stored messages
if (this.encrypted) {
const db = database.active;
- const messagesCollection = db.collections.get('messages');
+ const messagesCollection = db.get('messages');
const likeString = sanitizeLikeString(searchText);
return messagesCollection
.query(
@@ -143,7 +144,9 @@ class SearchMessagesView extends React.Component {
}
renderItem = ({ item }) => {
- const { user, baseUrl, theme } = this.props;
+ const {
+ user, baseUrl, theme, useRealName
+ } = this.props;
return (
{}}
getCustomEmoji={this.getCustomEmoji}
navToRoomInfo={this.navToRoomInfo}
+ useRealName={useRealName}
theme={theme}
/>
);
@@ -206,6 +210,7 @@ class SearchMessagesView extends React.Component {
const mapStateToProps = state => ({
baseUrl: state.server.server,
user: getUserSelector(state),
+ useRealName: state.settings.UI_Use_Real_Name,
customEmojis: state.customEmojis
});
diff --git a/app/views/SelectServerView.js b/app/views/SelectServerView.js
index e04c05247..b3345c820 100644
--- a/app/views/SelectServerView.js
+++ b/app/views/SelectServerView.js
@@ -29,7 +29,7 @@ class SelectServerView extends React.Component {
async componentDidMount() {
const serversDB = database.servers;
- const serversCollection = serversDB.collections.get('servers');
+ const serversCollection = serversDB.get('servers');
const servers = await serversCollection.query(Q.where('rooms_updated_at', Q.notEq(null))).fetch();
this.setState({ servers });
}
diff --git a/app/views/SelectedUsersView.js b/app/views/SelectedUsersView.js
index 666cee3d1..f9d14e169 100644
--- a/app/views/SelectedUsersView.js
+++ b/app/views/SelectedUsersView.js
@@ -1,10 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { View, StyleSheet, FlatList } from 'react-native';
+import { View, FlatList } 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 database from '../lib/database';
import RocketChat from '../lib/rocketchat';
@@ -28,12 +28,6 @@ import {
import { showErrorAlert } from '../utils/info';
import SafeAreaView from '../containers/SafeAreaView';
-const styles = StyleSheet.create({
- separator: {
- marginLeft: 60
- }
-});
-
class SelectedUsersView extends React.Component {
static propTypes = {
baseUrl: PropTypes.string,
@@ -70,27 +64,6 @@ class SelectedUsersView extends React.Component {
this.setHeader(props.route.params?.showButton);
}
- shouldComponentUpdate(nextProps, nextState) {
- const { search, chats } = this.state;
- const { users, loading, theme } = this.props;
- if (nextProps.theme !== theme) {
- return true;
- }
- if (nextProps.loading !== loading) {
- return true;
- }
- if (!equal(nextProps.users, users)) {
- return true;
- }
- if (!equal(nextState.search, search)) {
- return true;
- }
- if (!equal(nextState.chats, chats)) {
- return true;
- }
- return false;
- }
-
componentDidUpdate(prevProps) {
if (this.isGroupChat()) {
const { users } = this.props;
@@ -247,11 +220,6 @@ class SelectedUsersView extends React.Component {
);
}
- renderSeparator = () => {
- const { theme } = this.props;
- return ;
- }
-
renderItem = ({ item, index }) => {
const { search, chats } = this.state;
const { baseUrl, user, theme } = this.props;
@@ -297,7 +265,7 @@ class SelectedUsersView extends React.Component {
extraData={this.props}
keyExtractor={item => item._id}
renderItem={this.renderItem}
- ItemSeparatorComponent={this.renderSeparator}
+ ItemSeparatorComponent={List.Separator}
ListHeaderComponent={this.renderHeader}
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }}
enableEmptySections
diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js
index bc11da7b7..b67c7f1ea 100644
--- a/app/views/SettingsView/index.js
+++ b/app/views/SettingsView/index.js
@@ -62,7 +62,7 @@ class SettingsView extends React.Component {
checkCookiesAndLogout = async() => {
const { logout, user } = this.props;
const db = database.servers;
- const usersCollection = db.collections.get('users');
+ const usersCollection = db.get('users');
try {
const userRecord = await usersCollection.find(user.id);
if (!userRecord.loginEmailPassword) {
diff --git a/app/views/ShareListView/index.js b/app/views/ShareListView/index.js
index aa8cf7f6d..38f3aff72 100644
--- a/app/views/ShareListView/index.js
+++ b/app/views/ShareListView/index.js
@@ -7,7 +7,7 @@ import ShareExtension from 'rn-extensions-share';
import * as FileSystem from 'expo-file-system';
import { connect } from 'react-redux';
import * as mime from 'react-native-mime-types';
-import isEqual from 'react-fast-compare';
+import { dequal } from 'dequal';
import { Q } from '@nozbe/watermelondb';
import database from '../../lib/database';
@@ -118,7 +118,7 @@ class ShareListView extends React.Component {
const { searchResults } = this.state;
if (nextState.searching) {
- if (!isEqual(nextState.searchResults, searchResults)) {
+ if (!dequal(nextState.searchResults, searchResults)) {
return true;
}
}
@@ -206,7 +206,7 @@ class ShareListView extends React.Component {
)
);
}
- const data = await db.collections.get('subscriptions').query(...defaultWhereClause).fetch();
+ const data = await db.get('subscriptions').query(...defaultWhereClause).fetch();
return data.map(item => ({
rid: item.rid,
t: item.t,
@@ -226,7 +226,7 @@ class ShareListView extends React.Component {
if (server) {
const chats = await this.query();
- const serversCollection = serversDB.collections.get('servers');
+ const serversCollection = serversDB.get('servers');
const serversCount = await serversCollection.query(Q.where('rooms_updated_at', Q.notEq(null))).fetchCount();
let serverInfo = {};
try {
diff --git a/app/views/ShareView/Thumbs.js b/app/views/ShareView/Thumbs.js
index 0ce0d7373..26bc54fb4 100644
--- a/app/views/ShareView/Thumbs.js
+++ b/app/views/ShareView/Thumbs.js
@@ -179,6 +179,7 @@ const Thumbs = React.memo(({
/>
);
}
+ return null;
});
Thumbs.propTypes = {
attachments: PropTypes.array,
diff --git a/app/views/ShareView/index.js b/app/views/ShareView/index.js
index 03ea45724..765860036 100644
--- a/app/views/ShareView/index.js
+++ b/app/views/ShareView/index.js
@@ -95,7 +95,7 @@ class ShareView extends Component {
getServerInfo = async() => {
const { server } = this.props;
const serversDB = database.servers;
- const serversCollection = serversDB.collections.get('servers');
+ const serversCollection = serversDB.get('servers');
try {
this.serverInfo = await serversCollection.find(server);
} catch (error) {
@@ -235,7 +235,9 @@ class ShareView extends Component {
newSelected = attachments[selectedIndex - 1] || {};
}
}
- this.setState({ attachments: attachments.filter(att => att.path !== item.path), selected: newSelected ?? selected });
+ this.setState({ attachments: attachments.filter(att => att.path !== item.path), selected: newSelected ?? selected }, () => {
+ this.messagebox?.current?.forceUpdate?.();
+ });
}
onChangeText = (text) => {
diff --git a/app/views/SidebarView/index.js b/app/views/SidebarView/index.js
index 9dd63b8dc..a14b178cd 100644
--- a/app/views/SidebarView/index.js
+++ b/app/views/SidebarView/index.js
@@ -4,19 +4,16 @@ import {
ScrollView, Text, View, TouchableWithoutFeedback
} from 'react-native';
import { connect } from 'react-redux';
-import { Q } from '@nozbe/watermelondb';
-import isEqual from 'react-fast-compare';
-
+import { dequal } from 'dequal';
import Avatar from '../../containers/Avatar';
import Status from '../../containers/Status/Status';
-import log, { logEvent, events } from '../../utils/log';
+import { logEvent, events } from '../../utils/log';
import I18n from '../../i18n';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { CustomIcon } from '../../lib/Icons';
import styles from './styles';
import SidebarItem from './SidebarItem';
import { themes } from '../../constants/colors';
-import database from '../../lib/database';
import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login';
import SafeAreaView from '../../containers/SafeAreaView';
@@ -27,13 +24,6 @@ Separator.propTypes = {
theme: PropTypes.string
};
-const permissions = [
- 'view-statistics',
- 'view-room-administration',
- 'view-user-administration',
- 'view-privileged-setting'
-];
-
class Sidebar extends Component {
static propTypes = {
baseUrl: PropTypes.string,
@@ -45,32 +35,24 @@ class Sidebar extends Component {
loadingServer: PropTypes.bool,
useRealName: PropTypes.bool,
allowStatusMessage: PropTypes.bool,
- isMasterDetail: PropTypes.bool
+ isMasterDetail: PropTypes.bool,
+ viewStatisticsPermission: PropTypes.object,
+ viewRoomAdministrationPermission: PropTypes.object,
+ viewUserAdministrationPermission: PropTypes.object,
+ viewPrivilegedSettingPermission: PropTypes.object
}
constructor(props) {
super(props);
this.state = {
- showStatus: false,
- isAdmin: false
+ showStatus: false
};
}
- componentDidMount() {
- this.setIsAdmin();
- }
-
- UNSAFE_componentWillReceiveProps(nextProps) {
- const { loadingServer } = this.props;
- if (loadingServer && nextProps.loadingServer !== loadingServer) {
- this.setIsAdmin();
- }
- }
-
shouldComponentUpdate(nextProps, nextState) {
const { showStatus, isAdmin } = this.state;
const {
- Site_Name, user, baseUrl, state, isMasterDetail, useRealName, theme
+ Site_Name, user, baseUrl, state, isMasterDetail, useRealName, theme, viewStatisticsPermission, viewRoomAdministrationPermission, viewUserAdministrationPermission, viewPrivilegedSettingPermission
} = this.props;
// Drawer navigation state
if (state?.index !== nextProps.state?.index) {
@@ -91,7 +73,7 @@ class Sidebar extends Component {
if (nextProps.theme !== theme) {
return true;
}
- if (!isEqual(nextProps.user, user)) {
+ if (!dequal(nextProps.user, user)) {
return true;
}
if (nextProps.isMasterDetail !== isMasterDetail) {
@@ -103,25 +85,42 @@ class Sidebar extends Component {
if (nextState.isAdmin !== isAdmin) {
return true;
}
+ if (!dequal(nextProps.viewStatisticsPermission, viewStatisticsPermission)) {
+ return true;
+ }
+ if (!dequal(nextProps.viewRoomAdministrationPermission, viewRoomAdministrationPermission)) {
+ return true;
+ }
+ if (!dequal(nextProps.viewUserAdministrationPermission, viewUserAdministrationPermission)) {
+ return true;
+ }
+ if (!dequal(nextProps.viewPrivilegedSettingPermission, viewPrivilegedSettingPermission)) {
+ return true;
+ }
return false;
}
- async setIsAdmin() {
- const db = database.active;
- const { user } = this.props;
+
+ getIsAdmin() {
+ const {
+ user, viewStatisticsPermission, viewRoomAdministrationPermission, viewUserAdministrationPermission, viewPrivilegedSettingPermission
+ } = this.props;
const { roles } = user;
- try {
- if (roles) {
- const permissionsCollection = db.collections.get('permissions');
- const permissionsFiltered = await permissionsCollection.query(Q.where('id', Q.oneOf(permissions))).fetch();
- const isAdmin = permissionsFiltered.reduce((result, permission) => (
- result || permission.roles.some(r => roles.indexOf(r) !== -1)),
- false);
- this.setState({ isAdmin });
- }
- } catch (e) {
- log(e);
+ const allPermissions = [viewStatisticsPermission, viewRoomAdministrationPermission, viewUserAdministrationPermission, viewPrivilegedSettingPermission];
+ let isAdmin = false;
+
+ if (roles) {
+ isAdmin = allPermissions.reduce((result, permission) => {
+ if (permission) {
+ return (
+ result || permission.some(r => roles.indexOf(r) !== -1)
+ );
+ }
+ return result;
+ },
+ false);
}
+ return isAdmin;
}
sidebarNavigate = (route) => {
@@ -143,9 +142,8 @@ class Sidebar extends Component {
}
renderAdmin = () => {
- const { isAdmin } = this.state;
const { theme, isMasterDetail } = this.props;
- if (!isAdmin) {
+ if (!this.getIsAdmin()) {
return null;
}
const routeName = isMasterDetail ? 'AdminPanelView' : 'AdminPanelStackNavigator';
@@ -275,7 +273,11 @@ const mapStateToProps = state => ({
loadingServer: state.server.loading,
useRealName: state.settings.UI_Use_Real_Name,
allowStatusMessage: state.settings.Accounts_AllowUserStatusMessageChange,
- isMasterDetail: state.app.isMasterDetail
+ isMasterDetail: state.app.isMasterDetail,
+ viewStatisticsPermission: state.permissions['view-statistics'],
+ viewRoomAdministrationPermission: state.permissions['view-room-administration'],
+ viewUserAdministrationPermission: state.permissions['view-user-administration'],
+ viewPrivilegedSettingPermission: state.permissions['view-privileged-setting']
});
export default connect(mapStateToProps)(withTheme(Sidebar));
diff --git a/app/views/ThreadMessagesView/Dropdown/index.js b/app/views/ThreadMessagesView/Dropdown/index.js
index 49533e4be..fdf1acba7 100644
--- a/app/views/ThreadMessagesView/Dropdown/index.js
+++ b/app/views/ThreadMessagesView/Dropdown/index.js
@@ -68,7 +68,7 @@ class Dropdown extends React.Component {
});
const backdropOpacity = this.animatedValue.interpolate({
inputRange: [0, 1],
- outputRange: [0, 0.3]
+ outputRange: [0, themes[theme].backdropOpacity]
});
return (
<>
diff --git a/app/views/ThreadMessagesView/index.js b/app/views/ThreadMessagesView/index.js
index d75328a99..417c10328 100644
--- a/app/views/ThreadMessagesView/index.js
+++ b/app/views/ThreadMessagesView/index.js
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { FlatList, InteractionManager } from 'react-native';
+import { FlatList } from 'react-native';
import { connect } from 'react-redux';
import { Q } from '@nozbe/watermelondb';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
@@ -73,9 +73,7 @@ class ThreadMessagesView extends React.Component {
componentDidMount() {
this.mounted = true;
- this.mountInteraction = InteractionManager.runAfterInteractions(() => {
- this.init();
- });
+ this.init();
}
componentDidUpdate(prevProps) {
@@ -89,12 +87,6 @@ class ThreadMessagesView extends React.Component {
componentWillUnmount() {
console.countReset(`${ this.constructor.name }.render calls`);
- if (this.mountInteraction && this.mountInteraction.cancel) {
- this.mountInteraction.cancel();
- }
- if (this.syncInteraction && this.syncInteraction.cancel) {
- this.syncInteraction.cancel();
- }
if (this.subSubscription && this.subSubscription.unsubscribe) {
this.subSubscription.unsubscribe();
}
@@ -249,7 +241,7 @@ class ThreadMessagesView extends React.Component {
try {
const db = database.active;
- const threadsCollection = db.collections.get('threads');
+ const threadsCollection = db.get('threads');
const allThreadsRecords = await subscription.threads.fetch();
let threadsToCreate = [];
let threadsToUpdate = [];
@@ -330,10 +322,8 @@ class ThreadMessagesView extends React.Component {
rid: this.rid, updatedSince: updatedSince.toISOString()
});
if (result.success && result.threads) {
- this.syncInteraction = InteractionManager.runAfterInteractions(() => {
- const { update, remove } = result.threads;
- this.updateThreads({ update, remove, lastThreadSync: updatedSince });
- });
+ const { update, remove } = result.threads;
+ this.updateThreads({ update, remove, lastThreadSync: updatedSince });
}
this.setState({
loading: false
diff --git a/ios/NotificationService/Info.plist b/ios/NotificationService/Info.plist
index d2f1244d4..9d9595029 100644
--- a/ios/NotificationService/Info.plist
+++ b/ios/NotificationService/Info.plist
@@ -4,6 +4,8 @@
AppGroup
group.ios.chat.rocket
+ IS_OFFICIAL
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
diff --git a/ios/ReplyNotification.swift b/ios/ReplyNotification.swift
index 87b8cc439..771a1056d 100644
--- a/ios/ReplyNotification.swift
+++ b/ios/ReplyNotification.swift
@@ -48,6 +48,9 @@ class ReplyNotification: RNNotificationEventHandler {
}
}
}
+ } else {
+ let body = RNNotificationParser.parseNotificationResponse(response)
+ RNEventEmitter.sendEvent(RNNotificationOpened, body: body)
}
}
}
diff --git a/ios/RocketChatRN-Bridging-Header.h b/ios/RocketChatRN-Bridging-Header.h
index 0c1ed67a4..142bd0845 100644
--- a/ios/RocketChatRN-Bridging-Header.h
+++ b/ios/RocketChatRN-Bridging-Header.h
@@ -7,6 +7,8 @@
#import
#import
#import
+#import
+#import
#import
#import
#import
diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj
index 4d8a2f561..af7653b82 100644
--- a/ios/RocketChatRN.xcodeproj/project.pbxproj
+++ b/ios/RocketChatRN.xcodeproj/project.pbxproj
@@ -1477,7 +1477,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative;
PRODUCT_NAME = "Rocket.Chat Experimental";
- PROVISIONING_PROFILE_SPECIFIER = "match Development chat.rocket.reactnative";
+ PROVISIONING_PROFILE_SPECIFIER = "match Development chat.rocket.reactnative 1614015157";
SWIFT_OBJC_BRIDGING_HEADER = "RocketChatRN-Bridging-Header.h";
SWIFT_OBJC_INTERFACE_HEADER_NAME = "RocketChatRN-Swift.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -1582,7 +1582,7 @@
"$(inherited)",
"$(SRCROOT)/../node_modules/rn-extensions-share/ios/**",
"$(SRCROOT)/../node_modules/react-native-firebase/ios/RNFirebase/**",
- "$PODS_CONFIGURATION_BUILD_DIR/Firebase",
+ $PODS_CONFIGURATION_BUILD_DIR/Firebase,
);
INFOPLIST_FILE = ShareRocketChatRN/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
@@ -1599,7 +1599,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "match Development chat.rocket.reactnative.ShareExtension";
+ PROVISIONING_PROFILE_SPECIFIER = "match Development chat.rocket.reactnative.ShareExtension 1614015146";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -1648,7 +1648,7 @@
"$(inherited)",
"$(SRCROOT)/../node_modules/rn-extensions-share/ios/**",
"$(SRCROOT)/../node_modules/react-native-firebase/ios/RNFirebase/**",
- "$PODS_CONFIGURATION_BUILD_DIR/Firebase",
+ $PODS_CONFIGURATION_BUILD_DIR/Firebase,
);
INFOPLIST_FILE = ShareRocketChatRN/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
@@ -1664,7 +1664,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "chat.rocket.reactnative.ShareExtension AppStore";
+ PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.reactnative.ShareExtension";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -1691,12 +1691,12 @@
INFOPLIST_FILE = NotificationService/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
- MARKETING_VERSION = 4.14.1;
+ MARKETING_VERSION = 4.15.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "match Development chat.rocket.reactnative.NotificationService";
+ PROVISIONING_PROFILE_SPECIFIER = "match Development chat.rocket.reactnative.NotificationService 1614015134";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OBJC_BRIDGING_HEADER = "NotificationService/NotificationService-Bridging-Header.h";
@@ -1728,11 +1728,11 @@
INFOPLIST_FILE = NotificationService/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
- MARKETING_VERSION = 4.14.1;
+ MARKETING_VERSION = 4.15.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "chat.rocket.reactnative.NotificationService AppStore";
+ PROVISIONING_PROFILE_SPECIFIER = "match AppStore chat.rocket.reactnative.NotificationService";
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "NotificationService/NotificationService-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-O";
diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist
index b6b497f45..ada5fc6c0 100644
--- a/ios/RocketChatRN/Info.plist
+++ b/ios/RocketChatRN/Info.plist
@@ -23,7 +23,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 4.14.1
+ 4.15.0
CFBundleSignature
????
CFBundleURLTypes
diff --git a/ios/ShareRocketChatRN/Info.plist b/ios/ShareRocketChatRN/Info.plist
index 77e3c4e75..d545207a6 100644
--- a/ios/ShareRocketChatRN/Info.plist
+++ b/ios/ShareRocketChatRN/Info.plist
@@ -4,6 +4,8 @@
AppGroup
group.ios.chat.rocket
+ IS_OFFICIAL
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
@@ -19,7 +21,7 @@
CFBundlePackageType
XPC!
CFBundleShortVersionString
- 4.14.1
+ 4.15.0
CFBundleVersion
1
KeychainGroup
diff --git a/package.json b/package.json
index 71bfdbc1e..901e70955 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "rocket-chat-reactnative",
- "version": "0.0.1",
+ "version": "4.15.0",
"private": true,
"scripts": {
"start": "react-native start",
@@ -50,7 +50,7 @@
"bytebuffer": "^5.0.1",
"commonmark": "git+https://github.com/RocketChat/commonmark.js.git",
"commonmark-react-renderer": "git+https://github.com/RocketChat/commonmark-react-renderer.git",
- "deep-equal": "2.0.3",
+ "dequal": "^2.0.2",
"ejson": "2.2.0",
"eslint-config-airbnb": "^18.1.0",
"expo-apple-authentication": "^2.2.1",
@@ -70,7 +70,6 @@
"pretty-bytes": "^5.3.0",
"prop-types": "15.7.2",
"react": "16.13.1",
- "react-fast-compare": "^3.2.0",
"react-native": "RocketChat/react-native#0.63.4",
"react-native-animatable": "^1.3.3",
"react-native-appearance": "0.3.4",
@@ -121,7 +120,7 @@
"reselect": "4.0.0",
"rn-extensions-share": "RocketChat/rn-extensions-share",
"rn-fetch-blob": "0.12.0",
- "rn-root-view": "^1.0.3",
+ "rn-root-view": "1.0.3",
"semver": "7.3.2",
"ua-parser-js": "^0.7.21",
"url-parse": "^1.4.7",
@@ -130,6 +129,8 @@
},
"devDependencies": {
"@babel/core": "^7.8.4",
+ "@babel/eslint-parser": "^7.13.4",
+ "@babel/eslint-plugin": "^7.13.0",
"@babel/plugin-proposal-decorators": "^7.8.3",
"@babel/runtime": "^7.8.4",
"@storybook/addon-storyshots": "5.3.19",
@@ -137,7 +138,6 @@
"@types/react-native": "^0.62.7",
"axios": "^0.19.2",
"babel-core": "^6.26.3",
- "babel-eslint": "^9.0.0",
"babel-jest": "^25.1.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"babel-runtime": "^6.26.0",
diff --git a/patches/react-native-webview+10.3.2.patch b/patches/react-native-webview+10.3.2.patch
index 68412ee27..08b0e9721 100644
--- a/patches/react-native-webview+10.3.2.patch
+++ b/patches/react-native-webview+10.3.2.patch
@@ -1,5 +1,5 @@
diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
-index ab869cf..2aa7a9e 100644
+index ab869cf..08ce7ce 100644
--- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
+++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
@@ -84,6 +84,12 @@ import java.util.Map;
@@ -44,7 +44,7 @@ index ab869cf..2aa7a9e 100644
}
@Override
-@@ -742,12 +754,54 @@ public class RNCWebViewManager extends SimpleViewManager {
+@@ -742,12 +754,56 @@ public class RNCWebViewManager extends SimpleViewManager {
protected static class RNCWebViewClient extends WebViewClient {
@@ -93,6 +93,8 @@ index ab869cf..2aa7a9e 100644
+ }
+ };
+ task.execute();
++ } else {
++ super.onReceivedClientCertRequest(view, request);
+ }
+ }
+
diff --git a/storybook/stories/Message.js b/storybook/stories/Message.js
index e2f67a469..7e8e06646 100644
--- a/storybook/stories/Message.js
+++ b/storybook/stories/Message.js
@@ -821,6 +821,10 @@ export default ({ theme }) => {
+
+
+
+
diff --git a/yarn.lock b/yarn.lock
index 4a577acf4..633d519bd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -76,6 +76,22 @@
semver "^5.4.1"
source-map "^0.5.0"
+"@babel/eslint-parser@^7.13.4":
+ version "7.13.4"
+ resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.13.4.tgz#dd9df3c70f44d2fb5a6519e8e10ca06c67dca43a"
+ integrity sha512-WfFEd89SzqmtYox8crTLJuEXyJolZY6Uu6iJpJmw4aMu50zHbYnxzxwuVkCt2cWygw+gLkUPTtAuox7eSnrL8g==
+ dependencies:
+ eslint-scope "5.1.0"
+ eslint-visitor-keys "^1.3.0"
+ semver "7.0.0"
+
+"@babel/eslint-plugin@^7.13.0":
+ version "7.13.0"
+ resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.13.0.tgz#e6d99efcd6b8551adf479e382a47218726179b1b"
+ integrity sha512-YGwCLc/u/uc3bU+q/fvgRQ62+TkxuyVvdmybK6ElzE49vODp+RnRe16eJzMM7EwvcRPQfQvcOSuGmzfcbZE2+w==
+ dependencies:
+ eslint-rule-composer "^0.3.0"
+
"@babel/generator@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.4.tgz#e49eeed9fe114b62fa5b181856a43a5e32f5f243"
@@ -2258,7 +2274,7 @@
"@rocket.chat/sdk@RocketChat/Rocket.Chat.js.SDK#mobile":
version "1.0.0-mobile"
- resolved "https://codeload.github.com/RocketChat/Rocket.Chat.js.SDK/tar.gz/0a97c818e60670d7660868ea107b96e5ebb631af"
+ resolved "https://codeload.github.com/RocketChat/Rocket.Chat.js.SDK/tar.gz/0241e2fc0c29827c51655f2d46d96e7a7720d2b6"
dependencies:
js-sha256 "^0.9.0"
lru-cache "^4.1.1"
@@ -3457,11 +3473,6 @@ array-equal@^1.0.0:
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
-array-filter@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
- integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=
-
array-filter@~0.0.0:
version "0.0.1"
resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
@@ -3649,13 +3660,6 @@ autoprefixer@^9.7.2:
postcss "^7.0.30"
postcss-value-parser "^4.1.0"
-available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5"
- integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==
- dependencies:
- array-filter "^1.0.0"
-
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
@@ -3717,18 +3721,6 @@ babel-core@^6.0.0, babel-core@^6.26.0, babel-core@^6.26.3:
slash "^1.0.0"
source-map "^0.5.7"
-babel-eslint@^9.0.0:
- version "9.0.0"
- resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-9.0.0.tgz#7d9445f81ed9f60aff38115f838970df9f2b6220"
- integrity sha512-itv1MwE3TMbY0QtNfeL7wzak1mV47Uy+n6HtSOO4Xd7rvmO+tsGQSgyOEEgo6Y2vHZKZphaoelNeSVj4vkLA1g==
- dependencies:
- "@babel/code-frame" "^7.0.0"
- "@babel/parser" "^7.0.0"
- "@babel/traverse" "^7.0.0"
- "@babel/types" "^7.0.0"
- eslint-scope "3.7.1"
- eslint-visitor-keys "^1.0.0"
-
babel-generator@^6.18.0, babel-generator@^6.26.0:
version "6.26.1"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90"
@@ -5741,26 +5733,6 @@ deep-assign@^3.0.0:
dependencies:
is-obj "^1.0.0"
-deep-equal@2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.3.tgz#cad1c15277ad78a5c01c49c2dee0f54de8a6a7b0"
- integrity sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==
- dependencies:
- es-abstract "^1.17.5"
- es-get-iterator "^1.1.0"
- is-arguments "^1.0.4"
- is-date-object "^1.0.2"
- is-regex "^1.0.5"
- isarray "^2.0.5"
- object-is "^1.1.2"
- object-keys "^1.1.1"
- object.assign "^4.1.0"
- regexp.prototype.flags "^1.3.0"
- side-channel "^1.0.2"
- which-boxed-primitive "^1.0.1"
- which-collection "^1.0.1"
- which-typed-array "^1.1.2"
-
deep-equal@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
@@ -5871,6 +5843,11 @@ dequal@^1.0.0:
resolved "https://registry.yarnpkg.com/dequal/-/dequal-1.0.0.tgz#41c6065e70de738541c82cdbedea5292277a017e"
integrity sha512-/Nd1EQbQbI9UbSHrMiKZjFLrXSnU328iQdZKPQf78XQI6C+gutkFUeoHpG5J08Ioa6HeRbRNFpSIclh1xyG0mw==
+dequal@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d"
+ integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==
+
des.js@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843"
@@ -6309,7 +6286,7 @@ es-array-method-boxes-properly@^1.0.0:
resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e"
integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==
-es-get-iterator@^1.0.2, es-get-iterator@^1.1.0:
+es-get-iterator@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8"
integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==
@@ -6484,10 +6461,15 @@ eslint-plugin-react@7.20.3:
resolve "^1.17.0"
string.prototype.matchall "^4.0.2"
-eslint-scope@3.7.1:
- version "3.7.1"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
- integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=
+eslint-rule-composer@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
+ integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==
+
+eslint-scope@5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5"
+ integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==
dependencies:
esrecurse "^4.1.0"
estraverse "^4.1.1"
@@ -6515,11 +6497,16 @@ eslint-utils@^1.4.3:
dependencies:
eslint-visitor-keys "^1.1.0"
-eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0:
+eslint-visitor-keys@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
+eslint-visitor-keys@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
+ integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
+
eslint@6.8.0:
version "6.8.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb"
@@ -7344,11 +7331,6 @@ for-own@^1.0.0:
dependencies:
for-in "^1.0.1"
-foreach@^2.0.5:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
- integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
-
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@@ -8458,11 +8440,6 @@ is-arrayish@^0.3.1:
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
-is-bigint@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4"
- integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==
-
is-binary-path@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
@@ -8477,11 +8454,6 @@ is-binary-path@~2.1.0:
dependencies:
binary-extensions "^2.0.0"
-is-boolean-object@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e"
- integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==
-
is-buffer@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
@@ -8525,7 +8497,7 @@ is-data-descriptor@^1.0.0:
dependencies:
kind-of "^6.0.0"
-is-date-object@^1.0.1, is-date-object@^1.0.2:
+is-date-object@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
@@ -8665,11 +8637,6 @@ is-map@^2.0.1:
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1"
integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==
-is-number-object@^1.0.3:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197"
- integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==
-
is-number@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
@@ -8767,16 +8734,6 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
dependencies:
has-symbols "^1.0.1"
-is-typed-array@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d"
- integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==
- dependencies:
- available-typed-arrays "^1.0.0"
- es-abstract "^1.17.4"
- foreach "^2.0.5"
- has-symbols "^1.0.1"
-
is-typedarray@^1.0.0, is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@@ -8787,16 +8744,6 @@ is-utf8@^0.2.0:
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
-is-weakmap@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2"
- integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==
-
-is-weakset@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83"
- integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==
-
is-windows@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
@@ -11522,7 +11469,7 @@ object-inspect@^1.7.0:
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
-object-is@^1.0.1, object-is@^1.1.2:
+object-is@^1.0.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6"
integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==
@@ -12786,11 +12733,6 @@ react-fast-compare@^3.0.1:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.1.1.tgz#0becf31e3812fa70dc231e259f40d892d4767900"
integrity sha512-SCsAORWK59BvauR2L1BTdjQbJcSGJJz03U0awektk2hshLKrITDDFTlgGCqIZpTDlPC/NFlZee6xTMzXPVLiHw==
-react-fast-compare@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
- integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
-
react-focus-lock@^2.1.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.3.1.tgz#9d5d85899773609c7eefa4fc54fff6a0f5f2fc47"
@@ -13189,7 +13131,7 @@ react-native-windows@^0.62.0-0:
uuid "^3.3.2"
xml-parser "^1.2.1"
-react-native@RocketChat/react-native#0.63.4, react-native@^0.63.1:
+react-native@RocketChat/react-native#0.63.4:
version "0.63.4"
resolved "https://codeload.github.com/RocketChat/react-native/tar.gz/616299bbc01eeb56cb7d5b52e543051e1ad2d136"
dependencies:
@@ -13832,8 +13774,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
rn-extensions-share@RocketChat/rn-extensions-share:
version "2.4.1"
resolved "https://codeload.github.com/RocketChat/rn-extensions-share/tar.gz/4d7c0e4c2f300e4fb116af7b7cc0dbbc8169150c"
- dependencies:
- react-native "^0.63.1"
rn-fetch-blob@0.12.0:
version "0.12.0"
@@ -13848,7 +13788,7 @@ rn-host-detect@1.2.0:
resolved "https://registry.yarnpkg.com/rn-host-detect/-/rn-host-detect-1.2.0.tgz#8b0396fc05631ec60c1cb8789e5070cdb04d0da0"
integrity sha512-btNg5kzHcjZZ7t7mvvV/4wNJ9e3MPgrWivkRgWURzXL0JJ0pwWlU4zrbmdlz3HHzHOxhBhHB4D+/dbMFfu4/4A==
-rn-root-view@^1.0.3:
+rn-root-view@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/rn-root-view/-/rn-root-view-1.0.3.tgz#a2cddc717278cb2175fb29b7c006e407b7f0d0e2"
integrity sha512-BIKm8hY5q8+pxK9B5ugYjqutoI9xn2JfxIZKWoaFmAl1bOIM4oXjwFQrRM1e6lFgzz99MN6Mf2dK3Alsywnvvw==
@@ -15995,44 +15935,11 @@ whatwg-url@^7.0.0:
tr46 "^1.0.1"
webidl-conversions "^4.0.2"
-which-boxed-primitive@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1"
- integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==
- dependencies:
- is-bigint "^1.0.0"
- is-boolean-object "^1.0.0"
- is-number-object "^1.0.3"
- is-string "^1.0.4"
- is-symbol "^1.0.2"
-
-which-collection@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906"
- integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==
- dependencies:
- is-map "^2.0.1"
- is-set "^2.0.1"
- is-weakmap "^2.0.1"
- is-weakset "^2.0.1"
-
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
-which-typed-array@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2"
- integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==
- dependencies:
- available-typed-arrays "^1.0.2"
- es-abstract "^1.17.5"
- foreach "^2.0.5"
- function-bind "^1.1.1"
- has-symbols "^1.0.1"
- is-typed-array "^1.1.3"
-
which@1.3.1, which@^1.2.12, which@^1.2.9, which@^1.3.0, which@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"