Skip to content

Commit 151636e

Browse files
committed
Basic search and select gif from Giphy
1 parent cc6abb0 commit 151636e

File tree

5 files changed

+229
-23
lines changed

5 files changed

+229
-23
lines changed

package-lock.json

Lines changed: 17 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,8 @@
1515
},
1616
"devDependencies": {
1717
"@wordpress/scripts": "^7.1.2"
18+
},
19+
"dependencies": {
20+
"react-photo-gallery": "^8.0.0"
1821
}
1922
}

src/components/SearchGiphy.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Component, Fragment } from '@wordpress/element';
2+
import { TextControl, Spinner } from '@wordpress/components';
3+
import Gallery from "react-photo-gallery";
4+
5+
export default class SearchGiphy extends Component {
6+
constructor(props) {
7+
super(props);
8+
}
9+
10+
render() {
11+
const {
12+
search,
13+
onSearchChangeHandler,
14+
isLoading,
15+
gifs,
16+
onGiphyClick,
17+
pagination
18+
} = this.props;
19+
20+
let result_gifs;
21+
if ( gifs[ pagination ] ) {
22+
result_gifs = [];
23+
result_gifs = gifs[ pagination ].map( ( gif_data ) => {
24+
return {
25+
src: gif_data.images.downsized_medium.url,
26+
width: gif_data.images.downsized_medium.width,
27+
height: gif_data.images.downsized_medium.height,
28+
};
29+
} )
30+
}
31+
32+
return (
33+
<Fragment>
34+
<TextControl
35+
label="Search GIF"
36+
value={ search }
37+
onChange={ ( search ) => {
38+
onSearchChangeHandler( search );
39+
} }
40+
/>
41+
42+
{ isLoading && (
43+
<Spinner />
44+
) }
45+
46+
{ result_gifs && result_gifs.length && (
47+
<Fragment>
48+
<Gallery photos={ result_gifs } onClick={ onGiphyClick } />
49+
</Fragment>
50+
) }
51+
52+
{ result_gifs && result_gifs.length === 0 && (
53+
<p>{ `Nothing found for '${search}'.` }</p>
54+
) }
55+
56+
</Fragment>
57+
);
58+
}
59+
}

src/edit.js

Lines changed: 133 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,146 @@
11
import { Component } from '@wordpress/element';
2-
import { TextControl } from '@wordpress/components';
2+
import { AlignmentToolbar, BlockControls, BlockAlignmentToolbar } from '@wordpress/editor';
3+
import { addQueryArgs } from '@wordpress/url';
4+
import { debounce } from 'lodash';
5+
6+
import SearchGiphy from "./components/SearchGiphy";
37

48
export default class Edit extends Component {
9+
constructor( props ) {
10+
super( props );
11+
12+
this.state = {
13+
isLoading: false, // If currently fetching from Giphy.
14+
isSearching: !props.attributes.gif, // If we need to show the search field.
15+
gifs: [], // Cache results from Giphy.
16+
pagination: 0, // Current pagination.
17+
};
18+
19+
// Get the API Key from https://developers.giphy.com/.
20+
this.API_KEY = '[INSERT YOUR API KEY HERE]';
21+
this.GIPHY_ENDPOINT = 'https://api.giphy.com/v1/gifs/search';
22+
this.GIPHY_RESULTS_LIMIT = 5;
23+
24+
this.onSearchChangeHandler = this.onSearchChangeHandler.bind( this );
25+
this.fetchGiphy = this.fetchGiphy.bind( this );
26+
// Use debounce to prevent multiple concurrent request to Giphy.
27+
this.onSearchChange = debounce( this.onSearchChange.bind( this ), 500 );
28+
this.onGiphyClick = this.onGiphyClick.bind( this );
29+
}
30+
31+
componentWillUnmount() {
32+
this.onSearchChange.cancel();
33+
}
34+
35+
/**
36+
* Debounced function
37+
*
38+
* @returns {Promise<void>}
39+
*/
40+
async onSearchChange() {
41+
const {
42+
attributes: { search }
43+
} = this.props;
44+
45+
const pagination = this.state.pagination / this.GIPHY_RESULTS_LIMIT;
46+
47+
const results = await this.fetchGiphy( search, pagination );
48+
49+
if ( 200 !== results.meta.status ) {
50+
// TODO handle error.
51+
return;
52+
}
53+
54+
// The idea is to 'cache' the past results in the state.
55+
// Something like
56+
// gifs[0] => Will contain results of pagination 0.
57+
// gifs[1] => Results of pagination 1.
58+
// and so on..
59+
let gifs = this.state.gifs;
60+
gifs[ pagination ] = results.data;
61+
62+
this.setState( {
63+
isLoading: false,
64+
pagination: pagination,
65+
gifs: gifs
66+
} );
67+
}
68+
69+
/**
70+
* Fetch data from Giphy.
71+
*
72+
* @param search string
73+
* @param pagination int
74+
*
75+
* @returns {Promise<any>}
76+
*/
77+
fetchGiphy( search, pagination ) {
78+
// Build the request url.
79+
const requestUrl = addQueryArgs( this.GIPHY_ENDPOINT, {
80+
q: search,
81+
limit: this.GIPHY_RESULTS_LIMIT,
82+
api_key: this.API_KEY,
83+
offset: pagination,
84+
} );
85+
86+
return fetch( requestUrl )
87+
.then( data => data.json() )
88+
.catch( error => error );
89+
};
90+
91+
onGiphyClick( event, { photo } ) {
92+
// Save the selected photo data as `gif` in the DB.
93+
this.props.setAttributes( { gif: photo } );
94+
this.setState( { isSearching: false } );
95+
}
96+
97+
onSearchChangeHandler( search ) {
98+
this.setState( { isLoading: true } );
99+
// Save the search keyword as `search` in the DB.
100+
this.props.setAttributes( { search } );
101+
this.onSearchChange();
102+
}
103+
5104
render() {
6105
const {
7106
attributes: {
8-
search
107+
search,
108+
gif,
109+
blockAlignment,
110+
textAlignment
9111
},
10-
setAttributes,
112+
className,
113+
setAttributes
11114
} = this.props;
12115

116+
const { isLoading, isSearching, gifs, pagination } = this.state;
117+
13118
return (
14-
<TextControl
15-
label="Search GIF"
16-
value={ search }
17-
onChange={ search => setAttributes( { search } )}
18-
/>
119+
<div className={ className }>
120+
<BlockControls>
121+
<BlockAlignmentToolbar
122+
value={ blockAlignment }
123+
onChange={ blockAlignment => setAttributes( { blockAlignment } ) }
124+
/>
125+
<AlignmentToolbar
126+
value={ textAlignment }
127+
onChange={ textAlignment => setAttributes( { textAlignment } ) }
128+
/>
129+
</BlockControls>
130+
131+
{ isSearching ? (
132+
<SearchGiphy
133+
search={ search }
134+
onSearchChangeHandler={ this.onSearchChangeHandler }
135+
isLoading={ isLoading }
136+
gifs={ gifs }
137+
onGiphyClick={ this.onGiphyClick }
138+
pagination={ pagination }
139+
/>
140+
) : (
141+
<img src={ gif.src } />
142+
) }
143+
</div>
19144
);
20145
}
21146
}

src/index.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,23 @@ registerBlockType( 'create-block/giphy-block', {
4848
icon: 'smiley',
4949

5050
attributes: {
51+
textAlignment: {
52+
type: 'string',
53+
},
54+
blockAlignment: {
55+
type: 'string',
56+
default: 'wide',
57+
},
5158
search: {
5259
type: 'string'
60+
},
61+
gif: {
62+
type: 'object'
63+
}
64+
},
65+
getEditWrapperProps( { blockAlignment } ) {
66+
if ( 'left' === blockAlignment || 'right' === blockAlignment || 'full' === blockAlignment ) {
67+
return { 'data-align': blockAlignment };
5368
}
5469
},
5570

@@ -81,14 +96,9 @@ registerBlockType( 'create-block/giphy-block', {
8196
*
8297
* @return {WPElement} Element to render.
8398
*/
84-
save() {
99+
save: props => {
85100
return (
86-
<p>
87-
{ __(
88-
'Giphy Block – hello from the saved content!',
89-
'create-block'
90-
) }
91-
</p>
101+
<img src={ props.attributes.gif.src } />
92102
);
93103
},
94104
} );

0 commit comments

Comments
 (0)