summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--check/app/server/api.py2
-rw-r--r--client/actions.js10
-rw-r--r--client/app.css1
-rw-r--r--client/components/query.component.js75
-rw-r--r--client/components/results.component.js40
-rw-r--r--client/store.js24
-rw-r--r--client/types.js5
7 files changed, 114 insertions, 43 deletions
diff --git a/check/app/server/api.py b/check/app/server/api.py
index f214696..f007314 100644
--- a/check/app/server/api.py
+++ b/check/app/server/api.py
@@ -81,6 +81,7 @@ def match():
return jsonify({
'success': False,
'match': False,
+ 'added': False,
'error': error,
})
@@ -106,6 +107,7 @@ def match():
return jsonify({
'success': True,
'match': match,
+ 'added': not match,
'results': results,
'timing': time.time() - start,
})
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