mirror of
https://github.com/filecoin-project/slate.git
synced 2024-12-27 02:52:25 +03:00
Merge pull request #239 from filecoin-project/@textile/staging
@textile/staging - The Filecoin Branch
This commit is contained in:
commit
d30d1be565
@ -33,15 +33,7 @@ const constructFilesTreeForNavigation = (library) => {
|
||||
return {
|
||||
...library[0],
|
||||
name: `Data`,
|
||||
children: [
|
||||
{
|
||||
id: "V1_NAVIGATION_ARCHIVE",
|
||||
decorator: "ARCHIVE",
|
||||
name: "Archive",
|
||||
pageTitle: "Archive on Filecoin",
|
||||
children: null,
|
||||
},
|
||||
],
|
||||
children: [],
|
||||
};
|
||||
};
|
||||
|
||||
@ -126,14 +118,35 @@ export const generate = ({ library = [], slates = [] }) => [
|
||||
decorator: "SETTINGS_DEVELOPER",
|
||||
name: "API",
|
||||
pageTitle: "Developer API",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: "V1_NAVIGATION_ARCHIVE",
|
||||
decorator: "FILECOIN",
|
||||
name: "Filecoin Testnet",
|
||||
pageTitle: "Archive on Filecoin",
|
||||
children: [
|
||||
{
|
||||
id: "V1_NAVIGATION_NETWORK",
|
||||
decorator: "NETWORK",
|
||||
name: "Filecoin Network",
|
||||
name: "Network API",
|
||||
pageTitle: "The Filecoin Network",
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: "V1_NAVIGATION_WALLET",
|
||||
decorator: "WALLET",
|
||||
name: "Wallet",
|
||||
pageTitle: "Your wallet and addresses",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: "V1_NAVIGATION_FILECOIN_SETTINGS",
|
||||
decorator: "SETTINGS",
|
||||
name: "Deal settings",
|
||||
pageTitle: "Deal Settings.",
|
||||
children: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -68,8 +68,13 @@ export const getCIDFromIPFS = (url) => {
|
||||
return cid;
|
||||
};
|
||||
|
||||
export const formatAsFilecoinConversion = (number) => {
|
||||
number = number / Math.pow(10, 18);
|
||||
return `${formatAsFilecoin(number)}`;
|
||||
};
|
||||
|
||||
export const formatAsFilecoin = (number) => {
|
||||
return `${number} FIL`;
|
||||
return `${formatNumber(number)} FIL`;
|
||||
};
|
||||
|
||||
export const pluralize = (text, count) => {
|
||||
@ -200,7 +205,10 @@ export const createSlug = (text, base = "untitled") => {
|
||||
return base;
|
||||
}
|
||||
|
||||
text = text.toString().toLowerCase().trim();
|
||||
text = text
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.trim();
|
||||
|
||||
const sets = [
|
||||
{ to: "a", from: "[ÀÁÂÃÅÆĀĂĄẠẢẤẦẨẪẬẮẰẲẴẶ]" },
|
||||
|
@ -285,37 +285,32 @@ export const ExpandArrow = (props) => (
|
||||
</svg>
|
||||
);
|
||||
|
||||
// export const Wallet = (props) => (
|
||||
// <svg
|
||||
// viewBox="0 0 24 24"
|
||||
// xmlns="http://www.w3.org/2000/svg"
|
||||
// height={props.height}
|
||||
// style={props.style}
|
||||
// >
|
||||
// <g
|
||||
// fill="none"
|
||||
// stroke="currentColor"
|
||||
// strokeLinecap="round"
|
||||
// strokeLinejoin="round"
|
||||
// >
|
||||
// <rect height="21" rx="1.5" width="22.5" x=".75" y=".75" />
|
||||
// <rect height="15" rx="1.5" width="15" x="5.25" y="3.75" />
|
||||
// <path d="m3.75 21.75v1.5" />
|
||||
// <path d="m20.25 21.75v1.5" />
|
||||
// <path d="m3.75 8.25h3" />
|
||||
// <path d="m3.75 14.25h3" />
|
||||
// <circle cx="12.75" cy="11.25" r="2.7" />
|
||||
// <path d="m12.75 8.55v-1.8" />
|
||||
// <path d="m12.75 15.75v-1.8" />
|
||||
// <path d="m15.45 11.25h1.8" />
|
||||
// <path d="m8.25 11.25h1.8" />
|
||||
// <path d="m9.986 14.432 1.075-1.075" />
|
||||
// <path d="m9.986 8.068 1.075 1.075" />
|
||||
// <path d="m15.514 14.432-1.075-1.075" />
|
||||
// <path d="m15.514 8.068-1.075 1.075" />
|
||||
// </g>
|
||||
// </svg>
|
||||
// );
|
||||
export const OldWallet = (props) => (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height={props.height}
|
||||
style={props.style}
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.5"
|
||||
>
|
||||
<path d="m22.5 20.25v1.227l.00000001.00000013c.0413973.931766-.675662 1.7229-1.607 1.773h-8.036-.00000003c-.931338-.0501042-1.6484-.841234-1.607-1.773v-6.2l-.00000001-.00000016c-.0436626-.933293.674031-1.72691 1.607-1.777h8.036-.00000007c.931338.0501042 1.6484.841234 1.607 1.773v.977" />
|
||||
<path d="m21.04 13.507-1.282-2.565.00000003.00000007c-.19778-.395089-.678395-.555039-1.07348-.357259-.00017232.00008626-.00034461.00017259-.00051687.00025898l-5.827 2.915" />
|
||||
<path d="m22.45 20.25h-.00000003c.441828.00000002.8-.358172.8-.8v-2.4c0-.441828-.358172-.8-.8-.8h-2-.00000009c-1.10457.00000005-2 .895431-2 2 .00000005 1.10457.895431 2 2 2z" />
|
||||
<path d="m5.737 21.347-.00000013-.00000008c-5.16103-3.46042-6.53965-10.4495-3.07924-15.6105 3.46042-5.16103 10.4495-6.53965 15.6105-3.07924 2.14401 1.43754 3.7285 3.5686 4.48772 6.03575.0486667.161333.0943333.322333.137.483" />
|
||||
<path d="m7.981 19.4-.00000183-.00000581c-1.20611-3.82323-1.51577-7.87283-.904998-11.835l-.00000004.00000035c.268804-2.2946 1.0232-4.50564 2.213-6.486" />
|
||||
<path d="m.77 11.25h10.48" />
|
||||
<path d="m2.48 6h19.04" />
|
||||
<path d="m1.69 16.5h5.55" />
|
||||
<path d="m14.711 1.079.00000073.00000119c1.00053 1.62525 1.6795 3.42757 2 5.309.118.58.213333 1.159.286 1.737" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const NavigationArrow = (props) => (
|
||||
<svg viewBox="0 0 24 24" height={props.height} style={props.style}>
|
||||
|
@ -87,7 +87,7 @@ const SCENES = {
|
||||
LOCAL_DATA: <SceneLocalData />,
|
||||
NETWORK: <SceneSentinel />,
|
||||
DIRECTORY: <SceneDirectory />,
|
||||
ARCHIVE: <SceneArchive />,
|
||||
FILECOIN: <SceneArchive />,
|
||||
};
|
||||
|
||||
export default class ApplicationPage extends React.Component {
|
||||
|
@ -12,15 +12,16 @@ const IconMap = {
|
||||
NETWORK: <SVG.Activity height="20px" />,
|
||||
DIRECTORY: <SVG.Directory height="20px" />,
|
||||
FOLDER: <SVG.Folder height="20px" />,
|
||||
WALLET: <SVG.Wallet height="20px" />,
|
||||
WALLET: <SVG.OldWallet height="20px" />,
|
||||
DEALS: <SVG.Deals height="20px" />,
|
||||
SLATES: <SVG.Layers height="20px" />,
|
||||
SLATE: <SVG.Slate height="20px" />,
|
||||
LOCAL_DATA: <SVG.HardDrive height="20px" />,
|
||||
PROFILE_PAGE: <SVG.ProfileUser height="20px" />,
|
||||
SETTINGS_DEVELOPER: <SVG.Tool height="20px" />,
|
||||
SETTINGS: <SVG.Layers height="20px" />,
|
||||
DIRECTORY: <SVG.Directory height="20px" />,
|
||||
ARCHIVE: <SVG.Layers height="20px" />,
|
||||
FILECOIN: <SVG.Wallet height="20px" />,
|
||||
};
|
||||
|
||||
const STYLES_NAVIGATION = css`
|
||||
|
@ -5,8 +5,7 @@ import * as System from "~/components/system";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
// NOTE(jim): 10 GB
|
||||
const MAX_IN_BYTES = 10737418240;
|
||||
const MAX_IN_BYTES = 10737418240 * 50;
|
||||
|
||||
const STYLES_CONTAINER = css`
|
||||
border-radius: 4px;
|
||||
|
@ -11,6 +11,7 @@ const STYLES_SECTION = css`
|
||||
border-radius: 4px;
|
||||
font-weight: 400;
|
||||
margin-top: 24px;
|
||||
white-space: pre-wrap;
|
||||
|
||||
:first-child {
|
||||
margin-top: 0px;
|
||||
|
@ -9,17 +9,14 @@ import { css } from "@emotion/react";
|
||||
import { dispatchCustomEvent } from "~/common/custom-events";
|
||||
|
||||
export default class SidebarFilecoinArchive extends React.Component {
|
||||
state = { response: "" };
|
||||
|
||||
async componentDidMount() {}
|
||||
|
||||
_handleMakeDeal = async () => {
|
||||
const response = await Actions.archive();
|
||||
console.log(response);
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: { message: "Deal archiving is still under development" },
|
||||
},
|
||||
});
|
||||
|
||||
this.setState({ response });
|
||||
};
|
||||
|
||||
_handleSubmit = async (e) => {
|
||||
@ -29,7 +26,8 @@ export default class SidebarFilecoinArchive extends React.Component {
|
||||
|
||||
this.props.onSidebarLoading(true);
|
||||
await this._handleMakeDeal();
|
||||
await this.props.onSubmit({});
|
||||
await this.props.onRehydrate();
|
||||
this.props.onSidebarLoading(false);
|
||||
};
|
||||
|
||||
_handleCancel = () => {
|
||||
@ -41,8 +39,6 @@ export default class SidebarFilecoinArchive extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
console.log(this.props);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<System.P
|
||||
@ -66,6 +62,10 @@ export default class SidebarFilecoinArchive extends React.Component {
|
||||
>
|
||||
Make storage deal
|
||||
</System.ButtonPrimary>
|
||||
|
||||
<div style={{ whiteSpace: "pre-wrap", marginTop: 48 }}>
|
||||
{JSON.stringify(this.state.response, null, 2)}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -6,4 +6,4 @@ export const GITHUB_URL = "https://github.com/filecoin-project/slate";
|
||||
export const ANALYTICS_URL = "https://slate-stats-dev.azurewebsites.net/";
|
||||
|
||||
// NOTE(jim): 1 GB from Ignacio
|
||||
export const TEXTILE_ACCOUNT_BYTE_LIMIT = 1073741824;
|
||||
export const TEXTILE_ACCOUNT_BYTE_LIMIT = 1073741824 * 50;
|
||||
|
@ -1,3 +1,9 @@
|
||||
// TODO(jim): The claim is that we can remove this
|
||||
// and the package.json depdencies at some later time.
|
||||
import { grpc } from "@improbable-eng/grpc-web";
|
||||
import { WebsocketTransport } from "@textile/grpc-transport";
|
||||
grpc.setDefaultTransport(WebsocketTransport());
|
||||
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Constants from "~/node_common/constants";
|
||||
@ -73,11 +79,6 @@ export const getById = async ({ id }) => {
|
||||
return {
|
||||
...Serializers.user(user),
|
||||
type: "VIEWER",
|
||||
|
||||
// NOTE(jim): The only safe viewer fields to expose.
|
||||
settings: {
|
||||
deals_auto_approve: user.data.settings_deals_auto_approve,
|
||||
},
|
||||
library: user.data.library,
|
||||
|
||||
// NOTE(jim): Remaining data.
|
||||
@ -94,3 +95,77 @@ export const getById = async ({ id }) => {
|
||||
pendingTrusted: r4.serializedPendingTrusted,
|
||||
};
|
||||
};
|
||||
|
||||
export const getTextileById = async ({ id }) => {
|
||||
const user = await Data.getUserById({
|
||||
id,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (user.error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let info = {};
|
||||
let status = {};
|
||||
let errors = [];
|
||||
let jobs = [];
|
||||
|
||||
const {
|
||||
buckets,
|
||||
bucketKey,
|
||||
bucketName,
|
||||
bucketRoot,
|
||||
} = await Utilities.getBucketAPIFromUserToken(user.data.tokens.api);
|
||||
|
||||
const {
|
||||
power,
|
||||
powerInfo,
|
||||
powerHealth,
|
||||
} = await Utilities.getPowergateAPIFromUserToken(user.data.tokens.api);
|
||||
|
||||
try {
|
||||
buckets.archiveWatch(bucketRoot.root.key, (job) => {
|
||||
if (!job) {
|
||||
return;
|
||||
}
|
||||
|
||||
job.id = job.id ? job.id : "UNDEFINED";
|
||||
jobs.push(job);
|
||||
});
|
||||
} catch (e) {
|
||||
errors.push({ decorator: "JOB", message: e.message, code: e.code });
|
||||
}
|
||||
|
||||
try {
|
||||
info = await buckets.archiveInfo(bucketRoot.root.key);
|
||||
} catch (e) {
|
||||
errors.push({ decorator: "INFO", message: e.message, code: e.code });
|
||||
}
|
||||
|
||||
try {
|
||||
status = await buckets.archiveStatus(bucketRoot.root.key);
|
||||
} catch (e) {
|
||||
errors.push({ decorator: "STATUS", message: e.message, code: e.code });
|
||||
}
|
||||
|
||||
console.log(jobs);
|
||||
|
||||
return {
|
||||
type: "VIEWER_FILECOIN",
|
||||
settings: {
|
||||
deals_auto_approve: user.data.settings_deals_auto_approve,
|
||||
},
|
||||
powerInfo,
|
||||
powerHealth,
|
||||
archive: {
|
||||
info,
|
||||
status,
|
||||
errors,
|
||||
jobs,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ import * as Constants from "~/node_common/constants";
|
||||
import JWT from "jsonwebtoken";
|
||||
import BCrypt from "bcrypt";
|
||||
|
||||
import { Buckets, PrivateKey } from "@textile/hub";
|
||||
import { Buckets, PrivateKey, Pow } from "@textile/hub";
|
||||
|
||||
const BUCKET_NAME = "data";
|
||||
|
||||
@ -79,6 +79,21 @@ export const parseAuthHeader = (value) => {
|
||||
return matches && { scheme: matches[1], value: matches[2] };
|
||||
};
|
||||
|
||||
// NOTE(jim): Requires @textile/hub
|
||||
export const getPowergateAPIFromUserToken = async (token) => {
|
||||
const identity = await PrivateKey.fromString(token);
|
||||
const power = await Pow.withKeyInfo(TEXTILE_KEY_INFO);
|
||||
await power.getToken(identity);
|
||||
const { info } = await power.info();
|
||||
const health = await power.health();
|
||||
|
||||
return {
|
||||
power,
|
||||
powerHealth: health,
|
||||
powerInfo: info,
|
||||
};
|
||||
};
|
||||
|
||||
// NOTE(jim): Requires @textile/hub
|
||||
export const getBucketAPIFromUserToken = async (token) => {
|
||||
const identity = await PrivateKey.fromString(token);
|
||||
|
@ -43,9 +43,11 @@
|
||||
"@emotion/css": "11.0.0-next.12",
|
||||
"@emotion/react": "11.0.0-next.12",
|
||||
"@emotion/server": "11.0.0-next.12",
|
||||
"@improbable-eng/grpc-web": "^0.13.0",
|
||||
"@react-hook/window-size": "^3.0.7",
|
||||
"@slack/webhook": "^5.0.3",
|
||||
"@textile/hub": "^0.6.2",
|
||||
"@textile/grpc-transport": "0.0.3",
|
||||
"@textile/hub": "^0.7.1",
|
||||
"babel-plugin-module-resolver": "^4.0.0",
|
||||
"bcrypt": "^5.0.0",
|
||||
"body-parser": "^1.19.0",
|
||||
|
@ -34,16 +34,20 @@ export default async (req, res) => {
|
||||
bucketRoot,
|
||||
} = await Utilities.getBucketAPIFromUserToken(user.data.tokens.api);
|
||||
|
||||
console.log(bucketRoot.root);
|
||||
|
||||
// bucketRoot.root.key
|
||||
// bucketRoot.root.path
|
||||
|
||||
const response = await buckets.archive(bucketRoot.root.key);
|
||||
console.log(response);
|
||||
let response = {};
|
||||
let error = {};
|
||||
try {
|
||||
response = await buckets.archive(bucketRoot.root.key);
|
||||
} catch (e) {
|
||||
error.message = e.message;
|
||||
error.code = e.code;
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_BUCKET_ARCHIVE_DEAL",
|
||||
data: {},
|
||||
data: { response, error },
|
||||
});
|
||||
};
|
||||
|
27
pages/api/network.js
Normal file
27
pages/api/network.js
Normal file
@ -0,0 +1,27 @@
|
||||
import * as ViewerManager from "~/node_common/managers/viewer";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
if (!id) {
|
||||
return res
|
||||
.status(500)
|
||||
.send({ decorator: "SERVER_FILECOIN_NETWORK_FAILURE", error: true });
|
||||
}
|
||||
|
||||
const data = await ViewerManager.getTextileById({ id });
|
||||
|
||||
if (!data) {
|
||||
return res
|
||||
.status(500)
|
||||
.send({
|
||||
decorator: "SERVER_FILECOIN_NETWORK_ERROR",
|
||||
error: true,
|
||||
data: null,
|
||||
});
|
||||
}
|
||||
|
||||
return res
|
||||
.status(200)
|
||||
.send({ decorator: "SERVER_FILECOIN_NETWORK", success: true, data });
|
||||
};
|
@ -49,8 +49,26 @@ export default async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(jim): Do not expose how many times you are salting
|
||||
// in OSS, add a random value as an environment variable.
|
||||
// TODO(jim): POWERGATE
|
||||
// Doesn't actually work yet.
|
||||
if (req.body.type === "SET_DEFAULT_STORAGE_CONFIG") {
|
||||
const {
|
||||
power,
|
||||
powerInfo,
|
||||
powerHealth,
|
||||
} = await Utilities.getPowergateAPIFromUserToken(user.data.tokens.api);
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await power.ffs.setDefaultStorageConfig(req.body.config);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return res
|
||||
.status(500)
|
||||
.send({ decorator: "SERVER_USER_UPDATE_SETTINGS_CONFIG", error: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (req.body.type == "CHANGE_PASSWORD") {
|
||||
if (!Validations.password(req.body.password)) {
|
||||
return res
|
||||
|
@ -1,46 +1,127 @@
|
||||
import * as React from "react";
|
||||
import * as System from "~/components/system";
|
||||
import * as Constants from "~/common/constants";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
|
||||
import Section from "~/components/core/Section";
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
import ScenePageHeader from "~/components/core/ScenePageHeader";
|
||||
|
||||
const STYLES_LABEL = css`
|
||||
font-family: ${Constants.font.semiBold};
|
||||
font-size: 16px;
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
|
||||
export default class SceneArchive extends React.Component {
|
||||
state = {};
|
||||
|
||||
async componentDidMount() {
|
||||
let networkViewer;
|
||||
try {
|
||||
const response = await fetch("/api/network");
|
||||
const json = await response.json();
|
||||
networkViewer = json.data;
|
||||
} catch (e) {}
|
||||
|
||||
this.setState({
|
||||
networkViewer,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ScenePage>
|
||||
<ScenePageHeader title="Archive">
|
||||
Slate provides a way to archive your data onto the Filecoin network.
|
||||
Once your archive is sealed on the Filecoin network, it will be shown
|
||||
here.
|
||||
<ScenePageHeader title="Filecoin Testnet">
|
||||
Filecoin is currently in Testnet phase. You can use this tab to test
|
||||
and verify Filecoin deals with Testnet FIL.
|
||||
</ScenePageHeader>
|
||||
|
||||
<Section
|
||||
title="Archives"
|
||||
onAction={this.props.onAction}
|
||||
buttons={[
|
||||
{
|
||||
name: "Archive",
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_FILECOIN_ARCHIVE",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<System.Table
|
||||
data={{
|
||||
columns: [
|
||||
{this.state.networkViewer ? (
|
||||
<React.Fragment>
|
||||
<Section
|
||||
title="Filecoin Testnet trusted miners"
|
||||
style={{ marginTop: 48 }}
|
||||
>
|
||||
<System.Table
|
||||
data={{
|
||||
columns: [
|
||||
{
|
||||
key: "miner",
|
||||
name: "Miner ID",
|
||||
width: "100%",
|
||||
},
|
||||
],
|
||||
rows: this.state.networkViewer.powerInfo.defaultStorageConfig.cold.filecoin.trustedMinersList.map(
|
||||
(miner) => {
|
||||
return {
|
||||
miner,
|
||||
};
|
||||
}
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Filecoin Testnet archive deals"
|
||||
onAction={this.props.onAction}
|
||||
buttons={[
|
||||
{
|
||||
key: "job",
|
||||
name: "Job ID",
|
||||
width: "100%",
|
||||
name: "Make Filecoin Testnet deal",
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_FILECOIN_ARCHIVE",
|
||||
},
|
||||
],
|
||||
rows: [],
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
]}
|
||||
>
|
||||
<div style={{ padding: 24 }}>
|
||||
<div css={STYLES_LABEL}>Info</div>
|
||||
{JSON.stringify(this.state.networkViewer.archive.info, null, 2)}
|
||||
|
||||
<div css={STYLES_LABEL} style={{ marginTop: 24 }}>
|
||||
Status
|
||||
</div>
|
||||
{JSON.stringify(
|
||||
this.state.networkViewer.archive.status,
|
||||
null,
|
||||
2
|
||||
)}
|
||||
|
||||
<div css={STYLES_LABEL} style={{ marginTop: 24 }}>
|
||||
Errors
|
||||
</div>
|
||||
{JSON.stringify(
|
||||
this.state.networkViewer.archive.errors,
|
||||
null,
|
||||
2
|
||||
)}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section title="Job history" style={{ marginTop: 48 }}>
|
||||
<System.Table
|
||||
data={{
|
||||
columns: [
|
||||
{
|
||||
key: "job",
|
||||
name: "Job Message",
|
||||
width: "100%",
|
||||
},
|
||||
],
|
||||
rows: this.state.networkViewer.archive.jobs.map((job) => {
|
||||
return {
|
||||
job: job.msg,
|
||||
};
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<LoaderSpinner style={{ marginTop: 48, height: 32, width: 32 }} />
|
||||
)}
|
||||
</ScenePage>
|
||||
);
|
||||
}
|
||||
|
@ -26,12 +26,13 @@ export default class SceneSentinel extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ScenePage>
|
||||
<ScenePageHeader title="Filecoin Network">
|
||||
Slate provides access to live data on the Filecoin Network through
|
||||
Sentinel. Each of these API endpoints can be used programatically.
|
||||
<ScenePageHeader title="Filecoin Testnet API">
|
||||
Slate provides access to live data on the Filecoin Testnet Network
|
||||
through Sentinel. Each of these API endpoints can be used
|
||||
programatically.
|
||||
</ScenePageHeader>
|
||||
|
||||
<Section title="Filecoin network API routes">
|
||||
<Section title="Filecoin Testnet API routes">
|
||||
<System.Table
|
||||
data={{
|
||||
columns: [
|
||||
|
@ -3,8 +3,11 @@ import * as Actions from "~/common/actions";
|
||||
import * as System from "~/components/system";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
|
||||
import Section from "~/components/core/Section";
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
import ScenePageHeader from "~/components/core/ScenePageHeader";
|
||||
|
||||
const STYLES_GROUP = css`
|
||||
display: flex;
|
||||
@ -36,19 +39,52 @@ const STYLES_RIGHT = css`
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
const createState = (config) => {
|
||||
return {
|
||||
settings_hot_enabled: config.hot.enabled,
|
||||
settings_hot_allow_unfreeze: config.hot.allowUnfreeze,
|
||||
settings_hot_ipfs_add_timeout: config.hot.ipfs.addTimeout,
|
||||
settings_cold_enabled: config.cold.enabled,
|
||||
settings_cold_default_address: config.cold.filecoin.addr,
|
||||
settings_cold_default_duration: config.cold.filecoin.dealMinDuration,
|
||||
settings_cold_default_replication_factor: config.cold.filecoin.repFactor,
|
||||
settings_cold_default_excluded_miners:
|
||||
config.cold.filecoin.excludedMinersList,
|
||||
settings_cold_default_trusted_miners:
|
||||
config.cold.filecoin.trustedMinersList,
|
||||
settings_cold_default_max_price: config.cold.filecoin.maxPrice,
|
||||
settings_cold_default_auto_renew: config.cold.filecoin.renew.enabled,
|
||||
settings_cold_default_auto_renew_max_price:
|
||||
config.cold.filecoin.renew.threshold,
|
||||
};
|
||||
};
|
||||
|
||||
export default class SceneSettings extends React.Component {
|
||||
state = { ...this.props.viewer };
|
||||
state = {};
|
||||
|
||||
async componentDidMount() {
|
||||
let networkViewer;
|
||||
try {
|
||||
const response = await fetch("/api/network");
|
||||
const json = await response.json();
|
||||
networkViewer = json.data;
|
||||
} catch (e) {}
|
||||
|
||||
this.setState({
|
||||
networkViewer,
|
||||
...createState(networkViewer.powerInfo.defaultStorageConfig),
|
||||
});
|
||||
}
|
||||
|
||||
_deferredSave = null;
|
||||
|
||||
_handleSave = async () => {
|
||||
this.setState({ loading: true });
|
||||
|
||||
return alert(`Changing settings is currently disabled.`);
|
||||
|
||||
await Actions.updateViewer({
|
||||
type: "SET_DEFAULT_STORAGE_CONFIG",
|
||||
data: {
|
||||
settings_deals_auto_approve: this.state.settings_deals_auto_approve,
|
||||
},
|
||||
config: {
|
||||
hot: {
|
||||
enabled: this.state.settings_hot_enabled,
|
||||
@ -88,179 +124,175 @@ export default class SceneSettings extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ScenePage>
|
||||
<System.H1>Filecoin settings</System.H1>
|
||||
<ScenePageHeader title="Filecoin Testnet settings (read-only)">
|
||||
Filecoin is currently in Testnet phase. You can use this page to view
|
||||
the default settings. Once your deal settings can be configured, you
|
||||
can edit your settings whenever you like.
|
||||
</ScenePageHeader>
|
||||
|
||||
<System.H2 style={{ marginTop: 48 }}>Storage defaults</System.H2>
|
||||
{this.state.networkViewer ? (
|
||||
<React.Fragment>
|
||||
<Section title="Trusted miners" style={{ marginTop: 48 }}>
|
||||
<System.Table
|
||||
data={{
|
||||
columns: [
|
||||
{
|
||||
key: "miner",
|
||||
name: "Miner ID",
|
||||
width: "100%",
|
||||
},
|
||||
],
|
||||
rows: this.state.settings_cold_default_trusted_miners.map(
|
||||
(miner) => {
|
||||
return {
|
||||
miner,
|
||||
};
|
||||
}
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<div css={STYLES_GROUP} style={{ marginTop: 32 }}>
|
||||
<div css={STYLES_LEFT}>
|
||||
<System.DescriptionGroup
|
||||
label="Automatically approve deals"
|
||||
tooltip="When this is enabled you will skip the confirmation step, but if you do not have enough Filecoin you will receive a warning."
|
||||
description="Enable this if every storage deal should be automatically approved to skip confirmation."
|
||||
/>
|
||||
</div>
|
||||
<div css={STYLES_RIGHT}>
|
||||
<System.Toggle
|
||||
name="settings_deals_auto_approve"
|
||||
onChange={this._handleChange}
|
||||
active={this.state.settings_deals_auto_approve}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div css={STYLES_GROUP} style={{ marginTop: 48 }}>
|
||||
<div css={STYLES_LEFT}>
|
||||
<System.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}>
|
||||
<System.Toggle
|
||||
name="settings_cold_enabled"
|
||||
onChange={this._handleChange}
|
||||
active={this.state.settings_cold_enabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<System.ButtonPrimary
|
||||
loading={this.state.loading}
|
||||
onClick={this._handleSave}
|
||||
>
|
||||
Save
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
{this.state.settings_cold_enabled ? (
|
||||
<div css={STYLES_SUBGROUP}>
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Default Filecoin address (Read only)"
|
||||
description="Default Filecoin address."
|
||||
name="settings_cold_default_duration"
|
||||
readOnly
|
||||
type="text"
|
||||
value={this.state.settings_cold_default_address}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
|
||||
<div css={STYLES_GROUP} style={{ marginTop: 48 }}>
|
||||
<div css={STYLES_LEFT}>
|
||||
<System.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}>
|
||||
<System.Toggle
|
||||
name="settings_cold_enabled"
|
||||
onChange={this._handleChange}
|
||||
active={this.state.settings_cold_enabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Default Filecoin deal duration"
|
||||
description="Current deal duration is in epochs but should change to months/weeks/days."
|
||||
name="settings_cold_default_duration"
|
||||
type="number"
|
||||
value={this.state.settings_cold_default_duration}
|
||||
placeholder="Type in epochs (~25 seconds)"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
|
||||
{this.state.settings_cold_enabled ? (
|
||||
<div css={STYLES_SUBGROUP}>
|
||||
<System.SelectMenu
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Default Filecoin address"
|
||||
description="Default Filecoin address settings description."
|
||||
tooltip="Placeholder."
|
||||
name="settings_cold_default_address"
|
||||
value={this.state.settings_cold_default_address}
|
||||
category="address"
|
||||
onChange={this._handleChange}
|
||||
options={this.state.addresses}
|
||||
/>
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Default Filecoin replication factor"
|
||||
description=""
|
||||
name="settings_cold_default_replication_factor"
|
||||
value={this.state.settings_cold_default_replication_factor}
|
||||
placeholder="Type in amount of miners"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Default Filecoin deal duration"
|
||||
description="Default Filecoin deal duration settings description. Current deal duration is in epochs but should change to months/weeks/days."
|
||||
tooltip="Placeholder."
|
||||
name="settings_cold_default_duration"
|
||||
type="number"
|
||||
value={this.state.settings_cold_default_duration}
|
||||
placeholder="Type in epochs (~25 seconds)"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Max Filecoin price."
|
||||
description="Set the maximum Filecoin price you're willing to pay."
|
||||
name="settings_cold_default_max_price"
|
||||
value={this.state.settings_cold_default_max_price}
|
||||
placeholder="Type in amount of Filecoin"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Default Filecoin replication factor"
|
||||
description="Default Filecoin replication factor settings description."
|
||||
tooltip="Placeholder."
|
||||
name="settings_cold_default_replication_factor"
|
||||
value={this.state.settings_cold_default_replication_factor}
|
||||
placeholder="Type in amount of miners"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
<System.CheckBox
|
||||
style={{ marginTop: 48 }}
|
||||
name="settings_cold_default_auto_renew"
|
||||
value={this.state.settings_cold_default_auto_renew}
|
||||
onChange={this._handleChange}
|
||||
>
|
||||
Enable auto renew for Filecoin Network deals.
|
||||
</System.CheckBox>
|
||||
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Max Filecoin price."
|
||||
description="Set the maximum Filecoin price you're willing to pay."
|
||||
tooltip="Placeholder."
|
||||
name="settings_cold_default_max_price"
|
||||
value={this.state.settings_cold_default_max_price}
|
||||
placeholder="Type in amount of Filecoin"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Max Filecoin deal auto renew price."
|
||||
description="Set the maximum Filecoin price you're willing to pay for auto renew."
|
||||
name="settings_cold_default_auto_renew_max_price"
|
||||
value={this.state.settings_cold_default_auto_renew_max_price}
|
||||
placeholder="Type in amount of Filecoin"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<System.ButtonPrimary
|
||||
loading={this.state.loading}
|
||||
onClick={this._handleSave}
|
||||
>
|
||||
Save
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
|
||||
<System.CheckBox
|
||||
style={{ marginTop: 48 }}
|
||||
name="settings_cold_default_auto_renew"
|
||||
value={this.state.settings_cold_default_auto_renew}
|
||||
onChange={this._handleChange}
|
||||
>
|
||||
Enable auto renew for Filecoin Network deals.
|
||||
</System.CheckBox>
|
||||
<div css={STYLES_GROUP} style={{ marginTop: 32 }}>
|
||||
<div css={STYLES_LEFT}>
|
||||
<System.DescriptionGroup
|
||||
label="Enable hot storage"
|
||||
description="By enabling hot storage, every time you make a deal your data will be stored on IPFS."
|
||||
/>
|
||||
</div>
|
||||
<div css={STYLES_RIGHT}>
|
||||
<System.Toggle
|
||||
name="settings_hot_enabled"
|
||||
onChange={this._handleChange}
|
||||
active={this.state.settings_hot_enabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Max Filecoin deal auto renew price."
|
||||
description="Set the maximum Filecoin price you're willing to pay for auto renew."
|
||||
tooltip="Placeholder."
|
||||
name="settings_cold_default_auto_renew_max_price"
|
||||
value={this.state.settings_cold_default_auto_renew_max_price}
|
||||
placeholder="Type in amount of Filecoin"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<System.ButtonPrimary
|
||||
loading={this.state.loading}
|
||||
onClick={this._handleSave}
|
||||
>
|
||||
Save
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
{this.state.settings_hot_enabled ? (
|
||||
<div css={STYLES_SUBGROUP}>
|
||||
<System.CheckBox
|
||||
style={{ marginTop: 48 }}
|
||||
name="settings_hot_allow_unfreeze"
|
||||
value={this.state.settings_hot_allow_unfreeze}
|
||||
onChange={this._handleChange}
|
||||
>
|
||||
IPFS allow unfreeze setting description.
|
||||
</System.CheckBox>
|
||||
|
||||
<div css={STYLES_GROUP} style={{ marginTop: 32 }}>
|
||||
<div css={STYLES_LEFT}>
|
||||
<System.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}>
|
||||
<System.Toggle
|
||||
name="settings_hot_enabled"
|
||||
onChange={this._handleChange}
|
||||
active={this.state.settings_hot_enabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{this.state.settings_hot_enabled ? (
|
||||
<div css={STYLES_SUBGROUP}>
|
||||
<System.CheckBox
|
||||
style={{ marginTop: 48 }}
|
||||
name="settings_hot_allow_unfreeze"
|
||||
value={this.state.settings_hot_allow_unfreeze}
|
||||
onChange={this._handleChange}
|
||||
>
|
||||
IPFS allow unfreeze setting description.
|
||||
</System.CheckBox>
|
||||
|
||||
<System.Input
|
||||
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 style={{ marginTop: 32 }}>
|
||||
<System.ButtonPrimary
|
||||
loading={this.state.loading}
|
||||
onClick={this._handleSave}
|
||||
>
|
||||
Save
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Add timeout"
|
||||
description="Add IPFS timeout setting description."
|
||||
name="settings_hot_ipfs_add_timeout"
|
||||
value={this.state.settings_hot_ipfs_add_timeout}
|
||||
placeholder="Type in seconds"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<System.ButtonPrimary
|
||||
loading={this.state.loading}
|
||||
onClick={this._handleSave}
|
||||
>
|
||||
Save
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<LoaderSpinner style={{ marginTop: 48, height: 32, width: 32 }} />
|
||||
)}
|
||||
</ScenePage>
|
||||
);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import * as System from "~/components/system";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { dispatchCustomEvent } from "~/common/custom-events";
|
||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||
|
||||
import Section from "~/components/core/Section";
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
@ -85,6 +86,22 @@ const STYLES_ITEM_GROUP = css`
|
||||
`;
|
||||
|
||||
export default class SceneWallet extends React.Component {
|
||||
state = {};
|
||||
|
||||
async componentDidMount() {
|
||||
let networkViewer;
|
||||
try {
|
||||
const response = await fetch("/api/network");
|
||||
const json = await response.json();
|
||||
networkViewer = json.data;
|
||||
} catch (e) {}
|
||||
console.log(networkViewer);
|
||||
|
||||
this.setState({
|
||||
networkViewer,
|
||||
});
|
||||
}
|
||||
|
||||
state = { table_transaction: null, visible: false };
|
||||
|
||||
_handleChange = (e) => {
|
||||
@ -109,133 +126,128 @@ export default class SceneWallet extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
let addresses = {};
|
||||
let lastAddress;
|
||||
// TODO(jim): Temporary because of read only Filecoin Addresses
|
||||
const { networkViewer } = this.state;
|
||||
|
||||
this.props.viewer.addresses.forEach((a) => {
|
||||
addresses[a.address] = a;
|
||||
lastAddress = a.address;
|
||||
});
|
||||
const addressMap = {};
|
||||
const addresses = [];
|
||||
let selected = null;
|
||||
|
||||
const currentAddress = this.props.selected.address
|
||||
? addresses[this.props.selected.address]
|
||||
: addresses[lastAddress];
|
||||
if (networkViewer) {
|
||||
networkViewer.powerInfo.balancesList.forEach((a) => {
|
||||
addressMap[a.addr.addr] = { ...a.addr, balance: a.balance };
|
||||
addresses.push({ ...a.addr, balance: a.balance });
|
||||
});
|
||||
|
||||
// TODO(jim):
|
||||
// Capture this state.
|
||||
if (!currentAddress) {
|
||||
return null;
|
||||
}
|
||||
if (addresses.length) {
|
||||
selected = addresses[0];
|
||||
}
|
||||
|
||||
let transactions = [];
|
||||
if (currentAddress.transactions) {
|
||||
transactions = [...currentAddress.transactions];
|
||||
let transactions = [];
|
||||
if (selected.transactions) {
|
||||
transactions = [...selected.transactions];
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<ScenePage>
|
||||
<ScenePageHeader title="Wallet [WIP]">
|
||||
This scene is currently a work in progress.
|
||||
<ScenePageHeader title="Filecoin Testnet wallet">
|
||||
This is your testnet wallet address. It is prefilled by Textile to
|
||||
help test the Filecoin Testnet.
|
||||
</ScenePageHeader>
|
||||
|
||||
<Section
|
||||
onAction={this.props.onAction}
|
||||
onNavigateTo={this.props.onNavigateTo}
|
||||
title="Addresses"
|
||||
style={{ maxWidth: `568px`, minWidth: "auto" }}
|
||||
buttons={[
|
||||
{networkViewer ? (
|
||||
<Section
|
||||
onAction={this.props.onAction}
|
||||
onNavigateTo={this.props.onNavigateTo}
|
||||
title="Your Filecoin Testnet address"
|
||||
style={{ maxWidth: `568px`, minWidth: "auto" }}
|
||||
buttons={
|
||||
[
|
||||
/*
|
||||
{
|
||||
name: "Create a new address",
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_CREATE_WALLET_ADDRESS",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<div css={STYLES_GROUP}>
|
||||
*/
|
||||
]
|
||||
}
|
||||
>
|
||||
{/* <div css={STYLES_GROUP}>
|
||||
<System.SelectMenu
|
||||
label="Select your address"
|
||||
name="address"
|
||||
value={this.props.selected.address}
|
||||
value={selected.addr}
|
||||
category="address"
|
||||
onChange={this._handleWalletChange}
|
||||
options={this.props.viewer.addresses}
|
||||
options={addresses}
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div css={STYLES_ROW} style={{ marginTop: 24 }}>
|
||||
<div css={STYLES_TEXT}>
|
||||
<div>
|
||||
<div css={STYLES_FOCUS}>
|
||||
{this.state.visible ? (
|
||||
currentAddress.address
|
||||
) : (
|
||||
<span css={STYLES_FOCUS_EMPAHSIS}>Hidden</span>
|
||||
)}
|
||||
</div>
|
||||
<div css={STYLES_SUBTEXT}>Filecoin address</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<div css={STYLES_FOCUS}>
|
||||
{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>
|
||||
|
||||
<div css={STYLES_ITEM_GROUP}>
|
||||
<div css={STYLES_ITEM}>
|
||||
<div css={STYLES_ROW} style={{ marginTop: 24 }}>
|
||||
<div css={STYLES_TEXT}>
|
||||
<div>
|
||||
<div css={STYLES_FOCUS}>
|
||||
{Strings.formatNumber(currentAddress.balance)}
|
||||
{this.state.visible ? (
|
||||
selected.addr
|
||||
) : (
|
||||
<span css={STYLES_FOCUS_EMPAHSIS}>Hidden</span>
|
||||
)}
|
||||
</div>
|
||||
<div css={STYLES_SUBTEXT}>Filecoin</div>
|
||||
<div css={STYLES_SUBTEXT}>Filecoin address</div>
|
||||
</div>
|
||||
|
||||
<div css={STYLES_ITEM}>
|
||||
<div css={STYLES_FOCUS}>{currentAddress.type}</div>
|
||||
<div css={STYLES_SUBTEXT}>Address type</div>
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<div css={STYLES_FOCUS}>
|
||||
{selected.name}{" "}
|
||||
{networkViewer.settings_cold_default_address ===
|
||||
selected.addr ? (
|
||||
<strong css={STYLES_FOCUS_EMPAHSIS}>(Primary)</strong>
|
||||
) : null}
|
||||
</div>
|
||||
<div css={STYLES_SUBTEXT}>Filecoin address alias</div>
|
||||
</div>
|
||||
|
||||
<div css={STYLES_ITEM_GROUP}>
|
||||
<div css={STYLES_ITEM}>
|
||||
<div css={STYLES_FOCUS}>
|
||||
{Strings.formatAsFilecoinConversion(selected.balance)}
|
||||
</div>
|
||||
<div css={STYLES_SUBTEXT}>Filecoin</div>
|
||||
</div>
|
||||
|
||||
<div css={STYLES_ITEM}>
|
||||
<div css={STYLES_FOCUS}>{selected.type}</div>
|
||||
<div css={STYLES_SUBTEXT}>Address type</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<System.ButtonPrimary
|
||||
onClick={() =>
|
||||
this.props.onAction({
|
||||
name: "Send Filecoin",
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_WALLET_SEND_FUNDS",
|
||||
})
|
||||
}
|
||||
<div css={STYLES_ACTIONS}>
|
||||
<span
|
||||
css={STYLES_CIRCLE_BUTTON}
|
||||
onClick={this._handleMakeAddressVisible}
|
||||
style={{
|
||||
marginRight: 16,
|
||||
backgroundColor: this.state.visible
|
||||
? null
|
||||
: Constants.system.brand,
|
||||
}}
|
||||
>
|
||||
Send Filecoin
|
||||
</System.ButtonPrimary>
|
||||
<SVG.Privacy height="16px" />
|
||||
</span>
|
||||
<span
|
||||
css={STYLES_CIRCLE_BUTTON}
|
||||
onClick={() => this._handleCopy(selected.address)}
|
||||
>
|
||||
<SVG.CopyAndPaste height="16px" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div css={STYLES_ACTIONS}>
|
||||
<span
|
||||
css={STYLES_CIRCLE_BUTTON}
|
||||
onClick={this._handleMakeAddressVisible}
|
||||
style={{
|
||||
marginRight: 16,
|
||||
backgroundColor: this.state.visible
|
||||
? null
|
||||
: Constants.system.brand,
|
||||
}}
|
||||
>
|
||||
<SVG.Privacy height="16px" />
|
||||
</span>
|
||||
<span
|
||||
css={STYLES_CIRCLE_BUTTON}
|
||||
onClick={() => this._handleCopy(currentAddress.address)}
|
||||
>
|
||||
<SVG.CopyAndPaste height="16px" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
</Section>
|
||||
) : (
|
||||
<LoaderSpinner style={{ marginTop: 48, height: 32, width: 32 }} />
|
||||
)}
|
||||
</ScenePage>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user