diff options
Diffstat (limited to 'client')
| -rw-r--r-- | client/actions.js | 10 | ||||
| -rw-r--r-- | client/app.css | 1 | ||||
| -rw-r--r-- | client/components/query.component.js | 75 | ||||
| -rw-r--r-- | client/components/results.component.js | 40 | ||||
| -rw-r--r-- | client/store.js | 24 | ||||
| -rw-r--r-- | client/types.js | 5 |
6 files changed, 112 insertions, 43 deletions
diff --git a/client/actions.js b/client/actions.js index 3fee515..5f396e2 100644 --- a/client/actions.js +++ b/client/actions.js @@ -8,7 +8,7 @@ export const api = (dispatch, method, tag, url, params, after) => { dispatch({ type: types.api.loaded, tag, data }) return data }).catch(err => { - dispatch({ type: types.api.error, tag, err }) + dispatch({ type: types.api.error, tag, error }) }) } @@ -19,16 +19,20 @@ export const upload = (blob, threshold) => dispatch => { api(dispatch, post, 'similar', '/api/v1/similar', params) } -export const submit = (url, threshold) => dispatch => { +export const similar = (url, threshold) => dispatch => { const params = new FormData() params.append('url', url) params.append('threshold', threshold) api(dispatch, post, 'similar', '/api/v1/similar', params) } -export const submit = (url, threshold) => dispatch => { +export const match = (url, threshold) => dispatch => { const params = new FormData() params.append('url', url) params.append('threshold', threshold) api(dispatch, post, 'similar', '/api/v1/match', params) } + +export const updateQuery = state => dispatch => { + dispatch({ type: types.api.updateQuery, state }) +}
\ No newline at end of file diff --git a/client/app.css b/client/app.css index 042485d..28df14c 100644 --- a/client/app.css +++ b/client/app.css @@ -68,6 +68,7 @@ label { .results .result img { max-width: 100%; max-height: 300px; + cursor: pointer; } .sha256 { display: inline-block; diff --git a/client/components/query.component.js b/client/components/query.component.js index d28d3a0..22a3655 100644 --- a/client/components/query.component.js +++ b/client/components/query.component.js @@ -3,48 +3,38 @@ import { bindActionCreators } from 'redux' import { connect } from 'react-redux' import * as actions from '../actions' +import * as types from '../types' import UploadImage from './uploadImage.component' -const MATCH_THRESHOLD = 6 -const SIMILAR_THRESHOLD = 20 - class Query extends Component { - state = { - image: null, - blob: null, - url: "", - threshold: SIMILAR_THRESHOLD, - searchType: 'file', - thresholdChanged: false, - saveIfNotFound: false, - } - handleUpload(blob) { - const { image, threshold } = this.state + const { actions, query } = this.props + const { image, threshold } = query if (image) { URL.revokeObjectURL(image) } const url = URL.createObjectURL(blob) - this.setState({ + actions.updateQuery({ image: url, blob, thresholdChanged: false, }) - this.props.actions.upload(blob, threshold) + actions.upload(blob, threshold) } handleURL() { - const { url, threshold, saveIfNotFound } = this.state + const { actions, query } = this.props + const { url, threshold, saveIfNotFound } = query if (!url || url.indexOf('http') !== 0) return let newThreshold = threshold if (saveIfNotFound) { - newThreshold = SIMILAR_THRESHOLD - this.props.actions.match(url, threshold) + newThreshold = types.SIMILAR_THRESHOLD + actions.match(url, threshold) } else { - this.props.actions.submit(url, threshold) + actions.similar(url, threshold) } - this.setState({ + actions.updateQuery({ image: url, blob: null, thresholdChanged: false, @@ -54,7 +44,7 @@ class Query extends Component { } resubmit() { - const { image, blob } = this.state + const { image, blob } = this.props.query if (blob) { this.handleUpload(blob) } else { @@ -63,7 +53,12 @@ class Query extends Component { } render() { - const { url, image, threshold, thresholdChanged, searchType } = this.state + const { actions, query } = this.props + const { + url, image, + threshold, thresholdChanged, + searchType, saveIfNotFound, + } = query const style = {} if (image) { style.backgroundImage = 'url(' + image + ')' @@ -78,7 +73,7 @@ class Query extends Component { name='searchType' value='file' checked={searchType === 'file'} - onChange={e => this.setState({ searchType: e.target.value })} + onChange={e => actions.updateQuery({ searchType: e.target.value })} /> File </label> <label> @@ -87,7 +82,7 @@ class Query extends Component { name='searchType' value='url' checked={searchType === 'url'} - onChange={e => this.setState({ searchType: e.target.value })} + onChange={e => actions.updateQuery({ searchType: e.target.value })} /> URL </label> </div> @@ -101,7 +96,7 @@ class Query extends Component { <input type='text' value={url} - onChange={e => this.setState({ url: e.target.value })} + onChange={e => actions.updateQuery({ url: e.target.value })} onKeyDown={e => e.keyCode === 13 && this.handleURL()} placeholder='https://' /> @@ -115,7 +110,10 @@ class Query extends Component { min={0} max={64} step={1} - onChange={e => this.setState({ threshold: parseInt(e.target.value), thresholdChanged: true }) } + onChange={e => actions.updateQuery({ + threshold: parseInt(e.target.value), + thresholdChanged: true, + }) } /> <input type='number' @@ -123,17 +121,28 @@ class Query extends Component { min={0} max={64} step={1} - onChange={e => this.setState({ threshold: parseInt(e.target.value), thresholdChanged: true }) } + onChange={e => actions.updateQuery({ + threshold: parseInt(e.target.value), + thresholdChanged: true + }) } /> - {thresholdChanged && <button onClick={this.resubmit.bind(this)}>Update</button>} + {thresholdChanged && + <button onClick={this.resubmit.bind(this)}>Update</button> + } </label> {searchType === 'url' && <label> - <span>Save if not found</span> + <span>Save if new?</span> <input type='checkbox' checked={saveIfNotFound} - onChange={e => this.setState({ saveIfNotFound: e.target.checked, threshold: e.target.checked ? MATCH_THRESHOLD : SIMILAR_THRESHOLD })} + onChange={e => actions.updateQuery({ + saveIfNotFound: e.target.checked, + threshold: e.target.checked + ? types.MATCH_THRESHOLD + : types.SIMILAR_THRESHOLD, + })} + /> </label> } {image && @@ -147,7 +156,9 @@ class Query extends Component { } } -const mapStateToProps = state => ({}) +const mapStateToProps = state => ({ + query: state.api.query || {} +}) const mapDispatchToProps = dispatch => ({ actions: bindActionCreators({ ...actions }, dispatch), }) diff --git a/client/components/results.component.js b/client/components/results.component.js index 6332135..ac028d2 100644 --- a/client/components/results.component.js +++ b/client/components/results.component.js @@ -1,7 +1,11 @@ import React, { Component } from 'react' +import { bindActionCreators } from 'redux' import { connect } from 'react-redux' -function Results({ loading, success, error, match, results }) { +import * as actions from '../actions' +import * as types from '../types' + +function Results({ actions, loading, success, error, match, results, added }) { if (!success && !error) { return ( <div className='results'> @@ -17,9 +21,10 @@ function Results({ loading, success, error, match, results }) { } if (error) { + console.log(error) return ( <div className='results'> - <b>Error: {error.replace(/_/g, ' ')}</b> + <b>Error: {typeof error == 'string' ? error.replace(/_/g, ' ') : 'Server error'}</b> </div> ) } @@ -27,7 +32,10 @@ function Results({ loading, success, error, match, results }) { if (!match) { return ( <div className='results'> - No match, image added to database + {added + ? "New image! Added URL to database" + : "No match found" + } </div> ) } @@ -36,7 +44,26 @@ function Results({ loading, success, error, match, results }) { <div className='results'> {results.map(({ phash, score, sha256, url }) => ( <div className='result' key={sha256}> - <div className='img'><img src={url} /></div> + <div className='img'> + <img + src={url} + onClick={() => { + let searchURL = url + if (searchURL.indexOf('http') !== 0) { + searchURL = window.location.origin + searchURL + } + actions.similar(searchURL, types.SIMILAR_THRESHOLD) + actions.updateQuery({ + image: url, + blob: null, + searchType: 'url', + thresholdChanged: false, + saveIfNotFound: false, + threshold: types.SIMILAR_THRESHOLD, + }) + }} + /> + </div> <br /> {score == 0 ? <span className='score'><b>Exact match</b></span> @@ -51,5 +78,8 @@ function Results({ loading, success, error, match, results }) { } const mapStateToProps = state => (state.api.similar || {}) +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators({ ...actions }, dispatch), +}) -export default connect(mapStateToProps)(Results) +export default connect(mapStateToProps, mapDispatchToProps)(Results) diff --git a/client/store.js b/client/store.js index c5f5555..802a47b 100644 --- a/client/store.js +++ b/client/store.js @@ -5,7 +5,18 @@ import thunk from 'redux-thunk' import * as types from './types' const initialState = () => ({ - api: null, + query: { + image: null, + blob: null, + url: "", + threshold: types.SIMILAR_THRESHOLD, + searchType: 'file', + thresholdChanged: false, + saveIfNotFound: false, + }, + api: { + similar: {} + }, }) export default function apiReducer(state = initialState(), action) { @@ -26,7 +37,16 @@ export default function apiReducer(state = initialState(), action) { case types.api.error: return { ...state, - [action.tag]: { error: action.err }, + [action.tag]: { error: action.error }, + } + + case types.api.updateQuery: + return { + ...state, + query: { + ...state.query, + ...action.state, + }, } default: diff --git a/client/types.js b/client/types.js index 6311816..9a66973 100644 --- a/client/types.js +++ b/client/types.js @@ -7,10 +7,13 @@ export const tagAsType = (type, names) => ( ) export const api = tagAsType('api', [ - 'loading', 'loaded', 'error', + 'loading', 'loaded', 'error', 'updateQuery', ]) export const system = tagAsType('system', [ ]) export const init = '@@INIT' + +export const MATCH_THRESHOLD = 6 +export const SIMILAR_THRESHOLD = 20 |
