[NEW] Threads (#798)
This commit is contained in:
parent
5aec0ec186
commit
9cf81bbab9
File diff suppressed because it is too large
Load Diff
|
@ -1,25 +1,5 @@
|
||||||
import * as types from './actionsTypes';
|
import * as types from './actionsTypes';
|
||||||
|
|
||||||
export function messagesRequest(room) {
|
|
||||||
return {
|
|
||||||
type: types.MESSAGES.REQUEST,
|
|
||||||
room
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function messagesSuccess() {
|
|
||||||
return {
|
|
||||||
type: types.MESSAGES.SUCCESS
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function messagesFailure(err) {
|
|
||||||
return {
|
|
||||||
type: types.MESSAGES.FAILURE,
|
|
||||||
err
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function actionsShow(actionMessage) {
|
export function actionsShow(actionMessage) {
|
||||||
return {
|
return {
|
||||||
type: types.MESSAGES.ACTIONS_SHOW,
|
type: types.MESSAGES.ACTIONS_SHOW,
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
import { SERVER } from './actionsTypes';
|
import { SERVER } from './actionsTypes';
|
||||||
|
|
||||||
export function selectServerRequest(server) {
|
export function selectServerRequest(server, version, fetchVersion = true) {
|
||||||
return {
|
return {
|
||||||
type: SERVER.SELECT_REQUEST,
|
type: SERVER.SELECT_REQUEST,
|
||||||
server
|
server,
|
||||||
|
version,
|
||||||
|
fetchVersion
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectServerSuccess(server) {
|
export function selectServerSuccess(server, version) {
|
||||||
return {
|
return {
|
||||||
type: SERVER.SELECT_SUCCESS,
|
type: SERVER.SELECT_SUCCESS,
|
||||||
server
|
server,
|
||||||
|
version
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,5 +61,8 @@ export default {
|
||||||
},
|
},
|
||||||
Assets_favicon_512: {
|
Assets_favicon_512: {
|
||||||
type: null
|
type: null
|
||||||
|
},
|
||||||
|
Threads_enabled: {
|
||||||
|
type: null
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -56,6 +56,7 @@ class MessageBox extends Component {
|
||||||
replyMessage: PropTypes.object,
|
replyMessage: PropTypes.object,
|
||||||
replying: PropTypes.bool,
|
replying: PropTypes.bool,
|
||||||
editing: PropTypes.bool,
|
editing: PropTypes.bool,
|
||||||
|
threadsEnabled: PropTypes.bool,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
username: PropTypes.string,
|
username: PropTypes.string,
|
||||||
|
@ -93,7 +94,7 @@ class MessageBox extends Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { rid } = this.props;
|
const { rid } = this.props;
|
||||||
const [room] = database.objects('subscriptions').filtered('rid = $0', rid);
|
const [room] = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||||
if (room.draftMessage && room.draftMessage !== '') {
|
if (room && room.draftMessage) {
|
||||||
this.setInput(room.draftMessage);
|
this.setInput(room.draftMessage);
|
||||||
this.setShowSend(true);
|
this.setShowSend(true);
|
||||||
}
|
}
|
||||||
|
@ -571,31 +572,42 @@ class MessageBox extends Component {
|
||||||
if (message.trim() === '') {
|
if (message.trim() === '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if is editing a message
|
|
||||||
const {
|
const {
|
||||||
editing, replying
|
editing, replying
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
// Edit
|
||||||
if (editing) {
|
if (editing) {
|
||||||
const { _id, rid } = editingMessage;
|
const { _id, rid } = editingMessage;
|
||||||
editRequest({ _id, msg: message, rid });
|
editRequest({ _id, msg: message, rid });
|
||||||
|
|
||||||
|
// Reply
|
||||||
} else if (replying) {
|
} else if (replying) {
|
||||||
const {
|
const { replyMessage, closeReply, threadsEnabled } = this.props;
|
||||||
user, replyMessage, roomType, closeReply
|
|
||||||
} = this.props;
|
|
||||||
const permalink = await this.getPermalink(replyMessage);
|
|
||||||
let msg = `[ ](${ permalink }) `;
|
|
||||||
|
|
||||||
// if original message wasn't sent by current user and neither from a direct room
|
// Thread
|
||||||
if (user.username !== replyMessage.u.username && roomType !== 'd' && replyMessage.mention) {
|
if (threadsEnabled) {
|
||||||
msg += `@${ replyMessage.u.username } `;
|
onSubmit(message, replyMessage._id);
|
||||||
|
|
||||||
|
// Legacy reply
|
||||||
|
} else {
|
||||||
|
const { user, roomType } = this.props;
|
||||||
|
const permalink = await this.getPermalink(replyMessage);
|
||||||
|
let msg = `[ ](${ permalink }) `;
|
||||||
|
|
||||||
|
// if original message wasn't sent by current user and neither from a direct room
|
||||||
|
if (user.username !== replyMessage.u.username && roomType !== 'd' && replyMessage.mention) {
|
||||||
|
msg += `@${ replyMessage.u.username } `;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = `${ msg } ${ message }`;
|
||||||
|
onSubmit(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = `${ msg } ${ message }`;
|
|
||||||
onSubmit(msg);
|
|
||||||
closeReply();
|
closeReply();
|
||||||
|
|
||||||
|
// Normal message
|
||||||
} else {
|
} else {
|
||||||
// if is submiting a new message
|
|
||||||
onSubmit(message);
|
onSubmit(message);
|
||||||
}
|
}
|
||||||
this.clearInput();
|
this.clearInput();
|
||||||
|
@ -820,6 +832,7 @@ const mapStateToProps = state => ({
|
||||||
replying: state.messages.replyMessage && !!state.messages.replyMessage.msg,
|
replying: state.messages.replyMessage && !!state.messages.replyMessage.msg,
|
||||||
editing: state.messages.editing,
|
editing: state.messages.editing,
|
||||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||||
|
threadsEnabled: state.settings.Threads_enabled,
|
||||||
user: {
|
user: {
|
||||||
id: state.login.user && state.login.user.id,
|
id: state.login.user && state.login.user.id,
|
||||||
username: state.login.user && state.login.user.username,
|
username: state.login.user && state.login.user.username,
|
||||||
|
|
|
@ -85,8 +85,6 @@ const getInfoMessage = ({
|
||||||
return I18n.t('Room_changed_privacy', { type: msg, userBy: username });
|
return I18n.t('Room_changed_privacy', { type: msg, userBy: username });
|
||||||
} else if (type === 'message_snippeted') {
|
} else if (type === 'message_snippeted') {
|
||||||
return I18n.t('Created_snippet');
|
return I18n.t('Created_snippet');
|
||||||
} else if (type === 'thread-created') {
|
|
||||||
return I18n.t('Thread_created', { name: msg });
|
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
@ -99,6 +97,7 @@ export default class Message extends PureComponent {
|
||||||
baseUrl: PropTypes.string.isRequired,
|
baseUrl: PropTypes.string.isRequired,
|
||||||
customEmojis: PropTypes.object.isRequired,
|
customEmojis: PropTypes.object.isRequired,
|
||||||
timeFormat: PropTypes.string.isRequired,
|
timeFormat: PropTypes.string.isRequired,
|
||||||
|
customThreadTimeFormat: PropTypes.string,
|
||||||
msg: PropTypes.string,
|
msg: PropTypes.string,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
|
@ -137,6 +136,10 @@ export default class Message extends PureComponent {
|
||||||
useRealName: PropTypes.bool,
|
useRealName: PropTypes.bool,
|
||||||
dcount: PropTypes.number,
|
dcount: PropTypes.number,
|
||||||
dlm: PropTypes.instanceOf(Date),
|
dlm: PropTypes.instanceOf(Date),
|
||||||
|
tmid: PropTypes.string,
|
||||||
|
tcount: PropTypes.number,
|
||||||
|
tlm: PropTypes.instanceOf(Date),
|
||||||
|
tmsg: PropTypes.string,
|
||||||
// methods
|
// methods
|
||||||
closeReactions: PropTypes.func,
|
closeReactions: PropTypes.func,
|
||||||
onErrorPress: PropTypes.func,
|
onErrorPress: PropTypes.func,
|
||||||
|
@ -144,8 +147,10 @@ export default class Message extends PureComponent {
|
||||||
onReactionLongPress: PropTypes.func,
|
onReactionLongPress: PropTypes.func,
|
||||||
onReactionPress: PropTypes.func,
|
onReactionPress: PropTypes.func,
|
||||||
onDiscussionPress: PropTypes.func,
|
onDiscussionPress: PropTypes.func,
|
||||||
|
onThreadPress: PropTypes.func,
|
||||||
replyBroadcast: PropTypes.func,
|
replyBroadcast: PropTypes.func,
|
||||||
toggleReactionPicker: PropTypes.func
|
toggleReactionPicker: PropTypes.func,
|
||||||
|
fetchThreadName: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -169,6 +174,32 @@ export default class Message extends PureComponent {
|
||||||
onLongPress();
|
onLongPress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formatLastMessage = (lm) => {
|
||||||
|
const { customThreadTimeFormat } = this.props;
|
||||||
|
if (customThreadTimeFormat) {
|
||||||
|
return moment(lm).format(customThreadTimeFormat);
|
||||||
|
}
|
||||||
|
return lm ? moment(lm).calendar(null, {
|
||||||
|
lastDay: `[${ I18n.t('Yesterday') }]`,
|
||||||
|
sameDay: 'h:mm A',
|
||||||
|
lastWeek: 'dddd',
|
||||||
|
sameElse: 'MMM D'
|
||||||
|
}) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatMessageCount = (count, type) => {
|
||||||
|
const discussion = type === 'discussion';
|
||||||
|
let text = discussion ? I18n.t('No_messages_yet') : null;
|
||||||
|
if (count === 1) {
|
||||||
|
text = `${ count } ${ discussion ? I18n.t('message') : I18n.t('reply') }`;
|
||||||
|
} else if (count > 1 && count < 1000) {
|
||||||
|
text = `${ count } ${ discussion ? I18n.t('messages') : I18n.t('replies') }`;
|
||||||
|
} else if (count > 999) {
|
||||||
|
text = `+999 ${ discussion ? I18n.t('messages') : I18n.t('replies') }`;
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
isInfoMessage = () => {
|
isInfoMessage = () => {
|
||||||
const { type } = this.props;
|
const { type } = this.props;
|
||||||
return SYSTEM_MESSAGES.includes(type);
|
return SYSTEM_MESSAGES.includes(type);
|
||||||
|
@ -369,23 +400,11 @@ export default class Message extends PureComponent {
|
||||||
const {
|
const {
|
||||||
msg, dcount, dlm, onDiscussionPress
|
msg, dcount, dlm, onDiscussionPress
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const time = dlm ? moment(dlm).calendar(null, {
|
const time = this.formatLastMessage(dlm);
|
||||||
lastDay: `[${ I18n.t('Yesterday') }]`,
|
const buttonText = this.formatMessageCount(dcount, 'discussion');
|
||||||
sameDay: 'h:mm A',
|
|
||||||
lastWeek: 'dddd',
|
|
||||||
sameElse: 'MMM D'
|
|
||||||
}) : null;
|
|
||||||
let buttonText = 'No messages yet';
|
|
||||||
if (dcount === 1) {
|
|
||||||
buttonText = `${ dcount } message`;
|
|
||||||
} else if (dcount > 1 && dcount < 1000) {
|
|
||||||
buttonText = `${ dcount } messages`;
|
|
||||||
} else if (dcount > 999) {
|
|
||||||
buttonText = '+999 messages';
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Text style={styles.textInfo}>{I18n.t('Started_discussion')}</Text>
|
<Text style={styles.startedDiscussion}>{I18n.t('Started_discussion')}</Text>
|
||||||
<Text style={styles.text}>{msg}</Text>
|
<Text style={styles.text}>{msg}</Text>
|
||||||
<View style={styles.buttonContainer}>
|
<View style={styles.buttonContainer}>
|
||||||
<Touchable
|
<Touchable
|
||||||
|
@ -405,6 +424,56 @@ export default class Message extends PureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderThread = () => {
|
||||||
|
const {
|
||||||
|
tcount, tlm, onThreadPress, msg
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (!tlm) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const time = this.formatLastMessage(tlm);
|
||||||
|
const buttonText = this.formatMessageCount(tcount, 'thread');
|
||||||
|
return (
|
||||||
|
<View style={styles.buttonContainer}>
|
||||||
|
<Touchable
|
||||||
|
onPress={onThreadPress}
|
||||||
|
background={Touchable.Ripple('#fff')}
|
||||||
|
style={[styles.button, styles.smallButton]}
|
||||||
|
hitSlop={BUTTON_HIT_SLOP}
|
||||||
|
testID={`message-thread-button-${ msg }`}
|
||||||
|
>
|
||||||
|
<React.Fragment>
|
||||||
|
<CustomIcon name='thread' size={20} style={styles.buttonIcon} />
|
||||||
|
<Text style={styles.buttonText}>{buttonText}</Text>
|
||||||
|
</React.Fragment>
|
||||||
|
</Touchable>
|
||||||
|
<Text style={styles.time}>{time}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRepliedThread = () => {
|
||||||
|
const {
|
||||||
|
tmid, tmsg, header, onThreadPress, fetchThreadName
|
||||||
|
} = this.props;
|
||||||
|
if (!tmid || !header || this.isTemp()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tmsg) {
|
||||||
|
fetchThreadName(tmid);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text style={styles.repliedThread} numberOfLines={3} testID={`message-thread-replied-on-${ tmsg }`}>
|
||||||
|
{I18n.t('Replied_on')} <Text style={styles.repliedThreadName} onPress={onThreadPress}>{tmsg}</Text>
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderInner = () => {
|
renderInner = () => {
|
||||||
const { type } = this.props;
|
const { type } = this.props;
|
||||||
if (type === 'discussion-created') {
|
if (type === 'discussion-created') {
|
||||||
|
@ -418,9 +487,11 @@ export default class Message extends PureComponent {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{this.renderUsername()}
|
{this.renderUsername()}
|
||||||
|
{this.renderRepliedThread()}
|
||||||
{this.renderContent()}
|
{this.renderContent()}
|
||||||
{this.renderAttachment()}
|
{this.renderAttachment()}
|
||||||
{this.renderUrl()}
|
{this.renderUrl()}
|
||||||
|
{this.renderThread()}
|
||||||
{this.renderReactions()}
|
{this.renderReactions()}
|
||||||
{this.renderBroadcastReply()}
|
{this.renderBroadcastReply()}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
replyBroadcast as replyBroadcastAction
|
replyBroadcast as replyBroadcastAction
|
||||||
} from '../../actions/messages';
|
} from '../../actions/messages';
|
||||||
import { vibrate } from '../../utils/vibration';
|
import { vibrate } from '../../utils/vibration';
|
||||||
|
import debounce from '../../utils/debounce';
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||||
|
@ -27,15 +28,14 @@ import { vibrate } from '../../utils/vibration';
|
||||||
export default class MessageContainer extends React.Component {
|
export default class MessageContainer extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
item: PropTypes.object.isRequired,
|
item: PropTypes.object.isRequired,
|
||||||
reactions: PropTypes.any.isRequired,
|
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
username: PropTypes.string.isRequired,
|
username: PropTypes.string.isRequired,
|
||||||
token: PropTypes.string.isRequired
|
token: PropTypes.string.isRequired
|
||||||
}),
|
}),
|
||||||
customTimeFormat: PropTypes.string,
|
customTimeFormat: PropTypes.string,
|
||||||
|
customThreadTimeFormat: PropTypes.string,
|
||||||
style: ViewPropTypes.style,
|
style: ViewPropTypes.style,
|
||||||
status: PropTypes.number,
|
|
||||||
archived: PropTypes.bool,
|
archived: PropTypes.bool,
|
||||||
broadcast: PropTypes.bool,
|
broadcast: PropTypes.bool,
|
||||||
previousItem: PropTypes.object,
|
previousItem: PropTypes.object,
|
||||||
|
@ -47,6 +47,8 @@ export default class MessageContainer extends React.Component {
|
||||||
Message_TimeFormat: PropTypes.string,
|
Message_TimeFormat: PropTypes.string,
|
||||||
editingMessage: PropTypes.object,
|
editingMessage: PropTypes.object,
|
||||||
useRealName: PropTypes.bool,
|
useRealName: PropTypes.bool,
|
||||||
|
status: PropTypes.number,
|
||||||
|
navigation: PropTypes.object,
|
||||||
// methods - props
|
// methods - props
|
||||||
onLongPress: PropTypes.func,
|
onLongPress: PropTypes.func,
|
||||||
onReactionPress: PropTypes.func,
|
onReactionPress: PropTypes.func,
|
||||||
|
@ -54,7 +56,8 @@ export default class MessageContainer extends React.Component {
|
||||||
// methods - redux
|
// methods - redux
|
||||||
errorActionsShow: PropTypes.func,
|
errorActionsShow: PropTypes.func,
|
||||||
replyBroadcast: PropTypes.func,
|
replyBroadcast: PropTypes.func,
|
||||||
toggleReactionPicker: PropTypes.func
|
toggleReactionPicker: PropTypes.func,
|
||||||
|
fetchThreadName: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -73,7 +76,7 @@ export default class MessageContainer extends React.Component {
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
const { reactionsModal } = this.state;
|
const { reactionsModal } = this.state;
|
||||||
const {
|
const {
|
||||||
status, reactions, broadcast, _updatedAt, editingMessage, item
|
status, editingMessage, item, _updatedAt
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (reactionsModal !== nextState.reactionsModal) {
|
if (reactionsModal !== nextState.reactionsModal) {
|
||||||
|
@ -82,16 +85,10 @@ export default class MessageContainer extends React.Component {
|
||||||
if (status !== nextProps.status) {
|
if (status !== nextProps.status) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line
|
if (item.tmsg !== nextProps.item.tmsg) {
|
||||||
if (!!_updatedAt ^ !!nextProps._updatedAt) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!equal(reactions, nextProps.reactions)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (broadcast !== nextProps.broadcast) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!equal(editingMessage, nextProps.editingMessage)) {
|
if (!equal(editingMessage, nextProps.editingMessage)) {
|
||||||
if (nextProps.editingMessage && nextProps.editingMessage._id === item._id) {
|
if (nextProps.editingMessage && nextProps.editingMessage._id === item._id) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -99,7 +96,7 @@ export default class MessageContainer extends React.Component {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _updatedAt.toGMTString() !== nextProps._updatedAt.toGMTString();
|
return _updatedAt.toISOString() !== nextProps._updatedAt.toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
onLongPress = () => {
|
onLongPress = () => {
|
||||||
|
@ -127,6 +124,20 @@ export default class MessageContainer extends React.Component {
|
||||||
onDiscussionPress(item);
|
onDiscussionPress(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onThreadPress = debounce(() => {
|
||||||
|
const { navigation, item } = this.props;
|
||||||
|
if (item.tmid) {
|
||||||
|
navigation.push('RoomView', {
|
||||||
|
rid: item.rid, tmid: item.tmid, name: item.tmsg, t: 'thread'
|
||||||
|
});
|
||||||
|
} else if (item.tlm) {
|
||||||
|
const title = item.msg || (item.attachments && item.attachments.length && item.attachments[0].title);
|
||||||
|
navigation.push('RoomView', {
|
||||||
|
rid: item.rid, tmid: item._id, name: title, t: 'thread'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 1000, true)
|
||||||
|
|
||||||
get timeFormat() {
|
get timeFormat() {
|
||||||
const { customTimeFormat, Message_TimeFormat } = this.props;
|
const { customTimeFormat, Message_TimeFormat } = this.props;
|
||||||
return customTimeFormat || Message_TimeFormat;
|
return customTimeFormat || Message_TimeFormat;
|
||||||
|
@ -145,6 +156,7 @@ export default class MessageContainer extends React.Component {
|
||||||
&& (previousItem.u.username === item.u.username)
|
&& (previousItem.u.username === item.u.username)
|
||||||
&& !(previousItem.groupable === false || item.groupable === false || broadcast === true)
|
&& !(previousItem.groupable === false || item.groupable === false || broadcast === true)
|
||||||
&& (item.ts - previousItem.ts < Message_GroupingPeriod * 1000)
|
&& (item.ts - previousItem.ts < Message_GroupingPeriod * 1000)
|
||||||
|
&& (previousItem.tmid === item.tmid)
|
||||||
)) {
|
)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -169,14 +181,15 @@ export default class MessageContainer extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { reactionsModal } = this.state;
|
const { reactionsModal } = this.state;
|
||||||
const {
|
const {
|
||||||
item, editingMessage, user, style, archived, baseUrl, customEmojis, useRealName, broadcast
|
item, editingMessage, user, style, archived, baseUrl, customEmojis, useRealName, broadcast, fetchThreadName, customThreadTimeFormat
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const {
|
const {
|
||||||
msg, ts, attachments, urls, reactions, t, status, avatar, u, alias, editedBy, role, drid, dcount, dlm
|
_id, msg, ts, attachments, urls, reactions, t, status, avatar, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg
|
||||||
} = item;
|
} = item;
|
||||||
const isEditing = editingMessage._id === item._id;
|
const isEditing = editingMessage._id === item._id;
|
||||||
return (
|
return (
|
||||||
<Message
|
<Message
|
||||||
|
id={_id}
|
||||||
msg={msg}
|
msg={msg}
|
||||||
author={u}
|
author={u}
|
||||||
ts={ts}
|
ts={ts}
|
||||||
|
@ -192,6 +205,7 @@ export default class MessageContainer extends React.Component {
|
||||||
user={user}
|
user={user}
|
||||||
edited={editedBy && !!editedBy.username}
|
edited={editedBy && !!editedBy.username}
|
||||||
timeFormat={this.timeFormat}
|
timeFormat={this.timeFormat}
|
||||||
|
customThreadTimeFormat={customThreadTimeFormat}
|
||||||
style={style}
|
style={style}
|
||||||
archived={archived}
|
archived={archived}
|
||||||
broadcast={broadcast}
|
broadcast={broadcast}
|
||||||
|
@ -203,6 +217,11 @@ export default class MessageContainer extends React.Component {
|
||||||
drid={drid}
|
drid={drid}
|
||||||
dcount={dcount}
|
dcount={dcount}
|
||||||
dlm={dlm}
|
dlm={dlm}
|
||||||
|
tmid={tmid}
|
||||||
|
tcount={tcount}
|
||||||
|
tlm={tlm}
|
||||||
|
tmsg={tmsg}
|
||||||
|
fetchThreadName={fetchThreadName}
|
||||||
closeReactions={this.closeReactions}
|
closeReactions={this.closeReactions}
|
||||||
onErrorPress={this.onErrorPress}
|
onErrorPress={this.onErrorPress}
|
||||||
onLongPress={this.onLongPress}
|
onLongPress={this.onLongPress}
|
||||||
|
@ -211,6 +230,7 @@ export default class MessageContainer extends React.Component {
|
||||||
replyBroadcast={this.replyBroadcast}
|
replyBroadcast={this.replyBroadcast}
|
||||||
toggleReactionPicker={this.toggleReactionPicker}
|
toggleReactionPicker={this.toggleReactionPicker}
|
||||||
onDiscussionPress={this.onDiscussionPress}
|
onDiscussionPress={this.onDiscussionPress}
|
||||||
|
onThreadPress={this.onThreadPress}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ export default StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
backgroundColor: COLOR_PRIMARY,
|
backgroundColor: COLOR_PRIMARY,
|
||||||
borderRadius: 4
|
borderRadius: 2
|
||||||
},
|
},
|
||||||
smallButton: {
|
smallButton: {
|
||||||
height: 30
|
height: 30
|
||||||
|
@ -200,6 +200,13 @@ export default StyleSheet.create({
|
||||||
color: COLOR_PRIMARY,
|
color: COLOR_PRIMARY,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular
|
||||||
},
|
},
|
||||||
|
startedDiscussion: {
|
||||||
|
fontStyle: 'italic',
|
||||||
|
fontSize: 16,
|
||||||
|
marginBottom: 6,
|
||||||
|
...sharedStyles.textColorDescription,
|
||||||
|
...sharedStyles.textRegular
|
||||||
|
},
|
||||||
time: {
|
time: {
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
paddingLeft: 10,
|
paddingLeft: 10,
|
||||||
|
@ -207,5 +214,16 @@ export default StyleSheet.create({
|
||||||
...sharedStyles.textColorDescription,
|
...sharedStyles.textColorDescription,
|
||||||
...sharedStyles.textRegular,
|
...sharedStyles.textRegular,
|
||||||
fontWeight: '300'
|
fontWeight: '300'
|
||||||
|
},
|
||||||
|
repliedThread: {
|
||||||
|
fontSize: 16,
|
||||||
|
marginBottom: 6,
|
||||||
|
...sharedStyles.textColorDescription,
|
||||||
|
...sharedStyles.textRegular
|
||||||
|
},
|
||||||
|
repliedThreadName: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: COLOR_PRIMARY,
|
||||||
|
...sharedStyles.textSemibold
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -124,6 +124,7 @@ export default {
|
||||||
Connect: 'Connect',
|
Connect: 'Connect',
|
||||||
Connect_to_a_server: 'Connect to a server',
|
Connect_to_a_server: 'Connect to a server',
|
||||||
Connected: 'Connected',
|
Connected: 'Connected',
|
||||||
|
connecting_server: 'connecting to server',
|
||||||
Connecting: 'Connecting...',
|
Connecting: 'Connecting...',
|
||||||
Continue_with: 'Continue with',
|
Continue_with: 'Continue with',
|
||||||
Copied_to_clipboard: 'Copied to clipboard!',
|
Copied_to_clipboard: 'Copied to clipboard!',
|
||||||
|
@ -198,6 +199,8 @@ export default {
|
||||||
Message_actions: 'Message actions',
|
Message_actions: 'Message actions',
|
||||||
Message_pinned: 'Message pinned',
|
Message_pinned: 'Message pinned',
|
||||||
Message_removed: 'Message removed',
|
Message_removed: 'Message removed',
|
||||||
|
message: 'message',
|
||||||
|
messages: 'messages',
|
||||||
Messages: 'Messages',
|
Messages: 'Messages',
|
||||||
Microphone_Permission_Message: 'Rocket Chat needs access to your microphone so you can send audio message.',
|
Microphone_Permission_Message: 'Rocket Chat needs access to your microphone so you can send audio message.',
|
||||||
Microphone_Permission: 'Microphone Permission',
|
Microphone_Permission: 'Microphone Permission',
|
||||||
|
@ -217,10 +220,12 @@ export default {
|
||||||
No_pinned_messages: 'No pinned messages',
|
No_pinned_messages: 'No pinned messages',
|
||||||
No_results_found: 'No results found',
|
No_results_found: 'No results found',
|
||||||
No_starred_messages: 'No starred messages',
|
No_starred_messages: 'No starred messages',
|
||||||
|
No_thread_messages: 'No thread messages',
|
||||||
No_announcement_provided: 'No announcement provided.',
|
No_announcement_provided: 'No announcement provided.',
|
||||||
No_description_provided: 'No description provided.',
|
No_description_provided: 'No description provided.',
|
||||||
No_topic_provided: 'No topic provided.',
|
No_topic_provided: 'No topic provided.',
|
||||||
No_Message: 'No Message',
|
No_Message: 'No Message',
|
||||||
|
No_messages_yet: 'No messages yet',
|
||||||
No_Reactions: 'No Reactions',
|
No_Reactions: 'No Reactions',
|
||||||
Not_logged: 'Not logged',
|
Not_logged: 'Not logged',
|
||||||
Nothing_to_save: 'Nothing to save!',
|
Nothing_to_save: 'Nothing to save!',
|
||||||
|
@ -256,6 +261,9 @@ export default {
|
||||||
Read_Only: 'Read Only',
|
Read_Only: 'Read Only',
|
||||||
Register: 'Register',
|
Register: 'Register',
|
||||||
Repeat_Password: 'Repeat Password',
|
Repeat_Password: 'Repeat Password',
|
||||||
|
Replied_on: 'Replied on:',
|
||||||
|
replies: 'replies',
|
||||||
|
reply: 'reply',
|
||||||
Reply: 'Reply',
|
Reply: 'Reply',
|
||||||
Resend: 'Resend',
|
Resend: 'Resend',
|
||||||
Reset_password: 'Reset password',
|
Reset_password: 'Reset password',
|
||||||
|
@ -311,7 +319,8 @@ export default {
|
||||||
There_was_an_error_while_action: 'There was an error while {{action}}!',
|
There_was_an_error_while_action: 'There was an error while {{action}}!',
|
||||||
This_room_is_blocked: 'This room is blocked',
|
This_room_is_blocked: 'This room is blocked',
|
||||||
This_room_is_read_only: 'This room is read only',
|
This_room_is_read_only: 'This room is read only',
|
||||||
Thread_created: 'Started a new thread: "{{name}}"',
|
Thread: 'Thread',
|
||||||
|
Threads: 'Threads',
|
||||||
Timezone: 'Timezone',
|
Timezone: 'Timezone',
|
||||||
Toggle_Drawer: 'Toggle_Drawer',
|
Toggle_Drawer: 'Toggle_Drawer',
|
||||||
topic: 'topic',
|
topic: 'topic',
|
||||||
|
|
|
@ -131,6 +131,7 @@ export default {
|
||||||
Connect: 'Conectar',
|
Connect: 'Conectar',
|
||||||
Connect_to_a_server: 'Conectar a um servidor',
|
Connect_to_a_server: 'Conectar a um servidor',
|
||||||
Connected: 'Conectado',
|
Connected: 'Conectado',
|
||||||
|
connecting_server: 'conectando no servidor',
|
||||||
Connecting: 'Conectando...',
|
Connecting: 'Conectando...',
|
||||||
Continue_with: 'Entrar com',
|
Continue_with: 'Entrar com',
|
||||||
Copied_to_clipboard: 'Copiado para a área de transferência!',
|
Copied_to_clipboard: 'Copiado para a área de transferência!',
|
||||||
|
@ -202,6 +203,8 @@ export default {
|
||||||
Message_actions: 'Ações',
|
Message_actions: 'Ações',
|
||||||
Message_pinned: 'Fixou uma mensagem',
|
Message_pinned: 'Fixou uma mensagem',
|
||||||
Message_removed: 'Mensagem removida',
|
Message_removed: 'Mensagem removida',
|
||||||
|
message: 'mensagem',
|
||||||
|
messages: 'mensagens',
|
||||||
Messages: 'Mensagens',
|
Messages: 'Mensagens',
|
||||||
Microphone_Permission_Message: 'Rocket Chat precisa de acesso ao seu microfone para enviar mensagens de áudio.',
|
Microphone_Permission_Message: 'Rocket Chat precisa de acesso ao seu microfone para enviar mensagens de áudio.',
|
||||||
Microphone_Permission: 'Acesso ao Microfone',
|
Microphone_Permission: 'Acesso ao Microfone',
|
||||||
|
@ -220,10 +223,12 @@ export default {
|
||||||
No_pinned_messages: 'Não há mensagens fixadas',
|
No_pinned_messages: 'Não há mensagens fixadas',
|
||||||
No_results_found: 'Nenhum resultado encontrado',
|
No_results_found: 'Nenhum resultado encontrado',
|
||||||
No_starred_messages: 'Não há mensagens favoritas',
|
No_starred_messages: 'Não há mensagens favoritas',
|
||||||
|
No_thread_messages: 'Não há tópicos',
|
||||||
No_announcement_provided: 'Sem anúncio.',
|
No_announcement_provided: 'Sem anúncio.',
|
||||||
No_description_provided: 'Sem descrição.',
|
No_description_provided: 'Sem descrição.',
|
||||||
No_topic_provided: 'Sem tópico.',
|
No_topic_provided: 'Sem tópico.',
|
||||||
No_Message: 'Não há mensagens',
|
No_Message: 'Não há mensagens',
|
||||||
|
No_messages_yet: 'Não há mensagens ainda',
|
||||||
No_Reactions: 'Sem reações',
|
No_Reactions: 'Sem reações',
|
||||||
Nothing_to_save: 'Nada para salvar!',
|
Nothing_to_save: 'Nada para salvar!',
|
||||||
Notify_active_in_this_room: 'Notificar usuários ativos nesta sala',
|
Notify_active_in_this_room: 'Notificar usuários ativos nesta sala',
|
||||||
|
@ -258,6 +263,9 @@ export default {
|
||||||
Read_Only: 'Somente Leitura',
|
Read_Only: 'Somente Leitura',
|
||||||
Register: 'Registrar',
|
Register: 'Registrar',
|
||||||
Repeat_Password: 'Repetir Senha',
|
Repeat_Password: 'Repetir Senha',
|
||||||
|
Replied_on: 'Respondido em:',
|
||||||
|
replies: 'respostas',
|
||||||
|
reply: 'resposta',
|
||||||
Reply: 'Responder',
|
Reply: 'Responder',
|
||||||
Resend: 'Reenviar',
|
Resend: 'Reenviar',
|
||||||
Reset_password: 'Resetar senha',
|
Reset_password: 'Resetar senha',
|
||||||
|
@ -310,7 +318,8 @@ export default {
|
||||||
There_was_an_error_while_action: 'Aconteceu um erro {{action}}!',
|
There_was_an_error_while_action: 'Aconteceu um erro {{action}}!',
|
||||||
This_room_is_blocked: 'Este quarto está bloqueado',
|
This_room_is_blocked: 'Este quarto está bloqueado',
|
||||||
This_room_is_read_only: 'Este quarto é apenas de leitura',
|
This_room_is_read_only: 'Este quarto é apenas de leitura',
|
||||||
Thread_created: 'Iniciou uma thread: "{{name}}"',
|
Thread: 'Tópico',
|
||||||
|
Threads: 'Tópicos',
|
||||||
Timezone: 'Fuso horário',
|
Timezone: 'Fuso horário',
|
||||||
topic: 'tópico',
|
topic: 'tópico',
|
||||||
Topic: 'Tópico',
|
Topic: 'Tópico',
|
||||||
|
|
|
@ -29,6 +29,7 @@ import MentionedMessagesView from './views/MentionedMessagesView';
|
||||||
import StarredMessagesView from './views/StarredMessagesView';
|
import StarredMessagesView from './views/StarredMessagesView';
|
||||||
import SearchMessagesView from './views/SearchMessagesView';
|
import SearchMessagesView from './views/SearchMessagesView';
|
||||||
import PinnedMessagesView from './views/PinnedMessagesView';
|
import PinnedMessagesView from './views/PinnedMessagesView';
|
||||||
|
import ThreadMessagesView from './views/ThreadMessagesView';
|
||||||
import SelectedUsersView from './views/SelectedUsersView';
|
import SelectedUsersView from './views/SelectedUsersView';
|
||||||
import CreateChannelView from './views/CreateChannelView';
|
import CreateChannelView from './views/CreateChannelView';
|
||||||
import LegalView from './views/LegalView';
|
import LegalView from './views/LegalView';
|
||||||
|
@ -122,7 +123,8 @@ const ChatsStack = createStackNavigator({
|
||||||
StarredMessagesView,
|
StarredMessagesView,
|
||||||
SearchMessagesView,
|
SearchMessagesView,
|
||||||
PinnedMessagesView,
|
PinnedMessagesView,
|
||||||
SelectedUsersView
|
SelectedUsersView,
|
||||||
|
ThreadMessagesView
|
||||||
}, {
|
}, {
|
||||||
defaultNavigationOptions: defaultHeader
|
defaultNavigationOptions: defaultHeader
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,8 +39,13 @@ export default function loadMessagesForRoom(...args) {
|
||||||
if (data && data.length) {
|
if (data && data.length) {
|
||||||
InteractionManager.runAfterInteractions(() => {
|
InteractionManager.runAfterInteractions(() => {
|
||||||
database.write(() => data.forEach((message) => {
|
database.write(() => data.forEach((message) => {
|
||||||
|
message = buildMessage(message);
|
||||||
try {
|
try {
|
||||||
database.create('messages', buildMessage(message), true);
|
database.create('messages', message, true);
|
||||||
|
// if it's a thread "header"
|
||||||
|
if (message.tlm) {
|
||||||
|
database.create('threads', message, true);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('loadMessagesForRoom -> create messages', e);
|
log('loadMessagesForRoom -> create messages', e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,11 +31,15 @@ export default function loadMissedMessages(...args) {
|
||||||
if (data) {
|
if (data) {
|
||||||
if (data.updated && data.updated.length) {
|
if (data.updated && data.updated.length) {
|
||||||
const { updated } = data;
|
const { updated } = data;
|
||||||
updated.forEach(buildMessage);
|
|
||||||
InteractionManager.runAfterInteractions(() => {
|
InteractionManager.runAfterInteractions(() => {
|
||||||
database.write(() => updated.forEach((message) => {
|
database.write(() => updated.forEach((message) => {
|
||||||
try {
|
try {
|
||||||
|
message = buildMessage(message);
|
||||||
database.create('messages', message, true);
|
database.create('messages', message, true);
|
||||||
|
// if it's a thread "header"
|
||||||
|
if (message.tlm) {
|
||||||
|
database.create('threads', message, true);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('loadMissedMessages -> create messages', e);
|
log('loadMissedMessages -> create messages', e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { InteractionManager } from 'react-native';
|
||||||
|
import EJSON from 'ejson';
|
||||||
|
|
||||||
|
import buildMessage from './helpers/buildMessage';
|
||||||
|
import database from '../realm';
|
||||||
|
import log from '../../utils/log';
|
||||||
|
|
||||||
|
async function load({ tmid, skip }) {
|
||||||
|
try {
|
||||||
|
// RC 1.0
|
||||||
|
const data = await this.sdk.methodCall('getThreadMessages', { tmid, limit: 50, skip });
|
||||||
|
if (!data || data.status === 'error') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function loadThreadMessages({ tmid, skip }) {
|
||||||
|
return new Promise(async(resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await load.call(this, { tmid, skip });
|
||||||
|
|
||||||
|
if (data && data.length) {
|
||||||
|
InteractionManager.runAfterInteractions(() => {
|
||||||
|
database.write(() => data.forEach((m) => {
|
||||||
|
try {
|
||||||
|
const message = buildMessage(EJSON.fromJSONValue(m));
|
||||||
|
message.rid = tmid;
|
||||||
|
database.create('threadMessages', message, true);
|
||||||
|
} catch (e) {
|
||||||
|
log('loadThreadMessages -> create messages', e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return resolve(data);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return resolve([]);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log('loadThreadMessages', e);
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -5,12 +5,13 @@ import reduxStore from '../createStore';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import random from '../../utils/random';
|
import random from '../../utils/random';
|
||||||
|
|
||||||
export const getMessage = (rid, msg = {}) => {
|
export const getMessage = (rid, msg = '', tmid) => {
|
||||||
const _id = random(17);
|
const _id = random(17);
|
||||||
const message = {
|
const message = {
|
||||||
_id,
|
_id,
|
||||||
rid,
|
rid,
|
||||||
msg,
|
msg,
|
||||||
|
tmid,
|
||||||
ts: new Date(),
|
ts: new Date(),
|
||||||
_updatedAt: new Date(),
|
_updatedAt: new Date(),
|
||||||
status: messagesStatus.TEMP,
|
status: messagesStatus.TEMP,
|
||||||
|
@ -30,20 +31,28 @@ export const getMessage = (rid, msg = {}) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function sendMessageCall(message) {
|
export async function sendMessageCall(message) {
|
||||||
const { _id, rid, msg } = message;
|
const {
|
||||||
|
_id, rid, msg, tmid
|
||||||
|
} = message;
|
||||||
// RC 0.60.0
|
// RC 0.60.0
|
||||||
const data = await this.sdk.post('chat.sendMessage', { message: { _id, rid, msg } });
|
const data = await this.sdk.post('chat.sendMessage', {
|
||||||
|
message: {
|
||||||
|
_id, rid, msg, tmid
|
||||||
|
}
|
||||||
|
});
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function(rid, msg) {
|
export default async function(rid, msg, tmid) {
|
||||||
try {
|
try {
|
||||||
const message = getMessage(rid, msg);
|
const message = getMessage(rid, msg, tmid);
|
||||||
const [room] = database.objects('subscriptions').filtered('rid == $0', rid);
|
const [room] = database.objects('subscriptions').filtered('rid == $0', rid);
|
||||||
|
|
||||||
database.write(() => {
|
if (room) {
|
||||||
room.draftMessage = null;
|
database.write(() => {
|
||||||
});
|
room.draftMessage = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const ret = await sendMessageCall.call(this, message);
|
const ret = await sendMessageCall.call(this, message);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import log from '../../../utils/log';
|
||||||
import protectedFunction from '../helpers/protectedFunction';
|
import protectedFunction from '../helpers/protectedFunction';
|
||||||
import buildMessage from '../helpers/buildMessage';
|
import buildMessage from '../helpers/buildMessage';
|
||||||
import database from '../../realm';
|
import database from '../../realm';
|
||||||
|
import debounce from '../../../utils/debounce';
|
||||||
|
|
||||||
const unsubscribe = subscriptions => subscriptions.forEach(sub => sub.unsubscribe().catch(() => console.log('unsubscribeRoom')));
|
const unsubscribe = subscriptions => subscriptions.forEach(sub => sub.unsubscribe().catch(() => console.log('unsubscribeRoom')));
|
||||||
const removeListener = listener => listener.stop();
|
const removeListener = listener => listener.stop();
|
||||||
|
@ -107,27 +108,47 @@ export default function subscribeRoom({ rid }) {
|
||||||
const { _id } = ddpMessage.fields.args[0];
|
const { _id } = ddpMessage.fields.args[0];
|
||||||
const message = database.objects('messages').filtered('_id = $0', _id);
|
const message = database.objects('messages').filtered('_id = $0', _id);
|
||||||
database.delete(message);
|
database.delete(message);
|
||||||
|
const thread = database.objects('threads').filtered('_id = $0', _id);
|
||||||
|
database.delete(thread);
|
||||||
|
const threadMessage = database.objects('threadMessages').filtered('_id = $0', _id);
|
||||||
|
database.delete(threadMessage);
|
||||||
|
const cleanTmids = database.objects('messages').filtered('tmid = $0', _id).snapshot();
|
||||||
|
if (cleanTmids && cleanTmids.length) {
|
||||||
|
cleanTmids.forEach((m) => {
|
||||||
|
m.tmid = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const read = debounce(() => {
|
||||||
|
const [room] = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||||
|
if (room._id) {
|
||||||
|
this.readMessages(rid);
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
|
||||||
const handleMessageReceived = protectedFunction((ddpMessage) => {
|
const handleMessageReceived = protectedFunction((ddpMessage) => {
|
||||||
const message = buildMessage(ddpMessage.fields.args[0]);
|
const message = buildMessage(EJSON.fromJSONValue(ddpMessage.fields.args[0]));
|
||||||
if (rid !== message.rid) {
|
if (rid !== message.rid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
try {
|
try {
|
||||||
database.write(() => {
|
database.write(() => {
|
||||||
database.create('messages', EJSON.fromJSONValue(message), true);
|
database.create('messages', message, true);
|
||||||
|
// if it's a thread "header"
|
||||||
|
if (message.tlm) {
|
||||||
|
database.create('threads', message, true);
|
||||||
|
} else if (message.tmid) {
|
||||||
|
message.rid = message.tmid;
|
||||||
|
database.create('threadMessages', message, true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const [room] = database.objects('subscriptions').filtered('rid = $0', rid);
|
read();
|
||||||
|
|
||||||
if (room._id) {
|
|
||||||
this.readMessages(rid);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('handleMessageReceived', e);
|
console.warn('handleMessageReceived', e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ const serversSchema = {
|
||||||
id: 'string',
|
id: 'string',
|
||||||
name: { type: 'string', optional: true },
|
name: { type: 'string', optional: true },
|
||||||
iconURL: { type: 'string', optional: true },
|
iconURL: { type: 'string', optional: true },
|
||||||
roomsUpdatedAt: { type: 'date', optional: true }
|
roomsUpdatedAt: { type: 'date', optional: true },
|
||||||
|
version: 'string?'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -206,8 +207,6 @@ const messagesSchema = {
|
||||||
rid: { type: 'string', indexed: true },
|
rid: { type: 'string', indexed: true },
|
||||||
ts: 'date',
|
ts: 'date',
|
||||||
u: 'users',
|
u: 'users',
|
||||||
// mentions: [],
|
|
||||||
// channels: [],
|
|
||||||
alias: { type: 'string', optional: true },
|
alias: { type: 'string', optional: true },
|
||||||
parseUrls: { type: 'bool', optional: true },
|
parseUrls: { type: 'bool', optional: true },
|
||||||
groupable: { type: 'bool', optional: true },
|
groupable: { type: 'bool', optional: true },
|
||||||
|
@ -223,7 +222,70 @@ const messagesSchema = {
|
||||||
role: { type: 'string', optional: true },
|
role: { type: 'string', optional: true },
|
||||||
drid: { type: 'string', optional: true },
|
drid: { type: 'string', optional: true },
|
||||||
dcount: { type: 'int', optional: true },
|
dcount: { type: 'int', optional: true },
|
||||||
dlm: { type: 'date', optional: true }
|
dlm: { type: 'date', optional: true },
|
||||||
|
tmid: { type: 'string', optional: true },
|
||||||
|
tcount: { type: 'int', optional: true },
|
||||||
|
tlm: { type: 'date', optional: true },
|
||||||
|
replies: 'string[]'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const threadsSchema = {
|
||||||
|
name: 'threads',
|
||||||
|
primaryKey: '_id',
|
||||||
|
properties: {
|
||||||
|
_id: 'string',
|
||||||
|
msg: { type: 'string', optional: true },
|
||||||
|
t: { type: 'string', optional: true },
|
||||||
|
rid: { type: 'string', indexed: true },
|
||||||
|
ts: 'date',
|
||||||
|
u: 'users',
|
||||||
|
alias: { type: 'string', optional: true },
|
||||||
|
parseUrls: { type: 'bool', optional: true },
|
||||||
|
groupable: { type: 'bool', optional: true },
|
||||||
|
avatar: { type: 'string', optional: true },
|
||||||
|
attachments: { type: 'list', objectType: 'attachment' },
|
||||||
|
urls: { type: 'list', objectType: 'url', default: [] },
|
||||||
|
_updatedAt: { type: 'date', optional: true },
|
||||||
|
status: { type: 'int', optional: true },
|
||||||
|
pinned: { type: 'bool', optional: true },
|
||||||
|
starred: { type: 'bool', optional: true },
|
||||||
|
editedBy: 'messagesEditedBy',
|
||||||
|
reactions: { type: 'list', objectType: 'messagesReactions' },
|
||||||
|
role: { type: 'string', optional: true },
|
||||||
|
drid: { type: 'string', optional: true },
|
||||||
|
dcount: { type: 'int', optional: true },
|
||||||
|
dlm: { type: 'date', optional: true },
|
||||||
|
tmid: { type: 'string', optional: true },
|
||||||
|
tcount: { type: 'int', optional: true },
|
||||||
|
tlm: { type: 'date', optional: true },
|
||||||
|
replies: 'string[]'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const threadMessagesSchema = {
|
||||||
|
name: 'threadMessages',
|
||||||
|
primaryKey: '_id',
|
||||||
|
properties: {
|
||||||
|
_id: 'string',
|
||||||
|
msg: { type: 'string', optional: true },
|
||||||
|
t: { type: 'string', optional: true },
|
||||||
|
rid: { type: 'string', indexed: true },
|
||||||
|
ts: 'date',
|
||||||
|
u: 'users',
|
||||||
|
alias: { type: 'string', optional: true },
|
||||||
|
parseUrls: { type: 'bool', optional: true },
|
||||||
|
groupable: { type: 'bool', optional: true },
|
||||||
|
avatar: { type: 'string', optional: true },
|
||||||
|
attachments: { type: 'list', objectType: 'attachment' },
|
||||||
|
urls: { type: 'list', objectType: 'url', default: [] },
|
||||||
|
_updatedAt: { type: 'date', optional: true },
|
||||||
|
status: { type: 'int', optional: true },
|
||||||
|
pinned: { type: 'bool', optional: true },
|
||||||
|
starred: { type: 'bool', optional: true },
|
||||||
|
editedBy: 'messagesEditedBy',
|
||||||
|
reactions: { type: 'list', objectType: 'messagesReactions' },
|
||||||
|
role: { type: 'string', optional: true }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -296,6 +358,8 @@ const schema = [
|
||||||
subscriptionSchema,
|
subscriptionSchema,
|
||||||
subscriptionRolesSchema,
|
subscriptionRolesSchema,
|
||||||
messagesSchema,
|
messagesSchema,
|
||||||
|
threadsSchema,
|
||||||
|
threadMessagesSchema,
|
||||||
usersSchema,
|
usersSchema,
|
||||||
roomsSchema,
|
roomsSchema,
|
||||||
attachment,
|
attachment,
|
||||||
|
@ -323,9 +387,9 @@ class DB {
|
||||||
schema: [
|
schema: [
|
||||||
serversSchema
|
serversSchema
|
||||||
],
|
],
|
||||||
schemaVersion: 2,
|
schemaVersion: 4,
|
||||||
migration: (oldRealm, newRealm) => {
|
migration: (oldRealm, newRealm) => {
|
||||||
if (oldRealm.schemaVersion === 1 && newRealm.schemaVersion === 2) {
|
if (oldRealm.schemaVersion >= 1 && newRealm.schemaVersion <= 3) {
|
||||||
const newServers = newRealm.objects('servers');
|
const newServers = newRealm.objects('servers');
|
||||||
|
|
||||||
// eslint-disable-next-line no-plusplus
|
// eslint-disable-next-line no-plusplus
|
||||||
|
@ -363,6 +427,10 @@ class DB {
|
||||||
return this.database.objects(...args);
|
return this.database.objects(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
objectForPrimaryKey(...args) {
|
||||||
|
return this.database.objectForPrimaryKey(...args);
|
||||||
|
}
|
||||||
|
|
||||||
get database() {
|
get database() {
|
||||||
return this.databases.activeDB;
|
return this.databases.activeDB;
|
||||||
}
|
}
|
||||||
|
@ -376,9 +444,9 @@ class DB {
|
||||||
return this.databases.activeDB = new Realm({
|
return this.databases.activeDB = new Realm({
|
||||||
path: `${ path }.realm`,
|
path: `${ path }.realm`,
|
||||||
schema,
|
schema,
|
||||||
schemaVersion: 4,
|
schemaVersion: 6,
|
||||||
migration: (oldRealm, newRealm) => {
|
migration: (oldRealm, newRealm) => {
|
||||||
if (oldRealm.schemaVersion === 3 && newRealm.schemaVersion === 4) {
|
if (oldRealm.schemaVersion >= 3 && newRealm.schemaVersion <= 6) {
|
||||||
const newSubs = newRealm.objects('subscriptions');
|
const newSubs = newRealm.objects('subscriptions');
|
||||||
|
|
||||||
// eslint-disable-next-line no-plusplus
|
// eslint-disable-next-line no-plusplus
|
||||||
|
|
|
@ -9,6 +9,7 @@ import messagesStatus from '../constants/messagesStatus';
|
||||||
import database, { safeAddListener } 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';
|
||||||
|
import EventEmitter from '../utils/events';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
setUser, setLoginServices, loginRequest, loginFailure, logout
|
setUser, setLoginServices, loginRequest, loginFailure, logout
|
||||||
|
@ -31,6 +32,7 @@ import canOpenRoom from './methods/canOpenRoom';
|
||||||
|
|
||||||
import loadMessagesForRoom from './methods/loadMessagesForRoom';
|
import loadMessagesForRoom from './methods/loadMessagesForRoom';
|
||||||
import loadMissedMessages from './methods/loadMissedMessages';
|
import loadMissedMessages from './methods/loadMissedMessages';
|
||||||
|
import loadThreadMessages from './methods/loadThreadMessages';
|
||||||
|
|
||||||
import sendMessage, { getMessage, sendMessageCall } from './methods/sendMessage';
|
import sendMessage, { getMessage, sendMessageCall } from './methods/sendMessage';
|
||||||
import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
|
import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
|
||||||
|
@ -78,26 +80,24 @@ const RocketChat = {
|
||||||
console.warn(`AsyncStorage error: ${ error.message }`);
|
console.warn(`AsyncStorage error: ${ error.message }`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async testServer(server) {
|
async getServerInfo(server) {
|
||||||
try {
|
try {
|
||||||
const result = await fetch(`${ server }/api/v1/info`).then(response => response.json());
|
const result = await fetch(`${ server }/api/info`).then(response => response.json());
|
||||||
if (result.success && result.info) {
|
if (result.success) {
|
||||||
if (semver.lt(result.info.version, MIN_ROCKETCHAT_VERSION)) {
|
if (semver.lt(result.version, MIN_ROCKETCHAT_VERSION)) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Invalid_server_version',
|
message: 'Invalid_server_version',
|
||||||
messageOptions: {
|
messageOptions: {
|
||||||
currentVersion: result.info.version,
|
currentVersion: result.version,
|
||||||
minVersion: MIN_ROCKETCHAT_VERSION
|
minVersion: MIN_ROCKETCHAT_VERSION
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return result;
|
||||||
success: true
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('testServer', e);
|
log('getServerInfo', e);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
|
@ -135,6 +135,7 @@ const RocketChat = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async loginSuccess({ user }) {
|
async loginSuccess({ user }) {
|
||||||
|
EventEmitter.emit('connected');
|
||||||
reduxStore.dispatch(setUser(user));
|
reduxStore.dispatch(setUser(user));
|
||||||
reduxStore.dispatch(roomsRequest());
|
reduxStore.dispatch(roomsRequest());
|
||||||
|
|
||||||
|
@ -370,6 +371,7 @@ const RocketChat = {
|
||||||
},
|
},
|
||||||
loadMissedMessages,
|
loadMissedMessages,
|
||||||
loadMessagesForRoom,
|
loadMessagesForRoom,
|
||||||
|
loadThreadMessages,
|
||||||
getMessage,
|
getMessage,
|
||||||
sendMessage,
|
sendMessage,
|
||||||
getRooms,
|
getRooms,
|
||||||
|
@ -568,9 +570,9 @@ const RocketChat = {
|
||||||
// RC 0.64.0
|
// RC 0.64.0
|
||||||
return this.sdk.post('rooms.favorite', { roomId, favorite });
|
return this.sdk.post('rooms.favorite', { roomId, favorite });
|
||||||
},
|
},
|
||||||
getRoomMembers(rid, allUsers) {
|
getRoomMembers(rid, allUsers, skip = 0, limit = 10) {
|
||||||
// RC 0.42.0
|
// RC 0.42.0
|
||||||
return this.sdk.methodCall('getUsersOfRoom', rid, allUsers);
|
return this.sdk.methodCall('getUsersOfRoom', rid, allUsers, { skip, limit });
|
||||||
},
|
},
|
||||||
getUserRoles() {
|
getUserRoles() {
|
||||||
// RC 0.27.0
|
// RC 0.27.0
|
||||||
|
@ -649,6 +651,10 @@ const RocketChat = {
|
||||||
// RC 0.51.0
|
// RC 0.51.0
|
||||||
return this.sdk.methodCall('addUsersToRoom', { rid, users });
|
return this.sdk.methodCall('addUsersToRoom', { rid, users });
|
||||||
},
|
},
|
||||||
|
getSingleMessage(msgId) {
|
||||||
|
// RC 0.57.0
|
||||||
|
return this.sdk.methodCall('getSingleMessage', msgId);
|
||||||
|
},
|
||||||
hasPermission(permissions, rid) {
|
hasPermission(permissions, rid) {
|
||||||
let roles = [];
|
let roles = [];
|
||||||
try {
|
try {
|
||||||
|
@ -768,6 +774,17 @@ const RocketChat = {
|
||||||
roomId,
|
roomId,
|
||||||
searchText
|
searchText
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
toggleFollowMessage(mid, follow) {
|
||||||
|
// RC 1.0
|
||||||
|
if (follow) {
|
||||||
|
return this.sdk.methodCall('followMessage', { mid });
|
||||||
|
}
|
||||||
|
return this.sdk.methodCall('unfollowMessage', { mid });
|
||||||
|
},
|
||||||
|
getThreadsList({ rid, limit, skip }) {
|
||||||
|
// RC 1.0
|
||||||
|
return this.sdk.methodCall('getThreadsList', { rid, limit, skip });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import * as types from '../actions/actionsTypes';
|
import * as types from '../actions/actionsTypes';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
isFetching: false,
|
|
||||||
failure: false,
|
|
||||||
message: {},
|
message: {},
|
||||||
actionMessage: {},
|
actionMessage: {},
|
||||||
replyMessage: {},
|
replyMessage: {},
|
||||||
|
@ -14,23 +12,6 @@ const initialState = {
|
||||||
|
|
||||||
export default function messages(state = initialState, action) {
|
export default function messages(state = initialState, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case types.MESSAGES.REQUEST:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
isFetching: true
|
|
||||||
};
|
|
||||||
case types.MESSAGES.SUCCESS:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
isFetching: false
|
|
||||||
};
|
|
||||||
case types.LOGIN.FAILURE:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
isFetching: false,
|
|
||||||
failure: true,
|
|
||||||
errorMessage: action.err
|
|
||||||
};
|
|
||||||
case types.MESSAGES.ACTIONS_SHOW:
|
case types.MESSAGES.ACTIONS_SHOW:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|
|
@ -5,6 +5,7 @@ const initialState = {
|
||||||
connected: false,
|
connected: false,
|
||||||
failure: false,
|
failure: false,
|
||||||
server: '',
|
server: '',
|
||||||
|
version: null,
|
||||||
loading: true,
|
loading: true,
|
||||||
adding: false
|
adding: false
|
||||||
};
|
};
|
||||||
|
@ -29,6 +30,7 @@ export default function server(state = initialState, action) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
server: action.server,
|
server: action.server,
|
||||||
|
version: action.version,
|
||||||
connecting: true,
|
connecting: true,
|
||||||
connected: false,
|
connected: false,
|
||||||
loading: true
|
loading: true
|
||||||
|
@ -37,6 +39,7 @@ export default function server(state = initialState, action) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
server: action.server,
|
server: action.server,
|
||||||
|
version: action.version,
|
||||||
connecting: false,
|
connecting: false,
|
||||||
connected: true,
|
connected: true,
|
||||||
loading: false
|
loading: false
|
||||||
|
|
|
@ -69,7 +69,7 @@ const handleOpen = function* handleOpen({ params }) {
|
||||||
yield navigate({ params });
|
yield navigate({ params });
|
||||||
} else {
|
} else {
|
||||||
// if deep link is from a different server
|
// if deep link is from a different server
|
||||||
const result = yield RocketChat.testServer(server);
|
const result = yield RocketChat.getServerInfo(server);
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { APP } from '../actions/actionsTypes';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
import Navigation from '../lib/Navigation';
|
import Navigation from '../lib/Navigation';
|
||||||
|
import database from '../lib/realm';
|
||||||
|
|
||||||
const restore = function* restore() {
|
const restore = function* restore() {
|
||||||
try {
|
try {
|
||||||
|
@ -27,7 +28,8 @@ const restore = function* restore() {
|
||||||
]);
|
]);
|
||||||
yield put(actions.appStart('outside'));
|
yield put(actions.appStart('outside'));
|
||||||
} else if (server) {
|
} else if (server) {
|
||||||
yield put(selectServerRequest(server));
|
const serverObj = database.databases.serversDB.objectForPrimaryKey('servers', server);
|
||||||
|
yield put(selectServerRequest(server, serverObj && serverObj.version));
|
||||||
}
|
}
|
||||||
|
|
||||||
yield put(actions.appReady({}));
|
yield put(actions.appReady({}));
|
||||||
|
|
|
@ -4,8 +4,6 @@ import { takeLatest, put, call } from 'redux-saga/effects';
|
||||||
import Navigation from '../lib/Navigation';
|
import Navigation from '../lib/Navigation';
|
||||||
import { MESSAGES } from '../actions/actionsTypes';
|
import { MESSAGES } from '../actions/actionsTypes';
|
||||||
import {
|
import {
|
||||||
messagesSuccess,
|
|
||||||
messagesFailure,
|
|
||||||
deleteSuccess,
|
deleteSuccess,
|
||||||
deleteFailure,
|
deleteFailure,
|
||||||
editSuccess,
|
editSuccess,
|
||||||
|
@ -25,19 +23,6 @@ const editMessage = message => RocketChat.editMessage(message);
|
||||||
const toggleStarMessage = message => RocketChat.toggleStarMessage(message);
|
const toggleStarMessage = message => RocketChat.toggleStarMessage(message);
|
||||||
const togglePinMessage = message => RocketChat.togglePinMessage(message);
|
const togglePinMessage = message => RocketChat.togglePinMessage(message);
|
||||||
|
|
||||||
const get = function* get({ room }) {
|
|
||||||
try {
|
|
||||||
if (room.lastOpen) {
|
|
||||||
yield RocketChat.loadMissedMessages(room);
|
|
||||||
} else {
|
|
||||||
yield RocketChat.loadMessagesForRoom(room);
|
|
||||||
}
|
|
||||||
yield put(messagesSuccess());
|
|
||||||
} catch (err) {
|
|
||||||
yield put(messagesFailure(err));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteRequest = function* handleDeleteRequest({ message }) {
|
const handleDeleteRequest = function* handleDeleteRequest({ message }) {
|
||||||
try {
|
try {
|
||||||
yield call(deleteMessage, message);
|
yield call(deleteMessage, message);
|
||||||
|
@ -97,7 +82,6 @@ const handleReplyBroadcast = function* handleReplyBroadcast({ message }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const root = function* root() {
|
const root = function* root() {
|
||||||
yield takeLatest(MESSAGES.REQUEST, get);
|
|
||||||
yield takeLatest(MESSAGES.DELETE_REQUEST, handleDeleteRequest);
|
yield takeLatest(MESSAGES.DELETE_REQUEST, handleDeleteRequest);
|
||||||
yield takeLatest(MESSAGES.EDIT_REQUEST, handleEditRequest);
|
yield takeLatest(MESSAGES.EDIT_REQUEST, handleEditRequest);
|
||||||
yield takeLatest(MESSAGES.TOGGLE_STAR_REQUEST, handleToggleStarRequest);
|
yield takeLatest(MESSAGES.TOGGLE_STAR_REQUEST, handleToggleStarRequest);
|
||||||
|
|
|
@ -12,8 +12,31 @@ import database from '../lib/realm';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
|
||||||
const handleSelectServer = function* handleSelectServer({ server }) {
|
const getServerInfo = function* getServerInfo({ server }) {
|
||||||
try {
|
try {
|
||||||
|
const serverInfo = yield RocketChat.getServerInfo(server);
|
||||||
|
if (!serverInfo.success) {
|
||||||
|
Alert.alert(I18n.t('Oops'), I18n.t(serverInfo.message, serverInfo.messageOptions));
|
||||||
|
yield put(serverFailure());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
database.databases.serversDB.write(() => {
|
||||||
|
database.databases.serversDB.create('servers', { id: server, version: serverInfo.version }, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return serverInfo;
|
||||||
|
} catch (e) {
|
||||||
|
log('getServerInfo', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelectServer = function* handleSelectServer({ server, version, fetchVersion }) {
|
||||||
|
try {
|
||||||
|
let serverInfo;
|
||||||
|
if (fetchVersion) {
|
||||||
|
serverInfo = yield getServerInfo({ server });
|
||||||
|
}
|
||||||
yield AsyncStorage.setItem('currentServer', server);
|
yield AsyncStorage.setItem('currentServer', server);
|
||||||
const userStringified = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
const userStringified = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
||||||
|
|
||||||
|
@ -37,7 +60,7 @@ const handleSelectServer = function* handleSelectServer({ server }) {
|
||||||
return result;
|
return result;
|
||||||
}, {})));
|
}, {})));
|
||||||
|
|
||||||
yield put(selectServerSuccess(server));
|
yield put(selectServerSuccess(server, fetchVersion ? serverInfo && serverInfo.version : version));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('handleSelectServer', e);
|
log('handleSelectServer', e);
|
||||||
}
|
}
|
||||||
|
@ -45,13 +68,9 @@ const handleSelectServer = function* handleSelectServer({ server }) {
|
||||||
|
|
||||||
const handleServerRequest = function* handleServerRequest({ server }) {
|
const handleServerRequest = function* handleServerRequest({ server }) {
|
||||||
try {
|
try {
|
||||||
const result = yield RocketChat.testServer(server);
|
const serverInfo = yield getServerInfo({ server });
|
||||||
if (!result.success) {
|
|
||||||
Alert.alert(I18n.t('Oops'), I18n.t(result.message, result.messageOptions));
|
|
||||||
yield put(serverFailure());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO: cai aqui O.o
|
||||||
const loginServicesLength = yield RocketChat.getLoginServices(server);
|
const loginServicesLength = yield RocketChat.getLoginServices(server);
|
||||||
if (loginServicesLength === 0) {
|
if (loginServicesLength === 0) {
|
||||||
Navigation.navigate('LoginView');
|
Navigation.navigate('LoginView');
|
||||||
|
@ -59,10 +78,7 @@ const handleServerRequest = function* handleServerRequest({ server }) {
|
||||||
Navigation.navigate('LoginSignupView');
|
Navigation.navigate('LoginSignupView');
|
||||||
}
|
}
|
||||||
|
|
||||||
database.databases.serversDB.write(() => {
|
yield put(selectServerRequest(server, serverInfo.version, false));
|
||||||
database.databases.serversDB.create('servers', { id: server }, true);
|
|
||||||
});
|
|
||||||
yield put(selectServerRequest(server));
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
yield put(serverFailure());
|
yield put(serverFailure());
|
||||||
log('handleServerRequest', e);
|
log('handleServerRequest', e);
|
||||||
|
|
|
@ -21,6 +21,8 @@ import protectedFunction from '../../lib/methods/helpers/protectedFunction';
|
||||||
import { CustomHeaderButtons, Item } from '../../containers/HeaderButton';
|
import { CustomHeaderButtons, Item } from '../../containers/HeaderButton';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
|
|
||||||
|
const PAGE_SIZE = 25;
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||||
user: {
|
user: {
|
||||||
|
@ -62,19 +64,20 @@ export default class RoomMembersView extends LoggedView {
|
||||||
this.CANCEL_INDEX = 0;
|
this.CANCEL_INDEX = 0;
|
||||||
this.MUTE_INDEX = 1;
|
this.MUTE_INDEX = 1;
|
||||||
this.actionSheetOptions = [''];
|
this.actionSheetOptions = [''];
|
||||||
const { rid, members } = props.navigation.state.params;
|
const { rid } = props.navigation.state.params;
|
||||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
|
||||||
this.permissions = RocketChat.hasPermission(['mute-user'], rid);
|
this.permissions = RocketChat.hasPermission(['mute-user'], rid);
|
||||||
this.state = {
|
this.state = {
|
||||||
isLoading: true,
|
isLoading: false,
|
||||||
allUsers: false,
|
allUsers: false,
|
||||||
filtering: false,
|
filtering: false,
|
||||||
rid,
|
rid,
|
||||||
members,
|
members: [],
|
||||||
membersFiltered: [],
|
membersFiltered: [],
|
||||||
userLongPressed: {},
|
userLongPressed: {},
|
||||||
room: this.rooms[0] || {},
|
room: this.rooms[0] || {},
|
||||||
options: []
|
options: [],
|
||||||
|
end: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +173,9 @@ export default class RoomMembersView extends LoggedView {
|
||||||
toggleStatus = () => {
|
toggleStatus = () => {
|
||||||
try {
|
try {
|
||||||
const { allUsers } = this.state;
|
const { allUsers } = this.state;
|
||||||
this.fetchMembers(!allUsers);
|
this.setState({ members: [], allUsers: !allUsers, end: false }, () => {
|
||||||
|
this.fetchMembers();
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('RoomMembers.toggleStatus', e);
|
log('RoomMembers.toggleStatus', e);
|
||||||
}
|
}
|
||||||
|
@ -186,15 +191,26 @@ export default class RoomMembersView extends LoggedView {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchMembers = async(status) => {
|
// eslint-disable-next-line react/sort-comp
|
||||||
this.setState({ isLoading: true });
|
fetchMembers = async() => {
|
||||||
const { rid } = this.state;
|
const {
|
||||||
|
rid, members, isLoading, allUsers, end
|
||||||
|
} = this.state;
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
|
if (isLoading || end) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ isLoading: true });
|
||||||
try {
|
try {
|
||||||
const membersResult = await RocketChat.getRoomMembers(rid, status);
|
const membersResult = await RocketChat.getRoomMembers(rid, allUsers, members.length, PAGE_SIZE);
|
||||||
const members = membersResult.records;
|
const newMembers = membersResult.records;
|
||||||
this.setState({ allUsers: status, members, isLoading: false });
|
this.setState({
|
||||||
navigation.setParams({ allUsers: status });
|
members: members.concat(newMembers || []),
|
||||||
|
isLoading: false,
|
||||||
|
end: newMembers.length < PAGE_SIZE
|
||||||
|
});
|
||||||
|
navigation.setParams({ allUsers });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('TCL: fetchMembers -> error', error);
|
console.log('TCL: fetchMembers -> error', error);
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false });
|
||||||
|
@ -260,9 +276,9 @@ export default class RoomMembersView extends LoggedView {
|
||||||
const {
|
const {
|
||||||
filtering, members, membersFiltered, isLoading
|
filtering, members, membersFiltered, isLoading
|
||||||
} = this.state;
|
} = this.state;
|
||||||
if (isLoading) {
|
// if (isLoading) {
|
||||||
return <ActivityIndicator style={styles.loading} />;
|
// return <ActivityIndicator style={styles.loading} />;
|
||||||
}
|
// }
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.list} testID='room-members-view' forceInset={{ bottom: 'never' }}>
|
<SafeAreaView style={styles.list} testID='room-members-view' forceInset={{ bottom: 'never' }}>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
|
@ -273,6 +289,16 @@ export default class RoomMembersView extends LoggedView {
|
||||||
keyExtractor={item => item._id}
|
keyExtractor={item => item._id}
|
||||||
ItemSeparatorComponent={this.renderSeparator}
|
ItemSeparatorComponent={this.renderSeparator}
|
||||||
ListHeaderComponent={this.renderSearchBar}
|
ListHeaderComponent={this.renderSearchBar}
|
||||||
|
ListFooterComponent={() => {
|
||||||
|
if (isLoading) {
|
||||||
|
return <ActivityIndicator style={styles.loading} />;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}}
|
||||||
|
onEndReachedThreshold={0.1}
|
||||||
|
onEndReached={this.fetchMembers}
|
||||||
|
maxToRenderPerBatch={5}
|
||||||
|
windowSize={10}
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|
|
@ -3,23 +3,26 @@ import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
View, Text, StyleSheet, ScrollView
|
View, Text, StyleSheet, ScrollView
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import { emojify } from 'react-emojione';
|
||||||
|
|
||||||
import I18n from '../../../i18n';
|
import I18n from '../../../i18n';
|
||||||
import sharedStyles from '../../Styles';
|
import sharedStyles from '../../Styles';
|
||||||
import { isIOS } from '../../../utils/deviceInfo';
|
import { isIOS, isAndroid } from '../../../utils/deviceInfo';
|
||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
import { COLOR_TEXT_DESCRIPTION, HEADER_TITLE, COLOR_WHITE } from '../../../constants/colors';
|
import { COLOR_TEXT_DESCRIPTION, HEADER_TITLE, COLOR_WHITE } from '../../../constants/colors';
|
||||||
|
|
||||||
const TITLE_SIZE = 16;
|
const TITLE_SIZE = 16;
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
|
||||||
height: '100%'
|
height: '100%'
|
||||||
},
|
},
|
||||||
titleContainer: {
|
titleContainer: {
|
||||||
flex: 6,
|
flex: 6,
|
||||||
flexDirection: 'row'
|
flexDirection: 'row'
|
||||||
},
|
},
|
||||||
|
threadContainer: {
|
||||||
|
marginRight: isAndroid ? 20 : undefined
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
...sharedStyles.textSemibold,
|
...sharedStyles.textSemibold,
|
||||||
color: HEADER_TITLE,
|
color: HEADER_TITLE,
|
||||||
|
@ -62,7 +65,7 @@ Typing.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Header = React.memo(({
|
const Header = React.memo(({
|
||||||
prid, title, type, status, usersTyping, width, height
|
title, type, status, usersTyping, width, height, prid, tmid, widthOffset
|
||||||
}) => {
|
}) => {
|
||||||
const portrait = height > width;
|
const portrait = height > width;
|
||||||
let scale = 1;
|
let scale = 1;
|
||||||
|
@ -72,9 +75,13 @@ const Header = React.memo(({
|
||||||
scale = 0.8;
|
scale = 0.8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (title) {
|
||||||
|
title = emojify(title, { output: 'unicode' });
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={[styles.container, { width: width - widthOffset }]}>
|
||||||
<View style={styles.titleContainer}>
|
<View style={[styles.titleContainer, tmid && styles.threadContainer]}>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
horizontal
|
horizontal
|
||||||
|
@ -82,10 +89,10 @@ const Header = React.memo(({
|
||||||
contentContainerStyle={styles.scroll}
|
contentContainerStyle={styles.scroll}
|
||||||
>
|
>
|
||||||
<Icon type={prid ? 'discussion' : type} status={status} />
|
<Icon type={prid ? 'discussion' : type} status={status} />
|
||||||
<Text style={[styles.title, { fontSize: TITLE_SIZE * scale }]} numberOfLines={1}>{title}</Text>
|
<Text style={[styles.title, { fontSize: TITLE_SIZE * scale }]} numberOfLines={1} testID={`room-view-title-${ title }`}>{title}</Text>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
<Typing usersTyping={usersTyping} />
|
{type === 'thread' ? null : <Typing usersTyping={usersTyping} />}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -96,8 +103,10 @@ Header.propTypes = {
|
||||||
width: PropTypes.number.isRequired,
|
width: PropTypes.number.isRequired,
|
||||||
height: PropTypes.number.isRequired,
|
height: PropTypes.number.isRequired,
|
||||||
prid: PropTypes.string,
|
prid: PropTypes.string,
|
||||||
|
tmid: PropTypes.string,
|
||||||
status: PropTypes.string,
|
status: PropTypes.string,
|
||||||
usersTyping: PropTypes.array
|
usersTyping: PropTypes.array,
|
||||||
|
widthOffset: PropTypes.number
|
||||||
};
|
};
|
||||||
|
|
||||||
Header.defaultProps = {
|
Header.defaultProps = {
|
||||||
|
|
|
@ -30,6 +30,8 @@ const Icon = React.memo(({ type, status }) => {
|
||||||
let icon;
|
let icon;
|
||||||
if (type === 'discussion') {
|
if (type === 'discussion') {
|
||||||
icon = 'chat';
|
icon = 'chat';
|
||||||
|
} else if (type === 'thread') {
|
||||||
|
icon = 'thread';
|
||||||
} else if (type === 'c') {
|
} else if (type === 'c') {
|
||||||
icon = 'hashtag';
|
icon = 'hashtag';
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { CustomHeaderButtons, Item } from '../../../containers/HeaderButton';
|
||||||
|
import database, { safeAddListener } from '../../../lib/realm';
|
||||||
|
import RocketChat from '../../../lib/rocketchat';
|
||||||
|
import log from '../../../utils/log';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
more: {
|
||||||
|
marginHorizontal: 0, marginLeft: 0, marginRight: 5
|
||||||
|
},
|
||||||
|
thread: {
|
||||||
|
marginHorizontal: 0, marginLeft: 0, marginRight: 10
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
@connect(state => ({
|
||||||
|
userId: state.login.user && state.login.user.id,
|
||||||
|
threadsEnabled: state.settings.Threads_enabled
|
||||||
|
}))
|
||||||
|
class RightButtonsContainer extends React.PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
userId: PropTypes.string,
|
||||||
|
threadsEnabled: PropTypes.bool,
|
||||||
|
rid: PropTypes.string,
|
||||||
|
t: PropTypes.string,
|
||||||
|
tmid: PropTypes.string,
|
||||||
|
navigation: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
if (props.tmid) {
|
||||||
|
this.thread = database.objectForPrimaryKey('messages', props.tmid);
|
||||||
|
safeAddListener(this.thread, this.updateThread);
|
||||||
|
}
|
||||||
|
this.state = {
|
||||||
|
isFollowingThread: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
updateThread = () => {
|
||||||
|
const { userId } = this.props;
|
||||||
|
this.setState({
|
||||||
|
isFollowingThread: this.thread.replies && !!this.thread.replies.find(t => t === userId)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
goThreadsView = () => {
|
||||||
|
const { rid, t, navigation } = this.props;
|
||||||
|
navigation.navigate('ThreadMessagesView', { rid, t });
|
||||||
|
}
|
||||||
|
|
||||||
|
goRoomActionsView = () => {
|
||||||
|
const { rid, t, navigation } = this.props;
|
||||||
|
navigation.navigate('RoomActionsView', { rid, t });
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleFollowThread = async() => {
|
||||||
|
const { isFollowingThread } = this.state;
|
||||||
|
const { tmid } = this.props;
|
||||||
|
try {
|
||||||
|
await RocketChat.toggleFollowMessage(tmid, !isFollowingThread);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('TCL: RightButtonsContainer -> toggleFollowThread -> e', e);
|
||||||
|
log('toggleFollowThread', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isFollowingThread } = this.state;
|
||||||
|
const { t, tmid, threadsEnabled } = this.props;
|
||||||
|
if (t === 'l') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (tmid) {
|
||||||
|
return (
|
||||||
|
<CustomHeaderButtons>
|
||||||
|
<Item
|
||||||
|
title='bell'
|
||||||
|
iconName={isFollowingThread ? 'Bell-off' : 'bell'}
|
||||||
|
onPress={this.toggleFollowThread}
|
||||||
|
testID={isFollowingThread ? 'room-view-header-unfollow' : 'room-view-header-follow'}
|
||||||
|
/>
|
||||||
|
</CustomHeaderButtons>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<CustomHeaderButtons>
|
||||||
|
{threadsEnabled ? (
|
||||||
|
<Item
|
||||||
|
title='thread'
|
||||||
|
iconName='thread'
|
||||||
|
onPress={this.goThreadsView}
|
||||||
|
testID='room-view-header-threads'
|
||||||
|
buttonStyle={styles.thread}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<Item
|
||||||
|
title='more'
|
||||||
|
iconName='menu'
|
||||||
|
onPress={this.goRoomActionsView}
|
||||||
|
testID='room-view-header-actions'
|
||||||
|
buttonStyle={styles.more}
|
||||||
|
/>
|
||||||
|
</CustomHeaderButtons>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RightButtonsContainer;
|
|
@ -6,6 +6,7 @@ import equal from 'deep-equal';
|
||||||
|
|
||||||
import database from '../../../lib/realm';
|
import database from '../../../lib/realm';
|
||||||
import Header from './Header';
|
import Header from './Header';
|
||||||
|
import RightButtons from './RightButtons';
|
||||||
|
|
||||||
@responsive
|
@responsive
|
||||||
@connect((state, ownProps) => {
|
@connect((state, ownProps) => {
|
||||||
|
@ -33,9 +34,11 @@ export default class RoomHeaderView extends Component {
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
prid: PropTypes.string,
|
prid: PropTypes.string,
|
||||||
|
tmid: PropTypes.string,
|
||||||
rid: PropTypes.string,
|
rid: PropTypes.string,
|
||||||
window: PropTypes.object,
|
window: PropTypes.object,
|
||||||
status: PropTypes.string
|
status: PropTypes.string,
|
||||||
|
widthOffset: PropTypes.number
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -89,19 +92,23 @@ export default class RoomHeaderView extends Component {
|
||||||
render() {
|
render() {
|
||||||
const { usersTyping } = this.state;
|
const { usersTyping } = this.state;
|
||||||
const {
|
const {
|
||||||
window, title, type, status, prid
|
window, title, type, status, prid, tmid, widthOffset
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header
|
<Header
|
||||||
prid={prid}
|
prid={prid}
|
||||||
|
tmid={tmid}
|
||||||
title={title}
|
title={title}
|
||||||
type={type}
|
type={type}
|
||||||
status={status}
|
status={status}
|
||||||
width={window.width}
|
width={window.width}
|
||||||
height={window.height}
|
height={window.height}
|
||||||
usersTyping={usersTyping}
|
usersTyping={usersTyping}
|
||||||
|
widthOffset={widthOffset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { RightButtons };
|
||||||
|
|
|
@ -1,125 +1,137 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ActivityIndicator, FlatList, InteractionManager } from 'react-native';
|
import { ActivityIndicator, FlatList, InteractionManager } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { emojify } from 'react-emojione';
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import database, { safeAddListener } 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 RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import EmptyRoom from './EmptyRoom';
|
import EmptyRoom from './EmptyRoom';
|
||||||
// import ScrollBottomButton from './ScrollBottomButton';
|
|
||||||
|
|
||||||
export class List extends React.Component {
|
export class List extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onEndReached: PropTypes.func,
|
onEndReached: PropTypes.func,
|
||||||
renderFooter: PropTypes.func,
|
renderFooter: PropTypes.func,
|
||||||
renderRow: PropTypes.func,
|
renderRow: PropTypes.func,
|
||||||
rid: PropTypes.string,
|
rid: PropTypes.string,
|
||||||
t: PropTypes.string,
|
t: PropTypes.string,
|
||||||
window: PropTypes.object
|
tmid: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
console.time(`${ this.constructor.name } init`);
|
console.time(`${ this.constructor.name } init`);
|
||||||
console.time(`${ this.constructor.name } mount`);
|
console.time(`${ this.constructor.name } mount`);
|
||||||
this.data = database
|
if (props.tmid) {
|
||||||
.objects('messages')
|
this.data = database
|
||||||
.filtered('rid = $0', props.rid)
|
.objects('threadMessages')
|
||||||
.sorted('ts', true);
|
.filtered('rid = $0', props.tmid)
|
||||||
|
.sorted('ts', true);
|
||||||
|
this.threads = [];
|
||||||
|
} else {
|
||||||
|
this.data = database
|
||||||
|
.objects('messages')
|
||||||
|
.filtered('rid = $0', props.rid)
|
||||||
|
.sorted('ts', true);
|
||||||
|
this.threads = database.objects('threads').filtered('rid = $0', props.rid);
|
||||||
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: true,
|
loading: true,
|
||||||
loadingMore: false,
|
|
||||||
end: false,
|
end: false,
|
||||||
messages: this.data.slice()
|
messages: this.data.slice(),
|
||||||
// showScollToBottomButton: false
|
threads: this.threads.slice()
|
||||||
};
|
};
|
||||||
|
|
||||||
safeAddListener(this.data, this.updateState);
|
safeAddListener(this.data, this.updateState);
|
||||||
console.timeEnd(`${ this.constructor.name } init`);
|
console.timeEnd(`${ this.constructor.name } init`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldComponentUpdate(nextProps, nextState) {
|
|
||||||
// const {
|
|
||||||
// loadingMore, loading, end, showScollToBottomButton, messages
|
|
||||||
// } = this.state;
|
|
||||||
// const { window } = this.props;
|
|
||||||
// return end !== nextState.end
|
|
||||||
// || loadingMore !== nextState.loadingMore
|
|
||||||
// || loading !== nextState.loading
|
|
||||||
// || showScollToBottomButton !== nextState.showScollToBottomButton
|
|
||||||
// // || messages.length !== nextState.messages.length
|
|
||||||
// || !equal(messages, nextState.messages)
|
|
||||||
// || window.width !== nextProps.window.width;
|
|
||||||
// }
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
console.timeEnd(`${ this.constructor.name } mount`);
|
console.timeEnd(`${ this.constructor.name } mount`);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.data.removeAllListeners();
|
this.data.removeAllListeners();
|
||||||
|
this.threads.removeAllListeners();
|
||||||
if (this.updateState && this.updateState.stop) {
|
if (this.updateState && this.updateState.stop) {
|
||||||
this.updateState.stop();
|
this.updateState.stop();
|
||||||
}
|
}
|
||||||
if (this.interactionManager && this.interactionManager.cancel) {
|
if (this.updateThreads && this.updateThreads.stop) {
|
||||||
this.interactionManager.cancel();
|
this.updateThreads.stop();
|
||||||
|
}
|
||||||
|
if (this.interactionManagerState && this.interactionManagerState.cancel) {
|
||||||
|
this.interactionManagerState.cancel();
|
||||||
|
}
|
||||||
|
if (this.interactionManagerThreads && this.interactionManagerThreads.cancel) {
|
||||||
|
this.interactionManagerThreads.cancel();
|
||||||
}
|
}
|
||||||
console.countReset(`${ this.constructor.name }.render calls`);
|
console.countReset(`${ this.constructor.name }.render calls`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/sort-comp
|
// eslint-disable-next-line react/sort-comp
|
||||||
updateState = debounce(() => {
|
updateState = debounce(() => {
|
||||||
this.interactionManager = InteractionManager.runAfterInteractions(() => {
|
this.interactionManagerState = InteractionManager.runAfterInteractions(() => {
|
||||||
this.setState({ messages: this.data.slice(), loading: false, loadingMore: false });
|
this.setState({
|
||||||
|
messages: this.data.slice(),
|
||||||
|
threads: this.threads.slice(),
|
||||||
|
loading: false
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}, 300);
|
}, 300, { leading: true });
|
||||||
|
|
||||||
onEndReached = async() => {
|
onEndReached = async() => {
|
||||||
const {
|
const {
|
||||||
loadingMore, loading, end, messages
|
loading, end, messages
|
||||||
} = this.state;
|
} = this.state;
|
||||||
if (loadingMore || loading || end || messages.length < 50) {
|
if (loading || end || messages.length < 50) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ loadingMore: true });
|
this.setState({ loading: true });
|
||||||
const { rid, t } = this.props;
|
const { rid, t, tmid } = this.props;
|
||||||
try {
|
try {
|
||||||
const result = await RocketChat.loadMessagesForRoom({ rid, t, latest: this.data[this.data.length - 1].ts });
|
let result;
|
||||||
|
if (tmid) {
|
||||||
|
result = await RocketChat.loadThreadMessages({ tmid, skip: messages.length });
|
||||||
|
} else {
|
||||||
|
result = await RocketChat.loadMessagesForRoom({ rid, t, latest: messages[messages.length - 1].ts });
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({ end: result.length < 50 });
|
this.setState({ end: result.length < 50 });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState({ loadingMore: false });
|
this.setState({ loading: false });
|
||||||
log('ListView.onEndReached', e);
|
log('ListView.onEndReached', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// scrollToBottom = () => {
|
|
||||||
// requestAnimationFrame(() => {
|
|
||||||
// this.list.scrollToOffset({ offset: isNotch ? -90 : -60 });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// handleScroll = (event) => {
|
|
||||||
// if (event.nativeEvent.contentOffset.y > 0) {
|
|
||||||
// this.setState({ showScollToBottomButton: true });
|
|
||||||
// } else {
|
|
||||||
// this.setState({ showScollToBottomButton: false });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
renderFooter = () => {
|
renderFooter = () => {
|
||||||
const { loadingMore, loading } = this.state;
|
const { loading } = this.state;
|
||||||
if (loadingMore || loading) {
|
if (loading) {
|
||||||
return <ActivityIndicator style={styles.loadingMore} />;
|
return <ActivityIndicator style={styles.loading} />;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderItem = ({ item, index }) => {
|
||||||
|
const { messages, threads } = this.state;
|
||||||
|
const { renderRow } = this.props;
|
||||||
|
if (item.tmid) {
|
||||||
|
const thread = threads.find(t => t._id === item.tmid);
|
||||||
|
if (thread) {
|
||||||
|
let tmsg = thread.msg || (thread.attachments && thread.attachments.length && thread.attachments[0].title);
|
||||||
|
tmsg = emojify(tmsg, { output: 'unicode' });
|
||||||
|
item = { ...item, tmsg };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return renderRow(item, messages[index + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.count(`${ this.constructor.name }.render calls`);
|
console.count(`${ this.constructor.name }.render calls`);
|
||||||
const { renderRow } = this.props;
|
|
||||||
const { messages } = this.state;
|
const { messages } = this.state;
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
@ -130,10 +142,9 @@ export class List extends React.Component {
|
||||||
keyExtractor={item => item._id}
|
keyExtractor={item => item._id}
|
||||||
data={messages}
|
data={messages}
|
||||||
extraData={this.state}
|
extraData={this.state}
|
||||||
renderItem={({ item, index }) => renderRow(item, messages[index + 1])}
|
renderItem={this.renderItem}
|
||||||
contentContainerStyle={styles.contentContainer}
|
contentContainerStyle={styles.contentContainer}
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
// onScroll={this.handleScroll}
|
|
||||||
inverted
|
inverted
|
||||||
removeClippedSubviews
|
removeClippedSubviews
|
||||||
initialNumToRender={1}
|
initialNumToRender={1}
|
||||||
|
@ -144,11 +155,6 @@ export class List extends React.Component {
|
||||||
ListFooterComponent={this.renderFooter}
|
ListFooterComponent={this.renderFooter}
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
/>
|
/>
|
||||||
{/* <ScrollBottomButton
|
|
||||||
show={showScollToBottomButton}
|
|
||||||
onPress={this.scrollToBottom}
|
|
||||||
landscape={window.width > window.height}
|
|
||||||
/> */}
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { TouchableOpacity, StyleSheet } from 'react-native';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { isNotch } from '../../utils/deviceInfo';
|
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
|
||||||
import { COLOR_BUTTON_PRIMARY } from '../../constants/colors';
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
button: {
|
|
||||||
position: 'absolute',
|
|
||||||
width: 42,
|
|
||||||
height: 42,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
backgroundColor: '#EAF2FE',
|
|
||||||
borderRadius: 21,
|
|
||||||
shadowColor: '#000',
|
|
||||||
shadowOffset: {
|
|
||||||
width: 0,
|
|
||||||
height: 1
|
|
||||||
},
|
|
||||||
shadowOpacity: 0.20,
|
|
||||||
shadowRadius: 1.41,
|
|
||||||
elevation: 2
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let right;
|
|
||||||
let bottom = 80;
|
|
||||||
if (isNotch) {
|
|
||||||
bottom = 120;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ScrollBottomButton = React.memo(({ show, onPress, landscape }) => {
|
|
||||||
if (show) {
|
|
||||||
if (landscape) {
|
|
||||||
right = 45;
|
|
||||||
} else {
|
|
||||||
right = 30;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<TouchableOpacity
|
|
||||||
activeOpacity={0.8}
|
|
||||||
style={[styles.button, { right, bottom }]}
|
|
||||||
onPress={onPress}
|
|
||||||
>
|
|
||||||
<CustomIcon name='arrow-down' color={COLOR_BUTTON_PRIMARY} size={30} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
ScrollBottomButton.propTypes = {
|
|
||||||
show: PropTypes.bool.isRequired,
|
|
||||||
onPress: PropTypes.func.isRequired,
|
|
||||||
landscape: PropTypes.bool
|
|
||||||
};
|
|
||||||
export default ScrollBottomButton;
|
|
|
@ -9,11 +9,11 @@ import { SafeAreaView } from 'react-navigation';
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import 'react-native-console-time-polyfill';
|
import 'react-native-console-time-polyfill';
|
||||||
|
import EJSON from 'ejson';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
toggleReactionPicker as toggleReactionPickerAction,
|
toggleReactionPicker as toggleReactionPickerAction,
|
||||||
actionsShow as actionsShowAction,
|
actionsShow as actionsShowAction,
|
||||||
messagesRequest as messagesRequestAction,
|
|
||||||
editCancel as editCancelAction,
|
editCancel as editCancelAction,
|
||||||
replyCancel as replyCancelAction
|
replyCancel as replyCancelAction
|
||||||
} from '../../actions/messages';
|
} from '../../actions/messages';
|
||||||
|
@ -30,14 +30,15 @@ import UploadProgress from './UploadProgress';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import { isIOS } from '../../utils/deviceInfo';
|
import { isIOS } from '../../utils/deviceInfo';
|
||||||
|
import EventEmitter from '../../utils/events';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import ConnectionBadge from '../../containers/ConnectionBadge';
|
import ConnectionBadge from '../../containers/ConnectionBadge';
|
||||||
import { CustomHeaderButtons, Item } from '../../containers/HeaderButton';
|
import RoomHeaderView, { RightButtons } from './Header';
|
||||||
import RoomHeaderView from './Header';
|
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import Separator from './Separator';
|
import Separator from './Separator';
|
||||||
import { COLOR_WHITE } from '../../constants/colors';
|
import { COLOR_WHITE } from '../../constants/colors';
|
||||||
import debounce from '../../utils/debounce';
|
import debounce from '../../utils/debounce';
|
||||||
|
import buildMessage from '../../lib/methods/helpers/buildMessage';
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
user: {
|
user: {
|
||||||
|
@ -49,13 +50,13 @@ import debounce from '../../utils/debounce';
|
||||||
showActions: state.messages.showActions,
|
showActions: state.messages.showActions,
|
||||||
showErrorActions: state.messages.showErrorActions,
|
showErrorActions: state.messages.showErrorActions,
|
||||||
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background',
|
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background',
|
||||||
useRealName: state.settings.UI_Use_Real_Name
|
useRealName: state.settings.UI_Use_Real_Name,
|
||||||
|
isAuthenticated: state.login.isAuthenticated
|
||||||
}), dispatch => ({
|
}), dispatch => ({
|
||||||
editCancel: () => dispatch(editCancelAction()),
|
editCancel: () => dispatch(editCancelAction()),
|
||||||
replyCancel: () => dispatch(replyCancelAction()),
|
replyCancel: () => dispatch(replyCancelAction()),
|
||||||
toggleReactionPicker: message => dispatch(toggleReactionPickerAction(message)),
|
toggleReactionPicker: message => dispatch(toggleReactionPickerAction(message)),
|
||||||
actionsShow: actionMessage => dispatch(actionsShowAction(actionMessage)),
|
actionsShow: actionMessage => dispatch(actionsShowAction(actionMessage))
|
||||||
messagesRequest: room => dispatch(messagesRequestAction(room))
|
|
||||||
}))
|
}))
|
||||||
/** @extends React.Component */
|
/** @extends React.Component */
|
||||||
export default class RoomView extends LoggedView {
|
export default class RoomView extends LoggedView {
|
||||||
|
@ -64,15 +65,13 @@ export default class RoomView extends LoggedView {
|
||||||
const prid = navigation.getParam('prid');
|
const prid = navigation.getParam('prid');
|
||||||
const title = navigation.getParam('name');
|
const title = navigation.getParam('name');
|
||||||
const t = navigation.getParam('t');
|
const t = navigation.getParam('t');
|
||||||
|
const tmid = navigation.getParam('tmid');
|
||||||
return {
|
return {
|
||||||
headerTitle: <RoomHeaderView rid={rid} prid={prid} title={title} type={t} />,
|
headerTitleContainerStyle: styles.headerTitleContainerStyle,
|
||||||
headerRight: t === 'l'
|
headerTitle: (
|
||||||
? null
|
<RoomHeaderView rid={rid} prid={prid} tmid={tmid} title={title} type={t} widthOffset={tmid ? 95 : 130} />
|
||||||
: (
|
),
|
||||||
<CustomHeaderButtons>
|
headerRight: <RightButtons rid={rid} tmid={tmid} t={t} navigation={navigation} />
|
||||||
<Item title='more' iconName='menu' onPress={() => navigation.navigate('RoomActionsView', { rid, t })} testID='room-view-header-actions' />
|
|
||||||
</CustomHeaderButtons>
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,9 +87,9 @@ export default class RoomView extends LoggedView {
|
||||||
actionMessage: PropTypes.object,
|
actionMessage: PropTypes.object,
|
||||||
appState: PropTypes.string,
|
appState: PropTypes.string,
|
||||||
useRealName: PropTypes.bool,
|
useRealName: PropTypes.bool,
|
||||||
|
isAuthenticated: PropTypes.bool,
|
||||||
toggleReactionPicker: PropTypes.func.isRequired,
|
toggleReactionPicker: PropTypes.func.isRequired,
|
||||||
actionsShow: PropTypes.func,
|
actionsShow: PropTypes.func,
|
||||||
messagesRequest: PropTypes.func,
|
|
||||||
editCancel: PropTypes.func,
|
editCancel: PropTypes.func,
|
||||||
replyCancel: PropTypes.func
|
replyCancel: PropTypes.func
|
||||||
};
|
};
|
||||||
|
@ -101,6 +100,7 @@ export default class RoomView extends LoggedView {
|
||||||
console.time(`${ this.constructor.name } mount`);
|
console.time(`${ this.constructor.name } mount`);
|
||||||
this.rid = props.navigation.getParam('rid');
|
this.rid = props.navigation.getParam('rid');
|
||||||
this.t = props.navigation.getParam('t');
|
this.t = props.navigation.getParam('t');
|
||||||
|
this.tmid = props.navigation.getParam('tmid');
|
||||||
this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid);
|
this.rooms = database.objects('subscriptions').filtered('rid = $0', this.rid);
|
||||||
this.state = {
|
this.state = {
|
||||||
joined: this.rooms.length > 0,
|
joined: this.rooms.length > 0,
|
||||||
|
@ -110,38 +110,37 @@ export default class RoomView extends LoggedView {
|
||||||
this.beginAnimating = false;
|
this.beginAnimating = false;
|
||||||
this.beginAnimatingTimeout = setTimeout(() => this.beginAnimating = true, 300);
|
this.beginAnimatingTimeout = setTimeout(() => this.beginAnimating = true, 300);
|
||||||
this.messagebox = React.createRef();
|
this.messagebox = React.createRef();
|
||||||
|
safeAddListener(this.rooms, this.updateRoom);
|
||||||
console.timeEnd(`${ this.constructor.name } init`);
|
console.timeEnd(`${ this.constructor.name } init`);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.didMountInteraction = InteractionManager.runAfterInteractions(async() => {
|
this.didMountInteraction = InteractionManager.runAfterInteractions(() => {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const { messagesRequest, navigation } = this.props;
|
const { navigation, isAuthenticated } = this.props;
|
||||||
messagesRequest(room);
|
|
||||||
|
|
||||||
// if room is joined
|
if (room._id && !this.tmid) {
|
||||||
if (room._id) {
|
|
||||||
navigation.setParams({ name: this.getRoomTitle(room), t: room.t });
|
navigation.setParams({ name: this.getRoomTitle(room), t: room.t });
|
||||||
this.sub = await RocketChat.subscribeRoom(room);
|
|
||||||
RocketChat.readMessages(room.rid);
|
|
||||||
if (room.alert || room.unread || room.userMentions) {
|
|
||||||
this.setLastOpen(room.ls);
|
|
||||||
} else {
|
|
||||||
this.setLastOpen(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
safeAddListener(this.rooms, this.updateRoom);
|
|
||||||
|
if (isAuthenticated) {
|
||||||
|
this.init();
|
||||||
|
} else {
|
||||||
|
EventEmitter.addEventListener('connected', this.handleConnected);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
console.timeEnd(`${ this.constructor.name } mount`);
|
console.timeEnd(`${ this.constructor.name } mount`);
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
const {
|
const {
|
||||||
room, joined
|
room, joined, lastOpen
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { showActions, showErrorActions, appState } = this.props;
|
const { showActions, showErrorActions, appState } = this.props;
|
||||||
|
|
||||||
if (room.ro !== nextState.room.ro) {
|
if (lastOpen !== nextState.lastOpen) {
|
||||||
|
return true;
|
||||||
|
} else if (room.ro !== nextState.room.ro) {
|
||||||
return true;
|
return true;
|
||||||
} else if (room.f !== nextState.room.f) {
|
} else if (room.f !== nextState.room.f) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -180,10 +179,12 @@ export default class RoomView extends LoggedView {
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this.messagebox && this.messagebox.current && this.messagebox.current.text) {
|
if (this.messagebox && this.messagebox.current && this.messagebox.current.text) {
|
||||||
const { text } = this.messagebox.current;
|
const { text } = this.messagebox.current;
|
||||||
database.write(() => {
|
const [room] = this.rooms;
|
||||||
const [room] = this.rooms;
|
if (room) {
|
||||||
room.draftMessage = text;
|
database.write(() => {
|
||||||
});
|
room.draftMessage = text;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.rooms.removeAllListeners();
|
this.rooms.removeAllListeners();
|
||||||
if (this.sub && this.sub.stop) {
|
if (this.sub && this.sub.stop) {
|
||||||
|
@ -204,9 +205,41 @@ export default class RoomView extends LoggedView {
|
||||||
if (this.updateStateInteraction && this.updateStateInteraction.cancel) {
|
if (this.updateStateInteraction && this.updateStateInteraction.cancel) {
|
||||||
this.updateStateInteraction.cancel();
|
this.updateStateInteraction.cancel();
|
||||||
}
|
}
|
||||||
|
if (this.initInteraction && this.initInteraction.cancel) {
|
||||||
|
this.initInteraction.cancel();
|
||||||
|
}
|
||||||
|
EventEmitter.removeListener('connected', this.handleConnected);
|
||||||
console.countReset(`${ this.constructor.name }.render calls`);
|
console.countReset(`${ this.constructor.name }.render calls`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/sort-comp
|
||||||
|
init = () => {
|
||||||
|
try {
|
||||||
|
this.initInteraction = InteractionManager.runAfterInteractions(async() => {
|
||||||
|
const { room } = this.state;
|
||||||
|
if (this.tmid) {
|
||||||
|
RocketChat.loadThreadMessages({ tmid: this.tmid, t: this.t });
|
||||||
|
} else {
|
||||||
|
await this.getMessages(room);
|
||||||
|
|
||||||
|
// if room is joined
|
||||||
|
if (room._id) {
|
||||||
|
if (room.alert || room.unread || room.userMentions) {
|
||||||
|
this.setLastOpen(room.ls);
|
||||||
|
} else {
|
||||||
|
this.setLastOpen(null);
|
||||||
|
}
|
||||||
|
RocketChat.readMessages(room.rid).catch(e => console.log(e));
|
||||||
|
this.sub = await RocketChat.subscribeRoom(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log('TCL: init -> e', e);
|
||||||
|
log('RoomView.init', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMessageLongPress = (message) => {
|
onMessageLongPress = (message) => {
|
||||||
const { actionsShow } = this.props;
|
const { actionsShow } = this.props;
|
||||||
actionsShow(message);
|
actionsShow(message);
|
||||||
|
@ -232,6 +265,11 @@ export default class RoomView extends LoggedView {
|
||||||
});
|
});
|
||||||
}, 1000, true)
|
}, 1000, true)
|
||||||
|
|
||||||
|
handleConnected = () => {
|
||||||
|
this.init();
|
||||||
|
EventEmitter.removeListener('connected', this.handleConnected);
|
||||||
|
}
|
||||||
|
|
||||||
internalSetState = (...args) => {
|
internalSetState = (...args) => {
|
||||||
if (isIOS && this.beginAnimating) {
|
if (isIOS && this.beginAnimating) {
|
||||||
LayoutAnimation.easeInEaseOut();
|
LayoutAnimation.easeInEaseOut();
|
||||||
|
@ -241,14 +279,16 @@ export default class RoomView extends LoggedView {
|
||||||
|
|
||||||
updateRoom = () => {
|
updateRoom = () => {
|
||||||
this.updateStateInteraction = InteractionManager.runAfterInteractions(() => {
|
this.updateStateInteraction = InteractionManager.runAfterInteractions(() => {
|
||||||
const room = JSON.parse(JSON.stringify(this.rooms[0] || {}));
|
if (this.rooms[0]) {
|
||||||
this.internalSetState({ room });
|
const room = JSON.parse(JSON.stringify(this.rooms[0] || {}));
|
||||||
|
this.internalSetState({ room });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage = (message) => {
|
sendMessage = (message, tmid) => {
|
||||||
LayoutAnimation.easeInEaseOut();
|
LayoutAnimation.easeInEaseOut();
|
||||||
RocketChat.sendMessage(this.rid, message).then(() => {
|
RocketChat.sendMessage(this.rid, message, this.tmid || tmid).then(() => {
|
||||||
this.setLastOpen(null);
|
this.setLastOpen(null);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -258,6 +298,20 @@ export default class RoomView extends LoggedView {
|
||||||
return ((room.prid || useRealName) && room.fname) || room.name;
|
return ((room.prid || useRealName) && room.fname) || room.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMessages = () => {
|
||||||
|
const { room } = this.state;
|
||||||
|
try {
|
||||||
|
if (room.lastOpen) {
|
||||||
|
return RocketChat.loadMissedMessages(room);
|
||||||
|
} else {
|
||||||
|
return RocketChat.loadMessagesForRoom(room);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('TCL: getMessages -> e', e);
|
||||||
|
log('getMessages', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setLastOpen = lastOpen => this.setState({ lastOpen });
|
setLastOpen = lastOpen => this.setState({ lastOpen });
|
||||||
|
|
||||||
joinRoom = async() => {
|
joinRoom = async() => {
|
||||||
|
@ -301,9 +355,22 @@ export default class RoomView extends LoggedView {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/sort-comp
|
||||||
|
fetchThreadName = async(tmid) => {
|
||||||
|
try {
|
||||||
|
// TODO: we should build a tmid queue here in order to search for a single tmid only once
|
||||||
|
const thread = await RocketChat.getSingleMessage(tmid);
|
||||||
|
database.write(() => {
|
||||||
|
database.create('threads', buildMessage(EJSON.fromJSONValue(thread)), true);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log('TCL: fetchThreadName -> error', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
renderItem = (item, previousItem) => {
|
renderItem = (item, previousItem) => {
|
||||||
const { room, lastOpen } = this.state;
|
const { room, lastOpen } = this.state;
|
||||||
const { user } = this.props;
|
const { user, navigation } = this.props;
|
||||||
let dateSeparator = null;
|
let dateSeparator = null;
|
||||||
let showUnreadSeparator = false;
|
let showUnreadSeparator = false;
|
||||||
|
|
||||||
|
@ -319,23 +386,28 @@ export default class RoomView extends LoggedView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const message = (
|
||||||
|
<Message
|
||||||
|
key={item._id}
|
||||||
|
item={item}
|
||||||
|
user={user}
|
||||||
|
archived={room.archived}
|
||||||
|
broadcast={room.broadcast}
|
||||||
|
status={item.status}
|
||||||
|
_updatedAt={item._updatedAt}
|
||||||
|
previousItem={previousItem}
|
||||||
|
navigation={navigation}
|
||||||
|
fetchThreadName={this.fetchThreadName}
|
||||||
|
onReactionPress={this.onReactionPress}
|
||||||
|
onLongPress={this.onMessageLongPress}
|
||||||
|
onDiscussionPress={this.onDiscussionPress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
if (showUnreadSeparator || dateSeparator) {
|
if (showUnreadSeparator || dateSeparator) {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Message
|
{message}
|
||||||
key={item._id}
|
|
||||||
item={item}
|
|
||||||
status={item.status}
|
|
||||||
reactions={JSON.parse(JSON.stringify(item.reactions))}
|
|
||||||
user={user}
|
|
||||||
archived={room.archived}
|
|
||||||
broadcast={room.broadcast}
|
|
||||||
previousItem={previousItem}
|
|
||||||
_updatedAt={item._updatedAt}
|
|
||||||
onReactionPress={this.onReactionPress}
|
|
||||||
onLongPress={this.onMessageLongPress}
|
|
||||||
onDiscussionPress={this.onDiscussionPress}
|
|
||||||
/>
|
|
||||||
<Separator
|
<Separator
|
||||||
ts={dateSeparator}
|
ts={dateSeparator}
|
||||||
unread={showUnreadSeparator}
|
unread={showUnreadSeparator}
|
||||||
|
@ -344,28 +416,13 @@ export default class RoomView extends LoggedView {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return message;
|
||||||
<Message
|
|
||||||
key={item._id}
|
|
||||||
item={item}
|
|
||||||
status={item.status}
|
|
||||||
reactions={JSON.parse(JSON.stringify(item.reactions))}
|
|
||||||
user={user}
|
|
||||||
archived={room.archived}
|
|
||||||
broadcast={room.broadcast}
|
|
||||||
previousItem={previousItem}
|
|
||||||
_updatedAt={item._updatedAt}
|
|
||||||
onReactionPress={this.onReactionPress}
|
|
||||||
onLongPress={this.onMessageLongPress}
|
|
||||||
onDiscussionPress={this.onDiscussionPress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFooter = () => {
|
renderFooter = () => {
|
||||||
const { joined, room } = this.state;
|
const { joined, room } = this.state;
|
||||||
|
|
||||||
if (!joined) {
|
if (!joined && !this.tmid) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.joinRoomContainer} key='room-view-join' testID='room-view-join'>
|
<View style={styles.joinRoomContainer} key='room-view-join' testID='room-view-join'>
|
||||||
<Text style={styles.previewMode}>{I18n.t('You_are_in_preview_mode')}</Text>
|
<Text style={styles.previewMode}>{I18n.t('You_are_in_preview_mode')}</Text>
|
||||||
|
@ -397,13 +454,21 @@ export default class RoomView extends LoggedView {
|
||||||
return <MessageBox ref={this.messagebox} onSubmit={this.sendMessage} rid={this.rid} roomType={room.t} />;
|
return <MessageBox ref={this.messagebox} onSubmit={this.sendMessage} rid={this.rid} roomType={room.t} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
renderList = () => {
|
renderActions = () => {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const { rid, t } = room;
|
const {
|
||||||
|
user, showActions, showErrorActions, navigation
|
||||||
|
} = this.props;
|
||||||
|
if (!navigation.isFocused()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<List rid={rid} t={t} renderRow={this.renderItem} />
|
{room._id && showActions
|
||||||
{this.renderFooter()}
|
? <MessageActions room={room} user={user} />
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
{showErrorActions ? <MessageErrorActions /> : null}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -411,17 +476,14 @@ export default class RoomView extends LoggedView {
|
||||||
render() {
|
render() {
|
||||||
console.count(`${ this.constructor.name }.render calls`);
|
console.count(`${ this.constructor.name }.render calls`);
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const { user, showActions, showErrorActions } = this.props;
|
const { rid, t } = room;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container} testID='room-view' forceInset={{ bottom: 'never' }}>
|
<SafeAreaView style={styles.container} testID='room-view' forceInset={{ bottom: 'never' }}>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
{this.renderList()}
|
<List rid={rid} t={t} tmid={this.tmid} renderRow={this.renderItem} />
|
||||||
{room._id && showActions
|
{this.renderFooter()}
|
||||||
? <MessageActions room={room} user={user} />
|
{this.renderActions()}
|
||||||
: null
|
|
||||||
}
|
|
||||||
{showErrorActions ? <MessageErrorActions /> : null}
|
|
||||||
<ReactionPicker onEmojiSelected={this.onReactionPress} />
|
<ReactionPicker onEmojiSelected={this.onReactionPress} />
|
||||||
<UploadProgress rid={this.rid} />
|
<UploadProgress rid={this.rid} />
|
||||||
<ConnectionBadge />
|
<ConnectionBadge />
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
COLOR_SEPARATOR, COLOR_PRIMARY, COLOR_WHITE, COLOR_TEXT_DESCRIPTION
|
COLOR_SEPARATOR, COLOR_PRIMARY, COLOR_WHITE, COLOR_TEXT_DESCRIPTION
|
||||||
} from '../../constants/colors';
|
} from '../../constants/colors';
|
||||||
|
import { isIOS } from '../../utils/deviceInfo';
|
||||||
import sharedStyles from '../Styles';
|
import sharedStyles from '../Styles';
|
||||||
|
|
||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
|
@ -23,8 +24,8 @@ export default StyleSheet.create({
|
||||||
height: 1,
|
height: 1,
|
||||||
backgroundColor: COLOR_SEPARATOR
|
backgroundColor: COLOR_SEPARATOR
|
||||||
},
|
},
|
||||||
loadingMore: {
|
loading: {
|
||||||
textAlign: 'center',
|
flex: 1,
|
||||||
padding: 15,
|
padding: 15,
|
||||||
color: COLOR_TEXT_DESCRIPTION
|
color: COLOR_TEXT_DESCRIPTION
|
||||||
},
|
},
|
||||||
|
@ -40,9 +41,6 @@ export default StyleSheet.create({
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
flexDirection: 'column'
|
flexDirection: 'column'
|
||||||
},
|
},
|
||||||
loading: {
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
joinRoomContainer: {
|
joinRoomContainer: {
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
@ -67,5 +65,9 @@ export default StyleSheet.create({
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textMedium,
|
...sharedStyles.textMedium,
|
||||||
...sharedStyles.textColorNormal
|
...sharedStyles.textColorNormal
|
||||||
|
},
|
||||||
|
headerTitleContainerStyle: {
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
left: isIOS ? 40 : 50
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -264,10 +264,11 @@ export default class RoomsListView extends LoggedView {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (server && this.hasActiveDB()) {
|
if (server && this.hasActiveDB()) {
|
||||||
|
this.data = database.objects('subscriptions').filtered('archived != true && open == true && t != $0', 'l');
|
||||||
if (sortBy === 'alphabetical') {
|
if (sortBy === 'alphabetical') {
|
||||||
this.data = database.objects('subscriptions').filtered('archived != true && open == true').sorted('name', false);
|
this.data = this.data.sorted('name', false);
|
||||||
} else {
|
} else {
|
||||||
this.data = database.objects('subscriptions').filtered('archived != true && open == true').sorted('roomUpdatedAt', true);
|
this.data = this.data.sorted('roomUpdatedAt', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let chats = [];
|
let chats = [];
|
||||||
|
@ -281,7 +282,7 @@ export default class RoomsListView extends LoggedView {
|
||||||
|
|
||||||
// unread
|
// unread
|
||||||
if (showUnread) {
|
if (showUnread) {
|
||||||
this.unread = this.data.filtered('archived != true && open == true').filtered('(unread > 0 || alert == true)');
|
this.unread = this.data.filtered('(unread > 0 || alert == true)');
|
||||||
unread = this.removeRealmInstance(this.unread);
|
unread = this.removeRealmInstance(this.unread);
|
||||||
safeAddListener(this.unread, debounce(() => this.internalSetState({ unread: this.removeRealmInstance(this.unread) }), 300));
|
safeAddListener(this.unread, debounce(() => this.internalSetState({ unread: this.removeRealmInstance(this.unread) }), 300));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {
|
||||||
|
FlatList, View, Text, InteractionManager
|
||||||
|
} from 'react-native';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { SafeAreaView } from 'react-navigation';
|
||||||
|
import equal from 'deep-equal';
|
||||||
|
import EJSON from 'ejson';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
import LoggedView from '../View';
|
||||||
|
import styles from './styles';
|
||||||
|
import Message from '../../containers/message';
|
||||||
|
import RCActivityIndicator from '../../containers/ActivityIndicator';
|
||||||
|
import I18n from '../../i18n';
|
||||||
|
import RocketChat from '../../lib/rocketchat';
|
||||||
|
import database, { safeAddListener } from '../../lib/realm';
|
||||||
|
import StatusBar from '../../containers/StatusBar';
|
||||||
|
import buildMessage from '../../lib/methods/helpers/buildMessage';
|
||||||
|
import log from '../../utils/log';
|
||||||
|
import debounce from '../../utils/debounce';
|
||||||
|
|
||||||
|
const Separator = React.memo(() => <View style={styles.separator} />);
|
||||||
|
|
||||||
|
@connect(state => ({
|
||||||
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
|
||||||
|
customEmojis: state.customEmojis,
|
||||||
|
user: {
|
||||||
|
id: state.login.user && state.login.user.id,
|
||||||
|
username: state.login.user && state.login.user.username,
|
||||||
|
token: state.login.user && state.login.user.token
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
/** @extends React.Component */
|
||||||
|
export default class ThreadMessagesView extends LoggedView {
|
||||||
|
static navigationOptions = {
|
||||||
|
title: I18n.t('Threads')
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
user: PropTypes.object,
|
||||||
|
navigation: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super('ThreadMessagesView', props);
|
||||||
|
this.rid = props.navigation.getParam('rid');
|
||||||
|
this.t = props.navigation.getParam('t');
|
||||||
|
this.messages = database.objects('threads').filtered('rid = $0', this.rid);
|
||||||
|
safeAddListener(this.messages, this.updateMessages);
|
||||||
|
this.state = {
|
||||||
|
loading: false,
|
||||||
|
messages: this.messages.slice(),
|
||||||
|
end: false,
|
||||||
|
total: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
const { loading, messages, end } = this.state;
|
||||||
|
if (nextState.loading !== loading) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!equal(nextState.messages, messages)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!equal(nextState.end, end)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMessages = () => {
|
||||||
|
this.setState({ messages: this.messages.slice() });
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/sort-comp
|
||||||
|
load = debounce(async() => {
|
||||||
|
const {
|
||||||
|
loading, end, total
|
||||||
|
} = this.state;
|
||||||
|
if (end || loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ loading: true });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await RocketChat.getThreadsList({ rid: this.rid, limit: 50, skip: total });
|
||||||
|
|
||||||
|
database.write(() => result.forEach((message) => {
|
||||||
|
try {
|
||||||
|
database.create('threads', buildMessage(EJSON.fromJSONValue(message)), true);
|
||||||
|
} catch (e) {
|
||||||
|
log('ThreadMessagesView -> load -> create', e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
InteractionManager.runAfterInteractions(() => {
|
||||||
|
this.setState(prevState => ({
|
||||||
|
loading: false,
|
||||||
|
end: result.length < 50,
|
||||||
|
total: prevState.total + result.length
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log('ThreadMessagesView -> catch -> error', error);
|
||||||
|
this.setState({ loading: false, end: true });
|
||||||
|
}
|
||||||
|
}, 300, true)
|
||||||
|
|
||||||
|
formatMessage = lm => (
|
||||||
|
lm ? moment(lm).calendar(null, {
|
||||||
|
lastDay: `[${ I18n.t('Yesterday') }]`,
|
||||||
|
sameDay: 'h:mm A',
|
||||||
|
lastWeek: 'dddd',
|
||||||
|
sameElse: 'MMM D'
|
||||||
|
}) : null
|
||||||
|
)
|
||||||
|
|
||||||
|
renderSeparator = () => <Separator />
|
||||||
|
|
||||||
|
renderEmpty = () => (
|
||||||
|
<View style={styles.listEmptyContainer} testID='thread-messages-view'>
|
||||||
|
<Text style={styles.noDataFound}>{I18n.t('No_thread_messages')}</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
|
||||||
|
renderItem = ({ item }) => {
|
||||||
|
const { user, navigation } = this.props;
|
||||||
|
return (
|
||||||
|
<Message
|
||||||
|
key={item._id}
|
||||||
|
item={item}
|
||||||
|
user={user}
|
||||||
|
archived={false}
|
||||||
|
broadcast={false}
|
||||||
|
status={item.status}
|
||||||
|
_updatedAt={item._updatedAt}
|
||||||
|
navigation={navigation}
|
||||||
|
customTimeFormat='MMM D'
|
||||||
|
customThreadTimeFormat='MMM Do YYYY, h:mm:ss a'
|
||||||
|
fetchThreadName={this.fetchThreadName}
|
||||||
|
onDiscussionPress={this.onDiscussionPress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { messages, loading } = this.state;
|
||||||
|
|
||||||
|
if (!loading && messages.length === 0) {
|
||||||
|
return this.renderEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={styles.list} testID='thread-messages-view' forceInset={{ bottom: 'never' }}>
|
||||||
|
<StatusBar />
|
||||||
|
<FlatList
|
||||||
|
data={messages}
|
||||||
|
renderItem={this.renderItem}
|
||||||
|
style={styles.list}
|
||||||
|
contentContainerStyle={styles.contentContainer}
|
||||||
|
keyExtractor={item => item._id}
|
||||||
|
onEndReached={this.load}
|
||||||
|
onEndReachedThreshold={0.5}
|
||||||
|
maxToRenderPerBatch={5}
|
||||||
|
initialNumToRender={1}
|
||||||
|
ItemSeparatorComponent={this.renderSeparator}
|
||||||
|
ListFooterComponent={loading ? <RCActivityIndicator /> : null}
|
||||||
|
/>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import sharedStyles from '../Styles';
|
||||||
|
import { COLOR_WHITE, COLOR_SEPARATOR } from '../../constants/colors';
|
||||||
|
|
||||||
|
export default StyleSheet.create({
|
||||||
|
list: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: COLOR_WHITE
|
||||||
|
},
|
||||||
|
listEmptyContainer: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: COLOR_WHITE
|
||||||
|
},
|
||||||
|
noDataFound: {
|
||||||
|
fontSize: 14,
|
||||||
|
...sharedStyles.textRegular,
|
||||||
|
...sharedStyles.textColorNormal
|
||||||
|
},
|
||||||
|
contentContainer: {
|
||||||
|
paddingBottom: 30
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
height: StyleSheet.hairlineWidth,
|
||||||
|
width: '100%',
|
||||||
|
marginLeft: 60,
|
||||||
|
marginTop: 10,
|
||||||
|
backgroundColor: COLOR_SEPARATOR
|
||||||
|
}
|
||||||
|
});
|
|
@ -21,6 +21,8 @@ async function navigateToRoom() {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Room screen', () => {
|
describe('Room screen', () => {
|
||||||
|
const mainRoom = `private${ data.random }`;
|
||||||
|
|
||||||
before(async() => {
|
before(async() => {
|
||||||
await navigateToRoom();
|
await navigateToRoom();
|
||||||
});
|
});
|
||||||
|
@ -28,6 +30,8 @@ describe('Room screen', () => {
|
||||||
describe('Render', async() => {
|
describe('Render', async() => {
|
||||||
it('should have room screen', async() => {
|
it('should have room screen', async() => {
|
||||||
await expect(element(by.id('room-view'))).toBeVisible();
|
await expect(element(by.id('room-view'))).toBeVisible();
|
||||||
|
await waitFor(element(by.id(`room-view-title-${ mainRoom }`))).toBeVisible().withTimeout(5000);
|
||||||
|
await expect(element(by.id(`room-view-title-${ mainRoom }`))).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have messages list', async() => {
|
it('should have messages list', async() => {
|
||||||
|
@ -228,17 +232,6 @@ describe('Room screen', () => {
|
||||||
await expect(element(by.id('message-reaction-:grinning:'))).toBeNotVisible();
|
await expect(element(by.id('message-reaction-:grinning:'))).toBeNotVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reply message', async() => {
|
|
||||||
await mockMessage('reply');
|
|
||||||
await element(by.text(`${ data.random }reply`)).longPress();
|
|
||||||
await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
|
|
||||||
await expect(element(by.text('Message actions'))).toBeVisible();
|
|
||||||
await element(by.text('Reply')).tap();
|
|
||||||
await element(by.id('messagebox-input')).typeText('replied');
|
|
||||||
await element(by.id('messagebox-send-message')).tap();
|
|
||||||
// TODO: test if reply was sent
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should edit message', async() => {
|
it('should edit message', async() => {
|
||||||
await mockMessage('edit');
|
await mockMessage('edit');
|
||||||
await element(by.text(`${ data.random }edit`)).longPress();
|
await element(by.text(`${ data.random }edit`)).longPress();
|
||||||
|
@ -281,6 +274,67 @@ describe('Room screen', () => {
|
||||||
// TODO: delete message - swipe on action sheet missing
|
// TODO: delete message - swipe on action sheet missing
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Thread', async() => {
|
||||||
|
const thread = `${ data.random }thread`;
|
||||||
|
it('should create thread', async() => {
|
||||||
|
await mockMessage('thread');
|
||||||
|
await element(by.text(thread)).longPress();
|
||||||
|
await waitFor(element(by.text('Message actions'))).toBeVisible().withTimeout(5000);
|
||||||
|
await expect(element(by.text('Message actions'))).toBeVisible();
|
||||||
|
await element(by.text('Reply')).tap();
|
||||||
|
await element(by.id('messagebox-input')).typeText('replied');
|
||||||
|
await element(by.id('messagebox-send-message')).tap();
|
||||||
|
await waitFor(element(by.id(`message-thread-button-${ thread }`))).toBeVisible().withTimeout(5000);
|
||||||
|
await expect(element(by.id(`message-thread-button-${ thread }`))).toBeVisible();
|
||||||
|
await waitFor(element(by.id(`message-thread-replied-on-${ thread }`))).toBeVisible().withTimeout(5000);
|
||||||
|
await expect(element(by.id(`message-thread-replied-on-${ thread }`))).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate to thread from button', async() => {
|
||||||
|
await element(by.id(`message-thread-button-${ thread }`)).tap();
|
||||||
|
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
|
||||||
|
await waitFor(element(by.id(`room-view-title-${ thread }`))).toBeVisible().withTimeout(5000);
|
||||||
|
await expect(element(by.id(`room-view-title-${ thread }`))).toBeVisible();
|
||||||
|
await tapBack();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should toggle follow thread', async() => {
|
||||||
|
await element(by.id(`message-thread-button-${ thread }`)).tap();
|
||||||
|
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
|
||||||
|
await waitFor(element(by.id(`room-view-title-${ thread }`))).toBeVisible().withTimeout(5000);
|
||||||
|
await expect(element(by.id(`room-view-title-${ thread }`))).toBeVisible();
|
||||||
|
await element(by.id('room-view-header-unfollow')).tap();
|
||||||
|
await waitFor(element(by.id('room-view-header-follow'))).toBeVisible().withTimeout(60000);
|
||||||
|
await expect(element(by.id('room-view-header-follow'))).toBeVisible();
|
||||||
|
await element(by.id('room-view-header-follow')).tap();
|
||||||
|
await waitFor(element(by.id('room-view-header-unfollow'))).toBeVisible().withTimeout(60000);
|
||||||
|
await expect(element(by.id('room-view-header-unfollow'))).toBeVisible();
|
||||||
|
await tapBack();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate to thread from thread name', async() => {
|
||||||
|
await element(by.id(`message-thread-replied-on-${ thread }`)).tap();
|
||||||
|
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
|
||||||
|
await waitFor(element(by.id(`room-view-title-${ thread }`))).toBeVisible().withTimeout(5000);
|
||||||
|
await expect(element(by.id(`room-view-title-${ thread }`))).toBeVisible();
|
||||||
|
await tapBack();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate to thread from threads view', async() => {
|
||||||
|
await element(by.id('room-view-header-threads')).tap();
|
||||||
|
await waitFor(element(by.id('thread-messages-view'))).toBeVisible().withTimeout(5000);
|
||||||
|
await expect(element(by.id('thread-messages-view'))).toBeVisible();
|
||||||
|
await element(by.id(`message-thread-button-${ thread }`)).tap();
|
||||||
|
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
|
||||||
|
await waitFor(element(by.id(`room-view-title-${ thread }`))).toBeVisible().withTimeout(5000);
|
||||||
|
await expect(element(by.id(`room-view-title-${ thread }`))).toBeVisible();
|
||||||
|
await tapBack();
|
||||||
|
await waitFor(element(by.id('thread-messages-view'))).toBeVisible().withTimeout(5000);
|
||||||
|
await expect(element(by.id('thread-messages-view'))).toBeVisible();
|
||||||
|
await tapBack();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(async() => {
|
afterEach(async() => {
|
||||||
takeScreenshot();
|
takeScreenshot();
|
||||||
});
|
});
|
||||||
|
|
|
@ -44,10 +44,6 @@ describe('Join public room', () => {
|
||||||
|
|
||||||
// Render - Header
|
// Render - Header
|
||||||
describe('Header', async() => {
|
describe('Header', async() => {
|
||||||
it('should have star button', async() => {
|
|
||||||
await expect(element(by.id('room-view-header-star'))).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have actions button ', async() => {
|
it('should have actions button ', async() => {
|
||||||
await expect(element(by.id('room-view-header-actions'))).toBeVisible();
|
await expect(element(by.id('room-view-header-actions'))).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
"react-navigation-header-buttons": "^2.1.2",
|
"react-navigation-header-buttons": "^2.1.2",
|
||||||
"react-redux": "^6.0.0",
|
"react-redux": "^6.0.0",
|
||||||
"reactotron-react-native": "2.2",
|
"reactotron-react-native": "2.2",
|
||||||
"realm": "2.24",
|
"realm": "2.26.1",
|
||||||
"redux": "^4.0.1",
|
"redux": "^4.0.1",
|
||||||
"redux-enhancer-react-native-appstate": "^0.3.1",
|
"redux-enhancer-react-native-appstate": "^0.3.1",
|
||||||
"redux-immutable-state-invariant": "^2.1.0",
|
"redux-immutable-state-invariant": "^2.1.0",
|
||||||
|
|
|
@ -26,6 +26,7 @@ const author = {
|
||||||
const baseUrl = 'https://open.rocket.chat';
|
const baseUrl = 'https://open.rocket.chat';
|
||||||
const customEmojis = { react_rocket: 'png', nyan_rocket: 'png', marioparty: 'gif' };
|
const customEmojis = { react_rocket: 'png', nyan_rocket: 'png', marioparty: 'gif' };
|
||||||
const date = new Date(2017, 10, 10, 10);
|
const date = new Date(2017, 10, 10, 10);
|
||||||
|
const longText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
|
||||||
|
|
||||||
const Message = props => (
|
const Message = props => (
|
||||||
<MessageComponent
|
<MessageComponent
|
||||||
|
@ -50,7 +51,7 @@ export default (
|
||||||
<Message msg='Message' />
|
<Message msg='Message' />
|
||||||
|
|
||||||
<Separator title='Long message' />
|
<Separator title='Long message' />
|
||||||
<Message msg='Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum' />
|
<Message msg={longText} />
|
||||||
|
|
||||||
<Separator title='Grouped messages' />
|
<Separator title='Grouped messages' />
|
||||||
<Message msg='...' />
|
<Message msg='...' />
|
||||||
|
@ -58,7 +59,7 @@ export default (
|
||||||
msg='Different user'
|
msg='Different user'
|
||||||
author={{
|
author={{
|
||||||
...author,
|
...author,
|
||||||
username: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum'
|
username: longText
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Message msg='This is the third message' header={false} />
|
<Message msg='This is the third message' header={false} />
|
||||||
|
@ -74,7 +75,7 @@ export default (
|
||||||
msg='Message'
|
msg='Message'
|
||||||
author={{
|
author={{
|
||||||
...author,
|
...author,
|
||||||
username: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum'
|
username: longText
|
||||||
}}
|
}}
|
||||||
alias='Diego Mello'
|
alias='Diego Mello'
|
||||||
/>
|
/>
|
||||||
|
@ -262,6 +263,7 @@ export default (
|
||||||
header={false}
|
header={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Legacy thread */}
|
||||||
<Separator title='Message with reply' />
|
<Separator title='Message with reply' />
|
||||||
<Message
|
<Message
|
||||||
msg="I'm fine!"
|
msg="I'm fine!"
|
||||||
|
@ -282,6 +284,127 @@ export default (
|
||||||
}]}
|
}]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Separator title='Message with thread' />
|
||||||
|
<Message
|
||||||
|
msg='How are you?'
|
||||||
|
tcount={1}
|
||||||
|
tlm={date}
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg="I'm fine!"
|
||||||
|
tmid='1'
|
||||||
|
tmsg='How are you?'
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg="I'm fine!"
|
||||||
|
tmid='1'
|
||||||
|
tmsg='Thread with emoji :) :joy:'
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg="I'm fine!"
|
||||||
|
tmid='1'
|
||||||
|
tmsg={longText}
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg={longText}
|
||||||
|
tmid='1'
|
||||||
|
tmsg='How are you?'
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg={longText}
|
||||||
|
tmid='1'
|
||||||
|
tmsg={longText}
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg='How are you?'
|
||||||
|
tcount={0}
|
||||||
|
tlm={date}
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg='How are you?'
|
||||||
|
tcount={9999}
|
||||||
|
tlm={date}
|
||||||
|
/>
|
||||||
|
{/* <Message
|
||||||
|
msg='How are you?'
|
||||||
|
tcount={9999}
|
||||||
|
tlm={moment().subtract(1, 'hour')}
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg='How are you?'
|
||||||
|
tcount={9999}
|
||||||
|
tlm={moment().subtract(1, 'day')}
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg='How are you?'
|
||||||
|
tcount={9999}
|
||||||
|
tlm={moment().subtract(5, 'day')}
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
msg='How are you?'
|
||||||
|
tcount={9999}
|
||||||
|
tlm={moment().subtract(30, 'day')}
|
||||||
|
/> */}
|
||||||
|
|
||||||
|
<Separator title='Discussion' />
|
||||||
|
<Message
|
||||||
|
type='discussion-created'
|
||||||
|
drid='aisduhasidhs'
|
||||||
|
dcount={null}
|
||||||
|
dlm={null}
|
||||||
|
msg='This is a discussion'
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
type='discussion-created'
|
||||||
|
drid='aisduhasidhs'
|
||||||
|
dcount={1}
|
||||||
|
dlm={date}
|
||||||
|
msg='This is a discussion'
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
type='discussion-created'
|
||||||
|
drid='aisduhasidhs'
|
||||||
|
dcount={10}
|
||||||
|
dlm={date}
|
||||||
|
msg={longText}
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
type='discussion-created'
|
||||||
|
drid='aisduhasidhs'
|
||||||
|
dcount={1000}
|
||||||
|
dlm={date}
|
||||||
|
msg='This is a discussion'
|
||||||
|
/>
|
||||||
|
{/* <Message
|
||||||
|
type='discussion-created'
|
||||||
|
drid='aisduhasidhs'
|
||||||
|
dcount={1000}
|
||||||
|
dlm={moment().subtract(1, 'hour')}
|
||||||
|
msg='This is a discussion'
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
type='discussion-created'
|
||||||
|
drid='aisduhasidhs'
|
||||||
|
dcount={1000}
|
||||||
|
dlm={moment().subtract(1, 'day')}
|
||||||
|
msg='This is a discussion'
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
type='discussion-created'
|
||||||
|
drid='aisduhasidhs'
|
||||||
|
dcount={1000}
|
||||||
|
dlm={moment().subtract(5, 'day')}
|
||||||
|
msg='This is a discussion'
|
||||||
|
/>
|
||||||
|
<Message
|
||||||
|
type='discussion-created'
|
||||||
|
drid='aisduhasidhs'
|
||||||
|
dcount={1000}
|
||||||
|
dlm={moment().subtract(30, 'day')}
|
||||||
|
msg='This is a discussion'
|
||||||
|
/> */}
|
||||||
|
|
||||||
|
|
||||||
<Separator title='URL' />
|
<Separator title='URL' />
|
||||||
<Message
|
<Message
|
||||||
urls={[{
|
urls={[{
|
||||||
|
@ -360,64 +483,6 @@ export default (
|
||||||
<Separator title='Broadcast' />
|
<Separator title='Broadcast' />
|
||||||
<Message msg='Broadcasted message' broadcast replyBroadcast={() => alert('broadcast!')} />
|
<Message msg='Broadcasted message' broadcast replyBroadcast={() => alert('broadcast!')} />
|
||||||
|
|
||||||
<Separator title='Discussion' />
|
|
||||||
<Message
|
|
||||||
type='discussion-created'
|
|
||||||
drid='aisduhasidhs'
|
|
||||||
dcount={null}
|
|
||||||
dlm={null}
|
|
||||||
msg='This is a discussion'
|
|
||||||
/>
|
|
||||||
<Message
|
|
||||||
type='discussion-created'
|
|
||||||
drid='aisduhasidhs'
|
|
||||||
dcount={1}
|
|
||||||
dlm={date}
|
|
||||||
msg='This is a discussion'
|
|
||||||
/>
|
|
||||||
<Message
|
|
||||||
type='discussion-created'
|
|
||||||
drid='aisduhasidhs'
|
|
||||||
dcount={10}
|
|
||||||
dlm={date}
|
|
||||||
msg='Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
|
|
||||||
/>
|
|
||||||
<Message
|
|
||||||
type='discussion-created'
|
|
||||||
drid='aisduhasidhs'
|
|
||||||
dcount={1000}
|
|
||||||
dlm={date}
|
|
||||||
msg='This is a discussion'
|
|
||||||
/>
|
|
||||||
{/* <Message
|
|
||||||
type='discussion-created'
|
|
||||||
drid='aisduhasidhs'
|
|
||||||
dcount={1000}
|
|
||||||
dlm={moment().subtract(1, 'hour')}
|
|
||||||
msg='This is a discussion'
|
|
||||||
/>
|
|
||||||
<Message
|
|
||||||
type='discussion-created'
|
|
||||||
drid='aisduhasidhs'
|
|
||||||
dcount={1000}
|
|
||||||
dlm={moment().subtract(1, 'day')}
|
|
||||||
msg='This is a discussion'
|
|
||||||
/>
|
|
||||||
<Message
|
|
||||||
type='discussion-created'
|
|
||||||
drid='aisduhasidhs'
|
|
||||||
dcount={1000}
|
|
||||||
dlm={moment().subtract(5, 'day')}
|
|
||||||
msg='This is a discussion'
|
|
||||||
/>
|
|
||||||
<Message
|
|
||||||
type='discussion-created'
|
|
||||||
drid='aisduhasidhs'
|
|
||||||
dcount={1000}
|
|
||||||
dlm={moment().subtract(30, 'day')}
|
|
||||||
msg='This is a discussion'
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
<Separator title='Archived' />
|
<Separator title='Archived' />
|
||||||
<Message msg='This message is inside an archived room' archived />
|
<Message msg='This message is inside an archived room' archived />
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,9 @@ const Header = props => (
|
||||||
height={480}
|
height={480}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
<CustomHeaderButtons>
|
||||||
|
<Item title='thread' iconName='thread' />
|
||||||
|
</CustomHeaderButtons>
|
||||||
<CustomHeaderButtons>
|
<CustomHeaderButtons>
|
||||||
<Item title='more' iconName='menu' />
|
<Item title='more' iconName='menu' />
|
||||||
</CustomHeaderButtons>
|
</CustomHeaderButtons>
|
||||||
|
@ -47,6 +50,7 @@ export default (
|
||||||
<Header type='c' />
|
<Header type='c' />
|
||||||
<Header type='p' />
|
<Header type='p' />
|
||||||
<Header type='discussion' />
|
<Header type='discussion' />
|
||||||
|
<Header type='thread' />
|
||||||
|
|
||||||
<StoriesSeparator title='Typing' />
|
<StoriesSeparator title='Typing' />
|
||||||
<Header usersTyping={[{ username: 'diego.mello' }]} />
|
<Header usersTyping={[{ username: 'diego.mello' }]} />
|
||||||
|
|
260
yarn.lock
260
yarn.lock
|
@ -1878,6 +1878,20 @@ arr-union@^3.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
|
resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
|
||||||
integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
|
integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
|
||||||
|
|
||||||
|
array-back@^1.0.3, array-back@^1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/array-back/-/array-back-1.0.4.tgz#644ba7f095f7ffcf7c43b5f0dc39d3c1f03c063b"
|
||||||
|
integrity sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=
|
||||||
|
dependencies:
|
||||||
|
typical "^2.6.0"
|
||||||
|
|
||||||
|
array-back@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/array-back/-/array-back-2.0.0.tgz#6877471d51ecc9c9bfa6136fb6c7d5fe69748022"
|
||||||
|
integrity sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==
|
||||||
|
dependencies:
|
||||||
|
typical "^2.6.1"
|
||||||
|
|
||||||
array-equal@^1.0.0:
|
array-equal@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
|
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
|
||||||
|
@ -3080,7 +3094,7 @@ binstring@^0.2.1:
|
||||||
resolved "https://registry.yarnpkg.com/binstring/-/binstring-0.2.1.tgz#8a174d301f6d54efda550dd98bb4cb524eacd75d"
|
resolved "https://registry.yarnpkg.com/binstring/-/binstring-0.2.1.tgz#8a174d301f6d54efda550dd98bb4cb524eacd75d"
|
||||||
integrity sha1-ihdNMB9tVO/aVQ3Zi7TLUk6s110=
|
integrity sha1-ihdNMB9tVO/aVQ3Zi7TLUk6s110=
|
||||||
|
|
||||||
bl@^1.2.1:
|
bl@^1.0.0, bl@^1.2.1:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
|
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
|
||||||
integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==
|
integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==
|
||||||
|
@ -3306,11 +3320,29 @@ bser@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
node-int64 "^0.4.0"
|
node-int64 "^0.4.0"
|
||||||
|
|
||||||
buffer-crc32@^0.2.13:
|
buffer-alloc-unsafe@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
|
||||||
|
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
|
||||||
|
|
||||||
|
buffer-alloc@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
|
||||||
|
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
|
||||||
|
dependencies:
|
||||||
|
buffer-alloc-unsafe "^1.1.0"
|
||||||
|
buffer-fill "^1.0.0"
|
||||||
|
|
||||||
|
buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
|
||||||
version "0.2.13"
|
version "0.2.13"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||||
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
||||||
|
|
||||||
|
buffer-fill@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
|
||||||
|
integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
|
||||||
|
|
||||||
buffer-from@^1.0.0:
|
buffer-from@^1.0.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||||
|
@ -3330,6 +3362,14 @@ buffer@^4.3.0:
|
||||||
ieee754 "^1.1.4"
|
ieee754 "^1.1.4"
|
||||||
isarray "^1.0.0"
|
isarray "^1.0.0"
|
||||||
|
|
||||||
|
buffer@^5.2.1:
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6"
|
||||||
|
integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.0.2"
|
||||||
|
ieee754 "^1.1.4"
|
||||||
|
|
||||||
builtin-modules@^1.0.0:
|
builtin-modules@^1.0.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
|
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
|
||||||
|
@ -3723,6 +3763,15 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
delayed-stream "~1.0.0"
|
delayed-stream "~1.0.0"
|
||||||
|
|
||||||
|
command-line-args@^4.0.6:
|
||||||
|
version "4.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-4.0.7.tgz#f8d1916ecb90e9e121eda6428e41300bfb64cc46"
|
||||||
|
integrity sha512-aUdPvQRAyBvQd2n7jXcsMDz68ckBJELXNzBybCHOibUWEg0mWTnaYCSRU8h9R+aNRSvDihJtssSRCiDRpLaezA==
|
||||||
|
dependencies:
|
||||||
|
array-back "^2.0.0"
|
||||||
|
find-replace "^1.0.3"
|
||||||
|
typical "^2.6.1"
|
||||||
|
|
||||||
commander@2.15.1:
|
commander@2.15.1:
|
||||||
version "2.15.1"
|
version "2.15.1"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
|
||||||
|
@ -3743,6 +3792,13 @@ commander@~2.13.0:
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
|
||||||
integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==
|
integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==
|
||||||
|
|
||||||
|
commander@~2.8.1:
|
||||||
|
version "2.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
|
||||||
|
integrity sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=
|
||||||
|
dependencies:
|
||||||
|
graceful-readlink ">= 1.0.0"
|
||||||
|
|
||||||
commist@^1.0.0:
|
commist@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/commist/-/commist-1.0.0.tgz#c0c352501cf6f52e9124e3ef89c9806e2022ebef"
|
resolved "https://registry.yarnpkg.com/commist/-/commist-1.0.0.tgz#c0c352501cf6f52e9124e3ef89c9806e2022ebef"
|
||||||
|
@ -4201,6 +4257,59 @@ decode-uri-component@^0.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||||
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
|
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
|
||||||
|
|
||||||
|
decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
|
||||||
|
integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==
|
||||||
|
dependencies:
|
||||||
|
file-type "^5.2.0"
|
||||||
|
is-stream "^1.1.0"
|
||||||
|
tar-stream "^1.5.2"
|
||||||
|
|
||||||
|
decompress-tarbz2@^4.0.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
|
||||||
|
integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==
|
||||||
|
dependencies:
|
||||||
|
decompress-tar "^4.1.0"
|
||||||
|
file-type "^6.1.0"
|
||||||
|
is-stream "^1.1.0"
|
||||||
|
seek-bzip "^1.0.5"
|
||||||
|
unbzip2-stream "^1.0.9"
|
||||||
|
|
||||||
|
decompress-targz@^4.0.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
|
||||||
|
integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==
|
||||||
|
dependencies:
|
||||||
|
decompress-tar "^4.1.1"
|
||||||
|
file-type "^5.2.0"
|
||||||
|
is-stream "^1.1.0"
|
||||||
|
|
||||||
|
decompress-unzip@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
|
||||||
|
integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k=
|
||||||
|
dependencies:
|
||||||
|
file-type "^3.8.0"
|
||||||
|
get-stream "^2.2.0"
|
||||||
|
pify "^2.3.0"
|
||||||
|
yauzl "^2.4.2"
|
||||||
|
|
||||||
|
decompress@^4.2.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d"
|
||||||
|
integrity sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=
|
||||||
|
dependencies:
|
||||||
|
decompress-tar "^4.0.0"
|
||||||
|
decompress-tarbz2 "^4.0.0"
|
||||||
|
decompress-targz "^4.0.0"
|
||||||
|
decompress-unzip "^4.0.1"
|
||||||
|
graceful-fs "^4.1.10"
|
||||||
|
make-dir "^1.0.0"
|
||||||
|
pify "^2.3.0"
|
||||||
|
strip-dirs "^2.0.0"
|
||||||
|
|
||||||
deep-equal@^1.0.1:
|
deep-equal@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
|
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
|
||||||
|
@ -5334,6 +5443,13 @@ fbjs@^1.0.0:
|
||||||
setimmediate "^1.0.5"
|
setimmediate "^1.0.5"
|
||||||
ua-parser-js "^0.7.18"
|
ua-parser-js "^0.7.18"
|
||||||
|
|
||||||
|
fd-slicer@~1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||||
|
integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
|
||||||
|
dependencies:
|
||||||
|
pend "~1.2.0"
|
||||||
|
|
||||||
figgy-pudding@^3.5.1:
|
figgy-pudding@^3.5.1:
|
||||||
version "3.5.1"
|
version "3.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
|
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
|
||||||
|
@ -5379,6 +5495,21 @@ file-system-cache@^1.0.5:
|
||||||
fs-extra "^0.30.0"
|
fs-extra "^0.30.0"
|
||||||
ramda "^0.21.0"
|
ramda "^0.21.0"
|
||||||
|
|
||||||
|
file-type@^3.8.0:
|
||||||
|
version "3.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
|
||||||
|
integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek=
|
||||||
|
|
||||||
|
file-type@^5.2.0:
|
||||||
|
version "5.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
|
||||||
|
integrity sha1-LdvqfHP/42No365J3DOMBYwritY=
|
||||||
|
|
||||||
|
file-type@^6.1.0:
|
||||||
|
version "6.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
|
||||||
|
integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==
|
||||||
|
|
||||||
file-uri-to-path@1:
|
file-uri-to-path@1:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
||||||
|
@ -5467,6 +5598,14 @@ find-cache-dir@^2.0.0:
|
||||||
make-dir "^1.0.0"
|
make-dir "^1.0.0"
|
||||||
pkg-dir "^3.0.0"
|
pkg-dir "^3.0.0"
|
||||||
|
|
||||||
|
find-replace@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-1.0.3.tgz#b88e7364d2d9c959559f388c66670d6130441fa0"
|
||||||
|
integrity sha1-uI5zZNLZyVlVnziMZmcNYTBEH6A=
|
||||||
|
dependencies:
|
||||||
|
array-back "^1.0.4"
|
||||||
|
test-value "^2.1.0"
|
||||||
|
|
||||||
find-up@3.0.0, find-up@^3.0.0:
|
find-up@3.0.0, find-up@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
|
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
|
||||||
|
@ -5577,6 +5716,11 @@ from2@^2.1.0:
|
||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
readable-stream "^2.0.0"
|
readable-stream "^2.0.0"
|
||||||
|
|
||||||
|
fs-constants@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||||
|
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||||
|
|
||||||
fs-extra@^0.30.0:
|
fs-extra@^0.30.0:
|
||||||
version "0.30.0"
|
version "0.30.0"
|
||||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0"
|
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0"
|
||||||
|
@ -5597,7 +5741,7 @@ fs-extra@^1.0.0:
|
||||||
jsonfile "^2.1.0"
|
jsonfile "^2.1.0"
|
||||||
klaw "^1.0.0"
|
klaw "^1.0.0"
|
||||||
|
|
||||||
fs-extra@^4.0.2:
|
fs-extra@^4.0.2, fs-extra@^4.0.3:
|
||||||
version "4.0.3"
|
version "4.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
|
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
|
||||||
integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==
|
integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==
|
||||||
|
@ -5714,6 +5858,14 @@ get-port@^2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
pinkie-promise "^2.0.0"
|
pinkie-promise "^2.0.0"
|
||||||
|
|
||||||
|
get-stream@^2.2.0:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
|
||||||
|
integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=
|
||||||
|
dependencies:
|
||||||
|
object-assign "^4.0.1"
|
||||||
|
pinkie-promise "^2.0.0"
|
||||||
|
|
||||||
get-stream@^3.0.0:
|
get-stream@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
|
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
|
||||||
|
@ -5928,11 +6080,16 @@ got@^6.7.1:
|
||||||
unzip-response "^2.0.1"
|
unzip-response "^2.0.1"
|
||||||
url-parse-lax "^1.0.0"
|
url-parse-lax "^1.0.0"
|
||||||
|
|
||||||
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
|
graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
|
||||||
version "4.1.15"
|
version "4.1.15"
|
||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||||
|
|
||||||
|
"graceful-readlink@>= 1.0.0":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
|
||||||
|
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
|
||||||
|
|
||||||
graphlib@^2.1.1, graphlib@^2.1.5:
|
graphlib@^2.1.1, graphlib@^2.1.5:
|
||||||
version "2.1.7"
|
version "2.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.7.tgz#b6a69f9f44bd9de3963ce6804a2fc9e73d86aecc"
|
resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.7.tgz#b6a69f9f44bd9de3963ce6804a2fc9e73d86aecc"
|
||||||
|
@ -6423,7 +6580,7 @@ inherits@2.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
||||||
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
|
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
|
||||||
|
|
||||||
ini@^1.3.0, ini@^1.3.4, ini@~1.3.0:
|
ini@^1.3.0, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
|
||||||
version "1.3.5"
|
version "1.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||||
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
|
||||||
|
@ -6720,6 +6877,11 @@ is-installed-globally@^0.1.0:
|
||||||
global-dirs "^0.1.0"
|
global-dirs "^0.1.0"
|
||||||
is-path-inside "^1.0.0"
|
is-path-inside "^1.0.0"
|
||||||
|
|
||||||
|
is-natural-number@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
|
||||||
|
integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=
|
||||||
|
|
||||||
is-negated-glob@^1.0.0:
|
is-negated-glob@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2"
|
resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2"
|
||||||
|
@ -9425,12 +9587,17 @@ pbkdf2@^3.0.3:
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
sha.js "^2.4.8"
|
sha.js "^2.4.8"
|
||||||
|
|
||||||
|
pend@~1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||||
|
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
|
||||||
|
|
||||||
performance-now@^2.1.0:
|
performance-now@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||||
|
|
||||||
pify@^2.0.0:
|
pify@^2.0.0, pify@^2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||||
integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
|
integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
|
||||||
|
@ -9680,7 +9847,7 @@ process@~0.5.1:
|
||||||
resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf"
|
resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf"
|
||||||
integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=
|
integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=
|
||||||
|
|
||||||
progress@^2.0.0:
|
progress@^2.0.0, progress@^2.0.3:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||||
|
@ -10646,7 +10813,7 @@ read-pkg@^3.0.0:
|
||||||
normalize-package-data "^2.3.2"
|
normalize-package-data "^2.3.2"
|
||||||
path-type "^3.0.0"
|
path-type "^3.0.0"
|
||||||
|
|
||||||
"readable-stream@1 || 2", readable-stream@2, "readable-stream@> 1.0.0 < 3.0.0", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6:
|
"readable-stream@1 || 2", readable-stream@2, "readable-stream@> 1.0.0 < 3.0.0", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6:
|
||||||
version "2.3.6"
|
version "2.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||||
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
|
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
|
||||||
|
@ -10709,16 +10876,22 @@ readdirp@^2.0.0:
|
||||||
micromatch "^3.1.10"
|
micromatch "^3.1.10"
|
||||||
readable-stream "^2.0.2"
|
readable-stream "^2.0.2"
|
||||||
|
|
||||||
realm@2.24:
|
realm@2.26.1:
|
||||||
version "2.24.0"
|
version "2.26.1"
|
||||||
resolved "https://registry.yarnpkg.com/realm/-/realm-2.24.0.tgz#4c804bed23360b7d4f23964e708d142608f7a335"
|
resolved "https://registry.yarnpkg.com/realm/-/realm-2.26.1.tgz#9d890c85c4d0946bef0a3ece736551c6a8a5dc49"
|
||||||
integrity sha512-pIeZNoUfqrfo9WRdP3PtVVjh2aEde9l6T5tReN4as9MLn9sC/17tppatrl5S3gfakxvNQH3uJ9FdYI7lS6EspQ==
|
integrity sha512-kkDOMV5vgaPOYgTELHFPws9suEF0LI/kSb8SIZ615STKHLHLiRxioxgBcu5beO5HVkjxe5jYx7duSB3NASr+AA==
|
||||||
dependencies:
|
dependencies:
|
||||||
|
command-line-args "^4.0.6"
|
||||||
|
decompress "^4.2.0"
|
||||||
deepmerge "2.1.0"
|
deepmerge "2.1.0"
|
||||||
|
fs-extra "^4.0.3"
|
||||||
|
https-proxy-agent "^2.2.1"
|
||||||
|
ini "^1.3.5"
|
||||||
nan "^2.12.1"
|
nan "^2.12.1"
|
||||||
node-fetch "^1.7.3"
|
node-fetch "^1.7.3"
|
||||||
node-machine-id "^1.1.10"
|
node-machine-id "^1.1.10"
|
||||||
node-pre-gyp "^0.11.0"
|
node-pre-gyp "^0.11.0"
|
||||||
|
progress "^2.0.3"
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
request "^2.88.0"
|
request "^2.88.0"
|
||||||
stream-counter "^1.0.0"
|
stream-counter "^1.0.0"
|
||||||
|
@ -11288,6 +11461,13 @@ secure-keys@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/secure-keys/-/secure-keys-1.0.0.tgz#f0c82d98a3b139a8776a8808050b824431087fca"
|
resolved "https://registry.yarnpkg.com/secure-keys/-/secure-keys-1.0.0.tgz#f0c82d98a3b139a8776a8808050b824431087fca"
|
||||||
integrity sha1-8MgtmKOxOah3aogIBQuCRDEIf8o=
|
integrity sha1-8MgtmKOxOah3aogIBQuCRDEIf8o=
|
||||||
|
|
||||||
|
seek-bzip@^1.0.5:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc"
|
||||||
|
integrity sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=
|
||||||
|
dependencies:
|
||||||
|
commander "~2.8.1"
|
||||||
|
|
||||||
semver-diff@^2.0.0:
|
semver-diff@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
|
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
|
||||||
|
@ -12087,6 +12267,13 @@ strip-bom@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-utf8 "^0.2.0"
|
is-utf8 "^0.2.0"
|
||||||
|
|
||||||
|
strip-dirs@^2.0.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
|
||||||
|
integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==
|
||||||
|
dependencies:
|
||||||
|
is-natural-number "^4.0.1"
|
||||||
|
|
||||||
strip-eof@^1.0.0:
|
strip-eof@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
|
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
|
||||||
|
@ -12192,6 +12379,19 @@ tapable@^1.0.0, tapable@^1.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.1.tgz#4d297923c5a72a42360de2ab52dadfaaec00018e"
|
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.1.tgz#4d297923c5a72a42360de2ab52dadfaaec00018e"
|
||||||
integrity sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==
|
integrity sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==
|
||||||
|
|
||||||
|
tar-stream@^1.5.2:
|
||||||
|
version "1.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
|
||||||
|
integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
|
||||||
|
dependencies:
|
||||||
|
bl "^1.0.0"
|
||||||
|
buffer-alloc "^1.2.0"
|
||||||
|
end-of-stream "^1.0.0"
|
||||||
|
fs-constants "^1.0.0"
|
||||||
|
readable-stream "^2.3.0"
|
||||||
|
to-buffer "^1.1.1"
|
||||||
|
xtend "^4.0.0"
|
||||||
|
|
||||||
tar@^4:
|
tar@^4:
|
||||||
version "4.4.8"
|
version "4.4.8"
|
||||||
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
|
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
|
||||||
|
@ -12274,6 +12474,14 @@ test-exclude@^4.2.1:
|
||||||
read-pkg-up "^1.0.1"
|
read-pkg-up "^1.0.1"
|
||||||
require-main-filename "^1.0.1"
|
require-main-filename "^1.0.1"
|
||||||
|
|
||||||
|
test-value@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/test-value/-/test-value-2.1.0.tgz#11da6ff670f3471a73b625ca4f3fdcf7bb748291"
|
||||||
|
integrity sha1-Edpv9nDzRxpztiXKTz/c97t0gpE=
|
||||||
|
dependencies:
|
||||||
|
array-back "^1.0.3"
|
||||||
|
typical "^2.6.0"
|
||||||
|
|
||||||
text-table@0.2.0, text-table@^0.2.0:
|
text-table@0.2.0, text-table@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||||
|
@ -12324,7 +12532,7 @@ through2@^2.0.0, through2@^2.0.1, through2@^2.0.2, through2@~2.0.0:
|
||||||
readable-stream "~2.3.6"
|
readable-stream "~2.3.6"
|
||||||
xtend "~4.0.1"
|
xtend "~4.0.1"
|
||||||
|
|
||||||
through@^2.3.6:
|
through@^2.3.6, through@^2.3.8:
|
||||||
version "2.3.8"
|
version "2.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||||
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||||
|
@ -12381,6 +12589,11 @@ to-arraybuffer@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
|
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
|
||||||
integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
|
integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
|
||||||
|
|
||||||
|
to-buffer@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
|
||||||
|
integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
|
||||||
|
|
||||||
to-fast-properties@^1.0.3:
|
to-fast-properties@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
|
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
|
||||||
|
@ -12514,6 +12727,11 @@ typedarray@^0.0.6:
|
||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||||
|
|
||||||
|
typical@^2.6.0, typical@^2.6.1:
|
||||||
|
version "2.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d"
|
||||||
|
integrity sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=
|
||||||
|
|
||||||
ua-parser-js@^0.7.18:
|
ua-parser-js@^0.7.18:
|
||||||
version "0.7.19"
|
version "0.7.19"
|
||||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
|
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
|
||||||
|
@ -12557,6 +12775,14 @@ ultron@~1.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
|
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
|
||||||
integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==
|
integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==
|
||||||
|
|
||||||
|
unbzip2-stream@^1.0.9:
|
||||||
|
version "1.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a"
|
||||||
|
integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==
|
||||||
|
dependencies:
|
||||||
|
buffer "^5.2.1"
|
||||||
|
through "^2.3.8"
|
||||||
|
|
||||||
unc-path-regex@^0.1.2:
|
unc-path-regex@^0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
|
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
|
||||||
|
@ -13345,3 +13571,11 @@ yargs@^9.0.0:
|
||||||
which-module "^2.0.0"
|
which-module "^2.0.0"
|
||||||
y18n "^3.2.1"
|
y18n "^3.2.1"
|
||||||
yargs-parser "^7.0.0"
|
yargs-parser "^7.0.0"
|
||||||
|
|
||||||
|
yauzl@^2.4.2:
|
||||||
|
version "2.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||||
|
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
|
||||||
|
dependencies:
|
||||||
|
buffer-crc32 "~0.2.3"
|
||||||
|
fd-slicer "~1.1.0"
|
||||||
|
|
Loading…
Reference in New Issue