diff --git a/.env.example b/.env.example index d32263146..ad90daa94 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,3 @@ -BACKEND_URL = https://myapi.com -SEARCH_API_URL = https://search.com -SEARCH_API_TOKEN = abcde \ No newline at end of file +BACKEND_URL=https://myapi.com +SEARCH_API_URL=https://search.com +SEARCH_API_TOKEN=abcde \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 856b53e6c..9f7b71a66 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: "com.android.application" +apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" import com.android.build.OutputFile diff --git a/ios/eSteem.xcodeproj/project.pbxproj b/ios/eSteem.xcodeproj/project.pbxproj index dff2079bc..1bc19b785 100755 --- a/ios/eSteem.xcodeproj/project.pbxproj +++ b/ios/eSteem.xcodeproj/project.pbxproj @@ -199,13 +199,6 @@ remoteGlobalIDString = EB2648DF1C7BE17A00B8F155; remoteInfo = ReactNativeConfig; }; - 0CA1AD3C21A818CF001EAFBD /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C1AC2A99CB0143E580183824 /* RNViewOverflow.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RNViewOverflow; - }; 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; @@ -402,7 +395,7 @@ remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4; remoteInfo = "jschelpers-tvOS"; }; - 47241C6321A85341001094B9 /* PBXContainerItemProxy */ = { + 58C9351B21AB3BA000D2EB1F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = C1AC2A99CB0143E580183824 /* RNViewOverflow.xcodeproj */; proxyType = 2; @@ -757,7 +750,7 @@ 0CE2BA1E21A7301B0052071F /* Products */ = { isa = PBXGroup; children = ( - 0CE2BA2221A7301B0052071F /* libRNViewOverflow.a */, + 58C9351C21AB3BA000D2EB1F /* libRNViewOverflow.a */, ); name = Products; sourceTree = ""; @@ -1084,7 +1077,7 @@ }; }; }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "eSteem" */; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "esteem" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; @@ -1309,13 +1302,6 @@ remoteRef = 0C09B6DB21A3393A00536C9D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 0CE2BA2221A7301B0052071F /* libRNViewOverflow.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRNViewOverflow.a; - remoteRef = 0CE2BA2121A7301B0052071F /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -1505,11 +1491,11 @@ remoteRef = 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 47241C6421A85341001094B9 /* libRNViewOverflow.a */ = { + 58C9351C21AB3BA000D2EB1F /* libRNViewOverflow.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libRNViewOverflow.a; - remoteRef = 47241C6321A85341001094B9 /* PBXContainerItemProxy */; + remoteRef = 58C9351B21AB3BA000D2EB1F /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { @@ -2195,6 +2181,9 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_OTHER_PREPROCESSOR_FLAGS = "-traditional"; + INFOPLIST_PREFIX_HEADER = "${BUILD_DIR}/GeneratedInfoPlistDotEnv.h"; + INFOPLIST_PREPROCESS = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -2241,6 +2230,9 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_OTHER_PREPROCESSOR_FLAGS = "-traditional"; + INFOPLIST_PREFIX_HEADER = "${BUILD_DIR}/GeneratedInfoPlistDotEnv.h"; + INFOPLIST_PREPROCESS = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; @@ -2287,7 +2279,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "eSteem" */ = { + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "esteem" */ = { isa = XCConfigurationList; buildConfigurations = ( 83CBBA201A601CBA00E9B192 /* Debug */, diff --git a/src/components/header/view/headerView.js b/src/components/header/view/headerView.js index 043033ea0..db1edf32f 100644 --- a/src/components/header/view/headerView.js +++ b/src/components/header/view/headerView.js @@ -4,6 +4,7 @@ import { } from 'react-native'; import FastImage from 'react-native-fast-image'; import LinearGradient from 'react-native-linear-gradient'; +import { SearchModal } from '../../searchModal'; // Utils import { getReputation } from '../../../utils/user'; @@ -25,7 +26,9 @@ class HeaderView extends Component { constructor(props) { super(props); - this.state = {}; + this.state = { + isSearchModalOpen: false, + }; } // Component Life Cycles @@ -52,6 +55,10 @@ class HeaderView extends Component { return DEFAULT_IMAGE; }; + _handleOnCloseSearch = () => { + this.setState({ isSearchModalOpen: false }); + } + render() { const { handleOpenDrawer, @@ -60,12 +67,14 @@ class HeaderView extends Component { isReverse, currentAccount, } = this.props; + const { isSearchModalOpen } = this.state; const _name = this._getNameOfUser(); const _reputation = getReputation(currentAccount.reputation); return ( ); } diff --git a/src/components/modal/view/modalView.js b/src/components/modal/view/modalView.js index f719df3ad..936c9382e 100644 --- a/src/components/modal/view/modalView.js +++ b/src/components/modal/view/modalView.js @@ -32,7 +32,7 @@ export default class Modal extends Component { render() { const { - isFullScreen, isOpen, children, isRadius, + isFullScreen, isOpen, children, isRadius, isTransparent, } = this.props; return ( this._handleOnClose(this)} diff --git a/src/components/searchModal/container/searchModalContainer.js b/src/components/searchModal/container/searchModalContainer.js new file mode 100644 index 000000000..dce289a4d --- /dev/null +++ b/src/components/searchModal/container/searchModalContainer.js @@ -0,0 +1,96 @@ +import React, { Component } from 'react'; +import { withNavigation } from 'react-navigation'; + +// Services and Actions +import { search } from '../../../providers/esteem/esteem'; +import { lookupAccounts } from '../../../providers/steem/dsteem'; + +// Middleware + +// Constants +import { default as ROUTES } from '../../../constants/routeNames'; + +// Utilities + +// Component +import { SearchModalView } from '..'; + +/* + * Props Name Description Value + *@props --> props name here description here Value Type Here + * + */ + +class SearchModalContainer extends Component { + constructor(props) { + super(props); + this.state = { + searchResults: {}, + }; + } + + // Component Life Cycle Functions + + // Component Functions + _handleCloseButton = () => { + const { navigation } = this.props; + navigation.goBack(); + }; + + _handleOnChangeSearchInput = (text) => { + if (text && text !== '@') { + if (text[0] === '@') { + lookupAccounts(text.substr(1)).then((res) => { + const users = res.map(item => ({ author: item })); + this.setState({ searchResults: { type: 'user', data: users } }); + }); + } else { + search({ q: text }).then((res) => { + res.results = res.results.filter(item => item.title !== ''); + this.setState({ searchResults: { type: 'content', data: res.results } }); + }); + } + } + }; + + _handleOnPressListItem = (type, item) => { + const { navigation, handleOnClose } = this.props; + handleOnClose(); + this.setState({ searchResults: {} }); + if (type === 'user') { + navigation.navigate({ + routeName: ROUTES.SCREENS.PROFILE, + params: { + username: item.author, + }, + key: item.author, + }); + } else if (type === 'content') { + navigation.navigate({ + routeName: ROUTES.SCREENS.POST, + params: { + author: item.author, + permlink: item.permlink, + }, + key: item.permlink, + }); + } + }; + + render() { + const { searchResults } = this.state; + const { handleOnClose, isOpen } = this.props; + return ( + + ); + } +} + +export default withNavigation(SearchModalContainer); diff --git a/src/components/searchModal/index.js b/src/components/searchModal/index.js new file mode 100644 index 000000000..e621ae8b6 --- /dev/null +++ b/src/components/searchModal/index.js @@ -0,0 +1,5 @@ +import SearchModalView from './view/searchModalView'; +import SearchModal from './container/searchModalContainer'; + +export { SearchModalView, SearchModal }; +export default SearchModal; diff --git a/src/components/searchModal/view/searchModalStyles.js b/src/components/searchModal/view/searchModalStyles.js new file mode 100644 index 000000000..10a14c785 --- /dev/null +++ b/src/components/searchModal/view/searchModalStyles.js @@ -0,0 +1,64 @@ +import EStyleSheet from 'react-native-extended-stylesheet'; + +export default EStyleSheet.create({ + container: { + flex: 1, + flexDirection: 'column', + backgroundColor: 'rgba(0,0,0,0.8)', + }, + inputWrapper: { + backgroundColor: '$primaryLightBackground', + flexDirection: 'row', + height: 44, + margin: 16, + borderRadius: 8, + marginTop: 20, + padding: 5, + justifyContent: 'center', + }, + icon: { + alignSelf: 'center', + color: '$primaryDarkGray', + marginLeft: 16, + }, + input: { + color: '$primaryDarkGray', + fontSize: 14, + flexGrow: 1, + paddingHorizontal: 10, + }, + closeIconButton: { + backgroundColor: '$iconColor', + width: 20, + height: 20, + borderRadius: 20 / 2, + justifyContent: 'center', + alignSelf: 'center', + marginRight: 16, + }, + closeIcon: { + color: '$white', + fontSize: 16, + }, + body: { + marginTop: 16, + marginRight: 24, + }, + searhItems: { + marginHorizontal: 30, + marginVertical: 10, + flexDirection: 'row', + alignItems: 'center', + }, + searchItemImage: { + width: 40, + height: 40, + borderRadius: 20, + borderWidth: 1, + borderColor: '$primaryGray', + }, + searchItemText: { + color: '$white', + marginLeft: 10, + }, +}); diff --git a/src/components/searchModal/view/searchModalView.js b/src/components/searchModal/view/searchModalView.js new file mode 100644 index 000000000..511060003 --- /dev/null +++ b/src/components/searchModal/view/searchModalView.js @@ -0,0 +1,99 @@ +import React, { Component, Fragment } from 'react'; +import { + View, Text, TextInput, FlatList, TouchableHighlight, Image, +} from 'react-native'; + +// Constants + +// Components +import { Icon } from '../../icon'; +import { IconButton } from '../../iconButton'; +import { Modal } from '../..'; + +// Styles +// eslint-disable-next-line +import styles from './searchModalStyles'; + +class SearchModalView extends Component { + /* Props + * ------------------------------------------------ + * @prop { type } name - Description.... + */ + + constructor(props) { + super(props); + this.state = {}; + } + + // Component Life Cycles + + // Component Functions + + render() { + const { + isOpen, + handleOnClose, + searchResults, + handleOnPressListItem, + handleOnChangeSearchInput, + } = this.props; + + return ( + + + + + + handleOnChangeSearchInput(text)} + placeholder="Search..." + placeholderTextColor="#c1c5c7" + /> + handleOnClose()} + /> + + + ( + handleOnPressListItem(searchResults.type, item)} + > + + + + {searchResults.type === 'user' ? item.author : item.title} + + + + )} + keyExtractor={(post, index) => index.toString()} + removeClippedSubviews + onEndThreshold={0} + initialNumToRender={20} + /> + + + + + ); + } +} + +export default SearchModalView; diff --git a/src/config/search.js b/src/config/search.js index e3d58413e..74766b2fe 100644 --- a/src/config/search.js +++ b/src/config/search.js @@ -4,7 +4,7 @@ import Config from 'react-native-config'; const search = axios.create({ baseURL: Config.SEARCH_API_URL, headers: { - 'Authorization': Config.SEARCH_API_TOKEN, + Authorization: Config.SEARCH_API_TOKEN, 'Content-Type': 'application/json', }, }); diff --git a/src/providers/esteem/esteem.js b/src/providers/esteem/esteem.js index ffe14372a..69ceab18f 100644 --- a/src/providers/esteem/esteem.js +++ b/src/providers/esteem/esteem.js @@ -1,4 +1,5 @@ import api from '../../config/api'; +import searchApi from '../../config/search'; export const getDrafts = data => new Promise((resolve, reject) => { api @@ -93,3 +94,14 @@ export const getUnreadActivityCount = data => new Promise((resolve, reject) => { reject(error); }); }); + +export const search = data => new Promise((resolve, reject) => { + searchApi + .post('/search', data) + .then((res) => { + resolve(res.data); + }) + .catch((error) => { + reject(error); + }); +}); diff --git a/src/screens/home/screen/homeScreen.js b/src/screens/home/screen/homeScreen.js index a489584b4..521f54077 100644 --- a/src/screens/home/screen/homeScreen.js +++ b/src/screens/home/screen/homeScreen.js @@ -34,6 +34,7 @@ export default class HomeScreen extends PureComponent { return (
+