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/**/*
.library/**/*

View File

@ -8,7 +8,7 @@ const REQUEST_HEADERS = {
'Content-Type': 'application/json',
};
const SERVER_PATH = '';
const SERVER_PATH = 'http://localhost:1337';
export const rehydrateViewer = async () => {
const options = {
@ -18,10 +18,24 @@ export const rehydrateViewer = async () => {
body: JSON.stringify({}),
};
const response = await fetch(`/_/viewer`, options);
const response = await fetch(`${SERVER_PATH}/_/viewer`, options);
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) => {
@ -36,7 +50,7 @@ export const createWalletAddress = async (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();
return json;
@ -62,7 +76,7 @@ export const sendWalletAddressFilecoin = async (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();
return json;

View File

@ -488,6 +488,7 @@ export const NavigationState = [
name: 'Wallet',
pageTitle: 'your wallet and addresses',
decorator: 'WALLET',
/*
children: [
{
id: 3,
@ -497,6 +498,7 @@ export const NavigationState = [
decorator: 'CHANNELS',
},
],
*/
},
constructFilesTreeForNavigation(),
{
@ -514,6 +516,7 @@ export const NavigationState = [
},
],
},
/*
{
id: 9,
name: 'Stats',
@ -557,6 +560,7 @@ export const NavigationState = [
decorator: 'LOGS',
children: null,
},
*/
{
id: 13,
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 balanceMap = {};
@ -8,7 +8,8 @@ const transformAddresses = (addrsList, info) => {
return addrsList.map((each, index) => {
return {
value: `${index + 1}`,
id: each.addr,
value: each.addr,
balance: balanceMap[each.addr],
name: each.name,
address: each.addr,
@ -26,7 +27,7 @@ const transformPeers = (peersList) => {
'peer-avatar': null,
'chain-head': null,
height: null,
location: each.location.country,
location: null,
upload: null,
download: null,
};
@ -35,12 +36,11 @@ const transformPeers = (peersList) => {
export const getInitialState = (props) => {
const { status, messageList, peersList, addrsList, info } = props;
console.log(props);
return {
id: info.id,
name: 'Andrew Hill',
photoURL: '/static/avatar-andrew-hill.jpg',
name: 'New Node',
photoURL: '/static/system/avatar.png',
config: '',
upload_bandwidth: 0,
download_bandwidth: 0,

View File

@ -5,37 +5,42 @@ const WEEK = DAY * 7;
const MONTH = (DAY * 365) / 12;
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) => {
hex = hex.replace("#", "");
var r = parseInt(
hex.length == 3 ? hex.slice(0, 1).repeat(2) : hex.slice(0, 2),
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
);
hex = hex.replace('#', '');
var r = parseInt(hex.length == 3 ? hex.slice(0, 1).repeat(2) : hex.slice(0, 2), 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) {
return "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")";
return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')';
} else {
return "rgb(" + r + ", " + g + ", " + b + ")";
return 'rgb(' + r + ', ' + g + ', ' + b + ')';
}
};
export const bytesToSize = (bytes, decimals = 2) => {
if (bytes === 0) return "0 Bytes";
if (bytes === 0) return '0 Bytes';
const k = 1024;
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));
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) => {
@ -43,18 +48,18 @@ export const getRemainingTime = (seconds) => {
let [value, unit] =
seconds < MINUTE
? [Math.round(seconds), "second"]
? [Math.round(seconds), 'second']
: seconds < HOUR
? [Math.round(seconds / MINUTE), "minute"]
? [Math.round(seconds / MINUTE), 'minute']
: seconds < DAY
? [Math.round(seconds / HOUR), "hour"]
? [Math.round(seconds / HOUR), 'hour']
: seconds < WEEK
? [Math.round(seconds / DAY), "day"]
? [Math.round(seconds / DAY), 'day']
: seconds < MONTH
? [Math.round(seconds / WEEK), "week"]
? [Math.round(seconds / WEEK), 'week']
: seconds < YEAR
? [Math.round(seconds / MONTH), "month"]
: [Math.round(seconds / YEAR), "year"];
? [Math.round(seconds / MONTH), 'month']
: [Math.round(seconds / YEAR), 'year'];
unit = pluralize(unit, value);
@ -73,7 +78,7 @@ export const pluralize = (text, count) => {
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)) {
return emptyState;
}
@ -91,5 +96,5 @@ export const toDate = (data) => {
};
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 Strings from "~/common/strings";
import * as Constants from "~/common/constants";
import * as System from "~/components/system";
import * as SVG from "~/common/svg";
import * as React from 'react';
import * as Constants from '~/common/constants';
import * as System from '~/components/system';
import * as SVG from '~/common/svg';
import { css } from "@emotion/react";
import { css } from '@emotion/react';
import Avatar from "~/components/core/Avatar";
import Pill from "~/components/core/Pill";
import Avatar from '~/components/core/Avatar';
const STYLES_CIRCLE = css`
height: 32px;
@ -115,118 +113,52 @@ const STYLES_INPUT = css`
transition: 200ms ease all;
:focus {
box-shadow: 0 1px 4px rgba(0, 71, 255, 0.3),
inset 0 0 0 1px ${Constants.system.brand};
box-shadow: 0 1px 4px rgba(0, 71, 255, 0.3), inset 0 0 0 1px ${Constants.system.brand};
outline: 0;
}
`;
export default class ApplicationHeader extends React.Component {
render() {
const isBackDisabled =
this.props.currentIndex === 0 || this.props.history.length < 2;
const isBackDisabled = this.props.currentIndex === 0 || this.props.history.length < 2;
const isForwardDisabled =
this.props.currentIndex === this.props.history.length - 1 ||
this.props.history.length < 2;
this.props.currentIndex === this.props.history.length - 1 || this.props.history.length < 2;
return (
<header css={STYLES_APPLICATION_HEADER}>
<div css={STYLES_LEFT}>
<span
css={STYLES_ICON_ELEMENT_CUSTOM}
style={{ marginRight: 16, marginLeft: 12 }}
>
<span css={STYLES_ICON_ELEMENT_CUSTOM} style={{ marginRight: 16, marginLeft: 12 }}>
<SVG.Logo height="32px" />
</span>
<span
css={STYLES_ICON_ELEMENT}
style={
isBackDisabled
? { cursor: "not-allowed", color: Constants.system.border }
: null
}
onClick={isBackDisabled ? () => {} : this.props.onBack}
>
<SVG.NavigationArrow
height="16px"
style={{ transform: `rotate(180deg)` }}
/>
style={isBackDisabled ? { cursor: 'not-allowed', color: Constants.system.border } : null}
onClick={isBackDisabled ? () => {} : this.props.onBack}>
<SVG.NavigationArrow height="16px" style={{ transform: `rotate(180deg)` }} />
</span>
<span
css={STYLES_ICON_ELEMENT}
style={
isForwardDisabled
? { cursor: "not-allowed", color: Constants.system.border }
: null
}
onClick={isForwardDisabled ? () => {} : this.props.onForward}
>
style={isForwardDisabled ? { cursor: 'not-allowed', color: Constants.system.border } : null}
onClick={isForwardDisabled ? () => {} : this.props.onForward}>
<SVG.NavigationArrow height="16px" />
</span>
</div>
<div css={STYLES_MIDDLE}>
<input
css={STYLES_INPUT}
placeholder={`Type to search ${this.props.pageTitle}`}
/>
</div>
<div css={STYLES_MIDDLE}></div>
<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
style={{ marginLeft: 12 }}
onClick={() => {}}
size={32}
url={this.props.viewer.photoURL}
popover={
<System.PopoverNavigation
style={{ right: 0, top: "48px" }}
style={{ right: 0, top: '48px', cursor: 'pointer' }}
onNavigateTo={this.props.onNavigateTo}
onAction={this.props.onAction}
navigation={[
{ text: "Edit account", value: 13 },
{ text: "Settings", value: 14 },
{ text: 'Edit account', value: 13 },
{ text: 'Settings', value: 14 },
]}
/>
}

View File

@ -1,9 +1,9 @@
import * as React from "react";
import * as Constants from "~/common/constants";
import * as React from 'react';
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`
display: inline-flex;
@ -56,9 +56,8 @@ export default class AvatarEntity extends React.Component {
height: `${this.props.size}px`,
borderRadius: `${this.props.size}px`,
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.props.online ? <span css={STYLES_AVATAR_ONLINE} /> : null}
</Dismissible>

View File

@ -9,23 +9,36 @@ import { css } from '@emotion/react';
const SELECT_MENU_OPTIONS = [
{ value: '1', name: 'BLS' },
{ value: '2', name: 'SECP256K1' },
{ value: '3', name: 'MULTISIG' },
// { value: '3', name: 'MULTISIG' },
];
const SELECT_MENU_MAP = {
'1': 'BLS',
'2': 'SECP256K1',
'3': 'MULTISIG',
// '3': 'MULTISIG',
};
const SELECT_MENU_SAVE_STRINGS = {
'1': 'bls',
'2': 'secp256k1',
};
export default class SidebarCreateWalletAddress extends React.Component {
state = {
name: '',
type: '1',
default: false,
};
_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 = () => {
@ -60,6 +73,14 @@ export default class SidebarCreateWalletAddress extends React.Component {
{SELECT_MENU_MAP[this.state.type]}
</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}>
Create {this.state.name}
</System.ButtonPrimaryFull>

View File

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

View File

@ -1,7 +1,6 @@
import * as React from 'react';
import * as Strings from '~/common/strings';
import * as Constants from '~/common/constants';
import * as SVG from '~/components/system/svg';
import * as System from '~/components/system';
import { css } from '@emotion/react';
@ -104,12 +103,12 @@ export default class SidebarWalletSendFunds extends React.Component {
/>
<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>
<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>

View File

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

View File

@ -15,7 +15,7 @@
"@emotion/css": "11.0.0-next.12",
"@emotion/react": "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",
"body-parser": "^1.19.0",
"chart.js": "^2.9.3",
@ -23,6 +23,7 @@
"compression": "^1.7.4",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"formidable": "^1.2.2",
"isomorphic-fetch": "^2.2.1",
"next": "^9.4.0",
"react": "^16.12.0",

View File

@ -62,9 +62,15 @@ const getCurrentNavigationStateById = (navigation, targetId) => {
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 {
props: { ...context.query },
props: { ...data.data },
};
};
@ -74,7 +80,7 @@ export default class IndexPage extends React.Component {
currentIndex: 0,
data: null,
selected: {
address: '1',
address: null,
},
viewer: this.props.production ? Fixtures.getInitialState() : State.getInitialState(this.props),
sidebar: null,
@ -85,6 +91,11 @@ export default class IndexPage extends React.Component {
console.log(this.props);
}
rehydrate = async () => {
const viewer = await Actions.rehydrateViewer();
this.setState({ viewer: { ...State.getInitialState(viewer.data) } });
};
_handleSubmit = async (data) => {
if (this.props.production) {
alert('TODO');
@ -92,9 +103,13 @@ export default class IndexPage extends React.Component {
}
if (data.type === 'CREATE_WALLET_ADDRESS') {
const response = await Actions.createWalletAddress({ name: data.name });
const viewer = await Actions.rehydrateViewer();
this.setState({ viewer });
const address = await Actions.createWalletAddress({
name: data.name,
type: data.wallet_type,
makeDefault: data.makeDefault,
});
await this.rehydrate();
}
if (data.type === 'SEND_WALLET_ADDRESS_FILECOIN') {
@ -104,8 +119,7 @@ export default class IndexPage extends React.Component {
amount: data.amount,
});
const viewer = await Actions.rehydrateViewer();
this.setState({ viewer });
await this.rehydrate();
}
this._handleDismissSidebar();
@ -272,6 +286,7 @@ export default class IndexPage extends React.Component {
);
const scene = React.cloneElement(this.scenes[current.target.decorator], {
rehydrate: this.rehydrate,
viewer: this.state.viewer,
selected: this.state.selected,
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 Strings from "~/common/strings";
import * as Constants from "~/common/constants";
import * as Fixtures from "~/common/fixtures";
import * as System from "~/components/system";
import * as React from 'react';
import * as System from '~/components/system';
import * as Actions from '~/common/actions';
import { css } from "@emotion/react";
import { css } from '@emotion/react';
import Section from "~/components/core/Section";
import ScenePage from "~/components/core/ScenePage";
import Avatar from "~/components/core/Avatar";
import ScenePage from '~/components/core/ScenePage';
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 {
_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) => {
this.props.onViewerChange(e);
};
@ -26,17 +60,13 @@ export default class SceneEditAccount extends React.Component {
description="This image will appear in various lists."
/>
<Avatar
style={{ marginTop: 24 }}
size={256}
url={this.props.viewer.photoURL}
/>
<Avatar style={{ marginTop: 24 }} size={256} url={this.props.viewer.photoURL} />
<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
</System.ButtonPrimary>
<System.ButtonSecondary>Delete</System.ButtonSecondary>
</div>
<System.Input
@ -48,31 +78,6 @@ export default class SceneEditAccount extends React.Component {
placeholder="Name"
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>
);
}

View File

@ -54,132 +54,6 @@ export default class SceneHome extends React.Component {
render() {
return (
<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
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
@ -190,6 +64,11 @@ export default class SceneHome extends React.Component {
type: 'NAVIGATE',
value: 'folder-root',
},
{
name: 'Store file on network',
type: 'SIDEBAR',
value: 'SIDEBAR_FILE_STORAGE_DEAL',
},
]}>
<System.Table
data={{
@ -203,7 +82,7 @@ export default class SceneHome extends React.Component {
},
{ key: 'remaining', name: 'Remaining time', width: '180px' },
],
rows: Data.EXAMPLE_FILES.slice(0, 4),
rows: [],
}}
selectedRowId={this.state.data}
onChange={this._handleChange}
@ -217,18 +96,22 @@ export default class SceneHome extends React.Component {
<Section
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
title="Recent transactions"
title="Wallet addresses"
buttons={[
{
name: 'View wallet',
name: 'View all',
type: 'NAVIGATE',
value: 2,
},
]}>
<System.Table
data={{
columns: SchemaTable.Transactions,
rows: this.props.viewer.addresses[0].transactions,
columns: [
{ key: 'address', name: 'Address' },
{ key: 'balance', name: 'Filecoin', width: '228px' },
{ key: 'type', name: 'Type' },
],
rows: this.props.viewer.addresses,
}}
selectedRowId={this.state.transaction}
onChange={this._handleChange}

View File

@ -1,26 +1,11 @@
import * as React from 'react';
import * as Strings from '~/common/strings';
import * as Constants from '~/common/constants';
import * as Fixtures from '~/common/fixtures';
import * as Actions from '~/common/actions';
import * as System from '~/components/system';
import { css } from '@emotion/react';
import Section from '~/components/core/Section';
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`
display: flex;
align-items: center;
@ -45,8 +30,47 @@ const STYLES_RIGHT = css`
`;
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) => {
window.clearTimeout(this._deferredSave);
this._deferredSave = null;
this.props.onViewerChange(e);
this._deferredSave = window.setTimeout(async () => {
await this._handleSave();
}, 2000);
};
render() {
@ -110,7 +134,7 @@ export default class SceneSettings extends React.Component {
category="address"
onChange={this._handleChange}
options={this.props.viewer.addresses}>
{currentAddress.name}
{currentAddress ? currentAddress.name : 'None'}
</System.SelectMenu>
<System.Input

View File

@ -15,6 +15,16 @@ const STYLES_GROUP = css`
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`
background: ${Constants.system.white};
border-radius: 4px;
@ -88,7 +98,7 @@ const STYLES_ITEM = css`
margin-top: 24px;
display: inline-flex;
flex-direction: column;
max-width: 180px;
max-width: 220px;
margin-right: 32px;
`;
@ -128,14 +138,23 @@ export default class SceneWallet extends React.Component {
this.setState({ visible: !this.state.visible });
};
_handleCopy = (text) => {
Strings.copyText(text);
alert(`${text} Added to clipboard.`);
};
render() {
let addresses = {};
let lastAddress;
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):
// Capture this state.
@ -150,7 +169,7 @@ export default class SceneWallet extends React.Component {
return (
<ScenePage>
<System.H1>Your wallet</System.H1>
<System.H1>Wallet</System.H1>
<Section
onAction={this.props.onAction}
@ -158,15 +177,10 @@ export default class SceneWallet extends React.Component {
title="Addresses"
buttons={[
{
name: 'Create new',
name: 'Create a new address',
type: 'SIDEBAR',
value: 'SIDEBAR_CREATE_WALLET_ADDRESS',
},
{
name: 'Delete',
type: 'SIDEBAR',
value: 'SIDEBAR_DELETE_WALLET_ADDRESS',
},
]}>
<div css={STYLES_GROUP}>
<System.SelectMenu
@ -191,7 +205,10 @@ export default class SceneWallet extends React.Component {
<div style={{ marginTop: 24 }}>
<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 css={STYLES_SUBTEXT}>Filecoin address alias</div>
</div>
@ -206,11 +223,6 @@ export default class SceneWallet extends React.Component {
<div css={STYLES_FOCUS}>{currentAddress.type}</div>
<div css={STYLES_SUBTEXT}>Address type</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 style={{ marginTop: 24 }}>
@ -236,41 +248,12 @@ export default class SceneWallet extends React.Component {
}}>
<SVG.Privacy height="16px" />
</span>
<span css={STYLES_CIRCLE_BUTTON}>
<span css={STYLES_CIRCLE_BUTTON} onClick={() => this._handleCopy(currentAddress.address)}>
<SVG.CopyAndPaste height="16px" />
</span>
</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>
</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>
);
}

112
server.js
View File

@ -7,6 +7,7 @@ import * as Middleware from '~/common/middleware';
import FS from 'fs';
import express from 'express';
import formidable from 'formidable';
import next from 'next';
import bodyParser from 'body-parser';
import compression from 'compression';
@ -15,14 +16,15 @@ const dev = process.env.NODE_ENV !== 'production';
const port = process.env.PORT || 1337;
const app = next({ dev, quiet: false });
const nextRequestHandler = app.getRequestHandler();
const AVATAR_STORAGE_URL = `${__dirname}/public/static/system/`;
// TODO(jim): Just a solution for testing.
let token;
let status;
let messageList;
let peersList;
let addrsList;
let info;
let token = null;
let status = null;
let messageList = null;
let peersList = null;
let addrsList = null;
let info = null;
const refresh = async () => {
const Health = await pow.health.check();
@ -35,10 +37,25 @@ const refresh = async () => {
const refreshWithToken = async () => {
const Addresses = await pow.ffs.addrs();
addrsList = Addresses.addrsList;
addrsList = Addresses.addrsList ? Addresses.addrsList : null;
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 () => {
@ -54,18 +71,26 @@ app.prepare().then(async () => {
token = FFS.token ? FFS.token : null;
// NOTE(jim): Write a new token file.
FS.writeFileSync('./.data/powergate-token', token);
if (token) {
FS.writeFileSync('./.data/powergate-token', token);
}
} else {
token = FS.readFileSync('./.data/powergate-token', 'utf8');
}
pow.setToken(token);
if (token) {
pow.setToken(token);
}
await refreshWithToken();
} catch (e) {
console.log(e);
}
if (!token) {
throw new Error('[ prototype ] can not start client without proper auth token');
}
const server = express();
if (!dev) {
@ -85,18 +110,57 @@ app.prepare().then(async () => {
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) => {
await refresh();
await refreshWithToken();
const data = {
production: !dev,
status,
messageList,
peersList,
addrsList,
info,
};
return res.status(200).send({ success: true, data: await getData() });
});
server.post('/_/settings', async (req, res) => {
let data;
try {
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 });
});
@ -104,7 +168,7 @@ app.prepare().then(async () => {
server.post('/_/wallet/create', async (req, res) => {
let data;
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) {
return res.status(500).send({ error: e.message });
}
@ -124,14 +188,7 @@ app.prepare().then(async () => {
});
server.get('/', async (req, res) => {
return app.render(req, res, '/', {
production: !dev,
status,
messageList,
peersList,
addrsList,
info,
});
return app.render(req, res, '/', { production: false });
});
server.get('*', async (req, res) => {
@ -146,5 +203,6 @@ app.prepare().then(async () => {
console.log('[ prototype ] initializing ');
console.log('[ prototype ] powergate token:', token);
console.log(`[ prototype ] listening on: http://localhost:${port}`);
console.log(`[ prototype ] avatar storage: ${AVATAR_STORAGE_URL}`);
});
});