improve search ui & ux

This commit is contained in:
ammarahm-ed
2020-11-04 20:29:45 +05:00
parent 6e9366d308
commit 3f46d26a80
4 changed files with 203 additions and 144 deletions

View File

@@ -5,84 +5,84 @@ import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import {useTracked} from '../../provider'; import {useTracked} from '../../provider';
import {getElevation} from '../../utils'; import {getElevation} from '../../utils';
import {PressableButton} from '../PressableButton'; import {PressableButton} from '../PressableButton';
import {pv, SIZE, WEIGHT} from "../../utils/SizeUtils"; import {normalize, pv, SIZE, WEIGHT} from '../../utils/SizeUtils';
import {DDS} from "../../services/DeviceDetection"; import {DDS} from '../../services/DeviceDetection';
export const ContainerBottomButton = ({title, onPress, color}) => { export const ContainerBottomButton = ({title, onPress, color}) => {
const [state,] = useTracked(); const [state] = useTracked();
const {colors} = state; const {colors} = state;
const [buttonHide, setButtonHide] = useState(false); const [buttonHide, setButtonHide] = useState(false);
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const onKeyboardHide = () => { const onKeyboardHide = () => {
if (DDS.isTab) return; if (DDS.isTab) return;
setButtonHide(false); setButtonHide(false);
} };
const onKeyboardShow = () => { const onKeyboardShow = () => {
if (DDS.isTab) return; if (DDS.isTab) return;
setButtonHide(true); setButtonHide(true);
} };
useEffect(() => { useEffect(() => {
Keyboard.addListener('keyboardDidShow', onKeyboardShow); Keyboard.addListener('keyboardDidShow', onKeyboardShow);
Keyboard.addListener('keyboardDidHide', onKeyboardHide); Keyboard.addListener('keyboardDidHide', onKeyboardHide);
return () => { return () => {
Keyboard.removeListener('keyboardDidShow', onKeyboardShow); Keyboard.removeListener('keyboardDidShow', onKeyboardShow);
Keyboard.removeListener('keyboardDidHide', onKeyboardHide); Keyboard.removeListener('keyboardDidHide', onKeyboardHide);
}; };
}, []); }, []);
return ( return (
<View
style={{
width: '100%',
opacity: buttonHide ? 0 : 1,
position: 'absolute',
paddingHorizontal: 12,
bottom: Platform.OS === 'ios' ? insets.bottom - 10 : insets.bottom + 20,
zIndex: 10,
transform: [
{
translateY: buttonHide ? 200 : 0,
},
],
}}>
<PressableButton
testID={'container_bottom_btn'}
color={color || colors.accent}
selectedColor={color || colors.accent}
customStyle={{
...getElevation(5),
}}
onPress={onPress}>
<View <View
style={{
justifyContent: 'flex-start',
alignItems: 'center',
flexDirection: 'row',
width: '100%',
padding: pv,
borderRadius: 5,
height: normalize(60),
}}>
<Icon
name={title === 'Clear all trash' ? 'delete' : 'plus'}
color="white"
size={SIZE.xl}
/>
<Text
testID="container_bottom_btn_text"
style={{ style={{
width: '100%', fontSize: SIZE.md,
opacity: buttonHide ? 0 : 1, color: 'white',
position: 'absolute', fontFamily: WEIGHT.regular,
paddingHorizontal: 12, textAlignVertical: 'center',
bottom: Platform.OS === 'ios' ? insets.bottom - 10 : insets.bottom + 20,
zIndex: 10,
transform: [
{
translateY: buttonHide ? 200 : 0,
},
],
}}> }}>
<PressableButton {' ' + title}
testID={'container_bottom_btn'} </Text>
color={color || colors.accent}
selectedColor={color || colors.accent}
customStyle={{
...getElevation(5),
}}
onPress={onPress}>
<View
style={{
justifyContent: 'flex-start',
alignItems: 'center',
flexDirection: 'row',
width: '100%',
padding: pv,
borderRadius: 5,
paddingVertical: pv + 5,
}}>
<Icon
name={title === 'Clear all trash' ? 'delete' : 'plus'}
color="white"
size={SIZE.xl}
/>
<Text
testID="container_bottom_btn_text"
style={{
fontSize: SIZE.md,
color: 'white',
fontFamily: WEIGHT.regular,
textAlignVertical: 'center',
}}>
{' ' + title}
</Text>
</View>
</PressableButton>
</View> </View>
); </PressableButton>
</View>
);
}; };

View File

@@ -1,45 +1,76 @@
import React from 'react'; import React, {useEffect} from 'react';
import {useTracked} from '../../provider'; import {useTracked} from '../../provider';
import {SIZE} from "../../utils/SizeUtils"; import {SIZE} from '../../utils/SizeUtils';
import {DDS} from "../../services/DeviceDetection"; import {DDS} from '../../services/DeviceDetection';
import {ActionIcon} from "../ActionIcon"; import {ActionIcon} from '../ActionIcon';
import NavigationService from "../../services/Navigation"; import NavigationService from '../../services/Navigation';
import {eSendEvent} from '../../services/EventManager';
import {eClearSearch} from '../../utils/Events';
import {BackHandler} from 'react-native';
export const HeaderLeftMenu = () => { export const HeaderLeftMenu = () => {
const [state,] = useTracked(); const [state] = useTracked();
const {colors, headerMenuState} = state; const {colors, headerMenuState, searchResults} = state;
const onLeftButtonPress = () => { const onLeftButtonPress = () => {
if (headerMenuState) { if (searchResults.results.length > 0) {
NavigationService.openDrawer(); eSendEvent(eClearSearch);
return; return;
} }
NavigationService.goBack(); if (headerMenuState) {
NavigationService.openDrawer();
return;
}
NavigationService.goBack();
};
const onBackPress = () => {
if (searchResults.results.length > 0) {
eSendEvent(eClearSearch);
return true;
}
return false;
};
useEffect(() => {
if (searchResults.results.length > 0) {
BackHandler.addEventListener('hardwareBackPress', onBackPress);
} else {
BackHandler.removeEventListener('hardwareBackPress', onBackPress);
}
return () => {
BackHandler.removeEventListener('hardwareBackPress', onBackPress);
}; };
}, [searchResults.results]);
return ( return (
<> <>
{!DDS.isTab ? ( {!DDS.isTab ? (
<ActionIcon <ActionIcon
testID="left_menu_button" testID="left_menu_button"
customStyle={{ customStyle={{
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
height: 40, height: 40,
width: 40, width: 40,
borderRadius: 100, borderRadius: 100,
marginLeft: -5, marginLeft: -5,
marginRight: 25, marginRight: 25,
}} }}
onPress={onLeftButtonPress} onPress={onLeftButtonPress}
name={!headerMenuState ? 'arrow-left' : 'menu'} name={
size={SIZE.xxxl} !headerMenuState || searchResults.results.length > 0
color={colors.pri} ? 'arrow-left'
iconStyle={{ : 'menu'
marginLeft: !headerMenuState ? -5 : 0, }
}} size={SIZE.xxxl}
/> color={colors.pri}
) : undefined} iconStyle={{
</> marginLeft: !headerMenuState ? -5 : 0,
); }}
/>
) : undefined}
</>
);
}; };

View File

@@ -16,9 +16,10 @@ import {selection} from '../../utils';
import Animated, {Easing} from 'react-native-reanimated'; import Animated, {Easing} from 'react-native-reanimated';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import {TextInput, Text} from 'react-native'; import {TextInput, Text} from 'react-native';
import {br, SIZE, WEIGHT} from '../../utils/SizeUtils'; import {br, normalize, SIZE, WEIGHT} from '../../utils/SizeUtils';
import {db} from '../../utils/DB'; import {db} from '../../utils/DB';
import {DDS} from '../../services/DeviceDetection'; import {DDS} from '../../services/DeviceDetection';
import {ActionIcon} from '../ActionIcon';
const {Value, timing, block} = Animated; const {Value, timing, block} = Animated;
let searchResult = []; let searchResult = [];
@@ -54,6 +55,8 @@ const animation = (margin, opacity, border) => {
}, 200); }, 200);
}; };
let timeout = null;
export const Search = (props) => { export const Search = (props) => {
const [state, dispatch] = useTracked(); const [state, dispatch] = useTracked();
const {colors, searchResults} = state; const {colors, searchResults} = state;
@@ -142,6 +145,15 @@ export const Search = (props) => {
keyword: null, keyword: null,
}, },
}); });
} else {
dispatch({
type: Actions.SEARCH_RESULTS,
results: {
results: [],
type: null,
keyword: null,
},
});
} }
}; };
@@ -152,15 +164,22 @@ export const Search = (props) => {
}; };
}, []); }, []);
useEffect(() => {
if (!text || text.length === 0) {
clearSearch();
}
}, [text]);
const onChangeText = (value) => { const onChangeText = (value) => {
setText(value); setText(value);
}; };
const onSubmitEditing = async () => { const onSubmitEditing = async () => {
if (!text || text.length < 1) { if (!text || text.length < 1) {
ToastEvent.show('Please enter a search keyword');
clearSearch(); clearSearch();
return; return;
} }
let type = searchState.type; let type = searchState.type;
if (!type) return; if (!type) return;
@@ -208,25 +227,40 @@ export const Search = (props) => {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
paddingLeft: 12,
width: '100%', width: '100%',
alignSelf: 'center', alignSelf: 'center',
borderRadius: br, borderRadius: br,
height: '90%', height: normalize(55),
backgroundColor: focus backgroundColor: focus
? searchState.color ? searchState.color
? searchState.color ? searchState.color
: colors.shade : colors.shade
: colors.nav, : colors.nav,
}}> }}>
<ActionIcon
customStyle={{
width: 50,
height: 50,
}}
name="magnify"
color={
focus
? searchState.color
? searchState.color
: colors.accent
: colors.icon
}
size={SIZE.xxl}
/>
<TextInput <TextInput
ref={inputRef} ref={inputRef}
style={{ style={{
fontFamily: WEIGHT.regular, fontFamily: WEIGHT.regular,
color: colors.pri, color: colors.pri,
maxWidth: '85%', fontSize: SIZE.md,
width: '85%', flexGrow: 1,
fontSize: SIZE.sm, flex: 1,
flexWrap: 'wrap',
}} }}
onChangeText={onChangeText} onChangeText={onChangeText}
onSubmitEditing={onSubmitEditing} onSubmitEditing={onSubmitEditing}
@@ -242,17 +276,13 @@ export const Search = (props) => {
placeholder={searchState.placeholder} placeholder={searchState.placeholder}
placeholderTextColor={colors.icon} placeholderTextColor={colors.icon}
/> />
<Icon <ActionIcon
style={{paddingRight: DDS.isTab ? 12 : 12}} customStyle={{
onPress={onSubmitEditing} width: 50,
name="magnify" height: 50,
color={ }}
focus name="tune"
? searchState.color color={colors.icon}
? searchState.color
: colors.accent
: colors.icon
}
size={SIZE.xxl} size={SIZE.xxl}
/> />
</Animated.View> </Animated.View>

View File

@@ -13,7 +13,12 @@ import {DataProvider, LayoutProvider, RecyclerListView} from 'recyclerlistview';
import {useTracked} from '../../provider'; import {useTracked} from '../../provider';
import {Actions} from '../../provider/Actions'; import {Actions} from '../../provider/Actions';
import {eSendEvent, ToastEvent} from '../../services/EventManager'; import {eSendEvent, ToastEvent} from '../../services/EventManager';
import {eClearSearch, eOpenJumpToDialog, eOpenLoginDialog, eScrollEvent} from '../../utils/Events'; import {
eClearSearch,
eOpenJumpToDialog,
eOpenLoginDialog,
eScrollEvent,
} from '../../utils/Events';
import {PressableButton} from '../PressableButton'; import {PressableButton} from '../PressableButton';
import {COLORS_NOTE} from '../../utils/Colors'; import {COLORS_NOTE} from '../../utils/Colors';
import {SIZE, WEIGHT} from '../../utils/SizeUtils'; import {SIZE, WEIGHT} from '../../utils/SizeUtils';
@@ -35,7 +40,7 @@ const SimpleList = ({
refreshCallback, refreshCallback,
sortMenuButton, sortMenuButton,
scrollRef, scrollRef,
jumpToDialog jumpToDialog,
}) => { }) => {
const [state, dispatch] = useTracked(); const [state, dispatch] = useTracked();
const {colors, selectionMode, messageBoardState} = state; const {colors, selectionMode, messageBoardState} = state;
@@ -59,7 +64,7 @@ const SimpleList = ({
useEffect(() => { useEffect(() => {
loadData(); loadData();
}, [listData]); }, [listData, searchResults.results]);
const loadData = () => { const loadData = () => {
let mainData = let mainData =
@@ -85,18 +90,18 @@ const SimpleList = ({
}}> }}>
<Text <Text
onPress={() => { onPress={() => {
console.log('clicekd'); console.log('clicekd');
if (jumpToDialog) { if (jumpToDialog) {
eSendEvent(eOpenJumpToDialog); eSendEvent(eOpenJumpToDialog);
} }
}} }}
style={[ style={[
styles.sectionHeader, styles.sectionHeader,
{ {
color: colors.accent, color: colors.accent,
height:30, height: 30,
minWidth:60, minWidth: 60,
textAlignVertical:"bottom" textAlignVertical: 'bottom',
}, },
]}> ]}>
{item.title} {item.title}
@@ -367,14 +372,7 @@ const MessageCard = ({data}) => {
}; };
const ListHeaderComponent = ({type, data}) => { const ListHeaderComponent = ({type, data}) => {
const [state] = useTracked(); return <MessageCard type={type} data={data} />;
const searchResults = {...state.searchResults};
return searchResults.type === type && searchResults.results.length > 0 ? (
<SearchHeader />
) : (
<MessageCard type={type} data={data} />
);
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({