Improvements to the UriBar implementation and some navigation tweaks.

This commit is contained in:
Akinwale Ariwodola 2018-05-26 00:20:44 +01:00
parent 7c8f9a6662
commit 6a60ce07e9
20 changed files with 206 additions and 43 deletions

View file

@ -38,7 +38,6 @@ const discoverStack = StackNavigator({
navigationOptions: ({ navigation }) => ({ navigationOptions: ({ navigation }) => ({
title: 'Discover', title: 'Discover',
headerLeft: <Feather name="menu" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />, headerLeft: <Feather name="menu" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
headerRight: <Feather name="search" size={24} style={discoverStyle.rightHeaderIcon} onPress={() => navigation.navigate('Search')} />
}) })
}, },
File: { File: {
@ -51,9 +50,7 @@ const discoverStack = StackNavigator({
Search: { Search: {
screen: SearchPage, screen: SearchPage,
navigationOptions: ({ navigation }) => ({ navigationOptions: ({ navigation }) => ({
drawerLockMode: 'locked-closed', drawerLockMode: 'locked-closed'
headerTitle: <SearchInput style={searchStyle.searchInput} />,
headerRight: <SearchRightHeaderIcon style={discoverStyle.rightHeaderIcon} size={24} navigation={navigation} />
}) })
} }
}, { }, {
@ -66,7 +63,6 @@ const walletStack = StackNavigator({
navigationOptions: ({ navigation }) => ({ navigationOptions: ({ navigation }) => ({
title: 'Wallet', title: 'Wallet',
headerLeft: <Feather name="menu" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />, headerLeft: <Feather name="menu" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />,
headerRight: <Feather name="search" size={24} style={discoverStyle.rightHeaderIcon} onPress={() => navigation.navigate('Search')} />
}) })
}, },
TransactionHistory: { TransactionHistory: {
@ -185,6 +181,7 @@ class AppWithNavigationState extends React.Component {
if (evt.url) { if (evt.url) {
const navigateAction = NavigationActions.navigate({ const navigateAction = NavigationActions.navigate({
routeName: 'File', routeName: 'File',
key: 'filePage',
params: { uri: evt.url } params: { uri: evt.url }
}); });
dispatch(navigateAction); dispatch(navigateAction);

View file

@ -61,7 +61,7 @@ class FileItem extends React.PureComponent {
if (NativeModules.Mixpanel) { if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('Discover Tap', { Uri: uri }); NativeModules.Mixpanel.track('Discover Tap', { Uri: uri });
} }
navigation.navigate('File', { uri: uri }); navigation.navigate({ routeName: 'File', key: 'filePage', params: { uri } });
} }
}> }>
<FileItemMedia title={title} thumbnail={thumbnail} blurRadius={obscureNsfw ? 15 : 0} resizeMode="cover" /> <FileItemMedia title={title} thumbnail={thumbnail} blurRadius={obscureNsfw ? 15 : 0} resizeMode="cover" />

View file

@ -23,7 +23,11 @@ class TransactionListItem extends React.PureComponent {
{name && claimId && ( {name && claimId && (
<Link <Link
style={transactionListStyle.link} style={transactionListStyle.link}
onPress={() => navigation && navigation.navigate('File', { uri: buildURI({ claimName: name, claimId }) })} onPress={() => navigation && navigation.navigate({
routeName: 'File',
key: 'filePage',
params: { uri: buildURI({ claimName: name, claimId }) }})
}
text={name} /> text={name} />
)} )}
</View> </View>

View file

@ -1,12 +1,17 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doUpdateSearchQuery, selectSearchState as selectSearch } from 'lbry-redux';
import UriBar from './view'; import UriBar from './view';
const select = state => ({ const select = state => {
const { ...searchState } = selectSearch(state);
}); return {
...searchState
};
};
const perform = dispatch => ({ const perform = dispatch => ({
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
}); });
export default connect(select, perform)(UriBar); export default connect(select, perform)(UriBar);

View file

@ -0,0 +1,38 @@
// @flow
import React from 'react';
import { SEARCH_TYPES, normalizeURI } from 'lbry-redux';
import { Text, TouchableOpacity, View } from 'react-native';
import Feather from 'react-native-vector-icons/Feather';
import uriBarStyle from '../../../styles/uriBar';
class UriBarItem extends React.PureComponent {
render() {
const { item, onPress } = this.props;
const { type, value } = item;
let icon;
switch (type) {
case SEARCH_TYPES.CHANNEL:
icon = <Feather name="at-sign" size={18} />
break;
case SEARCH_TYPES.SEARCH:
icon = <Feather name="search" size={18} />
break;
case SEARCH_TYPES.FILE:
default:
icon = <Feather name="compass" size={18} />
break;
}
return (
<TouchableOpacity style={uriBarStyle.item} onPress={onPress}>
{icon}
<Text style={uriBarStyle.itemText} numberOfLines={1}>{value}</Text>
</TouchableOpacity>
)
}
}
export default UriBarItem;

View file

@ -1,41 +1,96 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { normalizeURI } from 'lbry-redux'; import { SEARCH_TYPES, isNameValid, normalizeURI } from 'lbry-redux';
import { TextInput, View } from 'react-native'; import { FlatList, Keyboard, TextInput, View } from 'react-native';
import UriBarItem from './internal/uri-bar-item';
import uriBarStyle from '../../styles/uriBar'; import uriBarStyle from '../../styles/uriBar';
class UriBar extends React.PureComponent { class UriBar extends React.PureComponent {
static INPUT_TIMEOUT = 500;
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
uri: null, changeTextTimeout: null,
currentValue: null currentValue: null,
inputText: null,
focused: false
}; };
} }
handleChangeText = text => {
const newValue = text ? text : '';
clearTimeout(this.state.changeTextTimeout);
const { updateSearchQuery } = this.props;
let timeout = setTimeout(() => {
updateSearchQuery(text);
}, UriBar.INPUT_TIMEOUT);
this.setState({ inputText: newValue, currentValue: newValue, changeTextTimeout: timeout });
}
handleItemPress = (item) => {
const { navigation, updateSearchQuery } = this.props;
const { type, value } = item;
Keyboard.dismiss();
if (SEARCH_TYPES.SEARCH === type) {
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: value }});
} else {
navigation.navigate({ routeName: 'File', key: 'filePage', params: { uri: normalizeURI(value) }});
}
}
render() { render() {
const { value, navigation } = this.props; const { navigation, suggestions, updateSearchQuery, value } = this.props;
if (!this.state.currentValue) { if (this.state.currentValue === null) {
this.setState({ currentValue: value }); this.setState({ currentValue: value });
} }
// TODO: Search and URI suggestions overlay let style = [uriBarStyle.overlay];
if (this.state.focused) {
style.push(uriBarStyle.inFocus);
}
return ( return (
<View style={uriBarStyle.uriContainer}> <View style={style}>
<TextInput style={uriBarStyle.uriText} {this.state.focused && (
placeholder={'Enter a LBRY URI or some text'} <View style={uriBarStyle.suggestions}>
underlineColorAndroid={'transparent'} <FlatList style={uriBarStyle.suggestionList}
numberOfLines={1} data={suggestions}
value={this.state.currentValue} keyboardShouldPersistTaps={'handled'}
returnKeyType={'go'} keyExtractor={(item, value) => item.value}
onChangeText={(text) => this.setState({uri: text, currentValue: text})} renderItem={({item}) => <UriBarItem item={item}
onSubmitEditing={() => { navigation={navigation}
if (this.state.uri) { onPress={() => this.handleItemPress(item)} />} />
let uri = this.state.uri; </View>)}
uri = uri.replace(/ /g, '-'); <View style={uriBarStyle.uriContainer}>
navigation.navigate('File', { uri: normalizeURI(uri) }); <TextInput style={uriBarStyle.uriText}
} selectTextOnFocus={true}
}}/> placeholder={'Search for videos, music, games and more'}
underlineColorAndroid={'transparent'}
numberOfLines={1}
clearButtonMode={'while-editing'}
value={this.state.currentValue}
returnKeyType={'go'}
inlineImageLeft={'baseline_search_black_24'}
inlineImagePadding={16}
onFocus={() => this.setState({ focused: true })}
onBlur={() => this.setState({ focused: false })}
onChangeText={this.handleChangeText}
onSubmitEditing={() => {
if (this.state.inputText) {
let inputText = this.state.inputText;
if (isNameValid(inputText)) {
navigation.navigate({ routeName: 'File', key: 'filePage', params: { uri: normalizeURI(inputText) }});
} else {
// Open the search page with the query populated
navigation.navigate({ routeName: 'Search', key: 'searchPage', params: { searchQuery: inputText }});
}
}
}}/>
</View>
</View> </View>
); );
} }

View file

@ -156,7 +156,7 @@ class FilePage extends React.PureComponent {
<ActivityIndicator size="large" color={Colors.LbryGreen} /> <ActivityIndicator size="large" color={Colors.LbryGreen} />
<Text style={filePageStyle.infoText}>Loading decentralized data...</Text> <Text style={filePageStyle.infoText}>Loading decentralized data...</Text>
</View>} </View>}
{ claim === null && !isResolvingUri && {claim === null && !isResolvingUri &&
<View style={filePageStyle.container}> <View style={filePageStyle.container}>
<Text style={filePageStyle.emptyClaimText}>There's nothing at this location.</Text> <Text style={filePageStyle.emptyClaimText}>There's nothing at this location.</Text>
</View> </View>
@ -220,7 +220,7 @@ class FilePage extends React.PureComponent {
renderIndicator={() => null} />} renderIndicator={() => null} />}
{!this.state.showWebView && ( {!this.state.showWebView && (
<View style={filePageStyle.pageContainer}> <View style={filePageStyle.innerPageContainer}>
<View style={filePageStyle.mediaContainer}> <View style={filePageStyle.mediaContainer}>
{(canOpen || (!fileInfo || (isPlayable && !canLoadMedia))) && {(canOpen || (!fileInfo || (isPlayable && !canLoadMedia))) &&
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={metadata.thumbnail} />} <FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={metadata.thumbnail} />}

View file

@ -8,14 +8,29 @@ import {
View, View,
ScrollView ScrollView
} from 'react-native'; } from 'react-native';
import SearchResultItem from '../../component/searchResultItem';
import Colors from '../../styles/colors'; import Colors from '../../styles/colors';
import PageHeader from '../../component/pageHeader';
import SearchResultItem from '../../component/searchResultItem';
import UriBar from '../../component/uriBar';
import searchStyle from '../../styles/search'; import searchStyle from '../../styles/search';
class SearchPage extends React.PureComponent { class SearchPage extends React.PureComponent {
static navigationOptions = {
title: 'Search Results'
};
componentDidMount() {
const { navigation, search } = this.props;
const { searchQuery } = navigation.state.params;
if (searchQuery && searchQuery.trim().length > 0) {
search(searchQuery);
}
}
render() { render() {
const { isSearching, navigation, uris } = this.props; const { isSearching, navigation, uris, query } = this.props;
const { searchQuery } = navigation.state.params;
return ( return (
<View style={searchStyle.container}> <View style={searchStyle.container}>
{!isSearching && (!uris || uris.length === 0) && {!isSearching && (!uris || uris.length === 0) &&
@ -26,10 +41,15 @@ class SearchPage extends React.PureComponent {
uri={uri} uri={uri}
style={searchStyle.resultItem} style={searchStyle.resultItem}
navigation={navigation} navigation={navigation}
onPress={() => {navigation.navigate('File', { uri: uri }); }}/>) onPress={() => navigation.navigate({
routeName: 'File',
key: 'filePage',
params: { uri }})
}/>)
) : null } ) : null }
</ScrollView> </ScrollView>
{isSearching && <ActivityIndicator size="large" color={Colors.LbryGreen} style={searchStyle.loading} /> } {isSearching && <ActivityIndicator size="large" color={Colors.LbryGreen} style={searchStyle.loading} /> }
<UriBar value={searchQuery} navigation={navigation} />
</View> </View>
); );
} }

View file

@ -54,7 +54,7 @@ class SplashScreen extends React.PureComponent {
navigation.dispatch(resetAction); navigation.dispatch(resetAction);
if (this.state.launchUrl) { if (this.state.launchUrl) {
navigation.navigate('File', { uri: this.state.launchUrl }); navigation.navigate({ routeName: 'File', key: 'filePage', params: { uri: this.state.launchUrl } });
} }
}); });
return; return;

View file

@ -11,7 +11,8 @@ const channelPageStyle = StyleSheet.create({
}, },
fileList: { fileList: {
paddingTop: 30, paddingTop: 30,
flex: 1 flex: 1,
marginBottom: 60
}, },
title: { title: {
color: Colors.LbryGreen, color: Colors.LbryGreen,

View file

@ -5,7 +5,8 @@ const discoverStyle = StyleSheet.create({
flex: 1 flex: 1
}, },
scrollContainer: { scrollContainer: {
flex: 1 flex: 1,
marginBottom: 60
}, },
busyContainer: { busyContainer: {
flex: 1, flex: 1,

View file

@ -12,6 +12,10 @@ const filePageStyle = StyleSheet.create({
pageContainer: { pageContainer: {
flex: 1 flex: 1
}, },
innerPageContainer: {
flex: 1,
marginBottom: 60
},
mediaContainer: { mediaContainer: {
alignItems: 'center', alignItems: 'center',
width: screenWidth, width: screenWidth,
@ -150,7 +154,7 @@ const filePageStyle = StyleSheet.create({
left: 0, left: 0,
right: 0, right: 0,
top: 0, top: 0,
bottom: 0, bottom: 60,
zIndex: 100 zIndex: 100
} }
}); });

View file

@ -10,7 +10,8 @@ const searchStyle = StyleSheet.create({
flex: 1, flex: 1,
width: '100%', width: '100%',
height: '100%', height: '100%',
padding: 16 padding: 16,
marginBottom: 60
}, },
scrollPadding: { scrollPadding: {
paddingBottom: 16 paddingBottom: 16

View file

@ -6,6 +6,7 @@ const uriBarStyle = StyleSheet.create({
backgroundColor: Colors.White, backgroundColor: Colors.White,
padding: 8, padding: 8,
alignSelf: 'flex-end', alignSelf: 'flex-end',
height: 60,
width: '100%', width: '100%',
shadowColor: Colors.Black, shadowColor: Colors.Black,
shadowOpacity: 0.1, shadowOpacity: 0.1,
@ -24,6 +25,32 @@ const uriBarStyle = StyleSheet.create({
fontFamily: 'Metropolis-Regular', fontFamily: 'Metropolis-Regular',
fontSize: 16, fontSize: 16,
width: '100%' width: '100%'
},
overlay: {
position: 'absolute',
backgroundColor: 'transparent',
bottom: 0,
width: '100%',
zIndex: 200,
elevation: 16
},
inFocus: {
height: '100%'
},
suggestions: {
backgroundColor: 'white',
flex: 1
},
item: {
flexDirection: 'row',
justifyContent: 'flex-start',
padding: 12
},
itemText: {
fontFamily: 'Metropolis-Regular',
fontSize: 16,
marginLeft: 12,
marginRight: 12
} }
}); });

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
</vector>