[redesign] navigation/wallet pages
This commit is contained in:
parent
2d72d1f663
commit
f1c960c3c6
93 changed files with 1551 additions and 6033 deletions
|
@ -18,6 +18,7 @@ module.name_mapper='^types\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/types\1'
|
|||
module.name_mapper='^component\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/component\1'
|
||||
module.name_mapper='^page\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/page\1'
|
||||
module.name_mapper='^lbry\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/lbry\1'
|
||||
module.name_mapper='^rewards\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/rewards\1'
|
||||
module.name_mapper='^modal\(.*\)$' -> '<PROJECT_ROOT>/src/renderer/modal\1'
|
||||
|
||||
[strict]
|
||||
|
|
3
flow-typed/react-feather.js
vendored
Normal file
3
flow-typed/react-feather.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
declare module 'react-feather' {
|
||||
declare module.exports: any;
|
||||
}
|
25
npm-debug.log.3540228334
Normal file
25
npm-debug.log.3540228334
Normal file
|
@ -0,0 +1,25 @@
|
|||
0 info it worked if it ends with ok
|
||||
1 verbose cli [ '/Users/seanyesmunt/.nvm/versions/node/v6.12.0/bin/node',
|
||||
1 verbose cli '/Users/seanyesmunt/.nvm/versions/node/v6.12.0/bin/npm',
|
||||
1 verbose cli 'config',
|
||||
1 verbose cli '--loglevel=warn',
|
||||
1 verbose cli 'get',
|
||||
1 verbose cli 'prefix' ]
|
||||
2 info using npm@3.10.10
|
||||
3 info using node@v6.12.0
|
||||
4 verbose exit [ 0, true ]
|
||||
5 verbose stack Error: write EPIPE
|
||||
5 verbose stack at exports._errnoException (util.js:1020:11)
|
||||
5 verbose stack at WriteWrap.afterWrite (net.js:800:14)
|
||||
6 verbose cwd /Users/seanyesmunt/Workspace/lbry/lbry-app
|
||||
7 error Darwin 17.3.0
|
||||
8 error argv "/Users/seanyesmunt/.nvm/versions/node/v6.12.0/bin/node" "/Users/seanyesmunt/.nvm/versions/node/v6.12.0/bin/npm" "config" "--loglevel=warn" "get" "prefix"
|
||||
9 error node v6.12.0
|
||||
10 error npm v3.10.10
|
||||
11 error code EPIPE
|
||||
12 error errno EPIPE
|
||||
13 error syscall write
|
||||
14 error write EPIPE
|
||||
15 error If you need help, you may report this error at:
|
||||
15 error <https://github.com/npm/npm/issues>
|
||||
16 verbose exit [ 1, true ]
|
25
npm-debug.log.3958211012
Normal file
25
npm-debug.log.3958211012
Normal file
|
@ -0,0 +1,25 @@
|
|||
0 info it worked if it ends with ok
|
||||
1 verbose cli [ '/Users/seanyesmunt/.nvm/versions/node/v6.12.0/bin/node',
|
||||
1 verbose cli '/Users/seanyesmunt/.nvm/versions/node/v6.12.0/bin/npm',
|
||||
1 verbose cli 'config',
|
||||
1 verbose cli '--loglevel=warn',
|
||||
1 verbose cli 'get',
|
||||
1 verbose cli 'prefix' ]
|
||||
2 info using npm@3.10.10
|
||||
3 info using node@v6.12.0
|
||||
4 verbose exit [ 0, true ]
|
||||
5 verbose stack Error: write EPIPE
|
||||
5 verbose stack at exports._errnoException (util.js:1020:11)
|
||||
5 verbose stack at WriteWrap.afterWrite (net.js:800:14)
|
||||
6 verbose cwd /Users/seanyesmunt/Workspace/lbry/lbry-app
|
||||
7 error Darwin 17.3.0
|
||||
8 error argv "/Users/seanyesmunt/.nvm/versions/node/v6.12.0/bin/node" "/Users/seanyesmunt/.nvm/versions/node/v6.12.0/bin/npm" "config" "--loglevel=warn" "get" "prefix"
|
||||
9 error node v6.12.0
|
||||
10 error npm v3.10.10
|
||||
11 error code EPIPE
|
||||
12 error errno EPIPE
|
||||
13 error syscall write
|
||||
14 error write EPIPE
|
||||
15 error If you need help, you may report this error at:
|
||||
15 error <https://github.com/npm/npm/issues>
|
||||
16 verbose exit [ 1, true ]
|
|
@ -52,6 +52,7 @@
|
|||
"rc-progress": "^2.0.6",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-feather": "^1.0.8",
|
||||
"react-markdown": "^2.5.0",
|
||||
"react-modal": "^3.1.7",
|
||||
"react-paginate": "^5.0.0",
|
||||
|
|
|
@ -1,52 +1,56 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { clipboard } from 'electron';
|
||||
import Link from 'component/link';
|
||||
import classnames from 'classnames';
|
||||
import { FormField } from 'component/common/form';
|
||||
import Button from 'component/link';
|
||||
|
||||
export default class Address extends React.PureComponent {
|
||||
static propTypes = {
|
||||
address: PropTypes.string,
|
||||
};
|
||||
type Props = {
|
||||
address: string,
|
||||
doShowSnackBar: ({ message: string }) => void,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
export default class Address extends React.PureComponent<Props> {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._inputElem = null;
|
||||
this.input = null;
|
||||
}
|
||||
|
||||
input: ?HTMLInputElement;
|
||||
|
||||
render() {
|
||||
const { address, showCopyButton, doShowSnackBar } = this.props;
|
||||
const { address, doShowSnackBar } = this.props;
|
||||
|
||||
return (
|
||||
<div className="form-field form-field--address">
|
||||
<FormField
|
||||
name="address"
|
||||
render={() => (
|
||||
<React.Fragment>
|
||||
<input
|
||||
className={classnames('input-copyable', {
|
||||
'input-copyable--with-copy-btn': showCopyButton,
|
||||
})}
|
||||
type="text"
|
||||
id="address"
|
||||
className="input-copyable"
|
||||
readOnly
|
||||
value={address || ''}
|
||||
ref={input => {
|
||||
this._inputElem = input;
|
||||
this.input = input;
|
||||
}}
|
||||
onFocus={() => {
|
||||
this._inputElem.select();
|
||||
if (this.input) {
|
||||
this.input.select();
|
||||
}
|
||||
}}
|
||||
readOnly="readonly"
|
||||
value={address || ''}
|
||||
/>
|
||||
{showCopyButton && (
|
||||
<span className="header__item">
|
||||
<Link
|
||||
button="alt button--flat"
|
||||
icon="clipboard"
|
||||
<Button
|
||||
alt
|
||||
icon="Clipboard"
|
||||
onClick={() => {
|
||||
clipboard.writeText(address);
|
||||
doShowSnackBar({ message: __('Address copied') });
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
selectPageTitle,
|
||||
|
@ -10,7 +9,7 @@ import { doAlertError } from 'redux/actions/app';
|
|||
import { doRecordScroll } from 'redux/actions/navigation';
|
||||
import App from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
const select = state => ({
|
||||
pageTitle: selectPageTitle(state),
|
||||
user: selectUser(state),
|
||||
currentStackIndex: selectHistoryIndex(state),
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Router from 'component/router/index';
|
||||
import Header from 'component/header';
|
||||
import Theme from 'component/theme';
|
||||
import ModalRouter from 'modal/modalRouter';
|
||||
import ReactModal from 'react-modal';
|
||||
import throttle from 'util/throttle';
|
||||
import SideBar from 'component/sideBar';
|
||||
import Header from 'component/header';
|
||||
|
||||
type Props = {
|
||||
alertError: (string | {}) => void,
|
||||
|
@ -33,7 +34,7 @@ class App extends React.PureComponent<Props> {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
const mainContent = document.getElementById('main-content');
|
||||
const mainContent = document.getElementById('content');
|
||||
this.mainContent = mainContent;
|
||||
|
||||
if (this.mainContent) {
|
||||
|
@ -82,10 +83,15 @@ class App extends React.PureComponent<Props> {
|
|||
return (
|
||||
<div id="window">
|
||||
<Theme />
|
||||
<main className="page">
|
||||
<SideBar />
|
||||
<Header />
|
||||
<div className="content" id="content">
|
||||
<Router />
|
||||
<ModalRouter />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,78 +44,6 @@ export class CurrencySymbol extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
export class CreditAmount extends React.PureComponent {
|
||||
static propTypes = {
|
||||
amount: PropTypes.number.isRequired,
|
||||
precision: PropTypes.number,
|
||||
isEstimate: PropTypes.bool,
|
||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
|
||||
showFree: PropTypes.bool,
|
||||
showFullPrice: PropTypes.bool,
|
||||
showPlus: PropTypes.bool,
|
||||
look: PropTypes.oneOf(['indicator', 'plain', 'fee']),
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
precision: 2,
|
||||
label: true,
|
||||
showFree: false,
|
||||
look: 'indicator',
|
||||
showFullPrice: false,
|
||||
showPlus: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
const minimumRenderableAmount = Math.pow(10, -1 * this.props.precision);
|
||||
const { amount, precision, showFullPrice } = this.props;
|
||||
|
||||
let formattedAmount;
|
||||
const fullPrice = formatFullPrice(amount, 2);
|
||||
|
||||
if (showFullPrice) {
|
||||
formattedAmount = fullPrice;
|
||||
} else {
|
||||
formattedAmount =
|
||||
amount > 0 && amount < minimumRenderableAmount
|
||||
? `<${minimumRenderableAmount}`
|
||||
: formatCredits(amount, precision);
|
||||
}
|
||||
|
||||
let amountText;
|
||||
if (this.props.showFree && parseFloat(this.props.amount) === 0) {
|
||||
amountText = __('free');
|
||||
} else {
|
||||
if (this.props.label) {
|
||||
const label =
|
||||
typeof this.props.label === 'string'
|
||||
? this.props.label
|
||||
: parseFloat(amount) == 1 ? __('credit') : __('credits');
|
||||
|
||||
amountText = `${formattedAmount} ${label}`;
|
||||
} else {
|
||||
amountText = formattedAmount;
|
||||
}
|
||||
if (this.props.showPlus && amount > 0) {
|
||||
amountText = `+${amountText}`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={`credit-amount credit-amount--${this.props.look}`} title={fullPrice}>
|
||||
<span>{amountText}</span>
|
||||
{this.props.isEstimate ? (
|
||||
<span
|
||||
className="credit-amount__estimate"
|
||||
title={__('This is an estimate and does not include data fees')}
|
||||
>
|
||||
*
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class Thumbnail extends React.PureComponent {
|
||||
static propTypes = {
|
||||
src: PropTypes.string,
|
||||
|
|
|
@ -200,7 +200,7 @@ class CategoryList extends React.PureComponent<Props, State> {
|
|||
<h3>
|
||||
{categoryLink ? (
|
||||
<Button
|
||||
className="button-text no-underline"
|
||||
noStyle
|
||||
label={category}
|
||||
navigate="/show"
|
||||
navigateParams={{ uri: categoryLink }}
|
||||
|
@ -219,20 +219,20 @@ class CategoryList extends React.PureComponent<Props, State> {
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className="card-row__scroll-btns">
|
||||
<Button
|
||||
inverse
|
||||
circle
|
||||
disabled={!canScrollPrevious}
|
||||
onClick={this.handleScrollPrevious}
|
||||
icon="chevron-left"
|
||||
icon="ChevronLeft"
|
||||
/>
|
||||
<Button
|
||||
inverse
|
||||
circle
|
||||
disabled={!canScrollNext}
|
||||
onClick={this.handleScrollNext}
|
||||
icon="chevron-right"
|
||||
icon="ChevronRight"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
76
src/renderer/component/common/credit-amount.jsx
Normal file
76
src/renderer/component/common/credit-amount.jsx
Normal file
|
@ -0,0 +1,76 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { formatCredits, formatFullPrice } from 'util/formatCredits';
|
||||
|
||||
type Props = {
|
||||
amount: number,
|
||||
precision: number,
|
||||
showFree: boolean,
|
||||
showFullPrice: boolean,
|
||||
showPlus: boolean,
|
||||
isEstimate?: boolean,
|
||||
large?: boolean,
|
||||
};
|
||||
|
||||
class CreditAmount extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
precision: 2,
|
||||
showFree: false,
|
||||
showFullPrice: false,
|
||||
showPlus: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { amount, precision, showFullPrice, showFree, showPlus, large, isEstimate } = this.props;
|
||||
|
||||
const minimumRenderableAmount = 10 ** (-1 * precision);
|
||||
const fullPrice = formatFullPrice(amount, 2);
|
||||
const isFree = parseFloat(amount) === 0;
|
||||
|
||||
let formattedAmount;
|
||||
if (showFullPrice) {
|
||||
formattedAmount = fullPrice;
|
||||
} else {
|
||||
formattedAmount =
|
||||
amount > 0 && amount < minimumRenderableAmount
|
||||
? `<${minimumRenderableAmount}`
|
||||
: formatCredits(amount, precision);
|
||||
}
|
||||
|
||||
let amountText;
|
||||
if (showFree && isFree) {
|
||||
amountText = __('FREE');
|
||||
} else {
|
||||
amountText = `${formattedAmount} ${__('LBC')}`;
|
||||
|
||||
if (showPlus && amount > 0) {
|
||||
amountText = `+${amountText}`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
title={fullPrice}
|
||||
className={classnames('credit-amount', {
|
||||
'credit-amount--free': !large && isFree,
|
||||
'credit-amount--cost': !large && !isFree,
|
||||
'credit-amount--large': large,
|
||||
})}
|
||||
>
|
||||
{amountText}
|
||||
|
||||
{isEstimate ? (
|
||||
<span
|
||||
className="credit-amount__estimate"
|
||||
title={__('This is an estimate and does not include data fees')}
|
||||
>
|
||||
*
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CreditAmount;
|
85
src/renderer/component/common/form.jsx
Normal file
85
src/renderer/component/common/form.jsx
Normal file
|
@ -0,0 +1,85 @@
|
|||
// @flow
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import * as React from 'react';
|
||||
import Button from 'component/link';
|
||||
|
||||
type FormRowProps = {
|
||||
children: React.Node,
|
||||
};
|
||||
|
||||
export const FormRow = (props: FormRowProps) => {
|
||||
const { children } = props;
|
||||
return <div className="form-row">{children}</div>;
|
||||
};
|
||||
|
||||
type FormFieldProps = {
|
||||
render: () => React.Node,
|
||||
label?: string,
|
||||
prefix?: string,
|
||||
postfix?: string,
|
||||
error?: string | boolean,
|
||||
};
|
||||
|
||||
export class FormField extends React.PureComponent<FormFieldProps> {
|
||||
render() {
|
||||
const { render, label, prefix, postfix, error } = this.props;
|
||||
return (
|
||||
<div className="form-field">
|
||||
{label && (
|
||||
<label className="form-field__label">
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<div className="form-field__wrapper">
|
||||
{prefix && <span className="form-field__prefix">{prefix}</span>}
|
||||
{render()}
|
||||
{postfix && <span className="form-field__postfix">{postfix}</span>}
|
||||
</div>
|
||||
{error && (
|
||||
<div className="form-field__error">
|
||||
{typeof error === 'string' ? error : __('There was an error')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
type SubmitProps = {
|
||||
label: string,
|
||||
disabled: boolean,
|
||||
};
|
||||
|
||||
export class Submit extends React.PureComponent<SubmitProps> {
|
||||
static defaultProps = {
|
||||
label: 'Submit',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { label, disabled } = this.props;
|
||||
return <Button type="submit" label={label} disabled={disabled} />;
|
||||
}
|
||||
}
|
||||
|
||||
type FormProps = {
|
||||
children: React.Node,
|
||||
onSubmit: any => any,
|
||||
};
|
||||
|
||||
export class Form extends React.PureComponent<FormProps> {
|
||||
render() {
|
||||
const { children, onSubmit } = this.props;
|
||||
return (
|
||||
<form
|
||||
className="form"
|
||||
onSubmit={event => {
|
||||
event.preventDefault();
|
||||
onSubmit(event);
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
/* eslint-enable react/no-multi-comp */
|
|
@ -1,43 +1,22 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import * as icons from 'constants/icons';
|
||||
// import * as icons from 'constants/icons';
|
||||
import * as Icons from 'react-feather';
|
||||
|
||||
type Props = {
|
||||
icon: string,
|
||||
fixed?: boolean,
|
||||
padded?: boolean,
|
||||
size?: number,
|
||||
};
|
||||
|
||||
class Icon extends React.PureComponent<Props> {
|
||||
getIconTitle() {
|
||||
const { icon } = this.props;
|
||||
|
||||
switch (icon) {
|
||||
case icons.FEATURED:
|
||||
return __('Watch this and earn rewards.');
|
||||
case icons.LOCAL:
|
||||
return __('You have a copy of this file.');
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
class IconComponent extends React.PureComponent<Props> {
|
||||
// TODO: Move all icons to constants and add titles for all
|
||||
// Add some some sort of hover flyout with the title?
|
||||
|
||||
render() {
|
||||
const { icon, fixed, padded } = this.props;
|
||||
const iconClassName = icon.startsWith('icon-') ? icon : `icon-${icon}`;
|
||||
const title = this.getIconTitle();
|
||||
|
||||
const spanClassName = classnames(
|
||||
{
|
||||
'icon--fixed-width': fixed,
|
||||
'icon--padded': padded,
|
||||
},
|
||||
iconClassName
|
||||
);
|
||||
|
||||
return <span className={spanClassName} title={title} />;
|
||||
const { icon, size = 14 } = this.props;
|
||||
const Icon = Icons[icon];
|
||||
return Icon ? <Icon size={size} className="icon" /> : null;
|
||||
}
|
||||
}
|
||||
|
||||
export default Icon;
|
||||
export default IconComponent;
|
||||
|
|
|
@ -46,7 +46,7 @@ class ToolTip extends React.PureComponent<Props, State> {
|
|||
<span className="tooltip">
|
||||
<Button fakeLink className="help tooltip__link" onClick={this.handleClick}>
|
||||
{label}
|
||||
{showTooltip && <Icon icon="times" fixed />}
|
||||
{showTooltip && <Icon icon="X" />}
|
||||
</Button>
|
||||
<div className={classnames('tooltip__body', { hidden: !showTooltip })}>{body}</div>
|
||||
</span>
|
||||
|
|
|
@ -78,6 +78,9 @@ class FileCard extends React.PureComponent<Props> {
|
|||
})}
|
||||
>
|
||||
<CardMedia thumbnail={thumbnail} />
|
||||
<div className="card-media__internal-links">
|
||||
<FilePrice uri={uri} />
|
||||
</div>
|
||||
|
||||
<div className="card__title-identity">
|
||||
<div className="card__title--small">
|
||||
|
@ -87,8 +90,8 @@ class FileCard extends React.PureComponent<Props> {
|
|||
<div className="card__subtitle">
|
||||
<UriIndicator uri={uri} link />
|
||||
<div className="card--file-subtitle">
|
||||
<FilePrice uri={uri} /> {isRewardContent && <Icon icon={icons.FEATURED} padded />}
|
||||
{fileInfo && <Icon icon={icons.LOCAL} padded />}
|
||||
{isRewardContent && <Icon icon={icons.FEATURED} />}
|
||||
{fileInfo && <Icon icon={icons.LOCAL} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,35 +1,48 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { CreditAmount } from 'component/common';
|
||||
import CreditAmount from 'component/common/credit-amount';
|
||||
|
||||
type Props = {
|
||||
showFullPrice: boolean,
|
||||
costInfo: ?{ includesData: boolean, cost: number },
|
||||
fetchCostInfo: string => void,
|
||||
uri: string,
|
||||
fetching: boolean,
|
||||
claim: ?{},
|
||||
};
|
||||
|
||||
class FilePrice extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
showFullPrice: false,
|
||||
};
|
||||
|
||||
class FilePrice extends React.PureComponent {
|
||||
componentWillMount() {
|
||||
this.fetchCost(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
componentWillReceiveProps(nextProps: Props) {
|
||||
this.fetchCost(nextProps);
|
||||
}
|
||||
|
||||
fetchCost(props) {
|
||||
fetchCost = (props: Props) => {
|
||||
const { costInfo, fetchCostInfo, uri, fetching, claim } = props;
|
||||
|
||||
if (costInfo === undefined && !fetching && claim) {
|
||||
fetchCostInfo(uri);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { costInfo, look = 'indicator', showFullPrice = false } = this.props;
|
||||
const { costInfo, showFullPrice } = this.props;
|
||||
|
||||
const isEstimate = costInfo ? !costInfo.includesData : null;
|
||||
const isEstimate = costInfo ? !costInfo.includesData : false;
|
||||
|
||||
if (!costInfo) {
|
||||
return <span className={`credit-amount credit-amount--${look}`}>???</span>;
|
||||
return <span className="credit-amount">???</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<CreditAmount
|
||||
label={false}
|
||||
amount={costInfo.cost}
|
||||
isEstimate={isEstimate}
|
||||
showFree
|
||||
|
|
|
@ -1,186 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import FormField from 'component/formField';
|
||||
import Icon from 'component/common/icon';
|
||||
|
||||
let formFieldCounter = 0;
|
||||
|
||||
export const formFieldNestedLabelTypes = ['radio', 'checkbox'];
|
||||
|
||||
export function formFieldId() {
|
||||
return `form-field-${++formFieldCounter}`;
|
||||
}
|
||||
|
||||
export class Form extends React.PureComponent {
|
||||
static propTypes = {
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
this.props.onSubmit();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <form onSubmit={event => this.handleSubmit(event)}>{this.props.children}</form>;
|
||||
}
|
||||
}
|
||||
|
||||
export class FormRow extends React.PureComponent {
|
||||
static propTypes = {
|
||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
|
||||
errorMessage: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
// helper: PropTypes.html,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
isFocus: false,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._field = null;
|
||||
|
||||
this._fieldRequiredText = __('This field is required');
|
||||
|
||||
this.state = this.getStateFromProps(props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.setState(this.getStateFromProps(nextProps));
|
||||
}
|
||||
|
||||
getStateFromProps(props) {
|
||||
return {
|
||||
isError: !!props.errorMessage,
|
||||
errorMessage:
|
||||
typeof props.errorMessage === 'string'
|
||||
? props.errorMessage
|
||||
: props.errorMessage instanceof Error ? props.errorMessage.toString() : '',
|
||||
};
|
||||
}
|
||||
|
||||
showError(text) {
|
||||
this.setState({
|
||||
isError: true,
|
||||
errorMessage: text,
|
||||
});
|
||||
}
|
||||
|
||||
showRequiredError() {
|
||||
this.showError(this._fieldRequiredText);
|
||||
}
|
||||
|
||||
clearError(text) {
|
||||
this.setState({
|
||||
isError: false,
|
||||
errorMessage: '',
|
||||
});
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this._field.getValue();
|
||||
}
|
||||
|
||||
getSelectedElement() {
|
||||
return this._field.getSelectedElement();
|
||||
}
|
||||
|
||||
getOptions() {
|
||||
return this._field.getOptions();
|
||||
}
|
||||
|
||||
focus() {
|
||||
this._field.focus();
|
||||
}
|
||||
|
||||
onFocus() {
|
||||
this.setState({ isFocus: true });
|
||||
}
|
||||
|
||||
onBlur() {
|
||||
this.setState({ isFocus: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const fieldProps = Object.assign({}, this.props),
|
||||
elementId = formFieldId(),
|
||||
renderLabelInFormField = formFieldNestedLabelTypes.includes(this.props.type);
|
||||
|
||||
if (!renderLabelInFormField) {
|
||||
delete fieldProps.label;
|
||||
}
|
||||
delete fieldProps.helper;
|
||||
delete fieldProps.errorMessage;
|
||||
delete fieldProps.isFocus;
|
||||
|
||||
return (
|
||||
<div className={`form-row${this.state.isFocus ? ' form-row--focus' : ''}`}>
|
||||
{this.props.label && !renderLabelInFormField ? (
|
||||
<div
|
||||
className={`form-row__label-row ${
|
||||
this.props.labelPrefix ? 'form-row__label-row--prefix' : ''
|
||||
}`}
|
||||
>
|
||||
<label
|
||||
htmlFor={elementId}
|
||||
className={`form-field__label ${
|
||||
this.state.isError ? 'form-field__label--error' : ' '
|
||||
}`}
|
||||
>
|
||||
{this.props.label}
|
||||
</label>
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
<FormField
|
||||
ref={ref => {
|
||||
this._field = ref ? ref.getWrappedInstance() : null;
|
||||
}}
|
||||
hasError={this.state.isError}
|
||||
onFocus={this.onFocus.bind(this)}
|
||||
onBlur={this.onBlur.bind(this)}
|
||||
{...fieldProps}
|
||||
/>
|
||||
{!this.state.isError && this.props.helper ? (
|
||||
<div className="form-field__helper">{this.props.helper}</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{this.state.isError ? (
|
||||
<div className="form-field__error">{this.state.errorMessage}</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const Submit = props => {
|
||||
const { title, label, icon, disabled } = props;
|
||||
|
||||
const className = `${'button-block' +
|
||||
' button-primary' +
|
||||
' button-set-item' +
|
||||
' button--submit'}${disabled ? ' disabled' : ''}`;
|
||||
|
||||
const content = (
|
||||
<span className="button__content">
|
||||
{'icon' in props ? <Icon icon={icon} fixed /> : null}
|
||||
{label ? <span className="button-label">{label}</span> : null}
|
||||
</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<button type="submit" className={className} title={title}>
|
||||
{content}
|
||||
</button>
|
||||
);
|
||||
};
|
|
@ -1,8 +1,10 @@
|
|||
// This file is going to die
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import FileSelector from 'component/file-selector.js';
|
||||
import SimpleMDE from 'react-simplemde-editor';
|
||||
import { formFieldNestedLabelTypes, formFieldId } from '../form';
|
||||
import { formFieldNestedLabelTypes, formFieldId } from 'component/common/form';
|
||||
import style from 'react-simplemde-editor/dist/simplemde.min.css';
|
||||
|
||||
const formFieldFileSelectorTypes = ['file', 'directory'];
|
||||
|
@ -195,3 +197,4 @@ class FormField extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default FormField;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,25 +1,17 @@
|
|||
import React from 'react';
|
||||
import { formatCredits } from 'util/formatCredits';
|
||||
import { connect } from 'react-redux';
|
||||
import { selectIsBackDisabled, selectIsForwardDisabled } from 'redux/selectors/navigation';
|
||||
import { selectBalance } from 'redux/selectors/wallet';
|
||||
import { doNavigate, doHistoryBack, doHistoryForward } from 'redux/actions/navigation';
|
||||
import Header from './view';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { selectIsUpgradeAvailable } from 'redux/selectors/app';
|
||||
import { doDownloadUpgrade } from 'redux/actions/app';
|
||||
import { formatCredits } from 'util/formatCredits';
|
||||
import { selectBalance } from 'redux/selectors/wallet';
|
||||
import Header from './view';
|
||||
|
||||
const select = state => ({
|
||||
isBackDisabled: selectIsBackDisabled(state),
|
||||
isForwardDisabled: selectIsForwardDisabled(state),
|
||||
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
||||
balance: formatCredits(selectBalance(state) || 0, 2),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
back: () => dispatch(doHistoryBack()),
|
||||
forward: () => dispatch(doHistoryForward()),
|
||||
downloadUpgrade: () => dispatch(doDownloadUpgrade()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(Header);
|
||||
|
|
|
@ -5,78 +5,34 @@ import WunderBar from 'component/wunderbar';
|
|||
|
||||
type Props = {
|
||||
balance: string,
|
||||
back: any => void,
|
||||
forward: any => void,
|
||||
isBackDisabled: boolean,
|
||||
isForwardDisabled: boolean,
|
||||
isUpgradeAvailable: boolean,
|
||||
navigate: any => void,
|
||||
downloadUpgrade: any => void,
|
||||
isUpgradeAvailable: boolean,
|
||||
};
|
||||
|
||||
export const Header = (props: Props) => {
|
||||
const {
|
||||
balance,
|
||||
back,
|
||||
forward,
|
||||
isBackDisabled,
|
||||
isForwardDisabled,
|
||||
isUpgradeAvailable,
|
||||
navigate,
|
||||
downloadUpgrade,
|
||||
} = props;
|
||||
const Header = (props: Props) => {
|
||||
const { balance, isUpgradeAvailable, navigate, downloadUpgrade } = props;
|
||||
return (
|
||||
<header id="header">
|
||||
<div className="header__actions-left">
|
||||
<Button
|
||||
alt
|
||||
circle
|
||||
onClick={back}
|
||||
disabled={isBackDisabled}
|
||||
icon="arrow-left"
|
||||
description={__('Navigate back')}
|
||||
/>
|
||||
|
||||
<Button
|
||||
alt
|
||||
circle
|
||||
onClick={forward}
|
||||
disabled={isForwardDisabled}
|
||||
icon="arrow-right"
|
||||
description={__('Navigate forward')}
|
||||
/>
|
||||
|
||||
<Button alt onClick={() => navigate('/discover')} icon="home" description={__('Home')} />
|
||||
</div>
|
||||
|
||||
<header className="header">
|
||||
<WunderBar />
|
||||
|
||||
<div className="header__actions-right">
|
||||
<Button
|
||||
inverse
|
||||
onClick={() => navigate('/wallet')}
|
||||
icon="user"
|
||||
icon="User"
|
||||
label={isUpgradeAvailable ? `${balance} LBC` : `You have ${balance} LBC`}
|
||||
description={__('Your wallet')}
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={() => navigate('/publish')}
|
||||
icon="cloud-upload"
|
||||
icon="UploadCloud"
|
||||
label={isUpgradeAvailable ? '' : __('Publish')}
|
||||
description={__('Publish content')}
|
||||
/>
|
||||
|
||||
<Button
|
||||
alt
|
||||
onClick={() => navigate('/settings')}
|
||||
icon="gear"
|
||||
description={__('Settings')}
|
||||
/>
|
||||
|
||||
<Button alt onClick={() => navigate('/help')} icon="question" description={__('Help')} />
|
||||
{isUpgradeAvailable && (
|
||||
<Button onClick={() => downloadUpgrade()} icon="arrow-up" label={__('Upgrade App')} />
|
||||
<Button onClick={() => downloadUpgrade()} icon="Download" label={__('Upgrade App')} />
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import { BusyMessage, CreditAmount } from 'component/common';
|
||||
import { Form, FormRow, Submit } from 'component/form.js';
|
||||
import { BusyMessage } from 'component/common';
|
||||
import CreditAmount from 'component/common/credit-amount';
|
||||
// import { Form, FormField } from 'component/common/form';
|
||||
|
||||
class FormInviteNew extends React.PureComponent {
|
||||
constructor(props) {
|
||||
|
@ -24,25 +27,25 @@ class FormInviteNew extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const { errorMessage, isPending } = this.props;
|
||||
|
||||
return (
|
||||
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||
<FormRow
|
||||
type="text"
|
||||
label="Email"
|
||||
placeholder="youremail@example.org"
|
||||
name="email"
|
||||
value={this.state.email}
|
||||
errorMessage={errorMessage}
|
||||
onChange={event => {
|
||||
this.handleEmailChanged(event);
|
||||
}}
|
||||
/>
|
||||
<div className="form-row-submit">
|
||||
<Submit label={__('Send Invite')} disabled={isPending} />
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
return null;
|
||||
// return (
|
||||
// <Form onSubmit={this.handleSubmit.bind(this)}>
|
||||
// <FormRow
|
||||
// type="text"
|
||||
// label="Email"
|
||||
// placeholder="youremail@example.org"
|
||||
// name="email"
|
||||
// value={this.state.email}
|
||||
// errorMessage={errorMessage}
|
||||
// onChange={event => {
|
||||
// this.handleEmailChanged(event);
|
||||
// }}
|
||||
// />
|
||||
// <div className="form-row-submit">
|
||||
// <Submit label={__('Send Invite')} disabled={isPending} />
|
||||
// </div>
|
||||
// </Form>
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,3 +83,4 @@ class InviteNew extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default InviteNew;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -22,6 +22,8 @@ type Props = {
|
|||
alt: ?boolean,
|
||||
flat: ?boolean,
|
||||
fakeLink: ?boolean,
|
||||
noStyle: ?boolean,
|
||||
noUnderline: ?boolean,
|
||||
description: ?string,
|
||||
};
|
||||
|
||||
|
@ -45,19 +47,24 @@ const Button = (props: Props) => {
|
|||
flat,
|
||||
fakeLink,
|
||||
description,
|
||||
noStyle,
|
||||
noUnderline,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const combinedClassName = classnames(
|
||||
{
|
||||
btn: !fakeLink,
|
||||
'btn',
|
||||
noStyle
|
||||
? 'btn--no-style'
|
||||
: {
|
||||
'btn--link': fakeLink,
|
||||
'btn--primary': !fakeLink && !alt,
|
||||
'btn--primary': !alt && !fakeLink,
|
||||
'btn--alt': alt,
|
||||
'btn--inverse': inverse,
|
||||
'btn--disabled': disabled,
|
||||
'btn--circle': circle,
|
||||
'btn--flat': flat,
|
||||
'btn--no-underline': fakeLink && noUnderline,
|
||||
},
|
||||
className
|
||||
);
|
||||
|
@ -72,10 +79,10 @@ const Button = (props: Props) => {
|
|||
|
||||
const content = (
|
||||
<React.Fragment>
|
||||
{icon && <Icon icon={icon} fixed />}
|
||||
{icon && <Icon icon={icon} />}
|
||||
{label && <span className="btn__label">{label}</span>}
|
||||
{children && children}
|
||||
{iconRight && <Icon icon={iconRight} fixed />}
|
||||
{iconRight && <Icon icon={iconRight} />}
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Icon from 'component/common/icon';
|
||||
import Link from 'component/link';
|
||||
|
||||
export class DropDownMenuItem extends React.PureComponent {
|
||||
static propTypes = {
|
||||
href: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
icon: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
iconPosition: 'left',
|
||||
};
|
||||
|
||||
render() {
|
||||
const icon = this.props.icon ? <Icon icon={this.props.icon} fixed /> : null;
|
||||
|
||||
return (
|
||||
<a
|
||||
className="menu__menu-item"
|
||||
onClick={this.props.onClick}
|
||||
href={this.props.href || 'javascript:'}
|
||||
label={this.props.label}
|
||||
>
|
||||
{this.props.iconPosition == 'left' ? icon : null}
|
||||
{this.props.label}
|
||||
{this.props.iconPosition == 'left' ? null : icon}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class DropDownMenu extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._isWindowClickBound = false;
|
||||
this._menuDiv = null;
|
||||
|
||||
this.state = {
|
||||
menuOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._isWindowClickBound) {
|
||||
window.removeEventListener('click', this.handleWindowClick, false);
|
||||
}
|
||||
}
|
||||
|
||||
handleMenuIconClick(e) {
|
||||
this.setState({
|
||||
menuOpen: !this.state.menuOpen,
|
||||
});
|
||||
if (!this.state.menuOpen && !this._isWindowClickBound) {
|
||||
this._isWindowClickBound = true;
|
||||
window.addEventListener('click', this.handleWindowClick, false);
|
||||
e.stopPropagation();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
handleMenuClick(e) {
|
||||
// Event bubbles up to the menu after a link is clicked
|
||||
this.setState({
|
||||
menuOpen: false,
|
||||
});
|
||||
}
|
||||
|
||||
/* this will force "this" to always be the class, even when passed to an event listener */
|
||||
handleWindowClick = e => {
|
||||
if (this.state.menuOpen && (!this._menuDiv || !this._menuDiv.contains(e.target))) {
|
||||
this.setState({
|
||||
menuOpen: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.state.menuOpen && this._isWindowClickBound) {
|
||||
this._isWindowClickBound = false;
|
||||
window.removeEventListener('click', this.handleWindowClick, false);
|
||||
}
|
||||
return (
|
||||
<div className="menu-container">
|
||||
<Link
|
||||
ref={span => (this._menuButton = span)}
|
||||
button="text"
|
||||
icon="icon-ellipsis-v"
|
||||
onClick={event => {
|
||||
this.handleMenuIconClick(event);
|
||||
}}
|
||||
/>
|
||||
{this.state.menuOpen ? (
|
||||
<div
|
||||
ref={div => (this._menuDiv = div)}
|
||||
className="menu"
|
||||
onClick={event => {
|
||||
this.handleMenuClick(event);
|
||||
}}
|
||||
>
|
||||
{this.props.children}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,31 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { selectPageTitle } from 'redux/selectors/navigation';
|
||||
import {
|
||||
selectPageTitle,
|
||||
selectIsBackDisabled,
|
||||
selectIsForwardDisabled,
|
||||
selectNavLinks,
|
||||
} from 'redux/selectors/navigation';
|
||||
import { doNavigate, doHistoryBack, doHistoryForward } from 'redux/actions/navigation';
|
||||
import { doDownloadUpgrade } from 'redux/actions/app';
|
||||
import { selectIsUpgradeAvailable } from 'redux/selectors/app';
|
||||
import { formatCredits } from 'util/formatCredits';
|
||||
import { selectBalance } from 'redux/selectors/wallet';
|
||||
import Page from './view';
|
||||
|
||||
const select = state => ({
|
||||
title: selectPageTitle(state),
|
||||
pageTitle: selectPageTitle(state),
|
||||
navLinks: selectNavLinks(state),
|
||||
isBackDisabled: selectIsBackDisabled(state),
|
||||
isForwardDisabled: selectIsForwardDisabled(state),
|
||||
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
||||
balance: formatCredits(selectBalance(state) || 0, 2),
|
||||
});
|
||||
|
||||
export default connect(select, null)(Page);
|
||||
const perform = dispatch => ({
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
back: () => dispatch(doHistoryBack()),
|
||||
forward: () => dispatch(doHistoryForward()),
|
||||
downloadUpgrade: () => dispatch(doDownloadUpgrade()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(Page);
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
// @flow
|
||||
import * as React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { BusyMessage } from 'component/common';
|
||||
|
||||
type Props = {
|
||||
children: React.Node,
|
||||
title: ?string,
|
||||
pageTitle: ?string,
|
||||
noPadding: ?boolean,
|
||||
isLoading: ?boolean,
|
||||
};
|
||||
|
||||
const Page = (props: Props) => {
|
||||
const { children, title, noPadding, isLoading } = props;
|
||||
const { pageTitle, children, noPadding } = props;
|
||||
return (
|
||||
<main id="main-content">
|
||||
<main className={classnames('main', { 'main--no-padding': noPadding })}>
|
||||
{pageTitle && (
|
||||
<div className="page__header">
|
||||
{title && <h1 className="page__title">{title}</h1>}
|
||||
{isLoading && <BusyMessage message={__('Fetching content')} />}
|
||||
{pageTitle && <h1 className="page__title">{pageTitle}</h1>}
|
||||
</div>
|
||||
<div className={classnames('main', { 'main--no-padding': noPadding })}>{children}</div>
|
||||
)}
|
||||
{children}
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import { isNameValid } from 'lbryURI';
|
||||
import { FormRow } from 'component/form.js';
|
||||
import { FormRow } from 'component/common/form';
|
||||
import { BusyMessage } from 'component/common';
|
||||
import Link from 'component/link';
|
||||
|
||||
|
@ -167,3 +169,4 @@ class ChannelSection extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default ChannelSection;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import lbry from 'lbry';
|
||||
import { isNameValid, buildURI, regexInvalidURI } from 'lbryURI';
|
||||
import FormField from 'component/formField';
|
||||
import { Form, FormRow, Submit } from 'component/form.js';
|
||||
import { Form, FormRow } from 'component/common/form';
|
||||
import Link from 'component/link';
|
||||
import FormFieldPrice from 'component/formFieldPrice';
|
||||
import Modal from 'modal/modal';
|
||||
|
@ -832,7 +834,8 @@ class PublishForm extends React.PureComponent {
|
|||
</section>
|
||||
|
||||
<div className="card-series-submit">
|
||||
<Submit
|
||||
<Link
|
||||
type="submit"
|
||||
label={!this.state.submitting ? __('Publish') : __('Publishing...')}
|
||||
disabled={
|
||||
this.props.balance <= 0 ||
|
||||
|
@ -878,3 +881,4 @@ class PublishForm extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default PublishForm;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,7 +1,20 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import LinkTransaction from 'component/linkTransaction';
|
||||
|
||||
const RewardListClaimed = props => {
|
||||
type Reward = {
|
||||
id: string,
|
||||
reward_title: string,
|
||||
reward_amount: number,
|
||||
transaction_id: string,
|
||||
created_at: string,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
rewards: Array<Reward>,
|
||||
};
|
||||
|
||||
const RewardListClaimed = (props: Props) => {
|
||||
const { rewards } = props;
|
||||
|
||||
if (!rewards || !rewards.length) {
|
||||
|
@ -9,7 +22,7 @@ const RewardListClaimed = props => {
|
|||
}
|
||||
|
||||
return (
|
||||
<section className="card">
|
||||
<section className="card card--section">
|
||||
<div className="card__title-identity">
|
||||
<h3>Claimed Rewards</h3>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import { CreditAmount } from 'component/common';
|
||||
import * as React from 'react';
|
||||
import Button from 'component/link';
|
||||
import CreditAmount from 'component/common/credit-amount';
|
||||
|
||||
type Props = {
|
||||
unclaimedRewardAmount: number,
|
||||
|
@ -9,24 +9,25 @@ type Props = {
|
|||
|
||||
const RewardSummary = (props: Props) => {
|
||||
const { unclaimedRewardAmount } = props;
|
||||
const hasRewards = unclaimedRewardAmount > 0;
|
||||
|
||||
return (
|
||||
<section className="card">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('Rewards')}</h3>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
{unclaimedRewardAmount > 0 ? (
|
||||
<p>
|
||||
<section className="card card--section">
|
||||
<h2>{__('Rewards')}</h2>
|
||||
<p className="card__subtitle">
|
||||
{hasRewards ? (
|
||||
<React.Fragment>
|
||||
{__('You have')} <CreditAmount amount={unclaimedRewardAmount} precision={8} />{' '}
|
||||
{__('in unclaimed rewards')}.
|
||||
</p>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<p>{__('There are no rewards available at this time, please check back later')}.</p>
|
||||
<React.Fragment>
|
||||
{__('There are no rewards available at this time, please check back later')}.
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
</p>
|
||||
<div className="card__actions">
|
||||
<Link button="primary" navigate="/rewards" label={__('Claim Rewards')} />
|
||||
<Button navigate="/rewards" label={hasRewards ? __('Claim Rewards') : __('View Rewards')} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
|
|
@ -1,16 +1,30 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { CreditAmount, Icon } from 'component/common';
|
||||
import CreditAmount from 'component/common/credit-amount';
|
||||
import Icon from 'component/common/icon';
|
||||
import RewardLink from 'component/rewardLink';
|
||||
import Link from 'component/link';
|
||||
import Button from 'component/link';
|
||||
import rewards from 'rewards';
|
||||
|
||||
const RewardTile = props => {
|
||||
type Props = {
|
||||
reward: {
|
||||
id: string,
|
||||
reward_title: string,
|
||||
reward_amount: number,
|
||||
transaction_id: string,
|
||||
created_at: string,
|
||||
reward_description: string,
|
||||
reward_type: string,
|
||||
},
|
||||
};
|
||||
|
||||
const RewardTile = (props: Props) => {
|
||||
const { reward } = props;
|
||||
|
||||
const claimed = !!reward.transaction_id;
|
||||
|
||||
return (
|
||||
<section className="card">
|
||||
<section className="card card--section">
|
||||
<div className="card__inner">
|
||||
<div className="card__title-primary">
|
||||
<CreditAmount amount={reward.reward_amount} />
|
||||
|
@ -18,8 +32,8 @@ const RewardTile = props => {
|
|||
</div>
|
||||
<div className="card__content">{reward.reward_description}</div>
|
||||
<div className="card__actions ">
|
||||
{reward.reward_type == rewards.TYPE_REFERRAL && (
|
||||
<Link button="alt" navigate="/invite" label={__('Go To Invites')} />
|
||||
{reward.reward_type === rewards.TYPE_REFERRAL && (
|
||||
<Button alt navigate="/invite" label={__('Go To Invites')} />
|
||||
)}
|
||||
{reward.reward_type !== rewards.TYPE_REFERRAL &&
|
||||
(claimed ? (
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import { getExampleAddress } from 'util/shape_shift';
|
||||
import { Submit, FormRow } from 'component/form';
|
||||
import { FormField, Submit } from 'component/common/form';
|
||||
import type { ShapeShiftFormValues, Dispatch } from 'redux/actions/shape_shift';
|
||||
import ShiftMarketInfo from './market_info';
|
||||
|
||||
|
@ -12,7 +12,7 @@ type ShapeShiftFormErrors = {
|
|||
type Props = {
|
||||
values: ShapeShiftFormValues,
|
||||
errors: ShapeShiftFormErrors,
|
||||
touched: boolean,
|
||||
touched: { returnAddress: boolean },
|
||||
handleChange: Event => any,
|
||||
handleBlur: Event => any,
|
||||
handleSubmit: Event => any,
|
||||
|
@ -21,7 +21,6 @@ type Props = {
|
|||
originCoin: string,
|
||||
updating: boolean,
|
||||
getCoinStats: Dispatch,
|
||||
receiveAddress: string,
|
||||
originCoinDepositFee: number,
|
||||
originCoinDepositMin: string,
|
||||
originCoinDepositMax: number,
|
||||
|
@ -41,7 +40,6 @@ export default (props: Props) => {
|
|||
originCoin,
|
||||
updating,
|
||||
getCoinStats,
|
||||
receiveAddress,
|
||||
originCoinDepositMax,
|
||||
originCoinDepositMin,
|
||||
originCoinDepositFee,
|
||||
|
@ -49,8 +47,10 @@ export default (props: Props) => {
|
|||
} = props;
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-field">
|
||||
<span>{__('Exchange')} </span>
|
||||
<FormField
|
||||
prefix={__('Exchange')}
|
||||
postfix={__('for LBC')}
|
||||
render={() => (
|
||||
<select
|
||||
className="form-field__input form-field__input-select"
|
||||
name="originCoin"
|
||||
|
@ -65,7 +65,9 @@ export default (props: Props) => {
|
|||
</option>
|
||||
))}
|
||||
</select>
|
||||
<span> {__('for LBC')}</span>
|
||||
)}
|
||||
/>
|
||||
<div>
|
||||
<div className="shapeshift__tx-info">
|
||||
{!updating &&
|
||||
originCoinDepositMax && (
|
||||
|
@ -80,16 +82,19 @@ export default (props: Props) => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<FormRow
|
||||
<FormField
|
||||
label={__('Return address')}
|
||||
error={touched.returnAddress && !!errors.returnAddress && errors.returnAddress}
|
||||
render={() => (
|
||||
<input
|
||||
type="text"
|
||||
name="returnAddress"
|
||||
placeholder={getExampleAddress(originCoin)}
|
||||
label={__('Return address')}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
value={values.returnAddress}
|
||||
errorMessage={errors.returnAddress}
|
||||
hasError={touched.returnAddress && !!errors.returnAddress}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<span className="help">
|
||||
<span>
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
// @flow
|
||||
import * as React from 'react';
|
||||
import { shell } from 'electron';
|
||||
import { Formik } from 'formik';
|
||||
import classnames from 'classnames';
|
||||
import * as statuses from 'constants/shape_shift';
|
||||
import { validateShapeShiftForm } from 'util/shape_shift';
|
||||
import Link from 'component/link';
|
||||
import Spinner from 'component/common/spinner';
|
||||
import { BusyMessage } from 'component/common';
|
||||
import ShapeShiftForm from './internal/form';
|
||||
import ActiveShapeShift from './internal/active-shift';
|
||||
|
||||
import type { ShapeShiftState } from 'redux/reducers/shape_shift';
|
||||
import type { Dispatch, ShapeShiftFormValues } from 'redux/actions/shape_shift';
|
||||
import ShapeShiftForm from './internal/form';
|
||||
import ActiveShapeShift from './internal/active-shift';
|
||||
|
||||
type Props = {
|
||||
shapeShift: ShapeShiftState,
|
||||
|
@ -72,28 +66,17 @@ class ShapeShift extends React.PureComponent<Props> {
|
|||
};
|
||||
|
||||
return (
|
||||
// add the "shapeshift__intital-wrapper class so we can avoid content jumping once everything loads"
|
||||
// it just gives the section a min-height equal to the height of the content when the form is rendered
|
||||
// if the markup below changes for the initial render (form.jsx) there will be content jumping
|
||||
// the styling in shapeshift.scss will need to be updated to the correct min-height
|
||||
<section
|
||||
className={classnames('card shapeshift__wrapper', {
|
||||
'shapeshift__initial-wrapper': loading,
|
||||
})}
|
||||
>
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('Convert Crypto to LBC')}</h3>
|
||||
<p className="help">
|
||||
<section className="card card--section">
|
||||
<h2>{__('Convert Crypto to LBC')}</h2>
|
||||
<p className="card__subtitle">
|
||||
{__('Powered by ShapeShift. Read our FAQ')}{' '}
|
||||
<Link href="https://lbry.io/faq/shapeshift">{__('here')}</Link>.
|
||||
<Link fakeLink label={__('here')} href="https://lbry.io/faq/shapeshift" />.
|
||||
{hasActiveShift &&
|
||||
shiftState !== 'complete' && <span>{__('This will update automatically.')}</span>}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="card__content shapeshift__content">
|
||||
{error && <div className="form-field__error">{error}</div>}
|
||||
{loading && <Spinner dark />}
|
||||
{!loading &&
|
||||
!hasActiveShift &&
|
||||
!!shiftSupportedCoins.length && (
|
||||
|
@ -113,7 +96,6 @@ class ShapeShift extends React.PureComponent<Props> {
|
|||
originCoinDepositMin={originCoinDepositMin}
|
||||
originCoinDepositFee={originCoinDepositFee}
|
||||
shapeShiftRate={shapeShiftRate}
|
||||
updating={updating}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@ -124,7 +106,6 @@ class ShapeShift extends React.PureComponent<Props> {
|
|||
shiftCoinType={shiftCoinType}
|
||||
shiftReturnAddress={shiftReturnAddress}
|
||||
shiftDepositAddress={shiftDepositAddress}
|
||||
originCoinDepositMax={originCoinDepositMax}
|
||||
shiftOrderId={shiftOrderId}
|
||||
shiftState={shiftState}
|
||||
clearShapeShift={clearShapeShift}
|
||||
|
|
24
src/renderer/component/sideBar/index.js
Normal file
24
src/renderer/component/sideBar/index.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doNavigate, doHistoryBack, doHistoryForward } from 'redux/actions/navigation';
|
||||
import {
|
||||
selectIsBackDisabled,
|
||||
selectIsForwardDisabled,
|
||||
selectIsHome,
|
||||
selectNavLinks,
|
||||
} from 'redux/selectors/navigation';
|
||||
import SideBar from './view';
|
||||
|
||||
const select = state => ({
|
||||
navLinks: selectNavLinks(state),
|
||||
isBackDisabled: selectIsBackDisabled(state),
|
||||
isForwardDisabled: selectIsForwardDisabled(state),
|
||||
isHome: selectIsHome(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
back: () => dispatch(doHistoryBack()),
|
||||
forward: () => dispatch(doHistoryForward()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(SideBar);
|
101
src/renderer/component/sideBar/view.jsx
Normal file
101
src/renderer/component/sideBar/view.jsx
Normal file
|
@ -0,0 +1,101 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Button from 'component/link';
|
||||
import classnames from 'classnames';
|
||||
|
||||
type SideBarLink = {
|
||||
label: string,
|
||||
path: string,
|
||||
active: boolean,
|
||||
icon: ?string,
|
||||
subLinks: Array<SideBarLink>,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
navigate: any => void,
|
||||
back: any => void,
|
||||
forward: any => void,
|
||||
isBackDisabled: boolean,
|
||||
isForwardDisabled: boolean,
|
||||
isHome: boolean,
|
||||
navLinks: {
|
||||
primary: Array<SideBarLink>,
|
||||
secondary: Array<SideBarLink>,
|
||||
},
|
||||
};
|
||||
|
||||
const SideBar = (props: Props) => {
|
||||
const { navigate, back, forward, isBackDisabled, isForwardDisabled, isHome, navLinks } = props;
|
||||
return (
|
||||
<nav className="nav">
|
||||
<div className="nav__actions-top">
|
||||
<Button
|
||||
alt
|
||||
icon="Home"
|
||||
description={__('Home')}
|
||||
onClick={() => navigate('/discover')}
|
||||
disabled={isHome}
|
||||
/>
|
||||
<div className="nav__actions-history">
|
||||
<Button
|
||||
alt
|
||||
icon="ArrowLeft"
|
||||
description={__('Navigate back')}
|
||||
onClick={back}
|
||||
disabled={isBackDisabled}
|
||||
/>
|
||||
<Button
|
||||
alt
|
||||
icon="ArrowRight"
|
||||
description={__('Navigate forward')}
|
||||
onClick={forward}
|
||||
disabled={isForwardDisabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="nav__links">
|
||||
<ul className="nav__primary">
|
||||
{navLinks.primary.map(({ label, path, active, icon }) => (
|
||||
<li
|
||||
key={path}
|
||||
className={classnames('nav__link nav__primary-link', { 'nav__link--active': active })}
|
||||
>
|
||||
<Button noStyle navigate={path} label={label} icon={icon} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<hr />
|
||||
<ul className="nav__secondary">
|
||||
{navLinks.secondary.map(({ label, path, active, icon, subLinks = [] }) => (
|
||||
<li
|
||||
key={path}
|
||||
className={classnames('nav__link nav__secondary-link', {
|
||||
'nav__link--active': active && !subLinks.length,
|
||||
})}
|
||||
>
|
||||
<Button noStyle navigate={path} label={label} icon={icon} />
|
||||
{!!subLinks.length &&
|
||||
active && (
|
||||
<ul className="nav__sub">
|
||||
{subLinks.map(({ label: subLabel, path: subPath, active: subLinkActive }) => (
|
||||
<li
|
||||
key={subPath}
|
||||
className={classnames('nav__link nav__sub-link', {
|
||||
'nav__link--active': subLinkActive,
|
||||
})}
|
||||
>
|
||||
<Button noStyle navigate={subPath} label={subLabel} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default SideBar;
|
|
@ -1,16 +0,0 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { selectCurrentPage, selectHeaderLinks } from 'redux/selectors/navigation';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import SubHeader from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
currentPage: selectCurrentPage(state),
|
||||
subLinks: selectHeaderLinks(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(SubHeader);
|
|
@ -1,34 +0,0 @@
|
|||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import classnames from 'classnames';
|
||||
|
||||
const SubHeader = props => {
|
||||
const { subLinks, currentPage, navigate, fullWidth, smallMargin } = props;
|
||||
|
||||
const links = [];
|
||||
|
||||
for (const link of Object.keys(subLinks)) {
|
||||
links.push(
|
||||
<Link
|
||||
onClick={event => navigate(`/${link}`, event)}
|
||||
key={link}
|
||||
className={link == currentPage ? 'sub-header-selected' : 'sub-header-unselected'}
|
||||
>
|
||||
{subLinks[link]}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={classnames('sub-header', {
|
||||
'sub-header--full-width': fullWidth,
|
||||
'sub-header--small-margin': smallMargin,
|
||||
})}
|
||||
>
|
||||
{links}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubHeader;
|
|
@ -1,6 +1,8 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import LinkTransaction from 'component/linkTransaction';
|
||||
import { CreditAmount } from 'component/common';
|
||||
import CreditAmount from 'component/common/credit-amount';
|
||||
import DateTime from 'component/dateTime';
|
||||
import Link from 'component/link';
|
||||
import { buildURI } from 'lbryURI';
|
||||
|
@ -89,3 +91,4 @@ class TransactionListItem extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default TransactionListItem;
|
||||
/* eslint-disable */
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import TransactionListItem from './internal/TransactionListItem';
|
||||
import FormField from 'component/formField';
|
||||
import { FormRow } from 'component/common/form';
|
||||
import Link from 'component/link';
|
||||
import * as icons from 'constants/icons';
|
||||
import * as modals from 'constants/modal_types';
|
||||
|
@ -44,9 +46,13 @@ class TransactionList extends React.PureComponent {
|
|||
return (
|
||||
<div>
|
||||
{(transactionList.length || this.state.filter) && (
|
||||
<span className="sort-section">
|
||||
{__('Filter')}{' '}
|
||||
<FormField type="select" onChange={this.handleFilterChanged.bind(this)}>
|
||||
<FormRow
|
||||
prefix={__('Filter')}
|
||||
postfix={
|
||||
<Link fakeLink href="https://lbry.io/faq/transaction-types" label={__('Help')} />
|
||||
}
|
||||
render={() => (
|
||||
<select>
|
||||
<option value="">{__('All')}</option>
|
||||
<option value="spend">{__('Spends')}</option>
|
||||
<option value="receive">{__('Receives')}</option>
|
||||
|
@ -55,9 +61,9 @@ class TransactionList extends React.PureComponent {
|
|||
<option value="tip">{__('Tips')}</option>
|
||||
<option value="support">{__('Supports')}</option>
|
||||
<option value="update">{__('Updates')}</option>
|
||||
</FormField>{' '}
|
||||
<Link href="https://lbry.io/faq/transaction-types" icon={icons.HELP_CIRCLE} />
|
||||
</span>
|
||||
</select>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{!transactionList.length && (
|
||||
<div className="empty">{emptyMessage || __('No transactions to list.')}</div>
|
||||
|
@ -92,3 +98,4 @@ class TransactionList extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default TransactionList;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { BusyMessage } from 'component/common';
|
||||
import Link from 'component/link';
|
||||
import Button from 'component/link';
|
||||
import TransactionList from 'component/transactionList';
|
||||
import * as icons from 'constants/icons';
|
||||
|
||||
class TransactionListRecent extends React.PureComponent {
|
||||
type Props = {
|
||||
fetchTransactions: () => void,
|
||||
fetchingTransactions: boolean,
|
||||
hasTransactions: boolean,
|
||||
transactions: Array<{}>, // Will iron this out when I work on transactions page - Sean
|
||||
};
|
||||
|
||||
class TransactionListRecent extends React.PureComponent<Props> {
|
||||
componentWillMount() {
|
||||
this.props.fetchTransactions();
|
||||
}
|
||||
|
@ -13,28 +21,20 @@ class TransactionListRecent extends React.PureComponent {
|
|||
const { fetchingTransactions, hasTransactions, transactions } = this.props;
|
||||
|
||||
return (
|
||||
<section className="card">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('Recent Transactions')}</h3>
|
||||
</div>
|
||||
<section className="card card--section">
|
||||
<h2>{__('Recent Transactions')}</h2>
|
||||
<div className="card__content">
|
||||
{fetchingTransactions && <BusyMessage message={__('Loading transactions')} />}
|
||||
{!fetchingTransactions && (
|
||||
<TransactionList
|
||||
transactions={transactions}
|
||||
emptyMessage={__('You have no recent transactions.')}
|
||||
emptyMessage={__("Looks like you don't have any recent transactions.")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{hasTransactions && (
|
||||
<div className="card__actions card__actions--bottom">
|
||||
<Link
|
||||
navigate="/history"
|
||||
label={__('Full History')}
|
||||
icon={icons.HISTORY}
|
||||
className="no-underline"
|
||||
button="text"
|
||||
/>
|
||||
<div className="card__actions">
|
||||
<Button navigate="/history" label={__('Full History')} icon="Clock" />
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import Button from 'component/link';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import classnames from 'classnames';
|
||||
import Icon from 'component/common/icon';
|
||||
// import Icon from 'component/common/icon';
|
||||
|
||||
type Props = {
|
||||
isResolvingUri: boolean,
|
||||
|
@ -57,7 +57,8 @@ class UriIndicator extends React.PureComponent<Props> {
|
|||
return <span>Anonymous</span>;
|
||||
}
|
||||
|
||||
let icon;
|
||||
// I'll look at this when working on the file page
|
||||
// let icon;
|
||||
let channelLink;
|
||||
let modifier;
|
||||
|
||||
|
@ -65,8 +66,8 @@ class UriIndicator extends React.PureComponent<Props> {
|
|||
modifier = 'valid';
|
||||
channelLink = link ? buildURI({ channelName, claimId: channelClaimId }, false) : false;
|
||||
} else {
|
||||
icon = 'icon-times-circle';
|
||||
modifier = 'invalid';
|
||||
// icon = 'icon-times-circle';
|
||||
// modifier = 'invalid';
|
||||
}
|
||||
|
||||
// {!signatureIsValid ? (
|
||||
|
@ -94,7 +95,7 @@ class UriIndicator extends React.PureComponent<Props> {
|
|||
}
|
||||
|
||||
return (
|
||||
<Button navigate="/show" navigateParams={{ uri: channelLink }} fakeLink>
|
||||
<Button navigate="/show" navigateParams={{ uri: channelLink }} noStyle>
|
||||
{inner}
|
||||
</Button>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import { Form, FormRow, Submit } from 'component/form.js';
|
||||
import { Form, FormRow, Submit } from 'component/common/form';
|
||||
|
||||
class UserEmailNew extends React.PureComponent {
|
||||
constructor(props) {
|
||||
|
@ -53,3 +55,4 @@ class UserEmailNew extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default UserEmailNew;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import { Form, FormRow, Submit } from 'component/form.js';
|
||||
import { Form, FormField, Submit } from 'component/common/form';
|
||||
|
||||
class UserEmailVerify extends React.PureComponent {
|
||||
constructor(props) {
|
||||
|
@ -29,19 +31,22 @@ class UserEmailVerify extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const { cancelButton, errorMessage, email, isPending } = this.props;
|
||||
// <FormField
|
||||
// label={__('Verification Code')}
|
||||
// errorMessage={errorMessage}
|
||||
// render{() => (
|
||||
// <input
|
||||
// name="code"
|
||||
// value={this.state.code}
|
||||
// onChange={event => {
|
||||
// this.handleCodeChanged(event);
|
||||
// }}
|
||||
// />
|
||||
// )}
|
||||
// />
|
||||
return (
|
||||
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||
<p>Please enter the verification code emailed to {email}.</p>
|
||||
<FormRow
|
||||
type="text"
|
||||
label={__('Verification Code')}
|
||||
name="code"
|
||||
value={this.state.code}
|
||||
onChange={event => {
|
||||
this.handleCodeChanged(event);
|
||||
}}
|
||||
errorMessage={errorMessage}
|
||||
/>
|
||||
{/* render help separately so it always shows */}
|
||||
<div className="form-field__helper">
|
||||
<p>
|
||||
|
@ -60,3 +65,4 @@ class UserEmailVerify extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default UserEmailVerify;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import { Form, FormRow, Submit } from 'component/form.js';
|
||||
import FormField from 'component/formField';
|
||||
import { Form, FormRow, FormField } from 'component/common/form';
|
||||
|
||||
const os = require('os').type();
|
||||
const countryCodes = require('country-data')
|
||||
|
@ -77,29 +78,36 @@ class UserPhoneNew extends React.PureComponent {
|
|||
</p>
|
||||
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||
<div className="form-row-phone">
|
||||
<FormField type="select" onChange={this.handleSelect.bind(this)}>
|
||||
<FormField
|
||||
onChange={this.handleSelect.bind(this)}
|
||||
render={() => (
|
||||
<select>
|
||||
{countryCodes.map((country, index) => (
|
||||
<option key={index} value={country.countryCallingCode}>
|
||||
{os === 'Darwin' ? country.emoji : `(${country.alpha2})`}{' '}
|
||||
{country.countryCallingCode}
|
||||
</option>
|
||||
))}
|
||||
</FormField>
|
||||
<FormRow
|
||||
type="text"
|
||||
placeholder={this.state.country_code === '+1' ? '(555) 555-5555' : '5555555555'}
|
||||
name="phone"
|
||||
value={this.state.phone}
|
||||
</select>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
errorMessage={phoneErrorMessage}
|
||||
onChange={event => {
|
||||
this.handleChanged(event);
|
||||
}}
|
||||
render={() => (
|
||||
<input
|
||||
type="text"
|
||||
placeholder={this.state.country_code === '+1' ? '(555) 555-5555' : '5555555555'}
|
||||
name="phone"
|
||||
value={this.state.phone}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-row-submit">
|
||||
<Submit label="Submit" disabled={isPending} />
|
||||
{cancelButton}
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
@ -107,3 +115,4 @@ class UserPhoneNew extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default UserPhoneNew;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import { Form, FormRow, Submit } from 'component/form.js';
|
||||
import { Form, FormElement, Submit } from 'component/common/form';
|
||||
|
||||
class UserPhoneVerify extends React.PureComponent {
|
||||
constructor(props) {
|
||||
|
@ -33,21 +35,23 @@ class UserPhoneVerify extends React.PureComponent {
|
|||
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||
<p>
|
||||
{__(
|
||||
`Please enter the verification code sent to +${countryCode}${
|
||||
phone
|
||||
}. Didn't receive it? `
|
||||
`Please enter the verification code sent to +${countryCode}${phone}. Didn't receive it? `
|
||||
)}
|
||||
<Link onClick={this.reset.bind(this)} label="Go back." />
|
||||
</p>
|
||||
<FormRow
|
||||
type="text"
|
||||
<FormElement
|
||||
label={__('Verification Code')}
|
||||
errorMessage={phoneErrorMessage}
|
||||
render={() => (
|
||||
<input
|
||||
type="text"
|
||||
name="code"
|
||||
value={this.state.code}
|
||||
onChange={event => {
|
||||
this.handleCodeChanged(event);
|
||||
}}
|
||||
errorMessage={phoneErrorMessage}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* render help separately so it always shows */}
|
||||
<div className="form-field__helper">
|
||||
|
@ -67,3 +71,4 @@ class UserPhoneVerify extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default UserPhoneVerify;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// I'll come back to This
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import CardVerify from 'component/cardVerify';
|
||||
|
@ -25,8 +27,8 @@ class UserVerify extends React.PureComponent {
|
|||
render() {
|
||||
const { errorMessage, isPending, navigate, verifyPhone, modal } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<section className="card card--form">
|
||||
<React.Fragment>
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h1>{__('Final Human Proof')}</h1>
|
||||
</div>
|
||||
|
@ -36,7 +38,7 @@ class UserVerify extends React.PureComponent {
|
|||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section className="card card--form">
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('1) Proof via Credit')}</h3>
|
||||
</div>
|
||||
|
@ -64,7 +66,7 @@ class UserVerify extends React.PureComponent {
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="card card--form">
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('2) Proof via Phone')}</h3>
|
||||
</div>
|
||||
|
@ -117,7 +119,7 @@ class UserVerify extends React.PureComponent {
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="card card--form">
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('4) Proof via Chat')}</h3>
|
||||
</div>
|
||||
|
@ -142,7 +144,7 @@ class UserVerify extends React.PureComponent {
|
|||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section className="card card--form">
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h5>{__('Or, Skip It Entirely')}</h5>
|
||||
</div>
|
||||
|
@ -157,9 +159,10 @@ class UserVerify extends React.PureComponent {
|
|||
<Link onClick={() => navigate('/discover')} button="alt" label={__('Skip Rewards')} />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserVerify;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import Address from 'component/address';
|
||||
|
||||
class WalletAddress extends React.PureComponent {
|
||||
type Props = {
|
||||
checkAddressIsMine: string => void,
|
||||
receiveAddress: string,
|
||||
getNewAddress: () => void,
|
||||
gettingNewAddress: boolean,
|
||||
};
|
||||
|
||||
class WalletAddress extends React.PureComponent<Props> {
|
||||
componentWillMount() {
|
||||
this.props.checkAddressIsMine(this.props.receiveAddress);
|
||||
}
|
||||
|
@ -11,21 +19,22 @@ class WalletAddress extends React.PureComponent {
|
|||
const { receiveAddress, getNewAddress, gettingNewAddress } = this.props;
|
||||
|
||||
return (
|
||||
<section className="card">
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('Receive Credits')}</h3>
|
||||
<h2>{__('Receive Credits')}</h2>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<p>
|
||||
<p className="card__subtitle">
|
||||
{__('Use this wallet address to receive credits sent by another user (or yourself).')}
|
||||
</p>
|
||||
|
||||
<div className="card__content">
|
||||
<Address address={receiveAddress} showCopyButton />
|
||||
</div>
|
||||
|
||||
<div className="card__actions">
|
||||
<Link
|
||||
label={__('Get New Address')}
|
||||
button="primary"
|
||||
icon="icon-refresh"
|
||||
icon="RefreshCw"
|
||||
onClick={getNewAddress}
|
||||
disabled={gettingNewAddress}
|
||||
/>
|
||||
|
|
|
@ -1,33 +1,19 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import { CreditAmount } from 'component/common';
|
||||
import CreditAmount from 'component/common/credit-amount';
|
||||
|
||||
const WalletBalance = props => {
|
||||
const { balance, navigate } = props;
|
||||
/*
|
||||
<div className="help">
|
||||
<Link
|
||||
onClick={() => navigate("/backup")}
|
||||
label={__("Backup Your Wallet")}
|
||||
/>
|
||||
</div>
|
||||
*/
|
||||
type Props = {
|
||||
balance: number,
|
||||
};
|
||||
|
||||
const WalletBalance = (props: Props) => {
|
||||
const { balance } = props;
|
||||
return (
|
||||
<section className="card">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('Balance')}</h3>
|
||||
</div>
|
||||
<section className="card card--section">
|
||||
<h2>{__('Balance')}</h2>
|
||||
<span className="card__subtitle">{__('You currently have')}</span>
|
||||
<div className="card__content">
|
||||
{(balance || balance === 0) && <CreditAmount amount={balance} precision={8} />}
|
||||
</div>
|
||||
<div className="card__actions">
|
||||
<Link button="alt" navigate="/getcredits" label={__('Get Credits')} />
|
||||
<Link
|
||||
button="alt"
|
||||
disabled={balance === 0}
|
||||
navigate="/backup"
|
||||
label={__('Backup Wallet')}
|
||||
/>
|
||||
{(balance || balance === 0) && <CreditAmount large amount={balance} precision={8} />}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
|
|
@ -1,28 +1,9 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
doSendDraftTransaction,
|
||||
doSetDraftTransactionAmount,
|
||||
doSetDraftTransactionAddress,
|
||||
} from 'redux/actions/wallet';
|
||||
import {
|
||||
selectDraftTransactionAmount,
|
||||
selectDraftTransactionAddress,
|
||||
selectDraftTransactionError,
|
||||
} from 'redux/selectors/wallet';
|
||||
|
||||
import { doSendDraftTransaction } from 'redux/actions/wallet';
|
||||
import WalletSend from './view';
|
||||
|
||||
const select = state => ({
|
||||
address: selectDraftTransactionAddress(state),
|
||||
amount: selectDraftTransactionAmount(state),
|
||||
error: selectDraftTransactionError(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
sendToAddress: () => dispatch(doSendDraftTransaction()),
|
||||
setAmount: event => dispatch(doSetDraftTransactionAmount(event.target.value)),
|
||||
setAddress: event => dispatch(doSetDraftTransactionAddress(event.target.value)),
|
||||
sendToAddress: values => dispatch(doSendDraftTransaction(values)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(WalletSend);
|
||||
export default connect(null, perform)(WalletSend);
|
||||
|
|
|
@ -1,55 +1,105 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { Form, FormRow, Submit } from 'component/form';
|
||||
import { regexAddress } from 'lbryURI';
|
||||
import Button from 'component/link';
|
||||
import { Form, FormRow, FormField } from 'component/common/form';
|
||||
import { Formik } from 'formik';
|
||||
import { validateSendTx } from 'util/form-validation';
|
||||
|
||||
class WalletSend extends React.PureComponent {
|
||||
handleSubmit() {
|
||||
const { amount, address, sendToAddress } = this.props;
|
||||
const validSubmit = parseFloat(amount) > 0.0 && address;
|
||||
type DraftTransaction = {
|
||||
address: string,
|
||||
amount: number | string, // So we can use a placeholder in the input
|
||||
};
|
||||
|
||||
if (validSubmit) {
|
||||
sendToAddress();
|
||||
type Props = {
|
||||
sendToAddress: DraftTransaction => void,
|
||||
};
|
||||
|
||||
class WalletSend extends React.PureComponent<Props> {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
(this: any).handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
handleSubmit(values: DraftTransaction) {
|
||||
const { sendToAddress } = this.props;
|
||||
sendToAddress(values);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { closeModal, modal, setAmount, setAddress, amount, address, error } = this.props;
|
||||
|
||||
return (
|
||||
<section className="card">
|
||||
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('Send Credits')}</h3>
|
||||
<h2>{__('Send Credits')}</h2>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<FormRow
|
||||
<Formik
|
||||
initialValues={{
|
||||
address: '',
|
||||
amount: '',
|
||||
}}
|
||||
onSubmit={this.handleSubmit}
|
||||
validate={validateSendTx}
|
||||
render={({
|
||||
values,
|
||||
errors,
|
||||
touched,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
handleSubmit,
|
||||
isSubmitting,
|
||||
}) => (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<FormRow>
|
||||
<FormField
|
||||
label={__('Amount')}
|
||||
postfix={__('LBC')}
|
||||
step="any"
|
||||
min="0"
|
||||
error={!!values.amount && touched.amount && errors.amount}
|
||||
render={() => (
|
||||
<input
|
||||
className="input--lbc-amount"
|
||||
type="number"
|
||||
placeholder="1.23"
|
||||
size="10"
|
||||
onChange={setAmount}
|
||||
value={amount}
|
||||
name="amount"
|
||||
min="0"
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
value={values.amount}
|
||||
/>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<FormRow
|
||||
label={__('Recipient Address')}
|
||||
placeholder="bbFxRyXXXXXXXXXXXZD8nE7XTLUxYnddTs"
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
label={__('Recipient address')}
|
||||
error={!!values.address && touched.address && errors.address}
|
||||
render={() => (
|
||||
<input
|
||||
className="input--address"
|
||||
type="text"
|
||||
size="60"
|
||||
onChange={setAddress}
|
||||
value={address}
|
||||
regexp={regexAddress}
|
||||
trim
|
||||
name="address"
|
||||
placeholder="bbFxRyXXXXXXXXXXXZD8nE7XTLUxYnddTs"
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
value={values.address}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormRow>
|
||||
<div className="card__actions">
|
||||
<Button
|
||||
type="submit"
|
||||
icon="Send"
|
||||
label={__('Send')}
|
||||
disabled={
|
||||
!values.address ||
|
||||
!!Object.keys(errors).length ||
|
||||
!(parseFloat(values.amount) > 0.0)
|
||||
}
|
||||
/>
|
||||
<div className="form-row-submit">
|
||||
<Submit label={__('Send')} disabled={!(parseFloat(amount) > 0.0) || !address} />
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import { FormRow } from 'component/form';
|
||||
import { FormRow } from 'component/common/form';
|
||||
import UriIndicator from 'component/uriIndicator';
|
||||
|
||||
class WalletSendTip extends React.PureComponent {
|
||||
|
@ -67,3 +69,4 @@ class WalletSendTip extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default WalletSendTip;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -466,6 +466,7 @@ export default class Autocomplete extends React.Component {
|
|||
const menu = this.props.renderMenu(items, this.props.value, style);
|
||||
return React.cloneElement(menu, {
|
||||
ref: e => (this.refs.menu = e),
|
||||
className: 'wunderbar__menu',
|
||||
// Ignore blur to prevent menu from de-rendering before we can process click
|
||||
onMouseEnter: () => this.setIgnoreBlur(true),
|
||||
onMouseLeave: () => this.setIgnoreBlur(false),
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
import React from 'react';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import classnames from 'classnames';
|
||||
import throttle from 'util/throttle';
|
||||
import Icon from 'component/common/icon';
|
||||
import Autocomplete from './internal/autocomplete';
|
||||
|
||||
type Props = {
|
||||
|
@ -16,29 +18,22 @@ type Props = {
|
|||
};
|
||||
|
||||
class WunderBar extends React.PureComponent<Props> {
|
||||
constructor() {
|
||||
super();
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
(this: any).handleSubmit = this.handleSubmit.bind(this);
|
||||
(this: any).handleChange = this.handleChange.bind(this);
|
||||
(this: any).focus = this.focus.bind(this);
|
||||
(this: any).throttledGetSearchSuggestions = throttle(this.props.getSearchSuggestions, 1000);
|
||||
this.input = undefined;
|
||||
}
|
||||
|
||||
input: ?HTMLInputElement;
|
||||
|
||||
handleChange(e: SyntheticInputEvent<*>) {
|
||||
const { updateSearchQuery, getSearchSuggestions } = this.props;
|
||||
const { value } = e.target;
|
||||
|
||||
updateSearchQuery(value);
|
||||
getSearchSuggestions(value);
|
||||
}
|
||||
|
||||
focus() {
|
||||
const { input } = this;
|
||||
if (input) {
|
||||
input.focus();
|
||||
}
|
||||
this.throttledGetSearchSuggestions(value);
|
||||
}
|
||||
|
||||
handleSubmit(value: string) {
|
||||
|
@ -70,6 +65,16 @@ class WunderBar extends React.PureComponent<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
focus() {
|
||||
const { input } = this;
|
||||
if (input) {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
|
||||
input: ?HTMLInputElement;
|
||||
throttledGetSearchSuggestions: string => void;
|
||||
|
||||
render() {
|
||||
const { searchQuery, isActive, address, suggestions } = this.props;
|
||||
|
||||
|
@ -83,12 +88,13 @@ class WunderBar extends React.PureComponent<Props> {
|
|||
'header__wunderbar--active': isActive,
|
||||
})}
|
||||
>
|
||||
<Icon icon="Search" />
|
||||
<Autocomplete
|
||||
autoHighlight
|
||||
ref={ref => {
|
||||
this.input = ref;
|
||||
}}
|
||||
wrapperStyle={{ flex: 1, minHeight: 0 }}
|
||||
wrapperStyle={{ flex: 1 }}
|
||||
value={wunderbarValue}
|
||||
items={suggestions}
|
||||
getItemValue={item => item.value}
|
||||
|
@ -108,7 +114,8 @@ class WunderBar extends React.PureComponent<Props> {
|
|||
'wunderbar__active-suggestion': isHighlighted,
|
||||
})}
|
||||
>
|
||||
{item.label}
|
||||
<Icon icon={item.icon} />
|
||||
<span className="wunderbar__suggestion-label">{item.label}</span>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -37,8 +37,6 @@ export const FETCH_TRANSACTIONS_COMPLETED = 'FETCH_TRANSACTIONS_COMPLETED';
|
|||
export const UPDATE_BALANCE = 'UPDATE_BALANCE';
|
||||
export const CHECK_ADDRESS_IS_MINE_STARTED = 'CHECK_ADDRESS_IS_MINE_STARTED';
|
||||
export const CHECK_ADDRESS_IS_MINE_COMPLETED = 'CHECK_ADDRESS_IS_MINE_COMPLETED';
|
||||
export const SET_DRAFT_TRANSACTION_AMOUNT = 'SET_DRAFT_TRANSACTION_AMOUNT';
|
||||
export const SET_DRAFT_TRANSACTION_ADDRESS = 'SET_DRAFT_TRANSACTION_ADDRESS';
|
||||
export const SEND_TRANSACTION_STARTED = 'SEND_TRANSACTION_STARTED';
|
||||
export const SEND_TRANSACTION_COMPLETED = 'SEND_TRANSACTION_COMPLETED';
|
||||
export const SEND_TRANSACTION_FAILED = 'SEND_TRANSACTION_FAILED';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export const FEATURED = 'rocket';
|
||||
export const LOCAL = 'folder';
|
||||
export const FEATURED = 'Award';
|
||||
export const LOCAL = 'Folder';
|
||||
export const FILE = 'file';
|
||||
export const HISTORY = 'history';
|
||||
export const HELP_CIRCLE = 'question-circle';
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
// I'll come back to this
|
||||
/* esline-disable */
|
||||
import React from 'react';
|
||||
import { Modal } from 'modal/modal';
|
||||
import { CreditAmount, CurrencySymbol } from 'component/common';
|
||||
import { CurrencySymbol } from 'component/common';
|
||||
import CreditAmount from 'component/common/credit-amount';
|
||||
import Link from 'component/link/index';
|
||||
|
||||
const ModalCreditIntro = props => {
|
||||
|
@ -46,3 +49,4 @@ const ModalCreditIntro = props => {
|
|||
};
|
||||
|
||||
export default ModalCreditIntro;
|
||||
/* esline-enable */
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// I"ll come back to This
|
||||
/* esline-disable */
|
||||
import React from 'react';
|
||||
import { Modal } from 'modal/modal';
|
||||
import { CreditAmount } from 'component/common';
|
||||
import CreditAmount from 'component/common/credit-amount';
|
||||
|
||||
class ModalFirstReward extends React.PureComponent {
|
||||
render() {
|
||||
|
@ -42,3 +44,4 @@ class ModalFirstReward extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default ModalFirstReward;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -4,6 +4,7 @@ import Link from 'component/link';
|
|||
import UserEmailNew from 'component/userEmailNew';
|
||||
import UserEmailVerify from 'component/userEmailVerify';
|
||||
import UserVerify from 'component/userVerify';
|
||||
import Page from 'component/page';
|
||||
|
||||
export class AuthPage extends React.PureComponent {
|
||||
componentWillMount() {
|
||||
|
@ -58,9 +59,10 @@ export class AuthPage extends React.PureComponent {
|
|||
const { email, user, isPending, navigate } = this.props;
|
||||
const [innerContent, useTemplate] = this.renderMain();
|
||||
|
||||
return useTemplate ? (
|
||||
<main>
|
||||
<section className="card card--form">
|
||||
return (
|
||||
<Page>
|
||||
{useTemplate ? (
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h1>{this.getTitle()}</h1>
|
||||
</div>
|
||||
|
@ -74,9 +76,10 @@ export class AuthPage extends React.PureComponent {
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
) : (
|
||||
innerContent
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import SubHeader from 'component/subHeader';
|
||||
import Link from 'component/link';
|
||||
import Page from 'component/page';
|
||||
|
||||
class BackupPage extends React.PureComponent {
|
||||
render() {
|
||||
|
@ -16,9 +16,8 @@ class BackupPage extends React.PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
<section className="card">
|
||||
<Page>
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('Backup Your LBRY Credits')}</h3>
|
||||
</div>
|
||||
|
@ -57,7 +56,7 @@ class BackupPage extends React.PureComponent {
|
|||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import Link from 'component/link';
|
|||
import { FileTile } from 'component/fileTile';
|
||||
import { BusyMessage, Thumbnail } from 'component/common.js';
|
||||
import FileList from 'component/fileList';
|
||||
import SubHeader from 'component/subHeader';
|
||||
|
||||
class FileListDownloaded extends React.PureComponent {
|
||||
componentWillMount() {
|
||||
|
@ -31,12 +30,7 @@ class FileListDownloaded extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
{content}
|
||||
</main>
|
||||
);
|
||||
return <main className="main--single-column">{content}</main>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import Link from 'component/link';
|
|||
import FileTile from 'component/fileTile';
|
||||
import { BusyMessage, Thumbnail } from 'component/common.js';
|
||||
import FileList from 'component/fileList';
|
||||
import SubHeader from 'component/subHeader';
|
||||
|
||||
class FileListPublished extends React.PureComponent {
|
||||
componentWillMount() {
|
||||
|
@ -41,12 +40,7 @@ class FileListPublished extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
{content}
|
||||
</main>
|
||||
);
|
||||
return <main className="main--single-column">{content}</main>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
import React from 'react';
|
||||
import SubHeader from 'component/subHeader';
|
||||
import Link from 'component/link';
|
||||
import RewardSummary from 'component/rewardSummary';
|
||||
import ShapeShift from 'component/shapeShift';
|
||||
import Page from 'component/page';
|
||||
|
||||
const GetCreditsPage = props => (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
<Page>
|
||||
<RewardSummary />
|
||||
<ShapeShift />
|
||||
<section className="card">
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('From External Wallet')}</h3>
|
||||
<h2>{__('From External Wallet')}</h2>
|
||||
</div>
|
||||
<div className="card__actions">
|
||||
<Link button="alt" navigate="/send" icon="icon-send" label={__('Send / Receive')} />
|
||||
<Link navigate="/send" label={__('Send / Receive')} />
|
||||
</div>
|
||||
</section>
|
||||
<section className="card">
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('More ways to get LBRY Credits')}</h3>
|
||||
<h2>{__('More ways to get LBRY Credits')}</h2>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<p>
|
||||
|
@ -29,10 +28,10 @@ const GetCreditsPage = props => (
|
|||
</p>
|
||||
</div>
|
||||
<div className="card__actions">
|
||||
<Link button="alt" href="https://lbry.io/faq/earn-credits" label={__('Read More')} />
|
||||
<Link fakeLink href="https://lbry.io/faq/earn-credits" label={__('Read More')} />
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</Page>
|
||||
);
|
||||
|
||||
export default GetCreditsPage;
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
import React from 'react';
|
||||
import lbry from 'lbry.js';
|
||||
import Link from 'component/link';
|
||||
import SubHeader from 'component/subHeader';
|
||||
import { BusyMessage } from 'component/common';
|
||||
import Icon from 'component/common/icon';
|
||||
import Page from 'component/page';
|
||||
|
||||
class HelpPage extends React.PureComponent {
|
||||
constructor(props) {
|
||||
|
@ -70,8 +70,7 @@ class HelpPage extends React.PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
<Page>
|
||||
<section className="card">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('Read the FAQ')}</h3>
|
||||
|
@ -210,7 +209,7 @@ class HelpPage extends React.PureComponent {
|
|||
)}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import { BusyMessage } from 'component/common';
|
||||
import SubHeader from 'component/subHeader';
|
||||
import InviteNew from 'component/inviteNew';
|
||||
import InviteList from 'component/inviteList';
|
||||
|
||||
|
@ -14,7 +13,6 @@ class InvitePage extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
{isPending && <BusyMessage message={__('Checking your invite status')} />}
|
||||
{!isPending &&
|
||||
isFailed && <span className="empty">{__('Failed to retrieve invite status.')}</span>}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import { FormRow } from 'component/form';
|
||||
import { FormRow } from 'component/common/form';
|
||||
import { doShowSnackBar } from 'redux/actions/app';
|
||||
import lbry from '../lbry.js';
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ import React from 'react';
|
|||
import { BusyMessage } from 'component/common';
|
||||
import RewardListClaimed from 'component/rewardListClaimed';
|
||||
import RewardTile from 'component/rewardTile';
|
||||
import SubHeader from 'component/subHeader';
|
||||
import Link from 'component/link';
|
||||
import Page from 'component/page';
|
||||
|
||||
class RewardsPage extends React.PureComponent {
|
||||
/*
|
||||
|
@ -34,7 +34,7 @@ class RewardsPage extends React.PureComponent {
|
|||
if (user && !user.is_reward_approved) {
|
||||
if (!user.primary_email || !user.has_verified_email || !user.is_identity_verified) {
|
||||
return (
|
||||
<section className="card">
|
||||
<section className="card card--section">
|
||||
<div className="card__title-primary">
|
||||
<h3>{__('Humans Only')}</h3>
|
||||
</div>
|
||||
|
@ -102,6 +102,7 @@ class RewardsPage extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
return (
|
||||
// TODO: come back to me and actually implement a grid
|
||||
<div className="card-grid">
|
||||
{rewards.map(reward => <RewardTile key={reward.reward_type} reward={reward} />)}
|
||||
</div>
|
||||
|
@ -110,12 +111,11 @@ class RewardsPage extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
<Page>
|
||||
{this.renderPageHeader()}
|
||||
{this.renderUnclaimedRewards()}
|
||||
{<RewardListClaimed />}
|
||||
</main>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import React from 'react';
|
||||
import SubHeader from 'component/subHeader';
|
||||
import WalletSend from 'component/walletSend';
|
||||
import WalletAddress from 'component/walletAddress';
|
||||
import Page from 'component/page';
|
||||
|
||||
const SendReceivePage = props => (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
<Page>
|
||||
<WalletSend />
|
||||
<WalletAddress />
|
||||
</main>
|
||||
</Page>
|
||||
);
|
||||
|
||||
export default SendReceivePage;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react';
|
||||
import FormField from 'component/formField';
|
||||
import { FormRow } from 'component/form.js';
|
||||
import SubHeader from 'component/subHeader';
|
||||
import { FormRow } from 'component/common/form';
|
||||
import * as settings from 'constants/settings';
|
||||
import lbry from 'lbry.js';
|
||||
import Link from 'component/link';
|
||||
import FormFieldPrice from 'component/formFieldPrice';
|
||||
import Page from 'component/page';
|
||||
|
||||
class SettingsPage extends React.PureComponent {
|
||||
constructor(props) {
|
||||
|
@ -144,8 +144,7 @@ class SettingsPage extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
<Page>
|
||||
{/*
|
||||
<section className="card">
|
||||
<div className="card__content">
|
||||
|
@ -348,7 +347,7 @@ class SettingsPage extends React.PureComponent {
|
|||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import SubHeader from 'component/subHeader';
|
||||
import Page from 'component/page';
|
||||
import { BusyMessage } from 'component/common';
|
||||
import CategoryList from 'component/common/category-list';
|
||||
import type { Subscription } from 'redux/reducers/subscriptions';
|
||||
|
@ -61,16 +61,10 @@ export default class extends React.PureComponent<Props> {
|
|||
(subscriptions.length !== savedSubscriptions.length || someClaimsNotLoaded);
|
||||
|
||||
return (
|
||||
<main className="main main--no-margin">
|
||||
<SubHeader fullWidth smallMargin />
|
||||
<Page noPadding isLoading={fetchingSubscriptions}>
|
||||
{!savedSubscriptions.length && (
|
||||
<span>{__("You haven't subscribed to any channels yet")}</span>
|
||||
)}
|
||||
{fetchingSubscriptions && (
|
||||
<div className="card-row__placeholder">
|
||||
<BusyMessage message={__('Fetching subscriptions')} />
|
||||
</div>
|
||||
)}
|
||||
{!!savedSubscriptions.length && (
|
||||
<div>
|
||||
{!!subscriptions.length &&
|
||||
|
@ -93,7 +87,7 @@ export default class extends React.PureComponent<Props> {
|
|||
})}
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { BusyMessage } from 'component/common';
|
||||
import SubHeader from 'component/subHeader';
|
||||
import TransactionList from 'component/transactionList';
|
||||
import Page from 'component/page';
|
||||
|
||||
class TransactionHistoryPage extends React.PureComponent {
|
||||
componentWillMount() {
|
||||
|
@ -12,9 +12,8 @@ class TransactionHistoryPage extends React.PureComponent {
|
|||
const { fetchingTransactions, transactions } = this.props;
|
||||
|
||||
return (
|
||||
<main className="main--single-column">
|
||||
<SubHeader />
|
||||
<section className="card">
|
||||
<Page>
|
||||
<section className="card card--section">
|
||||
<div
|
||||
className={`card__title-primary ${
|
||||
fetchingTransactions && transactions.length ? 'reloading' : ''
|
||||
|
@ -35,7 +34,7 @@ class TransactionHistoryPage extends React.PureComponent {
|
|||
)}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import React from 'react';
|
||||
import SubHeader from 'component/subHeader';
|
||||
import WalletBalance from 'component/walletBalance';
|
||||
import RewardSummary from 'component/rewardSummary';
|
||||
import TransactionListRecent from 'component/transactionListRecent';
|
||||
import WalletAddress from 'component/walletAddress';
|
||||
import Page from 'component/page';
|
||||
|
||||
const WalletPage = props => (
|
||||
<main className="main--single-column page--wallet">
|
||||
<SubHeader />
|
||||
<div className="card-grid">
|
||||
const WalletPage = () => (
|
||||
<Page>
|
||||
<div className="columns">
|
||||
<WalletBalance />
|
||||
<RewardSummary />
|
||||
</div>
|
||||
<WalletAddress />
|
||||
<TransactionListRecent />
|
||||
</main>
|
||||
</Page>
|
||||
);
|
||||
|
||||
export default WalletPage;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as ACTIONS from 'constants/action_types';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import { normalizeURI, buildURI } from 'lbryURI';
|
||||
import { doResolveUri } from 'redux/actions/content';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { selectCurrentPage } from 'redux/selectors/navigation';
|
||||
|
@ -89,10 +89,17 @@ export const getSearchSuggestions = value => dispatch => {
|
|||
fetch(`https://lighthouse.lbry.io/autocomplete?s=${searchValue}`)
|
||||
.then(handleSearchApiResponse)
|
||||
.then(suggestions => {
|
||||
const formattedSuggestions = suggestions.slice(0, 5).map(suggestion => ({
|
||||
label: suggestion,
|
||||
const formattedSuggestions = suggestions.slice(0, 5).map(suggestion => {
|
||||
// This will need to be more robust when the api starts returning lbry uris
|
||||
const isChannel = suggestion.startsWith('@');
|
||||
const suggestionObj = {
|
||||
value: suggestion,
|
||||
}));
|
||||
label: isChannel ? suggestion.slice(1) : suggestion,
|
||||
icon: isChannel ? 'AtSign' : 'Search',
|
||||
};
|
||||
|
||||
return suggestionObj;
|
||||
});
|
||||
|
||||
// Should we add lbry://{query} as the first result?
|
||||
// If it's not a valid uri, then add a "search for {query}" result
|
||||
|
@ -100,12 +107,12 @@ export const getSearchSuggestions = value => dispatch => {
|
|||
try {
|
||||
const uri = normalizeURI(value);
|
||||
formattedSuggestions.unshift(
|
||||
{ label: uri, value: uri },
|
||||
{ label: searchLabel, value: `${value}?search` }
|
||||
{ label: uri, value: uri, icon: 'Compass' },
|
||||
{ label: searchLabel, value: `${value}?search`, icon: 'Search' }
|
||||
);
|
||||
} catch (e) {
|
||||
if (value) {
|
||||
formattedSuggestions.unshift({ label: searchLabel, value });
|
||||
formattedSuggestions.unshift({ label: searchLabel, value, icon: 'Search' });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ import { doOpenModal, doShowSnackBar } from 'redux/actions/app';
|
|||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import {
|
||||
selectBalance,
|
||||
selectDraftTransaction,
|
||||
selectDraftTransactionAmount,
|
||||
} from 'redux/selectors/wallet';
|
||||
|
||||
export function doUpdateBalance() {
|
||||
|
@ -89,12 +87,10 @@ export function doCheckAddressIsMine(address) {
|
|||
};
|
||||
}
|
||||
|
||||
export function doSendDraftTransaction() {
|
||||
export function doSendDraftTransaction({ amount, address }) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const draftTx = selectDraftTransaction(state);
|
||||
const balance = selectBalance(state);
|
||||
const amount = selectDraftTransactionAmount(state);
|
||||
|
||||
if (balance - amount <= 0) {
|
||||
dispatch(doOpenModal(MODALS.INSUFFICIENT_CREDITS));
|
||||
|
@ -135,26 +131,12 @@ export function doSendDraftTransaction() {
|
|||
};
|
||||
|
||||
Lbry.wallet_send({
|
||||
amount: draftTx.amount,
|
||||
address: draftTx.address,
|
||||
amount,
|
||||
address,
|
||||
}).then(successCallback, errorCallback);
|
||||
};
|
||||
}
|
||||
|
||||
export function doSetDraftTransactionAmount(amount) {
|
||||
return {
|
||||
type: ACTIONS.SET_DRAFT_TRANSACTION_AMOUNT,
|
||||
data: { amount },
|
||||
};
|
||||
}
|
||||
|
||||
export function doSetDraftTransactionAddress(address) {
|
||||
return {
|
||||
type: ACTIONS.SET_DRAFT_TRANSACTION_ADDRESS,
|
||||
data: { address },
|
||||
};
|
||||
}
|
||||
|
||||
export function doSendSupport(amount, claimId, uri) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
|
|
@ -2,10 +2,6 @@ import * as ACTIONS from 'constants/action_types';
|
|||
|
||||
const reducers = {};
|
||||
const receiveAddress = localStorage.getItem('receiveAddress');
|
||||
const buildDraftTransaction = () => ({
|
||||
amount: undefined,
|
||||
address: undefined,
|
||||
});
|
||||
|
||||
const defaultState = {
|
||||
balance: undefined,
|
||||
|
@ -14,8 +10,8 @@ const defaultState = {
|
|||
fetchingTransactions: false,
|
||||
receiveAddress,
|
||||
gettingNewAddress: false,
|
||||
draftTransaction: buildDraftTransaction(),
|
||||
sendingSupport: false,
|
||||
sendingTx: false
|
||||
};
|
||||
|
||||
reducers[ACTIONS.FETCH_TRANSACTIONS_STARTED] = state =>
|
||||
|
@ -68,51 +64,21 @@ reducers[ACTIONS.CHECK_ADDRESS_IS_MINE_COMPLETED] = state =>
|
|||
checkingAddressOwnership: false,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SET_DRAFT_TRANSACTION_AMOUNT] = (state, action) => {
|
||||
const oldDraft = state.draftTransaction;
|
||||
const newDraft = Object.assign({}, oldDraft, {
|
||||
amount: parseFloat(action.data.amount),
|
||||
});
|
||||
|
||||
return Object.assign({}, state, {
|
||||
draftTransaction: newDraft,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.SET_DRAFT_TRANSACTION_ADDRESS] = (state, action) => {
|
||||
const oldDraft = state.draftTransaction;
|
||||
const newDraft = Object.assign({}, oldDraft, {
|
||||
address: action.data.address,
|
||||
});
|
||||
|
||||
return Object.assign({}, state, {
|
||||
draftTransaction: newDraft,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.SEND_TRANSACTION_STARTED] = state => {
|
||||
const newDraftTransaction = Object.assign({}, state.draftTransaction, {
|
||||
sending: true,
|
||||
});
|
||||
|
||||
return Object.assign({}, state, {
|
||||
draftTransaction: newDraftTransaction,
|
||||
sendingTx: true,
|
||||
});
|
||||
};
|
||||
|
||||
reducers[ACTIONS.SEND_TRANSACTION_COMPLETED] = state =>
|
||||
Object.assign({}, state, {
|
||||
draftTransaction: buildDraftTransaction(),
|
||||
sendingTx: false,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.SEND_TRANSACTION_FAILED] = (state, action) => {
|
||||
const newDraftTransaction = Object.assign({}, state.draftTransaction, {
|
||||
sending: false,
|
||||
error: action.data.error,
|
||||
});
|
||||
|
||||
return Object.assign({}, state, {
|
||||
draftTransaction: newDraftTransaction,
|
||||
sendingTx: false,
|
||||
error: action.data.error
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import { parseQueryParams, toQueryString } from 'util/query_params';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import { parseQueryParams } from 'util/query_params';
|
||||
|
||||
export const selectState = state => state.navigation || {};
|
||||
|
||||
|
@ -22,72 +21,6 @@ export const selectCurrentParams = createSelector(selectCurrentPath, path => {
|
|||
export const makeSelectCurrentParam = param =>
|
||||
createSelector(selectCurrentParams, params => (params ? params[param] : undefined));
|
||||
|
||||
export const selectHeaderLinks = createSelector(selectCurrentPage, page => {
|
||||
// This contains intentional fall throughs
|
||||
switch (page) {
|
||||
case 'wallet':
|
||||
case 'history':
|
||||
case 'send':
|
||||
case 'getcredits':
|
||||
case 'invite':
|
||||
case 'rewards':
|
||||
case 'backup':
|
||||
return {
|
||||
wallet: __('Overview'),
|
||||
getcredits: __('Get Credits'),
|
||||
send: __('Send / Receive'),
|
||||
rewards: __('Rewards'),
|
||||
invite: __('Invites'),
|
||||
history: __('History'),
|
||||
};
|
||||
case 'downloaded':
|
||||
case 'published':
|
||||
return {
|
||||
downloaded: __('Downloaded'),
|
||||
published: __('Published'),
|
||||
};
|
||||
case 'settings':
|
||||
case 'help':
|
||||
return {
|
||||
settings: __('Settings'),
|
||||
help: __('Help'),
|
||||
};
|
||||
case 'discover':
|
||||
case 'subscriptions':
|
||||
return {
|
||||
discover: __('Discover'),
|
||||
subscriptions: __('Subscriptions'),
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
export const selectPageTitle = createSelector(
|
||||
selectCurrentPage,
|
||||
selectCurrentParams,
|
||||
(page, params) => {
|
||||
switch (page) {
|
||||
case 'show': {
|
||||
const parts = [normalizeURI(params.uri)];
|
||||
// If the params has any keys other than "uri"
|
||||
if (Object.keys(params).length > 1) {
|
||||
parts.push(toQueryString(Object.assign({}, params, { uri: null })));
|
||||
}
|
||||
return parts.join('?');
|
||||
}
|
||||
case 'discover':
|
||||
return __('Discover');
|
||||
case false:
|
||||
case null:
|
||||
case '':
|
||||
return '';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const selectPathAfterAuth = createSelector(selectState, state => state.pathAfterAuth);
|
||||
|
||||
export const selectIsBackDisabled = createSelector(selectState, state => state.index === 0);
|
||||
|
@ -97,6 +30,8 @@ export const selectIsForwardDisabled = createSelector(
|
|||
state => state.index === state.stack.length - 1
|
||||
);
|
||||
|
||||
export const selectIsHome = createSelector(selectCurrentPage, page => page === 'discover');
|
||||
|
||||
export const selectHistoryIndex = createSelector(selectState, state => state.index);
|
||||
|
||||
export const selectHistoryStack = createSelector(selectState, state => state.stack);
|
||||
|
@ -106,3 +41,126 @@ export const selectActiveHistoryEntry = createSelector(
|
|||
selectState,
|
||||
state => state.stack[state.index]
|
||||
);
|
||||
|
||||
export const selectPageTitle = createSelector(
|
||||
selectCurrentPage,
|
||||
(page) => {
|
||||
switch (page) {
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const selectNavLinks = createSelector(
|
||||
selectCurrentPage,
|
||||
selectHistoryStack,
|
||||
(currentPage, historyStack) => {
|
||||
const isWalletPage = page =>
|
||||
page === 'wallet' ||
|
||||
page === 'send' ||
|
||||
page === 'getcredits' ||
|
||||
page === 'rewards' ||
|
||||
page === 'history';
|
||||
|
||||
let walletLink;
|
||||
if (isWalletPage(currentPage)) {
|
||||
// If they are on a wallet page, the top level link should direct them to the overview page
|
||||
walletLink = '/wallet';
|
||||
} else {
|
||||
// check to see if they've recently been on a wallet sub-link
|
||||
const previousStack = historyStack.slice().reverse();
|
||||
for (let i = 0; i < previousStack.length; i += 1) {
|
||||
const currentStackItem = previousStack[i];
|
||||
|
||||
// Trim off the "/" from the path
|
||||
const pageInStack = currentStackItem.path.slice(1);
|
||||
if (isWalletPage(pageInStack)) {
|
||||
walletLink = currentStackItem.path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const walletSubLinks = [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: '/wallet',
|
||||
active: currentPage === 'wallet',
|
||||
},
|
||||
{
|
||||
label: 'Send & Recieve',
|
||||
path: '/send',
|
||||
active: currentPage === 'send',
|
||||
},
|
||||
{
|
||||
label: 'Get Credits',
|
||||
path: '/getcredits',
|
||||
active: currentPage === 'getcredits',
|
||||
},
|
||||
{
|
||||
label: 'Rewards',
|
||||
path: '/rewards',
|
||||
active: currentPage === 'rewards',
|
||||
},
|
||||
{
|
||||
label: 'My Transactions',
|
||||
path: '/history',
|
||||
active: currentPage === 'history',
|
||||
},
|
||||
];
|
||||
|
||||
const navLinks = {
|
||||
primary: [
|
||||
{
|
||||
label: 'Explore',
|
||||
path: '/discover',
|
||||
active: currentPage === 'discover',
|
||||
icon: 'Compass',
|
||||
},
|
||||
{
|
||||
label: 'Subscriptions',
|
||||
path: '/subscriptions',
|
||||
active: currentPage === 'subscriptions',
|
||||
icon: 'AtSign',
|
||||
},
|
||||
],
|
||||
secondary: [
|
||||
{
|
||||
label: 'Wallet',
|
||||
path: walletLink || '/wallet', // If they've never been to a wallet page, take them to the overview
|
||||
active:
|
||||
currentPage === 'wallet' ||
|
||||
!!walletSubLinks.find(({ path }) => currentPage === path.slice(1)),
|
||||
subLinks: walletSubLinks,
|
||||
icon: 'CreditCard',
|
||||
},
|
||||
{
|
||||
label: 'Publish',
|
||||
path: '/publish',
|
||||
active: currentPage === 'publish',
|
||||
icon: 'UploadCloud',
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
path: '/settings',
|
||||
active: currentPage === 'settings',
|
||||
icon: 'Settings',
|
||||
},
|
||||
{
|
||||
label: 'Backup Wallet',
|
||||
path: '/backup',
|
||||
active: currentPage === 'backup',
|
||||
icon: 'Save',
|
||||
},
|
||||
{
|
||||
label: 'Help',
|
||||
path: '/help',
|
||||
active: currentPage === 'help',
|
||||
icon: 'HelpCircle',
|
||||
},
|
||||
],
|
||||
};
|
||||
return navLinks;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -96,26 +96,6 @@ export const selectGettingNewAddress = createSelector(
|
|||
state => state.gettingNewAddress
|
||||
);
|
||||
|
||||
export const selectDraftTransaction = createSelector(
|
||||
selectState,
|
||||
state => state.draftTransaction || {}
|
||||
);
|
||||
|
||||
export const selectDraftTransactionAmount = createSelector(
|
||||
selectDraftTransaction,
|
||||
draft => draft.amount
|
||||
);
|
||||
|
||||
export const selectDraftTransactionAddress = createSelector(
|
||||
selectDraftTransaction,
|
||||
draft => draft.address
|
||||
);
|
||||
|
||||
export const selectDraftTransactionError = createSelector(
|
||||
selectDraftTransaction,
|
||||
draft => draft.error
|
||||
);
|
||||
|
||||
export const selectBlocks = createSelector(selectState, state => state.blocks);
|
||||
|
||||
export const makeSelectBlockDate = block =>
|
||||
|
|
|
@ -1,18 +1,25 @@
|
|||
// Generic html styles used accross the App
|
||||
// component specific styling should go in the component scss file
|
||||
|
||||
// The actual fonts used will change ex: medium vs regular
|
||||
@font-face {
|
||||
font-family: 'Metropolis';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
text-rendering: optimizeLegibility;
|
||||
src: url('../../../static/font/metropolis/Metropolis-Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Metropolis';
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
text-rendering: optimizeLegibility;
|
||||
src: url('../../../static/font/metropolis/Metropolis-Medium.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Metropolis';
|
||||
font-weight: 600;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
text-rendering: optimizeLegibility;
|
||||
src: url('../../../static/font/metropolis/Metropolis-SemiBold.woff2') format('woff2');
|
||||
|
@ -63,19 +70,6 @@ h5 {
|
|||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
sup,
|
||||
sub {
|
||||
vertical-align: baseline;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.4em;
|
||||
}
|
||||
sub {
|
||||
top: 0.4em;
|
||||
}
|
||||
|
||||
code {
|
||||
font: 0.8em Consolas, 'Lucida Console', 'Source Sans', monospace;
|
||||
background-color: var(--color-bg-alt);
|
||||
|
@ -83,35 +77,61 @@ code {
|
|||
|
||||
// Without this buttons don't have the Metropolis font
|
||||
button {
|
||||
font-weight: inherit;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
#window {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#main-content {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
// don't use {bottom/top} here
|
||||
// they cause flashes of un-rendered content when scrolling
|
||||
margin-top: var(--header-height);
|
||||
// TODO: fix this scrollbar extends beyond screen at the bottom
|
||||
padding-bottom: var(--header-height);
|
||||
input {
|
||||
width: 100%;
|
||||
cursor: text;
|
||||
border-bottom: var(--input-border-size) solid var(--input-border-color);
|
||||
color: var(--input-color);
|
||||
line-height: 1;
|
||||
|
||||
&.input-copyable {
|
||||
background: var(--input-bg);
|
||||
color: var(--input-disabled-color);
|
||||
width: 100%;
|
||||
font-family: 'Consolas', 'Lucida Console', 'Adobe Source Code Pro', monospace;
|
||||
border-bottom: 1px dotted var(--color-divider);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
.page {
|
||||
display: grid;
|
||||
grid-template-rows: var(--header-height) calc(100vh - var(--header-height));
|
||||
grid-template-columns: 170px auto;
|
||||
grid-template-areas:
|
||||
'nav header'
|
||||
'nav content';
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
/*
|
||||
Page content
|
||||
*/
|
||||
.content {
|
||||
grid-area: content;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 0 $spacing-vertical * 2/3;
|
||||
padding: 0 $spacing-vertical $spacing-vertical;
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.main--no-padding {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
margin: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.page__header {
|
||||
|
@ -124,26 +144,66 @@ button {
|
|||
font-size: 3em;
|
||||
}
|
||||
|
||||
.columns {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
> * {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
|
||||
&:not(:first-of-type):not(:last-of-type) {
|
||||
margin: 0 $spacing-vertical / 3;
|
||||
}
|
||||
|
||||
&:first-of-type {
|
||||
margin-right: $spacing-vertical / 3;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
margin-left: $spacing-vertical / 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom text selection */
|
||||
*::selection {
|
||||
background: var(--text-selection-bg);
|
||||
color: var(--text-selection-color);
|
||||
}
|
||||
|
||||
.credit-amount--indicator {
|
||||
font-weight: 500;
|
||||
color: var(--color-money);
|
||||
}
|
||||
|
||||
.credit-amount--fee {
|
||||
font-size: 0.9em;
|
||||
color: var(--color-meta-light);
|
||||
}
|
||||
|
||||
.credit-amount--bold {
|
||||
.credit-amount {
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
font-weight: 700;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
.credit-amount--free {
|
||||
color: var(--color-black);
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.credit-amount--cost {
|
||||
color: var(--color-black);
|
||||
background-color: var(--color-third);
|
||||
}
|
||||
|
||||
.credit-amount--large {
|
||||
font-size: 2em;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
//
|
||||
// .credit-amount--fee {
|
||||
// font-size: 0.9em;
|
||||
// color: var(--color-meta-light);
|
||||
// }
|
||||
//
|
||||
// .credit-amount--bold {
|
||||
// font-weight: 700;
|
||||
// }
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,33 +3,39 @@ Both of these should probably die and become variables as well
|
|||
*/
|
||||
$spacing-vertical: 24px;
|
||||
$width-page-constrained: 800px;
|
||||
$text-color: #000;
|
||||
|
||||
:root {
|
||||
--spacing-vertical: 24px;
|
||||
|
||||
/* Colors */
|
||||
--color-white: #fff;
|
||||
--color-black: #000; // I shouldn't use color names like this
|
||||
--color-grey: #d6d6d6;
|
||||
--color-grey-dark: #8e8e8e;
|
||||
--color-primary: #44b098;
|
||||
--color-primary-dark: #2c6e60;
|
||||
--color-secondary: #6afbda;
|
||||
--color-third: #fbd55e;
|
||||
--color-divider: #e3e3e3;
|
||||
|
||||
--text-color: var(--color-black);
|
||||
--color-brand: #155b4a;
|
||||
--color-primary: #155b4a;
|
||||
--color-primary-light: saturate(lighten(#155b4a, 50%), 20%);
|
||||
--color-light-alt: hsl(hue(#155b4a), 15, 85);
|
||||
--color-dark-overlay: rgba(32, 32, 32, 0.9);
|
||||
// --color-dark-overlay: rgba(32, 32, 32, 0.9);
|
||||
--color-help: rgba(0, 0, 0, 0.54);
|
||||
--color-notice: #8a6d3b;
|
||||
// --color-notice: #8a6d3b;
|
||||
--color-error: #a94442;
|
||||
--color-load-screen-text: #c3c3c3;
|
||||
--color-meta-light: #505050;
|
||||
--color-money: #216c2a;
|
||||
--color-download: rgba(0, 0, 0, 0.75);
|
||||
--color-canvas: #f5f5f5;
|
||||
--color-bg: #ffffff;
|
||||
// --color-load-screen-text: #c3c3c3;
|
||||
// --color-meta-light: #505050;
|
||||
// --color-money: #216c2a;
|
||||
// --color-download: rgba(0, 0, 0, 0.75);
|
||||
// --color-canvas: #f5f5f5;
|
||||
--color-bg: #fafafa;
|
||||
--color-bg-alt: #f6f6f6;
|
||||
--color-placeholder: #ececec;
|
||||
--color-nav-bg: #f6f6f6;
|
||||
|
||||
/* Misc */
|
||||
--content-max-width: 1000px;
|
||||
--nsfw-blur-intensity: 20px;
|
||||
--height-video-embedded: $width-page-constrained * 9 / 16;
|
||||
// --content-max-width: 1000px;
|
||||
// --nsfw-blur-intensity: 20px;
|
||||
// --height-video-embedded: $width-page-constrained * 9 / 16;
|
||||
|
||||
/* Font */
|
||||
--font-size: 16px;
|
||||
|
@ -37,16 +43,10 @@ $text-color: #000;
|
|||
--font-size-subtext-multiple: 0.82;
|
||||
|
||||
/* Shadows */
|
||||
--box-shadow-layer: 0px 1px 3px 0px rgba(0, 0, 0, 0.2);
|
||||
--box-shadow-focus: 2px 4px 4px 0 rgba(0, 0, 0, 0.14), 2px 5px 3px -2px rgba(0, 0, 0, 0.2),
|
||||
2px 3px 7px 0 rgba(0, 0, 0, 0.12);
|
||||
|
||||
/* Transition */
|
||||
--transition-duration: 0.225s;
|
||||
--transition-type: ease;
|
||||
// --box-shadow-layer: 0px 1px 3px 0px rgba(0, 0, 0, 0.2);
|
||||
--box-shadow-layer: 0 4px 9px -2px var(--color-grey);
|
||||
|
||||
/* Text */
|
||||
--text-color: $text-color;
|
||||
--text-help-color: #eee;
|
||||
--text-max-width: 660px;
|
||||
--text-link-padding: 4px;
|
||||
|
@ -58,10 +58,10 @@ $text-color: #000;
|
|||
|
||||
/* Input */
|
||||
--input-bg: transparent;
|
||||
--input-width: 330px;
|
||||
--input-label-color: var(--color-grey-dark);
|
||||
--input-color: var(--text-color);
|
||||
--input-border-size: 2px;
|
||||
--input-border-color: rgba(0, 0, 0, 0.54);
|
||||
--input-border-size: 1px;
|
||||
--input-border-color: var(--color-grey-dark);
|
||||
|
||||
/* input:active */
|
||||
--input-active-bg: transparent;
|
||||
|
@ -81,39 +81,28 @@ $text-color: #000;
|
|||
--select-color: var(--text-color);
|
||||
--select-height: 30px;
|
||||
|
||||
//TODO: determine proper button variables;
|
||||
/* Button */
|
||||
--btn-primary-color: #fff;
|
||||
--button-alt-color: var(--text-color);
|
||||
--btn-primary-bg: var(--color-primary);
|
||||
--btn-alt-bg: red;
|
||||
--btn-radius: 10px;
|
||||
// below needed?
|
||||
--btn-padding: $spacing-vertical * 2/3;
|
||||
--btn-height: $spacing-vertical * 1.5;
|
||||
--btn-intra-margin: $spacing-vertical;
|
||||
--btn-primary-bg: var(--color-primary-dark);
|
||||
--btn-inverse-color: var(--color-primary-dark);
|
||||
--btn-inverse-bg: var(--color-white);
|
||||
--btn-radius: 20px;
|
||||
--btn-height: 40px;
|
||||
|
||||
/* Header */
|
||||
--header-bg: var(--color-bg);
|
||||
--header-color: #666;
|
||||
--header-bg: var(--color-white);
|
||||
--header-color: var(--color-text);
|
||||
--header-active-color: rgba(0, 0, 0, 0.85);
|
||||
--header-height: 65px;
|
||||
--header-height: $spacing-vertical * 3;
|
||||
--header-button-bg: transparent; //var(--button-bg);
|
||||
--header-button-hover-bg: rgba(100, 100, 100, 0.15);
|
||||
|
||||
/* Header -> search */
|
||||
--search-bg: rgba(255, 255, 255, 0.7);
|
||||
--search-border: 1px solid #ccc;
|
||||
--search-color: #666;
|
||||
--search-bg-color: #fff;
|
||||
--search-active-color: var(--header-active-color);
|
||||
--search-active-shadow: 0 0 3px 0px var(--text-selection-bg);
|
||||
|
||||
/* Tabs */
|
||||
--tab-bg: transparent;
|
||||
--tab-color: rgba(0, 0, 0, 0.5);
|
||||
--tab-active-color: var(--color-primary);
|
||||
--tab-border-size: 2px;
|
||||
--tab-border: var(--tab-border-size) solid var(--tab-active-color);
|
||||
--search-active-shadow: 0 6px 9px -2px var(--color-grey--dark);
|
||||
|
||||
/* Table */
|
||||
--table-border: 1px solid #e2e2e2;
|
||||
|
@ -121,13 +110,10 @@ $text-color: #000;
|
|||
--table-item-odd: #f4f4f4;
|
||||
|
||||
/* Card */
|
||||
--card-bg: var(--color-bg);
|
||||
--card-hover-translate: 10px;
|
||||
--card-margin: $spacing-vertical * 2/3;
|
||||
--card-max-width: $width-page-constrained;
|
||||
--card-padding: $spacing-vertical * 2/3;
|
||||
--card-radius: 2px;
|
||||
--card-link-scaling: 1.1;
|
||||
--card-small-width: $spacing-vertical * 10;
|
||||
|
||||
/* Modal */
|
||||
|
@ -136,12 +122,7 @@ $text-color: #000;
|
|||
--modal-overlay-bg: rgba(#f5f5f5, 0.75); // --color-canvas: #F5F5F5
|
||||
--modal-border: 1px solid rgb(204, 204, 204);
|
||||
|
||||
/* Menu */
|
||||
--menu-bg: var(--color-bg);
|
||||
--menu-radius: 2px;
|
||||
--menu-item-hover-bg: var(--color-bg-alt);
|
||||
|
||||
/* Tooltip */
|
||||
// /* Tooltip */
|
||||
--tooltip-width: 300px;
|
||||
--tooltip-bg: var(--color-bg);
|
||||
--tooltip-color: var(--text-color);
|
||||
|
@ -153,10 +134,10 @@ $text-color: #000;
|
|||
--scrollbar-thumb-active-bg: var(--color-primary);
|
||||
--scrollbar-track-bg: transparent;
|
||||
|
||||
/* Divider */
|
||||
--divider: 1px solid rgba(0, 0, 0, 0.12);
|
||||
|
||||
/* Animation :) */
|
||||
--animation-duration: 0.3s;
|
||||
--animation-style: cubic-bezier(0.55, 0, 0.1, 1);
|
||||
// /* Divider */
|
||||
// --divider: 1px solid rgba(0, 0, 0, 0.12);
|
||||
//
|
||||
// /* Animation :) */
|
||||
// --animation-duration: 0.3s;
|
||||
// --animation-style: cubic-bezier(0.55, 0, 0.1, 1);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
@charset "UTF-8";
|
||||
@import '_reset';
|
||||
@import '_vars';
|
||||
@import '_icons';
|
||||
@import '_gui';
|
||||
@import 'component/_table';
|
||||
@import 'component/_button.scss';
|
||||
|
@ -28,4 +27,5 @@
|
|||
@import 'component/_radio.scss';
|
||||
@import 'component/_shapeshift.scss';
|
||||
@import 'component/_spinner.scss';
|
||||
@import 'component/_nav.scss';
|
||||
@import 'page/_show.scss';
|
||||
|
|
|
@ -3,34 +3,35 @@ TODO:
|
|||
Determine [disabled] or .disabled
|
||||
Add <a> support (probably just get rid of button prefix)
|
||||
*/
|
||||
|
||||
button {
|
||||
.btn {
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
button:disabled.btn--disabled {
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
button.btn {
|
||||
font-weight: 600;
|
||||
padding: 10px;
|
||||
margin: 0 5px;
|
||||
height: var(--btn-height);
|
||||
min-width: var(--btn-height);
|
||||
border-radius: var(--btn-radius);
|
||||
color: var(--btn-primary-color);
|
||||
background-color: var(--btn-primary-bg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
fill: currentColor; // for proper icon color
|
||||
|
||||
&:hover:not(.btn--disabled) {
|
||||
&:hover {
|
||||
box-shadow: var(--box-shadow-layer);
|
||||
}
|
||||
|
||||
.icon + .btn__label {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
button.btn.btn--alt {
|
||||
.btn.btn--alt {
|
||||
color: var(--btn-alt-color);
|
||||
background-color: #efefef;
|
||||
background-color: var(--color-white);
|
||||
|
||||
&:hover {
|
||||
color: #111;
|
||||
|
@ -46,30 +47,71 @@ button.btn.btn--alt {
|
|||
}
|
||||
}
|
||||
|
||||
button.btn.btn--circle {
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s;
|
||||
.btn.btn--inverse {
|
||||
background-color: transparent;
|
||||
color: var(--btn-inverse-color);
|
||||
|
||||
&:hover:not([disabled]) {
|
||||
border-radius: var(--btn-radius);
|
||||
&:hover {
|
||||
background-color: var(--btn-inverse-bg);
|
||||
}
|
||||
}
|
||||
|
||||
button.btn.btn--inverse {
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
color: var(--btn-primary-bg);
|
||||
}
|
||||
|
||||
button.btn--link {
|
||||
.btn.btn--link {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: inherit;
|
||||
font-size: 0.9em;
|
||||
color: var(--btn-primary-bg); // this should be a different color
|
||||
font-size: 1em;
|
||||
color: var(--btn-inverse-color);
|
||||
border-radius: 0;
|
||||
display: inline;
|
||||
|
||||
&:hover {
|
||||
border-bottom: 1px solid;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn--no-style {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
border-radius: 0;
|
||||
padding: 5px 0;
|
||||
margin: 0;
|
||||
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.btn.btn--link.btn--no-underline:hover {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.btn--link,
|
||||
.btn--no-style {
|
||||
height: auto;
|
||||
|
||||
.btn__label {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn.btn--disabled:disabled {
|
||||
cursor: default;
|
||||
|
||||
&.btn--primary {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
&.btn--alt {
|
||||
// Come back to me
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
.card {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: var(--card-max-width);
|
||||
border-radius: var(--card-radius);
|
||||
overflow: auto;
|
||||
user-select: text;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card--placeholder {
|
||||
background-color: black;
|
||||
.card--section {
|
||||
flex-direction: column;
|
||||
background-color: var(--color-white);
|
||||
padding: $spacing-vertical;
|
||||
margin-top: $spacing-vertical * 2/3;
|
||||
box-shadow: var(--box-shadow-layer);
|
||||
}
|
||||
|
||||
.card--small {
|
||||
width: var(--card-small-width);
|
||||
min-height: var(--card-small-width);
|
||||
overflow-x: hidden;
|
||||
white-space: normal;
|
||||
}
|
||||
|
@ -48,7 +51,8 @@
|
|||
margin-top: $spacing-vertical * 1/3;
|
||||
}
|
||||
|
||||
// TODO: regular .card__title for show page
|
||||
// TODO: regular .card__title
|
||||
// maybe not needed?
|
||||
.card__title--small {
|
||||
font-weight: 600;
|
||||
font-size: 0.9em;
|
||||
|
@ -60,145 +64,24 @@
|
|||
padding-top: $spacing-vertical * 1/3;
|
||||
}
|
||||
|
||||
// .card__title-primary .meta {
|
||||
// white-space: nowrap;
|
||||
// overflow: hidden;
|
||||
// text-overflow: ellipsis;
|
||||
// }
|
||||
//
|
||||
.card-media__internal-links {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
//
|
||||
// .card__actions {
|
||||
// margin-top: var(--card-margin);
|
||||
// margin-bottom: var(--card-margin);
|
||||
// user-select: none;
|
||||
// }
|
||||
//
|
||||
// .card__actions--bottom {
|
||||
// margin-top: $spacing-vertical * 1/3;
|
||||
// margin-bottom: $spacing-vertical * 1/3;
|
||||
// border-top: var(--divider);
|
||||
// }
|
||||
//
|
||||
// .card__actions--form-submit {
|
||||
// margin-top: $spacing-vertical;
|
||||
// margin-bottom: var(--card-margin);
|
||||
// }
|
||||
//
|
||||
// .card__action--right {
|
||||
// float: right;
|
||||
// }
|
||||
//
|
||||
// .card__content {
|
||||
// margin-top: var(--card-margin);
|
||||
// margin-bottom: var(--card-margin);
|
||||
// table:not(:last-child) {
|
||||
// margin-bottom: var(--card-margin);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// .card__actions--only-vertical {
|
||||
// margin-left: 0;
|
||||
// margin-right: 0;
|
||||
// padding-left: 0;
|
||||
// padding-right: 0;
|
||||
// }
|
||||
//
|
||||
// .card__content--extra-vertical-space {
|
||||
// margin: $spacing-vertical 0;
|
||||
// }
|
||||
//
|
||||
// $font-size-subtext-multiple: 0.82;
|
||||
// .card__subtext {
|
||||
// color: var(--color-meta-light);
|
||||
// font-size: calc(var(--font-size-subtext-multiple) * 1em);
|
||||
// margin-top: $spacing-vertical * 1/3;
|
||||
// margin-bottom: $spacing-vertical * 1/3;
|
||||
// }
|
||||
// .card__subtext--allow-newlines {
|
||||
// white-space: pre-wrap;
|
||||
// }
|
||||
// .card__subtext--two-lines {
|
||||
// height: calc(
|
||||
// var(--font-size) * var(--font-size-subtext-multiple) * var(--font-line-height) * 2
|
||||
// ); /*this is so one line text still has the proper height*/
|
||||
// }
|
||||
// .card-overlay {
|
||||
// position: absolute;
|
||||
// left: 0px;
|
||||
// right: 0px;
|
||||
// top: 0px;
|
||||
// bottom: 0px;
|
||||
// padding: 20px;
|
||||
// background-color: var(--color-dark-overlay);
|
||||
// color: #fff;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// font-weight: 600;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// .card__media--autothumb {
|
||||
// position: relative;
|
||||
// }
|
||||
// .card__media--autothumb.purple {
|
||||
// background-color: #9c27b0;
|
||||
// }
|
||||
// .card__media--autothumb.red {
|
||||
// background-color: #e53935;
|
||||
// }
|
||||
// .card__media--autothumb.pink {
|
||||
// background-color: #e91e63;
|
||||
// }
|
||||
// .card__media--autothumb.indigo {
|
||||
// background-color: #3f51b5;
|
||||
// }
|
||||
// .card__media--autothumb.blue {
|
||||
// background-color: #2196f3;
|
||||
// }
|
||||
// .card__media--autothumb.light-blue {
|
||||
// background-color: #039be5;
|
||||
// }
|
||||
// .card__media--autothumb.cyan {
|
||||
// background-color: #00acc1;
|
||||
// }
|
||||
// .card__media--autothumb.teal {
|
||||
// background-color: #009688;
|
||||
// }
|
||||
// .card__media--autothumb.green {
|
||||
// background-color: #43a047;
|
||||
// }
|
||||
// .card__media--autothumb.yellow {
|
||||
// background-color: #ffeb3b;
|
||||
// }
|
||||
// .card__media--autothumb.orange {
|
||||
// background-color: #ffa726;
|
||||
// }
|
||||
//
|
||||
// .card__media--autothumb .card__autothumb__text {
|
||||
// font-size: 2em;
|
||||
// width: 100%;
|
||||
// color: #ffffff;
|
||||
// text-align: center;
|
||||
// position: absolute;
|
||||
// top: 36%;
|
||||
// }
|
||||
//
|
||||
// .card--form {
|
||||
// width: calc(var(--input-width) + var(--card-padding) * 2);
|
||||
// }
|
||||
//
|
||||
.card__content {
|
||||
margin-top: var(--card-margin);
|
||||
margin-bottom: var(--card-margin);
|
||||
}
|
||||
|
||||
//
|
||||
// .card-series-submit {
|
||||
// margin-left: auto;
|
||||
// margin-right: auto;
|
||||
// max-width: var(--card-max-width);
|
||||
// padding: $spacing-vertical / 2;
|
||||
// }
|
||||
.card__actions {
|
||||
margin-top: var(--card-margin);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/*
|
||||
.card-row is used on the discover page
|
||||
.card-row is used on the discover/subscriptions page
|
||||
It is a list of cards that extend past the right edge of the screen
|
||||
There are left/right arrows to scroll the cards and view hidden content
|
||||
*/
|
||||
|
@ -208,6 +91,14 @@
|
|||
width: 100%;
|
||||
min-width: var(--card-small-width);
|
||||
padding-top: $spacing-vertical;
|
||||
|
||||
&:first-of-type {
|
||||
padding-top: $spacing-vertical * 2/3;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
padding-bottom: $spacing-vertical * 2/3;
|
||||
}
|
||||
}
|
||||
|
||||
.card-row__header {
|
||||
|
@ -225,6 +116,10 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.card-row__scroll-btns {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.card-row__scrollhouse {
|
||||
padding-top: $spacing-vertical * 2/3;
|
||||
overflow: hidden;
|
||||
|
@ -233,26 +128,10 @@
|
|||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-left: $spacing-vertical * 2/3;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.card:last-of-type {
|
||||
padding-right: $spacing-vertical * 2/3;
|
||||
margin-right: $spacing-vertical * 2/3;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if we keep doing things like this, we should add a real grid system, but I'm going to be a selective dick about it - Jeremy
|
||||
*/
|
||||
//TODO: css grid
|
||||
// .card-grid {
|
||||
// $margin-card-grid: $spacing-vertical * 2/3;
|
||||
// display: flex;
|
||||
// flex-wrap: wrap;
|
||||
// > .card {
|
||||
// width: $width-page-constrained / 2 - $margin-card-grid / 2;
|
||||
// flex-grow: 1;
|
||||
// }
|
||||
// > .card:nth-of-type(2n - 1):not(:last-child) {
|
||||
// margin-right: $margin-card-grid;
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -1,195 +1,40 @@
|
|||
.form-row-submit {
|
||||
margin-top: $spacing-vertical;
|
||||
}
|
||||
.form-row-submit--with-footer {
|
||||
margin-bottom: $spacing-vertical;
|
||||
}
|
||||
|
||||
.form-row-phone {
|
||||
.form-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.form-field__input-text {
|
||||
margin-left: 5px;
|
||||
width: calc(0.85 * var(--input-width));
|
||||
.form-field:not(:first-of-type) {
|
||||
padding-left: $spacing-vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.form-row__label-row {
|
||||
margin-top: $spacing-vertical * 5/6;
|
||||
margin-bottom: 0px;
|
||||
line-height: 1;
|
||||
font-size: calc(0.9 * var(--font-size));
|
||||
}
|
||||
.form-row__label-row--prefix {
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.form-row--focus {
|
||||
.form-field__label,
|
||||
.form-field__prefix {
|
||||
color: var(--color-primary) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.form-field {
|
||||
display: inline-block;
|
||||
margin: 8px 0;
|
||||
|
||||
select {
|
||||
transition: outline var(--transition-duration) var(--transition-type);
|
||||
box-sizing: border-box;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
height: var(--select-height);
|
||||
background: var(--select-bg);
|
||||
color: var(--select-color);
|
||||
&:focus {
|
||||
outline: var(--input-border-size) solid var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
input[type='radio'],
|
||||
input[type='checkbox'] {
|
||||
&:checked + .form-field__label {
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
|
||||
input[type='text'].input-copyable {
|
||||
background: var(--input-bg);
|
||||
color: var(--input-disabled-color);
|
||||
line-height: 1;
|
||||
padding-top: $spacing-vertical * 1/3;
|
||||
padding-bottom: $spacing-vertical * 1/3;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
width: 100%;
|
||||
font-family: 'Consolas', 'Lucida Console', 'Adobe Source Code Pro', monospace;
|
||||
|
||||
&.input-copyable--with-copy-btn {
|
||||
width: 85%;
|
||||
}
|
||||
}
|
||||
|
||||
input[readonly] {
|
||||
color: var(--input-disabled-color) !important;
|
||||
border-bottom: 1px dashed var(--input-disabled-border-color) !important;
|
||||
}
|
||||
|
||||
input[readonly]:focus {
|
||||
background: var(--input-bg) !important;
|
||||
border-bottom: 1px dashed var(--input-disabled-border-color) !important;
|
||||
}
|
||||
|
||||
textarea,
|
||||
input[type='text'],
|
||||
input[type='password'],
|
||||
input[type='email'],
|
||||
input[type='number'],
|
||||
input[type='search'],
|
||||
input[type='date'] {
|
||||
background: var(--input-bg);
|
||||
border-bottom: var(--input-border-size) solid var(--input-border-color);
|
||||
caret-color: var(--color-primary);
|
||||
color: var(--input-color);
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
padding: 0 1px 8px 1px;
|
||||
box-sizing: border-box;
|
||||
-webkit-appearance: none;
|
||||
transition: all var(--transition-duration) var(--transition-type);
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
color: var(--input-placeholder-color);
|
||||
opacity: var(--input-placeholder-opacity) !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: var(--color-primary);
|
||||
background: var(--input-active-bg);
|
||||
}
|
||||
|
||||
&:hover:not(:focus) {
|
||||
border-color: var(--input-hover-border-color);
|
||||
}
|
||||
|
||||
&.form-field__input--error {
|
||||
border-color: var(--color-error);
|
||||
}
|
||||
|
||||
&.form-field__input--inline {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom-width: var(--input-border-size);
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
padding: 2px;
|
||||
border: var(--input-border-size) solid var(--input-border-color);
|
||||
}
|
||||
}
|
||||
.form-field--address {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-field--SimpleMDE {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-field__label,
|
||||
.form-row__label {
|
||||
color: var(--form-label-color);
|
||||
&[for] {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.form-row__label-row .form-field__label--error {
|
||||
/*the row restriction is to prevent coloring checkboxes and radio labels*/
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
||||
.form-field__input-text {
|
||||
width: var(--input-width);
|
||||
}
|
||||
|
||||
.form-field__prefix {
|
||||
margin-right: 4px;
|
||||
}
|
||||
.form-field__postfix {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.form-field__input-number {
|
||||
width: 70px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.form-field--textarea {
|
||||
width: 100%;
|
||||
}
|
||||
.form-field__input-textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-field__error,
|
||||
.form-field__helper {
|
||||
margin-top: $spacing-vertical * 1/3;
|
||||
font-size: 0.8em;
|
||||
transition: opacity var(--transition-duration) var(--transition-type);
|
||||
.form-field__wrapper {
|
||||
display: flex;
|
||||
padding: $spacing-vertical / 3 0;
|
||||
}
|
||||
|
||||
.form-field__error {
|
||||
color: var(--color-error);
|
||||
}
|
||||
.form-field__helper {
|
||||
color: var(--color-help);
|
||||
|
||||
.form-field__label {
|
||||
color: var(--color-grey-dark);
|
||||
}
|
||||
|
||||
.form-field__input.form-field__input-SimpleMDE .CodeMirror-scroll {
|
||||
height: auto;
|
||||
.form-field__prefix {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.form-field__postfix {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
// Not sure if I like these
|
||||
// Maybe this should be in gui.scss?
|
||||
.input--lbc-amount {
|
||||
width: 75px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.input--address {
|
||||
width: 370px;
|
||||
}
|
||||
|
|
|
@ -1,60 +1,86 @@
|
|||
#header {
|
||||
.header {
|
||||
grid-area: header;
|
||||
display: flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: var(--header-height);
|
||||
z-index: 3;
|
||||
box-sizing: border-box;
|
||||
color: var(--header-color);
|
||||
background-color: var(--header-bg);
|
||||
}
|
||||
|
||||
.header__actions-left {
|
||||
display: flex;
|
||||
padding: 0 5px;
|
||||
justify-content: space-between;
|
||||
padding: 0 $spacing-vertical;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
.header__actions-right {
|
||||
margin-left: auto;
|
||||
padding-left: $spacing-vertical / 2;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.header__wunderbar {
|
||||
z-index: 1;
|
||||
flex: 1;
|
||||
max-width: 325px;
|
||||
min-width: 175px;
|
||||
overflow: hidden;
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 5px;
|
||||
cursor: text;
|
||||
}
|
||||
position: relative;
|
||||
|
||||
.wunderbar__input {
|
||||
height: 50%;
|
||||
width: 100%;
|
||||
color: var(--search-color);
|
||||
padding: 10px;
|
||||
background-color: #f3f3f3;
|
||||
border-radius: 10px;
|
||||
font-size: 0.9em;
|
||||
|
||||
&:focus {
|
||||
// TODO: focus style
|
||||
.icon {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.wunderbar__input {
|
||||
height: var(--btn-height);
|
||||
border-radius: var(--btn-radius);
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
color: var(--search-color);
|
||||
background-color: var(--search-bg-color);
|
||||
box-shadow: var(--box-shadow-layer);
|
||||
padding: 10px;
|
||||
padding-left: 30px;
|
||||
font-size: 0.9em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: none;
|
||||
|
||||
&:focus {
|
||||
background-color: var(--color-bg);
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid var(--color-grey);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.wunderbar__menu {
|
||||
max-width: 100px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.wunderbar__suggestion {
|
||||
padding: 5px;
|
||||
padding: 10px;
|
||||
background-color: var(--header-bg);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:not(:first-of-type) {
|
||||
border-top: 1px solid var(--color-divider);
|
||||
}
|
||||
}
|
||||
|
||||
.wunderbar__suggestion-label {
|
||||
padding-left: $spacing-vertical;
|
||||
}
|
||||
|
||||
.wunderbar__active-suggestion {
|
||||
background-color: #a3ffb0;
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
|
|
51
src/renderer/scss/component/_nav.scss
Normal file
51
src/renderer/scss/component/_nav.scss
Normal file
|
@ -0,0 +1,51 @@
|
|||
.nav {
|
||||
grid-area: nav;
|
||||
background-color: var(--color-nav-bg);
|
||||
padding-top: 16px;
|
||||
|
||||
hr {
|
||||
width: 40px;
|
||||
border: solid 1px var(--color-grey);
|
||||
margin: $spacing-vertical $spacing-vertical * 2/3 $spacing-vertical * 2;
|
||||
}
|
||||
}
|
||||
|
||||
.nav__actions-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.nav__actions-history {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.nav__primary {
|
||||
padding-top: $spacing-vertical * 3;
|
||||
}
|
||||
|
||||
.nav__link {
|
||||
padding: $spacing-vertical / 3 0 $spacing-vertical / 3 $spacing-vertical * 2/3;
|
||||
font-weight: bold;
|
||||
color: var(--color-grey-dark);
|
||||
|
||||
// The hover effect should be on the li
|
||||
// Need to have the button grow
|
||||
& .btn:hover {
|
||||
color: var(--color-black);
|
||||
}
|
||||
}
|
||||
|
||||
.nav__link--active {
|
||||
color: var(--color-black);
|
||||
}
|
||||
|
||||
.nav__sub {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.nav__sub-link {
|
||||
padding: 5px $spacing-vertical * 2/3;
|
||||
font-size: 0.8em;
|
||||
}
|
|
@ -1,17 +1,3 @@
|
|||
// Can't think of a better way to do this
|
||||
// The initial shapeshift form is 311px tall
|
||||
// the .shapeshift__initial-wrapper class is only added when the form is being loaded
|
||||
// Once the form is rendered, there is a very smooth transition because the height doesn't change
|
||||
.shapeshift__wrapper.shapeshift__initial-wrapper {
|
||||
min-height: 346px;
|
||||
}
|
||||
|
||||
.shapeshift__content {
|
||||
.spinner {
|
||||
margin-top: $spacing-vertical * 3;
|
||||
}
|
||||
}
|
||||
|
||||
.shapeshift__tx-info {
|
||||
min-height: 63px;
|
||||
}
|
||||
|
|
23
src/renderer/util/form-validation.js
Normal file
23
src/renderer/util/form-validation.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
// @flow
|
||||
/* eslint-disable prefer-default-export */
|
||||
import { REGEXP_ADDRESS } from 'lbryURI';
|
||||
|
||||
type DraftTxValues = {
|
||||
address: string,
|
||||
// amount: number
|
||||
}
|
||||
|
||||
export const validateSendTx = (formValues: DraftTxValues) => {
|
||||
const { address } = formValues
|
||||
const errors = {};
|
||||
|
||||
// All we need to check is if the address is valid
|
||||
// If values are missing, users wont' be able to submit the form
|
||||
if (address && !REGEXP_ADDRESS.test(address)) {
|
||||
errors.address = __('Not a valid LBRY address');
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
|
||||
/* eslint-enable prefer-default-export */
|
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load diff
Before Width: | Height: | Size: 434 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -7486,6 +7486,10 @@ react-dom@^16.2.0:
|
|||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
react-feather@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-1.0.8.tgz#69b13d5c729949f194d33201dee91bab67fa31a2"
|
||||
|
||||
react-markdown@^2.5.0:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-2.5.1.tgz#f7a6c26a3a5faf5d4c2098155d9775e826fd56ee"
|
||||
|
|
Loading…
Reference in a new issue