diff --git a/.eslintrc.js b/.eslintrc.js index cc1c926e7..933fb9646 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -37,6 +37,7 @@ module.exports = { "react/no-unused-prop-types": [2, { "skipShapeProps": true }], + "react/no-did-mount-set-state": 0, "react/no-multi-comp": [0], "react/jsx-indent": [2, "tab"], "react/jsx-indent-props": [2, "tab"], diff --git a/__tests__/__snapshots__/RoomItem.js.snap b/__tests__/__snapshots__/RoomItem.js.snap index 4bb3753e8..dbbdb23c1 100644 --- a/__tests__/__snapshots__/RoomItem.js.snap +++ b/__tests__/__snapshots__/RoomItem.js.snap @@ -136,12 +136,15 @@ exports[`render channel 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -300,12 +303,15 @@ exports[`render no icon 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -464,12 +470,15 @@ exports[`render private group 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -640,12 +649,15 @@ exports[`render unread +999 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -838,12 +850,15 @@ exports[`render unread 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -1036,12 +1051,15 @@ exports[`renders correctly 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap index 185c7371d..6c77fd294 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -299,12 +299,15 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -471,12 +474,17 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + Object { + "color": "#1d74f5", + }, + ] } > Nov 10 @@ -641,12 +649,15 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -836,12 +847,17 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + Object { + "color": "#1d74f5", + }, + ] } > Nov 10 @@ -1029,12 +1045,15 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -1222,12 +1241,15 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -1415,12 +1437,15 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -1608,12 +1633,15 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -1801,12 +1829,15 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -1971,12 +2002,15 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 @@ -2141,12 +2175,15 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` ellipsizeMode="tail" numberOfLines={1} style={ - Object { - "alignItems": "center", - "color": "#888", - "fontSize": 10, - "justifyContent": "center", - } + Array [ + Object { + "alignItems": "center", + "color": "#888", + "fontSize": 10, + "justifyContent": "center", + }, + undefined, + ] } > Nov 10 diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index 8546f7f41..aa3ec69bc 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -93,8 +93,9 @@ export const SERVER = createRequestTypes('SERVER', [ export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT', 'DISCONNECT_BY_USER']); export const LOGOUT = 'LOGOUT'; // logout is always success export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET', 'REQUEST']); -export const STARRED_MESSAGES = createRequestTypes('STARRED_MESSAGES', ['OPEN', 'CLOSE', 'MESSAGE_RECEIVED', 'MESSAGE_UNSTARRED']); -export const PINNED_MESSAGES = createRequestTypes('PINNED_MESSAGES', ['OPEN', 'CLOSE', 'MESSAGE_RECEIVED', 'MESSAGE_UNPINNED']); +export const STARRED_MESSAGES = createRequestTypes('STARRED_MESSAGES', ['OPEN', 'CLOSE', 'MESSAGES_RECEIVED', 'MESSAGE_UNSTARRED']); +export const PINNED_MESSAGES = createRequestTypes('PINNED_MESSAGES', ['OPEN', 'CLOSE', 'MESSAGES_RECEIVED', 'MESSAGE_UNPINNED']); +export const MENTIONED_MESSAGES = createRequestTypes('MENTIONED_MESSAGES', ['OPEN', 'CLOSE', 'MESSAGES_RECEIVED']); export const INCREMENT = 'INCREMENT'; export const DECREMENT = 'DECREMENT'; diff --git a/app/actions/mentionedMessages.js b/app/actions/mentionedMessages.js new file mode 100644 index 000000000..5c938bbce --- /dev/null +++ b/app/actions/mentionedMessages.js @@ -0,0 +1,21 @@ +import * as types from './actionsTypes'; + +export function openMentionedMessages(rid) { + return { + type: types.MENTIONED_MESSAGES.OPEN, + rid + }; +} + +export function closeMentionedMessages() { + return { + type: types.MENTIONED_MESSAGES.CLOSE + }; +} + +export function mentionedMessagesReceived(messages) { + return { + type: types.MENTIONED_MESSAGES.MESSAGES_RECEIVED, + messages + }; +} diff --git a/app/actions/pinnedMessages.js b/app/actions/pinnedMessages.js index a8a8f228e..e344b441f 100644 --- a/app/actions/pinnedMessages.js +++ b/app/actions/pinnedMessages.js @@ -13,10 +13,10 @@ export function closePinnedMessages() { }; } -export function pinnedMessageReceived(message) { +export function pinnedMessagesReceived(messages) { return { - type: types.PINNED_MESSAGES.MESSAGE_RECEIVED, - message + type: types.PINNED_MESSAGES.MESSAGES_RECEIVED, + messages }; } diff --git a/app/actions/starredMessages.js b/app/actions/starredMessages.js index d050d84c1..36b701a86 100644 --- a/app/actions/starredMessages.js +++ b/app/actions/starredMessages.js @@ -13,10 +13,10 @@ export function closeStarredMessages() { }; } -export function starredMessageReceived(message) { +export function starredMessagesReceived(messages) { return { - type: types.STARRED_MESSAGES.MESSAGE_RECEIVED, - message + type: types.STARRED_MESSAGES.MESSAGES_RECEIVED, + messages }; } diff --git a/app/containers/message/Reply.js b/app/containers/message/Reply.js index e5b2a1d58..f6df68380 100644 --- a/app/containers/message/Reply.js +++ b/app/containers/message/Reply.js @@ -143,7 +143,7 @@ const Reply = ({ attachment, timeFormat }) => { {renderTitle()} {renderText()} {renderFields()} - {attachment.attachments.map(attach => )} + {attachment.attachments && attachment.attachments.map(attach => )} ); diff --git a/app/containers/message/index.js b/app/containers/message/index.js index e5d85e905..7e95216b7 100644 --- a/app/containers/message/index.js +++ b/app/containers/message/index.js @@ -4,7 +4,7 @@ import { View, TouchableHighlight, Text, TouchableOpacity, Vibration, ViewPropTy import { connect } from 'react-redux'; import Icon from 'react-native-vector-icons/MaterialIcons'; import moment from 'moment'; -import equal from 'deep-equal'; +// import equal from 'deep-equal'; import { KeyboardUtils } from 'react-native-keyboard-input'; import { actionsShow, errorActionsShow, toggleReactionPicker } from '../../actions/messages'; @@ -52,22 +52,16 @@ export default class Message extends React.Component { this.state = { reactionsModal: false }; this.onClose = this.onClose.bind(this); } - componentWillReceiveProps() { - this.extraStyle = this.extraStyle || {}; - if (this.props.item.status === messageStatus.TEMP || this.props.item.status === messageStatus.ERROR) { - this.extraStyle.opacity = 0.3; - } - } - shouldComponentUpdate(nextProps, nextState) { - if (!equal(this.props.reactions, nextProps.reactions)) { - return true; - } - if (this.state.reactionsModal !== nextState.reactionsModal) { - return true; - } - return this.props.item._updatedAt.toGMTString() !== nextProps.item._updatedAt.toGMTString() || this.props.item.status !== nextProps.item.status; - } + // shouldComponentUpdate(nextProps, nextState) { + // if (!equal(this.props.reactions, nextProps.reactions)) { + // return true; + // } + // if (this.state.reactionsModal !== nextState.reactionsModal) { + // return true; + // } + // return this.props.item._updatedAt.toGMTString() !== nextProps.item._updatedAt.toGMTString() || this.props.item.status !== nextProps.item.status; + // } onPress = () => { KeyboardUtils.dismiss(); @@ -129,6 +123,10 @@ export default class Message extends React.Component { return this.props.item.t === 'rm'; } + isTemp() { + return this.props.item.status === messageStatus.TEMP || this.props.item.status === messageStatus.ERROR; + } + hasError() { return this.props.item.status === messageStatus.ERROR; } @@ -241,7 +239,7 @@ export default class Message extends React.Component { > {this.renderError()} - + { if (ddpMessage.msg === 'added') { + this.starredMessages = this.starredMessages || []; + + if (this.starredMessagesTimer) { + clearTimeout(this.starredMessagesTimer); + this.starredMessagesTimer = null; + } + + this.starredMessagesTimer = setTimeout(() => { + reduxStore.dispatch(starredMessagesReceived(this.starredMessages)); + this.starredMessagesTimer = null; + return this.starredMessages = []; + }, 1000); const message = ddpMessage.fields; message._id = ddpMessage.id; const starredMessage = this._buildMessage(message); - return reduxStore.dispatch(starredMessageReceived(starredMessage)); + this.starredMessages = [...this.starredMessages, starredMessage]; } if (ddpMessage.msg === 'removed') { - return reduxStore.dispatch(starredMessageUnstarred(ddpMessage.id)); + if (reduxStore.getState().starredMessages.isOpen) { + return reduxStore.dispatch(starredMessageUnstarred(ddpMessage.id)); + } } }); this.ddp.on('rocketchat_pinned_message', (ddpMessage) => { if (ddpMessage.msg === 'added') { + this.pinnedMessages = this.pinnedMessages || []; + + if (this.pinnedMessagesTimer) { + clearTimeout(this.pinnedMessagesTimer); + this.pinnedMessagesTimer = null; + } + + this.pinnedMessagesTimer = setTimeout(() => { + reduxStore.dispatch(pinnedMessagesReceived(this.pinnedMessages)); + this.pinnedMessagesTimer = null; + return this.pinnedMessages = []; + }, 1000); const message = ddpMessage.fields; message._id = ddpMessage.id; const pinnedMessage = this._buildMessage(message); - return reduxStore.dispatch(pinnedMessageReceived(pinnedMessage)); + this.pinnedMessages = [...this.pinnedMessages, pinnedMessage]; } if (ddpMessage.msg === 'removed') { - return reduxStore.dispatch(pinnedMessageUnpinned(ddpMessage.id)); + if (reduxStore.getState().pinnedMessages.isOpen) { + return reduxStore.dispatch(pinnedMessageUnpinned(ddpMessage.id)); + } + } + }); + + this.ddp.on('rocketchat_mentioned_message', (ddpMessage) => { + if (ddpMessage.msg === 'added') { + this.mentionedMessages = this.mentionedMessages || []; + + if (this.mentionedMessagesTimer) { + clearTimeout(this.mentionedMessagesTimer); + this.mentionedMessagesTimer = null; + } + + this.mentionedMessagesTimer = setTimeout(() => { + reduxStore.dispatch(mentionedMessagesReceived(this.mentionedMessages)); + this.mentionedMessagesTimer = null; + return this.mentionedMessages = []; + }, 1000); + const message = ddpMessage.fields; + message._id = ddpMessage.id; + const mentionedMessage = this._buildMessage(message); + this.mentionedMessages = [...this.mentionedMessages, mentionedMessage]; } }); @@ -324,6 +374,7 @@ const RocketChat = { message.status = messagesStatus.SENT; normalizeMessage(message); message.urls = message.urls ? RocketChat._parseUrls(message.urls) : []; + message._updatedAt = new Date(); // loadHistory returns message.starred as object // stream-room-messages returns message.starred as an array message.starred = message.starred && (Array.isArray(message.starred) ? message.starred.length > 0 : !!message.starred); @@ -497,6 +548,9 @@ const RocketChat = { subscription.roomUpdatedAt = room._updatedAt; subscription.lastMessage = normalizeMessage(room.lastMessage); subscription.ro = room.ro; + subscription.description = room.description; + subscription.topic = room.topic; + subscription.announcement = room.announcement; } if (subscription.roles) { subscription.roles = subscription.roles.map(role => ({ value: role })); @@ -667,6 +721,9 @@ const RocketChat = { }, toggleFavorite(rid, f) { return call('toggleFavorite', rid, !f); + }, + getRoomMembers(rid, allUsers) { + return call('getUsersOfRoom', rid, allUsers); } }; diff --git a/app/presentation/RoomItem.js b/app/presentation/RoomItem.js index 946796b7d..16677bb1d 100644 --- a/app/presentation/RoomItem.js +++ b/app/presentation/RoomItem.js @@ -83,6 +83,9 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center' }, + updateAlert: { + color: '#1d74f5' + }, status: { position: 'absolute', bottom: -3, @@ -173,7 +176,7 @@ export default class RoomItem extends React.PureComponent { get lastMessage() { const { - lastMessage, alert, type + lastMessage, type } = this.props; if (!this.props.StoreLastMessage) { @@ -183,7 +186,6 @@ export default class RoomItem extends React.PureComponent { return 'No Message'; } - let prefix = ''; if (lastMessage.u.username === this.props.user.username) { @@ -193,13 +195,7 @@ export default class RoomItem extends React.PureComponent { } const msg = `${ prefix }${ lastMessage.msg.replace(/[\n\t\r]/igm, '') }`; - const maxChars = 35; - - - if (alert) { - return `**${ msg.slice(0, maxChars) }${ msg.replace(/:[a-z0-9]+:/gi, ':::').length > maxChars ? '...' : '' }**`; - } return `${ msg.slice(0, maxChars) }${ msg.replace(/:[a-z0-9]+:/gi, ':::').length > maxChars ? '...' : '' }`; } @@ -237,7 +233,7 @@ export default class RoomItem extends React.PureComponent { { name } - {_updatedAt ? { date } : null} + {_updatedAt ? { date } : null} alert(e)); +}; + +const root = function* root() { + yield takeLatest(types.MENTIONED_MESSAGES.OPEN, watchMentionedMessagesRoom); +}; +export default root; diff --git a/app/views/MentionedMessagesView/index.js b/app/views/MentionedMessagesView/index.js new file mode 100644 index 000000000..92ff90283 --- /dev/null +++ b/app/views/MentionedMessagesView/index.js @@ -0,0 +1,71 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FlatList, Text, View } from 'react-native'; +import { connect } from 'react-redux'; + +import { openMentionedMessages, closeMentionedMessages } from '../../actions/mentionedMessages'; +import styles from './styles'; +import Message from '../../containers/message'; + +@connect( + state => ({ + messages: state.mentionedMessages.messages, + user: state.login.user, + baseUrl: state.settings.Site_Url || state.server ? state.server.server : '' + }), + dispatch => ({ + openMentionedMessages: rid => dispatch(openMentionedMessages(rid)), + closeMentionedMessages: () => dispatch(closeMentionedMessages()) + }) +) +export default class MentionedMessagesView extends React.PureComponent { + static propTypes = { + navigation: PropTypes.object, + messages: PropTypes.array, + user: PropTypes.object, + baseUrl: PropTypes.string, + openMentionedMessages: PropTypes.func, + closeMentionedMessages: PropTypes.func + } + + componentDidMount() { + this.props.openMentionedMessages(this.props.navigation.state.params.rid); + } + + componentWillUnmount() { + this.props.closeMentionedMessages(); + } + + renderEmpty = () => ( + + No mentioned messages + + ) + + renderItem = ({ item }) => ( + {}} + /> + ) + + render() { + if (this.props.messages.length === 0) { + return this.renderEmpty(); + } + return ( + item._id} + /> + ); + } +} diff --git a/app/views/MentionedMessagesView/styles.js b/app/views/MentionedMessagesView/styles.js new file mode 100644 index 000000000..33a5e8d11 --- /dev/null +++ b/app/views/MentionedMessagesView/styles.js @@ -0,0 +1,17 @@ +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + list: { + flex: 1, + backgroundColor: '#ffffff' + }, + message: { + transform: [{ scaleY: 1 }] + }, + listEmptyContainer: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#ffffff' + } +}); diff --git a/app/views/PinnedMessagesView/index.js b/app/views/PinnedMessagesView/index.js index d737b3af7..eab5bff61 100644 --- a/app/views/PinnedMessagesView/index.js +++ b/app/views/PinnedMessagesView/index.js @@ -43,7 +43,7 @@ export default class PinnedMessagesView extends React.PureComponent { }; } - componentWillMount() { + componentDidMount() { this.props.openPinnedMessages(this.props.navigation.state.params.rid); } diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js index 098bf3469..287c98b10 100644 --- a/app/views/RoomActionsView/index.js +++ b/app/views/RoomActionsView/index.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { View, SectionList, Text, StyleSheet } from 'react-native'; +import { View, SectionList, Text } from 'react-native'; import Icon from 'react-native-vector-icons/Ionicons'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import { connect } from 'react-redux'; @@ -9,6 +9,7 @@ import styles from './styles'; import Avatar from '../../containers/Avatar'; import Touch from '../../utils/touch'; import database from '../../lib/realm'; +import RocketChat from '../../lib/rocketchat'; @connect(state => ({ baseUrl: state.settings.Site_Url || state.server ? state.server.server : '' @@ -29,25 +30,19 @@ export default class RoomActionsView extends React.PureComponent { }; } - componentWillMount() { + componentDidMount() { this.updateRoom(); this.updateSections(); - } - - componentDidMount() { this.rooms.addListener(this.updateRoom); } updateRoom = () => { const [room] = this.rooms; this.setState({ room }); - this.props.navigation.setParams({ - f: room.f - }); this.updateSections(); } - updateSections = () => { + updateSections = async() => { const { rid, t } = this.state.room; const sections = [{ data: [{ icon: 'ios-star', name: 'USER' }], @@ -61,7 +56,12 @@ export default class RoomActionsView extends React.PureComponent { }, { data: [ { icon: 'ios-attach', name: 'Files' }, - { icon: 'ios-at-outline', name: 'Mentions' }, + { + icon: 'ios-at-outline', + name: 'Mentions', + route: 'MentionedMessages', + params: { rid } + }, { icon: 'ios-star-outline', name: 'Starred', @@ -90,7 +90,16 @@ export default class RoomActionsView extends React.PureComponent { renderItem: this.renderItem }); } else if (t === 'c' || t === 'p') { - sections[2].data.unshift({ icon: 'ios-people', name: 'Members', description: '42 members' }); + const membersResult = await RocketChat.getRoomMembers(rid, false); + const members = membersResult.records; + + sections[2].data.unshift({ + icon: 'ios-people', + name: 'Members', + description: (members.length === 1 ? `${ members.length } member` : `${ members.length } members`), + route: 'RoomMembers', + params: { rid, members } + }); sections.push({ data: [ { icon: 'ios-volume-off', name: 'Mute channel' }, @@ -102,21 +111,28 @@ export default class RoomActionsView extends React.PureComponent { this.setState({ sections }); } - renderRoomInfo = ({ item }) => this.renderTouchableItem([ - , - - {this.state.room.fname} - @{this.state.room.name} - , - - ], item) + renderRoomInfo = ({ item }) => { + const { + fname, name, t, topic + } = this.state.room; + return ( + this.renderTouchableItem([ + , + + {t === 'd' ? fname : name} + {t === 'd' ? `@${ name }` : topic} + , + + ], item) + ); + } renderTouchableItem = (subview, item) => ( ({ + user: state.login.user, + baseUrl: state.settings.Site_Url || state.server ? state.server.server : '' +})) +export default class MentionedMessagesView extends React.PureComponent { + static propTypes = { + navigation: PropTypes.object + } + + static navigationOptions = ({ navigation }) => { + const params = navigation.state.params || {}; + const label = params.allUsers ? 'All' : 'Online'; + if (params.allUsers === undefined) { + return; + } + return { + headerRight: ( + + + {label} + + + ) + }; + }; + + constructor(props) { + super(props); + const { rid, members } = props.navigation.state.params; + this.state = { + allUsers: false, + filtering: false, + rid, + members, + membersFiltered: [] + }; + } + + componentWillMount() { + this.props.navigation.setParams({ + onPressToogleStatus: this.onPressToogleStatus, + allUsers: this.state.allUsers + }); + } + + onSearchChangeText = (text) => { + let membersFiltered = []; + if (text) { + membersFiltered = this.state.members.filter(m => m.username.toLowerCase().match(text.toLowerCase())); + } + this.setState({ filtering: !!text, membersFiltered }); + } + + onPressToogleStatus = async() => { + const allUsers = !this.state.allUsers; + this.props.navigation.setParams({ allUsers }); + const membersResult = await RocketChat.getRoomMembers(this.state.rid, allUsers); + const members = membersResult.records; + this.setState({ allUsers, members }); + } + + onPressItem = async(item) => { + const subscriptions = database.objects('subscriptions').filtered('name = $0', item.username); + if (subscriptions.length) { + goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name }); + } else { + const room = await RocketChat.createDirectMessage(item.username); + goRoom({ room: room.rid, name: item.username }); + } + } + + renderSearchBar = () => ( + + this.onSearchChangeText(text)} + returnKeyType='search' + placeholder='Search' + clearButtonMode='while-editing' + blurOnSubmit + /> + + ) + + renderSeparator = () => ; + + renderItem = ({ item }) => ( + this.onPressItem(item)} + underlayColor='#ffffff' + activeOpacity={0.5} + accessibilityLabel={`Start a conversation with ${ item.username }`} + accessibilityTraits='button' + > + + {} + {item.username} + + + ) + + render() { + const { filtering, members, membersFiltered } = this.state; + return ( + item._id} + ItemSeparatorComponent={this.renderSeparator} + ListHeaderComponent={this.renderSearchBar} + {...scrollPersistTaps} + /> + ); + } +} diff --git a/app/views/RoomMembersView/styles.js b/app/views/RoomMembersView/styles.js new file mode 100644 index 000000000..68d6cc5ed --- /dev/null +++ b/app/views/RoomMembersView/styles.js @@ -0,0 +1,59 @@ +import { StyleSheet } from 'react-native'; + +export default StyleSheet.create({ + list: { + flex: 1, + backgroundColor: '#ffffff' + }, + item: { + flexDirection: 'row', + paddingVertical: 8, + paddingHorizontal: 16, + alignItems: 'center' + }, + avatar: { + marginRight: 16 + }, + status: { + position: 'absolute', + bottom: -3, + right: -3, + borderWidth: 2, + borderColor: '#fff', + borderRadius: 12, + width: 12, + height: 12 + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: '#ddd' + }, + username: { + flex: 1, + fontSize: 16, + color: '#444' + }, + headerButtonTouchable: { + borderRadius: 4 + }, + headerButton: { + padding: 6, + backgroundColor: 'transparent', + alignItems: 'center', + justifyContent: 'center' + }, + headerButtonText: { + color: '#292E35' + }, + searchBoxView: { + backgroundColor: '#eee' + }, + searchBox: { + backgroundColor: '#fff', + margin: 5, + borderRadius: 5, + padding: 5, + paddingLeft: 10, + color: '#aaa' + } +}); diff --git a/app/views/RoomView/ListView.js b/app/views/RoomView/ListView.js index 500e693bb..40fdc4601 100644 --- a/app/views/RoomView/ListView.js +++ b/app/views/RoomView/ListView.js @@ -27,7 +27,7 @@ export class DataSource extends OldList.DataSource { } } -const ds = new DataSource({ rowHasChanged: (r1, r2) => r1._id !== r2._id }); +const ds = new DataSource({ rowHasChanged: (r1, r2) => r1._id !== r2._id || r1._updatedAt.toISOString() !== r2._updatedAt.toISOString() }); export class List extends React.Component { static propTypes = { diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index 974112c8c..b9513c02f 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -78,7 +78,7 @@ export default class RoomView extends React.Component { this.onReactionPress = this.onReactionPress.bind(this); } - async componentWillMount() { + async componentDidMount() { this.props.navigation.setParams({ title: this.name }); diff --git a/app/views/StarredMessagesView/index.js b/app/views/StarredMessagesView/index.js index c1fd7d578..2c4a2066d 100644 --- a/app/views/StarredMessagesView/index.js +++ b/app/views/StarredMessagesView/index.js @@ -43,7 +43,7 @@ export default class StarredMessagesView extends React.PureComponent { }; } - componentWillMount() { + componentDidMount() { this.props.openStarredMessages(this.props.navigation.state.params.rid); }