Redux #115
43 changed files with 1471 additions and 565 deletions
|
@ -1,5 +1,8 @@
|
||||||
const {app, BrowserWindow, ipcMain} = require('electron');
|
const {app, BrowserWindow, ipcMain} = require('electron');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
|
|
||||||
|
require('electron-debug')({showDevTools: true});
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const jayson = require('jayson');
|
const jayson = require('jayson');
|
||||||
const semver = require('semver');
|
const semver = require('semver');
|
||||||
|
|
|
@ -41,7 +41,8 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^1.4.15",
|
"electron": "^1.4.15",
|
||||||
"electron-builder": "^11.7.0"
|
"electron-builder": "^11.7.0",
|
||||||
|
"electron-debug": "^1.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
|
206
ui/js/actions/app.js
Normal file
206
ui/js/actions/app.js
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
import * as types from 'constants/action_types'
|
||||||
|
import lbry from 'lbry'
|
||||||
|
import {
|
||||||
|
selectUpdateUrl,
|
||||||
|
selectUpgradeDownloadDir,
|
||||||
|
selectUpgradeDownloadItem,
|
||||||
|
selectUpgradeFilename,
|
||||||
|
} from 'selectors/app'
|
||||||
|
|
||||||
|
const {remote, ipcRenderer, shell} = require('electron');
|
||||||
|
const path = require('path');
|
||||||
|
const app = require('electron').remote.app;
|
||||||
|
const {download} = remote.require('electron-dl');
|
||||||
|
const fs = remote.require('fs');
|
||||||
|
|
||||||
|
export function doNavigate(path) {
|
||||||
|
return {
|
||||||
|
type: types.NAVIGATE,
|
||||||
|
data: {
|
||||||
|
path: path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doLogoClick() {
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doOpenDrawer() {
|
||||||
|
return {
|
||||||
|
type: types.OPEN_DRAWER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doCloseDrawer() {
|
||||||
|
return {
|
||||||
|
type: types.CLOSE_DRAWER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doOpenModal(modal) {
|
||||||
|
return {
|
||||||
|
type: types.OPEN_MODAL,
|
||||||
|
data: {
|
||||||
|
modal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doCloseModal() {
|
||||||
|
return {
|
||||||
|
type: types.CLOSE_MODAL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doUpdateBalance(balance) {
|
||||||
|
return {
|
||||||
|
type: types.UPDATE_BALANCE,
|
||||||
|
data: {
|
||||||
|
balance: balance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doUpdateDownloadProgress(percent) {
|
||||||
|
return {
|
||||||
|
type: types.UPGRADE_DOWNLOAD_PROGRESSED,
|
||||||
|
data: {
|
||||||
|
percent: percent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doSkipUpgrade() {
|
||||||
|
return {
|
||||||
|
type: types.SKIP_UPGRADE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doStartUpgrade() {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const state = getState()
|
||||||
|
const upgradeDownloadPath = selectUpgradeDownloadDir(state)
|
||||||
|
|
||||||
|
ipcRenderer.send('upgrade', upgradeDownloadPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doDownloadUpgrade() {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const state = getState()
|
||||||
|
// Make a new directory within temp directory so the filename is guaranteed to be available
|
||||||
|
const dir = fs.mkdtempSync(app.getPath('temp') + require('path').sep);
|
||||||
|
const upgradeFilename = selectUpgradeFilename(state)
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
onProgress: (p) => dispatch(doUpdateDownloadProgress(Math.round(p * 100))),
|
||||||
|
directory: dir,
|
||||||
|
};
|
||||||
|
download(remote.getCurrentWindow(), selectUpdateUrl(state), options)
|
||||||
|
.then(downloadItem => {
|
||||||
|
/**
|
||||||
|
* TODO: get the download path directly from the download object. It should just be
|
||||||
|
* downloadItem.getSavePath(), but the copy on the main process is being garbage collected
|
||||||
|
* too soon.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const _upgradeDownloadItem = downloadItem;
|
||||||
|
const _upgradeDownloadPath = path.join(dir, upgradeFilename);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: types.UPGRADE_DOWNLOAD_COMPLETED,
|
||||||
|
data: {
|
||||||
|
dir,
|
||||||
|
downloadItem
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: types.UPGRADE_DOWNLOAD_STARTED
|
||||||
|
})
|
||||||
|
dispatch({
|
||||||
|
type: types.OPEN_MODAL,
|
||||||
|
data: {
|
||||||
|
modal: 'downloading'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doCancelUpgrade() {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const state = getState()
|
||||||
|
const upgradeDownloadItem = selectUpgradeDownloadItem(state)
|
||||||
|
|
||||||
|
if (upgradeDownloadItem) {
|
||||||
|
/*
|
||||||
|
* Right now the remote reference to the download item gets garbage collected as soon as the
|
||||||
|
* the download is over (maybe even earlier), so trying to cancel a finished download may
|
||||||
|
* throw an error.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
upgradeDownloadItem.cancel();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ type: types.UPGRADE_CANCELLED })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doCheckUpgradeAvailable() {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const state = getState()
|
||||||
|
|
||||||
|
lbry.checkNewVersionAvailable(({isAvailable}) => {
|
||||||
|
if (!isAvailable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbry.getVersionInfo((versionInfo) => {
|
||||||
|
dispatch({
|
||||||
|
type: types.UPDATE_VERSION,
|
||||||
|
data: {
|
||||||
|
version: versionInfo.lbrynet_version
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dispatch({
|
||||||
|
type: types.OPEN_MODAL,
|
||||||
|
data: {
|
||||||
|
modal: 'upgrade'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doAlertError(errorList) {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const state = getState()
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: types.OPEN_MODAL,
|
||||||
|
data: {
|
||||||
|
modal: 'error',
|
||||||
|
error: errorList
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doSearch(term) {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const state = getState()
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: types.START_SEARCH,
|
||||||
|
data: {
|
||||||
|
searchTerm: term
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
651
ui/js/app.js
651
ui/js/app.js
|
@ -1,321 +1,334 @@
|
||||||
import React from 'react';
|
import store from 'store.js';
|
||||||
import {Line} from 'rc-progress';
|
|
||||||
|
|
||||||
import lbry from './lbry.js';
|
const env = process.env.NODE_ENV || 'development';
|
||||||
import SettingsPage from './page/settings.js';
|
const config = require(`./config/${env}`);
|
||||||
import HelpPage from './page/help.js';
|
const logs = [];
|
||||||
import WatchPage from './page/watch.js';
|
const app = {
|
||||||
import ReportPage from './page/report.js';
|
env: env,
|
||||||
import StartPage from './page/start.js';
|
config: config,
|
||||||
import RewardsPage from './page/rewards.js';
|
store: store,
|
||||||
import RewardPage from './page/reward.js';
|
logs: logs,
|
||||||
import WalletPage from './page/wallet.js';
|
log: function(message) {
|
||||||
import ShowPage from './page/show.js';
|
console.log(message);
|
||||||
import PublishPage from './page/publish.js';
|
logs.push(message);
|
||||||
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';
|
|
||||||
import {Link} from './component/link.js';
|
|
||||||
|
|
||||||
|
|
||||||
const {remote, ipcRenderer, shell} = require('electron');
|
|
||||||
const {download} = remote.require('electron-dl');
|
|
||||||
const path = require('path');
|
|
||||||
const app = require('electron').remote.app;
|
|
||||||
const fs = remote.require('fs');
|
|
||||||
|
|
||||||
|
|
||||||
var App = React.createClass({
|
|
||||||
_error_key_labels: {
|
|
||||||
connectionString: 'API connection string',
|
|
||||||
method: 'Method',
|
|
||||||
params: 'Parameters',
|
|
||||||
code: 'Error code',
|
|
||||||
message: 'Error message',
|
|
||||||
data: 'Error data',
|
|
||||||
},
|
|
||||||
_fullScreenPages: ['watch'],
|
|
||||||
_storeHistoryOfNextRender: false,
|
|
||||||
|
|
||||||
_upgradeDownloadItem: null,
|
|
||||||
_isMounted: false,
|
|
||||||
_version: null,
|
|
||||||
getUpdateUrl: function() {
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'darwin':
|
|
||||||
return 'https://lbry.io/get/lbry.dmg';
|
|
||||||
case 'linux':
|
|
||||||
return 'https://lbry.io/get/lbry.deb';
|
|
||||||
case 'win32':
|
|
||||||
return 'https://lbry.io/get/lbry.exe';
|
|
||||||
default:
|
|
||||||
throw 'Unknown platform';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Hard code the filenames as a temporary workaround, because
|
|
||||||
// electron-dl throws errors when you try to get the filename
|
|
||||||
getUpgradeFilename: function() {
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'darwin':
|
|
||||||
return `LBRY-${this._version}.dmg`;
|
|
||||||
case 'linux':
|
|
||||||
return `LBRY_${this._version}_amd64.deb`;
|
|
||||||
case 'windows':
|
|
||||||
return `LBRY.Setup.${this._version}.exe`;
|
|
||||||
default:
|
|
||||||
throw 'Unknown platform';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getViewingPageAndArgs: function(address) {
|
|
||||||
// For now, routes are in format ?page or ?page=args
|
|
||||||
let [isMatch, viewingPage, pageArgs] = address.match(/\??([^=]*)(?:=(.*))?/);
|
|
||||||
return {
|
|
||||||
viewingPage: viewingPage,
|
|
||||||
pageArgs: pageArgs === undefined ? null : decodeURIComponent(pageArgs)
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getInitialState: function() {
|
|
||||||
return Object.assign(this.getViewingPageAndArgs(window.location.search), {
|
|
||||||
viewingPage: 'discover',
|
|
||||||
appUrl: null,
|
|
||||||
errorInfo: null,
|
|
||||||
modal: null,
|
|
||||||
downloadProgress: null,
|
|
||||||
downloadComplete: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
componentWillMount: function() {
|
|
||||||
window.addEventListener("popstate", this.onHistoryPop);
|
|
||||||
|
|
||||||
document.addEventListener('unhandledError', (event) => {
|
|
||||||
this.alertError(event.detail);
|
|
||||||
});
|
|
||||||
|
|
||||||
//open links in external browser and skip full redraw on changing page
|
|
||||||
document.addEventListener('click', (event) => {
|
|
||||||
var target = event.target;
|
|
||||||
while (target && target !== document) {
|
|
||||||
if (target.matches('a[href^="http"]')) {
|
|
||||||
event.preventDefault();
|
|
||||||
shell.openExternal(target.href);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (target.matches('a[href^="?"]')) {
|
|
||||||
event.preventDefault();
|
|
||||||
if (this._isMounted) {
|
|
||||||
let appUrl = target.getAttribute('href');
|
|
||||||
this._storeHistoryOfNextRender = true;
|
|
||||||
this.setState(Object.assign({}, this.getViewingPageAndArgs(appUrl), { appUrl: appUrl }));
|
|
||||||
document.body.scrollTop = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
target = target.parentNode;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!sessionStorage.getItem('upgradeSkipped')) {
|
|
||||||
lbry.getVersionInfo().then(({remoteVersion, upgradeAvailable}) => {
|
|
||||||
if (upgradeAvailable) {
|
|
||||||
this._version = remoteVersion;
|
|
||||||
this.setState({
|
|
||||||
modal: 'upgrade',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closeModal: function() {
|
|
||||||
this.setState({
|
|
||||||
modal: null,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
componentDidMount: function() {
|
|
||||||
this._isMounted = true;
|
|
||||||
},
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this._isMounted = false;
|
|
||||||
window.removeEventListener("popstate", this.onHistoryPop);
|
|
||||||
},
|
|
||||||
onHistoryPop: function() {
|
|
||||||
this.setState(this.getViewingPageAndArgs(location.search));
|
|
||||||
},
|
|
||||||
onSearch: function(term) {
|
|
||||||
this._storeHistoryOfNextRender = true;
|
|
||||||
const isShow = term.startsWith('lbry://');
|
|
||||||
this.setState({
|
|
||||||
viewingPage: isShow ? "show" : "search",
|
|
||||||
appUrl: (isShow ? "?show=" : "?search=") + encodeURIComponent(term),
|
|
||||||
pageArgs: term
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onSubmit: function(uri) {
|
|
||||||
this._storeHistoryOfNextRender = true;
|
|
||||||
this.setState({
|
|
||||||
address: uri,
|
|
||||||
appUrl: "?show=" + encodeURIComponent(uri),
|
|
||||||
viewingPage: "show",
|
|
||||||
pageArgs: uri
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleUpgradeClicked: function() {
|
|
||||||
// Make a new directory within temp directory so the filename is guaranteed to be available
|
|
||||||
const dir = fs.mkdtempSync(app.getPath('temp') + require('path').sep);
|
|
||||||
|
|
||||||
let options = {
|
|
||||||
onProgress: (p) => this.setState({downloadProgress: Math.round(p * 100)}),
|
|
||||||
directory: dir,
|
|
||||||
};
|
|
||||||
download(remote.getCurrentWindow(), this.getUpdateUrl(), options)
|
|
||||||
.then(downloadItem => {
|
|
||||||
/**
|
|
||||||
* TODO: get the download path directly from the download object. It should just be
|
|
||||||
* downloadItem.getSavePath(), but the copy on the main process is being garbage collected
|
|
||||||
* too soon.
|
|
||||||
*/
|
|
||||||
|
|
||||||
this._upgradeDownloadItem = downloadItem;
|
|
||||||
this._upgradeDownloadPath = path.join(dir, this.getUpgradeFilename());
|
|
||||||
this.setState({
|
|
||||||
downloadComplete: true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.setState({modal: 'downloading'});
|
|
||||||
},
|
|
||||||
handleStartUpgradeClicked: function() {
|
|
||||||
ipcRenderer.send('upgrade', this._upgradeDownloadPath);
|
|
||||||
},
|
|
||||||
cancelUpgrade: function() {
|
|
||||||
if (this._upgradeDownloadItem) {
|
|
||||||
/*
|
|
||||||
* Right now the remote reference to the download item gets garbage collected as soon as the
|
|
||||||
* the download is over (maybe even earlier), so trying to cancel a finished download may
|
|
||||||
* throw an error.
|
|
||||||
*/
|
|
||||||
try {
|
|
||||||
this._upgradeDownloadItem.cancel();
|
|
||||||
} catch (err) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
downloadProgress: null,
|
|
||||||
downloadComplete: false,
|
|
||||||
modal: null,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleSkipClicked: function() {
|
|
||||||
sessionStorage.setItem('upgradeSkipped', true);
|
|
||||||
this.setState({
|
|
||||||
modal: null,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
alertError: function(error) {
|
|
||||||
var errorInfoList = [];
|
|
||||||
for (let key of Object.keys(error)) {
|
|
||||||
let val = typeof error[key] == 'string' ? error[key] : JSON.stringify(error[key]);
|
|
||||||
let label = this._error_key_labels[key];
|
|
||||||
errorInfoList.push(<li key={key}><strong>{label}</strong>: <code>{val}</code></li>);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
modal: 'error',
|
|
||||||
errorInfo: <ul className="error-modal__error-list">{errorInfoList}</ul>,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getContentAndAddress: function()
|
|
||||||
{
|
|
||||||
switch(this.state.viewingPage)
|
|
||||||
{
|
|
||||||
case 'search':
|
|
||||||
return [this.state.pageArgs ? this.state.pageArgs : "Search", 'icon-search', <SearchPage query={this.state.pageArgs} />];
|
|
||||||
case 'settings':
|
|
||||||
return ["Settings", "icon-gear", <SettingsPage />];
|
|
||||||
case 'help':
|
|
||||||
return ["Help", "icon-question", <HelpPage />];
|
|
||||||
case 'report':
|
|
||||||
return ['Report an Issue', 'icon-file', <ReportPage />];
|
|
||||||
case 'downloaded':
|
|
||||||
return ["Downloads & Purchases", "icon-folder", <FileListDownloaded />];
|
|
||||||
case 'published':
|
|
||||||
return ["Publishes", "icon-folder", <FileListPublished />];
|
|
||||||
case 'start':
|
|
||||||
return ["Start", "icon-file", <StartPage />];
|
|
||||||
case 'rewards':
|
|
||||||
return ["Rewards", "icon-bank", <RewardsPage />];
|
|
||||||
case 'wallet':
|
|
||||||
case 'send':
|
|
||||||
case 'receive':
|
|
||||||
return [this.state.viewingPage.charAt(0).toUpperCase() + this.state.viewingPage.slice(1), "icon-bank", <WalletPage viewingPage={this.state.viewingPage} />]
|
|
||||||
case 'show':
|
|
||||||
return [lbryuri.normalize(this.state.pageArgs), "icon-file", <ShowPage uri={this.state.pageArgs} />];
|
|
||||||
case 'publish':
|
|
||||||
return ["Publish", "icon-upload", <PublishPage />];
|
|
||||||
case 'developer':
|
|
||||||
return ["Developer", "icon-file", <DeveloperPage />];
|
|
||||||
case 'discover':
|
|
||||||
default:
|
|
||||||
return ["Home", "icon-home", <DiscoverPage />];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render: function() {
|
|
||||||
let [address, wunderBarIcon, mainContent] = this.getContentAndAddress();
|
|
||||||
|
|
||||||
lbry.setTitle(address);
|
|
||||||
|
|
||||||
if (this._storeHistoryOfNextRender) {
|
|
||||||
this._storeHistoryOfNextRender = false;
|
|
||||||
history.pushState({}, document.title, this.state.appUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
this._fullScreenPages.includes(this.state.viewingPage) ?
|
|
||||||
mainContent :
|
|
||||||
<div id="window">
|
|
||||||
<Header onSearch={this.onSearch} onSubmit={this.onSubmit} address={address} wunderBarIcon={wunderBarIcon} viewingPage={this.state.viewingPage} />
|
|
||||||
<div id="main-content">
|
|
||||||
{mainContent}
|
|
||||||
</div>
|
|
||||||
<Modal isOpen={this.state.modal == 'upgrade'} contentLabel="Update available"
|
|
||||||
type="confirm" confirmButtonLabel="Upgrade" abortButtonLabel="Skip"
|
|
||||||
onConfirmed={this.handleUpgradeClicked} onAborted={this.handleSkipClicked}>
|
|
||||||
Your version of LBRY is out of date and may be unreliable or insecure.
|
|
||||||
</Modal>
|
|
||||||
<Modal isOpen={this.state.modal == 'downloading'} contentLabel="Downloading Update" type="custom">
|
|
||||||
Downloading Update{this.state.downloadProgress ? `: ${this.state.downloadProgress}%` : null}
|
|
||||||
<Line percent={this.state.downloadProgress} strokeWidth="4"/>
|
|
||||||
{this.state.downloadComplete ? (
|
|
||||||
<div>
|
|
||||||
<br />
|
|
||||||
<p>Click "Begin Upgrade" to start the upgrade process.</p>
|
|
||||||
<p>The app will close, and you will be prompted to install the latest version of LBRY.</p>
|
|
||||||
<p>After the install is complete, please reopen the app.</p>
|
|
||||||
</div>
|
|
||||||
) : null }
|
|
||||||
<div className="modal__buttons">
|
|
||||||
{this.state.downloadComplete
|
|
||||||
? <Link button="primary" label="Begin Upgrade" className="modal__button" onClick={this.handleStartUpgradeClicked} />
|
|
||||||
: null}
|
|
||||||
<Link button="alt" label="Cancel" className="modal__button" onClick={this.cancelUpgrade} />
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
<ExpandableModal isOpen={this.state.modal == 'error'} contentLabel="Error" className="error-modal"
|
|
||||||
overlayClassName="error-modal-overlay" onConfirmed={this.closeModal}
|
|
||||||
extraContent={this.state.errorInfo}>
|
|
||||||
<h3 className="modal__header">Error</h3>
|
|
||||||
|
|
||||||
<div className="error-modal__content">
|
|
||||||
<div><img className="error-modal__warning-symbol" src={lbry.imagePath('warning.png')} /></div>
|
|
||||||
<p>We're sorry that LBRY has encountered an error. This has been reported and we will investigate the problem.</p>
|
|
||||||
</div>
|
|
||||||
</ExpandableModal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
global.app = app;
|
||||||
|
module.exports = app;
|
||||||
export default App;
|
//
|
||||||
|
// import React from 'react';
|
||||||
|
// import {Line} from 'rc-progress';
|
||||||
|
//
|
||||||
|
// import lbry from './lbry.js';
|
||||||
|
// import SettingsPage from './page/settings.js';
|
||||||
|
// import HelpPage from './page/help.js';
|
||||||
|
// import WatchPage from './page/watch.js';
|
||||||
|
// import ReportPage from './page/report.js';
|
||||||
|
// import StartPage from './page/start.js';
|
||||||
|
// import RewardsPage from './page/rewards.js';
|
||||||
|
// import RewardPage from './page/reward.js';
|
||||||
|
// import WalletPage from './page/wallet.js';
|
||||||
|
// import ShowPage from './page/show.js';
|
||||||
|
// 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';
|
||||||
|
// import {Link} from './component/link.js';
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// const {remote, ipcRenderer, shell} = require('electron');
|
||||||
|
// const {download} = remote.require('electron-dl');
|
||||||
|
// const path = require('path');
|
||||||
|
// const app = require('electron').remote.app;
|
||||||
|
// const fs = remote.require('fs');
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// var App = React.createClass({
|
||||||
|
// _error_key_labels: {
|
||||||
|
// connectionString: 'API connection string',
|
||||||
|
// method: 'Method',
|
||||||
|
// params: 'Parameters',
|
||||||
|
// code: 'Error code',
|
||||||
|
// message: 'Error message',
|
||||||
|
// data: 'Error data',
|
||||||
|
// },
|
||||||
|
// _fullScreenPages: ['watch'],
|
||||||
|
// _storeHistoryOfNextRender: false,
|
||||||
|
//
|
||||||
|
// _upgradeDownloadItem: null,
|
||||||
|
// _isMounted: false,
|
||||||
|
// _version: null,
|
||||||
|
// getUpdateUrl: function() {
|
||||||
|
// switch (process.platform) {
|
||||||
|
// case 'darwin':
|
||||||
|
// return 'https://lbry.io/get/lbry.dmg';
|
||||||
|
// case 'linux':
|
||||||
|
// return 'https://lbry.io/get/lbry.deb';
|
||||||
|
// case 'win32':
|
||||||
|
// return 'https://lbry.io/get/lbry.exe';
|
||||||
|
// default:
|
||||||
|
// throw 'Unknown platform';
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// // Hard code the filenames as a temporary workaround, because
|
||||||
|
// // electron-dl throws errors when you try to get the filename
|
||||||
|
// getUpgradeFilename: function() {
|
||||||
|
// switch (process.platform) {
|
||||||
|
// case 'darwin':
|
||||||
|
// return `LBRY-${this._version}.dmg`;
|
||||||
|
// case 'linux':
|
||||||
|
// return `LBRY_${this._version}_amd64.deb`;
|
||||||
|
// case 'windows':
|
||||||
|
// return `LBRY.Setup.${this._version}.exe`;
|
||||||
|
// default:
|
||||||
|
// throw 'Unknown platform';
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// getViewingPageAndArgs: function(address) {
|
||||||
|
// // For now, routes are in format ?page or ?page=args
|
||||||
|
// let [isMatch, viewingPage, pageArgs] = address.match(/\??([^=]*)(?:=(.*))?/);
|
||||||
|
// return {
|
||||||
|
// viewingPage: viewingPage,
|
||||||
|
// pageArgs: pageArgs === undefined ? null : decodeURIComponent(pageArgs)
|
||||||
|
// };
|
||||||
|
// },
|
||||||
|
// getInitialState: function() {
|
||||||
|
// return Object.assign(this.getViewingPageAndArgs(window.location.search), {
|
||||||
|
// viewingPage: 'discover',
|
||||||
|
// appUrl: null,
|
||||||
|
// errorInfo: null,
|
||||||
|
// modal: null,
|
||||||
|
// downloadProgress: null,
|
||||||
|
// downloadComplete: false,
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// componentWillMount: function() {
|
||||||
|
// window.addEventListener("popstate", this.onHistoryPop);
|
||||||
|
//
|
||||||
|
// document.addEventListener('unhandledError', (event) => {
|
||||||
|
// this.alertError(event.detail);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// //open links in external browser and skip full redraw on changing page
|
||||||
|
// document.addEventListener('click', (event) => {
|
||||||
|
// var target = event.target;
|
||||||
|
// while (target && target !== document) {
|
||||||
|
// if (target.matches('a[href^="http"]')) {
|
||||||
|
// event.preventDefault();
|
||||||
|
// shell.openExternal(target.href);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if (target.matches('a[href^="?"]')) {
|
||||||
|
// event.preventDefault();
|
||||||
|
// if (this._isMounted) {
|
||||||
|
// let appUrl = target.getAttribute('href');
|
||||||
|
// this._storeHistoryOfNextRender = true;
|
||||||
|
// this.setState(Object.assign({}, this.getViewingPageAndArgs(appUrl), { appUrl: appUrl }));
|
||||||
|
// document.body.scrollTop = 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// target = target.parentNode;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// if (!sessionStorage.getItem('upgradeSkipped')) {
|
||||||
|
// lbry.getVersionInfo().then(({remoteVersion, upgradeAvailable}) => {
|
||||||
|
// if (upgradeAvailable) {
|
||||||
|
// this._version = remoteVersion;
|
||||||
|
// this.setState({
|
||||||
|
// modal: 'upgrade',
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// closeModal: function() {
|
||||||
|
// this.setState({
|
||||||
|
// modal: null,
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// componentDidMount: function() {
|
||||||
|
// this._isMounted = true;
|
||||||
|
// },
|
||||||
|
// componentWillUnmount: function() {
|
||||||
|
// this._isMounted = false;
|
||||||
|
// window.removeEventListener("popstate", this.onHistoryPop);
|
||||||
|
// },
|
||||||
|
// onHistoryPop: function() {
|
||||||
|
// this.setState(this.getViewingPageAndArgs(location.search));
|
||||||
|
// },
|
||||||
|
// onSearch: function(term) {
|
||||||
|
// this._storeHistoryOfNextRender = true;
|
||||||
|
// const isShow = term.startsWith('lbry://');
|
||||||
|
// this.setState({
|
||||||
|
// viewingPage: isShow ? "show" : "search",
|
||||||
|
// appUrl: (isShow ? "?show=" : "?search=") + encodeURIComponent(term),
|
||||||
|
// pageArgs: term
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// onSubmit: function(uri) {
|
||||||
|
// this._storeHistoryOfNextRender = true;
|
||||||
|
// this.setState({
|
||||||
|
// address: uri,
|
||||||
|
// appUrl: "?show=" + encodeURIComponent(uri),
|
||||||
|
// viewingPage: "show",
|
||||||
|
// pageArgs: uri
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// handleUpgradeClicked: function() {
|
||||||
|
// // Make a new directory within temp directory so the filename is guaranteed to be available
|
||||||
|
// const dir = fs.mkdtempSync(app.getPath('temp') + require('path').sep);
|
||||||
|
//
|
||||||
|
// let options = {
|
||||||
|
// onProgress: (p) => this.setState({downloadProgress: Math.round(p * 100)}),
|
||||||
|
// directory: dir,
|
||||||
|
// };
|
||||||
|
// download(remote.getCurrentWindow(), this.getUpdateUrl(), options)
|
||||||
|
// .then(downloadItem => {
|
||||||
|
// /**
|
||||||
|
// * TODO: get the download path directly from the download object. It should just be
|
||||||
|
// * downloadItem.getSavePath(), but the copy on the main process is being garbage collected
|
||||||
|
// * too soon.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
// this._upgradeDownloadItem = downloadItem;
|
||||||
|
// this._upgradeDownloadPath = path.join(dir, this.getUpgradeFilename());
|
||||||
|
// this.setState({
|
||||||
|
// downloadComplete: true
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// this.setState({modal: 'downloading'});
|
||||||
|
// },
|
||||||
|
// handleStartUpgradeClicked: function() {
|
||||||
|
// ipcRenderer.send('upgrade', this._upgradeDownloadPath);
|
||||||
|
// },
|
||||||
|
// cancelUpgrade: function() {
|
||||||
|
// if (this._upgradeDownloadItem) {
|
||||||
|
// /*
|
||||||
|
// * Right now the remote reference to the download item gets garbage collected as soon as the
|
||||||
|
// * the download is over (maybe even earlier), so trying to cancel a finished download may
|
||||||
|
// * throw an error.
|
||||||
|
// */
|
||||||
|
// try {
|
||||||
|
// this._upgradeDownloadItem.cancel();
|
||||||
|
// } catch (err) {
|
||||||
|
// // Do nothing
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// this.setState({
|
||||||
|
// downloadProgress: null,
|
||||||
|
// downloadComplete: false,
|
||||||
|
// modal: null,
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// handleSkipClicked: function() {
|
||||||
|
// sessionStorage.setItem('upgradeSkipped', true);
|
||||||
|
// this.setState({
|
||||||
|
// modal: null,
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// alertError: function(error) {
|
||||||
|
// var errorInfoList = [];
|
||||||
|
// for (let key of Object.keys(error)) {
|
||||||
|
// let val = typeof error[key] == 'string' ? error[key] : JSON.stringify(error[key]);
|
||||||
|
// let label = this._error_key_labels[key];
|
||||||
|
// errorInfoList.push(<li key={key}><strong>{label}</strong>: <code>{val}</code></li>);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// this.setState({
|
||||||
|
// modal: 'error',
|
||||||
|
// errorInfo: <ul className="error-modal__error-list">{errorInfoList}</ul>,
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// getContentAndAddress: function()
|
||||||
|
// {
|
||||||
|
// switch(this.state.viewingPage)
|
||||||
|
// {
|
||||||
|
// case 'search':
|
||||||
|
// return [this.state.pageArgs ? this.state.pageArgs : "Search", 'icon-search', <SearchPage query={this.state.pageArgs} />];
|
||||||
|
// case 'settings':
|
||||||
|
// return ["Settings", "icon-gear", <SettingsPage />];
|
||||||
|
// case 'help':
|
||||||
|
// return ["Help", "icon-question", <HelpPage />];
|
||||||
|
// case 'report':
|
||||||
|
// return ['Report an Issue', 'icon-file', <ReportPage />];
|
||||||
|
// case 'downloaded':
|
||||||
|
// return ["Downloads & Purchases", "icon-folder", <FileListDownloaded />];
|
||||||
|
// case 'published':
|
||||||
|
// return ["Publishes", "icon-folder", <FileListPublished />];
|
||||||
|
// case 'start':
|
||||||
|
// return ["Start", "icon-file", <StartPage />];
|
||||||
|
// case 'rewards':
|
||||||
|
// return ["Rewards", "icon-bank", <RewardsPage />];
|
||||||
|
// case 'wallet':
|
||||||
|
// case 'send':
|
||||||
|
// case 'receive':
|
||||||
|
// return [this.state.viewingPage.charAt(0).toUpperCase() + this.state.viewingPage.slice(1), "icon-bank", <WalletPage viewingPage={this.state.viewingPage} />]
|
||||||
|
// case 'show':
|
||||||
|
// return [lbryuri.normalize(this.state.pageArgs), "icon-file", <ShowPage uri={this.state.pageArgs} />];
|
||||||
|
// case 'publish':
|
||||||
|
// return ["Publish", "icon-upload", <PublishPage />];
|
||||||
|
// case 'developer':
|
||||||
|
// return ["Developer", "icon-file", <DeveloperPage />];
|
||||||
|
// case 'discover':
|
||||||
|
// default:
|
||||||
|
// return ["Home", "icon-home", <DiscoverPage />];
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// render: function() {
|
||||||
|
// let [address, wunderBarIcon, mainContent] = this.getContentAndAddress();
|
||||||
|
//
|
||||||
|
// lbry.setTitle(address);
|
||||||
|
//
|
||||||
|
// if (this._storeHistoryOfNextRender) {
|
||||||
|
// this._storeHistoryOfNextRender = false;
|
||||||
|
// history.pushState({}, document.title, this.state.appUrl);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return (
|
||||||
|
// this._fullScreenPages.includes(this.state.viewingPage) ?
|
||||||
|
// mainContent :
|
||||||
|
// <div id="window">
|
||||||
|
// <Header onSearch={this.onSearch} onSubmit={this.onSubmit} address={address} wunderBarIcon={wunderBarIcon} viewingPage={this.state.viewingPage} />
|
||||||
|
// <div id="main-content">
|
||||||
|
// {mainContent}
|
||||||
|
// </div>
|
||||||
|
// <Modal isOpen={this.state.modal == 'upgrade'} contentLabel="Update available"
|
||||||
|
// type="confirm" confirmButtonLabel="Upgrade" abortButtonLabel="Skip"
|
||||||
|
// onConfirmed={this.handleUpgradeClicked} onAborted={this.handleSkipClicked}>
|
||||||
|
// Your version of LBRY is out of date and may be unreliable or insecure.
|
||||||
|
// </Modal>
|
||||||
|
// <Modal isOpen={this.state.modal == 'downloading'} contentLabel="Downloading Update" type="custom">
|
||||||
|
// Downloading Update{this.state.downloadProgress ? `: ${this.state.downloadProgress}%` : null}
|
||||||
|
// <Line percent={this.state.downloadProgress} strokeWidth="4"/>
|
||||||
|
// {this.state.downloadComplete ? (
|
||||||
|
// <div>
|
||||||
|
// <br />
|
||||||
|
// <p>Click "Begin Upgrade" to start the upgrade process.</p>
|
||||||
|
// <p>The app will close, and you will be prompted to install the latest version of LBRY.</p>
|
||||||
|
// <p>After the install is complete, please reopen the app.</p>
|
||||||
|
// </div>
|
||||||
|
// ) : null }
|
||||||
|
// <div className="modal__buttons">
|
||||||
|
// {this.state.downloadComplete
|
||||||
|
// ? <Link button="primary" label="Begin Upgrade" className="modal__button" onClick={this.handleStartUpgradeClicked} />
|
||||||
|
// : null}
|
||||||
|
// <Link button="alt" label="Cancel" className="modal__button" onClick={this.cancelUpgrade} />
|
||||||
|
// </div>
|
||||||
|
// </Modal>
|
||||||
|
// <ExpandableModal isOpen={this.state.modal == 'error'} contentLabel="Error" className="error-modal"
|
||||||
|
// overlayClassName="error-modal-overlay" onConfirmed={this.closeModal}
|
||||||
|
// extraContent={this.state.errorInfo}>
|
||||||
|
// <h3 className="modal__header">Error</h3>
|
||||||
|
//
|
||||||
|
// <div className="error-modal__content">
|
||||||
|
// <div><img className="error-modal__warning-symbol" src={lbry.imagePath('warning.png')} /></div>
|
||||||
|
// <p>We're sorry that LBRY has encountered an error. This has been reported and we will investigate the problem.</p>
|
||||||
|
// </div>
|
||||||
|
// </ExpandableModal>
|
||||||
|
// </div>
|
||||||
|
// );
|
37
ui/js/component/app/index.js
Normal file
37
ui/js/component/app/index.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
|
import {
|
||||||
|
selectCurrentPage,
|
||||||
|
selectCurrentModal,
|
||||||
|
selectDrawerOpen,
|
||||||
|
selectHeaderLinks,
|
||||||
|
selectSearchTerm,
|
||||||
|
} from 'selectors/app'
|
||||||
|
import {
|
||||||
|
doCheckUpgradeAvailable,
|
||||||
|
doOpenDrawer,
|
||||||
|
doCloseDrawer,
|
||||||
|
doOpenModal,
|
||||||
|
doCloseModal,
|
||||||
|
doSearch,
|
||||||
|
} from 'actions/app'
|
||||||
|
import App from './view'
|
||||||
|
|
||||||
|
const select = (state) => ({
|
||||||
|
currentPage: selectCurrentPage(state),
|
||||||
|
modal: selectCurrentModal(state),
|
||||||
|
drawerOpen: selectDrawerOpen(state),
|
||||||
|
headerLinks: selectHeaderLinks(state),
|
||||||
|
searchTerm: selectSearchTerm(state)
|
||||||
|
})
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
|
checkUpgradeAvailable: () => dispatch(doCheckUpgradeAvailable()),
|
||||||
|
openDrawer: () => dispatch(doOpenDrawer()),
|
||||||
|
closeDrawer: () => dispatch(doCloseDrawer()),
|
||||||
|
openModal: () => dispatch(doOpenModal()),
|
||||||
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(select, perform)(App)
|
65
ui/js/component/app/view.jsx
Normal file
65
ui/js/component/app/view.jsx
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import lbry from 'lbry.js';
|
||||||
|
import Router from 'component/router'
|
||||||
|
import Drawer from 'component/drawer';
|
||||||
|
import Header from 'component/header.js';
|
||||||
|
import {Modal, ExpandableModal} from 'component/modal.js';
|
||||||
|
import ErrorModal from 'component/errorModal'
|
||||||
|
import DownloadingModal from 'component/downloadingModal'
|
||||||
|
import UpgradeModal from 'component/upgradeModal'
|
||||||
|
import Link from 'component/link';
|
||||||
|
import {Line} from 'rc-progress';
|
||||||
|
|
||||||
|
const App = React.createClass({
|
||||||
|
// Temporary workaround since electron-dl throws errors when you try to get the filename
|
||||||
|
getViewingPageAndArgs: function(address) {
|
||||||
|
// For now, routes are in format ?page or ?page=args
|
||||||
|
let [isMatch, viewingPage, pageArgs] = address.match(/\??([^=]*)(?:=(.*))?/);
|
||||||
|
return {
|
||||||
|
viewingPage: viewingPage,
|
||||||
|
pageArgs: pageArgs === undefined ? null : pageArgs
|
||||||
|
};
|
||||||
|
},
|
||||||
|
componentWillMount: function() {
|
||||||
|
document.addEventListener('unhandledError', (event) => {
|
||||||
|
this.props.alertError(event.detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this.props.upgradeSkipped) {
|
||||||
|
this.props.checkUpgradeAvailable()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
const {
|
||||||
|
currentPage,
|
||||||
|
openDrawer,
|
||||||
|
closeDrawer,
|
||||||
|
openModal,
|
||||||
|
closeModal,
|
||||||
|
modal,
|
||||||
|
drawerOpen,
|
||||||
|
headerLinks,
|
||||||
|
search,
|
||||||
|
searchTerm,
|
||||||
|
} = this.props
|
||||||
|
const searchQuery = (currentPage == 'discover' && searchTerm ? searchTerm : '')
|
||||||
|
|
||||||
|
return (
|
||||||
|
currentPage == 'watch' ?
|
||||||
|
<Router /> :
|
||||||
|
<div id="window" className={ drawerOpen ? 'drawer-open' : 'drawer-closed' }>
|
||||||
|
<Drawer onCloseDrawer={closeDrawer} viewingPage={currentPage} />
|
||||||
|
<div id="main-content" className={ headerLinks ? 'with-sub-nav' : 'no-sub-nav' }>
|
||||||
|
<Header onOpenDrawer={openDrawer} initialQuery={searchQuery} onSearch={search} links={headerLinks} />
|
||||||
|
<Router />
|
||||||
|
</div>
|
||||||
|
{modal == 'upgrade' && <UpgradeModal />}
|
||||||
|
{modal == 'downloading' && <DownloadingModal />}
|
||||||
|
{modal == 'error' && <ErrorModal />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default App
|
25
ui/js/component/downloadingModal/index.jsx
Normal file
25
ui/js/component/downloadingModal/index.jsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect
|
||||||
|
} from 'react-redux'
|
||||||
|
import {
|
||||||
|
doStartUpgrade,
|
||||||
|
doCancelUpgrade,
|
||||||
|
} from 'actions/app'
|
||||||
|
import {
|
||||||
|
selectDownloadProgress,
|
||||||
|
selectDownloadComplete,
|
||||||
|
} from 'selectors/app'
|
||||||
|
import DownloadingModal from './view'
|
||||||
|
|
||||||
|
const select = (state) => ({
|
||||||
|
downloadProgress: selectDownloadProgress(state),
|
||||||
|
downloadComplete: selectDownloadComplete(state),
|
||||||
|
})
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
|
startUpgrade: () => dispatch(doStartUpgrade()),
|
||||||
|
cancelUpgrade: () => dispatch(doCancelUpgrade())
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(select, perform)(DownloadingModal)
|
40
ui/js/component/downloadingModal/view.jsx
Normal file
40
ui/js/component/downloadingModal/view.jsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
Modal
|
||||||
|
} from 'component/modal'
|
||||||
|
import {Line} from 'rc-progress';
|
||||||
|
import Link from 'component/link'
|
||||||
|
|
||||||
|
class DownloadingModal extends React.Component {
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
downloadProgress,
|
||||||
|
downloadComplete,
|
||||||
|
startUpgrade,
|
||||||
|
cancelUpgrade,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={true} contentLabel="Downloading Update" type="custom">
|
||||||
|
Downloading Update{downloadProgress ? `: ${downloadProgress}%` : null}
|
||||||
|
<Line percent={downloadProgress} strokeWidth="4"/>
|
||||||
|
{downloadComplete ? (
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
<p>Click "Begin Upgrade" to start the upgrade process.</p>
|
||||||
|
<p>The app will close, and you will be prompted to install the latest version of LBRY.</p>
|
||||||
|
<p>After the install is complete, please reopen the app.</p>
|
||||||
|
</div>
|
||||||
|
) : null }
|
||||||
|
<div className="modal__buttons">
|
||||||
|
{downloadComplete
|
||||||
|
? <Link button="primary" label="Begin Upgrade" className="modal__button" onClick={startUpgrade} />
|
||||||
|
: null}
|
||||||
|
<Link button="alt" label="Cancel" className="modal__button" onClick={cancelUpgrade} />
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DownloadingModal
|
29
ui/js/component/drawer/index.jsx
Normal file
29
ui/js/component/drawer/index.jsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect
|
||||||
|
} from 'react-redux'
|
||||||
|
import Drawer from './view'
|
||||||
|
import {
|
||||||
|
doNavigate,
|
||||||
|
doCloseDrawer,
|
||||||
|
doLogoClick,
|
||||||
|
doUpdateBalance,
|
||||||
|
} from 'actions/app'
|
||||||
|
import {
|
||||||
|
selectCurrentPage,
|
||||||
|
selectBalance,
|
||||||
|
} from 'selectors/app'
|
||||||
|
|
||||||
|
const select = (state) => ({
|
||||||
|
currentPage: selectCurrentPage(state),
|
||||||
|
balance: selectBalance(state),
|
||||||
|
})
|
||||||
|
|
||||||
|
const perform = {
|
||||||
|
linkClick: doNavigate,
|
||||||
|
logoClick: doLogoClick,
|
||||||
|
closeDrawerClick: doCloseDrawer,
|
||||||
|
updateBalance: doUpdateBalance,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(select, perform)(Drawer)
|
68
ui/js/component/drawer/view.jsx
Normal file
68
ui/js/component/drawer/view.jsx
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import lbry from 'lbry.js';
|
||||||
|
import React from 'react';
|
||||||
|
import Link from 'component/link';
|
||||||
|
|
||||||
|
const DrawerItem = (props) => {
|
||||||
|
const {
|
||||||
|
currentPage,
|
||||||
|
href,
|
||||||
|
subPages,
|
||||||
|
label,
|
||||||
|
linkClick,
|
||||||
|
icon,
|
||||||
|
} = props
|
||||||
|
const isSelected = (
|
||||||
|
currentPage == href.substr(1) ||
|
||||||
|
(subPages && subPages.indexOf(currentPage) != -1)
|
||||||
|
)
|
||||||
|
|
||||||
|
return <Link icon={icon} label={label} onClick={() => linkClick(href)} className={ 'drawer-item ' + (isSelected ? 'drawer-item-selected' : '') } />
|
||||||
|
}
|
||||||
|
|
||||||
|
var drawerImageStyle = { //@TODO: remove this, img should be properly scaled once size is settled
|
||||||
|
height: '36px'
|
||||||
|
};
|
||||||
|
|
||||||
|
class Drawer extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this._balanceSubscribeId = null
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { updateBalance } = this.props
|
||||||
|
|
||||||
|
this._balanceSubscribeId = lbry.balanceSubscribe(function(balance) {
|
||||||
|
updateBalance(balance)
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this._balanceSubscribeId) {
|
||||||
|
lbry.balanceUnsubscribe(this._balanceSubscribeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
closeDrawerClick,
|
||||||
|
logoClick,
|
||||||
|
currentPage,
|
||||||
|
balance,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
return(<nav id="drawer">
|
||||||
|
<div id="drawer-handle">
|
||||||
|
<Link title="Close" onClick={closeDrawerClick} icon="icon-bars" className="close-drawer-link"/>
|
||||||
|
<a href="discover" onMouseUp={logoClick}><img src={lbry.imagePath("lbry-dark-1600x528.png")} style={drawerImageStyle}/></a>
|
||||||
|
</div>
|
||||||
|
<DrawerItem {...this.props} href='discover' label="Discover" icon="icon-search" />
|
||||||
|
<DrawerItem {...this.props} href='publish' label="Publish" icon="icon-upload" />
|
||||||
|
<DrawerItem {...this.props} href='downloaded' subPages={['published']} label="My Files" icon='icon-cloud-download' />
|
||||||
|
<DrawerItem {...this.props} href="wallet" subPages={['send', 'receive', 'claim', 'referral']} label="My Wallet" badge={lbry.formatCredits(balance) } icon="icon-bank" />
|
||||||
|
<DrawerItem {...this.props} href='settings' label="Settings" icon='icon-gear' />
|
||||||
|
<DrawerItem {...this.props} href='help' label="Help" icon='icon-question-circle' />
|
||||||
|
</nav>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Drawer;
|
23
ui/js/component/errorModal/index.jsx
Normal file
23
ui/js/component/errorModal/index.jsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect
|
||||||
|
} from 'react-redux'
|
||||||
|
import {
|
||||||
|
selectCurrentModal,
|
||||||
|
selectError,
|
||||||
|
} from 'selectors/app'
|
||||||
|
import {
|
||||||
|
doCloseModal,
|
||||||
|
} from 'actions/app'
|
||||||
|
import ErrorModal from './view'
|
||||||
|
|
||||||
|
const select = (state) => ({
|
||||||
|
modal: selectCurrentModal(state),
|
||||||
|
error: selectError(state),
|
||||||
|
})
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
|
closeModal: () => dispatch(doCloseModal())
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(select, perform)(ErrorModal)
|
50
ui/js/component/errorModal/view.jsx
Normal file
50
ui/js/component/errorModal/view.jsx
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import React from 'react'
|
||||||
|
import lbry from 'lbry'
|
||||||
|
import {
|
||||||
|
ExpandableModal
|
||||||
|
} from 'component/modal'
|
||||||
|
|
||||||
|
class ErrorModal extends React.Component {
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
modal,
|
||||||
|
closeModal,
|
||||||
|
error,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
const _error_key_labels = {
|
||||||
|
connectionString: 'API connection string',
|
||||||
|
method: 'Method',
|
||||||
|
params: 'Parameters',
|
||||||
|
code: 'Error code',
|
||||||
|
message: 'Error message',
|
||||||
|
data: 'Error data',
|
||||||
|
}
|
||||||
|
const errorInfo = <ul className="error-modal__error-list"></ul>
|
||||||
|
const errorInfoList = []
|
||||||
|
for (let key of Object.keys(error)) {
|
||||||
|
let val = typeof error[key] == 'string' ? error[key] : JSON.stringify(error[key]);
|
||||||
|
let label = this._error_key_labels[key];
|
||||||
|
errorInfoList.push(<li key={key}><strong>{label}</strong>: <code>{val}</code></li>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<ExpandableModal
|
||||||
|
isOpen={modal == 'error'}
|
||||||
|
contentLabel="Error" className="error-modal"
|
||||||
|
overlayClassName="error-modal-overlay"
|
||||||
|
onConfirmed={closeModal}
|
||||||
|
extraContent={errorInfo}
|
||||||
|
>
|
||||||
|
<h3 className="modal__header">Error</h3>
|
||||||
|
|
||||||
|
<div className="error-modal__content">
|
||||||
|
<div><img className="error-modal__warning-symbol" src={lbry.imagePath('warning.png')} /></div>
|
||||||
|
<p>We're sorry that LBRY has encountered an error. This has been reported and we will investigate the problem.</p>
|
||||||
|
</div>
|
||||||
|
</ExpandableModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ErrorModal
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from '../lbry.js';
|
import lbry from '../lbry.js';
|
||||||
import lbryuri from '../lbryuri.js';
|
import lbryuri from '../lbryuri.js';
|
||||||
import {Link} from '../component/link.js';
|
|
||||||
import {Icon, FilePrice} from '../component/common.js';
|
import {Icon, FilePrice} from '../component/common.js';
|
||||||
import {Modal} from './modal.js';
|
import {Modal} from './modal.js';
|
||||||
import {FormField} from './form.js';
|
import {FormField} from './form.js';
|
||||||
|
import Link from 'component/link';
|
||||||
import {ToolTip} from '../component/tooltip.js';
|
import {ToolTip} from '../component/tooltip.js';
|
||||||
import {DropDownMenu, DropDownMenuItem} from './menu.js';
|
import {DropDownMenu, DropDownMenuItem} from './menu.js';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from '../lbry.js';
|
import lbry from '../lbry.js';
|
||||||
import lbryuri from '../lbryuri.js';
|
import lbryuri from '../lbryuri.js';
|
||||||
import {Link} from '../component/link.js';
|
import Link from 'component/link';
|
||||||
import {FileActions} from '../component/file-actions.js';
|
import {FileActions} from '../component/file-actions.js';
|
||||||
import {BusyMessage, TruncatedText, FilePrice} from '../component/common.js';
|
import {BusyMessage, TruncatedText, FilePrice} from '../component/common.js';
|
||||||
import UriIndicator from '../component/channel-indicator.js';
|
import UriIndicator from '../component/channel-indicator.js';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbryuri from '../lbryuri.js';
|
import lbryuri from '../lbryuri.js';
|
||||||
import {Link} from './link.js';
|
import Link from 'component/link';
|
||||||
import {Icon, CreditAmount} from './common.js';
|
import {Icon, CreditAmount} from './common.js';
|
||||||
|
|
||||||
var Header = React.createClass({
|
var Header = React.createClass({
|
||||||
|
|
7
ui/js/component/link/index.jsx
Normal file
7
ui/js/component/link/index.jsx
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect,
|
||||||
|
} from 'react-redux'
|
||||||
|
import Link from './view'
|
||||||
|
|
||||||
|
export default connect(null, null)(Link)
|
100
ui/js/component/link/view.js
Normal file
100
ui/js/component/link/view.js
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {Icon} from 'component/common.js';
|
||||||
|
|
||||||
|
const Link = (props) => {
|
||||||
|
const {
|
||||||
|
href,
|
||||||
|
title,
|
||||||
|
onClick,
|
||||||
|
style,
|
||||||
|
label,
|
||||||
|
icon,
|
||||||
|
badge,
|
||||||
|
button,
|
||||||
|
hidden,
|
||||||
|
disabled,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
|
||||||
|
const className = (props.className || '') +
|
||||||
|
(!props.className && !props.button ? 'button-text' : '') + // Non-button links get the same look as text buttons
|
||||||
|
(props.button ? ' button-block button-' + props.button + ' button-set-item' : '') +
|
||||||
|
(props.disabled ? ' disabled' : '');
|
||||||
|
|
||||||
|
|
||||||
|
let content;
|
||||||
|
if (props.children) {
|
||||||
|
content = this.props.children
|
||||||
|
} else {
|
||||||
|
content = (
|
||||||
|
<span {... 'button' in props ? {className: 'button__content'} : {}}>
|
||||||
|
{'icon' in props ? <Icon icon={icon} fixed={true} /> : null}
|
||||||
|
{label ? <span className="link-label">{label}</span> : null}
|
||||||
|
{'badge' in props ? <span className="badge">{badge}</span> : null}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a className={className} href={href || 'javascript:;'} title={title}
|
||||||
|
onClick={onClick}
|
||||||
|
{... 'style' in props ? {style: style} : {}}>
|
||||||
|
{content}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Link
|
||||||
|
|
||||||
|
// export let Link = React.createClass({
|
||||||
|
// propTypes: {
|
||||||
|
// label: React.PropTypes.string,
|
||||||
|
// icon: React.PropTypes.string,
|
||||||
|
// button: React.PropTypes.string,
|
||||||
|
// badge: React.PropTypes.string,
|
||||||
|
// hidden: React.PropTypes.bool,
|
||||||
|
// },
|
||||||
|
// getDefaultProps: function() {
|
||||||
|
// return {
|
||||||
|
// hidden: false,
|
||||||
|
// disabled: false,
|
||||||
|
// };
|
||||||
|
// },
|
||||||
|
// handleClick: function(e) {
|
||||||
|
// if (this.props.onClick) {
|
||||||
|
// this.props.onClick(e);
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// render: function() {
|
||||||
|
// if (this.props.hidden) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /* The way the class name is generated here is a mess -- refactor */
|
||||||
|
|
||||||
|
// const className = (this.props.className || '') +
|
||||||
|
// (!this.props.className && !this.props.button ? 'button-text' : '') + // Non-button links get the same look as text buttons
|
||||||
|
// (this.props.button ? ' button-block button-' + this.props.button + ' button-set-item' : '') +
|
||||||
|
// (this.props.disabled ? ' disabled' : '');
|
||||||
|
|
||||||
|
// let content;
|
||||||
|
// if (this.props.children) { // Custom content
|
||||||
|
// content = this.props.children;
|
||||||
|
// } else {
|
||||||
|
// 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>}
|
||||||
|
// {'badge' in this.props ? <span className="badge">{this.props.badge}</span> : null}
|
||||||
|
// </span>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <a className={className} href={this.props.href || 'javascript:;'} title={this.props.title}
|
||||||
|
// onClick={this.handleClick} {... 'style' in this.props ? {style: this.props.style} : {}}>
|
||||||
|
// {content}
|
||||||
|
// </a>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// });
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from '../lbry.js';
|
import lbry from '../lbry.js';
|
||||||
import {BusyMessage, Icon} from './common.js';
|
import {BusyMessage, Icon} from './common.js';
|
||||||
import {Link} from '../component/link.js'
|
import Link from 'component/link'
|
||||||
|
|
||||||
var LoadScreen = React.createClass({
|
var LoadScreen = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Icon} from './common.js';
|
import {Icon} from './common.js';
|
||||||
import {Link} from '../component/link.js';
|
import Link from 'component/link';
|
||||||
|
|
||||||
export let DropDownMenuItem = React.createClass({
|
export let DropDownMenuItem = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactModal from 'react-modal';
|
import ReactModal from 'react-modal';
|
||||||
import {Link} from './link.js';
|
import Link from 'component/link';
|
||||||
|
|
||||||
|
|
||||||
export const Modal = React.createClass({
|
export const Modal = React.createClass({
|
||||||
|
|
15
ui/js/component/router/index.jsx
Normal file
15
ui/js/component/router/index.jsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import Router from './view.jsx';
|
||||||
|
import {
|
||||||
|
selectCurrentPage
|
||||||
|
} from 'selectors/app.js';
|
||||||
|
|
||||||
|
const select = (state) => ({
|
||||||
|
currentPage: selectCurrentPage(state)
|
||||||
|
})
|
||||||
|
|
||||||
|
const perform = {
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(select, null)(Router);
|
51
ui/js/component/router/view.jsx
Normal file
51
ui/js/component/router/view.jsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import React from 'react';
|
||||||
|
import SettingsPage from 'page/settings.js';
|
||||||
|
import HelpPage from 'page/help';
|
||||||
|
import WatchPage from 'page/watch.js';
|
||||||
|
import ReportPage from 'page/report.js';
|
||||||
|
import StartPage from 'page/start.js';
|
||||||
|
import ClaimCodePage from 'page/claim_code.js';
|
||||||
|
import ReferralPage from 'page/referral.js';
|
||||||
|
import WalletPage from 'page/wallet.js';
|
||||||
|
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 {
|
||||||
|
FileListDownloaded,
|
||||||
|
FileListPublished
|
||||||
|
} from 'page/file-list.js';
|
||||||
|
|
||||||
|
const route = (page, routesMap) => {
|
||||||
|
const component = routesMap[page]
|
||||||
|
|
||||||
|
return component
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const Router = (props) => {
|
||||||
|
const {
|
||||||
|
currentPage,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return route(currentPage, {
|
||||||
|
'settings': <SettingsPage {...props} />,
|
||||||
|
'help': <HelpPage {...props} />,
|
||||||
|
'watch': <WatchPage {...props} />,
|
||||||
|
'report': <ReportPage {...props} />,
|
||||||
|
'downloaded': <FileListDownloaded {...props} />,
|
||||||
|
'published': <FileListPublished {...props} />,
|
||||||
|
'start': <StartPage {...props} />,
|
||||||
|
'claim': <ClaimCodePage {...props} />,
|
||||||
|
'wallet': <WalletPage {...props} />,
|
||||||
|
'send': <WalletPage {...props} />,
|
||||||
|
'receive': <WalletPage {...props} />,
|
||||||
|
'show': <DetailPage {...props} />,
|
||||||
|
'publish': <PublishPage {...props} />,
|
||||||
|
'developer': <DeveloperPage {...props} />,
|
||||||
|
'discover': <DiscoverPage {...props} />,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Router
|
19
ui/js/component/upgradeModal/index.jsx
Normal file
19
ui/js/component/upgradeModal/index.jsx
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect
|
||||||
|
} from 'react-redux'
|
||||||
|
import {
|
||||||
|
doDownloadUpgrade,
|
||||||
|
doSkipUpgrade,
|
||||||
|
} from 'actions/app'
|
||||||
|
import UpgradeModal from './view'
|
||||||
|
|
||||||
|
const select = (state) => ({
|
||||||
|
})
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
|
downloadUpgrade: () => dispatch(doDownloadUpgrade()),
|
||||||
|
skipUpgrade: () => dispatch(doSkipUpgrade()),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(select, perform)(UpgradeModal)
|
32
ui/js/component/upgradeModal/view.jsx
Normal file
32
ui/js/component/upgradeModal/view.jsx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
Modal
|
||||||
|
} from 'component/modal'
|
||||||
|
import {
|
||||||
|
downloadUpgrade,
|
||||||
|
skipUpgrade
|
||||||
|
} from 'actions/app'
|
||||||
|
|
||||||
|
class UpgradeModal extends React.Component {
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
downloadUpgrade,
|
||||||
|
skipUpgrade
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={true}
|
||||||
|
contentLabel="Update available"
|
||||||
|
type="confirm"
|
||||||
|
confirmButtonLabel="Upgrade"
|
||||||
|
abortButtonLabel="Skip"
|
||||||
|
onConfirmed={downloadUpgrade}
|
||||||
|
onAborted={skipUpgrade}>
|
||||||
|
Your version of LBRY is out of date and may be unreliable or insecure.
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UpgradeModal
|
2
ui/js/config/development.js
Normal file
2
ui/js/config/development.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
module.exports = {
|
||||||
|
}
|
21
ui/js/constants/action_types.js
Normal file
21
ui/js/constants/action_types.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
export const UPDATE_BALANCE = 'UPDATE_BALANCE'
|
||||||
|
export const NAVIGATE = 'NAVIGATE'
|
||||||
|
|
||||||
|
// Upgrades
|
||||||
|
export const UPGRADE_CANCELLED = 'UPGRADE_CANCELLED'
|
||||||
|
export const DOWNLOAD_UPGRADE = 'DOWNLOAD_UPGRADE'
|
||||||
|
export const UPGRADE_DOWNLOAD_STARTED = 'UPGRADE_DOWNLOAD_STARTED'
|
||||||
|
export const UPGRADE_DOWNLOAD_COMPLETED = 'UPGRADE_DOWNLOAD_COMPLETED'
|
||||||
|
export const UPGRADE_DOWNLOAD_PROGRESSED = 'UPGRADE_DOWNLOAD_PROGRESSED'
|
||||||
|
export const CHECK_UPGRADE_AVAILABLE = 'CHECK_UPGRADE_AVAILABLE'
|
||||||
|
export const UPDATE_VERSION = 'UPDATE_VERSION'
|
||||||
|
export const SKIP_UPGRADE = 'SKIP_UPGRADE'
|
||||||
|
export const START_UPGRADE = 'START_UPGRADE'
|
||||||
|
|
||||||
|
export const OPEN_MODAL = 'OPEN_MODAL'
|
||||||
|
export const CLOSE_MODAL = 'CLOSE_MODAL'
|
||||||
|
|
||||||
|
export const OPEN_DRAWER = 'OPEN_DRAWER'
|
||||||
|
export const CLOSE_DRAWER = 'CLOSE_DRAWER'
|
||||||
|
|
||||||
|
export const START_SEARCH = 'START_SEARCH'
|
|
@ -3,13 +3,16 @@ import ReactDOM from 'react-dom';
|
||||||
import lbry from './lbry.js';
|
import lbry from './lbry.js';
|
||||||
import lbryio from './lbryio.js';
|
import lbryio from './lbryio.js';
|
||||||
import lighthouse from './lighthouse.js';
|
import lighthouse from './lighthouse.js';
|
||||||
import App from './app.js';
|
import App from './component/app/index.js';
|
||||||
import SplashScreen from './component/splash.js';
|
import SplashScreen from './component/splash.js';
|
||||||
import SnackBar from './component/snack-bar.js';
|
import SnackBar from './component/snack-bar.js';
|
||||||
import {AuthOverlay} from './component/auth.js';
|
import {AuthOverlay} from './component/auth.js';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import store from 'store.js';
|
||||||
|
|
||||||
const {remote} = require('electron');
|
const {remote} = require('electron');
|
||||||
const contextMenu = remote.require('./menu/context-menu');
|
const contextMenu = remote.require('./menu/context-menu');
|
||||||
|
const app = require('./app')
|
||||||
|
|
||||||
lbry.showMenuIfNeeded();
|
lbry.showMenuIfNeeded();
|
||||||
|
|
||||||
|
@ -19,7 +22,9 @@ window.addEventListener('contextmenu', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
let init = function() {
|
const initialState = app.store.getState();
|
||||||
|
|
||||||
|
var init = function() {
|
||||||
window.lbry = lbry;
|
window.lbry = lbry;
|
||||||
window.lighthouse = lighthouse;
|
window.lighthouse = lighthouse;
|
||||||
let canvas = document.getElementById('canvas');
|
let canvas = document.getElementById('canvas');
|
||||||
|
@ -30,7 +35,7 @@ let init = function() {
|
||||||
|
|
||||||
function onDaemonReady() {
|
function onDaemonReady() {
|
||||||
window.sessionStorage.setItem('loaded', 'y'); //once we've made it here once per session, we don't need to show splash again
|
window.sessionStorage.setItem('loaded', 'y'); //once we've made it here once per session, we don't need to show splash again
|
||||||
ReactDOM.render(<div>{ lbryio.enabled ? <AuthOverlay/> : '' }<App /><SnackBar /></div>, canvas)
|
ReactDOM.render(<Provider store={store}><div>{ lbryio.enabled ? <AuthOverlay/> : '' }<App /><SnackBar /></div></Provider>, canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.sessionStorage.getItem('loaded') == 'y') {
|
if (window.sessionStorage.getItem('loaded') == 'y') {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import lbry from '../lbry.js';
|
import lbry from '../lbry.js';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {FormField} from '../component/form.js';
|
import {FormField} from '../component/form.js';
|
||||||
import {Link} from '../component/link.js';
|
import Link from '../component/link';
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const {ipcRenderer} = require('electron');
|
const {ipcRenderer} = require('electron');
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbryio from '../lbryio.js';
|
import lbry from '../lbry.js';
|
||||||
import {FileTile, FileTileStream} from '../component/file-tile.js';
|
import lighthouse from '../lighthouse.js';
|
||||||
|
import {FileTile} from '../component/file-tile.js';
|
||||||
|
import Link from 'component/link';
|
||||||
import {ToolTip} from '../component/tooltip.js';
|
import {ToolTip} from '../component/tooltip.js';
|
||||||
|
|
||||||
const communityCategoryToolTipText = ('Community Content is a public space where anyone can share content with the ' +
|
const communityCategoryToolTipText = ('Community Content is a public space where anyone can share content with the ' +
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from '../lbry.js';
|
import lbry from '../lbry.js';
|
||||||
import lbryuri from '../lbryuri.js';
|
import lbryuri from '../lbryuri.js';
|
||||||
import {Link} from '../component/link.js';
|
import Link from 'component/link';
|
||||||
import {FormField} from '../component/form.js';
|
import FormField from '../component/form.js';
|
||||||
import {SubHeader} from '../component/header.js';
|
import {SubHeader} from '../component/header.js';
|
||||||
import {FileTileStream} from '../component/file-tile.js';
|
import {FileTileStream} from '../component/file-tile.js';
|
||||||
import rewards from '../rewards.js';
|
import rewards from '../rewards.js';
|
||||||
|
|
7
ui/js/page/help/index.jsx
Normal file
7
ui/js/page/help/index.jsx
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect
|
||||||
|
} from 'react-redux'
|
||||||
|
import HelpPage from './view'
|
||||||
|
|
||||||
|
export default connect(null, null)(HelpPage)
|
|
@ -1,10 +1,9 @@
|
||||||
//@TODO: Customize advice based on OS
|
//@TODO: Customize advice based on OS
|
||||||
//@TODO: Customize advice based on OS
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from '../lbry.js';
|
import lbry from 'lbry.js';
|
||||||
import {Link} from '../component/link.js';
|
import Link from 'component/link';
|
||||||
import {SettingsNav} from './settings.js';
|
import {SettingsNav} from './settings.js';
|
||||||
import {version as uiVersion} from 'json!../../package.json';
|
import {version as uiVersion} from 'json!../../../package.json';
|
||||||
|
|
||||||
var HelpPage = React.createClass({
|
var HelpPage = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -119,4 +118,4 @@ var HelpPage = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default HelpPage;
|
export default HelpPage;
|
|
@ -1,9 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from '../lbry.js';
|
import lbry from '../lbry.js';
|
||||||
import {FormField, FormRow} from '../component/form.js';
|
import FormField from '../component/form.js';
|
||||||
import {Link} from '../component/link.js';
|
import Link from 'component/link';
|
||||||
import rewards from '../rewards.js';
|
import rewards from '../rewards.js';
|
||||||
import lbryio from '../lbryio.js';
|
|
||||||
import Modal from '../component/modal.js';
|
import Modal from '../component/modal.js';
|
||||||
|
|
||||||
var PublishPage = React.createClass({
|
var PublishPage = React.createClass({
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Link} from '../component/link.js';
|
import Link from 'component/link';
|
||||||
import Modal from '../component/modal.js';
|
import Modal from '../component/modal.js';
|
||||||
import lbry from '../lbry.js';
|
import lbry from '../lbry.js';
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import lbryuri from '../lbryuri.js';
|
||||||
import {Video} from '../page/watch.js'
|
import {Video} from '../page/watch.js'
|
||||||
import {TruncatedText, Thumbnail, FilePrice, BusyMessage} from '../component/common.js';
|
import {TruncatedText, Thumbnail, FilePrice, BusyMessage} from '../component/common.js';
|
||||||
import {FileActions} from '../component/file-actions.js';
|
import {FileActions} from '../component/file-actions.js';
|
||||||
import {Link} from '../component/link.js';
|
import Link from '../component/link';
|
||||||
import UriIndicator from '../component/channel-indicator.js';
|
import UriIndicator from '../component/channel-indicator.js';
|
||||||
|
|
||||||
var FormatItem = React.createClass({
|
var FormatItem = React.createClass({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from '../lbry.js';
|
import lbry from '../lbry.js';
|
||||||
import {Link} from '../component/link.js';
|
import Link from 'component/link';
|
||||||
import Modal from '../component/modal.js';
|
import Modal from '../component/modal.js';
|
||||||
import {SubHeader} from '../component/header.js';
|
import {SubHeader} from '../component/header.js';
|
||||||
import {FormField, FormRow} from '../component/form.js';
|
import {FormField, FormRow} from '../component/form.js';
|
||||||
|
|
|
@ -1,215 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import {Icon, Thumbnail, FilePrice} from '../component/common.js';
|
|
||||||
import {Link} from '../component/link.js';
|
|
||||||
import lbry from '../lbry.js';
|
|
||||||
import Modal from '../component/modal.js';
|
|
||||||
import lbryio from '../lbryio.js';
|
|
||||||
import rewards from '../rewards.js';
|
|
||||||
import LoadScreen from '../component/load_screen.js'
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const VideoStream = require('videostream');
|
|
||||||
|
|
||||||
export let WatchLink = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
uri: React.PropTypes.string,
|
|
||||||
metadata: React.PropTypes.object,
|
|
||||||
downloadStarted: React.PropTypes.bool,
|
|
||||||
onGet: React.PropTypes.func,
|
|
||||||
},
|
|
||||||
getInitialState: function() {
|
|
||||||
affirmedPurchase: false
|
|
||||||
},
|
|
||||||
play: function() {
|
|
||||||
lbry.get({uri: this.props.uri}).then((streamInfo) => {
|
|
||||||
if (streamInfo === null || typeof streamInfo !== 'object') {
|
|
||||||
this.setState({
|
|
||||||
modal: 'timedOut',
|
|
||||||
attemptingDownload: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
lbryio.call('file', 'view', {
|
|
||||||
uri: this.props.uri,
|
|
||||||
outpoint: streamInfo.outpoint,
|
|
||||||
claimId: streamInfo.claim_id
|
|
||||||
}).catch(() => {})
|
|
||||||
});
|
|
||||||
if (this.props.onGet) {
|
|
||||||
this.props.onGet()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onWatchClick: function() {
|
|
||||||
this.setState({
|
|
||||||
loading: true
|
|
||||||
});
|
|
||||||
lbry.getCostInfo(this.props.uri).then(({cost}) => {
|
|
||||||
lbry.getBalance((balance) => {
|
|
||||||
if (cost > balance) {
|
|
||||||
this.setState({
|
|
||||||
modal: 'notEnoughCredits',
|
|
||||||
attemptingDownload: false,
|
|
||||||
});
|
|
||||||
} else if (cost <= 0.01) {
|
|
||||||
this.play()
|
|
||||||
} else {
|
|
||||||
lbry.file_list({outpoint: this.props.outpoint}).then((fileInfo) => {
|
|
||||||
if (fileInfo) { // Already downloaded
|
|
||||||
this.play();
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
modal: 'affirmPurchase'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
modal: null,
|
|
||||||
loading: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
closeModal: function() {
|
|
||||||
this.setState({
|
|
||||||
loading: false,
|
|
||||||
modal: null,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
render: function() {
|
|
||||||
return (<div>
|
|
||||||
<Link button={ this.props.button ? this.props.button : null }
|
|
||||||
disabled={this.state.loading}
|
|
||||||
label={this.props.label ? this.props.label : ""}
|
|
||||||
className={this.props.className}
|
|
||||||
icon="icon-play"
|
|
||||||
onClick={this.onWatchClick} />
|
|
||||||
<Modal contentLabel="Not enough credits" isOpen={this.state.modal == 'notEnoughCredits'} onConfirmed={this.closeModal}>
|
|
||||||
You don't have enough LBRY credits to pay for this stream.
|
|
||||||
</Modal>
|
|
||||||
<Modal type="confirm" isOpen={this.state.modal == 'affirmPurchase'}
|
|
||||||
contentLabel="Confirm Purchase" onConfirmed={this.play} onAborted={this.closeModal}>
|
|
||||||
Are you sure you'd like to buy <strong>{this.props.metadata.title}</strong> for <strong><FilePrice uri={this.props.uri} metadata={this.props.metadata} label={false} look="plain" /></strong> credits?
|
|
||||||
</Modal>
|
|
||||||
</div>);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
export let Video = React.createClass({
|
|
||||||
_isMounted: false,
|
|
||||||
_controlsHideDelay: 3000, // Note: this needs to be shorter than the built-in delay in Electron, or Electron will hide the controls before us
|
|
||||||
_controlsHideTimeout: null,
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
uri: React.PropTypes.string.isRequired,
|
|
||||||
metadata: React.PropTypes.object,
|
|
||||||
outpoint: React.PropTypes.string,
|
|
||||||
},
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
downloadStarted: false,
|
|
||||||
readyToPlay: false,
|
|
||||||
isPlaying: false,
|
|
||||||
isPurchased: false,
|
|
||||||
loadStatusMessage: "Requesting stream... it may sit here for like 15-20 seconds in a really awkward way... we're working on it",
|
|
||||||
mimeType: null,
|
|
||||||
controlsShown: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
onGet: function() {
|
|
||||||
lbry.get({uri: this.props.uri}).then((fileInfo) => {
|
|
||||||
this.updateLoadStatus();
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
isPlaying: true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
componentDidMount: function() {
|
|
||||||
if (this.props.autoplay) {
|
|
||||||
this.start()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleMouseMove: function() {
|
|
||||||
if (this._controlsTimeout) {
|
|
||||||
clearTimeout(this._controlsTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.state.controlsShown) {
|
|
||||||
this.setState({
|
|
||||||
controlsShown: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this._controlsTimeout = setTimeout(() => {
|
|
||||||
if (!this.isMounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
controlsShown: false,
|
|
||||||
});
|
|
||||||
}, this._controlsHideDelay);
|
|
||||||
},
|
|
||||||
handleMouseLeave: function() {
|
|
||||||
if (this._controlsTimeout) {
|
|
||||||
clearTimeout(this._controlsTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.controlsShown) {
|
|
||||||
this.setState({
|
|
||||||
controlsShown: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateLoadStatus: function() {
|
|
||||||
lbry.file_list({
|
|
||||||
outpoint: this.props.outpoint,
|
|
||||||
full_status: true,
|
|
||||||
}).then(([status]) => {
|
|
||||||
if (!status || status.written_bytes == 0) {
|
|
||||||
// Download hasn't started yet, so update status message (if available) then try again
|
|
||||||
// TODO: Would be nice to check if we have the MOOV before starting playing
|
|
||||||
if (status) {
|
|
||||||
this.setState({
|
|
||||||
loadStatusMessage: status.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setTimeout(() => { this.updateLoadStatus() }, 250);
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
readyToPlay: true,
|
|
||||||
mimeType: status.mime_type,
|
|
||||||
})
|
|
||||||
const mediaFile = {
|
|
||||||
createReadStream: function (opts) {
|
|
||||||
// Return a readable stream that provides the bytes
|
|
||||||
// between offsets "start" and "end" inclusive
|
|
||||||
console.log('Stream between ' + opts.start + ' and ' + opts.end + '.');
|
|
||||||
return fs.createReadStream(status.download_path, opts)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
rewards.claimNextPurchaseReward()
|
|
||||||
|
|
||||||
var elem = this.refs.video;
|
|
||||||
var videostream = VideoStream(mediaFile, elem);
|
|
||||||
elem.play();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<div className={"video " + this.props.className + (this.state.isPlaying && this.state.readyToPlay ? " video--active" : " video--hidden")}>{
|
|
||||||
this.state.isPlaying ?
|
|
||||||
!this.state.readyToPlay ?
|
|
||||||
<span>this is the world's worst loading screen and we shipped our software with it anyway... <br/><br/>{this.state.loadStatusMessage}</span> :
|
|
||||||
<video controls id="video" ref="video"></video> :
|
|
||||||
<div className="video__cover" style={{backgroundImage: 'url("' + this.props.metadata.thumbnail + '")'}}>
|
|
||||||
<WatchLink className="video__play-button" uri={this.props.uri} metadata={this.props.metadata} outpoint={this.props.outpoint} onGet={this.onGet} icon="icon-play"></WatchLink>
|
|
||||||
</div>
|
|
||||||
}</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
105
ui/js/reducers/app.js
Normal file
105
ui/js/reducers/app.js
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import * as types from 'constants/action_types'
|
||||||
|
|
||||||
|
const reducers = {}
|
||||||
|
const defaultState = {
|
||||||
|
isLoaded: false,
|
||||||
|
currentPage: 'discover',
|
||||||
|
platform: process.platform,
|
||||||
|
drawerOpen: sessionStorage.getItem('drawerOpen') || true,
|
||||||
|
upgradeSkipped: sessionStorage.getItem('upgradeSkipped')
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.UPDATE_BALANCE] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
balance: action.data.balance
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.NAVIGATE] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
currentPage: action.data.path
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.UPGRADE_CANCELLED] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
downloadProgress: null,
|
||||||
|
downloadComplete: false,
|
||||||
|
modal: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.UPGRADE_DOWNLOAD_COMPLETED] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
downloadDir: action.data.dir,
|
||||||
|
downloadComplete: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.UPGRADE_DOWNLOAD_STARTED] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
upgradeDownloading: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.UPGRADE_DOWNLOAD_COMPLETED] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
upgradeDownloading: false,
|
||||||
|
upgradeDownloadCompleted: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.SKIP_UPGRADE] = function(state, action) {
|
||||||
|
sessionStorage.setItem('upgradeSkipped', true);
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
upgradeSkipped: true,
|
||||||
|
modal: null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.UPDATE_VERSION] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
version: action.data.version
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.OPEN_MODAL] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
modal: action.data.modal,
|
||||||
|
extraContent: action.data.errorList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.CLOSE_MODAL] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
modal: undefined,
|
||||||
|
extraContent: undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.OPEN_DRAWER] = function(state, action) {
|
||||||
|
sessionStorage.setItem('drawerOpen', false)
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
drawerOpen: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.CLOSE_DRAWER] = function(state, action) {
|
||||||
|
sessionStorage.setItem('drawerOpen', false)
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
drawerOpen: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.UPGRADE_DOWNLOAD_PROGRESSED] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
downloadProgress: action.data.percent
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function reducer(state = defaultState, action) {
|
||||||
|
const handler = reducers[action.type];
|
||||||
|
if (handler) return handler(state, action);
|
||||||
|
return state;
|
||||||
|
}
|
145
ui/js/selectors/app.js
Normal file
145
ui/js/selectors/app.js
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import { createSelector } from 'reselect'
|
||||||
|
|
||||||
|
export const _selectState = state => state.app || {}
|
||||||
|
|
||||||
|
export const selectIsLoaded = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => {
|
||||||
|
return state.isLoaded
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectCurrentPage = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => {
|
||||||
|
return state.currentPage
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectBalance = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => {
|
||||||
|
return state.balance || 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectPlatform = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => {
|
||||||
|
return state.platform
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectUpdateUrl = createSelector(
|
||||||
|
selectPlatform,
|
||||||
|
(platform) => {
|
||||||
|
switch (platform) {
|
||||||
|
case 'darwin':
|
||||||
|
return 'https://lbry.io/get/lbry.dmg';
|
||||||
|
case 'linux':
|
||||||
|
return 'https://lbry.io/get/lbry.deb';
|
||||||
|
case 'win32':
|
||||||
|
return 'https://lbry.io/get/lbry.exe';
|
||||||
|
default:
|
||||||
|
throw 'Unknown platform';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectVersion = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => {
|
||||||
|
return state.version
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectUpgradeFilename = createSelector(
|
||||||
|
selectPlatform,
|
||||||
|
selectVersion,
|
||||||
|
(platform, version) => {
|
||||||
|
switch (platform) {
|
||||||
|
case 'darwin':
|
||||||
|
return `LBRY-${version}.dmg`;
|
||||||
|
case 'linux':
|
||||||
|
return `LBRY_${version}_amd64.deb`;
|
||||||
|
case 'windows':
|
||||||
|
return `LBRY.Setup.${version}.exe`;
|
||||||
|
default:
|
||||||
|
throw 'Unknown platform';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectCurrentModal = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.modal
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectDownloadProgress = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.downloadProgress
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectDownloadComplete = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.upgradeDownloadCompleted
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectDrawerOpen = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.drawerOpen
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectHeaderLinks = createSelector(
|
||||||
|
selectCurrentPage,
|
||||||
|
(page) => {
|
||||||
|
switch(page)
|
||||||
|
{
|
||||||
|
case 'wallet':
|
||||||
|
case 'send':
|
||||||
|
case 'receive':
|
||||||
|
case 'claim':
|
||||||
|
case 'referral':
|
||||||
|
return {
|
||||||
|
'?wallet' : 'Overview',
|
||||||
|
'?send' : 'Send',
|
||||||
|
'?receive' : 'Receive',
|
||||||
|
'?claim' : 'Claim Beta Code',
|
||||||
|
'?referral' : 'Check Referral Credit',
|
||||||
|
};
|
||||||
|
case 'downloaded':
|
||||||
|
case 'published':
|
||||||
|
return {
|
||||||
|
'?downloaded': 'Downloaded',
|
||||||
|
'?published': 'Published',
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectUpgradeSkipped = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.upgradeSkipped
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectUpgradeDownloadDir = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.downloadDir
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectUpgradeDownloadItem = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.downloadItem
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectSearchTerm = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.searchTerm
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectError = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.error
|
||||||
|
)
|
37
ui/js/store.js
Normal file
37
ui/js/store.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
const redux = require('redux');
|
||||||
|
const thunk = require("redux-thunk").default;
|
||||||
|
const env = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createLogger
|
||||||
|
} from 'redux-logger'
|
||||||
|
import appReducer from 'reducers/app';
|
||||||
|
|
||||||
|
function isFunction(object) {
|
||||||
|
return typeof object === 'function';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNotFunction(object) {
|
||||||
|
return !isFunction(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
const reducers = redux.combineReducers({
|
||||||
|
app: appReducer,
|
||||||
|
});
|
||||||
|
|
||||||
|
var middleware = [thunk]
|
||||||
|
|
||||||
|
if (env === 'development') {
|
||||||
|
const logger = createLogger({
|
||||||
|
collapsed: true
|
||||||
|
});
|
||||||
|
middleware.push(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
var createStoreWithMiddleware = redux.compose(
|
||||||
|
redux.applyMiddleware(...middleware)
|
||||||
|
)(redux.createStore);
|
||||||
|
|
||||||
|
var reduxStore = createStoreWithMiddleware(reducers);
|
||||||
|
|
||||||
|
export default reduxStore;
|
|
@ -27,6 +27,11 @@
|
||||||
"react": "^15.4.0",
|
"react": "^15.4.0",
|
||||||
"react-dom": "^15.4.0",
|
"react-dom": "^15.4.0",
|
||||||
"react-modal": "^1.5.2",
|
"react-modal": "^1.5.2",
|
||||||
|
"react-redux": "^5.0.3",
|
||||||
|
"redux": "^3.6.0",
|
||||||
|
"redux-logger": "^3.0.1",
|
||||||
|
"redux-thunk": "^2.2.0",
|
||||||
|
"reselect": "^3.0.0",
|
||||||
"videostream": "^2.4.2"
|
"videostream": "^2.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const appPath = path.resolve(__dirname, 'js');
|
||||||
|
|
||||||
const PATHS = {
|
const PATHS = {
|
||||||
app: path.join(__dirname, 'app'),
|
app: path.join(__dirname, 'app'),
|
||||||
|
@ -13,6 +14,10 @@ module.exports = {
|
||||||
filename: "bundle.js"
|
filename: "bundle.js"
|
||||||
},
|
},
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
|
resolve: {
|
||||||
|
root: appPath,
|
||||||
|
extensions: ['', '.js', '.jsx', '.css'],
|
||||||
|
},
|
||||||
module: {
|
module: {
|
||||||
preLoaders: [
|
preLoaders: [
|
||||||
{
|
{
|
||||||
|
@ -25,8 +30,8 @@ module.exports = {
|
||||||
loaders: [
|
loaders: [
|
||||||
{ test: /\.css$/, loader: "style!css" },
|
{ test: /\.css$/, loader: "style!css" },
|
||||||
{
|
{
|
||||||
test: /\.jsx?$/,
|
test: /\.jsx?$/,
|
||||||
loader: 'babel',
|
loader: 'babel',
|
||||||
query: {
|
query: {
|
||||||
cacheDirectory: true,
|
cacheDirectory: true,
|
||||||
presets:[ 'es2015', 'react', 'stage-2' ]
|
presets:[ 'es2015', 'react', 'stage-2' ]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const appPath = path.resolve(__dirname, 'js');
|
||||||
|
|
||||||
const PATHS = {
|
const PATHS = {
|
||||||
app: path.join(__dirname, 'app'),
|
app: path.join(__dirname, 'app'),
|
||||||
|
@ -16,6 +17,10 @@ module.exports = {
|
||||||
debug: true,
|
debug: true,
|
||||||
cache: true,
|
cache: true,
|
||||||
devtool: 'eval',
|
devtool: 'eval',
|
||||||
|
resolve: {
|
||||||
|
root: appPath,
|
||||||
|
extensions: ['', '.js', '.jsx', '.css'],
|
||||||
|
},
|
||||||
module: {
|
module: {
|
||||||
preLoaders: [
|
preLoaders: [
|
||||||
{
|
{
|
||||||
|
@ -28,9 +33,9 @@ module.exports = {
|
||||||
loaders: [
|
loaders: [
|
||||||
{ test: /\.css$/, loader: "style!css" },
|
{ test: /\.css$/, loader: "style!css" },
|
||||||
{
|
{
|
||||||
test: /\.jsx?$/,
|
test: /\.jsx?$/,
|
||||||
loader: 'babel',
|
loader: 'babel',
|
||||||
query: {
|
query: {
|
||||||
cacheDirectory: true,
|
cacheDirectory: true,
|
||||||
presets:[ 'es2015', 'react', 'stage-2' ]
|
presets:[ 'es2015', 'react', 'stage-2' ]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue