result display improvements, search to header

This commit is contained in:
Jeremy Kauffman 2016-08-07 22:48:45 -04:00 committed by Alex Liebowitz
parent 007da592f1
commit 2b07c982e6
5 changed files with 126 additions and 139 deletions

View file

@ -66,15 +66,18 @@ var App = React.createClass({
sessionStorage.setItem('drawerOpen', false); sessionStorage.setItem('drawerOpen', false);
this.setState({ drawerOpen: false }); this.setState({ drawerOpen: false });
}, },
onSearchStart: function() { onSearch: function(term) {
this.setState({ viewingPage: 'discover' }); this.setState({
viewingPage: 'discover',
pageArgs: term
});
}, },
getMainContent: function() getMainContent: function()
{ {
switch(this.state.viewingPage) switch(this.state.viewingPage)
{ {
case 'discover': case 'discover':
return <DiscoverPage />; return <DiscoverPage query={this.state.pageArgs} />;
case 'settings': case 'settings':
return <SettingsPage />; return <SettingsPage />;
case 'help': case 'help':
@ -104,7 +107,7 @@ var App = React.createClass({
<div id="window" className={ this.state.drawerOpen ? 'drawer-open' : 'drawer-closed' }> <div id="window" className={ this.state.drawerOpen ? 'drawer-open' : 'drawer-closed' }>
<Drawer onCloseDrawer={this.closeDrawer} viewingPage={this.state.viewingPage} /> <Drawer onCloseDrawer={this.closeDrawer} viewingPage={this.state.viewingPage} />
<div id="main-content"> <div id="main-content">
<Header onOpenDrawer={this.openDrawer} onSearchStart={this.onSearchStart} /> <Header onOpenDrawer={this.openDrawer} onSearch={this.onSearch} />
{mainContent} {mainContent}
</div> </div>
</div> </div>

View file

@ -13,41 +13,44 @@ var Header = React.createClass({
{ subtree: true, characterData: true, childList: true } { subtree: true, characterData: true, childList: true }
); );
}, },
componentDidMount() { componentDidMount: function() {
document.addEventListener('scroll', this.handleScroll); document.addEventListener('scroll', this.handleScroll);
}, },
componentWillUnmount() { componentWillUnmount: function() {
document.removeEventListener('scroll', this.handleScroll); document.removeEventListener('scroll', this.handleScroll);
if (this.userTypingTimer)
{
clearTimeout(this.userTypingTimer);
}
}, },
handleScroll() { handleScroll: function() {
this.setState({ this.setState({
isScrolled: event.srcElement.body.scrollTop > 0 isScrolled: event.srcElement.body.scrollTop > 0
}); });
}, },
onQueryChange: function(event) { onQueryChange: function(event) {
this.props.onSearchStart();
if (this.userTypingTimer) if (this.userTypingTimer)
{ {
clearTimeout(this.userTypingTimer); clearTimeout(this.userTypingTimer);
} }
//@TODO: Switch to React.js timing //@TODO: Switch to React.js timing
this.userTypingTimer = setTimeout(this.search, 800); // 800ms delay, tweak for faster/slower var searchTerm = event.target.value;
this.userTypingTimer = setTimeout(() => {
this.props.onSearch(searchTerm);
}, 800); // 800ms delay, tweak for faster/slower
// this.setState({
// searching: event.target.value.length > 0,
// query: event.target.value
// });
}, },
render: function() { render: function() {
return ( return (
<header id="header" className={this.state.isScrolled ? 'header-scrolled' : 'header-unscrolled'}> <header id="header" className={this.state.isScrolled ? 'header-scrolled' : 'header-unscrolled'}>
<Link onClick={this.props.onOpenDrawer} icon="icon-bars" className="open-drawer-link" /> <Link onClick={this.props.onOpenDrawer} icon="icon-bars" className="open-drawer-link" />
<h1>{ this.state.title }</h1> <h1>{ this.state.title }</h1>
<input type="search" onChange={this.onQueryChange} <div className="header-search">
<input type="search" onChange={this.onQueryChange}
placeholder="Find movies, music, games, and more"/> placeholder="Find movies, music, games, and more"/>
</div>
</header> </header>
); );
} }

View file

@ -14,10 +14,10 @@ var searchInputStyle = {
var SearchActive = React.createClass({ var SearchActive = React.createClass({
render: function() { render: function() {
return ( return (
<section style={fetchResultsStyle}> <div style={fetchResultsStyle}>
Looking up the Dewey Decimals Looking up the Dewey Decimals
<span className="busy-indicator"></span> <span className="busy-indicator"></span>
</section> </div>
); );
} }
}); });
@ -50,7 +50,7 @@ var SearchResults = React.createClass({
); );
}); });
return ( return (
<section>{rows}</section> <div>{rows}</div>
); );
} }
}); });
@ -94,21 +94,23 @@ var SearchResultRow = React.createClass({
}, },
render: function() { render: function() {
return ( return (
<section className="row-fluid"> <section className="card">
<div className="span3"> <div className="row-fluid">
<img src={this.props.imgUrl} alt={'Photo for ' + (this.props.title || this.props.name)} style={searchRowImgStyle} /> <div className="span3">
</div> <img src={this.props.imgUrl} alt={'Photo for ' + (this.props.title || this.props.name)} style={searchRowImgStyle} />
<div className="span9"> </div>
<span style={searchRowCostStyle}> <div className="span9">
<CreditAmount amount={this.props.cost_est} isEstimate={true}/> <span style={searchRowCostStyle}>
</span> <CreditAmount amount={this.props.cost_est} isEstimate={true}/>
<h2 style={searchRowTitleStyle}><a href={'/?show=' + this.props.name}>{this.props.title}</a></h2> </span>
<div style={searchRowNameStyle}>lbry://{this.props.name}</div> <h2 style={searchRowTitleStyle}><a href={'/?show=' + this.props.name}>{this.props.title}</a></h2>
<p style={searchRowDescriptionStyle}><TruncatedText>{this.props.description}</TruncatedText></p> <div style={searchRowNameStyle}>lbry://{this.props.name}</div>
<div> <p style={searchRowDescriptionStyle}><TruncatedText>{this.props.description}</TruncatedText></p>
<WatchLink streamName={this.props.name} button="primary" /> <div>
{ ' ' } <WatchLink streamName={this.props.name} button="primary" />
<DownloadLink streamName={this.props.name} button="alt" /> { ' ' }
<DownloadLink streamName={this.props.name} button="alt" />
</div>
</div> </div>
</div> </div>
</section> </section>
@ -117,16 +119,14 @@ var SearchResultRow = React.createClass({
}); });
var featuredContentItemStyle = { var featuredContentItemStyle = {
fontSize: '0.95em', height: '120px',
marginTop: '10px', overflowY: 'hidden'
maxHeight: '220px'
}, featuredContentItemImgStyle = { }, featuredContentItemImgStyle = {
maxWidth: '100%', maxWidth: '100%',
maxHeight: '100%', maxHeight: '120px',
display: 'block', display: 'block',
marginLeft: 'auto', marginLeft: 'auto',
marginRight: 'auto', marginRight: 'auto'
marginTop: '5px',
}, featuredContentHeaderStyle = { }, featuredContentHeaderStyle = {
fontWeight: 'bold', fontWeight: 'bold',
marginBottom: '5px' marginBottom: '5px'
@ -135,7 +135,6 @@ var featuredContentItemStyle = {
fontSize: '0.9em' fontSize: '0.9em'
}, featuredContentItemDescriptionStyle = { }, featuredContentItemDescriptionStyle = {
color: '#444', color: '#444',
marginBottom: '5px',
fontSize: '0.9em', fontSize: '0.9em',
}, featuredContentItemCostStyle = { }, featuredContentItemCostStyle = {
float: 'right' float: 'right'
@ -189,28 +188,27 @@ var FeaturedContentItem = React.createClass({
} }
return ( return (
<div className="row-fluid" style={featuredContentItemStyle}> <section className="card">
<div className="span4"> <div className="row-fluid" style={featuredContentItemStyle}>
<img src={metadata.thumbnail} alt={'Photo for ' + this.state.title} style={thumbStyle} /> <div className="span4">
</div> <img src={metadata.thumbnail} alt={'Photo for ' + this.state.title} style={thumbStyle} />
<div className="span8"> </div>
<h4 style={featuredContentHeaderStyle}><a href={'/?show=' + this.props.name}>{this.state.title}</a></h4> <div className="span8">
<div style={featuredContentSubheaderStyle}> <h4 style={featuredContentHeaderStyle}><a href={'/?show=' + this.props.name}>{this.state.title}</a></h4>
<div style={featuredContentItemCostStyle}><CreditAmount amount={this.state.amount} isEstimate={true}/></div> <div style={featuredContentSubheaderStyle}>
<WatchLink streamName={this.props.name} /> <div style={featuredContentItemCostStyle}><CreditAmount amount={this.state.amount} isEstimate={true}/></div>
&nbsp;&nbsp;&nbsp; <WatchLink streamName={this.props.name} />
<DownloadLink streamName={this.props.name} /> &nbsp;&nbsp;&nbsp;
<DownloadLink streamName={this.props.name} />
</div>
<p style={featuredContentItemDescriptionStyle}><TruncatedText>{metadata.description}</TruncatedText></p>
</div> </div>
<p style={featuredContentItemDescriptionStyle}><TruncatedText>{metadata.description}</TruncatedText></p>
</div> </div>
</div>); </section>);
} }
}); });
var featuredContentStyle = { var featuredContentLegendStyle = {
width: '100%',
marginTop: '-8px',
}, featuredContentLegendStyle = {
fontSize: '12px', fontSize: '12px',
color: '#aaa', color: '#aaa',
verticalAlign: '15%', verticalAlign: '15%',
@ -218,56 +216,38 @@ var featuredContentStyle = {
var FeaturedContent = React.createClass({ var FeaturedContent = React.createClass({
render: function() { render: function() {
return (<section style={featuredContentStyle}> return (
<div className="row-fluid"> <div className="row-fluid">
<div className="span6"> <div className="span6">
<h3>Featured Content</h3> <h3>Featured Content</h3>
<FeaturedContentItem name="what" />
<FeaturedContentItem name="itsadisaster" />
<FeaturedContentItem name="keynesvhayek" />
<FeaturedContentItem name="meetlbry1" />
</div> </div>
<div className="span6"> <div className="span6">
<h3>Community Content <ToolTipLink style={featuredContentLegendStyle} label="What's this?" <h3>Community Content <ToolTipLink style={featuredContentLegendStyle} label="What's this?"
tooltip='Community Content is a public space where anyone can share content with the rest of the LBRY community. Bid on the names "one," "two," "three" and "four" to put your content here!' /></h3> tooltip='Community Content is a public space where anyone can share content with the rest of the LBRY community. Bid on the names "one," "two," "three" and "four" to put your content here!' /></h3>
</div>
</div>
<div className="row-fluid">
<div className="span6">
<FeaturedContentItem name="what" />
</div>
<div className="span6">
<FeaturedContentItem name="one" /> <FeaturedContentItem name="one" />
</div>
</div>
<div className="row-fluid">
<div className="span6">
<FeaturedContentItem name="itsadisaster" narrow />
</div>
<div className="span6">
<FeaturedContentItem name="two" /> <FeaturedContentItem name="two" />
</div>
</div>
<div className="row-fluid">
<div className="span6">
<FeaturedContentItem name="keynesvhayek" />
</div>
<div className="span6">
<FeaturedContentItem name="three" /> <FeaturedContentItem name="three" />
</div>
</div>
<div className="row-fluid">
<div className="span6">
<FeaturedContentItem name="meetlbry1" />
</div>
<div className="span6">
<FeaturedContentItem name="four" /> <FeaturedContentItem name="four" />
</div> </div>
</div> </div>
</section>); );
} }
}); });
var DiscoverPage = React.createClass({ var DiscoverPage = React.createClass({
userTypingTimer: null, userTypingTimer: null,
componentDidUpdate: function() {
if (this.props.query)
{
lbry.search(this.props.query, this.searchCallback.bind(this, this.props.query));
}
},
componentDidMount: function() { componentDidMount: function() {
document.title = "Discover"; document.title = "Discover";
}, },
@ -275,57 +255,29 @@ var DiscoverPage = React.createClass({
getInitialState: function() { getInitialState: function() {
return { return {
results: [], results: [],
searching: false, searching: this.props.query && this.props.query.length > 0
query: ''
}; };
}, },
search: function() {
if (this.state.query)
{
lbry.search(this.state.query, this.searchCallback.bind(this, this.state.query));
}
else
{
this.setState({
searching: false,
results: []
});
}
},
searchCallback: function(originalQuery, results) { searchCallback: function(originalQuery, results) {
console.log('search callback');
console.log(this.state);
if (this.state.searching) //could have canceled while results were pending, in which case nothing to do if (this.state.searching) //could have canceled while results were pending, in which case nothing to do
{ {
this.setState({ this.setState({
results: results, results: results,
searching: this.state.query != originalQuery, //multiple searches can be out, we're only done if we receive one we actually care about searching: this.props.query != originalQuery, //multiple searches can be out, we're only done if we receive one we actually care about
}); });
} }
}, },
onQueryChange: function(event) {
if (this.userTypingTimer)
{
clearTimeout(this.userTypingTimer);
}
//@TODO: Switch to React.js timing
this.userTypingTimer = setTimeout(this.search, 800); // 800ms delay, tweak for faster/slower
this.setState({
searching: event.target.value.length > 0,
query: event.target.value
});
},
render: function() { render: function() {
return ( return (
<main> <main>
{ this.state.searching ? <SearchActive /> : null } { this.state.searching ? <SearchActive /> : null }
{ !this.state.searching && this.state.query && this.state.results.length ? <SearchResults results={this.state.results} /> : null } { !this.state.searching && this.props.query && this.state.results.length ? <SearchResults results={this.state.results} /> : null }
{ !this.state.searching && this.state.query && !this.state.results.length ? <SearchNoResults query={this.state.query} /> : null } { !this.state.searching && this.props.query && !this.state.results.length ? <SearchNoResults query={this.props.query} /> : null }
{ !this.state.query && !this.state.searching ? <FeaturedContent /> : null } { !this.props.query && !this.state.searching ? <FeaturedContent /> : null }
</main> </main>
); );
} }

View file

@ -68,6 +68,7 @@ $drawer-width: 240px;
{ {
#main-content { margin-left: $drawer-width; } #main-content { margin-left: $drawer-width; }
.open-drawer-link { visibility: hidden; } .open-drawer-link { visibility: hidden; }
#header { padding-left: $drawer-width + $spacing-vertical / 2; }
} }
#header #header
@ -75,18 +76,29 @@ $drawer-width: 240px;
background: $color-primary; background: $color-primary;
color: white; color: white;
height: $header-height; height: $header-height;
padding: $spacing-vertical / 2 $spacing-vertical / 2 $spacing-vertical / 2 $drawer-width + $spacing-vertical / 2; padding: $spacing-vertical / 2;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
h1 { font-size: 1.8em; line-height: $header-height - $spacing-vertical; display: inline-block; } h1 { font-size: 1.8em; line-height: $header-height - $spacing-vertical; display: inline-block; float: left; }
&.header-scrolled &.header-scrolled
{ {
box-shadow: $default-box-shadow; box-shadow: $default-box-shadow;
} }
} }
.header-search
{
margin-left: 60px;
text-align: center;
input[type="search"] {
background: rgba(255, 255, 255, 0.3);
color: white;
width: 400px;
@include placeholder-color(#e8e8e8);
}
}
#main-content #main-content
{ {
@ -97,6 +109,19 @@ $drawer-width: 240px;
margin-top: $header-height; margin-top: $header-height;
padding: $spacing-vertical; padding: $spacing-vertical;
} }
h2
{
margin-bottom: $spacing-vertical;
}
h3, h4
{
margin-bottom: $spacing-vertical / 2;
margin-top: $spacing-vertical;
&:first-child
{
margin-top: 0;
}
}
} }
$header-icon-size: 1.5em; $header-icon-size: 1.5em;
@ -105,7 +130,7 @@ $header-icon-size: 1.5em;
{ {
display: inline-block; display: inline-block;
font-size: $header-icon-size; font-size: $header-icon-size;
padding: 0 18px 0 6px; padding: 2px 6px 0 6px;
float: left; float: left;
} }
.close-lbry-link .close-lbry-link
@ -123,19 +148,7 @@ $header-icon-size: 1.5em;
background: $color-bg; background: $color-bg;
box-shadow: $default-box-shadow; box-shadow: $default-box-shadow;
border-radius: 2px; border-radius: 2px;
h2
{
margin-bottom: $spacing-vertical;
}
h3, h4
{
margin-bottom: $spacing-vertical / 2;
margin-top: $spacing-vertical;
&:first-child
{
margin-top: 0;
}
}
} }
.full-screen .full-screen

View file

@ -43,6 +43,22 @@ $default-box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0
border-radius: $radius; border-radius: $radius;
} }
@mixin placeholder-color($color) {
/*do not group these it breaks because CSS*/
&:-moz-placeholder {
color: $color;
}
&::-moz-placeholder {
color: $color;
}
&:-ms-input-placeholder {
color: $color;
}
&::-webkit-input-placeholder {
color: $color;
}
}
@mixin display-flex() @mixin display-flex()
{ {
display: -webkit-box; display: -webkit-box;