[FIX] Add Realm.safeAddListener (#785)

This commit is contained in:
Diego Mello 2019-04-04 15:08:40 -03:00 committed by GitHub
parent 46a36d7764
commit 2019ec58ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 52 additions and 34 deletions

View File

@ -10,7 +10,7 @@ import TabBar from './TabBar';
import EmojiCategory from './EmojiCategory'; import EmojiCategory from './EmojiCategory';
import styles from './styles'; import styles from './styles';
import categories from './categories'; import categories from './categories';
import database from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import { emojisByCategory } from '../../emojis'; import { emojisByCategory } from '../../emojis';
import protectedFunction from '../../lib/methods/helpers/protectedFunction'; import protectedFunction from '../../lib/methods/helpers/protectedFunction';
@ -45,8 +45,8 @@ export default class EmojiPicker extends Component {
this.updateFrequentlyUsed(); this.updateFrequentlyUsed();
this.updateCustomEmojis(); this.updateCustomEmojis();
requestAnimationFrame(() => this.setState({ show: true })); requestAnimationFrame(() => this.setState({ show: true }));
this.frequentlyUsed.addListener(this.updateFrequentlyUsed); safeAddListener(this.frequentlyUsed, this.updateFrequentlyUsed);
this.customEmojis.addListener(this.updateCustomEmojis); safeAddListener(this.customEmojis, this.updateCustomEmojis);
} }
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {

View File

@ -346,4 +346,22 @@ class DB {
}); });
} }
} }
export default new DB(); const db = new DB();
export default db;
// Realm workaround for "Cannot create asynchronous query while in a write transaction"
// inpired from https://github.com/realm/realm-js/issues/1188#issuecomment-359223918
export function safeAddListener(results, callback, database = db) {
if (!results || !results.addListener) {
console.log('⚠️ safeAddListener called for non addListener-compliant object');
return;
}
if (database.isInTransaction) {
setTimeout(() => {
safeAddListener(results, callback);
}, 50);
} else {
results.addListener(callback);
}
}

View File

@ -6,7 +6,7 @@ import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk';
import reduxStore from './createStore'; import reduxStore from './createStore';
import defaultSettings from '../constants/settings'; import defaultSettings from '../constants/settings';
import messagesStatus from '../constants/messagesStatus'; import messagesStatus from '../constants/messagesStatus';
import database from './realm'; import database, { safeAddListener } from './realm';
import log from '../utils/log'; import log from '../utils/log';
import { isIOS, getBundleId } from '../utils/deviceInfo'; import { isIOS, getBundleId } from '../utils/deviceInfo';
@ -65,7 +65,7 @@ const RocketChat = {
if (data.length) { if (data.length) {
return resolve(data[0]); return resolve(data[0]);
} }
data.addListener(() => { safeAddListener(data, () => {
if (!data.length) { return; } if (!data.length) { return; }
data.removeAllListeners(); data.removeAllListeners();
resolve(data[0]); resolve(data[0]);

View File

@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation'; import { SafeAreaView } from 'react-navigation';
import equal from 'deep-equal'; import equal from 'deep-equal';
import database from '../lib/realm'; import database, { safeAddListener } from '../lib/realm';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import UserItem from '../presentation/UserItem'; import UserItem from '../presentation/UserItem';
import debounce from '../utils/debounce'; import debounce from '../utils/debounce';
@ -79,7 +79,7 @@ export default class NewMessageView extends LoggedView {
this.state = { this.state = {
search: [] search: []
}; };
this.data.addListener(this.updateState); safeAddListener(this.data, this.updateState);
} }
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {

View File

@ -14,7 +14,7 @@ import sharedStyles from '../Styles';
import Avatar from '../../containers/Avatar'; import Avatar from '../../containers/Avatar';
import Status from '../../containers/Status'; import Status from '../../containers/Status';
import Touch from '../../utils/touch'; import Touch from '../../utils/touch';
import database from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import log from '../../utils/log'; import log from '../../utils/log';
import RoomTypeIcon from '../../containers/RoomTypeIcon'; import RoomTypeIcon from '../../containers/RoomTypeIcon';
@ -81,7 +81,7 @@ export default class RoomActionsView extends LoggedView {
} else if (room.t === 'd') { } else if (room.t === 'd') {
this.updateRoomMember(); this.updateRoomMember();
} }
this.rooms.addListener(this.updateRoom); safeAddListener(this.rooms, this.updateRoom);
} }
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {

View File

@ -14,7 +14,7 @@ import sharedStyles from '../Styles';
import styles from './styles'; import styles from './styles';
import scrollPersistTaps from '../../utils/scrollPersistTaps'; import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { showErrorAlert, showToast } from '../../utils/info'; import { showErrorAlert, showToast } from '../../utils/info';
import database from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import RCTextInput from '../../containers/TextInput'; import RCTextInput from '../../containers/TextInput';
import Loading from '../../containers/Loading'; import Loading from '../../containers/Loading';
@ -77,7 +77,7 @@ export default class RoomInfoEditView extends LoggedView {
componentDidMount() { componentDidMount() {
this.updateRoom(); this.updateRoom();
this.init(); this.init();
this.rooms.addListener(this.updateRoom); safeAddListener(this.rooms, this.updateRoom);
const { room } = this.state; const { room } = this.state;
this.permissions = RocketChat.hasPermission(PERMISSIONS_ARRAY, room.rid); this.permissions = RocketChat.hasPermission(PERMISSIONS_ARRAY, room.rid);
} }

View File

@ -11,7 +11,7 @@ import Status from '../../containers/Status';
import Avatar from '../../containers/Avatar'; import Avatar from '../../containers/Avatar';
import styles from './styles'; import styles from './styles';
import sharedStyles from '../Styles'; import sharedStyles from '../Styles';
import database from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import log from '../../utils/log'; import log from '../../utils/log';
import RoomTypeIcon from '../../containers/RoomTypeIcon'; import RoomTypeIcon from '../../containers/RoomTypeIcon';
@ -87,7 +87,7 @@ export default class RoomInfoView extends LoggedView {
} }
async componentDidMount() { async componentDidMount() {
this.rooms.addListener(this.updateRoom); safeAddListener(this.rooms, this.updateRoom);
const { room } = this.state; const { room } = this.state;
const permissions = RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid); const permissions = RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid);
if (permissions[PERMISSION_EDIT_ROOM]) { if (permissions[PERMISSION_EDIT_ROOM]) {

View File

@ -11,7 +11,7 @@ import styles from './styles';
import UserItem from '../../presentation/UserItem'; import UserItem from '../../presentation/UserItem';
import scrollPersistTaps from '../../utils/scrollPersistTaps'; import scrollPersistTaps from '../../utils/scrollPersistTaps';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import database from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import { showToast } from '../../utils/info'; import { showToast } from '../../utils/info';
import log from '../../utils/log'; import log from '../../utils/log';
import { vibrate } from '../../utils/vibration'; import { vibrate } from '../../utils/vibration';
@ -81,7 +81,7 @@ export default class RoomMembersView extends LoggedView {
componentDidMount() { componentDidMount() {
this.fetchMembers(); this.fetchMembers();
this.rooms.addListener(this.updateRoom); safeAddListener(this.rooms, this.updateRoom);
const { navigation } = this.props; const { navigation } = this.props;
navigation.setParams({ toggleStatus: this.toggleStatus }); navigation.setParams({ toggleStatus: this.toggleStatus });

View File

@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import { responsive } from 'react-native-responsive-ui'; import { responsive } from 'react-native-responsive-ui';
import styles from './styles'; import styles from './styles';
import database from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import scrollPersistTaps from '../../utils/scrollPersistTaps'; import scrollPersistTaps from '../../utils/scrollPersistTaps';
import debounce from '../../utils/debounce'; import debounce from '../../utils/debounce';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
@ -36,7 +36,7 @@ export class List extends React.Component {
messages: this.data.slice(), messages: this.data.slice(),
showScollToBottomButton: false showScollToBottomButton: false
}; };
this.data.addListener(this.updateState); safeAddListener(this.data, this.updateState);
} }
// shouldComponentUpdate(nextProps, nextState) { // shouldComponentUpdate(nextProps, nextState) {

View File

@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
import { responsive } from 'react-native-responsive-ui'; import { responsive } from 'react-native-responsive-ui';
import equal from 'deep-equal'; import equal from 'deep-equal';
import database from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import log from '../../utils/log'; import log from '../../utils/log';
import I18n from '../../i18n'; import I18n from '../../i18n';
@ -74,7 +74,7 @@ export default class UploadProgress extends Component {
}; };
const { rid } = this.props; const { rid } = this.props;
this.uploads = database.objects('uploads').filtered('rid = $0', rid); this.uploads = database.objects('uploads').filtered('rid = $0', rid);
this.uploads.addListener(this.updateUploads); safeAddListener(this.uploads, this.updateUploads);
} }
componentDidMount() { componentDidMount() {

View File

@ -13,7 +13,7 @@ import { openRoom as openRoomAction, closeRoom as closeRoomAction } from '../../
import { toggleReactionPicker as toggleReactionPickerAction, actionsShow as actionsShowAction } from '../../actions/messages'; import { toggleReactionPicker as toggleReactionPickerAction, actionsShow as actionsShowAction } from '../../actions/messages';
import LoggedView from '../View'; import LoggedView from '../View';
import { List } from './List'; import { List } from './List';
import database from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import Message from '../../containers/message'; import Message from '../../containers/message';
import MessageActions from '../../containers/MessageActions'; import MessageActions from '../../containers/MessageActions';
@ -114,7 +114,7 @@ export default class RoomView extends LoggedView {
() => this.updateRoom() () => this.updateRoom()
); );
} }
this.rooms.addListener(this.updateRoom); safeAddListener(this.rooms, this.updateRoom);
this.internalSetState({ loaded: true }); this.internalSetState({ loaded: true });
} }

View File

@ -11,7 +11,7 @@ import { toggleServerDropdown as toggleServerDropdownAction } from '../../action
import { selectServerRequest as selectServerRequestAction } from '../../actions/server'; import { selectServerRequest as selectServerRequestAction } from '../../actions/server';
import { appStart as appStartAction } from '../../actions'; import { appStart as appStartAction } from '../../actions';
import styles from './styles'; import styles from './styles';
import database from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import Touch from '../../utils/touch'; import Touch from '../../utils/touch';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import I18n from '../../i18n'; import I18n from '../../i18n';
@ -46,7 +46,7 @@ class ServerDropdown extends Component {
servers: this.servers servers: this.servers
}; };
this.animatedValue = new Animated.Value(0); this.animatedValue = new Animated.Value(0);
this.servers.addListener(this.updateState); safeAddListener(this.servers, this.updateState);
} }
componentDidMount() { componentDidMount() {

View File

@ -10,7 +10,7 @@ import Orientation from 'react-native-orientation-locker';
import SearchBox from '../../containers/SearchBox'; import SearchBox from '../../containers/SearchBox';
import ConnectionBadge from '../../containers/ConnectionBadge'; import ConnectionBadge from '../../containers/ConnectionBadge';
import database from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import RoomItem, { ROW_HEIGHT } from '../../presentation/RoomItem'; import RoomItem, { ROW_HEIGHT } from '../../presentation/RoomItem';
import styles from './styles'; import styles from './styles';
@ -277,7 +277,7 @@ export default class RoomsListView extends LoggedView {
if (showUnread) { if (showUnread) {
this.unread = this.data.filtered('archived != true && open == true').filtered('(unread > 0 || alert == true)'); this.unread = this.data.filtered('archived != true && open == true').filtered('(unread > 0 || alert == true)');
unread = this.removeRealmInstance(this.unread); unread = this.removeRealmInstance(this.unread);
this.unread.addListener(debounce(() => this.internalSetState({ unread: this.removeRealmInstance(this.unread) }), 300)); safeAddListener(this.unread, debounce(() => this.internalSetState({ unread: this.removeRealmInstance(this.unread) }), 300));
} else { } else {
this.removeListener(unread); this.removeListener(unread);
} }
@ -285,7 +285,7 @@ export default class RoomsListView extends LoggedView {
if (showFavorites) { if (showFavorites) {
this.favorites = this.data.filtered('f == true'); this.favorites = this.data.filtered('f == true');
favorites = this.removeRealmInstance(this.favorites); favorites = this.removeRealmInstance(this.favorites);
this.favorites.addListener(debounce(() => this.internalSetState({ favorites: this.removeRealmInstance(this.favorites) }), 300)); safeAddListener(this.favorites, debounce(() => this.internalSetState({ favorites: this.removeRealmInstance(this.favorites) }), 300));
} else { } else {
this.removeListener(favorites); this.removeListener(favorites);
} }
@ -307,10 +307,10 @@ export default class RoomsListView extends LoggedView {
this.livechat = this.data.filtered('t == $0', 'l'); this.livechat = this.data.filtered('t == $0', 'l');
livechat = this.removeRealmInstance(this.livechat); livechat = this.removeRealmInstance(this.livechat);
this.channels.addListener(debounce(() => this.internalSetState({ channels: this.removeRealmInstance(this.channels) }), 300)); safeAddListener(this.channels, debounce(() => this.internalSetState({ channels: this.removeRealmInstance(this.channels) }), 300));
this.privateGroup.addListener(debounce(() => this.internalSetState({ privateGroup: this.removeRealmInstance(this.privateGroup) }), 300)); safeAddListener(this.privateGroup, debounce(() => this.internalSetState({ privateGroup: this.removeRealmInstance(this.privateGroup) }), 300));
this.direct.addListener(debounce(() => this.internalSetState({ direct: this.removeRealmInstance(this.direct) }), 300)); safeAddListener(this.direct, debounce(() => this.internalSetState({ direct: this.removeRealmInstance(this.direct) }), 300));
this.livechat.addListener(debounce(() => this.internalSetState({ livechat: this.removeRealmInstance(this.livechat) }), 300)); safeAddListener(this.livechat, debounce(() => this.internalSetState({ livechat: this.removeRealmInstance(this.livechat) }), 300));
this.removeListener(this.chats); this.removeListener(this.chats);
} else { } else {
// chats // chats
@ -321,7 +321,7 @@ export default class RoomsListView extends LoggedView {
} }
chats = this.removeRealmInstance(this.chats); chats = this.removeRealmInstance(this.chats);
this.chats.addListener(debounce(() => this.internalSetState({ chats: this.removeRealmInstance(this.chats) }), 300)); safeAddListener(this.chats, debounce(() => this.internalSetState({ chats: this.removeRealmInstance(this.chats) }), 300));
this.removeListener(this.channels); this.removeListener(this.channels);
this.removeListener(this.privateGroup); this.removeListener(this.privateGroup);
this.removeListener(this.direct); this.removeListener(this.direct);

View File

@ -10,7 +10,7 @@ import equal from 'deep-equal';
import { import {
addUser as addUserAction, removeUser as removeUserAction, reset as resetAction, setLoading as setLoadingAction addUser as addUserAction, removeUser as removeUserAction, reset as resetAction, setLoading as setLoadingAction
} from '../actions/selectedUsers'; } from '../actions/selectedUsers';
import database from '../lib/realm'; import database, { safeAddListener } from '../lib/realm';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import UserItem from '../presentation/UserItem'; import UserItem from '../presentation/UserItem';
import Loading from '../containers/Loading'; import Loading from '../containers/Loading';
@ -88,7 +88,7 @@ export default class SelectedUsersView extends LoggedView {
this.state = { this.state = {
search: [] search: []
}; };
this.data.addListener(this.updateState); safeAddListener(this.data, this.updateState);
} }
componentDidMount() { componentDidMount() {