diff options
Diffstat (limited to 'frontend/app/screens/messaging')
| -rw-r--r-- | frontend/app/screens/messaging/chat.js | 214 | ||||
| -rw-r--r-- | frontend/app/screens/messaging/chatList.js | 147 | ||||
| -rw-r--r-- | frontend/app/screens/messaging/comments.js | 98 | ||||
| -rw-r--r-- | frontend/app/screens/messaging/index.js | 3 |
4 files changed, 462 insertions, 0 deletions
diff --git a/frontend/app/screens/messaging/chat.js b/frontend/app/screens/messaging/chat.js new file mode 100644 index 0000000..c6b0b5e --- /dev/null +++ b/frontend/app/screens/messaging/chat.js @@ -0,0 +1,214 @@ +import React from 'react'; +import { + FlatList, + View, + Platform, + Image, + TouchableOpacity, + Keyboard, + InteractionManager, +} from 'react-native'; +import { + RkButton, + RkText, + RkTextInput, + RkAvoidKeyboard, + RkStyleSheet, + RkTheme, +} from 'react-native-ui-kitten'; +import _ from 'lodash'; +import { FontAwesome } from '../../assets/icons'; +import { data } from '../../data'; +import { Avatar } from '../../components/avatar'; +import { scale } from '../../utils/scale'; +import NavigationType from '../../config/navigation/propTypes'; + +const moment = require('moment'); + +export class Chat extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = ({ navigation }) => { + const userId = navigation.state.params ? navigation.state.params.userId : undefined; + const user = data.getUser(userId); + return ({ + headerTitle: Chat.renderNavigationTitle(navigation, user), + headerRight: Chat.renderNavigationAvatar(navigation, user), + }); + }; + + constructor(props) { + super(props); + const userId = this.props.navigation.getParam('userId', undefined); + this.state = { + data: data.getConversation(userId), + }; + } + + componentDidMount() { + InteractionManager.runAfterInteractions(() => { + this.listRef.scrollToEnd(); + }); + } + + setListRef = (ref) => { + this.listRef = ref; + }; + + extractItemKey = (item) => `${item.id}`; + + scrollToEnd = () => { + if (Platform.OS === 'ios') { + this.listRef.scrollToEnd(); + } else { + _.delay(this.listRef.scrollToEnd, 100); + } + }; + + onInputChanged = (text) => { + this.setState({ message: text }); + }; + + onSendButtonPressed = () => { + if (!this.state.message) { + return; + } + this.state.data.messages.push({ + id: this.state.data.messages.length, time: 0, type: 'out', text: this.state.message, + }); + this.setState({ message: '' }); + this.scrollToEnd(true); + }; + + static onNavigationTitlePressed = (navigation, user) => { + navigation.navigate('ProfileV1', { id: user.id }); + }; + + static onNavigationAvatarPressed = (navigation, user) => { + navigation.navigate('ProfileV1', { id: user.id }); + }; + + static renderNavigationTitle = (navigation, user) => ( + <TouchableOpacity onPress={() => Chat.onNavigationTitlePressed(navigation, user)}> + <View style={styles.header}> + <RkText rkType='header5'>{`${user.firstName} ${user.lastName}`}</RkText> + <RkText rkType='secondary3 secondaryColor'>Online</RkText> + </View> + </TouchableOpacity> + ); + + static renderNavigationAvatar = (navigation, user) => ( + <TouchableOpacity onPress={() => Chat.onNavigationAvatarPressed(navigation, user)}> + <Avatar style={styles.avatar} rkType='small' img={user.photo} /> + </TouchableOpacity> + ); + + renderDate = (date) => ( + <RkText style={styles.time} rkType='secondary7 hintColor'> + {moment().add(date, 'seconds').format('LT')} + </RkText> + ); + + renderItem = ({ item }) => { + const isIncoming = item.type === 'in'; + const backgroundColor = isIncoming + ? RkTheme.current.colors.chat.messageInBackground + : RkTheme.current.colors.chat.messageOutBackground; + const itemStyle = isIncoming ? styles.itemIn : styles.itemOut; + + return ( + <View style={[styles.item, itemStyle]}> + {!isIncoming && this.renderDate(item.time)} + <View style={[styles.balloon, { backgroundColor }]}> + <RkText rkType='primary2 mediumLine chat' style={{ paddingTop: 5 }}>{item.text}</RkText> + </View> + {isIncoming && this.renderDate(item.time)} + </View> + ); + }; + + render = () => ( + <RkAvoidKeyboard + style={styles.container} + onResponderRelease={Keyboard.dismiss}> + <FlatList + ref={this.setListRef} + extraData={this.state} + style={styles.list} + data={this.state.data.messages} + keyExtractor={this.extractItemKey} + renderItem={this.renderItem} + /> + <View style={styles.footer}> + <RkButton style={styles.plus} rkType='clear'> + <RkText rkType='awesome secondaryColor'>{FontAwesome.plus}</RkText> + </RkButton> + <RkTextInput + onFocus={this.scrollToEnd} + onBlur={this.scrollToEnd} + onChangeText={this.onInputChanged} + value={this.state.message} + rkType='row sticker' + placeholder="Add a comment..." + /> + <RkButton onPress={this.onSendButtonPressed} style={styles.send} rkType='circle highlight'> + <Image source={require('../../assets/icons/sendIcon.png')} /> + </RkButton> + </View> + </RkAvoidKeyboard> + + ) +} + +const styles = RkStyleSheet.create(theme => ({ + header: { + alignItems: 'center', + }, + avatar: { + marginRight: 16, + }, + container: { + flex: 1, + backgroundColor: theme.colors.screen.base, + }, + list: { + paddingHorizontal: 17, + }, + footer: { + flexDirection: 'row', + minHeight: 60, + padding: 10, + backgroundColor: theme.colors.screen.alter, + }, + item: { + marginVertical: 14, + flex: 1, + flexDirection: 'row', + }, + itemIn: {}, + itemOut: { + alignSelf: 'flex-end', + }, + balloon: { + maxWidth: scale(250), + paddingHorizontal: 15, + paddingTop: 10, + paddingBottom: 15, + borderRadius: 20, + }, + time: { + alignSelf: 'flex-end', + margin: 15, + }, + plus: { + paddingVertical: 10, + paddingHorizontal: 10, + marginRight: 7, + }, + send: { + width: 40, + height: 40, + marginLeft: 10, + }, +})); diff --git a/frontend/app/screens/messaging/chatList.js b/frontend/app/screens/messaging/chatList.js new file mode 100644 index 0000000..36cde5c --- /dev/null +++ b/frontend/app/screens/messaging/chatList.js @@ -0,0 +1,147 @@ +import React from 'react'; +import { + FlatList, + View, + StyleSheet, + TouchableOpacity, +} from 'react-native'; +import _ from 'lodash'; +import { + RkStyleSheet, + RkText, + RkTextInput, +} from 'react-native-ui-kitten'; +import { Avatar } from '../../components'; +import { FontAwesome } from '../../assets/icons'; +import { data } from '../../data'; +import NavigationType from '../../config/navigation/propTypes'; + +const moment = require('moment'); + +export class ChatList extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Chats List'.toUpperCase(), + }; + + state = { + data: { + original: data.getChatList(), + filtered: data.getChatList(), + }, + }; + + extractItemKey = (item) => `${item.withUser.id}`; + + onInputChanged = (event) => { + const pattern = new RegExp(event.nativeEvent.text, 'i'); + const chats = _.filter(this.state.data.original, chat => { + const filterResult = { + firstName: chat.withUser.firstName.search(pattern), + lastName: chat.withUser.lastName.search(pattern), + }; + return filterResult.firstName !== -1 || filterResult.lastName !== -1 ? chat : undefined; + }); + this.setState({ + data: { + original: this.state.data.original, + filtered: chats, + }, + }); + }; + + onItemPressed = (item) => { + const navigationParams = { userId: item.withUser.id }; + this.props.navigation.navigate('Chat', navigationParams); + }; + + renderSeparator = () => ( + <View style={styles.separator} /> + ); + + renderInputLabel = () => ( + <RkText rkType='awesome'>{FontAwesome.search}</RkText> + ); + + renderHeader = () => ( + <View style={styles.searchContainer}> + <RkTextInput + autoCapitalize='none' + autoCorrect={false} + onChange={this.onInputChanged} + label={this.renderInputLabel()} + rkType='row' + placeholder='Search' + /> + </View> + ); + + renderItem = ({ item }) => { + const last = item.messages[item.messages.length - 1]; + return ( + <TouchableOpacity onPress={() => this.onItemPressed(item)}> + <View style={styles.container}> + <Avatar rkType='circle' style={styles.avatar} img={item.withUser.photo} /> + <View style={styles.content}> + <View style={styles.contentHeader}> + <RkText rkType='header5'>{`${item.withUser.firstName} ${item.withUser.lastName}`}</RkText> + <RkText rkType='secondary4 hintColor'> + {moment().add(last.time, 'seconds').format('LT')} + </RkText> + </View> + <RkText numberOfLines={2} rkType='primary3 mediumLine' style={{ paddingTop: 5 }}> + {last.text} + </RkText> + </View> + </View> + </TouchableOpacity> + ); + }; + + render = () => ( + <FlatList + style={styles.root} + data={this.state.data.filtered} + extraData={this.state} + ListHeaderComponent={this.renderHeader} + ItemSeparatorComponent={this.renderSeparator} + keyExtractor={this.extractItemKey} + renderItem={this.renderItem} + /> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + searchContainer: { + backgroundColor: theme.colors.screen.bold, + paddingHorizontal: 16, + paddingVertical: 10, + height: 60, + alignItems: 'center', + }, + container: { + paddingLeft: 19, + paddingRight: 16, + paddingBottom: 12, + paddingTop: 7, + flexDirection: 'row', + }, + content: { + marginLeft: 16, + flex: 1, + }, + contentHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + marginBottom: 6, + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: theme.colors.border.base, + }, +})); diff --git a/frontend/app/screens/messaging/comments.js b/frontend/app/screens/messaging/comments.js new file mode 100644 index 0000000..3b772c5 --- /dev/null +++ b/frontend/app/screens/messaging/comments.js @@ -0,0 +1,98 @@ +import React from 'react'; +import { + FlatList, + View, + StyleSheet, + TouchableOpacity, +} from 'react-native'; +import { + RkStyleSheet, + RkText, +} from 'react-native-ui-kitten'; +import { Avatar } from '../../components'; +import { data } from '../../data'; +import NavigationType from '../../config/navigation/propTypes'; + +const moment = require('moment'); + +export class Comments extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Comments'.toUpperCase(), + }; + + constructor(props) { + super(props); + const postId = this.props.navigation.getParam('postId', undefined); + this.state = { + data: data.getComments(postId), + }; + } + + extractItemKey = (item) => `${item.id}`; + + onItemPressed = (item) => { + const navigationParams = { id: item.user.id }; + this.props.navigation.navigate('ProfileV1', navigationParams); + }; + + renderSeparator = () => ( + <View style={styles.separator} /> + ); + + renderItem = ({ item }) => ( + <View style={styles.container}> + <TouchableOpacity onPress={() => this.onItemPressed(item)}> + <Avatar rkType='circle' style={styles.avatar} img={item.user.photo} /> + </TouchableOpacity> + <View style={styles.content}> + <View style={styles.contentHeader}> + <RkText rkType='header5'>{`${item.user.firstName} ${item.user.lastName}`}</RkText> + <RkText rkType='secondary4 hintColor'> + {moment().add(item.time, 'seconds').format('LT')} + </RkText> + </View> + <RkText rkType='primary3 mediumLine'>{item.text}</RkText> + </View> + </View> + ); + + render = () => ( + <FlatList + style={styles.root} + data={this.state.data} + extraData={this.state} + ItemSeparatorComponent={this.renderSeparator} + keyExtractor={this.extractItemKey} + renderItem={this.renderItem} + /> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + container: { + paddingLeft: 19, + paddingRight: 16, + paddingVertical: 12, + flexDirection: 'row', + alignItems: 'flex-start', + }, + content: { + marginLeft: 16, + flex: 1, + }, + contentHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + marginBottom: 6, + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: theme.colors.border.base, + }, +})); diff --git a/frontend/app/screens/messaging/index.js b/frontend/app/screens/messaging/index.js new file mode 100644 index 0000000..0d39244 --- /dev/null +++ b/frontend/app/screens/messaging/index.js @@ -0,0 +1,3 @@ +export * from './chat'; +export * from './chatList'; +export * from './comments'; |