lbryweb working with desktop

This commit is contained in:
Sean Yesmunt 2019-02-22 00:01:59 -05:00
parent 100291cf57
commit 5a97077c9f
24 changed files with 1905 additions and 106 deletions

View file

@ -37,6 +37,12 @@
} }
], ],
"func-names": ["warn", "as-needed"], "func-names": ["warn", "as-needed"],
"no-param-reassign": [
"error",
{
"props": false
}
],
"jsx-a11y/label-has-for": 0, "jsx-a11y/label-has-for": 0,
"import/prefer-default-export": 0, "import/prefer-default-export": 0,
"no-return-assign": 0, "no-return-assign": 0,
@ -52,6 +58,7 @@
"no-restricted-syntax": 0, "no-restricted-syntax": 0,
"no-empty": 0, "no-empty": 0,
"react/prefer-stateless-function": 0, "react/prefer-stateless-function": 0,
"react/sort-comp": 0 "react/sort-comp": 0,
"jsx-a11y/media-has-caption": 0
} }
} }

View file

@ -61,6 +61,7 @@
"mixpanel-browser": "^2.17.1", "mixpanel-browser": "^2.17.1",
"moment": "^2.22.0", "moment": "^2.22.0",
"node-fetch": "^2.3.0", "node-fetch": "^2.3.0",
"preprocess-loader": "^0.3.0",
"qrcode.react": "^0.8.0", "qrcode.react": "^0.8.0",
"rc-progress": "^2.0.6", "rc-progress": "^2.0.6",
"react": "^16.8.2", "react": "^16.8.2",
@ -87,6 +88,7 @@
"stream-to-blob-url": "^2.1.1", "stream-to-blob-url": "^2.1.1",
"three": "^0.93.0", "three": "^0.93.0",
"tree-kill": "^1.1.0", "tree-kill": "^1.1.0",
"video.js": "^7.2.2",
"y18n": "^4.0.0" "y18n": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
@ -121,12 +123,14 @@
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"lint-staged": "^7.0.2", "lint-staged": "^7.0.2",
"make-runnable": "^1.3.6", "make-runnable": "^1.3.6",
"node-libs-browser": "^2.1.0",
"node-loader": "^0.6.0", "node-loader": "^0.6.0",
"node-sass": "^4.11.0", "node-sass": "^4.11.0",
"prettier": "^1.11.1", "prettier": "^1.11.1",
"sass-loader": "^6.0.7", "sass-loader": "^6.0.7",
"webpack": "^3.10.0", "webpack": "^3.10.0",
"webpack-build-notifier": "^0.1.23", "webpack-build-notifier": "^0.1.23",
"webpack-cli": "^2.0.0",
"yarnhook": "^0.2.0" "yarnhook": "^0.2.0"
}, },
"engines": { "engines": {

View file

@ -1,5 +1,6 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
// Module imports // Module imports
// @if TARGET='app'
import keytar from 'keytar'; import keytar from 'keytar';
import SemVer from 'semver'; import SemVer from 'semver';
import url from 'url'; import url from 'url';
@ -99,10 +100,12 @@ app.on('ready', async () => {
if (isDev) { if (isDev) {
await installExtensions(); await installExtensions();
} }
rendererWindow = createWindow(appState); rendererWindow = createWindow(appState);
rendererWindow.webContents.on('devtools-opened', () => { rendererWindow.webContents.on('devtools-opened', () => {
rendererWindow.webContents.send('devtools-is-opened'); rendererWindow.webContents.send('devtools-is-opened');
}); });
tray = createTray(rendererWindow); tray = createTray(rendererWindow);
// HACK: patch webrequest to fix devtools incompatibility with electron 2.x. // HACK: patch webrequest to fix devtools incompatibility with electron 2.x.
// See https://github.com/electron/electron/issues/13008#issuecomment-400261941 // See https://github.com/electron/electron/issues/13008#issuecomment-400261941
@ -324,3 +327,4 @@ const isSecondInstance = app.makeSingleInstance(argv => {
if (isSecondInstance) { if (isSecondInstance) {
app.exit(); app.exit();
} }
// @endif

View file

@ -1,15 +1,30 @@
/* eslint-disable no-redeclare */
import store from 'store'; import store from 'store';
import { remote } from 'electron';
import Path from 'path'; import Path from 'path';
// @if TARGET='app'
import y18n from 'y18n'; import y18n from 'y18n';
import { remote } from 'electron';
import isDev from 'electron-is-dev'; import isDev from 'electron-is-dev';
// @endif
// @if TARGET='web'
import { y18n } from 'web/stubs';
// @endif
// @if TARGET='app'
const env = process.env.NODE_ENV || 'production'; const env = process.env.NODE_ENV || 'production';
const i18n = y18n({ const i18n = y18n({
directory: Path.join(remote.app.getAppPath(), '/../static/locales').replace(/\\/g, '\\\\'), directory: Path.join(remote.app.getAppPath(), '/../static/locales').replace(/\\/g, '\\\\'),
updateFiles: false, updateFiles: false,
locale: 'en', locale: 'en',
}); });
// @endif
// @if TARGET='web'
const env = process.env.NODE_ENV || 'development';
const i18n = y18n({
updateFiles: false,
locale: 'en',
});
// @endif
const logs = []; const logs = [];
const app = { const app = {
@ -22,6 +37,7 @@ const app = {
}, },
}; };
// @if TARGET='app'
// Workaround for https://github.com/electron-userland/electron-webpack/issues/52 // Workaround for https://github.com/electron-userland/electron-webpack/issues/52
if (!isDev) { if (!isDev) {
window.staticResourcesPath = Path.join(remote.app.getAppPath(), '../static').replace( window.staticResourcesPath = Path.join(remote.app.getAppPath(), '../static').replace(
@ -31,6 +47,7 @@ if (!isDev) {
} else { } else {
window.staticResourcesPath = ''; window.staticResourcesPath = '';
} }
// @endif
// eslint-disable-next-line no-underscore-dangle // eslint-disable-next-line no-underscore-dangle
global.__ = i18n.__; global.__ = i18n.__;
@ -42,3 +59,4 @@ global.app = app;
global.store = app.store; global.store = app.store;
export default app; export default app;
/* eslint-enable no-redeclare */

View file

@ -5,7 +5,16 @@ import path from 'path';
import React from 'react'; import React from 'react';
import Button from 'component/button'; import Button from 'component/button';
import parseData from 'util/parse-data'; import parseData from 'util/parse-data';
/* eslint-disable no-redeclare */
// @if TARGET='app'
// $FlowFixMe
import { remote } from 'electron'; import { remote } from 'electron';
// @endif
// @if TARGET='web'
// $FlowFixMe
import { remote } from 'web/stubs';
// @endif
/* eslint-enable no-redeclare */
type Props = { type Props = {
data: Array<any>, data: Array<any>,

View file

@ -1,6 +1,15 @@
// @flow // @flow
import React from 'react'; import * as React from 'react';
/* eslint-disable no-redeclare */
// @if TARGET='app'
// $FlowFixMe
import { remote } from 'electron'; import { remote } from 'electron';
// @endif
// @if TARGET='web'
// $FlowFixMe
import { remote } from 'web/stubs';
// @endif
/* eslint-enable no-redeclare */
import Button from 'component/button'; import Button from 'component/button';
import { FormField } from 'component/common/form'; import { FormField } from 'component/common/form';
import path from 'path'; import path from 'path';
@ -24,9 +33,14 @@ class FileSelector extends React.PureComponent<Props> {
type: 'file', type: 'file',
}; };
fileInput: { current: React.ElementRef<any> };
constructor() { constructor() {
super(); super();
this.input = null; this.input = null;
// @if TARGET='web'
this.fileInput = React.createRef();
// @endif
} }
handleButtonClick() { handleButtonClick() {
@ -54,6 +68,20 @@ class FileSelector extends React.PureComponent<Props> {
); );
} }
handleFileInputSelection() {
const { files } = this.fileInput.current;
if (!files) {
return;
}
const filePath = files[0];
const fileName = filePath.name;
if (this.props.onFileChosen) {
this.props.onFileChosen(filePath, fileName);
}
}
input: ?HTMLInputElement; input: ?HTMLInputElement;
render() { render() {
@ -63,6 +91,8 @@ class FileSelector extends React.PureComponent<Props> {
type === 'file' ? fileLabel || __('Choose File') : directoryLabel || __('Choose Directory'); type === 'file' ? fileLabel || __('Choose File') : directoryLabel || __('Choose Directory');
return ( return (
<React.Fragment>
{/* @if TARGET='app' */}
<FormField <FormField
webkitdirectory="true" webkitdirectory="true"
className="form-field--copyable" className="form-field--copyable"
@ -79,6 +109,11 @@ class FileSelector extends React.PureComponent<Props> {
<Button button="primary" onClick={() => this.handleButtonClick()} label={label} /> <Button button="primary" onClick={() => this.handleButtonClick()} label={label} />
} }
/> />
{/* @endif */}
{/* @if TARGET='web' */}
<input type="file" ref={this.fileInput} onChange={() => this.handleFileInputSelection()} />
{/* @endif */}
</React.Fragment>
); );
} }
} }

View file

@ -7,9 +7,11 @@ import ThreeViewer from 'component/viewers/threeViewer';
import DocumentViewer from 'component/viewers/documentViewer'; import DocumentViewer from 'component/viewers/documentViewer';
import DocxViewer from 'component/viewers/docxViewer'; import DocxViewer from 'component/viewers/docxViewer';
import HtmlViewer from 'component/viewers/htmlViewer'; import HtmlViewer from 'component/viewers/htmlViewer';
import AudioVideoViewer from 'component/viewers/audioVideoViewer';
type Props = { type Props = {
mediaType: string, mediaType: string,
poster?: string,
source: { source: {
stream: string => void, stream: string => void,
fileName: string, fileName: string,
@ -54,6 +56,7 @@ class FileRender extends React.PureComponent<Props> {
// Process command // Process command
let message = {}; let message = {};
try { try {
// $FlowFixMe
message = JSON.parse(/^\$LBRY_IPC:(.*)/.exec(e.message)[1]); message = JSON.parse(/^\$LBRY_IPC:(.*)/.exec(e.message)[1]);
} catch (err) {} } catch (err) {}
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -88,10 +91,10 @@ class FileRender extends React.PureComponent<Props> {
} }
renderViewer() { renderViewer() {
const { source, mediaType, currentTheme } = this.props; const { source, mediaType, currentTheme, poster } = this.props;
// Extract relevant data to render file // Extract relevant data to render file
const { stream, fileType, contentType, downloadPath } = source; const { stream, fileType, contentType, downloadPath, fileName } = source;
// Human-readable files (scripts and plain-text files) // Human-readable files (scripts and plain-text files)
const readableFiles = ['text', 'document', 'script']; const readableFiles = ['text', 'document', 'script'];
@ -112,6 +115,14 @@ class FileRender extends React.PureComponent<Props> {
webpreferences="sandbox=true,contextIsolation=true,webviewTag=false,enableRemoteModule=false,devTools=false" webpreferences="sandbox=true,contextIsolation=true,webviewTag=false,enableRemoteModule=false,devTools=false"
/> />
), ),
video: (
<AudioVideoViewer
source={{ downloadPath, fileName }}
contentType={contentType}
poster={poster}
/>
),
audio: <AudioVideoViewer source={{ downloadPath, fileName }} contentType={contentType} />,
// Add routes to viewer... // Add routes to viewer...
}; };

View file

@ -53,7 +53,6 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
componentDidMount() { componentDidMount() {
const { checkDaemonVersion } = this.props; const { checkDaemonVersion } = this.props;
this.adjustErrorTimeout(); this.adjustErrorTimeout();
Lbry.connect() Lbry.connect()
.then(checkDaemonVersion) .then(checkDaemonVersion)
@ -93,12 +92,21 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
} }
updateStatus() { updateStatus() {
// @if TARGET='app'
Lbry.status().then(status => { Lbry.status().then(status => {
this.updateStatusCallback(status); this.updateStatusCallback(status);
}); });
// @endif
// @if TARGET='web'
Lbry.status().then(status => {
Lbry.account_list().then(accountList => {
this.updateStatusCallback(status, accountList);
});
});
// @endif
} }
updateStatusCallback(status: Status) { updateStatusCallback(status: Status, accountList: any) {
const { notifyUnlockWallet, authenticate, modal } = this.props; const { notifyUnlockWallet, authenticate, modal } = this.props;
const { launchedModal } = this.state; const { launchedModal } = this.state;
@ -117,11 +125,21 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
const { wallet, blockchain_headers: blockchainHeaders } = status; const { wallet, blockchain_headers: blockchainHeaders } = status;
// If the wallet is locked, stop doing anything and make the user input their password // If the wallet is locked, stop doing anything and make the user input their password
// @if TARGET='app'
if (wallet && wallet.is_locked) { if (wallet && wallet.is_locked) {
// Clear the error timeout, it might sit on this step for a while until someone enters their password // Clear the error timeout, it might sit on this step for a while until someone enters their password
if (this.timeout) { if (this.timeout) {
clearTimeout(this.timeout); clearTimeout(this.timeout);
} }
// @endif
// @if TARGET='web'
console.log('I think web needs this', accountList);
// if (account_list && account_list.encrypted) {
// this.setState({
// isRunning: true,
// });
// @endif
// Make sure there isn't another active modal (like INCOMPATIBLE_DAEMON) // Make sure there isn't another active modal (like INCOMPATIBLE_DAEMON)
if (launchedModal === false && !modal) { if (launchedModal === false && !modal) {

View file

@ -0,0 +1,67 @@
// @flow
import React from 'react';
import { stopContextMenu } from 'util/context-menu';
import videojs from 'video.js';
import 'video.js/dist/video-js.css';
type Props = {
source: {
downloadPath: string,
fileName: string,
},
contentType: string,
poster?: string,
};
class AudioVideoViewer extends React.PureComponent<Props> {
videoNode: ?HTMLVideoElement;
player: ?{ dispose: () => void };
componentDidMount() {
const { source, contentType, poster } = this.props;
const { downloadPath, fileName } = source;
const indexOfFileName = downloadPath.indexOf(fileName);
const basePath = downloadPath.slice(0, indexOfFileName);
const encodedFileName = encodeURIComponent(fileName);
// We only want to encode the fileName so forward slashes "/" are handled properly by the file system
// TODO: Determine changes needed for windows
const path = `${basePath}${encodedFileName}`;
const sources = [
{
src: path,
type: contentType,
},
];
const videoJsOptions = {
autoplay: true,
controls: true,
preload: 'auto',
poster,
sources,
};
this.player = videojs(this.videoNode, videoJsOptions, () => {});
}
componentWillUnmount() {
if (this.player) {
this.player.dispose();
}
}
render() {
return (
<div className="file-render__viewer" onContextMenu={stopContextMenu}>
<div data-vjs-player>
<video ref={node => (this.videoNode = node)} className="video-js" />
</div>
</div>
);
}
}
export default AudioVideoViewer;

View file

@ -3,10 +3,12 @@
import App from 'component/app'; import App from 'component/app';
import SnackBar from 'component/snackBar'; import SnackBar from 'component/snackBar';
import SplashScreen from 'component/splash'; import SplashScreen from 'component/splash';
// @if TARGET='app'
import moment from 'moment'; import moment from 'moment';
import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
import { ipcRenderer, remote, shell } from 'electron'; import { ipcRenderer, remote, shell } from 'electron';
import * as ACTIONS from 'constants/action_types';
// @endif
import * as MODALS from 'constants/modal_types';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
@ -28,10 +30,12 @@ import app from './app';
import analytics from './analytics'; import analytics from './analytics';
import doLogWarningConsoleMessage from './logWarningConsoleMessage'; import doLogWarningConsoleMessage from './logWarningConsoleMessage';
const { autoUpdater } = remote.require('electron-updater');
const APPPAGEURL = 'lbry://?'; const APPPAGEURL = 'lbry://?';
// @if TARGET='app'
const { autoUpdater } = remote.require('electron-updater');
autoUpdater.logger = remote.require('electron-log'); autoUpdater.logger = remote.require('electron-log');
// @endif
if (process.env.LBRY_API_URL) { if (process.env.LBRY_API_URL) {
Lbryio.setLocalApi(process.env.LBRY_API_URL); Lbryio.setLocalApi(process.env.LBRY_API_URL);
@ -74,7 +78,9 @@ Lbryio.setOverride(
const newAuthToken = response.auth_token; const newAuthToken = response.auth_token;
authToken = newAuthToken; authToken = newAuthToken;
// @if TARGET='app'
ipcRenderer.send('set-auth-token', authToken); ipcRenderer.send('set-auth-token', authToken);
// @endif
resolve(); resolve();
}); });
}) })
@ -87,12 +93,14 @@ Lbryio.setOverride(
if (authToken) { if (authToken) {
resolve(authToken); resolve(authToken);
} else { } else {
// @if TARGET='app'
ipcRenderer.once('auth-token-response', (event, token) => { ipcRenderer.once('auth-token-response', (event, token) => {
Lbryio.authToken = token; Lbryio.authToken = token;
resolve(token); resolve(token);
}); });
ipcRenderer.send('get-auth-token'); ipcRenderer.send('get-auth-token');
// @endif
} }
}) })
); );
@ -109,6 +117,7 @@ rewards.setCallback('claimRewardSuccess', () => {
app.store.dispatch(doHideModal(MODALS.REWARD_APPROVAL_REQUIRED)); app.store.dispatch(doHideModal(MODALS.REWARD_APPROVAL_REQUIRED));
}); });
// @if TARGET='app'
ipcRenderer.on('open-uri-requested', (event, uri, newSession) => { ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
if (uri && uri.startsWith('lbry://')) { if (uri && uri.startsWith('lbry://')) {
if (uri.startsWith('lbry://?verify')) { if (uri.startsWith('lbry://?verify')) {
@ -146,6 +155,7 @@ ipcRenderer.on('devtools-is-opened', () => {
const logOnDevelopment = false; const logOnDevelopment = false;
doLogWarningConsoleMessage(logOnDevelopment); doLogWarningConsoleMessage(logOnDevelopment);
}); });
// @endif
document.addEventListener('dragover', event => { document.addEventListener('dragover', event => {
event.preventDefault(); event.preventDefault();
@ -178,15 +188,18 @@ document.addEventListener('click', event => {
} }
} }
if (target.matches('a[href^="http"]') || target.matches('a[href^="mailto"]')) { if (target.matches('a[href^="http"]') || target.matches('a[href^="mailto"]')) {
// @if TARGET='app'
event.preventDefault(); event.preventDefault();
shell.openExternal(target.href); shell.openExternal(target.href);
return; return;
// @endif
} }
target = target.parentNode; target = target.parentNode;
} }
}); });
const init = () => { const init = () => {
// @if TARGET='app'
moment.locale(remote.app.getLocale()); moment.locale(remote.app.getLocale());
autoUpdater.on('error', error => { autoUpdater.on('error', error => {
@ -210,6 +223,7 @@ const init = () => {
app.store.dispatch(doUpdateIsNightAsync()); app.store.dispatch(doUpdateIsNightAsync());
app.store.dispatch(doDownloadLanguages()); app.store.dispatch(doDownloadLanguages());
app.store.dispatch(doBlackListedOutpointsSubscribe()); app.store.dispatch(doBlackListedOutpointsSubscribe());
// @endif
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
@ -224,6 +238,9 @@ const init = () => {
</Provider>, </Provider>,
document.getElementById('app') document.getElementById('app')
); );
// @if TARGET='web'
// window.sessionStorage.removeItem('loaded');
// @endif
} }
if (window.sessionStorage.getItem('loaded') === 'y') { if (window.sessionStorage.getItem('loaded') === 'y') {

View file

@ -4,12 +4,19 @@ const Native = {};
Native.getAppVersionInfo = () => Native.getAppVersionInfo = () =>
new Promise(resolve => { new Promise(resolve => {
// @if TARGET='app'
ipcRenderer.once('version-info-received', (event, versionInfo) => { ipcRenderer.once('version-info-received', (event, versionInfo) => {
resolve(versionInfo); resolve(versionInfo);
}); });
ipcRenderer.send('version-info-requested'); ipcRenderer.send('version-info-requested');
// @endif
}); });
// @if TARGET='app'
Native.imagePath = file => `${staticResourcesPath}/img/${file}`; Native.imagePath = file => `${staticResourcesPath}/img/${file}`;
// @endif
// @if TARGET='web'
Native.imagePath = file => `staticResourcesPath/img/${file}`;
// @endif
export default Native; export default Native;

View file

@ -1,7 +1,9 @@
// @if TARGET='app'
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import isDev from 'electron-is-dev'; import isDev from 'electron-is-dev';
import path from 'path'; import path from 'path';
import { ipcRenderer, remote } from 'electron'; import { ipcRenderer, remote } from 'electron';
// @endif
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types'; import * as MODALS from 'constants/modal_types';
import { Lbry, doBalanceSubscribe, doFetchFileInfosAndPublishedClaims, doError } from 'lbry-redux'; import { Lbry, doBalanceSubscribe, doFetchFileInfosAndPublishedClaims, doError } from 'lbry-redux';
@ -23,9 +25,11 @@ import {
import { doAuthenticate } from 'lbryinc'; import { doAuthenticate } from 'lbryinc';
import { lbrySettings as config, version as appVersion } from 'package.json'; import { lbrySettings as config, version as appVersion } from 'package.json';
// @if TARGET='app'
const { autoUpdater } = remote.require('electron-updater'); const { autoUpdater } = remote.require('electron-updater');
const { download } = remote.require('electron-dl'); const { download } = remote.require('electron-dl');
const Fs = remote.require('fs'); const Fs = remote.require('fs');
// @endif
const CHECK_UPGRADE_INTERVAL = 10 * 60 * 1000; const CHECK_UPGRADE_INTERVAL = 10 * 60 * 1000;
@ -71,6 +75,7 @@ export function doStartUpgrade() {
export function doDownloadUpgrade() { export function doDownloadUpgrade() {
return (dispatch, getState) => { return (dispatch, getState) => {
// @if TARGET='app'
const state = getState(); const state = getState();
// Make a new directory within temp directory so the filename is guaranteed to be available // Make a new directory within temp directory so the filename is guaranteed to be available
const dir = Fs.mkdtempSync(remote.app.getPath('temp') + path.sep); const dir = Fs.mkdtempSync(remote.app.getPath('temp') + path.sep);
@ -101,6 +106,7 @@ export function doDownloadUpgrade() {
}); });
dispatch(doHideModal()); dispatch(doHideModal());
dispatch(doOpenModal(MODALS.DOWNLOADING)); dispatch(doOpenModal(MODALS.DOWNLOADING));
// @endif
}; };
} }
@ -255,6 +261,7 @@ export function doCheckUpgradeSubscribe() {
export function doCheckDaemonVersion() { export function doCheckDaemonVersion() {
return dispatch => { return dispatch => {
// @if TARGET='app'
Lbry.version().then(({ lbrynet_version: lbrynetVersion }) => { Lbry.version().then(({ lbrynet_version: lbrynetVersion }) => {
// Avoid the incompatible daemon modal if running in dev mode // Avoid the incompatible daemon modal if running in dev mode
// Lets you run a different daemon than the one specified in package.json // Lets you run a different daemon than the one specified in package.json
@ -270,6 +277,12 @@ export function doCheckDaemonVersion() {
return dispatch(doOpenModal(MODALS.INCOMPATIBLE_DAEMON)); return dispatch(doOpenModal(MODALS.INCOMPATIBLE_DAEMON));
}); });
// @endif
// @if TARGET='web'
dispatch({
type: ACTIONS.DAEMON_VERSION_MATCH,
});
// @endif
}; };
} }
@ -305,12 +318,14 @@ export function doDaemonReady() {
dispatch({ type: ACTIONS.DAEMON_READY }); dispatch({ type: ACTIONS.DAEMON_READY });
dispatch(doFetchDaemonSettings()); dispatch(doFetchDaemonSettings());
dispatch(doBalanceSubscribe()); dispatch(doBalanceSubscribe());
// @if TARGET='app'
dispatch(doFetchFileInfosAndPublishedClaims()); dispatch(doFetchFileInfosAndPublishedClaims());
if (!selectIsUpgradeSkipped(state)) { if (!selectIsUpgradeSkipped(state)) {
dispatch(doCheckUpgradeAvailable()); dispatch(doCheckUpgradeAvailable());
} }
dispatch(doCheckUpgradeSubscribe()); dispatch(doCheckUpgradeSubscribe());
dispatch(doCheckSubscriptionsInit()); dispatch(doCheckSubscriptionsInit());
// @endif
}; };
} }
@ -324,14 +339,16 @@ export function doClearCache() {
export function doQuit() { export function doQuit() {
return () => { return () => {
// @if TARGET='app'
remote.app.quit(); remote.app.quit();
// @endif
}; };
} }
export function doQuitAnyDaemon() { export function doQuitAnyDaemon() {
return dispatch => { return dispatch => {
// @if TARGET='app'
Lbry.stop() Lbry.stop()
.then()
.catch(() => { .catch(() => {
try { try {
if (process.platform === 'win32') { if (process.platform === 'win32') {
@ -342,8 +359,11 @@ export function doQuitAnyDaemon() {
} catch (error) { } catch (error) {
dispatch(doAlertError(`Quitting daemon failed due to: ${error.message}`)); dispatch(doAlertError(`Quitting daemon failed due to: ${error.message}`));
} }
}); })
.finally(() => {
dispatch(doQuit()); dispatch(doQuit());
});
// @endif
}; };
} }

View file

@ -6,11 +6,9 @@ import {
doAbandonClaim, doAbandonClaim,
selectMyClaimsOutpoints, selectMyClaimsOutpoints,
selectFileInfosByOutpoint, selectFileInfosByOutpoint,
selectTotalDownloadProgress,
} from 'lbry-redux'; } from 'lbry-redux';
import { doHideModal } from 'redux/actions/app'; import { doHideModal } from 'redux/actions/app';
import { doHistoryBack } from 'redux/actions/navigation'; import { doHistoryBack } from 'redux/actions/navigation';
import setProgressBar from 'util/set-progress-bar';
export function doOpenFileInFolder(path) { export function doOpenFileInFolder(path) {
return () => { return () => {
@ -56,9 +54,6 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
outpoint, outpoint,
}, },
}); });
const totalProgress = selectTotalDownloadProgress(getState());
setProgressBar(totalProgress);
}; };
} }

View file

@ -2,7 +2,16 @@
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types'; import * as MODALS from 'constants/modal_types';
/* eslint-disable no-redeclare */
// @if TARGET='app'
// $FlowFixMe
import { remote } from 'electron'; import { remote } from 'electron';
// @endif
// @if TARGET='web'
// $FlowFixMe
import { remote } from 'web/stubs';
// @endif
/* eslint-enable no-redeclare */
const win = remote.BrowserWindow.getFocusedWindow(); const win = remote.BrowserWindow.getFocusedWindow();

View file

@ -27,10 +27,17 @@
height: 100%; height: 100%;
iframe, iframe,
webview { webview,
.video-js {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
// Removing the play button because we have autoplay turned on
// These are classes added by video.js
// .video-js .vjs-big-play-button {
// display: none;
// }
} }
.document-viewer { .document-viewer {

View file

@ -1,5 +1,12 @@
/* eslint-disable no-redeclare */
// @if TARGET='app'
import { clipboard, remote } from 'electron'; import { clipboard, remote } from 'electron';
import isDev from 'electron-is-dev'; import isDev from 'electron-is-dev';
// @endif
// @if TARGET='web'
import { remote, isDev } from 'web/stubs';
// @endif
/* eslint-enable no-redeclare */
function injectDevelopmentTemplate(event, templates) { function injectDevelopmentTemplate(event, templates) {
if (!isDev) return templates; if (!isDev) return templates;

View file

@ -1,3 +1,5 @@
/* eslint-disable no-redeclare */
// @if TARGET='app'
import { remote } from 'electron'; import { remote } from 'electron';
const application = remote.app; const application = remote.app;
@ -9,5 +11,11 @@ const setBadge = text => {
dock.setBadge(text); dock.setBadge(text);
}; };
// @endif
// @if TARGET='web'
const setBadge = () => {};
// @endif
/* eslint-enable no-redeclare */
export default setBadge; export default setBadge;

View file

@ -1,9 +0,0 @@
import { remote } from 'electron';
const win = remote.getCurrentWindow();
const setProgressBar = progress => {
win.setProgressBar(progress);
};
export default setProgressBar;

View file

@ -0,0 +1,2 @@
import '../index';
import './publish';

View file

@ -0,0 +1,67 @@
// @flow
import { Lbry } from 'lbry-redux';
function checkAndParseFix(response) {
if (response.status >= 200 && response.status < 300) {
return response.json();
}
return response.json().then(json => {
let error;
if (json.error) {
error = new Error(json.error);
} else {
error = new Error('Protocol error with unknown response signature');
}
return Promise.reject(error);
});
}
// A modified version of Lbry.apiCall that allows
// to perform calling methods at arbitrary urls
// and pass form file fields
function apiCallViaWeb(
connectionString: string,
method: string,
params: { file_path: string },
resolve: Function,
reject: Function
) {
const counter = new Date().getTime();
const fileField = params.file_path;
// Putting a dummy value here, the server is going to process the POSTed file
// and set the file_path itself
params.file_path = '__POST_FILE__';
const jsonPayload = JSON.stringify({
jsonrpc: '2.0',
method,
params,
id: counter,
});
const body = new FormData();
body.append('file', fileField);
body.append('json_payload', jsonPayload);
const options = {
method: 'POST',
body,
};
return fetch(connectionString, options)
.then(checkAndParseFix)
.then(response => {
const error = response.error || (response.result && response.result.error);
if (error) {
return reject(error);
}
return resolve(response.result);
})
.catch(reject);
}
Lbry.setOverride(
'publish',
params =>
new Promise((resolve, reject) => {
apiCallViaWeb('/storage/content/', 'publish', params, resolve, reject);
})
);

29
src/renderer/web/stubs.js Normal file
View file

@ -0,0 +1,29 @@
const callable = () => {
throw Error('Need to fix this stub');
};
const returningCallable = value => () => value;
export const remote = {
dialog: {
showOpenDialog: callable,
},
getCurrentWindow: callable,
app: {
getAppPath: callable,
},
BrowserWindow: {
getFocusedWindow: callable,
},
Menu: {
getApplicationMenu: callable,
},
require: callable,
};
export const y18n = () => ({
getLocale: returningCallable('en'),
__: value => value,
__n: value => value,
});
export const isDev = false;

114
webpack.config.js Normal file
View file

@ -0,0 +1,114 @@
const path = require('path');
const ELECTRON_RENDERER_PROCESS_ROOT = path.resolve(__dirname, 'src/renderer/');
module.exports = env => {
return {
// commented out because of webpack 3
// mode: 'development',
entry: './src/renderer/web/index.js',
output: {
path: path.resolve(__dirname, 'dist/web'),
filename: 'bundle.js',
publicPath: '/static/app/',
},
// commented out because of webpack 3
// optimization: {
// minimize: false,
// },
target: 'web',
node: {
fs: 'empty',
// electron: "empty",
'electron-is-dev': 'mock',
store: 'mock',
y18n: 'mock',
tls: 'mock',
net: 'mock',
},
module: {
rules: [
{
test: /\.jsx?$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['env', 'react', 'stage-2'],
},
},
{
loader: 'preprocess-loader',
options: {
TARGET: 'web',
LBRYNET_PROXY_URL: '/api_proxy/',
ppOptions: {
type: 'js',
},
},
},
],
exclude: /node_modules/,
},
// {
// test: /\.jsx?$/,
// loader: 'babel-loader',
// options: {
// presets: ['env', 'react', 'stage-2'],
// },
// exclude: /node_modules/,
// },
{
test: /\.scss$/,
use: [
'style-loader', // creates style nodes from JS strings
'css-loader', // translates CSS into CommonJS
'sass-loader', // compiles Sass to CSS, using Node Sass by default
],
},
{
test: /\.(png|woff|woff2|eot|ttf|svg|gif)$/,
// loader: 'url-loader?limit=100000'
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
// outputPath: 'resources/'
},
},
],
},
{
test: /\.css$/,
loader: ['style-loader', 'css-loader'],
},
],
},
resolve: {
modules: [ELECTRON_RENDERER_PROCESS_ROOT, 'node_modules', __dirname],
extensions: ['.js', '.jsx', '.scss', '.json'],
},
externals: [
(function() {
var IGNORES = [
'electron',
'breakdance',
'i18n',
// 'electron-is-dev',
// 'store',
// 'y18n',
// 'tls',
// 'net'
];
return function(context, request, callback) {
if (IGNORES.indexOf(request) >= 0) {
// return callback(null, "require('" + request + "')");
return callback(null, '{}');
}
return callback();
};
})(),
],
};
};

View file

@ -8,20 +8,32 @@ if (PROCESS_ARGV) {
PROCESS_ARGV = JSON.parse(PROCESS_ARGV); PROCESS_ARGV = JSON.parse(PROCESS_ARGV);
} }
const isDev = PROCESS_ARGV && PROCESS_ARGV.original && const isDev = PROCESS_ARGV && PROCESS_ARGV.original && PROCESS_ARGV.original.indexOf('dev') !== -1;
(PROCESS_ARGV.original.indexOf('dev') !== -1);
module.exports = { module.exports = {
// This rule is temporarily necessary until https://github.com/electron-userland/electron-webpack/issues/60 is fixed.
module: { module: {
rules: [ rules: [
{ {
test: /\.jsx?$/, test: /\.jsx?$/,
use: [
// This rule is temporarily necessary until https://github.com/electron-userland/electron-webpack/issues/60 is fixed.
{
loader: 'babel-loader', loader: 'babel-loader',
options: { options: {
presets: ['env', 'react', 'stage-2'], presets: ['env', 'react', 'stage-2'],
}, },
}, },
{
loader: 'preprocess-loader',
options: {
TARGET: 'app',
ppOptions: {
type: 'js',
},
},
},
],
},
], ],
}, },
// This allows imports to be made from the renderer process root (https://moduscreate.com/blog/es6-es2015-import-no-relative-path-webpack/). // This allows imports to be made from the renderer process root (https://moduscreate.com/blog/es6-es2015-import-no-relative-path-webpack/).
@ -29,9 +41,11 @@ module.exports = {
modules: [ELECTRON_RENDERER_PROCESS_ROOT, 'node_modules', __dirname], modules: [ELECTRON_RENDERER_PROCESS_ROOT, 'node_modules', __dirname],
extensions: ['.js', '.jsx', '.scss'], extensions: ['.js', '.jsx', '.scss'],
}, },
plugins: isDev ? [ plugins: isDev
? [
new FilewatcherPlugin({ new FilewatcherPlugin({
watchFileRegex: [require.resolve('lbry-redux'), require.resolve('lbryinc')], watchFileRegex: [require.resolve('lbry-redux'), require.resolve('lbryinc')],
}), }),
] : [], ]
: [],
}; };

1441
yarn.lock

File diff suppressed because it is too large Load diff