Merge branch 'master' into picasa-client

This commit is contained in:
Dan Willhite
2015-09-03 08:29:19 -07:00
6 changed files with 90 additions and 39 deletions

View File

@@ -20,13 +20,20 @@ func (suite *HttpStoreTestSuite) SetupTest() {
suite.store = NewHttpStoreClient("http://localhost:8000")
suite.server = NewHttpStoreServer(&MemoryStore{}, 8000)
go suite.server.Run()
// This call to a non-existing URL allows us to exit being sure that the server started. Otherwise, we sometimes get races with Stop() below.
req, err := http.NewRequest("GET", "http://localhost:8000/notHere", nil)
suite.NoError(err)
res, err := http.DefaultClient.Do(req)
suite.NoError(err)
suite.Equal(res.StatusCode, http.StatusNotFound)
}
func (suite *HttpStoreTestSuite) TearDownTest() {
suite.server.Stop()
// Stop will have closed it's side of an existing KeepAlive socket. The next request will fail.
req, err := http.NewRequest("GET", "http://localhost:8000", nil)
req, err := http.NewRequest("GET", "http://localhost:8000/notHere", nil)
suite.NoError(err)
_, err = http.DefaultClient.Do(req)
suite.Error(err)

View File

@@ -34,9 +34,10 @@ func NewLevelDBStore(dir string) *LevelDBStore {
d.Exp.NotEmpty(dir)
d.Exp.NoError(os.MkdirAll(dir, 0700))
db, err := leveldb.OpenFile(dir, &opt.Options{
Compression: opt.NoCompression,
Filter: filter.NewBloomFilter(10), // 10 bits/key
WriteBuffer: 1 << 24, // 16MiB
Compression: opt.NoCompression,
Filter: filter.NewBloomFilter(10), // 10 bits/key
OpenFilesCacheCapacity: 240, // To stay under OSX 255 max open fd (plus 15, for good measure, because using 255 still hit the limit)
WriteBuffer: 1 << 24, // 16MiB
})
d.Chk.NoError(err)
return &LevelDBStore{db, &sync.Mutex{}, 0}

View File

@@ -2,6 +2,13 @@
<head>
<meta charset="UTF-8">
<script src="tagshow.js"></script>
<style>
body {
background: black;
color: white;
margin: 0;
}
</style>
</head>
<body>
<div id="root"></div>

View File

@@ -8,14 +8,6 @@ var React = require('react');
var SlideShow = require('./slideshow.js');
var TagCloud = require('./tagcloud.js');
var containerStyle = {
display: 'flex',
};
var slideShowStyle = {
flex: 1,
};
var Root = React.createClass({
mixins: [ImmutableRenderMixin],
@@ -69,11 +61,7 @@ var Root = React.createClass({
}
return (
<div style={containerStyle}>
<div style={slideShowStyle}>
<SlideShow ds={dataset} tags={selectedTags}/>
</div>
</div>
<SlideShow ds={dataset} tags={selectedTags}/>
);
},
});

View File

@@ -6,11 +6,22 @@ var noms = require('noms')
var React = require('react');
var containerStyle = {
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
};
var imageStyle = {
maxHeight: 300,
marginRight: 7,
width: '100%',
height: '100%',
objectFit: 'contain',
};
var SlideShow = React.createClass({
@@ -24,9 +35,19 @@ var SlideShow = React.createClass({
getInitialState: function() {
return {
photos: Immutable.Set(),
index: 0,
nextSlideTime: 0,
}
},
handleTimeout: function() {
var newIndex = this.state.index + 1;
if (newIndex >= this.state.photos.size) {
newIndex = 0;
}
this.setState({index: newIndex});
},
render: function() {
this.props.ds
.then(head => head.get('value').deref())
@@ -36,21 +57,33 @@ var SlideShow = React.createClass({
.valueSeq()
.map(ref => ref.deref()))
}).then(sets => {
this.setState({photos: Immutable.Set(sets[0]).intersect(...sets)})
this.setState({
photos:
Immutable.List(
Immutable.Set(sets[0])
.intersect(...sets)
// This sorts the photos deterministically, by the ref of their image
// blob.
// TODO: Sort by create date if it ends up that the common image type
// has a create date.
.sort((a, b) => a.ref < b.ref)
)
});
});
return <div style={containerStyle}>{
this.state.photos
.sort(
// This sorts the photos deterministically, by the ref of their image
// blob.
// TODO: Sort by create date if it ends up that the common image type
// has a create date.
(a, b) => a.ref < b.ref)
.map(
photoRef => <Item key={photoRef.ref} photoRef={photoRef}/>)
.toArray()
}</div>
var photoRef = this.state.photos.get(this.state.index);
if (!photoRef) {
return null;
}
return (
<div style={containerStyle}>
<Item
key={photoRef.ref}
photoRef={photoRef}
onTimeout={this.handleTimeout.bind(this)} />
</div>
);
},
});
@@ -58,6 +91,7 @@ var Item = React.createClass({
mixins: [ImmutableRenderMixin],
propTypes: {
onTimeout: React.PropTypes.func.isRequired,
photoRef: React.PropTypes.instanceOf(noms.Ref),
},
@@ -68,6 +102,20 @@ var Item = React.createClass({
};
},
componentDidMount: function() {
this.setState({
timerId: window.setTimeout(this.props.onTimeout, 3000),
});
},
componentWillUnmount: function() {
window.clearTimeout(this.state.timerId);
},
handleLoad: function(e) {
URL.revokeObjectURL(e.target.src);
},
render: function() {
this.props.photoRef.deref().then(
p => p.get('image').deref()).then(
@@ -82,11 +130,10 @@ var Item = React.createClass({
}
return (
<div style={{display:'inline-block'}}>
<img style={imageStyle} src={URL.createObjectURL(this.state.blob)}/>
<br/>
{this.state.tags.join(', ')}
</div>
<img
style={imageStyle}
src={URL.createObjectURL(this.state.blob)}
onLoad={this.handleLoad}/>
);
},
});

View File

@@ -25,7 +25,7 @@ class Ref{
}
hashCode() {
return parseInt(this.ref.slice(0, 8), 16);
return parseInt(this.ref.slice(-8), 16);
}
// BUG 88 (instance of is failing in dev build)
@@ -93,7 +93,8 @@ function decodeCompoundList(value, ref, getChunk) {
return Promise.all(
value
.filter((v, i) => i % 2 === 0)
.map(v => decodeValue(v, ref, getChunk)))
// v is a string representing the ref here.
.map(v => decodeRef(v, ref, getChunk).deref()))
.then(childLists => Immutable.List(childLists).flatten(1));
}