diff --git a/__tests__/RoomItem.js b/__tests__/RoomItem.js index cc4d76d2..253b5d45 100644 --- a/__tests__/RoomItem.js +++ b/__tests__/RoomItem.js @@ -3,7 +3,7 @@ import { Provider } from 'react-redux'; import { createStore, combineReducers } from 'redux'; -const reducers = combineReducers({settings:() => ({})}); +const reducers = combineReducers({login:() => ({user: {}}), settings:() => ({})}); const store = createStore(reducers); import React from 'react'; diff --git a/__tests__/__snapshots__/RoomItem.js.snap b/__tests__/__snapshots__/RoomItem.js.snap index 23643947..4bb3753e 100644 --- a/__tests__/__snapshots__/RoomItem.js.snap +++ b/__tests__/__snapshots__/RoomItem.js.snap @@ -34,9 +34,11 @@ exports[`render channel 1`] = ` Array [ Object { "alignItems": "center", + "borderBottomColor": "#ddd", + "borderBottomWidth": 0.5, "flexDirection": "row", "paddingHorizontal": 16, - "paddingVertical": 10, + "paddingVertical": 12, }, undefined, ] @@ -48,13 +50,12 @@ exports[`render channel 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#00BCD4", "borderRadius": 4, - "height": 40, - "width": 40, + "height": 46, + "width": 46, }, undefined, ] @@ -75,7 +76,7 @@ exports[`render channel 1`] = ` "color": "#ffffff", }, Object { - "fontSize": 20, + "fontSize": 23, }, ], Object { @@ -93,44 +94,70 @@ exports[`render channel 1`] = ` style={ Object { "flex": 1, + "height": "100%", "marginLeft": 16, "marginRight": 4, } } > - - general - - - Nov 10 - + + general + + + Nov 10 + + + @@ -171,9 +198,11 @@ exports[`render no icon 1`] = ` Array [ Object { "alignItems": "center", + "borderBottomColor": "#ddd", + "borderBottomWidth": 0.5, "flexDirection": "row", "paddingHorizontal": 16, - "paddingVertical": 10, + "paddingVertical": 12, }, undefined, ] @@ -185,13 +214,12 @@ exports[`render no icon 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#3F51B5", "borderRadius": 4, - "height": 40, - "width": 40, + "height": 46, + "width": 46, }, undefined, ] @@ -212,7 +240,7 @@ exports[`render no icon 1`] = ` "color": "#ffffff", }, Object { - "fontSize": 20, + "fontSize": 23, }, ], Object { @@ -230,44 +258,70 @@ exports[`render no icon 1`] = ` style={ Object { "flex": 1, + "height": "100%", "marginLeft": 16, "marginRight": 4, } } > - - name - - - Nov 10 - + + name + + + Nov 10 + + + @@ -308,9 +362,11 @@ exports[`render private group 1`] = ` Array [ Object { "alignItems": "center", + "borderBottomColor": "#ddd", + "borderBottomWidth": 0.5, "flexDirection": "row", "paddingHorizontal": 16, - "paddingVertical": 10, + "paddingVertical": 12, }, undefined, ] @@ -322,13 +378,12 @@ exports[`render private group 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#FF9800", "borderRadius": 4, - "height": 40, - "width": 40, + "height": 46, + "width": 46, }, undefined, ] @@ -349,7 +404,7 @@ exports[`render private group 1`] = ` "color": "#ffffff", }, Object { - "fontSize": 20, + "fontSize": 23, }, ], Object { @@ -367,44 +422,70 @@ exports[`render private group 1`] = ` style={ Object { "flex": 1, + "height": "100%", "marginLeft": 16, "marginRight": 4, } } > - - private-group - - - Nov 10 - + + private-group + + + Nov 10 + + + @@ -446,9 +527,11 @@ exports[`render unread +999 1`] = ` Array [ Object { "alignItems": "center", + "borderBottomColor": "#ddd", + "borderBottomWidth": 0.5, "flexDirection": "row", "paddingHorizontal": 16, - "paddingVertical": 10, + "paddingVertical": 12, }, undefined, ] @@ -460,13 +543,12 @@ exports[`render unread +999 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#3F51B5", "borderRadius": 4, - "height": 40, - "width": 40, + "height": 46, + "width": 46, }, undefined, ] @@ -482,7 +564,7 @@ exports[`render unread +999 1`] = ` "color": "#ffffff", }, Object { - "fontSize": 20, + "fontSize": 23, }, ] } @@ -490,70 +572,120 @@ exports[`render unread +999 1`] = ` NA + - - name - - - Nov 10 - - - + name + + + Nov 10 + + + - 999+ - + > + + 999+ + + + @@ -593,9 +725,11 @@ exports[`render unread 1`] = ` Array [ Object { "alignItems": "center", + "borderBottomColor": "#ddd", + "borderBottomWidth": 0.5, "flexDirection": "row", "paddingHorizontal": 16, - "paddingVertical": 10, + "paddingVertical": 12, }, undefined, ] @@ -607,13 +741,12 @@ exports[`render unread 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#3F51B5", "borderRadius": 4, - "height": 40, - "width": 40, + "height": 46, + "width": 46, }, undefined, ] @@ -629,7 +762,7 @@ exports[`render unread 1`] = ` "color": "#ffffff", }, Object { - "fontSize": 20, + "fontSize": 23, }, ] } @@ -637,70 +770,120 @@ exports[`render unread 1`] = ` NA + - - name - - - Nov 10 - - - + name + + + Nov 10 + + + - 1 - + > + + 1 + + + @@ -740,9 +923,11 @@ exports[`renders correctly 1`] = ` Array [ Object { "alignItems": "center", + "borderBottomColor": "#ddd", + "borderBottomWidth": 0.5, "flexDirection": "row", "paddingHorizontal": 16, - "paddingVertical": 10, + "paddingVertical": 12, }, undefined, ] @@ -754,13 +939,12 @@ exports[`renders correctly 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#3F51B5", "borderRadius": 4, - "height": 40, - "width": 40, + "height": 46, + "width": 46, }, undefined, ] @@ -776,7 +960,7 @@ exports[`renders correctly 1`] = ` "color": "#ffffff", }, Object { - "fontSize": 20, + "fontSize": 23, }, ] } @@ -784,49 +968,96 @@ exports[`renders correctly 1`] = ` NA + - - name - - - Nov 10 - + + name + + + Nov 10 + + + diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap index 7a0a3c1b..185c7371 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -9,7 +9,6 @@ exports[`Storyshots Avatar avatar 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#3F51B5", @@ -46,7 +45,6 @@ exports[`Storyshots Avatar avatar 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#9C27B0", @@ -83,7 +81,6 @@ exports[`Storyshots Avatar avatar 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#9C27B0", @@ -120,7 +117,6 @@ exports[`Storyshots Avatar avatar 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#3F51B5", @@ -190,9 +186,11 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` Array [ Object { "alignItems": "center", + "borderBottomColor": "#ddd", + "borderBottomWidth": 0.5, "flexDirection": "row", "paddingHorizontal": 16, - "paddingVertical": 10, + "paddingVertical": 12, }, undefined, ] @@ -204,13 +202,12 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#8BC34A", "borderRadius": 4, - "height": 40, - "width": 40, + "height": 46, + "width": 46, }, undefined, ] @@ -226,7 +223,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "color": "#ffffff", }, Object { - "fontSize": 20, + "fontSize": 23, }, ] } @@ -234,49 +231,96 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` RC + - - rocket.cat - - - Nov 10 - + + rocket.cat + + + Nov 10 + + + @@ -312,9 +356,11 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` Array [ Object { "alignItems": "center", + "borderBottomColor": "#ddd", + "borderBottomWidth": 0.5, "flexDirection": "row", "paddingHorizontal": 16, - "paddingVertical": 10, + "paddingVertical": 12, }, undefined, ] @@ -326,13 +372,12 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#8BC34A", "borderRadius": 4, - "height": 40, - "width": 40, + "height": 46, + "width": 46, }, undefined, ] @@ -348,7 +393,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "color": "#ffffff", }, Object { - "fontSize": 20, + "fontSize": 23, }, ] } @@ -356,51 +401,98 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` RC + - - rocket.cat - - - Nov 10 - + + rocket.cat + + + Nov 10 + + + @@ -436,9 +528,11 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` Array [ Object { "alignItems": "center", + "borderBottomColor": "#ddd", + "borderBottomWidth": 0.5, "flexDirection": "row", "paddingHorizontal": 16, - "paddingVertical": 10, + "paddingVertical": 12, }, undefined, ] @@ -450,13 +544,12 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#8BC34A", "borderRadius": 4, - "height": 40, - "width": 40, + "height": 46, + "width": 46, }, undefined, ] @@ -472,7 +565,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "color": "#ffffff", }, Object { - "fontSize": 20, + "fontSize": 23, }, ] } @@ -480,70 +573,120 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` RC + - - rocket.cat - - - Nov 10 - - - + rocket.cat + + + Nov 10 + + + - 1 - + > + + 1 + + + + - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries - - - Nov 10 - - - + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + + Nov 10 + + + - 9 - + > + + 9 + + + + - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries - - - Nov 10 - - - + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + + Nov 10 + + + - 99 - + > + + 99 + + + + - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries - - - Nov 10 - - - + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + + Nov 10 + + + - 100 - + > + + 100 + + + + - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries - - - Nov 10 - - - + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + + Nov 10 + + + - 999+ - + > + + 999+ + + + + - - Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries - - - Nov 10 - - - + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries + + + Nov 10 + + + - @ 999+ - + > + + @ 999+ + + + + - - W - - - Nov 10 - + + W + + + Nov 10 + + + @@ -1412,9 +1858,11 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` Array [ Object { "alignItems": "center", + "borderBottomColor": "#ddd", + "borderBottomWidth": 0.5, "flexDirection": "row", "paddingHorizontal": 16, - "paddingVertical": 10, + "paddingVertical": 12, }, undefined, ] @@ -1426,13 +1874,12 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": "#9C27B0", "borderRadius": 4, - "height": 40, - "width": 40, + "height": 46, + "width": 46, }, undefined, ] @@ -1448,7 +1895,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "color": "#ffffff", }, Object { - "fontSize": 20, + "fontSize": 23, }, ] } @@ -1456,49 +1903,96 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` WW + - - WW - - - Nov 10 - + + WW + + + Nov 10 + + + @@ -1534,9 +2028,11 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` Array [ Object { "alignItems": "center", + "borderBottomColor": "#ddd", + "borderBottomWidth": 0.5, "flexDirection": "row", "paddingHorizontal": 16, - "paddingVertical": 10, + "paddingVertical": 12, }, undefined, ] @@ -1548,13 +2044,12 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` Object { "alignItems": "center", "justifyContent": "center", - "overflow": "hidden", }, Object { "backgroundColor": undefined, "borderRadius": 4, - "height": 40, - "width": 40, + "height": 46, + "width": 46, }, undefined, ] @@ -1570,7 +2065,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` "color": "#ffffff", }, Object { - "fontSize": 20, + "fontSize": 23, }, ] } @@ -1578,49 +2073,96 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` + - - - - - Nov 10 - + + + + + Nov 10 + + + diff --git a/android/app/src/main/assets/fonts/Feather.ttf b/android/app/src/main/assets/fonts/Feather.ttf new file mode 100755 index 00000000..244854c5 Binary files /dev/null and b/android/app/src/main/assets/fonts/Feather.ttf differ diff --git a/android/app/src/main/java/com/rocketchatrn/CustomTabsAndroid.java b/android/app/src/main/java/com/rocketchatrn/CustomTabsAndroid.java index 605a058e..e0a9db1f 100644 --- a/android/app/src/main/java/com/rocketchatrn/CustomTabsAndroid.java +++ b/android/app/src/main/java/com/rocketchatrn/CustomTabsAndroid.java @@ -1,4 +1,4 @@ -package com.rocketchatrn; +package chat.rocket.reactnative; import android.content.Intent; import android.content.pm.ResolveInfo; diff --git a/android/app/src/main/java/com/rocketchatrn/CustomTabsHelper.java b/android/app/src/main/java/com/rocketchatrn/CustomTabsHelper.java index 99934ec3..b7f3eb72 100644 --- a/android/app/src/main/java/com/rocketchatrn/CustomTabsHelper.java +++ b/android/app/src/main/java/com/rocketchatrn/CustomTabsHelper.java @@ -1,4 +1,4 @@ -package com.rocketchatrn; +package chat.rocket.reactnative; import android.content.Context; import android.content.Intent; diff --git a/android/app/src/main/java/com/rocketchatrn/MainApplication.java b/android/app/src/main/java/com/rocketchatrn/MainApplication.java index beb8cc02..986de10f 100644 --- a/android/app/src/main/java/com/rocketchatrn/MainApplication.java +++ b/android/app/src/main/java/com/rocketchatrn/MainApplication.java @@ -44,7 +44,8 @@ public class MainApplication extends Application implements ReactApplication { new ReactVideoPackage(), new SplashScreenReactPackage(), new RCTToastPackage(), - new KeyboardInputPackage(MainApplication.this) + new KeyboardInputPackage(MainApplication.this), + new RocketChatNativePackage() ); } }; diff --git a/android/app/src/main/java/com/rocketchatrn/RocketChatNativePackage.java b/android/app/src/main/java/com/rocketchatrn/RocketChatNativePackage.java index bbc20f72..16cef019 100644 --- a/android/app/src/main/java/com/rocketchatrn/RocketChatNativePackage.java +++ b/android/app/src/main/java/com/rocketchatrn/RocketChatNativePackage.java @@ -1,4 +1,4 @@ -package com.rocketchatrn; +package chat.rocket.reactnative; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.JavaScriptModule; diff --git a/app/containers/Avatar.js b/app/containers/Avatar.js index f547b778..3ab6c266 100644 --- a/app/containers/Avatar.js +++ b/app/containers/Avatar.js @@ -8,7 +8,7 @@ import avatarInitialsAndColor from '../utils/avatarInitialsAndColor'; const styles = StyleSheet.create({ iconContainer: { - overflow: 'hidden', + // overflow: 'hidden', justifyContent: 'center', alignItems: 'center' }, @@ -31,7 +31,8 @@ export default class Avatar extends React.PureComponent { avatar: PropTypes.string, size: PropTypes.number, borderRadius: PropTypes.number, - type: PropTypes.string + type: PropTypes.string, + children: PropTypes.object }; render() { const { @@ -68,6 +69,7 @@ export default class Avatar extends React.PureComponent { {initials} {image} + {this.props.children} ); } diff --git a/app/containers/MessageActions.js b/app/containers/MessageActions.js index e362c69d..6a2de4b4 100644 --- a/app/containers/MessageActions.js +++ b/app/containers/MessageActions.js @@ -18,6 +18,7 @@ import { } from '../actions/messages'; import { showToast } from '../utils/info'; +const returnAnArray = obj => obj || []; @connect( state => ({ showActions: state.messages.showActions, @@ -171,11 +172,11 @@ export default class MessageActions extends React.Component { } setPermissions(permissions) { - this.hasEditPermission = permissions['edit-message'] + this.hasEditPermission = returnAnArray(permissions['edit-message']) .some(item => this.mergedRoles.indexOf(item) !== -1); - this.hasDeletePermission = permissions['delete-message'] + this.hasDeletePermission = returnAnArray(permissions['delete-message']) .some(item => this.mergedRoles.indexOf(item) !== -1); - this.hasForceDeletePermission = permissions['force-delete-message'] + this.hasForceDeletePermission = returnAnArray(permissions['force-delete-message']) .some(item => this.mergedRoles.indexOf(item) !== -1); } diff --git a/app/containers/message/Markdown.js b/app/containers/message/Markdown.js index 805c915f..1b50f901 100644 --- a/app/containers/message/Markdown.js +++ b/app/containers/message/Markdown.js @@ -1,5 +1,5 @@ import React from 'react'; -import { Text, StyleSheet } from 'react-native'; +import { Text, StyleSheet, ViewPropTypes } from 'react-native'; import PropTypes from 'prop-types'; import EasyMarkdown from 'react-native-easy-markdown'; // eslint-disable-line import SimpleMarkdown from 'simple-markdown'; @@ -17,13 +17,15 @@ const BlockCode = ({ node, state }) => ( ); const mentionStyle = { color: '#13679a' }; -const Markdown = ({ msg, customEmojis }) => { +const Markdown = ({ + msg, customEmojis, style, markdownStyle, customRules, renderInline +}) => { if (!msg) { return null; } msg = emojify(msg, { output: 'unicode' }); - const rules = { + const defaultRules = { username: { order: -1, match: SimpleMarkdown.inlineRegex(/^@[0-9a-zA-Z-_.]+/), @@ -121,11 +123,13 @@ const Markdown = ({ msg, customEmojis }) => { }; const codeStyle = StyleSheet.flatten(styles.codeStyle); + style = StyleSheet.flatten(style); return ( {msg} ); @@ -133,7 +137,12 @@ const Markdown = ({ msg, customEmojis }) => { Markdown.propTypes = { msg: PropTypes.string.isRequired, - customEmojis: PropTypes.object + customEmojis: PropTypes.object, + // eslint-disable-next-line react/no-typos + style: ViewPropTypes.style, + markdownStyle: PropTypes.object, + customRules: PropTypes.object, + renderInline: PropTypes.bool }; BlockCode.propTypes = { diff --git a/app/containers/status.js b/app/containers/status.js new file mode 100644 index 00000000..55a78fcc --- /dev/null +++ b/app/containers/status.js @@ -0,0 +1,39 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { StyleSheet, View, ViewPropTypes } from 'react-native'; +import { STATUS_COLORS } from '../constants/colors'; + +const styles = StyleSheet.create({ + status: { + borderRadius: 16, + width: 16, + height: 16 + } +}); + +@connect(state => ({ + activeUsers: state.activeUsers +})) + +export default class Status extends React.Component { + static propTypes = { + style: ViewPropTypes.style, + id: PropTypes.string, + activeUsers: PropTypes.object + }; + + shouldComponentUpdate(nextProps) { + const userId = this.props.id; + return this.status !== nextProps.activeUsers[userId]; + } + + get status() { + const userId = this.props.id; + return (this.props.activeUsers && this.props.activeUsers[userId]) || 'offline'; + } + + render() { + return (); + } +} diff --git a/app/lib/realm.js b/app/lib/realm.js index 14766045..bca5a7a1 100644 --- a/app/lib/realm.js +++ b/app/lib/realm.js @@ -49,6 +49,7 @@ const roomsSchema = { properties: { _id: 'string', t: 'string', + lastMessage: 'messages', _updatedAt: { type: 'date', optional: true } } }; @@ -80,7 +81,8 @@ const subscriptionSchema = { // userMentions: 0, // groupMentions: 0, roomUpdatedAt: { type: 'date', optional: true }, - ro: { type: 'bool', optional: true } + ro: { type: 'bool', optional: true }, + lastMessage: { type: 'messages', optional: true } } }; @@ -134,7 +136,7 @@ const attachment = { const url = { name: 'url', properties: { - _id: 'int', + // _id: { type: 'int', optional: true }, url: { type: 'string', optional: true }, title: { type: 'string', optional: true }, description: { type: 'string', optional: true }, @@ -183,7 +185,7 @@ const messagesSchema = { groupable: { type: 'bool', optional: true }, avatar: { type: 'string', optional: true }, attachments: { type: 'list', objectType: 'attachment' }, - urls: { type: 'list', objectType: 'url' }, + urls: { type: 'list', objectType: 'url', default: [] }, _updatedAt: { type: 'date', optional: true }, status: { type: 'int', optional: true }, pinned: { type: 'bool', optional: true }, diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index f56eb1c9..292476bb 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -128,11 +128,12 @@ const RocketChat = { const sub = database.objects('subscriptions').filtered('rid == $0', data._id)[0]; database.write(() => { sub.roomUpdatedAt = data._updatedAt; + sub.lastMessage = data.lastMessage; sub.ro = data.ro; }); } }); - }); + }).catch(console.log); }, me({ server, token, userId }) { @@ -428,6 +429,7 @@ const RocketChat = { const room = rooms.find(({ _id }) => _id === subscription.rid); if (room) { subscription.roomUpdatedAt = room._updatedAt; + subscription.lastMessage = room.lastMessage; subscription.ro = room.ro; } if (subscription.roles) { @@ -435,10 +437,14 @@ const RocketChat = { } return subscription; }); + + database.write(() => { - data.forEach(subscription => - database.create('subscriptions', subscription, true)); + data.forEach(subscription => database.create('subscriptions', subscription, true)); + // rooms.forEach(room => database.create('rooms', room, true)); }); + + this.ddp.subscribe('stream-notify-user', `${ login.user.id }/subscriptions-changed`, false); this.ddp.subscribe('stream-notify-user', `${ login.user.id }/rooms-changed`, false); return data; diff --git a/app/presentation/RoomItem.js b/app/presentation/RoomItem.js index d198f91f..946796b7 100644 --- a/app/presentation/RoomItem.js +++ b/app/presentation/RoomItem.js @@ -2,36 +2,60 @@ import React from 'react'; import moment from 'moment'; import PropTypes from 'prop-types'; import { View, Text, StyleSheet } from 'react-native'; + +import { connect } from 'react-redux'; +import SimpleMarkdown from 'simple-markdown'; + import Avatar from '../containers/Avatar'; +import Status from '../containers/status'; import Touch from '../utils/touch/index'; //eslint-disable-line +import Markdown from '../containers/message/Markdown'; const styles = StyleSheet.create({ container: { flexDirection: 'row', paddingHorizontal: 16, - paddingVertical: 10, - alignItems: 'center' + paddingVertical: 12, + alignItems: 'center', + borderBottomWidth: 0.5, + borderBottomColor: '#ddd' }, number: { - minWidth: 20, - borderRadius: 3, + minWidth: 25, + borderRadius: 4, backgroundColor: '#1d74f5', color: '#fff', - textAlign: 'center', overflow: 'hidden', fontSize: 14, + paddingVertical: 4, paddingHorizontal: 5, - paddingVertical: 2 + + textAlign: 'center', + alignItems: 'center', + justifyContent: 'center' }, roomNameView: { flex: 1, + height: '100%', marginLeft: 16, marginRight: 4 }, roomName: { flex: 1, - fontSize: 16, - color: '#444' + fontSize: 18, + color: '#444', + + marginRight: 8 + }, + lastMessage: { + flex: 1, + flexShrink: 1, + marginRight: 8, + maxHeight: 20, + overflow: 'hidden', + flexDirection: 'row', + alignItems: 'flex-start', + justifyContent: 'flex-start' }, alert: { fontWeight: 'bold' @@ -39,30 +63,144 @@ const styles = StyleSheet.create({ favorite: { // backgroundColor: '#eee' }, - update: { + row: { + width: '100%', flex: 1, + flexDirection: 'row', + alignItems: 'flex-end', + justifyContent: 'flex-end' + }, + firstRow: { + width: '100%', + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center' + }, + update: { fontSize: 10, - // height: 10, - color: '#888' + color: '#888', + alignItems: 'center', + justifyContent: 'center' + }, + status: { + position: 'absolute', + bottom: -3, + right: -3, + borderWidth: 3, + borderColor: '#fff' } }); +const markdownStyle = { block: { marginBottom: 0, flexWrap: 'wrap', flexDirection: 'row' } }; +const parseInline = (parse, content, state) => { + const isCurrentlyInline = state.inline || false; + state.inline = true; + const result = parse(content, state); + state.inline = isCurrentlyInline; + return result; +}; +const parseCaptureInline = (capture, parse, state) => ({ content: parseInline(parse, capture[1], state) }); +const customRules = { + strong: { + order: -4, + match: SimpleMarkdown.inlineRegex(/^\*\*([\s\S]+?)\*\*(?!\*)/), + parse: parseCaptureInline, + react: (node, output, state) => ({ + type: 'strong', + key: state.key, + props: { + children: output(node.content, state) + } + }) + }, + text: { + order: -3, + match: SimpleMarkdown.inlineRegex(/^[\s\S]+?(?=[^0-9A-Za-z\s\u00c0-\uffff]|\n\n| {2,}\n|\w+:\S|$)/), + parse: capture => ({ content: capture[0] }), + react: node => node.content + } +}; + +const renderNumber = (unread, userMentions) => { + if (!unread || unread <= 0) { + return; + } + + if (unread >= 1000) { + unread = '999+'; + } + + if (userMentions > 0) { + unread = `@ ${ unread }`; + } + + return ( + + { unread } + + ); +}; + +@connect(state => ({ + user: state.login && state.login.user, + StoreLastMessage: state.settings.Store_Last_Message, + customEmojis: state.customEmojis +})) export default class RoomItem extends React.PureComponent { static propTypes = { type: PropTypes.string.isRequired, name: PropTypes.string.isRequired, + StoreLastMessage: PropTypes.bool, _updatedAt: PropTypes.instanceOf(Date), + lastMessage: PropTypes.object, favorite: PropTypes.bool, alert: PropTypes.bool, unread: PropTypes.number, userMentions: PropTypes.number, - baseUrl: PropTypes.string, - onPress: PropTypes.func + id: PropTypes.string, + onPress: PropTypes.func, + customEmojis: PropTypes.object, + user: PropTypes.object } get icon() { - const { type, name, baseUrl } = this.props; - return ; + const { + type, name, id + } = this.props; + return ({type === 'd' ? : null }); + } + + get lastMessage() { + const { + lastMessage, alert, type + } = this.props; + + if (!this.props.StoreLastMessage) { + return ''; + } + if (!lastMessage) { + return 'No Message'; + } + + + let prefix = ''; + + if (lastMessage.u.username === this.props.user.username) { + prefix = 'You: '; + } else if (type !== 'd') { + prefix = `${ lastMessage.u.username }: `; + } + + 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 ? '...' : '' }`; } formatDate = date => moment(date).calendar(null, { @@ -72,29 +210,9 @@ export default class RoomItem extends React.PureComponent { sameElse: 'MMM D' }) - renderNumber = (unread, userMentions) => { - if (!unread || unread <= 0) { - return; - } - - if (unread >= 1000) { - unread = '999+'; - } - - if (userMentions > 0) { - unread = `@ ${ unread }`; - } - - return ( - - { unread } - - ); - } - render() { const { - favorite, alert, unread, userMentions, name, _updatedAt + favorite, unread, userMentions, name, _updatedAt, customEmojis, alert } = this.props; const date = this.formatDate(_updatedAt); @@ -117,10 +235,23 @@ export default class RoomItem extends React.PureComponent { {this.icon} - { name } - {_updatedAt ? { date } : null} + + { name } + {_updatedAt ? { date } : null} + + + + {renderNumber(unread, userMentions)} + - {this.renderNumber(unread, userMentions)} ); diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index 4d32ca07..ee0594d3 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Text, View, Button, LayoutAnimation } from 'react-native'; +import { Text, View, Button } from 'react-native'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import equal from 'deep-equal'; @@ -51,8 +51,8 @@ export default class RoomView extends React.Component { Message_TimeFormat: PropTypes.string, loading: PropTypes.bool, actionMessage: PropTypes.object, - toggleReactionPicker: PropTypes.func.isRequired, - layoutAnimation: PropTypes.instanceOf(Date) + toggleReactionPicker: PropTypes.func.isRequired + // layoutAnimation: PropTypes.instanceOf(Date) }; static navigationOptions = ({ navigation }) => ({ @@ -90,11 +90,11 @@ export default class RoomView extends React.Component { this.rooms.addListener(this.updateRoom); } - componentWillReceiveProps(nextProps) { - if (this.props.layoutAnimation !== nextProps.layoutAnimation) { - LayoutAnimation.spring(); - } - } + // componentWillReceiveProps(nextProps) { + // // if (this.props.layoutAnimation !== nextProps.layoutAnimation) { + // // LayoutAnimation.spring(); + // // } + // } shouldComponentUpdate(nextProps, nextState) { return !(equal(this.props, nextProps) && equal(this.state, nextState)); } diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index f2292672..75b36c6e 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -18,6 +18,7 @@ import styles from './styles'; const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }); @connect(state => ({ + user: state.login.user, server: state.server.server, login: state.login, Site_Url: state.settings.Site_Url, @@ -31,6 +32,7 @@ const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }); export default class RoomsListView extends React.Component { static propTypes = { navigation: PropTypes.object.isRequired, + user: PropTypes.object, Site_Url: PropTypes.string, server: PropTypes.string, searchText: PropTypes.string @@ -69,7 +71,9 @@ export default class RoomsListView extends React.Component { this.search(props.searchText); } } - + // componentWillUpdate() { + // LayoutAnimation.easeInEaseOut(); + // } componentWillUnmount() { this.data.removeAllListeners(); } @@ -79,6 +83,12 @@ export default class RoomsListView extends React.Component { this.search(text); } + getLastMessage = (subscription) => { + const [room] = database.objects('rooms').filtered('_id = $0', subscription.rid).slice(); + console.log('ROOM', room); + return room && room.lastMessage; + } + search(text) { const searchText = text.trim(); if (searchText === '') { @@ -197,20 +207,23 @@ export default class RoomsListView extends React.Component { ); - renderItem = item => ( - { + const id = item.rid.replace(this.props.user.id, '').trim(); + return ( this._onPressItem(item)} - /> - ) + />); + } renderList = () => (