mirror of
https://github.com/dolthub/dolt.git
synced 2026-05-23 02:40:49 -05:00
Delete tagshow
This commit is contained in:
@@ -48,4 +48,3 @@ cache:
|
||||
- clients/crunchbase/ui/node_modules
|
||||
- clients/pitchmap/ui/node_modules
|
||||
- clients/splore/node_modules
|
||||
- clients/tagshow/node_modules
|
||||
|
||||
@@ -43,7 +43,7 @@ go build
|
||||
# What next?
|
||||
|
||||
* Learn the core tools: [`server`](clients/server), [`splore`](clients/splore), [`shove`](clients/shove), [`csv_importer`](clients/csv_importer), [`json_importer`](clients/json_importer), [`xml_importer`](clients/xml_importer)
|
||||
* Run sample apps: [`sfcrime`](clients/sfcrime), [`tagshow`](clients/tagshow) (photo viewer)
|
||||
* Run sample apps: [`sfcrime`](clients/sfcrime)
|
||||
* NomDL reference (TODO)
|
||||
* Go SDK reference (TODO)
|
||||
* JavaScript SDK reference (TODO)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
.babelrc
|
||||
.eslintrc
|
||||
.flowconfig
|
||||
node_modules
|
||||
tagshow.js
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os, subprocess, sys
|
||||
|
||||
SRC = ['babel-regenerator-runtime', 'src/main.js']
|
||||
OUT = 'tagshow.js'
|
||||
|
||||
def main():
|
||||
env = os.environ
|
||||
env['NODE_ENV'] = sys.argv[1]
|
||||
env['BABEL_ENV'] = sys.argv[1]
|
||||
if 'NOMS_SERVER' not in env:
|
||||
env['NOMS_SERVER'] = 'http://localhost:8000'
|
||||
subprocess.check_call(['node_modules/.bin/webpack'] + SRC + [OUT], env=env, shell=False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,46 +0,0 @@
|
||||
# Tagshow
|
||||
|
||||
This is a slideshow that displays photos that have a particular tag.
|
||||
|
||||
## Setup
|
||||
|
||||
* Tag some of your photos at either Flickr and/or Picasa (aka Google Photos)
|
||||
* Import photos from Flickr and/or Picasa
|
||||
* To import photos from Flickr:
|
||||
* Apply for a [Flickr API key](https://www.flickr.com/services/apps/create/apply)
|
||||
* `cd $GOPATH/src/github.com/attic-labs/noms/clients/flickr`
|
||||
* `go build`
|
||||
* `./flickr -api-key="<apikey>" -api-key-secret="<apikeysecret>" -ldb="/tmp/tagshowdemo" -ds="flickr"`
|
||||
* To import photos from Picasa:
|
||||
* `cd $GOPATH/src/github.com/attic-labs/noms/clients/picasa`
|
||||
* `go build`
|
||||
* `./picasa`
|
||||
* Follow the instructions printed out to create Google API credentials
|
||||
* `./picasa -api-key="<apikey>" -api-key-secret="<apikeysecret>" -ldb="/tmp/tagshowdemo" -ds="picasa"`
|
||||
* Index photos by tag
|
||||
* `cd $GOPATH/src/github.com/attic-labs/noms/clients/tagdex`
|
||||
* `go build`
|
||||
* `./tagdex -ldb="/tmp/tagshowdemo" -in="<input dataset to read (flickr or picasa)>" -out="tagdex"`
|
||||
|
||||
## Run
|
||||
|
||||
```
|
||||
cd $GOPATH/src/github.com/attic-labs/noms/clients/server
|
||||
go build
|
||||
./server -ldb="/tmp/tagshowdemo"
|
||||
|
||||
cd ../tagshow
|
||||
./build.py
|
||||
./node_modules/.bin/http-server
|
||||
```
|
||||
|
||||
Then, navigate to [http://localhost:8080](http://localhost:8080).
|
||||
|
||||
## Develop
|
||||
|
||||
```
|
||||
./build.py # only necessary first time
|
||||
NOMS_SERVER="http://localhost:8000" npm run start
|
||||
```
|
||||
|
||||
This will start watchify which is continually building a shippable (but non minified) build
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os, os.path, subprocess, sys
|
||||
|
||||
sys.path.append(os.path.abspath('../../tools'))
|
||||
|
||||
import noms.symlink as symlink
|
||||
|
||||
def main():
|
||||
symlink.Force('../../js/.babelrc', os.path.abspath('.babelrc'))
|
||||
symlink.Force('../../js/.eslintrc', os.path.abspath('.eslintrc'))
|
||||
symlink.Force('../../js/.flowconfig', os.path.abspath('.flowconfig'))
|
||||
|
||||
subprocess.check_call(['npm', 'install'], shell=False)
|
||||
subprocess.check_call(['npm', 'run', 'build'], env=os.environ, shell=False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,20 +0,0 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>tagshow</title>
|
||||
<script src="tagshow.js"></script>
|
||||
<style>
|
||||
html, body, #root {
|
||||
background: black;
|
||||
color: white;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"name": "noms-tagshow",
|
||||
"dependencies": {
|
||||
"noms": "file:../../js",
|
||||
"noms-webpack-config": "file:../../js/webpack-config",
|
||||
"query-string": "^2.4.0",
|
||||
"react": "^0.14.1",
|
||||
"react-dom": "^0.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.3.15",
|
||||
"babel-core": "^6.3.15",
|
||||
"babel-eslint": "^4.1.5",
|
||||
"babel-plugin-syntax-async-functions": "^6.3.13",
|
||||
"babel-plugin-syntax-flow": "^6.3.13",
|
||||
"babel-plugin-transform-async-to-generator": "^6.3.13",
|
||||
"babel-plugin-transform-class-properties": "^6.3.13",
|
||||
"babel-plugin-transform-es2015-destructuring": "^6.3.15",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.3.16",
|
||||
"babel-plugin-transform-es2015-parameters": "^6.3.13",
|
||||
"babel-polyfill": "^6.3.14",
|
||||
"babel-preset-es2015": "^6.3.13",
|
||||
"babel-preset-react": "^6.3.13",
|
||||
"chai": "^3.2.0",
|
||||
"eslint": "^1.9.0",
|
||||
"eslint-plugin-react": "^3.8.0",
|
||||
"flow-bin": "^0.21.0",
|
||||
"http-server": "^0.8.5",
|
||||
"mocha": "^2.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "python .npm_build_helper.py development",
|
||||
"build": "python .npm_build_helper.py production",
|
||||
"TODO:": "re-enable tests (e.g. eq_test.js)",
|
||||
"test": "eslint src/ && flow src/"
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import eq from './eq.js';
|
||||
import React from 'react';
|
||||
import type {ChunkStore} from 'noms';
|
||||
import {invariant, NomsMap, readValue, Ref} from 'noms';
|
||||
|
||||
type DefaultProps = {
|
||||
selected: string
|
||||
};
|
||||
|
||||
type Props = {
|
||||
store: ChunkStore,
|
||||
onChange: (value: string) => void,
|
||||
selected: string
|
||||
};
|
||||
|
||||
type State = {
|
||||
datasets: Set<string>
|
||||
};
|
||||
|
||||
export default class DatasetPicker extends React.Component<DefaultProps, Props, State> {
|
||||
state: State;
|
||||
static defaultProps: DefaultProps;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
datasets: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
handleSelectChange(e: Event) {
|
||||
invariant(e.target instanceof HTMLSelectElement);
|
||||
this.props.onChange(e.target.value);
|
||||
}
|
||||
|
||||
async _updateDatasets(props: Props) : Promise<void> {
|
||||
const store = props.store;
|
||||
const rootRef = await store.getRoot();
|
||||
const datasets: NomsMap<string, Ref> = await readValue(rootRef, store);
|
||||
invariant(datasets);
|
||||
const s = new Set();
|
||||
await datasets.forEach((v, k) => {
|
||||
s.add(k);
|
||||
});
|
||||
this.setState({
|
||||
datasets: s,
|
||||
});
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: Props, nextState: State) : boolean {
|
||||
return !eq(nextProps, this.props) || !eq(nextState, this.state);
|
||||
}
|
||||
|
||||
render() : React.Element {
|
||||
this._updateDatasets(this.props);
|
||||
|
||||
const options = [];
|
||||
for (const v of this.state.datasets) {
|
||||
options.push(<option value={v} key={v}>{v}</option>);
|
||||
}
|
||||
return <form>
|
||||
Choose dataset:
|
||||
<br/>
|
||||
<select value={this.props.selected}
|
||||
onChange={e => this.handleSelectChange(e)}>
|
||||
<option/>
|
||||
{options}
|
||||
</select>
|
||||
</form>;
|
||||
}
|
||||
}
|
||||
|
||||
DatasetPicker.defaultProps = {selected: ''};
|
||||
@@ -1,54 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import {Ref} from 'noms';
|
||||
|
||||
export default function eq(a: any, b: any) : boolean {
|
||||
// Babel bug: https://github.com/babel/babel/issues/3046
|
||||
let i;
|
||||
if (a === b) return true;
|
||||
const ta = typeof a;
|
||||
const tb = typeof b;
|
||||
if (ta !== tb) return false;
|
||||
if (a === null || b === null) return false;
|
||||
if (ta !== 'object') return false;
|
||||
if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false;
|
||||
if (a instanceof Ref) {
|
||||
return a.equals(b);
|
||||
}
|
||||
// https://github.com/attic-labs/noms/issues/615
|
||||
// const ar = a.ref;
|
||||
// const br = b.ref;
|
||||
// if (ar && br) {
|
||||
// return eq(ar, br);
|
||||
// }
|
||||
if (a instanceof Array) {
|
||||
if (a.length !== b.length) return false;
|
||||
for (i = 0; i < a.length; i++) {
|
||||
if (!eq(a[i], b[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (a instanceof Set) {
|
||||
if (a.size !== b.size) return false;
|
||||
// has uses object identity
|
||||
return eq([...a].sort(), [...b].sort());
|
||||
}
|
||||
if (a instanceof Map) {
|
||||
if (a.size !== b.size) return false;
|
||||
// get uses object identity
|
||||
const compare = (a, b) => {
|
||||
if (eq(a[0], b[0])) return 0;
|
||||
if (a[0] < b[0]) return -1;
|
||||
return 1;
|
||||
};
|
||||
return eq([...a].sort(compare), [...b].sort(compare));
|
||||
}
|
||||
|
||||
const ka = Object.keys(a);
|
||||
const kb = Object.keys(b);
|
||||
if (ka.length !== kb.length) return false;
|
||||
for (i = 0; i < ka.length; i++) {
|
||||
if (!eq(a[ka[i]], b[ka[i]])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import {assert} from 'chai';
|
||||
import {suite, test} from 'mocha';
|
||||
import eq from './eq.js';
|
||||
import {Ref} from 'noms';
|
||||
|
||||
suite('eq', () => {
|
||||
test('different', () => {
|
||||
const r0 = Ref.parse('sha1-0000000000000000000000000000000000000000');
|
||||
const r1 = Ref.parse('sha1-0000000000000000000000000000000000000001');
|
||||
|
||||
const different = [
|
||||
null,
|
||||
undefined,
|
||||
true,
|
||||
false,
|
||||
1,
|
||||
2,
|
||||
'1',
|
||||
{},
|
||||
{a: 3},
|
||||
{a: 4},
|
||||
{a: 3, b: 5},
|
||||
[],
|
||||
[6],
|
||||
[7],
|
||||
[6, 7],
|
||||
new Set(),
|
||||
new Set([8]),
|
||||
new Set([9]),
|
||||
new Set([8, 9]),
|
||||
new Map(),
|
||||
new Map([[10, 11]]),
|
||||
new Map([[10, 12]]),
|
||||
new Map([[10, 11], [13, 14]]),
|
||||
r0,
|
||||
r1,
|
||||
];
|
||||
for (let i = 0; i < different.length; i++) {
|
||||
for (let j = 0; j < different.length; j++) {
|
||||
assert.equal(i === j, eq(different[i], different[j]), `${different[i]} == ${different[j]}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('same', () => {
|
||||
const r1 = Ref.parse('sha1-0000000000000000000000000000000000000000');
|
||||
const r2 = Ref.parse('sha1-0000000000000000000000000000000000000000');
|
||||
const same = [
|
||||
[new Set([1, 2]), new Set([2, 1])],
|
||||
[new Map([[1, 2], [3, 4]]), new Map([[3, 4], [1, 2]])],
|
||||
[{a: 1, b: 2}, {b: 2, a: 1}],
|
||||
[new Set([{a: 1}]), new Set([{a: 1}])],
|
||||
[new Map([[{a: 1}, {b: 2}]]), new Map([[{a: 1}, {b: 2}]])],
|
||||
[new Set([r1]), new Set([r2])],
|
||||
[new Map([[r1, 42]]), new Map([[r2, 42]])],
|
||||
];
|
||||
for (const vs of same) {
|
||||
for (let i = 0; i < vs.length; i++) {
|
||||
for (let j = 0; j < vs.length; j++) {
|
||||
assert.equal(true, eq(vs[i], vs[i]), `${vs[i]} == ${vs[j]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import {DataStore, HttpStore} from 'noms';
|
||||
import queryString from 'query-string';
|
||||
import React from 'react'; // eslint-disable-line no-unused-vars
|
||||
import ReactDOM from 'react-dom';
|
||||
import Root from './root.js';
|
||||
|
||||
window.onload = window.onhashchange = render;
|
||||
|
||||
const nomsServer: ?string = process.env.NOMS_SERVER;
|
||||
if (!nomsServer) {
|
||||
throw new Error('NOMS_SERVER not set');
|
||||
}
|
||||
|
||||
function updateQuery(qs: {[key: string]: string}) {
|
||||
location.hash = queryString.stringify(qs);
|
||||
}
|
||||
|
||||
function render() {
|
||||
const qs = Object.freeze(queryString.parse(location.hash));
|
||||
const target = document.getElementById('root');
|
||||
const store = new DataStore(new HttpStore(nomsServer));
|
||||
|
||||
ReactDOM.render(
|
||||
<Root qs={qs} store={store} updateQuery={updateQuery}/>,
|
||||
target);
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import eq from './eq.js';
|
||||
import React from 'react';
|
||||
import type {ChunkStore, Ref, NomsMap, Struct} from 'noms';
|
||||
import {readValue} from 'noms';
|
||||
|
||||
type DefaultProps = {
|
||||
onLoad: () => void,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
onLoad: () => void,
|
||||
photoRef: Ref,
|
||||
style: Object,
|
||||
store: ChunkStore
|
||||
};
|
||||
|
||||
type State = {
|
||||
photo: ?Struct,
|
||||
sizes: Array<{size: Struct, url: string}>
|
||||
};
|
||||
|
||||
export default class Photo extends React.Component<DefaultProps, Props, State> {
|
||||
state: State;
|
||||
static defaultProps: DefaultProps;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
photo: null,
|
||||
sizes: [],
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: Props, nextState: State) : boolean {
|
||||
return !eq(nextProps, this.props) || !eq(nextState, this.state);
|
||||
}
|
||||
|
||||
async _updatePhoto(props: Props) : Promise<void> {
|
||||
function area(size: Struct) : number {
|
||||
return size.get('Width') * size.get('Height');
|
||||
}
|
||||
|
||||
const photo: Struct = await readValue(props.photoRef, props.store);
|
||||
|
||||
// Sizes is a Map(Size, String) where the string is a URL.
|
||||
const sizes = [];
|
||||
const s: NomsMap<Struct, string> = photo.get('Sizes');
|
||||
await s.forEach((url, size) => {
|
||||
sizes.push({size, url});
|
||||
});
|
||||
sizes.sort((a, b) => area(a.size) - area(b.size));
|
||||
this.setState({photo, sizes});
|
||||
}
|
||||
|
||||
render() : ?React.Element {
|
||||
this._updatePhoto(this.props);
|
||||
|
||||
if (!this.state.photo || this.state.sizes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<img
|
||||
style={this.props.style}
|
||||
src={this.getURL()}
|
||||
onLoad={this.props.onLoad}/>
|
||||
);
|
||||
}
|
||||
|
||||
getURL() : string {
|
||||
// If there are some remote URLs we can use, just pick the most appropriate size. We need the
|
||||
// smallest one that is bigger than our current dimensions.
|
||||
const sizes = this.state.sizes;
|
||||
const w = this.props.style.width || 0;
|
||||
const h = this.props.style.height || 0;
|
||||
const size = sizes.find(({size}) => size.get('Width') >= w && size.get('Height') >= h);
|
||||
return size ? size.url : sizes[sizes.length - 1].url;
|
||||
}
|
||||
}
|
||||
|
||||
Photo.defaultProps = {
|
||||
onLoad() {},
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import Photo from './photo.js';
|
||||
import React from 'react';
|
||||
import type {ChunkStore, Ref} from 'noms';
|
||||
|
||||
const photoStyle = {
|
||||
display: 'inline-block',
|
||||
marginRight: 5,
|
||||
height: 300,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
photos: Array<Ref>,
|
||||
store: ChunkStore,
|
||||
};
|
||||
|
||||
export default function Preview(props: Props) : React.Element {
|
||||
return <div>{
|
||||
props.photos.map(r => <Photo key={r.toString()} photoRef={r} store={props.store}
|
||||
style={photoStyle}/>)
|
||||
}</div>;
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import DatasetPicker from './datasetpicker.js';
|
||||
import eq from './eq.js';
|
||||
import React from 'react';
|
||||
import SlideShow from './slideshow.js';
|
||||
import TagChooser from './tagchooser.js';
|
||||
import type {DataStore} from 'noms';
|
||||
import {invariant, NomsMap, NomsSet, Ref} from 'noms';
|
||||
|
||||
type QueryStringObject = {[key: string]: string};
|
||||
|
||||
type Props = {
|
||||
store: DataStore,
|
||||
qs: QueryStringObject,
|
||||
updateQuery: (qs: QueryStringObject) => void,
|
||||
};
|
||||
|
||||
type State = {
|
||||
selectedTags: Set<string>,
|
||||
selectedPhotos: Array<Ref>,
|
||||
tags: Array<string>,
|
||||
};
|
||||
|
||||
export default class Root extends React.Component<void, Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectedTags: new Set(),
|
||||
selectedPhotos: [],
|
||||
tags: [],
|
||||
};
|
||||
}
|
||||
|
||||
async _updateState(props: Props) : Promise<void> {
|
||||
const selectedTags = this.getSelectedTags(props);
|
||||
const tags = [];
|
||||
const selectedPhotos: Array<Ref> = [];
|
||||
|
||||
if (props.qs.ds) {
|
||||
const {store} = props;
|
||||
const commit = await store.head(props.qs.ds);
|
||||
invariant(commit);
|
||||
const v = commit.get('value');
|
||||
if (v instanceof NomsMap) {
|
||||
const seenRefs: Set<string> = new Set();
|
||||
|
||||
const sets = [];
|
||||
|
||||
await v.forEach((value, tag) => {
|
||||
tags.push(tag);
|
||||
if (selectedTags.has(tag) && value instanceof NomsSet) {
|
||||
sets.push(value);
|
||||
}
|
||||
});
|
||||
|
||||
for (const s of sets) {
|
||||
await s.forEach(r => {
|
||||
const rs = r.toString();
|
||||
if (!seenRefs.has(rs)) {
|
||||
seenRefs.add(rs);
|
||||
selectedPhotos.push(r);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// This sorts the photos deterministically, by the ref
|
||||
// TODO: Sort by create date if it ends up that the common image type
|
||||
// has a create date.
|
||||
selectedPhotos.sort((a, b) => a.equals(b) ? 0 : a.less(b) ? -1 : 1);
|
||||
tags.sort();
|
||||
}
|
||||
|
||||
this.setState({selectedTags, tags, selectedPhotos});
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: Props, nextState: State) : boolean {
|
||||
return !eq(nextProps, this.props) || !eq(nextState, this.state);
|
||||
}
|
||||
|
||||
handleDataSetPicked(ds: string) {
|
||||
const qs = Object.assign({}, this.props.qs, {ds});
|
||||
this.props.updateQuery(qs);
|
||||
}
|
||||
|
||||
getSelectedTags(props: Props) : Set<string> {
|
||||
const tags = props.qs.tags;
|
||||
if (!tags) {
|
||||
return new Set();
|
||||
}
|
||||
return new Set(tags.split(','));
|
||||
}
|
||||
|
||||
handleTagsChange(selectedTags: Set<string>) {
|
||||
// FIXME: https://github.com/facebook/flow/issues/1059
|
||||
const workaround: any = selectedTags;
|
||||
const tags = [...workaround].join(',');
|
||||
const qs = Object.assign({}, this.props.qs, {tags});
|
||||
this.props.updateQuery(qs);
|
||||
}
|
||||
|
||||
handleTagsConfirm() {
|
||||
const qs = Object.assign({}, this.props.qs, {show: '1'});
|
||||
this.props.updateQuery(qs);
|
||||
}
|
||||
|
||||
render() : React.Element {
|
||||
this._updateState(this.props);
|
||||
|
||||
if (!this.props.qs.ds) {
|
||||
return <DatasetPicker store={this.props.store}
|
||||
onChange={ds => this.handleDataSetPicked(ds)}/>;
|
||||
}
|
||||
|
||||
if (!this.props.qs.show || this.state.selectedTags.size === 0) {
|
||||
return <TagChooser
|
||||
store={this.props.store}
|
||||
tags={this.state.tags}
|
||||
selectedPhotos={this.state.selectedPhotos}
|
||||
selectedTags={this.state.selectedTags}
|
||||
onChange={selectedTags => this.handleTagsChange(selectedTags)}
|
||||
onConfirm={() => this.handleTagsConfirm()}/>;
|
||||
}
|
||||
|
||||
return <SlideShow store={this.props.store} photos={this.state.selectedPhotos}/>;
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import Photo from './photo.js';
|
||||
import React from 'react';
|
||||
import type {ChunkStore, Ref} from 'noms';
|
||||
|
||||
const containerStyle = {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
};
|
||||
|
||||
type Props = {
|
||||
store: ChunkStore,
|
||||
photos: Array<Ref>,
|
||||
};
|
||||
|
||||
type State = {
|
||||
index: number,
|
||||
};
|
||||
|
||||
export default class SlideShow extends React.Component<void, Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
index: 0,
|
||||
};
|
||||
}
|
||||
|
||||
handleTimeout() {
|
||||
let index = this.state.index + 1;
|
||||
if (index >= this.props.photos.length) {
|
||||
index = 0;
|
||||
}
|
||||
this.setState({index});
|
||||
}
|
||||
|
||||
render() : ?React.Element {
|
||||
const photoRef = this.props.photos[this.state.index];
|
||||
if (!photoRef) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<Item
|
||||
photoRef={photoRef}
|
||||
store={this.props.store}
|
||||
onTimeout={() => this.handleTimeout()} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
type ItemProps = {
|
||||
onTimeout: () => void,
|
||||
photoRef: Ref,
|
||||
store: ChunkStore
|
||||
};
|
||||
|
||||
type ItemState = {
|
||||
timerId: number
|
||||
};
|
||||
|
||||
class Item extends React.Component<void, ItemProps, ItemState> {
|
||||
state: ItemState;
|
||||
|
||||
constructor(props: ItemProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
timerId: 0,
|
||||
};
|
||||
}
|
||||
|
||||
setTimeout() {
|
||||
this.setState({
|
||||
timerId: window.setTimeout(this.props.onTimeout, 3000),
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.clearTimeout(this.state.timerId);
|
||||
}
|
||||
|
||||
render() : React.Element {
|
||||
const style = {
|
||||
objectFit: 'contain',
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
};
|
||||
|
||||
return (
|
||||
<Photo
|
||||
store={this.props.store}
|
||||
onLoad={() => this.setTimeout()}
|
||||
photoRef={this.props.photoRef}
|
||||
style={style}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import Preview from './preview.js';
|
||||
import React from 'react';
|
||||
import TagList from './taglist.js';
|
||||
import type {ChunkStore, Ref} from 'noms';
|
||||
|
||||
const styles = {
|
||||
root: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
|
||||
panes: {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
left: {
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
marginRight: '1em',
|
||||
},
|
||||
|
||||
right: {
|
||||
flex: 1,
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
padding: '1em',
|
||||
},
|
||||
|
||||
bottom: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
|
||||
button: {
|
||||
fontSize: '1.5em',
|
||||
margin: '1em',
|
||||
width: '50%',
|
||||
},
|
||||
};
|
||||
|
||||
type Props = {
|
||||
store: ChunkStore,
|
||||
tags: Array<string>,
|
||||
selectedPhotos: Array<Ref>,
|
||||
selectedTags: Set<string>,
|
||||
onChange: (selected: Set<string>) => void,
|
||||
onConfirm: () => void,
|
||||
};
|
||||
|
||||
export default function TagChooser(props: Props) : React.Element {
|
||||
return (
|
||||
<div style={styles.root}>
|
||||
<div style={styles.panes}>
|
||||
<div style={styles.left}>
|
||||
<TagList
|
||||
tags={props.tags}
|
||||
selected={props.selectedTags}
|
||||
onChange={props.onChange}/>
|
||||
</div>
|
||||
<div style={styles.right}>
|
||||
<Preview photos={props.selectedPhotos} store={props.store}/>
|
||||
</div>
|
||||
</div>
|
||||
<div style={styles.bottom}>
|
||||
<button style={styles.button} onClick={props.onConfirm}>
|
||||
PUSH THIS BUTTON
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
const tagStyle = {
|
||||
display: 'block',
|
||||
margin: '3px',
|
||||
marginRight: '25px',
|
||||
whiteSpace: 'nowrap',
|
||||
};
|
||||
|
||||
type Props = {
|
||||
selected: Set<string>,
|
||||
tags: Array<string>,
|
||||
onChange: (selected: Set<string>) => void,
|
||||
};
|
||||
|
||||
function handleChange(props: Props, tag: string) {
|
||||
const selected = new Set(props.selected);
|
||||
selected.has(tag) ? selected.delete(tag) : selected.add(tag);
|
||||
props.onChange(selected);
|
||||
}
|
||||
|
||||
export default function TagList(props: Props) : React.Element {
|
||||
const tags = [...props.tags].sort();
|
||||
const labels = tags.map(tag => <label style={tagStyle} key={tag}>
|
||||
<input type="checkbox" name="tc"
|
||||
checked={props.selected.has(tag)}
|
||||
onChange={() => handleChange(props, tag) }/>
|
||||
{tag}
|
||||
</label>);
|
||||
|
||||
return <div>{labels}</div>;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = require('noms-webpack-config')({
|
||||
requiredEnvVars: ['NOMS_SERVER'],
|
||||
});
|
||||
Reference in New Issue
Block a user