Web stability improvements and global error handler #2310

Merged
neb-b merged 5 commits from web-stability into master 2019-03-11 03:58:31 +01:00
31 changed files with 297 additions and 258 deletions

View file

@ -1,5 +1,12 @@
{
"plugins": ["flowtype"],
"plugins": ["flowtype", "import"],
"settings": {
"import/resolver": {
"webpack": {
"config": "webpack.base.config.js"
}
}
},
"extends": [
"airbnb",
"plugin:import/electron",
@ -53,6 +60,8 @@
"react/prefer-stateless-function": 0,
"react/sort-comp": 0,
"jsx-a11y/media-has-caption": 0,
"no-underscore-dangle": 0
"no-underscore-dangle": 0,
"import/extensions": 0,
"react/default-props-match-prop-types": 0
}
}

1
.gitignore vendored
View file

@ -2,6 +2,7 @@
/node_modules
/dist
/static/lbrynet
/static/daemon
/static/locales
yarn-error.log
package-lock.json

View file

@ -24,10 +24,10 @@
"compile:web": "webpack --config webpack.web.config.js",
"compile": "yarn compile:electron && yarn compile:web",
"dev:electron": "webpack-dev-server --hot --progress --config webpack.electron.config.js",
"dev:web": "webpack-dev-server --hot --progress --config webpack.web.config.js",
"dev:web": "webpack-dev-server --open --hot --progress --config webpack.web.config.js",
"dev:internal-apis": "LBRY_API_URL='http://localhost:8080' yarn dev:electron",
"run:electron": "electron ./dist/electron/main.js",
"run:web": "yarn compile:web && node ./dist/web/server.js",
"run:web": "cross-env NODE_ENV=production yarn compile:web && node ./dist/web/server.js",
"pack": "electron-builder --dir",
"dist": "electron-builder",
"build": "yarn compile && electron-builder build",
@ -112,6 +112,7 @@
"babel-loader": "^8.0.5",
"babel-plugin-add-module-exports": "^1.0.0",
"copy-webpack-plugin": "^4.6.0",
"cross-env": "^5.2.0",
"css-loader": "^2.1.0",
"decompress": "^4.2.0",
"del": "^3.0.0",
@ -124,13 +125,13 @@
"eslint": "^4.19.0",
"eslint-config-airbnb": "^16.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-import-resolver-webpack": "^0.9.0",
"eslint-import-resolver-webpack": "^0.11.0",
"eslint-plugin-flowtype": "^2.46.1",
"eslint-plugin-import": "^2.10.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-prettier": "^2.6.0",
"eslint-plugin-react": "^7.7.0",
"flow-bin": "^0.89.0",
"flow-bin": "^0.94.0",
"flow-typed": "^2.3.0",
"husky": "^0.14.3",
"json-loader": "^0.5.4",
@ -141,11 +142,12 @@
"node-sass": "^4.11.0",
"preprocess-loader": "^0.3.0",
"prettier": "^1.11.1",
"raw-loader": "^1.0.0",
"react-hot-loader": "^4.7.2",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"webpack": "^4.28.4",
"webpack-build-notifier": "^0.1.23",
"webpack-config-utils": "^2.3.1",
"webpack-dev-middleware": "^3.6.0",
"webpack-dev-server": "^3.1.14",
"webpack-merge": "^4.2.1",

View file

@ -1,38 +0,0 @@
// @flow
import * as React from 'react';
import Native from 'native';
import classnames from 'classnames';
type Props = {
title: string,
subtitle: string | React.Node,
type: string,
className?: string,
};
const yrblTypes = {
happy: 'gerbil-happy.png',
sad: 'gerbil-sad.png',
};
export default class extends React.PureComponent<Props> {
static defaultProps = {
type: 'happy',
};
render() {
const { title, subtitle, type, className } = this.props;
const image = yrblTypes[type];
return (
<div className={classnames('yrbl-wrap', className)}>
<img alt="Friendly gerbil" className="yrbl" src={Native.imagePath(image)} />
<div className="card__content">
<h2 className="card__title">{title}</h2>
<div className="card__subtitle">{subtitle}</div>
</div>
</div>
);
}
}

View file

@ -0,0 +1,4 @@
import ErrorBoundary from './view';
// TODO: bring in device/user(?) info in the future
export default ErrorBoundary;
skhameneh commented 2019-03-11 03:12:33 +01:00 (Migrated from github.com)
Review

What is the intended purpose of this component? The name gives me the impression this may be something we want to discuss.

What is the intended purpose of this component? The name gives me the impression this may be something we want to discuss.
neb-b commented 2019-03-11 03:36:45 +01:00 (Migrated from github.com)
Review

The purpose is a global error handler. If we don't catch anything below, catch this at the top and report it. My idea was that we could include user info if we wanted to reach out/more info on what could have caused the issue, but if we go to internal-apis we should have everything there.

The purpose is a global error handler. If we don't catch anything below, catch this at the top and report it. My idea was that we could include user info if we wanted to reach out/more info on what could have caused the issue, but if we go to internal-apis we should have everything there.

View file

@ -0,0 +1,68 @@
// @flow
import * as React from 'react';
import Yrbl from 'component/yrbl';
import Button from 'component/button';
const WEB_HOOK_URL =
'https://hooks.slack.com/services/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';
type Props = {
children: React.Node,
};
type State = {
hasError: boolean,
};
export default class ErrorBoundary extends React.Component<Props, State> {
constructor() {
super();
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: { stack: string }) {
declare var app: { env: string };
if (app.env === 'production') {
fetch(WEB_HOOK_URL, {
method: 'POST',
body: JSON.stringify({
text: error.stack,
}),
});
}
}
render() {
if (this.state.hasError) {
return (
<div className="load-screen">
<Yrbl
type="sad"
title={__('Aw shucks!')}
subtitle={
<div>
<p>
{__("There was an error. It's been reported and will be fixed")}. {__('Try')}{' '}
<Button
button="link"
className="load-screen__button"
label={__('refreshing the app')}
onClick={() => window.location.reload()}
/>{' '}
{__('to fix it')}.
</p>
</div>
}
/>
</div>
);
}
return this.props.children;
}
}

View file

@ -3,11 +3,13 @@ import { remote } from 'electron';
import React from 'react';
import LoadingScreen from 'component/common/loading-screen';
import PdfViewer from 'component/viewers/pdfViewer';
import ThreeViewer from 'component/viewers/threeViewer';
import DocumentViewer from 'component/viewers/documentViewer';
import DocxViewer from 'component/viewers/docxViewer';
import HtmlViewer from 'component/viewers/htmlViewer';
import AudioVideoViewer from 'component/viewers/audioVideoViewer';
// @if TARGET='app'
import ThreeViewer from 'component/viewers/threeViewer';
// @endif
type Props = {
mediaType: string,
@ -101,7 +103,10 @@ class FileRender extends React.PureComponent<Props> {
// Supported mediaTypes
const mediaTypes = {
// @if TARGET='app'
'3D-file': <ThreeViewer source={{ fileType, downloadPath }} theme={currentTheme} />,
// @endif
application: !source.url ? null : (
<webview
ref={element => this.processSandboxRef(element)}

View file

@ -11,7 +11,7 @@ import classnames from 'classnames';
import FilePrice from 'component/filePrice';
import UriIndicator from 'component/uriIndicator';
import DateTime from 'component/dateTime';
import Yrbl from 'component/common/yrbl';
import Yrbl from 'component/yrbl';
type Props = {
obscureNsfw: boolean,

View file

@ -35,7 +35,8 @@ class LoadScreen extends React.PureComponent<Props> {
<div className="load-screen__actions">
<Button
label="Refresh"
className="button--load-screen"
button="link"
className="load-screen__button"
onClick={() => window.location.reload()}
/>
</div>
@ -48,7 +49,8 @@ class LoadScreen extends React.PureComponent<Props> {
<p>
{__('Reach out to hello@lbry.io for help, or check out')}{' '}
<Button
className="button--load-screen"
button="link"
className="load-screen__button"
href="https://lbry.io/faq/startup-troubleshooting"
label="this link"
/>

View file

@ -97,7 +97,7 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
});
}
updateStatusCallback(status: Status, accountList: any) {
updateStatusCallback(status: Status) {
const { notifyUnlockWallet, authenticate, modal } = this.props;
const { launchedModal } = this.state;
@ -127,7 +127,7 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
} else if (status.is_running) {
// If we cleared the error timout due to a wallet being locked, make sure to start it back up
if (!this.timeout) {
this.adjustErrorTimseout();
this.adjustErrorTimeout();
}
Lbry.resolve({ urls: 'lbry://one' }).then(() => {

View file

@ -5,14 +5,24 @@ import classNames from 'classnames';
import LoadingScreen from 'component/common/loading-screen';
// ThreeJS
import * as THREE from 'three-full';
import { LoadingManager } from 'three-full/sources/loaders/LoadingManager';
import { STLLoader } from 'three-full/sources/loaders/STLLoader';
import { OBJLoader2 } from 'three-full/sources/loaders/OBJLoader2';
import { OrbitControls } from 'three-full/sources/controls/OrbitControls';
import { Geometry } from 'three-full/sources/core/Geometry';
import { Box3 } from 'three-full/sources/math/Box3';
import { Vector3 } from 'three-full/sources/math/Vector3';
import { Mesh } from 'three-full/sources/objects/Mesh';
import { Group } from 'three-full/sources/objects/Group';
import { PerspectiveCamera } from 'three-full/sources/cameras/PerspectiveCamera';
import { MeshPhongMaterial } from 'three-full/sources/materials/MeshPhongMaterial';
import detectWebGL from './internal/detector';
import ThreeGrid from './internal/grid';
import ThreeScene from './internal/scene';
import ThreeRenderer from './internal/renderer';
const Manager = ({ onLoad, onStart, onError }) => {
const manager = new THREE.LoadingManager();
const manager = new LoadingManager();
manager.onLoad = onLoad;
manager.onStart = onStart;
manager.onError = onError;
@ -22,8 +32,8 @@ const Manager = ({ onLoad, onStart, onError }) => {
const Loader = (fileType, manager) => {
const fileTypes = {
stl: () => new THREE.STLLoader(manager),
obj: () => new THREE.OBJLoader2(manager),
stl: () => new STLLoader(manager),
obj: () => new OBJLoader2(manager),
};
return fileTypes[fileType] ? fileTypes[fileType]() : null;
};
@ -70,7 +80,7 @@ class ThreeViewer extends React.PureComponent<Props, State> {
});
static createOrbitControls(camera: any, canvas: any) {
const controls = new THREE.OrbitControls(camera, canvas);
const controls = new OrbitControls(camera, canvas);
// Controls configuration
controls.enableDamping = true;
controls.dampingFactor = 0.75;
@ -88,8 +98,8 @@ class ThreeViewer extends React.PureComponent<Props, State> {
const min = { x: 0, y: 0, z: 0 };
group.traverse(child => {
if (child instanceof THREE.Mesh) {
const box = new THREE.Box3().setFromObject(child);
if (child instanceof Mesh) {
const box = new Box3().setFromObject(child);
// Max
max.x = box.max.x > max.x ? box.max.x : max.x;
max.y = box.max.y > max.y ? box.max.y : max.y;
@ -108,7 +118,7 @@ class ThreeViewer extends React.PureComponent<Props, State> {
group.scale.set(scaleFactor, scaleFactor, scaleFactor);
group.position.setY((meshY / 2) * scaleFactor);
// Reset object position
const box = new THREE.Box3().setFromObject(group);
const box = new Box3().setFromObject(group);
// Update position
box.getCenter(group.position);
group.position.multiplyScalar(-1);
@ -195,7 +205,7 @@ class ThreeViewer extends React.PureComponent<Props, State> {
}
// Clean up group items
this.mesh.traverse(child => {
if (child instanceof THREE.Mesh) {
if (child instanceof Mesh) {
if (child.geometry) child.geometry.dispose();
if (child.material) child.material.dispose();
}
@ -257,7 +267,7 @@ class ThreeViewer extends React.PureComponent<Props, State> {
ThreeViewer.fitMeshToCamera(group);
if (!this.targetCenter) {
const box = new THREE.Box3();
const box = new Box3();
this.targetCenter = box.setFromObject(this.mesh).getCenter();
}
this.updateControlsTarget(this.targetCenter);
@ -334,9 +344,9 @@ class ThreeViewer extends React.PureComponent<Props, State> {
this.bufferGeometry.computeBoundingBox();
this.bufferGeometry.center();
this.bufferGeometry.rotateX(-Math.PI / 2);
this.bufferGeometry.lookAt(new THREE.Vector3(0, 0, 1));
this.bufferGeometry.lookAt(new Vector3(0, 0, 1));
// Get geometry from bufferGeometry
this.geometry = new THREE.Geometry().fromBufferGeometry(this.bufferGeometry);
this.geometry = new Geometry().fromBufferGeometry(this.bufferGeometry);
this.geometry.mergeVertices();
this.geometry.computeVertexNormals();
}
@ -391,7 +401,7 @@ class ThreeViewer extends React.PureComponent<Props, State> {
renderStl(data: any) {
this.createGeometry(data);
this.mesh = new THREE.Mesh(this.geometry, this.material);
this.mesh = new Mesh(this.geometry, this.material);
this.mesh.name = 'model';
this.scene.add(this.mesh);
this.transformGroup(this.mesh);
@ -399,19 +409,19 @@ class ThreeViewer extends React.PureComponent<Props, State> {
renderObj(event: any) {
const mesh = event.detail.loaderRootNode;
this.mesh = new THREE.Group();
this.mesh = new Group();
this.mesh.name = 'model';
// Assign new material
mesh.traverse(child => {
if (child instanceof THREE.Mesh) {
if (child instanceof Mesh) {
// Get geometry from child
const geometry = new THREE.Geometry();
const geometry = new Geometry();
geometry.fromBufferGeometry(child.geometry);
geometry.mergeVertices();
geometry.computeVertexNormals();
// Create and regroup inner objects
const innerObj = new THREE.Mesh(geometry, this.material);
const innerObj = new Mesh(geometry, this.material);
this.mesh.add(innerObj);
// Clean up geometry
geometry.dispose();
@ -454,7 +464,7 @@ class ThreeViewer extends React.PureComponent<Props, State> {
this.grid = ThreeGrid({ size: 100, gridColor, centerLineColor });
this.scene.add(this.grid);
// Camera
this.camera = new THREE.PerspectiveCamera(80, width / height, 0.1, 1000);
this.camera = new PerspectiveCamera(80, width / height, 0.1, 1000);
this.camera.position.set(-9.5, 14, 11);
// Controls
@ -464,10 +474,10 @@ class ThreeViewer extends React.PureComponent<Props, State> {
this.renderer.setSize(width, height);
// Create model material
this.material = new THREE.MeshPhongMaterial({
this.material = new MeshPhongMaterial({
depthWrite: true,
flatShading: true,
vertexColors: THREE.FaceColors,
vertexColors: 1,
});
// Set material color

View file

@ -1,4 +1,5 @@
import { GridHelper, Color } from 'three-full';
import { Color } from 'three-full/sources/math/Color';
import { GridHelper } from 'three-full/sources/helpers/GridHelper';
const ThreeGrid = ({ size, gridColor, centerLineColor }) => {
const divisions = size / 2;

View file

@ -1,4 +1,4 @@
import { WebGLRenderer } from 'three-full';
import { WebGLRenderer } from 'three-full/sources/renderers/WebGLRenderer';
const ThreeRenderer = ({ antialias, shadowMap, gammaCorrection }) => {
const renderer = new WebGLRenderer({ antialias });

View file

@ -1,15 +1,19 @@
import * as THREE from 'three-full';
import { Color } from 'three-full/sources/math/Color';
import { HemisphereLight } from 'three-full/sources/lights/HemisphereLight';
import { DirectionalLight } from 'three-full/sources/lights/DirectionalLight';
import { Scene } from 'three-full/sources/scenes/Scene';
import { Fog } from 'three-full/sources/scenes/Fog';
const addLights = (scene, color, groundColor) => {
// Light color
const lightColor = new THREE.Color(color);
const lightColor = new Color(color);
// Main light
const light = new THREE.HemisphereLight(lightColor, groundColor, 0.4);
const light = new HemisphereLight(lightColor, groundColor, 0.4);
// Shadow light
const shadowLight = new THREE.DirectionalLight(lightColor, 0.4);
const shadowLight = new DirectionalLight(lightColor, 0.4);
shadowLight.position.set(100, 50, 100);
// Back light
const backLight = new THREE.DirectionalLight(lightColor, 0.6);
const backLight = new DirectionalLight(lightColor, 0.6);
backLight.position.set(-100, 200, 50);
// Add lights to scene
scene.add(backLight);
@ -17,19 +21,19 @@ const addLights = (scene, color, groundColor) => {
scene.add(shadowLight);
};
const Scene = ({ backgroundColor, groundColor, showFog }) => {
const ViewerScene = ({ backgroundColor, groundColor, showFog }) => {
// Convert color
const bgColor = new THREE.Color(backgroundColor);
const bgColor = new Color(backgroundColor);
// New scene
const scene = new THREE.Scene();
const scene = new Scene();
// Background color
scene.background = bgColor;
// Fog effect
scene.fog = showFog === true ? new THREE.Fog(bgColor, 1, 95) : null;
scene.fog = showFog === true ? new Fog(bgColor, 1, 95) : null;
// Add basic lights
addLights(scene, '#FFFFFF', groundColor);
// Return new three scene
return scene;
};
export default Scene;
export default ViewerScene;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 278 KiB

View file

@ -32,7 +32,7 @@ export default class extends React.PureComponent<Props> {
{title && subtitle && (
<div className="card__content">
<h2 className="card__title">{title}</h2>
<p className="card__subtitle">{subtitle}</p>
<div className="card__subtitle">{subtitle}</div>
</div>
)}
</div>

View file

@ -1,4 +1,4 @@
/* eslint-disable no-console */
import ErrorBoundary from 'component/errorBoundary';
import App from 'component/app';
import SnackBar from 'component/snackBar';
import SplashScreen from 'component/splash';
@ -214,8 +214,8 @@ const init = () => {
// @if TARGET='app'
moment.locale(remote.app.getLocale());
/* eslint-disable no-console */
autoUpdater.on('error', error => {
// eslint-disable-next-line no-console
console.error(error.message);
});
@ -231,6 +231,7 @@ const init = () => {
app.store.dispatch(doAutoUpdate());
});
}
/* eslint-enable no-console */
app.store.dispatch(doUpdateIsNightAsync());
// @endif
@ -244,10 +245,10 @@ const init = () => {
ReactDOM.render(
<Provider store={store}>
<React.Fragment>
<ErrorBoundary>
<App />
<SnackBar />
</React.Fragment>
</ErrorBoundary>
</Provider>,
document.getElementById('app')
);
@ -272,6 +273,3 @@ const init = () => {
};
init();
/* eslint-enable react/jsx-filename-extension */
/* eslint-enable no-console */

View file

View file

@ -8,7 +8,6 @@ import { shell } from 'electron';
import { Lbry } from 'lbry-redux';
import Native from 'native';
import Button from 'component/button';
import BusyIndicator from 'component/common/busy-indicator';
import Page from 'component/page';
type DeamonSettings = {
@ -55,12 +54,16 @@ class HelpPage extends React.PureComponent<Props, State> {
}
componentDidMount() {
// @if TARGET='app'
Native.getAppVersionInfo().then(({ localVersion, upgradeAvailable }) => {
this.setState({
uiVersion: localVersion,
upgradeAvailable,
});
});
if (!this.props.accessToken) this.props.fetchAccessToken();
// @endif
Lbry.version().then(info => {
this.setState({
versionInfo: info,
@ -71,8 +74,6 @@ class HelpPage extends React.PureComponent<Props, State> {
lbryId: info.installation_id,
});
});
if (!this.props.accessToken) this.props.fetchAccessToken();
}
showAccessToken() {
@ -225,70 +226,66 @@ class HelpPage extends React.PureComponent<Props, State> {
</header>
<div className="card__content">
{this.state.uiVersion && ver ? (
<table className="table table--stretch">
<tbody>
<tr>
<td>{__('App')}</td>
<td>{this.state.uiVersion}</td>
</tr>
<tr>
<td>{__('Daemon (lbrynet)')}</td>
<td>{ver.lbrynet_version}</td>
</tr>
<tr>
<td>{__('Connected Email')}</td>
<td>
{user && user.primary_email ? (
<React.Fragment>
{user.primary_email}{' '}
<Button
button="link"
href={`https://lbry.io/list/edit/${accessToken}`}
label={__('Update mailing preferences')}
/>
</React.Fragment>
) : (
<React.Fragment>
<span className="empty">{__('none')} </span>
<Button button="link" onClick={() => doAuth()} label={__('set email')} />
</React.Fragment>
)}
</td>
</tr>
<tr>
<td>{__('Reward Eligible')}</td>
<td>{user && user.is_reward_approved ? __('Yes') : __('No')}</td>
</tr>
<tr>
<td>{__('Platform')}</td>
<td>{platform}</td>
</tr>
<tr>
<td>{__('Installation ID')}</td>
<td>{this.state.lbryId}</td>
</tr>
<tr>
<td>{__('Access Token')}</td>
<td>
{this.state.accessTokenHidden && (
<Button button="link" label={__('View')} onClick={this.showAccessToken} />
)}
{!this.state.accessTokenHidden && accessToken && (
<div>
<p>{accessToken}</p>
<div className="alert-text">
{__('This is equivalent to a password. Do not post or share this.')}
</div>
<table className="table table--stretch">
<tbody>
<tr>
<td>{__('App')}</td>
<td>{this.state.uiVersion}</td>
</tr>
<tr>
<td>{__('Daemon (lbrynet)')}</td>
<td>{ver ? ver.lbrynet_version : __('Loading...')}</td>
</tr>
<tr>
<td>{__('Connected Email')}</td>
<td>
{user && user.primary_email ? (
<React.Fragment>
{user.primary_email}{' '}
<Button
button="link"
href={`https://lbry.io/list/edit/${accessToken}`}
label={__('Update mailing preferences')}
/>
</React.Fragment>
) : (
<React.Fragment>
<span className="empty">{__('none')} </span>
<Button button="link" onClick={() => doAuth()} label={__('set email')} />
</React.Fragment>
)}
</td>
</tr>
<tr>
<td>{__('Reward Eligible')}</td>
<td>{user && user.is_reward_approved ? __('Yes') : __('No')}</td>
</tr>
<tr>
<td>{__('Platform')}</td>
<td>{platform}</td>
</tr>
<tr>
<td>{__('Installation ID')}</td>
<td>{this.state.lbryId}</td>
</tr>
<tr>
<td>{__('Access Token')}</td>
<td>
{this.state.accessTokenHidden && (
<Button button="link" label={__('View')} onClick={this.showAccessToken} />
)}
{!this.state.accessTokenHidden && accessToken && (
<div>
<p>{accessToken}</p>
<div className="alert-text">
{__('This is equivalent to a password. Do not post or share this.')}
</div>
)}
</td>
</tr>
</tbody>
</table>
) : (
<BusyIndicator message={__('Looking up version info')} />
)}
</div>
)}
</td>
</tr>
</tbody>
</table>
</div>
</section>
</Page>

View file

@ -92,31 +92,6 @@
}
}
.button--link {
word-break: break-all;
&:not(:disabled) {
html[data-mode='dark'] & {
&:not(:hover) {
color: $lbry-teal-4;
}
&:hover {
color: $lbry-teal-3;
}
}
}
}
.button--load-screen {
border-bottom: 1px solid $lbry-white;
display: inline-block;
&:hover {
border-bottom: 1px solid $lbry-blue-1;
color: $lbry-blue-1;
}
}
.button--selected {
font-weight: 800;
color: $lbry-teal-5;

View file

@ -17,6 +17,17 @@
}
}
.load-screen__button {
transition: none;
color: $lbry-white;
border-bottom: 1px solid $lbry-white;
&:hover {
border-bottom: 1px solid $lbry-blue-1;
color: $lbry-blue-1;
}
}
.load-screen__details {
font-weight: 600;
line-height: 1;

View file

@ -3,6 +3,7 @@
display: flex;
justify-content: center;
vertical-align: middle;
text-align: left;
margin-bottom: var(--spacing-vertical-large);
}

View file

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 992 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View file

@ -1,14 +1,17 @@
const path = require('path');
const merge = require('webpack-merge');
const { DefinePlugin, ProvidePlugin } = require('webpack');
const { getIfUtils, removeEmpty } = require('webpack-config-utils');
const { ifProduction } = getIfUtils(process.env.NODE_ENV || 'development');
const UI_ROOT = path.resolve(__dirname, 'src/ui/');
const STATIC_ROOT = path.resolve(__dirname, 'static/');
const DIST_ROOT = path.resolve(__dirname, 'dist/');
const baseConfig = {
mode: 'development',
devtool: 'eval-source-map',
mode: ifProduction('production', 'development'),
devtool: ifProduction('source-map', 'eval-source-map'),
node: {
__dirname: false,
},
@ -48,6 +51,15 @@ const baseConfig = {
},
},
},
{
test: /\.glsl/,
use: {
loader: 'file-loader',
options: {
outputPath: 'ui/three',
},
},
},
],
},
// Allows imports for all directories inside '/ui'

View file

@ -58,7 +58,7 @@ const mainConfig = {
const renderConfig = {
target: 'electron-renderer',
entry: {
ui: './src/ui/index.js',
ui: './src/ui/index.jsx',
},
output: {
filename: '[name].js',

View file

@ -10,7 +10,7 @@ const WEB_PLATFORM_ROOT = path.resolve(__dirname, 'src/platforms/web/');
const webConfig = {
target: 'web',
entry: {
ui: './src/ui/index.js',
ui: './src/ui/index.jsx',
},
output: {
filename: '[name].js',
@ -46,6 +46,10 @@ const webConfig = {
from: `${STATIC_ROOT}/index.html`,
to: `${DIST_ROOT}/web/index.html`,
},
{
from: `${STATIC_ROOT}/img/favicon.ico`,
to: `${DIST_ROOT}/web/favicon.ico`,
},
{
from: `${WEB_PLATFORM_ROOT}/server.js`,
to: `${DIST_ROOT}/web/server.js`,

117
yarn.lock
View file

@ -2641,6 +2641,14 @@ crocket@^0.9.11:
dependencies:
xpipe "*"
cross-env@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.0.tgz#6ecd4c015d5773e614039ee529076669b9d126f2"
integrity sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==
dependencies:
cross-spawn "^6.0.5"
is-windows "^1.0.0"
cross-spawn@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
@ -3678,10 +3686,10 @@ eslint-import-resolver-node@^0.3.2:
debug "^2.6.9"
resolve "^1.5.0"
eslint-import-resolver-webpack@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.9.0.tgz#231ce1578ad5124da5799f029bd33d28137623e3"
integrity sha1-IxzhV4rVEk2leZ8Cm9M9KBN2I+M=
eslint-import-resolver-webpack@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.11.0.tgz#75d08ee06fc55eb24bd75147b7b4b6756886b12f"
integrity sha512-vX8rYSPdKtTLkK2FoU1ZRyEsl6wP1FB40ytjrEgMhzUkEkBLuZAkv1KNR+2Ml7lzMOObXI3yaEDiaQ/Yvoczhw==
dependencies:
array-find "^1.0.0"
debug "^2.6.8"
@ -3689,8 +3697,7 @@ eslint-import-resolver-webpack@^0.9.0:
find-root "^1.1.0"
has "^1.0.1"
interpret "^1.0.0"
is-absolute "^0.2.3"
lodash.get "^4.4.2"
lodash "^4.17.4"
node-libs-browser "^1.0.0 || ^2.0.0"
resolve "^1.4.0"
semver "^5.3.0"
@ -4332,10 +4339,10 @@ flatten@^1.0.2:
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=
flow-bin@^0.89.0:
version "0.89.0"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.89.0.tgz#6bd29c2af7e0f429797f820662f33749105c32fa"
integrity sha512-DkO4PsXYrl53V6G5+t5HbRMC5ajYUQej2LEGPUZ+j9okTb41Sn5j9vfxsCpXMEAslYnQoysHhYu4GUZsQX/DrQ==
flow-bin@^0.94.0:
version "0.94.0"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.94.0.tgz#b5d58fe7559705b73a18229f97edfc3ab6ffffcb"
integrity sha512-DYF7r9CJ/AksfmmB4+q+TyLMoeQPRnqtF1Pk7KY3zgfkB/nVuA3nXyzqgsIPIvnMSiFEXQcFK4z+iPxSLckZhQ==
flow-typed@^2.3.0:
version "2.5.1"
@ -4776,11 +4783,6 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
handle-thing@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754"
@ -5333,14 +5335,6 @@ is-absolute-url@^2.0.0:
resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=
is-absolute@^0.2.3:
version "0.2.6"
resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb"
integrity sha1-IN5p89uULvLYe5wto28XIjWxtes=
dependencies:
is-relative "^0.2.1"
is-windows "^0.2.0"
is-accessor-descriptor@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
@ -5623,13 +5617,6 @@ is-regexp@^1.0.0:
resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk=
is-relative@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5"
integrity sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=
dependencies:
is-unc-path "^0.1.1"
is-resolvable@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
@ -5664,13 +5651,6 @@ is-typedarray@~1.0.0:
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
is-unc-path@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.2.tgz#6ab053a72573c10250ff416a3814c35178af39b9"
integrity sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=
dependencies:
unc-path-regex "^0.1.0"
is-utf8@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
@ -5681,12 +5661,7 @@ is-whitespace-character@^1.0.0:
resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz#ede53b4c6f6fb3874533751ec9280d01928d03ed"
integrity sha512-SzM+T5GKUCtLhlHFKt2SDAX2RFzfS6joT91F2/WSi9LxgFdsnhfPK/UIA+JhRR2xuyLdrCys2PiFDrtn1fU5hQ==
is-windows@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c"
integrity sha1-3hqm1j6indJIc3tp8f+LgALSEIw=
is-windows@^1.0.1, is-windows@^1.0.2:
is-windows@^1.0.0, is-windows@^1.0.1, is-windows@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
@ -6978,16 +6953,6 @@ node-loader@^0.6.0:
resolved "https://registry.yarnpkg.com/node-loader/-/node-loader-0.6.0.tgz#c797ef51095ed5859902b157f6384f6361e05ae8"
integrity sha1-x5fvUQle1YWZArFX9jhPY2HgWug=
node-notifier@5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.2.1.tgz#fa313dd08f5517db0e2502e5758d664ac69f9dea"
integrity sha512-MIBs+AAd6dJ2SklbbE8RUDRlIVhU8MaNLh1A9SUZDUHPiZkWLFde6UNwG41yQHZEToHgJMXqyVZ9UcS/ReOVTg==
dependencies:
growly "^1.3.0"
semver "^5.4.1"
shellwords "^0.1.1"
which "^1.3.0"
node-pre-gyp@^0.10.0:
version "0.10.3"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc"
@ -8289,6 +8254,11 @@ qs@6.5.2, qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
qs@^6.5.2:
version "6.6.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.6.0.tgz#a99c0f69a8d26bf7ef012f871cdabb0aee4424c2"
integrity sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA==
query-string@^4.1.0:
version "4.3.4"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
@ -8349,6 +8319,14 @@ raw-body@2.3.3:
iconv-lite "0.4.23"
unpipe "1.0.0"
raw-loader@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-1.0.0.tgz#3f9889e73dadbda9a424bce79809b4133ad46405"
integrity sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==
dependencies:
loader-utils "^1.1.0"
schema-utils "^1.0.0"
rc-progress@^2.0.6:
version "2.3.0"
resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-2.3.0.tgz#cfbd07ff9026c450100980de209a92650e24f313"
@ -9309,11 +9287,6 @@ shebang-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
shellwords@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
@ -10257,11 +10230,6 @@ unbzip2-stream@^1.0.9:
buffer "^5.2.1"
through "^2.3.8"
unc-path-regex@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo=
underscore@>1.4.4:
version "1.9.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
@ -10736,15 +10704,6 @@ wbuf@^1.1.0, wbuf@^1.7.3:
dependencies:
minimalistic-assert "^1.0.0"
webpack-build-notifier@^0.1.23:
version "0.1.30"
resolved "https://registry.yarnpkg.com/webpack-build-notifier/-/webpack-build-notifier-0.1.30.tgz#837e9002d13c1d76bdf954ccafe0b824b38bb789"
integrity sha512-HeZ4Wr8XP7W0kSmPQkZCXARQVIjVFNPyJBdUqkqcE0ySYNjr6vOH3ufHESLPuy5KmMRUjHJdqJ6y3McDfCjJxQ==
dependencies:
ansi-regex "^2.0.0"
node-notifier "5.2.1"
strip-ansi "^3.0.1"
webpack-cli@^3.2.1:
version "3.2.3"
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.2.3.tgz#13653549adfd8ccd920ad7be1ef868bacc22e346"
@ -10762,6 +10721,20 @@ webpack-cli@^3.2.1:
v8-compile-cache "^2.0.2"
yargs "^12.0.4"
webpack-combine-loaders@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/webpack-combine-loaders/-/webpack-combine-loaders-2.0.4.tgz#27814d52b8329ed6565be39009aac76361e7e22c"
integrity sha512-5O5PYVE5tZ3I3uUm3QB7niLEJzLketl8hvAcJwa4YmwNWS/vixfVsqhtUaBciP8J4u/GwIHV52d7kkgZJFvDnw==
dependencies:
qs "^6.5.2"
webpack-config-utils@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/webpack-config-utils/-/webpack-config-utils-2.3.1.tgz#a51826ddd4de26c10dfdb81cbff8aac0debb0a36"
integrity sha512-0uC5uj7sThFTePTQjfpe5Wqcbw3KSCxqswOmW96lwk2ZI2CU098rWY2ZqOVGJQYJ3hfEltmjcLNkKutw8LJAlg==
dependencies:
webpack-combine-loaders "2.0.4"
webpack-dev-middleware@^3.5.1, webpack-dev-middleware@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.6.0.tgz#71f1b04e52ff8d442757af2be3a658237d53a3e5"