Merge branch 'master' into patch-2
This commit is contained in:
commit
b16d5e87de
55 changed files with 622 additions and 990 deletions
|
@ -40,6 +40,8 @@
|
||||||
"react/require-default-props": 0,
|
"react/require-default-props": 0,
|
||||||
"react/jsx-closing-tag-location": 0,
|
"react/jsx-closing-tag-location": 0,
|
||||||
"jsx-a11y/no-noninteractive-element-to-interactive-role": 0,
|
"jsx-a11y/no-noninteractive-element-to-interactive-role": 0,
|
||||||
"class-methods-use-this": 0
|
"class-methods-use-this": 0,
|
||||||
|
"jsx-a11y/interactive-supports-focus": 0,
|
||||||
|
"jsx-a11y/click-events-have-key-events": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -6,14 +6,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
* Allow typing of encryption password without clicking entry box ([#1977](https://github.com/lbryio/lbry-desktop/pull/1977))
|
* Allow typing of encryption password without clicking entry box ([#1977](https://github.com/lbryio/lbry-desktop/pull/1977))
|
||||||
|
* Focus on search bar with {cmd,ctrl} + "l" ([#2003](https://github.com/lbryio/lbry-desktop/pull/2003))
|
||||||
### Changed
|
### Changed
|
||||||
* Make tooltip smarter ([#1979](https://github.com/lbryio/lbry-desktop/pull/1979))
|
* Make tooltip smarter ([#1979](https://github.com/lbryio/lbry-desktop/pull/1979))
|
||||||
|
* Change channel pages to have 48 items instead of 10 ([#2002](https://github.com/lbryio/lbry-desktop/pull/2002))
|
||||||
|
* Update to https ([#2016](https://github.com/lbryio/lbry-desktop/pull/2016))
|
||||||
|
* Simplify FileCard and FileTile component styling ([#2011](https://github.com/lbryio/lbry-desktop/pull/2011))
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* Invite table cutoff with large number of invites ([#1985](https://github.com/lbryio/lbry-desktop/pull/1985))
|
* Fixed Transactions filter menu collides with transaction table ([#2005](https://github.com/lbryio/lbry-desktop/pull/2005))
|
||||||
* Fixed Transactions filter menu collides with transaction table ([#2005](https://github.com/lbryio/lbry-desktop/pull/2005))
|
* Invite table cutoff with large number of invites ([#1985](https://github.com/lbryio/lbry-desktop/pull/1985))
|
||||||
|
* History styling on large screens and link issue with claims ([#1999](https://github.com/lbryio/lbry-desktop/pull/1999))
|
||||||
|
* Satisfy console warnings in publishForm and validation messaging ([#2010](https://github.com/lbryio/lbry-desktop/pull/2010))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.25.1] - 2018-09-18
|
## [0.25.1] - 2018-09-18
|
||||||
|
|
|
@ -23,12 +23,10 @@
|
||||||
"extract-langs": "node build/extractLocals.js",
|
"extract-langs": "node build/extractLocals.js",
|
||||||
"compile": "electron-webpack && yarn extract-langs",
|
"compile": "electron-webpack && yarn extract-langs",
|
||||||
"build": "yarn compile && electron-builder build",
|
"build": "yarn compile && electron-builder build",
|
||||||
"build:dir": "yarn build -- --dir -c.compression=store -c.mac.identity=null",
|
|
||||||
"dev": "electron-webpack dev",
|
"dev": "electron-webpack dev",
|
||||||
"lint": "eslint 'src/**/*.{js,jsx}' --fix && flow",
|
"lint": "eslint 'src/**/*.{js,jsx}' --fix && flow",
|
||||||
"format": "prettier 'src/**/*.{js,jsx,scss,json}' --write",
|
"format": "prettier 'src/**/*.{js,jsx,scss,json}' --write",
|
||||||
"flow-defs": "flow-typed install",
|
"flow-defs": "flow-typed install",
|
||||||
"release": "yarn compile && electron-builder build",
|
|
||||||
"precommit": "lint-staged",
|
"precommit": "lint-staged",
|
||||||
"preinstall": "yarn cache clean lbry-redux && yarn cache clean lbryinc",
|
"preinstall": "yarn cache clean lbry-redux && yarn cache clean lbryinc",
|
||||||
"postinstall": "electron-builder install-app-deps && node build/downloadDaemon.js"
|
"postinstall": "electron-builder install-app-deps && node build/downloadDaemon.js"
|
||||||
|
@ -50,7 +48,7 @@
|
||||||
"formik": "^0.10.4",
|
"formik": "^0.10.4",
|
||||||
"hast-util-sanitize": "^1.1.2",
|
"hast-util-sanitize": "^1.1.2",
|
||||||
"keytar": "^4.2.1",
|
"keytar": "^4.2.1",
|
||||||
"lbry-redux": "lbryio/lbry-redux#c079b108c3bc4ba2b4fb85fb112b52cfc040c301",
|
"lbry-redux": "lbryio/lbry-redux#4ee6c376e5f2c3e3e96d199a56970e2621a84af1",
|
||||||
"lbryinc": "lbryio/lbryinc#de7ff055605b02a24821f0f9bab1d206eb7f235d",
|
"lbryinc": "lbryio/lbryinc#de7ff055605b02a24821f0f9bab1d206eb7f235d",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
"mammoth": "^1.4.6",
|
"mammoth": "^1.4.6",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import * as icons from 'constants/icons';
|
import * as icons from 'constants/icons';
|
||||||
|
|
||||||
|
@ -7,11 +7,9 @@ let scriptLoading = false;
|
||||||
let scriptLoaded = false;
|
let scriptLoaded = false;
|
||||||
let scriptDidError = false;
|
let scriptDidError = false;
|
||||||
|
|
||||||
class CardVerify extends React.Component {
|
type Props = {
|
||||||
static propTypes = {
|
disabled: boolean,
|
||||||
disabled: PropTypes.bool,
|
label: ?string,
|
||||||
|
|
||||||
label: PropTypes.string,
|
|
||||||
|
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// Required by stripe
|
// Required by stripe
|
||||||
|
@ -21,16 +19,17 @@ class CardVerify extends React.Component {
|
||||||
|
|
||||||
// Your publishable key (test or live).
|
// Your publishable key (test or live).
|
||||||
// can't use "key" as a prop in react, so have to change the keyname
|
// can't use "key" as a prop in react, so have to change the keyname
|
||||||
stripeKey: PropTypes.string.isRequired,
|
stripeKey: string,
|
||||||
|
|
||||||
// The callback to invoke when the Checkout process is complete.
|
// The callback to invoke when the Checkout process is complete.
|
||||||
// function(token)
|
// function(token)
|
||||||
// token is the token object created.
|
// token is the token object created.
|
||||||
// token.id can be used to create a charge or customer.
|
// token.id can be used to create a charge or customer.
|
||||||
// token.email contains the email address entered by the user.
|
// token.email contains the email address entered by the user.
|
||||||
token: PropTypes.func.isRequired,
|
token: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CardVerify extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
|
|
@ -60,32 +60,13 @@ class ChannelTile extends React.PureComponent<Props> {
|
||||||
>
|
>
|
||||||
<CardMedia title={channelName} thumbnail={null} />
|
<CardMedia title={channelName} thumbnail={null} />
|
||||||
<div className="file-tile__info">
|
<div className="file-tile__info">
|
||||||
{isResolvingUri && (
|
{isResolvingUri && <div className="file-tile__title">{__('Loading...')}</div>}
|
||||||
<div
|
|
||||||
className={classnames({
|
|
||||||
'card__title--small': size !== 'large',
|
|
||||||
'card__title--large': size === 'large',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{__('Loading...')}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!isResolvingUri && (
|
{!isResolvingUri && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div
|
<div className="file-tile__title">
|
||||||
className={classnames({
|
|
||||||
'card__title--file': size === 'regular',
|
|
||||||
'card__title--x-small': size === 'small',
|
|
||||||
'card__title--large': size === 'large',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<TruncatedText text={channelName || uri} lines={1} />
|
<TruncatedText text={channelName || uri} lines={1} />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="card__subtitle">
|
||||||
className={classnames('card__subtitle', {
|
|
||||||
'card__subtitle--large': size === 'large',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{totalItems > 0 && (
|
{totalItems > 0 && (
|
||||||
<span>
|
<span>
|
||||||
{totalItems} {totalItems === 1 ? 'file' : 'files'}
|
{totalItems} {totalItems === 1 ? 'file' : 'files'}
|
||||||
|
|
|
@ -14,6 +14,7 @@ type Props = {
|
||||||
icon: string,
|
icon: string,
|
||||||
tooltip?: string, // tooltip direction
|
tooltip?: string, // tooltip direction
|
||||||
iconColor?: string,
|
iconColor?: string,
|
||||||
|
size?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
class IconComponent extends React.PureComponent<Props> {
|
class IconComponent extends React.PureComponent<Props> {
|
||||||
|
@ -42,7 +43,7 @@ class IconComponent extends React.PureComponent<Props> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { icon, tooltip, iconColor } = this.props;
|
const { icon, tooltip, iconColor, size } = this.props;
|
||||||
const Icon = FeatherIcons[icon];
|
const Icon = FeatherIcons[icon];
|
||||||
|
|
||||||
if (!Icon) {
|
if (!Icon) {
|
||||||
|
@ -54,16 +55,17 @@ class IconComponent extends React.PureComponent<Props> {
|
||||||
color = this.getIconColor(iconColor);
|
color = this.getIconColor(iconColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = 14;
|
let iconSize = size || 14;
|
||||||
|
// Arrow icons are quite a bit smaller than the other icons we use
|
||||||
if (icon === icons.ARROW_LEFT || icon === icons.ARROW_RIGHT) {
|
if (icon === icons.ARROW_LEFT || icon === icons.ARROW_RIGHT) {
|
||||||
size = 20;
|
iconSize = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tooltipText;
|
let tooltipText;
|
||||||
if (tooltip) {
|
if (tooltip) {
|
||||||
tooltipText = this.getTooltip(icon);
|
tooltipText = this.getTooltip(icon);
|
||||||
}
|
}
|
||||||
const inner = <Icon size={size} className="icon" color={color} />;
|
const inner = <Icon size={iconSize} className="icon" color={color} />;
|
||||||
|
|
||||||
return tooltipText ? (
|
return tooltipText ? (
|
||||||
<Tooltip icon body={tooltipText} direction={tooltip}>
|
<Tooltip icon body={tooltipText} direction={tooltip}>
|
||||||
|
|
10
src/renderer/component/copyableText/index.js
Normal file
10
src/renderer/component/copyableText/index.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { doNotify } from 'lbry-redux';
|
||||||
|
import CopyableText from './view';
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
doNotify,
|
||||||
|
}
|
||||||
|
)(CopyableText);
|
63
src/renderer/component/copyableText/view.jsx
Normal file
63
src/renderer/component/copyableText/view.jsx
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// @flow
|
||||||
|
import * as React from 'react';
|
||||||
|
import { clipboard } from 'electron';
|
||||||
|
import { FormRow } from 'component/common/form';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import * as icons from 'constants/icons';
|
||||||
|
/*
|
||||||
|
noSnackbar added due to issue 1945
|
||||||
|
https://github.com/lbryio/lbry-desktop/issues/1945
|
||||||
|
"Snackbars and modals can't be displayed at the same time"
|
||||||
|
*/
|
||||||
|
type Props = {
|
||||||
|
copyable: string,
|
||||||
|
noSnackbar: boolean,
|
||||||
|
doNotify: ({ message: string, displayType: Array<string> }) => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class CopyableText extends React.PureComponent<Props> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.input = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
input: ?HTMLInputElement;
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { copyable, doNotify, noSnackbar } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormRow verticallyCentered padded stretch>
|
||||||
|
<input
|
||||||
|
className="input-copyable form-field__input"
|
||||||
|
readOnly
|
||||||
|
value={copyable || ''}
|
||||||
|
ref={input => {
|
||||||
|
this.input = input;
|
||||||
|
}}
|
||||||
|
onFocus={() => {
|
||||||
|
if (this.input) {
|
||||||
|
this.input.select();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
noPadding
|
||||||
|
button="secondary"
|
||||||
|
icon={icons.CLIPBOARD}
|
||||||
|
onClick={() => {
|
||||||
|
clipboard.writeText(copyable);
|
||||||
|
if (!noSnackbar) {
|
||||||
|
doNotify({
|
||||||
|
message: __('Text copied'),
|
||||||
|
displayType: ['snackbar'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -91,8 +91,7 @@ class FileCard extends React.PureComponent<Props> {
|
||||||
onContextMenu={handleContextMenu}
|
onContextMenu={handleContextMenu}
|
||||||
>
|
>
|
||||||
<CardMedia thumbnail={thumbnail} />
|
<CardMedia thumbnail={thumbnail} />
|
||||||
<div className="card__title-identity">
|
<div className="card__title card__title--file-card">
|
||||||
<div className="card__title--small card__title--file-card">
|
|
||||||
<TruncatedText text={title} lines={2} />
|
<TruncatedText text={title} lines={2} />
|
||||||
</div>
|
</div>
|
||||||
<div className="card__subtitle">
|
<div className="card__subtitle">
|
||||||
|
@ -103,7 +102,6 @@ class FileCard extends React.PureComponent<Props> {
|
||||||
{isRewardContent && <Icon iconColor="red" icon={icons.FEATURED} />}
|
{isRewardContent && <Icon iconColor="red" icon={icons.FEATURED} />}
|
||||||
{fileInfo && <Icon icon={icons.LOCAL} />}
|
{fileInfo && <Icon icon={icons.LOCAL} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
/* eslint-enable jsx-a11y/click-events-have-key-events */
|
/* eslint-enable jsx-a11y/click-events-have-key-events */
|
||||||
|
|
|
@ -108,36 +108,14 @@ class FileTile extends React.PureComponent<Props> {
|
||||||
>
|
>
|
||||||
<CardMedia title={title || name} thumbnail={thumbnail} />
|
<CardMedia title={title || name} thumbnail={thumbnail} />
|
||||||
<div className="file-tile__info">
|
<div className="file-tile__info">
|
||||||
{isResolvingUri && (
|
{isResolvingUri && <div className="file-tile__title">{__('Loading...')}</div>}
|
||||||
<div
|
|
||||||
className={classnames({
|
|
||||||
'card__title--small': size !== 'large',
|
|
||||||
'card__title--large': size === 'large',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{__('Loading...')}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!isResolvingUri && (
|
{!isResolvingUri && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div
|
<div className="file-tile__title">
|
||||||
className={classnames({
|
|
||||||
'card__title--file': size === 'regular',
|
|
||||||
'card__title--x-small': size === 'small',
|
|
||||||
'card__title--large': size === 'large',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<TruncatedText text={title || name} lines={size === 'small' ? 2 : 3} />
|
<TruncatedText text={title || name} lines={size === 'small' ? 2 : 3} />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="card__subtitle">
|
||||||
className={classnames('card__subtitle', {
|
|
||||||
'card__subtitle--x-small': size === 'small',
|
|
||||||
'card__subtitle--large': size === 'large',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<span className="file-tile__channel">
|
|
||||||
{showUri ? uri : <UriIndicator uri={uri} link />}
|
{showUri ? uri : <UriIndicator uri={uri} link />}
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="card__file-properties">
|
<div className="card__file-properties">
|
||||||
<FilePrice hideFree uri={uri} />
|
<FilePrice hideFree uri={uri} />
|
||||||
|
@ -145,12 +123,7 @@ class FileTile extends React.PureComponent<Props> {
|
||||||
{showLocal && isDownloaded && <Icon icon={icons.LOCAL} />}
|
{showLocal && isDownloaded && <Icon icon={icons.LOCAL} />}
|
||||||
</div>
|
</div>
|
||||||
{displayDescription && (
|
{displayDescription && (
|
||||||
<div
|
<div className="card__subtext">
|
||||||
className={classnames('card__subtext', {
|
|
||||||
'card__subtext--small': size !== 'small',
|
|
||||||
'card__subtext--large': size === 'large',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<TruncatedText text={description} lines={size === 'large' ? 4 : 3} />
|
<TruncatedText text={description} lines={size === 'large' ? 4 : 3} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import FormField from './view';
|
|
||||||
|
|
||||||
export default connect(null, null, null, { withRef: true })(FormField);
|
|
|
@ -1,200 +0,0 @@
|
||||||
// This file is going to die
|
|
||||||
/* eslint-disable */
|
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import FileSelector from 'component/common/file-selector';
|
|
||||||
import SimpleMDE from 'react-simplemde-editor';
|
|
||||||
import { formFieldNestedLabelTypes, formFieldId } from 'component/common/form';
|
|
||||||
import style from 'react-simplemde-editor/dist/simplemde.min.css';
|
|
||||||
|
|
||||||
const formFieldFileSelectorTypes = ['file', 'directory'];
|
|
||||||
|
|
||||||
class FormField extends React.PureComponent {
|
|
||||||
static propTypes = {
|
|
||||||
type: PropTypes.string.isRequired,
|
|
||||||
prefix: PropTypes.string,
|
|
||||||
postfix: PropTypes.string,
|
|
||||||
hasError: PropTypes.bool,
|
|
||||||
trim: PropTypes.bool,
|
|
||||||
regexp: PropTypes.oneOfType([PropTypes.instanceOf(RegExp), PropTypes.string]),
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
trim: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this._fieldRequiredText = __('This field is required');
|
|
||||||
this._type = null;
|
|
||||||
this._element = null;
|
|
||||||
this._extraElementProps = {};
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isError: null,
|
|
||||||
errorMessage: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
if (['text', 'number', 'radio', 'checkbox'].includes(this.props.type)) {
|
|
||||||
this._element = 'input';
|
|
||||||
this._type = this.props.type;
|
|
||||||
} else if (this.props.type == 'text-number') {
|
|
||||||
this._element = 'input';
|
|
||||||
this._type = 'text';
|
|
||||||
} else if (this.props.type == 'SimpleMDE') {
|
|
||||||
this._element = SimpleMDE;
|
|
||||||
this._type = 'textarea';
|
|
||||||
this._extraElementProps.options = {
|
|
||||||
placeholder: this.props.placeholder,
|
|
||||||
hideIcons: ['heading', 'image', 'fullscreen', 'side-by-side'],
|
|
||||||
};
|
|
||||||
} else if (formFieldFileSelectorTypes.includes(this.props.type)) {
|
|
||||||
this._element = 'input';
|
|
||||||
this._type = 'hidden';
|
|
||||||
} else {
|
|
||||||
// Non <input> field, e.g. <select>, <textarea>
|
|
||||||
this._element = this.props.type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
/**
|
|
||||||
* We have to add the webkitdirectory attribute here because React doesn't allow it in JSX
|
|
||||||
* https://github.com/facebook/react/issues/3468
|
|
||||||
*/
|
|
||||||
if (this.props.type == 'directory') {
|
|
||||||
this.refs.field.webkitdirectory = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleFileChosen(path) {
|
|
||||||
this.refs.field.value = path;
|
|
||||||
if (this.props.onChange) {
|
|
||||||
// Updating inputs programmatically doesn't generate an event, so we have to make our own
|
|
||||||
const event = new Event('change', { bubbles: true });
|
|
||||||
this.refs.field.dispatchEvent(event); // This alone won't generate a React event, but we use it to attach the field as a target
|
|
||||||
this.props.onChange(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showError(text) {
|
|
||||||
this.setState({
|
|
||||||
isError: true,
|
|
||||||
errorMessage: text,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clearError() {
|
|
||||||
this.setState({
|
|
||||||
isError: false,
|
|
||||||
errorMessage: '',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getValue() {
|
|
||||||
if (this.props.type == 'checkbox') {
|
|
||||||
return this.refs.field.checked;
|
|
||||||
} else if (this.props.type == 'SimpleMDE') {
|
|
||||||
return this.refs.field.simplemde.value();
|
|
||||||
}
|
|
||||||
return this.props.trim ? this.refs.field.value.trim() : this.refs.field.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSelectedElement() {
|
|
||||||
return this.refs.field.options[this.refs.field.selectedIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
getOptions() {
|
|
||||||
return this.refs.field.options;
|
|
||||||
}
|
|
||||||
|
|
||||||
validate() {
|
|
||||||
if ('regexp' in this.props) {
|
|
||||||
if (!this.getValue().match(this.props.regexp)) {
|
|
||||||
this.showError(__('Invalid format.'));
|
|
||||||
} else {
|
|
||||||
this.clearError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.props.onBlur && this.props.onBlur();
|
|
||||||
}
|
|
||||||
|
|
||||||
focus() {
|
|
||||||
this.refs.field.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
// Pass all unhandled props to the field element
|
|
||||||
const otherProps = Object.assign({}, this.props),
|
|
||||||
isError = this.state.isError !== null ? this.state.isError : this.props.hasError,
|
|
||||||
elementId = this.props.elementId ? this.props.elementId : formFieldId(),
|
|
||||||
renderElementInsideLabel =
|
|
||||||
this.props.label && formFieldNestedLabelTypes.includes(this.props.type);
|
|
||||||
|
|
||||||
delete otherProps.type;
|
|
||||||
delete otherProps.label;
|
|
||||||
delete otherProps.hasError;
|
|
||||||
delete otherProps.className;
|
|
||||||
delete otherProps.postfix;
|
|
||||||
delete otherProps.prefix;
|
|
||||||
delete otherProps.dispatch;
|
|
||||||
delete otherProps.regexp;
|
|
||||||
delete otherProps.trim;
|
|
||||||
|
|
||||||
const element = (
|
|
||||||
<this._element
|
|
||||||
id={elementId}
|
|
||||||
type={this._type}
|
|
||||||
name={this.props.name}
|
|
||||||
ref="field"
|
|
||||||
placeholder={this.props.placeholder}
|
|
||||||
onBlur={() => this.validate()}
|
|
||||||
onFocus={() => this.props.onFocus && this.props.onFocus()}
|
|
||||||
className={`form-field__input form-field__input-${this.props.type} ${this.props.className ||
|
|
||||||
''}${isError ? 'form-field__input--error' : ''}`}
|
|
||||||
{...otherProps}
|
|
||||||
{...this._extraElementProps}
|
|
||||||
>
|
|
||||||
{this.props.children}
|
|
||||||
</this._element>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`form-field form-field--${this.props.type}`}>
|
|
||||||
{this.props.prefix ? <span className="form-field__prefix">{this.props.prefix}</span> : ''}
|
|
||||||
{element}
|
|
||||||
{renderElementInsideLabel && (
|
|
||||||
<label
|
|
||||||
htmlFor={elementId}
|
|
||||||
className={`form-field__label ${isError ? 'form-field__label--error' : ''}`}
|
|
||||||
>
|
|
||||||
{this.props.label}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
{formFieldFileSelectorTypes.includes(this.props.type) ? (
|
|
||||||
<FileSelector
|
|
||||||
type={this.props.type}
|
|
||||||
onFileChosen={this.handleFileChosen.bind(this)}
|
|
||||||
{...(this.props.defaultValue ? { initPath: this.props.defaultValue } : {})}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{this.props.postfix ? (
|
|
||||||
<span className="form-field__postfix">{this.props.postfix}</span>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
{isError && this.state.errorMessage ? (
|
|
||||||
<div className="form-field__error">{this.state.errorMessage}</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FormField;
|
|
||||||
/* eslint-enable */
|
|
|
@ -5,13 +5,11 @@ import { CC_LICENSES, COPYRIGHT, OTHER, PUBLIC_DOMAIN, NONE } from 'constants/li
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
licenseType: string,
|
licenseType: string,
|
||||||
copyrightNotice: ?string,
|
|
||||||
licenseUrl: ?string,
|
licenseUrl: ?string,
|
||||||
otherLicenseDescription: ?string,
|
otherLicenseDescription: ?string,
|
||||||
handleLicenseChange: (string, string) => void,
|
handleLicenseChange: (string, string) => void,
|
||||||
handleLicenseDescriptionChange: (SyntheticInputEvent<*>) => void,
|
handleLicenseDescriptionChange: (SyntheticInputEvent<*>) => void,
|
||||||
handleLicenseUrlChange: (SyntheticInputEvent<*>) => void,
|
handleLicenseUrlChange: (SyntheticInputEvent<*>) => void,
|
||||||
handleCopyrightNoticeChange: (SyntheticInputEvent<*>) => void,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class LicenseType extends React.PureComponent<Props> {
|
class LicenseType extends React.PureComponent<Props> {
|
||||||
|
@ -38,11 +36,8 @@ class LicenseType extends React.PureComponent<Props> {
|
||||||
licenseType,
|
licenseType,
|
||||||
otherLicenseDescription,
|
otherLicenseDescription,
|
||||||
licenseUrl,
|
licenseUrl,
|
||||||
copyrightNotice,
|
|
||||||
handleLicenseChange,
|
|
||||||
handleLicenseDescriptionChange,
|
handleLicenseDescriptionChange,
|
||||||
handleLicenseUrlChange,
|
handleLicenseUrlChange,
|
||||||
handleCopyrightNoticeChange,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -72,8 +67,8 @@ class LicenseType extends React.PureComponent<Props> {
|
||||||
label={__('Copyright notice')}
|
label={__('Copyright notice')}
|
||||||
type="text"
|
type="text"
|
||||||
name="copyright-notice"
|
name="copyright-notice"
|
||||||
value={copyrightNotice}
|
value={otherLicenseDescription}
|
||||||
onChange={handleCopyrightNoticeChange}
|
onChange={handleLicenseDescriptionChange}
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -37,7 +37,6 @@ type Props = {
|
||||||
name: ?string,
|
name: ?string,
|
||||||
tosAccepted: boolean,
|
tosAccepted: boolean,
|
||||||
updatePublishForm: UpdatePublishFormData => void,
|
updatePublishForm: UpdatePublishFormData => void,
|
||||||
bid: number,
|
|
||||||
nameError: ?string,
|
nameError: ?string,
|
||||||
isResolvingUri: boolean,
|
isResolvingUri: boolean,
|
||||||
winningBidForClaimUri: number,
|
winningBidForClaimUri: number,
|
||||||
|
@ -45,7 +44,6 @@ type Props = {
|
||||||
licenseType: string,
|
licenseType: string,
|
||||||
otherLicenseDescription: ?string,
|
otherLicenseDescription: ?string,
|
||||||
licenseUrl: ?string,
|
licenseUrl: ?string,
|
||||||
copyrightNotice: ?string,
|
|
||||||
uri: ?string,
|
uri: ?string,
|
||||||
bidError: ?string,
|
bidError: ?string,
|
||||||
publishing: boolean,
|
publishing: boolean,
|
||||||
|
@ -200,7 +198,6 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
handlePublish() {
|
handlePublish() {
|
||||||
const {
|
const {
|
||||||
filePath,
|
filePath,
|
||||||
copyrightNotice,
|
|
||||||
licenseType,
|
licenseType,
|
||||||
licenseUrl,
|
licenseUrl,
|
||||||
otherLicenseDescription,
|
otherLicenseDescription,
|
||||||
|
@ -211,8 +208,6 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
let publishingLicense;
|
let publishingLicense;
|
||||||
switch (licenseType) {
|
switch (licenseType) {
|
||||||
case COPYRIGHT:
|
case COPYRIGHT:
|
||||||
publishingLicense = copyrightNotice;
|
|
||||||
break;
|
|
||||||
case OTHER:
|
case OTHER:
|
||||||
publishingLicense = otherLicenseDescription;
|
publishingLicense = otherLicenseDescription;
|
||||||
break;
|
break;
|
||||||
|
@ -233,7 +228,6 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
license: publishingLicense,
|
license: publishingLicense,
|
||||||
licenseUrl: publishingLicenseUrl,
|
licenseUrl: publishingLicenseUrl,
|
||||||
otherLicenseDescription,
|
otherLicenseDescription,
|
||||||
copyrightNotice,
|
|
||||||
name: this.props.name,
|
name: this.props.name,
|
||||||
contentIsFree: this.props.contentIsFree,
|
contentIsFree: this.props.contentIsFree,
|
||||||
price: this.props.price,
|
price: this.props.price,
|
||||||
|
@ -301,7 +295,7 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
{!title && <div>{__('A title is required')}</div>}
|
{!title && <div>{__('A title is required')}</div>}
|
||||||
{!name && <div>{__('A URL is required')}</div>}
|
{!name && <div>{__('A URL is required')}</div>}
|
||||||
{name && nameError && <div>{__('The URL you created is not valid')}</div>}
|
{name && nameError && <div>{__('The URL you created is not valid')}</div>}
|
||||||
{!bid && <div>{__('A bid amount is required')}</div>}
|
{!bid && <div>{__('A deposit amount is required')}</div>}
|
||||||
{!!bid && bidError && <div>{bidError}</div>}
|
{!!bid && bidError && <div>{bidError}</div>}
|
||||||
{uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS && (
|
{uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS && (
|
||||||
<div>{__('Please wait for thumbnail to finish uploading')}</div>
|
<div>{__('Please wait for thumbnail to finish uploading')}</div>
|
||||||
|
@ -339,7 +333,6 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
licenseType,
|
licenseType,
|
||||||
otherLicenseDescription,
|
otherLicenseDescription,
|
||||||
licenseUrl,
|
licenseUrl,
|
||||||
copyrightNotice,
|
|
||||||
uri,
|
uri,
|
||||||
bidError,
|
bidError,
|
||||||
publishing,
|
publishing,
|
||||||
|
@ -535,7 +528,7 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
step="any"
|
step="any"
|
||||||
label={__('Deposit')}
|
label={__('Deposit')}
|
||||||
postfix="LBC"
|
postfix="LBC"
|
||||||
value={bid}
|
value={bid || ''}
|
||||||
error={bidError}
|
error={bidError}
|
||||||
min="0"
|
min="0"
|
||||||
disabled={!name}
|
disabled={!name}
|
||||||
|
@ -585,7 +578,6 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
licenseType={licenseType}
|
licenseType={licenseType}
|
||||||
otherLicenseDescription={otherLicenseDescription}
|
otherLicenseDescription={otherLicenseDescription}
|
||||||
licenseUrl={licenseUrl}
|
licenseUrl={licenseUrl}
|
||||||
copyrightNotice={copyrightNotice}
|
|
||||||
handleLicenseChange={(newLicenseType, newLicenseUrl) =>
|
handleLicenseChange={(newLicenseType, newLicenseUrl) =>
|
||||||
updatePublishForm({
|
updatePublishForm({
|
||||||
licenseType: newLicenseType,
|
licenseType: newLicenseType,
|
||||||
|
@ -600,9 +592,6 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
handleLicenseUrlChange={event =>
|
handleLicenseUrlChange={event =>
|
||||||
updatePublishForm({ licenseUrl: event.target.value })
|
updatePublishForm({ licenseUrl: event.target.value })
|
||||||
}
|
}
|
||||||
handleCopyrightNoticeChange={event =>
|
|
||||||
updatePublishForm({ copyrightNotice: event.target.value })
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectUnclaimedRewardValue, selectFetchingRewards, doRewardList } from 'lbryinc';
|
import {
|
||||||
import { doFetchRewardedContent } from 'redux/actions/content';
|
selectUnclaimedRewardValue,
|
||||||
|
selectFetchingRewards,
|
||||||
|
doRewardList,
|
||||||
|
doFetchRewardedContent,
|
||||||
|
} from 'lbryinc';
|
||||||
import RewardSummary from './view';
|
import RewardSummary from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
|
|
@ -89,7 +89,7 @@ class SelectThumbnail extends React.PureComponent<Props, State> {
|
||||||
type="text"
|
type="text"
|
||||||
name="content_thumbnail"
|
name="content_thumbnail"
|
||||||
label="URL"
|
label="URL"
|
||||||
placeholder="http://spee.ch/mylogo"
|
placeholder="https://spee.ch/mylogo"
|
||||||
value={thumbnail}
|
value={thumbnail}
|
||||||
disabled={formDisabled}
|
disabled={formDisabled}
|
||||||
onChange={this.handleThumbnailChange}
|
onChange={this.handleThumbnailChange}
|
||||||
|
|
|
@ -101,7 +101,7 @@ class ActiveShapeShift extends React.PureComponent<Props> {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{shiftState === statuses.RECEIVED && (
|
{shiftState === statuses.RECEIVED && (
|
||||||
<div className="card__content--extra-vertical-space">
|
<div>
|
||||||
<p>
|
<p>
|
||||||
{__('ShapeShift has received your payment! Sending the funds to your LBRY wallet.')}
|
{__('ShapeShift has received your payment! Sending the funds to your LBRY wallet.')}
|
||||||
</p>
|
</p>
|
||||||
|
@ -110,7 +110,7 @@ class ActiveShapeShift extends React.PureComponent<Props> {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{shiftState === statuses.COMPLETE && (
|
{shiftState === statuses.COMPLETE && (
|
||||||
<div className="card__content--extra-vertical-space">
|
<div>
|
||||||
<p>{__('Transaction complete! You should see the new LBC in your wallet.')}</p>
|
<p>{__('Transaction complete! You should see the new LBC in your wallet.')}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -3,12 +3,13 @@ import React from 'react';
|
||||||
import type { Claim } from 'types/claim';
|
import type { Claim } from 'types/claim';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import * as icons from 'constants/icons';
|
import * as icons from 'constants/icons';
|
||||||
import Tooltip from 'component/common/tooltip';
|
import CopyableText from 'component/copyableText';
|
||||||
import Address from 'component/address';
|
import ToolTip from 'component/common/tooltip';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
claim: Claim,
|
claim: Claim,
|
||||||
onDone: () => void,
|
onDone: () => void,
|
||||||
|
speechShareable: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
class SocialShare extends React.PureComponent<Props> {
|
class SocialShare extends React.PureComponent<Props> {
|
||||||
|
@ -27,20 +28,27 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
channel_name: channelName,
|
channel_name: channelName,
|
||||||
value,
|
value,
|
||||||
} = this.props.claim;
|
} = this.props.claim;
|
||||||
|
const { speechShareable, onDone } = this.props;
|
||||||
const channelClaimId =
|
const channelClaimId =
|
||||||
value && value.publisherSignature && value.publisherSignature.certificateId;
|
value && value.publisherSignature && value.publisherSignature.certificateId;
|
||||||
const { onDone } = this.props;
|
const speechPrefix = 'https://spee.ch/';
|
||||||
const speechPrefix = 'http://spee.ch/';
|
const lbryPrefix = 'https://open.lbry.io/';
|
||||||
|
|
||||||
const speechURL =
|
const speechURL =
|
||||||
channelName && channelClaimId
|
channelName && channelClaimId
|
||||||
? `${speechPrefix}${channelName}:${channelClaimId}/${claimName}`
|
? `${speechPrefix}${channelName}:${channelClaimId}/${claimName}`
|
||||||
: `${speechPrefix}${claimName}#${claimId}`;
|
: `${speechPrefix}${claimName}#${claimId}`;
|
||||||
|
|
||||||
|
const lbryURL = `${lbryPrefix}${claimName}#${claimId}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="card__content">
|
<section className="card__content">
|
||||||
<Address address={speechURL} noSnackbar />
|
{speechShareable && (
|
||||||
|
<div className="card__content">
|
||||||
|
<label className="card__subtitle">{__('Web link')}</label>
|
||||||
|
<CopyableText copyable={speechURL} noSnackbar />
|
||||||
<div className="card__actions card__actions--center">
|
<div className="card__actions card__actions--center">
|
||||||
<Tooltip onComponent body={__('Facebook')}>
|
<ToolTip onComponent body={__('Facebook')}>
|
||||||
<Button
|
<Button
|
||||||
iconColor="blue"
|
iconColor="blue"
|
||||||
icon={icons.FACEBOOK}
|
icon={icons.FACEBOOK}
|
||||||
|
@ -48,8 +56,8 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
label={__('')}
|
label={__('')}
|
||||||
href={`https://facebook.com/sharer/sharer.php?u=${speechURL}`}
|
href={`https://facebook.com/sharer/sharer.php?u=${speechURL}`}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</ToolTip>
|
||||||
<Tooltip onComponent body={__('Twitter')}>
|
<ToolTip onComponent body={__('Twitter')}>
|
||||||
<Button
|
<Button
|
||||||
iconColor="blue"
|
iconColor="blue"
|
||||||
icon={icons.TWITTER}
|
icon={icons.TWITTER}
|
||||||
|
@ -57,8 +65,8 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
label={__('')}
|
label={__('')}
|
||||||
href={`https://twitter.com/home?status=${speechURL}`}
|
href={`https://twitter.com/home?status=${speechURL}`}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</ToolTip>
|
||||||
<Tooltip onComponent body={__('View on Spee.ch')}>
|
<ToolTip onComponent body={__('View on Spee.ch')}>
|
||||||
<Button
|
<Button
|
||||||
icon={icons.GLOBE}
|
icon={icons.GLOBE}
|
||||||
iconColor="blue"
|
iconColor="blue"
|
||||||
|
@ -66,7 +74,33 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
label={__('')}
|
label={__('')}
|
||||||
href={`${speechURL}`}
|
href={`${speechURL}`}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</ToolTip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="card__content">
|
||||||
|
<label className="card__subtitle">{__('LBRY App link')}</label>
|
||||||
|
<CopyableText copyable={lbryURL} noSnackbar />
|
||||||
|
<div className="card__actions card__actions--center">
|
||||||
|
<ToolTip onComponent body={__('Facebook')}>
|
||||||
|
<Button
|
||||||
|
iconColor="blue"
|
||||||
|
icon={icons.FACEBOOK}
|
||||||
|
button="alt"
|
||||||
|
label={__('')}
|
||||||
|
href={`https://facebook.com/sharer/sharer.php?u=${lbryURL}`}
|
||||||
|
/>
|
||||||
|
</ToolTip>
|
||||||
|
<ToolTip onComponent body={__('Twitter')}>
|
||||||
|
<Button
|
||||||
|
iconColor="blue"
|
||||||
|
icon={icons.TWITTER}
|
||||||
|
button="alt"
|
||||||
|
label={__('')}
|
||||||
|
href={`https://twitter.com/home?status=${lbryURL}`}
|
||||||
|
/>
|
||||||
|
</ToolTip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
<Button button="link" label={__('Done')} onClick={onDone} />
|
<Button button="link" label={__('Done')} onClick={onDone} />
|
||||||
|
|
|
@ -24,6 +24,10 @@ class TransactionListRecent extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<div className="card__title">{__('Recent Transactions')}</div>
|
<div className="card__title">{__('Recent Transactions')}</div>
|
||||||
|
<div className="card__subtitle">
|
||||||
|
{__('To view all of your transactions, navigate to the')}{' '}
|
||||||
|
<Button button="link" navigate="/history" label={__('transactions page')} />.
|
||||||
|
</div>
|
||||||
{fetchingTransactions && (
|
{fetchingTransactions && (
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<BusyIndicator message={__('Loading transactions')} />
|
<BusyIndicator message={__('Loading transactions')} />
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import TruncatedMarkdown from './view';
|
|
||||||
|
|
||||||
export default connect()(TruncatedMarkdown);
|
|
|
@ -1,38 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import ReactMarkdown from 'react-markdown';
|
|
||||||
import ReactDOMServer from 'react-dom/server';
|
|
||||||
|
|
||||||
class TruncatedMarkdown extends React.PureComponent {
|
|
||||||
static propTypes = {
|
|
||||||
lines: PropTypes.number,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
lines: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
transformMarkdown(text) {
|
|
||||||
// render markdown to html string then trim html tag
|
|
||||||
const htmlString = ReactDOMServer.renderToStaticMarkup(
|
|
||||||
<ReactMarkdown source={this.props.children} />
|
|
||||||
);
|
|
||||||
const txt = document.createElement('textarea');
|
|
||||||
txt.innerHTML = htmlString;
|
|
||||||
return txt.value.replace(/<(?:.|\n)*?>/gm, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const content =
|
|
||||||
this.props.children && typeof this.props.children === 'string'
|
|
||||||
? this.transformMarkdown(this.props.children)
|
|
||||||
: this.props.children;
|
|
||||||
return (
|
|
||||||
<span className="truncated-text" style={{ WebkitLineClamp: this.props.lines }}>
|
|
||||||
{content}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TruncatedMarkdown;
|
|
|
@ -2,7 +2,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import { buildURI } from 'lbry-redux';
|
import { buildURI } from 'lbry-redux';
|
||||||
import classnames from 'classnames';
|
|
||||||
import type { Claim } from 'types/claim';
|
import type { Claim } from 'types/claim';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -59,17 +58,7 @@ class UriIndicator extends React.PureComponent<Props> {
|
||||||
channelLink = link ? buildURI({ channelName, claimId: channelClaimId }) : false;
|
channelLink = link ? buildURI({ channelName, claimId: channelClaimId }) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const inner = (
|
const inner = <span className="channel-name">{channelName}</span>;
|
||||||
<span>
|
|
||||||
<span
|
|
||||||
className={classnames('channel-name', {
|
|
||||||
'button-text no-underline': link,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{channelName}
|
|
||||||
</span>{' '}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!channelLink) {
|
if (!channelLink) {
|
||||||
return inner;
|
return inner;
|
||||||
|
|
|
@ -99,7 +99,8 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
|
||||||
|
|
||||||
const allSelected = Object.keys(itemsSelected).length === history.length;
|
const allSelected = Object.keys(itemsSelected).length === history.length;
|
||||||
const selectHandler = allSelected ? this.unselectAll : this.selectAll;
|
const selectHandler = allSelected ? this.unselectAll : this.selectAll;
|
||||||
return (
|
|
||||||
|
return history.length ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className="card__actions card__actions--between">
|
<div className="card__actions card__actions--between">
|
||||||
{Object.keys(itemsSelected).length ? (
|
{Object.keys(itemsSelected).length ? (
|
||||||
|
@ -109,7 +110,6 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
|
||||||
{/* Using an empty span so spacing stays the same if the button isn't rendered */}
|
{/* Using an empty span so spacing stays the same if the button isn't rendered */}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
button="link"
|
button="link"
|
||||||
label={allSelected ? __('Cancel') : __('Select All')}
|
label={allSelected ? __('Cancel') : __('Select All')}
|
||||||
|
@ -117,8 +117,7 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!!history.length && (
|
{!!history.length && (
|
||||||
<table className="card--section table table--stretch table--history">
|
<section className="item-list">
|
||||||
<tbody>
|
|
||||||
{history.map(item => (
|
{history.map(item => (
|
||||||
<UserHistoryItem
|
<UserHistoryItem
|
||||||
key={item.uri}
|
key={item.uri}
|
||||||
|
@ -130,8 +129,7 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</section>
|
||||||
</table>
|
|
||||||
)}
|
)}
|
||||||
{pageCount > 1 && (
|
{pageCount > 1 && (
|
||||||
<FormRow padded verticallyCentered centered>
|
<FormRow padded verticallyCentered centered>
|
||||||
|
@ -161,6 +159,13 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
|
||||||
</FormRow>
|
</FormRow>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<div className="page__empty">
|
||||||
|
{__("You don't have anything saved in history yet, go check out some content on LBRY!")}
|
||||||
|
<div className="card__actions card__actions--center">
|
||||||
|
<Button button="primary" navigate="/discover" label={__('Explore new content')} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,18 +36,16 @@ class UserHistoryItem extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr
|
<div
|
||||||
|
role="button"
|
||||||
onClick={onSelect}
|
onClick={onSelect}
|
||||||
className={classnames({
|
className={classnames('item-list__item', {
|
||||||
history__selected: selected,
|
'item-list__item--selected': selected,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<td>
|
|
||||||
<input checked={selected} type="checkbox" onClick={onSelect} />
|
<input checked={selected} type="checkbox" onClick={onSelect} />
|
||||||
</td>
|
<span className="time time--ago">{moment(lastViewed).from(moment())}</span>
|
||||||
<td>{moment(lastViewed).from(moment())}</td>
|
<span className="item-list__item--cutoff">{title}</span>
|
||||||
<td>{title}</td>
|
|
||||||
<td>
|
|
||||||
<Button
|
<Button
|
||||||
tourniquet
|
tourniquet
|
||||||
button="link"
|
button="link"
|
||||||
|
@ -55,8 +53,7 @@ class UserHistoryItem extends React.PureComponent<Props> {
|
||||||
navigate="/show"
|
navigate="/show"
|
||||||
navigateParams={{ uri }}
|
navigateParams={{ uri }}
|
||||||
/>
|
/>
|
||||||
</td>
|
</div>
|
||||||
</tr>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import CardVerify from 'component/cardVerify';
|
import CardVerify from 'component/cardVerify';
|
||||||
import Lbryio from 'lbryinc';
|
import { Lbryio } from 'lbryinc';
|
||||||
import * as icons from 'constants/icons';
|
import * as icons from 'constants/icons';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
|
@ -19,7 +19,7 @@ export default (props: Props) => {
|
||||||
icon={icons.GLOBE}
|
icon={icons.GLOBE}
|
||||||
button="alt"
|
button="alt"
|
||||||
label={__('Share')}
|
label={__('Share')}
|
||||||
href={`http://spee.ch/${speechURL}`}
|
href={`https://spee.ch/${speechURL}`}
|
||||||
/>
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,6 @@ https://github.com/reactjs/react-autocomplete/issues/239
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const PropTypes = require('prop-types');
|
|
||||||
const { findDOMNode } = require('react-dom');
|
const { findDOMNode } = require('react-dom');
|
||||||
const scrollIntoView = require('dom-scroll-into-view');
|
const scrollIntoView = require('dom-scroll-into-view');
|
||||||
|
|
||||||
|
@ -45,129 +44,129 @@ function getScrollOffset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Autocomplete extends React.Component {
|
export default class Autocomplete extends React.Component {
|
||||||
static propTypes = {
|
// static propTypes = {
|
||||||
/**
|
// /**
|
||||||
* The items to display in the dropdown menu
|
// * The items to display in the dropdown menu
|
||||||
*/
|
// */
|
||||||
items: PropTypes.array.isRequired,
|
// items: PropTypes.array.isRequired,
|
||||||
/**
|
// /**
|
||||||
* The value to display in the input field
|
// * The value to display in the input field
|
||||||
*/
|
// */
|
||||||
value: PropTypes.any,
|
// value: PropTypes.any,
|
||||||
/**
|
// /**
|
||||||
* Arguments: `event: Event, value: String`
|
// * Arguments: `event: Event, value: String`
|
||||||
*
|
// *
|
||||||
* Invoked every time the user changes the input's value.
|
// * Invoked every time the user changes the input's value.
|
||||||
*/
|
// */
|
||||||
onChange: PropTypes.func,
|
// onChange: PropTypes.func,
|
||||||
/**
|
// /**
|
||||||
* Arguments: `value: String, item: Any`
|
// * Arguments: `value: String, item: Any`
|
||||||
*
|
// *
|
||||||
* Invoked when the user selects an item from the dropdown menu.
|
// * Invoked when the user selects an item from the dropdown menu.
|
||||||
*/
|
// */
|
||||||
onSelect: PropTypes.func,
|
// onSelect: PropTypes.func,
|
||||||
/**
|
// /**
|
||||||
* Arguments: `item: Any, value: String`
|
// * Arguments: `item: Any, value: String`
|
||||||
*
|
// *
|
||||||
* Invoked for each entry in `items` and its return value is used to
|
// * Invoked for each entry in `items` and its return value is used to
|
||||||
* determine whether or not it should be displayed in the dropdown menu.
|
// * determine whether or not it should be displayed in the dropdown menu.
|
||||||
* By default all items are always rendered.
|
// * By default all items are always rendered.
|
||||||
*/
|
// */
|
||||||
shouldItemRender: PropTypes.func,
|
// shouldItemRender: PropTypes.func,
|
||||||
/**
|
// /**
|
||||||
* Arguments: `itemA: Any, itemB: Any, value: String`
|
// * Arguments: `itemA: Any, itemB: Any, value: String`
|
||||||
*
|
// *
|
||||||
* The function which is used to sort `items` before display.
|
// * The function which is used to sort `items` before display.
|
||||||
*/
|
// */
|
||||||
sortItems: PropTypes.func,
|
// sortItems: PropTypes.func,
|
||||||
/**
|
// /**
|
||||||
* Arguments: `item: Any`
|
// * Arguments: `item: Any`
|
||||||
*
|
// *
|
||||||
* Used to read the display value from each entry in `items`.
|
// * Used to read the display value from each entry in `items`.
|
||||||
*/
|
// */
|
||||||
getItemValue: PropTypes.func.isRequired,
|
// getItemValue: PropTypes.func.isRequired,
|
||||||
/**
|
// /**
|
||||||
* Arguments: `item: Any, isHighlighted: Boolean, styles: Object`
|
// * Arguments: `item: Any, isHighlighted: Boolean, styles: Object`
|
||||||
*
|
// *
|
||||||
* Invoked for each entry in `items` that also passes `shouldItemRender` to
|
// * Invoked for each entry in `items` that also passes `shouldItemRender` to
|
||||||
* generate the render tree for each item in the dropdown menu. `styles` is
|
// * generate the render tree for each item in the dropdown menu. `styles` is
|
||||||
* an optional set of styles that can be applied to improve the look/feel
|
// * an optional set of styles that can be applied to improve the look/feel
|
||||||
* of the items in the dropdown menu.
|
// * of the items in the dropdown menu.
|
||||||
*/
|
// */
|
||||||
renderItem: PropTypes.func.isRequired,
|
// renderItem: PropTypes.func.isRequired,
|
||||||
/**
|
// /**
|
||||||
* Arguments: `items: Array<Any>, value: String, styles: Object`
|
// * Arguments: `items: Array<Any>, value: String, styles: Object`
|
||||||
*
|
// *
|
||||||
* Invoked to generate the render tree for the dropdown menu. Ensure the
|
// * Invoked to generate the render tree for the dropdown menu. Ensure the
|
||||||
* returned tree includes every entry in `items` or else the highlight order
|
// * returned tree includes every entry in `items` or else the highlight order
|
||||||
* and keyboard navigation logic will break. `styles` will contain
|
// * and keyboard navigation logic will break. `styles` will contain
|
||||||
* { top, left, minWidth } which are the coordinates of the top-left corner
|
// * { top, left, minWidth } which are the coordinates of the top-left corner
|
||||||
* and the width of the dropdown menu.
|
// * and the width of the dropdown menu.
|
||||||
*/
|
// */
|
||||||
renderMenu: PropTypes.func,
|
// renderMenu: PropTypes.func,
|
||||||
/**
|
// /**
|
||||||
* Styles that are applied to the dropdown menu in the default `renderMenu`
|
// * Styles that are applied to the dropdown menu in the default `renderMenu`
|
||||||
* implementation. If you override `renderMenu` and you want to use
|
// * implementation. If you override `renderMenu` and you want to use
|
||||||
* `menuStyle` you must manually apply them (`this.props.menuStyle`).
|
// * `menuStyle` you must manually apply them (`this.props.menuStyle`).
|
||||||
*/
|
// */
|
||||||
menuStyle: PropTypes.object,
|
// menuStyle: PropTypes.object,
|
||||||
/**
|
// /**
|
||||||
* Arguments: `props: Object`
|
// * Arguments: `props: Object`
|
||||||
*
|
// *
|
||||||
* Invoked to generate the input element. The `props` argument is the result
|
// * Invoked to generate the input element. The `props` argument is the result
|
||||||
* of merging `props.inputProps` with a selection of props that are required
|
// * of merging `props.inputProps` with a selection of props that are required
|
||||||
* both for functionality and accessibility. At the very least you need to
|
// * both for functionality and accessibility. At the very least you need to
|
||||||
* apply `props.ref` and all `props.on<event>` event handlers. Failing to do
|
// * apply `props.ref` and all `props.on<event>` event handlers. Failing to do
|
||||||
* this will cause `Autocomplete` to behave unexpectedly.
|
// * this will cause `Autocomplete` to behave unexpectedly.
|
||||||
*/
|
// */
|
||||||
renderInput: PropTypes.func,
|
// renderInput: PropTypes.func,
|
||||||
/**
|
// /**
|
||||||
* Props passed to `props.renderInput`. By default these props will be
|
// * Props passed to `props.renderInput`. By default these props will be
|
||||||
* applied to the `<input />` element rendered by `Autocomplete`, unless you
|
// * applied to the `<input />` element rendered by `Autocomplete`, unless you
|
||||||
* have specified a custom value for `props.renderInput`. Any properties
|
// * have specified a custom value for `props.renderInput`. Any properties
|
||||||
* supported by `HTMLInputElement` can be specified, apart from the
|
// * supported by `HTMLInputElement` can be specified, apart from the
|
||||||
* following which are set by `Autocomplete`: value, autoComplete, role,
|
// * following which are set by `Autocomplete`: value, autoComplete, role,
|
||||||
* aria-autocomplete. `inputProps` is commonly used for (but not limited to)
|
// * aria-autocomplete. `inputProps` is commonly used for (but not limited to)
|
||||||
* placeholder, event handlers (onFocus, onBlur, etc.), autoFocus, etc..
|
// * placeholder, event handlers (onFocus, onBlur, etc.), autoFocus, etc..
|
||||||
*/
|
// */
|
||||||
inputProps: PropTypes.object,
|
// inputProps: PropTypes.object,
|
||||||
/**
|
// /**
|
||||||
* Props that are applied to the element which wraps the `<input />` and
|
// * Props that are applied to the element which wraps the `<input />` and
|
||||||
* dropdown menu elements rendered by `Autocomplete`.
|
// * dropdown menu elements rendered by `Autocomplete`.
|
||||||
*/
|
// */
|
||||||
wrapperProps: PropTypes.object,
|
// wrapperProps: PropTypes.object,
|
||||||
/**
|
// /**
|
||||||
* This is a shorthand for `wrapperProps={{ style: <your styles> }}`.
|
// * This is a shorthand for `wrapperProps={{ style: <your styles> }}`.
|
||||||
* Note that `wrapperStyle` is applied before `wrapperProps`, so the latter
|
// * Note that `wrapperStyle` is applied before `wrapperProps`, so the latter
|
||||||
* will win if it contains a `style` entry.
|
// * will win if it contains a `style` entry.
|
||||||
*/
|
// */
|
||||||
wrapperStyle: PropTypes.object,
|
// wrapperStyle: PropTypes.object,
|
||||||
/**
|
// /**
|
||||||
* Whether or not to automatically highlight the top match in the dropdown
|
// * Whether or not to automatically highlight the top match in the dropdown
|
||||||
* menu.
|
// * menu.
|
||||||
*/
|
// */
|
||||||
autoHighlight: PropTypes.bool,
|
// autoHighlight: PropTypes.bool,
|
||||||
/**
|
// /**
|
||||||
* Whether or not to automatically select the highlighted item when the
|
// * Whether or not to automatically select the highlighted item when the
|
||||||
* `<input>` loses focus.
|
// * `<input>` loses focus.
|
||||||
*/
|
// */
|
||||||
selectOnBlur: PropTypes.bool,
|
// selectOnBlur: PropTypes.bool,
|
||||||
/**
|
// /**
|
||||||
* Arguments: `isOpen: Boolean`
|
// * Arguments: `isOpen: Boolean`
|
||||||
*
|
// *
|
||||||
* Invoked every time the dropdown menu's visibility changes (i.e. every
|
// * Invoked every time the dropdown menu's visibility changes (i.e. every
|
||||||
* time it is displayed/hidden).
|
// * time it is displayed/hidden).
|
||||||
*/
|
// */
|
||||||
onMenuVisibilityChange: PropTypes.func,
|
// onMenuVisibilityChange: PropTypes.func,
|
||||||
/**
|
// /**
|
||||||
* Used to override the internal logic which displays/hides the dropdown
|
// * Used to override the internal logic which displays/hides the dropdown
|
||||||
* menu. This is useful if you want to force a certain state based on your
|
// * menu. This is useful if you want to force a certain state based on your
|
||||||
* UX/business logic. Use it together with `onMenuVisibilityChange` for
|
// * UX/business logic. Use it together with `onMenuVisibilityChange` for
|
||||||
* fine-grained control over the dropdown menu dynamics.
|
// * fine-grained control over the dropdown menu dynamics.
|
||||||
*/
|
// */
|
||||||
open: PropTypes.bool,
|
// open: PropTypes.bool,
|
||||||
debug: PropTypes.bool,
|
// debug: PropTypes.bool,
|
||||||
};
|
// };
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
value: '',
|
value: '',
|
||||||
|
|
|
@ -7,24 +7,36 @@ import { parseQueryParams } from 'util/query_params';
|
||||||
import * as icons from 'constants/icons';
|
import * as icons from 'constants/icons';
|
||||||
import Autocomplete from './internal/autocomplete';
|
import Autocomplete from './internal/autocomplete';
|
||||||
|
|
||||||
|
const L_KEY_CODE = 76;
|
||||||
|
const ESC_KEY_CODE = 27;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
updateSearchQuery: string => void,
|
updateSearchQuery: string => void,
|
||||||
onSearch: string => void,
|
onSearch: (string, ?number) => void,
|
||||||
onSubmit: (string, {}) => void,
|
onSubmit: (string, {}) => void,
|
||||||
wunderbarValue: ?string,
|
wunderbarValue: ?string,
|
||||||
suggestions: Array<string>,
|
suggestions: Array<string>,
|
||||||
doFocus: () => void,
|
doFocus: () => void,
|
||||||
doBlur: () => void,
|
doBlur: () => void,
|
||||||
resultCount: number,
|
resultCount: number,
|
||||||
|
focused: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
class WunderBar extends React.PureComponent<Props> {
|
class WunderBar extends React.PureComponent<Props> {
|
||||||
constructor(props: Props) {
|
constructor() {
|
||||||
super(props);
|
super();
|
||||||
|
|
||||||
(this: any).handleSubmit = this.handleSubmit.bind(this);
|
(this: any).handleSubmit = this.handleSubmit.bind(this);
|
||||||
(this: any).handleChange = this.handleChange.bind(this);
|
(this: any).handleChange = this.handleChange.bind(this);
|
||||||
this.input = undefined;
|
(this: any).handleKeyDown = this.handleKeyDown.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
window.addEventListener('keydown', this.handleKeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('keydown', this.handleKeyDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSuggestionIcon = (type: string) => {
|
getSuggestionIcon = (type: string) => {
|
||||||
|
@ -38,6 +50,31 @@ class WunderBar extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleKeyDown(event: SyntheticKeyboardEvent<*>) {
|
||||||
|
const { ctrlKey, metaKey, keyCode } = event;
|
||||||
|
const { doFocus, doBlur, focused } = this.props;
|
||||||
|
|
||||||
|
if (!this.input) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (focused && keyCode === ESC_KEY_CODE) {
|
||||||
|
doBlur();
|
||||||
|
this.input.blur();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldFocus =
|
||||||
|
process.platform === 'darwin'
|
||||||
|
? keyCode === L_KEY_CODE && metaKey
|
||||||
|
: keyCode === L_KEY_CODE && ctrlKey;
|
||||||
|
|
||||||
|
if (shouldFocus) {
|
||||||
|
doFocus();
|
||||||
|
this.input.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleChange(e: SyntheticInputEvent<*>) {
|
handleChange(e: SyntheticInputEvent<*>) {
|
||||||
const { updateSearchQuery } = this.props;
|
const { updateSearchQuery } = this.props;
|
||||||
const { value } = e.target;
|
const { value } = e.target;
|
||||||
|
@ -106,6 +143,10 @@ class WunderBar extends React.PureComponent<Props> {
|
||||||
renderInput={props => (
|
renderInput={props => (
|
||||||
<input
|
<input
|
||||||
{...props}
|
{...props}
|
||||||
|
ref={el => {
|
||||||
|
props.ref(el);
|
||||||
|
this.input = el;
|
||||||
|
}}
|
||||||
className="wunderbar__input"
|
className="wunderbar__input"
|
||||||
placeholder="Enter LBRY URL here or search for videos, music, games and more"
|
placeholder="Enter LBRY URL here or search for videos, music, games and more"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -6,14 +6,15 @@ import SocialShare from 'component/socialShare';
|
||||||
type Props = {
|
type Props = {
|
||||||
closeModal: () => void,
|
closeModal: () => void,
|
||||||
uri: string,
|
uri: string,
|
||||||
|
speechShareable: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModalSocialShare extends React.PureComponent<Props> {
|
class ModalSocialShare extends React.PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { closeModal, uri } = this.props;
|
const { closeModal, uri, speechShareable } = this.props;
|
||||||
return (
|
return (
|
||||||
<Modal isOpen onAborted={closeModal} type="custom" title={__('Share')}>
|
<Modal isOpen onAborted={closeModal} type="custom" title={__('Share')}>
|
||||||
<SocialShare uri={uri} onDone={closeModal} />
|
<SocialShare uri={uri} onDone={closeModal} speechShareable={speechShareable} />
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,17 +80,17 @@ class ChannelPage extends React.PureComponent<Props> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page notContained>
|
<Page notContained>
|
||||||
<section className="card__channel-info card__channel-info--large">
|
<section>
|
||||||
<h1>
|
<h1>
|
||||||
{name}
|
{name}
|
||||||
{fetching && <BusyIndicator />}
|
{fetching && <BusyIndicator />}
|
||||||
</h1>
|
</h1>
|
||||||
<div className="card__actions card__actions--no-margin">
|
</section>
|
||||||
|
<div className="card__actions">
|
||||||
<SubscribeButton uri={permanentUrl} channelName={name} />
|
<SubscribeButton uri={permanentUrl} channelName={name} />
|
||||||
<ViewOnWebButton claimId={claimId} claimName={name} />
|
<ViewOnWebButton claimId={claimId} claimName={name} />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
<section className="card__content">{contentList}</section>
|
||||||
<section>{contentList}</section>
|
|
||||||
{(!fetching || (claimsInChannel && claimsInChannel.length)) &&
|
{(!fetching || (claimsInChannel && claimsInChannel.length)) &&
|
||||||
totalPages > 1 && (
|
totalPages > 1 && (
|
||||||
<FormRow verticallyCentered centered>
|
<FormRow verticallyCentered centered>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doFetchFeaturedUris, doFetchRewardedContent } from 'redux/actions/content';
|
import { selectFeaturedUris, selectFetchingFeaturedUris, doFetchFeaturedUris } from 'lbry-redux';
|
||||||
import { selectFeaturedUris, selectFetchingFeaturedUris } from 'redux/selectors/content';
|
import { doFetchRewardedContent } from 'lbryinc';
|
||||||
import DiscoverPage from './view';
|
import DiscoverPage from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
|
|
@ -145,12 +145,11 @@ class FilePage extends React.Component<Props> {
|
||||||
if (channelName && channelClaimId) {
|
if (channelName && channelClaimId) {
|
||||||
subscriptionUri = buildURI({ channelName, claimId: channelClaimId }, false);
|
subscriptionUri = buildURI({ channelName, claimId: channelClaimId }, false);
|
||||||
}
|
}
|
||||||
const speechSharable =
|
const speechShareable =
|
||||||
costInfo &&
|
costInfo &&
|
||||||
costInfo.cost === 0 &&
|
costInfo.cost === 0 &&
|
||||||
contentType &&
|
contentType &&
|
||||||
['video', 'image'].includes(contentType.split('/')[0]);
|
['video', 'image'].includes(contentType.split('/')[0]);
|
||||||
|
|
||||||
// We want to use the short form uri for editing
|
// We want to use the short form uri for editing
|
||||||
// This is what the user is used to seeing, they don't care about the claim id
|
// This is what the user is used to seeing, they don't care about the claim id
|
||||||
// We will select the claim id before they publish
|
// We will select the claim id before they publish
|
||||||
|
@ -184,21 +183,20 @@ class FilePage extends React.Component<Props> {
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<div className="card__title-identity--file">
|
<div className="card__title__space-between">
|
||||||
<h1 className="card__title card__title--file">{title}</h1>
|
<h1>{title}</h1>
|
||||||
<div className="card__title-identity-icons">
|
<div className="card__title-identity-icons">
|
||||||
{isRewardContent && <Icon iconColor="red" tooltip="bottom" icon={icons.FEATURED} />}
|
{isRewardContent && (
|
||||||
|
<Icon size={20} iconColor="red" tooltip="bottom" icon={icons.FEATURED} />
|
||||||
|
)}
|
||||||
<FilePrice filePage uri={normalizeURI(uri)} />
|
<FilePrice filePage uri={normalizeURI(uri)} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="card__subtitle card__subtitle--file">
|
<span className="card__subtitle">
|
||||||
{__('Published on')}
|
<UriIndicator uri={uri} link /> {__('published on')}{' '}
|
||||||
<DateTime block={height} show={DateTime.SHOW_DATE} />
|
<DateTime block={height} show={DateTime.SHOW_DATE} />
|
||||||
</span>
|
</span>
|
||||||
{metadata.nsfw && <div>NSFW</div>}
|
{metadata.nsfw && <div>NSFW</div>}
|
||||||
<div className="card__channel-info">
|
|
||||||
<UriIndicator uri={uri} link />
|
|
||||||
</div>
|
|
||||||
<div className="card__actions card__actions--no-margin card__actions--between">
|
<div className="card__actions card__actions--no-margin card__actions--between">
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
{claimIsMine ? (
|
{claimIsMine ? (
|
||||||
|
@ -222,7 +220,7 @@ class FilePage extends React.Component<Props> {
|
||||||
onClick={() => openModal({ id: MODALS.SEND_TIP }, { uri })}
|
onClick={() => openModal({ id: MODALS.SEND_TIP }, { uri })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{speechSharable && (
|
{speechShareable && (
|
||||||
<Button
|
<Button
|
||||||
button="alt"
|
button="alt"
|
||||||
icon={icons.GLOBE}
|
icon={icons.GLOBE}
|
||||||
|
@ -237,7 +235,7 @@ class FilePage extends React.Component<Props> {
|
||||||
<FileActions uri={uri} claimId={claim.claim_id} />
|
<FileActions uri={uri} claimId={claim.claim_id} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<FormRow padded>
|
<FormRow>
|
||||||
<ToolTip direction="right" body={__('Automatically download and play free content.')}>
|
<ToolTip direction="right" body={__('Automatically download and play free content.')}>
|
||||||
<FormField
|
<FormField
|
||||||
name="autoplay"
|
name="autoplay"
|
||||||
|
@ -248,7 +246,7 @@ class FilePage extends React.Component<Props> {
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<div className="card__content--extra-padding">
|
<div className="card__content">
|
||||||
<FileDetails uri={uri} />
|
<FileDetails uri={uri} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -199,7 +199,7 @@ class HelpPage extends React.PureComponent<Props, State> {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{this.state.uiVersion && ver ? (
|
{this.state.uiVersion && ver ? (
|
||||||
<table className="table table--stretch table--help">
|
<table className="card__content table table--stretch table--help">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{__('App')}</td>
|
<td>{__('App')}</td>
|
||||||
|
|
|
@ -42,8 +42,8 @@ class ShowPage extends React.PureComponent<Props> {
|
||||||
if ((isResolvingUri && !claim) || !claim) {
|
if ((isResolvingUri && !claim) || !claim) {
|
||||||
const { claimName } = parseURI(uri);
|
const { claimName } = parseURI(uri);
|
||||||
innerContent = (
|
innerContent = (
|
||||||
<Page>
|
<Page notContained>
|
||||||
<section className="card">
|
<section>
|
||||||
<h1>{claimName}</h1>
|
<h1>{claimName}</h1>
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
{isResolvingUri && <BusyIndicator message={__('Loading decentralized data...')} />}
|
{isResolvingUri && <BusyIndicator message={__('Loading decentralized data...')} />}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
doHideNotification,
|
doHideNotification,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import Native from 'native';
|
import Native from 'native';
|
||||||
import { doFetchRewardedContent } from 'redux/actions/content';
|
|
||||||
import { doFetchDaemonSettings } from 'redux/actions/settings';
|
import { doFetchDaemonSettings } from 'redux/actions/settings';
|
||||||
import { doAuthNavigate } from 'redux/actions/navigation';
|
import { doAuthNavigate } from 'redux/actions/navigation';
|
||||||
import { doCheckSubscriptionsInit } from 'redux/actions/subscriptions';
|
import { doCheckSubscriptionsInit } from 'redux/actions/subscriptions';
|
||||||
|
@ -27,7 +26,7 @@ import {
|
||||||
selectRemoteVersion,
|
selectRemoteVersion,
|
||||||
selectUpgradeTimer,
|
selectUpgradeTimer,
|
||||||
} from 'redux/selectors/app';
|
} from 'redux/selectors/app';
|
||||||
import { doAuthenticate } from 'lbryinc';
|
import { doAuthenticate, doFetchRewardedContent } from 'lbryinc';
|
||||||
import { lbrySettings as config, version as appVersion } from 'package.json';
|
import { lbrySettings as config, version as appVersion } from 'package.json';
|
||||||
|
|
||||||
const { autoUpdater } = remote.require('electron-updater');
|
const { autoUpdater } = remote.require('electron-updater');
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { doNavigate } from 'redux/actions/navigation';
|
||||||
import {
|
import {
|
||||||
setSubscriptionLatest,
|
setSubscriptionLatest,
|
||||||
setSubscriptionNotification,
|
setSubscriptionNotification,
|
||||||
setSubscriptionNotifications,
|
|
||||||
} from 'redux/actions/subscriptions';
|
} from 'redux/actions/subscriptions';
|
||||||
import { selectNotifications } from 'redux/selectors/subscriptions';
|
import { selectNotifications } from 'redux/selectors/subscriptions';
|
||||||
import { selectBadgeNumber } from 'redux/selectors/app';
|
import { selectBadgeNumber } from 'redux/selectors/app';
|
||||||
|
@ -16,8 +15,6 @@ import {
|
||||||
Lbry,
|
Lbry,
|
||||||
Lbryapi,
|
Lbryapi,
|
||||||
buildURI,
|
buildURI,
|
||||||
batchActions,
|
|
||||||
doResolveUris,
|
|
||||||
doFetchClaimListMine,
|
doFetchClaimListMine,
|
||||||
makeSelectCostInfoForUri,
|
makeSelectCostInfoForUri,
|
||||||
makeSelectFileInfoForUri,
|
makeSelectFileInfoForUri,
|
||||||
|
@ -31,74 +28,9 @@ import { makeSelectClientSetting, selectosNotificationsEnabled } from 'redux/sel
|
||||||
import setBadge from 'util/setBadge';
|
import setBadge from 'util/setBadge';
|
||||||
import setProgressBar from 'util/setProgressBar';
|
import setProgressBar from 'util/setProgressBar';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import { Lbryio } from 'lbryinc';
|
|
||||||
|
|
||||||
const DOWNLOAD_POLL_INTERVAL = 250;
|
const DOWNLOAD_POLL_INTERVAL = 250;
|
||||||
|
|
||||||
export function doFetchFeaturedUris() {
|
|
||||||
return dispatch => {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.FETCH_FEATURED_CONTENT_STARTED,
|
|
||||||
});
|
|
||||||
|
|
||||||
const success = ({ Uris }) => {
|
|
||||||
const urisToResolve = Object.keys(Uris).reduce(
|
|
||||||
(resolve, category) => [...resolve, ...Uris[category]],
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const actions = [
|
|
||||||
doResolveUris(urisToResolve),
|
|
||||||
{
|
|
||||||
type: ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED,
|
|
||||||
data: {
|
|
||||||
uris: Uris,
|
|
||||||
success: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
dispatch(batchActions(...actions));
|
|
||||||
};
|
|
||||||
|
|
||||||
const failure = () => {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED,
|
|
||||||
data: {
|
|
||||||
uris: {},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Lbryio.call('file', 'list_homepage').then(success, failure);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function doFetchRewardedContent() {
|
|
||||||
return dispatch => {
|
|
||||||
const success = nameToClaimId => {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.FETCH_REWARD_CONTENT_COMPLETED,
|
|
||||||
data: {
|
|
||||||
claimIds: Object.values(nameToClaimId),
|
|
||||||
success: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const failure = () => {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.FETCH_REWARD_CONTENT_COMPLETED,
|
|
||||||
data: {
|
|
||||||
claimIds: [],
|
|
||||||
success: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Lbryio.call('reward', 'list_featured').then(success, failure);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function doUpdateLoadStatus(uri, outpoint) {
|
export function doUpdateLoadStatus(uri, outpoint) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
Lbry.file_list({
|
Lbry.file_list({
|
||||||
|
@ -360,13 +292,13 @@ export function doPurchaseUri(uri, specificCostInfo, shouldRecordViewEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doFetchClaimsByChannel(uri, page) {
|
export function doFetchClaimsByChannel(uri, page) {
|
||||||
return (dispatch, getState) => {
|
return dispatch => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED,
|
type: ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED,
|
||||||
data: { uri, page },
|
data: { uri, page },
|
||||||
});
|
});
|
||||||
|
|
||||||
Lbry.claim_list_by_channel({ uri, page: page || 1 }).then(result => {
|
Lbry.claim_list_by_channel({ uri, page: page || 1, page_size: 48 }).then(result => {
|
||||||
const claimResult = result[uri] || {};
|
const claimResult = result[uri] || {};
|
||||||
const { claims_in_channel: claimsInChannel, returned_page: returnedPage } = claimResult;
|
const { claims_in_channel: claimsInChannel, returned_page: returnedPage } = claimResult;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,14 @@ export function doNavigate(path, params = {}, options = {}) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensure uri always has "lbry://" prefix
|
||||||
|
const navigationParams = params;
|
||||||
|
if (path === '/show') {
|
||||||
|
if (navigationParams.uri && !navigationParams.uri.startsWith('lbry://')) {
|
||||||
|
navigationParams.uri = `lbry://${navigationParams.uri}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let url = path;
|
let url = path;
|
||||||
if (params && Object.values(params).length) {
|
if (params && Object.values(params).length) {
|
||||||
url += `?${toQueryString(params)}`;
|
url += `?${toQueryString(params)}`;
|
||||||
|
|
|
@ -18,25 +18,13 @@ import { selectosNotificationsEnabled } from 'redux/selectors/settings';
|
||||||
import { doNavigate } from 'redux/actions/navigation';
|
import { doNavigate } from 'redux/actions/navigation';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { CC_LICENSES, COPYRIGHT, OTHER } from 'constants/licenses';
|
||||||
|
|
||||||
type Action = UpdatePublishFormAction | { type: ACTIONS.CLEAR_PUBLISH };
|
type Action = UpdatePublishFormAction | { type: ACTIONS.CLEAR_PUBLISH };
|
||||||
type PromiseAction = Promise<Action>;
|
type PromiseAction = Promise<Action>;
|
||||||
type Dispatch = (action: Action | PromiseAction | Array<Action>) => any;
|
type Dispatch = (action: Action | PromiseAction | Array<Action>) => any;
|
||||||
type GetState = () => {};
|
type GetState = () => {};
|
||||||
|
|
||||||
export const doClearPublish = () => (dispatch: Dispatch): PromiseAction => {
|
|
||||||
dispatch({ type: ACTIONS.CLEAR_PUBLISH });
|
|
||||||
return dispatch(doResetThumbnailStatus());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const doUpdatePublishForm = (publishFormValue: UpdatePublishFormData) => (
|
|
||||||
dispatch: Dispatch
|
|
||||||
): UpdatePublishFormAction =>
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.UPDATE_PUBLISH_FORM,
|
|
||||||
data: { ...publishFormValue },
|
|
||||||
});
|
|
||||||
|
|
||||||
export const doResetThumbnailStatus = () => (dispatch: Dispatch): PromiseAction => {
|
export const doResetThumbnailStatus = () => (dispatch: Dispatch): PromiseAction => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.UPDATE_PUBLISH_FORM,
|
type: ACTIONS.UPDATE_PUBLISH_FORM,
|
||||||
|
@ -73,6 +61,19 @@ export const doResetThumbnailStatus = () => (dispatch: Dispatch): PromiseAction
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const doClearPublish = () => (dispatch: Dispatch): PromiseAction => {
|
||||||
|
dispatch({ type: ACTIONS.CLEAR_PUBLISH });
|
||||||
|
return dispatch(doResetThumbnailStatus());
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doUpdatePublishForm = (publishFormValue: UpdatePublishFormData) => (
|
||||||
|
dispatch: Dispatch
|
||||||
|
): UpdatePublishFormAction =>
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.UPDATE_PUBLISH_FORM,
|
||||||
|
data: { ...publishFormValue },
|
||||||
|
});
|
||||||
|
|
||||||
export const doUploadThumbnail = (filePath: string, nsfw: boolean) => (dispatch: Dispatch) => {
|
export const doUploadThumbnail = (filePath: string, nsfw: boolean) => (dispatch: Dispatch) => {
|
||||||
const thumbnail = fs.readFileSync(filePath);
|
const thumbnail = fs.readFileSync(filePath);
|
||||||
const fileExt = path.extname(filePath);
|
const fileExt = path.extname(filePath);
|
||||||
|
@ -164,15 +165,27 @@ export const doPrepareEdit = (claim: any, uri: string) => (dispatch: Dispatch) =
|
||||||
description,
|
description,
|
||||||
fee,
|
fee,
|
||||||
language,
|
language,
|
||||||
licenseType: license,
|
|
||||||
licenseUrl,
|
|
||||||
nsfw,
|
nsfw,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
title,
|
title,
|
||||||
uri,
|
uri,
|
||||||
uploadThumbnailStatus: thumbnail ? THUMBNAIL_STATUSES.MANUAL : undefined,
|
uploadThumbnailStatus: thumbnail ? THUMBNAIL_STATUSES.MANUAL : undefined,
|
||||||
|
licenseUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Make sure custom liscence's are mapped properly
|
||||||
|
if (!CC_LICENSES.some(({ value }) => value === license)) {
|
||||||
|
if (!licenseUrl) {
|
||||||
|
publishData.licenseType = COPYRIGHT;
|
||||||
|
} else {
|
||||||
|
publishData.licenseType = OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
publishData.otherLicenseDescription = license;
|
||||||
|
} else {
|
||||||
|
publishData.licenseType = license;
|
||||||
|
}
|
||||||
|
|
||||||
dispatch({ type: ACTIONS.DO_PREPARE_EDIT, data: publishData });
|
dispatch({ type: ACTIONS.DO_PREPARE_EDIT, data: publishData });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ type PublishState = {
|
||||||
bidError: ?string,
|
bidError: ?string,
|
||||||
otherLicenseDescription: string,
|
otherLicenseDescription: string,
|
||||||
licenseUrl: string,
|
licenseUrl: string,
|
||||||
copyrightNotice: string,
|
|
||||||
pendingPublishes: Array<any>,
|
pendingPublishes: Array<any>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,7 +53,6 @@ export type UpdatePublishFormData = {
|
||||||
bidError?: string,
|
bidError?: string,
|
||||||
otherLicenseDescription?: string,
|
otherLicenseDescription?: string,
|
||||||
licenseUrl?: string,
|
licenseUrl?: string,
|
||||||
copyrightNotice?: string,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdatePublishFormAction = {
|
export type UpdatePublishFormAction = {
|
||||||
|
@ -114,9 +112,8 @@ const defaultState: PublishState = {
|
||||||
bid: 0.1,
|
bid: 0.1,
|
||||||
bidError: undefined,
|
bidError: undefined,
|
||||||
licenseType: 'None',
|
licenseType: 'None',
|
||||||
otherLicenseDescription: '',
|
otherLicenseDescription: 'All rights reserved',
|
||||||
licenseUrl: '',
|
licenseUrl: '',
|
||||||
copyrightNotice: 'All rights reserved',
|
|
||||||
publishing: false,
|
publishing: false,
|
||||||
publishSuccess: false,
|
publishSuccess: false,
|
||||||
publishError: undefined,
|
publishError: undefined,
|
||||||
|
|
|
@ -4,13 +4,6 @@ import { HISTORY_ITEMS_PER_PAGE } from 'constants/content';
|
||||||
|
|
||||||
export const selectState = state => state.content || {};
|
export const selectState = state => state.content || {};
|
||||||
|
|
||||||
export const selectFeaturedUris = createSelector(selectState, state => state.featuredUris);
|
|
||||||
|
|
||||||
export const selectFetchingFeaturedUris = createSelector(
|
|
||||||
selectState,
|
|
||||||
state => state.fetchingFeaturedContent
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectPlayingUri = createSelector(selectState, state => state.playingUri);
|
export const selectPlayingUri = createSelector(selectState, state => state.playingUri);
|
||||||
|
|
||||||
export const selectChannelClaimCounts = createSelector(
|
export const selectChannelClaimCounts = createSelector(
|
||||||
|
|
|
@ -39,7 +39,7 @@ html {
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'metropolis-semibold';
|
font-family: 'metropolis-medium';
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
@ -74,7 +74,6 @@ input {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
cursor: text;
|
cursor: text;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
|
|
||||||
&[type='radio'],
|
&[type='radio'],
|
||||||
&[type='checkbox'],
|
&[type='checkbox'],
|
||||||
|
@ -92,6 +91,7 @@ input {
|
||||||
color: var(--input-copyable-color);
|
color: var(--input-copyable-color);
|
||||||
padding: 10px 16px;
|
padding: 10px 16px;
|
||||||
border: 1px dashed var(--input-copyable-border);
|
border: 1px dashed var(--input-copyable-border);
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.input-copyable):not(.wunderbar__input):not(:placeholder-shown):not(:disabled) {
|
&:not(.input-copyable):not(.wunderbar__input):not(:placeholder-shown):not(:disabled) {
|
||||||
|
@ -109,7 +109,6 @@ input {
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
border: 1px solid var(--color-divider);
|
border: 1px solid var(--color-divider);
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -152,8 +151,6 @@ dd {
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
|
|
||||||
&:not(:first-of-type) {
|
&:not(:first-of-type) {
|
||||||
margin-top: $spacing-vertical * 1/3;
|
margin-top: $spacing-vertical * 1/3;
|
||||||
}
|
}
|
||||||
|
@ -227,7 +224,6 @@ p {
|
||||||
.page__empty {
|
.page__empty {
|
||||||
margin-top: 200px;
|
margin-top: 200px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -262,7 +258,7 @@ p {
|
||||||
}
|
}
|
||||||
|
|
||||||
.credit-amount {
|
.credit-amount {
|
||||||
font-size: 10px;
|
font-size: 0.9em;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +296,6 @@ p {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
font-weight: inherit;
|
font-weight: inherit;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,10 +328,6 @@ p {
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
.busy-indicator {
|
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
}
|
|
||||||
|
|
||||||
.busy-indicator__loader {
|
.busy-indicator__loader {
|
||||||
background: url('../../../static/img/busy.gif') no-repeat center center;
|
background: url('../../../static/img/busy.gif') no-repeat center center;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -355,7 +346,6 @@ p {
|
||||||
|
|
||||||
.help {
|
.help {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
color: var(--color-help);
|
color: var(--color-help);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,7 +354,6 @@ p {
|
||||||
}
|
}
|
||||||
|
|
||||||
.meta {
|
.meta {
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
color: var(--color-meta-light);
|
color: var(--color-meta-light);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,8 @@ $large-breakpoint: 1921px;
|
||||||
--color-search-placeholder: var(--color-placeholder);
|
--color-search-placeholder: var(--color-placeholder);
|
||||||
--color-credit-free: var(--color-dark-blue);
|
--color-credit-free: var(--color-dark-blue);
|
||||||
--color-credit-price: var(--card-text-color);
|
--color-credit-price: var(--card-text-color);
|
||||||
|
--color-text-black: #444;
|
||||||
|
--color-text-white: #efefef;
|
||||||
|
|
||||||
/* Shadows */
|
/* Shadows */
|
||||||
--box-shadow-layer: transparent; // 0 2px 4px rgba(0,0,0,0.25);
|
--box-shadow-layer: transparent; // 0 2px 4px rgba(0,0,0,0.25);
|
||||||
|
@ -60,8 +62,8 @@ $large-breakpoint: 1921px;
|
||||||
--box-shadow-header: 0px 6px 20px 1px rgba(0, 0, 0, 0.05);
|
--box-shadow-header: 0px 6px 20px 1px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
/* Text */
|
/* Text */
|
||||||
--text-color: var(--color-black);
|
--text-color: var(--color-text-black);
|
||||||
--text-color-inverse: var(--color-white);
|
--text-color-inverse: var(--color-text-white);
|
||||||
--text-help-color: var(--color-help);
|
--text-help-color: var(--color-help);
|
||||||
--text-max-width: 660px;
|
--text-max-width: 660px;
|
||||||
--text-link-padding: 4px;
|
--text-link-padding: 4px;
|
||||||
|
|
|
@ -29,3 +29,6 @@
|
||||||
@import 'component/_toggle.scss';
|
@import 'component/_toggle.scss';
|
||||||
@import 'component/_search.scss';
|
@import 'component/_search.scss';
|
||||||
@import 'component/_dat-gui.scss';
|
@import 'component/_dat-gui.scss';
|
||||||
|
@import 'component/_item-list.scss';
|
||||||
|
@import 'component/_time.scss';
|
||||||
|
@import 'component/_icon.scss';
|
||||||
|
|
|
@ -19,7 +19,6 @@ button:disabled {
|
||||||
fill: currentColor; // for proper icon color
|
fill: currentColor; // for proper icon color
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
transition: all var(--animation-duration) var(--animation-style);
|
transition: all var(--animation-duration) var(--animation-style);
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
|
|
||||||
&:not(:disabled) {
|
&:not(:disabled) {
|
||||||
box-shadow: var(--box-shadow-button);
|
box-shadow: var(--box-shadow-button);
|
||||||
|
@ -161,6 +160,7 @@ button:disabled {
|
||||||
|
|
||||||
.btn--uri-indicator {
|
.btn--uri-indicator {
|
||||||
transition: color var(--animation-duration) var(--animation-style);
|
transition: color var(--animation-duration) var(--animation-style);
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--btn-color-inverse);
|
color: var(--btn-color-inverse);
|
||||||
|
@ -172,7 +172,6 @@ button:disabled {
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.btn--header-balance {
|
.btn.btn--header-balance {
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--header-primary-color);
|
color: var(--header-primary-color);
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,18 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: var(--card-max-width);
|
max-width: var(--card-max-width);
|
||||||
}
|
white-space: normal;
|
||||||
|
|
||||||
.card > h1 {
|
.card__media {
|
||||||
word-wrap: break-word;
|
padding-top: var(--video-aspect-ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text that is shown if a piece of content has no thumbnail
|
||||||
|
// We need to do this because the thumbnail uses padding-top: var(--video-aspect-ratio); for dynamic height
|
||||||
|
// this lets the text sit in the middle instead of the bottom
|
||||||
|
.card__media-text {
|
||||||
|
margin-top: calc(var(--video-aspect-ratio) * -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--section {
|
.card--section {
|
||||||
|
@ -20,25 +28,25 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--small {
|
.card--small {
|
||||||
white-space: normal;
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
.card__media {
|
.card__media {
|
||||||
padding-top: var(--video-aspect-ratio);
|
background-size: cover;
|
||||||
}
|
background-repeat: no-repeat;
|
||||||
|
background-position: 50% 50%;
|
||||||
|
background-color: var(--color-placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
.card__media-text {
|
.card__media--no-img {
|
||||||
// for the weird padding required for dynamic height
|
display: flex;
|
||||||
// this lets the text sit in the middle instead of the bottom
|
justify-content: center;
|
||||||
margin-top: calc(var(--video-aspect-ratio) * -1);
|
align-items: center;
|
||||||
}
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.channel-name {
|
.card__media--nsfw {
|
||||||
font-size: 12px;
|
background-color: var(--color-error);
|
||||||
|
|
||||||
@media only screen and (min-width: $medium-breakpoint) {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--link {
|
.card--link {
|
||||||
|
@ -66,31 +74,29 @@
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__media {
|
.card__title {
|
||||||
background-size: cover;
|
font-size: 1.5em;
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: 50% 50%;
|
|
||||||
background-color: var(--color-placeholder);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__media--no-img {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__media--nsfw {
|
|
||||||
background-color: var(--color-error);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__title-identity {
|
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
padding-top: $spacing-vertical * 1/3;
|
||||||
|
|
||||||
|
@media (min-width: $large-breakpoint) {
|
||||||
|
font-size: 1.7em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__title-identity--file {
|
.card__title--file-card {
|
||||||
|
// FileCard is slightly different where the title is only slightly bigger than the subtitle
|
||||||
|
// Slightly bigger than 2 lines for consistent channel placement
|
||||||
|
font-size: 1.1em;
|
||||||
|
height: 4em;
|
||||||
|
|
||||||
|
@media only screen and (min-width: $large-breakpoint) {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card__title__space-between {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
|
@ -104,73 +110,26 @@
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__title {
|
|
||||||
font-size: 18px;
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__title--small {
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 12px;
|
|
||||||
|
|
||||||
@media only screen and (min-width: $medium-breakpoint) {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__title--x-small {
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__title--large {
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__title--file {
|
|
||||||
font-family: 'metropolis-bold';
|
|
||||||
font-size: 28px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__title--file-card {
|
|
||||||
padding-top: $spacing-vertical * 1/3;
|
|
||||||
// height is the same height that two lines of title fill
|
|
||||||
// doing this so content below the title is inline accross the row
|
|
||||||
height: 30px;
|
|
||||||
|
|
||||||
@media only screen and (min-width: $medium-breakpoint) {
|
|
||||||
height: 36px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__subtitle {
|
.card__subtitle {
|
||||||
margin: 0;
|
|
||||||
font-size: 14px;
|
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
color: var(--card-text-color);
|
color: var(--card-text-color);
|
||||||
|
font-size: 1em;
|
||||||
|
line-height: 1em;
|
||||||
|
|
||||||
|
@media (min-width: $large-breakpoint) {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__subtitle--x-small {
|
.card__subtext-title {
|
||||||
font-size: 12px;
|
font-size: 1.1em;
|
||||||
|
|
||||||
|
&:not(:first-of-type) {
|
||||||
|
margin-top: $spacing-vertical * 3/2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__subtitle--large {
|
.card__subtext {
|
||||||
font-size: 18px;
|
font-size: 0.85em;
|
||||||
padding-bottom: $spacing-vertical * 1/3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__subtitle-price {
|
|
||||||
padding-top: $spacing-vertical * 1/3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__title--small + .card__subtitle,
|
|
||||||
.card__title--x-small + .card__subtitle {
|
|
||||||
padding-top: $spacing-vertical * 1/3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__meta {
|
.card__meta {
|
||||||
|
@ -185,11 +144,6 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-top: $spacing-vertical * 1/3;
|
padding-top: $spacing-vertical * 1/3;
|
||||||
color: var(--card-text-color);
|
color: var(--card-text-color);
|
||||||
|
|
||||||
.icon + .icon,
|
|
||||||
.credit-amount + .icon {
|
|
||||||
margin-left: $spacing-vertical * 1/3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// .card-media__internal__links should always be inside a card
|
// .card-media__internal__links should always be inside a card
|
||||||
|
@ -201,58 +155,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--small {
|
|
||||||
.card-media__internal-links {
|
|
||||||
top: $spacing-vertical * 1/3;
|
|
||||||
right: $spacing-vertical * 1/3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Channel info with buttons on the right side
|
|
||||||
.card__channel-info {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: $spacing-width 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__channel-info--large {
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: $spacing-vertical * 2/3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__content {
|
.card__content {
|
||||||
margin-top: $spacing-vertical * 2/3;
|
margin-top: $spacing-vertical * 2/3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__content--extra-padding {
|
|
||||||
margin-top: $spacing-vertical * 3/2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__subtext-title {
|
|
||||||
color: var(--text-color);
|
|
||||||
font-size: calc(var(--font-size-subtext-multiple) * 1.2em);
|
|
||||||
|
|
||||||
&:not(:first-of-type) {
|
|
||||||
margin-top: $spacing-vertical * 3/2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__subtext {
|
|
||||||
font-size: calc(var(--font-size-subtext-multiple) * 1em);
|
|
||||||
padding-top: $spacing-vertical * 1/3;
|
|
||||||
word-break: break-word;
|
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__subtext--small {
|
|
||||||
font-size: calc(var(--font-size-subtext-multiple) * 0.8em);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__subtext--large {
|
|
||||||
font-size: calc(var(--font-size-subtext-multiple) * 0.9em);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__actions {
|
.card__actions {
|
||||||
margin-top: $spacing-vertical * 2/3;
|
margin-top: $spacing-vertical * 2/3;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -335,6 +241,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-row__title {
|
.card-row__title {
|
||||||
|
font-family: 'metropolis-semibold';
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
@ -462,43 +369,3 @@
|
||||||
padding: $spacing-vertical * 1/3;
|
padding: $spacing-vertical * 1/3;
|
||||||
margin: $spacing-vertical * 1/3 0;
|
margin: $spacing-vertical * 1/3 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__media--autothumb {
|
|
||||||
color: red !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__media {
|
|
||||||
&.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,3 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-indicator__icon--invalid {
|
|
||||||
color: var(--color-error);
|
|
||||||
}
|
|
||||||
|
|
|
@ -95,10 +95,6 @@
|
||||||
&.content__empty--nsfw {
|
&.content__empty--nsfw {
|
||||||
background-color: var(--color-nsfw);
|
background-color: var(--color-nsfw);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__media-text {
|
|
||||||
margin-top: calc(var(--video-aspect-ratio) * -1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
|
|
@ -22,25 +22,32 @@
|
||||||
|
|
||||||
.file-tile {
|
.file-tile {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
font-size: 14px;
|
||||||
padding-top: $spacing-vertical;
|
padding-top: $spacing-vertical;
|
||||||
|
|
||||||
.card__media {
|
.card__media {
|
||||||
height: var(--file-tile-media-height);
|
height: var(--file-tile-media-height);
|
||||||
flex: 0 0 var(--file-tile-media-width);
|
flex: 0 0 var(--file-tile-media-width);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card__subtitle {
|
.file-tile--small {
|
||||||
line-height: 1;
|
font-size: 12px;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
.card__media {
|
||||||
|
height: var(--file-tile-media-height-small);
|
||||||
|
flex: 0 0 var(--file-tile-media-width-small);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-tile__channel {
|
.file-tile--large {
|
||||||
padding-right: $spacing-width * 1/4;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-tile.file-tile--small {
|
.file-tile__title {
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-tile--small {
|
||||||
padding-top: $spacing-vertical * 2/3;
|
padding-top: $spacing-vertical * 2/3;
|
||||||
|
|
||||||
.card__media {
|
.card__media {
|
||||||
|
@ -49,7 +56,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-tile.file-tile--large {
|
.file-tile--large {
|
||||||
.card__media {
|
.card__media {
|
||||||
height: var(--file-tile-media-height-large);
|
height: var(--file-tile-media-height-large);
|
||||||
flex: 0 0 var(--file-tile-media-width-large);
|
flex: 0 0 var(--file-tile-media-width-large);
|
||||||
|
|
6
src/renderer/scss/component/_icon.scss
Normal file
6
src/renderer/scss/component/_icon.scss
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Icons with icons directly following should have a margin-right for proper spacing
|
||||||
|
// Same for prices on cards
|
||||||
|
.icon + .icon,
|
||||||
|
.credit-amount + .icon {
|
||||||
|
margin-left: $spacing-vertical * 1/3;
|
||||||
|
}
|
26
src/renderer/scss/component/_item-list.scss
Normal file
26
src/renderer/scss/component/_item-list.scss
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
.item-list {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
margin-top: $spacing-vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-list__item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: $spacing-vertical * 1/3;
|
||||||
|
|
||||||
|
input,
|
||||||
|
.item-list__item--cutoff {
|
||||||
|
margin-right: $spacing-vertical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-list__item--selected {
|
||||||
|
background-color: var(--table-item-odd);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-list__item--cutoff {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow-x: hidden;
|
||||||
|
max-width: 350px;
|
||||||
|
}
|
|
@ -39,7 +39,6 @@
|
||||||
|
|
||||||
/* Paragraphs */
|
/* Paragraphs */
|
||||||
p {
|
p {
|
||||||
margin: $spacing-vertical * 2/3 0;
|
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
padding-left: 30px;
|
padding-left: 30px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -54,7 +53,6 @@
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-items: flex-start;
|
justify-items: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
|
|
||||||
&:not(:first-of-type) {
|
&:not(:first-of-type) {
|
||||||
border-top: 1px solid var(--search-item-border-color);
|
border-top: 1px solid var(--search-item-border-color);
|
||||||
|
@ -75,7 +73,6 @@
|
||||||
.wunderbar__suggestion-label--action {
|
.wunderbar__suggestion-label--action {
|
||||||
margin-left: $spacing-vertical * 1/3;
|
margin-left: $spacing-vertical * 1/3;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-family: 'metropolis-medium';
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 0.1; // to vertically align because the font size is smaller
|
line-height: 0.1; // to vertically align because the font size is smaller
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ table.table,
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-top: $spacing-vertical * 2/3;
|
|
||||||
|
|
||||||
tr td:first-of-type,
|
tr td:first-of-type,
|
||||||
tr th:first-of-type {
|
tr th:first-of-type {
|
||||||
|
@ -107,48 +106,3 @@ table.table--transactions {
|
||||||
width: 15%;
|
width: 15%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table.table--history {
|
|
||||||
margin-top: $spacing-vertical * 1/3;
|
|
||||||
|
|
||||||
tbody {
|
|
||||||
tr {
|
|
||||||
&:nth-child(even),
|
|
||||||
&:nth-child(odd) {
|
|
||||||
background-color: var(--table-item-even);
|
|
||||||
|
|
||||||
&.history__selected {
|
|
||||||
color: red;
|
|
||||||
background-color: var(--table-item-odd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
cursor: default;
|
|
||||||
padding: $spacing-vertical * 1/3 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
td:nth-of-type(1) {
|
|
||||||
width: 7.5%;
|
|
||||||
}
|
|
||||||
td:nth-of-type(2) {
|
|
||||||
width: 17.5%;
|
|
||||||
}
|
|
||||||
td:nth-of-type(3) {
|
|
||||||
width: 40%;
|
|
||||||
max-width: 30vw;
|
|
||||||
padding-right: $spacing-vertical * 2/3;
|
|
||||||
}
|
|
||||||
td:nth-of-type(4) {
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
td:nth-of-type(3),
|
|
||||||
td:nth-of-type(4) {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
9
src/renderer/scss/component/_time.scss
Normal file
9
src/renderer/scss/component/_time.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// All CSS for date & time ui
|
||||||
|
|
||||||
|
.time {
|
||||||
|
color: var(--color-help);
|
||||||
|
}
|
||||||
|
|
||||||
|
.time--ago {
|
||||||
|
min-width: 160px;
|
||||||
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
--color-credit-free: var(--color-secondary);
|
--color-credit-free: var(--color-secondary);
|
||||||
|
|
||||||
/* Text */
|
/* Text */
|
||||||
--text-color: var(--color-white);
|
--text-color: var(--color-text-white);
|
||||||
--text-help-color: var(--color-help);
|
--text-help-color: var(--color-help);
|
||||||
|
|
||||||
/* Form */
|
/* Form */
|
||||||
|
|
Loading…
Reference in a new issue