prototype: local file storage and local preview

This commit is contained in:
jimmylee 2020-06-09 11:00:36 -07:00
parent bf4761eb37
commit 3736ed162c
68 changed files with 744 additions and 1047 deletions

14
.gitignore vendored
View File

@ -1,10 +1,9 @@
.next .next
.data
.env .env
.DS_STORE .DS_STORE
DS_STORE
package-lock.json package-lock.json
node_modules node_modules
DS_STORE
/**/*/package-lock.json /**/*/package-lock.json
/**/*/.DS_STORE /**/*/.DS_STORE
@ -12,5 +11,12 @@ DS_STORE
/**/*/.next /**/*/.next
/**/*/.data /**/*/.data
.data/**/* .data/*
.library/**/* !.data/.gitkeep
public/static/files/*
!public/static/files/.gitkeep
public/static/system/*
!public/static/system/.gitkeep
!public/static/system/avatar.png

View File

@ -8,7 +8,9 @@ const REQUEST_HEADERS = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}; };
const SERVER_PATH = 'http://localhost:1337'; const dev = process.env.NODE_ENV !== 'production';
const SERVER_PATH = dev ? 'http://localhost:1337' : 'https://filecoin.onrender.com';
export const rehydrateViewer = async () => { export const rehydrateViewer = async () => {
const options = { const options = {

View File

@ -1,481 +1,17 @@
import * as Strings from '~/common/strings'; const constructFilesTreeForNavigation = (library) => {
import * as Data from '~/common/data'; for (let i = 0; i < library.length; i++) {
for (let j = 0; j < library[i].children.length; j++) {
const LOCAL_CONFIG = ` let e = library[i].children[j];
"Identity": { if (e.decorator === 'FILE') {
"PeerID": "Qma9T5YraSnpRDZqRR4krcS", library[i].children[j].ignore = true;
"PubKey": "CAAS4AQwggJcAgEAAoGBAMB" }
"PrivKey": "CAASogEwgZ8wDQYJKoZIhvd"
}
`;
export const getInitialState = () => {
return {
name: 'Andrew Hill',
photoURL: '/static/avatar-andrew-hill.jpg',
config: LOCAL_CONFIG,
upload_bandwidth: 40023,
download_bandwidth: 12323,
settings_deal_country: `3`,
settings_deals_auto_approve: true,
settings_deal_default_duration: 1,
settings_deal_replication_factor: 1,
settings_deal_maximum_storage_payment: 100,
settings_deal_default_miners: 't0123, t0124, t0125',
notifications: [
{
id: 6,
type: 'DEFAULT',
text: 'cats-are-cool.png was retrieved from the Filecoin network.',
createdAt: '2017-01-01 00:00:00 UTC',
},
{
id: 5,
type: 'DEFAULT',
text: 'cats-are-cool.png was transfered from Miner B over to your local storage.',
createdAt: '2016-12-28 00:00:00 UTC',
},
{
id: 4,
type: 'DEFAULT',
text: 'A transfer has been initiated for cats-are-cool.png from Miner B.',
createdAt: '2016-12-27 00:00:00 UTC',
},
{
id: 3,
type: 'DEFAULT',
text: 'cats-are-cool.png retrieval deal accepted by Miner B.',
createdAt: '2016-12-26 00:00:00 UTC',
},
{
id: 2,
type: 'DEFAULT',
text: 'You have proposed a deal with Miner B for cats-are-cool.png.',
createdAt: '2016-12-25 00:00:00 UTC',
},
{
id: 1,
type: 'DEFAULT',
text: 'cats-are-cool-2.png is stored with Miner A on the Filecoin Network.',
createdAt: '2016-12-24 00:00:00 UTC',
},
{
id: 1,
type: 'DEFAULT',
text: 'cats-are-cool-2.png has been transferred to Miner A.',
createdAt: '2016-12-23 00:00:00 UTC',
},
{
id: 1,
type: 'DEFAULT',
text: 'A transfer has begun for cats-are-cool-2.png to Miner A.',
createdAt: '2016-12-22 00:00:00 UTC',
},
{
id: 1,
type: 'DEFAULT',
text: 'The storage deal for cats-are-cool-2.png has been accepted by Miner A.',
createdAt: '2016-12-21 00:00:00 UTC',
},
{
id: 1,
type: 'DEFAULT',
text: 'A storage deal for cats-are-cool-2.png has been proposed to Miner A.',
createdAt: '2016-12-20 00:00:00 UTC',
},
{
id: 1,
type: 'DEFAULT',
text: 'Searching for a Miner A to store cats-are-cool-2.png.',
createdAt: '2016-12-19 00:00:00 UTC',
},
],
payment_channels_active: [
{
id: 1,
category: 1,
'channel-id': 'example-channel-id-1',
'max-value': Strings.formatAsFilecoin(3232100),
'current-value': Strings.formatAsFilecoin(423233),
redeemable: 'Redeem',
},
{
id: 2,
category: 2,
'channel-id': 'example-channel-id-2',
'max-value': Strings.formatAsFilecoin(3232100),
'current-value': Strings.formatAsFilecoin(423233),
},
],
payment_channels_redeemed: [
{
id: 1,
category: 1,
'channel-id': 'example-channel-id-3',
'max-value': Strings.formatAsFilecoin(3232100),
'redeemed-value': Strings.formatAsFilecoin(423233),
},
{
id: 2,
category: 1,
'channel-id': 'example-channel-id-4',
'max-value': Strings.formatAsFilecoin(223100),
'redeemed-value': Strings.formatAsFilecoin(12200),
},
],
data_transfers: [
{
id: 1,
'data-cid': '44Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': '55Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'data-source': 'LOCAL',
'data-destination': 't05141',
size: Strings.bytesToSize(202000),
},
{
id: 2,
'data-cid': '66Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': '77Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'data-source': 'LOCAL',
'data-destination': 't05141',
size: Strings.bytesToSize(202000),
},
{
id: 3,
'data-cid': '44Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': '55Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'data-source': 't05141',
'data-destination': 'LOCAL',
size: Strings.bytesToSize(202000),
},
],
peers: [
{
id: 1,
'peer-avatar': '/static/avatar-adrian-lanzafame.png',
online: true,
'chain-head': 'bafy2bzacecvuycbaik2dn4ktxeyrzrtuviqxvhbk67qxt5lqgrwogxhk4twx6',
height: 8888,
location: 1,
upload: 22222,
download: 11111,
},
{
id: 2,
'peer-avatar': '/static/avatar-andrew-hill.jpg',
online: true,
'chain-head': 'bafy2bzacecvuycbaik2dn4ktxeyrzrtuviqxvhbk67qxt5lqgrwogxhk4twx6',
height: 8888,
location: 2,
upload: 22222,
download: 11111,
},
{
id: 3,
'peer-avatar': '/static/avatar-colin-evran.jpg',
'chain-head': 'bafy2bzacecvuycbaik2dn4ktxeyrzrtuviqxvhbk67qxt5lqgrwogxhk4twx6',
height: 8888,
location: 3,
upload: 22222,
download: 11111,
},
{
id: 4,
'peer-avatar': '/static/avatar-juan-benet.png',
'chain-head': 'bafy2bzacecvuycbaik2dn4ktxeyrzrtuviqxvhbk67qxt5lqgrwogxhk4twx6',
height: 8888,
location: 3,
upload: 22222,
download: 11111,
},
],
deals: [
{
id: 1,
'deal-category': 1,
'data-cid': '14Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': '23Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
miner: 'Example Miner A',
price: Strings.formatAsFilecoin(1000),
'auto-renew': 1,
remaining: null,
status: 1,
},
{
id: 2,
'deal-category': 1,
'data-cid': '34Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': '56Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
miner: 'Example Miner B',
price: Strings.formatAsFilecoin(1000),
'auto-renew': 1,
remaining: null,
status: 2,
},
{
id: 3,
'deal-category': 1,
'data-cid': '78Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': '89Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
miner: 'Example Miner C',
price: Strings.formatAsFilecoin(1000),
'auto-renew': 2,
remaining: null,
status: 3,
},
{
id: 4,
'deal-category': 1,
'data-cid': '99Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': '11Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
miner: 'Example Miner D',
price: Strings.formatAsFilecoin(1000),
'auto-renew': 2,
remaining: null,
status: 4,
},
{
id: 5,
'deal-category': 1,
'data-cid': '12Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': '34Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
miner: 'Example Miner E',
price: Strings.formatAsFilecoin(1000),
'auto-renew': 2,
remaining: null,
status: 5,
},
{
id: 6,
'deal-category': 1,
'data-cid': '56Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': '78Y7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
miner: 'Example Miner F',
price: Strings.formatAsFilecoin(1000),
'auto-renew': 1,
remaining: Strings.getRemainingTime(184000),
status: 6,
},
{
id: 7,
'deal-category': 2,
'data-cid': 'abY7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': 'cdY7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
miner: 'Example Miner A',
price: Strings.formatAsFilecoin(100),
status: 1,
},
{
id: 8,
'deal-category': 2,
'data-cid': 'efY7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': 'ghY7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
miner: 'Example Miner B',
price: Strings.formatAsFilecoin(100),
status: 2,
},
{
id: 9,
'deal-category': 2,
'data-cid': 'ilY7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': 'qqY7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
miner: 'Example Miner C',
price: Strings.formatAsFilecoin(100),
status: 3,
},
{
id: 10,
'deal-category': 2,
'data-cid': 'xxY7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': 'wwY7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
miner: 'Example Miner D',
price: Strings.formatAsFilecoin(100),
status: 4,
},
{
id: 11,
'deal-category': 2,
'data-cid': 'zzY7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
'deal-cid': 'qzY7Yh4UquoXHLPFo2XbhXkhBvFoPwmQUSa92pxnxjQuPU',
miner: 'Example Miner E',
price: Strings.formatAsFilecoin(100),
status: 5,
},
],
addresses: [
{
value: '1',
name: 'Capricorn',
address: Data.EXAMPLE_ADDRESSES[0],
transactions: [
{
id: 1,
category: 1,
amount: Strings.formatAsFilecoin(11000),
source: Data.EXTERNAL_ADDRESSES[0],
destination: 'Capricorn',
date: Strings.toDate('2017-01-01 00:00:00 UTC'),
status: 1,
},
{
id: 2,
category: 2,
amount: Strings.formatAsFilecoin(11000),
source: 'Capricorn',
destination: Data.EXTERNAL_ADDRESSES[0],
date: Strings.toDate('2017-01-01 00:00:00 UTC'),
status: 2,
},
{
id: 3,
category: 1,
amount: Strings.formatAsFilecoin(11000),
source: Data.EXTERNAL_ADDRESSES[0],
destination: 'Capricorn',
date: Strings.toDate('2017-01-01 00:00:00 UTC'),
status: 2,
},
{
id: 4,
category: 2,
amount: Strings.formatAsFilecoin(11000),
source: 'Capricorn',
destination: Data.EXTERNAL_ADDRESSES[0],
date: Strings.toDate('2017-01-01 00:00:00 UTC'),
status: 1,
},
],
type: 'SECP256K',
balance: 10230,
deals: 42,
},
{
value: '2',
name: 'Aquarius',
address: Data.EXAMPLE_ADDRESSES[1],
transactions: [],
type: 'MULTISIG',
balance: 0,
deals: 0,
},
{
value: '3',
name: 'Pisces',
address: Data.EXAMPLE_ADDRESSES[2],
transactions: [],
type: 'BLS',
balance: 0,
deals: 0,
},
],
};
};
const getFolderById = (id) => {
for (let i = 0; i < Data.EXAMPLE_FOLDERS.length; i++) {
if (id === Data.EXAMPLE_FOLDERS[i].id) {
return Data.EXAMPLE_FOLDERS[i];
} }
} }
return null; return library;
}; };
const getFileById = (id) => { export const generateNavigationState = (library) => [
for (let i = 0; i < Data.EXAMPLE_FILES.length; i++) {
if (id === Data.EXAMPLE_FILES[i].id) {
return Data.EXAMPLE_FILES[i];
}
}
};
const constructFilesTreeForNavigation = () => {
const SCAFFOLD = [
{
folderId: `folder-root`,
children: [
`folder-1`,
`folder-2`,
`file-1`,
`file-2`,
`file-3`,
`file-8`,
`file-9`,
`file-10`,
`file-11`,
`file-12`,
`file-13`,
`file-14`,
`file-15`,
`file-16`,
`file-17`,
],
},
{
folderId: `folder-1`,
children: [`folder-3`, `file-4`, `file-5`, `file-6`],
},
{
folderId: `folder-2`,
children: [],
},
{
folderId: `folder-3`,
children: [`file-7`],
},
];
const navigation = {
...getFolderById('folder-root'),
};
// TODO(jim): Refactor after proving the concept this is ugly.
SCAFFOLD.forEach((o) => {
if (navigation.children) {
for (let i = 0; i < navigation.children.length; i++) {
if (navigation.children[i] && navigation.children[i].id === o.folderId) {
if (!navigation.children[i].children) {
navigation.children[i].children = o.children.map((each) => {
const folder = getFolderById(each);
if (folder) {
return folder;
}
const file = getFileById(each);
if (file) {
return { ...file, ignore: true };
}
return null;
});
}
}
}
}
if (navigation.id === o.folderId) {
if (!navigation.children) {
navigation.children = o.children.map((each) => {
const folder = getFolderById(each);
if (folder) {
return folder;
}
const file = getFileById(each);
if (file) {
return { ...file, ignore: true };
}
return null;
});
}
}
});
return navigation;
};
export const NavigationState = [
{ {
id: 1, id: 1,
name: 'Home', name: 'Home',
@ -488,79 +24,8 @@ export const NavigationState = [
name: 'Wallet', name: 'Wallet',
pageTitle: 'your wallet and addresses', pageTitle: 'your wallet and addresses',
decorator: 'WALLET', decorator: 'WALLET',
/*
children: [
{
id: 3,
name: 'Payment Channels',
children: null,
pageTitle: 'your payment channels',
decorator: 'CHANNELS',
},
],
*/
}, },
constructFilesTreeForNavigation(), ...constructFilesTreeForNavigation(library),
{
id: 5,
name: 'Deals',
pageTitle: 'your deals',
decorator: 'DEALS',
children: [
{
id: 6,
name: 'Data Transfer',
pageTitle: 'your data transfers',
decorator: 'DATA_TRANSFER',
children: null,
},
],
},
/*
{
id: 9,
name: 'Stats',
pageTitle: 'your filecoin usage',
decorator: 'STATS',
children: [
{
id: 10,
name: 'Storage Market',
pageTitle: 'the storage market',
decorator: 'STORAGE_MARKET',
children: null,
},
{
id: 11,
name: 'Miners',
pageTitle: 'miners',
decorator: 'MINERS',
children: null,
},
],
},
{
id: 7,
name: 'Status',
pageTitle: 'your status',
decorator: 'STATUS',
children: null,
},
{
id: 8,
name: 'Peers',
pageTitle: 'your peers',
decorator: 'PEERS',
children: null,
},
{
id: 12,
name: 'Logs',
pageTitle: 'your logs',
decorator: 'LOGS',
children: null,
},
*/
{ {
id: 13, id: 13,
name: 'Edit account', name: 'Edit account',

View File

@ -35,13 +35,26 @@ const transformPeers = (peersList) => {
}; };
export const getInitialState = (props) => { export const getInitialState = (props) => {
const { status, messageList, peersList, addrsList, info } = props; const { status, messageList, peersList, addrsList, info, library } = props;
if (!info || !info.id) {
return {
id: null,
notifications: [],
payment_channels_active: [],
payment_channels_redeemed: [],
data_transfers: [],
peers: [],
deals: [],
addresses: [],
library: [],
};
}
return { return {
id: info.id, id: info.id,
name: 'New Node', name: 'New Node',
photoURL: '/static/system/avatar.png', photoURL: '/static/system/avatar.png',
config: '',
upload_bandwidth: 0, upload_bandwidth: 0,
download_bandwidth: 0, download_bandwidth: 0,
@ -53,7 +66,7 @@ export const getInitialState = (props) => {
settings_cold_enabled: info.defaultConfig.cold.enabled, settings_cold_enabled: info.defaultConfig.cold.enabled,
settings_cold_default_address: info.defaultConfig.cold.filecoin.addr, settings_cold_default_address: info.defaultConfig.cold.filecoin.addr,
settings_cold_default_duration: info.defaultConfig.cold.filecoin.dealDuration, settings_cold_default_duration: info.defaultConfig.cold.filecoin.dealMinDuration,
settings_cold_default_replication_factor: info.defaultConfig.cold.filecoin.repFactor, settings_cold_default_replication_factor: info.defaultConfig.cold.filecoin.repFactor,
settings_cold_default_excluded_miners: info.defaultConfig.cold.filecoin.excludedMinersList, settings_cold_default_excluded_miners: info.defaultConfig.cold.filecoin.excludedMinersList,
settings_cold_default_trusted_miners: info.defaultConfig.cold.filecoin.trustedMinersList, settings_cold_default_trusted_miners: info.defaultConfig.cold.filecoin.trustedMinersList,
@ -68,5 +81,6 @@ export const getInitialState = (props) => {
peers: transformPeers(peersList), peers: transformPeers(peersList),
deals: [], deals: [],
addresses: transformAddresses(addrsList, info), addresses: transformAddresses(addrsList, info),
library,
}; };
}; };

View File

@ -11,6 +11,7 @@ const STYLES_AVATAR = css`
background-position: 50% 50%; background-position: 50% 50%;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
position: relative; position: relative;
background-color: ${Constants.system.black};
`; `;
const STYLES_AVATAR_ONLINE = css` const STYLES_AVATAR_ONLINE = css`

View File

@ -6,6 +6,16 @@ import * as System from '~/components/system';
import { css } from '@emotion/react'; import { css } from '@emotion/react';
const STYLES_FILE_HIDDEN = css`
height: 1px;
width: 1px;
opacity: 0;
visibility: hidden;
position: fixed;
top: -1px;
left: -1px;
`;
const STYLES_FOCUS = css` const STYLES_FOCUS = css`
font-size: ${Constants.typescale.lvl1}; font-size: ${Constants.typescale.lvl1};
font-family: 'inter-medium'; font-family: 'inter-medium';
@ -49,13 +59,64 @@ const SELECT_MENU_MAP = {
export default class SidebarFileStorageDeal extends React.Component { export default class SidebarFileStorageDeal extends React.Component {
state = { state = {
settings_deal_duration: this.props.viewer.settings_cold_default_duration, file: null,
settings_replication_factor: this.props.viewer.settings_cold_default_replication_factor, settings_cold_default_duration: this.props.viewer.settings_cold_default_duration,
settings_cold_default_replication_factor: this.props.viewer.settings_cold_default_replication_factor,
}; };
_handleSubmit = () => { _handleUpload = async (e) => {
alert('TODO: Make a storage deal'); e.persist();
this.props.onSubmit({}); let file = e.target.files[0];
if (!file) {
alert('Something went wrong');
return;
}
let data = new FormData();
data.append('image', file);
const options = {
method: 'POST',
headers: {
Accept: 'application/json',
},
body: data,
};
const response = await fetch(`/_/storage/${file.name}`, options);
const json = await response.json();
if (json && json.success) {
this.setState({ file });
}
};
_handleMakeDeal = async (src) => {
console.log(src);
const options = {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ src }),
};
const response = await fetch('/_/deals/storage', options);
const json = await response.json();
return json;
};
_handleSubmit = async (e) => {
e.persist();
const path = `/public/static/files/${this.state.file.name}`;
await this._handleMakeDeal(path);
await this.props.onSubmit({});
}; };
_handleCancel = () => { _handleCancel = () => {
@ -82,56 +143,68 @@ export default class SidebarFileStorageDeal extends React.Component {
return ( return (
<React.Fragment> <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>
<input css={STYLES_FILE_HIDDEN} type="file" id="file" onChange={this._handleUpload} />
<img src="/static/test-image-upload.jpg" css={STYLES_IMAGE_PREVIEW} /> {this.state.file ? (
<div>
<img src={`/static/files/${this.state.file.name}`} css={STYLES_IMAGE_PREVIEW} />
<div css={STYLES_ITEM}> <div css={STYLES_ITEM}>
<div css={STYLES_FOCUS}>test-image-upload.jpg</div> <div css={STYLES_FOCUS}>{this.state.file.name}</div>
<div css={STYLES_SUBTEXT}>Name</div> <div css={STYLES_SUBTEXT}>Name</div>
</div> </div>
<div css={STYLES_ITEM}> <div css={STYLES_ITEM}>
<div css={STYLES_FOCUS}>42 MB</div> <div css={STYLES_FOCUS}>{this.state.file.size}</div>
<div css={STYLES_SUBTEXT}>File size</div> <div css={STYLES_SUBTEXT}>File size</div>
</div> </div>
</div>
) : null}
<System.ButtonSecondaryFull style={{ marginTop: 24 }}>Change file</System.ButtonSecondaryFull> <System.ButtonSecondaryFull type="label" htmlFor="file" style={{ marginTop: 24 }}>
Add file
</System.ButtonSecondaryFull>
<System.Input {this.state.file ? (
containerStyle={{ marginTop: 48 }} <System.Input
label="Deal duration" containerStyle={{ marginTop: 48 }}
name="settings_deal_duration" label="Deal duration"
value={this.state.settings_deal_duration} name="settings_cold_default_duration"
onChange={this._handleChange} placeholder="Type in months"
/> type="number"
value={this.state.settings_cold_default_duration}
onChange={this._handleChange}
/>
) : null}
<System.Input {this.state.file ? (
containerStyle={{ marginTop: 24 }} <System.Input
label="Replication factor" containerStyle={{ marginTop: 24 }}
name="settings_replication_factor" label="Replication factor"
value={this.state.settings_replication_factor} name="settings_cold_default_replication_factor"
onChange={this._handleChange} value={this.state.settings_cold_default_replication_factor}
/> onChange={this._handleChange}
/>
) : null}
<System.SelectMenuFull {this.state.file ? (
containerStyle={{ marginTop: 24 }} <System.SelectMenuFull
name="address" containerStyle={{ marginTop: 24 }}
label="Payment address" name="address"
value={this.props.selected.address} label="Payment address"
category="address" value={this.props.selected.address}
onChange={this.props.onSelectedChange} category="address"
options={this.props.viewer.addresses}> onChange={this.props.onSelectedChange}
{currentAddress.name} options={this.props.viewer.addresses}>
</System.SelectMenuFull> {currentAddress.name}
</System.SelectMenuFull>
) : null}
<div css={STYLES_ITEM}> {this.state.file ? (
<div css={STYLES_FOCUS}>2 FIL</div> <System.ButtonPrimaryFull style={{ marginTop: 48 }} onClick={this._handleSubmit}>
<div css={STYLES_SUBTEXT}>Last order price</div> Make storage deal
</div> </System.ButtonPrimaryFull>
) : null}
<System.ButtonPrimaryFull style={{ marginTop: 48 }} onClick={this._handleSubmit}>
Make storage deal
</System.ButtonPrimaryFull>
</React.Fragment> </React.Fragment>
); );
} }

View File

@ -431,11 +431,6 @@ export class Table extends React.Component {
onAction: () => console.log('No action function set'), onAction: () => console.log('No action function set'),
}; };
// NOTE(jim): Local state for local filtering.
state = {
data: this.props.data,
};
_handleChange = (value) => { _handleChange = (value) => {
this.props.onChange({ this.props.onChange({
target: { target: {
@ -446,7 +441,7 @@ export class Table extends React.Component {
}; };
render() { render() {
const { data } = this.state; const { data } = this.props;
const ac = {}; const ac = {};
@ -642,6 +637,10 @@ const STYLES_BUTTON_PRIMARY_FULL = css`
`; `;
export const ButtonPrimaryFull = (props) => { export const ButtonPrimaryFull = (props) => {
if (props.type === 'label') {
return <label css={STYLES_BUTTON_PRIMARY_FULL} {...props} />;
}
return <button css={STYLES_BUTTON_PRIMARY_FULL} {...props} />; return <button css={STYLES_BUTTON_PRIMARY_FULL} {...props} />;
}; };
@ -665,6 +664,10 @@ const STYLES_BUTTON_SECONDARY = css`
`; `;
export const ButtonSecondary = (props) => { export const ButtonSecondary = (props) => {
if (props.type === 'label') {
return <label css={STYLES_BUTTON_SECONDARY} {...props} />;
}
return <button css={STYLES_BUTTON_SECONDARY} {...props} />; return <button css={STYLES_BUTTON_SECONDARY} {...props} />;
}; };
@ -688,6 +691,10 @@ const STYLES_BUTTON_SECONDARY_FULL = css`
`; `;
export const ButtonSecondaryFull = (props) => { export const ButtonSecondaryFull = (props) => {
if (props.type === 'label') {
return <label css={STYLES_BUTTON_SECONDARY_FULL} {...props} />;
}
return <button css={STYLES_BUTTON_SECONDARY_FULL} {...props} />; return <button css={STYLES_BUTTON_SECONDARY_FULL} {...props} />;
}; };

View File

@ -1,30 +1,32 @@
import * as React from "react"; import * as React from 'react';
import * as Constants from "~/common/constants"; import * as Constants from '~/common/constants';
import * as SVG from "~/components/system/svg"; import * as SVG from '~/components/system/svg';
import * as OldSVG from "~/common/svg"; import * as OldSVG from '~/common/svg';
import * as Strings from "~/common/strings"; import * as Strings from '~/common/strings';
import { css } from "@emotion/react"; import { css } from '@emotion/react';
import { Tooltip } from "react-tippy"; import { Tooltip } from 'react-tippy';
import Avatar from "~/components/core/Avatar"; import Avatar from '~/components/core/Avatar';
const STORAGE_DEAL_STATES = { const STORAGE_DEAL_STATES = {
"1": "Searching for miners.", '0': 'Local file only.',
"2": "Proposing storage deal.", '1': 'Searching for miners.',
"3": "Accepted by miners.", '2': 'Proposing storage deal.',
"4": "Data transfer in progress.", '3': 'Accepted by miners.',
"5": "Data transfer complete.", '4': 'Data transfer in progress.',
"6": "Stored", '5': 'Data transfer complete.',
'6': 'Stored on network.',
}; };
const RETRIEVAL_DEAL_STATES = { const RETRIEVAL_DEAL_STATES = {
"1": "Available on network", '0': 'Local file',
"2": "Retrieval deal proposed.", '1': 'Available on network',
"3": "Retrieval deal accepted.", '2': 'Retrieval deal proposed.',
"4": "Data transfer in progress.", '3': 'Retrieval deal accepted.',
"5": "Data transfer completed.", '4': 'Data transfer in progress.',
"6": "Retrieved", '5': 'Data transfer completed.',
'6': 'Retrieved from network.',
}; };
const COMPONENTS_ICON = { const COMPONENTS_ICON = {
@ -34,7 +36,7 @@ const COMPONENTS_ICON = {
const STYLES_TABLE_TAG = css` const STYLES_TABLE_TAG = css`
font-weight: 400; font-weight: 400;
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
letter-spacing: 0.2px; letter-spacing: 0.2px;
padding: 4px 6px 4px 6px; padding: 4px 6px 4px 6px;
font-size: 10px; font-size: 10px;
@ -46,21 +48,18 @@ const STYLES_TABLE_TAG = css`
`; `;
const COMPONENTS_TRANSACTION_DIRECTION = { const COMPONENTS_TRANSACTION_DIRECTION = {
"1": ( '1': (
<span css={STYLES_TABLE_TAG} style={{ background: Constants.system.green }}> <span css={STYLES_TABLE_TAG} style={{ background: Constants.system.green }}>
+ incoming + incoming
</span> </span>
), ),
"2": <span css={STYLES_TABLE_TAG}>- outgoing</span>, '2': <span css={STYLES_TABLE_TAG}>- outgoing</span>,
}; };
const COMPONENTS_TRANSACTION_STATUS = { const COMPONENTS_TRANSACTION_STATUS = {
"1": <span css={STYLES_TABLE_TAG}>complete</span>, '1': <span css={STYLES_TABLE_TAG}>complete</span>,
"2": ( '2': (
<span <span css={STYLES_TABLE_TAG} style={{ background: Constants.system.yellow }}>
css={STYLES_TABLE_TAG}
style={{ background: Constants.system.yellow }}
>
pending pending
</span> </span>
), ),
@ -115,7 +114,7 @@ const STYLES_CONTENT_BUTTON = css`
`; `;
const STYLES_TABLE_CONTENT_LINK = css` const STYLES_TABLE_CONTENT_LINK = css`
font-family: "inter-medium"; font-family: 'inter-medium';
text-decoration: underline; text-decoration: underline;
cursor: pointer; cursor: pointer;
@ -140,10 +139,7 @@ export const TableColumn = (props) => {
) : null; ) : null;
return ( return (
<span <span css={props.top ? STYLES_TOP_COLUMN : STYLES_COLUMN} style={props.style}>
css={props.top ? STYLES_TOP_COLUMN : STYLES_COLUMN}
style={props.style}
>
<span css={STYLES_CONTENT}>{props.children}</span> <span css={STYLES_CONTENT}>{props.children}</span>
{tooltipElement} {tooltipElement}
{copyableElement} {copyableElement}
@ -151,94 +147,75 @@ export const TableColumn = (props) => {
); );
}; };
export const TableContent = ({ export const TableContent = ({ type, text, action, data = {}, onNavigateTo, onAction }) => {
type,
text,
action,
data = {},
onNavigateTo,
onAction,
}) => {
const { status, online } = data; const { status, online } = data;
if (Strings.isEmpty(text)) { if (text === null || text === undefined) {
return null; return null;
} }
switch (type) { switch (type) {
case "DEAL_CATEGORY": case 'DEAL_CATEGORY':
return <React.Fragment>{text == 1 ? 'Storage' : 'Retrieval'}</React.Fragment>;
case 'LOCATION':
return 'United States';
case 'BUTTON':
return ( return (
<React.Fragment>{text == 1 ? "Storage" : "Retrieval"}</React.Fragment> <span css={STYLES_TABLE_CONTENT_LINK} onClick={() => onAction({ type: 'SIDEBAR', value: action })}>
);
case "LOCATION":
return "United States";
case "BUTTON":
return (
<span
css={STYLES_TABLE_CONTENT_LINK}
onClick={() => onAction({ type: "SIDEBAR", value: action })}
>
{text} {text}
</span> </span>
); );
case "TRANSACTION_DIRECTION": case 'TRANSACTION_DIRECTION':
return COMPONENTS_TRANSACTION_DIRECTION[text]; return COMPONENTS_TRANSACTION_DIRECTION[text];
case "TRANSACTION_STATUS": case 'TRANSACTION_STATUS':
return COMPONENTS_TRANSACTION_STATUS[text]; return COMPONENTS_TRANSACTION_STATUS[text];
case "ICON": case 'ICON':
return COMPONENTS_ICON[text]; return COMPONENTS_ICON[text];
case "AVATAR": case 'AVATAR':
return <Avatar url={text} size={40} online={online} />; return <Avatar url={text} size={40} online={online} />;
case "DEAL_STATUS_RETRIEVAL": case 'DEAL_STATUS_RETRIEVAL':
return RETRIEVAL_DEAL_STATES[`${text}`]; return RETRIEVAL_DEAL_STATES[`${text}`];
case "DEAL_STATUS": case 'DEAL_STATUS':
return data["deal-category"] === 1 return data['deal_category'] === 1 ? STORAGE_DEAL_STATES[`${text}`] : RETRIEVAL_DEAL_STATES[`${text}`];
? STORAGE_DEAL_STATES[`${text}`] case 'BANDWIDTH_UPLOAD':
: RETRIEVAL_DEAL_STATES[`${text}`];
case "BANDWIDTH_UPLOAD":
return ( return (
<React.Fragment> <React.Fragment>
<SVG.BandwidthUp height="16px" style={{ marginRight: 8 }} /> <SVG.BandwidthUp height="16px" style={{ marginRight: 8 }} />
{Strings.bytesToSize(text)} {Strings.bytesToSize(text)}
</React.Fragment> </React.Fragment>
); );
case "BANDWIDTH_DOWNLOAD": case 'BANDWIDTH_DOWNLOAD':
return ( return (
<React.Fragment> <React.Fragment>
<SVG.BandwidthDown height="16px" style={{ marginRight: 8 }} /> <SVG.BandwidthDown height="16px" style={{ marginRight: 8 }} />
{Strings.bytesToSize(text)} {Strings.bytesToSize(text)}
</React.Fragment> </React.Fragment>
); );
case "MINER_AVAILABILITY": case 'MINER_AVAILABILITY':
return text == 1 ? ( return text == 1 ? (
<span <span css={STYLES_TABLE_TAG} style={{ background: Constants.system.green }}>
css={STYLES_TABLE_TAG}
style={{ background: Constants.system.green }}
>
Online Online
</span> </span>
) : null; ) : null;
case "DEAL_AUTO_RENEW": case 'DEAL_AUTO_RENEW':
return text == 1 ? ( return text == 1 ? (
<span <span css={STYLES_TABLE_TAG} style={{ background: Constants.system.brand }}>
css={STYLES_TABLE_TAG}
style={{ background: Constants.system.brand }}
>
true true
</span> </span>
) : ( ) : (
<span css={STYLES_TABLE_TAG}>false</span> <span css={STYLES_TABLE_TAG}>false</span>
); );
case "NOTIFICATION_ERROR": case 'NOTIFICATION_ERROR':
return ( return (
<span <span css={STYLES_TABLE_TAG} style={{ background: Constants.system.red }}>
css={STYLES_TABLE_TAG} {text} {Strings.pluralize('error', text)}
style={{ background: Constants.system.red }}
>
{text} {Strings.pluralize("error", text)}
</span> </span>
); );
case "FILE_LINK": case 'FILE_DATE':
return Strings.toDate(text);
case 'FILE_SIZE':
return Strings.bytesToSize(text, 2);
case 'FILE_LINK':
// NOTE(jim): Special case to prevent navigation. // NOTE(jim): Special case to prevent navigation.
if (!data) { if (!data) {
return text; return text;
@ -249,44 +226,45 @@ export const TableContent = ({
return ( return (
<span <span
css={STYLES_TABLE_CONTENT_LINK} css={STYLES_TABLE_CONTENT_LINK}
onClick={() => onClick={() => onAction({ type: 'NAVIGATE', value: data.folderId, data })}>
onAction({ type: "NAVIGATE", value: data.folderId, data })
}
>
{text} {text}
</span> </span>
); );
} }
// NOTE(jim): Special case for navigating to a sidebar. // NOTE(jim): Special case for navigating to a sidebar.
if (data && data["retrieval-status"] === 1) { if (data && data['retrieval_status'] === 1) {
return ( return (
<span <span
css={STYLES_TABLE_CONTENT_LINK} css={STYLES_TABLE_CONTENT_LINK}
onClick={() => onClick={() =>
onAction({ onAction({
type: "SIDEBAR", type: 'SIDEBAR',
value: "SIDEBAR_FILE_STORAGE_DEAL", value: 'SIDEBAR_FILE_STORAGE_DEAL',
}) })
} }>
>
{text} {text}
</span> </span>
); );
} }
// NOTE(jim): Special case to prevent navigation. // NOTE(jim): Special case to prevent navigation.
if (data && data["retrieval-status"] !== 6) { if (
data &&
(data['retrieval_status'] === 5 ||
data['retrieval_status'] === 4 ||
data['retrieval_status'] === 3 ||
data['retrieval_status'] === 2)
) {
return ( return (
<span <span
onClick={() => onClick={() =>
onAction({ onAction({
name: "File does not exist", name: 'File does not exist',
type: "ACTION", type: 'ACTION',
value: "ACTION_FILE_MISSING", value: 'ACTION_FILE_MISSING',
}) })
} }>
>
{text} {text}
</span> </span>
); );
@ -294,10 +272,7 @@ export const TableContent = ({
// NOTE(jim): Navigates to file. // NOTE(jim): Navigates to file.
return ( return (
<span <span css={STYLES_TABLE_CONTENT_LINK} onClick={() => onNavigateTo({ id: 15 }, data)}>
css={STYLES_TABLE_CONTENT_LINK}
onClick={() => onNavigateTo({ id: 15 }, data)}
>
{text} {text}
</span> </span>
); );

View File

@ -15,7 +15,7 @@
"@emotion/css": "11.0.0-next.12", "@emotion/css": "11.0.0-next.12",
"@emotion/react": "11.0.0-next.12", "@emotion/react": "11.0.0-next.12",
"@emotion/server": "11.0.0-next.12", "@emotion/server": "11.0.0-next.12",
"@textile/powergate-client": "0.1.0-beta.7", "@textile/powergate-client": "0.1.0-beta.9",
"babel-plugin-module-resolver": "^4.0.0", "babel-plugin-module-resolver": "^4.0.0",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"chart.js": "^2.9.3", "chart.js": "^2.9.3",
@ -29,6 +29,7 @@
"react": "^16.12.0", "react": "^16.12.0",
"react-dom": "^16.12.0", "react-dom": "^16.12.0",
"react-tippy": "^1.3.4", "react-tippy": "^1.3.4",
"three": "^0.108.0" "three": "^0.108.0",
"ws": "^7.3.0"
} }
} }

View File

@ -64,36 +64,46 @@ const getCurrentNavigationStateById = (navigation, targetId) => {
export const getServerSideProps = async (context) => { export const getServerSideProps = async (context) => {
if (context.query && context.query.production) { if (context.query && context.query.production) {
return { production: true }; return { production: true, ...context.query };
} }
const data = await Actions.rehydrateViewer(); const data = await Actions.rehydrateViewer();
return { return {
props: { ...data.data }, props: { ...context.query, ...data.data },
}; };
}; };
export default class IndexPage extends React.Component { export default class IndexPage extends React.Component {
_socket = null;
state = { state = {
history: [{ id: 1, scrollTop: 0 }], history: [{ id: 1, scrollTop: 0 }],
currentIndex: 0, currentIndex: 0,
data: null, data: null,
selected: { selected: {
address: null, address: '',
}, },
viewer: this.props.production ? Fixtures.getInitialState() : State.getInitialState(this.props), viewer: State.getInitialState(this.props),
sidebar: null, sidebar: null,
}; };
async componentDidMount() { componentDidMount() {
// TODO(jim): You don't really need this. this._socket = new WebSocket(`ws://localhost:${this.props.wsPort}`);
console.log(this.props); this._socket.onmessage = (m) => {
console.log(m);
if (m.type === 'message') {
const parsed = JSON.parse(m.data);
if (parsed.action === 'UPDATE_VIEWER') {
this.rehydrate({ data: parsed.data });
}
}
};
} }
rehydrate = async () => { rehydrate = async ({ data }) => {
const viewer = await Actions.rehydrateViewer(); this.setState({ viewer: { ...State.getInitialState(data) } });
this.setState({ viewer: { ...State.getInitialState(viewer.data) } });
}; };
_handleSubmit = async (data) => { _handleSubmit = async (data) => {
@ -108,8 +118,6 @@ export default class IndexPage extends React.Component {
type: data.wallet_type, type: data.wallet_type,
makeDefault: data.makeDefault, makeDefault: data.makeDefault,
}); });
await this.rehydrate();
} }
if (data.type === 'SEND_WALLET_ADDRESS_FILECOIN') { if (data.type === 'SEND_WALLET_ADDRESS_FILECOIN') {
@ -118,8 +126,6 @@ export default class IndexPage extends React.Component {
target: data.target, target: data.target,
amount: data.amount, amount: data.amount,
}); });
await this.rehydrate();
} }
this._handleDismissSidebar(); this._handleDismissSidebar();
@ -258,21 +264,26 @@ export default class IndexPage extends React.Component {
}; };
render() { render() {
const next = this.state.history[this.state.currentIndex]; if (this.props.production) {
const current = getCurrentNavigationStateById(Fixtures.NavigationState, next.id); return null;
}
const navigation = ( const navigation = Fixtures.generateNavigationState(this.state.viewer.library);
const next = this.state.history[this.state.currentIndex];
const current = getCurrentNavigationStateById(navigation, next.id);
const navigationElement = (
<ApplicationNavigation <ApplicationNavigation
viewer={this.state.viewer} viewer={this.state.viewer}
activeId={current.target.id} activeId={current.target.id}
activeIds={current.activeIds} activeIds={current.activeIds}
navigation={Fixtures.NavigationState} navigation={navigation}
onNavigateTo={this._handleNavigateTo} onNavigateTo={this._handleNavigateTo}
onAction={this._handleAction} onAction={this._handleAction}
/> />
); );
const header = ( const headerElement = (
<ApplicationHeader <ApplicationHeader
viewer={this.state.viewer} viewer={this.state.viewer}
pageTitle={current.target.pageTitle} pageTitle={current.target.pageTitle}
@ -286,7 +297,6 @@ export default class IndexPage extends React.Component {
); );
const scene = React.cloneElement(this.scenes[current.target.decorator], { const scene = React.cloneElement(this.scenes[current.target.decorator], {
rehydrate: this.rehydrate,
viewer: this.state.viewer, viewer: this.state.viewer,
selected: this.state.selected, selected: this.state.selected,
data: current.target, data: current.target,
@ -299,9 +309,9 @@ export default class IndexPage extends React.Component {
onForward: this._handleForward, onForward: this._handleForward,
}); });
let sidebar; let sidebarElement;
if (this.state.sidebar) { if (this.state.sidebar) {
sidebar = React.cloneElement(this.state.sidebar, { sidebarElement = React.cloneElement(this.state.sidebar, {
viewer: this.state.viewer, viewer: this.state.viewer,
selected: this.state.selected, selected: this.state.selected,
onSelectedChange: this._handleSelectedChange, onSelectedChange: this._handleSelectedChange,
@ -318,9 +328,9 @@ export default class IndexPage extends React.Component {
<React.Fragment> <React.Fragment>
<WebsitePrototypeWrapper title={title} description={description} url={url}> <WebsitePrototypeWrapper title={title} description={description} url={url}>
<ApplicationLayout <ApplicationLayout
navigation={navigation} navigation={navigationElement}
header={header} header={headerElement}
sidebar={sidebar} sidebar={sidebarElement}
onDismissSidebar={this._handleDismissSidebar}> onDismissSidebar={this._handleDismissSidebar}>
{scene} {scene}
</ApplicationLayout> </ApplicationLayout>

View File

@ -1,6 +1,6 @@
import * as React from "react"; import * as React from 'react';
import { css } from "@emotion/react"; import { css } from '@emotion/react';
const STYLES_LAYOUT = css` const STYLES_LAYOUT = css`
max-width: 928px; max-width: 928px;
@ -23,13 +23,13 @@ const STYLES_PARAGRAPH = css`
margin-bottom: 48px; margin-bottom: 48px;
strong { strong {
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
font-weight: 400; font-weight: 400;
} }
`; `;
const STYLES_NAME = css` const STYLES_NAME = css`
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
font-size: 24px; font-size: 24px;
margin: 16px; margin: 16px;
position: relative; position: relative;
@ -88,66 +88,66 @@ const STYLES_CHOICE = css`
`; `;
const LOGOS = [ const LOGOS = [
{ src: "idea-logo-1.jpg", votes: 1 }, { src: 'idea-logo-1.jpg', votes: 1 },
{ src: "idea-logo-2.jpg", votes: 2 }, { src: 'idea-logo-2.jpg', votes: 2 },
{ src: "idea-logo-3.jpg", votes: 0 }, { src: 'idea-logo-3.jpg', votes: 0 },
{ src: "idea-logo-4.jpg", votes: 2 }, { src: 'idea-logo-4.jpg', votes: 2 },
{ src: "idea-logo-5.jpg", votes: 1 }, { src: 'idea-logo-5.jpg', votes: 1 },
{ src: "idea-logo-6.jpg", votes: 0 }, { src: 'idea-logo-6.jpg', votes: 0 },
{ src: "idea-logo-7.jpg", votes: 0 }, { src: 'idea-logo-7.jpg', votes: 0 },
{ src: "idea-logo-8.jpg", votes: 1 }, { src: 'idea-logo-8.jpg', votes: 1 },
{ src: "idea-logo-9.jpg", votes: 3 }, { src: 'idea-logo-9.jpg', votes: 3 },
{ src: "idea-logo-10.jpg", votes: 1 }, { src: 'idea-logo-10.jpg', votes: 1 },
{ src: "idea-logo-11.jpg", votes: 3 }, { src: 'idea-logo-11.jpg', votes: 3 },
{ src: "idea-logo-12.jpg", votes: 1 }, { src: 'idea-logo-12.jpg', votes: 1 },
{ src: "idea-logo-13.jpg", votes: 2 }, { src: 'idea-logo-13.jpg', votes: 2 },
{ src: "idea-logo-14.jpg", votes: 0 }, { src: 'idea-logo-14.jpg', votes: 0 },
{ src: "idea-logo-15.jpg", votes: 2 }, { src: 'idea-logo-15.jpg', votes: 2 },
{ src: "idea-logo-16.jpg", votes: 3 }, { src: 'idea-logo-16.jpg', votes: 3 },
{ src: "idea-logo-17.jpg", votes: 0 }, { src: 'idea-logo-17.jpg', votes: 0 },
{ src: "idea-logo-18.jpg", votes: 0 }, { src: 'idea-logo-18.jpg', votes: 0 },
{ src: "idea-logo-19.jpg", votes: 0 }, { src: 'idea-logo-19.jpg', votes: 0 },
{ src: "idea-logo-20.jpg", votes: 2 }, { src: 'idea-logo-20.jpg', votes: 2 },
{ src: "idea-logo-21.jpg", votes: 0 }, { src: 'idea-logo-21.jpg', votes: 0 },
{ src: "idea-logo-22.jpg", votes: 3 }, { src: 'idea-logo-22.jpg', votes: 3 },
{ src: "idea-logo-23.jpg", votes: 2 }, { src: 'idea-logo-23.jpg', votes: 2 },
{ src: "idea-logo-24.jpg", votes: 0 }, { src: 'idea-logo-24.jpg', votes: 0 },
]; ];
const NAMES = [ const NAMES = [
{ src: "Petal", votes: 1 }, { src: 'Petal', votes: 1 },
{ src: "Petals", votes: 0 }, { src: 'Petals', votes: 0 },
{ src: "Pistil", votes: 0 }, { src: 'Pistil', votes: 0 },
{ src: "Stamen", votes: 0 }, { src: 'Stamen', votes: 0 },
{ src: "Pollen", votes: 0 }, { src: 'Pollen', votes: 0 },
{ src: "Mission Control", votes: 0 }, { src: 'Mission Control', votes: 0 },
{ src: "Hex", votes: 2 }, { src: 'Hex', votes: 2 },
{ src: "Hive", votes: 2 }, { src: 'Hive', votes: 2 },
{ src: "Hexagons", votes: 0 }, { src: 'Hexagons', votes: 0 },
{ src: "Spheres", votes: 0 }, { src: 'Spheres', votes: 0 },
{ src: "FileManager", votes: 0 }, { src: 'FileManager', votes: 0 },
{ src: "FilePatron", votes: 0 }, { src: 'FilePatron', votes: 0 },
{ src: "Filecoin Client", votes: 0 }, { src: 'Filecoin Client', votes: 0 },
{ src: "Ponds", votes: 1 }, { src: 'Ponds', votes: 1 },
{ src: "Outre", votes: 1 }, { src: 'Outre', votes: 1 },
{ src: "Slate", votes: 2 }, { src: 'Slate', votes: 2 },
{ src: "Case", votes: 2 }, { src: 'Case', votes: 2 },
{ src: "Materials", votes: 0 }, { src: 'Materials', votes: 0 },
{ src: "SeedDrive", votes: 0 }, { src: 'SeedDrive', votes: 0 },
{ src: "Monet", votes: 2 }, { src: 'Monet', votes: 2 },
{ src: "Lilypad", votes: 0 }, { src: 'Lilypad', votes: 0 },
{ src: "StoreBuddy", votes: 0 }, { src: 'StoreBuddy', votes: 0 },
{ src: "FileCabinet", votes: 0 }, { src: 'FileCabinet', votes: 0 },
{ src: "Jacana", votes: 1 }, { src: 'Jacana', votes: 1 },
{ src: "Argo", votes: 2 }, { src: 'Argo', votes: 2 },
{ src: "Octavius", votes: 0 }, { src: 'Octavius', votes: 0 },
{ src: "Corgi", votes: 0 }, { src: 'Corgi', votes: 0 },
{ src: "Filing Cabinet", votes: 0 }, { src: 'Filing Cabinet', votes: 0 },
{ src: "FileCorgi", votes: 0 }, { src: 'FileCorgi', votes: 0 },
{ src: "Max", votes: 0 }, { src: 'Max', votes: 0 },
{ src: "Nominal", votes: 0 }, { src: 'Nominal', votes: 0 },
{ src: "X AE", votes: 0 }, { src: 'X AE', votes: 0 },
{ src: "A 12", votes: 0 }, { src: 'A 12', votes: 0 },
]; ];
export default class LogoNameTest extends React.Component { export default class LogoNameTest extends React.Component {
@ -155,17 +155,13 @@ export default class LogoNameTest extends React.Component {
return ( return (
<div css={STYLES_LAYOUT}> <div css={STYLES_LAYOUT}>
<p css={STYLES_PARAGRAPH}> <p css={STYLES_PARAGRAPH}>
If you are looking at this page, it is a list of name and logo ideas If you are looking at this page, it is a list of name and logo ideas (I did not make them). Names and logos do
(I did not make them). Names and logos do not correspond with each not correspond with each other.{' '}
other.{" "} <strong>Please let me know which ones you like through Slack direct message.</strong>
<strong>
Please let me know which ones you like through Slack direct message.
</strong>
<br /> <br />
<br /> <br />
All votes are anonymous. We will take the votes, consolidate the All votes are anonymous. We will take the votes, consolidate the choices and then ask other designers to help
choices and then ask other designers to help out with refining what out with refining what the team prefers.
the team prefers.
<br /> <br />
<br /> <br />
Key Key
@ -183,18 +179,14 @@ export default class LogoNameTest extends React.Component {
<span <span
css={STYLES_PILL} css={STYLES_PILL}
style={{ style={{
backgroundColor: "#0047FF", backgroundColor: '#0047FF',
bottom: "auto", bottom: 'auto',
top: -16, top: -16,
}} }}>
>
{n.votes} {n.votes}
</span> </span>
) : null} ) : null}
<span <span css={STYLES_CHOICE} style={{ background: 'transparent', color: '#000' }}>
css={STYLES_CHOICE}
style={{ background: "transparent", color: "#000" }}
>
Option #{index + 1} Option #{index + 1}
</span> </span>
</span> </span>
@ -208,20 +200,15 @@ export default class LogoNameTest extends React.Component {
<div> <div>
{LOGOS.map((l, index) => { {LOGOS.map((l, index) => {
return ( return (
<span <span key={l.src} css={STYLES_LOGO} style={{ backgroundImage: `url('/static/temp/${l.src}')` }}>
key={l.src}
css={STYLES_LOGO}
style={{ backgroundImage: `url('/static/${l.src}')` }}
>
{l.votes > 0 ? ( {l.votes > 0 ? (
<span <span
css={STYLES_PILL} css={STYLES_PILL}
style={{ style={{
backgroundColor: "#0047FF", backgroundColor: '#0047FF',
bottom: "auto", bottom: 'auto',
top: -16, top: -16,
}} }}>
>
{l.votes} {l.votes}
</span> </span>
) : null} ) : null}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1000 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 857 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

View File

@ -1,17 +1,17 @@
import * as React from "react"; import * as React from 'react';
import * as Strings from "~/common/strings"; import * as Strings from '~/common/strings';
import * as Constants from "~/common/constants"; import * as Constants from '~/common/constants';
import * as Fixtures from "~/common/fixtures"; import * as Fixtures from '~/common/fixtures';
import * as System from "~/components/system"; import * as System from '~/components/system';
import * as SchemaTable from "~/common/schema-table"; import * as SchemaTable from '~/common/schema-table';
import { css } from "@emotion/react"; import { css } from '@emotion/react';
import ScenePage from "~/components/core/ScenePage"; import ScenePage from '~/components/core/ScenePage';
import Section from "~/components/core/Section"; import Section from '~/components/core/Section';
export default class SceneDataTransfer extends React.Component { export default class SceneDataTransfer extends React.Component {
state = { sub_navigation: "1" }; state = { sub_navigation: '1' };
_handleChange = (e) => { _handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value }); this.setState({ [e.target.name]: e.target.value });
@ -26,19 +26,15 @@ export default class SceneDataTransfer extends React.Component {
style={{ marginTop: 24 }} style={{ marginTop: 24 }}
name="sub_navigation" name="sub_navigation"
options={[ options={[
{ value: "1", label: "Current transfers" }, { value: '1', label: 'Current transfers' },
{ value: "2", label: "Past transfers" }, { value: '2', label: 'Past transfers' },
]} ]}
value={this.state.sub_navigation} value={this.state.sub_navigation}
onChange={this._handleChange} onChange={this._handleChange}
/> />
{this.state.sub_navigation === "2" ? ( {this.state.sub_navigation === '2' ? (
<Section <Section title="Past transfers" onAction={this.props.onAction} onNavigateTo={this.props.onNavigateTo}>
title="Past transfers"
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
>
<System.Table <System.Table
data={{ data={{
columns: SchemaTable.DataTransfer, columns: SchemaTable.DataTransfer,
@ -53,19 +49,8 @@ export default class SceneDataTransfer extends React.Component {
</Section> </Section>
) : null} ) : null}
{this.state.sub_navigation === "1" ? ( {this.state.sub_navigation === '1' ? (
<Section <Section onAction={this.props.onAction} onNavigateTo={this.props.onNavigateTo} title="Current transfers">
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
title="Current transfers"
buttons={[
{
name: "Cancel all",
type: "ACTION",
value: "ACTION_CANCEL_DATA_TRANSFERS",
},
]}
>
<System.P style={{ padding: 24 }}>There are no transfers</System.P> <System.P style={{ padding: 24 }}>There are no transfers</System.P>
</Section> </Section>
) : null} ) : null}

View File

@ -1,14 +1,14 @@
import * as React from "react"; import * as React from 'react';
import * as Strings from "~/common/strings"; import * as Strings from '~/common/strings';
import * as Constants from "~/common/constants"; import * as Constants from '~/common/constants';
import * as Fixtures from "~/common/fixtures"; import * as Fixtures from '~/common/fixtures';
import * as System from "~/components/system"; import * as System from '~/components/system';
import * as SchemaTable from "~/common/schema-table"; import * as SchemaTable from '~/common/schema-table';
import { css } from "@emotion/react"; import { css } from '@emotion/react';
import Section from "~/components/core/Section"; import Section from '~/components/core/Section';
import ScenePage from "~/components/core/ScenePage"; import ScenePage from '~/components/core/ScenePage';
export default class SceneDeals extends React.Component { export default class SceneDeals extends React.Component {
state = {}; state = {};
@ -20,18 +20,7 @@ export default class SceneDeals extends React.Component {
render() { render() {
return ( return (
<ScenePage> <ScenePage>
<Section <Section onAction={this.props.onAction} onNavigateTo={this.props.onNavigateTo} title="All deals" buttons={[]}>
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
title="All deals"
buttons={[
{
name: "Export",
type: "DOWNLOAD",
value: "CSV_ALL_DEALS",
},
]}
>
<System.Table <System.Table
onAction={this.props.onAction} onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo} onNavigateTo={this.props.onNavigateTo}

View File

@ -37,12 +37,7 @@ export default class SceneEditAccount extends React.Component {
body: data, body: data,
}; };
const response = await fetch(`/_/upload/avatar`, options); await fetch(`/_/upload/avatar`, options);
const json = await response.json();
if (json && json.success) {
console.log('reload');
}
}; };
_handleChange = (e) => { _handleChange = (e) => {
@ -64,7 +59,7 @@ export default class SceneEditAccount extends React.Component {
<div style={{ marginTop: 24 }}> <div style={{ marginTop: 24 }}>
<input css={STYLES_FILE_HIDDEN} type="file" id="file" onChange={this._handleUpload} /> <input css={STYLES_FILE_HIDDEN} type="file" id="file" onChange={this._handleUpload} />
<System.ButtonPrimary style={{ margin: '0 16px 16px 0' }} type="label" for="file"> <System.ButtonPrimary style={{ margin: '0 16px 16px 0' }} type="label" htmlFor="file">
Upload Upload
</System.ButtonPrimary> </System.ButtonPrimary>
</div> </div>

View File

@ -1,14 +1,14 @@
import * as React from "react"; import * as React from 'react';
import * as Strings from "~/common/strings"; import * as Strings from '~/common/strings';
import * as Constants from "~/common/constants"; import * as Constants from '~/common/constants';
import * as Fixtures from "~/common/fixtures"; import * as Fixtures from '~/common/fixtures';
import * as System from "~/components/system"; import * as System from '~/components/system';
import * as SVG from "~/components/system/svg"; import * as SVG from '~/components/system/svg';
import { css } from "@emotion/react"; import { css } from '@emotion/react';
import Section from "~/components/core/Section"; import Section from '~/components/core/Section';
import ScenePage from "~/components/core/ScenePage"; import ScenePage from '~/components/core/ScenePage';
const STYLES_FLEX = css` const STYLES_FLEX = css`
display: flex; display: flex;
@ -21,6 +21,7 @@ const STYLES_FLEX = css`
const STYLES_TOP = css` const STYLES_TOP = css`
background: ${Constants.system.pitchBlack}; background: ${Constants.system.pitchBlack};
border-bottom: 1px solid ${Constants.system.black};
color: ${Constants.system.white}; color: ${Constants.system.white};
width: 100%; width: 100%;
padding: 12px 16px 12px 48px; padding: 12px 16px 12px 48px;
@ -53,7 +54,9 @@ const STYLES_ASSET = css`
padding: 0; padding: 0;
min-height: 10%; min-height: 10%;
height: 100%; height: 100%;
background-size: cover; background-color: ${Constants.system.pitchBlack};
background-size: contain;
background-repeat: no-repeat;
background-position: 50% 50%; background-position: 50% 50%;
`; `;
@ -69,7 +72,7 @@ const STYLES_BOTTOM = css`
`; `;
const STYLES_PATH = css` const STYLES_PATH = css`
font-family: "mono"; font-family: 'mono';
color: ${Constants.system.white}; color: ${Constants.system.white};
font-size: 12px; font-size: 12px;
text-transform: uppercase; text-transform: uppercase;
@ -87,7 +90,7 @@ const STYLES_ITEM = css`
justify-content: center; justify-content: center;
font-size: 12px; font-size: 12px;
letter-spacing: 0.2px; letter-spacing: 0.2px;
font-family: "inter-semi-bold"; font-family: 'inter-semi-bold';
transition: 200ms ease all; transition: 200ms ease all;
cursor: pointer; cursor: pointer;
background-color: ${Constants.system.brand}; background-color: ${Constants.system.brand};
@ -113,7 +116,7 @@ export default class SceneFile extends React.Component {
}; };
render() { render() {
const fileURL = `/static/${this.props.file.file}`; const fileURL = `/static/files/${this.props.file.file}`;
return ( return (
<div css={STYLES_FLEX}> <div css={STYLES_FLEX}>
@ -125,20 +128,7 @@ export default class SceneFile extends React.Component {
<SVG.Dismiss height="24px" /> <SVG.Dismiss height="24px" />
</div> </div>
</div> </div>
<div <div css={STYLES_ASSET} style={{ backgroundImage: `url('${fileURL}')` }} />
css={STYLES_ASSET}
style={{ backgroundImage: `url('${fileURL}')` }}
/>
<div css={STYLES_BOTTOM}>
<div css={STYLES_LEFT}>
<span css={STYLES_PATH}>{this.props.file["cid"]}</span>
</div>
<div css={STYLES_RIGHT}>
<span css={STYLES_ITEM}>Copy CID</span>
<span css={STYLES_ITEM}>Copy gateways</span>
<span css={STYLES_ITEM}>Copy to downloads</span>
</div>
</div>
</div> </div>
); );
} }

View File

@ -1,13 +1,13 @@
import * as React from "react"; import * as React from 'react';
import * as Strings from "~/common/strings"; import * as Strings from '~/common/strings';
import * as Constants from "~/common/constants"; import * as Constants from '~/common/constants';
import * as Fixtures from "~/common/fixtures"; import * as Fixtures from '~/common/fixtures';
import * as System from "~/components/system"; import * as System from '~/components/system';
import { css } from "@emotion/react"; import { css } from '@emotion/react';
import Section from "~/components/core/Section"; import Section from '~/components/core/Section';
import ScenePage from "~/components/core/ScenePage"; import ScenePage from '~/components/core/ScenePage';
export default class SceneFilesFolder extends React.Component { export default class SceneFilesFolder extends React.Component {
state = {}; state = {};
@ -24,29 +24,21 @@ export default class SceneFilesFolder extends React.Component {
const data = { const data = {
columns: [ columns: [
{ key: "icon", hideLabel: true, width: "32px", type: "ICON" }, { key: 'icon', hideLabel: true, width: '32px', type: 'ICON' },
{ key: "file", name: "File", width: "100%", type: "FILE_LINK" }, { key: 'file', name: 'File', width: '100%', type: 'FILE_LINK' },
{ key: "size", name: "Size", width: "140px" }, { key: 'size', name: 'Size', width: '140px', type: 'FILE_SIZE' },
{ {
key: "date", key: 'date',
name: "Date uploaded", name: 'Date uploaded',
width: "160px", width: '160px',
tooltip: tooltip: 'This date represents when the file was first uploaded to the network.',
"This date represents when the file was first uploaded to the network.", type: 'FILE_DATE',
}, },
{ {
key: "remaining", key: 'storage_status',
name: "Remaining time", name: 'Status',
tooltip: type: 'DEAL_STATUS',
"This file will remain available to you on the network for a limited time. Once time elapses you will have to create a new storage deal.",
width: "180px",
}, },
{
key: "retrieval-status",
name: "Status",
type: "DEAL_STATUS_RETRIEVAL",
},
{ key: "errors", hideLabel: true, type: "NOTIFICATION_ERROR" },
], ],
rows, rows,
}; };
@ -59,17 +51,11 @@ export default class SceneFilesFolder extends React.Component {
title={this.props.data.name} title={this.props.data.name}
buttons={[ buttons={[
{ {
name: "Retrieve", name: 'Store file on network',
type: "SIDEBAR", type: 'SIDEBAR',
value: "SIDEBAR_FILE_RETRIEVAL_DEAL", value: 'SIDEBAR_FILE_STORAGE_DEAL',
}, },
{ ]}>
name: "Store file on network",
type: "SIDEBAR",
value: "SIDEBAR_FILE_STORAGE_DEAL",
},
]}
>
<System.Table <System.Table
key={this.props.data.folderId} key={this.props.data.folderId}
data={data} data={data}

View File

@ -54,43 +54,50 @@ export default class SceneHome extends React.Component {
render() { render() {
return ( return (
<ScenePage> <ScenePage>
<Section {this.props.viewer.library[0] ? (
onAction={this.props.onAction} <Section
onNavigateTo={this.props.onNavigateTo}
title="Recent data"
buttons={[
{
name: 'View files',
type: 'NAVIGATE',
value: 'folder-root',
},
{
name: 'Store file on network',
type: 'SIDEBAR',
value: 'SIDEBAR_FILE_STORAGE_DEAL',
},
]}>
<System.Table
data={{
columns: [
{ key: 'file', name: 'File', type: 'FILE_LINK' },
{
key: 'date',
name: 'Date uploaded',
width: '160px',
tooltip: 'This date represents when the file was first uploaded to the network.',
},
{ key: 'remaining', name: 'Remaining time', width: '180px' },
],
rows: [],
}}
selectedRowId={this.state.data}
onChange={this._handleChange}
onAction={this.props.onAction} onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo} onNavigateTo={this.props.onNavigateTo}
name="data" title="Recent data"
/> buttons={[
</Section> {
name: 'View files',
type: 'NAVIGATE',
value: this.props.viewer.library[0].folderId,
},
{
name: 'Store file on network',
type: 'SIDEBAR',
value: 'SIDEBAR_FILE_STORAGE_DEAL',
},
]}>
<System.Table
data={{
columns: [
{ key: 'file', name: 'File', type: 'FILE_LINK' },
{ key: 'size', name: 'Size', width: '140px', type: 'FILE_SIZE' },
{
key: 'date',
name: 'Date uploaded',
width: '160px',
type: 'FILE_DATE',
},
{
key: 'storage_status',
name: 'Status',
type: 'DEAL_STATUS',
},
],
rows: this.props.viewer.library[0].children,
}}
selectedRowId={this.state.data}
onChange={this._handleChange}
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
name="data"
/>
</Section>
) : null}
{this.props.viewer.addresses[0] ? ( {this.props.viewer.addresses[0] ? (
<Section <Section

View File

@ -33,7 +33,7 @@ export default class SceneSettings extends React.Component {
_deferredSave = null; _deferredSave = null;
_handleSave = async () => { _handleSave = async () => {
const response = await Actions.setDefaultConfig({ await Actions.setDefaultConfig({
config: { config: {
hot: { hot: {
enabled: this.props.viewer.settings_cold_enabled, enabled: this.props.viewer.settings_cold_enabled,
@ -46,7 +46,7 @@ export default class SceneSettings extends React.Component {
enabled: this.props.viewer.settings_cold_enabled, enabled: this.props.viewer.settings_cold_enabled,
filecoin: { filecoin: {
addr: this.props.viewer.settings_cold_default_address, addr: this.props.viewer.settings_cold_default_address,
dealDuration: this.props.viewer.settings_cold_default_duration, dealMinDuration: this.props.viewer.settings_cold_default_duration,
repFactor: this.props.viewer.settings_cold_default_replication_factor, repFactor: this.props.viewer.settings_cold_default_replication_factor,
excludedMinersList: this.props.viewer.settings_cold_default_excluded_miners, excludedMinersList: this.props.viewer.settings_cold_default_excluded_miners,
trustedMinersList: this.props.viewer.settings_cold_default_trusted_miners, trustedMinersList: this.props.viewer.settings_cold_default_trusted_miners,
@ -59,8 +59,6 @@ export default class SceneSettings extends React.Component {
}, },
}, },
}); });
await this.props.rehydrate();
}; };
_handleChange = (e) => { _handleChange = (e) => {
@ -143,6 +141,7 @@ export default class SceneSettings extends React.Component {
description="Default Filecoin deal duration settings description." description="Default Filecoin deal duration settings description."
tooltip="Placeholder." tooltip="Placeholder."
name="settings_cold_default_duration" name="settings_cold_default_duration"
type="number"
value={this.props.viewer.settings_cold_default_duration} value={this.props.viewer.settings_cold_default_duration}
placeholder="Type in months" placeholder="Type in months"
onChange={this._handleChange} onChange={this._handleChange}

333
server.js
View File

@ -1,9 +1,10 @@
import { createPow } from '@textile/powergate-client'; import { createPow, ffs } from '@textile/powergate-client';
const host = 'http://0.0.0.0:6002'; const host = 'http://0.0.0.0:6002';
const pow = createPow({ host }); const PowerGate = createPow({ host });
import * as Middleware from '~/common/middleware'; import * as Middleware from '~/common/middleware';
import * as Strings from '~/common/strings';
import FS from 'fs'; import FS from 'fs';
import express from 'express'; import express from 'express';
@ -11,15 +12,22 @@ import formidable from 'formidable';
import next from 'next'; import next from 'next';
import bodyParser from 'body-parser'; import bodyParser from 'body-parser';
import compression from 'compression'; import compression from 'compression';
import WebSocketServer from 'ws';
const dev = process.env.NODE_ENV !== 'production'; const dev = process.env.NODE_ENV !== 'production';
const port = process.env.PORT || 1337; const port = process.env.PORT || 1337;
const wsPort = 2448;
const app = next({ dev, quiet: false }); const app = next({ dev, quiet: false });
const nextRequestHandler = app.getRequestHandler(); const nextRequestHandler = app.getRequestHandler();
const AVATAR_STORAGE_URL = `${__dirname}/public/static/system/`; const AVATAR_STORAGE_URL = `${__dirname}/public/static/system/`;
const FILE_STORAGE_URL = `${__dirname}/public/static/files/`;
// TODO(jim): Just a solution for testing. // TODO(jim): Just a solution for testing.
// Probably should refactor this or use a database.
let client = null;
let token = null; let token = null;
let library = null;
let status = null; let status = null;
let messageList = null; let messageList = null;
let peersList = null; let peersList = null;
@ -27,22 +35,66 @@ let addrsList = null;
let info = null; let info = null;
const refresh = async () => { const refresh = async () => {
const Health = await pow.health.check(); const Health = await PowerGate.health.check();
status = Health.status ? Health.status : null; status = Health.status ? Health.status : null;
messageList = Health.messageList ? Health.messageList : null; messageList = Health.messageList ? Health.messageList : null;
const Peers = await pow.net.peers(); const Peers = await PowerGate.net.peers();
peersList = Peers.peersList ? Peers.peersList : null; peersList = Peers.peersList ? Peers.peersList : null;
}; };
const refreshWithToken = async () => { const refreshWithToken = async () => {
const Addresses = await pow.ffs.addrs(); const Addresses = await PowerGate.ffs.addrs();
addrsList = Addresses.addrsList ? Addresses.addrsList : null; addrsList = Addresses.addrsList ? Addresses.addrsList : null;
const NetworkInfo = await pow.ffs.info(); const NetworkInfo = await PowerGate.ffs.info();
info = NetworkInfo.info ? NetworkInfo.info : null; info = NetworkInfo.info ? NetworkInfo.info : null;
}; };
const getFileName = (s) => {
let target = s;
if (target.endsWith('/')) {
target = target.substring(0, target.length - 1);
}
return target.substr(target.lastIndexOf('/') + 1);
};
const createFile = (id, data) => {
return {
decorator: 'FILE',
id: id,
icon: 'PNG',
file: getFileName(id),
miner: null,
job_id: null,
cid: null,
date: new Date(),
size: data.size,
amount: 0,
remaining: null,
deal_category: 1,
retrieval_status: 0,
storage_status: 0,
errors: [],
};
};
const createFolder = (id) => {
return {
decorator: 'FOLDER',
id,
folderId: id,
icon: 'FOLDER',
file: getFileName(id),
name: getFileName(id),
pageTitle: null,
date: null,
size: null,
children: [],
};
};
const getData = async () => { const getData = async () => {
const data = { const data = {
production: !dev, production: !dev,
@ -51,47 +103,134 @@ const getData = async () => {
peersList, peersList,
addrsList, addrsList,
info, info,
library,
}; };
console.log('ON THE SERVER', data);
return data; return data;
}; };
app.prepare().then(async () => { const emitStateUpdate = async () => {
try { await refresh();
await refresh(); await refreshWithToken();
const data = await getData();
client.send(JSON.stringify({ action: 'UPDATE_VIEWER', data }));
};
// NOTE(jim): This is a configuration folder with all of the client tokens. const checkFileStatus = async () => {
!FS.existsSync(`./.data`) && FS.mkdirSync(`./.data`, { recursive: true }); // TODO(jim): Refactor this so we repeat this less often.
let write = false;
for (let i = 0; i < library.length; i++) {
for (let j = 0; j < library[i].children.length; j++) {
if (library[i].children[j].job_id) {
if (library[i].children[j].storage_status === 1) {
library[i].children[j].storage_status = 2;
write = true;
continue;
}
// NOTE(jim): This will create a token for authentication with powergate. PowerGate.ffs.watchJobs((job) => {
if (!FS.existsSync('./.data/powergate-token')) { console.log(job);
const FFS = await pow.ffs.create(); if (job.status === ffs.JobStatus.JOB_STATUS_SUCCESS) {
token = FFS.token ? FFS.token : null; library[i].children[j].storage_status = 6;
write = true;
// NOTE(jim): Write a new token file. }
if (token) { }, library[i].children[j].job_id);
FS.writeFileSync('./.data/powergate-token', token);
} }
} else {
token = FS.readFileSync('./.data/powergate-token', 'utf8');
} }
if (token) {
pow.setToken(token);
}
await refreshWithToken();
} catch (e) {
console.log(e);
} }
if (!token) { if (write) {
throw new Error('[ prototype ] can not start client without proper auth token'); FS.writeFileSync('./.data/library.json', JSON.stringify({ library }));
}
};
const setIntervalViewerUpdates = async () => {
console.log('[ prototype ] checking for library deal updates.');
if (client) {
try {
await emitStateUpdate();
await checkFileStatus();
} catch (e) {}
}
setTimeout(setIntervalViewerUpdates, 5000);
};
const resetAllLocalData = async () => {
// NOTE(jim): For testing purposes.
// We wipe all of the local data each time you run the application.
console.log('[ prototype ] deleting old token and library data ');
FS.rmdirSync('./.data', { recursive: true });
console.log('[ prototype ] deleting old avatar data ');
FS.rmdirSync(AVATAR_STORAGE_URL, { recursive: true });
console.log('[ prototype ] deleting old file data ');
FS.rmdirSync(FILE_STORAGE_URL, { recursive: true });
console.log('[ prototype ] creating new avatar folder ');
FS.mkdirSync(AVATAR_STORAGE_URL, { recursive: true });
FS.writeFileSync(`${AVATAR_STORAGE_URL}.gitkeep`, '');
console.log('[ prototype ] creating new local file folder ');
FS.mkdirSync(FILE_STORAGE_URL, { recursive: true });
FS.writeFileSync(`${FILE_STORAGE_URL}.gitkeep`, '');
};
app.prepare().then(async () => {
if (dev) {
await resetAllLocalData();
try {
await refresh();
// NOTE(jim): This is a configuration folder with all of the client tokens.
!FS.existsSync(`./.data`) && FS.mkdirSync(`./.data`, { recursive: true });
// NOTE(jim): This will create a token for authentication with powergate.
if (!FS.existsSync('./.data/powergate-token')) {
const FFS = await PowerGate.ffs.create();
token = FFS.token ? FFS.token : null;
// NOTE(jim): Write a new token file.
if (token) {
FS.writeFileSync('./.data/powergate-token', token);
}
} else {
token = FS.readFileSync('./.data/powergate-token', 'utf8');
}
if (token) {
PowerGate.setToken(token);
}
await refreshWithToken();
if (!FS.existsSync('./.data/library.json')) {
const librarySchema = { library: [{ ...createFolder(FILE_STORAGE_URL), file: 'Files', name: 'Files' }] };
FS.writeFileSync('./.data/library.json', JSON.stringify(librarySchema));
library = librarySchema.library;
} else {
const parsedLibrary = FS.readFileSync('./.data/library.json', 'utf8');
library = JSON.parse(parsedLibrary).library;
}
} catch (e) {
console.log(e);
}
} }
const server = express(); const server = express();
const WSS = new WebSocketServer.Server({ port: wsPort });
WSS.on('connection', (s) => {
// TODO(jim): Suppport more than one client.
client = s;
s.on('close', function () {
s.send(JSON.stringify({ action: null, data: 'closed' }));
});
s.send(JSON.stringify({ action: null, data: 'connected' }));
});
if (!dev) { if (!dev) {
server.use(compression()); server.use(compression());
@ -106,8 +245,87 @@ app.prepare().then(async () => {
}) })
); );
server.get('/health', async (req, res) => { server.post('/_/viewer', async (req, res) => {
res.send('ok'); if (dev) {
await refresh();
await refreshWithToken();
}
return res.status(200).send({ success: true, data: await getData() });
});
server.post('/_/deals/storage', async (req, res) => {
if (Strings.isEmpty(req.body.src)) {
return res.status(500).send({ success: false });
}
const localPath = `${__dirname}${req.body.src}`;
const buffer = FS.readFileSync(localPath);
const { cid } = await PowerGate.ffs.addToHot(buffer);
const { jobId } = await PowerGate.ffs.pushConfig(cid);
// TODO(jim): Refactor this so we repeat this less often.
let write = false;
for (let i = 0; i < library.length; i++) {
for (let j = 0; j < library[i].children.length; j++) {
if (localPath === library[i].children[j].id) {
library[i].children[j].job_id = jobId;
library[i].children[j].cid = cid;
library[i].children[j].storage_status = 1;
write = true;
}
}
}
if (write) {
FS.writeFileSync('./.data/library.json', JSON.stringify({ library }));
}
await emitStateUpdate();
return res.status(200).send({ success: true, cid, jobId });
});
server.post('/_/storage/:file', async (req, res) => {
const form = formidable({ multiples: true, uploadDir: FILE_STORAGE_URL });
form.once('error', console.error);
form.on('progress', (bytesReceived, bytesExpected) => {
console.log(`[ prototype ] ${bytesReceived} / ${bytesExpected}`);
});
form.parse(req, async (error, fields, files) => {
if (error) {
return res.status(500).send({ error });
} else {
if (!files.image) {
console.error('[ prototype ] File type unspported', files);
return res.status(500).send({ error: 'File type unsupported', files });
}
const newPath = form.uploadDir + req.params.file;
FS.rename(files.image.path, newPath, function (err) {});
const localFile = createFile(newPath, files.image);
let pushed = false;
for (let i = 0; i < library.length; i++) {
const currentFolder = library[i];
if (!pushed) {
currentFolder.children.push(localFile);
pushed = true;
break;
}
}
if (pushed) {
FS.writeFileSync('./.data/library.json', JSON.stringify({ library }));
}
await emitStateUpdate();
return res.status(200).send({ success: true, file: localFile });
}
});
}); });
server.post('/_/upload/avatar', async (req, res) => { server.post('/_/upload/avatar', async (req, res) => {
@ -119,83 +337,68 @@ app.prepare().then(async () => {
console.log(`[ prototype ] ${bytesReceived} / ${bytesExpected}`); console.log(`[ prototype ] ${bytesReceived} / ${bytesExpected}`);
}); });
form.on('fileBegin', (filename, file) => { form.parse(req, async (error, fields, files) => {
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) { if (error) {
return res.status(500).send({ error }); return res.status(500).send({ error });
} else { } else {
const newPath = form.uploadDir + 'avatar.png'; const newPath = form.uploadDir + 'avatar.png';
FS.rename(files.image.path, newPath, function (err) {}); FS.rename(files.image.path, newPath, function (err) {});
await emitStateUpdate();
return res.status(200).send({ success: true }); return res.status(200).send({ success: true });
} }
}); });
}); });
server.post('/_/viewer', async (req, res) => {
await refresh();
await refreshWithToken();
return res.status(200).send({ success: true, data: await getData() });
});
server.post('/_/settings', async (req, res) => { server.post('/_/settings', async (req, res) => {
let data; let data;
try { try {
data = await pow.ffs.setDefaultConfig(req.body.config); data = await PowerGate.ffs.setDefaultConfig(req.body.config);
} catch (e) { } catch (e) {
return res.status(500).send({ error: e.message }); return res.status(500).send({ error: e.message });
} }
await emitStateUpdate();
return res.status(200).send({ success: true, data }); return res.status(200).send({ success: true, data });
}); });
server.post('/_/wallet/create', async (req, res) => { server.post('/_/wallet/create', async (req, res) => {
let data; let data;
try { try {
data = await pow.ffs.newAddr(req.body.name, req.body.type, req.body.makeDefault); data = await PowerGate.ffs.newAddr(req.body.name, req.body.type, req.body.makeDefault);
} catch (e) { } catch (e) {
return res.status(500).send({ error: e.message }); return res.status(500).send({ error: e.message });
} }
await emitStateUpdate();
return res.status(200).send({ success: true, data }); return res.status(200).send({ success: true, data });
}); });
server.post('/_/wallet/send', async (req, res) => { server.post('/_/wallet/send', async (req, res) => {
let data; let data;
try { try {
data = await pow.ffs.sendFil(req.body.source, req.body.target, req.body.amount); data = await PowerGate.ffs.sendFil(req.body.source, req.body.target, req.body.amount);
} catch (e) { } catch (e) {
return res.status(500).send({ error: e.message }); return res.status(500).send({ error: e.message });
} }
await emitStateUpdate();
return res.status(200).send({ success: true, data: { ...data, ...req.body } }); return res.status(200).send({ success: true, data: { ...data, ...req.body } });
}); });
server.get('/', async (req, res) => { server.get('/', async (req, res) => {
return app.render(req, res, '/', { production: false }); if (!dev) {
return res.redirect('https://github.com/filecoin-project/filecoin-client');
}
return app.render(req, res, '/', { production: false, wsPort });
}); });
server.get('*', async (req, res) => { server.get('*', async (req, res) => {
return nextRequestHandler(req, res, req.url); return nextRequestHandler(req, res, req.url);
}); });
server.listen(port, (err) => { server.listen(port, async (err) => {
if (err) { if (err) {
throw err; throw err;
} }
@ -204,5 +407,7 @@ app.prepare().then(async () => {
console.log('[ prototype ] powergate token:', token); console.log('[ prototype ] powergate token:', token);
console.log(`[ prototype ] listening on: http://localhost:${port}`); console.log(`[ prototype ] listening on: http://localhost:${port}`);
console.log(`[ prototype ] avatar storage: ${AVATAR_STORAGE_URL}`); console.log(`[ prototype ] avatar storage: ${AVATAR_STORAGE_URL}`);
await setIntervalViewerUpdates();
}); });
}); });