releasable?
This commit is contained in:
parent
59da42a84b
commit
13e198f20e
12 changed files with 182 additions and 257 deletions
|
@ -15,6 +15,7 @@ import PublishPage from './page/publish.js';
|
|||
import SearchPage from './page/search.js';
|
||||
import DiscoverPage from './page/discover.js';
|
||||
import DeveloperPage from './page/developer.js';
|
||||
import lbryuri from './lbryuri.js';
|
||||
import {FileListDownloaded, FileListPublished} from './page/file-list.js';
|
||||
import Header from './component/header.js';
|
||||
import {Modal, ExpandableModal} from './component/modal.js';
|
||||
|
@ -250,7 +251,7 @@ var App = React.createClass({
|
|||
case 'receive':
|
||||
return [this.state.viewingPage.charAt(0).toUpperCase() + this.state.viewingPage.slice(1), "icon-bank", <WalletPage viewingPage={this.state.viewingPage} />]
|
||||
case 'show':
|
||||
return [this.state.pageArgs, "icon-file", <ShowPage uri={this.state.pageArgs} />];
|
||||
return [lbryuri.normalize(this.state.pageArgs), "icon-file", <ShowPage uri={this.state.pageArgs} />];
|
||||
case 'publish':
|
||||
return ["Publish", "icon-upload", <PublishPage />];
|
||||
case 'developer':
|
||||
|
|
|
@ -3,7 +3,7 @@ import lbry from '../lbry.js';
|
|||
import lbryuri from '../lbryuri.js';
|
||||
import {Link} from '../component/link.js';
|
||||
import {FileActions} from '../component/file-actions.js';
|
||||
import {Thumbnail, TruncatedText, FilePrice} from '../component/common.js';
|
||||
import {BusyMessage, TruncatedText, FilePrice} from '../component/common.js';
|
||||
import UriIndicator from '../component/channel-indicator.js';
|
||||
|
||||
/*should be merged into FileTile once FileTile is refactored to take a single id*/
|
||||
|
@ -77,37 +77,32 @@ export let FileTileStream = React.createClass({
|
|||
const isConfirmed = !!metadata;
|
||||
const title = isConfirmed ? metadata.title : uri;
|
||||
const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw;
|
||||
const primaryUrl = "?show=" + uri;
|
||||
return (
|
||||
<section className={ 'file-tile card ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver} onMouseLeave={this.handleMouseOut}>
|
||||
<div className={"row-fluid card__inner file-tile__row"}>
|
||||
<div className="span3 file-tile__thumbnail-container">
|
||||
<a href={'?show=' + uri}><Thumbnail className="file-tile__thumbnail" {... metadata && metadata.thumbnail ? {src: metadata.thumbnail} : {}} alt={'Photo for ' + this.props.uri} /></a>
|
||||
</div>
|
||||
<div className="span9">
|
||||
<div className="card__title-primary">
|
||||
{ !this.props.hidePrice
|
||||
? <FilePrice uri={this.props.uri} />
|
||||
: null}
|
||||
<div className="meta"><a href={'?show=' + this.props.uri}>{uri}</a></div>
|
||||
<h3>
|
||||
<a href={'?show=' + uri} title={title}>
|
||||
<TruncatedText lines={1}>
|
||||
{title}
|
||||
</TruncatedText>
|
||||
</a>
|
||||
</h3>
|
||||
<a href={primaryUrl} className="card__link">
|
||||
<div className={"card__inner file-tile__row"}>
|
||||
<div className="card__media"
|
||||
style={{ backgroundImage: "url('" + (metadata && metadata.thumbnail ? metadata.thumbnail : lbry.imagePath('default-thumb.svg')) + "')" }}>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<p className="file-tile__description">
|
||||
<TruncatedText lines={2}>
|
||||
<div className="file-tile__content">
|
||||
<div className="card__title-primary">
|
||||
{ !this.props.hidePrice
|
||||
? <FilePrice uri={this.props.uri} />
|
||||
: null}
|
||||
<div className="meta">{uri}</div>
|
||||
<h3><TruncatedText lines={1}>{title}</TruncatedText></h3>
|
||||
</div>
|
||||
<div className="card__content card__subtext">
|
||||
<TruncatedText lines={3}>
|
||||
{isConfirmed
|
||||
? metadata.description
|
||||
: <span className="empty">This file is pending confirmation.</span>}
|
||||
</TruncatedText>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{this.state.showNsfwHelp
|
||||
? <div className='card-overlay'>
|
||||
<p>
|
||||
|
@ -224,6 +219,7 @@ export let FileCardStream = React.createClass({
|
|||
|
||||
export let FileTile = React.createClass({
|
||||
_isMounted: false,
|
||||
_isResolvePending: false,
|
||||
|
||||
propTypes: {
|
||||
uri: React.PropTypes.string.isRequired,
|
||||
|
@ -236,7 +232,9 @@ export let FileTile = React.createClass({
|
|||
}
|
||||
},
|
||||
resolve: function(uri) {
|
||||
this._isResolvePending = true;
|
||||
lbry.resolve({uri: uri}).then((resolutionInfo) => {
|
||||
this._isResolvePending = false;
|
||||
if (this._isMounted && resolutionInfo && resolutionInfo.claim && resolutionInfo.claim.value &&
|
||||
resolutionInfo.claim.value.stream && resolutionInfo.claim.value.stream.metadata) {
|
||||
// In case of a failed lookup, metadata will be null, in which case the component will never display
|
||||
|
@ -267,7 +265,9 @@ export let FileTile = React.createClass({
|
|||
}
|
||||
if (this.props.showEmpty)
|
||||
{
|
||||
return <div className="empty">Empty file tile for {this.props.uri}</div>
|
||||
return this._isResolvePending ?
|
||||
<BusyMessage message="Loading magic decentralized data" /> :
|
||||
<div className="empty">{lbryuri.normalize(this.props.uri)} is unclaimed. <Link label="Put something here" href="?publish" /></div>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -59,29 +59,36 @@ var Header = React.createClass({
|
|||
}
|
||||
});
|
||||
|
||||
let WunderBar = React.createClass({
|
||||
_userTypingTimer: null,
|
||||
_input: null,
|
||||
_stateBeforeSearch: null,
|
||||
_resetOnNextBlur: true,
|
||||
|
||||
propTypes: {
|
||||
class WunderBar extends React.PureComponent {
|
||||
static propTypes = {
|
||||
onSearch: React.PropTypes.func.isRequired,
|
||||
onSubmit: React.PropTypes.func.isRequired
|
||||
},
|
||||
}
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._userTypingTimer = null;
|
||||
this._input = null;
|
||||
this._stateBeforeSearch = null;
|
||||
this._resetOnNextBlur = true;
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onFocus = this.onFocus.bind(this);
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
this.onKeyPress = this.onKeyPress.bind(this);
|
||||
this.onReceiveRef = this.onReceiveRef.bind(this);
|
||||
this.state = {
|
||||
address: this.props.address,
|
||||
icon: this.props.icon
|
||||
};
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.userTypingTimer) {
|
||||
clearTimeout(this._userTypingTimer);
|
||||
}
|
||||
},
|
||||
onChange: function(event) {
|
||||
}
|
||||
|
||||
onChange(event) {
|
||||
|
||||
if (this._userTypingTimer)
|
||||
{
|
||||
|
@ -96,19 +103,21 @@ let WunderBar = React.createClass({
|
|||
this._resetOnNextBlur = false;
|
||||
this.props.onSearch(searchTerm);
|
||||
}, 800); // 800ms delay, tweak for faster/slower
|
||||
},
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.viewingPage !== this.props.viewingPage || nextProps.address != this.props.address) {
|
||||
this.setState({ address: nextProps.address, icon: nextProps.icon });
|
||||
}
|
||||
},
|
||||
onFocus: function() {
|
||||
}
|
||||
|
||||
onFocus() {
|
||||
this._stateBeforeSearch = this.state;
|
||||
let newState = {
|
||||
icon: "icon-search",
|
||||
isActive: true
|
||||
}
|
||||
// this._input.value = ""; //trigger placeholder
|
||||
|
||||
this._focusPending = true;
|
||||
//below is hacking, improved when we have proper routing
|
||||
if (!this.state.address.startsWith('lbry://') && this.state.icon !== "icon-search") //onFocus, if they are not on an exact URL or a search page, clear the bar
|
||||
|
@ -116,8 +125,9 @@ let WunderBar = React.createClass({
|
|||
newState.address = '';
|
||||
}
|
||||
this.setState(newState);
|
||||
},
|
||||
onBlur: function() {
|
||||
}
|
||||
|
||||
onBlur() {
|
||||
let commonState = {isActive: false};
|
||||
if (this._resetOnNextBlur) {
|
||||
this.setState(Object.assign({}, this._stateBeforeSearch, commonState));
|
||||
|
@ -127,28 +137,43 @@ let WunderBar = React.createClass({
|
|||
this._stateBeforeSearch = this.state;
|
||||
this.setState(commonState);
|
||||
}
|
||||
},
|
||||
componentDidUpdate: function() {
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this._input.value = this.state.address;
|
||||
if (this._input && this._focusPending) {
|
||||
this._input.select();
|
||||
this._focusPending = false;
|
||||
}
|
||||
},
|
||||
onKeyPress: function(event) {
|
||||
}
|
||||
|
||||
onKeyPress(event) {
|
||||
if (event.charCode == 13 && this._input.value) {
|
||||
let uri = lbryuri.normalize(this._input.value);
|
||||
clearTimeout(this._userTypingTimer);
|
||||
this.props.onSubmit(uri);
|
||||
this.setState({ value: uri })
|
||||
|
||||
let uri = null,
|
||||
method = "onSubmit";
|
||||
|
||||
this._resetOnNextBlur = false;
|
||||
clearTimeout(this._userTypingTimer);
|
||||
|
||||
try {
|
||||
uri = lbryuri.normalize(this._input.value);
|
||||
this.setState({ value: uri });
|
||||
} catch (error) { //then it's not a valid URL, so let's search
|
||||
uri = this._input.value;
|
||||
method = "onSearch";
|
||||
}
|
||||
|
||||
this.props[method](uri);
|
||||
this._input.blur();
|
||||
}
|
||||
},
|
||||
onReceiveRef: function(ref) {
|
||||
}
|
||||
|
||||
onReceiveRef(ref) {
|
||||
this._input = ref;
|
||||
},
|
||||
render: function() {
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={'wunderbar' + (this.state.isActive ? ' wunderbar--active' : '')}>
|
||||
{this.state.icon ? <Icon fixed icon={this.state.icon} /> : '' }
|
||||
|
@ -163,7 +188,7 @@ let WunderBar = React.createClass({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export let SubHeader = React.createClass({
|
||||
render: function() {
|
||||
|
|
|
@ -139,12 +139,9 @@ lbry.setTitle = function(title) {
|
|||
|
||||
//kill this with proper routing
|
||||
lbry.back = function() {
|
||||
console.log(window.history);
|
||||
if (window.history.length > 1) {
|
||||
console.log('history exists, go back');
|
||||
window.history.back();
|
||||
} else {
|
||||
console.log('no history, reload');
|
||||
window.location.href = "?discover";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,12 @@ let SearchPage = React.createClass({
|
|||
},
|
||||
|
||||
isValidUri: function(query) {
|
||||
return true;
|
||||
try {
|
||||
lbryuri.parse(query);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
|
@ -148,7 +153,7 @@ let SearchPage = React.createClass({
|
|||
<section className="section-spaced">
|
||||
<h3 className="card-row__header">
|
||||
Search Results for {this.props.query}
|
||||
<ToolTip label="?" body="These search results are provided by LBRY, Inc." className="tooltip--header"/>
|
||||
<ToolTip label="?" body="These search results are provided by LBRY Inc." className="tooltip--header"/>
|
||||
</h3>
|
||||
<SearchResults query={this.props.query} />
|
||||
</section>
|
||||
|
|
|
@ -266,7 +266,6 @@ let ShowPage = React.createClass({
|
|||
delete channelUriObj.path;
|
||||
delete channelUriObj.contentName;
|
||||
const channelUri = this.state.signatureIsValid && this.state.hasSignature && channelUriObj.isChannel ? lbryuri.build(channelUriObj, false) : null;
|
||||
console.log(this.state);
|
||||
innerContent = <FilePage
|
||||
uri={this._uri}
|
||||
channelUri={channelUri}
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
@import "global";
|
||||
|
||||
html
|
||||
{
|
||||
height: 100%;
|
||||
font-size: $font-size;
|
||||
}
|
||||
body
|
||||
{
|
||||
font-family: 'Source Sans Pro', sans-serif;
|
||||
line-height: $font-line-height;
|
||||
}
|
||||
|
||||
#window
|
||||
{
|
||||
min-height: 100vh;
|
||||
background: $color-canvas;
|
||||
}
|
||||
|
||||
.badge
|
||||
{
|
||||
background: $color-money;
|
||||
display: inline-block;
|
||||
padding: 2px;
|
||||
color: white;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.credit-amount--indicator
|
||||
{
|
||||
font-weight: bold;
|
||||
color: $color-money;
|
||||
}
|
||||
|
||||
#main-content
|
||||
{
|
||||
padding: $spacing-vertical;
|
||||
margin-top: $height-header;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
main {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
main.main--single-column
|
||||
{
|
||||
width: $width-page-constrained;
|
||||
}
|
||||
}
|
|
@ -160,4 +160,35 @@ $blur-intensity: 8px;
|
|||
width:1px;
|
||||
height:1px;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
@mixin text-link($color: $color-primary, $hover-opacity: 0.70) {
|
||||
.icon
|
||||
{
|
||||
&:first-child {
|
||||
padding-right: 5px;
|
||||
}
|
||||
&:last-child:not(:only-child) {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.no-underline) {
|
||||
text-decoration: underline;
|
||||
.icon {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
&:hover
|
||||
{
|
||||
opacity: $hover-opacity;
|
||||
transition: opacity $transition-standard;
|
||||
text-decoration: underline;
|
||||
.icon {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
color: $color;
|
||||
cursor: pointer;
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
@import "global";
|
||||
|
||||
$gutter_fluid: 4;
|
||||
|
||||
[class*="span"] {
|
||||
min-height: 1px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.span12 { width: 100%; }
|
||||
.span11 { width: 91.666%; }
|
||||
.span10 { width: 83.333%; }
|
||||
.span9 { width: 75%; }
|
||||
.span8 { width: 66.666%; }
|
||||
.span7 { width: 58.333%; }
|
||||
.span6 { width: 50%; }
|
||||
.span5 { width: 41.666%; }
|
||||
.span4 { width: 33.333%; }
|
||||
.span3 { width: 25%; }
|
||||
.span2 { width: 16.666%; }
|
||||
.span1 { width: 8.333%; }
|
||||
|
||||
.row-fluid {
|
||||
width: 100%;
|
||||
> [class*="span"] {
|
||||
float: left;
|
||||
width: 100%;
|
||||
margin-left: 1% * $gutter_fluid;
|
||||
&:first-child
|
||||
{
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
$column_width: (100% - $gutter_fluid * 11) / 12;
|
||||
|
||||
> .span12 { width: $column_width * 12 + $gutter_fluid * 11; }
|
||||
> .span11 { width: $column_width * 11 + $gutter_fluid * 10; }
|
||||
> .span10 { width: $column_width * 10 + $gutter_fluid * 9; }
|
||||
> .span9 { width: $column_width * 9 + $gutter_fluid * 8; }
|
||||
> .span8 { width: $column_width * 8 + $gutter_fluid * 7; }
|
||||
> .span7 { width: $column_width * 7 + $gutter_fluid * 6; }
|
||||
> .span6 { width: $column_width * 6 + $gutter_fluid * 5; }
|
||||
> .span5 { width: $column_width * 5 + $gutter_fluid * 4; }
|
||||
> .span4 { width: $column_width * 4 + $gutter_fluid * 3; }
|
||||
> .span3 { width: $column_width * 3 + $gutter_fluid * 2; }
|
||||
> .span2 { width: $column_width * 2 + $gutter_fluid * 1; }
|
||||
> .span1 { width: $column_width; }
|
||||
}
|
||||
|
||||
.tile-fluid {
|
||||
width: 100%;
|
||||
> [class*="span"] {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.column-fluid {
|
||||
@include display-flex();
|
||||
flex-wrap: wrap;
|
||||
> [class*="span"] {
|
||||
@include display-flex();
|
||||
@include flex(1 0 auto);
|
||||
overflow: hidden;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.row-fluid, .tile-fluid {
|
||||
@include clearfix();
|
||||
}
|
||||
|
||||
@media (max-width: $mobile-width-threshold) {
|
||||
.row-fluid, .tile-fluid, .column-fluid {
|
||||
width: 100%;
|
||||
}
|
||||
.pull-left, .pull-right
|
||||
{
|
||||
float: none;
|
||||
}
|
||||
[class*="span"] {
|
||||
float: none !important;
|
||||
width: 100% !important;
|
||||
margin-left: 0 !important;
|
||||
display: block !important;
|
||||
}
|
||||
}
|
|
@ -1,35 +1,51 @@
|
|||
@import "global";
|
||||
|
||||
@mixin text-link($color: $color-primary, $hover-opacity: 0.70) {
|
||||
html
|
||||
{
|
||||
height: 100%;
|
||||
font-size: $font-size;
|
||||
}
|
||||
body
|
||||
{
|
||||
font-family: 'Source Sans Pro', sans-serif;
|
||||
line-height: $font-line-height;
|
||||
}
|
||||
|
||||
.icon
|
||||
#window
|
||||
{
|
||||
min-height: 100vh;
|
||||
background: $color-canvas;
|
||||
}
|
||||
|
||||
.badge
|
||||
{
|
||||
background: $color-money;
|
||||
display: inline-block;
|
||||
padding: 2px;
|
||||
color: white;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.credit-amount--indicator
|
||||
{
|
||||
font-weight: bold;
|
||||
color: $color-money;
|
||||
}
|
||||
|
||||
#main-content
|
||||
{
|
||||
padding: $spacing-vertical;
|
||||
margin-top: $height-header;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
main {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
main.main--single-column
|
||||
{
|
||||
&:first-child {
|
||||
padding-right: 5px;
|
||||
}
|
||||
&:last-child:not(:only-child) {
|
||||
padding-left: 5px;
|
||||
}
|
||||
width: $width-page-constrained;
|
||||
}
|
||||
|
||||
&:not(.no-underline) {
|
||||
text-decoration: underline;
|
||||
.icon {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
&:hover
|
||||
{
|
||||
opacity: $hover-opacity;
|
||||
transition: opacity $transition-standard;
|
||||
text-decoration: underline;
|
||||
.icon {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
color: $color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-fixed-width {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
@import "_reset";
|
||||
@import "_grid";
|
||||
@import "_icons";
|
||||
@import "_mediaelement";
|
||||
@import "_canvas";
|
||||
@import "_gui";
|
||||
@import "component/_table";
|
||||
@import "component/_button.scss";
|
||||
|
|
|
@ -1,37 +1,26 @@
|
|||
@import "../global";
|
||||
|
||||
$height-file-tile: $spacing-vertical * 8;
|
||||
$height-file-tile: $spacing-vertical * 6;
|
||||
.file-tile__row {
|
||||
overflow: hidden;
|
||||
height: $height-file-tile;
|
||||
.credit-amount {
|
||||
float: right;
|
||||
}
|
||||
//Hack! Remove below!
|
||||
.card__title-primary {
|
||||
margin-top: $spacing-vertical * 2/3;
|
||||
//also a hack
|
||||
.card__media {
|
||||
height: $height-file-tile;
|
||||
max-width: $height-file-tile;
|
||||
width: $height-file-tile;
|
||||
margin-right: $spacing-vertical / 2;
|
||||
float: left;
|
||||
}
|
||||
//basically everything here is a hack now
|
||||
.file-tile__content {
|
||||
padding-top: $spacing-vertical * 1/3;
|
||||
margin-left: $height-file-tile + $spacing-vertical / 2;
|
||||
}
|
||||
.card__title-primary {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.file-tile__thumbnail {
|
||||
max-width: 100%;
|
||||
max-height: $height-file-tile;
|
||||
vertical-align: middle;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.file-tile__thumbnail-container
|
||||
{
|
||||
height: $height-file-tile;
|
||||
@include absolute-center();
|
||||
}
|
||||
|
||||
.file-tile__title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.file-tile__description {
|
||||
color: #444;
|
||||
margin-top: 12px;
|
||||
font-size: 0.9em;
|
||||
}
|
Loading…
Reference in a new issue