Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve status display for GIFs search #77

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 37 additions & 22 deletions web/src/giphy.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Component, html} from "../lib/htm/preact.js";
import * as widgetAPI from "./widget-api.js";
import {Spinner} from "./spinner.js";
import {SearchBox} from "./search-box.js";

const GIPHY_SEARCH_DEBOUNCE = 1000
Expand All @@ -24,6 +25,7 @@ export class GiphySearchTab extends Component {
searchTerm: "",
gifs: [],
loading: false,
debouncing: false,
error: null,
}
this.handleGifClick = this.handleGifClick.bind(this)
Expand All @@ -34,13 +36,14 @@ export class GiphySearchTab extends Component {

async makeGifSearchRequest() {
try {
const resp = await fetch(`https://api.giphy.com/v1/gifs/search?q=${this.state.searchTerm}&api_key=${GIPHY_API_KEY}`)
// TODO handle error responses properly?
const data = await resp.json()
if (data.data.length === 0) {
this.setState({gifs: [], error: "No results"})
} else {
this.setState({gifs: data.data, error: null})
this.setState({gifs: [], debouncing: false, loading: true})
try {
const resp = await fetch(`https://api.giphy.com/v1/gifs/search?q=${this.state.searchTerm}&api_key=${GIPHY_API_KEY}`)
const { data: gifs, meta } = await resp.json()
const error = meta.msg !== 'OK' ? `An issue happened, please try again (${meta.msg})` : null
this.setState({gifs, error, loading: false})
} catch (e) {
this.setState({gifs: [], error: `An issue happened, please try again (${e.message})`, loading: false})
}
} catch (error) {
this.setState({error})
Expand All @@ -59,7 +62,7 @@ export class GiphySearchTab extends Component {
}

updateGifSearchQuery(event) {
this.setState({searchTerm: event.target.value})
this.setState({searchTerm: event.target.value, debouncing: true})
clearTimeout(this.searchTimeout)
this.searchTimeout = setTimeout(() => this.makeGifSearchRequest(), GIPHY_SEARCH_DEBOUNCE)
}
Expand All @@ -82,24 +85,36 @@ export class GiphySearchTab extends Component {
}

render() {
// TODO display loading state?
return html`
<${SearchBox} onInput=${this.updateGifSearchQuery} onKeyUp=${this.searchKeyUp} value=${this.state.searchTerm} placeholder="Find GIFs"/>
<${SearchBox}
onInput=${this.updateGifSearchQuery}
onKeyUp=${this.searchKeyUp}
value=${this.state.searchTerm}
loading=${this.state.debouncing}
placeholder="Find GIFs"
/>
<div class="pack-list">
<section class="stickerpack" id="pack-giphy">
<div class="error">
${this.state.error}
</div>
<div class="sticker-list">
${this.state.gifs.map((gif) => html`
<div class="sticker" onClick=${() => this.handleGifClick(gif)} data-gif-id=${gif.id}>
<img src=${gif.images.fixed_height.url} alt=${gif.title} class="visible" data=/>
${this.state.loading ?
html`<${Spinner} size=${80} green/>` :
html`
<div class="search-error">
${this.state.error}
</div>
`)}
</div>
<div class="footer powered-by-giphy">
<img src="./res/powered-by-giphy.png" alt="Powered by GIPHY"/>
</div>
${this.state.searchTerm && this.state.gifs.length === 0 && !this.state.error && !this.state.loading
? html`<div class="search-empty">No GIFs match your search</div>`
: null}
<div class="sticker-list">
${this.state.gifs.map((gif) => html`
<div class="sticker" onClick=${() => this.handleGifClick(gif)} data-gif-id=${gif.id}>
<img src=${gif.images.fixed_height.url} alt=${gif.title} class="visible" data=/>
</div>
`)}
</div>
<div class="footer powered-by-giphy">
<img src="./res/powered-by-giphy.png" alt="Powered by GIPHY"/>
</div>
`}
</section>
</div>
`
Expand Down
2 changes: 1 addition & 1 deletion web/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ class App extends Component {
<${SearchBox} onInput=${this.searchStickers} value=${this.state.filtering.searchTerm ?? ""}/>
<div class="pack-list ${isMobileSafari ? "ios-safari-hack" : ""}" ref=${(elem) => (this.packListRef = elem)}>
${filterActive && packs.length === 0
? html`<div class="search-empty"><h1>No stickers match your search</h1></div>`
? html`<div class="search-empty">No stickers match your search</div>`
: null}
${packs.map((pack) => html`<${Pack} id=${pack.id} pack=${pack} send=${this.sendSticker}/>`)}
<${Settings} app=${this}/>
Expand Down
7 changes: 5 additions & 2 deletions web/src/search-box.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import {html} from "../lib/htm/preact.js"
import {Spinner} from "./spinner.js";

export const SearchBox = ({onInput, onKeyUp, value, placeholder = 'Find stickers'}) => {
export const SearchBox = ({onInput, onKeyUp, value, loading = false, placeholder = 'Find stickers'}) => {
const component = html`
<div class="search-box">
<input type="text" placeholder=${placeholder} value=${value} onInput=${onInput} onKeyUp=${onKeyUp}/>
<span class="icon icon-search"/>
${!loading ?
html`<span class="icon icon-search"/>` :
html`<div class="search-spinner"><${Spinner} size=${14}/></div>`}
</div>
`
return component
Expand Down
2 changes: 1 addition & 1 deletion web/style/index.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions web/style/index.sass
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,14 @@ div.pack-list.ios-safari-hack
right: 0
-webkit-overflow-scrolling: touch

div.search-error,
div.search-empty
margin: 1.2rem
text-align: center
font-weight: bold

div.search-error
color: #c43e3e

section.stickerpack
margin-top: .75rem
Expand Down Expand Up @@ -203,6 +208,7 @@ div.search-box
font-size: 1rem
color: var(--text-color)

>.search-spinner,
>span.icon
display: flex
position: absolute
Expand Down
2 changes: 1 addition & 1 deletion web/style/spinner.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion web/style/spinner.sass
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
height: 25%
border-radius: 100%
animation: sk-chase-dot-before 2.0s infinite ease-in-out both
background-color: #FFF
background-color: var(--text-color)

&:nth-child(1)
animation-delay: -1.1s
Expand Down