App download indicator.

This commit is contained in:
Thomas E Lackey 2020-06-08 16:10:00 -05:00
parent d3772602c6
commit dccd61c0c9
7 changed files with 108 additions and 47 deletions

View File

@ -14,7 +14,7 @@ First start the server:
Then start the Webpack devserver. Then start the Webpack devserver.
```bash ```bash
cd packages/consoe-app cd packages/console-app
yarn start yarn start
``` ```

View File

@ -22,20 +22,19 @@ system:
services: services:
app: app:
prefix: '/app' prefix: '/app'
server: 'http://127.0.0.1:5999' server: 'https://xbox.local'
wns: wns:
server: 'http://127.0.0.1:9473/api' server: 'https://xbox.local/dxos/wns/api'
webui: 'http://127.0.0.1:9473/webui' webui: 'https://xbox.local/dxos/wns/webui'
signal: signal:
server: 'http://127.0.0.1:4000' server: 'wss://xbox.local/dxos/signal'
api: 'http://127.0.0.1:4000' api: 'https://xbox.local/dxos/signal/'
ipfs: ipfs:
server: '/ip4/127.0.0.1/tcp/5001' server: 'https://xbox.local/dxos/ipfs/api'
gateway: '/ip4//127.0.0.1:8888/ipfs/' gateway: 'https://xbox.local/dxos/ipfs/gateway'
webui: 'http://127.0.0.1:5001/webui'
wellknown: wellknown:
endpoint: 'http://127.0.0.1:9000/.well-known/dxos' endpoint: 'https://xbox.local/.well-known/dxos'

View File

@ -0,0 +1,30 @@
//
// Copyright 2020 DxOS
//
import React from 'react';
import YesIcon from '@material-ui/icons/CheckCircleOutline';
import NoIcon from '@material-ui/icons/RadioButtonUnchecked';
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles(theme => ({
error: {
color: theme.palette.error.main
},
on: {
color: theme.palette.primary[500]
},
off: {
color: 'transparent'
}
}));
export const BooleanIcon = ({ yes = false, error = false }) => {
const classes = useStyles();
return (yes
? <YesIcon className={classes.on} />
: <NoIcon className={error ? classes.error : classes.off} />
);
};

View File

@ -3,36 +3,42 @@
// //
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { useQuery } from '@apollo/react-hooks'; import { useQuery } from '@apollo/react-hooks';
import WNS_RECORDS from '../../../gql/wns_records.graphql';
import { ConsoleContext, useQueryStatusReducer, useSorter } from '../../../hooks';
import Link from '@material-ui/core/Link'; import Link from '@material-ui/core/Link';
import TableHead from '@material-ui/core/TableHead'; import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow'; import TableRow from '@material-ui/core/TableRow';
import TableBody from '@material-ui/core/TableBody'; import TableBody from '@material-ui/core/TableBody';
import moment from 'moment';
import { getServiceUrl } from '../../../util/config'; import { BooleanIcon } from '../../../components/BooleanIcon';
import Table from '../../../components/Table'; import Table from '../../../components/Table';
import TableCell from '../../../components/TableCell'; import TableCell from '../../../components/TableCell';
import moment from 'moment'; import { ConsoleContext, useQueryStatusReducer, useSorter } from '../../../hooks';
import { getServiceUrl } from '../../../util/config';
import IPFS_STATUS from '../../../gql/ipfs_status.graphql';
import WNS_RECORDS from '../../../gql/wns_records.graphql';
const AppRecords = () => { const AppRecords = () => {
const { config } = useContext(ConsoleContext); const { config } = useContext(ConsoleContext);
const [sorter, sortBy] = useSorter('createTime', false); const [sorter, sortBy] = useSorter('createTime', false);
const data = useQueryStatusReducer(useQuery(WNS_RECORDS, { const appResponse = useQueryStatusReducer(useQuery(WNS_RECORDS, {
pollInterval: config.api.intervalQuery, pollInterval: config.api.intervalQuery,
variables: { type: 'wrn:app' } variables: { type: 'wrn:app' }
})); }));
if (!data) { // TODO(telackey): Does this also need an interval?
const ipfsResponse = useQueryStatusReducer(useQuery(IPFS_STATUS));
if (!appResponse || !ipfsResponse) {
return null; return null;
} }
const records = data.wns_records.json; const appData = appResponse.wns_records.json;
const ipfsData = JSON.parse(ipfsResponse.ipfs_status.json);
const localRefs = new Set(ipfsData.refs.local);
// TODO(burdon): Test if app is deployed. // TODO(burdon): Test if app is deployed.
const getAppUrl = ({ name, version }) => { const getAppUrl = ({ name, version }) => {
@ -47,8 +53,12 @@ const AppRecords = () => {
pathComponents.push(config.services.app.prefix.substring(1)); pathComponents.push(config.services.app.prefix.substring(1));
} }
pathComponents.push(`${name}@${version}`); if (version) {
return pathComponents.join('/'); pathComponents.push(`${name}@${version}`);
} else {
pathComponents.push(name);
}
return `${pathComponents.join('/')}/`;
}; };
return ( return (
@ -56,26 +66,29 @@ const AppRecords = () => {
<TableHead> <TableHead>
<TableRow> <TableRow>
<TableCell onClick={sortBy('name')}>Identifier</TableCell> <TableCell onClick={sortBy('name')}>Identifier</TableCell>
<TableCell onClick={sortBy('attributes.displayName')}>Name</TableCell>
<TableCell onClick={sortBy('version')} size='small'>Version</TableCell> <TableCell onClick={sortBy('version')} size='small'>Version</TableCell>
<TableCell onClick={sortBy('createTime')} size='small'>Created</TableCell> <TableCell onClick={sortBy('createTime')} size='small'>Created</TableCell>
<TableCell onClick={sortBy('attributes.displayName')}>Name</TableCell> <TableCell size='small'>Downloaded</TableCell>
<TableCell>Link</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{records.sort(sorter).map(({ id, name, version, createTime, attributes: { displayName, publicUrl } }) => { {appData.sort(sorter).map(({ id, name, version, createTime, attributes: { displayName, publicUrl, package: hash } }) => {
const link = getAppUrl({ id, name, version, publicUrl }); const verLink = getAppUrl({ id, name, version, publicUrl });
const appLink = getAppUrl({ id, name, publicUrl });
return ( return (
<TableRow key={id} size='small'> <TableRow key={id} size='small'>
<TableCell monospace>{name}</TableCell> <TableCell monospace>
<TableCell monospace>{version}</TableCell> <Link href={appLink} target={name}>{name}</Link>
<TableCell>{moment.utc(createTime).fromNow()}</TableCell> </TableCell>
<TableCell>{displayName}</TableCell> <TableCell>{displayName}</TableCell>
<TableCell monospace> <TableCell monospace>
{link && ( <Link href={verLink} target={version}>{version}</Link>
<Link href={link} target={name}>{link}</Link> </TableCell>
)} <TableCell>{moment.utc(createTime).fromNow()}</TableCell>
<TableCell>
<BooleanIcon yes={localRefs && localRefs.has(hash)} />
</TableCell> </TableCell>
</TableRow> </TableRow>
); );

View File

@ -1,7 +1,7 @@
{ {
"build": { "build": {
"name": "@dxos/console-app", "name": "@dxos/console-app",
"buildDate": "2020-06-01T01:13:48.575Z", "buildDate": "2020-06-08T18:45:46.717Z",
"version": "1.0.0-beta.0" "version": "1.0.0-beta.0"
} }
} }

View File

@ -3,8 +3,6 @@
# NOTE: Set CONFIG_FILE to swap out this config file. # NOTE: Set CONFIG_FILE to swap out this config file.
# #
# TODO(burdon): Set defaults.
app: app:
title: 'Console' title: 'Console'
org': 'DxOS' org': 'DxOS'
@ -24,20 +22,19 @@ system:
services: services:
app: app:
prefix: '/app' prefix: '/app'
server: 'http://127.0.0.1:5999' server: 'https://xbox.local'
wns: wns:
server: 'https://node1.dxos.network/wns/api' server: 'https://xbox.local/dxos/wns/api'
webui: 'https://node1.dxos.network/wns/webui' webui: 'https://xbox.local/dxos/wns/webui'
signal: signal:
server: 'http://127.0.0.1:4000' server: 'wss://xbox.local/dxos/signal'
api: 'http://127.0.0.1:4000' api: 'https://xbox.local/dxos/signal/status'
ipfs: ipfs:
server: '/ip4/127.0.0.1/tcp/5001' server: 'https://xbox.local/dxos/ipfs/api'
gateway: '/ip4//127.0.0.1:8888/ipfs/' gateway: 'https://xbox.local/dxos/ipfs/gateway'
webui: 'http://127.0.0.1:5001/webui'
wellknown: wellknown:
endpoint: 'http://127.0.0.1:9000/.well-known/dxos' endpoint: 'https://xbox.local/.well-known/dxos'

View File

@ -21,14 +21,36 @@ export const ipfsResolvers = {
// NOTE: Hangs if server not running. // NOTE: Hangs if server not running.
const ipfs = new IpfsHttpClient(config.services.ipfs.server); const ipfs = new IpfsHttpClient(config.services.ipfs.server);
const id = await ipfs.id();
const version = await ipfs.version(); const version = await ipfs.version();
const status = await ipfs.id(); const peers = await ipfs.swarm.peers();
const stats = await ipfs.stats.repo();
// Do not expose the repo path.
delete stats['repoPath'];
const refs = [];
for await (const ref of ipfs.refs.local()) {
if (ref.err) {
log(ref.err);
} else {
refs.push(ref.ref);
}
}
return { return {
timestamp: new Date().toUTCString(), timestamp: new Date().toUTCString(),
json: JSON.stringify({ json: JSON.stringify({
id,
version, version,
status repo: {
stats
},
refs: {
local: refs
},
swarm: {
peers
}
}) })
}; };
} }