feat(base/ui/native): Use new Input component (#12526)

feat(base/ui/native): replaced react native TextInput component with our native Input component
This commit is contained in:
Calinteodor 2022-11-08 17:46:46 +02:00 committed by GitHub
parent 74cd486232
commit 2c7dc5e40e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 366 additions and 694 deletions

View File

@ -89,7 +89,7 @@
"chat": {
"enter": "Enter room",
"error": "Error: your message was not sent. Reason: {{error}}",
"fieldPlaceHolder": "Type your message here",
"fieldPlaceHolder": "Aa",
"lobbyChatMessageTo": "Lobby chat message to {{recipient}}",
"message": "Message",
"messageAccessibleTitle": "{{user}} says:",
@ -266,7 +266,7 @@
"e2eeWarning": "WARNING: Not all participants in this meeting seem to have support for End-to-End encryption. If you enable it they won't be able to see nor hear you.",
"e2eeWillDisableDueToMaxModeDescription": "WARNING: End-to-End Encryption will be automatically disabled if more participants join the conference.",
"embedMeeting": "Embed meeting",
"enterDisplayName": "Enter your name here",
"enterDisplayName": "Enter your name",
"error": "Error",
"gracefulShutdown": "Our service is currently down for maintenance. Please try again later.",
"grantModeratorDialog": "Are you sure you want to grant moderator rights to {{participantName}}?",
@ -1003,6 +1003,7 @@
"displayName": "Display name",
"displayNamePlaceholderText": "Eg: John Doe",
"email": "Email",
"emailPlaceholderText": "email@example.com",
"goTo": "Go to",
"header": "Settings",
"help": "Help",

View File

@ -73,11 +73,11 @@ const SECTION_LIST_STYLES = {
listSection: {
alignItems: 'center',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
backgroundColor: BaseTheme.palette.ui02,
flex: 1,
flexDirection: 'row',
paddingVertical: 5,
paddingHorizontal: 10
paddingVertical: BaseTheme.spacing[1],
paddingHorizontal: BaseTheme.spacing[2]
},
listSectionText: {
@ -164,5 +164,10 @@ export default {
iconButtonContainerSecondary: {
...iconButtonContainer,
backgroundColor: BaseTheme.palette.action02
},
iconButtonContainerDisabled: {
...iconButtonContainer,
backgroundColor: BaseTheme.palette.disabled01
}
};

View File

@ -44,6 +44,11 @@ const IconButton: React.FC<IIconButtonProps> = ({
rippleColor = tapColor;
}
if (disabled) {
color = BaseTheme.palette.icon03;
iconButtonContainerStyles = styles.iconButtonContainerDisabled;
rippleColor = 'transparent';
}
return (
<TouchableRipple

View File

@ -1,10 +1,15 @@
import React, { useCallback, useState } from 'react';
/* eslint-disable lines-around-comment */
import React, { forwardRef, useCallback, useState } from 'react';
import {
NativeSyntheticEvent,
KeyboardTypeOptions,
NativeSyntheticEvent, ReturnKeyTypeOptions,
StyleProp,
Text,
TextInput,
TextInputChangeEventData,
TextInputFocusEventData, TextInputKeyPressEventData,
TextInputSubmitEditingEventData,
TouchableOpacity,
View,
ViewStyle
@ -18,11 +23,24 @@ import { IInputProps } from '../types';
import styles from './inputStyles';
interface IProps extends IInputProps {
/**
* Custom styles to be applied to the component.
*/
accessibilityLabel?: any;
autoCapitalize?: string | undefined;
autoFocus?: boolean;
blurOnSubmit?: boolean | undefined;
customStyles?: ICustomStyles;
editable?: boolean | undefined;
keyboardType?: KeyboardTypeOptions;
maxLength?: number | undefined;
minHeight?: number | string | undefined;
multiline?: boolean | undefined;
numberOfLines?: number | undefined;
onBlur?: ((e: NativeSyntheticEvent<TextInputFocusEventData>) => void) | undefined;
onFocus?: ((e: NativeSyntheticEvent<TextInputFocusEventData>) => void) | undefined;
onKeyPress?: ((e: NativeSyntheticEvent<TextInputKeyPressEventData>) => void) | undefined;
onSubmitEditing?: (value: string) => void;
returnKeyType?: ReturnKeyTypeOptions | undefined;
secureTextEntry?: boolean | undefined;
textContentType?: any;
}
interface ICustomStyles {
@ -30,17 +48,33 @@ interface ICustomStyles {
input?: Object;
}
const Input = ({
const Input = forwardRef<TextInput, IInputProps>(({
accessibilityLabel,
autoCapitalize,
autoFocus,
blurOnSubmit,
clearable,
customStyles,
disabled,
error,
icon,
keyboardType,
label,
maxLength,
minHeight,
multiline,
numberOfLines,
onBlur,
onChange,
onFocus,
onKeyPress,
onSubmitEditing,
placeholder,
returnKeyType,
secureTextEntry,
textContentType,
value
}: IProps) => {
}: IProps, ref) => {
const [ focused, setFocused ] = useState(false);
const handleChange = useCallback((e: NativeSyntheticEvent<TextInputChangeEventData>) => {
const { nativeEvent: { text } } = e;
@ -52,38 +86,71 @@ const Input = ({
onChange?.('');
}, []);
const blur = useCallback(() => {
const handleBlur = useCallback((e: NativeSyntheticEvent<TextInputFocusEventData>) => {
setFocused(false);
onBlur?.(e);
}, []);
const focus = useCallback(() => {
const handleFocus = useCallback((e: NativeSyntheticEvent<TextInputFocusEventData>) => {
setFocused(true);
onFocus?.(e);
}, []);
const handleKeyPress = useCallback((e: NativeSyntheticEvent<TextInputKeyPressEventData>) => {
onKeyPress?.(e);
}, []);
const handleSubmitEditing = useCallback((e: NativeSyntheticEvent<TextInputSubmitEditingEventData>) => {
const { nativeEvent: { text } } = e;
onSubmitEditing?.(text);
}, []);
return (<View style = { [ styles.inputContainer, customStyles?.container ] }>
{label && <Text style = { styles.label }>{label}</Text>}
{label && <Text style = { styles.label }>{ label }</Text>}
<View style = { styles.fieldContainer as StyleProp<ViewStyle> }>
{icon && <Icon
size = { 22 }
src = { icon }
style = { styles.icon } />}
<TextInput
accessibilityLabel = { accessibilityLabel }
// @ts-ignore
autoCapitalize = { autoCapitalize }
autoComplete = { 'off' }
autoCorrect = { false }
autoFocus = { autoFocus }
blurOnSubmit = { blurOnSubmit }
editable = { !disabled }
onBlur = { blur }
keyboardType = { keyboardType }
maxLength = { maxLength }
minHeight = { minHeight }
multiline = { multiline }
numberOfLines = { numberOfLines }
onBlur = { handleBlur }
onChange = { handleChange }
onFocus = { focus }
onFocus = { handleFocus }
onKeyPress = { handleKeyPress }
onSubmitEditing = { handleSubmitEditing }
placeholder = { placeholder }
placeholderTextColor = { BaseTheme.palette.text02 }
style = { [ styles.input,
disabled && styles.inputDisabled,
ref = { ref }
returnKeyType = { returnKeyType }
secureTextEntry = { secureTextEntry }
spellCheck = { false }
style = { [
styles.input,
clearable && styles.clearableInput,
icon && styles.iconInput,
focused && styles.inputFocused,
customStyles?.input,
disabled && styles.inputDisabled,
error && styles.inputError,
customStyles?.input
focused && styles.inputFocused,
icon && styles.iconInput,
multiline && styles.inputMultiline
] }
value = { `${value}` } />
{clearable && !disabled && value !== '' && (
textContentType = { textContentType }
value = { typeof value === 'number' ? `${value}` : value } />
{ clearable && !disabled && value !== '' && (
<TouchableOpacity
onPress = { clearInput }
style = { styles.clearButton as StyleProp<ViewStyle> }>
@ -95,6 +162,6 @@ const Input = ({
)}
</View>
</View>);
};
});
export default Input;

View File

@ -10,7 +10,7 @@ export default {
...BaseTheme.typography.bodyShortRegularLarge,
lineHeight: 0,
color: BaseTheme.palette.text01,
marginBottom: 8
marginBottom: BaseTheme.spacing[2]
},
fieldContainer: {
@ -20,8 +20,8 @@ export default {
icon: {
position: 'absolute',
zIndex: 1,
top: 13,
left: 16
top: 14,
left: 14
},
input: {
@ -32,7 +32,8 @@ export default {
borderWidth: 2,
color: BaseTheme.palette.text01,
paddingHorizontal: BaseTheme.spacing[3],
height: 48
height: BaseTheme.spacing[7],
lineHeight: 20
},
inputDisabled: {
@ -51,6 +52,11 @@ export default {
paddingLeft: BaseTheme.spacing[6]
},
inputMultiline: {
height: BaseTheme.spacing[10],
paddingTop: BaseTheme.spacing[2]
},
clearableInput: {
paddingRight: BaseTheme.spacing[6]
},
@ -60,9 +66,9 @@ export default {
borderWidth: 0,
position: 'absolute',
right: 0,
top: 13,
width: 40,
height: 48
top: 14,
width: BaseTheme.spacing[6],
height: BaseTheme.spacing[7]
},
clearIcon: {

View File

@ -1,11 +1,12 @@
// @flow
import React, { Component } from 'react';
import { Platform, TextInput, TouchableOpacity, View } from 'react-native';
import { Platform } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { translate } from '../../../base/i18n';
import { Icon, IconSend } from '../../../base/icons';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import { IconSend } from '../../../base/icons/svg';
import IconButton from '../../../base/ui/components/native/IconButton';
import Input from '../../../base/ui/components/native/Input';
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
import styles from './styles';
@ -70,32 +71,30 @@ class ChatInputBar extends Component<Props, State> {
*/
render() {
return (
<View
<SafeAreaView
edges = { [ 'bottom' ] }
style = { [
styles.inputBar,
this.state.addPadding ? styles.extraBarPadding : null
] }>
<TextInput
<Input
blurOnSubmit = { false }
customStyles = {{ input: styles.customInput }}
multiline = { false }
onBlur = { this._onFocused(false) }
onChangeText = { this._onChangeText }
onChange = { this._onChangeText }
onFocus = { this._onFocused(true) }
onSubmitEditing = { this._onSubmit }
placeholder = { this.props.t('chat.fieldPlaceHolder') }
placeholderTextColor = { BaseTheme.palette.text03 }
returnKeyType = 'send'
selectionColor = { BaseTheme.palette.text03 }
style = { styles.inputField }
value = { this.state.message } />
{
this.state.showSend && <TouchableOpacity onPress = { this._onSubmit }>
<Icon
src = { IconSend }
style = { styles.sendButtonIcon } />
</TouchableOpacity>
}
</View>
<IconButton
disabled = { !this.state.message }
onPress = { this._onSubmit }
src = { IconSend }
style = { styles.sendButton }
type = { BUTTON_TYPES.PRIMARY } />
</SafeAreaView>
);
}

View File

@ -54,17 +54,14 @@ export default {
inputBar: {
alignItems: 'center',
borderTopColor: 'rgb(209, 219, 231)',
borderTopWidth: 1,
flexDirection: 'row',
paddingBottom: '4%',
paddingHorizontal: BaseTheme.spacing[3]
justifyContent: 'space-between',
marginLeft: BaseTheme.spacing[3],
width: '100%'
},
inputField: {
color: BaseTheme.palette.text01,
flex: 1,
height: 48
customInput: {
width: 280
},
messageBubble: {
@ -96,9 +93,8 @@ export default {
flexDirection: 'row'
},
sendButtonIcon: {
color: BaseTheme.palette.icon01,
fontSize: 22
sendButton: {
marginRight: BaseTheme.spacing[5]
},
/**

View File

@ -5,9 +5,9 @@ import { useDispatch, useSelector } from 'react-redux';
import { createGifSentEvent, sendAnalytics } from '../../../analytics';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import Input from '../../../base/ui/components/native/Input';
import { sendMessage } from '../../../chat/actions.any';
import { goBack } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import ClearableInput from '../../../participants-pane/components/native/ClearableInput';
import { formatGifUrlMessage, getGifRating, getGifUrl } from '../../functions';
import GifsMenuFooter from './GifsMenuFooter';
@ -45,8 +45,9 @@ const GifsMenu = () => {
<JitsiScreen
footerComponent = { GifsMenuFooter }
style = { styles.container }>
<ClearableInput
customStyles = { styles.clearableInput }
<Input
clearable = { true }
customStyles = {{ container: styles.customContainer }}
onChange = { setSearchQuery }
placeholder = { t('giphy.search') }
value = { searchQuery } />

View File

@ -6,15 +6,9 @@ export default {
flex: 1
},
clearableInput: {
wrapper: {
marginBottom: BaseTheme.spacing[3],
marginTop: BaseTheme.spacing[3]
},
input: {
textAlign: 'left'
}
customContainer: {
marginHorizontal: BaseTheme.spacing[3],
marginVertical: BaseTheme.spacing[2]
},
grid: {

View File

@ -1,14 +1,11 @@
// @flow
import _ from 'lodash';
import React from 'react';
import React, { ReactElement } from 'react';
import {
ActivityIndicator,
FlatList,
TouchableOpacity,
View
} from 'react-native';
import { withTheme } from 'react-native-paper';
import { AlertDialog, openDialog } from '../../../../base/dialog';
import { translate } from '../../../../base/i18n';
@ -21,14 +18,12 @@ import {
IconShare
} from '../../../../base/icons';
import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
import {
AvatarListItem,
type Item
} from '../../../../base/react';
import { AvatarListItem, type Item } from '../../../../base/react';
import { connect } from '../../../../base/redux';
import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
import Input from '../../../../base/ui/components/native/Input';
import HeaderNavigationButton
from '../../../../mobile/navigation/components/HeaderNavigationButton';
import ClearableInput from '../../../../participants-pane/components/native/ClearableInput';
import { beginShareRoom } from '../../../../share-room';
import { INVITE_TYPES } from '../../../constants';
import AbstractAddPeopleDialog, {
@ -37,10 +32,7 @@ import AbstractAddPeopleDialog, {
_mapStateToProps as _abstractMapStateToProps
} from '../AbstractAddPeopleDialog';
import styles, {
AVATAR_SIZE,
DARK_GREY
} from './styles';
import styles, { AVATAR_SIZE } from './styles';
type Props = AbstractProps & {
@ -109,6 +101,8 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
/**
* TimeoutID to delay the search for the time the user is probably typing.
*/
/* eslint-disable-next-line no-undef */
searchTimeout: TimeoutID;
/**
@ -131,6 +125,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
this._onShareMeeting = this._onShareMeeting.bind(this);
this._onTypeQuery = this._onTypeQuery.bind(this);
this._renderShareMeetingButton = this._renderShareMeetingButton.bind(this);
this._renderIcon = this._renderIcon.bind(this);
}
/**
@ -189,8 +184,6 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
_dialOutEnabled
} = this.props;
const { inviteItems, selectableItems } = this.state;
const { theme } = this.props;
const { palette } = theme;
let placeholderKey = 'searchPlaceholder';
@ -204,26 +197,13 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
<JitsiScreen
footerComponent = { this._renderShareMeetingButton }
style = { styles.addPeopleContainer }>
<ClearableInput
<Input
autoFocus = { false }
customStyles = {{
wrapper: styles.searchFieldWrapper,
input: styles.searchField,
clearButton: styles.clearButton,
clearIcon: styles.clearIcon
}}
clearable = { true }
customStyles = {{ container: styles.customContainer }}
icon = { this._renderIcon }
onChange = { this._onTypeQuery }
placeholder = { this.props.t(`inviteDialog.${placeholderKey}`) }
placeholderColor = { palette.text04 }
prefixComponent = { <View style = { styles.searchIconWrapper }>
{this.state.searchInprogress
? <ActivityIndicator
color = { DARK_GREY }
size = 'small' />
: <Icon
src = { IconSearch }
style = { styles.searchIcon } />}
</View> }
value = { this.state.fieldValue } />
{ Boolean(inviteItems.length) && <View style = { styles.invitedList }>
<FlatList
@ -424,7 +404,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
_query: (string) => Promise<Array<Object>>;
_renderInvitedItem: Object => React$Element<any> | null;
_renderInvitedItem: Object => ReactElement | null;
/**
* Renders a single item in the invited {@code FlatList}.
@ -434,7 +414,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
* @param {number} index - The index of the currently rendered item.
* @returns {?React$Element<any>}
*/
_renderInvitedItem(flatListItem, index): React$Element<any> | null {
_renderInvitedItem(flatListItem, index): ReactElement | null {
const { item } = flatListItem;
const renderableItem = this._getRenderableItem(flatListItem);
@ -461,7 +441,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
);
}
_renderItem: Object => React$Element<any> | null;
_renderItem: Object => ReactElement | null;
/**
* Renders a single item in the search result {@code FlatList}.
@ -471,7 +451,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
* @param {number} index - The index of the currently rendered item.
* @returns {?React$Element<*>}
*/
_renderItem(flatListItem, index): React$Element<any> | null {
_renderItem(flatListItem, index): ReactElement | null {
const { item } = flatListItem;
const { inviteItems } = this.state;
let selected = false;
@ -516,7 +496,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
);
}
_renderSeparator: () => React$Element<*> | null;
_renderSeparator: () => ReactElement | null;
/**
* Renders the item separator.
@ -529,7 +509,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
);
}
_renderShareMeetingButton: () => React$Element<any>;
_renderShareMeetingButton: () => ReactElement;
/**
* Renders a button to share the meeting info.
@ -554,6 +534,30 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
);
}
_renderIcon: () => ReactElement;
/**
* Renders an icon.
*
* @returns {React#Element<*>}
*/
_renderIcon() {
if (this.state.searchInprogress) {
return (
<ActivityIndicator
color = { BaseTheme.palette.icon01 }
size = 'small' />
);
}
return (
<Icon
src = { IconSearch }
style = { styles.searchIcon } />
);
}
/**
* Shows an alert telling the user that some invitees were failed to be
* invited.
@ -586,4 +590,4 @@ function _mapStateToProps(state: Object) {
};
}
export default translate(connect(_mapStateToProps)(withTheme(AddPeopleDialog)));
export default translate(connect(_mapStateToProps)(AddPeopleDialog));

View File

@ -1,12 +1,9 @@
// @flow
import { BoxModel } from '../../../../base/styles';
import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
export const AVATAR_SIZE = 40;
export const DARK_GREY = 'rgb(28, 32, 37)';
export const LIGHT_GREY = 'rgb(209, 219, 232)';
export const ICON_SIZE = 15;
export default {
@ -19,6 +16,11 @@ export default {
backgroundColor: LIGHT_GREY
},
customContainer: {
marginHorizontal: BaseTheme.spacing[3],
marginVertical: BaseTheme.spacing[2]
},
avatarText: {
color: DARK_GREY,
fontSize: 12
@ -41,15 +43,6 @@ export default {
textAlign: 'center'
},
clearIconContainer: {
alignItems: 'center',
backgroundColor: BaseTheme.palette.section01,
borderRadius: 12,
justifyContent: 'center',
height: 24,
width: 24
},
/**
* A special padding to avoid issues on some devices (such as Android devices with custom suggestions bar).
*/
@ -92,18 +85,6 @@ export default {
padding: 5
},
searchField: {
backgroundColor: BaseTheme.palette.section01,
borderBottomRightRadius: 10,
borderTopRightRadius: 10,
color: DARK_GREY,
flex: 1,
fontSize: 17,
paddingVertical: 7,
paddingLeft: 0,
textAlign: 'left'
},
selectedIcon: {
color: BaseTheme.palette.icon01,
fontSize: 20,
@ -117,29 +98,9 @@ export default {
marginLeft: 85
},
searchFieldWrapper: {
backgroundColor: BaseTheme.palette.section01,
alignItems: 'stretch',
flexDirection: 'row',
height: 36,
marginHorizontal: 15,
marginVertical: 8,
borderWidth: 0,
borderRadius: 10,
overflow: 'hidden'
},
searchIcon: {
color: DARK_GREY,
fontSize: ICON_SIZE
},
searchIconWrapper: {
alignItems: 'center',
backgroundColor: BaseTheme.palette.section01,
flexDirection: 'row',
justifyContent: 'center',
width: ICON_SIZE + 16
color: BaseTheme.palette.icon01,
fontSize: 22
},
shareIcon: {

View File

@ -1,7 +1,5 @@
// @flow
import React from 'react';
import { Text, TextInput, View } from 'react-native';
import React, { ReactElement } from 'react';
import { Text, View } from 'react-native';
import { getConferenceName } from '../../../base/conference/functions';
import { translate } from '../../../base/i18n';
@ -9,8 +7,9 @@ import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { LoadingIndicator } from '../../../base/react';
import { connect } from '../../../base/redux';
import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui';
import BaseTheme from '../../../base/ui/components/BaseTheme';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import Button from '../../../base/ui/components/native/Button';
import Input from '../../../base/ui/components/native/Input';
import { BUTTON_TYPES } from '../../../base/ui/constants';
import { BrandingImageBackground } from '../../../dynamic-branding/components/native';
import { LargeVideo } from '../../../large-video/components';
@ -25,6 +24,7 @@ import AbstractLobbyScreen, {
import styles from './styles';
type Props = AbstractProps & {
/**
@ -99,9 +99,9 @@ class LobbyScreen extends AbstractLobbyScreen<Props> {
_onSwitchToPasswordMode: () => void;
_renderContent: () => React$Element<*>;
_renderContent: () => ReactElement;
_renderToolbarButtons: () => React$Element<*>;
_renderToolbarButtons: () => ReactElement;
_onNavigateToLobbyChat: () => void;
@ -152,10 +152,10 @@ class LobbyScreen extends AbstractLobbyScreen<Props> {
const { displayName } = this.state;
return (
<TextInput
onChangeText = { this._onChangeDisplayName }
<Input
customStyles = {{ input: styles.customInput }}
onChange = { this._onChangeDisplayName }
placeholder = { t('lobby.nameField') }
style = { styles.field }
value = { displayName } />
);
}
@ -179,13 +179,13 @@ class LobbyScreen extends AbstractLobbyScreen<Props> {
return (
<View style = { styles.formWrapper }>
<TextInput
<Input
autoCapitalize = 'none'
autoCompleteType = 'off'
onChangeText = { this._onChangePassword }
customStyles = {{ input: styles.customInput }}
onChange = { this._onChangePassword }
placeholder = { t('lobby.passwordField') }
secureTextEntry = { true }
style = { styles.field }
value = { this.state.password } />
{ _passwordJoinFailed && <Text style = { styles.fieldError }>
{ t('lobby.invalidPassword') }

View File

@ -124,20 +124,12 @@ export default {
formWrapper: {
alignSelf: 'stretch',
justifyContent: 'center'
justifyContent: 'center',
marginTop: 38
},
field: {
alignSelf: 'stretch',
backgroundColor: BaseTheme.palette.field02,
borderColor: SECONDARY_COLOR,
borderRadius: BaseTheme.shape.borderRadius,
borderWidth: 2,
color: BaseTheme.palette.text06,
height: BaseTheme.spacing[7],
marginTop: 38,
customInput: {
marginHorizontal: BaseTheme.spacing[3],
padding: BaseTheme.spacing[2],
textAlign: 'center'
},

View File

@ -1,195 +0,0 @@
// @flow
import React, { useCallback, useEffect, useState } from 'react';
import { TextInput, TouchableOpacity, View } from 'react-native';
import { withTheme } from 'react-native-paper';
import { Icon, IconCloseCircle } from '../../../base/icons';
import styles from './styles';
type Props = {
/**
* If the input should be focused on display.
*/
autoFocus?: boolean,
/**
* Custom styles for the component.
*/
customStyles?: Object,
/**
* Callback for the onBlur event of the field.
*/
onBlur?: Function,
/**
* Callback for the onChange event of the field.
*/
onChange: Function,
/**
* Callback for the onFocus event of the field.
*/
onFocus?: Function,
/**
* Callback to be used when the user hits Enter in the field.
*/
onSubmit?: Function,
/**
* Placeholder text for the field.
*/
placeholder: string,
/**
* Placeholder text color.
*/
placeholderColor?: string,
/**
* Component to be added to the beginning of the the input.
*/
prefixComponent?: React$Node,
/**
* The type of the return key.
*/
returnKeyType?: 'done' | 'go' | 'next' | 'search' | 'send' | 'none' | 'previous' | 'default',
/**
* Color of the caret and selection.
*/
selectionColor?: string,
/**
* Theme used for styles.
*/
theme: Object,
/**
* Externally provided value.
*/
value?: string
};
/**
* Implements a pre-styled clearable input field.
*
* @param {Props} props - The props of the component.
* @returns {ReactElement}
*/
function ClearableInput({
autoFocus = false,
customStyles = {},
onBlur,
onChange,
onFocus,
onSubmit,
placeholder,
placeholderColor,
prefixComponent,
returnKeyType = 'search',
selectionColor,
theme,
value
}: Props) {
const [ val, setVal ] = useState(value || '');
const [ focused, setFocused ] = useState(false);
const inputRef = React.createRef();
useEffect(() => {
if (value && value !== val) {
setVal(value);
}
}, [ value ]);
/**
* Callback for the onBlur event of the field.
*
* @returns {void}
*/
const _onBlur = useCallback(() => {
setFocused(false);
onBlur && onBlur();
}, [ onBlur ]);
/**
* Callback for the onChange event of the field.
*
* @param {Object} evt - The static event.
* @returns {void}
*/
const _onChange = useCallback(evt => {
const { nativeEvent: { text } } = evt;
setVal(text);
onChange && onChange(text);
}, [ onChange ]);
/**
* Callback for the onFocus event of the field.
*
* @returns {void}
*/
const _onFocus = useCallback(() => {
setFocused(true);
onFocus && onFocus();
}, [ onFocus ]);
/**
* Clears the input.
*
* @returns {void}
*/
const _clearInput = useCallback(() => {
if (inputRef.current) {
inputRef.current.focus();
}
setVal('');
onChange && onChange('');
}, [ onChange ]);
return (
<View
style = { [
styles.clearableInput,
focused ? styles.clearableInputFocus : {},
customStyles?.wrapper
] }>
{prefixComponent}
<TextInput
autoCorrect = { false }
autoFocus = { autoFocus }
onBlur = { _onBlur }
onChange = { _onChange }
onFocus = { _onFocus }
onSubmitEditing = { onSubmit }
placeholder = { placeholder }
placeholderTextColor = { placeholderColor ?? theme.palette.text01 }
ref = { inputRef }
returnKeyType = { returnKeyType }
selectionColor = { selectionColor }
style = { [ styles.clearableInputTextInput, customStyles?.input ] }
value = { val } />
{val !== '' && (
<TouchableOpacity
onPress = { _clearInput }
style = { [ styles.clearButton, customStyles?.clearButton ] }>
<Icon
size = { 22 }
src = { IconCloseCircle }
style = { [ styles.clearIcon, customStyles?.clearIcon ] } />
</TouchableOpacity>
)}
</View>
);
}
export default withTheme(ClearableInput);

View File

@ -1,4 +1,3 @@
import { MD_ITEM_HEIGHT } from '../../../base/dialog/components/native/styles';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
/**
@ -264,54 +263,6 @@ export default {
backgroundColor: BaseTheme.palette.dividerColor
},
clearableInput: {
display: 'flex',
height: MD_ITEM_HEIGHT,
borderWidth: 1,
borderStyle: 'solid',
borderColor: BaseTheme.palette.ui05,
backgroundColor: BaseTheme.palette.uiBackground,
borderRadius: BaseTheme.shape.borderRadius,
marginLeft: BaseTheme.spacing[3],
marginRight: BaseTheme.spacing[3],
marginBottom: BaseTheme.spacing[4]
},
clearableInputFocus: {
borderWidth: 3,
borderColor: BaseTheme.palette.field01Focus
},
clearButton: {
backgroundColor: 'transparent',
borderWidth: 0,
position: 'absolute',
right: 0,
top: 0,
paddingTop: 12,
paddingLeft: BaseTheme.spacing[2],
width: 40,
height: MD_ITEM_HEIGHT
},
clearIcon: {
color: BaseTheme.palette.icon02
},
clearableInputTextInput: {
backgroundColor: 'transparent',
borderWidth: 0,
height: '100%',
width: '100%',
textAlign: 'center',
color: BaseTheme.palette.text01,
paddingTop: BaseTheme.spacing[2],
paddingBottom: BaseTheme.spacing[2],
paddingLeft: BaseTheme.spacing[3],
paddingRight: BaseTheme.spacing[3],
fontSize: 16
},
inputContainer: {
marginLeft: BaseTheme.spacing[3],
marginRight: BaseTheme.spacing[3],

View File

@ -48,10 +48,9 @@ const AbstractPollCreate = (Component: AbstractComponent<AbstractProps>) => (pro
const [ answers, setAnswers ] = useState([ '', '' ]);
const setAnswer = useCallback((i, answer) => {
const newAnswers = [ ...answers ];
answers[i] = answer;
newAnswers[i] = answer;
setAnswers(newAnswers);
setAnswers([ ...answers ]);
});
const addAnswer = useCallback((i: ?number) => {

View File

@ -1,11 +1,9 @@
// @flow
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { FlatList, Platform, Text, TextInput, View } from 'react-native';
import { FlatList, Platform, Text, View } from 'react-native';
import { Divider, TouchableRipple } from 'react-native-paper';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import Button from '../../../base/ui/components/native/Button';
import Input from '../../../base/ui/components/native/Input';
import { BUTTON_TYPES } from '../../../base/ui/constants';
import styles
from '../../../settings/components/native/styles';
@ -42,12 +40,11 @@ const PollCreate = (props: AbstractProps) => {
return;
}
answerInputs.current[i] = input;
},
[ answerInputs ]
);
}, [ answerInputs ]);
useEffect(() => {
answerInputs.current = answerInputs.current.slice(0, answers.length);
}, [ answers ]);
/*
@ -105,22 +102,16 @@ const PollCreate = (props: AbstractProps) => {
(
<View
style = { dialogStyles.optionContainer }>
<Text style = { dialogStyles.optionFieldLabel }>
{ t('polls.create.pollOption', { index: index + 1 }) }
</Text>
<TextInput
<Input
blurOnSubmit = { false }
label = { t('polls.create.pollOption', { index: index + 1 }) }
maxLength = { CHAR_LIMIT }
multiline = { true }
onChangeText = { text => setAnswer(index, text) }
onChange = { text => setAnswer(index, text) }
onKeyPress = { ev => onAnswerKeyDown(index, ev) }
placeholder = { t('polls.create.answerPlaceholder', { index: index + 1 }) }
placeholderTextColor = { BaseTheme.palette.text03 }
ref = { input => registerFieldRef(index, input) }
selectionColor = { BaseTheme.palette.action01 }
style = { dialogStyles.field }
value = { answers[index] } />
{
answers.length > 2
&& createRemoveOptionButton(() => removeAnswer(index))
@ -133,20 +124,16 @@ const PollCreate = (props: AbstractProps) => {
return (
<View style = { chatStyles.pollCreateContainer }>
<View style = { chatStyles.pollCreateSubContainer }>
<Text style = { chatStyles.questionFieldLabel }>
{ t('polls.create.pollQuestion') }
</Text>
<TextInput
<Input
autoFocus = { true }
blurOnSubmit = { false }
customStyles = {{ container: dialogStyles.customContainer }}
label = { t('polls.create.pollQuestion') }
maxLength = { CHAR_LIMIT }
multiline = { true }
onChangeText = { setQuestion }
onChange = { setQuestion }
onSubmitEditing = { onQuestionKeyDown }
placeholder = { t('polls.create.questionPlaceholder') }
placeholderTextColor = { BaseTheme.palette.text03 }
selectionColor = { BaseTheme.palette.action01 }
style = { dialogStyles.questionField }
value = { question } />
<Divider style = { styles.fieldSeparator } />
<FlatList

View File

@ -1,10 +1,15 @@
// @flow
import { createStyleSheet } from '../../../base/styles';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
export const dialogStyles = createStyleSheet({
customContainer: {
marginBottom: BaseTheme.spacing[3],
marginHorizontal: BaseTheme.spacing[3],
marginTop: BaseTheme.spacing[2]
},
questionText: {
...BaseTheme.typography.bodyShortBold,
color: BaseTheme.palette.text01,
@ -19,31 +24,12 @@ export const dialogStyles = createStyleSheet({
marginLeft: BaseTheme.spacing[2]
},
questionField: {
borderWidth: 1,
borderColor: BaseTheme.palette.border05,
borderRadius: BaseTheme.shape.borderRadius,
color: BaseTheme.palette.text01,
fontSize: 14,
marginHorizontal: BaseTheme.spacing[3],
marginBottom: BaseTheme.spacing[3],
paddingBottom: BaseTheme.spacing[2],
paddingLeft: BaseTheme.spacing[3],
paddingRight: BaseTheme.spacing[3],
paddingTop: BaseTheme.spacing[2]
},
optionContainer: {
flexDirection: 'column',
marginTop: BaseTheme.spacing[3],
marginHorizontal: BaseTheme.spacing[3]
},
optionFieldLabel: {
color: BaseTheme.palette.text03,
marginBottom: BaseTheme.spacing[2]
},
optionRemoveButtonText: {
color: BaseTheme.palette.actionDangerActive
},
@ -62,6 +48,7 @@ export const dialogStyles = createStyleSheet({
});
export const resultsStyles = createStyleSheet({
title: {
fontSize: 24,
fontWeight: 'bold'
@ -120,11 +107,6 @@ export const resultsStyles = createStyleSheet({
});
export const chatStyles = createStyleSheet({
questionFieldLabel: {
color: BaseTheme.palette.text03,
marginBottom: BaseTheme.spacing[2],
marginLeft: BaseTheme.spacing[3]
},
noPollContent: {
alignItems: 'center',

View File

@ -1,4 +1,5 @@
/* eslint-disable lines-around-comment */
/* eslint-disable lines-around-comment */
import { useIsFocused } from '@react-navigation/native';
import React, { useCallback, useEffect, useLayoutEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -7,14 +8,12 @@ import {
Platform,
StyleProp,
Text,
TextInput,
TextStyle,
View,
ViewStyle
} from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
// @ts-ignore
import { appNavigate } from '../../../app/actions.native';
import { IReduxState } from '../../../app/types';
import { setAudioOnly } from '../../../base/audio-only/actions';
@ -24,16 +23,15 @@ import { IconCloseLarge } from '../../../base/icons/svg';
// @ts-ignore
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { getLocalParticipant } from '../../../base/participants/functions';
// @ts-ignore
import { getFieldValue } from '../../../base/react';
import { getFieldValue } from '../../../base/react/functions';
import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
import { updateSettings } from '../../../base/settings/actions';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import Button from '../../../base/ui/components/native/Button';
import { BUTTON_TYPES } from '../../../base/ui/constants';
import Input from '../../../base/ui/components/native/Input';
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
import { BrandingImageBackground } from '../../../dynamic-branding/components/native';
// @ts-ignore
import { LargeVideo } from '../../../large-video/components';
import LargeVideo from '../../../large-video/components/LargeVideo.native';
// @ts-ignore
import HeaderNavigationButton from '../../../mobile/navigation/components/HeaderNavigationButton';
// @ts-ignore
@ -162,11 +160,12 @@ const Prejoin: React.FC<IPrejoinProps> = ({ navigation }: IPrejoinProps) => {
}
<View style = { contentContainerStyles }>
<View style = { styles.formWrapper as StyleProp<ViewStyle> }>
<TextInput
onChangeText = { onChangeDisplayName }
<Input
// @ts-ignore
autoFocus = { true }
customStyles = {{ input: styles.customInput }}
onChange = { onChangeDisplayName }
placeholder = { t('dialog.enterDisplayName') }
placeholderTextColor = { BaseTheme.palette.text03 }
style = { styles.field as StyleProp<TextStyle> }
value = { displayName } />
<Button
accessibilityLabel = 'prejoin.joinMeeting'

View File

@ -79,6 +79,10 @@ export default {
marginHorizontal: BaseTheme.spacing[3]
},
customInput: {
textAlign: 'center'
},
field: {
backgroundColor: BaseTheme.palette.field02,
borderColor: SECONDARY_COLOR,

View File

@ -1,18 +1,18 @@
// @flow
import React from 'react';
import { Linking, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { Linking, Text, TouchableOpacity, View } from 'react-native';
import { _abstractMapStateToProps } from '../../../../base/dialog';
import { translate } from '../../../../base/i18n';
import { connect } from '../../../../base/redux';
import { StyleType } from '../../../../base/styles';
import Input from '../../../../base/ui/components/native/Input';
import AbstractStreamKeyForm, {
type Props as AbstractProps
} from '../AbstractStreamKeyForm';
import { getLiveStreaming } from '../functions';
import styles, { PLACEHOLDER_COLOR } from './styles';
import styles from './styles';
type Props = AbstractProps & {
@ -65,15 +65,10 @@ class StreamKeyForm extends AbstractStreamKeyForm<Props> {
t('dialog.streamKey')
}
</Text>
<TextInput
onChangeText = { this._onInputChange }
<Input
customStyles = {{ input: styles.streamKeyInput }}
onChange = { this._onInputChange }
placeholder = { t('liveStreaming.enterStreamKey') }
placeholderTextColor = { PLACEHOLDER_COLOR }
selectionColor = { PLACEHOLDER_COLOR }
style = { [
_dialogStyles.text,
styles.streamKeyInput
] }
value = { this.props.value } />
<View style = { styles.formFooter }>
{

View File

@ -8,11 +8,6 @@ import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
*/
export const ACTIVE_OPACITY = 0.3;
/**
* Color for the key input field placeholder.
*/
export const PLACEHOLDER_COLOR = BaseTheme.palette.text03;
/**
* Underlay of the TouchableHighlight.
*/

View File

@ -1,15 +1,14 @@
// @flow
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Platform, SafeAreaView, ScrollView, Text, TextInput, View } from 'react-native';
import { Button, withTheme } from 'react-native-paper';
import { Platform, SafeAreaView, ScrollView, Text, View } from 'react-native';
import { useSelector } from 'react-redux';
import { Icon, IconSearch } from '../../../base/icons';
import { IconSearch } from '../../../base/icons';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { LoadingIndicator } from '../../../base/react';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import Button from '../../../base/ui/components/native/Button';
import Input from '../../../base/ui/components/native/Input';
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { screen } from '../../../mobile/navigation/routes';
import { CONTENT_HEIGHT_OFFSET, LIST_HEIGHT_OFFSET, NOTES_LINES, NOTES_MAX_LENGTH } from '../../constants';
@ -67,22 +66,21 @@ const SalesforceLinkDialog = () => {
style = { [ styles.selectedRecord, { height: clientHeight - CONTENT_HEIGHT_OFFSET } ] }>
<View style = { styles.recordInfo }>
<RecordItem { ...selectedRecord } />
{selectedRecordOwner && <RecordItem { ...selectedRecordOwner } />}
{hasDetailsErrors && renderDetailsErrors()}
{ selectedRecordOwner && <RecordItem { ...selectedRecordOwner } /> }
{ hasDetailsErrors && renderDetailsErrors() }
</View>
<Text style = { styles.addNote }>
{t('dialog.addOptionalNote')}
</Text>
<TextInput
<Input
customStyles = {{ container: styles.notes }}
maxLength = { NOTES_MAX_LENGTH }
minHeight = { Platform.OS === 'ios' && NOTES_LINES ? 20 * NOTES_LINES : null }
multiline = { true }
numberOfLines = { Platform.OS === 'ios' ? null : NOTES_LINES }
/* eslint-disable-next-line react/jsx-no-bind */
onChangeText = { value => setNotes(value) }
onChange = { value => setNotes(value) }
placeholder = { t('dialog.addMeetingNote') }
placeholderTextColor = { BaseTheme.palette.text03 }
style = { styles.notes }
value = { notes } />
</ScrollView>
</SafeAreaView>
@ -90,17 +88,12 @@ const SalesforceLinkDialog = () => {
const renderRecordsSearch = () => (
<View style = { styles.recordsSearchContainer }>
<Icon
color = { BaseTheme.palette.icon03 }
src = { IconSearch }
style = { styles.searchIcon } />
<TextInput
<Input
icon = { IconSearch }
maxLength = { NOTES_MAX_LENGTH }
/* eslint-disable-next-line react/jsx-no-bind */
onChangeText = { value => setSearchTerm(value) }
onChange = { value => setSearchTerm(value) }
placeholder = { t('dialog.searchInSalesforce') }
placeholderTextColor = { BaseTheme.palette.text03 }
style = { styles.recordsSearch }
value = { searchTerm } />
{(!isLoading && !hasRecordsErrors) && (
<Text style = { styles.resultLabel }>
@ -173,20 +166,20 @@ const SalesforceLinkDialog = () => {
selectedRecord
&& <View style = { styles.footer }>
<Button
children = { t('dialog.Cancel') }
mode = 'contained'
labelKey = 'dialog.Cancel'
/* eslint-disable-next-line react/jsx-no-bind */
onPress = { () => setSelectedRecord(null) }
style = { styles.cancelButton } />
onClick = { () => setSelectedRecord(null) }
style = { styles.cancelButton }
type = { BUTTON_TYPES.SECONDARY } />
<Button
children = { t('dialog.linkMeeting') }
mode = 'contained'
onPress = { handlePress }
style = { styles.linkButton } />
labelKey = 'dialog.linkMeeting'
onClick = { handlePress }
style = { styles.linkButton }
type = { BUTTON_TYPES.PRIMARY } />
</View>
}
</JitsiScreen>
);
};
export default withTheme(SalesforceLinkDialog);
export default SalesforceLinkDialog;

View File

@ -9,12 +9,11 @@ export default {
backgroundColor: BaseTheme.palette.ui01
},
recordsSearchContainer: {
paddingHorizontal: BaseTheme.spacing[3],
paddingTop: BaseTheme.spacing[3],
backgroundColor: BaseTheme.palette.ui01,
alignSelf: 'stretch',
position: 'relative',
marginTop: BaseTheme.spacing[3]
backgroundColor: BaseTheme.palette.ui01,
paddingHorizontal: BaseTheme.spacing[3],
paddingTop: BaseTheme.spacing[2],
position: 'relative'
},
searchIcon: {
color: BaseTheme.palette.text03,
@ -32,17 +31,6 @@ export default {
paddingBottom: 8,
paddingTop: 16
},
recordsSearch: {
backgroundColor: BaseTheme.palette.field01,
borderColor: BaseTheme.palette.ui05,
borderRadius: BaseTheme.shape.borderRadius,
borderWidth: 1,
color: BaseTheme.palette.text01,
paddingLeft: 44,
paddingRight: 16,
paddingVertical: 10,
width: '100%'
},
recordsSpinner: {
alignItems: 'center',
display: 'flex',
@ -107,16 +95,15 @@ export default {
color: BaseTheme.palette.field02,
lineHeight: 18,
marginHorizontal: BaseTheme.spacing[3],
marginVertical: BaseTheme.spacing[2],
overflow: 'hidden',
padding: BaseTheme.spacing[2],
textAlignVertical: 'top'
},
cancelButton: {
backgroundColor: BaseTheme.palette.action02,
margin: BaseTheme.spacing[2]
},
linkButton: {
backgroundColor: BaseTheme.palette.action01,
marginBottom: BaseTheme.spacing[2],
marginHorizontal: BaseTheme.spacing[2]
},

View File

@ -10,7 +10,6 @@ import { translate } from '../../../../base/i18n';
import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
import { isLocalParticipantModerator } from '../../../../base/participants';
import { connect } from '../../../../base/redux';
import BaseTheme from '../../../../base/ui/components/BaseTheme';
import Button from '../../../../base/ui/components/native/Button';
import Input from '../../../../base/ui/components/native/Input';
import Switch from '../../../../base/ui/components/native/Switch';
@ -352,10 +351,9 @@ class SecurityDialog extends PureComponent<Props, State> {
accessibilityLabel = { t('info.addPassword') }
autoFocus = { true }
clearable = { true }
customStyles = {{ container: styles.passwordInput }}
customStyles = {{ container: styles.customContainer }}
onChange = { this._onChangeText }
placeholder = { t('dialog.password') }
placeholderTextColor = { BaseTheme.palette.text03 }
value = { passwordInputValue }
{ ...textInputProps } />
);

View File

@ -74,8 +74,7 @@ export default {
color: BaseTheme.palette.text01
},
passwordInput: {
color: BaseTheme.palette.text01,
customContainer: {
width: 208
},

View File

@ -12,13 +12,9 @@ import {
Text,
View
} from 'react-native';
import {
Divider,
TextInput
} from 'react-native-paper';
import { Divider } from 'react-native-paper';
// @ts-ignore
import { getDefaultURL } from '../../../app/functions';
import { getDefaultURL } from '../../../app/functions.native';
import { IReduxState } from '../../../app/types';
// @ts-ignore
import { Avatar } from '../../../base/avatar';
@ -28,21 +24,20 @@ import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { getLocalParticipant } from '../../../base/participants/functions';
import { connect } from '../../../base/redux/functions';
import { updateSettings } from '../../../base/settings/actions';
import BaseThemeNative from '../../../base/ui/components/BaseTheme.native';
import Input from '../../../base/ui/components/native/Input';
import Switch from '../../../base/ui/components/native/Switch';
// @ts-ignore
import { screen } from '../../../mobile/navigation/routes';
// @ts-ignore
import { AVATAR_SIZE } from '../../../welcome/components/styles';
// @ts-ignore
import { isServerURLChangeEnabled, normalizeUserInputURL } from '../../functions';
import { isServerURLChangeEnabled, normalizeUserInputURL } from '../../functions.native';
// @ts-ignore
import FormRow from './FormRow';
// @ts-ignore
import FormSectionAccordion from './FormSectionAccordion';
// @ts-ignore
import styles, { PLACEHOLDER_COLOR, PLACEHOLDER_TEXT_COLOR } from './styles';
import styles from './styles';
/**
* Application information module.
@ -205,9 +200,9 @@ class SettingsView extends Component<IProps, IState> {
disableCrashReporting,
disableP2P,
disableSelfView,
displayName,
email,
serverURL,
displayName: displayName || '',
email: email || '',
serverURL: serverURL || '',
startCarMode,
startWithAudioMuted,
startWithVideoMuted
@ -275,16 +270,6 @@ class SettingsView extends Component<IProps, IState> {
t
} = this.props;
const textInputTheme = {
colors: {
background: BaseThemeNative.palette.ui01,
placeholder: BaseThemeNative.palette.text01,
primary: PLACEHOLDER_COLOR,
underlineColor: 'transparent',
text: BaseThemeNative.palette.text01
}
};
return (
<JitsiScreen
disableForcedKeyboardDismiss = { true }
@ -298,51 +283,39 @@ class SettingsView extends Component<IProps, IState> {
</View>
<FormSectionAccordion
label = 'settingsView.profileSection'>
<TextInput
autoCorrect = { false }
<Input
// @ts-ignore
customStyles = {{ container: styles.customContainer }}
label = { t('settingsView.displayName') }
mode = 'outlined'
onChangeText = { this._onChangeDisplayName }
onChange = { this._onChangeDisplayName }
placeholder = { t('settingsView.displayNamePlaceholderText') }
placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
spellCheck = { false }
style = { styles.textInputContainer }
textContentType = { 'name' } // iOS only
theme = { textInputTheme }
value = { displayName } />
<Divider style = { styles.fieldSeparator } />
<TextInput
<Input
// @ts-ignore
autoCapitalize = 'none'
autoCorrect = { false }
customStyles = {{ container: styles.customContainer }}
keyboardType = { 'email-address' }
label = { t('settingsView.email') }
mode = 'outlined'
onChangeText = { this._onChangeEmail }
placeholder = 'email@example.com'
placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
spellCheck = { false }
style = { styles.textInputContainer }
onChange = { this._onChangeEmail }
placeholder = { t('settingsView.emailPlaceholderText') }
textContentType = { 'emailAddress' } // iOS only
theme = { textInputTheme }
value = { email } />
</FormSectionAccordion>
<FormSectionAccordion
label = 'settingsView.conferenceSection'>
<TextInput
<Input
// @ts-ignore
autoCapitalize = 'none'
autoCorrect = { false }
customStyles = {{ container: styles.customContainer }}
editable = { this.props._serverURLChangeEnabled }
keyboardType = { 'url' }
label = { t('settingsView.serverURL') }
mode = 'outlined'
onBlur = { this._onBlurServerURL }
onChangeText = { this._onChangeServerURL }
onChange = { this._onChangeServerURL }
placeholder = { this.props._serverURL }
placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
spellCheck = { false }
style = { styles.textInputContainer }
textContentType = { 'URL' } // iOS only
theme = { textInputTheme }
value = { serverURL } />
<Divider style = { styles.fieldSeparator } />
<FormRow label = 'settingsView.startCarModeInLowBandwidthMode'>

View File

@ -98,7 +98,10 @@ export default {
},
formSectionTitleText: {
color: BaseTheme.palette.text01
color: BaseTheme.palette.text01,
fontSize: 14,
opacity: 0.6,
textAlign: 'center'
},
section: {
@ -133,9 +136,7 @@ export default {
/**
* Text input container style.
*/
textInputContainer: {
flex: 1,
height: BaseTheme.spacing[7],
customContainer: {
marginBottom: BaseTheme.spacing[3],
marginHorizontal: BaseTheme.spacing[3],
marginTop: BaseTheme.spacing[2]

View File

@ -1,11 +1,8 @@
// @flow
import React, { useCallback, useEffect } from 'react';
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { escapeRegexp } from '../../../base/util';
import { initSearch, resetSearchCriteria } from '../../actions';
import { resetSearchCriteria } from '../../actions';
import SpeakerStatsList from './SpeakerStatsList';
@ -19,17 +16,15 @@ import style from './styles';
*/
const SpeakerStats = () => {
const dispatch = useDispatch();
const onSearch = useCallback((criteria = '') => {
dispatch(initSearch(escapeRegexp(criteria)));
}
, [ dispatch ]);
useEffect(() => () => dispatch(resetSearchCriteria()), []);
useEffect(() => {
dispatch(resetSearchCriteria());
}, []);
return (
<JitsiScreen
style = { style.speakerStatsContainer }>
<SpeakerStatsSearch onSearch = { onSearch } />
<SpeakerStatsSearch />
<SpeakerStatsList />
</JitsiScreen>
);

View File

@ -1,38 +1,32 @@
// @flow
import React from 'react';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { withTheme } from 'react-native-paper';
import { useSelector } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { Icon, IconSearch } from '../../../base/icons';
import ClearableInput from '../../../participants-pane/components/native/ClearableInput';
import { IconSearch } from '../../../base/icons';
import Input from '../../../base/ui/components/native/Input';
import { escapeRegexp } from '../../../base/util';
import { initSearch } from '../../actions';
import { isSpeakerStatsSearchDisabled } from '../../functions';
import styles from './styles';
/**
* The type of the React {@code Component} props of {@link SpeakerStatsSearch}.
*/
type Props = {
/**
* The function to initiate the change in the speaker stats table.
*/
onSearch: Function,
/**
* Theme used for styles.
*/
theme: Object
};
/**
* React component for display an individual user's speaker stats.
*
* @returns {React$Element<any>}
*/
function SpeakerStatsSearch({ onSearch, theme }: Props) {
const SpeakerStatsSearch = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
const [ searchQuery, setSearchQuery ] = useState('');
const onSearch = useCallback((criteria = '') => {
dispatch(initSearch(escapeRegexp(criteria)));
setSearchQuery(escapeRegexp(criteria));
}, [ dispatch ]);
const disableSpeakerStatsSearch = useSelector(isSpeakerStatsSearchDisabled);
@ -41,20 +35,14 @@ function SpeakerStatsSearch({ onSearch, theme }: Props) {
}
return (
<ClearableInput
customStyles = { styles.speakerStatsSearch }
<Input
clearable = { true }
customStyles = {{ container: styles.customContainer }}
icon = { IconSearch }
onChange = { onSearch }
placeholder = { t('speakerStats.search') }
placeholderColor = { theme.palette.text03 }
prefixComponent = {
<Icon
color = { theme.palette.text03 }
size = { 20 }
src = { IconSearch }
style = { styles.speakerStatsSearch.searchIcon } />
}
selectionColor = { theme.palette.text01 } />
value = { searchQuery } />
);
}
};
export default withTheme(SpeakerStatsSearch);

View File

@ -1,6 +1,11 @@
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
export default {
customContainer: {
marginVertical: BaseTheme.spacing[2]
},
speakerStatsContainer: {
flexDirection: 'column',
flex: 1,
@ -8,54 +13,43 @@ export default {
paddingHorizontal: BaseTheme.spacing[3],
backgroundColor: BaseTheme.palette.ui01
},
speakerStatsItemContainer: {
flexDirection: 'row',
alignSelf: 'stretch',
height: BaseTheme.spacing[9],
alignItems: 'center'
},
speakerStatsAvatar: {
width: BaseTheme.spacing[5],
height: BaseTheme.spacing[5],
marginRight: BaseTheme.spacing[3]
},
speakerStatsNameTime: {
flexDirection: 'row',
flex: 1,
justifyContent: 'space-between',
alignItems: 'center'
},
speakerStatsText: {
...BaseTheme.typography.bodyShortRegularLarge,
color: BaseTheme.palette.text01
},
speakerStatsTime: {
paddingHorizontal: 4,
paddingVertical: 2,
borderRadius: 4
},
speakerStatsDominant: {
backgroundColor: BaseTheme.palette.success02
},
speakerStatsLeft: {
color: BaseTheme.palette.text03
},
speakerStatsSearch: {
wrapper: {
marginLeft: 0,
marginRight: 0,
marginTop: BaseTheme.spacing[3],
marginBottom: BaseTheme.spacing[3],
flexDirection: 'row',
alignItems: 'center'
},
input: {
textAlign: 'left'
},
searchIcon: {
width: 10,
height: 20,
marginLeft: BaseTheme.spacing[3]
}
}
};

View File

@ -1,13 +1,5 @@
// @flow
import React from 'react';
import {
Animated,
SafeAreaView,
TextInput,
TouchableHighlight,
View
} from 'react-native';
import { Animated, SafeAreaView, TouchableHighlight, View } from 'react-native';
import { getName } from '../../app/functions';
import { translate } from '../../base/i18n';
@ -15,7 +7,8 @@ import { Icon, IconWarning } from '../../base/icons';
import JitsiStatusBar from '../../base/modal/components/JitsiStatusBar';
import { LoadingIndicator, Text } from '../../base/react';
import { connect } from '../../base/redux';
import BaseTheme from '../../base/ui/components/BaseTheme';
import BaseTheme from '../../base/ui/components/BaseTheme.native';
import Input from '../../base/ui/components/native/Input';
import WelcomePageTabs
from '../../mobile/navigation/components/welcome/components/WelcomePageTabs';
@ -24,7 +17,7 @@ import {
AbstractWelcomePage,
_mapStateToProps as _abstractMapStateToProps
} from './AbstractWelcomePage';
import styles, { PLACEHOLDER_TEXT_COLOR } from './styles';
import styles from './styles';
type Props = AbstractProps & {
@ -251,7 +244,7 @@ class WelcomePage extends AbstractWelcomePage<*> {
.start();
}
_renderHintBox: () => React$Element<any>;
_renderHintBox: () => React.ReactElement;
/**
* Renders the hint box if necessary.
@ -344,22 +337,19 @@ class WelcomePage extends AbstractWelcomePage<*> {
<Text style = { styles.enterRoomText }>
{ t('welcomepage.roomname') }
</Text>
<TextInput
<Input
accessibilityLabel = { t(roomnameAccLabel) }
autoCapitalize = { 'none' }
autoComplete = { 'off' }
autoCorrect = { false }
autoFocus = { false }
customStyles = {{ input: styles.customInput }}
onBlur = { this._onFieldBlur }
onChangeText = { this._onRoomChange }
onChange = { this._onRoomChange }
onFocus = { this._onFieldFocus }
onSubmitEditing = { this._onJoin }
placeholder = { this.state.roomPlaceholder }
placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
returnKeyType = { 'go' }
spellCheck = { false }
style = { styles.textInput }
underlineColorAndroid = 'transparent'
value = { this.state.room } />
{
this._renderInsecureRoomNameWarning()

View File

@ -214,6 +214,12 @@ export default {
overflow: 'hidden'
},
customInput: {
fontSize: 24,
lineHeight: 32,
textAlign: 'center'
},
recentList: {
backgroundColor: BaseTheme.palette.uiBackground,
flex: 1,