new build setup for web/electron
This commit is contained in:
parent
7ea2a1dade
commit
de3639f29c
440 changed files with 5859 additions and 3991 deletions
9
.babelrc
Normal file
9
.babelrc
Normal 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"
|
||||
]
|
||||
}
|
|
@ -6,13 +6,6 @@
|
|||
"plugin:flowtype/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"webpack": {
|
||||
"config": "webpack.renderer.additions.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"env": {
|
||||
"browser": true,
|
||||
|
@ -20,7 +13,7 @@
|
|||
},
|
||||
"globals": {
|
||||
"__static": true,
|
||||
"staticResourcesPath": true,
|
||||
"i18n": true,
|
||||
"__": true,
|
||||
"__n": true,
|
||||
"app": true
|
||||
|
@ -59,6 +52,7 @@
|
|||
"no-empty": 0,
|
||||
"react/prefer-stateless-function": 0,
|
||||
"react/sort-comp": 0,
|
||||
"jsx-a11y/media-has-caption": 0
|
||||
"jsx-a11y/media-has-caption": 0,
|
||||
"no-underscore-dangle": 0
|
||||
}
|
||||
}
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,7 +1,7 @@
|
|||
.DS_Store
|
||||
/node_modules
|
||||
/dist
|
||||
/static/daemon/lbrynet*
|
||||
/static/lbrynet
|
||||
/static/locales
|
||||
yarn-error.log
|
||||
package-lock.json
|
||||
|
|
|
@ -89,8 +89,4 @@ const downloadDaemon = targetPlatform =>
|
|||
}
|
||||
});
|
||||
|
||||
module.exports = downloadDaemon;
|
||||
|
||||
require('make-runnable/custom')({
|
||||
printOutputFrame: false,
|
||||
});
|
||||
downloadDaemon();
|
||||
|
|
|
@ -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!");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,6 +1,9 @@
|
|||
{
|
||||
"appId": "io.lbry.LBRY",
|
||||
"productName": "LBRY",
|
||||
"directories": {
|
||||
"output": "dist/electron"
|
||||
},
|
||||
"publish": [
|
||||
{
|
||||
"provider": "s3",
|
||||
|
@ -44,6 +47,7 @@
|
|||
],
|
||||
"linux": {
|
||||
"target": "deb",
|
||||
"executableName": "LBRY",
|
||||
"category": "AudioVideo;Video",
|
||||
"desktop": {
|
||||
"MimeType": "x-scheme-handler/lbry",
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"renderer": {
|
||||
"webpackConfig": "webpack.renderer.additions.js"
|
||||
}
|
||||
}
|
63
package.json
63
package.json
|
@ -18,16 +18,15 @@
|
|||
"name": "LBRY Inc.",
|
||||
"email": "hello@lbry.io"
|
||||
},
|
||||
"main": "src/main/index.js",
|
||||
"main": "./dist/main/main.js",
|
||||
"scripts": {
|
||||
"extract-langs": "node build/extractLocals.js",
|
||||
"compile": "electron-webpack && yarn extract-langs",
|
||||
"compile:electron": "webpack --progress --config webpack.electron.config.js",
|
||||
"compile:web": "webpack --progress --config webpack.web.config.js",
|
||||
"compile": "yarn compile:electron && yarn compile:web",
|
||||
"build": "yarn compile && electron-builder build",
|
||||
"build:dir": "yarn build -- --dir -c.compression=store -c.mac.identity=null",
|
||||
"build:web": "webpack",
|
||||
"dev": "electron-webpack dev",
|
||||
"dev:internal-apis": "LBRY_API_URL='http://localhost:8080' yarn dev",
|
||||
"dev:web": "webpack --watch",
|
||||
"dev:electron": "yarn compile:electron && electron ./dist/electron/main/bundle.js",
|
||||
"dev:internal-apis": "LBRY_API_URL='http://localhost:8080' yarn dev:electron",
|
||||
"lint": "eslint 'src/**/*.{js,jsx}' --fix && flow",
|
||||
"format": "prettier 'src/**/*.{js,jsx,scss,json}' --write",
|
||||
"flow-defs": "flow-typed install",
|
||||
|
@ -36,10 +35,9 @@
|
|||
"postinstall": "electron-builder install-app-deps && node build/downloadDaemon.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lbry/components": "^2.2.4",
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
"@types/three": "^0.93.1",
|
||||
"bluebird": "^3.5.1",
|
||||
"breakdance": "^3.0.1",
|
||||
"classnames": "^2.2.5",
|
||||
"codemirror": "^5.39.2",
|
||||
"country-data": "^0.0.31",
|
||||
|
@ -48,12 +46,12 @@
|
|||
"electron-dl": "^1.11.0",
|
||||
"electron-is-dev": "^0.3.0",
|
||||
"electron-log": "^2.2.12",
|
||||
"electron-updater": "^2.23.3",
|
||||
"electron-updater": "^4.0.0",
|
||||
"electron-window-state": "^4.1.1",
|
||||
"express": "^4.16.4",
|
||||
"formik": "^0.10.4",
|
||||
"hast-util-sanitize": "^1.1.2",
|
||||
"keytar": "^4.2.1",
|
||||
"keytar": "^4.3.0",
|
||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
||||
"lbry-redux": "lbryio/lbry-redux#406e1970b9d5594faf0407100c9bbed45d904cdf",
|
||||
"lbryinc": "lbryio/lbryinc#2334ad53e82c22d6291899785d5292347008f2a9",
|
||||
|
@ -62,8 +60,10 @@
|
|||
"mime": "^2.3.1",
|
||||
"mixpanel-browser": "^2.17.1",
|
||||
"moment": "^2.22.0",
|
||||
"node-abi": "^2.5.1",
|
||||
"node-fetch": "^2.3.0",
|
||||
"preprocess-loader": "^0.3.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"qrcode.react": "^0.8.0",
|
||||
"rc-progress": "^2.0.6",
|
||||
"react": "^16.8.2",
|
||||
|
@ -86,28 +86,37 @@
|
|||
"render-media": "^3.1.0",
|
||||
"reselect": "^3.0.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-full": "^11.3.2",
|
||||
"tree-kill": "^1.1.0",
|
||||
"video.js": "^7.2.2",
|
||||
"y18n": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^8.2.2",
|
||||
"babel-plugin-module-resolver": "^3.1.1",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-2": "^6.18.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||
"@babel/plugin-proposal-decorators": "^7.3.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.2.3",
|
||||
"@babel/preset-flow": "^7.0.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",
|
||||
"css-loader": "^2.1.0",
|
||||
"decompress": "^4.2.0",
|
||||
"del": "^3.0.0",
|
||||
"devtron": "^1.4.0",
|
||||
"electron": "^2.0.14",
|
||||
"electron-builder": "^20.22.0",
|
||||
"electron": "^4.0.4",
|
||||
"electron-builder": "^20.38.4",
|
||||
"electron-devtools-installer": "^2.2.3",
|
||||
"electron-publisher-s3": "^20.8.1",
|
||||
"electron-webpack": "^1.13.0",
|
||||
"electron-webpack": "^2.6.2",
|
||||
"eslint": "^4.19.0",
|
||||
"eslint-config-airbnb": "^16.1.0",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
|
@ -121,17 +130,21 @@
|
|||
"flow-bin": "^0.89.0",
|
||||
"flow-typed": "^2.3.0",
|
||||
"husky": "^0.14.3",
|
||||
"i18n-extract": "^0.5.1",
|
||||
"json-loader": "^0.5.4",
|
||||
"lint-staged": "^7.0.2",
|
||||
"make-runnable": "^1.3.6",
|
||||
"node-libs-browser": "^2.1.0",
|
||||
"node-loader": "^0.6.0",
|
||||
"node-sass": "^4.11.0",
|
||||
"preprocess-loader": "^0.3.0",
|
||||
"prettier": "^1.11.1",
|
||||
"sass-loader": "^6.0.7",
|
||||
"webpack": "^3.10.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"webpack": "^4.28.4",
|
||||
"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"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -141,7 +154,7 @@
|
|||
"lbrySettings": {
|
||||
"lbrynetDaemonVersion": "0.32.4",
|
||||
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-OSNAME.zip",
|
||||
"lbrynetDaemonDir": "static/daemon",
|
||||
"lbrynetDaemonDir": "static",
|
||||
"lbrynetDaemonFileName": "lbrynet"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}.`));
|
||||
}
|
|
@ -4,7 +4,7 @@ import { spawn, execSync } from 'child_process';
|
|||
import { Lbry } from 'lbry-redux';
|
||||
|
||||
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;
|
||||
handlers;
|
||||
|
|
@ -5,15 +5,15 @@ export default window => {
|
|||
let iconPath;
|
||||
switch (process.platform) {
|
||||
case 'darwin': {
|
||||
iconPath = path.join(__static, '/img/tray/mac/trayTemplate.png');
|
||||
iconPath = 'static/img/tray/mac/trayTemplate.png';
|
||||
break;
|
||||
}
|
||||
case 'win32': {
|
||||
iconPath = path.join(__static, '/img/tray/windows/tray.ico');
|
||||
iconPath = 'static/img/tray/windows/tray.ico';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
iconPath = path.join(__static, '/img/tray/default/tray.png');
|
||||
iconPath = 'static/img/tray/default/tray.png';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import path from 'path';
|
||||
import { app, BrowserWindow, dialog, shell, screen } from 'electron';
|
||||
import isDev from 'electron-is-dev';
|
||||
import windowStateKeeper from 'electron-window-state';
|
||||
|
@ -34,8 +35,8 @@ export default appState => {
|
|||
},
|
||||
};
|
||||
|
||||
const rendererURL = isDev
|
||||
? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`
|
||||
const rendererURL = isDev // ? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`
|
||||
? `file://${path.resolve(__dirname, '../index.html')}`
|
||||
: `file://${__dirname}/index.html`;
|
||||
|
||||
let window = new BrowserWindow(windowConfiguration);
|
||||
|
@ -45,6 +46,8 @@ export default appState => {
|
|||
// and restore the maximized or full screen state.
|
||||
windowState.manage(window);
|
||||
|
||||
console.log('url', rendererURL);
|
||||
console.log('window', window);
|
||||
window.loadURL(rendererURL);
|
||||
|
||||
let deepLinkingURI;
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable no-console */
|
||||
// Module imports
|
||||
// @if TARGET='app'
|
||||
import '@babel/polyfill';
|
||||
import keytar from 'keytar';
|
||||
import SemVer from 'semver';
|
||||
import url from 'url';
|
||||
|
@ -12,8 +12,8 @@ import { Lbry } from 'lbry-redux';
|
|||
import Daemon from './Daemon';
|
||||
import createTray from './createTray';
|
||||
import createWindow from './createWindow';
|
||||
import pjson from '../../package.json';
|
||||
import startSandbox from './startSandbox';
|
||||
import pjson from '../../../package.json';
|
||||
// import startSandbox from './startSandbox';
|
||||
|
||||
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
|
||||
// object is garbage collected.
|
||||
let rendererWindow;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
||||
let tray;
|
||||
let daemon;
|
||||
|
||||
const appState = {};
|
||||
|
||||
const installExtensions = async () => {
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies,global-require
|
||||
const installer = require('electron-devtools-installer');
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies,global-require
|
||||
const devtronExtension = require('devtron');
|
||||
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
|
||||
const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS'];
|
||||
|
||||
return Promise.all(
|
||||
extensions.map(
|
||||
name => installer.default(installer[name], forceDownload),
|
||||
devtronExtension.install()
|
||||
)
|
||||
).catch(console.log);
|
||||
// // eslint-disable-next-line import/no-extraneous-dependencies,global-require
|
||||
// const installer = require('electron-devtools-installer');
|
||||
// // eslint-disable-next-line import/no-extraneous-dependencies,global-require
|
||||
// const devtronExtension = require('devtron');
|
||||
// const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS'];
|
||||
// await devtronExtension.install();
|
||||
// return Promise.all(extensions.map(name => installer.default(installer[name]))).catch(console.log);
|
||||
};
|
||||
|
||||
app.setAsDefaultProtocolClient('lbry');
|
||||
|
@ -95,7 +89,7 @@ app.on('ready', async () => {
|
|||
daemon.launch();
|
||||
}
|
||||
|
||||
startSandbox();
|
||||
// startSandbox();
|
||||
|
||||
if (isDev) {
|
||||
await installExtensions();
|
||||
|
@ -295,7 +289,8 @@ process.on('uncaughtException', error => {
|
|||
});
|
||||
|
||||
// Force single instance application
|
||||
const isSecondInstance = app.makeSingleInstance(argv => {
|
||||
app.requestSingleInstanceLock();
|
||||
app.on('second-instance', (event, argv) => {
|
||||
if (rendererWindow) {
|
||||
if (
|
||||
(process.platform === 'win32' || process.platform === 'linux') &&
|
||||
|
@ -323,8 +318,3 @@ const isSecondInstance = app.makeSingleInstance(argv => {
|
|||
rendererWindow.show();
|
||||
}
|
||||
});
|
||||
|
||||
if (isSecondInstance) {
|
||||
app.exit();
|
||||
}
|
||||
// @endif
|
28
src/platforms/electron/startSandbox.js
Normal file
28
src/platforms/electron/startSandbox.js
Normal 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}.`));
|
||||
// }
|
11
src/platforms/web/electron.js
Normal file
11
src/platforms/web/electron.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
export const clipboard = () => {
|
||||
throw 'Fix me!';
|
||||
};
|
||||
|
||||
export const ipcRenderer = () => {
|
||||
throw 'Fix me!';
|
||||
};
|
||||
|
||||
export const remote = () => {
|
||||
throw 'Fix me!';
|
||||
};
|
23
src/platforms/web/stubs.js
Normal file
23
src/platforms/web/stubs.js
Normal 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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
});
|
|
@ -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
20
src/ui/app.js
Normal 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;
|
|
@ -8,7 +8,7 @@ import SideBar from 'component/sideBar';
|
|||
import Header from 'component/header';
|
||||
import { openContextMenu } from 'util/context-menu';
|
||||
import EnhancedLayoutListener from 'util/enhanced-layout';
|
||||
import Native from 'native';
|
||||
import Yrbl from 'component/yrbl';
|
||||
|
||||
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)}>
|
||||
<Header />
|
||||
<main className="page">
|
||||
{enhancedLayout && (
|
||||
<img
|
||||
alt="Friendly gerbil"
|
||||
className="yrbl--enhanced"
|
||||
src={Native.imagePath('gerbil-happy.png')}
|
||||
/>
|
||||
)}
|
||||
{enhancedLayout && <Yrbl className="yrbl--enhanced" />}
|
||||
{/* @if TARGET='app' */}
|
||||
<SideBar />
|
||||
{/* @endif */}
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Native from 'native';
|
||||
import Placeholder from './placeholder.png';
|
||||
|
||||
type Props = {
|
||||
thumbnail: ?string, // externally sourced image
|
||||
|
@ -15,7 +15,7 @@ class CardMedia extends React.PureComponent<Props> {
|
|||
style={
|
||||
thumbnail
|
||||
? { backgroundImage: `url('${thumbnail}')` }
|
||||
: { backgroundImage: `url('${Native.imagePath('placeholder.png')}')` }
|
||||
: { backgroundImage: `url(${Placeholder})` }
|
||||
}
|
||||
className="media__thumb"
|
||||
/>
|
|
@ -163,7 +163,7 @@ class CardVerify extends React.Component<Props, State> {
|
|||
<Button
|
||||
button="primary"
|
||||
label={this.props.label}
|
||||
icon={ICONS.LOCK}
|
||||
icon={ICONS.SECURE}
|
||||
disabled={this.props.disabled || this.state.open || this.hasPendingClick}
|
||||
onClick={this.onClick.bind(this)}
|
||||
/>
|
|
@ -1,7 +1,6 @@
|
|||
// @flow
|
||||
import * as ICONS from 'constants/icons';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
import parseData from 'util/parse-data';
|
||||
|
@ -39,6 +38,7 @@ class FileExporter extends React.PureComponent<Props> {
|
|||
|
||||
handleFileCreation(filename: string, data: any) {
|
||||
const { onFileCreated } = this.props;
|
||||
// @if TARGET='app'
|
||||
fs.writeFile(filename, data, err => {
|
||||
if (err) throw err;
|
||||
// Do something after creation
|
||||
|
@ -47,6 +47,7 @@ class FileExporter extends React.PureComponent<Props> {
|
|||
onFileCreated(filename);
|
||||
}
|
||||
});
|
||||
// @endif
|
||||
}
|
||||
|
||||
handleButtonClick() {
|
||||
|
@ -71,7 +72,9 @@ class FileExporter extends React.PureComponent<Props> {
|
|||
// User hit cancel so do nothing:
|
||||
if (!filename) return;
|
||||
// Get extension and remove initial dot
|
||||
// @if TARGET='app'
|
||||
const format = path.extname(filename).replace(/\./g, '');
|
||||
// @endif
|
||||
// Parse data to string with the chosen format
|
||||
const parsed = parseData(data, format, filters);
|
||||
// Write file
|
|
@ -58,6 +58,7 @@ class FileSelector extends React.PureComponent<Props> {
|
|||
}
|
||||
|
||||
const filePath = paths[0];
|
||||
|
||||
const extension = path.extname(filePath);
|
||||
const fileName = path.basename(filePath, extension);
|
||||
|
38
src/ui/component/common/form-components/form-row.jsx
Normal file
38
src/ui/component/common/form-components/form-row.jsx
Normal 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;
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import moment from 'moment';
|
||||
|
||||
type Props = {
|
||||
date?: number,
|
||||
date?: number | {},
|
||||
timeAgo?: boolean,
|
||||
formatOptions: {},
|
||||
show?: string,
|
||||
|
@ -43,7 +43,7 @@ class DateTime extends React.PureComponent<Props> {
|
|||
render() {
|
||||
const { date, formatOptions, timeAgo } = this.props;
|
||||
const show = this.props.show || DateTime.SHOW_BOTH;
|
||||
const locale = app.i18n.getLocale();
|
||||
const locale = i18n.getLocale();
|
||||
|
||||
if (timeAgo) {
|
||||
return date ? <span>{moment(date).from(moment())}</span> : <span />;
|
|
@ -6,8 +6,8 @@ import React, { Fragment, PureComponent } from 'react';
|
|||
import { Lbryio } from 'lbryinc';
|
||||
import MarkdownPreview from 'component/common/markdown-preview';
|
||||
import Button from 'component/button';
|
||||
import path from 'path';
|
||||
import Expandable from 'component/expandable';
|
||||
import path from 'path';
|
||||
|
||||
type Props = {
|
||||
claim: Claim,
|
|
@ -24,13 +24,12 @@ class FileListSearch extends React.PureComponent<Props> {
|
|||
<div className="search__results-title">{__('Search Results')}</div>
|
||||
<HiddenNsfwClaims uris={uris} />
|
||||
{!isSearching && uris && uris.length ? (
|
||||
uris.map(
|
||||
uri =>
|
||||
parseURI(uri).claimName[0] === '@' ? (
|
||||
<ChannelTile key={uri} uri={uri} />
|
||||
) : (
|
||||
<FileTile key={uri} uri={uri} />
|
||||
)
|
||||
uris.map(uri =>
|
||||
parseURI(uri).claimName[0] === '@' ? (
|
||||
<ChannelTile key={uri} uri={uri} />
|
||||
) : (
|
||||
<FileTile key={uri} uri={uri} />
|
||||
)
|
||||
)
|
||||
) : (
|
||||
<NoResults />
|
|
@ -1,9 +1,11 @@
|
|||
// @flow
|
||||
/* eslint-disable */
|
||||
import type { Claim } from 'types/claim';
|
||||
import * as React from 'react';
|
||||
// @if TARGET='app'
|
||||
import { remote } from 'electron';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
// @endif
|
||||
import player from 'render-media';
|
||||
import FileRender from 'component/fileRender';
|
||||
import LoadingScreen from 'component/common/loading-screen';
|
|
@ -3,7 +3,7 @@ import React, { PureComponent } from 'react';
|
|||
import posed from 'react-pose';
|
||||
import Button from 'component/button';
|
||||
import EmailCollection from 'component/emailCollection';
|
||||
import Native from 'native';
|
||||
import Yrbl from 'component/yrbl';
|
||||
|
||||
//
|
||||
// Animation for items inside banner
|
||||
|
@ -59,11 +59,7 @@ export default class FirstRun extends PureComponent<Props> {
|
|||
|
||||
return (
|
||||
<div className="banner banner--first-run">
|
||||
<img
|
||||
alt="Friendly gerbil"
|
||||
className="yrbl--first-run banner__item"
|
||||
src={Native.imagePath('gerbil-happy.png')}
|
||||
/>
|
||||
<Yrbl className="yrbl--first-run" />
|
||||
|
||||
<div className="banner__item">
|
||||
<div className="banner__item--static-for-animation">
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue