mattermost-mobile/app/components/search_bar/index.tsx
Avinash Lingaloo 7f91a6a78a
Gekidou - Account Screen (#5708)
* Added DrawerItem component

* WIP Account Screen

* Added react-native-paper

* Added StatusLabel Component

* Extracted i18n

* TS fix DrawerItem component

* WIP Account Screen

* Added server name label under log out

* Updated translation

* WIP

* Fixes the Offline text style

* Added Metropolis fonts

* WIP

* Typo clean up

* WIP

* WIP

* WIP

* Added server display name

* Writing OpenSans properly

* WIP

* WIP

* Added OptionsModal

* Opening OptionsModal

* Added translation keys

* Writes status to local db

* Fix missing translation

* Fix OptionModal not dismissing

* Pushing status to server

* Refactored

* Added CustomStatusExpiry component

* Added sub components

* Added CustomLabel

* CustomStatus WIP

* Added Custom Status screen WIP

* WIP - unsetCustomStatus and CustomStatus constant

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* Retrieving RecentCustomStatuses from Preferences table

* WIP

* WIP

* WIP

* Added Clear After Modal

* WIP - Transations

* WIP

* Done with showing modal cst

* wip

* Clear After Modal - DONE

* fix

* Added missing API calls

* wip

* Causing screen refresh

* wip

* WIP

* WIP

* WIP

* Code clean up

* Added OOO alert box

* Refactored Options-Item

* Refactored OptionsModalList component

* Opening 'status' in BottomSheet instead of OptionsModal

* AddReaction screen - WIP

* Add Reaction screen - WIP

* Added EmojiPickerRow

* Added @components/emoji_picker - WIP

* Emoji Picker - WIP

* WIP

* WIP

* WIP

* SectionList - WIP

* Installed react-native-section_list_get_item_layout

* Adding API calls - WIP

* WIP

* Search Bar component - WIP

* WIP

* WIP

* WIP

* Rendering Emoticons now - have to tackle some fixmes

* Code clean up

* Code clean up - WIP

* Code clean up

* WIP

* Major clean up

* wip

* WIP

* Fix rendering issue with SectionIcons and SearchBar

* Tackled the CustomEmojiPage

* Code clean up

* WIP

* Done with loading User Profiles for Custom Emoji

* Code clean up

* Code Clean up

* Fix screen Account

* Added missing sql file for IOS Pod

* Updated Podfile.lock

* Using queryConfig instead of queryCommonSystemValues

* Fix - Custom status

* Fix - Custom Status - Error

* Fix - Clear Pass Status - WIP

* Fix - Custom Status Clear

* Need to fix CST clear

* WIP

* Status clear - working

* Using catchError operator

* remove unnecessary prop

* Status  BottomSheet now has colored indicators

* Added KeyboardTrackingView from 'react-native-keyboard-tracking-view'

* Code clean up

* WIP

* code clean up

* Added a safety check

* Fix - Display suggestions

* Code clean up based on PR Review

* Code clean up

* Code clean up

* Code clean up

* Corrections

* Fix tsc

* TS fix

* Removed unnecessary prop

* Fix SearchBar Ts

* Updated tests

* Delete search_bar.test.js.snap

* Merge branch 'gekidou' into gekidou_account_screen

* Revert "Merge branch 'gekidou' into gekidou_account_screen"

This reverts commit 5defc313212478a55abf2f92cb4bd64dc7877342.

* Fix fonts

* Refactor home account screen

* fix theme provider

* refactor bottom sheet

* remove paper provider

* update drawer item snapshots

* Remove options modal screen

* remove react-native-ui-lib dependency

* Refactor & fix custom status & navigation (including tablet)

* Refactor emoji picker

Co-authored-by: Avinash Lingaloo <>
Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2021-10-12 19:24:24 +04:00

316 lines
12 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
import {IntlShape} from 'react-intl';
import {
Animated,
InteractionManager,
Keyboard,
StyleSheet,
View,
Platform,
ViewStyle,
ReturnKeyTypeOptions,
KeyboardTypeOptions,
NativeSyntheticEvent,
TextInputSelectionChangeEventData,
TextStyle,
} from 'react-native';
import {SearchBar} from 'react-native-elements';
import CompassIcon from '@components/compass_icon';
import ClearIcon from './components/clear_icon';
import SearchIcon from './components/search_icon';
import {getSearchStyles} from './styles';
const LEFT_COMPONENT_INITIAL_POSITION = Platform.OS === 'ios' ? 7 : 0;
type SearchProps = {
autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters' | undefined;
autoFocus?: boolean;
backArrowSize?: number;
backgroundColor: string;
blurOnSubmit?: boolean;
cancelButtonStyle?: ViewStyle;
cancelTitle?: string;
containerHeight?: number;
containerStyle?: ViewStyle;
deleteIconSize?: number;
editable?: boolean;
inputHeight: number;
inputStyle?: TextStyle;
intl?: IntlShape;
keyboardAppearance?: 'default' | 'light' | 'dark' | undefined;
keyboardShouldPersist?: boolean;
keyboardType?: KeyboardTypeOptions | undefined;
leftComponent?: JSX.Element;
onBlur?: () => void;
onCancelButtonPress: (text?: string) => void;
onChangeText: (text: string) => void;
onFocus?: () => void;
onSearchButtonPress?: (value: string) => void;
onSelectionChange?: (e: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => void;
placeholder?: string;
placeholderTextColor?: string;
returnKeyType?: ReturnKeyTypeOptions | undefined;
searchBarRightMargin?: number;
searchIconSize?: number;
selectionColor?: string;
showArrow?: boolean;
showCancel?: boolean;
testID: string;
tintColorDelete: string;
tintColorSearch: string;
titleCancelColor: string;
value?: string;
};
type SearchState = {
leftComponentWidth: number;
}
export default class Search extends PureComponent<SearchProps, SearchState> {
static defaultProps = {
backArrowSize: 24,
blurOnSubmit: false,
containerHeight: 40,
deleteIconSize: 20,
editable: true,
keyboardShouldPersist: false,
keyboardType: 'default',
onBlur: () => true,
onSelectionChange: () => true,
placeholderTextColor: 'grey',
returnKeyType: 'search',
searchBarRightMargin: 0,
searchIconSize: 24,
showArrow: false,
showCancel: true,
value: '',
};
private readonly leftComponentAnimated: Animated.Value;
private readonly searchContainerAnimated: Animated.Value;
private searchContainerRef: any;
private inputKeywordRef: any;
private readonly searchStyle: any;
constructor(props: SearchProps | Readonly<SearchProps>) {
super(props);
this.state = {
leftComponentWidth: 0,
};
this.leftComponentAnimated = new Animated.Value(LEFT_COMPONENT_INITIAL_POSITION);
this.searchContainerAnimated = new Animated.Value(0);
const {backgroundColor, cancelButtonStyle, containerHeight, inputHeight, inputStyle, placeholderTextColor, searchBarRightMargin, tintColorDelete, tintColorSearch, titleCancelColor} = props;
this.searchStyle = getSearchStyles(backgroundColor, cancelButtonStyle, containerHeight!, inputHeight, inputStyle, placeholderTextColor!, searchBarRightMargin!, tintColorDelete, tintColorSearch, titleCancelColor);
}
setSearchContainerRef = (ref: any) => {
this.searchContainerRef = ref;
};
setInputKeywordRef = (ref: any) => {
this.inputKeywordRef = ref;
};
blur = () => {
this.inputKeywordRef.blur();
};
focus = () => {
this.inputKeywordRef.focus();
};
onBlur = async () => {
this.props?.onBlur?.();
if (this.props.leftComponent) {
await this.collapseAnimation();
}
};
onLeftComponentLayout = (event: { nativeEvent: { layout: { width: any } } }) => {
const leftComponentWidth = event.nativeEvent.layout.width;
this.setState({leftComponentWidth});
};
onSearch = async () => {
const {keyboardShouldPersist, onSearchButtonPress, value} = this.props;
if (!keyboardShouldPersist) {
await Keyboard.dismiss();
}
if (value) {
onSearchButtonPress?.(value);
}
};
onChangeText = (text: string) => {
const {onChangeText} = this.props;
if (onChangeText) {
onChangeText(text);
}
};
onFocus = () => {
const {leftComponent, onFocus} = this.props;
InteractionManager.runAfterInteractions(async () => {
onFocus?.();
if (leftComponent) {
await this.expandAnimation();
}
});
};
onClear = () => {
this.focus();
this.props.onChangeText('');
};
onCancel = () => {
const {onCancelButtonPress} = this.props;
Keyboard.dismiss();
InteractionManager.runAfterInteractions(() => {
return onCancelButtonPress?.();
});
};
onSelectionChange = (event: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => {
const {onSelectionChange} = this.props;
onSelectionChange?.(event);
};
expandAnimation = () => {
return new Promise((resolve) => {
Animated.parallel([
Animated.timing(this.leftComponentAnimated, {toValue: -115, duration: 200} as Animated.TimingAnimationConfig),
Animated.timing(this.searchContainerAnimated, {toValue: this.state.leftComponentWidth * -1, duration: 200} as Animated.TimingAnimationConfig),
]).start(resolve);
});
};
collapseAnimation = () => {
return new Promise((resolve) => {
Animated.parallel([
Animated.timing(this.leftComponentAnimated, {toValue: LEFT_COMPONENT_INITIAL_POSITION, duration: 200} as Animated.TimingAnimationConfig),
Animated.timing(this.searchContainerAnimated, {toValue: 0, duration: 200} as Animated.TimingAnimationConfig),
]).start(resolve);
});
};
render() {
const {autoCapitalize, autoFocus, backArrowSize, blurOnSubmit, cancelTitle, deleteIconSize, editable, intl, keyboardAppearance, keyboardType, leftComponent, placeholder, placeholderTextColor, returnKeyType, searchIconSize, selectionColor, showArrow, showCancel, testID, tintColorDelete, tintColorSearch, titleCancelColor, value} = this.props;
const searchClearButtonTestID = `${testID}.search.clear.button`;
const searchCancelButtonTestID = `${testID}.search.cancel.button`;
const searchInputTestID = `${testID}.search.input`;
const {cancelButtonPropStyle, containerStyle, inputContainerStyle, inputTextStyle, searchBarStyle, styles} = this.searchStyle;
return (
<View
testID={testID}
style={[searchBarStyle.container, this.props.containerStyle]}
>
{leftComponent && (
<Animated.View
style={[styles.leftComponent, {left: this.leftComponentAnimated}]}
onLayout={this.onLeftComponentLayout}
>
{leftComponent}
</Animated.View>
)}
<Animated.View
style={[
styles.fullWidth,
searchBarStyle.searchBarWrapper,
{marginLeft: this.searchContainerAnimated},
]}
>
<SearchBar
testID={searchInputTestID}
autoCapitalize={autoCapitalize}
autoCorrect={false}
autoFocus={autoFocus}
blurOnSubmit={blurOnSubmit}
cancelButtonProps={cancelButtonPropStyle}
cancelButtonTitle={cancelTitle || intl?.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'}) || ''}
cancelIcon={
// Making sure the icon won't change depending on whether the input is in focus on Android devices
Platform.OS === 'android' && (
<CompassIcon
testID={searchCancelButtonTestID}
name='arrow-left'
size={25}
color={searchBarStyle.clearIconColorAndroid}
onPress={this.onCancel}
/>
)
}
// @ts-expect-error: The clearIcon can also accept a ReactElement
clearIcon={
<ClearIcon
deleteIconSizeAndroid={deleteIconSize!}
onClear={this.onClear}
placeholderTextColor={placeholderTextColor!}
searchClearButtonTestID={searchClearButtonTestID}
tintColorDelete={tintColorDelete}
titleCancelColor={titleCancelColor}
/>
}
containerStyle={containerStyle}
disableFullscreenUI={true}
editable={editable}
enablesReturnKeyAutomatically={true}
inputContainerStyle={inputContainerStyle}
inputStyle={inputTextStyle}
keyboardAppearance={keyboardAppearance}
keyboardType={keyboardType}
leftIconContainerStyle={styles.leftIcon}
placeholder={placeholder || intl?.formatMessage({id: 'search_bar.search', defaultMessage: 'Search'}) || ''}
placeholderTextColor={placeholderTextColor}
platform={Platform.OS as 'ios' | 'android'}
onBlur={this.onBlur}
onCancel={this.onCancel}
// @ts-expect-error: The TS definition for this SearchBar is messed up
onChangeText={this.onChangeText}
onClear={this.onClear}
onFocus={this.onFocus}
onSelectionChange={this.onSelectionChange}
onSubmitEditing={this.onSearch}
// @ts-expect-error: The searchIcon can also accept a ReactElement
searchIcon={
<SearchIcon
searchIconColor={tintColorSearch || placeholderTextColor!}
searchIconSize={searchIconSize!}
clearIconColorAndroid={titleCancelColor || placeholderTextColor!}
backArrowSize={backArrowSize!}
searchCancelButtonTestID={searchCancelButtonTestID}
onCancel={this.onCancel}
showArrow={showArrow!}
iOSStyle={StyleSheet.flatten([styles.fullWidth, searchBarStyle.searchIcon])}
/>
}
selectionColor={selectionColor}
showCancel={showCancel!}
ref={this.setInputKeywordRef}
returnKeyType={returnKeyType}
underlineColorAndroid='transparent'
value={value!}
/>
</Animated.View>
</View>
);
}
}