index.android.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. import {StackNavigator, TabNavigator} from 'react-navigation';
  2. import React, {Component} from 'react';
  3. import TitleBar from './app/views/TitleBar';
  4. import ContactsScreen from './app/screens/ContactsScreen';
  5. import FindScreen from './app/screens/FindScreen';
  6. import MeScreen from './app/screens/MeScreen';
  7. import SearchScreen from './app/screens/SearchScreen';
  8. import ContactDetailScreen from './app/screens/ContactDetailScreen';
  9. import ChattingScreen from './app/screens/ChattingScreen';
  10. import MomentScreen from './app/screens/MomentScreen';
  11. import ScanScreen from './app/screens/ScanScreen';
  12. import ScanResultScreen from './app/screens/ScanResultScreen';
  13. import ShoppingScreen from './app/screens/ShoppingScreen';
  14. import CardPackageScreen from './app/screens/CardPackageScreen';
  15. import SplashScreen from './app/screens/SplashScreen';
  16. import LoginScreen from './app/screens/LoginScreen';
  17. import RegisterScreen from './app/screens/RegisterScreen';
  18. import NewFriendsScreen from './app/screens/NewFriendsScreen';
  19. import PersonInfoScreen from './app/screens/PersonInfoScreen';
  20. import PublishMomentScreen from './app/screens/PublishMomentScreen';
  21. import ImageShowScreen from './app/screens/ImageShowScreen';
  22. import ShakeScreen from './app/screens/ShakeScreen';
  23. import SettingsScreen from './app/screens/SettingsScreen';
  24. import StorageUtil from './app/utils/StorageUtil';
  25. import UpgradeModule from './app/utils/UpgradeModule';
  26. import UpgradeDialog from './app/views/UpgradeDialog';
  27. import ConversationUtil from './app/utils/ConversationUtil';
  28. import TimeUtil from './app/utils/TimeUtil';
  29. import CountEmitter from './app/event/CountEmitter';
  30. import Global from './app/utils/Global';
  31. import Utils from './app/utils/Utils';
  32. import Toast from '@remobile/react-native-toast';
  33. import UserInfoUtil from './app/utils/UserInfoUtil';
  34. import {
  35. AppRegistry,
  36. StyleSheet,
  37. Text,
  38. View,
  39. Image,
  40. Dimensions,
  41. PixelRatio,
  42. StatusBar,
  43. FlatList,
  44. TouchableHighlight,
  45. Platform
  46. } from 'react-native';
  47. const {width} = Dimensions.get('window');
  48. export default class HomeScreen extends Component {
  49. static navigationOptions = {
  50. tabBarLabel: '微信',
  51. tabBarIcon: ({focused, tintColor}) => {
  52. if (focused) {
  53. return (
  54. <Image style={styles.tabBarIcon} source={require('./images/ic_weixin_selected.png')}/>
  55. );
  56. }
  57. return (
  58. <Image style={styles.tabBarIcon} source={require('./images/ic_weixin_normal.png')}/>
  59. );
  60. },
  61. };
  62. constructor(props) {
  63. super(props);
  64. this.state = {
  65. checkedUpgrade: true, // 标记是否检查了更新,这里置为true则不会检查更新,设置为false则每次启动时检查更新,该功能默认不开启
  66. recentConversation: []
  67. };
  68. this.registerHXListener();
  69. }
  70. loadConversations(username) {
  71. ConversationUtil.getConversations(username, (result) => {
  72. let count = result.length;
  73. if (count == 0) {
  74. // 没有会话,创建两个会话
  75. this.generateAutoConversation('tulingrobot');
  76. return;
  77. }
  78. let index = 0;
  79. for (let i = 0; i < count; i++) {
  80. let conversation = result[i];
  81. let chatWithUsername = conversation.conversationId.replace(username, '');
  82. UserInfoUtil.getUserInfo(chatWithUsername, (userInfo) => {
  83. index++;
  84. if (userInfo != null) {
  85. conversation['avatar'] = userInfo.avatar;
  86. conversation['nick'] = userInfo.nick;
  87. }
  88. if (index == count) {
  89. this.setState({recentConversation: result});
  90. ConversationUtil.showConversations();
  91. }
  92. });
  93. }
  94. });
  95. }
  96. // 生成自动回复的对话
  97. generateAutoConversation(chatUsername) {
  98. let id = WebIM.conn.getUniqueId(); // 生成本地消息id
  99. let msg = new WebIM.message('txt', id); // 创建文本消息
  100. let message = '你好,我是RNWeChat作者,欢迎使用RNWeChat,有任何问题都可以与我交流!';
  101. if (chatUsername == 'tulingrobot') {
  102. message = '我是图灵机器人,开心或者不开心,都可以找我聊天~';
  103. }
  104. msg.set({
  105. msg: message, // 消息内容
  106. to: this.state.username, // 接收消息对象(用户id)
  107. roomType: false,
  108. success: function (id, serverMsgId) {
  109. },
  110. fail: function (e) {
  111. }
  112. });
  113. msg.body.chatType = 'singleChat';
  114. ConversationUtil.addMessage({
  115. 'conversationId': ConversationUtil.generateConversationId(chatUsername, this.state.username),
  116. 'id': id,
  117. 'from': chatUsername,
  118. 'to': this.state.username,
  119. 'time': TimeUtil.currentTime(),
  120. 'data': message,
  121. 'msgType': 'txt'
  122. }, ()=>{
  123. if (chatUsername == 'tulingrobot' && this.state.username != 'yubo666') {
  124. this.generateAutoConversation('yubo666');
  125. } else {
  126. this.loadConversations(this.state.username);
  127. }
  128. });
  129. }
  130. registerHXListener() { // 注册环信的消息监听器
  131. WebIM.conn.listen({
  132. // xmpp连接成功
  133. onOpened: (msg) => {
  134. Toast.showShortCenter('onOpend')
  135. // 登录环信服务器成功后回调这里
  136. // 出席后才能接受推送消息
  137. WebIM.conn.setPresence();
  138. },
  139. // 出席消息
  140. onPresence: (msg) => {
  141. },
  142. // 各种异常
  143. onError: (error) => {
  144. Toast.showShortCenter('登录聊天服务器出错');
  145. console.log('onError: ' + JSON.stringify(error));
  146. },
  147. // 连接断开
  148. onClosed: (msg) => {
  149. Toast.showShortCenter('与聊天服务器连接断开');
  150. },
  151. // 更新黑名单
  152. onBlacklistUpdate: (list) => {
  153. },
  154. // 文本消息
  155. onTextMessage: (message) => {
  156. message.conversationId = ConversationUtil.generateConversationId(message.from, message.to);
  157. message.msgType = 'txt';
  158. message.time = TimeUtil.currentTime();
  159. ConversationUtil.addMessage(message, (error) => {
  160. // 重新加载会话
  161. this.loadConversations(this.state.username);
  162. // 若当前在聊天界面,还要通知聊天界面刷新
  163. CountEmitter.emit('notifyChattingRefresh');
  164. });
  165. },
  166. onPictureMessage: (message) => {
  167. message.conversationId = ConversationUtil.generateConversationId(message.from, message.to);
  168. message.msgType = 'img';
  169. message.time = TimeUtil.currentTime();
  170. ConversationUtil.addMessage(message, (error) => {
  171. // 重新加载会话
  172. this.loadConversations(this.state.username);
  173. // 若当前在聊天界面,还要通知聊天界面刷新
  174. CountEmitter.emit('notifyChattingRefresh');
  175. });
  176. }
  177. });
  178. }
  179. componentWillMount() {
  180. CountEmitter.addListener('notifyConversationListRefresh', () => {
  181. // 重新加载会话
  182. this.loadConversations(this.state.username);
  183. });
  184. }
  185. render() {
  186. return (
  187. <View style={styles.container}>
  188. <StatusBar
  189. backgroundColor='#393A3E'
  190. barStyle="light-content"
  191. />
  192. <TitleBar nav={this.props.navigation}/>
  193. <View style={styles.divider}></View>
  194. <View style={styles.content}>
  195. {
  196. this.state.recentConversation.length == 0 ? (
  197. <Text style={styles.emptyHintText}>暂无会话消息</Text>
  198. ) : (
  199. <FlatList
  200. data={this.state.recentConversation}
  201. renderItem={this.renderItem}
  202. keyExtractor={this._keyExtractor}
  203. />
  204. )
  205. }
  206. </View>
  207. <View style={styles.divider}></View>
  208. <View style={{backgroundColor: 'transparent', position: 'absolute', left: 0, top: 0, width: width}}>
  209. <UpgradeDialog ref="upgradeDialog" content={this.state.upgradeContent}/>
  210. </View>
  211. </View>
  212. );
  213. }
  214. unregisterListeners() {
  215. CountEmitter.removeListener('notifyConversationListRefresh', ()=>{});
  216. }
  217. _keyExtractor = (item, index) => item.conversationId
  218. componentDidMount() {
  219. StorageUtil.get('username', (error, object) => {
  220. if (!error && object && object.username) {
  221. this.setState({username: object.username});
  222. this.loadConversations(object.username);
  223. }
  224. });
  225. // 组件挂载完成后检查是否有更新,只针对Android平台检查
  226. if (!this.state.checkedUpgrade) {
  227. if (Platform.OS === 'android') {
  228. UpgradeModule.getVersionCodeName((versionCode, versionName) => {
  229. if (versionCode > 0 && !Utils.isEmpty(versionName)) {
  230. // 请求服务器查询更新
  231. let url = 'http://app.yubo725.top/upgrade?versionCode=' + versionCode + '&versionName=' + versionName;
  232. fetch(url).then((res) => res.json())
  233. .then((json) => {
  234. if (json != null && json.code == 1) {
  235. // 有新版本
  236. let data = json.msg;
  237. if (data != null) {
  238. let newVersionCode = data.versionCode;
  239. let newVersionName = data.versionName;
  240. let newVersionDesc = data.versionDesc;
  241. let downUrl = data.downUrl;
  242. let content = "版本号:" + newVersionCode + "\n\n版本名称:" + newVersionName + "\n\n更新说明:" + newVersionDesc;
  243. this.setState({upgradeContent: content}, () => {
  244. // 显示更新dialog
  245. this.refs.upgradeDialog.showModal();
  246. });
  247. }
  248. }
  249. }).catch((e) => {
  250. })
  251. }
  252. })
  253. }
  254. this.setState({checkedUpgrade: true});
  255. }
  256. }
  257. componentWillUnmount() {
  258. this.unregisterListeners();
  259. }
  260. renderItem = (data) => {
  261. let lastTime = data.item.lastTime;
  262. let lastMsg = data.item.messages[data.item.messages.length - 1];
  263. let contactId = lastMsg.from;
  264. if (contactId == this.state.username) {
  265. contactId = lastMsg.to;
  266. }
  267. let nick = data.item.nick;
  268. if (Utils.isEmpty(nick)) {
  269. nick = contactId;
  270. }
  271. let lastMsgContent = '';
  272. if (lastMsg.msgType == 'txt') {
  273. lastMsgContent = lastMsg.data;
  274. } else if (lastMsg.msgType == 'img') {
  275. lastMsgContent = '[图片]';
  276. }
  277. let avatar = require('./images/ic_list_icon.png');
  278. if (data.item.avatar != null) {
  279. avatar = {uri: data.item.avatar};
  280. }
  281. return (
  282. <View>
  283. <TouchableHighlight underlayColor={Global.touchableHighlightColor}
  284. onPress={() => {
  285. this.props.navigation.navigate('Chatting', {
  286. 'contactId': contactId,
  287. 'name': nick,
  288. 'avatar': avatar
  289. })
  290. }}>
  291. <View style={styles.listItemContainer}>
  292. <Image source={avatar} style={{width: 50, height: 50}}/>
  293. <View style={styles.listItemTextContainer}>
  294. <View style={styles.listItemSubContainer}>
  295. <Text numberOfLines={1} style={styles.listItemTitle}>{nick}</Text>
  296. <Text numberOfLines={1} style={styles.listItemTime}>{TimeUtil.formatChatTime(lastTime)}</Text>
  297. </View>
  298. <View style={styles.listItemSubContainer}>
  299. <Text numberOfLines={1} style={styles.listItemSubtitle}>{lastMsgContent}</Text>
  300. {
  301. data.item.unreadCount > 0 ? (
  302. <View style={styles.redDot}>
  303. <Text style={styles.redDotText}>{data.item.unreadCount}</Text>
  304. </View>
  305. ) : ( null )
  306. }
  307. </View>
  308. </View>
  309. </View>
  310. </TouchableHighlight>
  311. <View style={styles.divider}/>
  312. </View>
  313. );
  314. }
  315. }
  316. const styles = StyleSheet.create({
  317. container: {
  318. flex: 1,
  319. flexDirection: 'column',
  320. justifyContent: 'center',
  321. alignItems: 'center',
  322. },
  323. divider: {
  324. width: width,
  325. height: 1 / PixelRatio.get(),
  326. backgroundColor: Global.dividerColor
  327. },
  328. content: {
  329. flex: 1,
  330. width: width,
  331. flexDirection: 'column',
  332. justifyContent: 'center',
  333. alignItems: 'center',
  334. backgroundColor: Global.pageBackgroundColor
  335. },
  336. listItemContainer: {
  337. flexDirection: 'row',
  338. width: width,
  339. paddingLeft: 15,
  340. paddingRight: 15,
  341. paddingTop: 10,
  342. paddingBottom: 10,
  343. alignItems: 'center',
  344. backgroundColor: '#FFFFFF'
  345. },
  346. listItemTextContainer: {
  347. flexDirection: 'column',
  348. flex: 1,
  349. paddingLeft: 15,
  350. },
  351. listItemSubContainer: {
  352. flexDirection: 'row',
  353. alignItems: 'center',
  354. },
  355. listItemTitle: {
  356. color: '#333333',
  357. fontSize: 16,
  358. flex: 1,
  359. },
  360. listItemTime: {
  361. color: '#999999',
  362. fontSize: 12,
  363. },
  364. listItemSubtitle: {
  365. color: '#999999',
  366. fontSize: 14,
  367. marginTop: 3,
  368. flex: 1,
  369. },
  370. redDot: {
  371. borderRadius: 90,
  372. width: 18,
  373. height: 18,
  374. backgroundColor: '#FF0000',
  375. justifyContent: 'center',
  376. alignItems: 'center'
  377. },
  378. redDotText: {
  379. color: '#FFFFFF',
  380. fontSize: 14,
  381. },
  382. tabBarIcon: {
  383. width: 24,
  384. height: 24,
  385. },
  386. emptyHintText: {
  387. fontSize: 18,
  388. color: '#999999'
  389. }
  390. });
  391. const tabNavigatorScreen = TabNavigator({
  392. Home: {screen: HomeScreen},
  393. Contacts: {screen: ContactsScreen},
  394. Find: {screen: FindScreen},
  395. Me: {screen: MeScreen}
  396. }, {
  397. tabBarOptions: {
  398. activeTintColor: '#45C018',
  399. inactiveTintColor: '#999999',
  400. showIcon: true,
  401. labelStyle: {
  402. fontSize: 12,
  403. marginTop: 0,
  404. marginBottom: 0,
  405. },
  406. style: {
  407. marginBottom: -2,
  408. backgroundColor: '#FCFCFC',
  409. },
  410. tabStyle: {}
  411. },
  412. tabBarPosition: 'bottom',
  413. });
  414. const MyApp = StackNavigator({
  415. Splash: {screen: SplashScreen},
  416. Home: {screen: tabNavigatorScreen},
  417. Search: {screen: SearchScreen},
  418. ContactDetail: {screen: ContactDetailScreen},
  419. Chatting: {screen: ChattingScreen},
  420. Moment: {screen: MomentScreen},
  421. Scan: {screen: ScanScreen},
  422. ScanResult: {screen: ScanResultScreen},
  423. Shopping: {screen: ShoppingScreen},
  424. CardPackage: {screen: CardPackageScreen},
  425. Login: {screen: LoginScreen},
  426. Register: {screen: RegisterScreen},
  427. NewFriend: {screen: NewFriendsScreen},
  428. PersonInfo: {screen: PersonInfoScreen},
  429. PublishMoment: {screen: PublishMomentScreen},
  430. ImageShow: {screen: ImageShowScreen},
  431. Shake: {screen: ShakeScreen},
  432. Settings: {screen: SettingsScreen}
  433. }, {
  434. headerMode: 'none', // 此参数设置不渲染顶部的导航条
  435. });
  436. AppRegistry.registerComponent('RNWeChat', () => MyApp);