settings component and experience minus some polish

This commit is contained in:
Martina 2020-07-22 20:42:22 -07:00
parent 3d64268058
commit b73256c35b
9 changed files with 494 additions and 20 deletions

View File

@ -226,6 +226,11 @@ export default class SystemPage extends React.Component {
href="/experiences/list-filecoin-deals"
title="FilecoinDealsList"
/>
<SidebarLink
url={url}
href="/experiences/filecoin-settings"
title="FilecoinSettings"
/>
<span css={STYLES_LABEL}>
<br />

View File

@ -33,6 +33,12 @@ const STYLES_INPUT_CONTAINER = css`
min-width: 188px;
`;
const STYLES_INPUT_CONTAINER_FULL = css`
box-sizing: border-box;
position: relative;
min-width: 188px;
`;
const STYLES_INPUT = css`
${INPUT_STYLES}
padding: 0 24px 0 24px;
@ -128,7 +134,12 @@ export class Input extends React.Component {
render() {
return (
<div css={STYLES_INPUT_CONTAINER} style={this.props.containerStyle}>
<div
css={
this.props.full ? STYLES_INPUT_CONTAINER_FULL : STYLES_INPUT_CONTAINER
}
style={this.props.containerStyle}
>
<DescriptionGroup
tooltip={this.props.tooltip}
label={this.props.label}

View File

@ -31,15 +31,15 @@ const STYLES_SELECT_MENU = css`
display: inline-flex;
position: relative;
height: 40px;
max-width: 320px;
width: 100%;
`;
const STYLES_SELECT_MENU_FULL = css`
box-sizing: border-box;
display: inline-flex;
position: relative;
height: 40px;
const STYLES_CONTAINER = css`
width: 100%;
max-width: 480px;
`;
const STYLES_CONTAINER_FULL = css`
width: 100%;
`;
@ -86,7 +86,7 @@ export const SelectMenu = (props) => {
let presentationValue = map[props.value] ? map[props.value] : "Unselected";
return (
<React.Fragment>
<div css={props.full ? STYLES_CONTAINER_FULL : STYLES_CONTAINER}>
<DescriptionGroup
label={props.label}
description={props.description}
@ -94,15 +94,7 @@ export const SelectMenu = (props) => {
style={props.containerStyle}
/>
<div
css={
props.className
? props.className
: props.full
? STYLES_SELECT_MENU_FULL
: STYLES_SELECT_MENU
}
>
<div css={props.className ? props.className : STYLES_SELECT_MENU}>
<label css={STYLES_SELECT_MENU_LABEL} htmlFor={`id-${props.name}`}>
{map[props.value]}{" "}
{props.category ? (
@ -126,7 +118,7 @@ export const SelectMenu = (props) => {
})}
</select>
</div>
</React.Fragment>
</div>
);
};

View File

@ -14,6 +14,7 @@ import {
FilecoinStorageDealsList,
FilecoinRetrievalDealsList,
} from "~/components/system/modules/FilecoinDealsList";
import { FilecoinSettings } from "~/components/system/modules/FilecoinSettings";
// NOTE(jim): Global components
import { GlobalModal } from "~/components/system/components/GlobalModal";
@ -92,6 +93,7 @@ export {
FilecoinBalancesList,
FilecoinRetrievalDealsList,
FilecoinStorageDealsList,
FilecoinSettings,
// NOTE(jim): Components
ButtonPrimary,
ButtonPrimaryFull,

View File

@ -0,0 +1,335 @@
import * as React from "react";
import * as Constants from "~/common/constants";
import * as Strings from "~/common/strings";
import { css } from "@emotion/react";
import { DescriptionGroup } from "~/components/system/components/fragments/DescriptionGroup";
import { SelectMenu } from "~/components/system/components/SelectMenus";
import { Toggle } from "~/components/system/components/Toggle";
import { Input } from "~/components/system/components/Input";
import { CheckBox } from "~/components/system/components/CheckBox";
import { ButtonPrimary } from "~/components/system/components/Buttons";
import { CardTabGroup } from "~/components/system/components/CardTabGroup";
const STYLES_GROUP = css`
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
overflow-wrap: break-word;
white-space: pre-wrap;
max-width: 768px;
`;
const STYLES_SUBGROUP = css`
padding-left: 24px;
width: 100%;
overflow-wrap: break-word;
white-space: pre-wrap;
`;
const STYLES_LEFT = css`
padding: 12px 0 0 0;
min-width: 10%;
overflow-wrap: break-word;
white-space: pre-wrap;
`;
const STYLES_RIGHT = css`
padding-left: 48px;
padding-top: 24px;
flex-shrink: 0;
`;
const TAB_GROUP = [
{ value: "general", label: "General" },
{ value: "cold", label: "Cold Storage" },
{ value: "hot", label: "Hot Storage" },
];
export class FilecoinSettings extends React.Component {
static defaultProps = {
addrs: [],
settings_deals_auto_approve: false,
settings_hot_enabled: true,
//settings_hot_allow_unfreeze: this.props.allowUnfreeze,
settings_hot_ipfs_add_timeout: 60,
settings_cold_enabled: true,
//settings_cold_default_address: this.props.defaultAddr,
settings_cold_default_duration: 1000,
settings_cold_default_replication_factor: 1,
settings_cold_default_excluded_miners: [],
settings_cold_default_trusted_miners: [],
//settings_cold_default_max_price: this.props.maxPrice,
settings_cold_default_auto_renew: true,
//settings_cold_default_auto_renew_max_price: this.props.autoRenewMaxPrice,
//settings_repairable:
};
state = {
tabGroup: "general",
addrsList: this.props.addrsList.map((each) => {
return {
value: each.addr,
name: each.name,
};
}),
settings_deals_auto_approve: this.props.autoApprove, //left off changing these to match teh shape of this.props.defaultStorageConfig
settings_hot_enabled: this.props.hotEnabled, //and incorporate info from aaron
settings_hot_allow_unfreeze: this.props.allowUnfreeze, //we can use miner api for the list editor component (and incorp reputation)
settings_hot_ipfs_add_timeout: this.props.addTimeout,
settings_cold_enabled: this.props.coldEnabled,
settings_cold_default_address: this.props.defaultAddr,
settings_cold_default_duration: this.props.dealMinDuration,
settings_cold_default_replication_factor: this.props.repFactor,
settings_cold_default_excluded_miners: this.props.excludedMinersList,
settings_cold_default_trusted_miners: this.props.trustedMinersList,
settings_cold_default_max_price: this.props.maxPrice,
settings_cold_default_auto_renew: this.props.autoRenew,
settings_cold_default_auto_renew_max_price: this.props.autoRenewMaxPrice,
settings_repairable: this.props.repairable,
};
_handleSave = async () => {
this.props.onSave({
data: {
settings_deals_auto_approve: this.state.settings_deals_auto_approve,
},
config: {
hot: {
enabled: this.state.settings_hot_enabled,
allowUnfreeze: this.state.settings_hot_allow_unfreeze,
ipfs: {
addTimeout: this.state.settings_hot_ipfs_add_timeout,
},
},
cold: {
enabled: this.state.settings_cold_enabled,
filecoin: {
addr: this.state.settings_cold_default_address,
dealMinDuration: this.state.settings_cold_default_duration,
repFactor: this.state.settings_cold_default_replication_factor,
excludedMinersList: this.state
.settings_cold_default_excluded_miners,
trustedMinersList: this.state.settings_cold_default_trusted_miners,
maxPrice: this.state.settings_cold_default_max_price,
renew: {
enabled: this.state.settings_cold_default_auto_renew,
threshold: this.state.settings_cold_default_auto_renew_max_price,
},
},
},
repairable: this.state.settings_repairable,
},
});
};
_handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
render() {
return (
<div>
<CardTabGroup
name="tabGroup"
options={TAB_GROUP}
value={this.state.tabGroup}
onChange={this._handleChange}
/>
<div style={{ padding: "16px" }}>
{this.state.tabGroup === "general" ? (
<div>
<div css={STYLES_GROUP}>
<div css={STYLES_LEFT}>
<DescriptionGroup
label="Automatically approve deals"
tooltip="If you do not have enough Filecoin you will receive a warning, regardless of whether this is enabled."
description="When enabled, every storage deal will be automatically approved without asking for confirmation."
/>
</div>
<div css={STYLES_RIGHT}>
<Toggle
name="settings_deals_auto_approve"
onChange={this._handleChange}
active={this.state.settings_deals_auto_approve}
/>
</div>
</div>
<div>
<DescriptionGroup
style={{ marginTop: 24 }}
label="Repairable"
description="Placeholder."
tooltip="Placeholder."
/>
<CheckBox
name="settings_repairable"
value={this.state.settings_repairable}
onChange={this._handleChange}
>
Repairable
</CheckBox>
</div>
</div>
) : this.state.tabGroup === "cold" ? (
<div>
<div css={STYLES_GROUP}>
<div css={STYLES_LEFT}>
<DescriptionGroup
label="Enable cold storage"
tooltip="Placeholder"
description="By enabling cold storage, every time you make a deal your data will be stored on the Filecoin Network."
/>
</div>
<div css={STYLES_RIGHT}>
<Toggle
name="settings_cold_enabled"
onChange={this._handleChange}
active={this.state.settings_cold_enabled}
/>
</div>
</div>
{this.state.settings_cold_enabled ? (
<div>
<SelectMenu
full
containerStyle={{ marginTop: 24 }}
label="Default Filecoin address"
description="Deal payments will default to this address."
tooltip="You will always have the option to choose a different wallet before you make a deal."
name="settings_cold_default_address"
value={this.state.settings_cold_default_address}
category="address"
onChange={this._handleChange}
options={this.state.addrsList}
/>
<Input
full
containerStyle={{ marginTop: 24 }}
label="Default deal duration"
description="How long your files will be stored for."
tooltip="Placeholder."
name="settings_cold_default_duration"
type="number"
value={this.state.settings_cold_default_duration}
placeholder="Duration in epochs (~25 seconds)"
onChange={this._handleChange}
/>
<Input
full
containerStyle={{ marginTop: 24 }}
label="Default replication factor"
description="The number of miners that each file will be stored with."
tooltip="A higher replication factor means your files are more secure against loss, but also costs more."
name="settings_cold_default_replication_factor"
value={this.state.settings_cold_default_replication_factor}
placeholder="Number of miners"
type="number"
onChange={this._handleChange}
/>
<Input
full
containerStyle={{ marginTop: 24 }}
label="Max Filecoin price"
description="The maximum price you're willing to pay to store your file for the length of one deal duration (as specified above)."
tooltip="Slate will always try to find you the best price, regardless of how high you set this."
name="settings_cold_default_max_price"
type="number"
value={this.state.settings_cold_default_max_price}
placeholder="Price in Filecoin"
onChange={this._handleChange}
/>
<DescriptionGroup
style={{ marginTop: 24 }}
label="Auto renew deals"
description="If auto renew is enabled, your wallet will automatically be charged once the deal duration is up. This guarantees your files will remain stored even if you do not manually renew."
tooltip="This does not protect your files in the event that your wallet lacks sufficient funds or if there are no miners willing to store it for less than your max auto renew price."
/>
<CheckBox
name="settings_cold_default_auto_renew"
value={this.state.settings_cold_default_auto_renew}
onChange={this._handleChange}
>
Enable auto renew
</CheckBox>
<Input
full
containerStyle={{ marginTop: 24 }}
label="Max auto renew price."
description="Set the maximum Filecoin price you're willing to pay to auto renew a storage deal."
tooltip="Placeholder."
name="settings_cold_default_auto_renew_max_price"
type="number"
value={
this.state.settings_cold_default_auto_renew_max_price
}
placeholder="Price in Filecoin"
onChange={this._handleChange}
/>
</div>
) : null}
</div>
) : (
<div>
<div css={STYLES_GROUP}>
<div css={STYLES_LEFT}>
<DescriptionGroup
label="Enable hot storage"
tooltip="Placeholder"
description="By enabling hot storage, every time you make a deal your data will be stored on IPFS."
/>
</div>
<div css={STYLES_RIGHT}>
<Toggle
name="settings_hot_enabled"
onChange={this._handleChange}
active={this.state.settings_hot_enabled}
/>
</div>
</div>
{this.state.settings_hot_enabled ? (
<div>
<DescriptionGroup
style={{ marginTop: 24 }}
label="Allow Unfreeze"
description="Placeholder."
tooltip="Placeholder."
/>
<CheckBox
name="settings_hot_allow_unfreeze"
value={this.state.settings_hot_allow_unfreeze}
onChange={this._handleChange}
>
IPFS allow unfreeze setting description.
</CheckBox>
<Input
full
containerStyle={{ marginTop: 24 }}
label="Add timeout"
description="Add IPFS timeout setting description."
tooltip="Placeholder."
name="settings_hot_ipfs_add_timeout"
value={this.state.settings_hot_ipfs_add_timeout}
placeholder="Type in seconds"
onChange={this._handleChange}
/>
</div>
) : null}
</div>
)}
<div style={{ marginTop: 32 }}>
<ButtonPrimary onClick={this._handleSave}>Save</ButtonPrimary>
</div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,128 @@
import * as React from "react";
import * as System from "~/components/system";
import SystemPage from "~/components/system/SystemPage";
import ViewSourceLink from "~/components/system/ViewSourceLink";
import CodeBlock from "~/components/system/CodeBlock";
const EXAMPLE_CODE = `import * as React from 'react';
import { FilecoinSettings } from 'slate-react-system';
import { createPow } from "@textile/powergate-client";
const PowerGate = createPow({ host: "http://pow.slate.textile.io:6002" });
class Example extends React.Component {
componentDidMount = async () => {
const FFS = await PowerGate.ffs.create();
const token = FFS.token ? FFS.token : null;
PowerGate.setToken(token);
const { addrs } = await Powergate.ffs.addrs();
const { defaultStorageConfig } = await PowerGate.ffs.defaultStorageConfig();
this.setState({ token, defaultStorageConfig, addrsList });
}
_handleSave = async ({ data, config }) => {
const response = await Powergate.ffs.setDefaultStorageConfig(config);
this.setState({ data });
}
render() {
return (
<FilecoinSettings
defaultStorageConfig={this.state.defaultStorageConfig}
addrsList={this.state.addrsList}
onSave={this._handleSave}
/>
);
}
}
`;
export default class SystemPageFilecoinWalletBalances extends React.Component {
_handleSave = (value) => {
console.log(value);
};
render() {
const addrsList = [
{
addr:
"t3qwsglg755cwfaehqmsuzj2efebyyrqzlnhjogj2uwj44ce3anpowsdmaxdfnndukihmzrohqnpzakoq3tujq",
name: "Initial Address",
type: "bls",
},
{
addr:
"t3ual5q5qo5wolfxsui4ciujfucqwf6gqso4lettcjwl2tyismgol7c4tngvoono5rmytuqotye7oosfjv6g7a",
name: "Secondary Address",
type: "bls",
},
];
const defaultStorageConfig = {
defaultStorageConfig: {
cold: {
enabled: true,
filecoin: {
addr:
"t3whycszj43jeesnnagr3wbkyxoh6qv5ri6kqfmvk3u5x2nxcgiu4266dstutkdvi4pqbkz75odv7bxa6fjjkq",
countryCodesList: [],
dealMinDuration: 1000,
excludedMinersList: [],
maxPrice: 0,
renew: {
enabled: false,
threshold: 0,
},
repFactor: 1,
trustedMinersList: [],
},
},
hot: {
allowUnfreeze: false,
enabled: true,
ipfs: {
addTimeout: 30,
},
},
repairable: false,
},
};
return (
<SystemPage
title="SDS: Filecoin Settings"
description="..."
url="https://slate.host/experiences/filecoin-settings"
>
<System.H1>
Filecoin Settings{" "}
<ViewSourceLink file="experiences/filecoin-settings.js" />
</System.H1>
<br />
<br />
<System.P>
Here is an example of an experience for getting and setting Filecoin
Settings from{" "}
<a target="_blank" href="https://github.com/textileio/powergate/">
Textile's Powergate
</a>
.
</System.P>
<br />
<br />
<System.FilecoinSettings
addrsList={addrsList}
defaultStorageConfig={defaultStorageConfig}
onSave={this._handleSave}
/>
<br />
<br />
<br />
<System.H2>Code</System.H2>
<hr />
<br />
<CodeBlock>{EXAMPLE_CODE}</CodeBlock>
</SystemPage>
);
}
}

View File

@ -16,7 +16,7 @@ class Example extends React.Component {
token: null
}
_handleCreateToken = () => {
_handleCreateToken = async () => {
const PowerGate = createPow({ host: "http://pow.slate.textile.io:6002" });
const FFS = await PowerGate.ffs.create();
const token = FFS.token ? FFS.token : null;

View File

@ -139,6 +139,7 @@ class ExampleTwo extends React.Component {
value={this.state.eight}
onChange={this._handleChange}
/>
<br />
<System.TabGroup
name="nine"
options={TAB_GROUP_THREE}

View File

@ -51,7 +51,7 @@ export default class SceneSettings extends React.Component {
},
config: {
hot: {
enabled: this.state.settings_cold_enabled,
enabled: this.state.settings_hot_enabled,
allowUnfreeze: this.state.settings_hot_allow_unfreeze,
ipfs: {
addTimeout: this.state.settings_hot_ipfs_add_timeout,