Skip to content

Commit 4eec2fb

Browse files
Merge pull request #251 from GetStream/vishal/channel-preview-messenger-tests
CRNS-116: Channel preview tests and hooks
2 parents 654cfd4 + 444f98a commit 4eec2fb

File tree

15 files changed

+2103
-265
lines changed

15 files changed

+2103
-265
lines changed

babel.config.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module.exports = {
2+
presets: ['@babel/env', '@babel/react', '@babel/preset-flow'],
3+
plugins: [
4+
'macros',
5+
'@babel/proposal-class-properties',
6+
'@babel/transform-runtime',
7+
'babel-plugin-styled-components',
8+
],
9+
env: {
10+
production: {
11+
presets: [
12+
[
13+
'@babel/env',
14+
{
15+
modules: false,
16+
},
17+
],
18+
],
19+
},
20+
},
21+
overrides: [
22+
{
23+
compact: false,
24+
},
25+
],
26+
};

jest-setup.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { registerNativeHandlers } from './src/native';
2+
3+
registerNativeHandlers({
4+
NetInfo: {
5+
addEventListener: () => {},
6+
fetch: () =>
7+
new Promise((resolve) => {
8+
resolve();
9+
}),
10+
},
11+
pickImage: () => null,
12+
pickDocument: () => null,
13+
});

jest.config.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const jestPreset = require('@testing-library/react-native/jest-preset');
2+
3+
module.exports = Object.assign(jestPreset, {
4+
testRegex: [
5+
/**
6+
* If you want to test single file, mention it here
7+
* e.g.,
8+
* "src/components/ChannelList/__tests__/ChannelList.test.js",
9+
* "src/components/MessageList/__tests__/MessageList.test.js"
10+
*/
11+
],
12+
testPathIgnorePatterns: ['/node_modules/', '/examples/', '__snapshots__'],
13+
moduleNameMapper: {
14+
'mock-builders(.*)$': '<rootDir>/src/mock-builders$1',
15+
'@stream-io/styled-components':
16+
'<rootDir>/node_modules/@stream-io/styled-components/native/dist/styled-components.native.cjs.js',
17+
},
18+
setupFiles: [...jestPreset.setupFiles, require.resolve('./jest-setup.js')],
19+
});

native-package/yarn.lock

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -895,10 +895,10 @@
895895
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
896896
integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
897897

898-
"@stream-io/react-native-simple-markdown@^1.2.0":
899-
version "1.2.0"
900-
resolved "https://registry.yarnpkg.com/@stream-io/react-native-simple-markdown/-/react-native-simple-markdown-1.2.0.tgz#f1ea7db1e16712a3428b79fdd522464ae2caccdb"
901-
integrity sha512-rqs625+3v+Nz4yUocmMr/Ar6Jb31HZWXDOqEi4MjU3IDLCRi9XaIeH7FlCD4GrabqUEYjq8EY709fxpVBjaSxw==
898+
"@stream-io/react-native-simple-markdown@^1.2.1":
899+
version "1.2.1"
900+
resolved "https://registry.yarnpkg.com/@stream-io/react-native-simple-markdown/-/react-native-simple-markdown-1.2.1.tgz#ce3c909fda4e087f77a68ab051c3a8f034bc9adb"
901+
integrity sha512-Yj1IxO+Y632DHqSrUfUkMGlEi7qD30Qi2+7etVxaZb/7hr++VFoU9BQdyKPCarRJfIzFMvVZ/DJFvz8JhybfUQ==
902902
dependencies:
903903
lodash "^4.17.15"
904904
simple-markdown "^0.7.2"
@@ -6272,13 +6272,13 @@ stream-browserify@^2.0.1:
62726272
inherits "~2.0.1"
62736273
readable-stream "^2.0.2"
62746274

6275-
stream-chat-react-native-core@v0.14.0:
6276-
version "0.14.0"
6277-
resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-0.14.0.tgz#f4f406b056e3cbbeb9219a342170e044d3f0a609"
6278-
integrity sha512-LtEu7iUi4El4FSczrs+yhMddVF4lsII6DCuuk8dKTk4BPxW2ItGmNeZ1bOMa30YClj0DuDRnSiMEbJTnXMdLbQ==
6275+
stream-chat-react-native-core@v0.15.0:
6276+
version "0.15.0"
6277+
resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-0.15.0.tgz#2041b2986f2233e2635181abbf40971c194cd531"
6278+
integrity sha512-pCzua3KwzkT3b6SSrx/4P/RIu68aArP00Z9Sb9ZJ+4DiAdfGAbOsHVGOGHytmtOcTm/bBY1bpj5eXl98T35zxw==
62796279
dependencies:
62806280
"@babel/runtime" "^7.1.2"
6281-
"@stream-io/react-native-simple-markdown" "^1.2.0"
6281+
"@stream-io/react-native-simple-markdown" "^1.2.1"
62826282
"@stream-io/styled-components" "^4.2.1"
62836283
anchorme "^1.1.2"
62846284
babel-plugin-macros "^2.5.1"

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@
101101
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
102102
"@babel/polyfill": "^7.4.4",
103103
"@babel/preset-env": "^7.4.3",
104+
"@babel/preset-flow": "^7.10.4",
104105
"@babel/preset-react": "^7.0.0",
106+
"@testing-library/react-native": "^5.0.3",
105107
"@types/react": "^16.8.23",
106108
"@types/react-native": "^0.57.0",
107109
"ast-pretty-print": "^2.0.1",
@@ -121,8 +123,10 @@
121123
"prettier": "^1.16.4",
122124
"react": "^16.5.0",
123125
"react-dom": "^16.8.6",
126+
"react-native": "0.61.5",
124127
"react-native-web": "^0.11.4",
125128
"react-styleguidist": "^8.0.6",
129+
"react-test-renderer": "^16.13.1",
126130
"rollup": "^0.68.2",
127131
"rollup-plugin-babel": "^4.0.3",
128132
"rollup-plugin-commonjs": "^9.1.8",
Lines changed: 47 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,60 @@
1-
import React, { PureComponent } from 'react';
1+
import React, { useContext, useEffect, useState } from 'react';
22
import PropTypes from 'prop-types';
33

4-
import { withTranslationContext } from '../../context';
5-
6-
class ChannelPreview extends PureComponent {
7-
constructor(props) {
8-
super(props);
9-
10-
this.state = {
11-
unread: 0,
12-
lastMessage: {},
13-
lastRead: new Date(),
4+
import { ChatContext } from '../../context';
5+
import { useLatestMessagePreview } from './hooks/useLatestMessagePreview';
6+
7+
/**
8+
* NOTE: We created an inner component which only receives `client` from the ChatContext. This
9+
* practice will prevent unnecessary renders of the component when items in ChatContext change
10+
*/
11+
const ChannelPreviewWithContext = React.memo((props) => {
12+
const { channel, client } = props;
13+
const [lastMessage, setLastMessage] = useState({});
14+
const [unread, setUnread] = useState(channel.countUnread());
15+
const latestMessage = useLatestMessagePreview(channel, lastMessage);
16+
17+
useEffect(() => {
18+
const handleNewMessageEvent = (e) => {
19+
setLastMessage(e.message);
20+
setUnread(channel.countUnread());
1421
};
15-
}
16-
17-
static propTypes = {
18-
channel: PropTypes.object.isRequired,
19-
client: PropTypes.object.isRequired,
20-
setActiveChannel: PropTypes.func,
21-
Preview: PropTypes.oneOfType([PropTypes.node, PropTypes.elementType]),
22-
};
23-
24-
static defaultProps = {
25-
// Preview: ChannelPreviewCountOnly,
26-
};
27-
28-
componentDidMount() {
29-
// listen to change...
30-
const channel = this.props.channel;
31-
this.setState({ unread: channel.countUnread() });
32-
channel.on('message.new', this.handleNewMessageEvent);
33-
channel.on('message.read', this.handleReadEvent);
34-
}
35-
36-
componentWillUnmount() {
37-
const channel = this.props.channel;
38-
channel.off('message.new', this.handleNewMessageEvent);
39-
channel.off('message.read', this.handleReadEvent);
40-
}
4122

42-
handleReadEvent = (event) => {
43-
if (event.user.id === this.props.client.userID) {
44-
this.setState({ unread: this.props.channel.countUnread() });
45-
}
46-
};
23+
channel.on('message.new', handleNewMessageEvent);
4724

48-
handleNewMessageEvent = (event) => {
49-
const channel = this.props.channel;
50-
this.setState({
51-
lastMessage: event.message,
52-
unread: channel.countUnread(),
53-
});
54-
};
55-
56-
getLatestMessage = () => {
57-
const { channel, t, tDateTimeParser } = this.props;
58-
const message = channel.state.messages[channel.state.messages.length - 1];
25+
return () => {
26+
channel.off('message.new', handleNewMessageEvent);
27+
};
28+
}, []);
5929

60-
const latestMessage = {
61-
text: '',
62-
created_at: '',
63-
messageObject: { ...message },
30+
useEffect(() => {
31+
const handleReadEvent = (e) => {
32+
if (e.user.id === client.userID) {
33+
setUnread(0);
34+
}
6435
};
6536

66-
if (!message) {
67-
latestMessage.text = t('Nothing yet...');
68-
return latestMessage;
69-
}
70-
if (message.deleted_at) {
71-
latestMessage.text = t('Message deleted');
72-
return latestMessage;
73-
}
37+
channel.on('message.read', handleReadEvent);
7438

75-
if (message.text) {
76-
latestMessage.text = message.text;
77-
} else {
78-
if (message.command) {
79-
latestMessage.text = '/' + message.command;
80-
} else if (message.attachments.length) {
81-
latestMessage.text = t('🏙 Attachment...');
82-
} else {
83-
latestMessage.text = t('Empty message...');
84-
}
85-
}
39+
return () => {
40+
channel.off('message.read', handleReadEvent);
41+
};
42+
}, []);
8643

87-
if (tDateTimeParser(message.created_at).isSame(new Date(), 'day'))
88-
latestMessage.created_at = tDateTimeParser(message.created_at).format(
89-
'LT',
90-
);
91-
else {
92-
latestMessage.created_at = tDateTimeParser(message.created_at).format(
93-
'L',
94-
);
95-
}
44+
const { Preview } = props;
45+
return <Preview {...props} latestMessage={latestMessage} unread={unread} />;
46+
});
9647

97-
return latestMessage;
98-
};
48+
const ChannelPreview = (props) => {
49+
const { client } = useContext(ChatContext);
50+
return <ChannelPreviewWithContext {...props} {...{ client }} />;
51+
};
9952

100-
render() {
101-
const props = { ...this.state, ...this.props };
102-
const { Preview } = this.props;
103-
return <Preview {...props} latestMessage={this.getLatestMessage()} />;
104-
}
105-
}
53+
ChannelPreview.propTypes = {
54+
channel: PropTypes.object.isRequired,
55+
client: PropTypes.object.isRequired,
56+
setActiveChannel: PropTypes.func,
57+
Preview: PropTypes.oneOfType([PropTypes.node, PropTypes.elementType]),
58+
};
10659

107-
export default withTranslationContext(ChannelPreview);
60+
export default ChannelPreview;

0 commit comments

Comments
 (0)