Discovery fixes #2576
27 changed files with 325 additions and 4805 deletions
|
@ -39,6 +39,7 @@
|
||||||
"space-before-function-paren": ["error", "never"],
|
"space-before-function-paren": ["error", "never"],
|
||||||
"standard/object-curly-even-spacing": 0,
|
"standard/object-curly-even-spacing": 0,
|
||||||
"standard/no-callback-literal": 0,
|
"standard/no-callback-literal": 0,
|
||||||
|
"react/display-name": 0,
|
||||||
"semi": [
|
"semi": [
|
||||||
"error",
|
"error",
|
||||||
"always",
|
"always",
|
||||||
|
|
3847
flow-typed/npm/react-feather_vx.x.x.js
vendored
3847
flow-typed/npm/react-feather_vx.x.x.js
vendored
File diff suppressed because it is too large
Load diff
3
flow-typed/react-feather.js
vendored
3
flow-typed/react-feather.js
vendored
|
@ -1,3 +0,0 @@
|
||||||
declare module 'react-feather' {
|
|
||||||
declare module.exports: any;
|
|
||||||
}
|
|
|
@ -147,7 +147,6 @@
|
||||||
"rc-progress": "^2.0.6",
|
"rc-progress": "^2.0.6",
|
||||||
"react": "^16.8.2",
|
"react": "^16.8.2",
|
||||||
"react-dom": "^16.8.2",
|
"react-dom": "^16.8.2",
|
||||||
"react-feather": "^1.0.8",
|
|
||||||
"react-ga": "^2.5.7",
|
"react-ga": "^2.5.7",
|
||||||
"react-hot-loader": "^4.11.1",
|
"react-hot-loader": "^4.11.1",
|
||||||
"react-modal": "^3.1.7",
|
"react-modal": "^3.1.7",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import React, { forwardRef } from 'react';
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
|
@ -31,120 +31,107 @@ type Props = {
|
||||||
onMouseLeave: ?(any) => any,
|
onMouseLeave: ?(any) => any,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Button extends React.PureComponent<Props> {
|
const Button = forwardRef((props: Props, ref) => {
|
||||||
static defaultProps = {
|
const {
|
||||||
type: 'button',
|
type = 'button',
|
||||||
};
|
onClick,
|
||||||
|
href,
|
||||||
|
title,
|
||||||
|
label,
|
||||||
|
icon,
|
||||||
|
// This should rarely be used. Regular buttons should just use `icon`
|
||||||
|
// `iconRight` is used for the header (home) button with the LBRY icon and external links that are displayed inline
|
||||||
|
iconRight,
|
||||||
|
disabled,
|
||||||
|
children,
|
||||||
|
navigate,
|
||||||
|
className,
|
||||||
|
description,
|
||||||
|
button,
|
||||||
|
iconColor,
|
||||||
|
iconSize,
|
||||||
|
constrict,
|
||||||
|
activeClass,
|
||||||
|
...otherProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
render() {
|
const combinedClassName = classnames(
|
||||||
const {
|
'button',
|
||||||
id,
|
button
|
||||||
onClick,
|
? {
|
||||||
onMouseEnter,
|
'button--primary': button === 'primary',
|
||||||
onMouseLeave,
|
'button--secondary': button === 'secondary',
|
||||||
innerRef,
|
'button--alt': button === 'alt',
|
||||||
href,
|
'button--inverse': button === 'inverse',
|
||||||
title,
|
'button--close': button === 'close',
|
||||||
label,
|
'button--disabled': disabled,
|
||||||
icon,
|
'button--link': button === 'link',
|
||||||
// This should rarely be used. Regular buttons should just use `icon`
|
'button--constrict': constrict,
|
||||||
// `iconRight` is used for the header (home) button with the LBRY icon and external links that are displayed inline
|
}
|
||||||
iconRight,
|
: 'button--no-style',
|
||||||
disabled,
|
className
|
||||||
children,
|
);
|
||||||
navigate,
|
|
||||||
className,
|
|
||||||
description,
|
|
||||||
button,
|
|
||||||
type,
|
|
||||||
iconColor,
|
|
||||||
iconSize,
|
|
||||||
constrict,
|
|
||||||
activeClass,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const combinedClassName = classnames(
|
const content = (
|
||||||
'button',
|
<span className="button__content">
|
||||||
button
|
{icon && <Icon icon={icon} iconColor={iconColor} size={iconSize} />}
|
||||||
? {
|
{label && <span className="button__label">{label}</span>}
|
||||||
'button--primary': button === 'primary',
|
{children && children}
|
||||||
'button--secondary': button === 'secondary',
|
{iconRight && <Icon icon={iconRight} iconColor={iconColor} size={iconSize} />}
|
||||||
'button--alt': button === 'alt',
|
</span>
|
||||||
'button--inverse': button === 'inverse',
|
);
|
||||||
'button--close': button === 'close',
|
|
||||||
'button--disabled': disabled,
|
|
||||||
'button--link': button === 'link',
|
|
||||||
'button--constrict': constrict,
|
|
||||||
}
|
|
||||||
: 'button--no-style',
|
|
||||||
className
|
|
||||||
);
|
|
||||||
|
|
||||||
const content = (
|
if (href) {
|
||||||
<span className="button__content">
|
return (
|
||||||
{icon && <Icon icon={icon} iconColor={iconColor} size={iconSize} />}
|
<OutboundLink eventLabel="outboundClick" to={href} target="_blank" className={combinedClassName}>
|
||||||
{label && <span className="button__label">{label}</span>}
|
|
||||||
{children && children}
|
|
||||||
{iconRight && <Icon icon={iconRight} iconColor={iconColor} size={iconSize} />}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (href) {
|
|
||||||
return (
|
|
||||||
<OutboundLink eventLabel="outboundClick" to={href} target="_blank" className={combinedClassName}>
|
|
||||||
{content}
|
|
||||||
</OutboundLink>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle lbry:// uris passed in, or already formatted web urls
|
|
||||||
let path = navigate;
|
|
||||||
if (path) {
|
|
||||||
if (path.startsWith('lbry://')) {
|
|
||||||
path = formatLbryUriForWeb(path);
|
|
||||||
} else if (!path.startsWith('/')) {
|
|
||||||
// Force a leading slash so new paths aren't appended on to the current path
|
|
||||||
path = `/${path}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return path ? (
|
|
||||||
<NavLink
|
|
||||||
id={id}
|
|
||||||
exact
|
|
||||||
to={path}
|
|
||||||
title={title}
|
|
||||||
disabled={disabled}
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (onClick) {
|
|
||||||
onClick();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onMouseEnter={onMouseEnter}
|
|
||||||
onMouseLeave={onMouseLeave}
|
|
||||||
className={combinedClassName}
|
|
||||||
activeClassName={activeClass}
|
|
||||||
innerRef={innerRef}
|
|
||||||
>
|
|
||||||
{content}
|
{content}
|
||||||
</NavLink>
|
</OutboundLink>
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
id={id}
|
|
||||||
title={title}
|
|
||||||
aria-label={description || label || title}
|
|
||||||
className={combinedClassName}
|
|
||||||
onClick={onClick}
|
|
||||||
disabled={disabled}
|
|
||||||
type={type}
|
|
||||||
{...otherProps}
|
|
||||||
>
|
|
||||||
{content}
|
|
||||||
</button>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Handle lbry:// uris passed in, or already formatted web urls
|
||||||
|
let path = navigate;
|
||||||
|
if (path) {
|
||||||
|
if (path.startsWith('lbry://')) {
|
||||||
|
path = formatLbryUriForWeb(path);
|
||||||
|
} else if (!path.startsWith('/')) {
|
||||||
|
// Force a leading slash so new paths aren't appended on to the current path
|
||||||
|
path = `/${path}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return path ? (
|
||||||
|
<NavLink
|
||||||
|
ref={ref}
|
||||||
|
exact
|
||||||
|
to={path}
|
||||||
|
title={title}
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (onClick) {
|
||||||
|
onClick();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={combinedClassName}
|
||||||
|
activeClassName={activeClass}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</NavLink>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
ref={ref}
|
||||||
|
title={title}
|
||||||
|
aria-label={description || label || title}
|
||||||
|
className={combinedClassName}
|
||||||
|
onClick={onClick}
|
||||||
|
disabled={disabled}
|
||||||
|
type={type}
|
||||||
|
{...otherProps}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default Button;
|
export default Button;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
// A housing for all of our icons. Mostly taken fromhttps://github.com/feathericons/react-feather
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import React from 'react';
|
import React, { forwardRef } from 'react';
|
||||||
|
|
||||||
type IconProps = {
|
type IconProps = {
|
||||||
size: number,
|
size: number,
|
||||||
|
@ -8,28 +9,37 @@ type IconProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns a react component
|
// Returns a react component
|
||||||
const buildIcon = (iconStrokes: React$Node, options?: {} = {}) => (props: IconProps) => {
|
const buildIcon = (iconStrokes: React$Node, options?: {} = {}) =>
|
||||||
const { size = 24, color = 'currentColor', ...otherProps } = props;
|
forwardRef((props: IconProps, ref) => {
|
||||||
return (
|
const { size = 24, color = 'currentColor', ...otherProps } = props;
|
||||||
<svg
|
return (
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<svg
|
||||||
viewBox="0 0 24 24"
|
ref={ref}
|
||||||
width={size}
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
height={size}
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
width={size}
|
||||||
stroke={color}
|
height={size}
|
||||||
strokeWidth="2"
|
fill="none"
|
||||||
strokeLinecap="round"
|
stroke={color}
|
||||||
strokeLinejoin="round"
|
strokeWidth="2"
|
||||||
{...options}
|
strokeLinecap="round"
|
||||||
{...otherProps}
|
strokeLinejoin="round"
|
||||||
>
|
{...options}
|
||||||
{iconStrokes}
|
{...otherProps}
|
||||||
</svg>
|
>
|
||||||
);
|
{iconStrokes}
|
||||||
};
|
</svg>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export const customIcons = {
|
export const icons = {
|
||||||
|
// The LBRY icon is different from the base icon set so don't use buildIcon()
|
||||||
|
[ICONS.LBRY]: (
|
||||||
|
<svg stroke="currentColor" fill="currentColor" x="0px" y="0px" viewBox="0 0 322 254" className="icon lbry-icon">
|
||||||
|
<path d="M296,85.9V100l-138.8,85.3L52.6,134l0.2-7.9l104,51.2L289,96.1v-5.8L164.2,30.1L25,116.2v38.5l131.8,65.2 l137.6-84.4l3.9,6l-141.1,86.4L18.1,159.1v-46.8l145.8-90.2C163.9,22.1,296,85.9,296,85.9z" />
|
||||||
|
<path d="M294.3,150.9l2-12.6l-12.2-2.1l0.8-4.9l17.1,2.9l-2.8,17.5L294.3,150.9L294.3,150.9z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
[ICONS.ARROW_LEFT]: buildIcon(
|
[ICONS.ARROW_LEFT]: buildIcon(
|
||||||
<g fill="none" fillRule="evenodd" strokeLinecap="round">
|
<g fill="none" fillRule="evenodd" strokeLinecap="round">
|
||||||
<path d="M4, 12 L21, 12" />
|
<path d="M4, 12 L21, 12" />
|
||||||
|
@ -46,48 +56,12 @@ export const customIcons = {
|
||||||
<polyline strokeLinejoin="round" points="13 4 21 12 13 20" />
|
<polyline strokeLinejoin="round" points="13 4 21 12 13 20" />
|
||||||
</g>
|
</g>
|
||||||
),
|
),
|
||||||
[ICONS.VIEW]: buildIcon(
|
|
||||||
<g fill="none" fillRule="evenodd">
|
|
||||||
<path
|
|
||||||
d="M2, 12 C2, 12 5, 5 12, 5 C19, 5 22, 12 22, 12 C22, 12 19, 19 12, 19 C5, 19 2, 12 2, 12 Z"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<circle cx="12" cy="12" r="3" />
|
|
||||||
<path d="M12, 5 L12, 3" strokeLinecap="round" />
|
|
||||||
<path d="M18, 6.5 L19, 5" strokeLinecap="round" />
|
|
||||||
<path d="M21, 10 L22.5, 9" strokeLinecap="round" />
|
|
||||||
<path
|
|
||||||
d="M1.5, 10 L3, 9"
|
|
||||||
strokeLinecap="round"
|
|
||||||
transform="translate(2.250000, 9.500000) scale(1, -1) translate(-2.250000, -9.500000)"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5, 6.5 L6, 5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
transform="translate(5.500000, 5.750000) scale(-1, 1) translate(-5.500000, -5.750000)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
),
|
|
||||||
|
|
||||||
[ICONS.HOME]: buildIcon(
|
[ICONS.HOME]: buildIcon(
|
||||||
<g strokeWidth="2" fill="none" fillRule="evenodd" strokeLinecap="round" strokeLinejoin="round">
|
<g strokeWidth="2" fill="none" fillRule="evenodd" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<path d="M1, 11 L12, 2 C12, 2 22.9999989, 11.0000005 23, 11" />
|
<path d="M1, 11 L12, 2 C12, 2 22.9999989, 11.0000005 23, 11" />
|
||||||
<path d="M3, 10 C3, 10 3, 10.4453982 3, 10.9968336 L3, 20.0170446 C3, 20.5675806 3.43788135, 21.0138782 4.00292933, 21.0138781 L8.99707067, 21.0138779 C9.55097324, 21.0138779 10, 20.5751284 10, 20.0089602 L10, 15.0049177 C10, 14.449917 10.4433532, 14 11.0093689, 14 L12.9906311, 14 C13.5480902, 14 14, 14.4387495 14, 15.0049177 L14, 20.0089602 C14, 20.5639609 14.4378817, 21.0138779 15.0029302, 21.0138779 L19.9970758, 21.0138781 C20.5509789, 21.0138782 21.000006, 20.56848 21.000006, 20.0170446 L21.0000057, 10" />
|
<path d="M3, 10 C3, 10 3, 10.4453982 3, 10.9968336 L3, 20.0170446 C3, 20.5675806 3.43788135, 21.0138782 4.00292933, 21.0138781 L8.99707067, 21.0138779 C9.55097324, 21.0138779 10, 20.5751284 10, 20.0089602 L10, 15.0049177 C10, 14.449917 10.4433532, 14 11.0093689, 14 L12.9906311, 14 C13.5480902, 14 14, 14.4387495 14, 15.0049177 L14, 20.0089602 C14, 20.5639609 14.4378817, 21.0138779 15.0029302, 21.0138779 L19.9970758, 21.0138781 C20.5509789, 21.0138782 21.000006, 20.56848 21.000006, 20.0170446 L21.0000057, 10" />
|
||||||
</g>
|
</g>
|
||||||
),
|
),
|
||||||
[ICONS.MENU]: buildIcon(
|
|
||||||
<path
|
|
||||||
d="M3.5, 7 C3.5, 7.27910535 3.72002141, 7.5 3.99339768, 7.5 L20.0066023, 7.5 C20.2782464, 7.5 20.5, 7.27680164 20.5, 7 C20.5, 6.72089465 20.2799786, 6.5 20.0066023, 6.5 L3.99339768, 6.5 C3.72175357, 6.5 3.5, 6.72319836 3.5, 7 Z M3.5, 12 C3.5, 12.2791054 3.72002141, 12.5 3.99339768, 12.5 L20.0066023, 12.5 C20.2782464, 12.5 20.5, 12.2768016 20.5, 12 C20.5, 11.7208946 20.2799786, 11.5 20.0066023, 11.5 L3.99339768, 11.5 C3.72175357, 11.5 3.5, 11.7231984 3.5, 12 Z M3.5, 17 C3.5, 17.2791054 3.72002141, 17.5 3.99339768, 17.5 L20.0066023, 17.5 C20.2782464, 17.5 20.5, 17.2768016 20.5, 17 C20.5, 16.7208946 20.2799786, 16.5 20.0066023, 16.5 L3.99339768, 16.5 C3.72175357, 16.5 3.5, 16.7231984 3.5, 17 Z"
|
|
||||||
fill="none"
|
|
||||||
fillRule="evenodd"
|
|
||||||
strokeWidth="1"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[ICONS.PLAY]: buildIcon(
|
|
||||||
<g fill="white" fillRule="evenodd" strokeLinejoin="round">
|
|
||||||
<polygon points="5 21 5 3 21 12" />
|
|
||||||
</g>
|
|
||||||
),
|
|
||||||
[ICONS.UPLOAD]: buildIcon(
|
[ICONS.UPLOAD]: buildIcon(
|
||||||
<g fill="none" fillRule="evenodd" strokeLinecap="round">
|
<g fill="none" fillRule="evenodd" strokeLinecap="round">
|
||||||
<path
|
<path
|
||||||
|
@ -111,15 +85,129 @@ export const customIcons = {
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
),
|
),
|
||||||
// Extended from the feather-icons Heart
|
[ICONS.SUBSCRIPTION]: buildIcon(
|
||||||
[ICONS.UNSUBSCRIBE]: buildIcon(
|
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
|
||||||
<path d="M 12,5.67 10.94,4.61 C 5.7533356,-0.57666427 -2.0266644,7.2033357 3.16,12.39 l 1.06,1.06 7.78,7.78 7.78,-7.78 1.06,-1.06 c 2.149101,-2.148092 2.149101,-5.6319078 0,-7.78 -2.148092,-2.1491008 -5.631908,-2.1491008 -7.78,0 L 9.4481298,8.2303201 15.320603,9.2419066 11.772427,13.723825" />
|
|
||||||
),
|
),
|
||||||
// The LBRY icon is different from the base icon set so don't use buildIcon()
|
[ICONS.SETTINGS]: buildIcon(
|
||||||
[ICONS.LBRY]: props => (
|
<g>
|
||||||
<svg stroke="currentColor" fill="currentColor" x="0px" y="0px" viewBox="0 0 322 254" className="icon lbry-icon">
|
<circle cx="12" cy="12" r="3" />
|
||||||
<path d="M296,85.9V100l-138.8,85.3L52.6,134l0.2-7.9l104,51.2L289,96.1v-5.8L164.2,30.1L25,116.2v38.5l131.8,65.2 l137.6-84.4l3.9,6l-141.1,86.4L18.1,159.1v-46.8l145.8-90.2C163.9,22.1,296,85.9,296,85.9z" />
|
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
|
||||||
<path d="M294.3,150.9l2-12.6l-12.2-2.1l0.8-4.9l17.1,2.9l-2.8,17.5L294.3,150.9L294.3,150.9z" />
|
</g>
|
||||||
</svg>
|
),
|
||||||
|
[ICONS.ACCOUNT]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
|
||||||
|
<circle cx="12" cy="7" r="4" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.OVERVIEW]: buildIcon(<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />),
|
||||||
|
[ICONS.WALLET]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<line x1="8" y1="6" x2="21" y2="6" />
|
||||||
|
<line x1="8" y1="12" x2="21" y2="12" />
|
||||||
|
<line x1="8" y1="18" x2="21" y2="18" />
|
||||||
|
<line x1="3" y1="6" x2="3" y2="6" />
|
||||||
|
<line x1="3" y1="12" x2="3" y2="12" />
|
||||||
|
<line x1="3" y1="18" x2="3" y2="18" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.LIBRARY]: buildIcon(<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />),
|
||||||
|
[ICONS.EDIT]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<path d="M20 14.66V20a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h5.34" />
|
||||||
|
<polygon points="18 2 22 6 12 16 8 16 8 12 18 2" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.DOWNLOAD]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
||||||
|
<polyline points="7 10 12 15 17 10" />
|
||||||
|
<line x1="12" y1="15" x2="12" y2="3" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.HELP]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
|
||||||
|
<line x1="12" y1="17" x2="12" y2="17" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.LIGHT]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<circle cx="12" cy="12" r="5" />
|
||||||
|
<line x1="12" y1="1" x2="12" y2="3" />
|
||||||
|
<line x1="12" y1="21" x2="12" y2="23" />
|
||||||
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||||
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||||
|
<line x1="1" y1="12" x2="3" y2="12" />
|
||||||
|
<line x1="21" y1="12" x2="23" y2="12" />
|
||||||
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||||
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.DARK]: buildIcon(<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />),
|
||||||
|
[ICONS.SEARCH]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.TIP]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<polyline points="20 12 20 22 4 22 4 12" />
|
||||||
|
<rect x="2" y="7" width="20" height="5" />
|
||||||
|
<line x1="12" y1="22" x2="12" y2="7" />
|
||||||
|
<path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z" />
|
||||||
|
<path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.SHARE]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<circle cx="18" cy="5" r="3" />
|
||||||
|
<circle cx="6" cy="12" r="3" />
|
||||||
|
<circle cx="18" cy="19" r="3" />
|
||||||
|
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49" />
|
||||||
|
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.REPORT]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z" />
|
||||||
|
<line x1="4" y1="22" x2="4" y2="15" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.EXTERNAL]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
|
||||||
|
<polyline points="15 3 21 3 21 9" />
|
||||||
|
<line x1="10" y1="14" x2="21" y2="3" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.DELETE]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<polyline points="3 6 5 6 21 6" />
|
||||||
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.COPY]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
|
||||||
|
<rect x="8" y="2" width="8" height="4" rx="1" ry="1" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.REMOVE]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18" />
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.ADD]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<line x1="12" y1="5" x2="12" y2="19" />
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
[ICONS.CHAT]: buildIcon(
|
||||||
|
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import * as FEATHER_ICONS from 'react-feather';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Tooltip from 'component/common/tooltip';
|
import Tooltip from 'component/common/tooltip';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { customIcons } from './icon-custom';
|
import { icons } from './icon-custom';
|
||||||
|
|
||||||
// It would be nice to standardize this somehow
|
// It would be nice to standardize this somehow
|
||||||
// These are copied from `scss/vars`, can they both come from the same source?
|
// These are copied from `scss/vars`, can they both come from the same source?
|
||||||
|
@ -27,6 +26,10 @@ class IconComponent extends React.PureComponent<Props> {
|
||||||
return __('Featured content. Earn rewards for watching.');
|
return __('Featured content. Earn rewards for watching.');
|
||||||
case ICONS.DOWNLOAD:
|
case ICONS.DOWNLOAD:
|
||||||
return __('This file is downloaded.');
|
return __('This file is downloaded.');
|
||||||
|
case ICONS.SUBSCRIPTION:
|
||||||
|
return __('You are subscribed to this channel.');
|
||||||
|
case ICONS.SETTINGS:
|
||||||
|
return __('Your settings.');
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -47,9 +50,10 @@ class IconComponent extends React.PureComponent<Props> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { icon, tooltip, iconColor, size, className } = this.props;
|
const { icon, tooltip, iconColor, size, className } = this.props;
|
||||||
const Icon = customIcons[this.props.icon] || FEATHER_ICONS[this.props.icon];
|
const Icon = icons[this.props.icon];
|
||||||
|
|
||||||
if (!Icon) {
|
if (!Icon) {
|
||||||
|
console.error('no icon found for ', icon);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,13 +71,7 @@ class IconComponent extends React.PureComponent<Props> {
|
||||||
|
|
||||||
const inner = <Icon size={iconSize} className={classnames(`icon icon--${icon}`, className)} color={color} />;
|
const inner = <Icon size={iconSize} className={classnames(`icon icon--${icon}`, className)} color={color} />;
|
||||||
|
|
||||||
return tooltipText ? (
|
return tooltipText ? <Tooltip label={tooltipText}>{inner}</Tooltip> : inner;
|
||||||
<Tooltip icon body={tooltipText} direction={tooltip}>
|
|
||||||
{inner}
|
|
||||||
</Tooltip>
|
|
||||||
) : (
|
|
||||||
inner
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,71 +1,17 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import classnames from 'classnames';
|
import ReachTooltip from '@reach/tooltip';
|
||||||
|
import '@reach/tooltip/styles.css';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
body: string,
|
label: string,
|
||||||
label?: string,
|
|
||||||
children?: React.Node,
|
children?: React.Node,
|
||||||
icon?: boolean,
|
|
||||||
direction: string,
|
|
||||||
onComponent?: boolean, // extra padding to account for button/form field size
|
|
||||||
alwaysVisible?: boolean, // should tooltip stay open, guide callbacks will close it manually
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
function Tooltip(props: Props) {
|
||||||
direction: string,
|
const { children, label } = props;
|
||||||
};
|
|
||||||
|
|
||||||
class ToolTip extends React.PureComponent<Props, State> {
|
return <ReachTooltip label={label}>{children}</ReachTooltip>;
|
||||||
static defaultProps = {
|
|
||||||
direction: 'bottom',
|
|
||||||
alwaysVisible: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
direction: this.props.direction,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
tooltip: ?HTMLSpanElement;
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { direction } = this.state;
|
|
||||||
const { children, label, body, icon, onComponent, alwaysVisible } = this.props;
|
|
||||||
|
|
||||||
const tooltipContent = children || label;
|
|
||||||
const bodyLength = body.length;
|
|
||||||
const isShortDescription = bodyLength < 30;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
className={classnames('tooltip', {
|
|
||||||
'tooltip--label': label && !icon,
|
|
||||||
'tooltip--icon': icon,
|
|
||||||
'tooltip--top': direction === 'top',
|
|
||||||
'tooltip--right': direction === 'right',
|
|
||||||
'tooltip--bottom': direction === 'bottom',
|
|
||||||
'tooltip--left': direction === 'left',
|
|
||||||
'tooltip--on-component': onComponent,
|
|
||||||
'tooltip--always-visible': alwaysVisible,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{tooltipContent}
|
|
||||||
<span
|
|
||||||
ref={ref => {
|
|
||||||
this.tooltip = ref;
|
|
||||||
}}
|
|
||||||
className={classnames('tooltip__body', {
|
|
||||||
'tooltip__body--short': isShortDescription,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{body}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ToolTip;
|
export default Tooltip;
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default class CopyableText extends React.PureComponent<Props> {
|
||||||
inputButton={
|
inputButton={
|
||||||
<Button
|
<Button
|
||||||
button="inverse"
|
button="inverse"
|
||||||
icon={ICONS.CLIPBOARD}
|
icon={ICONS.COPY}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
clipboard.writeText(copyable);
|
clipboard.writeText(copyable);
|
||||||
doToast({
|
doToast({
|
||||||
|
|
|
@ -38,14 +38,19 @@ class FileActions extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{showFullscreen && (
|
{showFullscreen && (
|
||||||
<Tooltip onComponent body={__('Full screen (f)')}>
|
<Tooltip label={__('Full screen (f)')}>
|
||||||
<Button button="alt" description={__('Fullscreen')} icon={ICONS.FULLSCREEN} onClick={this.maximizeViewer} />
|
<Button
|
||||||
|
button="link"
|
||||||
|
description={__('Fullscreen')}
|
||||||
|
icon={ICONS.FULLSCREEN}
|
||||||
|
onClick={this.maximizeViewer}
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{showDelete && (
|
{showDelete && (
|
||||||
<Tooltip onComponent body={__('Delete this file')}>
|
<Tooltip label={__('Remove from your library')}>
|
||||||
<Button
|
<Button
|
||||||
button="alt"
|
button="link"
|
||||||
icon={ICONS.DELETE}
|
icon={ICONS.DELETE}
|
||||||
description={__('Delete')}
|
description={__('Delete')}
|
||||||
onClick={() => openModal(MODALS.CONFIRM_FILE_REMOVE, { uri })}
|
onClick={() => openModal(MODALS.CONFIRM_FILE_REMOVE, { uri })}
|
||||||
|
@ -53,8 +58,8 @@ class FileActions extends React.PureComponent<Props> {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{!claimIsMine && (
|
{!claimIsMine && (
|
||||||
<Tooltip onComponent body={__('Report content')}>
|
<Tooltip label={__('Report content')}>
|
||||||
<Button icon={ICONS.REPORT} href={`https://lbry.com/dmca?claim_id=${claimId}`} />
|
<Button button="link" icon={ICONS.REPORT} href={`https://lbry.com/dmca?claim_id=${claimId}`} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
|
@ -57,11 +57,10 @@ class FileDownloadLink extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolTip onComponent body={__('Download')}>
|
<ToolTip label={__('Download')}>
|
||||||
<Button
|
<Button
|
||||||
button="alt"
|
button="link"
|
||||||
icon={ICONS.DOWNLOAD}
|
icon={ICONS.DOWNLOAD}
|
||||||
iconColor="green"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
purchaseUri(uri);
|
purchaseUri(uri);
|
||||||
|
|
||||||
|
@ -76,10 +75,9 @@ class FileDownloadLink extends React.PureComponent<Props> {
|
||||||
);
|
);
|
||||||
} else if (fileInfo && fileInfo.download_path) {
|
} else if (fileInfo && fileInfo.download_path) {
|
||||||
return (
|
return (
|
||||||
<ToolTip onComponent body={__('Open file')}>
|
<ToolTip label={__('Open file')}>
|
||||||
<Button
|
<Button
|
||||||
button="alt"
|
button="link"
|
||||||
iconColor="green"
|
|
||||||
icon={ICONS.EXTERNAL}
|
icon={ICONS.EXTERNAL}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
pause();
|
pause();
|
||||||
|
|
|
@ -21,9 +21,9 @@ export default function FileProperties(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="file-properties">
|
<div className="file-properties">
|
||||||
{isSubscribed && <Icon icon={icons.SUBSCRIPTION} />}
|
{isSubscribed && <Icon tooltip icon={icons.SUBSCRIPTION} />}
|
||||||
{!claimIsMine && downloaded && <Icon icon={icons.DOWNLOAD} />}
|
{!claimIsMine && downloaded && <Icon tooltip icon={icons.DOWNLOAD} />}
|
||||||
{isRewardContent && <Icon iconColor="red" icon={icons.FEATURED} />}
|
{isRewardContent && <Icon tooltip iconColor="red" icon={icons.FEATURED} />}
|
||||||
<FilePrice hideFree uri={uri} />
|
<FilePrice hideFree uri={uri} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,12 +4,10 @@ import React from 'react';
|
||||||
import LoadingScreen from 'component/common/loading-screen';
|
import LoadingScreen from 'component/common/loading-screen';
|
||||||
import VideoViewer from 'component/viewers/videoViewer';
|
import VideoViewer from 'component/viewers/videoViewer';
|
||||||
|
|
||||||
const AudioViewer = React.lazy<*>(() =>
|
// const AudioViewer = React.lazy<*>(() =>
|
||||||
import(
|
// import(/* webpackChunkName: "audioViewer" */
|
||||||
/* webpackChunkName: "audioViewer" */
|
// 'component/viewers/audioViewer')
|
||||||
'component/viewers/audioViewer'
|
// );
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const DocumentViewer = React.lazy<*>(() =>
|
const DocumentViewer = React.lazy<*>(() =>
|
||||||
import(
|
import(
|
||||||
|
@ -166,7 +164,7 @@ class FileRender extends React.PureComponent<Props> {
|
||||||
video: (
|
video: (
|
||||||
<VideoViewer claim={claim} source={{ downloadPath, fileName }} contentType={contentType} poster={poster} />
|
<VideoViewer claim={claim} source={{ downloadPath, fileName }} contentType={contentType} poster={poster} />
|
||||||
),
|
),
|
||||||
audio: <AudioViewer claim={claim} source={{ downloadPath, fileName }} contentType={contentType} />,
|
// audio: <AudioViewer claim={claim} source={{ downloadPath, fileName }} contentType={contentType} />,
|
||||||
// Add routes to viewer...
|
// Add routes to viewer...
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,7 +206,7 @@ class FileRender extends React.PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="file-render">
|
<div className="file-render">
|
||||||
<React.Suspense fallback={<div />}>{this.renderViewer()}</React.Suspense>
|
<Suspense fallback={<div />}>{this.renderViewer()}</Suspense>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ const Header = (props: Props) => {
|
||||||
description={__('Navigate back')}
|
description={__('Navigate back')}
|
||||||
onClick={() => window.history.back()}
|
onClick={() => window.history.back()}
|
||||||
icon={ICONS.ARROW_LEFT}
|
icon={ICONS.ARROW_LEFT}
|
||||||
iconSize={15}
|
iconSize={18}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
@ -61,7 +61,7 @@ const Header = (props: Props) => {
|
||||||
description={__('Navigate forward')}
|
description={__('Navigate forward')}
|
||||||
onClick={() => window.history.forward()}
|
onClick={() => window.history.forward()}
|
||||||
icon={ICONS.ARROW_RIGHT}
|
icon={ICONS.ARROW_RIGHT}
|
||||||
iconSize={15}
|
iconSize={18}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* @endif */}
|
{/* @endif */}
|
||||||
|
@ -98,11 +98,11 @@ const Header = (props: Props) => {
|
||||||
</Menu>
|
</Menu>
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuButton className="header__navigation-item menu__title">
|
<MenuButton className="header__navigation-item menu__title">
|
||||||
<Icon icon={ICONS.SETTINGS} />
|
<Icon size={18} icon={ICONS.SETTINGS} />
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem className="menu__link" onSelect={() => history.push(`/$/settings`)}>
|
<MenuItem className="menu__link" onSelect={() => history.push(`/$/settings`)}>
|
||||||
<Icon aria-hidden icon={ICONS.SETTINGS} />
|
<Icon aria-hidden tootlip icon={ICONS.SETTINGS} />
|
||||||
{__('Settings')}
|
{__('Settings')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem className="menu__link" onSelect={() => history.push(`/$/help`)}>
|
<MenuItem className="menu__link" onSelect={() => history.push(`/$/help`)}>
|
||||||
|
|
|
@ -344,7 +344,7 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
<h2 className="card__title card__title--flex-between">
|
<h2 className="card__title card__title--flex-between">
|
||||||
{__('Content')}
|
{__('Content')}
|
||||||
{(filePath || !!editingURI) && (
|
{(filePath || !!editingURI) && (
|
||||||
<Button button="inverse" icon={ICONS.CLOSE} label={__('Clear')} onClick={clearPublish} />
|
<Button button="inverse" icon={ICONS.REMOVE} label={__('Clear')} onClick={clearPublish} />
|
||||||
)}
|
)}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="card__subtitle">
|
<p className="card__subtitle">
|
||||||
|
|
|
@ -40,7 +40,7 @@ function SideBar(props: Props) {
|
||||||
...buildLink(PAGES.PUBLISHED, __('Publishes'), ICONS.PUBLISHED),
|
...buildLink(PAGES.PUBLISHED, __('Publishes'), ICONS.PUBLISHED),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...buildLink(PAGES.LIBRARY, __('Library'), ICONS.DOWNLOAD),
|
...buildLink(PAGES.LIBRARY, __('Library'), ICONS.LIBRARY),
|
||||||
},
|
},
|
||||||
].map(renderLink)}
|
].map(renderLink)}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as ICONS from 'constants/icons';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import CopyableText from 'component/copyableText';
|
import CopyableText from 'component/copyableText';
|
||||||
import ToolTip from 'component/common/tooltip';
|
import Tooltip from 'component/common/tooltip';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
claim: StreamClaim,
|
claim: StreamClaim,
|
||||||
|
@ -73,7 +73,7 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
<label className="help">{__('Web link')}</label>
|
<label className="help">{__('Web link')}</label>
|
||||||
<CopyableText copyable={speechURL} />
|
<CopyableText copyable={speechURL} />
|
||||||
<div className="card__actions card__actions--center">
|
<div className="card__actions card__actions--center">
|
||||||
<ToolTip onComponent body={__('Facebook')}>
|
<Tooltip label={__('Facebook')}>
|
||||||
<Button
|
<Button
|
||||||
iconColor="blue"
|
iconColor="blue"
|
||||||
icon={ICONS.FACEBOOK}
|
icon={ICONS.FACEBOOK}
|
||||||
|
@ -81,8 +81,8 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
label={__('')}
|
label={__('')}
|
||||||
href={`https://facebook.com/sharer/sharer.php?u=${encodedSpeechURL}`}
|
href={`https://facebook.com/sharer/sharer.php?u=${encodedSpeechURL}`}
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</Tooltip>
|
||||||
<ToolTip onComponent body={__('Twitter')}>
|
<Tooltip label={__('Twitter')}>
|
||||||
<Button
|
<Button
|
||||||
iconColor="blue"
|
iconColor="blue"
|
||||||
icon={ICONS.TWITTER}
|
icon={ICONS.TWITTER}
|
||||||
|
@ -90,10 +90,10 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
label={__('')}
|
label={__('')}
|
||||||
href={`https://twitter.com/home?status=${encodedSpeechURL}`}
|
href={`https://twitter.com/home?status=${encodedSpeechURL}`}
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</Tooltip>
|
||||||
<ToolTip onComponent body={__('View on Spee.ch')}>
|
<Tooltip label={__('View on Spee.ch')}>
|
||||||
<Button icon={ICONS.WEB} iconColor="blue" button="alt" label={__('')} href={`${speechURL}`} />
|
<Button icon={ICONS.WEB} iconColor="blue" button="alt" label={__('')} href={`${speechURL}`} />
|
||||||
</ToolTip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -101,7 +101,7 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
<label className="help">{__('LBRY App link')}</label>
|
<label className="help">{__('LBRY App link')}</label>
|
||||||
<CopyableText copyable={lbryURL} noSnackbar />
|
<CopyableText copyable={lbryURL} noSnackbar />
|
||||||
<div className="card__actions card__actions--center">
|
<div className="card__actions card__actions--center">
|
||||||
<ToolTip onComponent body={__('Facebook')}>
|
<Tooltip label={__('Facebook')}>
|
||||||
<Button
|
<Button
|
||||||
iconColor="blue"
|
iconColor="blue"
|
||||||
icon={ICONS.FACEBOOK}
|
icon={ICONS.FACEBOOK}
|
||||||
|
@ -109,8 +109,8 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
label={__('')}
|
label={__('')}
|
||||||
href={`https://facebook.com/sharer/sharer.php?u=${encodedLbryURL}`}
|
href={`https://facebook.com/sharer/sharer.php?u=${encodedLbryURL}`}
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</Tooltip>
|
||||||
<ToolTip onComponent body={__('Twitter')}>
|
<Tooltip label={__('Twitter')}>
|
||||||
<Button
|
<Button
|
||||||
iconColor="blue"
|
iconColor="blue"
|
||||||
icon={ICONS.TWITTER}
|
icon={ICONS.TWITTER}
|
||||||
|
@ -118,7 +118,7 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
label={__('')}
|
label={__('')}
|
||||||
href={`https://twitter.com/home?status=${encodedLbryURL}`}
|
href={`https://twitter.com/home?status=${encodedLbryURL}`}
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
|
|
|
@ -26,7 +26,7 @@ export default function Tag(props: Props) {
|
||||||
})}
|
})}
|
||||||
label={name}
|
label={name}
|
||||||
iconSize={12}
|
iconSize={12}
|
||||||
iconRight={type !== 'link' && (type === 'remove' ? ICONS.CLOSE : ICONS.ADD)}
|
iconRight={type !== 'link' && (type === 'remove' ? ICONS.REMOVE : ICONS.ADD)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ export default function TagSelect(props: Props) {
|
||||||
<div className="card--section">
|
<div className="card--section">
|
||||||
<h2 className="card__title card__title--flex-between">
|
<h2 className="card__title card__title--flex-between">
|
||||||
{title}
|
{title}
|
||||||
{showClose && !hasClosed && <Button button="close" icon={ICONS.CLOSE} onClick={handleClose} />}
|
{showClose && !hasClosed && <Button button="close" icon={ICONS.REMOVE} onClick={handleClose} />}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="help">{__("The tags you follow will change what's trending for you.")}</p>
|
<p className="help">{__("The tags you follow will change what's trending for you.")}</p>
|
||||||
|
|
||||||
|
|
|
@ -1,296 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import * as ICONS from 'constants/icons';
|
|
||||||
import Button from 'component/button';
|
|
||||||
import Tooltip from 'component/common/tooltip';
|
|
||||||
import { stopContextMenu } from 'util/context-menu';
|
|
||||||
import butterchurn from 'butterchurn';
|
|
||||||
import detectButterchurnSupport from 'butterchurn/lib/isSupported.min';
|
|
||||||
import butterchurnPresets from 'butterchurn-presets';
|
|
||||||
import jsmediatags from 'jsmediatags/dist/jsmediatags';
|
|
||||||
import WaveSurfer from 'wavesurfer.js';
|
|
||||||
|
|
||||||
import styles from './audioViewer.module.scss';
|
|
||||||
|
|
||||||
const isButterchurnSupported = detectButterchurnSupport();
|
|
||||||
|
|
||||||
const EQ_BANDS_SIMPLE = [55, 150, 250, 400, 500, 1000, 2000, 4000, 8000, 16000];
|
|
||||||
/*
|
|
||||||
const EQ_LOWSHELF = EQ_BANDS_SIMPLE.shift();
|
|
||||||
const EQ_HIGHSHELF = EQ_BANDS_SIMPLE.pop();
|
|
||||||
|
|
||||||
const eqFilters = EQ.map(function(band) {
|
|
||||||
var filter = wavesurfer.backend.ac.createBiquadFilter();
|
|
||||||
filter.type = 'peaking';
|
|
||||||
filter.gain.value = 0;
|
|
||||||
filter.Q.value = 1;
|
|
||||||
filter.frequency.value = band.f;
|
|
||||||
return filter;
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
// type Props = {
|
|
||||||
// source: {
|
|
||||||
// downloadPath: string,
|
|
||||||
// fileName: string,
|
|
||||||
// },
|
|
||||||
// contentType: string,
|
|
||||||
// poster?: string,
|
|
||||||
// claim: StreamClaim,
|
|
||||||
// };
|
|
||||||
|
|
||||||
const presets = [
|
|
||||||
require('butterchurn-presets/presets/converted/Flexi - when monopolies were the future [simple warp + non-reactive moebius].json'),
|
|
||||||
require('butterchurn-presets/presets/converted/Rovastar & Loadus - FractalDrop (Active Sparks Mix).json'),
|
|
||||||
require('butterchurn-presets/presets/converted/shifter - tumbling cubes (ripples).json'),
|
|
||||||
require('butterchurn-presets/presets/converted/ORB - Blue Emotion.json'),
|
|
||||||
require('butterchurn-presets/presets/converted/shifter - urchin mod.json'),
|
|
||||||
require('butterchurn-presets/presets/converted/Stahlregen & fishbrain + flexi + geiss - The Machine that conquered the Aether.json'),
|
|
||||||
require('butterchurn-presets/presets/converted/Zylot - Crosshair Dimension (Light of Ages).json'),
|
|
||||||
];
|
|
||||||
|
|
||||||
class AudioVideoViewer extends React.PureComponent {
|
|
||||||
// audioNode: ?HTMLAudioElement;
|
|
||||||
// player: ?{ dispose: () => void };
|
|
||||||
|
|
||||||
state = {
|
|
||||||
playing: false,
|
|
||||||
enableMilkdrop: isButterchurnSupported,
|
|
||||||
showEqualizer: false,
|
|
||||||
showSongDetails: true,
|
|
||||||
enableArt: true,
|
|
||||||
artLoaded: false,
|
|
||||||
artist: null,
|
|
||||||
title: null,
|
|
||||||
album: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const me = this;
|
|
||||||
const { contentType, poster, claim } = me.props;
|
|
||||||
|
|
||||||
const path = `https://api.lbry.tv/content/claims/${claim.name}/${claim.claim_id}/stream.mp4`;
|
|
||||||
const sources = [
|
|
||||||
{
|
|
||||||
src: path,
|
|
||||||
type: contentType,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const audioNode = this.audioNode;
|
|
||||||
|
|
||||||
audioNode.crossOrigin = 'anonymous';
|
|
||||||
|
|
||||||
const canvasHeight = me.canvasNode.offsetHeight;
|
|
||||||
const canvasWidth = me.canvasNode.offsetWidth;
|
|
||||||
|
|
||||||
// Required for canvas, nuance of rendering
|
|
||||||
me.canvasNode.height = canvasHeight;
|
|
||||||
me.canvasNode.width = canvasWidth;
|
|
||||||
|
|
||||||
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
|
||||||
const audioContext = new AudioContext();
|
|
||||||
|
|
||||||
const audioSource = audioContext.createMediaElementSource(audioNode);
|
|
||||||
audioSource.connect(audioContext.destination);
|
|
||||||
|
|
||||||
if (isButterchurnSupported) {
|
|
||||||
const visualizer = (me.visualizer = butterchurn.createVisualizer(audioContext, me.canvasNode, {
|
|
||||||
height: canvasHeight,
|
|
||||||
width: canvasWidth,
|
|
||||||
pixelRatio: window.devicePixelRatio || 1,
|
|
||||||
textureRatio: 1,
|
|
||||||
}));
|
|
||||||
|
|
||||||
visualizer.connectAudio(audioSource);
|
|
||||||
visualizer.loadPreset(presets[Math.floor(Math.random() * presets.length)], 2.0);
|
|
||||||
|
|
||||||
me._frameCycle = () => {
|
|
||||||
requestAnimationFrame(me._frameCycle);
|
|
||||||
|
|
||||||
if (me.state.enableMilkdrop === true) {
|
|
||||||
visualizer.render();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
me._frameCycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
const wavesurfer = WaveSurfer.create({
|
|
||||||
barWidth: 3,
|
|
||||||
container: this.waveNode,
|
|
||||||
waveColor: '#000',
|
|
||||||
progressColor: '#fff',
|
|
||||||
mediaControls: true,
|
|
||||||
responsive: true,
|
|
||||||
normalize: true,
|
|
||||||
backend: 'MediaElement',
|
|
||||||
minPxPerSec: 100,
|
|
||||||
height: this.waveNode.offsetHeight,
|
|
||||||
});
|
|
||||||
|
|
||||||
wavesurfer.load(audioNode);
|
|
||||||
|
|
||||||
jsmediatags.Config.setDisallowedXhrHeaders(['If-Modified-Since', 'Range']);
|
|
||||||
jsmediatags.read(path, {
|
|
||||||
onSuccess: function(result) {
|
|
||||||
const { album, artist, title, picture } = result.tags;
|
|
||||||
|
|
||||||
if (picture) {
|
|
||||||
const byteArray = new Uint8Array(picture.data);
|
|
||||||
const blob = new Blob([byteArray], { type: picture.type });
|
|
||||||
const albumArtUrl = URL.createObjectURL(blob);
|
|
||||||
me.artNode.src = albumArtUrl;
|
|
||||||
|
|
||||||
me.setState({ artLoaded: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
me.setState({
|
|
||||||
album,
|
|
||||||
artist,
|
|
||||||
title,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onError: function(error) {
|
|
||||||
console.log(':(', error.type, error.info);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.player) {
|
|
||||||
this.player.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kill the render loop
|
|
||||||
this._frameCycle = () => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const me = this;
|
|
||||||
const { contentType, poster, claim } = me.props;
|
|
||||||
const {
|
|
||||||
album,
|
|
||||||
artist,
|
|
||||||
title,
|
|
||||||
enableMilkdrop,
|
|
||||||
showEqualizer,
|
|
||||||
showSongDetails,
|
|
||||||
enableArt,
|
|
||||||
artLoaded,
|
|
||||||
playing,
|
|
||||||
userActive,
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const renderArt = enableArt && artLoaded;
|
|
||||||
|
|
||||||
const path = `https://api.lbry.tv/content/claims/${claim.name}/${claim.claim_id}/stream.mp4`;
|
|
||||||
|
|
||||||
const playButton = (
|
|
||||||
<div
|
|
||||||
onClick={() => {
|
|
||||||
const audioNode = this.audioNode;
|
|
||||||
if (audioNode.paused) {
|
|
||||||
audioNode.play();
|
|
||||||
} else {
|
|
||||||
audioNode.pause();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className={playing ? styles.playButtonPause : styles.playButtonPlay}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={userActive ? styles.userActive : styles.wrapper}
|
|
||||||
onMouseEnter={() => me.setState({ userActive: true })}
|
|
||||||
onMouseLeave={() => me.setState({ userActive: false })}
|
|
||||||
onContextMenu={stopContextMenu}
|
|
||||||
>
|
|
||||||
<div className={enableMilkdrop ? styles.containerWithMilkdrop : styles.container}>
|
|
||||||
<div style={{ position: 'absolute', top: 0, right: 0 }}>
|
|
||||||
<Tooltip onComponent body={__('Toggle Visualizer')}>
|
|
||||||
<Button
|
|
||||||
icon={enableMilkdrop ? ICONS.VISUALIZER_ON : ICONS.VISUALIZER_OFF}
|
|
||||||
onClick={() => {
|
|
||||||
if (!isButterchurnSupported) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get new preset
|
|
||||||
this.visualizer.loadPreset(presets[Math.floor(Math.random() * presets.length)], 2.0);
|
|
||||||
|
|
||||||
this.setState({ enableMilkdrop: !enableMilkdrop });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip onComponent body={__('Toggle Album Art')}>
|
|
||||||
<Button
|
|
||||||
icon={enableArt ? ICONS.MUSIC_ART_ON : ICONS.MUSIC_ART_OFF}
|
|
||||||
onClick={() => this.setState({ enableArt: !enableArt })}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip onComponent body={__('Toggle Details')}>
|
|
||||||
<Button
|
|
||||||
icon={showSongDetails ? ICONS.MUSIC_DETAILS_ON : ICONS.MUSIC_DETAILS_OFF}
|
|
||||||
onClick={() => this.setState({ showSongDetails: !showSongDetails })}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip onComponent body={__('Equalizer')}>
|
|
||||||
<Button icon={ICONS.MUSIC_EQUALIZER} onClick={() => this.setState({ showEqualizer: !showEqualizer })} />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<div ref={node => (this.waveNode = node)} className={styles.wave} />
|
|
||||||
<div className={styles.infoContainer}>
|
|
||||||
<div className={renderArt ? styles.infoArtContainer : styles.infoArtContainerHidden}>
|
|
||||||
<img className={styles.infoArtImage} ref={node => (this.artNode = node)} />
|
|
||||||
{renderArt && playButton}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={
|
|
||||||
showSongDetails
|
|
||||||
? renderArt
|
|
||||||
? styles.songDetailsContainer
|
|
||||||
: styles.songDetailsContainerNoArt
|
|
||||||
: styles.songDetailsContainerHidden
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className={renderArt ? styles.songDetails : styles.songDetailsNoArt}>
|
|
||||||
{artist && (
|
|
||||||
<div className={styles.detailsLineArtist}>
|
|
||||||
<Button icon={ICONS.MUSIC_ARTIST} className={styles.detailsIconArtist} />
|
|
||||||
{artist}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{title && (
|
|
||||||
<div className={styles.detailsLineSong}>
|
|
||||||
<Button icon={ICONS.MUSIC_SONG} className={styles.detailsIconSong} />
|
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{album && (
|
|
||||||
<div className={styles.detailsLineAlbum}>
|
|
||||||
<Button icon={ICONS.MUSIC_ALBUM} className={styles.detailsIconAlbum} />
|
|
||||||
{album}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{!renderArt && <div className={styles.playButtonDetachedContainer}>{playButton}</div>}
|
|
||||||
</div>
|
|
||||||
<canvas
|
|
||||||
ref={node => (this.canvasNode = node)}
|
|
||||||
className={enableMilkdrop ? styles.milkdrop : styles.milkdropDisabled}
|
|
||||||
/>
|
|
||||||
<audio
|
|
||||||
ref={node => (this.audioNode = node)}
|
|
||||||
src={path}
|
|
||||||
style={{ position: 'absolute', top: '-100px' }}
|
|
||||||
onPlay={() => this.setState({ playing: true })}
|
|
||||||
onPause={() => this.setState({ playing: false })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AudioVideoViewer;
|
|
|
@ -1,193 +0,0 @@
|
||||||
.wrapper {
|
|
||||||
composes: 'file-render__viewer' from global;
|
|
||||||
}
|
|
||||||
|
|
||||||
.userActive {
|
|
||||||
composes: wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
background: #212529;
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.containerWithMilkdrop {
|
|
||||||
composes: container;
|
|
||||||
|
|
||||||
background: rgba(50, 50, 55, .7);
|
|
||||||
}
|
|
||||||
|
|
||||||
.wave {
|
|
||||||
position: absolute;
|
|
||||||
bottom: -20%;
|
|
||||||
height: 40%;
|
|
||||||
opacity: 0.5;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.infoContainer {
|
|
||||||
padding: 0 20%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-height: 42%;
|
|
||||||
align-self: center;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: -10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.infoArtContainer {
|
|
||||||
align-self: flex-start;
|
|
||||||
width: 40%;
|
|
||||||
float: left;
|
|
||||||
position: relative;
|
|
||||||
background: rgba(0, 0, 0 , 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.infoArtContainerHidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.infoArtImage {
|
|
||||||
display: block;
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity 0.7s;
|
|
||||||
|
|
||||||
.userActive & {
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.songDetailsContainer {
|
|
||||||
text-align: left;
|
|
||||||
padding: 3%;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.songDetailsContainerHidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.songDetailsContainerNoArt {
|
|
||||||
composes: songDetailsContainer;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.songDetails {
|
|
||||||
width: 150%;
|
|
||||||
text-shadow: 2px 2px 3px #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.songDetailsNoArt {
|
|
||||||
composes: songDetails;
|
|
||||||
|
|
||||||
width: 200%;
|
|
||||||
margin-left: -50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detailsIcon {
|
|
||||||
color: rgba(255, 255, 255, .5);
|
|
||||||
top: -3px;
|
|
||||||
padding-right: 10px;
|
|
||||||
width: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detailsIconArtist {
|
|
||||||
composes: detailsIcon;
|
|
||||||
|
|
||||||
top: -3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detailsIconSong {
|
|
||||||
composes: detailsIcon;
|
|
||||||
|
|
||||||
top: -5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detailsIconAlbum {
|
|
||||||
composes: detailsIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detailsLineArtist {
|
|
||||||
font-size: 26px;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detailsLineSong {
|
|
||||||
font-size: 34px;
|
|
||||||
line-height: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detailsLineAlbum {
|
|
||||||
font-size: 20px;
|
|
||||||
padding-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playButton {
|
|
||||||
position: absolute;
|
|
||||||
border: 5px solid #fff;
|
|
||||||
border-radius: 45px;
|
|
||||||
color: #fff;
|
|
||||||
font-family: arial;
|
|
||||||
font-size: 60px;
|
|
||||||
left: 50%;
|
|
||||||
line-height: 80px;
|
|
||||||
margin-left: -45px;
|
|
||||||
padding-left: 20px;
|
|
||||||
bottom: 50%;
|
|
||||||
margin-bottom: -45px;
|
|
||||||
height: 90px;
|
|
||||||
width: 90px;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity .7s;
|
|
||||||
|
|
||||||
.userActive & {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.playButtonPlay {
|
|
||||||
composes: playButton;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
display: block;
|
|
||||||
content: "▶";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.playButtonPause {
|
|
||||||
composes: playButton;
|
|
||||||
|
|
||||||
font-size: 50px;
|
|
||||||
line-height: 75px;
|
|
||||||
padding-left: 20px;
|
|
||||||
letter-spacing: -24px;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
display: block;
|
|
||||||
content: "▎▎";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.playButtonDetachedContainer {
|
|
||||||
bottom: 35%;
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.milkdrop {
|
|
||||||
top: 0;
|
|
||||||
z-index: 100;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.milkdropDisabled {
|
|
||||||
display: none;
|
|
||||||
}
|
|
|
@ -6,15 +6,15 @@
|
||||||
export const FEATURED = 'Award';
|
export const FEATURED = 'Award';
|
||||||
export const LOCAL = 'Folder';
|
export const LOCAL = 'Folder';
|
||||||
export const ALERT = 'AlertCircle';
|
export const ALERT = 'AlertCircle';
|
||||||
export const CLIPBOARD = 'Clipboard';
|
export const COPY = 'Clipboard';
|
||||||
export const ARROW_LEFT = 'ChevronLeft';
|
export const ARROW_LEFT = 'ChevronLeft';
|
||||||
export const ARROW_RIGHT = 'ChevronRight';
|
export const ARROW_RIGHT = 'ChevronRight';
|
||||||
export const DOWNLOAD = 'Download';
|
export const DOWNLOAD = 'Download';
|
||||||
export const UPLOAD = 'UploadCloud';
|
export const UPLOAD = 'UploadCloud';
|
||||||
export const PUBLISHED = 'Cloud';
|
export const PUBLISHED = 'Cloud';
|
||||||
export const CLOSE = 'X';
|
export const REMOVE = 'X';
|
||||||
export const ADD = 'Plus';
|
export const ADD = 'Plus';
|
||||||
export const EDIT = 'Edit3';
|
export const EDIT = 'Edit';
|
||||||
export const DELETE = 'Trash';
|
export const DELETE = 'Trash';
|
||||||
export const REPORT = 'Flag';
|
export const REPORT = 'Flag';
|
||||||
export const HELP = 'HelpCircle';
|
export const HELP = 'HelpCircle';
|
||||||
|
@ -69,4 +69,4 @@ export const MUSIC_SONG = 'Music';
|
||||||
export const MUSIC_EQUALIZER = 'Sliders';
|
export const MUSIC_EQUALIZER = 'Sliders';
|
||||||
export const LIGHT = 'Sun';
|
export const LIGHT = 'Sun';
|
||||||
export const DARK = 'Moon';
|
export const DARK = 'Moon';
|
||||||
export const AUTO = 'Clock';
|
export const LIBRARY = 'Folder';
|
||||||
|
|
|
@ -307,17 +307,7 @@ class FilePage extends React.Component<Props> {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="file-properties">
|
<div className="file-properties">
|
||||||
{isRewardContent && (
|
{isRewardContent && <Icon size={20} iconColor="red" icon={icons.FEATURED} />}
|
||||||
<Icon
|
|
||||||
size={20}
|
|
||||||
iconColor="red"
|
|
||||||
icon={icons.FEATURED}
|
|
||||||
// Figure out how to get the tooltip to overlap the navbar on the file page and I will love you
|
|
||||||
// https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue
|
|
||||||
// https://spee.ch/4/overflow-issue
|
|
||||||
// tooltip="bottom"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{nsfw && <div className="badge badge--nsfw">MATURE</div>}
|
{nsfw && <div className="badge badge--nsfw">MATURE</div>}
|
||||||
<FilePrice badge uri={normalizeURI(uri)} />
|
<FilePrice badge uri={normalizeURI(uri)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -46,6 +46,5 @@
|
||||||
@import 'component/tags';
|
@import 'component/tags';
|
||||||
@import 'component/time';
|
@import 'component/time';
|
||||||
@import 'component/toggle';
|
@import 'component/toggle';
|
||||||
@import 'component/tooltip';
|
|
||||||
@import 'component/wunderbar';
|
@import 'component/wunderbar';
|
||||||
@import 'component/yrbl';
|
@import 'component/yrbl';
|
||||||
|
|
|
@ -1,152 +0,0 @@
|
||||||
.tooltip {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.tooltip__body {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.tooltip__body {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip__body {
|
|
||||||
font-size: 1rem;
|
|
||||||
color: $lbry-black;
|
|
||||||
font-weight: 400;
|
|
||||||
padding: var(--spacing-miniscule);
|
|
||||||
position: absolute;
|
|
||||||
text-align: center;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
width: 200px;
|
|
||||||
background-color: $lbry-white;
|
|
||||||
border: 1px solid $lbry-gray-1;
|
|
||||||
box-shadow: 0 2px 5px rgba($lbry-black, 0.15);
|
|
||||||
|
|
||||||
&.tooltip__body--short {
|
|
||||||
width: 110px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 5px;
|
|
||||||
content: ' ';
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$tooltip-border: $lbry-gray-5;
|
|
||||||
&.tooltip--bottom .tooltip__body::after {
|
|
||||||
border-color: transparent transparent $tooltip-border transparent;
|
|
||||||
}
|
|
||||||
&.tooltip--left .tooltip__body::after {
|
|
||||||
border-color: transparent transparent transparent $tooltip-border;
|
|
||||||
}
|
|
||||||
&.tooltip--right .tooltip__body::after {
|
|
||||||
border-color: transparent $tooltip-border transparent transparent;
|
|
||||||
}
|
|
||||||
&.tooltip--top .tooltip__body::after {
|
|
||||||
border-color: $tooltip-border transparent transparent transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
html[data-mode='dark'] & {
|
|
||||||
.tooltip__body {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
$tooltip-border: $lbry-white;
|
|
||||||
|
|
||||||
&.tooltip--bottom .tooltip__body::after {
|
|
||||||
border-color: transparent transparent $tooltip-border transparent;
|
|
||||||
}
|
|
||||||
&.tooltip--left .tooltip__body::after {
|
|
||||||
border-color: transparent transparent transparent $tooltip-border;
|
|
||||||
}
|
|
||||||
&.tooltip--right .tooltip__body::after {
|
|
||||||
border-color: transparent $tooltip-border transparent transparent;
|
|
||||||
}
|
|
||||||
&.tooltip--top .tooltip__body::after {
|
|
||||||
border-color: $tooltip-border transparent transparent transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip--always-visible {
|
|
||||||
.tooltip__body {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip--label {
|
|
||||||
// When there is a label for the tooltip and not just using a button or icon
|
|
||||||
font-size: 14px;
|
|
||||||
padding-left: $spacing-vertical * 1/3;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip--on-component,
|
|
||||||
.tooltip--icon {
|
|
||||||
.tooltip__body {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip--left .tooltip__body {
|
|
||||||
top: -5px;
|
|
||||||
right: 105%;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
top: 17px;
|
|
||||||
left: 100%;
|
|
||||||
margin-top: -5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip--right .tooltip__body {
|
|
||||||
margin-top: -28px;
|
|
||||||
margin-left: 110%;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
top: 14px;
|
|
||||||
right: 100%; // To the left of the tooltip
|
|
||||||
margin-top: -5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip--bottom .tooltip__body {
|
|
||||||
top: 90%;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -100px;
|
|
||||||
|
|
||||||
&.tooltip__body--short {
|
|
||||||
margin-left: -56px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
bottom: 100%;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip--top .tooltip__body {
|
|
||||||
bottom: 120%;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -100px;
|
|
||||||
|
|
||||||
&.tooltip__body--short {
|
|
||||||
margin-left: -56px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
top: 100%;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -5px;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -91,7 +91,14 @@
|
||||||
.icon {
|
.icon {
|
||||||
margin-right: var(--spacing-small);
|
margin-right: var(--spacing-small);
|
||||||
margin-bottom: 0.2rem;
|
margin-bottom: 0.2rem;
|
||||||
|
|
||||||
stroke: $lbry-gray-5;
|
stroke: $lbry-gray-5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-mode='dark'] & {
|
||||||
|
color: $lbry-gray-2;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
stroke: $lbry-gray-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9459,11 +9459,6 @@ react-dom@^16.8.2, react-dom@^16.8.6:
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
scheduler "^0.13.6"
|
scheduler "^0.13.6"
|
||||||
|
|
||||||
react-feather@^1.0.8:
|
|
||||||
version "1.1.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-1.1.6.tgz#2a547e3d5cd5e383d3da0128d593cbdb3c1b32f7"
|
|
||||||
integrity sha512-iCofWhTjX+vQwvDmg7o6vg0XrUg1c41yBDZG+l83nz1FiCsleJoUgd3O+kHpOeWMXuPrRIFfCixvcqyOLGOgIg==
|
|
||||||
|
|
||||||
react-ga@^2.5.7:
|
react-ga@^2.5.7:
|
||||||
version "2.5.7"
|
version "2.5.7"
|
||||||
resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-2.5.7.tgz#1c80a289004bf84f84c26d46f3a6a6513081bf2e"
|
resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-2.5.7.tgz#1c80a289004bf84f84c26d46f3a6a6513081bf2e"
|
||||||
|
|
Loading…
Reference in a new issue