Add hint box with dynamic join button
This commit is contained in:
parent
547ddee3a5
commit
e23d4317eb
|
@ -49,6 +49,7 @@
|
|||
"appDescription": "Go ahead, video chat with the whole team. In fact, invite everyone you know. __app__ is a fully encrypted, 100% open source video conferencing solution that you can use all day, every day, for free — with no account needed.",
|
||||
"audioOnlyLabel": "Voice",
|
||||
"go": "GO",
|
||||
"hintText": "Enter a room name you want to join to, or simply create a new room name, eg. MeetingWithJohn",
|
||||
"join": "JOIN",
|
||||
"privacy": "Privacy",
|
||||
"roomname": "Enter room name",
|
||||
|
|
|
@ -5,12 +5,21 @@ import { ActivityIndicator } from 'react-native';
|
|||
|
||||
import { ColorPalette } from '../../../styles';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Prop to set the size of the indicator. This is the same as the
|
||||
* prop of the native component.
|
||||
*/
|
||||
size: 'large' | 'small'
|
||||
};
|
||||
|
||||
/**
|
||||
* An animated, large react-native {@link ActivityIndicator} which is considered
|
||||
* a suitable visualization of long-running processes with indeterminate amounts
|
||||
* of work to be done.
|
||||
*/
|
||||
export default class LoadingIndicator extends Component<*> {
|
||||
export default class LoadingIndicator extends Component<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
|
@ -22,7 +31,7 @@ export default class LoadingIndicator extends Component<*> {
|
|||
<ActivityIndicator
|
||||
animating = { true }
|
||||
color = { ColorPalette.white }
|
||||
size = { 'large' }
|
||||
size = { this.props.size || 'large' }
|
||||
{ ...this.props } />
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,11 @@ import { appNavigate } from '../../app';
|
|||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Indicates if the list is disabled or not.
|
||||
*/
|
||||
disabled: boolean,
|
||||
|
||||
/**
|
||||
* The redux store's {@code dispatch} function.
|
||||
*/
|
||||
|
@ -31,7 +36,9 @@ export default class AbstractRecentList extends Component<Props> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_onJoin(room) {
|
||||
room && this.props.dispatch(appNavigate(room));
|
||||
const { disabled, dispatch } = this.props;
|
||||
|
||||
!disabled && room && dispatch(appNavigate(room));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,8 +28,8 @@ class RecentList extends AbstractRecentList {
|
|||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._getAvatarStyle = this._getAvatarStyle.bind(this);
|
||||
|
@ -47,16 +47,22 @@ class RecentList extends AbstractRecentList {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
if (!this.props || !this.props._recentList) {
|
||||
const { _recentList, disabled } = this.props;
|
||||
|
||||
if (!_recentList) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const listViewDataSource
|
||||
= this.dataSource.cloneWithRows(
|
||||
getRecentRooms(this.props._recentList));
|
||||
getRecentRooms(_recentList));
|
||||
|
||||
return (
|
||||
<View style = { styles.container }>
|
||||
<View
|
||||
style = { [
|
||||
styles.container,
|
||||
disabled ? styles.containerDisabled : null
|
||||
] }>
|
||||
<ListView
|
||||
dataSource = { listViewDataSource }
|
||||
enableEmptySections = { true }
|
||||
|
|
|
@ -87,6 +87,13 @@ export default createStyleSheet({
|
|||
flex: 1
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the container disabled.
|
||||
*/
|
||||
containerDisabled: {
|
||||
opacity: 0.2
|
||||
},
|
||||
|
||||
/**
|
||||
* Second line of the list (date). May be extended with server name later.
|
||||
*/
|
||||
|
|
|
@ -14,6 +14,11 @@ import { generateRoomWithoutSeparator } from '../functions';
|
|||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Boolean to indicate if the room field is focused or not.
|
||||
*/
|
||||
_fieldFocused: boolean,
|
||||
|
||||
/**
|
||||
* The user's profile.
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Animated,
|
||||
Keyboard,
|
||||
SafeAreaView,
|
||||
Switch,
|
||||
|
@ -59,8 +60,13 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state.hintBoxAnimation = new Animated.Value(0);
|
||||
|
||||
this._getHintBoxStyle = this._getHintBoxStyle.bind(this);
|
||||
this._onFieldFocusChange = this._onFieldFocusChange.bind(this);
|
||||
this._onShowSideBar = this._onShowSideBar.bind(this);
|
||||
this._onStartAudioOnlyChange = this._onStartAudioOnlyChange.bind(this);
|
||||
this._renderHintBox = this._renderHintBox.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,7 +130,9 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
autoComplete = { false }
|
||||
autoCorrect = { false }
|
||||
autoFocus = { false }
|
||||
onBlur = { this._onFieldFocusChange(false) }
|
||||
onChangeText = { this._onRoomChange }
|
||||
onFocus = { this._onFieldFocusChange(true) }
|
||||
onSubmitEditing = { this._onJoin }
|
||||
placeholder = { t('welcomepage.roomname') }
|
||||
placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
|
||||
|
@ -133,9 +141,9 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
underlineColorAndroid = 'transparent'
|
||||
value = { this.state.room } />
|
||||
{
|
||||
this._renderJoinButton()
|
||||
this._renderHintBox()
|
||||
}
|
||||
<RecentList />
|
||||
<RecentList disabled = { this.state._fieldFocused } />
|
||||
</SafeAreaView>
|
||||
<AppSettings />
|
||||
</View>
|
||||
|
@ -144,6 +152,50 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a style array to handle the hint box animation.
|
||||
*
|
||||
* @private
|
||||
* @returns {Array<Object>}
|
||||
*/
|
||||
_getHintBoxStyle() {
|
||||
return [
|
||||
styles.hintContainer,
|
||||
{
|
||||
opacity: this.state.hintBoxAnimation
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for when the room field's focus changes so the hint box
|
||||
* must be rendered or removed.
|
||||
*
|
||||
* @private
|
||||
* @param {boolean} focused - The focused state of the field.
|
||||
* @returns {Function}
|
||||
*/
|
||||
_onFieldFocusChange(focused) {
|
||||
return () => {
|
||||
if (focused) {
|
||||
this.setState({
|
||||
_fieldFocused: true
|
||||
});
|
||||
}
|
||||
|
||||
Animated.timing(this.state.hintBoxAnimation, {
|
||||
duration: 300,
|
||||
toValue: focused ? 1 : 0
|
||||
}).start(animationState => {
|
||||
if (animationState.finished && !focused) {
|
||||
this.setState({
|
||||
_fieldFocused: false
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the side bar.
|
||||
*
|
||||
|
@ -171,6 +223,36 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the hint box if necessary.
|
||||
*
|
||||
* @private
|
||||
* @returns {React$Node}
|
||||
*/
|
||||
_renderHintBox() {
|
||||
if (this.state._fieldFocused) {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style = { this._getHintBoxStyle() }>
|
||||
<View style = { styles.hintTextContainer } >
|
||||
<Text>
|
||||
{ t('welcomepage.hintText') }
|
||||
</Text>
|
||||
</View>
|
||||
<View style = { styles.hintButtonContainer } >
|
||||
{
|
||||
this._renderJoinButton()
|
||||
}
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the join button.
|
||||
*
|
||||
|
@ -188,7 +270,9 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
// modify non-native children.
|
||||
children = (
|
||||
<View>
|
||||
<LoadingIndicator color = { styles.buttonText.color } />
|
||||
<LoadingIndicator
|
||||
color = { styles.buttonText.color }
|
||||
size = 'small' />
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
|
@ -201,12 +285,17 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
|
||||
/* eslint-enable no-extra-parens */
|
||||
|
||||
const buttonDisabled = this._isJoinDisabled();
|
||||
|
||||
return (
|
||||
<TouchableHighlight
|
||||
accessibilityLabel = { 'Tap to Join.' }
|
||||
disabled = { this._isJoinDisabled() }
|
||||
disabled = { buttonDisabled }
|
||||
onPress = { this._onJoin }
|
||||
style = { styles.button }
|
||||
style = { [
|
||||
styles.button,
|
||||
buttonDisabled ? styles.buttonDisabled : null
|
||||
] }
|
||||
underlayColor = { ColorPalette.white }>
|
||||
{
|
||||
children
|
||||
|
|
|
@ -54,10 +54,17 @@ export default createStyleSheet({
|
|||
borderColor: ColorPalette.blue,
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
height: 40,
|
||||
height: 30,
|
||||
justifyContent: 'center',
|
||||
marginBottom: BoxModel.margin,
|
||||
marginTop: BoxModel.margin
|
||||
paddingHorizontal: 20
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the button visually disabled.
|
||||
*/
|
||||
buttonDisabled: {
|
||||
backgroundColor: '#cccccc',
|
||||
borderColor: '#999999'
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -66,7 +73,7 @@ export default createStyleSheet({
|
|||
buttonText: {
|
||||
alignSelf: 'center',
|
||||
color: ColorPalette.white,
|
||||
fontSize: 18
|
||||
fontSize: 14
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -86,6 +93,36 @@ export default createStyleSheet({
|
|||
justifyContent: 'space-between'
|
||||
},
|
||||
|
||||
/**
|
||||
* Container for the button on the hint box.
|
||||
*/
|
||||
hintButtonContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end'
|
||||
},
|
||||
|
||||
/**
|
||||
* Container for the text on the hint box.
|
||||
*/
|
||||
hintTextContainer: {
|
||||
marginBottom: 2 * BoxModel.margin
|
||||
},
|
||||
|
||||
/**
|
||||
* Container for the hint box.
|
||||
*/
|
||||
hintContainer: {
|
||||
backgroundColor: ColorPalette.white,
|
||||
borderColor: ColorPalette.white,
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
flexDirection: 'column',
|
||||
marginVertical: 5,
|
||||
overflow: 'hidden',
|
||||
paddingHorizontal: BoxModel.padding,
|
||||
paddingVertical: 2 * BoxModel.padding
|
||||
},
|
||||
|
||||
/**
|
||||
* Container for the items in the side bar.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue