RC #72
4 changed files with 82 additions and 13 deletions
|
@ -72,7 +72,9 @@ class SuggestedSubscriptionItem extends React.PureComponent {
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<SubscribeButton style={subscriptionsStyle.suggestedItemSubscribe} uri={normalizeURI(uri)} />
|
{claim && (
|
||||||
|
<SubscribeButton style={subscriptionsStyle.suggestedItemSubscribe} uri={normalizeURI(claim.permanent_url)} />
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
|
doClaimSearch,
|
||||||
doSearch,
|
doSearch,
|
||||||
doUpdateSearchQuery,
|
doUpdateSearchQuery,
|
||||||
makeSelectSearchUris,
|
makeSelectSearchUris,
|
||||||
|
selectClaimSearchByQuery,
|
||||||
selectIsSearching,
|
selectIsSearching,
|
||||||
selectSearchValue,
|
selectSearchValue,
|
||||||
makeSelectQueryWithOptions,
|
makeSelectQueryWithOptions,
|
||||||
|
@ -13,9 +15,10 @@ import { selectCurrentRoute } from 'redux/selectors/drawer';
|
||||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||||
import SearchPage from './view';
|
import SearchPage from './view';
|
||||||
|
|
||||||
const numSearchResults = 50;
|
const numSearchResults = 25;
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
claimSearchByQuery: selectClaimSearchByQuery(state),
|
||||||
currentRoute: selectCurrentRoute(state),
|
currentRoute: selectCurrentRoute(state),
|
||||||
isSearching: selectIsSearching(state),
|
isSearching: selectIsSearching(state),
|
||||||
query: selectSearchValue(state),
|
query: selectSearchValue(state),
|
||||||
|
@ -25,6 +28,7 @@ const select = state => ({
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
search: query => dispatch(doSearch(query, numSearchResults)),
|
search: query => dispatch(doSearch(query, numSearchResults)),
|
||||||
|
claimSearch: options => dispatch(doClaimSearch(options)),
|
||||||
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
|
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
|
||||||
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SEARCH)),
|
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SEARCH)),
|
||||||
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
|
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Lbry, parseURI, normalizeURI, isURIValid } from 'lbry-redux';
|
import { Lbry, createNormalizedClaimSearchKey, parseURI, normalizeURI, isURIValid } from 'lbry-redux';
|
||||||
import { ActivityIndicator, Button, FlatList, NativeModules, Text, TextInput, View } from 'react-native';
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Button,
|
||||||
|
FlatList,
|
||||||
|
NativeModules,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
} from 'react-native';
|
||||||
import { navigateToUri } from 'utils/helper';
|
import { navigateToUri } from 'utils/helper';
|
||||||
import Colors from 'styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||||
|
@ -14,6 +23,9 @@ class SearchPage extends React.PureComponent {
|
||||||
state = {
|
state = {
|
||||||
currentQuery: null,
|
currentQuery: null,
|
||||||
currentUri: null,
|
currentUri: null,
|
||||||
|
showTagResult: false,
|
||||||
|
claimSearchRun: false,
|
||||||
|
claimSearchOptions: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
static navigationOptions = {
|
static navigationOptions = {
|
||||||
|
@ -43,6 +55,9 @@ class SearchPage extends React.PureComponent {
|
||||||
this.setState({
|
this.setState({
|
||||||
currentQuery: searchQuery,
|
currentQuery: searchQuery,
|
||||||
currentUri: isURIValid(searchQuery) ? normalizeURI(searchQuery) : null,
|
currentUri: isURIValid(searchQuery) ? normalizeURI(searchQuery) : null,
|
||||||
|
claimSearchOptions: null,
|
||||||
|
claimSearchRun: false,
|
||||||
|
showTagResult: false,
|
||||||
});
|
});
|
||||||
search(searchQuery);
|
search(searchQuery);
|
||||||
}
|
}
|
||||||
|
@ -80,7 +95,12 @@ class SearchPage extends React.PureComponent {
|
||||||
|
|
||||||
handleSearchSubmitted = keywords => {
|
handleSearchSubmitted = keywords => {
|
||||||
const { search } = this.props;
|
const { search } = this.props;
|
||||||
this.setState({ currentUri: isURIValid(keywords) ? normalizeURI(keywords) : null });
|
this.setState({
|
||||||
|
currentUri: isURIValid(keywords) ? normalizeURI(keywords) : null,
|
||||||
|
claimSearchOptions: null,
|
||||||
|
claimSearchRun: false,
|
||||||
|
showTagResult: false,
|
||||||
|
});
|
||||||
search(keywords);
|
search(keywords);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -96,15 +116,45 @@ class SearchPage extends React.PureComponent {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
listHeaderComponent = () => {
|
listHeaderComponent = showTagResult => {
|
||||||
const { navigation } = this.props;
|
const { navigation, claimSearch, claimSearchByQuery } = this.props;
|
||||||
const { currentUri } = this.state;
|
const { currentUri } = this.state;
|
||||||
|
const query = this.getSearchQuery();
|
||||||
|
|
||||||
|
const canBeTag = query && query.trim().length > 0 && isURIValid(query);
|
||||||
|
if (canBeTag && !this.state.claimSearchRun) {
|
||||||
|
const options = {
|
||||||
|
any_tags: [query],
|
||||||
|
page: 1,
|
||||||
|
no_totals: true,
|
||||||
|
};
|
||||||
|
this.setState({ claimSearchOptions: options, claimSearchRun: true }, () => claimSearch(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.claimSearchRun && this.state.claimSearchOptions) {
|
||||||
|
const claimSearchKey = createNormalizedClaimSearchKey(this.state.claimSearchOptions);
|
||||||
|
const claimSearchUris = claimSearchByQuery[claimSearchKey];
|
||||||
|
this.setState({ showTagResult: claimSearchUris && claimSearchUris.length > 0 });
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<View>
|
||||||
<FileListItem uri={currentUri} featuredResult style={searchStyle.featuredResultItem} navigation={navigation} />
|
<FileListItem uri={currentUri} featuredResult style={searchStyle.featuredResultItem} navigation={navigation} />
|
||||||
|
{showTagResult && (
|
||||||
|
<TouchableOpacity style={searchStyle.tagResultItem} onPress={() => this.handleTagResultPressed(query)}>
|
||||||
|
<Text style={searchStyle.tagResultTitle}>#{query}</Text>
|
||||||
|
<Text style={searchStyle.tagResultDescription}>Explore content for this tag</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleTagResultPressed = tag => {
|
||||||
|
const { navigation } = this.props;
|
||||||
|
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_TAG, key: `tagPage`, params: { tag } });
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isSearching, navigation, query, uris, urisByQuery } = this.props;
|
const { isSearching, navigation, query, uris, urisByQuery } = this.props;
|
||||||
|
|
||||||
|
@ -119,6 +169,7 @@ class SearchPage extends React.PureComponent {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<FlatList
|
<FlatList
|
||||||
|
extraData={this.state}
|
||||||
style={searchStyle.scrollContainer}
|
style={searchStyle.scrollContainer}
|
||||||
contentContainerStyle={searchStyle.scrollPadding}
|
contentContainerStyle={searchStyle.scrollPadding}
|
||||||
keyboardShouldPersistTaps={'handled'}
|
keyboardShouldPersistTaps={'handled'}
|
||||||
|
@ -128,7 +179,7 @@ class SearchPage extends React.PureComponent {
|
||||||
maxToRenderPerBatch={20}
|
maxToRenderPerBatch={20}
|
||||||
removeClippedSubviews
|
removeClippedSubviews
|
||||||
ListEmptyComponent={!isSearching ? this.listEmptyComponent() : null}
|
ListEmptyComponent={!isSearching ? this.listEmptyComponent() : null}
|
||||||
ListHeaderComponent={this.state.currentUri ? this.listHeaderComponent() : null}
|
ListHeaderComponent={this.state.currentUri ? this.listHeaderComponent(this.state.showTagResult) : null}
|
||||||
renderItem={({ item }) => (
|
renderItem={({ item }) => (
|
||||||
<FileListItem key={item} uri={item} style={searchStyle.resultItem} navigation={navigation} />
|
<FileListItem key={item} uri={item} style={searchStyle.resultItem} navigation={navigation} />
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -35,12 +35,24 @@ const searchStyle = StyleSheet.create({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
paddingTop: 8,
|
padding: 8,
|
||||||
paddingBottom: 8,
|
|
||||||
paddingLeft: 8,
|
|
||||||
paddingRight: 8,
|
|
||||||
backgroundColor: Colors.Black,
|
backgroundColor: Colors.Black,
|
||||||
},
|
},
|
||||||
|
tagResultItem: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 16,
|
||||||
|
backgroundColor: Colors.DarkerGrey,
|
||||||
|
},
|
||||||
|
tagResultTitle: {
|
||||||
|
fontFamily: 'Inter-UI-SemiBold',
|
||||||
|
fontSize: 24,
|
||||||
|
color: Colors.White,
|
||||||
|
},
|
||||||
|
tagResultDescription: {
|
||||||
|
fontFamily: 'Inter-UI-Regular',
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.VeryLightGrey,
|
||||||
|
},
|
||||||
searchInput: {
|
searchInput: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
|
|
Loading…
Reference in a new issue