prototype: local file storage and local preview
14
.gitignore
vendored
@ -1,10 +1,9 @@
|
||||
.next
|
||||
.data
|
||||
.env
|
||||
.DS_STORE
|
||||
DS_STORE
|
||||
package-lock.json
|
||||
node_modules
|
||||
DS_STORE
|
||||
|
||||
/**/*/package-lock.json
|
||||
/**/*/.DS_STORE
|
||||
@ -12,5 +11,12 @@ DS_STORE
|
||||
/**/*/.next
|
||||
/**/*/.data
|
||||
|
||||
.data/**/*
|
||||
.library/**/*
|
||||
.data/*
|
||||
!.data/.gitkeep
|
||||
|
||||
public/static/files/*
|
||||
!public/static/files/.gitkeep
|
||||
|
||||
public/static/system/*
|
||||
!public/static/system/.gitkeep
|
||||
!public/static/system/avatar.png
|
@ -8,7 +8,9 @@ const REQUEST_HEADERS = {
|
||||
'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 () => {
|
||||
const options = {
|
||||
|
@ -1,481 +1,17 @@
|
||||
import * as Strings from '~/common/strings';
|
||||
import * as Data from '~/common/data';
|
||||
|
||||
const LOCAL_CONFIG = `
|
||||
"Identity": {
|
||||
"PeerID": "Qma9T5YraSnpRDZqRR4krcS",
|
||||
"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];
|
||||
const constructFilesTreeForNavigation = (library) => {
|
||||
for (let i = 0; i < library.length; i++) {
|
||||
for (let j = 0; j < library[i].children.length; j++) {
|
||||
let e = library[i].children[j];
|
||||
if (e.decorator === 'FILE') {
|
||||
library[i].children[j].ignore = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return library;
|
||||
};
|
||||
|
||||
const getFileById = (id) => {
|
||||
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 = [
|
||||
export const generateNavigationState = (library) => [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Home',
|
||||
@ -488,79 +24,8 @@ export const NavigationState = [
|
||||
name: 'Wallet',
|
||||
pageTitle: 'your wallet and addresses',
|
||||
decorator: 'WALLET',
|
||||
/*
|
||||
children: [
|
||||
{
|
||||
id: 3,
|
||||
name: 'Payment Channels',
|
||||
children: null,
|
||||
pageTitle: 'your payment channels',
|
||||
decorator: 'CHANNELS',
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
constructFilesTreeForNavigation(),
|
||||
{
|
||||
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,
|
||||
},
|
||||
*/
|
||||
...constructFilesTreeForNavigation(library),
|
||||
{
|
||||
id: 13,
|
||||
name: 'Edit account',
|
||||
|
@ -35,13 +35,26 @@ const transformPeers = (peersList) => {
|
||||
};
|
||||
|
||||
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 {
|
||||
id: info.id,
|
||||
name: 'New Node',
|
||||
photoURL: '/static/system/avatar.png',
|
||||
config: '',
|
||||
upload_bandwidth: 0,
|
||||
download_bandwidth: 0,
|
||||
|
||||
@ -53,7 +66,7 @@ export const getInitialState = (props) => {
|
||||
|
||||
settings_cold_enabled: info.defaultConfig.cold.enabled,
|
||||
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_excluded_miners: info.defaultConfig.cold.filecoin.excludedMinersList,
|
||||
settings_cold_default_trusted_miners: info.defaultConfig.cold.filecoin.trustedMinersList,
|
||||
@ -68,5 +81,6 @@ export const getInitialState = (props) => {
|
||||
peers: transformPeers(peersList),
|
||||
deals: [],
|
||||
addresses: transformAddresses(addrsList, info),
|
||||
library,
|
||||
};
|
||||
};
|
||||
|
@ -11,6 +11,7 @@ const STYLES_AVATAR = css`
|
||||
background-position: 50% 50%;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
background-color: ${Constants.system.black};
|
||||
`;
|
||||
|
||||
const STYLES_AVATAR_ONLINE = css`
|
||||
|
@ -6,6 +6,16 @@ import * as System from '~/components/system';
|
||||
|
||||
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`
|
||||
font-size: ${Constants.typescale.lvl1};
|
||||
font-family: 'inter-medium';
|
||||
@ -49,13 +59,64 @@ const SELECT_MENU_MAP = {
|
||||
|
||||
export default class SidebarFileStorageDeal extends React.Component {
|
||||
state = {
|
||||
settings_deal_duration: this.props.viewer.settings_cold_default_duration,
|
||||
settings_replication_factor: this.props.viewer.settings_cold_default_replication_factor,
|
||||
file: null,
|
||||
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 = () => {
|
||||
alert('TODO: Make a storage deal');
|
||||
this.props.onSubmit({});
|
||||
_handleUpload = async (e) => {
|
||||
e.persist();
|
||||
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 = () => {
|
||||
@ -82,56 +143,68 @@ export default class SidebarFileStorageDeal extends React.Component {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<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_FOCUS}>test-image-upload.jpg</div>
|
||||
<div css={STYLES_SUBTEXT}>Name</div>
|
||||
</div>
|
||||
<div css={STYLES_ITEM}>
|
||||
<div css={STYLES_FOCUS}>{this.state.file.name}</div>
|
||||
<div css={STYLES_SUBTEXT}>Name</div>
|
||||
</div>
|
||||
|
||||
<div css={STYLES_ITEM}>
|
||||
<div css={STYLES_FOCUS}>42 MB</div>
|
||||
<div css={STYLES_SUBTEXT}>File size</div>
|
||||
</div>
|
||||
<div css={STYLES_ITEM}>
|
||||
<div css={STYLES_FOCUS}>{this.state.file.size}</div>
|
||||
<div css={STYLES_SUBTEXT}>File size</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
|
||||
containerStyle={{ marginTop: 48 }}
|
||||
label="Deal duration"
|
||||
name="settings_deal_duration"
|
||||
value={this.state.settings_deal_duration}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
{this.state.file ? (
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 48 }}
|
||||
label="Deal duration"
|
||||
name="settings_cold_default_duration"
|
||||
placeholder="Type in months"
|
||||
type="number"
|
||||
value={this.state.settings_cold_default_duration}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Replication factor"
|
||||
name="settings_replication_factor"
|
||||
value={this.state.settings_replication_factor}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
{this.state.file ? (
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Replication factor"
|
||||
name="settings_cold_default_replication_factor"
|
||||
value={this.state.settings_cold_default_replication_factor}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<System.SelectMenuFull
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
name="address"
|
||||
label="Payment address"
|
||||
value={this.props.selected.address}
|
||||
category="address"
|
||||
onChange={this.props.onSelectedChange}
|
||||
options={this.props.viewer.addresses}>
|
||||
{currentAddress.name}
|
||||
</System.SelectMenuFull>
|
||||
{this.state.file ? (
|
||||
<System.SelectMenuFull
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
name="address"
|
||||
label="Payment address"
|
||||
value={this.props.selected.address}
|
||||
category="address"
|
||||
onChange={this.props.onSelectedChange}
|
||||
options={this.props.viewer.addresses}>
|
||||
{currentAddress.name}
|
||||
</System.SelectMenuFull>
|
||||
) : null}
|
||||
|
||||
<div css={STYLES_ITEM}>
|
||||
<div css={STYLES_FOCUS}>2 FIL</div>
|
||||
<div css={STYLES_SUBTEXT}>Last order price</div>
|
||||
</div>
|
||||
|
||||
<System.ButtonPrimaryFull style={{ marginTop: 48 }} onClick={this._handleSubmit}>
|
||||
Make storage deal
|
||||
</System.ButtonPrimaryFull>
|
||||
{this.state.file ? (
|
||||
<System.ButtonPrimaryFull style={{ marginTop: 48 }} onClick={this._handleSubmit}>
|
||||
Make storage deal
|
||||
</System.ButtonPrimaryFull>
|
||||
) : null}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -431,11 +431,6 @@ export class Table extends React.Component {
|
||||
onAction: () => console.log('No action function set'),
|
||||
};
|
||||
|
||||
// NOTE(jim): Local state for local filtering.
|
||||
state = {
|
||||
data: this.props.data,
|
||||
};
|
||||
|
||||
_handleChange = (value) => {
|
||||
this.props.onChange({
|
||||
target: {
|
||||
@ -446,7 +441,7 @@ export class Table extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { data } = this.state;
|
||||
const { data } = this.props;
|
||||
|
||||
const ac = {};
|
||||
|
||||
@ -642,6 +637,10 @@ const STYLES_BUTTON_PRIMARY_FULL = css`
|
||||
`;
|
||||
|
||||
export const ButtonPrimaryFull = (props) => {
|
||||
if (props.type === 'label') {
|
||||
return <label 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) => {
|
||||
if (props.type === 'label') {
|
||||
return <label 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) => {
|
||||
if (props.type === 'label') {
|
||||
return <label css={STYLES_BUTTON_SECONDARY_FULL} {...props} />;
|
||||
}
|
||||
|
||||
return <button css={STYLES_BUTTON_SECONDARY_FULL} {...props} />;
|
||||
};
|
||||
|
||||
|
@ -1,30 +1,32 @@
|
||||
import * as React from "react";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as SVG from "~/components/system/svg";
|
||||
import * as OldSVG from "~/common/svg";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as React from 'react';
|
||||
import * as Constants from '~/common/constants';
|
||||
import * as SVG from '~/components/system/svg';
|
||||
import * as OldSVG from '~/common/svg';
|
||||
import * as Strings from '~/common/strings';
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { Tooltip } from "react-tippy";
|
||||
import { css } from '@emotion/react';
|
||||
import { Tooltip } from 'react-tippy';
|
||||
|
||||
import Avatar from "~/components/core/Avatar";
|
||||
import Avatar from '~/components/core/Avatar';
|
||||
|
||||
const STORAGE_DEAL_STATES = {
|
||||
"1": "Searching for miners.",
|
||||
"2": "Proposing storage deal.",
|
||||
"3": "Accepted by miners.",
|
||||
"4": "Data transfer in progress.",
|
||||
"5": "Data transfer complete.",
|
||||
"6": "Stored",
|
||||
'0': 'Local file only.',
|
||||
'1': 'Searching for miners.',
|
||||
'2': 'Proposing storage deal.',
|
||||
'3': 'Accepted by miners.',
|
||||
'4': 'Data transfer in progress.',
|
||||
'5': 'Data transfer complete.',
|
||||
'6': 'Stored on network.',
|
||||
};
|
||||
|
||||
const RETRIEVAL_DEAL_STATES = {
|
||||
"1": "Available on network",
|
||||
"2": "Retrieval deal proposed.",
|
||||
"3": "Retrieval deal accepted.",
|
||||
"4": "Data transfer in progress.",
|
||||
"5": "Data transfer completed.",
|
||||
"6": "Retrieved",
|
||||
'0': 'Local file',
|
||||
'1': 'Available on network',
|
||||
'2': 'Retrieval deal proposed.',
|
||||
'3': 'Retrieval deal accepted.',
|
||||
'4': 'Data transfer in progress.',
|
||||
'5': 'Data transfer completed.',
|
||||
'6': 'Retrieved from network.',
|
||||
};
|
||||
|
||||
const COMPONENTS_ICON = {
|
||||
@ -34,7 +36,7 @@ const COMPONENTS_ICON = {
|
||||
|
||||
const STYLES_TABLE_TAG = css`
|
||||
font-weight: 400;
|
||||
font-family: "inter-semi-bold";
|
||||
font-family: 'inter-semi-bold';
|
||||
letter-spacing: 0.2px;
|
||||
padding: 4px 6px 4px 6px;
|
||||
font-size: 10px;
|
||||
@ -46,21 +48,18 @@ const STYLES_TABLE_TAG = css`
|
||||
`;
|
||||
|
||||
const COMPONENTS_TRANSACTION_DIRECTION = {
|
||||
"1": (
|
||||
'1': (
|
||||
<span css={STYLES_TABLE_TAG} style={{ background: Constants.system.green }}>
|
||||
+ incoming
|
||||
</span>
|
||||
),
|
||||
"2": <span css={STYLES_TABLE_TAG}>- outgoing</span>,
|
||||
'2': <span css={STYLES_TABLE_TAG}>- outgoing</span>,
|
||||
};
|
||||
|
||||
const COMPONENTS_TRANSACTION_STATUS = {
|
||||
"1": <span css={STYLES_TABLE_TAG}>complete</span>,
|
||||
"2": (
|
||||
<span
|
||||
css={STYLES_TABLE_TAG}
|
||||
style={{ background: Constants.system.yellow }}
|
||||
>
|
||||
'1': <span css={STYLES_TABLE_TAG}>complete</span>,
|
||||
'2': (
|
||||
<span css={STYLES_TABLE_TAG} style={{ background: Constants.system.yellow }}>
|
||||
pending
|
||||
</span>
|
||||
),
|
||||
@ -115,7 +114,7 @@ const STYLES_CONTENT_BUTTON = css`
|
||||
`;
|
||||
|
||||
const STYLES_TABLE_CONTENT_LINK = css`
|
||||
font-family: "inter-medium";
|
||||
font-family: 'inter-medium';
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
|
||||
@ -140,10 +139,7 @@ export const TableColumn = (props) => {
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<span
|
||||
css={props.top ? STYLES_TOP_COLUMN : STYLES_COLUMN}
|
||||
style={props.style}
|
||||
>
|
||||
<span css={props.top ? STYLES_TOP_COLUMN : STYLES_COLUMN} style={props.style}>
|
||||
<span css={STYLES_CONTENT}>{props.children}</span>
|
||||
{tooltipElement}
|
||||
{copyableElement}
|
||||
@ -151,94 +147,75 @@ export const TableColumn = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const TableContent = ({
|
||||
type,
|
||||
text,
|
||||
action,
|
||||
data = {},
|
||||
onNavigateTo,
|
||||
onAction,
|
||||
}) => {
|
||||
export const TableContent = ({ type, text, action, data = {}, onNavigateTo, onAction }) => {
|
||||
const { status, online } = data;
|
||||
|
||||
if (Strings.isEmpty(text)) {
|
||||
if (text === null || text === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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 (
|
||||
<React.Fragment>{text == 1 ? "Storage" : "Retrieval"}</React.Fragment>
|
||||
);
|
||||
case "LOCATION":
|
||||
return "United States";
|
||||
case "BUTTON":
|
||||
return (
|
||||
<span
|
||||
css={STYLES_TABLE_CONTENT_LINK}
|
||||
onClick={() => onAction({ type: "SIDEBAR", value: action })}
|
||||
>
|
||||
<span css={STYLES_TABLE_CONTENT_LINK} onClick={() => onAction({ type: 'SIDEBAR', value: action })}>
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
case "TRANSACTION_DIRECTION":
|
||||
case 'TRANSACTION_DIRECTION':
|
||||
return COMPONENTS_TRANSACTION_DIRECTION[text];
|
||||
case "TRANSACTION_STATUS":
|
||||
case 'TRANSACTION_STATUS':
|
||||
return COMPONENTS_TRANSACTION_STATUS[text];
|
||||
case "ICON":
|
||||
case 'ICON':
|
||||
return COMPONENTS_ICON[text];
|
||||
case "AVATAR":
|
||||
case 'AVATAR':
|
||||
return <Avatar url={text} size={40} online={online} />;
|
||||
case "DEAL_STATUS_RETRIEVAL":
|
||||
case 'DEAL_STATUS_RETRIEVAL':
|
||||
return RETRIEVAL_DEAL_STATES[`${text}`];
|
||||
case "DEAL_STATUS":
|
||||
return data["deal-category"] === 1
|
||||
? STORAGE_DEAL_STATES[`${text}`]
|
||||
: RETRIEVAL_DEAL_STATES[`${text}`];
|
||||
case "BANDWIDTH_UPLOAD":
|
||||
case 'DEAL_STATUS':
|
||||
return data['deal_category'] === 1 ? STORAGE_DEAL_STATES[`${text}`] : RETRIEVAL_DEAL_STATES[`${text}`];
|
||||
case 'BANDWIDTH_UPLOAD':
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SVG.BandwidthUp height="16px" style={{ marginRight: 8 }} />
|
||||
{Strings.bytesToSize(text)}
|
||||
</React.Fragment>
|
||||
);
|
||||
case "BANDWIDTH_DOWNLOAD":
|
||||
case 'BANDWIDTH_DOWNLOAD':
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SVG.BandwidthDown height="16px" style={{ marginRight: 8 }} />
|
||||
{Strings.bytesToSize(text)}
|
||||
</React.Fragment>
|
||||
);
|
||||
case "MINER_AVAILABILITY":
|
||||
case 'MINER_AVAILABILITY':
|
||||
return text == 1 ? (
|
||||
<span
|
||||
css={STYLES_TABLE_TAG}
|
||||
style={{ background: Constants.system.green }}
|
||||
>
|
||||
<span css={STYLES_TABLE_TAG} style={{ background: Constants.system.green }}>
|
||||
Online
|
||||
</span>
|
||||
) : null;
|
||||
case "DEAL_AUTO_RENEW":
|
||||
case 'DEAL_AUTO_RENEW':
|
||||
return text == 1 ? (
|
||||
<span
|
||||
css={STYLES_TABLE_TAG}
|
||||
style={{ background: Constants.system.brand }}
|
||||
>
|
||||
<span css={STYLES_TABLE_TAG} style={{ background: Constants.system.brand }}>
|
||||
true
|
||||
</span>
|
||||
) : (
|
||||
<span css={STYLES_TABLE_TAG}>false</span>
|
||||
);
|
||||
case "NOTIFICATION_ERROR":
|
||||
case 'NOTIFICATION_ERROR':
|
||||
return (
|
||||
<span
|
||||
css={STYLES_TABLE_TAG}
|
||||
style={{ background: Constants.system.red }}
|
||||
>
|
||||
{text} {Strings.pluralize("error", text)}
|
||||
<span css={STYLES_TABLE_TAG} style={{ background: Constants.system.red }}>
|
||||
{text} {Strings.pluralize('error', text)}
|
||||
</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.
|
||||
if (!data) {
|
||||
return text;
|
||||
@ -249,44 +226,45 @@ export const TableContent = ({
|
||||
return (
|
||||
<span
|
||||
css={STYLES_TABLE_CONTENT_LINK}
|
||||
onClick={() =>
|
||||
onAction({ type: "NAVIGATE", value: data.folderId, data })
|
||||
}
|
||||
>
|
||||
onClick={() => onAction({ type: 'NAVIGATE', value: data.folderId, data })}>
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE(jim): Special case for navigating to a sidebar.
|
||||
if (data && data["retrieval-status"] === 1) {
|
||||
if (data && data['retrieval_status'] === 1) {
|
||||
return (
|
||||
<span
|
||||
css={STYLES_TABLE_CONTENT_LINK}
|
||||
onClick={() =>
|
||||
onAction({
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_FILE_STORAGE_DEAL",
|
||||
type: 'SIDEBAR',
|
||||
value: 'SIDEBAR_FILE_STORAGE_DEAL',
|
||||
})
|
||||
}
|
||||
>
|
||||
}>
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// 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 (
|
||||
<span
|
||||
onClick={() =>
|
||||
onAction({
|
||||
name: "File does not exist",
|
||||
type: "ACTION",
|
||||
value: "ACTION_FILE_MISSING",
|
||||
name: 'File does not exist',
|
||||
type: 'ACTION',
|
||||
value: 'ACTION_FILE_MISSING',
|
||||
})
|
||||
}
|
||||
>
|
||||
}>
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
@ -294,10 +272,7 @@ export const TableContent = ({
|
||||
|
||||
// NOTE(jim): Navigates to file.
|
||||
return (
|
||||
<span
|
||||
css={STYLES_TABLE_CONTENT_LINK}
|
||||
onClick={() => onNavigateTo({ id: 15 }, data)}
|
||||
>
|
||||
<span css={STYLES_TABLE_CONTENT_LINK} onClick={() => onNavigateTo({ id: 15 }, data)}>
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
|
@ -15,7 +15,7 @@
|
||||
"@emotion/css": "11.0.0-next.12",
|
||||
"@emotion/react": "11.0.0-next.12",
|
||||
"@emotion/server": "11.0.0-next.12",
|
||||
"@textile/powergate-client": "0.1.0-beta.7",
|
||||
"@textile/powergate-client": "0.1.0-beta.9",
|
||||
"babel-plugin-module-resolver": "^4.0.0",
|
||||
"body-parser": "^1.19.0",
|
||||
"chart.js": "^2.9.3",
|
||||
@ -29,6 +29,7 @@
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-tippy": "^1.3.4",
|
||||
"three": "^0.108.0"
|
||||
"three": "^0.108.0",
|
||||
"ws": "^7.3.0"
|
||||
}
|
||||
}
|
||||
|
@ -64,36 +64,46 @@ const getCurrentNavigationStateById = (navigation, targetId) => {
|
||||
|
||||
export const getServerSideProps = async (context) => {
|
||||
if (context.query && context.query.production) {
|
||||
return { production: true };
|
||||
return { production: true, ...context.query };
|
||||
}
|
||||
|
||||
const data = await Actions.rehydrateViewer();
|
||||
|
||||
return {
|
||||
props: { ...data.data },
|
||||
props: { ...context.query, ...data.data },
|
||||
};
|
||||
};
|
||||
|
||||
export default class IndexPage extends React.Component {
|
||||
_socket = null;
|
||||
|
||||
state = {
|
||||
history: [{ id: 1, scrollTop: 0 }],
|
||||
currentIndex: 0,
|
||||
data: null,
|
||||
selected: {
|
||||
address: null,
|
||||
address: '',
|
||||
},
|
||||
viewer: this.props.production ? Fixtures.getInitialState() : State.getInitialState(this.props),
|
||||
viewer: State.getInitialState(this.props),
|
||||
sidebar: null,
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
// TODO(jim): You don't really need this.
|
||||
console.log(this.props);
|
||||
componentDidMount() {
|
||||
this._socket = new WebSocket(`ws://localhost:${this.props.wsPort}`);
|
||||
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 () => {
|
||||
const viewer = await Actions.rehydrateViewer();
|
||||
this.setState({ viewer: { ...State.getInitialState(viewer.data) } });
|
||||
rehydrate = async ({ data }) => {
|
||||
this.setState({ viewer: { ...State.getInitialState(data) } });
|
||||
};
|
||||
|
||||
_handleSubmit = async (data) => {
|
||||
@ -108,8 +118,6 @@ export default class IndexPage extends React.Component {
|
||||
type: data.wallet_type,
|
||||
makeDefault: data.makeDefault,
|
||||
});
|
||||
|
||||
await this.rehydrate();
|
||||
}
|
||||
|
||||
if (data.type === 'SEND_WALLET_ADDRESS_FILECOIN') {
|
||||
@ -118,8 +126,6 @@ export default class IndexPage extends React.Component {
|
||||
target: data.target,
|
||||
amount: data.amount,
|
||||
});
|
||||
|
||||
await this.rehydrate();
|
||||
}
|
||||
|
||||
this._handleDismissSidebar();
|
||||
@ -258,21 +264,26 @@ export default class IndexPage extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const next = this.state.history[this.state.currentIndex];
|
||||
const current = getCurrentNavigationStateById(Fixtures.NavigationState, next.id);
|
||||
if (this.props.production) {
|
||||
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
|
||||
viewer={this.state.viewer}
|
||||
activeId={current.target.id}
|
||||
activeIds={current.activeIds}
|
||||
navigation={Fixtures.NavigationState}
|
||||
navigation={navigation}
|
||||
onNavigateTo={this._handleNavigateTo}
|
||||
onAction={this._handleAction}
|
||||
/>
|
||||
);
|
||||
|
||||
const header = (
|
||||
const headerElement = (
|
||||
<ApplicationHeader
|
||||
viewer={this.state.viewer}
|
||||
pageTitle={current.target.pageTitle}
|
||||
@ -286,7 +297,6 @@ export default class IndexPage extends React.Component {
|
||||
);
|
||||
|
||||
const scene = React.cloneElement(this.scenes[current.target.decorator], {
|
||||
rehydrate: this.rehydrate,
|
||||
viewer: this.state.viewer,
|
||||
selected: this.state.selected,
|
||||
data: current.target,
|
||||
@ -299,9 +309,9 @@ export default class IndexPage extends React.Component {
|
||||
onForward: this._handleForward,
|
||||
});
|
||||
|
||||
let sidebar;
|
||||
let sidebarElement;
|
||||
if (this.state.sidebar) {
|
||||
sidebar = React.cloneElement(this.state.sidebar, {
|
||||
sidebarElement = React.cloneElement(this.state.sidebar, {
|
||||
viewer: this.state.viewer,
|
||||
selected: this.state.selected,
|
||||
onSelectedChange: this._handleSelectedChange,
|
||||
@ -318,9 +328,9 @@ export default class IndexPage extends React.Component {
|
||||
<React.Fragment>
|
||||
<WebsitePrototypeWrapper title={title} description={description} url={url}>
|
||||
<ApplicationLayout
|
||||
navigation={navigation}
|
||||
header={header}
|
||||
sidebar={sidebar}
|
||||
navigation={navigationElement}
|
||||
header={headerElement}
|
||||
sidebar={sidebarElement}
|
||||
onDismissSidebar={this._handleDismissSidebar}>
|
||||
{scene}
|
||||
</ApplicationLayout>
|
||||
|
@ -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`
|
||||
max-width: 928px;
|
||||
@ -23,13 +23,13 @@ const STYLES_PARAGRAPH = css`
|
||||
margin-bottom: 48px;
|
||||
|
||||
strong {
|
||||
font-family: "inter-semi-bold";
|
||||
font-family: 'inter-semi-bold';
|
||||
font-weight: 400;
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_NAME = css`
|
||||
font-family: "inter-semi-bold";
|
||||
font-family: 'inter-semi-bold';
|
||||
font-size: 24px;
|
||||
margin: 16px;
|
||||
position: relative;
|
||||
@ -88,66 +88,66 @@ const STYLES_CHOICE = css`
|
||||
`;
|
||||
|
||||
const LOGOS = [
|
||||
{ src: "idea-logo-1.jpg", votes: 1 },
|
||||
{ src: "idea-logo-2.jpg", votes: 2 },
|
||||
{ src: "idea-logo-3.jpg", votes: 0 },
|
||||
{ src: "idea-logo-4.jpg", votes: 2 },
|
||||
{ src: "idea-logo-5.jpg", votes: 1 },
|
||||
{ src: "idea-logo-6.jpg", votes: 0 },
|
||||
{ src: "idea-logo-7.jpg", votes: 0 },
|
||||
{ src: "idea-logo-8.jpg", votes: 1 },
|
||||
{ src: "idea-logo-9.jpg", votes: 3 },
|
||||
{ src: "idea-logo-10.jpg", votes: 1 },
|
||||
{ src: "idea-logo-11.jpg", votes: 3 },
|
||||
{ src: "idea-logo-12.jpg", votes: 1 },
|
||||
{ src: "idea-logo-13.jpg", votes: 2 },
|
||||
{ src: "idea-logo-14.jpg", votes: 0 },
|
||||
{ src: "idea-logo-15.jpg", votes: 2 },
|
||||
{ src: "idea-logo-16.jpg", votes: 3 },
|
||||
{ src: "idea-logo-17.jpg", votes: 0 },
|
||||
{ src: "idea-logo-18.jpg", votes: 0 },
|
||||
{ src: "idea-logo-19.jpg", votes: 0 },
|
||||
{ src: "idea-logo-20.jpg", votes: 2 },
|
||||
{ src: "idea-logo-21.jpg", votes: 0 },
|
||||
{ src: "idea-logo-22.jpg", votes: 3 },
|
||||
{ src: "idea-logo-23.jpg", votes: 2 },
|
||||
{ src: "idea-logo-24.jpg", votes: 0 },
|
||||
{ src: 'idea-logo-1.jpg', votes: 1 },
|
||||
{ src: 'idea-logo-2.jpg', votes: 2 },
|
||||
{ src: 'idea-logo-3.jpg', votes: 0 },
|
||||
{ src: 'idea-logo-4.jpg', votes: 2 },
|
||||
{ src: 'idea-logo-5.jpg', votes: 1 },
|
||||
{ src: 'idea-logo-6.jpg', votes: 0 },
|
||||
{ src: 'idea-logo-7.jpg', votes: 0 },
|
||||
{ src: 'idea-logo-8.jpg', votes: 1 },
|
||||
{ src: 'idea-logo-9.jpg', votes: 3 },
|
||||
{ src: 'idea-logo-10.jpg', votes: 1 },
|
||||
{ src: 'idea-logo-11.jpg', votes: 3 },
|
||||
{ src: 'idea-logo-12.jpg', votes: 1 },
|
||||
{ src: 'idea-logo-13.jpg', votes: 2 },
|
||||
{ src: 'idea-logo-14.jpg', votes: 0 },
|
||||
{ src: 'idea-logo-15.jpg', votes: 2 },
|
||||
{ src: 'idea-logo-16.jpg', votes: 3 },
|
||||
{ src: 'idea-logo-17.jpg', votes: 0 },
|
||||
{ src: 'idea-logo-18.jpg', votes: 0 },
|
||||
{ src: 'idea-logo-19.jpg', votes: 0 },
|
||||
{ src: 'idea-logo-20.jpg', votes: 2 },
|
||||
{ src: 'idea-logo-21.jpg', votes: 0 },
|
||||
{ src: 'idea-logo-22.jpg', votes: 3 },
|
||||
{ src: 'idea-logo-23.jpg', votes: 2 },
|
||||
{ src: 'idea-logo-24.jpg', votes: 0 },
|
||||
];
|
||||
|
||||
const NAMES = [
|
||||
{ src: "Petal", votes: 1 },
|
||||
{ src: "Petals", votes: 0 },
|
||||
{ src: "Pistil", votes: 0 },
|
||||
{ src: "Stamen", votes: 0 },
|
||||
{ src: "Pollen", votes: 0 },
|
||||
{ src: "Mission Control", votes: 0 },
|
||||
{ src: "Hex", votes: 2 },
|
||||
{ src: "Hive", votes: 2 },
|
||||
{ src: "Hexagons", votes: 0 },
|
||||
{ src: "Spheres", votes: 0 },
|
||||
{ src: "FileManager", votes: 0 },
|
||||
{ src: "FilePatron", votes: 0 },
|
||||
{ src: "Filecoin Client", votes: 0 },
|
||||
{ src: "Ponds", votes: 1 },
|
||||
{ src: "Outre", votes: 1 },
|
||||
{ src: "Slate", votes: 2 },
|
||||
{ src: "Case", votes: 2 },
|
||||
{ src: "Materials", votes: 0 },
|
||||
{ src: "SeedDrive", votes: 0 },
|
||||
{ src: "Monet", votes: 2 },
|
||||
{ src: "Lilypad", votes: 0 },
|
||||
{ src: "StoreBuddy", votes: 0 },
|
||||
{ src: "FileCabinet", votes: 0 },
|
||||
{ src: "Jacana", votes: 1 },
|
||||
{ src: "Argo", votes: 2 },
|
||||
{ src: "Octavius", votes: 0 },
|
||||
{ src: "Corgi", votes: 0 },
|
||||
{ src: "Filing Cabinet", votes: 0 },
|
||||
{ src: "FileCorgi", votes: 0 },
|
||||
{ src: "Max", votes: 0 },
|
||||
{ src: "Nominal", votes: 0 },
|
||||
{ src: "X AE", votes: 0 },
|
||||
{ src: "A 12", votes: 0 },
|
||||
{ src: 'Petal', votes: 1 },
|
||||
{ src: 'Petals', votes: 0 },
|
||||
{ src: 'Pistil', votes: 0 },
|
||||
{ src: 'Stamen', votes: 0 },
|
||||
{ src: 'Pollen', votes: 0 },
|
||||
{ src: 'Mission Control', votes: 0 },
|
||||
{ src: 'Hex', votes: 2 },
|
||||
{ src: 'Hive', votes: 2 },
|
||||
{ src: 'Hexagons', votes: 0 },
|
||||
{ src: 'Spheres', votes: 0 },
|
||||
{ src: 'FileManager', votes: 0 },
|
||||
{ src: 'FilePatron', votes: 0 },
|
||||
{ src: 'Filecoin Client', votes: 0 },
|
||||
{ src: 'Ponds', votes: 1 },
|
||||
{ src: 'Outre', votes: 1 },
|
||||
{ src: 'Slate', votes: 2 },
|
||||
{ src: 'Case', votes: 2 },
|
||||
{ src: 'Materials', votes: 0 },
|
||||
{ src: 'SeedDrive', votes: 0 },
|
||||
{ src: 'Monet', votes: 2 },
|
||||
{ src: 'Lilypad', votes: 0 },
|
||||
{ src: 'StoreBuddy', votes: 0 },
|
||||
{ src: 'FileCabinet', votes: 0 },
|
||||
{ src: 'Jacana', votes: 1 },
|
||||
{ src: 'Argo', votes: 2 },
|
||||
{ src: 'Octavius', votes: 0 },
|
||||
{ src: 'Corgi', votes: 0 },
|
||||
{ src: 'Filing Cabinet', votes: 0 },
|
||||
{ src: 'FileCorgi', votes: 0 },
|
||||
{ src: 'Max', votes: 0 },
|
||||
{ src: 'Nominal', votes: 0 },
|
||||
{ src: 'X AE', votes: 0 },
|
||||
{ src: 'A 12', votes: 0 },
|
||||
];
|
||||
|
||||
export default class LogoNameTest extends React.Component {
|
||||
@ -155,17 +155,13 @@ export default class LogoNameTest extends React.Component {
|
||||
return (
|
||||
<div css={STYLES_LAYOUT}>
|
||||
<p css={STYLES_PARAGRAPH}>
|
||||
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 not correspond with each
|
||||
other.{" "}
|
||||
<strong>
|
||||
Please let me know which ones you like through Slack direct message.
|
||||
</strong>
|
||||
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
|
||||
not correspond with each other.{' '}
|
||||
<strong>Please let me know which ones you like through Slack direct message.</strong>
|
||||
<br />
|
||||
<br />
|
||||
All votes are anonymous. We will take the votes, consolidate the
|
||||
choices and then ask other designers to help out with refining what
|
||||
the team prefers.
|
||||
All votes are anonymous. We will take the votes, consolidate the choices and then ask other designers to help
|
||||
out with refining what the team prefers.
|
||||
<br />
|
||||
<br />
|
||||
Key
|
||||
@ -183,18 +179,14 @@ export default class LogoNameTest extends React.Component {
|
||||
<span
|
||||
css={STYLES_PILL}
|
||||
style={{
|
||||
backgroundColor: "#0047FF",
|
||||
bottom: "auto",
|
||||
backgroundColor: '#0047FF',
|
||||
bottom: 'auto',
|
||||
top: -16,
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
{n.votes}
|
||||
</span>
|
||||
) : null}
|
||||
<span
|
||||
css={STYLES_CHOICE}
|
||||
style={{ background: "transparent", color: "#000" }}
|
||||
>
|
||||
<span css={STYLES_CHOICE} style={{ background: 'transparent', color: '#000' }}>
|
||||
Option #{index + 1}
|
||||
</span>
|
||||
</span>
|
||||
@ -208,20 +200,15 @@ export default class LogoNameTest extends React.Component {
|
||||
<div>
|
||||
{LOGOS.map((l, index) => {
|
||||
return (
|
||||
<span
|
||||
key={l.src}
|
||||
css={STYLES_LOGO}
|
||||
style={{ backgroundImage: `url('/static/${l.src}')` }}
|
||||
>
|
||||
<span key={l.src} css={STYLES_LOGO} style={{ backgroundImage: `url('/static/temp/${l.src}')` }}>
|
||||
{l.votes > 0 ? (
|
||||
<span
|
||||
css={STYLES_PILL}
|
||||
style={{
|
||||
backgroundColor: "#0047FF",
|
||||
bottom: "auto",
|
||||
backgroundColor: '#0047FF',
|
||||
bottom: 'auto',
|
||||
top: -16,
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
{l.votes}
|
||||
</span>
|
||||
) : null}
|
||||
|
Before Width: | Height: | Size: 1000 KiB |
Before Width: | Height: | Size: 509 KiB |
Before Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 412 KiB |
Before Width: | Height: | Size: 182 KiB |
Before Width: | Height: | Size: 501 KiB |
Before Width: | Height: | Size: 318 KiB |
Before Width: | Height: | Size: 857 KiB |
Before Width: | Height: | Size: 492 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 137 KiB |
Before Width: | Height: | Size: 100 KiB |
0
public/static/files/.gitkeep
Normal file
Before Width: | Height: | Size: 39 KiB |
0
public/static/system/.gitkeep
Normal file
Before Width: | Height: | Size: 165 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 234 KiB |
Before Width: | Height: | Size: 524 KiB |
Before Width: | Height: | Size: 244 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 193 KiB |
Before Width: | Height: | Size: 159 KiB |
Before Width: | Height: | Size: 163 KiB |
@ -1,17 +1,17 @@
|
||||
import * as React from "react";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as Fixtures from "~/common/fixtures";
|
||||
import * as System from "~/components/system";
|
||||
import * as SchemaTable from "~/common/schema-table";
|
||||
import * as React from 'react';
|
||||
import * as Strings from '~/common/strings';
|
||||
import * as Constants from '~/common/constants';
|
||||
import * as Fixtures from '~/common/fixtures';
|
||||
import * as System from '~/components/system';
|
||||
import * as SchemaTable from '~/common/schema-table';
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
import Section from "~/components/core/Section";
|
||||
import ScenePage from '~/components/core/ScenePage';
|
||||
import Section from '~/components/core/Section';
|
||||
|
||||
export default class SceneDataTransfer extends React.Component {
|
||||
state = { sub_navigation: "1" };
|
||||
state = { sub_navigation: '1' };
|
||||
|
||||
_handleChange = (e) => {
|
||||
this.setState({ [e.target.name]: e.target.value });
|
||||
@ -26,19 +26,15 @@ export default class SceneDataTransfer extends React.Component {
|
||||
style={{ marginTop: 24 }}
|
||||
name="sub_navigation"
|
||||
options={[
|
||||
{ value: "1", label: "Current transfers" },
|
||||
{ value: "2", label: "Past transfers" },
|
||||
{ value: '1', label: 'Current transfers' },
|
||||
{ value: '2', label: 'Past transfers' },
|
||||
]}
|
||||
value={this.state.sub_navigation}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
|
||||
{this.state.sub_navigation === "2" ? (
|
||||
<Section
|
||||
title="Past transfers"
|
||||
onAction={this.props.onAction}
|
||||
onNavigateTo={this.props.onNavigateTo}
|
||||
>
|
||||
{this.state.sub_navigation === '2' ? (
|
||||
<Section title="Past transfers" onAction={this.props.onAction} onNavigateTo={this.props.onNavigateTo}>
|
||||
<System.Table
|
||||
data={{
|
||||
columns: SchemaTable.DataTransfer,
|
||||
@ -53,19 +49,8 @@ export default class SceneDataTransfer extends React.Component {
|
||||
</Section>
|
||||
) : null}
|
||||
|
||||
{this.state.sub_navigation === "1" ? (
|
||||
<Section
|
||||
onAction={this.props.onAction}
|
||||
onNavigateTo={this.props.onNavigateTo}
|
||||
title="Current transfers"
|
||||
buttons={[
|
||||
{
|
||||
name: "Cancel all",
|
||||
type: "ACTION",
|
||||
value: "ACTION_CANCEL_DATA_TRANSFERS",
|
||||
},
|
||||
]}
|
||||
>
|
||||
{this.state.sub_navigation === '1' ? (
|
||||
<Section onAction={this.props.onAction} onNavigateTo={this.props.onNavigateTo} title="Current transfers">
|
||||
<System.P style={{ padding: 24 }}>There are no transfers</System.P>
|
||||
</Section>
|
||||
) : null}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import * as React from "react";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as Fixtures from "~/common/fixtures";
|
||||
import * as System from "~/components/system";
|
||||
import * as SchemaTable from "~/common/schema-table";
|
||||
import * as React from 'react';
|
||||
import * as Strings from '~/common/strings';
|
||||
import * as Constants from '~/common/constants';
|
||||
import * as Fixtures from '~/common/fixtures';
|
||||
import * as System from '~/components/system';
|
||||
import * as SchemaTable from '~/common/schema-table';
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import Section from "~/components/core/Section";
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
import Section from '~/components/core/Section';
|
||||
import ScenePage from '~/components/core/ScenePage';
|
||||
|
||||
export default class SceneDeals extends React.Component {
|
||||
state = {};
|
||||
@ -20,18 +20,7 @@ export default class SceneDeals extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ScenePage>
|
||||
<Section
|
||||
onAction={this.props.onAction}
|
||||
onNavigateTo={this.props.onNavigateTo}
|
||||
title="All deals"
|
||||
buttons={[
|
||||
{
|
||||
name: "Export",
|
||||
type: "DOWNLOAD",
|
||||
value: "CSV_ALL_DEALS",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Section onAction={this.props.onAction} onNavigateTo={this.props.onNavigateTo} title="All deals" buttons={[]}>
|
||||
<System.Table
|
||||
onAction={this.props.onAction}
|
||||
onNavigateTo={this.props.onNavigateTo}
|
||||
|
@ -37,12 +37,7 @@ export default class SceneEditAccount extends React.Component {
|
||||
body: data,
|
||||
};
|
||||
|
||||
const response = await fetch(`/_/upload/avatar`, options);
|
||||
const json = await response.json();
|
||||
|
||||
if (json && json.success) {
|
||||
console.log('reload');
|
||||
}
|
||||
await fetch(`/_/upload/avatar`, options);
|
||||
};
|
||||
|
||||
_handleChange = (e) => {
|
||||
@ -64,7 +59,7 @@ export default class SceneEditAccount extends React.Component {
|
||||
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<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
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
|
@ -1,14 +1,14 @@
|
||||
import * as React from "react";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as Fixtures from "~/common/fixtures";
|
||||
import * as System from "~/components/system";
|
||||
import * as SVG from "~/components/system/svg";
|
||||
import * as React from 'react';
|
||||
import * as Strings from '~/common/strings';
|
||||
import * as Constants from '~/common/constants';
|
||||
import * as Fixtures from '~/common/fixtures';
|
||||
import * as System from '~/components/system';
|
||||
import * as SVG from '~/components/system/svg';
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import Section from "~/components/core/Section";
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
import Section from '~/components/core/Section';
|
||||
import ScenePage from '~/components/core/ScenePage';
|
||||
|
||||
const STYLES_FLEX = css`
|
||||
display: flex;
|
||||
@ -21,6 +21,7 @@ const STYLES_FLEX = css`
|
||||
|
||||
const STYLES_TOP = css`
|
||||
background: ${Constants.system.pitchBlack};
|
||||
border-bottom: 1px solid ${Constants.system.black};
|
||||
color: ${Constants.system.white};
|
||||
width: 100%;
|
||||
padding: 12px 16px 12px 48px;
|
||||
@ -53,7 +54,9 @@ const STYLES_ASSET = css`
|
||||
padding: 0;
|
||||
min-height: 10%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-color: ${Constants.system.pitchBlack};
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
`;
|
||||
|
||||
@ -69,7 +72,7 @@ const STYLES_BOTTOM = css`
|
||||
`;
|
||||
|
||||
const STYLES_PATH = css`
|
||||
font-family: "mono";
|
||||
font-family: 'mono';
|
||||
color: ${Constants.system.white};
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
@ -87,7 +90,7 @@ const STYLES_ITEM = css`
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.2px;
|
||||
font-family: "inter-semi-bold";
|
||||
font-family: 'inter-semi-bold';
|
||||
transition: 200ms ease all;
|
||||
cursor: pointer;
|
||||
background-color: ${Constants.system.brand};
|
||||
@ -113,7 +116,7 @@ export default class SceneFile extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const fileURL = `/static/${this.props.file.file}`;
|
||||
const fileURL = `/static/files/${this.props.file.file}`;
|
||||
|
||||
return (
|
||||
<div css={STYLES_FLEX}>
|
||||
@ -125,20 +128,7 @@ export default class SceneFile extends React.Component {
|
||||
<SVG.Dismiss height="24px" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
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 css={STYLES_ASSET} style={{ backgroundImage: `url('${fileURL}')` }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
import * as React from "react";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as Fixtures from "~/common/fixtures";
|
||||
import * as System from "~/components/system";
|
||||
import * as React from 'react';
|
||||
import * as Strings from '~/common/strings';
|
||||
import * as Constants from '~/common/constants';
|
||||
import * as Fixtures from '~/common/fixtures';
|
||||
import * as System from '~/components/system';
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import Section from "~/components/core/Section";
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
import Section from '~/components/core/Section';
|
||||
import ScenePage from '~/components/core/ScenePage';
|
||||
|
||||
export default class SceneFilesFolder extends React.Component {
|
||||
state = {};
|
||||
@ -24,29 +24,21 @@ export default class SceneFilesFolder extends React.Component {
|
||||
|
||||
const data = {
|
||||
columns: [
|
||||
{ key: "icon", hideLabel: true, width: "32px", type: "ICON" },
|
||||
{ key: "file", name: "File", width: "100%", type: "FILE_LINK" },
|
||||
{ key: "size", name: "Size", width: "140px" },
|
||||
{ key: 'icon', hideLabel: true, width: '32px', type: 'ICON' },
|
||||
{ key: 'file', name: 'File', width: '100%', type: 'FILE_LINK' },
|
||||
{ key: 'size', name: 'Size', width: '140px', type: 'FILE_SIZE' },
|
||||
{
|
||||
key: "date",
|
||||
name: "Date uploaded",
|
||||
width: "160px",
|
||||
tooltip:
|
||||
"This date represents when the file was first uploaded to the network.",
|
||||
key: 'date',
|
||||
name: 'Date uploaded',
|
||||
width: '160px',
|
||||
tooltip: 'This date represents when the file was first uploaded to the network.',
|
||||
type: 'FILE_DATE',
|
||||
},
|
||||
{
|
||||
key: "remaining",
|
||||
name: "Remaining time",
|
||||
tooltip:
|
||||
"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: 'storage_status',
|
||||
name: 'Status',
|
||||
type: 'DEAL_STATUS',
|
||||
},
|
||||
{
|
||||
key: "retrieval-status",
|
||||
name: "Status",
|
||||
type: "DEAL_STATUS_RETRIEVAL",
|
||||
},
|
||||
{ key: "errors", hideLabel: true, type: "NOTIFICATION_ERROR" },
|
||||
],
|
||||
rows,
|
||||
};
|
||||
@ -59,17 +51,11 @@ export default class SceneFilesFolder extends React.Component {
|
||||
title={this.props.data.name}
|
||||
buttons={[
|
||||
{
|
||||
name: "Retrieve",
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_FILE_RETRIEVAL_DEAL",
|
||||
name: 'Store file on network',
|
||||
type: 'SIDEBAR',
|
||||
value: 'SIDEBAR_FILE_STORAGE_DEAL',
|
||||
},
|
||||
{
|
||||
name: "Store file on network",
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_FILE_STORAGE_DEAL",
|
||||
},
|
||||
]}
|
||||
>
|
||||
]}>
|
||||
<System.Table
|
||||
key={this.props.data.folderId}
|
||||
data={data}
|
||||
|
@ -54,43 +54,50 @@ export default class SceneHome extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ScenePage>
|
||||
<Section
|
||||
onAction={this.props.onAction}
|
||||
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}
|
||||
{this.props.viewer.library[0] ? (
|
||||
<Section
|
||||
onAction={this.props.onAction}
|
||||
onNavigateTo={this.props.onNavigateTo}
|
||||
name="data"
|
||||
/>
|
||||
</Section>
|
||||
title="Recent data"
|
||||
buttons={[
|
||||
{
|
||||
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] ? (
|
||||
<Section
|
||||
|
@ -33,7 +33,7 @@ export default class SceneSettings extends React.Component {
|
||||
_deferredSave = null;
|
||||
|
||||
_handleSave = async () => {
|
||||
const response = await Actions.setDefaultConfig({
|
||||
await Actions.setDefaultConfig({
|
||||
config: {
|
||||
hot: {
|
||||
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,
|
||||
filecoin: {
|
||||
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,
|
||||
excludedMinersList: this.props.viewer.settings_cold_default_excluded_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) => {
|
||||
@ -143,6 +141,7 @@ export default class SceneSettings extends React.Component {
|
||||
description="Default Filecoin deal duration settings description."
|
||||
tooltip="Placeholder."
|
||||
name="settings_cold_default_duration"
|
||||
type="number"
|
||||
value={this.props.viewer.settings_cold_default_duration}
|
||||
placeholder="Type in months"
|
||||
onChange={this._handleChange}
|
||||
|
333
server.js
@ -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 pow = createPow({ host });
|
||||
const PowerGate = createPow({ host });
|
||||
|
||||
import * as Middleware from '~/common/middleware';
|
||||
import * as Strings from '~/common/strings';
|
||||
|
||||
import FS from 'fs';
|
||||
import express from 'express';
|
||||
@ -11,15 +12,22 @@ import formidable from 'formidable';
|
||||
import next from 'next';
|
||||
import bodyParser from 'body-parser';
|
||||
import compression from 'compression';
|
||||
import WebSocketServer from 'ws';
|
||||
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
const port = process.env.PORT || 1337;
|
||||
const wsPort = 2448;
|
||||
const app = next({ dev, quiet: false });
|
||||
const nextRequestHandler = app.getRequestHandler();
|
||||
|
||||
const AVATAR_STORAGE_URL = `${__dirname}/public/static/system/`;
|
||||
const FILE_STORAGE_URL = `${__dirname}/public/static/files/`;
|
||||
|
||||
// TODO(jim): Just a solution for testing.
|
||||
// Probably should refactor this or use a database.
|
||||
let client = null;
|
||||
let token = null;
|
||||
let library = null;
|
||||
let status = null;
|
||||
let messageList = null;
|
||||
let peersList = null;
|
||||
@ -27,22 +35,66 @@ let addrsList = null;
|
||||
let info = null;
|
||||
|
||||
const refresh = async () => {
|
||||
const Health = await pow.health.check();
|
||||
const Health = await PowerGate.health.check();
|
||||
status = Health.status ? Health.status : 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;
|
||||
};
|
||||
|
||||
const refreshWithToken = async () => {
|
||||
const Addresses = await pow.ffs.addrs();
|
||||
const Addresses = await PowerGate.ffs.addrs();
|
||||
addrsList = Addresses.addrsList ? Addresses.addrsList : null;
|
||||
|
||||
const NetworkInfo = await pow.ffs.info();
|
||||
const NetworkInfo = await PowerGate.ffs.info();
|
||||
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 data = {
|
||||
production: !dev,
|
||||
@ -51,47 +103,134 @@ const getData = async () => {
|
||||
peersList,
|
||||
addrsList,
|
||||
info,
|
||||
library,
|
||||
};
|
||||
|
||||
console.log('ON THE SERVER', data);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
app.prepare().then(async () => {
|
||||
try {
|
||||
await refresh();
|
||||
const emitStateUpdate = async () => {
|
||||
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.
|
||||
!FS.existsSync(`./.data`) && FS.mkdirSync(`./.data`, { recursive: true });
|
||||
const checkFileStatus = async () => {
|
||||
// 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.
|
||||
if (!FS.existsSync('./.data/powergate-token')) {
|
||||
const FFS = await pow.ffs.create();
|
||||
token = FFS.token ? FFS.token : null;
|
||||
|
||||
// NOTE(jim): Write a new token file.
|
||||
if (token) {
|
||||
FS.writeFileSync('./.data/powergate-token', token);
|
||||
PowerGate.ffs.watchJobs((job) => {
|
||||
console.log(job);
|
||||
if (job.status === ffs.JobStatus.JOB_STATUS_SUCCESS) {
|
||||
library[i].children[j].storage_status = 6;
|
||||
write = true;
|
||||
}
|
||||
}, library[i].children[j].job_id);
|
||||
}
|
||||
} else {
|
||||
token = FS.readFileSync('./.data/powergate-token', 'utf8');
|
||||
}
|
||||
|
||||
if (token) {
|
||||
pow.setToken(token);
|
||||
}
|
||||
|
||||
await refreshWithToken();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
throw new Error('[ prototype ] can not start client without proper auth token');
|
||||
if (write) {
|
||||
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 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) {
|
||||
server.use(compression());
|
||||
@ -106,8 +245,87 @@ app.prepare().then(async () => {
|
||||
})
|
||||
);
|
||||
|
||||
server.get('/health', async (req, res) => {
|
||||
res.send('ok');
|
||||
server.post('/_/viewer', async (req, res) => {
|
||||
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) => {
|
||||
@ -119,83 +337,68 @@ app.prepare().then(async () => {
|
||||
console.log(`[ prototype ] ${bytesReceived} / ${bytesExpected}`);
|
||||
});
|
||||
|
||||
form.on('fileBegin', (filename, file) => {
|
||||
form.emit('data', { name: '[ prototype ] file uploading', filename, value: file });
|
||||
});
|
||||
|
||||
form.on('file', (filename, file) => {
|
||||
form.emit('data', { name: '[ prototype ] file:', key: filename, value: file });
|
||||
});
|
||||
|
||||
form.on('field', (fieldName, fieldValue) => {
|
||||
form.emit('data', { name: '[ prototype ] field:', key: fieldName, value: fieldValue });
|
||||
});
|
||||
|
||||
form.once('end', () => {
|
||||
console.log('[ prototype ] finished upload');
|
||||
});
|
||||
|
||||
form.parse(req, (error, fields, files) => {
|
||||
form.parse(req, async (error, fields, files) => {
|
||||
if (error) {
|
||||
return res.status(500).send({ error });
|
||||
} else {
|
||||
const newPath = form.uploadDir + 'avatar.png';
|
||||
FS.rename(files.image.path, newPath, function (err) {});
|
||||
|
||||
await emitStateUpdate();
|
||||
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) => {
|
||||
let data;
|
||||
try {
|
||||
data = await pow.ffs.setDefaultConfig(req.body.config);
|
||||
data = await PowerGate.ffs.setDefaultConfig(req.body.config);
|
||||
} catch (e) {
|
||||
return res.status(500).send({ error: e.message });
|
||||
}
|
||||
|
||||
await emitStateUpdate();
|
||||
return res.status(200).send({ success: true, data });
|
||||
});
|
||||
|
||||
server.post('/_/wallet/create', async (req, res) => {
|
||||
let data;
|
||||
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) {
|
||||
return res.status(500).send({ error: e.message });
|
||||
}
|
||||
|
||||
await emitStateUpdate();
|
||||
return res.status(200).send({ success: true, data });
|
||||
});
|
||||
|
||||
server.post('/_/wallet/send', async (req, res) => {
|
||||
let data;
|
||||
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) {
|
||||
return res.status(500).send({ error: e.message });
|
||||
}
|
||||
|
||||
await emitStateUpdate();
|
||||
return res.status(200).send({ success: true, data: { ...data, ...req.body } });
|
||||
});
|
||||
|
||||
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) => {
|
||||
return nextRequestHandler(req, res, req.url);
|
||||
});
|
||||
|
||||
server.listen(port, (err) => {
|
||||
server.listen(port, async (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
@ -204,5 +407,7 @@ app.prepare().then(async () => {
|
||||
console.log('[ prototype ] powergate token:', token);
|
||||
console.log(`[ prototype ] listening on: http://localhost:${port}`);
|
||||
console.log(`[ prototype ] avatar storage: ${AVATAR_STORAGE_URL}`);
|
||||
|
||||
await setIntervalViewerUpdates();
|
||||
});
|
||||
});
|
||||
|