curl localhost:5984
and get back something like:{"couchdb":"Welcome","uuid":"xxxxxxxxxxxxxxxx","version":"1.6.0","vendor":{"name":"Ubuntu","version":"15.10"}}
{ "require": { "doctrine/couchdb-odm": "@dev" }, "minimum-stability": "dev", "prefer-stable": true }
doctrine/couchdb-odm
library now belongs to our project. She will help us work with CouchDB in PHP. To install it, run composer install
. <?php require 'vendor/autoload.php'; set_time_limit(0); $client = \Doctrine\CouchDB\CouchDBClient::create(array('dbname' => 'pokedex')); $pokedex = file_get_contents('http://pokeapi.co/api/v1/pokedex/1/'); $pokedex_data = json_decode($pokedex, true); foreach($pokedex_data['pokemon'] as $row){ //get details $pokemon = file_get_contents('http://pokeapi.co/' . $row['resource_uri']); $pokemon = json_decode($pokemon, true); //get description $pokemon_description = file_get_contents('http://pokeapi.co/' . $pokemon['descriptions'][0]['resource_uri']); $pokemon['description'] = json_decode($pokemon_description, true)['description']; //get sprites $pokemon_sprites = file_get_contents('http://pokeapi.co' . $pokemon['sprites'][0]['resource_uri']); $pokemon_sprites = json_decode($pokemon_sprites, true); $pokemon['small_photo'] = 'http://pokeapi.co' . $pokemon_sprites['image']; $client->postDocument($pokemon); }
set_time_limit
parameter is assigned a zero value. The fact is that with PHP scripts, by default the execution time is limited, and after this time is over the script is interrupted. Zeroing the above parameter, we disable the time limit. <?php require 'vendor/autoload.php'; set_time_limit(0);
<?php $client = \Doctrine\CouchDB\CouchDBClient::create(array('dbname' => 'pokedex'));
file_get_contents
we get from the API a full list of Pokemons. The data will be returned in JSON format, so for further work we will have to convert them into an array: <?php $pokedex = file_get_contents('http://pokeapi.co/api/v1/pokedex/1/'); $pokedex_data = json_decode($pokedex, true);
<?php foreach($pokedex_data['pokemon'] as $row){ ... }
resource_uri
and use it to create a URL that returns details: <?php //get details $pokemon = file_get_contents('http://pokeapi.co/' . $row['resource_uri']); $pokemon = json_decode($pokemon, true);
<?php //get description $pokemon_description = file_get_contents('http://pokeapi.co/' . $pokemon['descriptions'][0]['resource_uri']); $pokemon['description'] = json_decode($pokemon_description, true)['description']; //get sprites $pokemon_sprites = file_get_contents('http://pokeapi.co' . $pokemon['sprites'][0]['resource_uri']); $pokemon_sprites = json_decode($pokemon_sprites, true); $pokemon['small_photo'] = 'http://pokeapi.co' . $pokemon_sprites['image'];
<?php $client->postDocument($pokemon);
function(doc) { emit(doc.name, null); }
<?php require 'vendor/autoload.php'; $client = \Doctrine\CouchDB\CouchDBClient::create(array('dbname' => 'pokedex')); $pokemon = $_GET['name']; $query = $client->createViewQuery('pokemon', 'by_name'); $query->setKey($pokemon); $query->setReduce(false); $query->setIncludeDocs(true); $result = $query->execute(); if(!empty($result[0])){ $data = $result[0]; echo json_encode($data); }else{ $result = array('no_result' => true); echo json_encode($result); }
<?php $pokemon = $_GET['name'];
createViewQuery
method, createViewQuery
create the view, set the names for the Design document
and View name
, and then determine the option values. The setKey method is used to describe a query; data from the view is setReduce
using setReduce
, and setIncludeDocs
adds a specific document for each returned result. You may have noticed that there is no document in the screenshot above. The fact is that when setIncludeDocs
is setIncludeDocs
and the argument is true
, the document that was saved during the previous access to the pokemon.php file is used by default . <?php $query = $client->createViewQuery('pokemon', 'by_name'); // design document view name $query->setKey($pokemon); // pokemon $query->setReduce(false); // reduce $query->setIncludeDocs(true); // $result = $query->execute(); //
<?php if(!empty($result[0])){ $data = $result[0]; echo json_encode($data); }else{ $result = array('no_result' => true); echo json_encode($result); }
react-native init Pokedex
cd Pokedex
npm install lodash react-native-android-speech react-native-gifted-spinner --save
lodash
is used to translate text into a lodash
and, as well as to extract specific data from an array.react-native-android-speech
- converts textual description to speech.react-native-gifted-spinner
- displays the loading animation when a network request is made. module.exports = function(pokemon){ var url = 'http://192.168.xxx.xxx/pokedex/get.php?name=' + pokemon; return fetch(url).then(function(response){ return response.json(); }).then(function(json){ return json; }); }
fetch
function exports the data retrieval function from the server. In this way, network requests are made in React Native. This method gets the URL to create the request, returns a promise to use the then
method and gets the answer using the callback function.json
method, which is available from the response
object. From there, you can already get data in JSON format by calling another then
method. They are passed to the callback function as an argument, which we then return.fetch
method is also a promise. Therefore, when we later call this module from our main script, we will have to use the then
method again before accessing the JSON data.http://192.168.xxx.xxx/
to the previously mentioned IP address or domain name. Remember that pokedex is a folder inside the web index. 'use strict'; var React = require('react-native'); var tts = require('react-native-android-speech') var GiftedSpinner = require('react-native-gifted-spinner'); var _ = require('lodash');
var { AppRegistry, StyleSheet, Text, TextInput, View, Image, ListView } = React;
AppRegistry
- registers custom components. In React Native, everything is a component, and components can consist of even smaller components.StyleSheet
- used to describe the styles used in the application.Text
- is responsible for displaying text.TextInput
- used to create text fields.View
- the main component when creating a user interface. Usually used as a wrapper.Image
- displays images.ListView
- displays lists.api
method. var api = require('./src/api.js');
var Pokedex = React.createClass({ ... });
query
- user-entered text.hasResult
- says that there is a search result.noResult
- says that there is no search result. It performs the opposite task to hasResult
, but is used to decide whether to display a no-result message. We do not want the user to immediately see such a revelation at the first load of the application, when no search has yet been carried out.result
- stores the current result received from the server.isLoading
- displays the download indicator.dataSource
- contains the data source for the list view. First, a new instance of ListView.DataSource
is created, which receives an object containing the rowHasChanged
function. When the data source is changed, this function gives the ListView
command to re-render the line in the list. In our case, the source is an array consisting of objects of Pokemon types. If you look at the search
method, you will understand how this data is provided. getInitialState: function(){ return { query: null, hasResult: false, noResult: false, result: null, isLoading: false, dataSource: new ListView.DataSource({ rowHasChanged: (row1, row2) => row1 !== row2, }) } },
render
method. He is responsible for rendering the user interface. render: function() { ... },
return ( <View style={styles.container}> <View style={styles.search}> <TextInput style={styles.text_input} onChangeText={this.changeText} onSubmitEditing={this.search} placeholder="Type a pokemon name" /> </View> { this.state.hasResult && <View style={styles.result}> <View style={styles.main_details}> <Image source={{uri: this.state.result.small_photo}} style={styles.image_dimensions} resizeMode={Image.resizeMode.contain} /> <Text style={styles.main_text}>{this.state.result.name}</Text> <ListView contentContainerStyle={styles.types} dataSource={this.state.types} renderRow={this.renderType}></ListView> <View style={styles.description}> <Text style={styles.description_text}>{this.state.result.description}</Text> </View> </View> </View> } { this.state.noResult && <View style={styles.no_result}> <Text style={styles.main_text}>Pokemon not found</Text> <Text style={styles.sub_text}>Please type the exact name</Text> </View> } { this.state.isLoading && <View style={styles.loader}> <GiftedSpinner /> </View> } </View> );
<View style={styles.container}> </View>
style
attribute is an object that describes the styling of the component. Below you will learn how the styles
object is declared, but for now just remember that when using objects as values, do not use quotes.onChangeText
- sets the function to be executed each time the text changes in the text field.onSubmitEditing
- sets the function to be executed each time the text is sent in the text field.placeholder
- sets the text displayed when there is no data to enter. <View style={styles.search}> <TextInput style={styles.text_input} onChangeText={this.changeText} onSubmitEditing={this.search} placeholder="Type a pokemon name" /> </View>
Image
, which displays the image of a pokemon, and Text
with the name of the animal. Next comes the ListView
component, it contains information about the type of pokemon. The fact is that some Pokemons can belong to several species, so we need this component. Finally, the View
component is responsible for displaying the description of the Pokémon. { this.state.hasResult && <View style={styles.result}> <View style={styles.main_details}> <Image source={{uri: this.state.result.small_photo}} style={styles.image_dimensions} resizeMode={Image.resizeMode.contain} /> <Text style={styles.main_text}>{this.state.result.name}</Text> <ListView contentContainerStyle={styles.types} dataSource={this.state.types} renderRow={this.renderType}></ListView> <View style={styles.description}> <Text style={styles.description_text}>{this.state.result.description}</Text> </View> </View> </View> }
Image
gets the source
attribute, which allows us to specify the source of the image. This can be a picture from the network, from the local file system or from application resources. In our case, a picture from the network is used, and the data is stored in the state. resizeMode
sets the image resizing if it does not fit into the frame. The contain
used as the value. This means that the image fits into the frame without distorting the proportions. <Image source={{uri: this.state.result.small_photo}} style={styles.image_dimensions} resizeMode={Image.resizeMode.contain} />
Text
component displays text. In React Native, any output text must fit into the Text
component. <Text style={styles.main_text}>{this.state.result.name}</Text>
ListView
component displays a list. There is one nuance here: contentContainerStyle
used instead of the style
attribute to determine the style.dataSource
allows dataSource
to define a data source for rendering the list, and renderRow
specifies the function to be performed when rendering each item in the list. <ListView contentContainerStyle={styles.types} dataSource={this.state.types} renderRow={this.renderType}></ListView>
{ this.state.noResult && <View style={styles.no_result}> <Text style={styles.main_text}>Pokemon not found</Text> <Text style={styles.sub_text}>Please type the exact name</Text> </View> }
isLoading
property is true
. This is done immediately before performing the network request, and when the response is received, the value changes to false
. { this.state.isLoading && <View style={styles.loader}> <GiftedSpinner /> </View> }
ListView
component, renderRow
used as the value of the renderRow
attribute. This is the very method. renderType: function(type){ return ( <View style={[styles[type.name], styles.type]}> <Text style={styles.type_text}>{type.name}</Text> </View> ); },
ListView
code, you’ll see that in the renderRow
attribute we’ve not found the type
that we refer to below. The fact is that renderRow
automatically transfers data in a hidden way.types
object contains an array of objects corresponding to different types of Pokemon: [ { "name":"electric", "resource_uri":"\/api\/v1\/type\/13\/" } ]
renderType
method renderType
a type
argument. It is used to display the type of pokemon and style management. Further, in the style declaration, you will need to add different styles for each type. As you can see, for the View
component, we use two style declarations. Thus, each style declaration is added to the React Native array. <View style={[styles[type.name], styles.type]}> <Text style={styles.type_text}>{type.name}</Text> </View>
changeText
method is changeText
, which is executed each time the text in the text field changes. Since the actual text data is transferred to this method, it is a good place to update the state property that stores the current information entered by the user. changeText: function(text){ this.setState({ query: text }); },
search
method is executed each time data is sent in a text field. When you enter text into Android, the Done button appears, clicking which triggers the onSubmitEditing
event. Above, we already defined this.search
as the value for the onSubmitEditing
attribute. As a result, the following method is executed: search: function(){ var pokemon = _.capitalize(this.state.query); this.setState({ isLoading: true }); api(pokemon).then( (data) => { var speech = 'Pokemon was not found. Please type the exact name.'; if(data.doc){ var types = this.state.dataSource.cloneWithRows(data.doc.types); this.setState({ hasResult: true, noResult: false, result: data.doc, types: types, isLoading: false }); var type_names = _.map(data.doc.types, function(type){ return type.name; }); speech = data.doc.name + ". A " + type_names.join(' and ') + ' pokemon. ' + data.doc.description; }else{ this.setState({ hasResult: false, noResult: true, isLoading: false, result: null }); } tts.speak({ text: speech, forceStop : true , language : 'en' }); } ); }
capitalize
method provided by Lodash is called to convert all letters to uppercase, with the exception of the first letters. The state is then updated, and the isLoading
property is set to true
, so that the load indicator is displayed below the last component. var pokemon = _.capitalize(this.state.query); this.setState({ isLoading: true });
api
module, we perform a network request: api(pokemon).then( (data) => { ... } );
api(pokemon).then(function(data){ ... });
this
object also differs. According to the new syntax, this object refers to the external scope, and not to the callback function. This allows this
to be used to update the state from within the callback function, without the need to create a variable to store the current scope.var speech = 'Pokemon was not found. Please type the exact name.';
doc
, cloneWithRows
, , dataSource
. , dataSource
ListView
.type_names
, , . Lodash map
. , : if(data.doc){ //create the list view data source var types = this.state.dataSource.cloneWithRows(data.doc.types); //update the state this.setState({ hasResult: true, noResult: false, result: data.doc, types: types, isLoading: false }); //create an array containing the type names var type_names = _.map(data.doc.types, function(type){ return type.name; }); //construct the text to be used for the speech speech = data.doc.name + ". A " + type_names.join(' and ') + ' pokemon. ' + data.doc.description; }
hasResult
false
, , .noResult
true
, .isLoading
false
, .result
null
. ... else{ this.setState({ hasResult: false, noResult: true, isLoading: false, result: null }); }
if(data.doc){ ... }else{ ... } tts.speak({ text: speech, forceStop : true , language : 'en' });
Pokodex
StyleSheet
API: var styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#FFF' }, search: { flex: 1 }, result: { flex: 8 }, no_result: { flex: 8, alignItems: 'center' }, loader: { flex: 1, alignItems: 'center' }, main_details: { padding: 30, alignItems: 'center' }, image_dimensions: { width: 100, height: 100 }, main_text: { fontSize: 25, fontWeight: 'bold', textAlign: 'center' }, sub_text: { color: '#6e6e6e' }, description: { marginTop: 20 }, text_input: { height: 40, borderColor: 'gray', borderWidth: 1 }, types: { flexDirection: 'row', marginTop: 20 }, type: { padding: 5, width: 100, alignItems: 'center' }, type_text: { color: '#fff', }, normal: { backgroundColor: '#8a8a59' }, fire: { backgroundColor: '#f08030' }, water: { backgroundColor: '#6890f0' }, electric: { backgroundColor: '#f8d030' }, grass: { backgroundColor: '#78c850' }, ice: { backgroundColor: '#98d8d8' }, fighting: { backgroundColor: '#c03028' }, poison: { backgroundColor: '#a040a0' }, ground: { backgroundColor: '#e0c068' }, flying: { backgroundColor: '#a890f0' }, psychic: { backgroundColor: '#f85888' }, bug: { backgroundColor: '#a8b820' }, rock: { backgroundColor: '#b8a038' }, ghost: { backgroundColor: '#705898' }, dragon: { backgroundColor: '#7038f8' }, dark: { backgroundColor: '#705848' }, steel: { backgroundColor: '#b8b8d0' }, fairy: { backgroundColor: '#e898e8' } });
flex
1
, Flexbox. , . , . , . container: { flex: 1, backgroundColor: '#FFF' },
search: { flex: 1 }, result: { flex: 8 }, no_result: { flex: 8, alignItems: 'center' }, loader: { flex: 1, alignItems: 'center' },
flex: 1
, . 10 , 1 , 8 . <View style={styles.search}> ... </View> <View style={styles.result}> ... </View> <View style={styles.no_result}> ... </View> <View style={styles.loader}> ... </View>
main_details
. , , . main_details: { padding: 30, alignItems: 'center' },
image_dimensions: { width: 100, height: 100 }, main_text: { fontSize: 25, fontWeight: 'bold', textAlign: 'center' }, sub_text: { color: '#6e6e6e' }, description: { marginTop: 20 },
ListView
flexDirection
row
. , , (inline effect). . , width
. 5 . alignItems
. types: { flexDirection: 'row', marginTop: 20 }, type: { padding: 5, width: 100, alignItems: 'center', }, type_text: { color: '#fff', },
normal: { backgroundColor: '#8a8a59' }, fire: { backgroundColor: '#f08030' }, water: { backgroundColor: '#6890f0' }, electric: { backgroundColor: '#f8d030' }, grass: { backgroundColor: '#78c850' }, ice: { backgroundColor: '#98d8d8' }, fighting: { backgroundColor: '#c03028' }, poison: { backgroundColor: '#a040a0' }, ground: { backgroundColor: '#e0c068' }, flying: { backgroundColor: '#a890f0' }, psychic: { backgroundColor: '#f85888' }, bug: { backgroundColor: '#a8b820' }, rock: { backgroundColor: '#b8a038' }, ghost: { backgroundColor: '#705898' }, dragon: { backgroundColor: '#7038f8' }, dark: { backgroundColor: '#705848' }, steel: { backgroundColor: '#b8b8d0' }, fairy: { backgroundColor: '#e898e8' }
AppRegistry
. React Native Pokedex
. AppRegistry.registerComponent('Pokedex', () => Pokedex);
react-native run-android
. :react-native start
. : [7:38:33 AM] <START> Building Dependency Graph [7:38:33 AM] <START> Crawling File System [7:38:33 AM] <START> Loading bundles layout [7:38:33 AM] <END> Loading bundles layout (1ms) React packager ready. [7:38:46 AM] <END> Crawling File System (13516ms) [7:38:46 AM] <START> Building in-memory fs for JavaScript [7:38:52 AM] <END> Building in-memory fs for JavaScript (6200ms) [7:38:52 AM] <START> Building in-memory fs for Assets [7:38:59 AM] <END> Building in-memory fs for Assets (6048ms) [7:38:59 AM] <START> Building Haste Map [7:39:03 AM] <START> Building (deprecated) Asset Map [7:39:05 AM] <END> Building (deprecated) Asset Map (2191ms) [7:39:08 AM] <END> Building Haste Map (9348ms) [7:39:08 AM] <END> Building Dependency Graph (35135ms)
adb shell input keyevent 82
. . Dev settings , Debugging IP- , React.Error building DependencyGraph:
Error: Watcher took too long to load
Try running `watchman version` from your terminal
https://facebook.imtqy.com/watchman/docs/troubleshooting.html
at [object Object]._onTimeout (index.js:103:16)
at Timer.listOnTimeout (timers.js:89:15)
sudo sysctl fs.inotify.max_user_instances=99999
sudo sysctl fs.inotify.max_user_watches=99999
sudo sysctl fs.inotify.max_queued_events=99999
watchman shutdown-server
echo 999999 | sudo tee -a /proc/sys/fs/inotify/max_user_instances
echo 999999 | sudo tee -a /proc/sys/fs/inotify/max_user_watches
echo 999999 | sudo tee -a /proc/sys/fs/inotify/max_queued_events
watchman shutdown-server
Source: https://habr.com/ru/post/301734/
All Articles