Skip to content

Commit

Permalink
Extracted auto complete pop up from ComposeText. (#717)
Browse files Browse the repository at this point in the history
* Extracted auto complete pop up from ComposeText.

Don't show them between topic and message field.
Show them above topic field.

* Improved styles of popup.

- Change background color with theme.
- List looks like they are part of ComposeBox (like messenger).
- Use RawLabel for text in emoji row.

* Set max height of pop to 1/4th height of window.

almost half is taken by keyboard and compose box and another 1/4th should be kept for nav bar, status bar and some part of message list.
  • Loading branch information
jainkuniya authored and borisyankov committed Jun 23, 2017
1 parent 856b2ab commit 3387037
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 64 deletions.
41 changes: 41 additions & 0 deletions src/autocomplete/AutoCompleteView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* @flow */
import React from 'react';
import { View } from 'react-native';

import { MatchResult } from '../types';
import getAutocompletedText from './getAutocompletedText';
import EmojiAutocomplete from './EmojiAutocomplete';
import StreamAutocomplete from './StreamAutocomplete';
import PeopleAutocomplete from './PeopleAutocomplete';

type Props = {
text: string,
onAutocomplete: (input: string) => void,
};

export default class AutoCompleteView extends React.Component {

props: Props;

handleAutocomplete = (autocomplete: string) => {
const { text, onAutocomplete } = this.props;
const newText = getAutocompletedText(text, autocomplete);
onAutocomplete(newText);
}

render() {
const { text } = this.props;
const lastword: MatchResult = text.match(/\b(\w+)$/);
const lastWordPrefix = lastword && lastword.index && text[lastword.index - 1];
return (
<View>
{lastWordPrefix === ':' && lastword &&
<EmojiAutocomplete filter={lastword[0]} onAutocomplete={this.handleAutocomplete} />}
{lastWordPrefix === '#' && lastword &&
<StreamAutocomplete filter={lastword[0]} onAutocomplete={this.handleAutocomplete} />}
{lastWordPrefix === '@' && lastword &&
<PeopleAutocomplete filter={lastword[0]} onAutocomplete={this.handleAutocomplete} />}
</View>
);
}
}
23 changes: 9 additions & 14 deletions src/common/Popup.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
/* @flow */
import React, { Component } from 'react';
import { View, StyleSheet } from 'react-native';
import React from 'react';
import { View, Dimensions } from 'react-native';

import { BORDER_COLOR } from '../styles';
export default class Popup extends React.Component {

const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
borderColor: BORDER_COLOR,
borderWidth: 1,
borderRadius: 2,
},
});
static contextTypes = {
styles: () => null,
};

export default class Popup extends Component {
render() {
const { height } = Dimensions.get('window');
return (
<View
style={styles.container}
maxHeight={250}
style={this.context.styles.backgroundColor}
maxHeight={height / 4}
>
{this.props.children}
</View>
Expand Down
11 changes: 10 additions & 1 deletion src/compose/ComposeBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import CameraRollView from './CameraRollView';
import { getLastTopicInActiveNarrow } from '../chat/chatSelectors';
import StreamBox from './ModeViews/StreamBox';
import { isTopicNarrow, isStreamNarrow } from '../utils/narrow';
import AutoCompleteView from '../autocomplete/AutoCompleteView';

type Props = {
onSend: (content: string) => void,
Expand Down Expand Up @@ -38,11 +39,13 @@ class ComposeBox extends React.Component {
state: {
optionSelected: number,
operator: string,
text: string,
};

state = {
optionSelected: 0,
operator: '',
text: '',
};

setTopic = (operator: string) => this.setState({ operator });
Expand All @@ -52,12 +55,16 @@ class ComposeBox extends React.Component {

render() {
const { styles } = this.context;
const { optionSelected, operator } = this.state;
const { optionSelected, operator, text } = this.state;
const { auth, narrow, users, lastTopic } = this.props;
const ActiveComposeComponent = composeComponents[optionSelected];

return (
<View style={this.context.styles.composeBox}>
<AutoCompleteView
text={text}
onAutocomplete={(input) => this.setState({ text: input })}
/>
{(isTopicNarrow(narrow) || isStreamNarrow(narrow)) &&
<View style={styles.topicWrapper}>
<StreamBox
Expand All @@ -77,6 +84,8 @@ class ComposeBox extends React.Component {
narrow={narrow}
operator={operator}
users={users}
text={text}
handleChangeText={(input) => this.setState({ text: input })}
/>
</View>
);
Expand Down
72 changes: 26 additions & 46 deletions src/compose/ComposeText.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@
import React from 'react';
import { StyleSheet, View, ScrollView, TextInput } from 'react-native';

import { MatchResult, Auth, Narrow, User } from '../types';
import { Auth, Narrow, User } from '../types';
import { Input } from '../common';
import { isStreamNarrow, isTopicNarrow, isPrivateOrGroupNarrow } from '../utils/narrow';
import { registerUserInputActivity } from '../utils/activity';
import sendMessage from '../api/sendMessage';
import SendButton from './SendButton';
import getAutocompletedText from '../autocomplete/getAutocompletedText';
import EmojiAutocomplete from '../autocomplete/EmojiAutocomplete';
import StreamAutocomplete from '../autocomplete/StreamAutocomplete';
import PeopleAutocomplete from '../autocomplete/PeopleAutocomplete';
import getComposeInputPlaceholder from './getComposeInputPlaceholder';

const MIN_HEIGHT = 38;
Expand All @@ -32,6 +28,8 @@ type Props = {
narrow: Narrow,
operator: string,
users: User[],
text: string,
handleChangeText: (input: string) => void,
};

export default class ComposeText extends React.Component {
Expand All @@ -45,21 +43,18 @@ export default class ComposeText extends React.Component {

state: {
editing: boolean,
text: string,
autocomplete: boolean,
contentHeight: number,
}

state = {
editing: false,
text: '',
autocomplete: false,
contentHeight: MIN_HEIGHT,
};

handleSend = () => {
const { auth, narrow, operator } = this.props;
const { text } = this.state;
const { auth, narrow, operator, text } = this.props;

if (isPrivateOrGroupNarrow(narrow)) {
sendMessage(auth, 'private', narrow[0].operand, '', text);
Expand All @@ -81,59 +76,44 @@ export default class ComposeText extends React.Component {
clearInput = () => {
this.textInput.clear();
this.setState({
text: '',
contentHeight: MIN_HEIGHT,
});
this.props.handleChangeText('');
}

handleOnChange = (event: Object) =>
this.setState({ contentHeight: event.nativeEvent.contentSize.height });

handleChangeText = (text: string) => {
const { auth } = this.props;
const { auth, handleChangeText } = this.props;
registerUserInputActivity(auth);
this.setState({ text });
}

handleAutocomplete = (autocomplete: string) => {
const text = getAutocompletedText(this.state.text, autocomplete);
this.textInput.setNativeProps({ text });
this.setState({ text });
handleChangeText(text);
}

render() {
const { narrow, auth, users } = this.props;
const { contentHeight, text } = this.state;
const { narrow, auth, users, text } = this.props;
const { contentHeight } = this.state;
const height = Math.min(Math.max(MIN_HEIGHT, contentHeight), MAX_HEIGHT);
const lastword: MatchResult = text.match(/\b(\w+)$/);
const lastWordPrefix = lastword && lastword.index && text[lastword.index - 1];

return (
<View>
{lastWordPrefix === ':' && lastword &&
<EmojiAutocomplete filter={lastword[0]} onAutocomplete={this.handleAutocomplete} />}
{lastWordPrefix === '#' && lastword &&
<StreamAutocomplete filter={lastword[0]} onAutocomplete={this.handleAutocomplete} />}
{lastWordPrefix === '@' && lastword &&
<PeopleAutocomplete filter={lastword[0]} onAutocomplete={this.handleAutocomplete} />}
<View style={componentStyles.wrapper}>
<ScrollView style={{ height }} contentContainerStyle={componentStyles.messageBox}>
<Input
style={this.context.styles.composeText}
textInputRef={component => { this.textInput = component; }}
multiline
underlineColorAndroid="transparent"
height={contentHeight}
onChange={this.handleOnChange}
onChangeText={this.handleChangeText}
placeholder={getComposeInputPlaceholder(narrow, auth.email, users)}
/>
</ScrollView>
<SendButton
disabled={text.length === 0}
onPress={this.handleSend}
<View style={componentStyles.wrapper}>
<ScrollView style={{ height }} contentContainerStyle={componentStyles.messageBox}>
<Input
value={text}
style={this.context.styles.composeText}
textInputRef={component => { this.textInput = component; }}
multiline
underlineColorAndroid="transparent"
height={contentHeight}
onChange={this.handleOnChange}
onChangeText={this.handleChangeText}
placeholder={getComposeInputPlaceholder(narrow, auth.email, users)}
/>
</View>
</ScrollView>
<SendButton
disabled={text.length === 0}
onPress={this.handleSend}
/>
</View>
);
}
Expand Down
8 changes: 5 additions & 3 deletions src/emoji/EmojiRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
import React from 'react';
import {
StyleSheet,
Text,
View,
} from 'react-native';

import { Touchable } from '../common';
import { RawLabel, Touchable } from '../common';
import Emoji from '../emoji/Emoji';

const styles = StyleSheet.create({
Expand All @@ -33,7 +32,10 @@ export default class EmojiRow extends React.Component {
<Touchable onPress={onPress}>
<View style={styles.emojiRow}>
<Emoji name={name} size={15} />
<Text style={styles.text}>{name}</Text>
<RawLabel
style={styles.text}
text={name}
/>
</View>
</Touchable>
);
Expand Down

0 comments on commit 3387037

Please sign in to comment.