new build setup for web/electron

This commit is contained in:
Sean Yesmunt 2019-03-04 23:46:57 -05:00
parent 7ea2a1dade
commit de3639f29c
440 changed files with 5859 additions and 3991 deletions

9
.babelrc Normal file
View file

@ -0,0 +1,9 @@
{
"presets": ["@babel/react", "@babel/flow"],
"plugins": [
["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true }],
"@babel/plugin-transform-flow-strip-types",
"@babel/plugin-proposal-class-properties",
"babel-plugin-add-module-exports"
]
}

View file

@ -6,13 +6,6 @@
"plugin:flowtype/recommended", "plugin:flowtype/recommended",
"plugin:prettier/recommended" "plugin:prettier/recommended"
], ],
"settings": {
"import/resolver": {
"webpack": {
"config": "webpack.renderer.additions.js"
}
}
},
"parser": "babel-eslint", "parser": "babel-eslint",
"env": { "env": {
"browser": true, "browser": true,
@ -20,7 +13,7 @@
}, },
"globals": { "globals": {
"__static": true, "__static": true,
"staticResourcesPath": true, "i18n": true,
"__": true, "__": true,
"__n": true, "__n": true,
"app": true "app": true
@ -59,6 +52,7 @@
"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 "jsx-a11y/media-has-caption": 0,
"no-underscore-dangle": 0
} }
} }

2
.gitignore vendored
View file

@ -1,7 +1,7 @@
.DS_Store .DS_Store
/node_modules /node_modules
/dist /dist
/static/daemon/lbrynet* /static/lbrynet
/static/locales /static/locales
yarn-error.log yarn-error.log
package-lock.json package-lock.json

View file

@ -89,8 +89,4 @@ const downloadDaemon = targetPlatform =>
} }
}); });
module.exports = downloadDaemon; downloadDaemon();
require('make-runnable/custom')({
printOutputFrame: false,
});

View file

@ -1,49 +0,0 @@
const extract = require("i18n-extract");
const fs = require("fs");
const path = require("path");
const outputDir = `${__dirname}/../static/locales`;
const outputPath = `${outputDir}/en.json`;
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir);
}
fs.writeFile(outputPath, "{}", "utf8", err => {
if (err) {
return console.log(err);
}
const enLocale = require(outputPath);
const keys = extract.extractFromFiles("src/**/*.{js,jsx}", {
marker: "__",
});
let reports = [];
reports = reports.concat(extract.findMissing(enLocale, keys));
if (reports.length > 0) {
fs.readFile(outputPath, "utf8", (err, data) => {
if (err) {
console.log(err);
} else {
localeObj = JSON.parse(data);
for (let i = 0; i < reports.length; i++) {
// no need to care for other types than MISSING because starting file will always be empty
if (reports[i].type === "MISSING") {
localeObj[reports[i].key] = reports[i].key;
}
}
const json = JSON.stringify(localeObj, null, "\t"); // convert it back to json-string
fs.writeFile(outputPath, json, "utf8", err => {
if (err) {
throw err;
}
console.log("Extracted all strings!");
});
}
});
}
});

View file

@ -1,6 +1,9 @@
{ {
"appId": "io.lbry.LBRY", "appId": "io.lbry.LBRY",
"productName": "LBRY", "productName": "LBRY",
"directories": {
"output": "dist/electron"
},
"publish": [ "publish": [
{ {
"provider": "s3", "provider": "s3",
@ -44,6 +47,7 @@
], ],
"linux": { "linux": {
"target": "deb", "target": "deb",
"executableName": "LBRY",
"category": "AudioVideo;Video", "category": "AudioVideo;Video",
"desktop": { "desktop": {
"MimeType": "x-scheme-handler/lbry", "MimeType": "x-scheme-handler/lbry",

View file

@ -1,5 +0,0 @@
{
"renderer": {
"webpackConfig": "webpack.renderer.additions.js"
}
}

View file

@ -18,16 +18,15 @@
"name": "LBRY Inc.", "name": "LBRY Inc.",
"email": "hello@lbry.io" "email": "hello@lbry.io"
}, },
"main": "src/main/index.js", "main": "./dist/main/main.js",
"scripts": { "scripts": {
"extract-langs": "node build/extractLocals.js", "compile:electron": "webpack --progress --config webpack.electron.config.js",
"compile": "electron-webpack && yarn extract-langs", "compile:web": "webpack --progress --config webpack.web.config.js",
"compile": "yarn compile:electron && yarn compile:web",
"build": "yarn compile && electron-builder build", "build": "yarn compile && electron-builder build",
"build:dir": "yarn build -- --dir -c.compression=store -c.mac.identity=null", "build:dir": "yarn build -- --dir -c.compression=store -c.mac.identity=null",
"build:web": "webpack", "dev:electron": "yarn compile:electron && electron ./dist/electron/main/bundle.js",
"dev": "electron-webpack dev", "dev:internal-apis": "LBRY_API_URL='http://localhost:8080' yarn dev:electron",
"dev:internal-apis": "LBRY_API_URL='http://localhost:8080' yarn dev",
"dev:web": "webpack --watch",
"lint": "eslint 'src/**/*.{js,jsx}' --fix && flow", "lint": "eslint 'src/**/*.{js,jsx}' --fix && flow",
"format": "prettier 'src/**/*.{js,jsx,scss,json}' --write", "format": "prettier 'src/**/*.{js,jsx,scss,json}' --write",
"flow-defs": "flow-typed install", "flow-defs": "flow-typed install",
@ -36,10 +35,9 @@
"postinstall": "electron-builder install-app-deps && node build/downloadDaemon.js" "postinstall": "electron-builder install-app-deps && node build/downloadDaemon.js"
}, },
"dependencies": { "dependencies": {
"@lbry/components": "^2.2.4", "@babel/polyfill": "^7.2.5",
"@types/three": "^0.93.1", "@types/three": "^0.93.1",
"bluebird": "^3.5.1", "bluebird": "^3.5.1",
"breakdance": "^3.0.1",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"codemirror": "^5.39.2", "codemirror": "^5.39.2",
"country-data": "^0.0.31", "country-data": "^0.0.31",
@ -48,12 +46,12 @@
"electron-dl": "^1.11.0", "electron-dl": "^1.11.0",
"electron-is-dev": "^0.3.0", "electron-is-dev": "^0.3.0",
"electron-log": "^2.2.12", "electron-log": "^2.2.12",
"electron-updater": "^2.23.3", "electron-updater": "^4.0.0",
"electron-window-state": "^4.1.1", "electron-window-state": "^4.1.1",
"express": "^4.16.4", "express": "^4.16.4",
"formik": "^0.10.4", "formik": "^0.10.4",
"hast-util-sanitize": "^1.1.2", "hast-util-sanitize": "^1.1.2",
"keytar": "^4.2.1", "keytar": "^4.3.0",
"lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#406e1970b9d5594faf0407100c9bbed45d904cdf", "lbry-redux": "lbryio/lbry-redux#406e1970b9d5594faf0407100c9bbed45d904cdf",
"lbryinc": "lbryio/lbryinc#2334ad53e82c22d6291899785d5292347008f2a9", "lbryinc": "lbryio/lbryinc#2334ad53e82c22d6291899785d5292347008f2a9",
@ -62,8 +60,10 @@
"mime": "^2.3.1", "mime": "^2.3.1",
"mixpanel-browser": "^2.17.1", "mixpanel-browser": "^2.17.1",
"moment": "^2.22.0", "moment": "^2.22.0",
"node-abi": "^2.5.1",
"node-fetch": "^2.3.0", "node-fetch": "^2.3.0",
"preprocess-loader": "^0.3.0", "preprocess-loader": "^0.3.0",
"prop-types": "^15.6.2",
"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",
@ -86,28 +86,37 @@
"render-media": "^3.1.0", "render-media": "^3.1.0",
"reselect": "^3.0.0", "reselect": "^3.0.0",
"semver": "^5.3.0", "semver": "^5.3.0",
"source-map-support": "^0.5.4", "source-map-support": "^0.5.10",
"stream-to-blob-url": "^2.1.1",
"three": "^0.93.0", "three": "^0.93.0",
"three-full": "^11.3.2",
"tree-kill": "^1.1.0", "tree-kill": "^1.1.0",
"video.js": "^7.2.2", "video.js": "^7.2.2",
"y18n": "^4.0.0" "y18n": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"babel-eslint": "^8.2.2", "@babel/core": "^7.0.0",
"babel-plugin-module-resolver": "^3.1.1", "@babel/plugin-proposal-class-properties": "^7.0.0",
"babel-polyfill": "^6.26.0", "@babel/plugin-proposal-decorators": "^7.3.0",
"babel-preset-env": "^1.6.1", "@babel/plugin-transform-flow-strip-types": "^7.2.3",
"babel-preset-react": "^6.24.1", "@babel/preset-flow": "^7.0.0",
"babel-preset-stage-2": "^6.18.0", "@babel/preset-react": "^7.0.0",
"@lbry/color": "^1.0.2",
"@lbry/components": "^2.2.4",
"async-exit-hook": "^2.0.1",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.5",
"babel-plugin-add-module-exports": "^1.0.0",
"copy-webpack-plugin": "^4.6.0", "copy-webpack-plugin": "^4.6.0",
"css-loader": "^2.1.0",
"decompress": "^4.2.0", "decompress": "^4.2.0",
"del": "^3.0.0", "del": "^3.0.0",
"devtron": "^1.4.0", "devtron": "^1.4.0",
"electron": "^2.0.14", "electron": "^4.0.4",
"electron-builder": "^20.22.0", "electron-builder": "^20.38.4",
"electron-devtools-installer": "^2.2.3", "electron-devtools-installer": "^2.2.3",
"electron-publisher-s3": "^20.8.1", "electron-publisher-s3": "^20.8.1",
"electron-webpack": "^1.13.0", "electron-webpack": "^2.6.2",
"eslint": "^4.19.0", "eslint": "^4.19.0",
"eslint-config-airbnb": "^16.1.0", "eslint-config-airbnb": "^16.1.0",
"eslint-config-prettier": "^2.9.0", "eslint-config-prettier": "^2.9.0",
@ -121,17 +130,21 @@
"flow-bin": "^0.89.0", "flow-bin": "^0.89.0",
"flow-typed": "^2.3.0", "flow-typed": "^2.3.0",
"husky": "^0.14.3", "husky": "^0.14.3",
"i18n-extract": "^0.5.1",
"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-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",
"preprocess-loader": "^0.3.0",
"prettier": "^1.11.1", "prettier": "^1.11.1",
"sass-loader": "^6.0.7", "sass-loader": "^7.1.0",
"webpack": "^3.10.0", "style-loader": "^0.23.1",
"webpack": "^4.28.4",
"webpack-build-notifier": "^0.1.23", "webpack-build-notifier": "^0.1.23",
"webpack-dev-server": "^3.1.14",
"webpack-merge": "^4.2.1",
"webpack-node-externals": "^1.7.2",
"yarnhook": "^0.2.0" "yarnhook": "^0.2.0"
}, },
"engines": { "engines": {
@ -141,7 +154,7 @@
"lbrySettings": { "lbrySettings": {
"lbrynetDaemonVersion": "0.32.4", "lbrynetDaemonVersion": "0.32.4",
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-OSNAME.zip", "lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-OSNAME.zip",
"lbrynetDaemonDir": "static/daemon", "lbrynetDaemonDir": "static",
"lbrynetDaemonFileName": "lbrynet" "lbrynetDaemonFileName": "lbrynet"
} }
} }

View file

View file

@ -1,28 +0,0 @@
import express from 'express';
import unpackByOutpoint from './unpackByOutpoint';
// Polyfills and `lbry-redux`
global.fetch = require('node-fetch');
global.window = global;
// eslint-disable-next-line import/no-commonjs,global-require
const { Lbry } = require('lbry-redux');
delete global.window;
export default async function startSandbox() {
const sandbox = express();
const port = 5278;
sandbox.get('/set/:outpoint', async (req, res) => {
const { outpoint } = req.params;
const resolvedPath = await unpackByOutpoint(Lbry, outpoint);
sandbox.use(`/sandbox/${outpoint}/`, express.static(resolvedPath));
res.send(`/sandbox/${outpoint}/`);
});
// eslint-disable-next-line no-console
sandbox.listen(port, 'localhost', () => console.log(`Sandbox listening on port ${port}.`));
}

View file

@ -4,7 +4,7 @@ import { spawn, execSync } from 'child_process';
import { Lbry } from 'lbry-redux'; import { Lbry } from 'lbry-redux';
export default class Daemon { export default class Daemon {
static path = process.env.LBRY_DAEMON || path.join(__static, 'daemon/lbrynet'); static path = process.env.LBRY_DAEMON || path.join(__static, 'lbrynet');
subprocess; subprocess;
handlers; handlers;

View file

@ -5,15 +5,15 @@ export default window => {
let iconPath; let iconPath;
switch (process.platform) { switch (process.platform) {
case 'darwin': { case 'darwin': {
iconPath = path.join(__static, '/img/tray/mac/trayTemplate.png'); iconPath = 'static/img/tray/mac/trayTemplate.png';
break; break;
} }
case 'win32': { case 'win32': {
iconPath = path.join(__static, '/img/tray/windows/tray.ico'); iconPath = 'static/img/tray/windows/tray.ico';
break; break;
} }
default: { default: {
iconPath = path.join(__static, '/img/tray/default/tray.png'); iconPath = 'static/img/tray/default/tray.png';
} }
} }

View file

@ -1,3 +1,4 @@
import path from 'path';
import { app, BrowserWindow, dialog, shell, screen } from 'electron'; import { app, BrowserWindow, dialog, shell, screen } from 'electron';
import isDev from 'electron-is-dev'; import isDev from 'electron-is-dev';
import windowStateKeeper from 'electron-window-state'; import windowStateKeeper from 'electron-window-state';
@ -34,8 +35,8 @@ export default appState => {
}, },
}; };
const rendererURL = isDev const rendererURL = isDev // ? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`
? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}` ? `file://${path.resolve(__dirname, '../index.html')}`
: `file://${__dirname}/index.html`; : `file://${__dirname}/index.html`;
let window = new BrowserWindow(windowConfiguration); let window = new BrowserWindow(windowConfiguration);
@ -45,6 +46,8 @@ export default appState => {
// and restore the maximized or full screen state. // and restore the maximized or full screen state.
windowState.manage(window); windowState.manage(window);
console.log('url', rendererURL);
console.log('window', window);
window.loadURL(rendererURL); window.loadURL(rendererURL);
let deepLinkingURI; let deepLinkingURI;

View file

@ -1,6 +1,6 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
// Module imports // Module imports
// @if TARGET='app' import '@babel/polyfill';
import keytar from 'keytar'; import keytar from 'keytar';
import SemVer from 'semver'; import SemVer from 'semver';
import url from 'url'; import url from 'url';
@ -12,8 +12,8 @@ import { Lbry } from 'lbry-redux';
import Daemon from './Daemon'; import Daemon from './Daemon';
import createTray from './createTray'; import createTray from './createTray';
import createWindow from './createWindow'; import createWindow from './createWindow';
import pjson from '../../package.json'; import pjson from '../../../package.json';
import startSandbox from './startSandbox'; // import startSandbox from './startSandbox';
autoUpdater.autoDownload = true; autoUpdater.autoDownload = true;
@ -29,26 +29,20 @@ let showingAutoUpdateCloseAlert = false;
// Keep a global reference, if you don't, they will be closed automatically when the JavaScript // Keep a global reference, if you don't, they will be closed automatically when the JavaScript
// object is garbage collected. // object is garbage collected.
let rendererWindow; let rendererWindow;
// eslint-disable-next-line no-unused-vars
let tray; let tray;
let daemon; let daemon;
const appState = {}; const appState = {};
const installExtensions = async () => { const installExtensions = async () => {
// eslint-disable-next-line import/no-extraneous-dependencies,global-require // // eslint-disable-next-line import/no-extraneous-dependencies,global-require
const installer = require('electron-devtools-installer'); // const installer = require('electron-devtools-installer');
// eslint-disable-next-line import/no-extraneous-dependencies,global-require // // eslint-disable-next-line import/no-extraneous-dependencies,global-require
const devtronExtension = require('devtron'); // const devtronExtension = require('devtron');
const forceDownload = !!process.env.UPGRADE_EXTENSIONS; // const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS'];
const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS']; // await devtronExtension.install();
// return Promise.all(extensions.map(name => installer.default(installer[name]))).catch(console.log);
return Promise.all(
extensions.map(
name => installer.default(installer[name], forceDownload),
devtronExtension.install()
)
).catch(console.log);
}; };
app.setAsDefaultProtocolClient('lbry'); app.setAsDefaultProtocolClient('lbry');
@ -95,7 +89,7 @@ app.on('ready', async () => {
daemon.launch(); daemon.launch();
} }
startSandbox(); // startSandbox();
if (isDev) { if (isDev) {
await installExtensions(); await installExtensions();
@ -295,7 +289,8 @@ process.on('uncaughtException', error => {
}); });
// Force single instance application // Force single instance application
const isSecondInstance = app.makeSingleInstance(argv => { app.requestSingleInstanceLock();
app.on('second-instance', (event, argv) => {
if (rendererWindow) { if (rendererWindow) {
if ( if (
(process.platform === 'win32' || process.platform === 'linux') && (process.platform === 'win32' || process.platform === 'linux') &&
@ -323,8 +318,3 @@ const isSecondInstance = app.makeSingleInstance(argv => {
rendererWindow.show(); rendererWindow.show();
} }
}); });
if (isSecondInstance) {
app.exit();
}
// @endif

View file

@ -0,0 +1,28 @@
// import express from 'express';
// import unpackByOutpoint from './unpackByOutpoint';
// // Polyfills and `lbry-redux`
// global.fetch = require('node-fetch');
// global.window = global;
// // eslint-disable-next-line import/no-commonjs,global-require
// const { Lbry } = require('lbry-redux');
// delete global.window;
// export default async function startSandbox() {
// const sandbox = express();
// const port = 5278;
// sandbox.get('/set/:outpoint', async (req, res) => {
// const { outpoint } = req.params;
// const resolvedPath = await unpackByOutpoint(Lbry, outpoint);
// sandbox.use(`/sandbox/${outpoint}/`, express.static(resolvedPath));
// res.send(`/sandbox/${outpoint}/`);
// });
// // eslint-disable-next-line no-console
// sandbox.listen(port, 'localhost', () => console.log(`Sandbox listening on port ${port}.`));
// }

View file

@ -0,0 +1,11 @@
export const clipboard = () => {
throw 'Fix me!';
};
export const ipcRenderer = () => {
throw 'Fix me!';
};
export const remote = () => {
throw 'Fix me!';
};

View file

@ -0,0 +1,23 @@
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 isDev = false;

View file

@ -1,33 +0,0 @@
import { LoadingManager, STLLoader, OBJLoader2 } from './three';
const Manager = ({ onLoad, onStart, onError }) => {
const manager = new LoadingManager();
manager.onLoad = onLoad;
manager.onStart = onStart;
manager.onError = onError;
return manager;
};
const Loader = (fileType, manager) => {
const fileTypes = {
stl: () => new STLLoader(manager),
obj: () => new OBJLoader2(manager),
};
return fileTypes[fileType] ? fileTypes[fileType]() : null;
};
const ThreeLoader = ({ fileType = null, downloadPath = null }, renderModel, managerEvents) => {
if (fileType) {
const manager = Manager(managerEvents);
const loader = Loader(fileType, manager);
if (loader) {
loader.load(`file://${downloadPath}`, data => {
renderModel(fileType, data);
});
}
}
};
export default ThreeLoader;

View file

@ -1,11 +0,0 @@
import * as THREE from 'three';
// Currently it's not possible to import the files within the "examples/js" directory.
// Fix: https://github.com/mrdoob/three.js/issues/9562#issuecomment-383390251
global.THREE = THREE;
require('three/examples/js/controls/OrbitControls');
require('three/examples/js/loaders/LoaderSupport');
require('three/examples/js/loaders/OBJLoader2');
require('three/examples/js/loaders/STLLoader');
module.exports = global.THREE;

View file

@ -1,74 +0,0 @@
// @flow
import { createSelector } from 'reselect';
import {
makeSelectClaimForUri,
selectClaimsByUri,
makeSelectClaimsInChannelForCurrentPage,
} from 'lbry-redux';
import { HISTORY_ITEMS_PER_PAGE } from 'constants/content';
export const selectState = (state: any) => state.content || {};
export const selectPlayingUri = createSelector(selectState, state => state.playingUri);
export const selectChannelClaimCounts = createSelector(
selectState,
state => state.channelClaimCounts || {}
);
export const makeSelectTotalItemsForChannel = (uri: string) =>
createSelector(selectChannelClaimCounts, byUri => byUri && byUri[uri]);
export const selectRewardContentClaimIds = createSelector(
selectState,
state => state.rewardedContentClaimIds
);
export const makeSelectContentPositionForUri = (uri: string) =>
createSelector(selectState, makeSelectClaimForUri(uri), (state, claim) => {
if (!claim) {
return null;
}
const outpoint = `${claim.txid}:${claim.nout}`;
const id = claim.claim_id;
return state.positions[id] ? state.positions[id][outpoint] : null;
});
export const selectHistoryPageCount = createSelector(selectState, state =>
Math.ceil(state.history.length / HISTORY_ITEMS_PER_PAGE)
);
export const makeSelectHistoryForPage = (page: number) =>
createSelector(selectState, selectClaimsByUri, (state, claimsByUri) => {
const left = page * HISTORY_ITEMS_PER_PAGE;
const historyItems = state.history.slice(left, left + HISTORY_ITEMS_PER_PAGE);
// See if we have the claim info for the uris in your history
// If not, it will need to be fetched in the component
return historyItems.map(historyItem => {
const { uri, lastViewed } = historyItem;
const claimAtUri = claimsByUri[uri];
if (claimAtUri) {
return { lastViewed, uri, ...claimAtUri };
}
return historyItem;
});
});
export const makeSelectHistoryForUri = (uri: string) =>
createSelector(selectState, state => state.history.find(i => i.uri === uri));
export const makeSelectCategoryListUris = (uris: ?Array<string>, channel: string) =>
createSelector(makeSelectClaimsInChannelForCurrentPage(channel), channelClaims => {
if (uris) return uris;
if (channelClaims) {
const CATEGORY_LIST_SIZE = 10;
return channelClaims
.slice(0, CATEGORY_LIST_SIZE)
.map(({ name, claim_id: claimId }) => `${name}#${claimId}`);
}
return null;
});

View file

@ -1,199 +0,0 @@
import {
selectClaimsByUri,
selectIsFetchingClaimListMine,
selectMyClaims,
selectClaimsById,
buildURI,
} from 'lbry-redux';
import { createSelector } from 'reselect';
export const selectState = state => state.fileInfo || {};
export const selectFileInfosByOutpoint = createSelector(
selectState,
state => state.byOutpoint || {}
);
export const selectIsFetchingFileList = createSelector(
selectState,
state => state.isFetchingFileList
);
export const selectIsFetchingFileListDownloadedOrPublished = createSelector(
selectIsFetchingFileList,
selectIsFetchingClaimListMine,
(isFetchingFileList, isFetchingClaimListMine) => isFetchingFileList || isFetchingClaimListMine
);
export const makeSelectFileInfoForUri = uri =>
createSelector(selectClaimsByUri, selectFileInfosByOutpoint, (claims, byOutpoint) => {
const claim = claims[uri];
const outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined;
return outpoint ? byOutpoint[outpoint] : undefined;
});
export const selectDownloadingByOutpoint = createSelector(
selectState,
state => state.downloadingByOutpoint || {}
);
export const makeSelectDownloadingForUri = uri =>
createSelector(
selectDownloadingByOutpoint,
makeSelectFileInfoForUri(uri),
(byOutpoint, fileInfo) => {
if (!fileInfo) return false;
return byOutpoint[fileInfo.outpoint];
}
);
export const selectUrisLoading = createSelector(selectState, state => state.urisLoading || {});
export const makeSelectLoadingForUri = uri =>
createSelector(selectUrisLoading, byUri => byUri && byUri[uri]);
export const selectFileInfosDownloaded = createSelector(
selectFileInfosByOutpoint,
selectMyClaims,
(byOutpoint, myClaims) =>
Object.values(byOutpoint).filter(fileInfo => {
const myClaimIds = myClaims.map(claim => claim.claim_id);
return (
fileInfo &&
myClaimIds.indexOf(fileInfo.claim_id) === -1 &&
(fileInfo.completed || fileInfo.written_bytes)
);
})
);
// export const selectFileInfoForUri = (state, props) => {
// const claims = selectClaimsByUri(state),
// claim = claims[props.uri],
// fileInfos = selectAllFileInfos(state),
// outpoint = claim ? `${claim.txid}:${claim.nout}` : undefined;
// return outpoint && fileInfos ? fileInfos[outpoint] : undefined;
// };
export const selectDownloadingFileInfos = createSelector(
selectDownloadingByOutpoint,
selectFileInfosByOutpoint,
(downloadingByOutpoint, fileInfosByOutpoint) => {
const outpoints = Object.keys(downloadingByOutpoint);
const fileInfos = [];
outpoints.forEach(outpoint => {
const fileInfo = fileInfosByOutpoint[outpoint];
if (fileInfo) fileInfos.push(fileInfo);
});
return fileInfos;
}
);
export const selectTotalDownloadProgress = createSelector(selectDownloadingFileInfos, fileInfos => {
const progress = [];
fileInfos.forEach(fileInfo => {
progress.push((fileInfo.written_bytes / fileInfo.total_bytes) * 100);
});
const totalProgress = progress.reduce((a, b) => a + b, 0);
if (fileInfos.length > 0) return totalProgress / fileInfos.length / 100.0;
return -1;
});
export const selectSearchDownloadUris = query =>
createSelector(selectFileInfosDownloaded, selectClaimsById, (fileInfos, claimsById) => {
if (!query || !fileInfos.length) {
return null;
}
const queryParts = query.toLowerCase().split(' ');
const searchQueryDictionary = {};
queryParts.forEach(subQuery => {
searchQueryDictionary[subQuery] = subQuery;
});
const arrayContainsQueryPart = array => {
for (let i = 0; i < array.length; i += 1) {
const subQuery = array[i];
if (searchQueryDictionary[subQuery]) {
return true;
}
}
return false;
};
const downloadResultsFromQuery = [];
fileInfos.forEach(fileInfo => {
const { channel_name: channelName, claim_name: claimName, metadata } = fileInfo;
const { author, description, title } = metadata;
if (channelName) {
const lowerCaseChannel = channelName.toLowerCase();
const strippedOutChannelName = lowerCaseChannel.slice(1); // trim off the @
if (searchQueryDictionary[channelName] || searchQueryDictionary[strippedOutChannelName]) {
downloadResultsFromQuery.push(fileInfo);
return;
}
}
const nameParts = claimName.toLowerCase().split('-');
if (arrayContainsQueryPart(nameParts)) {
downloadResultsFromQuery.push(fileInfo);
return;
}
const titleParts = title.toLowerCase().split(' ');
if (arrayContainsQueryPart(titleParts)) {
downloadResultsFromQuery.push(fileInfo);
return;
}
if (author) {
const authorParts = author.toLowerCase().split(' ');
if (arrayContainsQueryPart(authorParts)) {
downloadResultsFromQuery.push(fileInfo);
return;
}
}
if (description) {
const descriptionParts = description.toLowerCase().split(' ');
if (arrayContainsQueryPart(descriptionParts)) {
downloadResultsFromQuery.push(fileInfo);
}
}
});
return downloadResultsFromQuery.length
? downloadResultsFromQuery.map(fileInfo => {
const { channel_name: channelName, claim_id: claimId, claim_name: claimName } = fileInfo;
const uriParams = {};
if (channelName) {
const claim = claimsById[claimId];
if (claim && claim.value) {
uriParams.claimId = claim.value.publisherSignature.certificateId;
} else {
uriParams.claimId = claimId;
}
uriParams.channelName = channelName;
uriParams.contentName = claimName;
} else {
uriParams.claimId = claimId;
uriParams.claimName = claimName;
}
const uri = buildURI(uriParams);
return uri;
})
: null;
});
export const selectFileInfoErrors = createSelector(selectState, state => state.errors || {});

20
src/ui/app.js Normal file
View file

@ -0,0 +1,20 @@
import store from 'store';
const env = process.env.NODE_ENV || 'production';
const logs = [];
const app = {
env,
store,
logs,
log(message) {
logs.push(message);
},
};
global.app = app;
// Lbryinc needs access to the redux store for dispatching auth-releated actions
global.store = app.store;
export default app;

View file

@ -8,7 +8,7 @@ import SideBar from 'component/sideBar';
import Header from 'component/header'; import Header from 'component/header';
import { openContextMenu } from 'util/context-menu'; import { openContextMenu } from 'util/context-menu';
import EnhancedLayoutListener from 'util/enhanced-layout'; import EnhancedLayoutListener from 'util/enhanced-layout';
import Native from 'native'; import Yrbl from 'component/yrbl';
const TWO_POINT_FIVE_MINUTES = 1000 * 60 * 2.5; const TWO_POINT_FIVE_MINUTES = 1000 * 60 * 2.5;
@ -112,13 +112,7 @@ class App extends React.PureComponent<Props> {
<div id="window" onContextMenu={e => openContextMenu(e)}> <div id="window" onContextMenu={e => openContextMenu(e)}>
<Header /> <Header />
<main className="page"> <main className="page">
{enhancedLayout && ( {enhancedLayout && <Yrbl className="yrbl--enhanced" />}
<img
alt="Friendly gerbil"
className="yrbl--enhanced"
src={Native.imagePath('gerbil-happy.png')}
/>
)}
{/* @if TARGET='app' */} {/* @if TARGET='app' */}
<SideBar /> <SideBar />
{/* @endif */} {/* @endif */}

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -1,6 +1,6 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import Native from 'native'; import Placeholder from './placeholder.png';
type Props = { type Props = {
thumbnail: ?string, // externally sourced image thumbnail: ?string, // externally sourced image
@ -15,7 +15,7 @@ class CardMedia extends React.PureComponent<Props> {
style={ style={
thumbnail thumbnail
? { backgroundImage: `url('${thumbnail}')` } ? { backgroundImage: `url('${thumbnail}')` }
: { backgroundImage: `url('${Native.imagePath('placeholder.png')}')` } : { backgroundImage: `url(${Placeholder})` }
} }
className="media__thumb" className="media__thumb"
/> />

View file

@ -163,7 +163,7 @@ class CardVerify extends React.Component<Props, State> {
<Button <Button
button="primary" button="primary"
label={this.props.label} label={this.props.label}
icon={ICONS.LOCK} icon={ICONS.SECURE}
disabled={this.props.disabled || this.state.open || this.hasPendingClick} disabled={this.props.disabled || this.state.open || this.hasPendingClick}
onClick={this.onClick.bind(this)} onClick={this.onClick.bind(this)}
/> />

View file

@ -1,7 +1,6 @@
// @flow // @flow
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import fs from 'fs';
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';
@ -39,6 +38,7 @@ class FileExporter extends React.PureComponent<Props> {
handleFileCreation(filename: string, data: any) { handleFileCreation(filename: string, data: any) {
const { onFileCreated } = this.props; const { onFileCreated } = this.props;
// @if TARGET='app'
fs.writeFile(filename, data, err => { fs.writeFile(filename, data, err => {
if (err) throw err; if (err) throw err;
// Do something after creation // Do something after creation
@ -47,6 +47,7 @@ class FileExporter extends React.PureComponent<Props> {
onFileCreated(filename); onFileCreated(filename);
} }
}); });
// @endif
} }
handleButtonClick() { handleButtonClick() {
@ -71,7 +72,9 @@ class FileExporter extends React.PureComponent<Props> {
// User hit cancel so do nothing: // User hit cancel so do nothing:
if (!filename) return; if (!filename) return;
// Get extension and remove initial dot // Get extension and remove initial dot
// @if TARGET='app'
const format = path.extname(filename).replace(/\./g, ''); const format = path.extname(filename).replace(/\./g, '');
// @endif
// Parse data to string with the chosen format // Parse data to string with the chosen format
const parsed = parseData(data, format, filters); const parsed = parseData(data, format, filters);
// Write file // Write file

View file

@ -58,6 +58,7 @@ class FileSelector extends React.PureComponent<Props> {
} }
const filePath = paths[0]; const filePath = paths[0];
const extension = path.extname(filePath); const extension = path.extname(filePath);
const fileName = path.basename(filePath, extension); const fileName = path.basename(filePath, extension);

View file

@ -0,0 +1,38 @@
// @flow
// Used as a wrapper for FormField to produce inline form elements
import * as React from 'react';
import classnames from 'classnames';
type Props = {
children: React.Node,
padded?: boolean,
verticallyCentered?: boolean,
stretch?: boolean,
alignRight?: boolean,
centered?: boolean,
};
export class FormRow extends React.PureComponent<Props> {
static defaultProps = {
padded: false,
};
render() {
const { children, padded, verticallyCentered, stretch, alignRight, centered } = this.props;
return (
<div
className={classnames('form-row', {
'form-row--padded': padded,
'form-row--vertically-centered': verticallyCentered,
'form-row--stretch': stretch,
'form-row--right': alignRight,
'form-row--centered': centered,
})}
>
{children}
</div>
);
}
}
export default FormRow;

View file

@ -3,7 +3,7 @@ import React from 'react';
import moment from 'moment'; import moment from 'moment';
type Props = { type Props = {
date?: number, date?: number | {},
timeAgo?: boolean, timeAgo?: boolean,
formatOptions: {}, formatOptions: {},
show?: string, show?: string,
@ -43,7 +43,7 @@ class DateTime extends React.PureComponent<Props> {
render() { render() {
const { date, formatOptions, timeAgo } = this.props; const { date, formatOptions, timeAgo } = this.props;
const show = this.props.show || DateTime.SHOW_BOTH; const show = this.props.show || DateTime.SHOW_BOTH;
const locale = app.i18n.getLocale(); const locale = i18n.getLocale();
if (timeAgo) { if (timeAgo) {
return date ? <span>{moment(date).from(moment())}</span> : <span />; return date ? <span>{moment(date).from(moment())}</span> : <span />;

View file

@ -6,8 +6,8 @@ import React, { Fragment, PureComponent } from 'react';
import { Lbryio } from 'lbryinc'; import { Lbryio } from 'lbryinc';
import MarkdownPreview from 'component/common/markdown-preview'; import MarkdownPreview from 'component/common/markdown-preview';
import Button from 'component/button'; import Button from 'component/button';
import path from 'path';
import Expandable from 'component/expandable'; import Expandable from 'component/expandable';
import path from 'path';
type Props = { type Props = {
claim: Claim, claim: Claim,

View file

@ -24,8 +24,7 @@ class FileListSearch extends React.PureComponent<Props> {
<div className="search__results-title">{__('Search Results')}</div> <div className="search__results-title">{__('Search Results')}</div>
<HiddenNsfwClaims uris={uris} /> <HiddenNsfwClaims uris={uris} />
{!isSearching && uris && uris.length ? ( {!isSearching && uris && uris.length ? (
uris.map( uris.map(uri =>
uri =>
parseURI(uri).claimName[0] === '@' ? ( parseURI(uri).claimName[0] === '@' ? (
<ChannelTile key={uri} uri={uri} /> <ChannelTile key={uri} uri={uri} />
) : ( ) : (

View file

@ -1,9 +1,11 @@
// @flow /* eslint-disable */
import type { Claim } from 'types/claim'; import type { Claim } from 'types/claim';
import * as React from 'react'; import * as React from 'react';
// @if TARGET='app'
import { remote } from 'electron'; import { remote } from 'electron';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
// @endif
import player from 'render-media'; import player from 'render-media';
import FileRender from 'component/fileRender'; import FileRender from 'component/fileRender';
import LoadingScreen from 'component/common/loading-screen'; import LoadingScreen from 'component/common/loading-screen';

View file

@ -3,7 +3,7 @@ import React, { PureComponent } from 'react';
import posed from 'react-pose'; import posed from 'react-pose';
import Button from 'component/button'; import Button from 'component/button';
import EmailCollection from 'component/emailCollection'; import EmailCollection from 'component/emailCollection';
import Native from 'native'; import Yrbl from 'component/yrbl';
// //
// Animation for items inside banner // Animation for items inside banner
@ -59,11 +59,7 @@ export default class FirstRun extends PureComponent<Props> {
return ( return (
<div className="banner banner--first-run"> <div className="banner banner--first-run">
<img <Yrbl className="yrbl--first-run" />
alt="Friendly gerbil"
className="yrbl--first-run banner__item"
src={Native.imagePath('gerbil-happy.png')}
/>
<div className="banner__item"> <div className="banner__item">
<div className="banner__item--static-for-animation"> <div className="banner__item--static-for-animation">

Some files were not shown because too many files have changed in this diff Show more