[NEW] Room actions: block user, snippet messages, room files and leave room (#250)
* - Block user - Load room members async - fixed reactive change of room's read only flag * Snippet messages * - Room files - Dismiss Video component on back button press - Improvements on Image component * Improvement on Video component * Leave room * Missing message types * lint
This commit is contained in:
parent
f07040c6a6
commit
5443a15f0a
|
@ -38,6 +38,7 @@ export const ROOM = createRequestTypes('ROOM', [
|
|||
'SOMEONE_TYPING',
|
||||
'OPEN',
|
||||
'CLOSE',
|
||||
'LEAVE',
|
||||
'USER_TYPING',
|
||||
'MESSAGE_RECEIVED',
|
||||
'SET_LAST_OPEN',
|
||||
|
@ -96,6 +97,8 @@ export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET', 'REQUEST'
|
|||
export const STARRED_MESSAGES = createRequestTypes('STARRED_MESSAGES', ['OPEN', 'CLOSE', 'MESSAGES_RECEIVED', 'MESSAGE_UNSTARRED']);
|
||||
export const PINNED_MESSAGES = createRequestTypes('PINNED_MESSAGES', ['OPEN', 'CLOSE', 'MESSAGES_RECEIVED', 'MESSAGE_UNPINNED']);
|
||||
export const MENTIONED_MESSAGES = createRequestTypes('MENTIONED_MESSAGES', ['OPEN', 'CLOSE', 'MESSAGES_RECEIVED']);
|
||||
export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPEN', 'CLOSE', 'MESSAGES_RECEIVED']);
|
||||
export const ROOM_FILES = createRequestTypes('ROOM_FILES', ['OPEN', 'CLOSE', 'MESSAGES_RECEIVED']);
|
||||
|
||||
export const INCREMENT = 'INCREMENT';
|
||||
export const DECREMENT = 'DECREMENT';
|
||||
|
|
|
@ -35,6 +35,13 @@ export function closeRoom() {
|
|||
};
|
||||
}
|
||||
|
||||
export function leaveRoom(rid) {
|
||||
return {
|
||||
type: types.ROOM.LEAVE,
|
||||
rid
|
||||
};
|
||||
}
|
||||
|
||||
export function userTyping(status = true) {
|
||||
return {
|
||||
type: types.ROOM.USER_TYPING,
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import * as types from './actionsTypes';
|
||||
|
||||
export function openRoomFiles(rid) {
|
||||
return {
|
||||
type: types.ROOM_FILES.OPEN,
|
||||
rid
|
||||
};
|
||||
}
|
||||
|
||||
export function closeRoomFiles() {
|
||||
return {
|
||||
type: types.ROOM_FILES.CLOSE
|
||||
};
|
||||
}
|
||||
|
||||
export function roomFilesReceived(messages) {
|
||||
return {
|
||||
type: types.ROOM_FILES.MESSAGES_RECEIVED,
|
||||
messages
|
||||
};
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import * as types from './actionsTypes';
|
||||
|
||||
export function openSnippetedMessages(rid) {
|
||||
return {
|
||||
type: types.SNIPPETED_MESSAGES.OPEN,
|
||||
rid
|
||||
};
|
||||
}
|
||||
|
||||
export function closeSnippetedMessages() {
|
||||
return {
|
||||
type: types.SNIPPETED_MESSAGES.CLOSE
|
||||
};
|
||||
}
|
||||
|
||||
export function snippetedMessagesReceived(messages) {
|
||||
return {
|
||||
type: types.SNIPPETED_MESSAGES.MESSAGES_RECEIVED,
|
||||
messages
|
||||
};
|
||||
}
|
|
@ -13,15 +13,11 @@ const styles = StyleSheet.create({
|
|||
borderWidth: 1,
|
||||
borderRadius: 6
|
||||
},
|
||||
imageContainer: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
image: {
|
||||
width: 256,
|
||||
height: 256,
|
||||
resizeMode: 'cover'
|
||||
flex: 1,
|
||||
height: undefined,
|
||||
width: undefined,
|
||||
resizeMode: 'contain'
|
||||
},
|
||||
labelContainer: {
|
||||
height: 62,
|
||||
|
@ -46,14 +42,7 @@ export default class Image extends React.PureComponent {
|
|||
user: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { baseUrl, file, user } = props;
|
||||
this.state = {
|
||||
modalVisible: false,
|
||||
img: `${ baseUrl }${ file.image_url }?rc_uid=${ user.id }&rc_token=${ user.token }`
|
||||
};
|
||||
}
|
||||
state = { modalVisible: false };
|
||||
|
||||
getDescription() {
|
||||
if (this.props.file.description) {
|
||||
|
@ -68,18 +57,18 @@ export default class Image extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { baseUrl, file, user } = this.props;
|
||||
const img = `${ baseUrl }${ file.image_url }?rc_uid=${ user.id }&rc_token=${ user.token }`;
|
||||
return (
|
||||
<View>
|
||||
<TouchableOpacity
|
||||
onPress={() => this._onPressButton()}
|
||||
style={styles.button}
|
||||
>
|
||||
<View style={styles.imageContainer}>
|
||||
<CachedImage
|
||||
style={styles.image}
|
||||
source={{ uri: encodeURI(this.state.img) }}
|
||||
/>
|
||||
</View>
|
||||
<CachedImage
|
||||
style={styles.image}
|
||||
source={{ uri: encodeURI(img) }}
|
||||
/>
|
||||
<View style={styles.labelContainer}>
|
||||
<Text style={styles.imageName}>{this.props.file.title}</Text>
|
||||
{this.getDescription()}
|
||||
|
@ -87,7 +76,7 @@ export default class Image extends React.PureComponent {
|
|||
</TouchableOpacity>
|
||||
<PhotoModal
|
||||
title={this.props.file.title}
|
||||
image={this.state.img}
|
||||
image={img}
|
||||
isVisible={this.state.modalVisible}
|
||||
onClose={() => this.setState({ modalVisible: false })}
|
||||
/>
|
||||
|
|
|
@ -34,15 +34,7 @@ export default class Video extends React.PureComponent {
|
|||
user: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { baseUrl, file, user } = props;
|
||||
this.state = {
|
||||
isVisible: false,
|
||||
uri: `${ baseUrl }${ file.video_url }?rc_uid=${ user.id }&rc_token=${ user.token }`
|
||||
};
|
||||
}
|
||||
|
||||
state = { isVisible: false };
|
||||
|
||||
toggleModal() {
|
||||
this.setState({
|
||||
|
@ -58,8 +50,10 @@ export default class Video extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { isVisible, uri } = this.state;
|
||||
const { description } = this.props.file;
|
||||
const { isVisible } = this.state;
|
||||
const { video_url, description } = this.props.file;
|
||||
const { baseUrl, user } = this.props;
|
||||
const uri = `${ baseUrl }${ video_url }?rc_uid=${ user.id }&rc_token=${ user.token }`;
|
||||
return (
|
||||
<View>
|
||||
<TouchableOpacity
|
||||
|
@ -76,6 +70,7 @@ export default class Video extends React.PureComponent {
|
|||
isVisible={isVisible}
|
||||
style={styles.modal}
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onBackButtonPress={() => this.toggleModal()}
|
||||
>
|
||||
<VideoPlayer
|
||||
source={{ uri }}
|
||||
|
|
|
@ -49,6 +49,11 @@ export default class Message extends React.Component {
|
|||
_updatedAt: PropTypes.instanceOf(Date)
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
onLongPress: () => {},
|
||||
_updatedAt: new Date()
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { reactionsModal: false };
|
||||
|
@ -90,26 +95,32 @@ export default class Message extends React.Component {
|
|||
|
||||
getInfoMessage() {
|
||||
let message = '';
|
||||
const messageType = this.props.item.t;
|
||||
const {
|
||||
t, role, msg, u
|
||||
} = this.props.item;
|
||||
|
||||
if (messageType === 'rm') {
|
||||
if (t === 'rm') {
|
||||
message = 'Message removed';
|
||||
} else if (messageType === 'uj') {
|
||||
} else if (t === 'uj') {
|
||||
message = 'Has joined the channel.';
|
||||
} else if (messageType === 'r') {
|
||||
message = `Room name changed to: ${ this.props.item.msg } by ${ this.props.item.u.username }`;
|
||||
} else if (messageType === 'message_pinned') {
|
||||
} else if (t === 'r') {
|
||||
message = `Room name changed to: ${ msg } by ${ u.username }`;
|
||||
} else if (t === 'message_pinned') {
|
||||
message = 'Message pinned';
|
||||
} else if (messageType === 'ul') {
|
||||
} else if (t === 'ul') {
|
||||
message = 'Has left the channel.';
|
||||
} else if (messageType === 'ru') {
|
||||
message = `User ${ this.props.item.msg } removed by ${ this.props.item.u.username }`;
|
||||
} else if (messageType === 'au') {
|
||||
message = `User ${ this.props.item.msg } added by ${ this.props.item.u.username }`;
|
||||
} else if (messageType === 'user-muted') {
|
||||
message = `User ${ this.props.item.msg } muted by ${ this.props.item.u.username }`;
|
||||
} else if (messageType === 'user-unmuted') {
|
||||
message = `User ${ this.props.item.msg } unmuted by ${ this.props.item.u.username }`;
|
||||
} else if (t === 'ru') {
|
||||
message = `User ${ msg } removed by ${ u.username }`;
|
||||
} else if (t === 'au') {
|
||||
message = `User ${ msg } added by ${ u.username }`;
|
||||
} else if (t === 'user-muted') {
|
||||
message = `User ${ msg } muted by ${ u.username }`;
|
||||
} else if (t === 'user-unmuted') {
|
||||
message = `User ${ msg } unmuted by ${ u.username }`;
|
||||
} else if (t === 'subscription-role-added') {
|
||||
message = `${ msg } was set ${ role } by ${ u.username }`;
|
||||
} else if (t === 'subscription-role-removed') {
|
||||
message = `${ msg } is no longer ${ role } by ${ u.username }`;
|
||||
}
|
||||
|
||||
return message;
|
||||
|
@ -118,7 +129,9 @@ export default class Message extends React.Component {
|
|||
parseMessage = () => JSON.parse(JSON.stringify(this.props.item));
|
||||
|
||||
isInfoMessage() {
|
||||
return ['r', 'au', 'ru', 'ul', 'uj', 'rm', 'user-muted', 'user-unmuted', 'message_pinned'].includes(this.props.item.t);
|
||||
return [
|
||||
'r', 'au', 'ru', 'ul', 'uj', 'rm', 'user-muted', 'user-unmuted', 'message_pinned', 'subscription-role-added', 'subscription-role-removed'
|
||||
].includes(this.props.item.t);
|
||||
}
|
||||
|
||||
isDeleted() {
|
||||
|
|
|
@ -11,6 +11,8 @@ import NewServerView from '../../views/NewServerView';
|
|||
import StarredMessagesView from '../../views/StarredMessagesView';
|
||||
import PinnedMessagesView from '../../views/PinnedMessagesView';
|
||||
import MentionedMessagesView from '../../views/MentionedMessagesView';
|
||||
import SnippetedMessagesView from '../../views/SnippetedMessagesView';
|
||||
import RoomFilesView from '../../views/RoomFilesView';
|
||||
import RoomMembersView from '../../views/RoomMembersView';
|
||||
|
||||
const AuthRoutes = StackNavigator(
|
||||
|
@ -67,6 +69,20 @@ const AuthRoutes = StackNavigator(
|
|||
headerTintColor: '#292E35'
|
||||
}
|
||||
},
|
||||
SnippetedMessages: {
|
||||
screen: SnippetedMessagesView,
|
||||
navigationOptions: {
|
||||
title: 'Snippet Messages',
|
||||
headerTintColor: '#292E35'
|
||||
}
|
||||
},
|
||||
RoomFiles: {
|
||||
screen: RoomFilesView,
|
||||
navigationOptions: {
|
||||
title: 'Room Files',
|
||||
headerTintColor: '#292E35'
|
||||
}
|
||||
},
|
||||
RoomMembers: {
|
||||
screen: RoomMembersView,
|
||||
navigationOptions: {
|
||||
|
|
|
@ -22,6 +22,15 @@ export function goBack() {
|
|||
}
|
||||
}
|
||||
|
||||
export function goRoomsList() {
|
||||
if (config.navigator) {
|
||||
const action = NavigationActions.reset({
|
||||
index: 0,
|
||||
actions: [NavigationActions.navigate({ routeName: 'RoomsList' })]
|
||||
});
|
||||
config.navigator.dispatch(action);
|
||||
}
|
||||
}
|
||||
|
||||
export function goRoom({ rid, name }, counter = 0) {
|
||||
// about counter: we can call this method before navigator be set. so we have to wait, if we tried a lot, we give up ...
|
||||
|
|
|
@ -88,7 +88,8 @@ const subscriptionSchema = {
|
|||
lastMessage: { type: 'messages', optional: true },
|
||||
description: { type: 'string', optional: true },
|
||||
announcement: { type: 'string', optional: true },
|
||||
topic: { type: 'string', optional: true }
|
||||
topic: { type: 'string', optional: true },
|
||||
blocked: { type: 'bool', optional: true }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -200,7 +201,8 @@ const messagesSchema = {
|
|||
pinned: { type: 'bool', optional: true },
|
||||
starred: { type: 'bool', optional: true },
|
||||
editedBy: 'messagesEditedBy',
|
||||
reactions: { type: 'list', objectType: 'messagesReactions' }
|
||||
reactions: { type: 'list', objectType: 'messagesReactions' },
|
||||
role: { type: 'string', optional: true }
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ import { requestActiveUser } from '../actions/activeUsers';
|
|||
import { starredMessagesReceived, starredMessageUnstarred } from '../actions/starredMessages';
|
||||
import { pinnedMessagesReceived, pinnedMessageUnpinned } from '../actions/pinnedMessages';
|
||||
import { mentionedMessagesReceived } from '../actions/mentionedMessages';
|
||||
import { snippetedMessagesReceived } from '../actions/snippetedMessages';
|
||||
import { roomFilesReceived } from '../actions/roomFiles';
|
||||
import Ddp from './ddp';
|
||||
|
||||
export { Accounts } from 'react-native-meteor';
|
||||
|
@ -153,6 +155,11 @@ const RocketChat = {
|
|||
if (data.roles) {
|
||||
data.roles = data.roles.map(role => ({ value: role }));
|
||||
}
|
||||
if (data.blocker) {
|
||||
data.blocked = true;
|
||||
} else {
|
||||
data.blocked = false;
|
||||
}
|
||||
database.write(() => {
|
||||
database.create('subscriptions', data, true);
|
||||
});
|
||||
|
@ -241,6 +248,71 @@ const RocketChat = {
|
|||
}
|
||||
});
|
||||
|
||||
this.ddp.on('rocketchat_snippeted_message', (ddpMessage) => {
|
||||
if (ddpMessage.msg === 'added') {
|
||||
this.snippetedMessages = this.snippetedMessages || [];
|
||||
|
||||
if (this.snippetedMessagesTimer) {
|
||||
clearTimeout(this.snippetedMessagesTimer);
|
||||
this.snippetedMessagesTimer = null;
|
||||
}
|
||||
|
||||
this.snippetedMessagesTimer = setTimeout(() => {
|
||||
reduxStore.dispatch(snippetedMessagesReceived(this.snippetedMessages));
|
||||
this.snippetedMessagesTimer = null;
|
||||
return this.snippetedMessages = [];
|
||||
}, 1000);
|
||||
const message = ddpMessage.fields;
|
||||
message._id = ddpMessage.id;
|
||||
const snippetedMessage = this._buildMessage(message);
|
||||
this.snippetedMessages = [...this.snippetedMessages, snippetedMessage];
|
||||
}
|
||||
});
|
||||
|
||||
this.ddp.on('room_files', (ddpMessage) => {
|
||||
if (ddpMessage.msg === 'added') {
|
||||
this.roomFiles = this.roomFiles || [];
|
||||
|
||||
if (this.roomFilesTimer) {
|
||||
clearTimeout(this.roomFilesTimer);
|
||||
this.roomFilesTimer = null;
|
||||
}
|
||||
|
||||
this.roomFilesTimer = setTimeout(() => {
|
||||
reduxStore.dispatch(roomFilesReceived(this.roomFiles));
|
||||
this.roomFilesTimer = null;
|
||||
return this.roomFiles = [];
|
||||
}, 1000);
|
||||
const { fields } = ddpMessage;
|
||||
const message = {
|
||||
_id: ddpMessage.id,
|
||||
ts: fields.uploadedAt,
|
||||
msg: fields.description,
|
||||
status: 0,
|
||||
attachments: [{
|
||||
title: fields.name
|
||||
}],
|
||||
urls: [],
|
||||
reactions: [],
|
||||
u: {
|
||||
username: fields.user.username
|
||||
}
|
||||
};
|
||||
const fileUrl = `/file-upload/${ ddpMessage.id }/${ fields.name }`;
|
||||
if (/image/.test(fields.type)) {
|
||||
message.attachments[0].image_type = fields.type;
|
||||
message.attachments[0].image_url = fileUrl;
|
||||
} else if (/audio/.test(fields.type)) {
|
||||
message.attachments[0].audio_type = fields.type;
|
||||
message.attachments[0].audio_url = fileUrl;
|
||||
} else if (/video/.test(fields.type)) {
|
||||
message.attachments[0].video_type = fields.type;
|
||||
message.attachments[0].video_url = fileUrl;
|
||||
}
|
||||
this.roomFiles = [...this.roomFiles, message];
|
||||
}
|
||||
});
|
||||
|
||||
this.ddp.on('meteor_accounts_loginServiceConfiguration', (ddpMessage) => {
|
||||
if (ddpMessage.msg === 'added') {
|
||||
this.loginServices = this.loginServices || {};
|
||||
|
@ -750,6 +822,15 @@ const RocketChat = {
|
|||
},
|
||||
getRoomMembers(rid, allUsers) {
|
||||
return call('getUsersOfRoom', rid, allUsers);
|
||||
},
|
||||
toggleBlockUser(rid, blocked, block) {
|
||||
if (block) {
|
||||
return call('blockUser', { rid, blocked });
|
||||
}
|
||||
return call('unblockUser', { rid, blocked });
|
||||
},
|
||||
leaveRoom(rid) {
|
||||
return call('leaveRoom', rid);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ import activeUsers from './activeUsers';
|
|||
import starredMessages from './starredMessages';
|
||||
import pinnedMessages from './pinnedMessages';
|
||||
import mentionedMessages from './mentionedMessages';
|
||||
import snippetedMessages from './snippetedMessages';
|
||||
import roomFiles from './roomFiles';
|
||||
|
||||
export default combineReducers({
|
||||
settings,
|
||||
|
@ -32,5 +34,7 @@ export default combineReducers({
|
|||
activeUsers,
|
||||
starredMessages,
|
||||
pinnedMessages,
|
||||
mentionedMessages
|
||||
mentionedMessages,
|
||||
snippetedMessages,
|
||||
roomFiles
|
||||
});
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import { ROOM_FILES } from '../actions/actionsTypes';
|
||||
|
||||
const initialState = {
|
||||
messages: []
|
||||
};
|
||||
|
||||
export default function server(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case ROOM_FILES.MESSAGES_RECEIVED:
|
||||
return {
|
||||
...state,
|
||||
messages: [...state.messages, ...action.messages]
|
||||
};
|
||||
case ROOM_FILES.CLOSE:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { SNIPPETED_MESSAGES } from '../actions/actionsTypes';
|
||||
|
||||
const initialState = {
|
||||
messages: []
|
||||
};
|
||||
|
||||
export default function server(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case SNIPPETED_MESSAGES.MESSAGES_RECEIVED:
|
||||
return {
|
||||
...state,
|
||||
messages: [...state.messages, ...action.messages]
|
||||
};
|
||||
case SNIPPETED_MESSAGES.CLOSE:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -12,6 +12,8 @@ import activeUsers from './activeUsers';
|
|||
import starredMessages from './starredMessages';
|
||||
import pinnedMessages from './pinnedMessages';
|
||||
import mentionedMessages from './mentionedMessages';
|
||||
import snippetedMessages from './snippetedMessages';
|
||||
import roomFiles from './roomFiles';
|
||||
|
||||
const root = function* root() {
|
||||
yield all([
|
||||
|
@ -27,7 +29,9 @@ const root = function* root() {
|
|||
activeUsers(),
|
||||
starredMessages(),
|
||||
pinnedMessages(),
|
||||
mentionedMessages()
|
||||
mentionedMessages(),
|
||||
snippetedMessages(),
|
||||
roomFiles()
|
||||
]);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { take, takeLatest } from 'redux-saga/effects';
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
|
||||
const watchRoomFiles = function* watchRoomFiles({ rid }) {
|
||||
const sub = yield RocketChat.subscribe('roomFiles', rid, 50);
|
||||
yield take(types.ROOM_FILES.CLOSE);
|
||||
sub.unsubscribe().catch(e => alert(e));
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeLatest(types.ROOM_FILES.OPEN, watchRoomFiles);
|
||||
};
|
||||
export default root;
|
|
@ -1,3 +1,4 @@
|
|||
import { Alert } from 'react-native';
|
||||
import { put, call, takeLatest, take, select, race, fork, cancel, takeEvery } from 'redux-saga/effects';
|
||||
import { delay } from 'redux-saga';
|
||||
import { FOREGROUND, BACKGROUND } from 'redux-enhancer-react-native-appstate';
|
||||
|
@ -7,6 +8,9 @@ import { addUserTyping, removeUserTyping, setLastOpen } from '../actions/room';
|
|||
import { messagesRequest } from '../actions/messages';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import database from '../lib/realm';
|
||||
import * as NavigationService from '../containers/routes/NavigationService';
|
||||
|
||||
const leaveRoom = rid => RocketChat.leaveRoom(rid);
|
||||
|
||||
const getRooms = function* getRooms() {
|
||||
return yield RocketChat.getRooms();
|
||||
|
@ -117,6 +121,26 @@ const updateLastOpen = function* updateLastOpen() {
|
|||
yield put(setLastOpen());
|
||||
};
|
||||
|
||||
const handleLeaveRoom = function* handleLeaveRoom({ rid }) {
|
||||
try {
|
||||
yield call(leaveRoom, rid);
|
||||
NavigationService.goRoomsList();
|
||||
yield delay(1000);
|
||||
database.write(() => {
|
||||
const messages = database.objects('messages').filtered('rid = $0', rid);
|
||||
database.delete(messages);
|
||||
const subscription = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||
database.delete(subscription);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.error === 'error-you-are-last-owner') {
|
||||
Alert.alert('You are the last owner. Please set new owner before leaving the room.');
|
||||
} else {
|
||||
Alert.alert(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeLatest(types.ROOM.USER_TYPING, watchuserTyping);
|
||||
yield takeLatest(types.LOGIN.SUCCESS, watchRoomsRequest);
|
||||
|
@ -125,5 +149,6 @@ const root = function* root() {
|
|||
yield takeLatest(FOREGROUND, updateRoom);
|
||||
yield takeLatest(FOREGROUND, watchRoomsRequest);
|
||||
yield takeLatest(BACKGROUND, updateLastOpen);
|
||||
yield takeLatest(types.ROOM.LEAVE, handleLeaveRoom);
|
||||
};
|
||||
export default root;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { take, takeLatest } from 'redux-saga/effects';
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
|
||||
const watchSnippetedMessagesRoom = function* watchSnippetedMessagesRoom({ rid }) {
|
||||
const sub = yield RocketChat.subscribe('snippetedMessages', rid, 50);
|
||||
yield take(types.SNIPPETED_MESSAGES.CLOSE);
|
||||
sub.unsubscribe().catch(e => alert(e));
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeLatest(types.SNIPPETED_MESSAGES.OPEN, watchSnippetedMessagesRoom);
|
||||
};
|
||||
export default root;
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { View, SectionList, Text } from 'react-native';
|
||||
import { View, SectionList, Text, Alert } from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -10,14 +10,20 @@ import Avatar from '../../containers/Avatar';
|
|||
import Touch from '../../utils/touch';
|
||||
import database from '../../lib/realm';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import { leaveRoom } from '../../actions/room';
|
||||
|
||||
@connect(state => ({
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||
user: state.login.user
|
||||
}), dispatch => ({
|
||||
leaveRoom: rid => dispatch(leaveRoom(rid))
|
||||
}))
|
||||
export default class RoomActionsView extends React.PureComponent {
|
||||
static propTypes = {
|
||||
baseUrl: PropTypes.string,
|
||||
navigation: PropTypes.object
|
||||
user: PropTypes.object,
|
||||
navigation: PropTypes.object,
|
||||
leaveRoom: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -26,24 +32,53 @@ export default class RoomActionsView extends React.PureComponent {
|
|||
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||
this.state = {
|
||||
sections: [],
|
||||
room: {}
|
||||
room: {},
|
||||
members: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateRoom();
|
||||
this.updateSections();
|
||||
async componentDidMount() {
|
||||
await this.updateRoom();
|
||||
this.updateRoomMembers();
|
||||
this.rooms.addListener(this.updateRoom);
|
||||
}
|
||||
|
||||
updateRoom = () => {
|
||||
componentWillUnmount() {
|
||||
this.rooms.removeAllListeners();
|
||||
}
|
||||
|
||||
onPressTouchable = (item) => {
|
||||
if (item.route) {
|
||||
return this.props.navigation.navigate(item.route, item.params);
|
||||
}
|
||||
if (item.event) {
|
||||
return item.event();
|
||||
}
|
||||
}
|
||||
|
||||
getRoomTitle = room => (room.t === 'd' ? room.fname : room.name);
|
||||
|
||||
updateRoomMembers = async() => {
|
||||
let members;
|
||||
try {
|
||||
const membersResult = await RocketChat.getRoomMembers(this.state.room.rid, false);
|
||||
members = membersResult.records;
|
||||
} catch (error) {
|
||||
return;
|
||||
}
|
||||
this.setState({ members });
|
||||
this.updateSections();
|
||||
}
|
||||
|
||||
updateRoom = async() => {
|
||||
const [room] = this.rooms;
|
||||
this.setState({ room });
|
||||
await this.setState({ room });
|
||||
this.updateSections();
|
||||
}
|
||||
|
||||
updateSections = async() => {
|
||||
const { rid, t } = this.state.room;
|
||||
const { rid, t, blocked } = this.state.room;
|
||||
const { members } = this.state;
|
||||
const sections = [{
|
||||
data: [{ icon: 'ios-star', name: 'USER' }],
|
||||
renderItem: this.renderRoomInfo
|
||||
|
@ -55,7 +90,12 @@ export default class RoomActionsView extends React.PureComponent {
|
|||
renderItem: this.renderItem
|
||||
}, {
|
||||
data: [
|
||||
{ icon: 'ios-attach', name: 'Files' },
|
||||
{
|
||||
icon: 'ios-attach',
|
||||
name: 'Files',
|
||||
route: 'RoomFiles',
|
||||
params: { rid }
|
||||
},
|
||||
{
|
||||
icon: 'ios-at-outline',
|
||||
name: 'Mentions',
|
||||
|
@ -76,7 +116,12 @@ export default class RoomActionsView extends React.PureComponent {
|
|||
route: 'PinnedMessages',
|
||||
params: { rid }
|
||||
},
|
||||
{ icon: 'ios-code', name: 'Snippets' },
|
||||
{
|
||||
icon: 'ios-code',
|
||||
name: 'Snippets',
|
||||
route: 'SnippetedMessages',
|
||||
params: { rid }
|
||||
},
|
||||
{ icon: 'ios-notifications-outline', name: 'Notifications preferences' }
|
||||
],
|
||||
renderItem: this.renderItem
|
||||
|
@ -85,25 +130,34 @@ export default class RoomActionsView extends React.PureComponent {
|
|||
sections.push({
|
||||
data: [
|
||||
{ icon: 'ios-volume-off', name: 'Mute user' },
|
||||
{ icon: 'block', name: 'Block user', type: 'danger' }
|
||||
{
|
||||
icon: 'block',
|
||||
name: `${ blocked ? 'Unblock' : 'Block' } user`,
|
||||
type: 'danger',
|
||||
event: () => this.toggleBlockUser()
|
||||
}
|
||||
],
|
||||
renderItem: this.renderItem
|
||||
});
|
||||
} else if (t === 'c' || t === 'p') {
|
||||
const membersResult = await RocketChat.getRoomMembers(rid, false);
|
||||
const members = membersResult.records;
|
||||
|
||||
sections[2].data.unshift({
|
||||
icon: 'ios-people',
|
||||
name: 'Members',
|
||||
description: (members.length === 1 ? `${ members.length } member` : `${ members.length } members`),
|
||||
route: 'RoomMembers',
|
||||
params: { rid, members }
|
||||
});
|
||||
if (members.length > 0) {
|
||||
sections[2].data.unshift({
|
||||
icon: 'ios-people',
|
||||
name: 'Members',
|
||||
description: (members.length === 1 ? `${ members.length } member` : `${ members.length } members`),
|
||||
route: 'RoomMembers',
|
||||
params: { rid, members }
|
||||
});
|
||||
}
|
||||
sections.push({
|
||||
data: [
|
||||
{ icon: 'ios-volume-off', name: 'Mute channel' },
|
||||
{ icon: 'block', name: 'Leave channel', type: 'danger' }
|
||||
{
|
||||
icon: 'block',
|
||||
name: 'Leave channel',
|
||||
type: 'danger',
|
||||
event: () => this.leaveChannel()
|
||||
}
|
||||
],
|
||||
renderItem: this.renderItem
|
||||
});
|
||||
|
@ -111,10 +165,37 @@ export default class RoomActionsView extends React.PureComponent {
|
|||
this.setState({ sections });
|
||||
}
|
||||
|
||||
toggleBlockUser = () => {
|
||||
const { rid, blocked } = this.state.room;
|
||||
const { members } = this.state;
|
||||
const member = members.find(m => m.id !== this.props.user.id);
|
||||
RocketChat.toggleBlockUser(rid, member._id, !blocked);
|
||||
}
|
||||
|
||||
leaveChannel = () => {
|
||||
const { room } = this.state;
|
||||
Alert.alert(
|
||||
'Are you sure?',
|
||||
`Are you sure you want to leave the room ${ this.getRoomTitle(room) }?`,
|
||||
[
|
||||
{
|
||||
text: 'Cancel',
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: 'Yes, leave it!',
|
||||
style: 'destructive',
|
||||
onPress: async() => {
|
||||
this.props.leaveRoom(room.rid);
|
||||
}
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
renderRoomInfo = ({ item }) => {
|
||||
const {
|
||||
fname, name, t, topic
|
||||
} = this.state.room;
|
||||
const { room } = this.state;
|
||||
const { name, t, topic } = room;
|
||||
return (
|
||||
this.renderTouchableItem([
|
||||
<Avatar
|
||||
|
@ -126,7 +207,7 @@ export default class RoomActionsView extends React.PureComponent {
|
|||
type={t}
|
||||
/>,
|
||||
<View key='name' style={styles.roomTitleContainer}>
|
||||
<Text style={styles.roomTitle}>{t === 'd' ? fname : name}</Text>
|
||||
<Text style={styles.roomTitle}>{ this.getRoomTitle(room) }</Text>
|
||||
<Text style={styles.roomDescription} ellipsizeMode='tail' numberOfLines={1}>{t === 'd' ? `@${ name }` : topic}</Text>
|
||||
</View>,
|
||||
<Icon key='icon' name='ios-arrow-forward' size={20} style={styles.sectionItemIcon} color='#cbced1' />
|
||||
|
@ -136,7 +217,7 @@ export default class RoomActionsView extends React.PureComponent {
|
|||
|
||||
renderTouchableItem = (subview, item) => (
|
||||
<Touch
|
||||
onPress={() => item.route && this.props.navigation.navigate(item.route, item.params)}
|
||||
onPress={() => this.onPressTouchable(item)}
|
||||
underlayColor='#FFFFFF'
|
||||
activeOpacity={0.5}
|
||||
accessibilityLabel={item.name}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FlatList, Text, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { openRoomFiles, closeRoomFiles } from '../../actions/roomFiles';
|
||||
import styles from './styles';
|
||||
import Message from '../../containers/message';
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
messages: state.roomFiles.messages,
|
||||
user: state.login.user,
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||
}),
|
||||
dispatch => ({
|
||||
openRoomFiles: rid => dispatch(openRoomFiles(rid)),
|
||||
closeRoomFiles: () => dispatch(closeRoomFiles())
|
||||
})
|
||||
)
|
||||
export default class RoomFilesView extends React.PureComponent {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
messages: PropTypes.array,
|
||||
user: PropTypes.object,
|
||||
baseUrl: PropTypes.string,
|
||||
openRoomFiles: PropTypes.func,
|
||||
closeRoomFiles: PropTypes.func
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.openRoomFiles(this.props.navigation.state.params.rid);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.closeRoomFiles();
|
||||
}
|
||||
|
||||
renderEmpty = () => (
|
||||
<View style={styles.listEmptyContainer}>
|
||||
<Text>No files</Text>
|
||||
</View>
|
||||
)
|
||||
|
||||
renderItem = ({ item }) => (
|
||||
<Message
|
||||
item={item}
|
||||
style={styles.message}
|
||||
user={this.props.user}
|
||||
baseUrl={this.props.baseUrl}
|
||||
Message_TimeFormat='MMMM Do YYYY, h:mm:ss a'
|
||||
/>
|
||||
)
|
||||
|
||||
render() {
|
||||
if (this.props.messages.length === 0) {
|
||||
return this.renderEmpty();
|
||||
}
|
||||
return (
|
||||
<FlatList
|
||||
key='room-files-view-list'
|
||||
data={this.props.messages}
|
||||
renderItem={this.renderItem}
|
||||
style={styles.list}
|
||||
keyExtractor={item => item._id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
|
||||
export default StyleSheet.create({
|
||||
list: {
|
||||
flex: 1,
|
||||
backgroundColor: '#ffffff'
|
||||
},
|
||||
message: {
|
||||
transform: [{ scaleY: 1 }]
|
||||
},
|
||||
listEmptyContainer: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#ffffff'
|
||||
}
|
||||
});
|
|
@ -53,7 +53,6 @@ export default class RoomView extends React.Component {
|
|||
loading: PropTypes.bool,
|
||||
actionMessage: PropTypes.object,
|
||||
toggleReactionPicker: PropTypes.func.isRequired,
|
||||
// layoutAnimation: PropTypes.instanceOf(Date),
|
||||
actionsShow: PropTypes.func
|
||||
};
|
||||
|
||||
|
@ -92,16 +91,10 @@ export default class RoomView extends React.Component {
|
|||
|
||||
this.rooms.addListener(this.updateRoom);
|
||||
}
|
||||
// componentWillReceiveProps(nextProps) {
|
||||
// // if (this.props.layoutAnimation !== nextProps.layoutAnimation) {
|
||||
// // LayoutAnimation.spring();
|
||||
// // }
|
||||
// }
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return !(equal(this.props, nextProps) && equal(this.state, nextState));
|
||||
return !(equal(this.props, nextProps) && equal(this.state, nextState) && this.state.room.ro === nextState.room.ro);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.timer);
|
||||
this.rooms.removeAllListeners();
|
||||
this.props.editCancel();
|
||||
}
|
||||
|
@ -136,6 +129,7 @@ export default class RoomView extends React.Component {
|
|||
|
||||
updateRoom = () => {
|
||||
this.setState({ room: this.rooms[0] });
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
sendMessage = (message) => {
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FlatList, Text, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { openSnippetedMessages, closeSnippetedMessages } from '../../actions/snippetedMessages';
|
||||
import styles from './styles';
|
||||
import Message from '../../containers/message';
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
messages: state.snippetedMessages.messages,
|
||||
user: state.login.user,
|
||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||
}),
|
||||
dispatch => ({
|
||||
openSnippetedMessages: rid => dispatch(openSnippetedMessages(rid)),
|
||||
closeSnippetedMessages: () => dispatch(closeSnippetedMessages())
|
||||
})
|
||||
)
|
||||
export default class SnippetedMessagesView extends React.PureComponent {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
messages: PropTypes.array,
|
||||
user: PropTypes.object,
|
||||
baseUrl: PropTypes.string,
|
||||
openSnippetedMessages: PropTypes.func,
|
||||
closeSnippetedMessages: PropTypes.func
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.openSnippetedMessages(this.props.navigation.state.params.rid);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.closeSnippetedMessages();
|
||||
}
|
||||
|
||||
renderEmpty = () => (
|
||||
<View style={styles.listEmptyContainer}>
|
||||
<Text>No snippet messages</Text>
|
||||
</View>
|
||||
)
|
||||
|
||||
renderItem = ({ item }) => (
|
||||
<Message
|
||||
item={item}
|
||||
style={styles.message}
|
||||
reactions={item.reactions}
|
||||
user={this.props.user}
|
||||
baseUrl={this.props.baseUrl}
|
||||
Message_TimeFormat='MMMM Do YYYY, h:mm:ss a'
|
||||
onLongPress={() => {}}
|
||||
/>
|
||||
)
|
||||
|
||||
render() {
|
||||
if (this.props.messages.length === 0) {
|
||||
return this.renderEmpty();
|
||||
}
|
||||
return (
|
||||
<FlatList
|
||||
key='snippet-messages-view-list'
|
||||
data={this.props.messages}
|
||||
renderItem={this.renderItem}
|
||||
style={styles.list}
|
||||
keyExtractor={item => item._id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
|
||||
export default StyleSheet.create({
|
||||
list: {
|
||||
flex: 1,
|
||||
backgroundColor: '#ffffff'
|
||||
},
|
||||
message: {
|
||||
transform: [{ scaleY: 1 }]
|
||||
},
|
||||
listEmptyContainer: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#ffffff'
|
||||
}
|
||||
});
|
122
yarn.lock
122
yarn.lock
|
@ -568,15 +568,15 @@ after@0.8.2:
|
|||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
|
||||
|
||||
ajv-keywords@^1.0.0:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
|
||||
ajv-keywords@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762"
|
||||
|
||||
ajv-keywords@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be"
|
||||
|
||||
ajv@^4.7.0, ajv@^4.9.1:
|
||||
ajv@^4.9.1:
|
||||
version "4.11.8"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
|
||||
dependencies:
|
||||
|
@ -592,7 +592,7 @@ ajv@^5.0.0:
|
|||
json-schema-traverse "^0.3.0"
|
||||
json-stable-stringify "^1.0.1"
|
||||
|
||||
ajv@^5.1.0, ajv@^5.3.0:
|
||||
ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0:
|
||||
version "5.5.2"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
|
||||
dependencies:
|
||||
|
@ -938,7 +938,7 @@ babel-core@^6.0.0, babel-core@^6.24.1, babel-core@^6.26.0, babel-core@^6.7.2:
|
|||
slash "^1.0.0"
|
||||
source-map "^0.5.6"
|
||||
|
||||
babel-eslint@^8.2.2:
|
||||
babel-eslint@^8.0.2:
|
||||
version "8.2.2"
|
||||
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.2.tgz#1102273354c6f0b29b4ea28a65f97d122296b68b"
|
||||
dependencies:
|
||||
|
@ -3651,7 +3651,7 @@ eslint-module-utils@^2.1.1:
|
|||
debug "^2.6.8"
|
||||
pkg-dir "^1.0.0"
|
||||
|
||||
eslint-plugin-import@^2.9.0:
|
||||
eslint-plugin-import@^2.8.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.9.0.tgz#26002efbfca5989b7288ac047508bd24f217b169"
|
||||
dependencies:
|
||||
|
@ -3688,7 +3688,7 @@ eslint-plugin-react-native@^3.2.0:
|
|||
dependencies:
|
||||
eslint-plugin-react-native-globals "^0.1.1"
|
||||
|
||||
eslint-plugin-react@^7.7.0:
|
||||
eslint-plugin-react@^7.5.1:
|
||||
version "7.7.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz#f606c719dbd8a1a2b3d25c16299813878cca0160"
|
||||
dependencies:
|
||||
|
@ -3712,9 +3712,9 @@ eslint-visitor-keys@^1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
|
||||
|
||||
eslint@^4.18.1:
|
||||
version "4.18.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.18.1.tgz#b9138440cb1e98b2f44a0d578c6ecf8eae6150b0"
|
||||
eslint@^4.12.0:
|
||||
version "4.18.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.18.2.tgz#0f81267ad1012e7d2051e186a9004cc2267b8d45"
|
||||
dependencies:
|
||||
ajv "^5.3.0"
|
||||
babel-code-frame "^6.22.0"
|
||||
|
@ -3751,7 +3751,7 @@ eslint@^4.18.1:
|
|||
semver "^5.3.0"
|
||||
strip-ansi "^4.0.0"
|
||||
strip-json-comments "~2.0.1"
|
||||
table "^4.0.1"
|
||||
table "4.0.2"
|
||||
text-table "~0.2.0"
|
||||
|
||||
espree@^3.5.2:
|
||||
|
@ -5906,14 +5906,6 @@ lodash.escape@^3.0.0:
|
|||
dependencies:
|
||||
lodash._root "^3.0.0"
|
||||
|
||||
lodash.every@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.every/-/lodash.every-4.6.0.tgz#eb89984bebc4364279bb3aefbbd1ca19bfa6c6a7"
|
||||
|
||||
lodash.filter@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
|
||||
|
||||
lodash.isarguments@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
|
||||
|
@ -5922,22 +5914,10 @@ lodash.isarray@^3.0.0:
|
|||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
|
||||
|
||||
lodash.isequal@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
|
||||
lodash.isfunction@^3.0.8:
|
||||
version "3.0.9"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
|
||||
lodash.isstring@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
|
||||
|
||||
lodash.keys@^3.0.0, lodash.keys@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
|
||||
|
@ -5946,10 +5926,6 @@ lodash.keys@^3.0.0, lodash.keys@^3.1.2:
|
|||
lodash.isarguments "^3.0.0"
|
||||
lodash.isarray "^3.0.0"
|
||||
|
||||
lodash.keys@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.2.0.tgz#a08602ac12e4fb83f91fc1fb7a360a4d9ba35205"
|
||||
|
||||
lodash.memoize@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
|
@ -6011,10 +5987,6 @@ lodash.throttle@^4.1.1:
|
|||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
|
||||
|
||||
lodash.union@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
|
||||
|
||||
lodash.uniq@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
|
@ -7694,6 +7666,10 @@ react-native-animatable@^1.2.4:
|
|||
dependencies:
|
||||
prop-types "^15.5.10"
|
||||
|
||||
react-native-audio@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-audio/-/react-native-audio-4.0.0.tgz#e263c7711150de37b013741eeb2ede37941d3011"
|
||||
|
||||
react-native-compat@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-compat/-/react-native-compat-1.0.0.tgz#491dbd8a0105ac061b8d0d926463ce6a3dff33bc"
|
||||
|
@ -7743,23 +7719,23 @@ react-native-iphone-x-helper@^1.0.1:
|
|||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.0.2.tgz#7dbca530930f7c1ce8633cc8fd13ba94102992e1"
|
||||
|
||||
react-native-keyboard-aware-scroll-view@^0.4.1:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/react-native-keyboard-aware-scroll-view/-/react-native-keyboard-aware-scroll-view-0.4.3.tgz#206e197730902ce01d026910ff8409a2a363e1ac"
|
||||
react-native-keyboard-aware-scroll-view@^0.4.4:
|
||||
version "0.4.4"
|
||||
resolved "https://registry.yarnpkg.com/react-native-keyboard-aware-scroll-view/-/react-native-keyboard-aware-scroll-view-0.4.4.tgz#116d08975a84482e4ee64dd0b526a8f04138b0a7"
|
||||
dependencies:
|
||||
prop-types "^15.6.0"
|
||||
react-native-iphone-x-helper "^1.0.1"
|
||||
|
||||
"react-native-keyboard-input@git+https://github.com/RocketChat/react-native-keyboard-input.git":
|
||||
version "5.0.0"
|
||||
resolved "git+https://github.com/RocketChat/react-native-keyboard-input.git#38273b0513f69a5e6e0719f65a675f9f2b5ee883"
|
||||
version "5.1.1"
|
||||
resolved "git+https://github.com/RocketChat/react-native-keyboard-input.git#5bc63ae8b80a4ca5a3714a81dd058ee6bbe00a30"
|
||||
dependencies:
|
||||
lodash "^4.17.4"
|
||||
react-native-keyboard-tracking-view "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git"
|
||||
|
||||
"react-native-keyboard-tracking-view@git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git":
|
||||
version "5.1.1"
|
||||
resolved "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git#3a4084f0a1063e23ae6435facdf1f79152558d15"
|
||||
version "5.3.2"
|
||||
resolved "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git#151979f82a5aba8755993e7001ba608c135de6b7"
|
||||
|
||||
react-native-loading-spinner-overlay@^0.5.2:
|
||||
version "0.5.2"
|
||||
|
@ -7879,9 +7855,9 @@ react-native-zeroconf@^0.8.3:
|
|||
dependencies:
|
||||
events "^1.1.0"
|
||||
|
||||
react-native@0.54.0-rc.4:
|
||||
version "0.54.0-rc.4"
|
||||
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.54.0-rc.4.tgz#d678ac7ae69345060c3dea639fd3665adb4016fd"
|
||||
react-native@0.54:
|
||||
version "0.54.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.54.0.tgz#1582cc2b2a639d46e39c44bcc50e051d0061820a"
|
||||
dependencies:
|
||||
absolute-path "^0.0.0"
|
||||
art "^0.10.0"
|
||||
|
@ -7942,9 +7918,9 @@ react-native@0.54.0-rc.4:
|
|||
xmldoc "^0.4.0"
|
||||
yargs "^9.0.0"
|
||||
|
||||
react-navigation@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-1.2.1.tgz#06cb2c97eb1b2e20bdb4ff7aee1acfa218a1561b"
|
||||
react-navigation@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-1.4.0.tgz#2e1ec70a5d37af78d4b2cab588caf179b2b16859"
|
||||
dependencies:
|
||||
clamp "^1.0.1"
|
||||
hoist-non-react-statics "^2.2.0"
|
||||
|
@ -8836,9 +8812,11 @@ slash@^1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
||||
|
||||
slice-ansi@0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
|
||||
slice-ansi@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
|
||||
dependencies:
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
|
||||
slide@^1.1.5:
|
||||
version "1.1.6"
|
||||
|
@ -9333,16 +9311,16 @@ sync-request@^3.0.1:
|
|||
http-response-object "^1.0.1"
|
||||
then-request "^2.0.1"
|
||||
|
||||
table@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/table/-/table-4.0.1.tgz#a8116c133fac2c61f4a420ab6cdf5c4d61f0e435"
|
||||
table@4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
|
||||
dependencies:
|
||||
ajv "^4.7.0"
|
||||
ajv-keywords "^1.0.0"
|
||||
chalk "^1.1.1"
|
||||
lodash "^4.0.0"
|
||||
slice-ansi "0.0.4"
|
||||
string-width "^2.0.0"
|
||||
ajv "^5.2.3"
|
||||
ajv-keywords "^2.1.0"
|
||||
chalk "^2.1.0"
|
||||
lodash "^4.17.4"
|
||||
slice-ansi "1.0.0"
|
||||
string-width "^2.1.1"
|
||||
|
||||
tapable@^0.2.7:
|
||||
version "0.2.8"
|
||||
|
@ -9986,20 +9964,6 @@ which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0:
|
|||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
why-did-you-update@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/why-did-you-update/-/why-did-you-update-0.1.1.tgz#c73d361511fecd899056e9954ca9b1ab760d3097"
|
||||
dependencies:
|
||||
lodash.every "^4.6.0"
|
||||
lodash.filter "^4.6.0"
|
||||
lodash.isequal "^4.5.0"
|
||||
lodash.isfunction "^3.0.8"
|
||||
lodash.isstring "^4.0.1"
|
||||
lodash.keys "^4.2.0"
|
||||
lodash.pick "^4.4.0"
|
||||
lodash.some "^4.6.0"
|
||||
lodash.union "^4.6.0"
|
||||
|
||||
wide-align@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"
|
||||
|
|
Loading…
Reference in New Issue