diff --git a/dist/index.html b/dist/index.html index 1a17db887..79c4e9bc2 100644 --- a/dist/index.html +++ b/dist/index.html @@ -20,6 +20,8 @@
+ + diff --git a/js/app.js b/js/app.js index 6055bcab1..19c97994e 100644 --- a/js/app.js +++ b/js/app.js @@ -13,6 +13,7 @@ import DetailPage from './page/show.js'; import PublishPage from './page/publish.js'; import DiscoverPage from './page/discover.js'; import SplashScreen from './component/splash.js'; +import DeveloperPage from './page/developer.js'; import Drawer from './component/drawer.js'; import Header from './component/header.js'; import Modal from './component/modal.js'; @@ -39,7 +40,7 @@ var App = React.createClass({ return { viewingPage: viewingPage, drawerOpen: drawerOpenRaw !== null ? JSON.parse(drawerOpenRaw) : true, - pageArgs: val, + pageArgs: typeof val !== 'undefined' ? val : null, errorInfo: null, modal: null, startNotice: null, @@ -191,9 +192,11 @@ var App = React.createClass({ return ; case 'publish': return ; + case 'developer': + return ; case 'discover': default: - return ; + return ; } }, render: function() { diff --git a/js/lbry.js b/js/lbry.js index ca9fde1e5..5dbf338ae 100644 --- a/js/lbry.js +++ b/js/lbry.js @@ -10,6 +10,9 @@ var lbry = { }, defaultClientSettings: { showNsfw: false, + debug: false, + useCustomLighthouseServers: false, + customLighthouseServers: [], } }; @@ -383,10 +386,7 @@ lbry.getSessionInfo = function(callback) { } lbry.reportBug = function(message, callback) { - lbry.call('upload_log', { - name_prefix: 'report', - exclude_previous: false, - force: true, + lbry.call('report_bug', { message: message }, callback); } diff --git a/js/lighthouse.js b/js/lighthouse.js index 0a5de3152..6d349a3a8 100644 --- a/js/lighthouse.js +++ b/js/lighthouse.js @@ -1,8 +1,8 @@ import lbry from './lbry.js'; var lighthouse = { - _search_timeout: 5000, - _max_search_tries: 5, + _query_timeout: 5000, + _max_query_tries: 5, servers: [ 'http://lighthouse4.lbry.io:50005', @@ -12,37 +12,56 @@ var lighthouse = { path: '/', call: function(method, params, callback, errorCallback, connectFailedCallback, timeout) { - lbry.jsonrpc_call(this.server + this.path, method, params, callback, errorCallback, connectFailedCallback, timeout); - }, - - search: function(query, callback, errorCallback, connectFailedCallback, timeout) { - let handleSearchFailed = function(tryNum=0) { - if (tryNum > lighthouse._max_search_tries) { + const handleConnectFailed = function(tryNum=0) { + if (tryNum > lighthouse._max_query_tries) { if (connectFailedCallback) { connectFailedCallback(); } else { throw new Error(`Could not connect to Lighthouse server. Last server attempted: ${lighthouse.server}`); } } else { - // Randomly choose one of the other search servers to switch to - let otherServers = lighthouse.servers.slice(); - otherServers.splice(otherServers.indexOf(lighthouse.server), 1); - lighthouse.server = otherServers[Math.round(Math.random() * (otherServers.length - 1))]; - - lighthouse.call('search', [query], callback, errorCallback, function() { - handleSearchFailed(tryNum + 1); - }, lighthouse._search_timeout); + lbry.call(method, params, callback, errorCallback, () => { handleConnectFailed(tryNum + 1) }, timeout); } } - lighthouse.call('search', [query], callback, errorCallback, function() { handleSearchFailed() }, lighthouse._search_timeout); + // Set the Lighthouse server if it hasn't been set yet, or if the current server is not in + // the current set of servers (most likely because of a settings change). + if (typeof lighthouse.server === 'undefined' || lighthouse.getServers().indexOf(lighthouse.server) == -1) { + lighthouse.chooseNewServer(); + } + + lbry.jsonrpc_call(this.server + this.path, method, params, callback, errorCallback, + () => { handleConnectFailed() }, timeout || lighthouse.query_timeout); + }, + + getServers: function() { + return lbry.getClientSetting('useCustomLighthouseServers') + ? lbry.getClientSetting('customLighthouseServers') + : lighthouse.servers; + }, + + chooseNewServer: function() { + // Randomly choose a new Lighthouse server and switch to it. If a server is already set, this + // will choose a different one (used for switching servers after a failed query). + const servers = lighthouse.getServers(); + let newServerChoices; + if (!lighthouse.server) { + newServerChoices = servers; + } else { + newServerChoices = lighthouse.getServers().slice(); + newServerChoices.splice(newServerChoices.indexOf(lighthouse.server), 1); + } + lighthouse.server = newServerChoices[Math.round(Math.random() * (newServerChoices.length - 1))]; + }, + + search: function(query, callback, errorCallback, connectFailedCallback, timeout) { + lighthouse.call('search', [query], callback, errorCallback, connectFailedCallback, timeout); }, getSizeForName: function(name, callback, errorCallback, connectFailedCallback, timeout) { - return lighthouse.call('get_size_for_name', [name], callback, errorCallback, connectFailedCallback, timeout); + lighthouse.call('get_size_for_name', [name], callback, errorCallback, connectFailedCallback, timeout); } }; -lighthouse.server = lighthouse.servers[Math.round(Math.random() * (lighthouse.servers.length - 1))]; export default lighthouse; diff --git a/js/main.js b/js/main.js index 8ff6853a8..d39fc84a5 100644 --- a/js/main.js +++ b/js/main.js @@ -1,11 +1,17 @@ import React from 'react'; import ReactDOM from 'react-dom'; import lbry from './lbry.js'; +import lighthouse from './lighthouse.js'; import App from './app.js'; import SplashScreen from './component/splash.js'; var init = function() { + if (lbry.getClientSetting('debug')) { + window.lbry = lbry; + window.lighthouse = lighthouse; + } + var canvas = document.getElementById('canvas'); ReactDOM.render( diff --git a/js/page/developer.js b/js/page/developer.js new file mode 100644 index 000000000..447418191 --- /dev/null +++ b/js/page/developer.js @@ -0,0 +1,56 @@ +import lbry from '../lbry.js'; +import React from 'react'; +import FormField from '../component/form.js'; + +const DeveloperPage = React.createClass({ + getInitialState: function() { + return { + debugMode: lbry.getClientSetting('debug'), + useCustomLighthouseServers: lbry.getClientSetting('useCustomLighthouseServers'), + customLighthouseServers: lbry.getClientSetting('customLighthouseServers').join('\n'), + }; + }, + handleDebugModeChange: function(event) { + lbry.setClientSetting('debug', event.target.checked); + this.setState({ + debugMode: event.target.checked, + }); + }, + handleUseCustomLighthouseServersChange: function(event) { + lbry.setClientSetting('useCustomLighthouseServers', event.target.checked); + this.setState({ + useCustomLighthouseServers: event.target.checked, + }); + }, + handleCustomLighthouseServersChange: function(event) { + lbry.setClientSetting('customLighthouseServers', event.target.value.trim().split('\n')); + this.setState({ + customLighthouseServers: event.target.value, + }); + }, + render: function() { + return ( +
+
+

Developer Settings

+
+ +
+
+ +
+ {this.state.useCustomLighthouseServers + ?
+ +
+ : null} +
+
+ ); + } +}); + +export default DeveloperPage; diff --git a/js/page/discover.js b/js/page/discover.js index 77633ca84..acd126925 100644 --- a/js/page/discover.js +++ b/js/page/discover.js @@ -2,7 +2,7 @@ import React from 'react'; import lbry from '../lbry.js'; import lighthouse from '../lighthouse.js'; import {Link, ToolTipLink, DownloadLink, WatchLink} from '../component/link.js'; -import {Thumbnail, CreditAmount, TruncatedText} from '../component/common.js'; +import {Thumbnail, CreditAmount, TruncatedText, BusyMessage} from '../component/common.js'; var fetchResultsStyle = { color: '#888', @@ -268,20 +268,27 @@ var DiscoverPage = React.createClass({ componentDidUpdate: function() { if (this.props.query != this.state.query) { - this.handleSearchChanged(); + this.handleSearchChanged(this.props.query); } }, - handleSearchChanged: function() { - this.setState({ - searching: true, - query: this.props.query, - }); - - lighthouse.search(this.props.query, this.searchCallback); + componentWillReceiveProps: function(nextProps, nextState) { + if (nextProps.query != nextState.query) + { + this.handleSearchChanged(nextProps.query); + } }, - componentDidMount: function() { + handleSearchChanged: function(query) { + this.setState({ + searching: true, + query: query, + }); + + lighthouse.search(query, this.searchCallback); + }, + + componentWillMount: function() { document.title = "Discover"; if (this.props.query) { // Rendering with a query already typed @@ -293,7 +300,7 @@ var DiscoverPage = React.createClass({ return { results: [], query: this.props.query, - searching: this.props.query && this.props.query.length > 0 + searching: ('query' in this.props) && (this.props.query.length > 0) }; }, diff --git a/js/page/help.js b/js/page/help.js index 75eca13ee..9a492113f 100644 --- a/js/page/help.js +++ b/js/page/help.js @@ -27,7 +27,7 @@ var HelpPage = React.createClass({ document.title = "Help"; }, render: function() { - let ver, osName, platform, newVerLink; + let ver, osName, platform, newVerLink, uiVersion; if (this.state.versionInfo) { ver = this.state.versionInfo; @@ -43,6 +43,12 @@ var HelpPage = React.createClass({ platform = `Windows (${ver.platform})`; newVerLink = 'https://lbry.io/get/lbry.msi'; } + + if (ver.ui_version == 'user-specified') { + uiVersion = '(User specified)'; + } else { + uiVersion = ver.ui_version || '(Unknown)'; + } } else { ver = null; } @@ -86,6 +92,10 @@ var HelpPage = React.createClass({ lbryum (wallet) {ver.lbryum_version} + + lbry-web-ui (interface) + {uiVersion} + Platform {platform} diff --git a/js/page/my_files.js b/js/page/my_files.js index 58f662c55..fb55a7f23 100644 --- a/js/page/my_files.js +++ b/js/page/my_files.js @@ -175,7 +175,6 @@ var MyFilesPage = React.createClass({ }, title: function(filesInfo) { return filesInfo.sort(function(a, b) { - console.log('in title sort. a is', a, '; b is', b) return ((a.metadata ? a.metadata.title.toLowerCase() : a.name) > (b.metadata ? b.metadata.title.toLowerCase() : b.name)); }); @@ -236,15 +235,9 @@ var MyFilesPage = React.createClass({ clearTimeout(this._fileTimeout); } }, - setFilesInfo: function(filesInfo) { - this.setState({ - filesInfo: this._sortFunctions[this.state.sortBy](filesInfo), - }); - }, handleSortChanged: function(event) { this.setState({ sortBy: event.target.value, - filesInfo: this._sortFunctions[event.target.value](this.state.filesInfo), }); }, updateFilesInfo: function() { @@ -253,17 +246,29 @@ var MyFilesPage = React.createClass({ if (this.props.show == 'published') { // We're in the Published tab, so populate this.state.filesInfo with data from the user's claims lbry.getMyClaims((claimsInfo) => { - let newFilesInfo = []; + /** + * Build newFilesInfo as a sparse array and drop elements in at the same position they + * occur in claimsInfo, so the order is preserved even if the API calls inside this loop + * return out of order. + */ + + let newFilesInfo = Array(claimsInfo.length); let claimInfoProcessedCount = 0; - for (let claimInfo of claimsInfo) { + for (let [i, claimInfo] of claimsInfo.entries()) { let metadata = JSON.parse(claimInfo.value); lbry.getFileInfoBySdHash(metadata.sources.lbry_sd_hash, (fileInfo) => { claimInfoProcessedCount++; if (fileInfo !== false) { - newFilesInfo.push(fileInfo); + newFilesInfo[i] = fileInfo; } if (claimInfoProcessedCount >= claimsInfo.length) { - this.setFilesInfo(newFilesInfo); + /** + * newFilesInfo may have gaps from claims that don't have associated files in + * lbrynet, so filter out any missing elements + */ + this.setState({ + filesInfo: newFilesInfo.filter(function() { return true }), + }); this._fileTimeout = setTimeout(() => { this.updateFilesInfo() }, 1000); } @@ -274,9 +279,11 @@ var MyFilesPage = React.createClass({ // We're in the Downloaded tab, so populate this.state.filesInfo with files the user has in // lbrynet, with published files filtered out. lbry.getFilesInfo((filesInfo) => { - this.setFilesInfo(filesInfo.filter(({sd_hash}) => { - return this.state.publishedFilesSdHashes.indexOf(sd_hash) == -1; - })); + this.setState({ + filesInfo: filesInfo.filter(({sd_hash}) => { + return this.state.publishedFilesSdHashes.indexOf(sd_hash) == -1; + }), + }); let newFilesAvailable; if (!(this._fileInfoCheckNum % this._fileInfoCheckRate)) { @@ -320,7 +327,8 @@ var MyFilesPage = React.createClass({ var content = [], seenUris = {}; - for (let fileInfo of this.state.filesInfo) { + const filesInfoSorted = this._sortFunctions[this.state.sortBy](this.state.filesInfo); + for (let fileInfo of filesInfoSorted) { let {completed, written_bytes, total_bytes, lbry_uri, file_name, download_path, stopped, metadata, sd_hash} = fileInfo; diff --git a/js/page/report.js b/js/page/report.js index 72eedf1a1..6cac655a7 100644 --- a/js/page/report.js +++ b/js/page/report.js @@ -1,4 +1,6 @@ import React from 'react'; +import {Link} from '../component/link.js'; +import Modal from '../component/modal.js'; import lbry from '../lbry.js'; var ReportPage = React.createClass({ diff --git a/js/page/watch.js b/js/page/watch.js index 2bc3580e7..c0bc2b67c 100644 --- a/js/page/watch.js +++ b/js/page/watch.js @@ -1,8 +1,6 @@ import React from 'react'; import lbry from '../lbry.js'; import LoadScreen from '../component/load_screen.js' -import MediaElementPlayer from 'mediaelement'; - var WatchPage = React.createClass({ propTypes: { diff --git a/scss/_gui.scss b/scss/_gui.scss index e533b8504..965d2eb9e 100644 --- a/scss/_gui.scss +++ b/scss/_gui.scss @@ -365,4 +365,5 @@ input[type="text"], input[type="search"] .download-started-modal__file-path { word-break: break-all; -} \ No newline at end of file +} + diff --git a/scss/all.scss b/scss/all.scss index cbaf6d901..e02fd6d3d 100644 --- a/scss/all.scss +++ b/scss/all.scss @@ -4,4 +4,5 @@ @import "_mediaelement"; @import "_canvas"; @import "_table"; -@import "_gui"; \ No newline at end of file +@import "_gui"; +@import "page/_developer.scss"; \ No newline at end of file diff --git a/scss/page/_developer.scss b/scss/page/_developer.scss new file mode 100644 index 000000000..1a3a118d6 --- /dev/null +++ b/scss/page/_developer.scss @@ -0,0 +1,5 @@ +.developer-page__custom-lighthouse-servers { + font: 0.8em monospace; + width: 30em; + height: 10em; +} \ No newline at end of file