[WAGYU-56-59] Update directory structure, housekeeping, storybook, and more (#65)

* run formatter

* remove vscode settings from gitignore

* add storybook

* add polished

* directly import colors

* add common Header typography component

* fix footer export

* fix App import / export

* use new Header

* format main.ts
This commit is contained in:
Jay Puntham-Baker
2021-08-13 15:58:43 -04:00
committed by GitHub
parent e221ab7432
commit f236bca658
23 changed files with 8358 additions and 355 deletions

3
.gitignore vendored
View File

@@ -1,6 +1,3 @@
node_modules/
dist/
.idea
# Ignore Visual Studio Code IDE workspace settings
.vscode/

View File

@@ -0,0 +1,14 @@
import React from 'react';
import styled from 'styled-components';
const Container = styled.main`
font-family: 'PT Mono', monospace;
`;
export const withGlobalStyles = (storyFn: any) => {
return (
<>
<Container>{storyFn()}</Container>
</>
);
};

4
.storybook/main.ts Normal file
View File

@@ -0,0 +1,4 @@
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
};

14
.storybook/preview.ts Normal file
View File

@@ -0,0 +1,14 @@
import { addDecorator } from '@storybook/react';
import { withGlobalStyles } from './decorators/withGlobalStyles';
addDecorator(withGlobalStyles);
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};

11
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"editor.formatOnSave": true,
"workbench.tree.renderIndentGuides": "always",
"typescript.tsdk": "node_modules/typescript/lib",
"eslint.options": {
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "off",
"prefer-const": "off"
}
}
}

View File

@@ -6,11 +6,17 @@
"author": "Colfax Selby <colfax.selby@gmail.com>",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.15.0",
"@storybook/addon-actions": "^6.3.7",
"@storybook/addon-essentials": "^6.3.7",
"@storybook/addon-links": "^6.3.7",
"@storybook/react": "^6.3.7",
"@types/js-yaml": "^4.0.0",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"@types/react-router-dom": "^5.1.7",
"@types/styled-components": "^5.1.7",
"babel-loader": "^8.2.2",
"electron": "^12.0.0",
"ts-loader": "^8.0.17",
"typescript": "^4.2.2",
@@ -23,15 +29,19 @@
"build:watch": "yarn build -- --watch",
"start": "electron ./dist/electron/index.js",
"dev:electron": "NODE_ENV=development webpack --config webpack.electron.config.js --mode development && electron .",
"dev:react": "NODE_ENV=development webpack-serve --config webpack.react.config.js --mode development"
"dev:react": "NODE_ENV=development webpack-serve --config webpack.react.config.js --mode development",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"dependencies": {
"@rauschma/stringio": "^1.4.0",
"html-webpack-plugin": "^5.2.0",
"js-yaml": "^4.0.0",
"polished": "^4.1.3",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "^5.2.0",
"sb": "^6.3.7",
"shebang-loader": "^0.0.1",
"styled-components": "^5.2.1"
}

View File

@@ -1,6 +1,6 @@
import { BrowserWindow, app, globalShortcut } from "electron";
import { BrowserWindow, app, globalShortcut } from 'electron';
app.on("ready", () => {
app.on('ready', () => {
// once electron has started up, create a window.
const window = new BrowserWindow({
width: 900,
@@ -12,18 +12,17 @@ app.on("ready", () => {
// TODO: is it a problem to disable this?
// https://www.electronjs.org/docs/tutorial/context-isolation#security-considerations
contextIsolation: false
}
contextIsolation: false,
},
});
// hide the default menu bar that comes with the browser window
window.setMenuBarVisibility(false);
globalShortcut.register('CommandOrControl+R', function() {
console.log('CommandOrControl+R is pressed')
window.reload()
})
globalShortcut.register('CommandOrControl+R', function () {
console.log('CommandOrControl+R is pressed');
window.reload();
});
// load a website to display
window.loadURL(`file://${__dirname}/../react/index.html`);

View File

@@ -1,12 +1,12 @@
import React from 'react';
import styled from 'styled-components';
import { HashRouter, Route, Switch } from 'react-router-dom';
import { Background } from './colors';
import { Slate1 } from './colors';
import { Deposit } from './components/Deposit';
import { Home } from './components/Home';
import { InstallFailed } from './components/InstallFailed';
import Installing from './components/Installing';
import Status from './components/Status';
import { Installing } from './components/Installing';
import { StatusPage as Status } from './components/Status';
import { SystemCheck } from './components/SystemCheck';
const Container = styled.main`
@@ -14,10 +14,10 @@ const Container = styled.main`
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: ${Background};
background-color: ${Slate1};
`;
const App = () => {
export const App = () => {
return (
<HashRouter>
<Container>
@@ -33,5 +33,3 @@ const App = () => {
</HashRouter>
);
};
export default App;

View File

@@ -1,30 +1,21 @@
export const Gray1 = "#F1F6FE";
export const Gray2 = "#E3EBF7";
export const Gray3 = "#d0dae8";
export const Gray4 = "#ACBED5";
export const DarkGray = "#A9A9A9";
export const White = '#FFFFFF';
export const Gray1 = '#F1F6FE';
export const Gray2 = '#E3EBF7';
export const Gray3 = '#d0dae8';
export const Gray4 = '#ACBED5';
export const DarkGray = '#A9A9A9';
export const Black = '#000000';
export const TextDark = "#0B1E58";
export const PrimaryBlue = "#3366FF";
export const PrimaryBlueDark = "#174BE6";
export const TextDark = '#0B1E58';
export const PrimaryBlue = '#3366FF';
export const PrimaryBlueDark = '#174BE6';
// https://colorhunt.co/palette/167893
export const DarkBlue = "#0f4c75";
export const MediumBlue = "#3282b8";
export const LightBlue = "#bbe1fa";
export const White = "#FFFFFF";
export const Red = "#fa1e0e";
export const LightGreen = "#52b788";
export const Background = "#1b262c";
export const Button = LightBlue;
export const ButtonHover = LightGreen;
export const Heading = MediumBlue;
export const MainContent = Gray3;
export const MainContentAlert = Red;
export const Black = "#000000"
export const DisabledButton = DarkGray;
export const DarkBlue = '#0F4C75';
export const MediumBlue = '#3282B8';
export const LightBlue = '#BBE1FA';
export const Red = '#FA1E0E';
export const LightGreen = '#52B788';
export const Slate1 = '#1B262C';

View File

@@ -1,24 +1,21 @@
import { executeCommandSync, executeCommandSyncReturnStdout } from "./ExecuteCommand";
import {
executeCommandSync,
executeCommandSyncReturnStdout,
} from './ExecuteCommand';
const doesFileExist = (filename: string): boolean => {
const cmd = "test -f " + filename;
export const doesFileExist = (filename: string): boolean => {
const cmd = 'test -f ' + filename;
const result = executeCommandSync(cmd);
return result == 0;
};
//TODO: add error handling
const readlink = (file: string): string => {
return executeCommandSyncReturnStdout("readlink -f " + file).trim();
}
// TODO: add error handling
export const readlink = (file: string): string => {
return executeCommandSyncReturnStdout('readlink -f ' + file).trim();
};
const which = (tool: string): boolean => {
const cmd = "which " + tool;
export const which = (tool: string): boolean => {
const cmd = 'which ' + tool;
const result = executeCommandSync(cmd);
return result == 0;
}
export {
doesFileExist,
readlink,
which
};
};

View File

@@ -1,18 +1,17 @@
import { exec, execSync, spawn } from 'child_process';
import { streamEnd, streamWrite } from '@rauschma/stringio';
import { Writable } from 'stream';
// TODO: better error handling and logging
// TODO: remove console.log
// TODO: make this work for different operating systems
const UBUNTU_TERMINAL_COMMAND = "/usr/bin/gnome-terminal";
const UBUNTU_TERMINAL_COMMAND = '/usr/bin/gnome-terminal';
type StdoutCallback = (text: string) => void;
const executeCommandAsync = async (cmd: string): Promise<any> => {
console.log("running command async with: " + cmd);
console.log('running command async with: ' + cmd);
return new Promise((resolve, reject) => {
const child = exec(cmd);
@@ -20,25 +19,31 @@ const executeCommandAsync = async (cmd: string): Promise<any> => {
child.once('exit', function (code) {
resolve(code);
});
child.on('error', function (err) {
reject(err);
});
});
}
};
const executeCommandInNewTerminal = (cmd: string, title: string): number => {
return executeCommandSync(UBUNTU_TERMINAL_COMMAND + " --title=\"" + title + "\" -- bash -c '" + cmd + "'");
}
return executeCommandSync(
UBUNTU_TERMINAL_COMMAND +
' --title="' +
title +
'" -- bash -c \'' +
cmd +
"'",
);
};
const executeCommandSync = (cmd: string): number => {
console.log("running command sync with: " + cmd);
console.log('running command sync with: ' + cmd);
try {
execSync(cmd, {stdio: 'inherit'});
execSync(cmd, { stdio: 'inherit' });
return 0;
}
catch (error) {
} catch (error) {
// TODO: more robust error handling
error.status;
error.message;
@@ -47,15 +52,14 @@ const executeCommandSync = (cmd: string): number => {
console.log(error.message);
return error.status;
}
}
};
const executeCommandSyncReturnStdout = (cmd: string): string => {
console.log("running command sync stdout with: " + cmd);
console.log('running command sync stdout with: ' + cmd);
try {
return execSync(cmd).toString();
}
catch (error) {
} catch (error) {
// TODO: more robust error handling
error.status;
error.message;
@@ -64,13 +68,16 @@ const executeCommandSyncReturnStdout = (cmd: string): string => {
console.log(error.message);
return error.message;
}
}
};
const executeCommandStream = (cmd: string, stdoutCallback: StdoutCallback): Promise<any> => {
console.log("running command stream with: " + cmd);
const executeCommandStream = (
cmd: string,
stdoutCallback: StdoutCallback,
): Promise<any> => {
console.log('running command stream with: ' + cmd);
return new Promise((resolve, reject) => {
const child = spawn(cmd, {
shell: true
shell: true,
});
child.stdout.on('data', (data: Buffer) => {
@@ -89,12 +96,20 @@ const executeCommandStream = (cmd: string, stdoutCallback: StdoutCallback): Prom
reject(err);
});
});
}
};
// good resource for this: https://2ality.com/2018/05/child-process-streams.html
const executeCommandWithPromptsAsync = (cmd: string, responses: string[], stdoutCallback: StdoutCallback): Promise<any> => {
console.log("running command with prompts async with: " + cmd + " and responses " + responses.join());
const executeCommandWithPromptsAsync = (
cmd: string,
responses: string[],
stdoutCallback: StdoutCallback,
): Promise<any> => {
console.log(
'running command with prompts async with: ' +
cmd +
' and responses ' +
responses.join(),
);
return new Promise((resolve, reject) => {
const child = spawn(cmd, {
@@ -111,19 +126,19 @@ const executeCommandWithPromptsAsync = (cmd: string, responses: string[], stdout
child.once('exit', function (code) {
resolve(code);
});
child.on('error', function (err) {
reject(err);
});
});
}
};
// TODO: using this sync wait to get the prompt responses to work properly is
// probably not the best - come up with alternative solution
const syncWait = (ms: number) => {
const end = Date.now() + ms
while (Date.now() < end) continue
}
const end = Date.now() + ms;
while (Date.now() < end) continue;
};
async function writeToWritable(writable: Writable, responses: string[]) {
syncWait(1000);
@@ -143,4 +158,4 @@ export {
executeCommandSync,
executeCommandSyncReturnStdout,
executeCommandWithPromptsAsync,
};
};

View File

@@ -1,24 +1,28 @@
import { doesFileExist, readlink } from "./BashUtils";
import fs from 'fs';
import yaml from 'js-yaml';
import { doesFileExist, readlink } from './BashUtils';
import {
executeCommandAsync,
executeCommandInNewTerminal,
executeCommandStream,
executeCommandSyncReturnStdout,
executeCommandWithPromptsAsync,
} from './ExecuteCommand'
} from './ExecuteCommand';
import { Status } from '../types';
import fs from "fs";
import yaml from "js-yaml";
import { Status } from "../types";
const ASKPASS_PATH = 'src/scripts/askpass.sh';
const ROCKET_POOL_EXECUTABLE = '~/bin/rocketpool';
const ROCKET_POOL_DIR = '~/.rocketpool';
const ROCKET_POOL_INSTALL_COMMAND =
'mkdir -p ~/bin && wget https://github.com/rocket-pool/smartnode-install/releases/download/1.0.0-beta.3/rocketpool-cli-linux-amd64 -O ' +
ROCKET_POOL_EXECUTABLE +
' && chmod +x ' +
ROCKET_POOL_EXECUTABLE;
const ASKPASS_PATH = "src/scripts/askpass.sh";
const ROCKET_POOL_EXECUTABLE = "~/bin/rocketpool";
const ROCKET_POOL_DIR = "~/.rocketpool"
const ROCKET_POOL_INSTALL_COMMAND = "mkdir -p ~/bin && wget https://github.com/rocket-pool/smartnode-install/releases/download/1.0.0-beta.3/rocketpool-cli-linux-amd64 -O " + ROCKET_POOL_EXECUTABLE + " && chmod +x " + ROCKET_POOL_EXECUTABLE;
const GETH_SYNC_STATUS_DOCKER_CMD = "docker exec rocketpool_eth1 geth --exec 'eth.syncing' attach ipc:ethclient/geth/geth.ipc";
const GETH_PEERS_DOCKER_CMD = "docker exec rocketpool_eth1 geth --exec 'admin.peers.length' attach ipc:ethclient/geth/geth.ipc";
const GETH_SYNC_STATUS_DOCKER_CMD =
"docker exec rocketpool_eth1 geth --exec 'eth.syncing' attach ipc:ethclient/geth/geth.ipc";
const GETH_PEERS_DOCKER_CMD =
"docker exec rocketpool_eth1 geth --exec 'admin.peers.length' attach ipc:ethclient/geth/geth.ipc";
// TODO: make an installer interface and implement it here, so we can easily extend
// to utilize multiple different installers
@@ -30,165 +34,216 @@ type NodeStatusCallback = (status: Status) => void;
type StdoutCallback = (text: string[]) => void;
const wrapCommandInDockerGroup = (command: string) => {
return "sg docker \"" + command + "\"";
}
return 'sg docker "' + command + '"';
};
// TODO: make this better, it is pretty brittle and peeks into the RP settings implementation
// this is required because we select the client at random, so we need to show the user what is running
const getEth2ClientName = (): string => {
try {
const rpSettings: any = yaml.load(fs.readFileSync(readlink(ROCKET_POOL_DIR + '/settings.yml'), 'utf8'));
const selectedClient = rpSettings["chains"]["eth2"]["client"]["selected"];
const rpSettings: any = yaml.load(
fs.readFileSync(readlink(ROCKET_POOL_DIR + '/settings.yml'), 'utf8'),
);
const selectedClient = rpSettings['chains']['eth2']['client']['selected'];
return selectedClient;
} catch (e) {
console.log(e);
return "";
return '';
}
}
};
const installAndStartRocketPool = async (callback: Callback, stdoutCallback: StdoutCallback) => {
const installAndStartRocketPool = async (
callback: Callback,
stdoutCallback: StdoutCallback,
) => {
// Used for reporting back log messages to caller
// TODO: there has to be a better way to do this...
const consoleMessages: string[] = [];
const internalStdoutCallback = (text: string) => {
consoleMessages.push(text);
stdoutCallback(consoleMessages);
}
};
// cache sudo credentials to be used for install later
const passwordRc = await executeCommandStream("export SUDO_ASKPASS='" + ASKPASS_PATH + "' && sudo -A echo 'Authentication successful.'", internalStdoutCallback);
const passwordRc = await executeCommandStream(
"export SUDO_ASKPASS='" +
ASKPASS_PATH +
"' && sudo -A echo 'Authentication successful.'",
internalStdoutCallback,
);
if (passwordRc != 0) {
console.log("password failed");
console.log('password failed');
callback(false);
return;
}
const cliRc = await executeCommandStream(ROCKET_POOL_INSTALL_COMMAND, internalStdoutCallback);
const cliRc = await executeCommandStream(
ROCKET_POOL_INSTALL_COMMAND,
internalStdoutCallback,
);
if (cliRc != 0) {
console.log("cli failed to install");
console.log('cli failed to install');
callback(false);
return;
}
const serviceRc = await executeCommandStream(ROCKET_POOL_EXECUTABLE + " service install --yes --network pyrmont", internalStdoutCallback);
const serviceRc = await executeCommandStream(
ROCKET_POOL_EXECUTABLE + ' service install --yes --network pyrmont',
internalStdoutCallback,
);
if (serviceRc != 0) {
console.log("service install failed");
console.log('service install failed');
callback(false);
return;
}
// For some reason executeCommandWithPromptsAsync needs the full path, so fetching it here
const rocketPoolExecutableFullPath = readlink(ROCKET_POOL_EXECUTABLE);
console.log("full path");
console.log('full path');
console.log(rocketPoolExecutableFullPath);
const promptResponses = [
"1\n", // which eth1 client? 1 geth, 2 infura, 3 custom
"\n", // ethstats label
"\n", // ethstats login
"\n", // Cache size
"\n", // Max peers
"\n", // P2P port
"y\n", // random eth2 client? y/n
"\n", // graffiti
"\n", // Max peers
"\n", // P2P port
]
'1\n', // which eth1 client? 1 geth, 2 infura, 3 custom
'\n', // ethstats label
'\n', // ethstats login
'\n', // Cache size
'\n', // Max peers
'\n', // P2P port
'y\n', // random eth2 client? y/n
'\n', // graffiti
'\n', // Max peers
'\n', // P2P port
];
const serviceConfigRc = await executeCommandWithPromptsAsync(rocketPoolExecutableFullPath + " service config", promptResponses, internalStdoutCallback);
const serviceConfigRc = await executeCommandWithPromptsAsync(
rocketPoolExecutableFullPath + ' service config',
promptResponses,
internalStdoutCallback,
);
if (serviceConfigRc != 0) {
console.log("service config failed");
console.log('service config failed');
callback(false);
return;
}
// Just in case nodes were running - pick up new config (might happen anyway on start, not sure)
const stopNodesRc = await executeCommandStream(wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + " service stop -y"), internalStdoutCallback);
const stopNodesRc = await executeCommandStream(
wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + ' service stop -y'),
internalStdoutCallback,
);
if (stopNodesRc != 0) {
console.log("stop nodes failed");
console.log('stop nodes failed');
callback(false);
}
const startNodesRc = await executeCommandStream(wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + " service start"), internalStdoutCallback);
const startNodesRc = await executeCommandStream(
wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + ' service start'),
internalStdoutCallback,
);
if (startNodesRc != 0) {
console.log("start nodes failed");
console.log('start nodes failed');
callback(false);
}
await executeCommandStream("echo 'Install complete - redirecting...'", internalStdoutCallback);
await executeCommandStream(
"echo 'Install complete - redirecting...'",
internalStdoutCallback,
);
callback(true);
}
};
const isRocketPoolInstalled = (): boolean => {
return doesFileExist(ROCKET_POOL_EXECUTABLE)
}
return doesFileExist(ROCKET_POOL_EXECUTABLE);
};
const openEth1Logs = () => {
const openEth1LogsRc = executeCommandInNewTerminal(wrapCommandInDockerGroup("docker container logs -f rocketpool_eth1"), "eth1 (geth) logs");
const openEth1LogsRc = executeCommandInNewTerminal(
wrapCommandInDockerGroup('docker container logs -f rocketpool_eth1'),
'eth1 (geth) logs',
);
if (openEth1LogsRc != 0) {
console.log("failed to open eth1 logs");
console.log('failed to open eth1 logs');
return;
}
}
};
const openEth2BeaconLogs = () => {
const openEth2BeaconLogsRc = executeCommandInNewTerminal(wrapCommandInDockerGroup("docker container logs -f rocketpool_eth2"), "eth2 beacon node (" + getEth2ClientName() + ") logs");
const openEth2BeaconLogsRc = executeCommandInNewTerminal(
wrapCommandInDockerGroup('docker container logs -f rocketpool_eth2'),
'eth2 beacon node (' + getEth2ClientName() + ') logs',
);
if (openEth2BeaconLogsRc != 0) {
console.log("failed to open eth2 beacon logs");
console.log('failed to open eth2 beacon logs');
return;
}
}
};
const openEth2ValidatorLogs = () => {
const openEth2ValidatorLogsRc = executeCommandInNewTerminal(wrapCommandInDockerGroup("docker container logs -f rocketpool_validator"), "eth2 validator (" + getEth2ClientName() + ") logs");
const openEth2ValidatorLogsRc = executeCommandInNewTerminal(
wrapCommandInDockerGroup('docker container logs -f rocketpool_validator'),
'eth2 validator (' + getEth2ClientName() + ') logs',
);
if (openEth2ValidatorLogsRc != 0) {
console.log("failed to open eth2 validator logs");
console.log('failed to open eth2 validator logs');
return;
}
}
};
const startNodes = (): Promise<any> => {
return executeCommandAsync(wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + " service start"));
}
return executeCommandAsync(
wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + ' service start'),
);
};
const stopNodes = (): Promise<any> => {
return executeCommandAsync(wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + " service stop -y"));
}
return executeCommandAsync(
wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + ' service stop -y'),
);
};
const queryEth1PeerCount = (): number => {
const numPeers = executeCommandSyncReturnStdout(wrapCommandInDockerGroup(GETH_PEERS_DOCKER_CMD));
const numPeers = executeCommandSyncReturnStdout(
wrapCommandInDockerGroup(GETH_PEERS_DOCKER_CMD),
);
const numPeersNumber = parseInt(numPeers.trim());
return isNaN(numPeersNumber) ? 0 : numPeersNumber;
}
};
const queryEth1Status = (nodeStatusCallback: NodeStatusCallback) => {
dockerContainerStatus("rocketpool_eth1", nodeStatusCallback);
}
dockerContainerStatus('rocketpool_eth1', nodeStatusCallback);
};
const queryEth1Syncing = (): boolean => {
const syncValue = executeCommandSyncReturnStdout(wrapCommandInDockerGroup(GETH_SYNC_STATUS_DOCKER_CMD));
return !syncValue.includes("false");
}
const syncValue = executeCommandSyncReturnStdout(
wrapCommandInDockerGroup(GETH_SYNC_STATUS_DOCKER_CMD),
);
return !syncValue.includes('false');
};
const queryEth2BeaconStatus = (nodeStatusCallback: NodeStatusCallback) => {
dockerContainerStatus("rocketpool_eth2", nodeStatusCallback);
}
dockerContainerStatus('rocketpool_eth2', nodeStatusCallback);
};
const queryEth2ValidatorStatus = (nodeStatusCallback: NodeStatusCallback) => {
dockerContainerStatus("rocketpool_validator", nodeStatusCallback);
}
dockerContainerStatus('rocketpool_validator', nodeStatusCallback);
};
const dockerContainerStatus = async (containerName: string, nodeStatusCallback: NodeStatusCallback) => {
const containerId = executeCommandSyncReturnStdout(wrapCommandInDockerGroup("docker ps -q -f name=" + containerName));
const dockerContainerStatus = async (
containerName: string,
nodeStatusCallback: NodeStatusCallback,
) => {
const containerId = executeCommandSyncReturnStdout(
wrapCommandInDockerGroup('docker ps -q -f name=' + containerName),
);
if (containerId.trim()) {
nodeStatusCallback(Status.Online);
} else {
nodeStatusCallback(Status.Offline);
}
}
};
export {
getEth2ClientName,
@@ -204,4 +259,4 @@ export {
queryEth1Syncing,
queryEth2BeaconStatus,
queryEth2ValidatorStatus,
}
};

View File

@@ -1,8 +1,10 @@
import React from 'react';
import { shell } from 'electron';
import styled from 'styled-components';
import { Black, DisabledButton, Heading, MainContent } from '../colors';
import Footer from './Footer';
import { rem } from 'polished';
import { Black, DarkGray, MediumBlue, Gray3 } from '../colors';
import { Footer } from './Footer';
import { Header } from './typography/Header';
const Container = styled.div`
display: flex;
@@ -12,24 +14,19 @@ const Container = styled.div`
min-height: 100vh;
`;
const LandingHeader = styled.div`
font-weight: 700;
font-size: 35;
margin-top: 50;
color: ${Heading};
max-width: 550;
const LandingHeader = styled(Header)`
flex-grow: 1;
`;
const Content = styled.div`
color: ${MainContent};
color: ${Gray3};
margin-top: 20;
width: 650;
flex-grow: 6;
`;
const StyledLink = styled.span`
color: ${Heading};
color: ${MediumBlue};
cursor: pointer;
`;
@@ -46,9 +43,9 @@ const ImportKeysButton = styled.div`
justify-content: center;
align-items: center;
width: fit-content;
background-color: ${DisabledButton};
background-color: ${DarkGray};
padding: 16 24;
border-radius: 20px;
border-radius: ${rem(20)};
text-decoration: none;
transition: 250ms background-color ease;

View File

@@ -1,7 +1,7 @@
import React from 'react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
import { Black, Button, ButtonHover } from '../colors';
import { Black, LightBlue, LightGreen } from '../colors';
type FooterProps = {
backLink: string;
@@ -29,7 +29,7 @@ const StyledButton = styled(Link)`
align-items: center;
align-self: flex-end;
height: 24;
background-color: ${Button};
background-color: ${LightBlue};
padding: 16 24;
border-radius: 10%;
text-decoration: none;
@@ -39,11 +39,11 @@ const StyledButton = styled(Link)`
margin: 60;
&:hover {
background-color: ${ButtonHover};
background-color: ${LightGreen};
}
`;
const Footer = (props: FooterProps) => {
export const Footer = (props: FooterProps) => {
return (
<FooterContainer>
{props.backLink ? (
@@ -59,5 +59,3 @@ const Footer = (props: FooterProps) => {
</FooterContainer>
);
};
export default Footer;

View File

@@ -6,12 +6,13 @@ import { History } from 'history';
import { isRocketPoolInstalled } from '../commands/RocketPool';
import {
Black,
Button,
ButtonHover,
Heading,
MainContent,
LightBlue,
LightGreen,
MediumBlue,
Gray3,
Red,
} from '../colors';
import { Header } from './typography/Header';
const Container = styled.div`
display: flex;
@@ -20,17 +21,13 @@ const Container = styled.div`
align-items: center;
`;
const LandingHeader = styled.div`
font-weight: 700;
font-size: 35;
const LandingHeader = styled(Header)`
margin-top: 120;
color: ${Heading};
max-width: 550;
text-align: center;
`;
const Content = styled.div`
color: ${MainContent};
color: ${Gray3};
margin-top: 40;
max-width: 650;
`;
@@ -42,7 +39,7 @@ const StartButton = styled(Link)`
justify-content: center;
align-items: center;
height: 24;
background-color: ${Button};
background-color: ${LightBlue};
padding: 16 24;
border-radius: 10%;
text-decoration: none;
@@ -52,12 +49,12 @@ const StartButton = styled(Link)`
margin-top: 60;
&:hover {
background-color: ${ButtonHover};
background-color: ${LightGreen};
}
`;
const StyledLink = styled.em`
color: ${Heading};
color: ${MediumBlue};
cursor: pointer;
`;

View File

@@ -1,7 +1,8 @@
import React from 'react';
import styled from 'styled-components';
import { Heading, MainContent } from '../colors';
import Footer from './Footer';
import { Gray3 } from '../colors';
import { Footer } from './Footer';
import { Header } from './typography/Header';
const Container = styled.div`
display: flex;
@@ -11,17 +12,12 @@ const Container = styled.div`
min-height: 100vh;
`;
const LandingHeader = styled.div`
font-weight: 700;
font-size: 35;
margin-top: 50;
color: ${Heading};
max-width: 550;
const LandingHeader = styled(Header)`
flex-grow: 1;
`;
const Content = styled.div`
color: ${MainContent};
color: ${Gray3};
margin-top: 20;
width: 650;
flex-grow: 6;

View File

@@ -1,9 +1,11 @@
import React, { useEffect, useRef, useState } from 'react';
import styled, { keyframes } from 'styled-components';
import { rem } from 'polished';
import { withRouter } from 'react-router-dom';
import { History } from 'history';
import { Heading, MainContent } from '../colors';
import { Gray3 } from '../colors';
import { installAndStartRocketPool } from '../commands/RocketPool';
import { Header } from './typography/Header';
const Container = styled.div`
display: flex;
@@ -13,17 +15,12 @@ const Container = styled.div`
min-height: 100vh;
`;
const LandingHeader = styled.div`
font-weight: 700;
font-size: 35;
margin-top: 50;
color: ${Heading};
max-width: 550;
const LandingHeader = styled(Header)`
flex-grow: 1;
`;
const Content = styled.div`
color: ${MainContent};
color: ${Gray3};
margin-top: 20;
width: 650;
flex-grow: 6;
@@ -41,22 +38,22 @@ const SpinnerContainer = styled.div`
`;
const LoadingSpinner = styled.div`
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border: ${rem(16)} solid #f3f3f3; /* Light grey */
border-top: ${rem(16)} solid #3498db; /* Blue */
border-radius: 50%;
margin-top: 30px;
width: 120px;
height: 120px;
margin-top: ${rem(30)};
width: ${rem(120)};
height: ${rem(120)};
animation: ${rotate} 2s linear infinite;
`;
const LogsContainer = styled.div`
height: 250px;
height: ${rem(250)};
width: 100%;
margin-top: 5px;
margin-top: ${rem(5)};
overflow-y: auto;
background-color: white;
border-radius: 5px;
border-radius: ${rem(5)};
border-style: groove;
color: black;
`;
@@ -74,7 +71,7 @@ const LogsListItem = styled.li`
const LogsContainerAnchor = styled.div``;
const Installing = ({ history }: { history: History }) => {
export const Installing = withRouter(({ history }: { history: History }) => {
const anchorRef = useRef(document.createElement('div'));
const [stdoutText, setStdoutText] = useState(['']);
@@ -139,6 +136,4 @@ const Installing = ({ history }: { history: History }) => {
</Content>
</Container>
);
};
export default withRouter(Installing);
});

View File

@@ -1,13 +1,14 @@
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { rem } from 'polished';
import {
Black,
Button,
ButtonHover,
LightBlue,
LightGreen,
DarkGray,
Gray4,
Heading,
MainContent,
MediumBlue,
Gray3,
} from '../colors';
import { Status, AllStatuses, NodeStatuses } from '../types';
import {
@@ -31,7 +32,8 @@ import {
startNodes,
stopNodes,
} from '../commands/RocketPool';
import Footer from './Footer';
import { Footer } from './Footer';
import { Header } from './typography/Header';
const NodeStatus: NodeStatuses = {
Online: { code: 0, text: 'Online', character: '\u2B24', color: 'green' },
@@ -48,17 +50,12 @@ const Container = styled.div`
min-height: 100vh;
`;
const LandingHeader = styled.div`
font-weight: 700;
font-size: 35;
margin-top: 50;
color: ${Heading};
max-width: 550;
const LandingHeader = styled(Header)`
flex-grow: 1;
`;
const Content = styled.div`
color: ${MainContent};
color: ${Gray3};
margin-top: 20;
width: 650;
flex-grow: 6;
@@ -67,13 +64,13 @@ const Content = styled.div`
const ResultsTable = styled.table`
border: 2px solid gray;
width: 100%;
padding: 15px;
padding: ${rem(15)};
text-align: left;
color: white;
`;
const StyledLink = styled.span`
color: ${Heading};
color: ${MediumBlue};
cursor: pointer;
`;
@@ -83,15 +80,15 @@ const LogsButton = styled.button`
flex-direction: row;
justify-content: center;
align-items: center;
background-color: ${Button};
border-radius: 10px;
background-color: ${LightBlue};
border-radius: ${rem(10)};
text-decoration: none;
transition: 250ms background-color ease;
cursor: pointer;
&:hover {
background-color: ${ButtonHover};
background-color: ${LightGreen};
}
&:disabled {
@@ -104,17 +101,21 @@ const LogsButton = styled.button`
// TODO: right after install, while nodes are starting up, this page says everything is "online"
// while things are looking for peers. Need to improve that logic.
const StatusPage = () => {
export const StatusPage = () => {
const [eth1ContainerStatus, setEth1ContainerStatus] = useState<Status>(
Status.Loading,
);
const [eth1PeerCount, setEth1PeerCount] = useState(0);
const [eth1Syncing, setEth1Syncing] = useState(false);
const [eth2ClientName, setEth2ClientName] = useState('');
const [eth2BeaconContainerStatus, setEth2BeaconContainerStatus] =
useState<Status>(Status.Loading);
const [eth2ValidatorContainerStatus, setEth2ValidatorContainerStatus] =
useState<Status>(Status.Loading);
const [
eth2BeaconContainerStatus,
setEth2BeaconContainerStatus,
] = useState<Status>(Status.Loading);
const [
eth2ValidatorContainerStatus,
setEth2ValidatorContainerStatus,
] = useState<Status>(Status.Loading);
const [ProcessingTotalStatus, setProcessingTotalStatus] = useState<Status>(
Status.Online,
);
@@ -378,5 +379,3 @@ const StatusPage = () => {
</Container>
);
};
export default StatusPage;

View File

@@ -1,8 +1,10 @@
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { DarkBlue, Heading, MainContent } from '../colors';
import Footer from './Footer';
import { rem } from 'polished';
import { DarkBlue, Gray3 } from '../colors';
import { Footer } from './Footer';
import { isRocketPoolInstalled } from '../commands/RocketPool';
import { Header } from './typography/Header';
const Container = styled.div`
display: flex;
@@ -12,17 +14,12 @@ const Container = styled.div`
min-height: 100vh;
`;
const LandingHeader = styled.div`
font-weight: 700;
font-size: 35;
margin-top: 50;
color: ${Heading};
max-width: 550;
const LandingHeader = styled(Header)`
flex-grow: 1;
`;
const Content = styled.div`
color: ${MainContent};
color: ${Gray3};
margin-top: 20;
width: 650;
flex-grow: 6;
@@ -38,7 +35,7 @@ const Advanced = styled.div`
const ResultsTable = styled.table`
border: 2px solid gray;
width: 75%;
padding: 15px;
padding: ${rem(15)};
text-align: left;
color: white;
`;

View File

@@ -0,0 +1,15 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { Header } from './Header';
export default {
title: 'Example/HeaderNew',
component: Header,
} as ComponentMeta<typeof Header>;
const Template: ComponentStory<typeof Header> = () => {
return <Header>Test Header Text</Header>;
};
export const LoggedIn = Template.bind({});

View File

@@ -0,0 +1,10 @@
import styled from 'styled-components';
import { MediumBlue } from '../../colors';
export const Header = styled.div`
font-weight: 700;
font-size: 35;
color: ${MediumBlue};
max-width: 550;
margin-top: 50;
`;

View File

@@ -1,10 +1,10 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App from "./App";
import { App } from './App';
// We find our app DOM element as before
const app = document.getElementById("app");
const app = document.getElementById('app');
// Finally, we render our top-level component to the actual DOM.
ReactDOM.render(<App />, app);

8074
yarn.lock

File diff suppressed because it is too large Load Diff