Home landing page (#4178)
* Home entry point (basics) * WIP store for web * Add DappUrlInput component * Updated tests * WIP store update * Adjust styling * Add home tab * Collapse first/last without extra divs * Navigation actually navigates * styling * Encoding of ethlink.io URLs * encodedUrl setup * base58 encoded URLs * Added decoding, updated tests to Parity-compliant * Base32 (synced with Rust implementation via tests) * Split URL into 63 character chunks * Fix store test * Cleanups * s/ethlink/dapplink/ * Display app navigation & histroy * Start on /accounts (for now, until expanded fully) * Update tests * ethlink.io -> web3.site * Basic list layout * Store history on navigation * Show Accounts & Dapps * Add skeleton for DappIcon (WIP) * DappIcon WIP * DappIcon in place * Split into maneable sub-components * WIP * Tests for views/Home * Swap default entry-point to /home * Expose registry.get via lookupMeta * Add getEntry interface, fix instance retrieval (with tests) * Add news display component * Add tests for added contracts/registry methods * Fix GHH test refactoring * render news via SectionList * News items store directly * Images * News & Urls has new layout * Convert remainder * First run-through of MVP for SectionList * Update tests * Deploycontract should not override global p styles * Allow styles overrides for head & body * Adjust layout styling * revert Container>flex * Adjust sizes of history items * Cleanups * HistoryStore for tracking relevant routes * Default route is still /accounts * Fix tests * Update 2015-2017 * Add lookupMeta & tests * Add getEntry & tests * Split Dapp icon into ui/DappIcon * Update copyright dates * Encoding for *.web3.site urls * Dapp history retrieval * Grow to only 40% on hover * Update description * Add DappUrlInput component * Update Web views with store * Update spec description * Update spec description * edited url does not allow in-place store edits * Use /web/<hash> urls for iframe * Removed (now) unused _list.css * Mistamtched merge fixed * Tab split (WIP) * Split Tab component * Update tests after merge * typo * Remove background !important * Set item width to parent * Set width, remove overflow-x: hidden * Align hover overlays * Container defaults to some opacity * Display history from listRecentDapps * Override styles for a tags * Open URLs in new window when extension is available * Fix tests after update * AccountCard width 100% * Re-add opening correct url in tab * Cleanup link rendering * Remove hardcoded news URL * pre-merge * Extra padding at Home bottom (Pr grumble) * Match js-vaults stretch * s/Web Apps via URL/Web Apps/ (PR grumble) * Store recent wallets (PR grumble) * Simplify inline style matching (PR comment) * Add store for new retrieval * Add missing observer * Auto-link based on account type * Fix UI overlaps * Extra spacing * Only show account when accountInfo is available * Align timestamp line-heights * Fix tests * Update tests * Really fix failing test (check for Connect(Account))
This commit is contained in:
parent
78917d728d
commit
63d2cfcbfc
@ -183,6 +183,7 @@
|
||||
"react-element-to-jsx-string": "6.0.0",
|
||||
"react-event-listener": "0.4.1",
|
||||
"react-intl": "2.1.5",
|
||||
"react-markdown": "2.4.4",
|
||||
"react-portal": "3.0.0",
|
||||
"react-redux": "4.4.6",
|
||||
"react-router": "3.0.0",
|
||||
|
@ -181,6 +181,16 @@ export function outReceipt (receipt) {
|
||||
return receipt;
|
||||
}
|
||||
|
||||
export function outRecentDapps (recentDapps) {
|
||||
if (recentDapps) {
|
||||
Object.keys(recentDapps).forEach((url) => {
|
||||
recentDapps[url] = outDate(recentDapps[url]);
|
||||
});
|
||||
}
|
||||
|
||||
return recentDapps;
|
||||
}
|
||||
|
||||
export function outSignerRequest (request) {
|
||||
if (request) {
|
||||
Object.keys(request).forEach((key) => {
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { outBlock, outAccountInfo, outAddress, outChainStatus, outDate, outHistogram, outNumber, outPeer, outPeers, outReceipt, outSyncing, outTransaction, outTrace, outVaultMeta } from './output';
|
||||
import { outBlock, outAccountInfo, outAddress, outChainStatus, outDate, outHistogram, outNumber, outPeer, outPeers, outReceipt, outRecentDapps, outSyncing, outTransaction, outTrace, outVaultMeta } from './output';
|
||||
import { isAddress, isBigNumber, isInstanceOf } from '../../../test/types';
|
||||
|
||||
describe('api/format/output', () => {
|
||||
@ -337,6 +337,14 @@ describe('api/format/output', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('outRecentDapps', () => {
|
||||
it('formats the URLs with timestamps', () => {
|
||||
expect(outRecentDapps({ testing: 0x57513668 })).to.deep.equal({
|
||||
testing: new Date('2016-06-03T07:48:56.000Z')
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outSyncing', () => {
|
||||
['currentBlock', 'highestBlock', 'startingBlock', 'warpChunksAmount', 'warpChunksProcessed'].forEach((input) => {
|
||||
it(`formats ${input} numbers as a number`, () => {
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { inAddress, inAddresses, inData, inHex, inNumber16, inOptions, inBlockNumber } from '../../format/input';
|
||||
import { outAccountInfo, outAddress, outAddresses, outChainStatus, outHistogram, outNumber, outPeers, outTransaction, outVaultMeta } from '../../format/output';
|
||||
import { outAccountInfo, outAddress, outAddresses, outChainStatus, outHistogram, outNumber, outPeers, outRecentDapps, outTransaction, outVaultMeta } from '../../format/output';
|
||||
|
||||
export default class Parity {
|
||||
constructor (transport) {
|
||||
@ -222,7 +222,8 @@ export default class Parity {
|
||||
|
||||
listRecentDapps () {
|
||||
return this._transport
|
||||
.execute('parity_listRecentDapps');
|
||||
.execute('parity_listRecentDapps')
|
||||
.then(outRecentDapps);
|
||||
}
|
||||
|
||||
listStorageKeys (address, count, hash = null, blockNumber = 'latest') {
|
||||
|
@ -48,6 +48,10 @@ export default {
|
||||
label: 'Contracts'
|
||||
},
|
||||
|
||||
home: {
|
||||
label: 'Home'
|
||||
},
|
||||
|
||||
status: {
|
||||
label: 'Status'
|
||||
},
|
||||
|
@ -91,7 +91,7 @@ export default class ParametersStep extends Component {
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={ styles.parameters }>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='deployContract.parameters.choose'
|
||||
|
@ -32,6 +32,8 @@
|
||||
padding-left: 3em;
|
||||
}
|
||||
|
||||
p {
|
||||
color: rgba(255, 255, 255, 0.498039);
|
||||
.parameters {
|
||||
p {
|
||||
color: rgba(255, 255, 255, 0.498039);
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import {
|
||||
Accounts, Account, Addresses, Address, Application,
|
||||
Contract, Contracts, Dapp, Dapps, HistoryStore,
|
||||
Contract, Contracts, Dapp, Dapps, HistoryStore, Home,
|
||||
Settings, SettingsBackground, SettingsParity, SettingsProxy,
|
||||
SettingsViews, Signer, Status,
|
||||
Wallet, Web, WriteContract
|
||||
@ -54,10 +54,16 @@ const accountsRoutes = [
|
||||
path: ':address',
|
||||
component: Account,
|
||||
onEnter: ({ params }) => {
|
||||
accountsHistory.add(params.address);
|
||||
accountsHistory.add(params.address, 'account');
|
||||
}
|
||||
},
|
||||
{ path: '/wallet/:address', component: Wallet }
|
||||
{
|
||||
path: '/wallet/:address',
|
||||
component: Wallet,
|
||||
onEnter: ({ params }) => {
|
||||
accountsHistory.add(params.address, 'wallet');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const addressesRoutes = [
|
||||
@ -86,8 +92,8 @@ const routes = [
|
||||
{ path: '/address/:address', onEnter: handleDeprecatedRoute },
|
||||
{ path: '/contract/:address', onEnter: handleDeprecatedRoute },
|
||||
|
||||
{ path: '/', onEnter: redirectTo('/accounts') },
|
||||
{ path: '/auth', onEnter: redirectTo('/accounts') },
|
||||
{ path: '/', onEnter: redirectTo('/home') },
|
||||
{ path: '/auth', onEnter: redirectTo('/home') },
|
||||
{ path: '/settings', onEnter: redirectTo('/settings/views') }
|
||||
];
|
||||
|
||||
@ -127,6 +133,7 @@ const childRoutes = [
|
||||
}
|
||||
},
|
||||
{ path: 'apps', component: Dapps },
|
||||
{ path: 'home', component: Home },
|
||||
{ path: 'web', component: Web },
|
||||
{ path: 'web/:url', component: Web },
|
||||
{ path: 'signer', component: Signer }
|
||||
|
@ -14,11 +14,37 @@
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
$background: rgba(18, 18, 18, 0.85);
|
||||
$backgroundOverlay: rgba(18, 18, 18, 1);
|
||||
|
||||
.container {
|
||||
background: $background;
|
||||
flex: 1;
|
||||
padding: 0em;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
height: 100%;
|
||||
padding: 0em;
|
||||
transition: all 0.75s cubic-bezier(0.23, 1, 0.32, 1);
|
||||
width: 100%;
|
||||
|
||||
.hoverOverlay {
|
||||
background: $backgroundOverlay;
|
||||
display: none;
|
||||
left: 0;
|
||||
margin-top: -1.5em;
|
||||
padding: 0 1.5em 1.5em 1.5em;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 100%;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $backgroundOverlay;
|
||||
|
||||
.hoverOverlay {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.compact,
|
||||
|
@ -28,6 +28,7 @@ export default class Container extends Component {
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string,
|
||||
compact: PropTypes.bool,
|
||||
hover: PropTypes.node,
|
||||
light: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
style: PropTypes.object,
|
||||
@ -68,10 +69,25 @@ export default class Container extends Component {
|
||||
{ this.renderTitle() }
|
||||
{ children }
|
||||
</Card>
|
||||
{ this.renderHover() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderHover () {
|
||||
const { hover } = this.props;
|
||||
|
||||
if (!hover) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={ styles.hoverOverlay }>
|
||||
{ hover }
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
renderTitle () {
|
||||
const { title } = this.props;
|
||||
|
||||
|
@ -37,10 +37,17 @@ describe('ui/Container', () => {
|
||||
});
|
||||
|
||||
describe('sections', () => {
|
||||
it('renders the Card', () => {
|
||||
it('renders the default Card', () => {
|
||||
expect(render().find('Card')).to.have.length(1);
|
||||
});
|
||||
|
||||
it('renders Hover Card when available', () => {
|
||||
const cards = render({ hover: <div>testingHover</div> }).find('Card');
|
||||
|
||||
expect(cards).to.have.length(2);
|
||||
expect(cards.get(1).props.children.props.children).to.equal('testingHover');
|
||||
});
|
||||
|
||||
it('renders the Title', () => {
|
||||
const title = render({ title: 'title' }).find('Title');
|
||||
|
||||
|
@ -14,66 +14,33 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import AddIcon from 'material-ui/svg-icons/content/add';
|
||||
import AttachFileIcon from 'material-ui/svg-icons/editor/attach-file';
|
||||
import CancelIcon from 'material-ui/svg-icons/content/clear';
|
||||
import CheckIcon from 'material-ui/svg-icons/navigation/check';
|
||||
import CloseIcon from 'material-ui/svg-icons/navigation/close';
|
||||
import CompareIcon from 'material-ui/svg-icons/action/compare-arrows';
|
||||
import ComputerIcon from 'material-ui/svg-icons/hardware/desktop-mac';
|
||||
import ContractIcon from 'material-ui/svg-icons/action/code';
|
||||
import CopyIcon from 'material-ui/svg-icons/content/content-copy';
|
||||
import DashboardIcon from 'material-ui/svg-icons/action/dashboard';
|
||||
import DeleteIcon from 'material-ui/svg-icons/action/delete';
|
||||
import DoneIcon from 'material-ui/svg-icons/action/done-all';
|
||||
import EditIcon from 'material-ui/svg-icons/content/create';
|
||||
import FingerprintIcon from 'material-ui/svg-icons/action/fingerprint';
|
||||
import LinkIcon from 'material-ui/svg-icons/content/link';
|
||||
import LockedIcon from 'material-ui/svg-icons/action/lock';
|
||||
import MoveIcon from 'material-ui/svg-icons/action/open-with';
|
||||
import NextIcon from 'material-ui/svg-icons/navigation/arrow-forward';
|
||||
import PrevIcon from 'material-ui/svg-icons/navigation/arrow-back';
|
||||
import PrintIcon from 'material-ui/svg-icons/action/print';
|
||||
import RefreshIcon from 'material-ui/svg-icons/action/autorenew';
|
||||
import SaveIcon from 'material-ui/svg-icons/content/save';
|
||||
import SendIcon from 'material-ui/svg-icons/content/send';
|
||||
import SnoozeIcon from 'material-ui/svg-icons/av/snooze';
|
||||
import StarCircleIcon from 'material-ui/svg-icons/action/stars';
|
||||
import StarIcon from 'material-ui/svg-icons/toggle/star';
|
||||
import StarOutlineIcon from 'material-ui/svg-icons/toggle/star-border';
|
||||
import VerifyIcon from 'material-ui/svg-icons/action/verified-user';
|
||||
import VisibleIcon from 'material-ui/svg-icons/image/remove-red-eye';
|
||||
import VpnIcon from 'material-ui/svg-icons/notification/vpn-lock';
|
||||
|
||||
export {
|
||||
AddIcon,
|
||||
AttachFileIcon,
|
||||
CancelIcon,
|
||||
CheckIcon,
|
||||
CloseIcon,
|
||||
CompareIcon,
|
||||
ComputerIcon,
|
||||
ContractIcon,
|
||||
CopyIcon,
|
||||
DashboardIcon,
|
||||
DeleteIcon,
|
||||
DoneIcon,
|
||||
EditIcon,
|
||||
FingerprintIcon,
|
||||
LinkIcon,
|
||||
LockedIcon,
|
||||
MoveIcon,
|
||||
NextIcon,
|
||||
PrevIcon,
|
||||
PrintIcon,
|
||||
RefreshIcon,
|
||||
SaveIcon,
|
||||
SendIcon,
|
||||
SnoozeIcon,
|
||||
StarIcon,
|
||||
StarCircleIcon,
|
||||
StarOutlineIcon,
|
||||
VerifyIcon,
|
||||
VisibleIcon,
|
||||
VpnIcon
|
||||
};
|
||||
export AddIcon from 'material-ui/svg-icons/content/add';
|
||||
export AttachFileIcon from 'material-ui/svg-icons/editor/attach-file';
|
||||
export CancelIcon from 'material-ui/svg-icons/content/clear';
|
||||
export CheckIcon from 'material-ui/svg-icons/navigation/check';
|
||||
export CloseIcon from 'material-ui/svg-icons/navigation/close';
|
||||
export CompareIcon from 'material-ui/svg-icons/action/compare-arrows';
|
||||
export ComputerIcon from 'material-ui/svg-icons/hardware/desktop-mac';
|
||||
export ContractIcon from 'material-ui/svg-icons/action/code';
|
||||
export CopyIcon from 'material-ui/svg-icons/content/content-copy';
|
||||
export DashboardIcon from 'material-ui/svg-icons/action/dashboard';
|
||||
export DeleteIcon from 'material-ui/svg-icons/action/delete';
|
||||
export DoneIcon from 'material-ui/svg-icons/action/done-all';
|
||||
export EditIcon from 'material-ui/svg-icons/content/create';
|
||||
export FingerprintIcon from 'material-ui/svg-icons/action/fingerprint';
|
||||
export LinkIcon from 'material-ui/svg-icons/content/link';
|
||||
export LockedIcon from 'material-ui/svg-icons/action/lock';
|
||||
export MoveIcon from 'material-ui/svg-icons/action/open-with';
|
||||
export NextIcon from 'material-ui/svg-icons/navigation/arrow-forward';
|
||||
export PrevIcon from 'material-ui/svg-icons/navigation/arrow-back';
|
||||
export PrintIcon from 'material-ui/svg-icons/action/print';
|
||||
export RefreshIcon from 'material-ui/svg-icons/action/autorenew';
|
||||
export SaveIcon from 'material-ui/svg-icons/content/save';
|
||||
export SendIcon from 'material-ui/svg-icons/content/send';
|
||||
export SnoozeIcon from 'material-ui/svg-icons/av/snooze';
|
||||
export StarCircleIcon from 'material-ui/svg-icons/action/stars';
|
||||
export StarIcon from 'material-ui/svg-icons/toggle/star';
|
||||
export StarOutlineIcon from 'material-ui/svg-icons/toggle/star-border';
|
||||
export VerifyIcon from 'material-ui/svg-icons/action/verified-user';
|
||||
export VisibleIcon from 'material-ui/svg-icons/image/remove-red-eye';
|
||||
export VpnIcon from 'material-ui/svg-icons/notification/vpn-lock';
|
||||
|
@ -16,8 +16,8 @@
|
||||
*/
|
||||
|
||||
.section {
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.overlay {
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
@ -33,7 +33,6 @@
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
overflow-x: hidden;
|
||||
|
||||
/* TODO: As per JS comments, the flex-base could be adjusted in the future to allow for */
|
||||
/* case where <> 3 columns are required should the need arrise from a UI pov. */
|
||||
@ -42,38 +41,28 @@
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex: 0 1 33.33%;
|
||||
opacity: 0.75;
|
||||
overflow-x: hidden;
|
||||
max-width: 33.33%;
|
||||
opacity: 0.85;
|
||||
padding: 0.25em;
|
||||
transition: all 0.75s cubic-bezier(0.23, 1, 0.32, 1);
|
||||
|
||||
/* TODO: The hover and no-hover states can be improved to not "just appear" */
|
||||
&:not(:hover) {
|
||||
& [data-hover="hide"] {
|
||||
}
|
||||
|
||||
& [data-hover="show"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
z-index: 100;
|
||||
|
||||
& [data-hover="hide"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& [data-hover="show"] {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.stretch-on:hover {
|
||||
flex: 0 0 50%;
|
||||
}
|
||||
&:hover {
|
||||
.item {
|
||||
&.stretchOn {
|
||||
flex: 0 1 29%;
|
||||
max-width: 29%;
|
||||
|
||||
&.stretch-off:hover {
|
||||
&:hover {
|
||||
flex: 0 0 42%;
|
||||
max-width: 42%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,29 +74,34 @@ export default class SectionList extends Component {
|
||||
className={ styles.row }
|
||||
key={ `row_${index}` }
|
||||
>
|
||||
{ row.map(this.renderItem) }
|
||||
{
|
||||
row
|
||||
.map(this.renderItem)
|
||||
.filter((item) => item)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderItem = (item, index) => {
|
||||
const { noStretch, renderItem } = this.props;
|
||||
const itemRendered = renderItem(item, index);
|
||||
|
||||
if (!itemRendered) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// NOTE: Any children that is to be showed or hidden (depending on hover state)
|
||||
// should have the data-hover="show|hide" attributes. For the current implementation
|
||||
// this does the trick, however there may be a case for adding a hover attribute
|
||||
// to an item (mouseEnter/mouseLeave events) and then adjusting the styling with
|
||||
// :root[hover]/:root:not[hover] for the tragetted elements. Currently it is a
|
||||
// CSS-only solution to let the browser do all the work via selectors.
|
||||
return (
|
||||
<div
|
||||
className={ [
|
||||
styles.item,
|
||||
styles[`stretch-${noStretch ? 'off' : 'on'}`]
|
||||
noStretch
|
||||
? styles.stretchOff
|
||||
: styles.stretchOn
|
||||
].join(' ') }
|
||||
key={ `item_${index}` }
|
||||
>
|
||||
{ renderItem(item, index) }
|
||||
{ itemRendered }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ let instance;
|
||||
let renderItem;
|
||||
|
||||
function render (props = {}) {
|
||||
renderItem = sinon.stub();
|
||||
renderItem = sinon.stub().returns('someThing');
|
||||
component = shallow(
|
||||
<SectionList
|
||||
className='testClass'
|
||||
|
@ -23,7 +23,7 @@ const lightTheme = getMuiTheme(lightBaseTheme);
|
||||
const muiTheme = getMuiTheme(darkBaseTheme);
|
||||
|
||||
muiTheme.inkBar.backgroundColor = 'transparent';
|
||||
muiTheme.paper.backgroundColor = 'rgba(0, 0, 0, 0.95)';
|
||||
muiTheme.paper.backgroundColor = 'rgb(18, 18, 18)';
|
||||
muiTheme.raisedButton.primaryTextColor = 'white';
|
||||
muiTheme.snackbar.backgroundColor = 'rgba(255, 30, 30, 0.9)';
|
||||
muiTheme.snackbar.textColor = 'rgba(255, 255, 255, 0.75)';
|
||||
|
@ -14,32 +14,33 @@
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.item {
|
||||
flex: 0 1 50%;
|
||||
width: 50%;
|
||||
position: relative;
|
||||
padding-bottom: 0.25em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.item {
|
||||
box-sizing: border-box;
|
||||
flex: 0 1 50%;
|
||||
padding-bottom: 0.25em;
|
||||
position: relative;
|
||||
width: 50%;
|
||||
|
||||
.item:nth-child(odd) {
|
||||
padding-right: 0.125em;
|
||||
}
|
||||
&:nth-child(odd) {
|
||||
padding-right: 0.125em;
|
||||
}
|
||||
|
||||
.item:nth-child(even) {
|
||||
padding-left: 0.125em;
|
||||
&:nth-child(even) {
|
||||
padding-left: 0.125em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
width: 100%;
|
||||
|
||||
.empty div {
|
||||
color: #aaa;
|
||||
div {
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
|
@ -193,7 +193,11 @@ export default class Summary extends Component {
|
||||
const viewLink = `/${baseLink}/${address}`;
|
||||
|
||||
const content = (
|
||||
<IdentityName address={ address } name={ name } unknown />
|
||||
<IdentityName
|
||||
address={ address }
|
||||
name={ name }
|
||||
unknown
|
||||
/>
|
||||
);
|
||||
|
||||
if (noLink) {
|
||||
|
@ -26,7 +26,7 @@ import styles from './extension.css';
|
||||
|
||||
@observer
|
||||
export default class Extension extends Component {
|
||||
store = new Store();
|
||||
store = Store.get();
|
||||
|
||||
render () {
|
||||
const { showWarning } = this.store;
|
||||
|
@ -29,7 +29,10 @@ const NEXT_DISPLAY = '_parity::extensionWarning::nextDisplay';
|
||||
// 'https://chrome.google.com/webstore/detail/parity-ethereum-integrati/himekenlppkgeaoeddcliojfddemadig';
|
||||
const EXTENSION_PAGE = 'https://chrome.google.com/webstore/detail/himekenlppkgeaoeddcliojfddemadig';
|
||||
|
||||
let instance;
|
||||
|
||||
export default class Store {
|
||||
@observable hasExtension = false;
|
||||
@observable isInstalling = false;
|
||||
@observable nextDisplay = 0;
|
||||
@observable shouldInstall = false;
|
||||
@ -43,6 +46,10 @@ export default class Store {
|
||||
return !this.isInstalling && this.shouldInstall && (Date.now() > this.nextDisplay);
|
||||
}
|
||||
|
||||
@action setExtensionActive = () => {
|
||||
this.hasExtension = true;
|
||||
}
|
||||
|
||||
@action setInstalling = (isInstalling) => {
|
||||
this.isInstalling = isInstalling;
|
||||
}
|
||||
@ -61,6 +68,7 @@ export default class Store {
|
||||
const ua = browser.analyze(navigator.userAgent || '');
|
||||
|
||||
if (hasExtension) {
|
||||
this.setExtensionActive();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -97,4 +105,12 @@ export default class Store {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static get () {
|
||||
if (!instance) {
|
||||
instance = new Store();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.toolbar {
|
||||
background: none !important;
|
||||
height: 72px !important;
|
||||
@ -53,7 +54,7 @@
|
||||
|
||||
.tabLink,
|
||||
.settings,
|
||||
.logo,
|
||||
.first,
|
||||
.last {
|
||||
background: rgba(0, 0, 0, 0.5) !important; /* rgba(0, 0, 0, 0.25) !important; */
|
||||
}
|
||||
@ -73,27 +74,17 @@
|
||||
right: -12px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 0 0 -24px;
|
||||
padding: 20px 24px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
height: 28px;
|
||||
margin-right: 0.75em;
|
||||
}
|
||||
|
||||
.logo div {
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
line-height: 32px;
|
||||
vertical-align: top;
|
||||
letter-spacing: 0.2em;
|
||||
}
|
||||
|
||||
.first,
|
||||
.last {
|
||||
margin: 0 -24px 0 0;
|
||||
margin: 0;
|
||||
padding: 36px 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-left: -24px;
|
||||
}
|
||||
|
||||
.last {
|
||||
margin-right: -24px;
|
||||
}
|
||||
|
@ -20,13 +20,16 @@ import { Link } from 'react-router';
|
||||
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
|
||||
import { Tooltip } from '~/ui';
|
||||
|
||||
import Tab from './Tab';
|
||||
import styles from './tabBar.css';
|
||||
|
||||
class TabBar extends Component {
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
isTest: PropTypes.bool,
|
||||
netChain: PropTypes.string,
|
||||
@ -41,72 +44,48 @@ class TabBar extends Component {
|
||||
render () {
|
||||
return (
|
||||
<Toolbar className={ styles.toolbar }>
|
||||
{ this.renderLogo() }
|
||||
{ this.renderTabs() }
|
||||
{ this.renderLast() }
|
||||
<ToolbarGroup className={ styles.first }>
|
||||
<div />
|
||||
</ToolbarGroup>
|
||||
<div className={ styles.tabs }>
|
||||
{ this.renderTabItems() }
|
||||
</div>
|
||||
<ToolbarGroup className={ styles.last }>
|
||||
<div />
|
||||
</ToolbarGroup>
|
||||
</Toolbar>
|
||||
);
|
||||
}
|
||||
|
||||
renderLogo () {
|
||||
return (
|
||||
<ToolbarGroup>
|
||||
<div className={ styles.logo }>
|
||||
<img
|
||||
height={ 28 }
|
||||
src={ imagesEthcoreBlock }
|
||||
/>
|
||||
</div>
|
||||
</ToolbarGroup>
|
||||
);
|
||||
}
|
||||
|
||||
renderLast () {
|
||||
return (
|
||||
<ToolbarGroup>
|
||||
<div className={ styles.last }>
|
||||
<div />
|
||||
</div>
|
||||
</ToolbarGroup>
|
||||
);
|
||||
}
|
||||
|
||||
renderTabs () {
|
||||
renderTabItems () {
|
||||
const { views, pending } = this.props;
|
||||
|
||||
const items = views
|
||||
.map((view, index) => {
|
||||
const body = (view.id === 'accounts')
|
||||
? (
|
||||
<Tooltip
|
||||
className={ styles.tabbarTooltip }
|
||||
text='navigate between the different parts and views of the application, switching between an account view, token view and distributed application view'
|
||||
/>
|
||||
)
|
||||
: null;
|
||||
return views.map((view, index) => {
|
||||
const body = (view.id === 'accounts')
|
||||
? (
|
||||
<Tooltip
|
||||
className={ styles.tabbarTooltip }
|
||||
text='navigate between the different parts and views of the application, switching between an account view, token view and distributed application view'
|
||||
/>
|
||||
)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<Link
|
||||
activeClassName={ styles.tabactive }
|
||||
className={ styles.tabLink }
|
||||
key={ view.id }
|
||||
to={ view.route }
|
||||
return (
|
||||
<Link
|
||||
activeClassName={ styles.tabactive }
|
||||
className={ styles.tabLink }
|
||||
key={ view.id }
|
||||
to={ view.route }
|
||||
>
|
||||
<Tab
|
||||
pendings={ pending.length }
|
||||
view={ view }
|
||||
>
|
||||
<Tab
|
||||
pendings={ pending.length }
|
||||
view={ view }
|
||||
>
|
||||
{ body }
|
||||
</Tab>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={ styles.tabs }>
|
||||
{ items }
|
||||
</div>
|
||||
);
|
||||
{ body }
|
||||
</Tab>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,99 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { withRouter } from 'react-router';
|
||||
|
||||
import Button from '~/ui/Button';
|
||||
import { LinkIcon } from '~/ui/Icons';
|
||||
import Input from '~/ui/Form/Input';
|
||||
|
||||
import styles from './urlButton.css';
|
||||
|
||||
const INPUT_STYLE = { display: 'inline-block', width: '20em' };
|
||||
|
||||
class UrlButton extends Component {
|
||||
static propTypes = {
|
||||
router: PropTypes.object.isRequired // injected by withRouter
|
||||
};
|
||||
|
||||
state = {
|
||||
inputShown: false
|
||||
};
|
||||
|
||||
render () {
|
||||
const { inputShown } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ inputShown ? this.renderInput() : null }
|
||||
<Button
|
||||
className={ styles.button }
|
||||
icon={ <LinkIcon /> }
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='dapps.button.url.label'
|
||||
defaultMessage='URL'
|
||||
/>
|
||||
}
|
||||
onClick={ this.toggleInput }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderInput () {
|
||||
return (
|
||||
<Input
|
||||
hint={
|
||||
<FormattedMessage
|
||||
id='dapps.button.url.input'
|
||||
defaultMessage='https://mkr.market'
|
||||
/>
|
||||
}
|
||||
onBlur={ this.hideInput }
|
||||
onFocus={ this.showInput }
|
||||
onSubmit={ this.inputOnSubmit }
|
||||
style={ INPUT_STYLE }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
toggleInput = () => {
|
||||
const { inputShown } = this.state;
|
||||
|
||||
this.setState({
|
||||
inputShown: !inputShown
|
||||
});
|
||||
}
|
||||
|
||||
hideInput = () => {
|
||||
this.setState({ inputShown: false });
|
||||
}
|
||||
|
||||
showInput = () => {
|
||||
this.setState({ inputShown: true });
|
||||
}
|
||||
|
||||
inputOnSubmit = (url) => {
|
||||
const { router } = this.props;
|
||||
|
||||
router.push(`/web/${encodeURIComponent(url)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(UrlButton);
|
@ -19,18 +19,20 @@
|
||||
flex-wrap: wrap;
|
||||
margin: -0.125em;
|
||||
position: relative;
|
||||
|
||||
.item {
|
||||
box-sizing: border-box;
|
||||
flex: 0 1 50%;
|
||||
opacity: 0.85;
|
||||
padding: 0.125em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.list+.list {
|
||||
margin-top: -0.25em;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 0.125em;
|
||||
flex: 0 1 50%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
bottom: 0.5em;
|
||||
|
@ -26,7 +26,6 @@ import PermissionStore from '~/modals/DappPermissions/store';
|
||||
import { Actionbar, Button, DappCard, Page } from '~/ui';
|
||||
import { LockedIcon, VisibleIcon } from '~/ui/Icons';
|
||||
|
||||
import UrlButton from './UrlButton';
|
||||
import DappsStore from './dappsStore';
|
||||
|
||||
import styles from './dapps.css';
|
||||
@ -92,7 +91,6 @@ class Dapps extends Component {
|
||||
/>
|
||||
}
|
||||
buttons={ [
|
||||
<UrlButton key='url' />,
|
||||
<Button
|
||||
icon={ <VisibleIcon /> }
|
||||
key='edit'
|
||||
|
50
js/src/views/Home/Accounts/accounts.css
Normal file
50
js/src/views/Home/Accounts/accounts.css
Normal file
@ -0,0 +1,50 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.accounts {
|
||||
margin-top: 1.5em;
|
||||
text-align: center;
|
||||
|
||||
.account {
|
||||
position: relative;
|
||||
line-height: 2em;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
|
||||
.icon {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.link, .name {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.name {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
color: #aaa;
|
||||
font-size: 0.75em;
|
||||
line-height: 1em;
|
||||
padding-top: 0;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
139
js/src/views/Home/Accounts/accounts.js
Normal file
139
js/src/views/Home/Accounts/accounts.js
Normal file
@ -0,0 +1,139 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import moment from 'moment';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import { Container, ContainerTitle, IdentityName, IdentityIcon, SectionList } from '~/ui';
|
||||
import { arrayOrObjectProptype } from '~/util/proptypes';
|
||||
|
||||
import styles from './accounts.css';
|
||||
|
||||
class Accounts extends Component {
|
||||
static propTypes = {
|
||||
accountsInfo: PropTypes.object,
|
||||
history: arrayOrObjectProptype().isRequired
|
||||
};
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className={ styles.accounts }>
|
||||
<ContainerTitle
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='home.accounts.title'
|
||||
defaultMessage='Recent Accounts'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{ this.renderHistory() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderHistory () {
|
||||
const { accountsInfo, history } = this.props;
|
||||
|
||||
if (!accountsInfo || !Object.keys(accountsInfo).length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!history.length) {
|
||||
return (
|
||||
<div className={ styles.empty }>
|
||||
<FormattedMessage
|
||||
id='home.accounts.none'
|
||||
defaultMessage='No recent accounts history available'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionList
|
||||
items={ history }
|
||||
renderItem={ this.renderHistoryItem }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderHistoryItem = (history) => {
|
||||
const { accountsInfo } = this.props;
|
||||
|
||||
if (!history || !history.entry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const account = accountsInfo[history.entry] || { meta: {} };
|
||||
let linkType = 'addresses';
|
||||
|
||||
if (account.uuid) {
|
||||
linkType = 'accounts';
|
||||
} else if (account.meta.wallet) {
|
||||
linkType = 'wallet';
|
||||
}
|
||||
|
||||
return (
|
||||
<Container
|
||||
className={ styles.account }
|
||||
key={ history.timestamp }
|
||||
hover={
|
||||
<div className={ styles.timestamp }>
|
||||
<FormattedMessage
|
||||
id='home.account.visited'
|
||||
defaultMessage='accessed {when}'
|
||||
values={ {
|
||||
when: moment(history.timestamp).fromNow()
|
||||
} }
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Link
|
||||
className={ styles.link }
|
||||
to={ `/${linkType}/${history.entry}` }
|
||||
>
|
||||
<IdentityIcon
|
||||
address={ history.entry }
|
||||
className={ styles.icon }
|
||||
center
|
||||
/>
|
||||
<IdentityName
|
||||
address={ history.entry }
|
||||
className={ styles.name }
|
||||
unknown
|
||||
/>
|
||||
</Link>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { accountsInfo } = state.personal;
|
||||
|
||||
return {
|
||||
accountsInfo
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(Accounts);
|
71
js/src/views/Home/Accounts/accounts.spec.js
Normal file
71
js/src/views/Home/Accounts/accounts.spec.js
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import Accounts from './';
|
||||
|
||||
let component;
|
||||
let store;
|
||||
|
||||
function createRedux () {
|
||||
store = {
|
||||
dispatch: sinon.stub(),
|
||||
subscribe: sinon.stub(),
|
||||
getState: () => {
|
||||
return {
|
||||
personal: {
|
||||
accountsInfo: { '0x123': {} }
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
function render (history = []) {
|
||||
component = shallow(
|
||||
<Accounts
|
||||
history={ history }
|
||||
/>,
|
||||
{
|
||||
context: {
|
||||
store: createRedux()
|
||||
}
|
||||
}
|
||||
).find('Accounts').shallow();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('views/Home/Accounts', () => {
|
||||
it('renders defaults', () => {
|
||||
expect(render()).to.be.ok;
|
||||
});
|
||||
|
||||
describe('no history', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('renders empty message', () => {
|
||||
expect(component.find('FormattedMessage').props().id).to.equal('home.accounts.none');
|
||||
});
|
||||
});
|
||||
});
|
@ -14,4 +14,4 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './urlButton';
|
||||
export default from './accounts';
|
89
js/src/views/Home/Dapps/dapp.js
Normal file
89
js/src/views/Home/Dapps/dapp.js
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import moment from 'moment';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import { Container, DappIcon } from '~/ui';
|
||||
|
||||
import styles from './dapps.css';
|
||||
|
||||
export default class Dapp extends Component {
|
||||
static propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
store: PropTypes.object.isRequired,
|
||||
timestamp: PropTypes.number.isRequired
|
||||
}
|
||||
|
||||
state = {
|
||||
dapp: null
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
return this.loadApp();
|
||||
}
|
||||
|
||||
render () {
|
||||
const { id, timestamp } = this.props;
|
||||
const { dapp } = this.state;
|
||||
|
||||
if (!dapp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Container
|
||||
className={ styles.dapp }
|
||||
hover={
|
||||
<div className={ styles.timestamp }>
|
||||
<FormattedMessage
|
||||
id='home.dapp.visited'
|
||||
defaultMessage='accessed {when}'
|
||||
values={ {
|
||||
when: moment(timestamp).fromNow()
|
||||
} }
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Link
|
||||
className={ styles.link }
|
||||
to={ `/app/${id}` }
|
||||
>
|
||||
<DappIcon
|
||||
app={ dapp }
|
||||
className={ styles.icon }
|
||||
/>
|
||||
<span className={ styles.name }>
|
||||
{ dapp.name }
|
||||
</span>
|
||||
</Link>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
loadApp = () => {
|
||||
const { id, store } = this.props;
|
||||
|
||||
return store
|
||||
.loadApp(id)
|
||||
.then((dapp) => {
|
||||
this.setState({ dapp });
|
||||
});
|
||||
}
|
||||
}
|
55
js/src/views/Home/Dapps/dapp.spec.js
Normal file
55
js/src/views/Home/Dapps/dapp.spec.js
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import Dapp from './dapp';
|
||||
|
||||
import { createStore } from './dapps.test.js';
|
||||
|
||||
let component;
|
||||
let instance;
|
||||
let store;
|
||||
|
||||
function render () {
|
||||
store = createStore();
|
||||
component = shallow(
|
||||
<Dapp
|
||||
id='testId'
|
||||
store={ store }
|
||||
timestamp={ Date.now() }
|
||||
/>
|
||||
);
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('views/Home/Dapp', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
return instance.componentWillMount();
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
it('loads the dapp on mount', () => {
|
||||
expect(store.loadApp).to.have.been.calledWith('testId');
|
||||
});
|
||||
});
|
48
js/src/views/Home/Dapps/dapps.css
Normal file
48
js/src/views/Home/Dapps/dapps.css
Normal file
@ -0,0 +1,48 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.dapps {
|
||||
margin-top: 1.5em;
|
||||
text-align: center;
|
||||
|
||||
.dapp {
|
||||
position: relative;
|
||||
line-height: 2em;
|
||||
vertical-align: middle;
|
||||
|
||||
.icon {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.link, .name {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.name {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
color: #aaa;
|
||||
font-size: 0.75em;
|
||||
line-height: 1em;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
86
js/src/views/Home/Dapps/dapps.js
Normal file
86
js/src/views/Home/Dapps/dapps.js
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { ContainerTitle, SectionList } from '~/ui';
|
||||
import { arrayOrObjectProptype } from '~/util/proptypes';
|
||||
|
||||
import Dapp from './dapp';
|
||||
import styles from './dapps.css';
|
||||
|
||||
export default class Dapps extends Component {
|
||||
static propTypes = {
|
||||
history: arrayOrObjectProptype().isRequired,
|
||||
store: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className={ styles.dapps }>
|
||||
<ContainerTitle
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='home.dapps.title'
|
||||
defaultMessage='Recent Dapps'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{ this.renderHistory() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderHistory () {
|
||||
const { history } = this.props;
|
||||
|
||||
if (!history.length) {
|
||||
return (
|
||||
<div className={ styles.empty }>
|
||||
<FormattedMessage
|
||||
id='home.dapps.none'
|
||||
defaultMessage='No recent Applications history available'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionList
|
||||
items={ history }
|
||||
renderItem={ this.renderHistoryItem }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderHistoryItem = (history) => {
|
||||
if (!history || !history.entry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { store } = this.props;
|
||||
|
||||
return (
|
||||
<Dapp
|
||||
id={ history.entry }
|
||||
key={ history.timestamp }
|
||||
store={ store }
|
||||
timestamp={ history.timestamp }
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
68
js/src/views/Home/Dapps/dapps.spec.js
Normal file
68
js/src/views/Home/Dapps/dapps.spec.js
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import Dapps from './';
|
||||
|
||||
import { createStore } from './dapps.test.js';
|
||||
|
||||
let component;
|
||||
let store;
|
||||
|
||||
function render (history = []) {
|
||||
store = createStore();
|
||||
component = shallow(
|
||||
<Dapps
|
||||
history={ history }
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('views/Home/Dapps', () => {
|
||||
it('renders defaults', () => {
|
||||
expect(render()).to.be.ok;
|
||||
});
|
||||
|
||||
describe('no history', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('renders empty message', () => {
|
||||
expect(component.find('FormattedMessage').props().id).to.equal('home.dapps.none');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with history', () => {
|
||||
const HISTORY = [
|
||||
{ timestamp: 1, entry: 'testABC' },
|
||||
{ timestamp: 2, entry: 'testDEF' }
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
render(HISTORY);
|
||||
});
|
||||
|
||||
it('renders SectionList', () => {
|
||||
expect(component.find('SectionList').length).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
27
js/src/views/Home/Dapps/dapps.test.js
Normal file
27
js/src/views/Home/Dapps/dapps.test.js
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sinon from 'sinon';
|
||||
|
||||
function createStore () {
|
||||
return {
|
||||
loadApp: sinon.stub().resolves({ name: 'testName' })
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
createStore
|
||||
};
|
17
js/src/views/Home/Dapps/index.js
Normal file
17
js/src/views/Home/Dapps/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './dapps';
|
17
js/src/views/Home/News/index.js
Normal file
17
js/src/views/Home/News/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './news';
|
73
js/src/views/Home/News/news.css
Normal file
73
js/src/views/Home/News/news.css
Normal file
@ -0,0 +1,73 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.news {
|
||||
}
|
||||
|
||||
.markdown {
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
.item {
|
||||
height: 240px;
|
||||
opacity: 0.85;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.background {
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
background-size: cover;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
background: white;
|
||||
color: #333;
|
||||
display: none;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 100%;
|
||||
padding: 0 1.5em 1em 1.5em;
|
||||
}
|
||||
|
||||
.title {
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
bottom: 0;
|
||||
color: #333;
|
||||
font-size: 1.17em;
|
||||
left: 0;
|
||||
padding: 1rem 1.5rem;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
|
||||
.overlay {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
92
js/src/views/Home/News/news.js
Normal file
92
js/src/views/Home/News/news.js
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
|
||||
import { SectionList } from '~/ui';
|
||||
|
||||
import { createRenderers } from './renderers';
|
||||
import Store from './store';
|
||||
import styles from './news.css';
|
||||
|
||||
const VERSION_ID = '1';
|
||||
|
||||
@observer
|
||||
export default class News extends Component {
|
||||
store = Store.get();
|
||||
|
||||
componentWillMount () {
|
||||
return this.store.retrieveNews(VERSION_ID);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { newsItems } = this.store;
|
||||
|
||||
if (!newsItems || !newsItems.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionList
|
||||
className={ styles.news }
|
||||
items={ newsItems }
|
||||
renderItem={ this.renderItem }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderItem = (item) => {
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const inlineStyles = item.style || {};
|
||||
|
||||
return (
|
||||
<div className={ styles.item }>
|
||||
<div
|
||||
className={ styles.background }
|
||||
style={ {
|
||||
backgroundImage: `url(${item.background})`
|
||||
} }
|
||||
/>
|
||||
<div
|
||||
className={ styles.title }
|
||||
style={ inlineStyles.head }
|
||||
>
|
||||
{ item.title }
|
||||
</div>
|
||||
<div
|
||||
className={ styles.overlay }
|
||||
style={ inlineStyles.body }
|
||||
>
|
||||
<ReactMarkdown
|
||||
className={ styles.markdown }
|
||||
renderers={ createRenderers(inlineStyles.tags) }
|
||||
source={ item.markdown }
|
||||
softBreak='br'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
VERSION_ID
|
||||
};
|
54
js/src/views/Home/News/news.spec.js
Normal file
54
js/src/views/Home/News/news.spec.js
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import News from './news';
|
||||
import { restoreGlobals, stubGlobals } from './news.test.js';
|
||||
|
||||
let component;
|
||||
let instance;
|
||||
|
||||
function render () {
|
||||
component = shallow(
|
||||
<News />
|
||||
);
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('views/Home/News', () => {
|
||||
beforeEach(() => {
|
||||
stubGlobals();
|
||||
render();
|
||||
|
||||
return instance.componentWillMount();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
restoreGlobals();
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
it('retrieves the content meta on mount', () => {
|
||||
expect(instance.store.newsItems).to.equal('testContent');
|
||||
});
|
||||
});
|
54
js/src/views/Home/News/news.test.js
Normal file
54
js/src/views/Home/News/news.test.js
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sinon from 'sinon';
|
||||
|
||||
import Contracts from '~/contracts';
|
||||
|
||||
import { VERSION_ID } from './news';
|
||||
|
||||
let contracts;
|
||||
let globalContractsGet;
|
||||
let globalFetch;
|
||||
|
||||
export function stubGlobals () {
|
||||
contracts = {
|
||||
githubHint: {
|
||||
getEntry: sinon.stub().resolves(['testUrl', 'testOwner', 'testCommit'])
|
||||
},
|
||||
registry: {
|
||||
lookupMeta: sinon.stub().resolves('testMeta')
|
||||
}
|
||||
};
|
||||
|
||||
globalContractsGet = Contracts.get;
|
||||
globalFetch = global.fetch;
|
||||
|
||||
sinon.stub(Contracts, 'get', () => contracts);
|
||||
sinon.stub(global, 'fetch').resolves({
|
||||
ok: true,
|
||||
json: sinon.stub().resolves({
|
||||
[VERSION_ID]: {
|
||||
items: 'testContent'
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
export function restoreGlobals () {
|
||||
Contracts.get = globalContractsGet;
|
||||
global.fetch = globalFetch;
|
||||
}
|
37
js/src/views/Home/News/renderers.js
Normal file
37
js/src/views/Home/News/renderers.js
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { createElement } from 'react';
|
||||
|
||||
export function createRenderers (tagStyles = {}) {
|
||||
return Object
|
||||
.keys(tagStyles)
|
||||
.reduce((renderers, tag) => {
|
||||
switch (tag) {
|
||||
case 'a':
|
||||
case 'link':
|
||||
renderers['link'] = (mdProps) => {
|
||||
const { children, href, title } = mdProps;
|
||||
const style = tagStyles[tag];
|
||||
|
||||
return createElement('a', { href, title, style }, children);
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return renderers;
|
||||
}, {});
|
||||
}
|
67
js/src/views/Home/News/store.js
Normal file
67
js/src/views/Home/News/store.js
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { action, observable } from 'mobx';
|
||||
import Contracts from '~/contracts';
|
||||
|
||||
let instance = null;
|
||||
|
||||
export default class Store {
|
||||
@observable newsItems = null;
|
||||
|
||||
@action setNewsItems = (newsItems) => {
|
||||
this.newsItems = newsItems;
|
||||
}
|
||||
|
||||
retrieveNews (versionId) {
|
||||
const contracts = Contracts.get();
|
||||
|
||||
return contracts.registry
|
||||
.lookupMeta('paritynews', 'CONTENT')
|
||||
.then((contentId) => {
|
||||
return contracts.githubHint.getEntry(contentId);
|
||||
})
|
||||
.then(([url, owner, commit]) => {
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fetch(url).then((response) => {
|
||||
if (!response.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
});
|
||||
})
|
||||
.then((news) => {
|
||||
if (news && news[versionId]) {
|
||||
this.setNewsItems(news[versionId].items);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('retrieveNews', error);
|
||||
});
|
||||
}
|
||||
|
||||
static get () {
|
||||
if (!instance) {
|
||||
instance = new Store();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
57
js/src/views/Home/News/store.spec.js
Normal file
57
js/src/views/Home/News/store.spec.js
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { VERSION_ID } from './news';
|
||||
import { restoreGlobals, stubGlobals } from './news.test.js';
|
||||
import Store from './store';
|
||||
|
||||
let store;
|
||||
|
||||
function create () {
|
||||
store = new Store();
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
describe('views/Home/News/Store', () => {
|
||||
beforeEach(() => {
|
||||
stubGlobals();
|
||||
create();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
restoreGlobals();
|
||||
});
|
||||
|
||||
describe('@action', () => {
|
||||
describe('setNewsItems', () => {
|
||||
it('sets the items', () => {
|
||||
store.setNewsItems('testing');
|
||||
expect(store.newsItems).to.equal('testing');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('operations', () => {
|
||||
describe('retrieveNews', () => {
|
||||
it('retrieves the items', () => {
|
||||
return store.retrieveNews(VERSION_ID).then(() => {
|
||||
expect(store.newsItems).to.equal('testContent');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
17
js/src/views/Home/Urls/index.js
Normal file
17
js/src/views/Home/Urls/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './urls';
|
80
js/src/views/Home/Urls/urls.css
Normal file
80
js/src/views/Home/Urls/urls.css
Normal file
@ -0,0 +1,80 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.urls {
|
||||
text-align: center;
|
||||
|
||||
.layout {
|
||||
box-sizing: border-box;
|
||||
margin: 1.5em auto;
|
||||
padding: 0 1em;
|
||||
width: 50%;
|
||||
|
||||
.empty {
|
||||
margin-top: 0.5em;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.historyItem {
|
||||
height: auto;
|
||||
margin-top: 1em;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.linkIcon {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: 0.5em;
|
||||
top: 0.5em;
|
||||
}
|
||||
|
||||
.url {
|
||||
display: block;
|
||||
color: rgb(0, 151, 167);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
color: #aaa;
|
||||
font-size: 0.75em;
|
||||
line-height: 1em;
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
|
||||
.linkIcon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
border-radius: 0.25em;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
display: block;
|
||||
font-size: 1.25em;
|
||||
padding: 0.5em;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
139
js/src/views/Home/Urls/urls.js
Normal file
139
js/src/views/Home/Urls/urls.js
Normal file
@ -0,0 +1,139 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import moment from 'moment';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Container, ContainerTitle, DappUrlInput, SectionList } from '~/ui';
|
||||
import { LinkIcon } from '~/ui/Icons';
|
||||
|
||||
import styles from './urls.css';
|
||||
|
||||
@observer
|
||||
export default class Urls extends Component {
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
extensionStore: PropTypes.object.isRequired,
|
||||
store: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
const { nextUrl } = this.props.store;
|
||||
|
||||
return (
|
||||
<div className={ styles.urls }>
|
||||
<div className={ styles.layout }>
|
||||
<ContainerTitle
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='home.url.title'
|
||||
defaultMessage='Web Applications'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<DappUrlInput
|
||||
className={ styles.input }
|
||||
onChange={ this.onChangeUrl }
|
||||
onGoto={ this.onGotoUrl }
|
||||
onRestore={ this.onRestoreUrl }
|
||||
url={ nextUrl }
|
||||
/>
|
||||
{ this.renderHistory() }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderHistory () {
|
||||
const { history } = this.props.store;
|
||||
|
||||
if (!history.length) {
|
||||
return (
|
||||
<div className={ styles.empty }>
|
||||
<FormattedMessage
|
||||
id='home.url.none'
|
||||
defaultMessage='No recent URL history available'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionList
|
||||
items={ history }
|
||||
renderItem={ this.renderHistoryItem }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderHistoryItem = (history) => {
|
||||
if (!history || !history.url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const onNavigate = () => this.onGotoUrl(history.url);
|
||||
|
||||
return (
|
||||
<Container
|
||||
className={ styles.historyItem }
|
||||
hover={
|
||||
<div className={ styles.timestamp }>
|
||||
<FormattedMessage
|
||||
id='home.url.visited'
|
||||
defaultMessage='visited {when}'
|
||||
values={ {
|
||||
when: moment(history.timestamp).fromNow()
|
||||
} }
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
key={ history.timestamp }
|
||||
onClick={ onNavigate }
|
||||
>
|
||||
<LinkIcon className={ styles.linkIcon } />
|
||||
<div className={ styles.url }>
|
||||
{ history.hostname }
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
onChangeUrl = (url) => {
|
||||
this.props.store.setNextUrl(url);
|
||||
}
|
||||
|
||||
onGotoUrl = (url) => {
|
||||
const { router } = this.context;
|
||||
const { extensionStore } = this.props;
|
||||
|
||||
this.props.store.gotoUrl(url);
|
||||
|
||||
if (extensionStore.hasExtension) {
|
||||
window.open(this.props.store.currentUrl, '_blank');
|
||||
} else {
|
||||
router.push('/web');
|
||||
}
|
||||
}
|
||||
|
||||
onRestoreUrl = () => {
|
||||
this.props.store.restoreUrl();
|
||||
}
|
||||
}
|
124
js/src/views/Home/Urls/urls.spec.js
Normal file
124
js/src/views/Home/Urls/urls.spec.js
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import Urls from './';
|
||||
|
||||
const NEXT_URL = 'http://somewhere.next';
|
||||
|
||||
let component;
|
||||
let instance;
|
||||
let router;
|
||||
let store;
|
||||
|
||||
function createRouter () {
|
||||
router = {
|
||||
push: sinon.stub()
|
||||
};
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
function createStore () {
|
||||
store = {
|
||||
history: [],
|
||||
gotoUrl: sinon.stub(),
|
||||
restoreUrl: sinon.stub(),
|
||||
setNextUrl: sinon.stub(),
|
||||
nextUrl: NEXT_URL
|
||||
};
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
function render () {
|
||||
component = shallow(
|
||||
<Urls
|
||||
extensionStore={ { hasExtension: false } }
|
||||
store={ createStore() }
|
||||
/>,
|
||||
{
|
||||
context: {
|
||||
router: createRouter()
|
||||
}
|
||||
}
|
||||
);
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('views/Home/Urls', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
describe('input', () => {
|
||||
let input;
|
||||
|
||||
beforeEach(() => {
|
||||
input = component.find('DappUrlInput');
|
||||
});
|
||||
|
||||
it('renders the input cmponent', () => {
|
||||
expect(input.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('passes nextUrl as url', () => {
|
||||
expect(input.props().url).to.equal(NEXT_URL);
|
||||
});
|
||||
});
|
||||
|
||||
describe('events', () => {
|
||||
describe('onChangeUrl', () => {
|
||||
it('performs setNextUrl on store', () => {
|
||||
instance.onChangeUrl('123');
|
||||
expect(store.setNextUrl).to.have.been.calledWith('123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onGotoUrl', () => {
|
||||
it('performs gotoUrl on store', () => {
|
||||
instance.onGotoUrl();
|
||||
expect(store.gotoUrl).to.have.been.called;
|
||||
});
|
||||
|
||||
it('passed the URL when provided', () => {
|
||||
instance.onGotoUrl('http://example.com');
|
||||
expect(store.gotoUrl).to.have.been.calledWith('http://example.com');
|
||||
});
|
||||
|
||||
it('does route navigation when executed', () => {
|
||||
instance.onGotoUrl();
|
||||
expect(router.push).to.have.been.calledWith('/web');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onRestoreUrl', () => {
|
||||
it('performs restoreUrl on store', () => {
|
||||
instance.onRestoreUrl();
|
||||
expect(store.restoreUrl).to.have.been.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -15,6 +15,26 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.button {
|
||||
vertical-align: middle;
|
||||
.accounts {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
|
||||
.empty {
|
||||
margin-top: 1.5em;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
|
||||
.column {
|
||||
box-sizing: border-box;
|
||||
flex: 0 1 50%;
|
||||
padding: 0 1.5em;
|
||||
width: 50%;
|
||||
}
|
||||
}
|
81
js/src/views/Home/home.js
Normal file
81
js/src/views/Home/home.js
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Page } from '~/ui';
|
||||
|
||||
import DappsStore from '../Dapps/dappsStore';
|
||||
import ExtensionStore from '../Application/Extension/store';
|
||||
import HistoryStore from '../historyStore';
|
||||
import WebStore from '../Web/store';
|
||||
|
||||
import Accounts from './Accounts';
|
||||
import Dapps from './Dapps';
|
||||
import News from './News';
|
||||
import Urls from './Urls';
|
||||
import styles from './home.css';
|
||||
|
||||
@observer
|
||||
export default class Home extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
dappsStore = DappsStore.get(this.context.api);
|
||||
extensionStore = ExtensionStore.get();
|
||||
webStore = WebStore.get(this.context.api);
|
||||
|
||||
accountsHistory = HistoryStore.get('accounts');
|
||||
dappsHistory = HistoryStore.get('dapps');
|
||||
|
||||
componentWillMount () {
|
||||
return this.webStore.loadHistory();
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<Page
|
||||
className={ styles.body }
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='home.title'
|
||||
defaultMessage='Parity Home'
|
||||
/>
|
||||
}
|
||||
>
|
||||
<News />
|
||||
<Urls
|
||||
extensionStore={ this.extensionStore }
|
||||
store={ this.webStore }
|
||||
/>
|
||||
<div className={ styles.row }>
|
||||
<div className={ styles.column }>
|
||||
<Dapps
|
||||
history={ this.dappsHistory.history }
|
||||
store={ this.dappsStore }
|
||||
/>
|
||||
</div>
|
||||
<div className={ styles.column }>
|
||||
<Accounts history={ this.accountsHistory.history } />
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
96
js/src/views/Home/home.spec.js
Normal file
96
js/src/views/Home/home.spec.js
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import Home from './';
|
||||
|
||||
const TEST_APP_HISTORY = [];
|
||||
|
||||
let api;
|
||||
let component;
|
||||
let instance;
|
||||
|
||||
function createApi () {
|
||||
api = {
|
||||
parity: {
|
||||
listRecentDapps: sinon.stub().resolves(TEST_APP_HISTORY)
|
||||
}
|
||||
};
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
function render () {
|
||||
component = shallow(
|
||||
<Home />,
|
||||
{
|
||||
context: {
|
||||
api: createApi()
|
||||
}
|
||||
}
|
||||
);
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('views/Home', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
describe('lifecycle', () => {
|
||||
describe('componentWillMount', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(instance.webStore, 'loadHistory');
|
||||
return instance.componentWillMount();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
instance.webStore.loadHistory.restore();
|
||||
});
|
||||
|
||||
it('calls into webStore loadHistory', () => {
|
||||
expect(instance.webStore.loadHistory).to.have.been.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('components', () => {
|
||||
it('renders Accounts', () => {
|
||||
expect(component.find('Connect(Accounts)').length).to.equal(1);
|
||||
});
|
||||
|
||||
it('renders Dapps', () => {
|
||||
expect(component.find('Dapps').length).to.equal(1);
|
||||
});
|
||||
|
||||
it('renders News', () => {
|
||||
expect(component.find('News').length).to.equal(1);
|
||||
});
|
||||
|
||||
it('renders Urls', () => {
|
||||
expect(component.find('Urls').length).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
17
js/src/views/Home/index.js
Normal file
17
js/src/views/Home/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './home';
|
@ -19,20 +19,7 @@ $overlayZ: 10000;
|
||||
$modalZ: 10001;
|
||||
|
||||
.account {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.accountOverlay {
|
||||
position: absolute;
|
||||
right: 0.5em;
|
||||
top: 0.5em;
|
||||
}
|
||||
|
||||
.iconDisabled {
|
||||
opacity: 0.15;
|
||||
}
|
||||
width: 100%;
|
||||
|
||||
.selected,
|
||||
.unselected {
|
||||
|
@ -23,7 +23,24 @@ import CommunicationContacts from 'material-ui/svg-icons/communication/contacts'
|
||||
import ImageGridOn from 'material-ui/svg-icons/image/grid-on';
|
||||
import NavigationApps from 'material-ui/svg-icons/navigation/apps';
|
||||
|
||||
import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
|
||||
|
||||
import styles from './views.css';
|
||||
|
||||
const defaultViews = {
|
||||
home: {
|
||||
active: true,
|
||||
fixed: true,
|
||||
icon: (
|
||||
<img
|
||||
className={ styles.logoIcon }
|
||||
src={ imagesEthcoreBlock }
|
||||
/>
|
||||
),
|
||||
route: '/home',
|
||||
value: 'home'
|
||||
},
|
||||
|
||||
accounts: {
|
||||
active: true,
|
||||
fixed: true,
|
||||
|
@ -43,6 +43,12 @@
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.logoIcon {
|
||||
height: 24px;
|
||||
margin-bottom: 5px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.info {
|
||||
color: #aaa;
|
||||
padding-left: 4.75em;
|
||||
|
@ -82,8 +82,31 @@ export default class Store {
|
||||
this.setNextUrl(this.currentUrl);
|
||||
}
|
||||
|
||||
@action setHistory = (history) => {
|
||||
this.history = history;
|
||||
@action setHistory = (urls) => {
|
||||
this.history = Object
|
||||
.keys(urls)
|
||||
.filter((url) => url && !url.startsWith(this._api.dappsUrl) && url.indexOf('127.0.0.1') === -1)
|
||||
.sort((urlA, urlB) => {
|
||||
const timeA = urls[urlA].getTime();
|
||||
const timeB = urls[urlB].getTime();
|
||||
|
||||
if (timeA > timeB) {
|
||||
return -1;
|
||||
} else if (timeA < timeB) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
})
|
||||
.map((url) => {
|
||||
const hostname = url.replace(/^http[s]?:\/\//, '').split('/')[0];
|
||||
|
||||
return {
|
||||
hostname,
|
||||
timestamp: urls[url],
|
||||
url
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@action setLoading = (isLoading) => {
|
||||
|
@ -18,7 +18,13 @@ import sinon from 'sinon';
|
||||
|
||||
import Store from './store';
|
||||
|
||||
const TEST_HISTORY = ['somethingA', 'somethingB'];
|
||||
const TEST_HISTORY_URLA = 'http://testingA';
|
||||
const TEST_HISTORY_URLB = 'http://testingB';
|
||||
const TEST_HISTORY = {
|
||||
'': new Date(678),
|
||||
[TEST_HISTORY_URLA]: new Date(123),
|
||||
[TEST_HISTORY_URLB]: new Date(456)
|
||||
};
|
||||
const TEST_TOKEN = 'testing-123';
|
||||
const TEST_URL1 = 'http://some.test.domain.com';
|
||||
const TEST_URL2 = 'http://something.different.com';
|
||||
@ -89,9 +95,28 @@ describe('views/Web/Store', () => {
|
||||
});
|
||||
|
||||
describe('setHistory', () => {
|
||||
it('sets the history', () => {
|
||||
let history;
|
||||
|
||||
beforeEach(() => {
|
||||
store.setHistory(TEST_HISTORY);
|
||||
expect(store.history.peek()).to.deep.equal(TEST_HISTORY);
|
||||
history = store.history.peek();
|
||||
});
|
||||
|
||||
it('sets the history', () => {
|
||||
expect(history.length).to.equal(2);
|
||||
});
|
||||
|
||||
it('adds hostname to entries', () => {
|
||||
expect(history[1].hostname).to.be.ok;
|
||||
});
|
||||
|
||||
it('removes hostname http prefixes', () => {
|
||||
expect(history[1].hostname.indexOf('http')).to.equal(-1);
|
||||
});
|
||||
|
||||
it('sorts the entries according to recently accessed', () => {
|
||||
expect(history[0].url).to.equal(TEST_HISTORY_URLB);
|
||||
expect(history[1].url).to.equal(TEST_HISTORY_URLA);
|
||||
});
|
||||
});
|
||||
|
||||
@ -195,7 +220,7 @@ describe('views/Web/Store', () => {
|
||||
});
|
||||
|
||||
it('sets the history as retrieved', () => {
|
||||
expect(store.history.peek()).to.deep.equal(TEST_HISTORY);
|
||||
expect(store.history.peek().length).not.to.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -30,10 +30,11 @@ export default class Store {
|
||||
this.load();
|
||||
}
|
||||
|
||||
@action add = (entry) => {
|
||||
@action add = (entry, type) => {
|
||||
this.history = [{
|
||||
entry,
|
||||
timestamp: Date.now(),
|
||||
entry
|
||||
type
|
||||
}].concat(this.history.filter((h) => h.entry !== entry)).slice(0, MAX_ENTRIES);
|
||||
this.save();
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ export Contracts from './Contracts';
|
||||
export Dapp from './Dapp';
|
||||
export Dapps from './Dapps';
|
||||
export HistoryStore from './historyStore';
|
||||
export Home from './Home';
|
||||
export ParityBar from './ParityBar';
|
||||
export Settings, { SettingsBackground, SettingsParity, SettingsProxy, SettingsViews } from './Settings';
|
||||
export Signer from './Signer';
|
||||
|
Loading…
Reference in New Issue
Block a user