├── .babelrc ├── .eslintrc.json ├── .flowconfig ├── .gitignore ├── .watchmanconfig ├── App.js ├── App.test.js ├── README.md ├── app.json ├── package.json ├── src ├── ItemSeperator.js ├── Repository.js ├── Results.js ├── SearchBox.js └── styles.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-expo"], 3 | "env": { 4 | "development": { 5 | "plugins": ["transform-react-jsx-source"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb", "prettier", "prettier/react"], 3 | "parser": "babel-eslint", 4 | "parserOptions": { 5 | "ecmaVersion": 8, 6 | "ecmaFeatures": { 7 | "experimentalObjectRestSpread": true, 8 | "impliedStrict": true, 9 | "classes": true 10 | } 11 | }, 12 | "env": { 13 | "browser": true, 14 | "node": true, 15 | "jquery": true 16 | }, 17 | "rules": { 18 | "no-unused-vars": [ 19 | 1, 20 | { 21 | "argsIgnorePattern": "res|next|^err" 22 | } 23 | ], 24 | "arrow-body-style": [2, "as-needed"], 25 | "no-param-reassign": [ 26 | 2, 27 | { 28 | "props": false 29 | } 30 | ], 31 | "no-console": 0, 32 | "import": 0, 33 | "func-names": 0, 34 | "space-before-function-paren": 0, 35 | "comma-dangle": 0, 36 | "max-len": 0, 37 | "import/extensions": 0, 38 | "no-underscore-dangle": 0, 39 | "consistent-return": 0, 40 | "react/display-name": 1, 41 | "react/react-in-jsx-scope": 0, 42 | "react/forbid-prop-types": 0, 43 | "react/no-unescaped-entities": 0, 44 | "react/jsx-filename-extension": [ 45 | 1, 46 | { 47 | "extensions": [".js", ".jsx"] 48 | } 49 | ], 50 | "radix": 0, 51 | "no-shadow": [ 52 | 2, 53 | { 54 | "hoist": "all", 55 | "allow": ["resolve", "reject", "done", "next", "err", "error"] 56 | } 57 | ], 58 | "quotes": [ 59 | 2, 60 | "single", 61 | { 62 | "avoidEscape": true, 63 | "allowTemplateLiterals": true 64 | } 65 | ], 66 | "prettier/prettier": [ 67 | "error", 68 | { 69 | "trailingComma": "es5", 70 | "singleQuote": true, 71 | "printWidth": 120 72 | } 73 | ], 74 | "jsx-a11y/href-no-hash": "off", 75 | "jsx-a11y/anchor-is-valid": [ 76 | "warn", 77 | { 78 | "aspects": ["invalidHref"] 79 | } 80 | ] 81 | }, 82 | "plugins": ["eslint-plugin-html", "prettier"] 83 | } 84 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | .*/Libraries/react-native/ReactNative.js 16 | 17 | ; Additional create-react-native-app ignores 18 | 19 | ; Ignore duplicate module providers 20 | .*/node_modules/fbemitter/lib/* 21 | 22 | ; Ignore misbehaving dev-dependencies 23 | .*/node_modules/xdl/build/* 24 | .*/node_modules/reqwest/tests/* 25 | 26 | ; Ignore missing expo-sdk dependencies (temporarily) 27 | ; https://github.com/expo/expo/issues/162 28 | .*/node_modules/expo/src/* 29 | 30 | ; Ignore react-native-fbads dependency of the expo sdk 31 | .*/node_modules/react-native-fbads/* 32 | 33 | [include] 34 | 35 | [libs] 36 | node_modules/react-native/Libraries/react-native/react-native-interface.js 37 | node_modules/react-native/flow 38 | flow/ 39 | 40 | [options] 41 | module.system=haste 42 | 43 | emoji=true 44 | 45 | experimental.strict_type_args=true 46 | 47 | munge_underscores=true 48 | 49 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 50 | 51 | suppress_type=$FlowIssue 52 | suppress_type=$FlowFixMe 53 | suppress_type=$FixMe 54 | 55 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-9]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 56 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-9]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 57 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 58 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 59 | 60 | unsafe.enable_getters_and_setters=true 61 | 62 | [version] 63 | ^0.49.1 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | npm-debug.* 4 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Text, View } from "react-native"; 3 | import { InstantSearch } from "react-instantsearch/native"; 4 | import styles from "./src/styles"; 5 | import SearchBox from "./src/SearchBox"; 6 | import Results from "./src/Results"; 7 | 8 | export default class App extends React.PureComponent { 9 | render() { 10 | return ( 11 | 12 | Github Repository Search 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import App from './App'; 3 | 4 | import renderer from 'react-test-renderer'; 5 | 6 | it('renders without crashing', () => { 7 | const rendered = renderer.create().toJSON(); 8 | expect(rendered).toBeTruthy(); 9 | }); 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Algolia Instant Search With Highlight Example 2 | 3 | Github Search React Native example application built with Algolia's Instant Search library. 4 | 5 | ### Watch Demo Video 6 | 7 | [![](https://img.youtube.com/vi/YR6Rf7NB-2g/0.jpg)](https://youtu.be/YR6Rf7NB-2g) 8 | 9 | ## Blog Post Explaining The Application 10 | 11 | https://bilalbudhani.com/building-algolia-powered-search-in-react-native/ 12 | 13 | ## Note From Author 14 | 15 | Hi, thanks for checking out this library. You can read more about me at https://bilalbudhani.com/about or you can follow me at https://twitter.com/BilalBudhani 16 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "sdkVersion": "21.0.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "algoliaSearch", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "jest-expo": "~20.0.0", 7 | "react-native-scripts": "1.3.1", 8 | "react-test-renderer": "16.0.0-alpha.12" 9 | }, 10 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js", 11 | "scripts": { 12 | "start": "react-native-scripts start", 13 | "eject": "react-native-scripts eject", 14 | "android": "react-native-scripts android", 15 | "ios": "react-native-scripts ios", 16 | "test": "node node_modules/jest/bin/jest.js --watch" 17 | }, 18 | "jest": { 19 | "preset": "jest-expo" 20 | }, 21 | "dependencies": { 22 | "expo": "21.0.2", 23 | "react": "16.0.0-alpha.12", 24 | "react-instantsearch": "^4.1.2", 25 | "react-native": "0.48.4" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ItemSeperator.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View } from "react-native"; 3 | import styles from "./styles"; 4 | 5 | const ItemSeperator = () => ; 6 | 7 | export default ItemSeperator; 8 | -------------------------------------------------------------------------------- /src/Repository.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, Text } from "react-native"; 3 | import { FontAwesome } from "@expo/vector-icons"; 4 | import { connectHighlight } from "react-instantsearch/connectors"; 5 | import styles from "./styles"; 6 | 7 | const Highlight = connectHighlight( 8 | ({ highlight, attributeName, hit, highlightProperty }) => { 9 | const parsedHit = highlight({ 10 | attributeName, 11 | hit, 12 | highlightProperty: "_highlightResult" 13 | }); 14 | 15 | const highlightedHit = parsedHit.map((part, idx) => { 16 | if (part.isHighlighted) { 17 | return ( 18 | 19 | {part.value} 20 | 21 | ); 22 | } 23 | return part.value; 24 | }); 25 | 26 | return {highlightedHit}; 27 | } 28 | ); 29 | 30 | const Repository = ({ repo }) => ( 31 | 32 | 33 | 34 | 35 | 36 | 37 | 42 | {repo.description} 43 | 44 | 45 | 46 | 47 | {repo.stars} 48 | 49 | 50 | ); 51 | 52 | export default Repository; 53 | -------------------------------------------------------------------------------- /src/Results.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FlatList } from "react-native"; 3 | import { connectInfiniteHits } from "react-instantsearch/connectors"; 4 | import Repository from "./Repository"; 5 | import ItemSeperator from "./ItemSeperator"; 6 | 7 | const Results = connectInfiniteHits(({ hits, hasMore, refine }) => { 8 | const onEndReached = () => { 9 | if (hasMore) { 10 | refine(); 11 | } 12 | }; 13 | 14 | return ( 15 | repo.objectID} 19 | renderItem={({ item }) => } 20 | ItemSeparatorComponent={ItemSeperator} 21 | /> 22 | ); 23 | }); 24 | 25 | export default Results; 26 | -------------------------------------------------------------------------------- /src/SearchBox.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { TextInput } from "react-native"; 3 | import { connectSearchBox } from "react-instantsearch/connectors"; 4 | import styles from "./styles"; 5 | 6 | const SearchBox = connectSearchBox(({ refine, currentRefinement }) => { 7 | return ( 8 | refine(text)} 11 | value={currentRefinement} 12 | placeholder="Search Something" 13 | clearButtonMode="always" 14 | spellCheck={false} 15 | autoCorrect={false} 16 | autoCapitalize="none" 17 | /> 18 | ); 19 | }); 20 | 21 | export default SearchBox; 22 | -------------------------------------------------------------------------------- /src/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from "react-native"; 2 | 3 | export default StyleSheet.create({ 4 | container: { 5 | flex: 1, 6 | backgroundColor: "#fff", 7 | alignItems: "center", 8 | justifyContent: "center", 9 | paddingTop: 40 10 | }, 11 | brandTitle: { 12 | fontSize: 20, 13 | fontWeight: "bold", 14 | color: "#333" 15 | }, 16 | textBox: { 17 | height: 40, 18 | borderWidth: 1, 19 | borderColor: "#333", 20 | padding: 10, 21 | margin: 10, 22 | flex: 1 23 | }, 24 | searchBoxContainer: { 25 | width: "100%", 26 | flexDirection: "row" 27 | }, 28 | repoContainer: { 29 | flexDirection: "row", 30 | alignItems: "center", 31 | justifyContent: "center", 32 | padding: 10 33 | }, 34 | repoIcon: { 35 | fontSize: 40, 36 | paddingHorizontal: 10, 37 | color: "#333" 38 | }, 39 | metaContainer: { 40 | justifyContent: "center", 41 | alignItems: "center", 42 | flexDirection: "row" 43 | }, 44 | starIcon: { 45 | color: "#333", 46 | marginRight: 2 47 | }, 48 | repoTitle: { 49 | color: "#333", 50 | fontWeight: "bold" 51 | }, 52 | repoDescription: { 53 | color: "#696969" 54 | }, 55 | seperator: { 56 | backgroundColor: "#333", 57 | height: StyleSheet.hairlineWidth 58 | } 59 | }); 60 | --------------------------------------------------------------------------------