Seed Support #56

Closed
ocnios wants to merge 173 commits from master into build
23 changed files with 272 additions and 275 deletions
Showing only changes of commit 71771aeb6f - Show all commits

View file

@ -244,51 +244,50 @@ var App = React.createClass({
return null;
}
},
getMainContent: function()
getContentAndAddress: function()
{
switch(this.state.viewingPage)
{
case 'settings':
return <SettingsPage />;
return ["Settings", "icon-gear", <SettingsPage />];
case 'help':
return <HelpPage />;
return ["Help", "icon-question", <HelpPage />];
case 'report':
return <ReportPage />;
return ['Report', 'icon-file', <ReportPage />];
case 'downloaded':
return <FileListDownloaded />;
return ["Downloads & Purchases", "icon-folder", <FileListDownloaded />];
case 'published':
return <FileListPublished />;
return ["Publishes", "icon-folder", <FileListPublished />];
case 'start':
return <StartPage />;
return ["Start", "icon-file", <StartPage />];
case 'rewards':
return <RewardsPage />;
return ["Rewards", "icon-bank", <RewardsPage />];
case 'wallet':
case 'send':
case 'receive':
return <WalletPage viewingPage={this.state.viewingPage} />;
return [this.state.viewingPage.charAt(0).toUpperCase() + this.state.viewingPage.slice(1), "icon-bank", <WalletPage viewingPage={this.state.viewingPage} />]
case 'show':
return <ShowPage uri={this.state.pageArgs} />;
return [this.state.pageArgs, "icon-file", <ShowPage uri={this.state.pageArgs} />];
case 'publish':
return <PublishPage />;
return ["Publish", "icon-upload", <PublishPage />];
case 'developer':
return <DeveloperPage />;
return ["Developer", "icon-file", <DeveloperPage />];
case 'discover':
default:
return <DiscoverPage showWelcome={this.state.justRegistered} {... this.state.pageArgs !== null ? {query: this.state.pageArgs} : {} } />;
return ["Home", "icon-home", <DiscoverPage showWelcome={this.state.justRegistered} {... this.state.pageArgs !== null ? {query: this.state.pageArgs} : {} } />];
}
},
render: function() {
var mainContent = this.getMainContent(),
headerLinks = this.getHeaderLinks(),
searchQuery = this.state.viewingPage == 'discover' && this.state.pageArgs ? this.state.pageArgs : '';
let [address, wunderBarIcon, mainContent] = this.getContentAndAddress(),
headerLinks = this.getHeaderLinks();
return (
this._fullScreenPages.includes(this.state.viewingPage) ?
mainContent :
<div id="window" className={ this.state.drawerOpen ? 'drawer-open' : 'drawer-closed' }>
<Drawer onCloseDrawer={this.closeDrawer} viewingPage={this.state.viewingPage} />
<div id="window">
<Header onOpenDrawer={this.openDrawer} address={address} wunderBarIcon={wunderBarIcon}
onSearch={this.onSearch} links={headerLinks} viewingPage={this.state.viewingPage} />
<div id="main-content" className={ headerLinks ? 'with-sub-nav' : 'no-sub-nav' }>
<Header onOpenDrawer={this.openDrawer} initialQuery={searchQuery} onSearch={this.onSearch} links={headerLinks} viewingPage={this.state.viewingPage} />
{mainContent}
</div>
<Modal isOpen={this.state.modal == 'upgrade'} contentLabel="Update available"

View file

@ -1,72 +1,135 @@
import React from 'react';
import {Link} from './link.js';
import {Icon} from './common.js';
import {Icon, CreditAmount} from './common.js';
var Header = React.createClass({
_balanceSubscribeId: null,
getInitialState: function() {
return {
title: "LBRY",
isScrolled: false
balance: 0
};
},
componentWillMount: function() {
new MutationObserver((mutations) => {
this.setState({ title: mutations[0].target.textContent });
}).observe(
document.querySelector('title'),
{ subtree: true, characterData: true, childList: true }
);
},
componentDidMount: function() {
document.addEventListener('scroll', this.handleScroll);
},
componentWillUnmount: function() {
document.removeEventListener('scroll', this.handleScroll);
if (this.userTypingTimer)
{
clearTimeout(this.userTypingTimer);
}
},
handleScroll: function() {
this.setState({
isScrolled: document.body.scrollTop > 0
this._balanceSubscribeId = lbry.balanceSubscribe((balance) => {
this.setState({ balance: balance });
});
},
onQueryChange: function(event) {
if (this.userTypingTimer)
{
clearTimeout(this.userTypingTimer);
componentWillUnmount: function() {
if (this._balanceSubscribeId) {
lbry.balanceUnsubscribe(this._balanceSubscribeId)
}
},
render: function() {
return <div>
<header id="header">
<div className="header__item">
<Link onClick={() => { history.back() }} button="alt button--flat" icon="icon-arrow-left" />
</div>
<div className="header__item">
<Link href="?discover" button="alt button--flat" icon="icon-home" />
</div>
<div className="header__item header__item--wunderbar">
<WunderBar address={this.props.address} icon={this.props.wunderBarIcon} onSearch={this.props.onSearch} />
</div>
<div className="header__item">
<Link href="?wallet" button="text" icon="icon-bank" label={lbry.formatCredits(this.state.balance, 1)} ></Link>
</div>
<div className="header__item">
<Link button="primary button--flat" href="?publish" icon="icon-upload" label="Publish" />
</div>
<div className="header__item">
<Link button="alt button--flat" href="?downloaded" icon="icon-folder" />
</div>
<div className="header__item">
<Link button="alt button--flat" href="?settings" icon="icon-gear" />
</div>
</header>
{this.props.links ?
<SubHeader links={this.props.links} viewingPage={this.props.viewingPage} /> :
''}
</div>
}
});
let WunderBar = React.createClass({
_userTypingTimer: null,
_input: null,
_stateBeforeSearch: null,
getInitialState: function() {
return {
address: this.props.address,
icon: this.props.icon
};
},
componentWillUnmount: function() {
if (this.userTypingTimer) {
clearTimeout(this._userTypingTimer);
}
},
onChange: function(event) {
if (this._userTypingTimer)
{
clearTimeout(this._userTypingTimer);
}
this.setState({ address: event.target.value })
//@TODO: Switch to React.js timing
var searchTerm = event.target.value;
this.userTypingTimer = setTimeout(() => {
this._userTypingTimer = setTimeout(() => {
this.props.onSearch(searchTerm);
}, 800); // 800ms delay, tweak for faster/slower
},
componentWillReceiveProps(nextProps) {
if (nextProps.address !== this.state.address || nextProps.icon !== this.state.icon) {
this.setState({ address: nextProps.address, icon: nextProps.icon });
}
},
onFocus: function() {
this._stateBeforeSearch = this.state;
let newState = {
icon: "icon-search"
}
// this._input.value = ""; //trigger placeholder
this._focusPending = true;
if (!this.state.address.match(/^lbry:\/\//)) //onFocus, if they are not on an exact URL, clear the bar
{
newState.address = "";
}
this.setState(newState);
},
onBlur: function() {
this.setState(this._stateBeforeSearch);
this._input.value = this.state.address;
},
componentDidUpdate: function() {
this._input.value = this.state.address;
if (this._input && this._focusPending) {
this._input.select();
this._focusPending = false;
}
},
onReceiveRef: function(ref) {
this._input = ref;
},
render: function() {
return (
<header id="header" className={ (this.state.isScrolled ? 'header-scrolled' : 'header-unscrolled') + ' ' + (this.props.links ? 'header-with-subnav' : 'header-no-subnav') }>
<div className="header-top-bar">
<Link onClick={this.props.onOpenDrawer} icon="icon-bars" className="open-drawer-link" />
<h1>{ this.state.title }</h1>
<div className="header-search">
<Icon icon="icon-search" />
<input type="search" onChange={this.onQueryChange} defaultValue={this.props.initialQuery}
placeholder="Find movies, music, games, and more"/>
</div>
</div>
{
this.props.links ?
<SubHeader links={this.props.links} viewingPage={this.props.viewingPage} /> :
''
}
</header>
);
return <div className="wunderbar">
{this.state.icon ? <Icon fixed icon={this.state.icon} /> : '' }
<input className="wunderbar__input" type="search" placeholder="Type a LBRY address or search term"
ref={this.onReceiveRef}
onFocus={this.onFocus}
onBlur={this.onBlur}
onChange={this.onChange}
value={ this.state.address }
placeholder="Find movies, music, games, and more" />
</div>
}
});
})
var SubHeader = React.createClass({
render: function() {

View file

@ -41,7 +41,7 @@ export let Link = React.createClass({
content = (
<span {... 'button' in this.props ? {className: 'button__content'} : {}}>
{'icon' in this.props ? <Icon icon={this.props.icon} fixed={true} /> : null}
{<span className="link-label">{this.props.label}</span>}
{this.props.label ? <span className="link-label">{this.props.label}</span> : null}
{'badge' in this.props ? <span className="badge">{this.props.badge}</span> : null}
</span>
);

View file

@ -627,18 +627,18 @@ lbry.claim_list_mine = function(params={}) {
}
lbry.resolve = function(params={}) {
const claimCacheKey = 'resolve_claim_cache',
claimCache = getSession(claimCacheKey, {})
const claimCacheKey = 'resolve_claim_cache3',
claimCache = getLocal(claimCacheKey, {})
return new Promise((resolve, reject) => {
if (!params.uri) {
throw "Resolve has hacked cache on top of it that requires a URI"
}
if (params.uri && claimCache[params.uri]) {
if (params.uri && claimCache[params.uri] !== undefined) {
resolve(claimCache[params.uri]);
} else {
lbry.call('resolve', params, function(data) {
claimCache[params.uri] = data;
setSession(claimCacheKey, claimCache)
setLocal(claimCacheKey, claimCache)
resolve(data)
}, reject)
}

View file

@ -7,10 +7,11 @@ const lbryio = {
_accessToken: getLocal('accessToken'),
_authenticationPromise: null,
_user : null,
enabled: true
enabled: false
};
const CONNECTION_STRING = process.env.LBRY_APP_API_URL ? process.env.LBRY_APP_API_URL : 'https://api.lbry.io/';
// const CONNECTION_STRING = process.env.LBRY_APP_API_URL ? process.env.LBRY_APP_API_URL : 'https://api.lbry.io/';
const CONNECTION_STRING = 'https://api.lbry.io/';
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
lbryio.getExchangeRates = function() {

View file

@ -164,7 +164,7 @@ var DiscoverPage = React.createClass({
},
componentWillMount: function() {
document.title = "Discover";
document.title = "Home";
if (this.props.query) {
// Rendering with a query already typed

View file

@ -19,7 +19,6 @@ export let FileListDownloaded = React.createClass({
},
componentDidMount: function() {
this._isMounted = true;
document.title = "Downloaded Files";
lbry.claim_list_mine().then((myClaimInfos) => {
if (!this._isMounted) { return; }

View file

@ -348,9 +348,6 @@ var PublishPage = React.createClass({
componentWillMount: function() {
this._updateChannelList();
},
componentDidMount: function() {
document.title = "Publish";
},
componentDidUpdate: function() {
},
onFileChange: function() {

View file

@ -17,7 +17,7 @@ var SettingsPage = React.createClass({
setClientSetting: function(name, value) {
lbry.setClientSetting(name, value)
this._onSettingSaveSuccess()
},
},
onRunOnStartChange: function (event) {
this.setDaemonSetting('run_on_startup', event.target.checked);
},

View file

@ -60,7 +60,6 @@ let ShowPage = React.createClass({
},
componentWillMount: function() {
this._uri = lbryuri.normalize(this.props.uri);
document.title = this._uri;
lbry.resolve({uri: this._uri}).then(({ claim: {txid, nout, has_signature, signature_is_valid, value: {stream: {metadata, source: {contentType}}}}}) => {
const outpoint = txid + ':' + nout;
@ -71,6 +70,8 @@ let ShowPage = React.createClass({
});
});
document.title = metadata.title ? metadata.title : this._uri;
this.setState({
outpoint: outpoint,
metadata: metadata,
@ -91,6 +92,7 @@ let ShowPage = React.createClass({
render: function() {
const metadata = this.state.metadata;
const title = metadata ? this.state.metadata.title : this._uri;
return (
<main className="constrained-page">
<section className="show-page-media">

View file

@ -5,9 +5,6 @@ var StartPage = React.createClass({
componentWillMount: function() {
lbry.stop();
},
componentDidMount: function() {
document.title = "LBRY is Closed";
},
render: function() {
return (
<main className="page">

View file

@ -270,9 +270,6 @@ var WalletPage = React.createClass({
propTypes: {
viewingPage: React.PropTypes.string,
},
componentDidMount: function() {
document.title = "My Wallet";
},
/*
Below should be refactored so that balance is shared all of wallet page. Or even broader?
What is the proper React pattern for sharing a global state like balance?

View file

@ -2,9 +2,9 @@
* Thin wrapper around localStorage.getItem(). Parses JSON and returns undefined if the value
* is not set yet.
*/
export function getLocal(key) {
export function getLocal(key, fallback=undefined) {
const itemRaw = localStorage.getItem(key);
return itemRaw === null ? undefined : JSON.parse(itemRaw);
return itemRaw === null ? fallback : JSON.parse(itemRaw);
}
/**

View file

@ -11,44 +11,12 @@ body
line-height: $font-line-height;
}
$drawer-width: 220px;
#drawer
#window
{
width: $drawer-width;
position: fixed;
min-height: 100vh;
left: 0;
top: 0;
background: $color-bg;
z-index: 3;
.drawer-item
{
display: block;
padding: $spacing-vertical / 2;
font-size: 1.2em;
height: $spacing-vertical * 1.5;
.icon
{
margin-right: 6px;
}
.link-label
{
line-height: $spacing-vertical * 1.5;
}
.badge
{
float: right;
margin-top: $spacing-vertical * 0.25 - 2;
background: $color-money;
}
}
.drawer-item-selected
{
background: $color-canvas;
color: $color-primary;
}
background: $color-canvas;
}
.badge
{
background: $color-money;
@ -62,119 +30,9 @@ $drawer-width: 220px;
font-weight: bold;
color: $color-money;
}
#drawer-handle
{
padding: $spacing-vertical / 2;
max-height: $height-header - $spacing-vertical;
text-align: center;
}
#window
{
position: relative; /*window has it's own z-index inside of it*/
z-index: 1;
}
#window.drawer-closed
{
#drawer { display: none }
}
#window.drawer-open
{
#main-content { margin-left: $drawer-width; }
.open-drawer-link { display: none }
#header { padding-left: $drawer-width + $spacing-vertical / 2; }
}
#header
{
background: $color-primary;
color: white;
&.header-no-subnav {
height: $height-header;
}
&.header-with-subnav {
height: $height-header * 2;
}
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 2;
box-sizing: border-box;
h1 { font-size: 1.8em; line-height: $height-header - $spacing-vertical; display: inline-block; float: left; }
&.header-scrolled
{
box-shadow: $default-box-shadow;
}
}
.header-top-bar
{
padding: $spacing-vertical / 2;
}
.header-search
{
margin-left: 60px;
$padding-adjust: 36px;
text-align: center;
.icon {
position: absolute;
top: $spacing-vertical * 1.5 / 2 + 2px; //hacked
margin-left: -$padding-adjust + 14px; //hacked
}
input[type="search"] {
position: relative;
left: -$padding-adjust;
background: rgba(255, 255, 255, 0.3);
color: white;
width: 400px;
height: $spacing-vertical * 1.5;
line-height: $spacing-vertical * 1.5;
padding-left: $padding-adjust + 3;
padding-right: 3px;
@include border-radius(2px);
@include placeholder-color(#e8e8e8);
&:focus {
box-shadow: $focus-box-shadow;
}
}
}
nav.sub-header
{
background: $color-primary;
text-transform: uppercase;
padding: $spacing-vertical / 2;
> a
{
$sub-header-selected-underline-height: 2px;
display: inline-block;
margin: 0 15px;
padding: 0 5px;
line-height: $height-header - $spacing-vertical - $sub-header-selected-underline-height;
color: #e8e8e8;
&:first-child
{
margin-left: 0;
}
&:last-child
{
margin-right: 0;
}
&.sub-header-selected
{
border-bottom: $sub-header-selected-underline-height solid #fff;
color: #fff;
}
&:hover
{
color: #fff;
}
}
}
#main-content
{
background: $color-canvas;
&.no-sub-nav
{
min-height: calc(100vh - 60px); //should be -$height-header, but I'm dumb I guess? It wouldn't work
@ -182,8 +40,7 @@ nav.sub-header
}
&.with-sub-nav
{
min-height: calc(100vh - 120px); //should be -$height-header, but I'm dumb I guess? It wouldn't work
main { margin-top: $height-header * 2; }
min-height: calc(100vh - 60px); //should be -$height-header, but I'm dumb I guess? It wouldn't work
}
main
{
@ -195,26 +52,4 @@ nav.sub-header
margin-right: auto;
}
}
}
$header-icon-size: 1.5em;
.open-drawer-link, .close-drawer-link
{
display: inline-block;
font-size: $header-icon-size;
padding: 2px 6px 0 6px;
float: left;
}
.close-lbry-link
{
font-size: $header-icon-size;
float: right;
padding: 0 6px 0 18px;
}
.full-screen
{
width: 100%;
height: 100%;
}

View file

@ -34,8 +34,8 @@ $height-header: $spacing-vertical * 2.5;
$height-button: $spacing-vertical * 1.5;
$height-video-embedded: $width-page-constrained * 9 / 16;
$default-box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
$focus-box-shadow: 2px 4px 4px 0 rgba(0,0,0,.14),2px 5px 3px -2px rgba(0,0,0,.2),2px 3px 7px 0 rgba(0,0,0,.12);
$box-shadow-layer: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
$box-shadow-focus: 2px 4px 4px 0 rgba(0,0,0,.14),2px 5px 3px -2px rgba(0,0,0,.2),2px 3px 7px 0 rgba(0,0,0,.12);
$transition-standard: .225s ease;

View file

@ -25,12 +25,6 @@
transform: translate(0, 0);
}
.icon-mega
{
font-size: 200px;
line-height: 1;
}
/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
readers do not read off random characters that represent icons */
.icon-glass:before {

View file

@ -10,6 +10,7 @@
@import "component/_file-actions.scss";
@import "component/_file-tile.scss";
@import "component/_form-field.scss";
@import "component/_header.scss";
@import "component/_menu.scss";
@import "component/_tooltip.scss";
@import "component/_load-screen.scss";

View file

@ -34,6 +34,11 @@ $button-focus-shift: 12%;
{
padding-left: 5px;
}
.icon:only-child
{
padding-left: 0;
padding-right: 0;
}
}
.button-block
{
@ -49,17 +54,17 @@ $button-focus-shift: 12%;
$color-button-text: white;
color: darken($color-button-text, $button-focus-shift * 0.5);
background-color: $color-primary;
box-shadow: $default-box-shadow;
box-shadow: $box-shadow-layer;
&:focus {
color: $color-button-text;
//box-shadow: $focus-box-shadow;
//box-shadow: $box-shadow-focus;
background-color: mix(black, $color-primary, $button-focus-shift)
}
}
.button-alt
{
background-color: $color-bg-alt;
box-shadow: $default-box-shadow;
box-shadow: $box-shadow-layer;
}
.button-text
@ -76,3 +81,7 @@ $button-focus-shift: 12%;
@include text-link(#aaa);
font-size: 0.8em;
}
.button--flat
{
box-shadow: none !important;
}

View file

@ -7,7 +7,7 @@ $padding-card-horizontal: $spacing-vertical * 2/3;
margin-right: auto;
max-width: $width-page-constrained;
background: $color-bg;
box-shadow: $default-box-shadow;
box-shadow: $box-shadow-layer;
border-radius: 2px;
margin-bottom: $spacing-vertical * 2/3;
overflow: auto;
@ -86,7 +86,7 @@ $card-link-scaling: 1.1;
.card--link:hover {
position: relative;
z-index: 1;
box-shadow: $focus-box-shadow;
box-shadow: $box-shadow-focus;
transform: scale($card-link-scaling);
transform-origin: 50% 50%;
overflow-x: visible;

View file

@ -0,0 +1,103 @@
@import "../global";
$color-header: #666;
$header-icon-size: 1.5em;
.open-drawer-link, .close-drawer-link
{
display: inline-block;
font-size: $header-icon-size;
padding: 2px 6px 0 6px;
float: left;
}
#header
{
color: $color-header;
background: #fff;
display: flex;
/*
&.header-no-subnav {
height: $height-header;
}
&.header-with-subnav {
height: $height-header * 2;
}*/
position: fixed;
box-shadow: $box-shadow-layer;
top: 0;
left: 0;
width: 100%;
z-index: 2;
padding: $spacing-vertical / 2;
box-sizing: border-box;
}
.header__item {
flex: 0 0 content;
padding-left: $spacing-vertical / 4;
padding-right: $spacing-vertical / 4;
}
.header__item--wunderbar {
flex-grow: 1;
}
.wunderbar
{
position: relative;
.icon {
position: absolute;
left: 10px;
top: $spacing-vertical / 2 - 4px; //hacked
}
}
.wunderbar__input {
background: rgba(255, 255, 255, 0.7);
width: 100%;
color: $color-header;
height: $spacing-vertical * 1.5;
line-height: $spacing-vertical * 1.5;
padding-left: 38px;
padding-right: 5px;
border: 1px solid $color-text-dark;
@include border-radius(2px);
border: 1px solid #ccc;
&:focus {
box-shadow: $box-shadow-focus;
border-color: $color-header;
}
}
nav.sub-header
{
text-transform: uppercase;
padding: $spacing-vertical / 2;
> a
{
$sub-header-selected-underline-height: 2px;
display: inline-block;
margin: 0 15px;
padding: 0 5px;
line-height: $height-header - $spacing-vertical - $sub-header-selected-underline-height;
color: $color-header;
&:first-child
{
margin-left: 0;
}
&:last-child
{
margin-right: 0;
}
$color-selected-subheader: darken($color-header, 20%);
&.sub-header-selected
{
border-bottom: $sub-header-selected-underline-height solid $color-selected-subheader;
color: $color-selected-subheader;
}
&:hover
{
color: $color-selected-subheader;
}
}
}

View file

@ -10,7 +10,7 @@ $border-radius-menu: 2px;
position: absolute;
white-space: nowrap;
background-color: white;
box-shadow: $default-box-shadow;
box-shadow: $box-shadow-layer;
border-radius: $border-radius-menu;
padding-top: ($spacing-vertical / 5) 0px;
z-index: 1;

View file

@ -29,7 +29,7 @@
overflow: auto;
border-radius: 4px;
padding: $spacing-vertical;
box-shadow: $default-box-shadow;
box-shadow: $box-shadow-layer;
max-width: 400px;
}

View file

@ -24,7 +24,7 @@
background-color: $color-bg;
font-size: $font-size * 7/8;
line-height: $font-line-height;
box-shadow: $default-box-shadow;
box-shadow: $box-shadow-layer;
}
.tooltip--header .tooltip__link {