diff options
Diffstat (limited to 'client')
| -rw-r--r-- | client/actions.js | 9 | ||||
| -rw-r--r-- | client/app.js | 46 | ||||
| -rw-r--r-- | client/common/activeLink.component.js | 16 | ||||
| -rw-r--r-- | client/common/classifier.component.js | 99 | ||||
| -rw-r--r-- | client/common/common.css | 347 | ||||
| -rw-r--r-- | client/common/detectionBoxes.component.js | 15 | ||||
| -rw-r--r-- | client/common/detectionList.component.js | 16 | ||||
| -rw-r--r-- | client/common/footer.component.js | 10 | ||||
| -rw-r--r-- | client/common/gate.component.js | 21 | ||||
| -rw-r--r-- | client/common/header.component.js | 1 | ||||
| -rw-r--r-- | client/common/index.js | 36 | ||||
| -rw-r--r-- | client/common/keyframe.component.js | 118 | ||||
| -rw-r--r-- | client/common/keyframes.component.js | 95 | ||||
| -rw-r--r-- | client/common/loader.component.js | 10 | ||||
| -rw-r--r-- | client/common/sidebar.component.js | 37 | ||||
| -rw-r--r-- | client/common/table.component.js | 121 | ||||
| -rw-r--r-- | client/common/video.component.js | 47 | ||||
| -rw-r--r-- | client/index.js | 19 | ||||
| -rw-r--r-- | client/metadata/index.js | 25 | ||||
| -rw-r--r-- | client/session.js | 5 | ||||
| -rw-r--r-- | client/store.js | 38 | ||||
| -rw-r--r-- | client/types.js | 21 | ||||
| -rw-r--r-- | client/util.js | 167 |
23 files changed, 0 insertions, 1319 deletions
diff --git a/client/actions.js b/client/actions.js deleted file mode 100644 index ba899f06..00000000 --- a/client/actions.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as search from './search/search.actions' -import * as review from './review/review.actions' -import * as metadata from './metadata/metadata.actions' - -export { - search, - review, - metadata, -} diff --git a/client/app.js b/client/app.js deleted file mode 100644 index 6c008ec6..00000000 --- a/client/app.js +++ /dev/null @@ -1,46 +0,0 @@ -import React, { Component } from 'react' -import { ConnectedRouter } from 'connected-react-router' -import { Route, Switch } from 'react-router' - -import { Header, Sidebar, Footer } from './common' -import * as Metadata from './metadata' -import * as Search from './search' -import * as Review from './review' - -export default class App extends Component { - render() { - return ( - <ConnectedRouter history={this.props.history}> - <div> - <Header /> - <div className='app'> - <Route path="/metadata/" component={Sidebar} /> - <div className='body'> - <Route path="/search/" component={Search.Menu} /> - <Route path="/metadata/:hash/" component={Metadata.Heading} /> - <Switch> - <Route exact path="/metadata/:hash/summary/" component={Metadata.Summary} /> - <Route exact path="/metadata/:hash/mediaRecord/" component={Metadata.MediaRecord} /> - <Route exact path="/metadata/:hash/mediaInfo/" component={Metadata.MediaInfo} /> - <Route exact path="/metadata/:hash/keyframe/:frame/" component={Metadata.KeyframeSingle} /> - <Route exact path="/metadata/:hash/keyframe/" component={Metadata.KeyframeList} /> - <Route exact path="/metadata/:hash/coco/" component={Metadata.Coco} /> - <Route exact path="/metadata/:hash/places365/" component={Metadata.Places365} /> - <Route exact path="/metadata/:hash/sugarcube/" component={Metadata.Sugarcube} /> - <Route exact path="/metadata/:hash/" component={Metadata.Summary} /> - <Route exact path="/metadata/" render={() => <div className='notFound'><h4>NOT FOUND</h4></div>} /> - <Route exact path="/search/" component={Search.Container} /> - <Route exact path="/search/keyframe/:verified/:hash/:frame/" component={Search.Container} /> - <Route exact path="/search/keyframe/:hash/:frame/" component={Search.Container} /> - <Route exact path="/search/browse/:hash/" component={Search.Browse} /> - <Route exact path="/search/random/" component={Search.Random} /> - <Route exact path="/search/review/" component={Review.Saved} /> - </Switch> - </div> - </div> - <Footer /> - </div> - </ConnectedRouter> - ) - } -} diff --git a/client/common/activeLink.component.js b/client/common/activeLink.component.js deleted file mode 100644 index 59f63881..00000000 --- a/client/common/activeLink.component.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react' -import { NavLink } from 'react-router-dom' - -export default function ActiveLink({ - to, - className = 'navlink', - children -}) { - return ( - <span className={className}> - <NavLink to={to}> - {children} - </NavLink> - </span> - ) -} diff --git a/client/common/classifier.component.js b/client/common/classifier.component.js deleted file mode 100644 index af6a4934..00000000 --- a/client/common/classifier.component.js +++ /dev/null @@ -1,99 +0,0 @@ -import React, { Component } from 'react' -import { courtesyS } from '../util' - -import { TableTuples, DetectionList, Keyframe } from '.' - -export default class Classifier extends Component { - render() { - const { - tag, - sha256, - verified, - keyframes = {}, - labels, - summary, - aspectRatio = 1.777, - showAll, - } = this.props - let totalDetections = 0 - const keys = Object - .keys(keyframes) - .map(s => parseInt(s, 10)) - const validKeyframes = keys - .sort((a, b) => a - b) - .map(frame => { - const detections = keyframes[frame] - if (detections.length || showAll) { - totalDetections += detections.length - return { frame, detections } - } - return null - }) - .filter(f => !!f) - const detectionLookup = validKeyframes - .reduce((a, b) => { - b.detections.reduce((aa, { idx }) => { - if (!(idx in aa)) aa[idx] = [labels[idx], 0] - aa[idx][1] += 1 - return aa - }, a) - return a - }, {}) - const detectionCounts = Object.keys(detectionLookup) - .map(idx => detectionLookup[idx]) - .sort((a, b) => b[1] - a[1]) - - if (summary) { - return ( - <div> - <h3>{tag}{' Detections'}</h3> - <TableTuples - list={detectionCounts} - /> - </div> - ) - } - return ( - <div> - <h2>{tag}</h2> - <h3>Detections</h3> - <TableTuples - list={detectionCounts} - /> - <h3>Frames</h3> - <ul className='meta'> - <li> - {'Displaying '}{validKeyframes.length}{' / '}{courtesyS(keys.length, 'frame')} - </li> - <li> - {courtesyS(totalDetections, 'detection')}{' found'} - </li> - </ul> - <div className='thumbnails'> - {validKeyframes.map(({ frame, detections }) => ( - <Keyframe - key={frame} - sha256={sha256} - frame={frame} - verified={verified} - size='th' - showFrame - showTimestamp - aspectRatio={aspectRatio} - detectionList={[ - { labels, detections } - ]} - > - <DetectionList - labels={labels} - detections={detections} - width={160} - height={90} - /> - </Keyframe> - ))} - </div> - </div> - ) - } -} diff --git a/client/common/common.css b/client/common/common.css deleted file mode 100644 index 4b939df0..00000000 --- a/client/common/common.css +++ /dev/null @@ -1,347 +0,0 @@ -/* css boilerplate */ - -* { box-sizing: border-box; } -html,body { - margin: 0; padding: 0; - width: 100%; height: 100%; -} -body { - font-family: Helvetica, sans-serif; - font-weight: 300; -} - -h1 { - -} -h2 { - font-weight: normal; - margin: 10px 0; - padding: 3px; - font-size: 24px; -} -h3 { - font-weight: normal; - margin: 10px 0 0 0; - padding: 3px; - font-size: 18px; -} -h4 { - font-weight: 300; - font-size: 12px; - letter-spacing: 2px; - color: #888; - text-transform: uppercase; - margin: 5px 10px; - margin-top: 20px; -} -h4:first-child { - margin-top: 10px; -} - -.app { - width: 100%; - height: 100%; - display: flex; - flex-direction: row; - align-items: flex-start; - justify-content: flex-start; -} - -/* header stuff */ - -header { - width: 100%; - background: #11f; - color: white; - align-items: stretch; - display: flex; - flex-wrap: wrap; - justify-content: space-between; - z-index: 3; -} -header > section { - justify-content: flex-start; - align-items: center; - display: flex; - flex: 1 0; - font-weight: bold; -} -header > section:last-of-type { - justify-content: flex-end; -} - -/* sidebar / body columns */ - -.sidebar { - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - height: 100%; - float: left; - width: 200px; - flex: 0 0 200px; - padding: 10px; - margin-right: 10px; -} -.sidebar a { - display: block; - padding: 10px 10px; - text-decoration: none; - color: #444; -} -.sidebar a.active { - font-weight: bold; - color: #222; -} -.body { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: flex-start; - flex-grow: 1; -} -.body > div { - padding-bottom: 40px; -} - -/* buttons / forms */ - -.btn:focus, .btn:hover { - background: #f1f1fc; - color: #4b48d6 !important; - text-decoration: none; -} -.btn { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: #fff; - border: .05rem solid; - border-radius: 2px; - margin-right: 5px; - color: #11f; - cursor: pointer; - display: inline-block; - font-size: .8rem; - height: 1.8rem; - line-height: 1rem; - outline: none; - padding: .35rem .4rem; - text-align: center; - text-decoration: none; - -webkit-transition: all .2s ease; - -o-transition: all .2s ease; - transition: all .2s ease; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - vertical-align: middle; - white-space: nowrap; -} -.btn.reset, -.btn.panic { - color: #b00; -} -.btn.btn-primary { - background: #11f; - border-color: #11f; - color: white; -} -.btn[disabled] { - color: #bbb !important; - border-color: #bbb !important; - background: white !important; - cursor: default; -} -.btn.btn-primary:focus, -.btn.btn-primary:hover { - background: #0808ee; - color: white !important; -} -.row .btn { - margin: 0 5px 0 0; -} -input[type=text] { - border: 1px solid #888; - padding: 4px; - font-size: 15px; -} - - -/* tables on metadata pages */ - -table { - border: 0; - margin: 0; - padding: 0; - border-spacing: 0; -} -.tableObject td, -.tableObject th { - padding: 3px; - vertical-align: top; -} -.tableObject hr { - width: 100%; - color: transparent; - border: 0; - border-bottom: 1px solid #bbb; - align: left; - margin: 3px 0; - padding: 0; -} -.tableObject th, -.tableTuples th { - min-width: 145px; - text-align: left; - text-transform: capitalize; - padding: 3px; - padding-right: 10px; - font-weight: 300; - color: #333; -} -.tableTuples td { - text-align: right; - padding: 3px; -} -.tableObject td { - font-weight: normal; - color: #000; -} -.tableObject .tableObject { - border: 1px solid #ddd; -} -.tableArray { - border: 1px solid #ddd; - border-spacing: 0; -} -.tableArray td { - border-bottom: 1px solid #ddd; -} -.gray { - font-size: 12px; - color: #888; - display: block; -} -.sha256.heading { - margin: 20px 0 0px; -} -.gray span { - padding-right: 5px; -} -.gray { - margin-bottom: 10px; -} -.gray a { - color: #666; -} - -.verified { - color: #080; - font-weight: bold; -} -.unverified { - color: #f00; - font-weight: 300; -} - -.loading, .error { - font-weight: normal; - margin: 10px 0; - padding: 3px; - font-size: 24px; -} - -.title { - text-transform: capitalize; -} -.rect { - position: absolute; -} -.rect { border: 1px solid rgba(0,0,255); background-color: rgba(0,0,255,0.1); } - -/* videos / video preloader */ - -video { - max-width: 640px; - margin: 10px 0; -} -.video { - margin: 0 0 10px 0; -} -.video .bg { - cursor: pointer; - position: relative; - background-size: cover; -} -.video .play { - position: absolute; - top: 50%; - left: 50%; - transform: translate3d(-50%, -50%, 0); - width: 20%; - height: 20%; - background-image: url(/search/static/img/play.png); - background-position: center center; - background-size: contain; - background-repeat: no-repeat; -} -.desktop .video .play:hover { - -webkit-filter: invert(60%) sepia(100%) saturate(500%) hue-rotate(160deg); -} - -/* spectre.css loader */ - -.loaderWrapper { - display: inline-block; - position: relative; - width: .8rem; - height: .8rem; - padding: 10px; -} -.loader { - color: transparent !important; - min-height: .8rem; - pointer-events: none; - position: relative; -} - -.loader::after { - animation: loader 500ms infinite linear; - border: .1rem solid #5755d9; - border-radius: 50%; - border-right-color: transparent; - border-top-color: transparent; - content: ""; - display: block; - height: .8rem; - left: 50%; - margin-left: -.4rem; - margin-top: -.4rem; - position: absolute; - top: 50%; - width: .8rem; - z-index: 1; -} - -.loader.loader-lg { - min-height: 2rem; -} - -.loader.loader-lg::after { - height: 1.6rem; - margin-left: -.8rem; - margin-top: -.8rem; - width: 1.6rem; -} - -@keyframes loader { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -}
\ No newline at end of file diff --git a/client/common/detectionBoxes.component.js b/client/common/detectionBoxes.component.js deleted file mode 100644 index c4872ea8..00000000 --- a/client/common/detectionBoxes.component.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' - -import { px } from '../util' - -export default function DetectionBoxes({ detections, width, height }) { - return detections.map(({ rect }, i) => ( - rect && - <div className='rect' key={i} style={{ - left: px(rect[0], width), - top: px(rect[1], height), - width: px(rect[2] - rect[0], width), - height: px(rect[3] - rect[1], height), - }} /> - )) -} diff --git a/client/common/detectionList.component.js b/client/common/detectionList.component.js deleted file mode 100644 index 416e66d8..00000000 --- a/client/common/detectionList.component.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react' - -export default function DetectionList({ detections, labels, tag, showEmpty }) { - return ( - <span className='detectionList'> - {tag && <h3>{tag}</h3>} - {!detections.length && showEmpty && <label><small>No detections</small></label>} - {detections.map(({ idx, score, rect }, i) => ( - <label key={i}> - <small className='title'>{(labels[idx] || 'Unknown').replace(/_/, ' ')}</small> - <small>{score.toFixed(2)}</small> - </label> - ))} - </span> - ) -} diff --git a/client/common/footer.component.js b/client/common/footer.component.js deleted file mode 100644 index 7c82b44b..00000000 --- a/client/common/footer.component.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import { Link } from 'react-router-dom' -import { connect } from 'react-redux' - -export default function Footer(props) { - return ( - <footer> - </footer> - ); -} diff --git a/client/common/gate.component.js b/client/common/gate.component.js deleted file mode 100644 index 9bf9287b..00000000 --- a/client/common/gate.component.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' -import { connect } from 'react-redux' - -function Gate(props) { - const { app, tag, View } = props - const data = app[tag] - if (!data) return null - if (data === 'loading') { - return <div className='tableObject loading'>{tag}{': Loading'}</div> - } - if (data.err) { - return <div className='tableObject error'>{tag}{' Error: '}{data.err}</div> - } - return <View data={data} {...props} /> -} - -const mapStateToProps = state => ({ - app: state.metadata -}) - -export default connect(mapStateToProps)(Gate) diff --git a/client/common/header.component.js b/client/common/header.component.js deleted file mode 100644 index 84fe306f..00000000 --- a/client/common/header.component.js +++ /dev/null @@ -1 +0,0 @@ -/* imported from main vcat application */ diff --git a/client/common/index.js b/client/common/index.js deleted file mode 100644 index ad9fe5e1..00000000 --- a/client/common/index.js +++ /dev/null @@ -1,36 +0,0 @@ -import Header from 'vcat-header' - -import ActiveLink from './activeLink.component' -import Classifier from './classifier.component' -import DetectionBoxes from './detectionBoxes.component' -import DetectionList from './detectionList.component' -// import Header from './header.component' -import Footer from './footer.component' -import Loader from './loader.component' -import Sidebar from './sidebar.component' -import Gate from './gate.component' -import Keyframe from './keyframe.component' -import Keyframes from './keyframes.component' -import Video from './video.component' -import { TableObject, TableArray, TableTuples, TableRow, TableCell } from './table.component' -import './common.css' - -export { - Header, - Footer, - Sidebar, - Loader, - Gate, - TableObject, - TableArray, - TableTuples, - TableRow, - TableCell, - ActiveLink, - Classifier, - DetectionList, - DetectionBoxes, - Keyframe, - Keyframes, - Video, -} diff --git a/client/common/keyframe.component.js b/client/common/keyframe.component.js deleted file mode 100644 index c77db3ac..00000000 --- a/client/common/keyframe.component.js +++ /dev/null @@ -1,118 +0,0 @@ -import React from 'react' -import { Link } from 'react-router-dom' -import { imageUrl, timestamp, keyframeUri, widths, verify } from '../util' -import { DetectionBoxes } from '.' - -import * as searchActions from '../search/search.actions' - -export default function Keyframe({ - verified, - sha256, - frame, - score, - isSaved, - fps = 25, - size = 'th', - className, - showHash, - showFrame, - showTimestamp, - showScore, - showSearchButton, - showSaveButton, - to, - children, - detectionList = [], - aspectRatio = 1.777, - onClick, - reviewActions, -}) { - if (!sha256) return null - const width = widths[size] - const height = Math.round(width / aspectRatio) - return ( - <div className={(className || 'keyframe') + (isSaved ? ' isSaved' : '')}> - <div className="thumbnail"> - <PossiblyExternalLink to={to || keyframeUri(sha256, frame)} onClick={onClick}> - <img - alt={'Frame #' + frame} - src={imageUrl(verified, sha256, frame, size)} - width={width} - height={height} - onClick={onClick} - /> - {detectionList.map(({ labels, detections }, i) => ( - <DetectionBoxes - key={i} - labels={labels} - detections={detections} - width={width} - height={height} - /> - ))} - </PossiblyExternalLink> - {(reviewActions && (showSearchButton || showSaveButton)) && - <label className='searchButtons'> - {showSearchButton && - <Link - to={searchActions.publicUrl.searchByVerifiedFrame(verified, sha256, frame)} - className='btn' - > - Search - </Link> - } - {showSaveButton && (isSaved - ? <button - onClick={() => reviewActions.unsave({ hash: sha256, frame, verified })} - className={'btn btn-primary saved'} - > - {'Saved'} - </button> - : <button - onClick={() => reviewActions.save({ hash: sha256, frame, verified })} - className={'btn btn save'} - > - {'Save'} - </button> - )} - </label> - } - </div> - {(showHash || showFrame || showTimestamp || showScore) && - <label> - {showHash && - <small> - <Link to={searchActions.publicUrl.browse(sha256)}> - <span - title={sha256} - className={'sha256 ' + verify(verified)} - > - {'▶ '} - {sha256.substr(0, 6)} - </span> - </Link> - </small> - } - {showFrame && - <small> - <span>{'Frame #'}{frame}</span> - </small> - } - {showTimestamp && <small>{timestamp(frame, fps)}</small>} - {showScore && !!score && <small>{score}</small>} - </label> - } - {children} - </div> - ) -} - -const PossiblyExternalLink = props => { - if (props.onClick) { - return props.children - } - if (props.to.match(/^http/)) { - return <a href={props.to} target='_blank' rel='noopener noreferrer'>{props.children}</a> - } - return <Link {...props} /> -} diff --git a/client/common/keyframes.component.js b/client/common/keyframes.component.js deleted file mode 100644 index 62eda45e..00000000 --- a/client/common/keyframes.component.js +++ /dev/null @@ -1,95 +0,0 @@ -import React from 'react' -import { Link } from 'react-router-dom' -import { bindActionCreators } from 'redux' -import { connect } from 'react-redux' - -import { Keyframe } from '.' -import * as reviewActions from '../review/review.actions' -import * as searchActions from '../search/search.actions' - -function Keyframes(props) { - // console.log(props) - let { - frames, - groupByHash, - } = props - let minDistance = 0 - if (frames && frames.length) { - minDistance = frames[0].distance || 0 - } - if (!groupByHash) { - return ( - <KeyframeList - minDistance={minDistance} - {...props} - /> - ) - } - const frameGroups = frames.reduce((a, b) => { - if (a[b.hash]) { - a[b.hash].push(b) - } else { - a[b.hash] = [b] - } - return a - }, {}) - return Object.keys(frameGroups) - .map(hash => [frameGroups[hash].length, hash]) - .sort((a, b) => b[0] - a[0]) - .map(([count, hash]) => ( - <KeyframeList - {...props} - count={count} - key={hash} - minDistance={minDistance} - frames={frameGroups[hash]} - label={hash} - /> - )) -} - -function KeyframeList(props) { - let { - saved = {}, - frames, - options, - review, - search, - minDistance, - label, - count, - ...frameProps - } = props - if (!frames) return null - return ( - <div className={label ? 'keyframes keyframeGroup' : 'keyframes'}> - {label && <h4><Link to={searchActions.publicUrl.browse(label)}>{label}</Link> ({count})</h4>} - {frames.map(({ hash, frame, verified, distance }) => ( - <Keyframe - key={hash + '_' + frame} - sha256={hash} - frame={frame} - score={100 - Math.round(distance - minDistance) + '%'} - verified={verified} - isSaved={!!saved[hash] && !!saved[hash].frames && !!saved[hash].frames[parseInt(frame, 10)]} - size={options.thumbnailSize} - onClick={() => review.toggleSaved({ verified, hash, frame })} - reviewActions={review} - {...frameProps} - /> - ))} - </div> - ) -} - -const mapStateToProps = state => ({ - saved: state.review.saved, - options: state.search.options, -}) - -const mapDispatchToProps = dispatch => ({ - review: bindActionCreators({ ...reviewActions }, dispatch), - search: bindActionCreators({ ...searchActions }, dispatch), -}) - -export default connect(mapStateToProps, mapDispatchToProps)(Keyframes) diff --git a/client/common/loader.component.js b/client/common/loader.component.js deleted file mode 100644 index 6795424b..00000000 --- a/client/common/loader.component.js +++ /dev/null @@ -1,10 +0,0 @@ -import React, { Component } from 'react' - -export default function Loader() { - return ( - <div className='loaderWrapper'> - <div className='loader'> - </div> - </div> - ) -}
\ No newline at end of file diff --git a/client/common/sidebar.component.js b/client/common/sidebar.component.js deleted file mode 100644 index 487f3289..00000000 --- a/client/common/sidebar.component.js +++ /dev/null @@ -1,37 +0,0 @@ -import React, { Component } from 'react' -import { NavLink } from 'react-router-dom' -import { connect } from 'react-redux' - -class Sidebar extends Component { - render() { - const { hash } = this.props - if (!hash) { - return ( - <div className="sidebar"> - </div> - ) - } - return ( - <div className="sidebar"> - <h4>Media</h4> - <NavLink to={'/metadata/' + hash + '/summary/'}>Summary</NavLink> - <NavLink to={'/metadata/' + hash + '/mediaRecord/'}>Media Record</NavLink> - <NavLink to={'/metadata/' + hash + '/mediaInfo/'}>Media Info</NavLink> - <NavLink to={'/metadata/' + hash + '/sugarcube/'}>Sugarcube</NavLink> - - <h4>Keyframes</h4> - <NavLink to={'/metadata/' + hash + '/keyframe/'}>Keyframe</NavLink> - - <h4>Detectors</h4> - <NavLink to={'/metadata/' + hash + '/places365/'}>Places 365</NavLink> - <NavLink to={'/metadata/' + hash + '/coco/'}>Coco</NavLink> - </div> - ) - } -} - -const mapStateToProps = state => ({ - hash: state.metadata.hash, -}) - -export default connect(mapStateToProps)(Sidebar) diff --git a/client/common/table.component.js b/client/common/table.component.js deleted file mode 100644 index 76a1d57c..00000000 --- a/client/common/table.component.js +++ /dev/null @@ -1,121 +0,0 @@ -import React from 'react' - -import { formatName } from '../util' - -const __HR__ = '__HR__' - -export function TableObject({ tag, object, order, summary }) { - if (!object) return null - if (object === 'loading') { - return <div className='tableObject loading'>{tag}{': Loading'}</div> - } - if (object.err) { - return <div className='tableObject error'>{tag}{' Error: '}{object.err}</div> - } - let objects = Object.keys(object) - if (order) { - const grouped = objects.reduce((a, b) => { - const index = order.indexOf(b) - if (index !== -1) { - a.order.push([index, b]) - } else { - a.alpha.push(b) - } - return a - }, { order: [], alpha: [] }) - objects = grouped.order - .sort((a, b) => a[0] - b[0]) - .map(([i, s]) => s) - if (!summary) { - objects = objects - // .concat([__HR__]) - .concat(grouped.alpha.sort()) - } - } else { - objects = objects.sort() - } - return ( - <div> - {tag && <h3>{tag}</h3>} - <table className={'tableObject ' + tag}> - <tbody> - {objects.map((key, i) => ( - <TableRow key={key + '_' + i} name={key} value={object[key]} /> - ))} - </tbody> - </table> - </div> - ) -} - -export function TableArray({ tag, list }) { - if (!list) return null - return ( - <div> - {tag && <h3>{tag}</h3>} - <table className={'tableArray ' + tag}> - <tbody> - {list.map((value, i) => ( - <tr key={tag + '_' + i}> - <TableCell value={value} /> - </tr> - ))} - </tbody> - </table> - </div> - ) -} - -export function TableTuples({ tag, list }) { - if (!list) return null - return ( - <div> - {tag && <h3>{tag}</h3>} - <table className={'tableTuples ' + tag}> - <tbody> - {list.map(([key, ...values], i) => ( - <tr key={tag + '_' + i}> - <th>{formatName(key)}</th> - {values.map((value, j) => ( - <TableCell key={i + '_' + j} value={value} /> - ))} - </tr> - ))} - </tbody> - </table> - </div> - ) -} - -export function TableRow({ name, value }) { - if (name === __HR__) { - return ( - <tr> - <th className='tr'> - <hr /> - </th> - </tr> - ) - } - return ( - <tr> - <th>{formatName(name)}</th> - <TableCell name={name} value={value} /> - </tr> - ) -} - -export function TableCell({ value }) { - if (value && typeof value === 'object') { - if (value._raw) { - value = value.value - } else if (value.length) { - value = <TableArray nested tag={''} list={value} /> - } else { - value = <TableObject nested tag={''} object={value} /> - } - } - return ( - <td>{value}</td> - ) -} diff --git a/client/common/video.component.js b/client/common/video.component.js deleted file mode 100644 index e5525bf6..00000000 --- a/client/common/video.component.js +++ /dev/null @@ -1,47 +0,0 @@ -import React, { Component } from 'react' -import { connect } from 'react-redux' -import { imageUrl, widths } from '../util' - -import { Gate } from '.' - -class Video extends Component { - state = { - playing: false, - } - - render() { - const { app, data, size } = this.props - const { playing } = this.state - const { sugarcube } = data.metadata - const url = sugarcube.fp.replace('/var/www/files/', 'https://cube.syrianarchive.org/') - const { sha256, verified } = app.mediainfo - const { video } = app.mediainfo.metadata.mediainfo - const keyframe = app.keyframe.metadata.keyframe.basic[0] - return ( - <div className='video'> - {playing - ? <video src={url} autoPlay controls muted /> - : <div - className='bg' - style={{ - width: widths[size || 'sm'], - height: widths[size || 'sm'] / video.aspect_ratio, - backgroundImage: 'url(' + imageUrl(verified, sha256, keyframe, size) + ')', - }} - onClick={() => this.setState({ playing: true })} - > - <div className='play'></div> - </div> - } - </div> - ) - } -} - -const mapStateToProps = () => ({ - tag: 'sugarcube', -}) - -export default connect(mapStateToProps)(props => ( - <Gate View={Video} {...props} /> -)) diff --git a/client/index.js b/client/index.js deleted file mode 100644 index eddc5fb2..00000000 --- a/client/index.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import { AppContainer } from 'react-hot-loader' -import { Provider } from 'react-redux' - -import App from './app' - -import { store, history } from './store' - -const container = document.createElement('div') -document.body.appendChild(container) - -ReactDOM.render( - <AppContainer> - <Provider store={store}> - <App history={history} /> - </Provider> - </AppContainer>, container -) diff --git a/client/metadata/index.js b/client/metadata/index.js deleted file mode 100644 index 0eef814e..00000000 --- a/client/metadata/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import Heading from './heading.component' -import MediaInfo from './mediaInfo.component' -import MediaRecord from './mediaRecord.component' -import Summary from './summary.component' -import KeyframeList from './keyframeList.component' -import KeyframeSingle from './keyframeSingle.component' -import KeyframeStatus from './keyframeStatus.component' -import Coco from './coco.component' -import Places365 from './places365.component' -import Sugarcube from './sugarcube.component' - -import './metadata.css' - -export { - Heading, - MediaRecord, - MediaInfo, - Summary, - KeyframeList, - KeyframeSingle, - KeyframeStatus, - Coco, - Places365, - Sugarcube, -} diff --git a/client/session.js b/client/session.js deleted file mode 100644 index 5bfae7eb..00000000 --- a/client/session.js +++ /dev/null @@ -1,5 +0,0 @@ -import Storage from 'store2' - -const session = Storage.namespace('vcat.search') - -export default session diff --git a/client/store.js b/client/store.js deleted file mode 100644 index 043af351..00000000 --- a/client/store.js +++ /dev/null @@ -1,38 +0,0 @@ -import { applyMiddleware, compose, combineReducers, createStore } from 'redux' -import { connectRouter, routerMiddleware } from 'connected-react-router' -import { createBrowserHistory } from 'history' -import thunk from 'redux-thunk' -import { login } from './util' - -import metadataReducer from './metadata/metadata.reducer' -import searchReducer from './search/search.reducer' -import reviewReducer from './review/review.reducer' - -const rootReducer = combineReducers({ - auth: (state = login()) => state, - metadata: metadataReducer, - search: searchReducer, - review: reviewReducer, -}) - -function configureStore(initialState = {}, history) { - const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose - - const store = createStore( - connectRouter(history)(rootReducer), // new root reducer with router state - initialState, - composeEnhancers( - applyMiddleware( - thunk, - routerMiddleware(history) - ), - ), - ) - - return store -} - -const history = createBrowserHistory() -const store = configureStore({}, history) - -export { store, history } diff --git a/client/types.js b/client/types.js deleted file mode 100644 index e3c64691..00000000 --- a/client/types.js +++ /dev/null @@ -1,21 +0,0 @@ -export const asType = (type, name) => [type, name].join('_').toUpperCase() -export const tagAsType = (type, names) => ( - names.reduce((tags, name) => { - tags[name] = asType(type, name) - return tags - }, {}) -) - -export const metadata = tagAsType('metadata', [ - 'loading', 'loaded', 'loaded_many', 'error', 'set_hash' -]) - -export const search = tagAsType('search', [ - 'loading', 'loaded', 'error', 'panic', 'update_options', -]) - -export const review = tagAsType('review', [ - 'loading', 'loaded', 'error', 'save', 'unsave', 'refresh', 'clear', 'dedupe', 'create', 'set_count' -]) - -export const init = '@@INIT' diff --git a/client/util.js b/client/util.js deleted file mode 100644 index ad303c64..00000000 --- a/client/util.js +++ /dev/null @@ -1,167 +0,0 @@ -/* Mobile check */ - -export const isiPhone = !!((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i))) -export const isiPad = !!(navigator.userAgent.match(/iPad/i)) -export const isAndroid = !!(navigator.userAgent.match(/Android/i)) -export const isMobile = isiPhone || isiPad || isAndroid -export const isDesktop = !isMobile - -const htmlClassList = document.body.parentNode.classList -htmlClassList.add(isDesktop ? 'desktop' : 'mobile') - -/* Default image dimensions */ - -export const widths = { - th: 160, - sm: 320, - md: 640, - lg: 1280, -} - -/* Formatting functions */ - -const acronyms = 'id url cc sa fp md5 sha256'.split(' ').map(s => '_' + s) -const acronymsUpperCase = acronyms.map(s => s.toUpperCase()) - -export const formatName = s => { - acronyms.forEach((acronym, i) => s = s.replace(acronym, acronymsUpperCase[i])) - return s.replace(/_/g, ' ') -} - -// Use to pad frame numbers with zeroes -export const pad = (n, m) => { - let s = String(n || 0) - while (s.length < m) { - s = '0' + s - } - return s -} - -// Verified is 0/1 when retrieved from SQL, but 'verified' or 'unverified' when retrieved elsewhere -export const isVerified = verified => verified === 1 || verified === '1' || verified === 'verified' -export const verify = verified => isVerified(verified) ? 'verified' : 'unverified' - -export const courtesyS = (n, s) => n + ' ' + (n === 1 ? s : s + 's') - -export const padSeconds = n => n < 10 ? '0' + n : n - -export const timestamp = (n = 0, fps = 25) => { - n /= fps - let s = padSeconds(Math.round(n) % 60) - n = Math.floor(n / 60) - if (n > 60) { - return Math.floor(n / 60) + ':' + padSeconds(n % 60) + ':' + s - } - return (n % 60) + ':' + s -} - -export const percent = n => (n * 100).toFixed(1) + '%' - -export const px = (n, w) => Math.round(n * w) + 'px' - -export const clamp = (n, a, b) => n < a ? a : n < b ? n : b - -/* URLs */ - -export const hashPath = sha256 => { - if (!sha256 || sha256.length < 9) { - throw new Error('Invalid sha256') - } - return [ - sha256.slice(0, 3), - sha256.slice(3, 6), - sha256.slice(6, 9), - sha256, - ].join('/') -} - -export const imageUrl = (verified, sha256, frame, size = 'th') => [ - 'https://' + process.env.S3_HOST + '/v1/media/keyframes', - isVerified(verified) ? null : 'unverified', - hashPath(sha256), - pad(frame, 6), - size, - 'index.jpg' -].filter(s => !!s).join('/') - -export const metadataUri = (sha256, tag) => '/metadata/' + sha256 + '/' + tag + '/' -export const keyframeUri = (sha256, frame) => '/metadata/' + sha256 + '/keyframe/' + pad(frame, 6) + '/' - -export const preloadImage = opt => { - let { verified, hash, frame, url } = opt - if (hash && frame) { - url = imageUrl(verified, hash, frame, 'md') - } - const image = new Image() - let loaded = false - image.onload = () => { - if (loaded) return - loaded = true - image.onload = null - } - // console.log(img.src) - image.crossOrigin = 'anonymous' - image.src = url - if (image.complete) { - image.onload() - } -} - -/* AJAX */ - -let cachedAuth = null -let token = '' -let username = '' - -export const post = (uri, data, credentials) => { - login() - let headers - if (data instanceof FormData) { - headers = { - Accept: 'application/json, application/xml, text/play, text/html, *.*', - } - } else { - headers = { - Accept: 'application/json, application/xml, text/play, text/html, *.*', - 'Content-Type': 'application/json; charset=utf-8', - } - data = JSON.stringify(data) - } - let opt = { - method: 'POST', - body: data, - headers, - credentials: 'include', - } - if (credentials) { - headers.Authorization = 'Token ' + token - } - // console.log(headers) - // headers['X-CSRFToken'] = csrftoken - return fetch(uri, opt).then(res => res.json()) -} - -// api queries -export const login = () => { - if (cachedAuth) return cachedAuth - const isLocal = (window.location.hostname === '0.0.0.0' || window.location.hostname === '127.0.0.1') - try { - const auth = JSON.parse(JSON.parse(localStorage.getItem('persist:root')).auth) - // console.log('auth', auth) - token = auth.token - username = auth.user.username - if (token) { - console.log('logged in', username) - } - cachedAuth = auth - if (!token && !isLocal) { - window.location.href = '/' - } - return auth - } catch (e) { - if (!isLocal) { - window.location.href = '/' - } - return {} - } -} |
