diff --git a/.gitignore b/.gitignore index abf5d9683..976ffbfd4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ dist/css/* dist/js/* +node_modules .sass-cache +.idea diff --git a/dist/index.html b/dist/index.html index b103d9121..9dfc19fd1 100644 --- a/dist/index.html +++ b/dist/index.html @@ -22,6 +22,11 @@ - + + + + + + diff --git a/js/app.js b/js/app.js new file mode 100644 index 000000000..cca053012 --- /dev/null +++ b/js/app.js @@ -0,0 +1,36 @@ +var appStyles = { + width: '800px', + marginLeft: 'auto', + marginRight: 'auto', +}; +var App = React.createClass({ + getInitialState: function() { + return { + viewingPage: window.location.search === '?settings' ? 'settings' : 'home' + } + }, + componentDidMount: function() { + lbry.getStartNotice(function(notice) { + if (notice) { + alert(notice); + } + }); + }, + setPage: function(page) { + this.setState({ + viewingPage: page + }); + }, + render: function() { + if (this.state.viewingPage == 'home') { + var content = ; + } else if (this.state.viewingPage == 'settings') { + var content = ; + } + return ( +
+ {content} +
+ ); + } +}); \ No newline at end of file diff --git a/js/component/common.js b/js/component/common.js new file mode 100644 index 000000000..ba39b5239 --- /dev/null +++ b/js/component/common.js @@ -0,0 +1,47 @@ +//component/icon.js + +var Icon = React.createClass({ + render: function() { + var className = 'icon ' + this.props.icon; + return + } +}); + +var Link = React.createClass({ + render: function() { + console.log(this.props); + var href = this.props.href ? this.props.href : 'javascript:;', + icon = this.props.icon ? : '', + className = (this.props.button ? 'button-block button-' + this.props.button : 'button-text'); + return ( + + {this.props.icon ? icon : '' } + {this.props.label} + + ); + } +}); + +var creditAmountStyle = { + color: '#216C2A', + fontWeight: 'bold', + fontSize: '0.8em' +}, estimateStyle = { + marginLeft : '5px', + color: '#aaa', +}; + +var CreditAmount = React.createClass({ + propTypes: { + amount: React.PropTypes.number, + }, + render: function() { + var formattedAmount = lbry.formatCredits(this.props.amount); + return ( + + {formattedAmount} + { this.props.isEstimate ? (est) : null } + + ); + } +}); \ No newline at end of file diff --git a/js/component/splash.js b/js/component/splash.js new file mode 100644 index 000000000..b3cc102f6 --- /dev/null +++ b/js/component/splash.js @@ -0,0 +1,33 @@ +var splashStyle = { + color: 'white', + backgroundImage: 'url(' + lbry.imagePath('lbry-bg.png') + ')', + backgroundSize: 'cover', + minHeight: '100vh', + minWidth: '100vw', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center' +}, splashMessageStyle = { + marginTop: '24px' +}; + +var SplashScreen = React.createClass({ + propTypes: { + message: React.PropTypes.string, + }, + render: function() { + var imgSrc = lbry.imagePath('lbry-white-485x160.png'); + return ( +
+ LBRY +
+

+ {this.props.message} + +

+
+
+ ); + } +}); \ No newline at end of file diff --git a/js/gui.js b/js/gui.js index f0429b8bd..8dcd3804c 100644 --- a/js/gui.js +++ b/js/gui.js @@ -1,545 +1,4 @@ -//component/icon.js -var Icon = React.createClass({ - render: function() { - var className = 'icon ' + this.props.icon; - return - } -}); - -//component/link.js - -var Link = React.createClass({ - render: function() { - console.log(this.props); - var href = this.props.href ? this.props.href : 'javascript:;', - icon = this.props.icon ? : '', - className = (this.props.button ? 'button-block button-' + this.props.button : 'button-text') + - (this.props.fadeIn ? ' fade-in-link' : ''); - return ( - - {this.props.icon ? icon : '' } - {this.props.label} - - ); - } -}); - - -//component/splash.js -var splashStyle = { - color: 'white', - backgroundImage: 'url(' + lbry.imagePath('lbry-bg.png') + ')', - backgroundSize: 'cover', - minHeight: '100vh', - minWidth: '100vw', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center' -}, splashMessageStyle = { - marginTop: '24px' -}; - -var SplashScreen = React.createClass({ - propTypes: { - message: React.PropTypes.string, - }, - render: function() { - var imgSrc = lbry.imagePath('lbry-white-485x160.png'); - return ( -
- LBRY -
-

- {this.props.message} - -

-
-
- ); - } -}); - -//component/credit-amount.js -var creditAmountStyle = { - color: '#216C2A', - fontWeight: 'bold', - fontSize: '0.8em' -}, estimateStyle = { - marginLeft : '5px', - color: '#aaa', -}; - -var CreditAmount = React.createClass({ - propTypes: { - amount: React.PropTypes.number, - }, - render: function() { - var formattedAmount = lbry.formatCredits(this.props.amount); - return ( - - {formattedAmount} - { this.props.isEstimate ? (est) : null } - - ); - } -}); - -//component/header.js -var logoStyle = { - padding: '48px 12px', - textAlign: 'center', - maxHeight: '80px', -}, - balanceStyle = { -// float: 'right', - marginTop: '3px' -}, - imgStyle = { //@TODO: remove this, img should be properly scaled once size is settled - height: '80px' -}; - -var Header = React.createClass({ - render: function() { - return ( -
-
- -
-
- ); - } -}); - -var topBarStyle = { - 'float': 'right' -}; - -var TopBar = React.createClass({ - getInitialState: function() { - return { - balance: 0 - }; - }, - componentDidMount: function() { - lbry.getBalance(function(balance) { - this.setState({ - balance: balance - }); - }.bind(this)); - }, - - render: function() { - return ( - - - - - - - ); - } -}); - -var menuStyle = { - position: 'relative', - top: '3px', - marginLeft: '2px' -}, menuItemStyle = { - marginLeft: '3px' -}; - -var chooseSettingsPage = function() { - this.props.onPageChosen('settings'); -}; - -var Menu = React.createClass({ - render: function() { - return ( - - - - ) - } -}); - - -//component/discover.js - -var searchInputStyle = { - width: '400px', - display: 'block', - marginBottom: '48px', - marginLeft: 'auto', - marginRight: 'auto' -}, - fetchResultsStyle = { - color: '#888', - textAlign: 'center', - fontSize: '1.2em' -}; - -var SearchActive = React.createClass({ - render: function() { - return ( -
- Looking up the Dewey Decimals - -
- ); - } -}); - -var searchNoResultsStyle = { - textAlign: 'center' -}, searchNoResultsMessageStyle = { - fontStyle: 'italic', - marginRight: '5px' -}; - -var SearchNoResults = React.createClass({ - render: function() { - return ( -
- No one has checked anything in for {this.props.query} yet. - -
- ); - } -}); - -var SearchResults = React.createClass({ - render: function() { - var rows = []; - console.log('results'); - console.log('made it here'); - this.props.results.forEach(function(result) { - rows.push( - - ); - }); - console.log(this.props.results); - console.log(rows); - console.log('done'); - return ( -
{rows}
- ); - } -}); - -var searchRowImgStyle = { - maxHeight: '100px', - display: 'block', - marginLeft: 'auto', - marginRight: 'auto', - float: 'left' -}, - searchRowCostStyle = { - float: 'right', - marginLeft: '20px', - marginTop: '5px', - display: 'inline-block' -}, - searchRowNameStyle = { - fontSize: '0.9em', - color: '#666', - marginBottom: '24px', - clear: 'both' -}, - searchRowDescriptionStyle = { - color : '#444', - marginBottom: '24px', - fontSize: '0.9em' -}; - - -var SearchResultRow = React.createClass({ - render: function() { - var uri = 'lbry://' + this.props.name; - return ( -
-
- Photo for {this.props.title} -
-
- - - -

{this.props.title}

-
{uri}
-

{this.props.description}

-
- - -
-
-
- ); - } -}); - - -var discoverMainStyle = { - color: '#333' -}; - -var Discover = React.createClass({ - userTypingTimer: null, - - getInitialState: function() { - return { - results: [], - searching: false, - query: '' - }; - }, - - search: function() { - if (this.state.query) - { - console.log('search'); - lbry.search(this.state.query, this.searchCallback.bind(this, this.state.query)); - } - else - { - this.setState({ - searching: false, - results: [] - }); - } - }, - - searchCallback: function(originalQuery, results) { - if (this.state.searching) //could have canceled while results were pending, in which case nothing to do - { - this.setState({ - results: results, - searching: this.state.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() { - console.log(this.state); - return ( -
-
- { this.state.searching ? : null } - { !this.state.searching && this.state.results.length ? : null } - { !this.state.searching && !this.state.results.length && this.state.query ? : null } -
- ); - } -}); - -var HomePage = React.createClass({ - render: function() { - return ( -
-
- -
- ); - } -}); - -var settingsPageStyles = { - paddingTop: '60px', - fontSize: '16px', -}, settingsPageHeaderStyles = { - textAlign: 'center' -}, settingsBottomButtonsStyles = { - textAlign: 'center' -}, settingsSectionStyles = { - paddingBottom: '15px', -}, settingsQuestionStyles = { - display: 'block', -}, maxDownloadQuestionStyles = { - display: 'block', - paddingTop: '3px' -}, maxUploadQuestionStyles = { - display: 'block' -}, settingsRadioOptionStyles = { - display: 'block', - marginLeft: '13px' -}, settingsCheckBoxOptionStyles = { - display: 'block', - marginLeft: '13px' -}, settingsNumberFieldStyles = { - width: '40px' -}, downloadDirectoryLabelStyles = { - fontSize: '.9em', - marginLeft: '13px' -}, downloadDirectoryFieldStyles= { - width: '300px' -}; - -var SettingsPage = React.createClass({ - storeSetting: function(setting, val) { - var settings = Object.assign({}, this.state.settings); - settings[setting] = val; - this.setState({ - 'settings': settings - }); - }, - onRunOnStartChange: function (event) { - this.storeSetting('run_on_startup', event.target.checked); - }, - onShareDataChange: function (event) { - this.storeSetting('upload_log', event.target.checked); - }, - onDownloadDirChange: function(event) { - this.storeSetting('default_download_directory', event.target.value); - }, - onMaxUploadPrefChange: function(isLimited) { - if (!isLimited) { - this.storeSetting('max_upload', 0.0); - } - this.setState({ - isMaxUpload: isLimited - }); - }, - onMaxUploadFieldChange: function(event) { - this.storeSetting('max_upload', Number(event.target.value)); - }, - onMaxDownloadPrefChange: function(isLimited) { - if (!isLimited) { - this.storeSetting('max_download', 0.0); - } - this.setState({ - isMaxDownload: isLimited - }); - }, - onMaxDownloadFieldChange: function(event) { - this.storeSetting('max_download', Number(event.target.value)); - }, - getInitialState: function() { - return { - settings: null - } - }, - componentWillMount: function() { - lbry.getSettings(function(settings) { - this.setState({ - settings: settings, - isMaxUpload: settings.max_upload != 0, - isMaxDownload: settings.max_download != 0 - }); - }.bind(this)); - }, - onDone: function() { - lbry.setSettings(this.state.settings); - this.props.closeCallback(); - }, - render: function() { - if (!this.state.settings) { // If the settings aren't loaded yet, don't render anything. - return null; - } - - return ( -
-

Settings

-
-

Run on startup

- -
- -
-

Download directory

- Where would you like the files you download from LBRY to be saved? - -
-
-

Share diagnostic data

- -
-
-

Bandwidth limits

- How much of your upload bandwidth may LBRY use? - - - How much of your download bandwidth may LBRY use? - - -
-
- -
-
- ); - } -}); - - -var appStyles = { - width: '800px', - marginLeft: 'auto', - marginRight: 'auto', -}; -var App = React.createClass({ - getInitialState: function() { - return { - viewingPage: 'home' - } - }, - componentDidMount: function() { - lbry.getStartNotice(function(notice) { - if (notice) { - alert(notice); - } - }); - }, - handlePageChosen: function(page) { - this.setState({ - viewingPage: page - }); - }, - render: function() { - if (this.state.viewingPage == 'home') { - var content = ; - } else if (this.state.viewingPage == 'settings') { - var content = ; - } - return ( -
- - {content} -
- ); - } -}); //main.js var init = function() { @@ -555,5 +14,4 @@ var init = function() { }) }; -init(); - +init(); \ No newline at end of file diff --git a/js/page/home.js b/js/page/home.js new file mode 100644 index 000000000..bb0297aa8 --- /dev/null +++ b/js/page/home.js @@ -0,0 +1,248 @@ +var searchInputStyle = { + width: '400px', + display: 'block', + marginBottom: '48px', + marginLeft: 'auto', + marginRight: 'auto' + }, + fetchResultsStyle = { + color: '#888', + textAlign: 'center', + fontSize: '1.2em' + }; + +var SearchActive = React.createClass({ + render: function() { + return ( +
+ Looking up the Dewey Decimals + +
+ ); + } +}); + +var searchNoResultsStyle = { + textAlign: 'center' +}, searchNoResultsMessageStyle = { + fontStyle: 'italic', + marginRight: '5px' +}; + +var SearchNoResults = React.createClass({ + render: function() { + return ( +
+ No one has checked anything in for {this.props.query} yet. + +
+ ); + } +}); + +var SearchResults = React.createClass({ + render: function() { + var rows = []; + console.log('results'); + console.log('made it here'); + this.props.results.forEach(function(result) { + rows.push( + + ); + }); + console.log(this.props.results); + console.log(rows); + console.log('done'); + return ( +
{rows}
+ ); + } +}); + +var searchRowImgStyle = { + maxHeight: '100px', + display: 'block', + marginLeft: 'auto', + marginRight: 'auto', + float: 'left' + }, + searchRowCostStyle = { + float: 'right', + marginLeft: '20px', + marginTop: '5px', + display: 'inline-block' + }, + searchRowNameStyle = { + fontSize: '0.9em', + color: '#666', + marginBottom: '24px', + clear: 'both' + }, + searchRowDescriptionStyle = { + color : '#444', + marginBottom: '24px', + fontSize: '0.9em' + }; + + +var SearchResultRow = React.createClass({ + render: function() { + var uri = 'lbry://' + this.props.name; + return ( +
+
+ Photo for {this.props.title} +
+
+ + + +

{this.props.title}

+
{uri}
+

{this.props.description}

+
+ + +
+
+
+ ); + } +}); + + +var discoverMainStyle = { + color: '#333' +}; + +var Discover = React.createClass({ + userTypingTimer: null, + + getInitialState: function() { + return { + results: [], + searching: false, + query: '' + }; + }, + + search: function() { + if (this.state.query) + { + console.log('search'); + lbry.search(this.state.query, this.searchCallback.bind(this, this.state.query)); + } + else + { + this.setState({ + searching: false, + results: [] + }); + } + }, + + searchCallback: function(originalQuery, results) { + if (this.state.searching) //could have canceled while results were pending, in which case nothing to do + { + this.setState({ + results: results, + searching: this.state.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() { + console.log(this.state); + return ( +
+
+ { this.state.searching ? : null } + { !this.state.searching && this.state.results.length ? : null } + { !this.state.searching && !this.state.results.length && this.state.query ? : null } +
+ ); + } +}); + +var logoStyle = { + padding: '48px 12px', + textAlign: 'center', + maxHeight: '80px', + }, + imgStyle = { //@TODO: remove this, img should be properly scaled once size is settled + height: '80px' + }; + +var Header = React.createClass({ + render: function() { + return ( +
+ +
+ +
+
+ ); + } +}); + +var topBarStyle = { + 'float': 'right' +}, +balanceStyle = { + 'marginRight': '5px' +}; + +var TopBar = React.createClass({ + getInitialState: function() { + return { + balance: 0 + }; + }, + componentDidMount: function() { + lbry.getBalance(function(balance) { + this.setState({ + balance: balance + }); + }.bind(this)); + }, + + render: function() { + return ( + + + + + + + ); + } +}); + +var HomePage = React.createClass({ + render: function() { + return ( +
+
+ +
+ ); + } +}); \ No newline at end of file diff --git a/js/page/settings.js b/js/page/settings.js new file mode 100644 index 000000000..496b444b4 --- /dev/null +++ b/js/page/settings.js @@ -0,0 +1,121 @@ +var settingsRadioOptionStyles = { + display: 'block', + marginLeft: '13px' +}, settingsCheckBoxOptionStyles = { + display: 'block', + marginLeft: '13px' +}, settingsNumberFieldStyles = { + width: '40px' +}, downloadDirectoryLabelStyles = { + fontSize: '.9em', + marginLeft: '13px' +}, downloadDirectoryFieldStyles= { + width: '300px' +}; + +var SettingsPage = React.createClass({ + storeSetting: function(setting, val) { + var settings = Object.assign({}, this.state.settings); + settings[setting] = val; + this.setState({ + 'settings': settings + }); + lbry.setSettings(settings); + }, + onRunOnStartChange: function (event) { + this.storeSetting('run_on_startup', event.target.checked); + }, + onShareDataChange: function (event) { + this.storeSetting('upload_log', event.target.checked); + }, + onDownloadDirChange: function(event) { + this.storeSetting('default_download_directory', event.target.value); + }, + onMaxUploadPrefChange: function(isLimited) { + if (!isLimited) { + this.storeSetting('max_upload', 0.0); + } + this.setState({ + isMaxUpload: isLimited + }); + }, + onMaxUploadFieldChange: function(event) { + this.storeSetting('max_upload', Number(event.target.value)); + }, + onMaxDownloadPrefChange: function(isLimited) { + if (!isLimited) { + this.storeSetting('max_download', 0.0); + } + this.setState({ + isMaxDownload: isLimited + }); + }, + onMaxDownloadFieldChange: function(event) { + this.storeSetting('max_download', Number(event.target.value)); + }, + getInitialState: function() { + return { + settings: null + } + }, + componentWillMount: function() { + lbry.getSettings(function(settings) { + this.setState({ + settings: settings, + isMaxUpload: settings.max_upload != 0, + isMaxDownload: settings.max_download != 0 + }); + }.bind(this)); + }, + render: function() { + if (!this.state.settings) { // If the settings aren't loaded yet, don't render anything. + return null; + } + + return ( +
+

Settings

+
+

Run on startup

+ +
+
+

Download directory

+
Where would you like the files you download from LBRY to be saved?
+ +
+
+

Max Upload

+ + +
+
+

Max Download

+ + +
+
+

Share diagnostic data

+ +
+
+ +
+
+ ); + } +}); \ No newline at end of file diff --git a/scss/_global.scss b/scss/_global.scss index 8322a03a9..513fda53e 100644 --- a/scss/_global.scss +++ b/scss/_global.scss @@ -5,10 +5,11 @@ $spacing-vertical: 24px; $color-primary: #155B4A; $color-light-alt: hsl(hue($color-primary), 15, 85); $color-text-dark: #000; +$color-help: #666; $color-money: #216C2A; $color-meta-light: #505050; -$font-size: 18px; +$font-size: 16px; $mobile-width-threshold: 801px; $max-content-width: 1000px; diff --git a/scss/_gui.scss b/scss/_gui.scss index 6069efb43..07df70b4c 100644 --- a/scss/_gui.scss +++ b/scss/_gui.scss @@ -13,7 +13,16 @@ body position: relative; } -h1 { font-size: 2.0em; } +section +{ + margin-bottom: $spacing-vertical; + &:last-child + { + margin-bottom: 0; + } +} + +h1 { font-size: 2.0em; margin-bottom: $spacing-vertical / 2; margin-top: $spacing-vertical * 1.5; } h2 { font-size: 1.75em; } h3 { font-size: 1.4em; } h4 { font-size: 1.2em; } @@ -24,13 +33,11 @@ sup, sub { } sup { top: -0.4em; } sub { top: 0.4em; } +label { cursor: pointer; } -.fade-in-link { - opacity: 0.55; - transition: opacity .225s ease; - &:hover { - opacity: 1; - } +header +{ + line-height: $spacing-vertical; } .hidden { @@ -106,4 +113,21 @@ input[type="search"] { color: $color-primary; text-decoration: underline; + &:hover + { + opacity: 0.70; + transition: opacity .225s ease; + } +} + +.fade-in-link { + + &:hover { + opacity: 1; + } +} + +.help +{ + color: $color-help; }