integration: reduced client to essential functions

This commit is contained in:
jimmylee 2020-06-07 23:45:53 -07:00
parent d65a9efd8e
commit bf4761eb37
19 changed files with 469 additions and 632 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ DS_STORE
/**/*/.data /**/*/.data
.data/**/* .data/**/*
.library/**/*

View File

@ -8,7 +8,7 @@ const REQUEST_HEADERS = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}; };
const SERVER_PATH = ''; const SERVER_PATH = 'http://localhost:1337';
export const rehydrateViewer = async () => { export const rehydrateViewer = async () => {
const options = { const options = {
@ -18,10 +18,24 @@ export const rehydrateViewer = async () => {
body: JSON.stringify({}), body: JSON.stringify({}),
}; };
const response = await fetch(`/_/viewer`, options); const response = await fetch(`${SERVER_PATH}/_/viewer`, options);
const json = await response.json(); const json = await response.json();
return State.getInitialState(json.data); return json;
};
export const setDefaultConfig = async (data) => {
const options = {
method: 'POST',
headers: REQUEST_HEADERS,
credentials: 'include',
body: JSON.stringify(data),
};
const response = await fetch(`${SERVER_PATH}/_/settings`, options);
const json = await response.json();
return json;
}; };
export const createWalletAddress = async (data) => { export const createWalletAddress = async (data) => {
@ -36,7 +50,7 @@ export const createWalletAddress = async (data) => {
body: JSON.stringify(data), body: JSON.stringify(data),
}; };
const response = await fetch(`/_/wallet/create`, options); const response = await fetch(`${SERVER_PATH}/_/wallet/create`, options);
const json = await response.json(); const json = await response.json();
return json; return json;
@ -62,7 +76,7 @@ export const sendWalletAddressFilecoin = async (data) => {
body: JSON.stringify(data), body: JSON.stringify(data),
}; };
const response = await fetch(`/_/wallet/send`, options); const response = await fetch(`${SERVER_PATH}/_/wallet/send`, options);
const json = await response.json(); const json = await response.json();
return json; return json;

View File

@ -488,6 +488,7 @@ export const NavigationState = [
name: 'Wallet', name: 'Wallet',
pageTitle: 'your wallet and addresses', pageTitle: 'your wallet and addresses',
decorator: 'WALLET', decorator: 'WALLET',
/*
children: [ children: [
{ {
id: 3, id: 3,
@ -497,6 +498,7 @@ export const NavigationState = [
decorator: 'CHANNELS', decorator: 'CHANNELS',
}, },
], ],
*/
}, },
constructFilesTreeForNavigation(), constructFilesTreeForNavigation(),
{ {
@ -514,6 +516,7 @@ export const NavigationState = [
}, },
], ],
}, },
/*
{ {
id: 9, id: 9,
name: 'Stats', name: 'Stats',
@ -557,6 +560,7 @@ export const NavigationState = [
decorator: 'LOGS', decorator: 'LOGS',
children: null, children: null,
}, },
*/
{ {
id: 13, id: 13,
name: 'Edit account', name: 'Edit account',

View File

@ -1,4 +1,4 @@
const STATIC_ADDRESS_TYPE_MAP = { bls: 'BLS' }; const STATIC_ADDRESS_TYPE_MAP = { bls: 'BLS', secp256k1: 'SECP256K1', multisig: 'MULTISIG' };
const transformAddresses = (addrsList, info) => { const transformAddresses = (addrsList, info) => {
const balanceMap = {}; const balanceMap = {};
@ -8,7 +8,8 @@ const transformAddresses = (addrsList, info) => {
return addrsList.map((each, index) => { return addrsList.map((each, index) => {
return { return {
value: `${index + 1}`, id: each.addr,
value: each.addr,
balance: balanceMap[each.addr], balance: balanceMap[each.addr],
name: each.name, name: each.name,
address: each.addr, address: each.addr,
@ -26,7 +27,7 @@ const transformPeers = (peersList) => {
'peer-avatar': null, 'peer-avatar': null,
'chain-head': null, 'chain-head': null,
height: null, height: null,
location: each.location.country, location: null,
upload: null, upload: null,
download: null, download: null,
}; };
@ -35,12 +36,11 @@ const transformPeers = (peersList) => {
export const getInitialState = (props) => { export const getInitialState = (props) => {
const { status, messageList, peersList, addrsList, info } = props; const { status, messageList, peersList, addrsList, info } = props;
console.log(props);
return { return {
id: info.id, id: info.id,
name: 'Andrew Hill', name: 'New Node',
photoURL: '/static/avatar-andrew-hill.jpg', photoURL: '/static/system/avatar.png',
config: '', config: '',
upload_bandwidth: 0, upload_bandwidth: 0,
download_bandwidth: 0, download_bandwidth: 0,

View File

@ -5,37 +5,42 @@ const WEEK = DAY * 7;
const MONTH = (DAY * 365) / 12; const MONTH = (DAY * 365) / 12;
const YEAR = DAY * 365; const YEAR = DAY * 365;
export const copyText = (str) => {
const el = document.createElement('textarea');
el.value = str;
el.setAttribute('readonly', '');
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
return true;
};
export const hexToRGBA = (hex, alpha = 1) => { export const hexToRGBA = (hex, alpha = 1) => {
hex = hex.replace("#", ""); hex = hex.replace('#', '');
var r = parseInt( var r = parseInt(hex.length == 3 ? hex.slice(0, 1).repeat(2) : hex.slice(0, 2), 16);
hex.length == 3 ? hex.slice(0, 1).repeat(2) : hex.slice(0, 2), var g = parseInt(hex.length == 3 ? hex.slice(1, 2).repeat(2) : hex.slice(2, 4), 16);
16 var b = parseInt(hex.length == 3 ? hex.slice(2, 3).repeat(2) : hex.slice(4, 6), 16);
);
var g = parseInt(
hex.length == 3 ? hex.slice(1, 2).repeat(2) : hex.slice(2, 4),
16
);
var b = parseInt(
hex.length == 3 ? hex.slice(2, 3).repeat(2) : hex.slice(4, 6),
16
);
if (alpha) { if (alpha) {
return "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"; return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')';
} else { } else {
return "rgb(" + r + ", " + g + ", " + b + ")"; return 'rgb(' + r + ', ' + g + ', ' + b + ')';
} }
}; };
export const bytesToSize = (bytes, decimals = 2) => { export const bytesToSize = (bytes, decimals = 2) => {
if (bytes === 0) return "0 Bytes"; if (bytes === 0) return '0 Bytes';
const k = 1024; const k = 1024;
const dm = decimals < 0 ? 0 : decimals; const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k)); const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]; return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}; };
export const getRemainingTime = (seconds) => { export const getRemainingTime = (seconds) => {
@ -43,18 +48,18 @@ export const getRemainingTime = (seconds) => {
let [value, unit] = let [value, unit] =
seconds < MINUTE seconds < MINUTE
? [Math.round(seconds), "second"] ? [Math.round(seconds), 'second']
: seconds < HOUR : seconds < HOUR
? [Math.round(seconds / MINUTE), "minute"] ? [Math.round(seconds / MINUTE), 'minute']
: seconds < DAY : seconds < DAY
? [Math.round(seconds / HOUR), "hour"] ? [Math.round(seconds / HOUR), 'hour']
: seconds < WEEK : seconds < WEEK
? [Math.round(seconds / DAY), "day"] ? [Math.round(seconds / DAY), 'day']
: seconds < MONTH : seconds < MONTH
? [Math.round(seconds / WEEK), "week"] ? [Math.round(seconds / WEEK), 'week']
: seconds < YEAR : seconds < YEAR
? [Math.round(seconds / MONTH), "month"] ? [Math.round(seconds / MONTH), 'month']
: [Math.round(seconds / YEAR), "year"]; : [Math.round(seconds / YEAR), 'year'];
unit = pluralize(unit, value); unit = pluralize(unit, value);
@ -73,7 +78,7 @@ export const pluralize = (text, count) => {
return count > 1 || count === 0 ? `${text}s` : text; return count > 1 || count === 0 ? `${text}s` : text;
}; };
export const elide = (string, length = 140, emptyState = "...") => { export const elide = (string, length = 140, emptyState = '...') => {
if (isEmpty(string)) { if (isEmpty(string)) {
return emptyState; return emptyState;
} }
@ -91,5 +96,5 @@ export const toDate = (data) => {
}; };
export const formatNumber = (x) => { export const formatNumber = (x) => {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}; };

View File

@ -1,13 +1,11 @@
import * as React from "react"; import * as React from 'react';
import * as Strings from "~/common/strings"; import * as Constants from '~/common/constants';
import * as Constants from "~/common/constants"; import * as System from '~/components/system';
import * as System from "~/components/system"; import * as SVG from '~/common/svg';
import * as SVG from "~/common/svg";
import { css } from "@emotion/react"; import { css } from '@emotion/react';
import Avatar from "~/components/core/Avatar"; import Avatar from '~/components/core/Avatar';
import Pill from "~/components/core/Pill";
const STYLES_CIRCLE = css` const STYLES_CIRCLE = css`
height: 32px; height: 32px;
@ -115,118 +113,52 @@ const STYLES_INPUT = css`
transition: 200ms ease all; transition: 200ms ease all;
:focus { :focus {
box-shadow: 0 1px 4px rgba(0, 71, 255, 0.3), box-shadow: 0 1px 4px rgba(0, 71, 255, 0.3), inset 0 0 0 1px ${Constants.system.brand};
inset 0 0 0 1px ${Constants.system.brand};
outline: 0; outline: 0;
} }
`; `;
export default class ApplicationHeader extends React.Component { export default class ApplicationHeader extends React.Component {
render() { render() {
const isBackDisabled = const isBackDisabled = this.props.currentIndex === 0 || this.props.history.length < 2;
this.props.currentIndex === 0 || this.props.history.length < 2;
const isForwardDisabled = const isForwardDisabled =
this.props.currentIndex === this.props.history.length - 1 || this.props.currentIndex === this.props.history.length - 1 || this.props.history.length < 2;
this.props.history.length < 2;
return ( return (
<header css={STYLES_APPLICATION_HEADER}> <header css={STYLES_APPLICATION_HEADER}>
<div css={STYLES_LEFT}> <div css={STYLES_LEFT}>
<span <span css={STYLES_ICON_ELEMENT_CUSTOM} style={{ marginRight: 16, marginLeft: 12 }}>
css={STYLES_ICON_ELEMENT_CUSTOM}
style={{ marginRight: 16, marginLeft: 12 }}
>
<SVG.Logo height="32px" /> <SVG.Logo height="32px" />
</span> </span>
<span <span
css={STYLES_ICON_ELEMENT} css={STYLES_ICON_ELEMENT}
style={ style={isBackDisabled ? { cursor: 'not-allowed', color: Constants.system.border } : null}
isBackDisabled onClick={isBackDisabled ? () => {} : this.props.onBack}>
? { cursor: "not-allowed", color: Constants.system.border } <SVG.NavigationArrow height="16px" style={{ transform: `rotate(180deg)` }} />
: null
}
onClick={isBackDisabled ? () => {} : this.props.onBack}
>
<SVG.NavigationArrow
height="16px"
style={{ transform: `rotate(180deg)` }}
/>
</span> </span>
<span <span
css={STYLES_ICON_ELEMENT} css={STYLES_ICON_ELEMENT}
style={ style={isForwardDisabled ? { cursor: 'not-allowed', color: Constants.system.border } : null}
isForwardDisabled onClick={isForwardDisabled ? () => {} : this.props.onForward}>
? { cursor: "not-allowed", color: Constants.system.border }
: null
}
onClick={isForwardDisabled ? () => {} : this.props.onForward}
>
<SVG.NavigationArrow height="16px" /> <SVG.NavigationArrow height="16px" />
</span> </span>
</div> </div>
<div css={STYLES_MIDDLE}> <div css={STYLES_MIDDLE}></div>
<input
css={STYLES_INPUT}
placeholder={`Type to search ${this.props.pageTitle}`}
/>
</div>
<div css={STYLES_RIGHT}> <div css={STYLES_RIGHT}>
<System.StatUpload style={{ marginRight: 8 }}>
{Strings.bytesToSize(this.props.viewer.upload_bandwidth)}
</System.StatUpload>{" "}
<System.StatDownload style={{ marginRight: 8 }}>
{Strings.bytesToSize(this.props.viewer.download_bandwidth)}
</System.StatDownload>
<div
css={STYLES_CIRCLE}
onClick={() =>
this.props.onAction({
name: "Connection",
type: "ACTION",
value: "ACTION_TOGGLE_CONNECTION",
})
}
>
<SVG.PowerButton height="16px" />
</div>
<div
css={STYLES_CIRCLE}
style={{
marginLeft: 12,
}}
onClick={() =>
this.props.onAction({
name: "Notifications",
type: "SIDEBAR",
value: "SIDEBAR_NOTIFICATIONS",
})
}
>
<SVG.Bell height="16px" />
{this.props.viewer.notifications.length > 0 ? (
<Pill
style={{
left: 20,
top: `-4px`,
}}
>
{this.props.viewer.notifications.length}
</Pill>
) : null}
</div>
<Avatar <Avatar
style={{ marginLeft: 12 }} style={{ marginLeft: 12 }}
onClick={() => {}}
size={32} size={32}
url={this.props.viewer.photoURL} url={this.props.viewer.photoURL}
popover={ popover={
<System.PopoverNavigation <System.PopoverNavigation
style={{ right: 0, top: "48px" }} style={{ right: 0, top: '48px', cursor: 'pointer' }}
onNavigateTo={this.props.onNavigateTo} onNavigateTo={this.props.onNavigateTo}
onAction={this.props.onAction} onAction={this.props.onAction}
navigation={[ navigation={[
{ text: "Edit account", value: 13 }, { text: 'Edit account', value: 13 },
{ text: "Settings", value: 14 }, { text: 'Settings', value: 14 },
]} ]}
/> />
} }

View File

@ -1,9 +1,9 @@
import * as React from "react"; import * as React from 'react';
import * as Constants from "~/common/constants"; import * as Constants from '~/common/constants';
import { css } from "@emotion/react"; import { css } from '@emotion/react';
import Dismissible from "~/components/core/Dismissible"; import Dismissible from '~/components/core/Dismissible';
const STYLES_AVATAR = css` const STYLES_AVATAR = css`
display: inline-flex; display: inline-flex;
@ -56,9 +56,8 @@ export default class AvatarEntity extends React.Component {
height: `${this.props.size}px`, height: `${this.props.size}px`,
borderRadius: `${this.props.size}px`, borderRadius: `${this.props.size}px`,
backgroundImage: `url('${this.props.url}')`, backgroundImage: `url('${this.props.url}')`,
cursor: this.props.onClick ? "pointer" : null, cursor: this.props.onClick ? 'pointer' : this.props.style,
}} }}>
>
{this.state.visible ? this.props.popover : null} {this.state.visible ? this.props.popover : null}
{this.props.online ? <span css={STYLES_AVATAR_ONLINE} /> : null} {this.props.online ? <span css={STYLES_AVATAR_ONLINE} /> : null}
</Dismissible> </Dismissible>

View File

@ -9,23 +9,36 @@ import { css } from '@emotion/react';
const SELECT_MENU_OPTIONS = [ const SELECT_MENU_OPTIONS = [
{ value: '1', name: 'BLS' }, { value: '1', name: 'BLS' },
{ value: '2', name: 'SECP256K1' }, { value: '2', name: 'SECP256K1' },
{ value: '3', name: 'MULTISIG' }, // { value: '3', name: 'MULTISIG' },
]; ];
const SELECT_MENU_MAP = { const SELECT_MENU_MAP = {
'1': 'BLS', '1': 'BLS',
'2': 'SECP256K1', '2': 'SECP256K1',
'3': 'MULTISIG', // '3': 'MULTISIG',
};
const SELECT_MENU_SAVE_STRINGS = {
'1': 'bls',
'2': 'secp256k1',
}; };
export default class SidebarCreateWalletAddress extends React.Component { export default class SidebarCreateWalletAddress extends React.Component {
state = { state = {
name: '', name: '',
type: '1', type: '1',
default: false,
}; };
_handleSubmit = () => { _handleSubmit = () => {
this.props.onSubmit({ name: this.state.name, type: 'CREATE_WALLET_ADDRESS' }); const data = {
name: this.state.name,
wallet_type: SELECT_MENU_SAVE_STRINGS[this.state.type],
makeDefault: this.state.default,
type: 'CREATE_WALLET_ADDRESS',
};
this.props.onSubmit(data);
}; };
_handleCancel = () => { _handleCancel = () => {
@ -60,6 +73,14 @@ export default class SidebarCreateWalletAddress extends React.Component {
{SELECT_MENU_MAP[this.state.type]} {SELECT_MENU_MAP[this.state.type]}
</System.SelectMenuFull> </System.SelectMenuFull>
<System.CheckBox
style={{ marginTop: 24 }}
name="default"
value={this.state.default}
onChange={this._handleChange}>
Make this wallet the default
</System.CheckBox>
<System.ButtonPrimaryFull style={{ marginTop: 48 }} onClick={this._handleSubmit}> <System.ButtonPrimaryFull style={{ marginTop: 48 }} onClick={this._handleSubmit}>
Create {this.state.name} Create {this.state.name}
</System.ButtonPrimaryFull> </System.ButtonPrimaryFull>

View File

@ -1,19 +1,19 @@
import * as React from "react"; import * as React from 'react';
import * as Strings from "~/common/strings"; import * as Strings from '~/common/strings';
import * as Constants from "~/common/constants"; import * as Constants from '~/common/constants';
import * as SVG from "~/components/system/svg"; import * as SVG from '~/components/system/svg';
import * as System from "~/components/system"; import * as System from '~/components/system';
import { css } from "@emotion/react"; import { css } from '@emotion/react';
const STYLES_FOCUS = css` const STYLES_FOCUS = css`
font-size: ${Constants.typescale.lvl1}; font-size: ${Constants.typescale.lvl1};
font-family: "inter-medium"; font-family: 'inter-medium';
overflow-wrap: break-word; overflow-wrap: break-word;
width: 100%; width: 100%;
strong { strong {
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
font-weight: 400; font-weight: 400;
} }
`; `;
@ -34,30 +34,27 @@ const STYLES_IMAGE_PREVIEW = css`
`; `;
const SELECT_MENU_OPTIONS = [ const SELECT_MENU_OPTIONS = [
{ value: "1", name: "Anywhere" }, { value: '1', name: 'Anywhere' },
{ value: "2", name: "China" }, { value: '2', name: 'China' },
{ value: "3", name: "Russia" }, { value: '3', name: 'Russia' },
{ value: "4", name: "USA" }, { value: '4', name: 'USA' },
]; ];
const SELECT_MENU_MAP = { const SELECT_MENU_MAP = {
"1": "Anywhere", '1': 'Anywhere',
"2": "China", '2': 'China',
"3": "Russia", '3': 'Russia',
"4": "USA", '4': 'USA',
}; };
export default class SidebarFileStorageDeal extends React.Component { export default class SidebarFileStorageDeal extends React.Component {
state = { state = {
settings_deal_duration: 1, settings_deal_duration: this.props.viewer.settings_cold_default_duration,
settings_replication_factor: 1, settings_replication_factor: this.props.viewer.settings_cold_default_replication_factor,
settings_country: "1",
settings_miners: "t111, t112, t113",
settings_confirmation: false,
}; };
_handleSubmit = () => { _handleSubmit = () => {
alert("TODO: Make a storage deal"); alert('TODO: Make a storage deal');
this.props.onSubmit({}); this.props.onSubmit({});
}; };
@ -71,18 +68,20 @@ export default class SidebarFileStorageDeal extends React.Component {
render() { render() {
let addresses = {}; let addresses = {};
let lastAddress;
this.props.viewer.addresses.forEach((a) => { this.props.viewer.addresses.forEach((a) => {
addresses[a.value] = a; addresses[a.address] = a;
lastAddress = a.address;
}); });
const currentAddress = addresses[this.props.selected.address]; const currentAddress = this.props.selected.address
? addresses[this.props.selected.address]
: addresses[lastAddress];
return ( return (
<React.Fragment> <React.Fragment>
<System.P style={{ fontFamily: "inter-semi-bold" }}> <System.P style={{ fontFamily: 'inter-semi-bold' }}>Upload a file to the network</System.P>
Upload a file to the network
</System.P>
<img src="/static/test-image-upload.jpg" css={STYLES_IMAGE_PREVIEW} /> <img src="/static/test-image-upload.jpg" css={STYLES_IMAGE_PREVIEW} />
@ -96,9 +95,7 @@ export default class SidebarFileStorageDeal extends React.Component {
<div css={STYLES_SUBTEXT}>File size</div> <div css={STYLES_SUBTEXT}>File size</div>
</div> </div>
<System.ButtonSecondaryFull style={{ marginTop: 24 }}> <System.ButtonSecondaryFull style={{ marginTop: 24 }}>Change file</System.ButtonSecondaryFull>
Change file
</System.ButtonSecondaryFull>
<System.Input <System.Input
containerStyle={{ marginTop: 48 }} containerStyle={{ marginTop: 48 }}
@ -123,49 +120,16 @@ export default class SidebarFileStorageDeal extends React.Component {
value={this.props.selected.address} value={this.props.selected.address}
category="address" category="address"
onChange={this.props.onSelectedChange} onChange={this.props.onSelectedChange}
options={this.props.viewer.addresses} options={this.props.viewer.addresses}>
>
{currentAddress.name} {currentAddress.name}
</System.SelectMenuFull> </System.SelectMenuFull>
<System.SelectMenuFull
containerStyle={{ marginTop: 24 }}
name="settings_country"
label="Country"
value={this.props.settings_country}
category="miner location"
onChange={this._handleChange}
options={SELECT_MENU_OPTIONS}
>
{SELECT_MENU_MAP[this.state.settings_country]}
</System.SelectMenuFull>
<System.Input
containerStyle={{ marginTop: 24 }}
label="Trusted miners"
name="settings_miners"
value={this.state.settings_miners}
onChange={this._handleChange}
/>
<System.CheckBox
style={{ marginTop: 24 }}
name="settings_confirmation"
value={this.state.settings_confirmation}
onChange={this._handleChange}
>
Please do not show this confirmation again.
</System.CheckBox>
<div css={STYLES_ITEM}> <div css={STYLES_ITEM}>
<div css={STYLES_FOCUS}>2 FIL</div> <div css={STYLES_FOCUS}>2 FIL</div>
<div css={STYLES_SUBTEXT}>Last order price</div> <div css={STYLES_SUBTEXT}>Last order price</div>
</div> </div>
<System.ButtonPrimaryFull <System.ButtonPrimaryFull style={{ marginTop: 48 }} onClick={this._handleSubmit}>
style={{ marginTop: 48 }}
onClick={this._handleSubmit}
>
Make storage deal Make storage deal
</System.ButtonPrimaryFull> </System.ButtonPrimaryFull>
</React.Fragment> </React.Fragment>

View File

@ -1,7 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import * as Strings from '~/common/strings'; import * as Strings from '~/common/strings';
import * as Constants from '~/common/constants'; import * as Constants from '~/common/constants';
import * as SVG from '~/components/system/svg';
import * as System from '~/components/system'; import * as System from '~/components/system';
import { css } from '@emotion/react'; import { css } from '@emotion/react';
@ -104,12 +103,12 @@ export default class SidebarWalletSendFunds extends React.Component {
/> />
<div css={STYLES_ITEM}> <div css={STYLES_ITEM}>
<div css={STYLES_FOCUS}>2 FIL</div> <div css={STYLES_FOCUS}>0 FIL</div>
<div css={STYLES_SUBTEXT}>Transaction Fee</div> <div css={STYLES_SUBTEXT}>Transaction Fee</div>
</div> </div>
<div css={STYLES_ITEM}> <div css={STYLES_ITEM}>
<div css={STYLES_FOCUS}>2</div> <div css={STYLES_FOCUS}>{Strings.formatNumber(this.state.amount)}</div>
<div css={STYLES_SUBTEXT}>Total Filecoin</div> <div css={STYLES_SUBTEXT}>Total Filecoin</div>
</div> </div>

View File

@ -1,11 +1,11 @@
import * as React from "react"; import * as React from 'react';
import * as Constants from "~/common/constants"; import * as Constants from '~/common/constants';
import * as SVG from "~/components/system/svg"; import * as SVG from '~/components/system/svg';
import * as OldSVG from "~/common/svg"; import * as OldSVG from '~/common/svg';
import * as Strings from "~/common/strings"; import * as Strings from '~/common/strings';
import * as SubSystem from "~/components/system/sub-system"; import * as SubSystem from '~/components/system/sub-system';
import { css } from "@emotion/react"; import { css } from '@emotion/react';
// //
// //
@ -30,7 +30,7 @@ import { css } from "@emotion/react";
// UTILITY COMPONENTS // UTILITY COMPONENTS
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
import TextareaAutoSize from "~/vendor/react-textarea-autosize"; import TextareaAutoSize from '~/vendor/react-textarea-autosize';
// //
// //
@ -55,9 +55,9 @@ import TextareaAutoSize from "~/vendor/react-textarea-autosize";
// VENDOR // VENDOR
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
import { Tooltip } from "react-tippy"; import { Tooltip } from 'react-tippy';
import { LineChart } from "~/vendor/react-chartkick"; import { LineChart } from '~/vendor/react-chartkick';
import "chart.js"; import 'chart.js';
// //
// //
@ -83,7 +83,7 @@ import "chart.js";
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
const STYLES_CODE_TEXTAREA = css` const STYLES_CODE_TEXTAREA = css`
font-family: "mono"; font-family: 'mono';
display: block; display: block;
max-width: 480px; max-width: 480px;
border-radius: 4px; border-radius: 4px;
@ -160,9 +160,7 @@ export class TooltipAnchor extends React.Component {
{this.props.children ? ( {this.props.children ? (
this.props.children this.props.children
) : ( ) : (
<SVG.Information <SVG.Information height={this.props.height ? this.props.height : '24px'} />
height={this.props.height ? this.props.height : "24px"}
/>
)} )}
</span> </span>
</Tooltip> </Tooltip>
@ -198,8 +196,7 @@ const STYLES_POPOVER = css`
width: 288px; width: 288px;
border-radius: 4px; border-radius: 4px;
background-color: ${Constants.system.white}; background-color: ${Constants.system.white};
box-shadow: inset 0 0 0 1px ${Constants.system.border}, box-shadow: inset 0 0 0 1px ${Constants.system.border}, 0 1px 4px rgba(0, 0, 0, 0.07);
0 1px 4px rgba(0, 0, 0, 0.07);
`; `;
const STYLES_POPOVER_ITEM = css` const STYLES_POPOVER_ITEM = css`
@ -223,7 +220,7 @@ const STYLES_POPOVER_ITEM = css`
export class PopoverNavigation extends React.Component { export class PopoverNavigation extends React.Component {
static defaultProps = { static defaultProps = {
onNavigateTo: () => { onNavigateTo: () => {
console.error("requires onNavigateTo"); console.error('requires onNavigateTo');
}, },
}; };
@ -232,11 +229,7 @@ export class PopoverNavigation extends React.Component {
<div css={STYLES_POPOVER} style={this.props.style}> <div css={STYLES_POPOVER} style={this.props.style}>
{this.props.navigation.map((each) => { {this.props.navigation.map((each) => {
return ( return (
<div <div key={each.value} css={STYLES_POPOVER_ITEM} onClick={() => this.props.onNavigateTo({ id: each.value })}>
key={each.value}
css={STYLES_POPOVER_ITEM}
onClick={() => this.props.onNavigateTo({ id: each.value })}
>
{each.text} {each.text}
</div> </div>
); );
@ -298,7 +291,7 @@ const STYLES_STAT_CARD_VALUE_GROUP = css`
`; `;
const STYLES_STAT_CARD_VALUE = css` const STYLES_STAT_CARD_VALUE = css`
font-family: "inter-medium"; font-family: 'inter-medium';
font-size: ${Constants.typescale.lvl3}; font-size: ${Constants.typescale.lvl3};
color: ${Constants.system.brand}; color: ${Constants.system.brand};
display: block; display: block;
@ -306,7 +299,7 @@ const STYLES_STAT_CARD_VALUE = css`
const STYLES_STAT_CARD_DENOMINATION = css` const STYLES_STAT_CARD_DENOMINATION = css`
display: block; display: block;
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
font-size: 10px; font-size: 10px;
letter-spacing: 0.1px; letter-spacing: 0.1px;
margin: 4px 0 16px 0; margin: 4px 0 16px 0;
@ -319,14 +312,12 @@ export const StatCard = (props) => {
<div css={STYLES_STAT_CARD}> <div css={STYLES_STAT_CARD}>
<div css={STYLES_STAT_CARD_TOP}> <div css={STYLES_STAT_CARD_TOP}>
<div css={STYLES_STAT_CARD_VALUE_GROUP}> <div css={STYLES_STAT_CARD_VALUE_GROUP}>
<span css={STYLES_STAT_CARD_VALUE}> <span css={STYLES_STAT_CARD_VALUE}>{Strings.formatNumber(props.value)}</span>{' '}
{Strings.formatNumber(props.value)}
</span>{" "}
<span css={STYLES_STAT_CARD_DENOMINATION}>{props.denomination}</span> <span css={STYLES_STAT_CARD_DENOMINATION}>{props.denomination}</span>
<LineChart <LineChart
data={props.data} data={props.data}
library={{ library={{
backgroundColor: "transparent", backgroundColor: 'transparent',
scales: { scales: {
yAxes: [ yAxes: [
{ {
@ -372,14 +363,14 @@ export const StatCard = (props) => {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
const TABLE_COLUMN_WIDTH_DEFAULTS = { const TABLE_COLUMN_WIDTH_DEFAULTS = {
1: "100%", 1: '100%',
2: "50%", 2: '50%',
3: "33.333%", 3: '33.333%',
4: "25%", 4: '25%',
5: "20%", 5: '20%',
6: "16.666%", 6: '16.666%',
7: "14.28%", 7: '14.28%',
8: "12.5%", 8: '12.5%',
}; };
const STYLES_TABLE_PLACEHOLDER = css` const STYLES_TABLE_PLACEHOLDER = css`
@ -426,7 +417,7 @@ const STYLES_TABLE_SELECTED_ROW = css`
`; `;
const STYLES_TABLE_TOP_ROW = css` const STYLES_TABLE_TOP_ROW = css`
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
width: 100%; width: 100%;
padding: 0 8px 0 8px; padding: 0 8px 0 8px;
border-bottom: 1px solid ${Constants.system.gray}; border-bottom: 1px solid ${Constants.system.gray};
@ -436,8 +427,8 @@ const STYLES_TABLE_TOP_ROW = css`
export class Table extends React.Component { export class Table extends React.Component {
static defaultProps = { static defaultProps = {
onNavigateTo: () => console.log("No navigation function set"), onNavigateTo: () => console.log('No navigation function set'),
onAction: () => console.log("No action function set"), onAction: () => console.log('No action function set'),
}; };
// NOTE(jim): Local state for local filtering. // NOTE(jim): Local state for local filtering.
@ -467,7 +458,7 @@ export class Table extends React.Component {
ac[data.columns[x].key] = { ac[data.columns[x].key] = {
...data.columns[x], ...data.columns[x],
index: x, index: x,
color: x % 2 !== 0 ? "rgba(0, 0, 0, 0.01)" : null, color: x % 2 !== 0 ? 'rgba(0, 0, 0, 0.01)' : null,
}; };
} }
@ -477,15 +468,11 @@ export class Table extends React.Component {
<React.Fragment> <React.Fragment>
<div css={STYLES_TABLE_TOP_ROW}> <div css={STYLES_TABLE_TOP_ROW}>
{data.columns.map((c, cIndex) => { {data.columns.map((c, cIndex) => {
const text = c.hideLabel const text = c.hideLabel ? '' : Strings.isEmpty(c.name) ? c.key : c.name;
? ""
: Strings.isEmpty(c.name)
? c.key
: c.name;
let localWidth = c.width ? c.width : width; let localWidth = c.width ? c.width : width;
let flexShrink = c.width && c.width !== "100%" ? "0" : null; let flexShrink = c.width && c.width !== '100%' ? '0' : null;
if (cIndex === 0 && !c.width) { if (cIndex === 0 && !c.width) {
localWidth = "100%"; localWidth = '100%';
} }
return ( return (
@ -497,13 +484,11 @@ export class Table extends React.Component {
backgroundColor: ac[c.key].color, backgroundColor: ac[c.key].color,
flexShrink, flexShrink,
}} }}
tooltip={c.tooltip} tooltip={c.tooltip}>
>
{text} {text}
</SubSystem.TableColumn> </SubSystem.TableColumn>
); );
})} })}
<div css={STYLES_TABLE_EXPAND_SECTION} />
</div> </div>
{data.rows.map((r, i) => { {data.rows.map((r, i) => {
@ -517,10 +502,9 @@ export class Table extends React.Component {
const text = r[each]; const text = r[each];
let localWidth = field.width ? field.width : width; let localWidth = field.width ? field.width : width;
let flexShrink = let flexShrink = field.width && field.width !== '100%' ? '0' : null;
field.width && field.width !== "100%" ? "0" : null;
if (cIndex === 0 && !field.width) { if (cIndex === 0 && !field.width) {
localWidth = "100%"; localWidth = '100%';
} }
return ( return (
@ -531,8 +515,7 @@ export class Table extends React.Component {
backgroundColor: field.color, backgroundColor: field.color,
flexShrink, flexShrink,
}} }}
copyable={field.copyable} copyable={field.copyable}>
>
<SubSystem.TableContent <SubSystem.TableContent
data={r} data={r}
text={text} text={text}
@ -544,22 +527,10 @@ export class Table extends React.Component {
</SubSystem.TableColumn> </SubSystem.TableColumn>
); );
})} })}
<div
css={STYLES_TABLE_EXPAND_SECTION}
onClick={() => this._handleChange(r.id)}
style={{ cursor: "pointer" }}
>
<SVG.Plus
height="16px"
style={{
transform: selected ? `rotate(45deg)` : null,
}}
/>
</div>
</div> </div>
{selected ? ( {selected ? (
<div css={STYLES_TABLE_SELECTED_ROW}> <div css={STYLES_TABLE_SELECTED_ROW}>
<span css={STYLES_TABLE_PLACEHOLDER}>Placeholder</span> <span css={STYLES_TABLE_PLACEHOLDER}>{r.children}</span>
</div> </div>
) : null} ) : null}
</React.Fragment> </React.Fragment>
@ -644,6 +615,10 @@ const STYLES_BUTTON_PRIMARY = css`
`; `;
export const ButtonPrimary = (props) => { export const ButtonPrimary = (props) => {
if (props.type === 'label') {
return <label css={STYLES_BUTTON_PRIMARY} {...props} />;
}
return <button css={STYLES_BUTTON_PRIMARY} {...props} />; return <button css={STYLES_BUTTON_PRIMARY} {...props} />;
}; };
@ -808,12 +783,8 @@ export class Toggle extends React.Component {
onClick={this._handleChange} onClick={this._handleChange}
style={{ style={{
backgroundColor: this.props.active ? Constants.system.brand : null, backgroundColor: this.props.active ? Constants.system.brand : null,
}} }}>
> <figure css={STYLES_DIAL} style={{ transform: this.props.active ? `translateX(40px)` : null }} />
<figure
css={STYLES_DIAL}
style={{ transform: this.props.active ? `translateX(40px)` : null }}
/>
</div> </div>
); );
} }
@ -903,7 +874,7 @@ const STYLES_RADIO_LABEL = css`
overflow-wrap: break-word; overflow-wrap: break-word;
strong { strong {
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
font-weight: 400; font-weight: 400;
} }
`; `;
@ -924,10 +895,7 @@ export class RadioGroup extends React.Component {
return ( return (
<label css={STYLES_RADIO} key={`radio-${radio.value}`}> <label css={STYLES_RADIO} key={`radio-${radio.value}`}>
<span css={STYLES_RADIO_CUSTOM}> <span css={STYLES_RADIO_CUSTOM}>
<span <span css={STYLES_RADIO_CUSTOM_SELECTED} style={{ opacity: checked ? 1 : 0 }} />
css={STYLES_RADIO_CUSTOM_SELECTED}
style={{ opacity: checked ? 1 : 0 }}
/>
</span> </span>
<input <input
css={STYLES_RADIO_INPUT} css={STYLES_RADIO_INPUT}
@ -935,7 +903,7 @@ export class RadioGroup extends React.Component {
value={radio.value} value={radio.value}
checked={checked} checked={checked}
onChange={() => this._handleChange(radio.value)} onChange={() => this._handleChange(radio.value)}
/>{" "} />{' '}
<span css={STYLES_RADIO_LABEL}>{radio.label}</span> <span css={STYLES_RADIO_LABEL}>{radio.label}</span>
</label> </label>
); );
@ -1008,7 +976,7 @@ const STYLES_CHECKBOX_LABEL = css`
overflow-wrap: break-word; overflow-wrap: break-word;
strong { strong {
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
font-weight: 400; font-weight: 400;
} }
`; `;
@ -1023,9 +991,7 @@ export class CheckBox extends React.Component {
render() { render() {
return ( return (
<label css={STYLES_CHECKBOX} style={this.props.style}> <label css={STYLES_CHECKBOX} style={this.props.style}>
<figure css={STYLES_CHECKBOX_FIGURE}> <figure css={STYLES_CHECKBOX_FIGURE}>{this.props.value ? <SVG.CheckBox height="20px" /> : null}</figure>
{this.props.value ? <SVG.CheckBox height="20px" /> : null}
</figure>
<input <input
css={STYLES_CHECKBOX_INPUT} css={STYLES_CHECKBOX_INPUT}
name={this.props.name} name={this.props.name}
@ -1066,15 +1032,14 @@ const STYLES_CARD_TAB_GROUP = css`
width: 100%; width: 100%;
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
box-shadow: 0 -1px 0 0 ${Constants.system.border}, box-shadow: 0 -1px 0 0 ${Constants.system.border}, 0 1px 0 0 ${Constants.system.border};
0 1px 0 0 ${Constants.system.border};
`; `;
const STYLES_CARD_TAB_GROUP_TAB = css` const STYLES_CARD_TAB_GROUP_TAB = css`
background: #fdfdfd; background: #fdfdfd;
color: rgba(0, 0, 0, 0.4); color: rgba(0, 0, 0, 0.4);
font-size: 14px; font-size: 14px;
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
height: 48px; height: 48px;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@ -1112,14 +1077,11 @@ export class CardTabGroup extends React.Component {
style={{ style={{
color: selected ? Constants.system.brand : null, color: selected ? Constants.system.brand : null,
backgroundColor: selected ? Constants.system.white : null, backgroundColor: selected ? Constants.system.white : null,
borderBottom: selected borderBottom: selected ? `2px solid ${Constants.system.brand}` : null,
? `2px solid ${Constants.system.brand}`
: null,
width: TAB_GROUP_SIZE_MAP[this.props.options.length], width: TAB_GROUP_SIZE_MAP[this.props.options.length],
cursor: !selected ? "pointer" : null, cursor: !selected ? 'pointer' : null,
}} }}
onClick={() => this._handleChange(tab.value)} onClick={() => this._handleChange(tab.value)}>
>
{tab.label} {tab.label}
</div> </div>
); );
@ -1167,7 +1129,7 @@ const STYLES_TAB_GROUP_TAB = css`
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 14px; font-size: 14px;
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
transition: 200ms ease all; transition: 200ms ease all;
user-select: none; user-select: none;
@ -1177,10 +1139,10 @@ const STYLES_TAB_GROUP_TAB = css`
`; `;
const TAB_GROUP_SIZE_MAP = { const TAB_GROUP_SIZE_MAP = {
1: "100%", 1: '100%',
2: "50%", 2: '50%',
3: "33.33%", 3: '33.33%',
4: "25%", 4: '25%',
}; };
export class TabGroup extends React.Component { export class TabGroup extends React.Component {
@ -1203,11 +1165,10 @@ export class TabGroup extends React.Component {
style={{ style={{
backgroundColor: selected ? Constants.system.white : null, backgroundColor: selected ? Constants.system.white : null,
width: TAB_GROUP_SIZE_MAP[this.props.options.length], width: TAB_GROUP_SIZE_MAP[this.props.options.length],
cursor: !selected ? "pointer" : null, cursor: !selected ? 'pointer' : null,
borderBottom: !selected ? `1px solid #D7D7D7` : null, borderBottom: !selected ? `1px solid #D7D7D7` : null,
}} }}
onClick={() => this._handleChange(tab.value)} onClick={() => this._handleChange(tab.value)}>
>
{tab.label} {tab.label}
</div> </div>
); );
@ -1241,7 +1202,7 @@ export class TabGroup extends React.Component {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
const STYLES_DESCRIPTION_GROUP_LABEL = css` const STYLES_DESCRIPTION_GROUP_LABEL = css`
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
font-size: 14px; font-size: 14px;
padding: 0 0 0 0; padding: 0 0 0 0;
margin-bottom: 8px; margin-bottom: 8px;
@ -1258,20 +1219,12 @@ export const DescriptionGroup = (props) => {
<div style={props.style}> <div style={props.style}>
{!Strings.isEmpty(props.label) ? ( {!Strings.isEmpty(props.label) ? (
<div css={STYLES_DESCRIPTION_GROUP_LABEL}> <div css={STYLES_DESCRIPTION_GROUP_LABEL}>
{props.label}{" "} {props.label}{' '}
{props.tooltip ? ( {props.tooltip ? <TooltipAnchor tooltip={props.tooltip} height="14px" style={{ paddingTop: 16 }} /> : null}
<TooltipAnchor
tooltip={props.tooltip}
height="14px"
style={{ paddingTop: 16 }}
/>
) : null}
</div> </div>
) : null} ) : null}
{!Strings.isEmpty(props.description) ? ( {!Strings.isEmpty(props.description) ? (
<div css={STYLES_DESCRIPTION_GROUP_DESCRIPTION}> <div css={STYLES_DESCRIPTION_GROUP_DESCRIPTION}>{props.description}</div>
{props.description}
</div>
) : null} ) : null}
</div> </div>
); );
@ -1326,14 +1279,12 @@ const INPUT_STYLES = css`
const STYLES_INPUT = css` const STYLES_INPUT = css`
${INPUT_STYLES} ${INPUT_STYLES}
padding: 0 24px 0 24px; padding: 0 24px 0 24px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15), box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15), inset 0 0 0 1px ${Constants.system.darkGray};
inset 0 0 0 1px ${Constants.system.darkGray};
:focus { :focus {
outline: 0; outline: 0;
border: 0; border: 0;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.07), box-shadow: 0 1px 4px rgba(0, 0, 0, 0.07), inset 0 0 0 2px ${Constants.system.brand};
inset 0 0 0 2px ${Constants.system.brand};
} }
::placeholder { ::placeholder {
@ -1377,7 +1328,7 @@ export class Input extends React.Component {
_handleCopy = (e) => { _handleCopy = (e) => {
this._input.select(); this._input.select();
document.execCommand("copy"); document.execCommand('copy');
}; };
_handleKeyUp = (e) => { _handleKeyUp = (e) => {
@ -1390,10 +1341,7 @@ export class Input extends React.Component {
}; };
_handleChange = (e) => { _handleChange = (e) => {
if ( if (!Strings.isEmpty(this.props.pattern) && !Strings.isEmpty(e.target.value)) {
!Strings.isEmpty(this.props.pattern) &&
!Strings.isEmpty(e.target.value)
) {
const TestRegex = new RegExp(this.props.pattern); const TestRegex = new RegExp(this.props.pattern);
if (!TestRegex.test(e.target.value)) { if (!TestRegex.test(e.target.value)) {
e.preventDefault(); e.preventDefault();
@ -1414,11 +1362,7 @@ export class Input extends React.Component {
render() { render() {
return ( return (
<div css={STYLES_INPUT_CONTAINER} style={this.props.containerStyle}> <div css={STYLES_INPUT_CONTAINER} style={this.props.containerStyle}>
<DescriptionGroup <DescriptionGroup tooltip={this.props.tooltip} label={this.props.label} description={this.props.description} />
tooltip={this.props.tooltip}
label={this.props.label}
description={this.props.description}
/>
<input <input
ref={(c) => { ref={(c) => {
this._input = c; this._input = c;
@ -1435,18 +1379,12 @@ export class Input extends React.Component {
style={{ style={{
...this.props.style, ...this.props.style,
boxShadow: this.props.validation boxShadow: this.props.validation
? `0 1px 4px rgba(0, 0, 0, 0.07), inset 0 0 0 2px ${ ? `0 1px 4px rgba(0, 0, 0, 0.07), inset 0 0 0 2px ${INPUT_COLOR_MAP[this.props.validation]}`
INPUT_COLOR_MAP[this.props.validation]
}`
: null, : null,
}} }}
/> />
{this.props.copyable ? ( {this.props.copyable ? (
<SVG.CopyAndPaste <SVG.CopyAndPaste height="16px" css={STYLES_COPY_AND_PASTE} onClick={this._handleCopy} />
height="16px"
css={STYLES_COPY_AND_PASTE}
onClick={this._handleCopy}
/>
) : null} ) : null}
</div> </div>
); );
@ -1494,8 +1432,7 @@ const STYLES_TEXTAREA = css`
box-sizing: border-box; box-sizing: border-box;
transition: 200ms ease all; transition: 200ms ease all;
padding: 16px 24px 16px 24px; padding: 16px 24px 16px 24px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15), box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15), inset 0 0 0 1px ${Constants.system.darkGray};
inset 0 0 0 1px ${Constants.system.darkGray};
`; `;
export class Textarea extends React.Component { export class Textarea extends React.Component {
@ -1591,10 +1528,7 @@ export const SelectMenu = (props) => {
<div css={props.className ? props.className : STYLES_SELECT_MENU}> <div css={props.className ? props.className : STYLES_SELECT_MENU}>
<label css={STYLES_SELECT_MENU_LABEL} htmlFor={`id-${props.name}`}> <label css={STYLES_SELECT_MENU_LABEL} htmlFor={`id-${props.name}`}>
{props.children}{" "} {props.children} {props.category ? <span css={STYLES_SELECT_MENU_CATEGORY}>{props.category}</span> : null}
{props.category ? (
<span css={STYLES_SELECT_MENU_CATEGORY}>{props.category}</span>
) : null}
<SVG.ChevronDown height="16px" css={STYLES_SELECT_MENU_CHEVRON} /> <SVG.ChevronDown height="16px" css={STYLES_SELECT_MENU_CHEVRON} />
</label> </label>
<select <select
@ -1602,8 +1536,7 @@ export const SelectMenu = (props) => {
value={props.value} value={props.value}
onChange={props.onChange} onChange={props.onChange}
name={props.name} name={props.name}
id={`id-${props.name}`} id={`id-${props.name}`}>
>
{props.options.map((each) => { {props.options.map((each) => {
return ( return (
<option value={each.value} key={each.value}> <option value={each.value} key={each.value}>
@ -1617,9 +1550,7 @@ export const SelectMenu = (props) => {
); );
}; };
export const SelectMenuFull = (props) => ( export const SelectMenuFull = (props) => <SelectMenu {...props} css={STYLES_SELECT_MENU_FULL} />;
<SelectMenu {...props} css={STYLES_SELECT_MENU_FULL} />
);
// //
// //
@ -1647,11 +1578,11 @@ export const SelectMenuFull = (props) => (
const STYLES_H1 = css` const STYLES_H1 = css`
font-size: ${Constants.typescale.lvl4}; font-size: ${Constants.typescale.lvl4};
line-height: 1.1; line-height: 1.1;
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
font-weight: 400; font-weight: 400;
strong { strong {
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
font-weight: 400; font-weight: 400;
} }
`; `;
@ -1663,11 +1594,11 @@ export const H1 = (props) => {
const STYLES_H2 = css` const STYLES_H2 = css`
font-size: ${Constants.typescale.lvl3}; font-size: ${Constants.typescale.lvl3};
line-height: 1.1; line-height: 1.1;
font-family: "inter-medium"; font-family: 'inter-medium';
font-weight: 400; font-weight: 400;
strong { strong {
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
font-weight: 400; font-weight: 400;
} }
`; `;
@ -1681,7 +1612,7 @@ const STYLES_P = css`
line-height: 1.5; line-height: 1.5;
strong { strong {
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
font-weight: 400; font-weight: 400;
} }
`; `;
@ -1716,7 +1647,7 @@ export const P = (props) => {
const STYLES_BANDWIDTH = css` const STYLES_BANDWIDTH = css`
padding: 8px 8px 8px 8px; padding: 8px 8px 8px 8px;
display: inline-flex; display: inline-flex;
font-family: "mono"; font-family: 'mono';
font-size: 12px; font-size: 12px;
letter-spacing: 0.2px; letter-spacing: 0.2px;
align-items: center; align-items: center;
@ -1726,8 +1657,7 @@ const STYLES_BANDWIDTH = css`
export const StatUpload = (props) => { export const StatUpload = (props) => {
return ( return (
<div css={STYLES_BANDWIDTH} style={props.style}> <div css={STYLES_BANDWIDTH} style={props.style}>
<SVG.BandwidthUp height="16px" style={{ marginRight: 8 }} />{" "} <SVG.BandwidthUp height="16px" style={{ marginRight: 8 }} /> {props.children}
{props.children}
</div> </div>
); );
}; };
@ -1735,8 +1665,7 @@ export const StatUpload = (props) => {
export const StatDownload = (props) => { export const StatDownload = (props) => {
return ( return (
<div css={STYLES_BANDWIDTH} style={props.style}> <div css={STYLES_BANDWIDTH} style={props.style}>
<SVG.BandwidthDown height="16px" style={{ marginRight: 8 }} />{" "} <SVG.BandwidthDown height="16px" style={{ marginRight: 8 }} /> {props.children}
{props.children}
</div> </div>
); );
}; };

View File

@ -15,7 +15,7 @@
"@emotion/css": "11.0.0-next.12", "@emotion/css": "11.0.0-next.12",
"@emotion/react": "11.0.0-next.12", "@emotion/react": "11.0.0-next.12",
"@emotion/server": "11.0.0-next.12", "@emotion/server": "11.0.0-next.12",
"@textile/powergate-client": "0.1.0-beta.6", "@textile/powergate-client": "0.1.0-beta.7",
"babel-plugin-module-resolver": "^4.0.0", "babel-plugin-module-resolver": "^4.0.0",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"chart.js": "^2.9.3", "chart.js": "^2.9.3",
@ -23,6 +23,7 @@
"compression": "^1.7.4", "compression": "^1.7.4",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.17.1", "express": "^4.17.1",
"formidable": "^1.2.2",
"isomorphic-fetch": "^2.2.1", "isomorphic-fetch": "^2.2.1",
"next": "^9.4.0", "next": "^9.4.0",
"react": "^16.12.0", "react": "^16.12.0",

View File

@ -62,9 +62,15 @@ const getCurrentNavigationStateById = (navigation, targetId) => {
return { target, activeIds }; return { target, activeIds };
}; };
export const getServerSideProps = (context) => { export const getServerSideProps = async (context) => {
if (context.query && context.query.production) {
return { production: true };
}
const data = await Actions.rehydrateViewer();
return { return {
props: { ...context.query }, props: { ...data.data },
}; };
}; };
@ -74,7 +80,7 @@ export default class IndexPage extends React.Component {
currentIndex: 0, currentIndex: 0,
data: null, data: null,
selected: { selected: {
address: '1', address: null,
}, },
viewer: this.props.production ? Fixtures.getInitialState() : State.getInitialState(this.props), viewer: this.props.production ? Fixtures.getInitialState() : State.getInitialState(this.props),
sidebar: null, sidebar: null,
@ -85,6 +91,11 @@ export default class IndexPage extends React.Component {
console.log(this.props); console.log(this.props);
} }
rehydrate = async () => {
const viewer = await Actions.rehydrateViewer();
this.setState({ viewer: { ...State.getInitialState(viewer.data) } });
};
_handleSubmit = async (data) => { _handleSubmit = async (data) => {
if (this.props.production) { if (this.props.production) {
alert('TODO'); alert('TODO');
@ -92,9 +103,13 @@ export default class IndexPage extends React.Component {
} }
if (data.type === 'CREATE_WALLET_ADDRESS') { if (data.type === 'CREATE_WALLET_ADDRESS') {
const response = await Actions.createWalletAddress({ name: data.name }); const address = await Actions.createWalletAddress({
const viewer = await Actions.rehydrateViewer(); name: data.name,
this.setState({ viewer }); type: data.wallet_type,
makeDefault: data.makeDefault,
});
await this.rehydrate();
} }
if (data.type === 'SEND_WALLET_ADDRESS_FILECOIN') { if (data.type === 'SEND_WALLET_ADDRESS_FILECOIN') {
@ -104,8 +119,7 @@ export default class IndexPage extends React.Component {
amount: data.amount, amount: data.amount,
}); });
const viewer = await Actions.rehydrateViewer(); await this.rehydrate();
this.setState({ viewer });
} }
this._handleDismissSidebar(); this._handleDismissSidebar();
@ -272,6 +286,7 @@ export default class IndexPage extends React.Component {
); );
const scene = React.cloneElement(this.scenes[current.target.decorator], { const scene = React.cloneElement(this.scenes[current.target.decorator], {
rehydrate: this.rehydrate,
viewer: this.state.viewer, viewer: this.state.viewer,
selected: this.state.selected, selected: this.state.selected,
data: current.target, data: current.target,

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

View File

@ -1,16 +1,50 @@
import * as React from "react"; import * as React from 'react';
import * as Strings from "~/common/strings"; import * as System from '~/components/system';
import * as Constants from "~/common/constants"; import * as Actions from '~/common/actions';
import * as Fixtures from "~/common/fixtures";
import * as System from "~/components/system";
import { css } from "@emotion/react"; import { css } from '@emotion/react';
import Section from "~/components/core/Section"; import ScenePage from '~/components/core/ScenePage';
import ScenePage from "~/components/core/ScenePage"; import Avatar from '~/components/core/Avatar';
import Avatar from "~/components/core/Avatar";
const STYLES_FILE_HIDDEN = css`
height: 1px;
width: 1px;
opacity: 0;
visibility: hidden;
position: fixed;
top: -1px;
left: -1px;
`;
export default class SceneEditAccount extends React.Component { export default class SceneEditAccount extends React.Component {
_handleUpload = async (e) => {
e.persist();
let file = e.target.files[0];
if (!file) {
alert('Something went wrong');
}
let data = new FormData();
data.append('image', file);
const options = {
method: 'POST',
headers: {
Accept: 'application/json',
},
body: data,
};
const response = await fetch(`/_/upload/avatar`, options);
const json = await response.json();
if (json && json.success) {
console.log('reload');
}
};
_handleChange = (e) => { _handleChange = (e) => {
this.props.onViewerChange(e); this.props.onViewerChange(e);
}; };
@ -26,17 +60,13 @@ export default class SceneEditAccount extends React.Component {
description="This image will appear in various lists." description="This image will appear in various lists."
/> />
<Avatar <Avatar style={{ marginTop: 24 }} size={256} url={this.props.viewer.photoURL} />
style={{ marginTop: 24 }}
size={256}
url={this.props.viewer.photoURL}
/>
<div style={{ marginTop: 24 }}> <div style={{ marginTop: 24 }}>
<System.ButtonPrimary style={{ margin: "0 16px 16px 0" }}> <input css={STYLES_FILE_HIDDEN} type="file" id="file" onChange={this._handleUpload} />
<System.ButtonPrimary style={{ margin: '0 16px 16px 0' }} type="label" for="file">
Upload Upload
</System.ButtonPrimary> </System.ButtonPrimary>
<System.ButtonSecondary>Delete</System.ButtonSecondary>
</div> </div>
<System.Input <System.Input
@ -48,31 +78,6 @@ export default class SceneEditAccount extends React.Component {
placeholder="Name" placeholder="Name"
onChange={this._handleChange} onChange={this._handleChange}
/> />
<System.DescriptionGroup
style={{ marginTop: 48 }}
label="Account secret configuration"
description="Manage your JSON config for your peer id, private key, and secret."
tooltip="If you make a mistake here, just click reset and you will have a new key."
/>
<System.CodeTextarea
value={this.props.viewer.config}
onChange={this._handleChange}
name="config"
/>
<div style={{ marginTop: 24 }}>
<System.ButtonSecondary style={{ margin: "0 16px 16px 0" }}>
Hide
</System.ButtonSecondary>
<System.ButtonSecondary style={{ margin: "0 16px 16px 0" }}>
Reset
</System.ButtonSecondary>
<System.ButtonSecondary style={{ margin: "0 16px 16px 0" }}>
Export
</System.ButtonSecondary>
</div>
</ScenePage> </ScenePage>
); );
} }

View File

@ -54,132 +54,6 @@ export default class SceneHome extends React.Component {
render() { render() {
return ( return (
<ScenePage> <ScenePage>
<GLRenderer width={1200} height={480} />
<div css={STYLES_ROW}>
<span css={STYLES_COLUMN}>
<System.StatCard
data={[
['2017-01-01 00:00:00 UTC', 7],
['2017-05-01 00:00:00 UTC', 14],
['2017-20-01 00:00:00 UTC', 16],
['2017-24-01 00:00:00 UTC', 2],
[new Date(), 24],
]}
value={1000}
denomination="GB">
Total data stored
</System.StatCard>
</span>
<span css={STYLES_COLUMN}>
<System.StatCard
data={[
['2017-01-01 00:00:00 UTC', 65],
['2017-05-01 00:00:00 UTC', 12],
['2017-20-01 00:00:00 UTC', 2],
['2017-24-01 00:00:00 UTC', 20],
[new Date(), 24],
]}
value={1000}
denomination="GB">
Total data retrieved
</System.StatCard>
</span>
<span css={STYLES_COLUMN}>
<System.StatCard
data={[
['2017-01-01 00:00:00 UTC', 7],
['2017-05-01 00:00:00 UTC', 12],
['2017-20-01 00:00:00 UTC', 16],
['2017-24-01 00:00:00 UTC', 33],
[new Date(), 24],
]}
value={1000}
denomination="Deals">
Total deals
</System.StatCard>
</span>
<span css={STYLES_COLUMN}>
<System.StatCard
data={[
['2017-01-01 00:00:00 UTC', 7],
['2017-05-01 00:00:00 UTC', 22],
['2017-20-01 00:00:00 UTC', 44],
['2017-24-01 00:00:00 UTC', 20],
[new Date(), 24],
]}
value={1000}
denomination="FIL">
Wallet Balance
</System.StatCard>
</span>
</div>
<div css={STYLES_ROW}>
<span css={STYLES_COLUMN}>
<System.StatCard
data={[
['2017-01-01 00:00:00 UTC', 7],
['2017-05-01 00:00:00 UTC', 12],
['2017-20-01 00:00:00 UTC', 12],
['2017-24-01 00:00:00 UTC', 20],
[new Date(), 24],
]}
value={1000}
denomination="FIL/GB/Month">
Your average storage price
</System.StatCard>
</span>
<span css={STYLES_COLUMN}>
<System.StatCard
data={[
['2017-01-01 00:00:00 UTC', 7],
['2017-05-01 00:00:00 UTC', 23],
['2017-20-01 00:00:00 UTC', 16],
['2017-24-01 00:00:00 UTC', 20],
[new Date(), 24],
]}
value={1000}
denomination="FIL/GB">
Your average retrieval price
</System.StatCard>
</span>
<span css={STYLES_COLUMN}>
<System.StatCard
data={[
['2017-01-01 00:00:00 UTC', 7],
['2017-05-01 00:00:00 UTC', 12],
['2017-20-01 00:00:00 UTC', 16],
['2017-24-01 00:00:00 UTC', 23],
[new Date(), 24],
]}
value={1000}
denomination="FIL/GB/Month">
Average storage market price
</System.StatCard>
</span>
<span css={STYLES_COLUMN}>
<System.StatCard
data={[
['2017-01-01 00:00:00 UTC', 47],
['2017-05-01 00:00:00 UTC', 42],
['2017-20-01 00:00:00 UTC', 46],
['2017-24-01 00:00:00 UTC', 40],
[new Date(), 24],
]}
value={1000}
denomination="FIL/GB">
Average market retrieval price
</System.StatCard>
</span>
</div>
<Section <Section
onAction={this.props.onAction} onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo} onNavigateTo={this.props.onNavigateTo}
@ -190,6 +64,11 @@ export default class SceneHome extends React.Component {
type: 'NAVIGATE', type: 'NAVIGATE',
value: 'folder-root', value: 'folder-root',
}, },
{
name: 'Store file on network',
type: 'SIDEBAR',
value: 'SIDEBAR_FILE_STORAGE_DEAL',
},
]}> ]}>
<System.Table <System.Table
data={{ data={{
@ -203,7 +82,7 @@ export default class SceneHome extends React.Component {
}, },
{ key: 'remaining', name: 'Remaining time', width: '180px' }, { key: 'remaining', name: 'Remaining time', width: '180px' },
], ],
rows: Data.EXAMPLE_FILES.slice(0, 4), rows: [],
}} }}
selectedRowId={this.state.data} selectedRowId={this.state.data}
onChange={this._handleChange} onChange={this._handleChange}
@ -217,18 +96,22 @@ export default class SceneHome extends React.Component {
<Section <Section
onAction={this.props.onAction} onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo} onNavigateTo={this.props.onNavigateTo}
title="Recent transactions" title="Wallet addresses"
buttons={[ buttons={[
{ {
name: 'View wallet', name: 'View all',
type: 'NAVIGATE', type: 'NAVIGATE',
value: 2, value: 2,
}, },
]}> ]}>
<System.Table <System.Table
data={{ data={{
columns: SchemaTable.Transactions, columns: [
rows: this.props.viewer.addresses[0].transactions, { key: 'address', name: 'Address' },
{ key: 'balance', name: 'Filecoin', width: '228px' },
{ key: 'type', name: 'Type' },
],
rows: this.props.viewer.addresses,
}} }}
selectedRowId={this.state.transaction} selectedRowId={this.state.transaction}
onChange={this._handleChange} onChange={this._handleChange}

View File

@ -1,26 +1,11 @@
import * as React from 'react'; import * as React from 'react';
import * as Strings from '~/common/strings'; import * as Actions from '~/common/actions';
import * as Constants from '~/common/constants';
import * as Fixtures from '~/common/fixtures';
import * as System from '~/components/system'; import * as System from '~/components/system';
import { css } from '@emotion/react'; import { css } from '@emotion/react';
import Section from '~/components/core/Section';
import ScenePage from '~/components/core/ScenePage'; import ScenePage from '~/components/core/ScenePage';
const SELECT_MENU_OPTIONS = [
{ value: '1', name: 'China' },
{ value: '2', name: 'United States' },
{ value: '3', name: 'Russia' },
];
const SELECT_MENU_MAP = {
'1': 'China',
'2': 'United States',
'3': 'Russia',
};
const STYLES_GROUP = css` const STYLES_GROUP = css`
display: flex; display: flex;
align-items: center; align-items: center;
@ -45,8 +30,47 @@ const STYLES_RIGHT = css`
`; `;
export default class SceneSettings extends React.Component { export default class SceneSettings extends React.Component {
_deferredSave = null;
_handleSave = async () => {
const response = await Actions.setDefaultConfig({
config: {
hot: {
enabled: this.props.viewer.settings_cold_enabled,
allowUnfreeze: this.props.viewer.settings_hot_allow_unfreeze,
ipfs: {
addTimeout: this.props.viewer.settings_hot_ipfs_add_timeout,
},
},
cold: {
enabled: this.props.viewer.settings_cold_enabled,
filecoin: {
addr: this.props.viewer.settings_cold_default_address,
dealDuration: this.props.viewer.settings_cold_default_duration,
repFactor: this.props.viewer.settings_cold_default_replication_factor,
excludedMinersList: this.props.viewer.settings_cold_default_excluded_miners,
trustedMinersList: this.props.viewer.settings_cold_default_trusted_miners,
maxPrice: this.props.viewer.settings_cold_default_max_price,
renew: {
enabled: this.props.viewer.settings_cold_default_auto_renew,
threshold: this.props.viewer.settings_cold_default_auto_renew_max_price,
},
},
},
},
});
await this.props.rehydrate();
};
_handleChange = (e) => { _handleChange = (e) => {
window.clearTimeout(this._deferredSave);
this._deferredSave = null;
this.props.onViewerChange(e); this.props.onViewerChange(e);
this._deferredSave = window.setTimeout(async () => {
await this._handleSave();
}, 2000);
}; };
render() { render() {
@ -110,7 +134,7 @@ export default class SceneSettings extends React.Component {
category="address" category="address"
onChange={this._handleChange} onChange={this._handleChange}
options={this.props.viewer.addresses}> options={this.props.viewer.addresses}>
{currentAddress.name} {currentAddress ? currentAddress.name : 'None'}
</System.SelectMenu> </System.SelectMenu>
<System.Input <System.Input

View File

@ -15,6 +15,16 @@ const STYLES_GROUP = css`
padding: 24px; padding: 24px;
`; `;
const STYLES_TARGET = css`
position: fixed;
top: -1;
left: -1;
height: 1px;
width: 1px;
overflow: hidden;
visibility: hidden;
`;
const STYLES_QR_CODE = css` const STYLES_QR_CODE = css`
background: ${Constants.system.white}; background: ${Constants.system.white};
border-radius: 4px; border-radius: 4px;
@ -88,7 +98,7 @@ const STYLES_ITEM = css`
margin-top: 24px; margin-top: 24px;
display: inline-flex; display: inline-flex;
flex-direction: column; flex-direction: column;
max-width: 180px; max-width: 220px;
margin-right: 32px; margin-right: 32px;
`; `;
@ -128,14 +138,23 @@ export default class SceneWallet extends React.Component {
this.setState({ visible: !this.state.visible }); this.setState({ visible: !this.state.visible });
}; };
_handleCopy = (text) => {
Strings.copyText(text);
alert(`${text} Added to clipboard.`);
};
render() { render() {
let addresses = {}; let addresses = {};
let lastAddress;
this.props.viewer.addresses.forEach((a) => { this.props.viewer.addresses.forEach((a) => {
addresses[a.value] = a; addresses[a.address] = a;
lastAddress = a.address;
}); });
const currentAddress = addresses[this.props.selected.address]; const currentAddress = this.props.selected.address
? addresses[this.props.selected.address]
: addresses[lastAddress];
// TODO(jim): // TODO(jim):
// Capture this state. // Capture this state.
@ -150,7 +169,7 @@ export default class SceneWallet extends React.Component {
return ( return (
<ScenePage> <ScenePage>
<System.H1>Your wallet</System.H1> <System.H1>Wallet</System.H1>
<Section <Section
onAction={this.props.onAction} onAction={this.props.onAction}
@ -158,15 +177,10 @@ export default class SceneWallet extends React.Component {
title="Addresses" title="Addresses"
buttons={[ buttons={[
{ {
name: 'Create new', name: 'Create a new address',
type: 'SIDEBAR', type: 'SIDEBAR',
value: 'SIDEBAR_CREATE_WALLET_ADDRESS', value: 'SIDEBAR_CREATE_WALLET_ADDRESS',
}, },
{
name: 'Delete',
type: 'SIDEBAR',
value: 'SIDEBAR_DELETE_WALLET_ADDRESS',
},
]}> ]}>
<div css={STYLES_GROUP}> <div css={STYLES_GROUP}>
<System.SelectMenu <System.SelectMenu
@ -191,7 +205,10 @@ export default class SceneWallet extends React.Component {
<div style={{ marginTop: 24 }}> <div style={{ marginTop: 24 }}>
<div css={STYLES_FOCUS}> <div css={STYLES_FOCUS}>
{currentAddress.name} <strong css={STYLES_FOCUS_EMPAHSIS}>(Primary)</strong> {currentAddress.name}{' '}
{this.props.viewer.settings_cold_default_address === currentAddress.address ? (
<strong css={STYLES_FOCUS_EMPAHSIS}>(Primary)</strong>
) : null}
</div> </div>
<div css={STYLES_SUBTEXT}>Filecoin address alias</div> <div css={STYLES_SUBTEXT}>Filecoin address alias</div>
</div> </div>
@ -206,11 +223,6 @@ export default class SceneWallet extends React.Component {
<div css={STYLES_FOCUS}>{currentAddress.type}</div> <div css={STYLES_FOCUS}>{currentAddress.type}</div>
<div css={STYLES_SUBTEXT}>Address type</div> <div css={STYLES_SUBTEXT}>Address type</div>
</div> </div>
<div css={STYLES_ITEM_CLICKABLE} onClick={() => this.props.onNavigateTo({ id: 5 })}>
<div css={STYLES_FOCUS}>{currentAddress.deals}</div>
<div css={STYLES_SUBTEXT}>Active deals</div>
</div>
</div> </div>
<div style={{ marginTop: 24 }}> <div style={{ marginTop: 24 }}>
@ -236,41 +248,12 @@ export default class SceneWallet extends React.Component {
}}> }}>
<SVG.Privacy height="16px" /> <SVG.Privacy height="16px" />
</span> </span>
<span css={STYLES_CIRCLE_BUTTON}> <span css={STYLES_CIRCLE_BUTTON} onClick={() => this._handleCopy(currentAddress.address)}>
<SVG.CopyAndPaste height="16px" /> <SVG.CopyAndPaste height="16px" />
</span> </span>
</div> </div>
<div css={STYLES_ACTIONS}>
<div css={STYLES_QR_CODE}>
<img src="/static/qr-code-example.jpg" css={STYLES_QR_CODE_IMAGE} />
</div>
</div>
</div> </div>
</Section> </Section>
<Section
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
title={`Transactions for ${currentAddress.name}`}
buttons={[
{
name: 'Export',
type: 'DOWNLOAD',
value: 'CSV_WALLET_TRANSACTIONS',
},
]}>
<System.Table
data={{
columns: SchemaTable.Wallet,
rows: transactions,
}}
selectedRowId={this.state.table_transaction}
onChange={this._handleChange}
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
name="table_transaction"
/>
</Section>
</ScenePage> </ScenePage>
); );
} }

112
server.js
View File

@ -7,6 +7,7 @@ import * as Middleware from '~/common/middleware';
import FS from 'fs'; import FS from 'fs';
import express from 'express'; import express from 'express';
import formidable from 'formidable';
import next from 'next'; import next from 'next';
import bodyParser from 'body-parser'; import bodyParser from 'body-parser';
import compression from 'compression'; import compression from 'compression';
@ -15,14 +16,15 @@ const dev = process.env.NODE_ENV !== 'production';
const port = process.env.PORT || 1337; const port = process.env.PORT || 1337;
const app = next({ dev, quiet: false }); const app = next({ dev, quiet: false });
const nextRequestHandler = app.getRequestHandler(); const nextRequestHandler = app.getRequestHandler();
const AVATAR_STORAGE_URL = `${__dirname}/public/static/system/`;
// TODO(jim): Just a solution for testing. // TODO(jim): Just a solution for testing.
let token; let token = null;
let status; let status = null;
let messageList; let messageList = null;
let peersList; let peersList = null;
let addrsList; let addrsList = null;
let info; let info = null;
const refresh = async () => { const refresh = async () => {
const Health = await pow.health.check(); const Health = await pow.health.check();
@ -35,10 +37,25 @@ const refresh = async () => {
const refreshWithToken = async () => { const refreshWithToken = async () => {
const Addresses = await pow.ffs.addrs(); const Addresses = await pow.ffs.addrs();
addrsList = Addresses.addrsList; addrsList = Addresses.addrsList ? Addresses.addrsList : null;
const NetworkInfo = await pow.ffs.info(); const NetworkInfo = await pow.ffs.info();
info = NetworkInfo.info; info = NetworkInfo.info ? NetworkInfo.info : null;
};
const getData = async () => {
const data = {
production: !dev,
status,
messageList,
peersList,
addrsList,
info,
};
console.log('ON THE SERVER', data);
return data;
}; };
app.prepare().then(async () => { app.prepare().then(async () => {
@ -54,18 +71,26 @@ app.prepare().then(async () => {
token = FFS.token ? FFS.token : null; token = FFS.token ? FFS.token : null;
// NOTE(jim): Write a new token file. // NOTE(jim): Write a new token file.
FS.writeFileSync('./.data/powergate-token', token); if (token) {
FS.writeFileSync('./.data/powergate-token', token);
}
} else { } else {
token = FS.readFileSync('./.data/powergate-token', 'utf8'); token = FS.readFileSync('./.data/powergate-token', 'utf8');
} }
pow.setToken(token); if (token) {
pow.setToken(token);
}
await refreshWithToken(); await refreshWithToken();
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
if (!token) {
throw new Error('[ prototype ] can not start client without proper auth token');
}
const server = express(); const server = express();
if (!dev) { if (!dev) {
@ -85,18 +110,57 @@ app.prepare().then(async () => {
res.send('ok'); res.send('ok');
}); });
server.post('/_/upload/avatar', async (req, res) => {
const form = formidable({ multiples: true, uploadDir: AVATAR_STORAGE_URL });
form.once('error', console.error);
form.on('progress', (bytesReceived, bytesExpected) => {
console.log(`[ prototype ] ${bytesReceived} / ${bytesExpected}`);
});
form.on('fileBegin', (filename, file) => {
form.emit('data', { name: '[ prototype ] file uploading', filename, value: file });
});
form.on('file', (filename, file) => {
form.emit('data', { name: '[ prototype ] file:', key: filename, value: file });
});
form.on('field', (fieldName, fieldValue) => {
form.emit('data', { name: '[ prototype ] field:', key: fieldName, value: fieldValue });
});
form.once('end', () => {
console.log('[ prototype ] finished upload');
});
form.parse(req, (error, fields, files) => {
if (error) {
return res.status(500).send({ error });
} else {
const newPath = form.uploadDir + 'avatar.png';
FS.rename(files.image.path, newPath, function (err) {});
return res.status(200).send({ success: true });
}
});
});
server.post('/_/viewer', async (req, res) => { server.post('/_/viewer', async (req, res) => {
await refresh(); await refresh();
await refreshWithToken(); await refreshWithToken();
const data = { return res.status(200).send({ success: true, data: await getData() });
production: !dev, });
status,
messageList, server.post('/_/settings', async (req, res) => {
peersList, let data;
addrsList, try {
info, data = await pow.ffs.setDefaultConfig(req.body.config);
}; } catch (e) {
return res.status(500).send({ error: e.message });
}
return res.status(200).send({ success: true, data }); return res.status(200).send({ success: true, data });
}); });
@ -104,7 +168,7 @@ app.prepare().then(async () => {
server.post('/_/wallet/create', async (req, res) => { server.post('/_/wallet/create', async (req, res) => {
let data; let data;
try { try {
data = await pow.ffs.newAddr(req.body.name); data = await pow.ffs.newAddr(req.body.name, req.body.type, req.body.makeDefault);
} catch (e) { } catch (e) {
return res.status(500).send({ error: e.message }); return res.status(500).send({ error: e.message });
} }
@ -124,14 +188,7 @@ app.prepare().then(async () => {
}); });
server.get('/', async (req, res) => { server.get('/', async (req, res) => {
return app.render(req, res, '/', { return app.render(req, res, '/', { production: false });
production: !dev,
status,
messageList,
peersList,
addrsList,
info,
});
}); });
server.get('*', async (req, res) => { server.get('*', async (req, res) => {
@ -146,5 +203,6 @@ app.prepare().then(async () => {
console.log('[ prototype ] initializing '); console.log('[ prototype ] initializing ');
console.log('[ prototype ] powergate token:', token); console.log('[ prototype ] powergate token:', token);
console.log(`[ prototype ] listening on: http://localhost:${port}`); console.log(`[ prototype ] listening on: http://localhost:${port}`);
console.log(`[ prototype ] avatar storage: ${AVATAR_STORAGE_URL}`);
}); });
}); });