diff --git a/app/src/component/AppNavigator.js b/app/src/component/AppNavigator.js index ae672103..925b9175 100644 --- a/app/src/component/AppNavigator.js +++ b/app/src/component/AppNavigator.js @@ -38,7 +38,6 @@ const discoverStack = StackNavigator({ navigationOptions: ({ navigation }) => ({ title: 'Discover', headerLeft: navigation.navigate('DrawerOpen')} />, - headerRight: navigation.navigate('Search')} /> }) }, File: { @@ -51,9 +50,7 @@ const discoverStack = StackNavigator({ Search: { screen: SearchPage, navigationOptions: ({ navigation }) => ({ - drawerLockMode: 'locked-closed', - headerTitle: , - headerRight: + drawerLockMode: 'locked-closed' }) } }, { @@ -66,7 +63,6 @@ const walletStack = StackNavigator({ navigationOptions: ({ navigation }) => ({ title: 'Wallet', headerLeft: navigation.navigate('DrawerOpen')} />, - headerRight: navigation.navigate('Search')} /> }) }, TransactionHistory: { @@ -185,6 +181,7 @@ class AppWithNavigationState extends React.Component { if (evt.url) { const navigateAction = NavigationActions.navigate({ routeName: 'File', + key: 'filePage', params: { uri: evt.url } }); dispatch(navigateAction); diff --git a/app/src/component/fileItem/view.js b/app/src/component/fileItem/view.js index 8a7bedff..378b2dc3 100644 --- a/app/src/component/fileItem/view.js +++ b/app/src/component/fileItem/view.js @@ -61,7 +61,7 @@ class FileItem extends React.PureComponent { if (NativeModules.Mixpanel) { NativeModules.Mixpanel.track('Discover Tap', { Uri: uri }); } - navigation.navigate('File', { uri: uri }); + navigation.navigate({ routeName: 'File', key: 'filePage', params: { uri } }); } }> diff --git a/app/src/component/transactionList/internal/transaction-list-item.js b/app/src/component/transactionList/internal/transaction-list-item.js index cb4d416f..0dd3b18c 100644 --- a/app/src/component/transactionList/internal/transaction-list-item.js +++ b/app/src/component/transactionList/internal/transaction-list-item.js @@ -23,7 +23,11 @@ class TransactionListItem extends React.PureComponent { {name && claimId && ( 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} /> )} diff --git a/app/src/component/uriBar/index.js b/app/src/component/uriBar/index.js index 703caa0d..343ffb39 100644 --- a/app/src/component/uriBar/index.js +++ b/app/src/component/uriBar/index.js @@ -1,12 +1,17 @@ import { connect } from 'react-redux'; +import { doUpdateSearchQuery, selectSearchState as selectSearch } from 'lbry-redux'; import UriBar from './view'; -const select = state => ({ +const select = state => { + const { ...searchState } = selectSearch(state); -}); + return { + ...searchState + }; +}; const perform = dispatch => ({ - + updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)), }); export default connect(select, perform)(UriBar); diff --git a/app/src/component/uriBar/internal/uri-bar-item.js b/app/src/component/uriBar/internal/uri-bar-item.js new file mode 100644 index 00000000..71126bf9 --- /dev/null +++ b/app/src/component/uriBar/internal/uri-bar-item.js @@ -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 = + break; + + case SEARCH_TYPES.SEARCH: + icon = + break; + + case SEARCH_TYPES.FILE: + default: + icon = + break; + } + + return ( + + {icon} + {value} + + ) + } +} + +export default UriBarItem; diff --git a/app/src/component/uriBar/view.js b/app/src/component/uriBar/view.js index a66a24ba..ace50902 100644 --- a/app/src/component/uriBar/view.js +++ b/app/src/component/uriBar/view.js @@ -1,41 +1,96 @@ // @flow import React from 'react'; -import { normalizeURI } from 'lbry-redux'; -import { TextInput, View } from 'react-native'; +import { SEARCH_TYPES, isNameValid, normalizeURI } from 'lbry-redux'; +import { FlatList, Keyboard, TextInput, View } from 'react-native'; +import UriBarItem from './internal/uri-bar-item'; import uriBarStyle from '../../styles/uriBar'; class UriBar extends React.PureComponent { + static INPUT_TIMEOUT = 500; + constructor(props) { super(props); this.state = { - uri: null, - currentValue: null + changeTextTimeout: 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() { - const { value, navigation } = this.props; - if (!this.state.currentValue) { + const { navigation, suggestions, updateSearchQuery, value } = this.props; + if (this.state.currentValue === null) { this.setState({ currentValue: value }); } - // TODO: Search and URI suggestions overlay + let style = [uriBarStyle.overlay]; + if (this.state.focused) { + style.push(uriBarStyle.inFocus); + } + return ( - - this.setState({uri: text, currentValue: text})} - onSubmitEditing={() => { - if (this.state.uri) { - let uri = this.state.uri; - uri = uri.replace(/ /g, '-'); - navigation.navigate('File', { uri: normalizeURI(uri) }); - } - }}/> + + {this.state.focused && ( + + item.value} + renderItem={({item}) => this.handleItemPress(item)} />} /> + )} + + 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 }}); + } + } + }}/> + ); } diff --git a/app/src/page/file/view.js b/app/src/page/file/view.js index fd887e02..746987e5 100644 --- a/app/src/page/file/view.js +++ b/app/src/page/file/view.js @@ -156,7 +156,7 @@ class FilePage extends React.PureComponent { Loading decentralized data... } - { claim === null && !isResolvingUri && + {claim === null && !isResolvingUri && There's nothing at this location. @@ -220,7 +220,7 @@ class FilePage extends React.PureComponent { renderIndicator={() => null} />} {!this.state.showWebView && ( - + {(canOpen || (!fileInfo || (isPlayable && !canLoadMedia))) && } diff --git a/app/src/page/search/view.js b/app/src/page/search/view.js index 3cdb8ab0..5caa14c7 100644 --- a/app/src/page/search/view.js +++ b/app/src/page/search/view.js @@ -8,14 +8,29 @@ import { View, ScrollView } from 'react-native'; -import SearchResultItem from '../../component/searchResultItem'; 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'; 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() { - const { isSearching, navigation, uris } = this.props; - + const { isSearching, navigation, uris, query } = this.props; + const { searchQuery } = navigation.state.params; + return ( {!isSearching && (!uris || uris.length === 0) && @@ -26,10 +41,15 @@ class SearchPage extends React.PureComponent { uri={uri} style={searchStyle.resultItem} navigation={navigation} - onPress={() => {navigation.navigate('File', { uri: uri }); }}/>) + onPress={() => navigation.navigate({ + routeName: 'File', + key: 'filePage', + params: { uri }}) + }/>) ) : null } {isSearching && } + ); } diff --git a/app/src/page/splash/view.js b/app/src/page/splash/view.js index 0ff86583..99d91a92 100644 --- a/app/src/page/splash/view.js +++ b/app/src/page/splash/view.js @@ -54,7 +54,7 @@ class SplashScreen extends React.PureComponent { navigation.dispatch(resetAction); if (this.state.launchUrl) { - navigation.navigate('File', { uri: this.state.launchUrl }); + navigation.navigate({ routeName: 'File', key: 'filePage', params: { uri: this.state.launchUrl } }); } }); return; diff --git a/app/src/styles/channelPage.js b/app/src/styles/channelPage.js index 8f9e465a..5f2cfef7 100644 --- a/app/src/styles/channelPage.js +++ b/app/src/styles/channelPage.js @@ -11,7 +11,8 @@ const channelPageStyle = StyleSheet.create({ }, fileList: { paddingTop: 30, - flex: 1 + flex: 1, + marginBottom: 60 }, title: { color: Colors.LbryGreen, diff --git a/app/src/styles/discover.js b/app/src/styles/discover.js index d6897d2d..0b9a1f46 100644 --- a/app/src/styles/discover.js +++ b/app/src/styles/discover.js @@ -5,7 +5,8 @@ const discoverStyle = StyleSheet.create({ flex: 1 }, scrollContainer: { - flex: 1 + flex: 1, + marginBottom: 60 }, busyContainer: { flex: 1, diff --git a/app/src/styles/filePage.js b/app/src/styles/filePage.js index 65b74841..ca145c46 100644 --- a/app/src/styles/filePage.js +++ b/app/src/styles/filePage.js @@ -12,6 +12,10 @@ const filePageStyle = StyleSheet.create({ pageContainer: { flex: 1 }, + innerPageContainer: { + flex: 1, + marginBottom: 60 + }, mediaContainer: { alignItems: 'center', width: screenWidth, @@ -150,7 +154,7 @@ const filePageStyle = StyleSheet.create({ left: 0, right: 0, top: 0, - bottom: 0, + bottom: 60, zIndex: 100 } }); diff --git a/app/src/styles/search.js b/app/src/styles/search.js index 4ad23e73..8ace8954 100644 --- a/app/src/styles/search.js +++ b/app/src/styles/search.js @@ -10,7 +10,8 @@ const searchStyle = StyleSheet.create({ flex: 1, width: '100%', height: '100%', - padding: 16 + padding: 16, + marginBottom: 60 }, scrollPadding: { paddingBottom: 16 diff --git a/app/src/styles/uriBar.js b/app/src/styles/uriBar.js index 9f9ab752..4061072f 100644 --- a/app/src/styles/uriBar.js +++ b/app/src/styles/uriBar.js @@ -6,6 +6,7 @@ const uriBarStyle = StyleSheet.create({ backgroundColor: Colors.White, padding: 8, alignSelf: 'flex-end', + height: 60, width: '100%', shadowColor: Colors.Black, shadowOpacity: 0.1, @@ -24,6 +25,32 @@ const uriBarStyle = StyleSheet.create({ fontFamily: 'Metropolis-Regular', fontSize: 16, 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 } }); diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-hdpi/baseline_search_black_24.png b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-hdpi/baseline_search_black_24.png new file mode 100755 index 00000000..c1066f3d Binary files /dev/null and b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-hdpi/baseline_search_black_24.png differ diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-mdpi/baseline_search_black_24.png b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-mdpi/baseline_search_black_24.png new file mode 100755 index 00000000..87e74ffb Binary files /dev/null and b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-mdpi/baseline_search_black_24.png differ diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-xhdpi/baseline_search_black_24.png b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-xhdpi/baseline_search_black_24.png new file mode 100755 index 00000000..090acd4b Binary files /dev/null and b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-xhdpi/baseline_search_black_24.png differ diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-xxhdpi/baseline_search_black_24.png b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-xxhdpi/baseline_search_black_24.png new file mode 100755 index 00000000..96f68e21 Binary files /dev/null and b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-xxhdpi/baseline_search_black_24.png differ diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-xxxhdpi/baseline_search_black_24.png b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-xxxhdpi/baseline_search_black_24.png new file mode 100755 index 00000000..ee2e09a3 Binary files /dev/null and b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable-xxxhdpi/baseline_search_black_24.png differ diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable/baseline_search_24.xml b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable/baseline_search_24.xml new file mode 100755 index 00000000..083d6364 --- /dev/null +++ b/p4a/pythonforandroid/bootstraps/lbry/build/src/main/res/drawable/baseline_search_24.xml @@ -0,0 +1,10 @@ + + +