paginates over search count rather than channel claim count #3271
166 changed files with 10880 additions and 2352 deletions
|
@ -2,10 +2,8 @@ module.exports = api => {
|
||||||
api.cache(false);
|
api.cache(false);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
presets: ['@babel/env', '@babel/react', '@babel/flow'],
|
presets: [['@babel/env', { loose: true, modules: false }], '@babel/react', '@babel/flow'],
|
||||||
plugins: [
|
plugins: [
|
||||||
'@babel/plugin-proposal-object-rest-spread',
|
|
||||||
'@babel/plugin-syntax-dynamic-import',
|
|
||||||
'import-glob',
|
'import-glob',
|
||||||
'@babel/plugin-transform-runtime',
|
'@babel/plugin-transform-runtime',
|
||||||
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],
|
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
const config = require('../../config');
|
const config = require('../../config');
|
||||||
const redirectHosts = ['open.lbry.com'];
|
const PAGES = require('../../ui/constants/pages');
|
||||||
|
const { formatInAppUrl } = require('../../ui/util/url');
|
||||||
|
const { parseURI } = require('lbry-redux');
|
||||||
|
|
||||||
async function redirectMiddleware(ctx, next) {
|
async function redirectMiddleware(ctx, next) {
|
||||||
const requestHost = ctx.host;
|
const requestHost = ctx.host;
|
||||||
|
@ -16,8 +18,16 @@ async function redirectMiddleware(ctx, next) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redirectHosts.includes(requestHost)) {
|
if (requestHost === 'open.lbry.com' || requestHost === 'open.lbry.io') {
|
||||||
const redirectUrl = config.URL + path;
|
const openQuery = '?src=open';
|
||||||
|
let redirectUrl = config.URL + formatInAppUrl(url, openQuery);
|
||||||
|
|
||||||
|
if (redirectUrl.includes('?')) {
|
||||||
|
redirectUrl = redirectUrl.replace('?', `${openQuery}&`);
|
||||||
|
} else {
|
||||||
|
redirectUrl += openQuery;
|
||||||
|
}
|
||||||
|
|
||||||
ctx.redirect(redirectUrl);
|
ctx.redirect(redirectUrl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"@babel/core": "^7.0.0",
|
"@babel/core": "^7.0.0",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||||
"@babel/plugin-proposal-decorators": "^7.3.0",
|
"@babel/plugin-proposal-decorators": "^7.3.0",
|
||||||
|
"@babel/plugin-proposal-object-rest-spread": "^7.6.2",
|
||||||
"@babel/plugin-transform-flow-strip-types": "^7.2.3",
|
"@babel/plugin-transform-flow-strip-types": "^7.2.3",
|
||||||
"@babel/plugin-transform-runtime": "^7.4.3",
|
"@babel/plugin-transform-runtime": "^7.4.3",
|
||||||
"@babel/polyfill": "^7.2.5",
|
"@babel/polyfill": "^7.2.5",
|
||||||
|
|
|
@ -28,9 +28,9 @@ const webConfig = {
|
||||||
{
|
{
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
test: /\.jsx?$/,
|
test: /\.jsx?$/,
|
||||||
exclude: /node_modules/,
|
|
||||||
options: {
|
options: {
|
||||||
rootMode: 'upward',
|
presets: ['@babel/env', '@babel/react', '@babel/flow'],
|
||||||
|
plugins: ['@babel/plugin-proposal-object-rest-spread', '@babel/plugin-proposal-class-properties'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
"@babel/register": "^7.0.0",
|
"@babel/register": "^7.0.0",
|
||||||
"@exponent/electron-cookies": "^2.0.0",
|
"@exponent/electron-cookies": "^2.0.0",
|
||||||
"@hot-loader/react-dom": "^16.8",
|
"@hot-loader/react-dom": "^16.8",
|
||||||
"@lbry/components": "^2.8.0",
|
"@lbry/components": "^3.0.2",
|
||||||
"@reach/menu-button": "^0.1.18",
|
"@reach/menu-button": "^0.1.18",
|
||||||
"@reach/rect": "^0.2.1",
|
"@reach/rect": "^0.2.1",
|
||||||
"@reach/tabs": "^0.1.5",
|
"@reach/tabs": "^0.1.5",
|
||||||
|
@ -79,6 +79,7 @@
|
||||||
"babel-plugin-add-module-exports": "^1.0.0",
|
"babel-plugin-add-module-exports": "^1.0.0",
|
||||||
"babel-plugin-import-glob": "^2.0.0",
|
"babel-plugin-import-glob": "^2.0.0",
|
||||||
"babel-plugin-transform-imports": "^1.5.1",
|
"babel-plugin-transform-imports": "^1.5.1",
|
||||||
|
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||||
"bluebird": "^3.5.1",
|
"bluebird": "^3.5.1",
|
||||||
"chalk": "^2.4.2",
|
"chalk": "^2.4.2",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
|
@ -167,6 +168,7 @@
|
||||||
"redux-persist-transform-compress": "^4.2.0",
|
"redux-persist-transform-compress": "^4.2.0",
|
||||||
"redux-persist-transform-filter": "0.0.16",
|
"redux-persist-transform-filter": "0.0.16",
|
||||||
"redux-thunk": "^2.2.0",
|
"redux-thunk": "^2.2.0",
|
||||||
|
"reakit": "^1.0.0-beta.13",
|
||||||
"remark": "^9.0.0",
|
"remark": "^9.0.0",
|
||||||
"remark-attr": "^0.8.3",
|
"remark-attr": "^0.8.3",
|
||||||
"remark-emoji": "^2.0.1",
|
"remark-emoji": "^2.0.1",
|
||||||
|
@ -184,7 +186,7 @@
|
||||||
"tree-kill": "^1.1.0",
|
"tree-kill": "^1.1.0",
|
||||||
"unist-util-visit": "^1.4.1",
|
"unist-util-visit": "^1.4.1",
|
||||||
"video.js": "^7.2.2",
|
"video.js": "^7.2.2",
|
||||||
"villain": "btzr-io/Villain",
|
"villain-react": "^1.0.6",
|
||||||
"wavesurfer.js": "^2.2.1",
|
"wavesurfer.js": "^2.2.1",
|
||||||
"webpack": "^4.28.4",
|
"webpack": "^4.28.4",
|
||||||
"webpack-bundle-analyzer": "^3.1.0",
|
"webpack-bundle-analyzer": "^3.1.0",
|
||||||
|
|
|
@ -820,7 +820,6 @@
|
||||||
"Advanced Editor": "Advanced Editor",
|
"Advanced Editor": "Advanced Editor",
|
||||||
"If you bid more than %amount% LBC, when someone navigates to %uri%, it will load your published content. However, you can get a longer version of this URL for any bid.": "If you bid more than %amount% LBC, when someone navigates to %uri%, it will load your published content. However, you can get a longer version of this URL for any bid.",
|
"If you bid more than %amount% LBC, when someone navigates to %uri%, it will load your published content. However, you can get a longer version of this URL for any bid.": "If you bid more than %amount% LBC, when someone navigates to %uri%, it will load your published content. However, you can get a longer version of this URL for any bid.",
|
||||||
"%nameOrTitle% has been published to lbry://%name%. Click here to view it.": "%nameOrTitle% has been published to lbry://%name%. Click here to view it.",
|
"%nameOrTitle% has been published to lbry://%name%. Click here to view it.": "%nameOrTitle% has been published to lbry://%name%. Click here to view it.",
|
||||||
"Discussion": "Discussion",
|
|
||||||
"If you don't choose a file, the file from your existing claim %name% will be used": "If you don't choose a file, the file from your existing claim %name% will be used",
|
"If you don't choose a file, the file from your existing claim %name% will be used": "If you don't choose a file, the file from your existing claim %name% will be used",
|
||||||
"To enable this feature, check 'Save Password' the next time you start the app.": "To enable this feature, check 'Save Password' the next time you start the app.",
|
"To enable this feature, check 'Save Password' the next time you start the app.": "To enable this feature, check 'Save Password' the next time you start the app.",
|
||||||
"for adding a subscription!": "for adding a subscription!",
|
"for adding a subscription!": "for adding a subscription!",
|
||||||
|
@ -890,4 +889,18 @@
|
||||||
"allow you to receive notifications related to new content.": "allow you to receive notifications related to new content.",
|
"allow you to receive notifications related to new content.": "allow you to receive notifications related to new content.",
|
||||||
"files": "files",
|
"files": "files",
|
||||||
"Invalid claim ID %claimId%.": "Invalid claim ID %claimId%."
|
"Invalid claim ID %claimId%.": "Invalid claim ID %claimId%."
|
||||||
}
|
"Suggested": "Suggested",
|
||||||
|
"%amountBehind% blocks behind": "%amountBehind% blocks behind",
|
||||||
|
"Startup Preferences": "Startup Preferences",
|
||||||
|
"This will clear the application cache, and might fix issues you are having. Your wallet will not be affected. ": "This will clear the application cache, and might fix issues you are having. Your wallet will not be affected. ",
|
||||||
|
"Start minimized": "Start minimized",
|
||||||
|
"Improve view speed and help the LBRY network by allowing the app to cuddle up in your system tray.": "Improve view speed and help the LBRY network by allowing the app to cuddle up in your system tray.",
|
||||||
|
"Content Type": "Content Type",
|
||||||
|
"Submit Feedback": "Submit Feedback",
|
||||||
|
"Checking your publishes...": "Checking your publishes...",
|
||||||
|
"Checking your publishes": "Checking your publishes",
|
||||||
|
"Checking for channels": "Checking for channels",
|
||||||
|
"Error Starting Up": "Error Starting Up",
|
||||||
|
"Reach out to hello@lbry.com for help, or check out %help_link%.": "Reach out to hello@lbry.com for help, or check out %help_link%.",
|
||||||
|
"You're not following any tags. Smash that %customize% button!": "You're not following any tags. Smash that %customize% button!",
|
||||||
|
"customize": "customize"
|
||||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -113,7 +113,7 @@ function App(props: Props) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
document.documentElement.setAttribute('data-mode', theme);
|
document.documentElement.setAttribute('theme', theme);
|
||||||
}, [theme]);
|
}, [theme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -26,7 +26,6 @@ export default function BlockButton(props: Props) {
|
||||||
return permanentUrl && !claimIsMine ? (
|
return permanentUrl && !claimIsMine ? (
|
||||||
<Button
|
<Button
|
||||||
ref={blockRef}
|
ref={blockRef}
|
||||||
iconColor="red"
|
|
||||||
icon={ICONS.BLOCK}
|
icon={ICONS.BLOCK}
|
||||||
button={'alt'}
|
button={'alt'}
|
||||||
label={blockedOverride || blockLabel}
|
label={blockedOverride || blockLabel}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import React, { forwardRef, useRef } 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';
|
||||||
import { formatLbryUriForWeb } from 'util/uri';
|
import { formatLbryUrlForWeb } from 'util/url';
|
||||||
import { OutboundLink } from 'react-ga';
|
import { OutboundLink } from 'react-ga';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import useCombinedRefs from 'effects/use-combined-refs';
|
import useCombinedRefs from 'effects/use-combined-refs';
|
||||||
|
@ -25,7 +25,6 @@ type Props = {
|
||||||
button: ?string, // primary, secondary, alt, link
|
button: ?string, // primary, secondary, alt, link
|
||||||
iconSize?: number,
|
iconSize?: number,
|
||||||
iconColor?: string,
|
iconColor?: string,
|
||||||
constrict: ?boolean, // to shorten the button and ellipsis, only use for links
|
|
||||||
activeClass?: string,
|
activeClass?: string,
|
||||||
innerRef: ?any,
|
innerRef: ?any,
|
||||||
// Events
|
// Events
|
||||||
|
@ -60,7 +59,6 @@ const Button = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
button,
|
button,
|
||||||
iconSize,
|
iconSize,
|
||||||
iconColor,
|
iconColor,
|
||||||
constrict,
|
|
||||||
activeClass,
|
activeClass,
|
||||||
emailVerified,
|
emailVerified,
|
||||||
requiresAuth,
|
requiresAuth,
|
||||||
|
@ -81,7 +79,6 @@ const Button = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
'button--close': button === 'close',
|
'button--close': button === 'close',
|
||||||
'button--disabled': disabled,
|
'button--disabled': disabled,
|
||||||
'button--link': button === 'link',
|
'button--link': button === 'link',
|
||||||
'button--constrict': constrict,
|
|
||||||
}
|
}
|
||||||
: 'button--no-style',
|
: 'button--no-style',
|
||||||
className
|
className
|
||||||
|
@ -111,7 +108,7 @@ const Button = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
let path = navigate;
|
let path = navigate;
|
||||||
if (path) {
|
if (path) {
|
||||||
if (path.startsWith('lbry://')) {
|
if (path.startsWith('lbry://')) {
|
||||||
path = formatLbryUriForWeb(path);
|
path = formatLbryUrlForWeb(path);
|
||||||
} else if (!path.startsWith('/')) {
|
} else if (!path.startsWith('/')) {
|
||||||
// Force a leading slash so new paths aren't appended on to the current path
|
// Force a leading slash so new paths aren't appended on to the current path
|
||||||
path = `/${path}`;
|
path = `/${path}`;
|
||||||
|
|
|
@ -13,9 +13,11 @@ $transition-duration: 300ms;
|
||||||
z-index: $base-zindex;
|
z-index: $base-zindex;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ff-canvas {
|
.ff-canvas {
|
||||||
|
border-radius: var(--border-radius);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
|
@ -27,23 +27,23 @@ function ChannelContent(props: Props) {
|
||||||
{showAbout && (
|
{showAbout && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{description && (
|
{description && (
|
||||||
<div className="media__info-text media__info-text--small">
|
<div className="media__info-text media__info-text--constrained">
|
||||||
<MarkdownPreview content={description} promptLinks />
|
<MarkdownPreview content={description} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{email && (
|
{email && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="media__info-title">{__('Contact')}</div>
|
<label>{__('Contact')}</label>
|
||||||
<div className="media__info-text">
|
<div className="media__info-text">
|
||||||
<MarkdownPreview content={formatEmail(email)} promptLinks />
|
<MarkdownPreview content={formatEmail(email)} />
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
{website && (
|
{website && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="media__info-title">{__('Site')}</div>
|
<label>{__('Site')}</label>
|
||||||
<div className="media__info-text">
|
<div className="media__info-text">
|
||||||
<MarkdownPreview content={website} promptLinks />
|
<MarkdownPreview content={website} />
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -41,7 +41,7 @@ function ChannelContent(props: Props) {
|
||||||
|
|
||||||
{!fetching && !hasContent && !channelIsBlocked && !channelIsBlackListed && (
|
{!fetching && !hasContent && !channelIsBlocked && !channelIsBlackListed && (
|
||||||
<div className="card--section">
|
<div className="card--section">
|
||||||
<h2 className="help">{__("This channel hasn't uploaded anything.")}</h2>
|
<h2 className="section__subtitle">{__("This channel hasn't uploaded anything.")}</h2>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@ import React, { useState } from 'react';
|
||||||
import { FormField } from 'component/common/form';
|
import { FormField } from 'component/common/form';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import SelectAsset from 'component/selectAsset';
|
import SelectAsset from 'component/selectAsset';
|
||||||
import TagsSelect from 'component/tagsSelect';
|
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import { MINIMUM_PUBLISH_BID } from 'constants/claim';
|
import { MINIMUM_PUBLISH_BID } from 'constants/claim';
|
||||||
|
import TagsSearch from 'component/tagsSearch';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
claim: ChannelClaim,
|
claim: ChannelClaim,
|
||||||
|
@ -173,13 +173,16 @@ function ChannelForm(props: Props) {
|
||||||
disabled={false}
|
disabled={false}
|
||||||
onChange={text => setParams({ ...params, description: text })}
|
onChange={text => setParams({ ...params, description: text })}
|
||||||
/>
|
/>
|
||||||
<TagsSelect
|
|
||||||
title={__('Add Tags')}
|
<TagsSearch
|
||||||
suggestMature
|
suggestMature
|
||||||
disableAutoFocus
|
disabledAutoFocus
|
||||||
help={__('The better your tags are, the easier it will be for people to discover your channel.')}
|
tagsPassedIn={params.tags || []}
|
||||||
empty={__('No tags added')}
|
label={__('Tags Selected')}
|
||||||
placeholder={__('Add a tag')}
|
onRemove={clickedTag => {
|
||||||
|
const newTags = params.tags.slice().filter(tag => tag.name !== clickedTag.name);
|
||||||
|
setParams({ ...params, tags: newTags });
|
||||||
|
}}
|
||||||
onSelect={newTags => {
|
onSelect={newTags => {
|
||||||
newTags.forEach(newTag => {
|
newTags.forEach(newTag => {
|
||||||
if (!params.tags.map(savedTag => savedTag.name).includes(newTag.name)) {
|
if (!params.tags.map(savedTag => savedTag.name).includes(newTag.name)) {
|
||||||
|
@ -190,11 +193,6 @@ function ChannelForm(props: Props) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onRemove={clickedTag => {
|
|
||||||
const newTags = params.tags.slice().filter(tag => tag.name !== clickedTag.name);
|
|
||||||
setParams({ ...params, tags: newTags });
|
|
||||||
}}
|
|
||||||
tagsChosen={params.tags || []}
|
|
||||||
/>
|
/>
|
||||||
<div className={'card__actions'}>
|
<div className={'card__actions'}>
|
||||||
<Button button="primary" label={__('Submit')} onClick={handleSubmit} />
|
<Button button="primary" label={__('Submit')} onClick={handleSubmit} />
|
||||||
|
|
|
@ -18,7 +18,7 @@ function ChannelThumbnail(props: Props) {
|
||||||
// Generate a random color class based on the first letter of the channel name
|
// Generate a random color class based on the first letter of the channel name
|
||||||
const { channelName } = parseURI(uri);
|
const { channelName } = parseURI(uri);
|
||||||
const initializer = channelName.charCodeAt(0) - 65; // will be between 0 and 57
|
const initializer = channelName.charCodeAt(0) - 65; // will be between 0 and 57
|
||||||
const colorClassName = `channel-thumbnail__default--${initializer % 4}`;
|
const colorClassName = `channel-thumbnail__default--${Math.abs(initializer % 4)}`;
|
||||||
const showThumb = !obscure && !!thumbnail;
|
const showThumb = !obscure && !!thumbnail;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -121,7 +121,13 @@ export default function ClaimList(props: Props) {
|
||||||
{urisLength > 0 && (
|
{urisLength > 0 && (
|
||||||
<ul className="ul--no-style">
|
<ul className="ul--no-style">
|
||||||
{sortedUris.map((uri, index) => (
|
{sortedUris.map((uri, index) => (
|
||||||
<ClaimPreview key={uri} uri={uri} type={type} showUserBlocked={showHiddenByUser} />
|
<ClaimPreview
|
||||||
|
key={uri}
|
||||||
|
uri={uri}
|
||||||
|
type={type}
|
||||||
|
properties={type !== 'small' ? undefined : false}
|
||||||
|
showUserBlocked={showHiddenByUser}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import type { Node } from 'react';
|
import type { Node } from 'react';
|
||||||
|
import * as PAGES from 'constants/pages';
|
||||||
import React, { Fragment, useEffect, useState } from 'react';
|
import React, { Fragment, useEffect, useState } from 'react';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { createNormalizedClaimSearchKey, MATURE_TAGS } from 'lbry-redux';
|
import { createNormalizedClaimSearchKey, MATURE_TAGS } from 'lbry-redux';
|
||||||
|
@ -173,7 +174,26 @@ function ClaimListDiscover(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const emptyState = !loading && (personalSort === SEARCH_SORT_CHANNELS && !hasContent ? noChannels : noResults);
|
const noTags = (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<I18nMessage
|
||||||
|
tokens={{
|
||||||
|
customize: <Button
|
||||||
|
button="link"
|
||||||
|
navigate={`/$/${PAGES.FOLLOWING}`}
|
||||||
|
label={__('customize')}
|
||||||
|
/>,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
You're not following any tags. Add tags above or smash that %customize% button!
|
||||||
|
</I18nMessage>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const noFollowing = (personalSort === SEARCH_SORT_YOU && noTags) || (personalSort === SEARCH_SORT_CHANNELS && noChannels);
|
||||||
|
const emptyState = !loading && !hasContent ? noFollowing : noResults;
|
||||||
|
|
||||||
function getSearch() {
|
function getSearch() {
|
||||||
let search = `?`;
|
let search = `?`;
|
||||||
|
@ -234,9 +254,9 @@ function ClaimListDiscover(props: Props) {
|
||||||
</FormField>
|
</FormField>
|
||||||
{!hideCustomization && (
|
{!hideCustomization && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<span>{__('For')}</span>
|
<span className="claim-list__conjuction">{__('for')}</span>
|
||||||
{!personalView && tags && tags.length ? (
|
{!personalView && tags && tags.length ? (
|
||||||
tags.map(tag => <Tag key={tag} name={tag} disabled />)
|
tags.map(tag => <Tag key={tag} name={tag} disabled type="large" />)
|
||||||
) : (
|
) : (
|
||||||
<FormField
|
<FormField
|
||||||
type="select"
|
type="select"
|
||||||
|
|
|
@ -5,7 +5,7 @@ import classnames from 'classnames';
|
||||||
import { parseURI, convertToShareLink } from 'lbry-redux';
|
import { parseURI, convertToShareLink } from 'lbry-redux';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { openCopyLinkMenu } from 'util/context-menu';
|
import { openCopyLinkMenu } from 'util/context-menu';
|
||||||
import { formatLbryUriForWeb } from 'util/uri';
|
import { formatLbryUrlForWeb } from 'util/url';
|
||||||
import { isEmpty } from 'util/object';
|
import { isEmpty } from 'util/object';
|
||||||
import CardMedia from 'component/cardMedia';
|
import CardMedia from 'component/cardMedia';
|
||||||
import UriIndicator from 'component/uriIndicator';
|
import UriIndicator from 'component/uriIndicator';
|
||||||
|
@ -49,6 +49,7 @@ type Props = {
|
||||||
actions: boolean | Node | string | number,
|
actions: boolean | Node | string | number,
|
||||||
properties: boolean | Node | string | number,
|
properties: boolean | Node | string | number,
|
||||||
onClick?: any => any,
|
onClick?: any => any,
|
||||||
|
hideBlock?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
|
@ -77,6 +78,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
actions,
|
actions,
|
||||||
properties,
|
properties,
|
||||||
onClick,
|
onClick,
|
||||||
|
hideBlock,
|
||||||
} = props;
|
} = props;
|
||||||
const shouldFetch =
|
const shouldFetch =
|
||||||
claim === undefined || (claim !== null && claim.value_type === 'channel' && isEmpty(claim.meta) && !pending);
|
claim === undefined || (claim !== null && claim.value_type === 'channel' && isEmpty(claim.meta) && !pending);
|
||||||
|
@ -147,7 +149,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
onClick(e);
|
onClick(e);
|
||||||
} else if ((isChannel || title) && !pending) {
|
} else if ((isChannel || title) && !pending) {
|
||||||
history.push(formatLbryUriForWeb(claim && claim.canonical_url ? claim.canonical_url : uri));
|
history.push(formatLbryUrlForWeb(claim && claim.canonical_url ? claim.canonical_url : uri));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +186,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
'claim-preview--large': type === 'large',
|
'claim-preview--large': type === 'large',
|
||||||
'claim-preview--inline': type === 'inline',
|
'claim-preview--inline': type === 'inline',
|
||||||
'claim-preview--tooltip': type === 'tooltip',
|
'claim-preview--tooltip': type === 'tooltip',
|
||||||
|
'claim-preview--channel': isChannel,
|
||||||
'claim-preview--visited': !isChannel && !claimIsMine && hasVisitedUri,
|
'claim-preview--visited': !isChannel && !claimIsMine && hasVisitedUri,
|
||||||
'claim-preview--pending': pending,
|
'claim-preview--pending': pending,
|
||||||
})}
|
})}
|
||||||
|
@ -203,7 +206,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
{isChannel && !channelIsBlocked && !claimIsMine && (
|
{isChannel && !channelIsBlocked && !claimIsMine && (
|
||||||
<SubscribeButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
|
<SubscribeButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
|
||||||
)}
|
)}
|
||||||
{isChannel && !isSubscribed && !claimIsMine && (
|
{!hideBlock && isChannel && !isSubscribed && !claimIsMine && (
|
||||||
<BlockButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
|
<BlockButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
|
||||||
)}
|
)}
|
||||||
{!isChannel && claim && <FileProperties uri={uri} />}
|
{!isChannel && claim && <FileProperties uri={uri} />}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
import { clipboard } from 'electron';
|
import { clipboard } from 'electron';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
|
|
||||||
|
@ -7,14 +8,15 @@ type Props = {
|
||||||
shortUrl: ?string,
|
shortUrl: ?string,
|
||||||
uri: string,
|
uri: string,
|
||||||
doToast: ({ message: string }) => void,
|
doToast: ({ message: string }) => void,
|
||||||
|
inline?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ClaimUri(props: Props) {
|
function ClaimUri(props: Props) {
|
||||||
const { shortUrl, uri, doToast } = props;
|
const { shortUrl, uri, doToast, inline = false } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className="media__uri"
|
className={classnames('media__uri', { 'media__uri--inline': inline })}
|
||||||
button="alt"
|
button="alt"
|
||||||
label={shortUrl || uri}
|
label={shortUrl || uri}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
@ -18,11 +18,16 @@ function Comment(props: Props) {
|
||||||
return (
|
return (
|
||||||
<li className="comment">
|
<li className="comment">
|
||||||
<div className="comment__meta">
|
<div className="comment__meta">
|
||||||
|
{!author ? (
|
||||||
|
<span className="comment__author">{__('Anonymous')}</span>
|
||||||
|
) : (
|
||||||
<Button
|
<Button
|
||||||
className="button--uri-indicator truncated-text comment__author"
|
className="button--uri-indicator truncated-text comment__author"
|
||||||
navigate={authorUri}
|
navigate={authorUri}
|
||||||
label={author || __('Anonymous')}
|
label={author}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<time className="comment__time" dateTime={timePosted}>
|
<time className="comment__time" dateTime={timePosted}>
|
||||||
{relativeDate(timePosted)}
|
{relativeDate(timePosted)}
|
||||||
</time>
|
</time>
|
||||||
|
@ -30,7 +35,7 @@ function Comment(props: Props) {
|
||||||
<div>
|
<div>
|
||||||
<Expandable>
|
<Expandable>
|
||||||
<div className={'comment__message'}>
|
<div className={'comment__message'}>
|
||||||
<MarkdownPreview content={message} promptLinks />
|
<MarkdownPreview content={message} />
|
||||||
</div>
|
</div>
|
||||||
</Expandable>
|
</Expandable>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,19 +10,21 @@ type Props = {
|
||||||
body?: string | Node,
|
body?: string | Node,
|
||||||
actions?: string | Node,
|
actions?: string | Node,
|
||||||
icon?: string,
|
icon?: string,
|
||||||
|
className?: string,
|
||||||
|
actionIconPadding?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Card(props: Props) {
|
export default function Card(props: Props) {
|
||||||
const { title, subtitle, body, actions, icon } = props;
|
const { title, subtitle, body, actions, icon, className, actionIconPadding = true } = props;
|
||||||
return (
|
return (
|
||||||
<section className={classnames('card')}>
|
<section className={classnames(className, 'card')}>
|
||||||
{title && (
|
{title && (
|
||||||
<div className="card__header">
|
<div className="card__header">
|
||||||
<div className="section__flex">
|
<div className="section__flex">
|
||||||
{icon && <Icon sectionIcon icon={icon} />}
|
{icon && <Icon sectionIcon icon={icon} />}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="section__title">{title}</h2>
|
<h2 className="section__title">{title}</h2>
|
||||||
<div className="section__subtitle">{subtitle}</div>
|
{subtitle && <div className="section__subtitle">{subtitle}</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -30,7 +32,11 @@ export default function Card(props: Props) {
|
||||||
|
|
||||||
{body && <div className={classnames('card__body', { 'card__body--with-icon': icon })}>{body}</div>}
|
{body && <div className={classnames('card__body', { 'card__body--with-icon': icon })}>{body}</div>}
|
||||||
{actions && (
|
{actions && (
|
||||||
<div className={classnames('card__main-actions', { 'card__main-actions--with-icon': icon })}>{actions}</div>
|
<div
|
||||||
|
className={classnames('card__main-actions', { 'card__main-actions--with-icon': icon && actionIconPadding })}
|
||||||
|
>
|
||||||
|
{actions}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|
|
@ -66,7 +66,7 @@ class FileSelector extends React.PureComponent<Props> {
|
||||||
readOnly="readonly"
|
readOnly="readonly"
|
||||||
value={placeHolder || __('Choose a file')}
|
value={placeHolder || __('Choose a file')}
|
||||||
inputButton={
|
inputButton={
|
||||||
<Button button="primary" disabled={disabled} onClick={this.fileInputButton} label={buttonLabel} />
|
<Button button="secondary" disabled={disabled} onClick={this.fileInputButton} label={buttonLabel} />
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -80,36 +80,24 @@ export class FormField extends React.PureComponent<Props> {
|
||||||
const errorMessage = typeof error === 'object' ? error.message : error;
|
const errorMessage = typeof error === 'object' ? error.message : error;
|
||||||
|
|
||||||
const Wrapper = blockWrap
|
const Wrapper = blockWrap
|
||||||
? ({ children: innerChildren }) => <fieldset-section>{innerChildren}</fieldset-section>
|
? ({ children: innerChildren }) => <fieldset-section class="radio">{innerChildren}</fieldset-section>
|
||||||
: ({ children: innerChildren }) => <React.Fragment>{innerChildren}</React.Fragment>;
|
: ({ children: innerChildren }) => <span className="radio">{innerChildren}</span>;
|
||||||
|
|
||||||
let input;
|
let input;
|
||||||
if (type) {
|
if (type) {
|
||||||
if (type === 'radio') {
|
if (type === 'radio') {
|
||||||
input = (
|
input = (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<radio-element>
|
|
||||||
<input id={name} type="radio" {...inputProps} />
|
<input id={name} type="radio" {...inputProps} />
|
||||||
<label htmlFor={name}>{label}</label>
|
<label htmlFor={name}>{label}</label>
|
||||||
<radio-toggle onClick={inputProps.onChange} />
|
|
||||||
</radio-element>
|
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
} else if (type === 'checkbox') {
|
} else if (type === 'checkbox') {
|
||||||
// web components treat props weird
|
|
||||||
// we need to fully remove it for proper component:attribute css styling
|
|
||||||
// $FlowFixMe
|
|
||||||
const elementProps = inputProps.disabled ? { disabled: true } : {};
|
|
||||||
input = (
|
input = (
|
||||||
<Wrapper>
|
<div className="checkbox">
|
||||||
<checkbox-element {...elementProps}>
|
<input id={name} type="checkbox" {...inputProps} />
|
||||||
<input id={name} type="checkbox" {...inputProps} tabIndex={0} />
|
<label htmlFor={name}>{label}</label>
|
||||||
<label htmlFor={name} tabIndex={-1}>
|
</div>
|
||||||
{label}
|
|
||||||
</label>
|
|
||||||
<checkbox-toggle onClick={inputProps.onChange} tabIndex={-1} />
|
|
||||||
</checkbox-element>
|
|
||||||
</Wrapper>
|
|
||||||
);
|
);
|
||||||
} else if (type === 'select') {
|
} else if (type === 'select') {
|
||||||
input = (
|
input = (
|
||||||
|
@ -172,7 +160,11 @@ export class FormField extends React.PureComponent<Props> {
|
||||||
input = (
|
input = (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<fieldset-section>
|
<fieldset-section>
|
||||||
<label htmlFor={name}>{errorMessage ? <span className="error-text">{errorMessage}</span> : label}</label>
|
{label && (
|
||||||
|
<label htmlFor={name}>
|
||||||
|
{errorMessage ? <span className="error-text">{errorMessage}</span> : label}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
{prefix && <label htmlFor={name}>{prefix}</label>}
|
{prefix && <label htmlFor={name}>{prefix}</label>}
|
||||||
{inner}
|
{inner}
|
||||||
</fieldset-section>
|
</fieldset-section>
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default function UnsupportedOnWeb(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
IS_WEB && (
|
IS_WEB && (
|
||||||
<div className="card__subtitle--status">
|
<div className="section__subtitle--status">
|
||||||
{type === 'page' && __('This page is not currently supported on the web')}
|
{type === 'page' && __('This page is not currently supported on the web')}
|
||||||
{type === 'feature' && __('This feature is not currently supported on the web')}.{' '}
|
{type === 'feature' && __('This feature is not currently supported on the web')}.{' '}
|
||||||
<Button button="link" label={__('Download the desktop app')} href="https://lbry.com/get" /> for full feature
|
<Button button="link" label={__('Download the desktop app')} href="https://lbry.com/get" /> for full feature
|
||||||
|
|
|
@ -9,10 +9,11 @@ type Props = {
|
||||||
snackMessage: ?string,
|
snackMessage: ?string,
|
||||||
doToast: ({ message: string }) => void,
|
doToast: ({ message: string }) => void,
|
||||||
label?: string,
|
label?: string,
|
||||||
|
primaryButton?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function CopyableText(props: Props) {
|
export default function CopyableText(props: Props) {
|
||||||
const { copyable, doToast, snackMessage, label } = props;
|
const { copyable, doToast, snackMessage, label, primaryButton = false } = props;
|
||||||
|
|
||||||
const input = useRef();
|
const input = useRef();
|
||||||
|
|
||||||
|
@ -43,7 +44,7 @@ export default function CopyableText(props: Props) {
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
inputButton={
|
inputButton={
|
||||||
<Button
|
<Button
|
||||||
button="inverse"
|
button={primaryButton ? 'primary' : 'secondary'}
|
||||||
icon={ICONS.COPY}
|
icon={ICONS.COPY}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
copyToClipboard();
|
copyToClipboard();
|
||||||
|
|
|
@ -47,17 +47,18 @@ export default function EmbedArea(props: Props) {
|
||||||
label={label}
|
label={label}
|
||||||
value={embedText || ''}
|
value={embedText || ''}
|
||||||
ref={input}
|
ref={input}
|
||||||
onFocus={onFocus}
|
helper={
|
||||||
/>
|
|
||||||
<div className="card__actions card__actions--center">
|
|
||||||
<Button
|
<Button
|
||||||
icon={ICONS.COPY}
|
icon={ICONS.COPY}
|
||||||
button="link"
|
button="link"
|
||||||
|
label={__('Copy')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
copyToClipboard();
|
copyToClipboard();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
}
|
||||||
|
onFocus={onFocus}
|
||||||
|
/>
|
||||||
</fieldset-section>
|
</fieldset-section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ export default function Expandable(props: Props) {
|
||||||
return (
|
return (
|
||||||
<div ref={ref}>
|
<div ref={ref}>
|
||||||
{rect && rect.height > COLLAPSED_HEIGHT ? (
|
{rect && rect.height > COLLAPSED_HEIGHT ? (
|
||||||
<div ref={ref} className="expandable">
|
<div ref={ref}>
|
||||||
<div
|
<div
|
||||||
className={classnames({
|
className={classnames({
|
||||||
'expandable--open': expanded,
|
'expandable--open': expanded,
|
||||||
|
|
|
@ -5,7 +5,6 @@ import * as React from 'react';
|
||||||
import { isURIValid } from 'lbry-redux';
|
import { isURIValid } from 'lbry-redux';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import ClaimLink from 'component/claimLink';
|
import ClaimLink from 'component/claimLink';
|
||||||
import { isLBRYDomain } from 'util/uri';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
href: string,
|
href: string,
|
||||||
|
@ -39,8 +38,7 @@ class ExternalLink extends React.PureComponent<Props> {
|
||||||
label={children}
|
label={children}
|
||||||
className="button--external-link"
|
className="button--external-link"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const isTrusted = isLBRYDomain(href);
|
openModal(MODALS.CONFIRM_EXTERNAL_RESOURCE, { uri: href, isTrusted: false });
|
||||||
openModal(MODALS.CONFIRM_EXTERNAL_RESOURCE, { uri: href, isTrusted: isTrusted });
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,7 +22,7 @@ class FileActions extends React.PureComponent<Props> {
|
||||||
{showDelete && (
|
{showDelete && (
|
||||||
<Tooltip label={__('Remove from your library')}>
|
<Tooltip label={__('Remove from your library')}>
|
||||||
<Button
|
<Button
|
||||||
button="link"
|
button="alt"
|
||||||
icon={ICONS.DELETE}
|
icon={ICONS.DELETE}
|
||||||
description={__('Delete')}
|
description={__('Delete')}
|
||||||
onClick={() => openModal(MODALS.CONFIRM_FILE_REMOVE, { uri })}
|
onClick={() => openModal(MODALS.CONFIRM_FILE_REMOVE, { uri })}
|
||||||
|
@ -31,7 +31,7 @@ class FileActions extends React.PureComponent<Props> {
|
||||||
)}
|
)}
|
||||||
{!claimIsMine && (
|
{!claimIsMine && (
|
||||||
<Tooltip label={__('Report content')}>
|
<Tooltip label={__('Report content')}>
|
||||||
<Button button="link" icon={ICONS.REPORT} href={`https://lbry.com/dmca/${claimId}`} />
|
<Button button="alt" icon={ICONS.REPORT} href={`https://lbry.com/dmca/${claimId}`} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
|
@ -4,8 +4,10 @@ import MarkdownPreview from 'component/common/markdown-preview';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import Expandable from 'component/expandable';
|
import Expandable from 'component/expandable';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import ClaimTags from 'component/claimTags';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
uri: string,
|
||||||
claim: StreamClaim,
|
claim: StreamClaim,
|
||||||
fileInfo: FileListItem,
|
fileInfo: FileListItem,
|
||||||
metadata: StreamMetadata,
|
metadata: StreamMetadata,
|
||||||
|
@ -16,7 +18,7 @@ type Props = {
|
||||||
|
|
||||||
class FileDetails extends PureComponent<Props> {
|
class FileDetails extends PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { claim, contentType, fileInfo, metadata, openFolder } = this.props;
|
const { uri, claim, contentType, fileInfo, metadata, openFolder } = this.props;
|
||||||
|
|
||||||
if (!claim || !metadata) {
|
if (!claim || !metadata) {
|
||||||
return <span className="empty">{__('Empty claim or metadata info.')}</span>;
|
return <span className="empty">{__('Empty claim or metadata info.')}</span>;
|
||||||
|
@ -43,38 +45,38 @@ class FileDetails extends PureComponent<Props> {
|
||||||
{description && (
|
{description && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="media__info-text">
|
<div className="media__info-text">
|
||||||
<MarkdownPreview content={description} promptLinks />
|
<MarkdownPreview content={description} />
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
<div className="media__info-title">Info</div>
|
<ClaimTags uri={uri} type="large" />
|
||||||
<div className="media__info-text">
|
<table className="table table--condensed table--fixed table--file-details">
|
||||||
<div>
|
<tbody>
|
||||||
{__('Content-Type')}
|
<tr>
|
||||||
{': '}
|
<td> {__('Content Type')}</td>
|
||||||
{mediaType}
|
<td>{mediaType}</td>
|
||||||
</div>
|
</tr>
|
||||||
{fileSize && (
|
{fileSize && (
|
||||||
<div>
|
<tr>
|
||||||
{__('File Size')}
|
<td> {__('File Size')}</td>
|
||||||
{': '}
|
<td>{fileSize}</td>
|
||||||
{fileSize}
|
</tr>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
<div>
|
{languages && (
|
||||||
{__('Languages')}
|
<tr>
|
||||||
{': '}
|
<td>{__('Languages')}</td>
|
||||||
{languages ? languages.join(' ') : null}
|
<td>{languages.join(' ')}</td>
|
||||||
</div>
|
</tr>
|
||||||
<div>
|
)}
|
||||||
{__('License')}
|
<tr>
|
||||||
{': '}
|
<td>{__('License')}</td>
|
||||||
{license}
|
<td>{license}</td>
|
||||||
</div>
|
</tr>
|
||||||
{downloadPath && (
|
{downloadPath && (
|
||||||
<div>
|
<tr>
|
||||||
{__('Downloaded to')}
|
<td>{__('Downloaded to')}</td>
|
||||||
{': '}
|
<td>
|
||||||
|
{/* {downloadPath.replace(/(.{10})/g, '$1\u200b')} */}
|
||||||
<Button
|
<Button
|
||||||
button="link"
|
button="link"
|
||||||
className="button--download-link"
|
className="button--download-link"
|
||||||
|
@ -83,11 +85,13 @@ class FileDetails extends PureComponent<Props> {
|
||||||
openFolder(downloadPath);
|
openFolder(downloadPath);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
label={downloadNote || downloadPath}
|
label={downloadNote || downloadPath.replace(/(.{10})/g, '$1\u200b')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</td>
|
||||||
|
</tr>
|
||||||
)}
|
)}
|
||||||
</div>
|
</tbody>
|
||||||
|
</table>
|
||||||
</Expandable>
|
</Expandable>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|
|
@ -32,7 +32,7 @@ function FileDownloadLink(props: Props) {
|
||||||
return (
|
return (
|
||||||
<ToolTip label={__('Open file')}>
|
<ToolTip label={__('Open file')}>
|
||||||
<Button
|
<Button
|
||||||
button="link"
|
button="alt"
|
||||||
icon={ICONS.EXTERNAL}
|
icon={ICONS.EXTERNAL}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
pause();
|
pause();
|
||||||
|
@ -45,7 +45,7 @@ function FileDownloadLink(props: Props) {
|
||||||
return (
|
return (
|
||||||
<ToolTip label={__('Add to your library')}>
|
<ToolTip label={__('Add to your library')}>
|
||||||
<Button
|
<Button
|
||||||
button="link"
|
button="alt"
|
||||||
icon={ICONS.DOWNLOAD}
|
icon={ICONS.DOWNLOAD}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
download(uri);
|
download(uri);
|
||||||
|
|
|
@ -18,10 +18,10 @@ export default function FileProperties(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="file-properties">
|
<div className="file-properties">
|
||||||
|
<FilePrice hideFree uri={uri} />
|
||||||
|
<VideoDuration uri={uri} />
|
||||||
{isSubscribed && <Icon tooltip icon={icons.SUBSCRIBE} />}
|
{isSubscribed && <Icon tooltip icon={icons.SUBSCRIBE} />}
|
||||||
{!claimIsMine && downloaded && <Icon tooltip icon={icons.DOWNLOAD} />}
|
{!claimIsMine && downloaded && <Icon tooltip icon={icons.DOWNLOAD} />}
|
||||||
<FilePrice hideFree uri={uri} />
|
|
||||||
<VideoDuration className="media__subtitle" uri={uri} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import ImageViewer from 'component/viewers/imageViewer';
|
||||||
import AppViewer from 'component/viewers/appViewer';
|
import AppViewer from 'component/viewers/appViewer';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { formatLbryUriForWeb } from 'util/uri';
|
import { formatLbryUrlForWeb } from 'util/url';
|
||||||
// @if TARGET='web'
|
// @if TARGET='web'
|
||||||
import { generateStreamUrl } from 'util/lbrytv';
|
import { generateStreamUrl } from 'util/lbrytv';
|
||||||
// @endif
|
// @endif
|
||||||
|
@ -16,11 +16,11 @@ import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import Yrbl from 'component/yrbl';
|
import Yrbl from 'component/yrbl';
|
||||||
|
|
||||||
// @if TARGET='app'
|
|
||||||
import DocumentViewer from 'component/viewers/documentViewer';
|
import DocumentViewer from 'component/viewers/documentViewer';
|
||||||
import DocxViewer from 'component/viewers/docxViewer';
|
|
||||||
import HtmlViewer from 'component/viewers/htmlViewer';
|
|
||||||
import PdfViewer from 'component/viewers/pdfViewer';
|
import PdfViewer from 'component/viewers/pdfViewer';
|
||||||
|
import HtmlViewer from 'component/viewers/htmlViewer';
|
||||||
|
// @if TARGET='app'
|
||||||
|
import DocxViewer from 'component/viewers/docxViewer';
|
||||||
import ComicBookViewer from 'component/viewers/comicBookViewer';
|
import ComicBookViewer from 'component/viewers/comicBookViewer';
|
||||||
import ThreeViewer from 'component/viewers/threeViewer';
|
import ThreeViewer from 'component/viewers/threeViewer';
|
||||||
// @endif
|
// @endif
|
||||||
|
@ -73,7 +73,7 @@ class FileRender extends React.PureComponent<Props> {
|
||||||
onEndedCb() {
|
onEndedCb() {
|
||||||
const { autoplay, nextUnplayed, history } = this.props;
|
const { autoplay, nextUnplayed, history } = this.props;
|
||||||
if (autoplay && nextUnplayed) {
|
if (autoplay && nextUnplayed) {
|
||||||
history.push(formatLbryUriForWeb(nextUnplayed));
|
history.push(formatLbryUrlForWeb(nextUnplayed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,26 +102,39 @@ class FileRender extends React.PureComponent<Props> {
|
||||||
// Add routes to viewer...
|
// Add routes to viewer...
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Supported contentTypes
|
||||||
|
const contentTypes = {
|
||||||
|
'application/x-ext-mkv': (
|
||||||
|
<VideoViewer uri={uri} source={source} contentType={contentType} onEndedCB={this.onEndedCb} />
|
||||||
|
),
|
||||||
|
'video/x-matroska': (
|
||||||
|
<VideoViewer uri={uri} source={source} contentType={contentType} onEndedCB={this.onEndedCb} />
|
||||||
|
),
|
||||||
|
'application/pdf': <PdfViewer source={downloadPath || source} />,
|
||||||
|
'text/html': <HtmlViewer source={downloadPath || source} />,
|
||||||
|
'text/htm': <HtmlViewer source={downloadPath || source} />,
|
||||||
|
};
|
||||||
|
|
||||||
// Supported fileType
|
// Supported fileType
|
||||||
const fileTypes = {
|
const fileTypes = {
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
pdf: <PdfViewer source={downloadPath} />,
|
|
||||||
docx: <DocxViewer source={downloadPath} />,
|
docx: <DocxViewer source={downloadPath} />,
|
||||||
html: <HtmlViewer source={downloadPath} />,
|
|
||||||
htm: <HtmlViewer source={downloadPath} />,
|
|
||||||
// @endif
|
// @endif
|
||||||
// Add routes to viewer...
|
// Add routes to viewer...
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check for a valid fileType or mediaType
|
// Check for a valid fileType, mediaType, or contentType
|
||||||
let viewer = (fileType && fileTypes[fileType]) || mediaTypes[mediaType];
|
let viewer = (fileType && fileTypes[fileType]) || mediaTypes[mediaType] || contentTypes[contentType];
|
||||||
|
|
||||||
// Check for Human-readable files
|
// Check for Human-readable files
|
||||||
if (!viewer && readableFiles.includes(mediaType)) {
|
if (!viewer && readableFiles.includes(mediaType)) {
|
||||||
viewer = (
|
viewer = (
|
||||||
<DocumentViewer
|
<DocumentViewer
|
||||||
source={{
|
source={{
|
||||||
stream: options => fs.createReadStream(downloadPath, options),
|
// @if TARGET='app'
|
||||||
|
file: options => fs.createReadStream(downloadPath, options),
|
||||||
|
// @endif
|
||||||
|
stream: source,
|
||||||
fileType,
|
fileType,
|
||||||
contentType,
|
contentType,
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
makeSelectThumbnailForUri,
|
makeSelectThumbnailForUri,
|
||||||
makeSelectStreamingUrlForUri,
|
makeSelectStreamingUrlForUri,
|
||||||
makeSelectMediaTypeForUri,
|
makeSelectMediaTypeForUri,
|
||||||
|
makeSelectContentTypeForUri,
|
||||||
makeSelectUriIsStreamable,
|
makeSelectUriIsStreamable,
|
||||||
makeSelectTitleForUri,
|
makeSelectTitleForUri,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
|
@ -23,6 +24,7 @@ const select = (state, props) => {
|
||||||
title: makeSelectTitleForUri(uri)(state),
|
title: makeSelectTitleForUri(uri)(state),
|
||||||
thumbnail: makeSelectThumbnailForUri(uri)(state),
|
thumbnail: makeSelectThumbnailForUri(uri)(state),
|
||||||
mediaType: makeSelectMediaTypeForUri(uri)(state),
|
mediaType: makeSelectMediaTypeForUri(uri)(state),
|
||||||
|
contentType: makeSelectContentTypeForUri(uri)(state),
|
||||||
fileInfo: makeSelectFileInfoForUri(uri)(state),
|
fileInfo: makeSelectFileInfoForUri(uri)(state),
|
||||||
obscurePreview: makeSelectShouldObscurePreview(uri)(state),
|
obscurePreview: makeSelectShouldObscurePreview(uri)(state),
|
||||||
isPlaying: makeSelectIsPlaying(uri)(state),
|
isPlaying: makeSelectIsPlaying(uri)(state),
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { onFullscreenChange } from 'util/full-screen';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
mediaType: string,
|
mediaType: string,
|
||||||
|
contentType: string,
|
||||||
isLoading: boolean,
|
isLoading: boolean,
|
||||||
isPlaying: boolean,
|
isPlaying: boolean,
|
||||||
fileInfo: FileListItem,
|
fileInfo: FileListItem,
|
||||||
|
@ -47,6 +48,7 @@ export default function FileViewer(props: Props) {
|
||||||
triggerAnalyticsView,
|
triggerAnalyticsView,
|
||||||
claimRewards,
|
claimRewards,
|
||||||
mediaType,
|
mediaType,
|
||||||
|
contentType,
|
||||||
} = props;
|
} = props;
|
||||||
const [playTime, setPlayTime] = useState();
|
const [playTime, setPlayTime] = useState();
|
||||||
const [fileViewerRect, setFileViewerRect] = usePersistedState('inline-file-viewer:rect');
|
const [fileViewerRect, setFileViewerRect] = usePersistedState('inline-file-viewer:rect');
|
||||||
|
@ -56,7 +58,12 @@ export default function FileViewer(props: Props) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const inline = pageUri === uri;
|
const inline = pageUri === uri;
|
||||||
const isReadyToPlay = (IS_WEB && isStreamable) || (isStreamable && streamingUrl) || (fileInfo && fileInfo.completed);
|
const forceVideo = ['application/x-ext-mkv', 'video/x-matroska'].includes(contentType);
|
||||||
|
const webStreamOnly = contentType === 'application/pdf' || mediaType === 'text';
|
||||||
|
const isReadyToPlay =
|
||||||
|
(IS_WEB && (isStreamable || webStreamOnly || forceVideo)) ||
|
||||||
|
((isStreamable || forceVideo) && streamingUrl) ||
|
||||||
|
(fileInfo && fileInfo.completed);
|
||||||
const loadingMessage =
|
const loadingMessage =
|
||||||
!isStreamable && fileInfo && fileInfo.blobs_completed >= 1 && (!fileInfo.download_path || !fileInfo.written_bytes)
|
!isStreamable && fileInfo && fileInfo.blobs_completed >= 1 && (!fileInfo.download_path || !fileInfo.written_bytes)
|
||||||
? __("It looks like you deleted or moved this file. We're rebuilding it now. It will only take a few seconds.")
|
? __("It looks like you deleted or moved this file. We're rebuilding it now. It will only take a few seconds.")
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
makeSelectThumbnailForUri,
|
makeSelectThumbnailForUri,
|
||||||
makeSelectStreamingUrlForUri,
|
makeSelectStreamingUrlForUri,
|
||||||
makeSelectMediaTypeForUri,
|
makeSelectMediaTypeForUri,
|
||||||
|
makeSelectContentTypeForUri,
|
||||||
makeSelectUriIsStreamable,
|
makeSelectUriIsStreamable,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { makeSelectCostInfoForUri } from 'lbryinc';
|
import { makeSelectCostInfoForUri } from 'lbryinc';
|
||||||
|
@ -16,6 +17,7 @@ import FileViewer from './view';
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
|
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
|
||||||
mediaType: makeSelectMediaTypeForUri(props.uri)(state),
|
mediaType: makeSelectMediaTypeForUri(props.uri)(state),
|
||||||
|
contentType: makeSelectContentTypeForUri(props.uri)(state),
|
||||||
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
obscurePreview: makeSelectShouldObscurePreview(props.uri)(state),
|
obscurePreview: makeSelectShouldObscurePreview(props.uri)(state),
|
||||||
isPlaying: makeSelectIsPlaying(props.uri)(state),
|
isPlaying: makeSelectIsPlaying(props.uri)(state),
|
||||||
|
|
|
@ -14,6 +14,7 @@ const SPACE_BAR_KEYCODE = 32;
|
||||||
type Props = {
|
type Props = {
|
||||||
play: string => void,
|
play: string => void,
|
||||||
mediaType: string,
|
mediaType: string,
|
||||||
|
contentType: string,
|
||||||
isLoading: boolean,
|
isLoading: boolean,
|
||||||
isPlaying: boolean,
|
isPlaying: boolean,
|
||||||
fileInfo: FileListItem,
|
fileInfo: FileListItem,
|
||||||
|
@ -31,6 +32,7 @@ export default function FileViewer(props: Props) {
|
||||||
const {
|
const {
|
||||||
play,
|
play,
|
||||||
mediaType,
|
mediaType,
|
||||||
|
contentType,
|
||||||
isPlaying,
|
isPlaying,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
uri,
|
uri,
|
||||||
|
@ -43,9 +45,11 @@ export default function FileViewer(props: Props) {
|
||||||
costInfo,
|
costInfo,
|
||||||
} = props;
|
} = props;
|
||||||
const cost = costInfo && costInfo.cost;
|
const cost = costInfo && costInfo.cost;
|
||||||
const isPlayable = ['audio', 'video'].includes(mediaType);
|
const forceVideo = ['application/x-ext-mkv', 'video/x-matroska'].includes(contentType);
|
||||||
|
const isPlayable = ['audio', 'video'].includes(mediaType) || forceVideo;
|
||||||
const fileStatus = fileInfo && fileInfo.status;
|
const fileStatus = fileInfo && fileInfo.status;
|
||||||
const supported = (IS_WEB && isStreamable) || !IS_WEB;
|
const webStreamOnly = contentType === 'application/pdf' || mediaType === 'text';
|
||||||
|
const supported = (IS_WEB && (isStreamable || webStreamOnly || forceVideo)) || !IS_WEB;
|
||||||
|
|
||||||
// Wrap this in useCallback because we need to use it to the keyboard effect
|
// Wrap this in useCallback because we need to use it to the keyboard effect
|
||||||
// If we don't a new instance will be created for every render and react will think the dependencies have changed, which will add/remove the listener for every render
|
// If we don't a new instance will be created for every render and react will think the dependencies have changed, which will add/remove the listener for every render
|
||||||
|
|
|
@ -78,6 +78,9 @@ const Header = (props: Props) => {
|
||||||
<header
|
<header
|
||||||
className={classnames('header', {
|
className={classnames('header', {
|
||||||
'header--minimal': authHeader,
|
'header--minimal': authHeader,
|
||||||
|
// @if TARGET='web'
|
||||||
|
'header--noauth-web': !authenticated,
|
||||||
|
// @endif
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
'header--mac': IS_MAC,
|
'header--mac': IS_MAC,
|
||||||
// @endif
|
// @endif
|
||||||
|
@ -118,8 +121,8 @@ const Header = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!authHeader ? (
|
{!authHeader ? (
|
||||||
<div className={classnames('header__menu', { 'header__menu--small': IS_WEB && !authenticated })}>
|
<div className={classnames('header__menu', { 'header__menu--with-balance': !IS_WEB || authenticated })}>
|
||||||
{!IS_WEB || authenticated ? (
|
{(!IS_WEB || authenticated) && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuButton className="header__navigation-item menu__title">{getWalletTitle()}</MenuButton>
|
<MenuButton className="header__navigation-item menu__title">{getWalletTitle()}</MenuButton>
|
||||||
|
@ -135,7 +138,7 @@ const Header = (props: Props) => {
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuButton className="header__navigation-item menu__title">
|
<MenuButton className="header__navigation-item menu__title header__navigation-item--icon">
|
||||||
<Icon size={18} icon={ICONS.ACCOUNT} />
|
<Icon size={18} icon={ICONS.ACCOUNT} />
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList className="menu__list--header">
|
<MenuList className="menu__list--header">
|
||||||
|
@ -161,9 +164,10 @@ const Header = (props: Props) => {
|
||||||
)}
|
)}
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuButton className="header__navigation-item menu__title">
|
<MenuButton className="header__navigation-item menu__title header__navigation-item--icon">
|
||||||
<Icon size={18} icon={ICONS.SETTINGS} />
|
<Icon size={18} icon={ICONS.SETTINGS} />
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList className="menu__list--header">
|
<MenuList className="menu__list--header">
|
||||||
|
@ -181,8 +185,7 @@ const Header = (props: Props) => {
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
</Fragment>
|
{IS_WEB && !authenticated && (
|
||||||
) : (
|
|
||||||
<Button navigate={`/$/${PAGES.AUTH}`} button="primary" label={__('Sign In')} />
|
<Button navigate={`/$/${PAGES.AUTH}`} button="primary" label={__('Sign In')} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -193,7 +196,7 @@ const Header = (props: Props) => {
|
||||||
{/* This pushes the close button to the right side */}
|
{/* This pushes the close button to the right side */}
|
||||||
<span />
|
<span />
|
||||||
<Tooltip label={__('Go Back')}>
|
<Tooltip label={__('Go Back')}>
|
||||||
<Button icon={ICONS.REMOVE} {...closeButtonNavigationProps} />
|
<Button button="link" icon={ICONS.REMOVE} {...closeButtonNavigationProps} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -37,8 +37,12 @@ class InviteList extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="table__header">
|
<div className="table__header">
|
||||||
<h2 className="card__title--between">
|
<div className="table__header-text--between">
|
||||||
{__('Invite History')}
|
<div>
|
||||||
|
<h2 className="card__title">{__('Invite History')}</h2>
|
||||||
|
<p className="section__subtitle">{rewardHelp}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{referralReward && showClaimable && (
|
{referralReward && showClaimable && (
|
||||||
<RewardLink
|
<RewardLink
|
||||||
button
|
button
|
||||||
|
@ -46,8 +50,7 @@ class InviteList extends React.PureComponent<Props> {
|
||||||
reward_type={rewards.TYPE_REFERRAL}
|
reward_type={rewards.TYPE_REFERRAL}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</h2>
|
</div>
|
||||||
<p className="section__subtitle">{rewardHelp}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table className="table section">
|
<table className="table section">
|
||||||
|
|
|
@ -5,17 +5,18 @@ import { Form, FormField } from 'component/common/form';
|
||||||
import CopyableText from 'component/copyableText';
|
import CopyableText from 'component/copyableText';
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
|
|
||||||
type FormProps = {
|
|
||||||
inviteNew: string => void,
|
|
||||||
errorMessage: ?string,
|
|
||||||
isPending: boolean,
|
|
||||||
};
|
|
||||||
|
|
||||||
type FormState = {
|
type FormState = {
|
||||||
email: string,
|
email: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
class FormInviteNew extends React.PureComponent<FormProps, FormState> {
|
type Props = {
|
||||||
|
errorMessage: ?string,
|
||||||
|
inviteNew: string => void,
|
||||||
|
isPending: boolean,
|
||||||
|
referralLink: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
class InviteNew extends React.PureComponent<Props, FormState> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ class FormInviteNew extends React.PureComponent<FormProps, FormState> {
|
||||||
(this: any).handleSubmit = this.handleSubmit.bind(this);
|
(this: any).handleSubmit = this.handleSubmit.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEmailChanged(event) {
|
handleEmailChanged(event: any) {
|
||||||
this.setState({
|
this.setState({
|
||||||
email: event.target.value,
|
email: event.target.value,
|
||||||
});
|
});
|
||||||
|
@ -38,9 +39,14 @@ class FormInviteNew extends React.PureComponent<FormProps, FormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { errorMessage, isPending } = this.props;
|
const { errorMessage, isPending, referralLink } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Card
|
||||||
|
title={__('Invite a Friend')}
|
||||||
|
subtitle={__('When your friends start using LBRY, the network gets stronger!')}
|
||||||
|
actions={
|
||||||
|
<React.Fragment>
|
||||||
<Form onSubmit={this.handleSubmit}>
|
<Form onSubmit={this.handleSubmit}>
|
||||||
<FormField
|
<FormField
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -50,41 +56,13 @@ class FormInviteNew extends React.PureComponent<FormProps, FormState> {
|
||||||
value={this.state.email}
|
value={this.state.email}
|
||||||
error={errorMessage}
|
error={errorMessage}
|
||||||
inputButton={
|
inputButton={
|
||||||
<Button button="inverse" type="submit" label="Invite" disabled={isPending || !this.state.email} />
|
<Button button="secondary" type="submit" label="Invite" disabled={isPending || !this.state.email} />
|
||||||
}
|
}
|
||||||
onChange={event => {
|
onChange={event => {
|
||||||
this.handleEmailChanged(event);
|
this.handleEmailChanged(event);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
errorMessage: ?string,
|
|
||||||
inviteNew: string => void,
|
|
||||||
isPending: boolean,
|
|
||||||
rewardAmount: number,
|
|
||||||
referralLink: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
class InviteNew extends React.PureComponent<Props> {
|
|
||||||
render() {
|
|
||||||
const { errorMessage, inviteNew, isPending, rewardAmount, referralLink } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
title={__('Invite a Friend')}
|
|
||||||
subtitle={__('When your friends start using LBRY, the network gets stronger!')}
|
|
||||||
actions={
|
|
||||||
<React.Fragment>
|
|
||||||
<FormInviteNew
|
|
||||||
errorMessage={errorMessage}
|
|
||||||
inviteNew={inviteNew}
|
|
||||||
isPending={isPending}
|
|
||||||
rewardAmount={rewardAmount}
|
|
||||||
/>
|
|
||||||
<CopyableText label={__('Or share this link with your friends')} copyable={referralLink} />
|
<CopyableText label={__('Or share this link with your friends')} copyable={referralLink} />
|
||||||
|
|
||||||
<p className="help">
|
<p className="help">
|
||||||
|
@ -93,6 +71,7 @@ class InviteNew extends React.PureComponent<Props> {
|
||||||
<Button button="link" label={__('FAQ')} href="https://lbry.com/faq/referrals" />{' '}
|
<Button button="link" label={__('FAQ')} href="https://lbry.com/faq/referrals" />{' '}
|
||||||
{__('to learn more about referrals')}.
|
{__('to learn more about referrals')}.
|
||||||
</p>
|
</p>
|
||||||
|
</Form>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -106,7 +106,9 @@ class UserHistoryPage extends React.PureComponent<Props, State> {
|
||||||
) : (
|
) : (
|
||||||
<div className="main--empty">
|
<div className="main--empty">
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<h2 className="card__title">{__('Your history is empty, what are you doing here?')}</h2>
|
<h2 className="card__title card__title--deprecated">
|
||||||
|
{__('Your history is empty, what are you doing here?')}
|
||||||
|
</h2>
|
||||||
|
|
||||||
<div className="card__actions card__actions--center">
|
<div className="card__actions card__actions--center">
|
||||||
<Button button="primary" navigate="/" label={__('Explore new content')} />
|
<Button button="primary" navigate="/" label={__('Explore new content')} />
|
||||||
|
|
|
@ -5,7 +5,7 @@ import classnames from 'classnames';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import { FormField } from 'component/common/form';
|
import { FormField } from 'component/common/form';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { formatLbryUriForWeb } from 'util/uri';
|
import { formatLbryUrlForWeb } from 'util/url';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
lastViewed: number,
|
lastViewed: number,
|
||||||
|
@ -34,14 +34,12 @@ class NavigationHistoryItem extends React.PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { lastViewed, selected, onSelect, claim, uri, slim, history } = this.props;
|
const { lastViewed, selected, onSelect, claim, uri, slim, history } = this.props;
|
||||||
|
|
||||||
let name;
|
|
||||||
let title;
|
let title;
|
||||||
if (claim && claim.value) {
|
if (claim && claim.value) {
|
||||||
({ name } = claim);
|
|
||||||
({ title } = claim.value);
|
({ title } = claim.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigatePath = formatLbryUriForWeb(uri);
|
const navigatePath = formatLbryUrlForWeb(uri);
|
||||||
const onClick =
|
const onClick =
|
||||||
onSelect ||
|
onSelect ||
|
||||||
function() {
|
function() {
|
||||||
|
@ -58,7 +56,7 @@ class NavigationHistoryItem extends React.PureComponent<Props> {
|
||||||
>
|
>
|
||||||
{!slim && <FormField checked={selected} type="checkbox" onChange={onSelect} />}
|
{!slim && <FormField checked={selected} type="checkbox" onChange={onSelect} />}
|
||||||
<span className="time time--ago">{moment(lastViewed).from(moment())}</span>
|
<span className="time time--ago">{moment(lastViewed).from(moment())}</span>
|
||||||
<Button className="item-list__element" constrict button="link" label={uri} navigate={uri} />
|
<Button className="item-list__element" button="link" label={uri} navigate={uri} />
|
||||||
<span className="item-list__element">{title}</span>
|
<span className="item-list__element">{title}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import UriIndicator from 'component/uriIndicator';
|
||||||
import TruncatedText from 'component/common/truncated-text';
|
import TruncatedText from 'component/common/truncated-text';
|
||||||
import MarkdownPreview from 'component/common/markdown-preview';
|
import MarkdownPreview from 'component/common/markdown-preview';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { formatLbryUriForWeb } from 'util/uri';
|
import { formatLbryUrlForWeb } from 'util/url';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
|
@ -17,7 +17,7 @@ type Props = {
|
||||||
class PreviewLink extends React.PureComponent<Props> {
|
class PreviewLink extends React.PureComponent<Props> {
|
||||||
handleClick = () => {
|
handleClick = () => {
|
||||||
const { uri, history } = this.props;
|
const { uri, history } = this.props;
|
||||||
history.push(formatLbryUriForWeb(uri));
|
history.push(formatLbryUrlForWeb(uri));
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -44,7 +44,7 @@ class PreviewLink extends React.PureComponent<Props> {
|
||||||
<span className={'claim-preview-properties'}>
|
<span className={'claim-preview-properties'}>
|
||||||
<span className={'preview-link__description media__subtitle'}>
|
<span className={'preview-link__description media__subtitle'}>
|
||||||
<TruncatedText lines={2} showTooltip={false}>
|
<TruncatedText lines={2} showTooltip={false}>
|
||||||
<MarkdownPreview content={description} promptLinks strip />
|
<MarkdownPreview content={description} strip />
|
||||||
</TruncatedText>
|
</TruncatedText>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -74,6 +74,7 @@ function PublishFile(props: Props) {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
|
actionIconPadding={false}
|
||||||
icon={ICONS.PUBLISH}
|
icon={ICONS.PUBLISH}
|
||||||
disabled={disabled || balance === 0}
|
disabled={disabled || balance === 0}
|
||||||
title={title}
|
title={title}
|
||||||
|
|
|
@ -138,12 +138,12 @@ function PublishForm(props: Props) {
|
||||||
<Card actions={<SelectThumbnail />} />
|
<Card actions={<SelectThumbnail />} />
|
||||||
|
|
||||||
<TagsSelect
|
<TagsSelect
|
||||||
title={__('Add Tags')}
|
|
||||||
suggestMature
|
suggestMature
|
||||||
disableAutoFocus
|
disableAutoFocus
|
||||||
help={__('The better your tags are, the easier it will be for people to discover your content.')}
|
hideHeader
|
||||||
|
label={__('Selected Tags')}
|
||||||
empty={__('No tags added')}
|
empty={__('No tags added')}
|
||||||
placeholder={__('Add a tag')}
|
placeholder={__('Add a tag...')}
|
||||||
onSelect={newTags => {
|
onSelect={newTags => {
|
||||||
const validatedTags = [];
|
const validatedTags = [];
|
||||||
newTags.forEach(newTag => {
|
newTags.forEach(newTag => {
|
||||||
|
|
|
@ -57,7 +57,7 @@ function PublishName(props: Props) {
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const totalAvailableBidAmount = previousBidAmount + balance;
|
const totalAvailableBidAmount = previousBidAmount ? previousBidAmount + balance : balance;
|
||||||
|
|
||||||
let bidError;
|
let bidError;
|
||||||
if (bid === 0) {
|
if (bid === 0) {
|
||||||
|
|
|
@ -25,14 +25,16 @@ const RewardListClaimed = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<header className="table__header">
|
<header className="table__header">
|
||||||
<h2 className="card__title">{__('Claimed Rewards')}</h2>
|
<div className="table__header-text">
|
||||||
|
<h2 className="card__title card__title--deprecated">{__('Claimed Rewards')}</h2>
|
||||||
|
|
||||||
<p className="card__subtitle">
|
<p className="section__subtitle">
|
||||||
{__(
|
{__(
|
||||||
'Reward history is tied to your email. In case of lost or multiple wallets, your balance may differ from the amounts claimed'
|
'Reward history is tied to your email. In case of lost or multiple wallets, your balance may differ from the amounts claimed'
|
||||||
)}
|
)}
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<table className="table table--rewards">
|
<table className="table table--rewards">
|
||||||
|
|
|
@ -25,6 +25,7 @@ class RewardSummary extends React.Component<Props> {
|
||||||
tokens={{
|
tokens={{
|
||||||
credit_amount: <CreditAmount inheritStyle amount={unclaimedRewardAmount} precision={8} />,
|
credit_amount: <CreditAmount inheritStyle amount={unclaimedRewardAmount} precision={8} />,
|
||||||
}}
|
}}
|
||||||
|
f
|
||||||
>
|
>
|
||||||
You have %credit_amount% in unclaimed rewards.
|
You have %credit_amount% in unclaimed rewards.
|
||||||
</I18nMessage>
|
</I18nMessage>
|
||||||
|
@ -34,14 +35,14 @@ class RewardSummary extends React.Component<Props> {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
<div className="section__actions">
|
<React.Fragment>
|
||||||
<Button
|
<Button
|
||||||
button="primary"
|
button="primary"
|
||||||
navigate="/$/rewards"
|
navigate="/$/rewards"
|
||||||
label={hasRewards ? __('Claim Rewards') : __('View Rewards')}
|
label={hasRewards ? __('Claim Rewards') : __('View Rewards')}
|
||||||
/>
|
/>
|
||||||
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/rewards" />.
|
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/rewards" />
|
||||||
</div>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import React, { useEffect, lazy, Suspense } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Route, Redirect, Switch, withRouter } from 'react-router-dom';
|
import { Route, Redirect, Switch, withRouter } from 'react-router-dom';
|
||||||
import SettingsPage from 'page/settings';
|
import SettingsPage from 'page/settings';
|
||||||
import HelpPage from 'page/help';
|
import HelpPage from 'page/help';
|
||||||
|
@ -17,8 +17,6 @@ import InvitePage from 'page/invite';
|
||||||
import SearchPage from 'page/search';
|
import SearchPage from 'page/search';
|
||||||
import LibraryPage from 'page/library';
|
import LibraryPage from 'page/library';
|
||||||
import WalletPage from 'page/wallet';
|
import WalletPage from 'page/wallet';
|
||||||
import WalletSendPage from 'page/walletSend';
|
|
||||||
import WalletReceivePage from 'page/walletReceive';
|
|
||||||
import TagsPage from 'page/tags';
|
import TagsPage from 'page/tags';
|
||||||
import FollowingPage from 'page/following';
|
import FollowingPage from 'page/following';
|
||||||
import ListBlockedPage from 'page/listBlocked';
|
import ListBlockedPage from 'page/listBlocked';
|
||||||
|
@ -71,7 +69,6 @@ function AppRouter(props: Props) {
|
||||||
}, [currentScroll, pathname]);
|
}, [currentScroll, pathname]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<span>LODIFJDSLKJFSLDKJFLDJ</span>}>
|
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/" exact component={DiscoverPage} />
|
<Route path="/" exact component={DiscoverPage} />
|
||||||
<Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} />
|
<Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} />
|
||||||
|
@ -80,6 +77,7 @@ function AppRouter(props: Props) {
|
||||||
<Route path={`/$/${PAGES.HELP}`} exact component={HelpPage} />
|
<Route path={`/$/${PAGES.HELP}`} exact component={HelpPage} />
|
||||||
<Route path={`/$/${PAGES.AUTH_VERIFY}`} exact component={SignInVerifyPage} />
|
<Route path={`/$/${PAGES.AUTH_VERIFY}`} exact component={SignInVerifyPage} />
|
||||||
<Route path={`/$/${PAGES.SEARCH}`} exact component={SearchPage} />
|
<Route path={`/$/${PAGES.SEARCH}`} exact component={SearchPage} />
|
||||||
|
<Route path={`/$/${PAGES.SETTINGS}`} exact component={SettingsPage} />
|
||||||
|
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.INVITE}`} component={InvitePage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.INVITE}`} component={InvitePage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.DOWNLOADED}`} component={FileListDownloaded} />
|
<PrivateRoute {...props} path={`/$/${PAGES.DOWNLOADED}`} component={FileListDownloaded} />
|
||||||
|
@ -87,15 +85,12 @@ function AppRouter(props: Props) {
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.PUBLISH}`} component={PublishPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.PUBLISH}`} component={PublishPage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.REPORT}`} component={ReportPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.REPORT}`} component={ReportPage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.REWARDS}`} component={RewardsPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.REWARDS}`} component={RewardsPage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.SETTINGS}`} component={SettingsPage} />
|
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.TRANSACTIONS}`} component={TransactionHistoryPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.TRANSACTIONS}`} component={TransactionHistoryPage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.LIBRARY}`} component={LibraryPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.LIBRARY}`} component={LibraryPage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.ACCOUNT}`} component={AccountPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.ACCOUNT}`} component={AccountPage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.FOLLOWING}`} component={FollowingPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.FOLLOWING}`} component={FollowingPage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.BLOCKED}`} component={ListBlockedPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.BLOCKED}`} component={ListBlockedPage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.WALLET}`} exact component={WalletPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.WALLET}`} exact component={WalletPage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.WALLET_SEND}`} exact component={WalletSendPage} />
|
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.WALLET_RECEIVE}`} exact component={WalletReceivePage} />
|
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.CHANNELS}`} component={ChannelsPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.CHANNELS}`} component={ChannelsPage} />
|
||||||
|
|
||||||
{/* Below need to go at the end to make sure we don't match any of our pages first */}
|
{/* Below need to go at the end to make sure we don't match any of our pages first */}
|
||||||
|
@ -103,7 +98,6 @@ function AppRouter(props: Props) {
|
||||||
<Route path="/:claimName/:streamName" exact component={ShowPage} />
|
<Route path="/:claimName/:streamName" exact component={ShowPage} />
|
||||||
<Route path="/*" component={FourOhFourPage} />
|
<Route path="/*" component={FourOhFourPage} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</Suspense>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ const SearchOptions = (props: Props) => {
|
||||||
{expanded && (
|
{expanded && (
|
||||||
<Form className="search__options">
|
<Form className="search__options">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend className="search__legend--1">{__('Search For')}</legend>
|
<legend className="search__legend">{__('Search For')}</legend>
|
||||||
{[
|
{[
|
||||||
{
|
{
|
||||||
option: SEARCH_OPTIONS.INCLUDE_FILES,
|
option: SEARCH_OPTIONS.INCLUDE_FILES,
|
||||||
|
@ -55,7 +55,7 @@ const SearchOptions = (props: Props) => {
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend className="search__legend--2">{__('File Types')}</legend>
|
<legend className="search__legend">{__('File Types')}</legend>
|
||||||
{[
|
{[
|
||||||
{
|
{
|
||||||
option: SEARCH_OPTIONS.MEDIA_VIDEO,
|
option: SEARCH_OPTIONS.MEDIA_VIDEO,
|
||||||
|
@ -92,7 +92,7 @@ const SearchOptions = (props: Props) => {
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend className="search__legend--3">{__('Other Options')}</legend>
|
<legend className="search__legend">{__('Other Options')}</legend>
|
||||||
<FormField
|
<FormField
|
||||||
type="select"
|
type="select"
|
||||||
name="result-count"
|
name="result-count"
|
||||||
|
|
|
@ -6,7 +6,7 @@ import BusyIndicator from 'component/common/busy-indicator';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
|
|
||||||
import { CHANNEL_NEW, CHANNEL_ANONYMOUS, INVALID_NAME_ERROR } from 'constants/claim';
|
import { CHANNEL_NEW, CHANNEL_ANONYMOUS, MINIMUM_PUBLISH_BID, INVALID_NAME_ERROR } from 'constants/claim';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
channel: string, // currently selected channel
|
channel: string, // currently selected channel
|
||||||
|
@ -102,6 +102,8 @@ class ChannelSection extends React.PureComponent<Props, State> {
|
||||||
newChannelBidError = __('Please decrease your deposit to account for transaction fees');
|
newChannelBidError = __('Please decrease your deposit to account for transaction fees');
|
||||||
} else if (newChannelBid > balance) {
|
} else if (newChannelBid > balance) {
|
||||||
newChannelBidError = __('Deposit cannot be higher than your balance');
|
newChannelBidError = __('Deposit cannot be higher than your balance');
|
||||||
|
} else if (newChannelBid < MINIMUM_PUBLISH_BID) {
|
||||||
|
newChannelBidError = __('Your deposit must be higher');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|
|
@ -5,7 +5,6 @@ import React from 'react';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import Tag from 'component/tag';
|
import Tag from 'component/tag';
|
||||||
import StickyBox from 'react-sticky-box/dist/esnext';
|
import StickyBox from 'react-sticky-box/dist/esnext';
|
||||||
import 'css-doodle';
|
|
||||||
import Spinner from 'component/spinner';
|
import Spinner from 'component/spinner';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -32,7 +31,6 @@ function SideBar(props: Props) {
|
||||||
<div className="card navigation--placeholder">
|
<div className="card navigation--placeholder">
|
||||||
<div className="wrap">
|
<div className="wrap">
|
||||||
<h2>LBRY</h2>
|
<h2>LBRY</h2>
|
||||||
|
|
||||||
<p>{__('The best decentralized content platform on the web.')}</p>
|
<p>{__('The best decentralized content platform on the web.')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,7 +7,6 @@ import EmbedArea from 'component/embedArea';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
claim: Claim,
|
claim: Claim,
|
||||||
onDone: () => void,
|
|
||||||
webShareable: boolean,
|
webShareable: boolean,
|
||||||
isChannel: boolean,
|
isChannel: boolean,
|
||||||
};
|
};
|
||||||
|
@ -28,7 +27,7 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { claim, isChannel } = this.props;
|
const { claim, isChannel } = this.props;
|
||||||
const { canonical_url: canonicalUrl, permanent_url: permanentUrl } = claim;
|
const { canonical_url: canonicalUrl, permanent_url: permanentUrl } = claim;
|
||||||
const { webShareable, onDone } = this.props;
|
const { webShareable } = this.props;
|
||||||
const OPEN_URL = 'https://open.lbry.com/';
|
const OPEN_URL = 'https://open.lbry.com/';
|
||||||
const lbryUrl = canonicalUrl ? canonicalUrl.split('lbry://')[1] : permanentUrl.split('lbry://')[1];
|
const lbryUrl = canonicalUrl ? canonicalUrl.split('lbry://')[1] : permanentUrl.split('lbry://')[1];
|
||||||
const lbryWebUrl = lbryUrl.replace(/#/g, ':');
|
const lbryWebUrl = lbryUrl.replace(/#/g, ':');
|
||||||
|
@ -41,13 +40,13 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<CopyableText label={__('LBRY App Link')} copyable={lbryURL} noSnackbar />
|
<CopyableText label={__('LBRY App Link')} copyable={lbryURL} noSnackbar />
|
||||||
<div className="card__actions card__actions--center">
|
<div className="">
|
||||||
<Button
|
<Button
|
||||||
icon={ICONS.FACEBOOK}
|
icon={ICONS.FACEBOOK}
|
||||||
button="link"
|
button="link"
|
||||||
description={shareOnFb}
|
description={shareOnFb}
|
||||||
href={`https://facebook.com/sharer/sharer.php?u=${encodedLbryURL}`}
|
href={`https://facebook.com/sharer/sharer.php?u=${encodedLbryURL}`}
|
||||||
/>
|
/>{' '}
|
||||||
<Button
|
<Button
|
||||||
icon={ICONS.TWITTER}
|
icon={ICONS.TWITTER}
|
||||||
button="link"
|
button="link"
|
||||||
|
@ -56,9 +55,6 @@ class SocialShare extends React.PureComponent<Props> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{webShareable && !isChannel && <EmbedArea label={__('Embedded')} claim={claim} noSnackbar />}
|
{webShareable && !isChannel && <EmbedArea label={__('Embedded')} claim={claim} noSnackbar />}
|
||||||
<div className="card__actions">
|
|
||||||
<Button button="link" label={__('Done')} onClick={onDone} />
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import ModalWalletUnlock from 'modal/modalWalletUnlock';
|
||||||
import ModalIncompatibleDaemon from 'modal/modalIncompatibleDaemon';
|
import ModalIncompatibleDaemon from 'modal/modalIncompatibleDaemon';
|
||||||
import ModalUpgrade from 'modal/modalUpgrade';
|
import ModalUpgrade from 'modal/modalUpgrade';
|
||||||
import ModalDownloading from 'modal/modalDownloading';
|
import ModalDownloading from 'modal/modalDownloading';
|
||||||
|
import Card from 'component/common/card';
|
||||||
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import 'css-doodle';
|
import 'css-doodle';
|
||||||
|
|
||||||
const FORTY_FIVE_SECONDS = 45 * 1000;
|
const FORTY_FIVE_SECONDS = 45 * 1000;
|
||||||
|
@ -227,7 +229,7 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
{!animationHidden && (
|
{!animationHidden && (
|
||||||
<css-doodle class="doodle">
|
<css-doodle class="doodle">
|
||||||
{`
|
{`
|
||||||
--color: @p(var(--lbry-teal-1), var(--lbry-orange-1), var(--lbry-cyan-3), var(--lbry-pink-5));
|
--color: @p(var(--color-primary), var(--color-secondary), var(--color-focus), var(--color-nothing));
|
||||||
:doodle {
|
:doodle {
|
||||||
@grid: 30x1 / 18vmin;
|
@grid: 30x1 / 18vmin;
|
||||||
--deg: @p(-180deg, 180deg);
|
--deg: @p(-180deg, 180deg);
|
||||||
|
@ -267,21 +269,28 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
onClick={() => setClientSetting(SETTINGS.HIDE_SPLASH_ANIMATION, !animationHidden)}
|
onClick={() => setClientSetting(SETTINGS.HIDE_SPLASH_ANIMATION, !animationHidden)}
|
||||||
/>
|
/>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="splash__error card card--section">
|
<Card
|
||||||
<p className="card__subtitle">
|
className="splash__error"
|
||||||
{__('Uh oh. The flux in our Retro Encabulator must be out of whack. Try refreshing to fix it.')}
|
title={__('Error Starting Up')}
|
||||||
</p>
|
subtitle={
|
||||||
<div className="card__actions--center">
|
<React.Fragment>
|
||||||
<Button button="primary" label={__('Refresh')} onClick={() => window.location.reload()} />
|
|
||||||
</div>
|
|
||||||
<div className="help">
|
|
||||||
<p>{__('If you still have issues, your anti-virus software or firewall may be preventing startup.')}</p>
|
|
||||||
<p>
|
<p>
|
||||||
{__('Reach out to hello@lbry.com for help, or check out')}{' '}
|
Try refreshing to fix it. If you still have issues, your anti-virus software or firewall may be
|
||||||
<Button button="link" href="https://lbry.com/faq/startup-troubleshooting" label="this link" />.
|
preventing startup.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<I18nMessage
|
||||||
</div>
|
tokens={{
|
||||||
|
help_link: (
|
||||||
|
<Button button="link" href="https://lbry.com/faq/startup-troubleshooting" label="this link" />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reach out to hello@lbry.com for help, or check out %help_link%.
|
||||||
|
</I18nMessage>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
actions={<Button button="primary" label={__('Refresh')} onClick={() => window.location.reload()} />}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{/* Temp hack: don't show any modals on splash screen daemon is running;
|
{/* Temp hack: don't show any modals on splash screen daemon is running;
|
||||||
daemon doesn't let you quit during startup, so the "Quit" buttons
|
daemon doesn't let you quit during startup, so the "Quit" buttons
|
||||||
|
|
|
@ -30,6 +30,7 @@ export default function Tag(props: Props) {
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
title={title}
|
title={title}
|
||||||
className={classnames('tag', {
|
className={classnames('tag', {
|
||||||
|
'tag--large': type === 'large',
|
||||||
'tag--remove': type === 'remove',
|
'tag--remove': type === 'remove',
|
||||||
// tag--add only adjusts the color, which causes issues with mature tag color clashing
|
// tag--add only adjusts the color, which causes issues with mature tag color clashing
|
||||||
'tag--add': !isMature && type === 'add',
|
'tag--add': !isMature && type === 'add',
|
||||||
|
@ -37,7 +38,7 @@ export default function Tag(props: Props) {
|
||||||
})}
|
})}
|
||||||
label={name}
|
label={name}
|
||||||
iconSize={12}
|
iconSize={12}
|
||||||
iconRight={type !== 'link' && (type === 'remove' ? ICONS.REMOVE : ICONS.ADD)}
|
iconRight={type !== 'link' && type !== 'large' && (type === 'remove' ? ICONS.REMOVE : ICONS.ADD)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ type Props = {
|
||||||
disableAutoFocus?: boolean,
|
disableAutoFocus?: boolean,
|
||||||
onRemove: Tag => void,
|
onRemove: Tag => void,
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
|
label?: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -36,6 +37,7 @@ export default function TagsSearch(props: Props) {
|
||||||
suggestMature,
|
suggestMature,
|
||||||
disableAutoFocus,
|
disableAutoFocus,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
label,
|
||||||
} = props;
|
} = props;
|
||||||
const [newTag, setNewTag] = useState('');
|
const [newTag, setNewTag] = useState('');
|
||||||
const doesTagMatch = name => {
|
const doesTagMatch = name => {
|
||||||
|
@ -107,6 +109,7 @@ export default function TagsSearch(props: Props) {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Form className="tags__input-wrapper" onSubmit={handleSubmit}>
|
<Form className="tags__input-wrapper" onSubmit={handleSubmit}>
|
||||||
|
<label>{label || __('Following')}</label>
|
||||||
<ul className="tags--remove">
|
<ul className="tags--remove">
|
||||||
{tagsPassedIn.map(tag => (
|
{tagsPassedIn.map(tag => (
|
||||||
<Tag
|
<Tag
|
||||||
|
@ -130,6 +133,7 @@ export default function TagsSearch(props: Props) {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</Form>
|
</Form>
|
||||||
|
<label>{__('Suggested')}</label>
|
||||||
<ul className="tags">
|
<ul className="tags">
|
||||||
{suggestedTags.map(tag => (
|
{suggestedTags.map(tag => (
|
||||||
<Tag key={`suggested${tag}`} name={tag} type="add" onClick={() => handleTagClick(tag)} />
|
<Tag key={`suggested${tag}`} name={tag} type="add" onClick={() => handleTagClick(tag)} />
|
||||||
|
|
|
@ -17,11 +17,13 @@ type Props = {
|
||||||
// The default component is for following tags
|
// The default component is for following tags
|
||||||
title?: string | boolean,
|
title?: string | boolean,
|
||||||
help?: string,
|
help?: string,
|
||||||
|
label?: string,
|
||||||
tagsChosen?: Array<Tag>,
|
tagsChosen?: Array<Tag>,
|
||||||
onSelect?: (Array<Tag>) => void,
|
onSelect?: (Array<Tag>) => void,
|
||||||
onRemove?: Tag => void,
|
onRemove?: Tag => void,
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
disableAutoFocus?: boolean,
|
disableAutoFocus?: boolean,
|
||||||
|
hideHeader?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -40,6 +42,8 @@ export default function TagsSelect(props: Props) {
|
||||||
suggestMature,
|
suggestMature,
|
||||||
disableAutoFocus,
|
disableAutoFocus,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
hideHeader,
|
||||||
|
label,
|
||||||
} = props;
|
} = props;
|
||||||
const [hasClosed, setHasClosed] = usePersistedState('tag-select:has-closed', false);
|
const [hasClosed, setHasClosed] = usePersistedState('tag-select:has-closed', false);
|
||||||
const tagsToDisplay = tagsChosen || followedTags;
|
const tagsToDisplay = tagsChosen || followedTags;
|
||||||
|
@ -71,14 +75,17 @@ export default function TagsSelect(props: Props) {
|
||||||
return (
|
return (
|
||||||
((showClose && !hasClosed) || !showClose) && (
|
((showClose && !hasClosed) || !showClose) && (
|
||||||
<Card
|
<Card
|
||||||
|
actionIconPadding={false}
|
||||||
icon={ICONS.TAG}
|
icon={ICONS.TAG}
|
||||||
title={
|
title={
|
||||||
|
hideHeader ? null : (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{title}
|
{title}
|
||||||
{showClose && tagsToDisplay.length > 0 && !hasClosed && (
|
{showClose && tagsToDisplay.length > 0 && !hasClosed && (
|
||||||
<Button button="close" icon={ICONS.REMOVE} onClick={handleClose} />
|
<Button button="close" icon={ICONS.REMOVE} onClick={handleClose} />
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
subtitle={
|
subtitle={
|
||||||
help !== false && (
|
help !== false && (
|
||||||
|
@ -91,6 +98,7 @@ export default function TagsSelect(props: Props) {
|
||||||
actions={
|
actions={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<TagsSearch
|
<TagsSearch
|
||||||
|
label={label}
|
||||||
onRemove={handleTagClick}
|
onRemove={handleTagClick}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
suggestMature={suggestMature && !hasMatureTag}
|
suggestMature={suggestMature && !hasMatureTag}
|
||||||
|
|
|
@ -42,8 +42,8 @@ function TransactionList(props: Props) {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<header className="table__header">
|
<header className="table__header">
|
||||||
<h2 className="card__title--between">
|
<div className="table__header-text--between">
|
||||||
{title}
|
<h2 className="card__title">{title}</h2>
|
||||||
<div className="card__actions--inline">
|
<div className="card__actions--inline">
|
||||||
<RefreshTransactionButton slim={slim} />
|
<RefreshTransactionButton slim={slim} />
|
||||||
{/* @if TARGET='app' */}
|
{/* @if TARGET='app' */}
|
||||||
|
@ -82,7 +82,7 @@ function TransactionList(props: Props) {
|
||||||
)}
|
)}
|
||||||
{slim && <Button button="primary" navigate={`/$/${PAGES.TRANSACTIONS}`} label={__('Full History')} />}
|
{slim && <Button button="primary" navigate={`/$/${PAGES.TRANSACTIONS}`} label={__('Full History')} />}
|
||||||
</div>
|
</div>
|
||||||
</h2>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{((loading && !transactions.length) || !transactions.length) && (
|
{((loading && !transactions.length) || !transactions.length) && (
|
||||||
|
|
|
@ -26,9 +26,9 @@ class TransactionListItem extends React.PureComponent<Props> {
|
||||||
|
|
||||||
getLink(type: string) {
|
getLink(type: string) {
|
||||||
if (type === TXN_TYPES.TIP) {
|
if (type === TXN_TYPES.TIP) {
|
||||||
return <Button icon={ICONS.UNLOCK} onClick={this.abandonClaim} title={__('Unlock Tip')} />;
|
return <Button button="secondary" icon={ICONS.UNLOCK} onClick={this.abandonClaim} title={__('Unlock Tip')} />;
|
||||||
}
|
}
|
||||||
return <Button icon={ICONS.DELETE} onClick={this.abandonClaim} title={__('Abandon Claim')} />;
|
return <Button button="secondary" icon={ICONS.DELETE} onClick={this.abandonClaim} title={__('Abandon Claim')} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
abandonClaim() {
|
abandonClaim() {
|
||||||
|
@ -65,13 +65,15 @@ class TransactionListItem extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<CreditAmount badge={false} showPlus amount={amount} precision={8} />
|
{date ? (
|
||||||
<br />
|
<div>
|
||||||
|
<DateTime date={date} show={DateTime.SHOW_DATE} formatOptions={dateFormat} />
|
||||||
{fee !== 0 && (
|
<div className="table__item-label">
|
||||||
<span className="table__item-label">
|
<DateTime date={date} show={DateTime.SHOW_TIME} />
|
||||||
<CreditAmount badge={false} fee amount={fee} precision={8} />
|
</div>
|
||||||
</span>
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className="empty">{__('Pending')}</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="table__item--actionable">
|
<td className="table__item--actionable">
|
||||||
|
@ -85,16 +87,14 @@ class TransactionListItem extends React.PureComponent<Props> {
|
||||||
<td>
|
<td>
|
||||||
<ButtonTransaction id={txid} />
|
<ButtonTransaction id={txid} />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td className="table__item--align-right">
|
||||||
{date ? (
|
<CreditAmount badge={false} showPlus amount={amount} precision={8} />
|
||||||
<div>
|
<br />
|
||||||
<DateTime date={date} show={DateTime.SHOW_DATE} formatOptions={dateFormat} />
|
|
||||||
<div className="table__item-label">
|
{fee !== 0 && (
|
||||||
<DateTime date={date} show={DateTime.SHOW_TIME} />
|
<span className="table__item-label">
|
||||||
</div>
|
<CreditAmount badge={false} fee amount={fee} precision={8} />
|
||||||
</div>
|
</span>
|
||||||
) : (
|
|
||||||
<span className="empty">{__('Pending')}</span>
|
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -36,11 +36,11 @@ function TransactionListTable(props: Props) {
|
||||||
<table className="table table--transactions">
|
<table className="table table--transactions">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{__('Amount')}</th>
|
<th>{__('Date')}</th>
|
||||||
<th>{__('Type')} </th>
|
<th>{__('Type')} </th>
|
||||||
<th>{__('Details')} </th>
|
<th>{__('Details')} </th>
|
||||||
<th>{__('Transaction')}</th>
|
<th>{__('Transaction')}</th>
|
||||||
<th>{__('Date')}</th>
|
<th className="table__item--align-right">{__('Amount')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -45,7 +45,7 @@ class TransactionRefreshButton extends PureComponent<Props, State> {
|
||||||
const { fetchingTransactions } = this.props;
|
const { fetchingTransactions } = this.props;
|
||||||
const { label, disabled } = this.state;
|
const { label, disabled } = this.state;
|
||||||
return (
|
return (
|
||||||
<Button button="link" label={label} onClick={this.handleClick} disabled={disabled || fetchingTransactions} />
|
<Button button="secondary" label={label} onClick={this.handleClick} disabled={disabled || fetchingTransactions} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ function UserEmail(props: Props) {
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
inputButton={<UserSignOutButton button="inverse" />}
|
inputButton={<UserSignOutButton button="secondary" />}
|
||||||
value={email || ''}
|
value={email || ''}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -72,7 +72,12 @@ function UserEmailNew(props: Props) {
|
||||||
<I18nMessage
|
<I18nMessage
|
||||||
tokens={{
|
tokens={{
|
||||||
terms: (
|
terms: (
|
||||||
<Button button="link" href="https://www.lbry.com/termsofservice" label={__('Terms of Service')} />
|
<Button
|
||||||
|
tabIndex="2"
|
||||||
|
button="link"
|
||||||
|
href="https://www.lbry.com/termsofservice"
|
||||||
|
label={__('Terms of Service')}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -83,7 +83,7 @@ class UserPhoneNew extends React.PureComponent<Props, State> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<p className="card__subtitle">
|
<p className="section__subtitle">
|
||||||
{__(
|
{__(
|
||||||
'Enter your phone number and we will send you a verification code. We will not share your phone number with third parties.'
|
'Enter your phone number and we will send you a verification code. We will not share your phone number with third parties.'
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,33 +1,37 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Villain from 'villain';
|
import Villain from 'villain-react';
|
||||||
import 'villain/dist/style.css';
|
import 'villain-react/dist/style.css';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
theme: string,
|
||||||
source: {
|
source: {
|
||||||
fileType: string,
|
fileType: string,
|
||||||
downloadPath: string,
|
downloadPath: string,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let workerPath = 'webworkers/worker-bundle.js';
|
let workerUrl = 'webworkers/worker-bundle.js';
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
// Don't add a leading slash in production because electron treats it as an absolute path
|
// Don't add a leading slash in production because electron treats it as an absolute path
|
||||||
workerPath = `/${workerPath}`;
|
workerUrl = `/${workerUrl}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const opts = {
|
|
||||||
workerPath,
|
|
||||||
allowFullScreen: false,
|
|
||||||
autoHideControls: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ComicBookViewer extends React.PureComponent<Props> {
|
class ComicBookViewer extends React.PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { downloadPath } = this.props.source || {};
|
const { downloadPath } = this.props.source || {};
|
||||||
|
// Archive source
|
||||||
const file = `file://${downloadPath}`;
|
const file = `file://${downloadPath}`;
|
||||||
|
// Villain options
|
||||||
|
const opts = {
|
||||||
|
theme: this.props.theme === 'dark' ? 'Dark' : 'Light',
|
||||||
|
allowFullScreen: true,
|
||||||
|
autoHideControls: false,
|
||||||
|
allowGlobalShortcuts: true,
|
||||||
|
};
|
||||||
|
|
||||||
return <Villain file={file} width={'100%'} height={'100%'} options={opts} />;
|
return <Villain file={file} style={{ width: '100%', height: '100%' }} options={opts} workerUrl={workerUrl} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import React, { Suspense } from 'react';
|
import React from 'react';
|
||||||
import LoadingScreen from 'component/common/loading-screen';
|
import LoadingScreen from 'component/common/loading-screen';
|
||||||
import MarkdownPreview from 'component/common/markdown-preview';
|
import MarkdownPreview from 'component/common/markdown-preview';
|
||||||
import CodeViewer from 'component/viewers/codeViewer';
|
import CodeViewer from 'component/viewers/codeViewer';
|
||||||
|
import * as https from 'https';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
theme: string,
|
theme: string,
|
||||||
source: {
|
source: {
|
||||||
stream: string => any,
|
file: (?string) => any,
|
||||||
|
stream: string,
|
||||||
fileType: string,
|
fileType: string,
|
||||||
contentType: string,
|
contentType: string,
|
||||||
},
|
},
|
||||||
|
@ -32,9 +34,9 @@ class DocumentViewer extends React.PureComponent<Props, State> {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { source } = this.props;
|
const { source } = this.props;
|
||||||
|
// @if TARGET='app'
|
||||||
if (source && source.stream) {
|
if (source && source.file) {
|
||||||
const stream = source.stream('utf8');
|
const stream = source.file('utf8');
|
||||||
|
|
||||||
let data = '';
|
let data = '';
|
||||||
|
|
||||||
|
@ -50,6 +52,30 @@ class DocumentViewer extends React.PureComponent<Props, State> {
|
||||||
this.setState({ error: true, loading: false });
|
this.setState({ error: true, loading: false });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// @endif
|
||||||
|
// @if TARGET='web'
|
||||||
|
if (source && source.stream) {
|
||||||
|
https.get(
|
||||||
|
source.stream,
|
||||||
|
function(response) {
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
let data = '';
|
||||||
|
response.on('data', function(chunk) {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
response.on(
|
||||||
|
'end',
|
||||||
|
function() {
|
||||||
|
this.setState({ content: data, loading: false });
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.setState({ error: true, loading: false });
|
||||||
|
}
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// @endif
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDocument() {
|
renderDocument() {
|
||||||
|
@ -58,10 +84,9 @@ class DocumentViewer extends React.PureComponent<Props, State> {
|
||||||
const { source, theme } = this.props;
|
const { source, theme } = this.props;
|
||||||
const { fileType, contentType } = source;
|
const { fileType, contentType } = source;
|
||||||
const markdownType = ['md', 'markdown'];
|
const markdownType = ['md', 'markdown'];
|
||||||
|
if (markdownType.includes(fileType) || contentType === 'text/markdown' || contentType === 'text/md') {
|
||||||
if (markdownType.includes(fileType)) {
|
|
||||||
// Render markdown
|
// Render markdown
|
||||||
viewer = <MarkdownPreview content={content} promptLinks />;
|
viewer = <MarkdownPreview content={content} />;
|
||||||
} else {
|
} else {
|
||||||
// Render plain text
|
// Render plain text
|
||||||
viewer = <CodeViewer value={content} contentType={contentType} theme={theme} />;
|
viewer = <CodeViewer value={content} contentType={contentType} theme={theme} />;
|
||||||
|
|
|
@ -11,7 +11,12 @@ class HtmlViewer extends React.PureComponent<Props> {
|
||||||
const { source } = this.props;
|
const { source } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="file-render__viewer" onContextMenu={stopContextMenu}>
|
<div className="file-render__viewer" onContextMenu={stopContextMenu}>
|
||||||
|
{/* @if TARGET='app' */}
|
||||||
<iframe sandbox="" title={__('File preview')} src={`file://${source}`} />
|
<iframe sandbox="" title={__('File preview')} src={`file://${source}`} />
|
||||||
|
{/* @endif */}
|
||||||
|
{/* @if TARGET='web' */}
|
||||||
|
<iframe sandbox="" title={__('File preview')} src={source} />
|
||||||
|
{/* @endif */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,21 +27,27 @@ class PdfViewer extends React.PureComponent<Props> {
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
shell.openExternal(path);
|
shell.openExternal(path);
|
||||||
// @endif
|
// @endif
|
||||||
// @if TARGET='web'
|
|
||||||
console.error('provide stub for shell.openExternal'); // eslint-disable-line
|
|
||||||
// @endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// We used to be able to just render a webview and display the pdf inside the app
|
// We used to be able to just render a webview and display the pdf inside the app
|
||||||
// This was disabled on electron@3
|
// This was disabled on electron@3
|
||||||
// https://github.com/electron/electron/issues/12337
|
// https://github.com/electron/electron/issues/12337
|
||||||
|
const { source } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="file-render__viewer--pdf" onContextMenu={stopContextMenu}>
|
<div className="file-render__viewer--pdf" onContextMenu={stopContextMenu}>
|
||||||
|
{/* @if TARGET='app' */}
|
||||||
<p>
|
<p>
|
||||||
{__('PDF opened externally.')} <Button button="link" label={__('Click here')} onClick={this.openFile} />{' '}
|
{__('PDF opened externally.')} <Button button="link" label={__('Click here')} onClick={this.openFile} />{' '}
|
||||||
{__('to open it again.')}
|
{__('to open it again.')}
|
||||||
</p>
|
</p>
|
||||||
|
{/* @endif */}
|
||||||
|
|
||||||
|
{/* @if TARGET='web' */}
|
||||||
|
<div className="file-render__viewer">
|
||||||
|
<iframe title={__('File preview')} src={source} />
|
||||||
|
</div>
|
||||||
|
{/* @endif */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { makeSelectFileInfoForUri } from 'lbry-redux';
|
import { makeSelectFileInfoForUri, makeSelectThumbnailForUri } from 'lbry-redux';
|
||||||
import { doChangeVolume, doChangeMute } from 'redux/actions/app';
|
import { doChangeVolume, doChangeMute } from 'redux/actions/app';
|
||||||
import { selectVolume, selectMute } from 'redux/selectors/app';
|
import { selectVolume, selectMute } from 'redux/selectors/app';
|
||||||
import { savePosition, doSetPlayingUri } from 'redux/actions/content';
|
import { savePosition, doSetPlayingUri } from 'redux/actions/content';
|
||||||
|
@ -11,6 +11,7 @@ const select = (state, props) => ({
|
||||||
position: makeSelectContentPositionForUri(props.uri)(state),
|
position: makeSelectContentPositionForUri(props.uri)(state),
|
||||||
muted: selectMute(state),
|
muted: selectMute(state),
|
||||||
hasFileInfo: Boolean(makeSelectFileInfoForUri(props.uri)(state)),
|
hasFileInfo: Boolean(makeSelectFileInfoForUri(props.uri)(state)),
|
||||||
|
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
|
|
@ -20,11 +20,11 @@ const SEEK_BACKWARD_KEYCODE = ARROW_LEFT_KEYCODE;
|
||||||
|
|
||||||
const SEEK_STEP = 10; // time to seek in seconds
|
const SEEK_STEP = 10; // time to seek in seconds
|
||||||
|
|
||||||
const VIDEO_JS_OPTIONS = {
|
const VIDEO_JS_OPTIONS: { poster?: string } = {
|
||||||
autoplay: true,
|
|
||||||
controls: true,
|
controls: true,
|
||||||
preload: 'auto',
|
preload: 'auto',
|
||||||
playbackRates: [0.25, 0.5, 0.75, 1, 1.1, 1.25, 1.5, 2],
|
playbackRates: [0.25, 0.5, 0.75, 1, 1.1, 1.25, 1.5, 2],
|
||||||
|
responsive: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -38,13 +38,25 @@ type Props = {
|
||||||
setPlayingUri: (string | null) => void,
|
setPlayingUri: (string | null) => void,
|
||||||
source: string,
|
source: string,
|
||||||
contentType: string,
|
contentType: string,
|
||||||
|
thumbnail: string,
|
||||||
hasFileInfo: boolean,
|
hasFileInfo: boolean,
|
||||||
onEndedCB: any,
|
onEndedCB: any,
|
||||||
};
|
};
|
||||||
|
|
||||||
function VideoViewer(props: Props) {
|
function VideoViewer(props: Props) {
|
||||||
const { contentType, source, setPlayingUri, onEndedCB, changeVolume, changeMute, volume, muted } = props;
|
const { contentType, source, setPlayingUri, onEndedCB, changeVolume, changeMute, volume, muted, thumbnail } = props;
|
||||||
const videoRef = useRef();
|
const videoRef = useRef();
|
||||||
|
const isAudio = contentType.includes('audio');
|
||||||
|
let forceTypes = [
|
||||||
|
'video/quicktime',
|
||||||
|
'application/x-ext-mkv',
|
||||||
|
'video/x-matroska',
|
||||||
|
'application/octet-stream',
|
||||||
|
'video/x-ms-wmv',
|
||||||
|
'video/x-msvideo',
|
||||||
|
'video/mpeg',
|
||||||
|
];
|
||||||
|
const forceMp4 = forceTypes.includes(contentType);
|
||||||
const [requireRedraw, setRequireRedraw] = useState(false);
|
const [requireRedraw, setRequireRedraw] = useState(false);
|
||||||
let player;
|
let player;
|
||||||
|
|
||||||
|
@ -88,16 +100,25 @@ function VideoViewer(props: Props) {
|
||||||
sources: [
|
sources: [
|
||||||
{
|
{
|
||||||
src: source,
|
src: source,
|
||||||
type: contentType,
|
type: forceMp4 ? 'video/mp4' : contentType,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isAudio) {
|
||||||
|
videoJsOptions.poster = thumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
if (!requireRedraw) {
|
if (!requireRedraw) {
|
||||||
player = videojs(videoNode, videoJsOptions, function() {
|
player = videojs(videoNode, videoJsOptions, function() {
|
||||||
const self = this;
|
player.volume(volume);
|
||||||
self.volume(volume);
|
player.muted(muted);
|
||||||
self.muted(muted);
|
player.ready(() => {
|
||||||
|
// In the future this should be replaced with something that checks if the
|
||||||
|
// video is actually playing after calling play()
|
||||||
|
// If it's not, fall back to the play button
|
||||||
|
player.play();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +193,7 @@ function VideoViewer(props: Props) {
|
||||||
<div className="file-render__viewer" onContextMenu={stopContextMenu}>
|
<div className="file-render__viewer" onContextMenu={stopContextMenu}>
|
||||||
{!requireRedraw && (
|
{!requireRedraw && (
|
||||||
<div data-vjs-player>
|
<div data-vjs-player>
|
||||||
<video ref={videoRef} className="video-js" />
|
{isAudio ? <audio ref={videoRef} className="video-js" /> : <video ref={videoRef} className="video-js" />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import * as ICONS from 'constants/icons';
|
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import CopyableText from 'component/copyableText';
|
import CopyableText from 'component/copyableText';
|
||||||
import QRCode from 'component/common/qr-code';
|
import QRCode from 'component/common/qr-code';
|
||||||
|
@ -11,7 +10,6 @@ type Props = {
|
||||||
receiveAddress: string,
|
receiveAddress: string,
|
||||||
getNewAddress: () => void,
|
getNewAddress: () => void,
|
||||||
gettingNewAddress: boolean,
|
gettingNewAddress: boolean,
|
||||||
history: { goBack: () => void },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -45,26 +43,26 @@ class WalletAddress extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { receiveAddress, getNewAddress, gettingNewAddress, history } = this.props;
|
const { receiveAddress, getNewAddress, gettingNewAddress } = this.props;
|
||||||
const { showQR } = this.state;
|
const { showQR } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
title={
|
title={__('Receive Credits')}
|
||||||
<React.Fragment>
|
|
||||||
{__('Receive Credits')}
|
|
||||||
<Button button="close" icon={ICONS.REMOVE} onClick={() => history.goBack()} />
|
|
||||||
</React.Fragment>
|
|
||||||
}
|
|
||||||
subtitle={__('Use this address to receive LBC.')}
|
subtitle={__('Use this address to receive LBC.')}
|
||||||
actions={
|
actions={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<CopyableText label={__('Your Address')} copyable={receiveAddress} snackMessage={__('Address copied.')} />
|
<CopyableText
|
||||||
|
primaryButton
|
||||||
|
label={__('Your Address')}
|
||||||
|
copyable={receiveAddress}
|
||||||
|
snackMessage={__('Address copied.')}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
{!IS_WEB && (
|
{!IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
button="inverse"
|
button="secondary"
|
||||||
label={__('Get New Address')}
|
label={__('Get New Address')}
|
||||||
onClick={getNewAddress}
|
onClick={getNewAddress}
|
||||||
disabled={gettingNewAddress}
|
disabled={gettingNewAddress}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
selectSupportsBalance,
|
selectSupportsBalance,
|
||||||
selectTipsBalance,
|
selectTipsBalance,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
import { selectClaimedRewards } from 'lbryinc';
|
import { selectClaimedRewards } from 'lbryinc';
|
||||||
import WalletBalance from './view';
|
import WalletBalance from './view';
|
||||||
|
|
||||||
|
@ -18,4 +19,9 @@ const select = state => ({
|
||||||
rewards: selectClaimedRewards(state),
|
rewards: selectClaimedRewards(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select)(WalletBalance);
|
export default connect(
|
||||||
|
select,
|
||||||
|
{
|
||||||
|
doOpenModal,
|
||||||
|
}
|
||||||
|
)(WalletBalance);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as MODALS from 'constants/modal_types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import CreditAmount from 'component/common/credit-amount';
|
import CreditAmount from 'component/common/credit-amount';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
|
@ -12,10 +12,11 @@ type Props = {
|
||||||
claimsBalance: number,
|
claimsBalance: number,
|
||||||
supportsBalance: number,
|
supportsBalance: number,
|
||||||
tipsBalance: number,
|
tipsBalance: number,
|
||||||
|
doOpenModal: string => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
const WalletBalance = (props: Props) => {
|
const WalletBalance = (props: Props) => {
|
||||||
const { balance, claimsBalance, supportsBalance, tipsBalance } = props;
|
const { balance, claimsBalance, supportsBalance, tipsBalance, doOpenModal } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
@ -27,8 +28,13 @@ const WalletBalance = (props: Props) => {
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="section__actions">
|
<div className="section__actions">
|
||||||
<Button button="inverse" icon={ICONS.SEND} label={__('Send Credits')} navigate={`$/${PAGES.WALLET_SEND}`} />
|
<Button button="primary" label={__('Your Address')} onClick={() => doOpenModal(MODALS.WALLET_RECEIVE)} />
|
||||||
<Button button="inverse" label={__('Your Address')} navigate={`$/${PAGES.WALLET_RECEIVE}`} />
|
<Button
|
||||||
|
button="secondary"
|
||||||
|
icon={ICONS.SEND}
|
||||||
|
label={__('Send Credits')}
|
||||||
|
onClick={() => doOpenModal(MODALS.WALLET_SEND)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as MODALS from 'constants/modal_types';
|
import * as MODALS from 'constants/modal_types';
|
||||||
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 { Form, FormField } from 'component/common/form';
|
import { Form, FormField } from 'component/common/form';
|
||||||
|
@ -16,7 +15,6 @@ type DraftTransaction = {
|
||||||
type Props = {
|
type Props = {
|
||||||
openModal: (id: string, { address: string, amount: number }) => void,
|
openModal: (id: string, { address: string, amount: number }) => void,
|
||||||
balance: number,
|
balance: number,
|
||||||
history: { goBack: () => void },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class WalletSend extends React.PureComponent<Props> {
|
class WalletSend extends React.PureComponent<Props> {
|
||||||
|
@ -36,16 +34,11 @@ class WalletSend extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { balance, history } = this.props;
|
const { balance } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
title={
|
title={__('Send Credits')}
|
||||||
<React.Fragment>
|
|
||||||
{__('Send Credits')}
|
|
||||||
<Button button="close" icon={ICONS.REMOVE} onClick={() => history.goBack()} />
|
|
||||||
</React.Fragment>
|
|
||||||
}
|
|
||||||
subtitle={__('Send LBC to your friends or favorite creators.')}
|
subtitle={__('Send LBC to your friends or favorite creators.')}
|
||||||
actions={
|
actions={
|
||||||
<Formik
|
<Formik
|
||||||
|
|
|
@ -12,7 +12,7 @@ export default function WebUploadItem(props: Props) {
|
||||||
const { params, progress, xhr } = props;
|
const { params, progress, xhr } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className={'claim-preview card--inline'}>
|
<li className={'claim-preview claim-preview--inactive card--inline'}>
|
||||||
<CardMedia thumbnail={params.thumbnail_url} />
|
<CardMedia thumbnail={params.thumbnail_url} />
|
||||||
<div className={'claim-preview-metadata'}>
|
<div className={'claim-preview-metadata'}>
|
||||||
<div className="claim-preview-info">
|
<div className="claim-preview-info">
|
||||||
|
|
|
@ -11,7 +11,7 @@ export type UploadItem = {
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
currentUploads: { [key: string]: UploadItem },
|
currentUploads: { [key: string]: UploadItem },
|
||||||
uploadCount: ?number,
|
uploadCount: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function WebUploadList(props: Props) {
|
export default function WebUploadList(props: Props) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import Wunderbar from './view';
|
import Wunderbar from './view';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { formatLbryUriForWeb } from 'util/uri';
|
import { formatLbryUrlForWeb } from 'util/url';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
suggestions: selectSearchSuggestions(state),
|
suggestions: selectSearchSuggestions(state),
|
||||||
|
@ -26,7 +26,7 @@ const perform = (dispatch, ownProps) => ({
|
||||||
analytics.apiLogSearch();
|
analytics.apiLogSearch();
|
||||||
},
|
},
|
||||||
onSubmit: uri => {
|
onSubmit: uri => {
|
||||||
const path = formatLbryUriForWeb(uri);
|
const path = formatLbryUrlForWeb(uri);
|
||||||
ownProps.history.push(path);
|
ownProps.history.push(path);
|
||||||
dispatch(doUpdateSearchQuery(''));
|
dispatch(doUpdateSearchQuery(''));
|
||||||
},
|
},
|
||||||
|
|
|
@ -370,8 +370,7 @@ export default class Autocomplete extends React.Component {
|
||||||
const matchedItem = this.getFilteredItems(props)[index];
|
const matchedItem = this.getFilteredItems(props)[index];
|
||||||
if (value !== '' && matchedItem) {
|
if (value !== '' && matchedItem) {
|
||||||
const itemValue = getItemValue(matchedItem);
|
const itemValue = getItemValue(matchedItem);
|
||||||
const itemValueDoesMatch =
|
const itemValueDoesMatch = itemValue.toLowerCase().includes(
|
||||||
itemValue.toLowerCase().includes(
|
|
||||||
value.toLowerCase()
|
value.toLowerCase()
|
||||||
// below line is the the only thing that is changed from the real component
|
// below line is the the only thing that is changed from the real component
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default class extends React.PureComponent<Props> {
|
||||||
<img alt="Friendly gerbil" className={classnames('yrbl', className)} src={`${image}`} />
|
<img alt="Friendly gerbil" className={classnames('yrbl', className)} src={`${image}`} />
|
||||||
{title && subtitle && (
|
{title && subtitle && (
|
||||||
<div className="yrbl__content">
|
<div className="yrbl__content">
|
||||||
<h2 className="card__title">{title}</h2>
|
<h2 className="card__title card__title--deprecated">{title}</h2>
|
||||||
<p>{subtitle}</p>
|
<p>{subtitle}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -29,3 +29,5 @@ export const WALLET_DECRYPT = 'wallet_decrypt';
|
||||||
export const WALLET_UNLOCK = 'wallet_unlock';
|
export const WALLET_UNLOCK = 'wallet_unlock';
|
||||||
export const WALLET_SYNC = 'wallet_sync';
|
export const WALLET_SYNC = 'wallet_sync';
|
||||||
export const WALLET_PASSWORD_UNSAVE = 'wallet_password_unsave';
|
export const WALLET_PASSWORD_UNSAVE = 'wallet_password_unsave';
|
||||||
|
export const WALLET_SEND = 'wallet_send';
|
||||||
|
export const WALLET_RECEIVE = 'wallet_receive';
|
||||||
|
|
|
@ -1,27 +1,25 @@
|
||||||
export const AUTH = 'signin';
|
exports.AUTH = 'signin';
|
||||||
export const AUTH_VERIFY = 'verify';
|
exports.AUTH_VERIFY = 'verify';
|
||||||
export const BACKUP = 'backup';
|
exports.BACKUP = 'backup';
|
||||||
export const CHANNEL = 'channel';
|
exports.CHANNEL = 'channel';
|
||||||
export const DISCOVER = 'discover';
|
exports.DISCOVER = 'discover';
|
||||||
export const DOWNLOADED = 'downloaded';
|
exports.DOWNLOADED = 'downloaded';
|
||||||
export const HELP = 'help';
|
exports.HELP = 'help';
|
||||||
export const LIBRARY = 'library';
|
exports.LIBRARY = 'library';
|
||||||
export const INVITE = 'invite';
|
exports.INVITE = 'invite';
|
||||||
export const PUBLISH = 'publish';
|
exports.PUBLISH = 'publish';
|
||||||
export const PUBLISHED = 'published';
|
exports.PUBLISHED = 'published';
|
||||||
export const GET_CREDITS = 'getcredits';
|
exports.GET_CREDITS = 'getcredits';
|
||||||
export const REPORT = 'report';
|
exports.REPORT = 'report';
|
||||||
export const REWARDS = 'rewards';
|
exports.REWARDS = 'rewards';
|
||||||
export const SEND = 'send';
|
exports.SEND = 'send';
|
||||||
export const SETTINGS = 'settings';
|
exports.SETTINGS = 'settings';
|
||||||
export const SHOW = 'show';
|
exports.SHOW = 'show';
|
||||||
export const ACCOUNT = 'account';
|
exports.ACCOUNT = 'account';
|
||||||
export const FOLLOWING = 'following';
|
exports.FOLLOWING = 'following';
|
||||||
export const SEARCH = 'search';
|
exports.SEARCH = 'search';
|
||||||
export const TRANSACTIONS = 'transactions';
|
exports.TRANSACTIONS = 'transactions';
|
||||||
export const TAGS = 'tags';
|
exports.TAGS = 'tags';
|
||||||
export const WALLET = 'wallet';
|
exports.WALLET = 'wallet';
|
||||||
export const WALLET_SEND = 'wallet/send';
|
exports.BLOCKED = 'blocked';
|
||||||
export const WALLET_RECEIVE = 'wallet/receive';
|
exports.CHANNELS = 'channels';
|
||||||
export const BLOCKED = 'blocked';
|
|
||||||
export const CHANNELS = 'channels';
|
|
||||||
|
|
35
ui/index.jsx
35
ui/index.jsx
|
@ -1,3 +1,5 @@
|
||||||
|
import 'babel-polyfill';
|
||||||
|
|
||||||
import ErrorBoundary from 'component/errorBoundary';
|
import ErrorBoundary from 'component/errorBoundary';
|
||||||
import App from 'component/app';
|
import App from 'component/app';
|
||||||
import SnackBar from 'component/snackBar';
|
import SnackBar from 'component/snackBar';
|
||||||
|
@ -11,7 +13,7 @@ import * as MODALS from 'constants/modal_types';
|
||||||
import React, { Fragment, useState, useEffect } from 'react';
|
import React, { Fragment, useState, useEffect } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { doConditionalAuthNavigate, doDaemonReady, doAutoUpdate, doOpenModal, doHideModal } from 'redux/actions/app';
|
import { doDaemonReady, doAutoUpdate, doOpenModal, doHideModal } from 'redux/actions/app';
|
||||||
import { Lbry, doToast, isURIValid, setSearchApi, apiCall } from 'lbry-redux';
|
import { Lbry, doToast, isURIValid, setSearchApi, apiCall } from 'lbry-redux';
|
||||||
import { doSetLanguage, doUpdateIsNightAsync } from 'redux/actions/settings';
|
import { doSetLanguage, doUpdateIsNightAsync } from 'redux/actions/settings';
|
||||||
import {
|
import {
|
||||||
|
@ -26,7 +28,7 @@ import pjson from 'package.json';
|
||||||
import app from './app';
|
import app from './app';
|
||||||
import doLogWarningConsoleMessage from './logWarningConsoleMessage';
|
import doLogWarningConsoleMessage from './logWarningConsoleMessage';
|
||||||
import { ConnectedRouter, push } from 'connected-react-router';
|
import { ConnectedRouter, push } from 'connected-react-router';
|
||||||
import { formatLbryUriForWeb } from 'util/uri';
|
import { formatLbryUrlForWeb, formatInAppUrl } from 'util/url';
|
||||||
import { PersistGate } from 'redux-persist/integration/react';
|
import { PersistGate } from 'redux-persist/integration/react';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import { getAuthToken, setAuthToken } from 'util/saved-passwords';
|
import { getAuthToken, setAuthToken } from 'util/saved-passwords';
|
||||||
|
@ -68,7 +70,6 @@ Lbry.setOverride(
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
analytics.startupEvent();
|
analytics.startupEvent();
|
||||||
|
|
||||||
const APPPAGEURL = 'lbry://?';
|
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
const { autoUpdater } = remote.require('electron-updater');
|
const { autoUpdater } = remote.require('electron-updater');
|
||||||
autoUpdater.logger = remote.require('electron-log');
|
autoUpdater.logger = remote.require('electron-log');
|
||||||
|
@ -155,25 +156,29 @@ rewards.setCallback('claimRewardSuccess', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
|
ipcRenderer.on('open-uri-requested', (event, url, newSession) => {
|
||||||
if (uri && uri.startsWith('lbry://')) {
|
function handleError() {
|
||||||
if (uri.startsWith('lbry://?verify')) {
|
|
||||||
app.store.dispatch(doConditionalAuthNavigate(newSession));
|
|
||||||
} else if (uri.startsWith(APPPAGEURL)) {
|
|
||||||
const navpage = uri.replace(APPPAGEURL, '').toLowerCase();
|
|
||||||
app.store.dispatch(push(`/$/${navpage}`));
|
|
||||||
} else if (isURIValid(uri)) {
|
|
||||||
const formattedUri = formatLbryUriForWeb(uri);
|
|
||||||
app.store.dispatch(push(formattedUri));
|
|
||||||
analytics.openUrlEvent(formattedUri);
|
|
||||||
} else {
|
|
||||||
app.store.dispatch(
|
app.store.dispatch(
|
||||||
doToast({
|
doToast({
|
||||||
message: __('Invalid LBRY URL requested'),
|
message: __('Invalid LBRY URL requested'),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const path = url.slice('lbry://'.length);
|
||||||
|
if (path.startsWith('?')) {
|
||||||
|
const redirectUrl = formatInAppUrl(path);
|
||||||
|
return app.store.dispatch(push(redirectUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isURIValid(url)) {
|
||||||
|
const formattedUrl = formatLbryUrlForWeb(url);
|
||||||
|
analytics.openUrlEvent(formattedUrl);
|
||||||
|
return app.store.dispatch(push(formattedUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If nothing redirected before here the url must be messed up
|
||||||
|
handleError();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on('language-set', (event, language) => {
|
ipcRenderer.on('language-set', (event, language) => {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
// These should probably just be combined into one modal component
|
// These should probably just be combined into one modal component
|
||||||
|
import * as ICONS from 'constants/icons';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import ReactModal from 'react-modal';
|
import ReactModal from 'react-modal';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
|
@ -50,12 +51,15 @@ export class Modal extends React.PureComponent<ModalProps> {
|
||||||
<ReactModal
|
<ReactModal
|
||||||
{...modalProps}
|
{...modalProps}
|
||||||
onRequestClose={onAborted || onConfirmed}
|
onRequestClose={onAborted || onConfirmed}
|
||||||
className={classnames('card modal', className)}
|
className={classnames('modal', className, {
|
||||||
|
'modal--card-internal': type === 'card',
|
||||||
|
})}
|
||||||
overlayClassName="modal-overlay"
|
overlayClassName="modal-overlay"
|
||||||
>
|
>
|
||||||
{title && <h1 className="card__title">{title}</h1>}
|
{title && <h1 className="card__title card__title--deprecated">{title}</h1>}
|
||||||
|
{type === 'card' && <Button button="close" icon={ICONS.REMOVE} onClick={onAborted} />}
|
||||||
{children}
|
{children}
|
||||||
{type === 'custom' ? null : ( // custom modals define their own buttons
|
{type === 'custom' || type === 'card' ? null : ( // custom modals define their own buttons
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
<Button
|
<Button
|
||||||
button="primary"
|
button="primary"
|
||||||
|
|
|
@ -39,7 +39,7 @@ class ModalAffirmPurchase extends React.PureComponent<Props> {
|
||||||
onConfirmed={this.onAffirmPurchase}
|
onConfirmed={this.onAffirmPurchase}
|
||||||
onAborted={cancelPurchase}
|
onAborted={cancelPurchase}
|
||||||
>
|
>
|
||||||
<p className="card__subtitle">
|
<p className="section__subtitle">
|
||||||
{__('This will purchase')} <strong>{title ? `"${title}"` : uri}</strong> {__('for')}{' '}
|
{__('This will purchase')} <strong>{title ? `"${title}"` : uri}</strong> {__('for')}{' '}
|
||||||
<strong>
|
<strong>
|
||||||
<FilePrice uri={uri} showFullPrice inheritStyle showLBC={false} />
|
<FilePrice uri={uri} showFullPrice inheritStyle showLBC={false} />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { Modal } from 'modal/modal';
|
import { Modal } from 'modal/modal';
|
||||||
import { formatPathForWeb } from 'util/uri';
|
import { formatFileSystemPath } from 'util/url';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
upload: WebFile => void,
|
upload: WebFile => void,
|
||||||
|
@ -15,15 +15,18 @@ function ModalAutoGenerateThumbnail(props: Props) {
|
||||||
const playerRef = useRef();
|
const playerRef = useRef();
|
||||||
let videoSrc;
|
let videoSrc;
|
||||||
if (typeof filePath === 'string') {
|
if (typeof filePath === 'string') {
|
||||||
videoSrc = formatPathForWeb(filePath);
|
videoSrc = formatFileSystemPath(filePath);
|
||||||
} else {
|
} else {
|
||||||
videoSrc = URL.createObjectURL(filePath);
|
videoSrc = URL.createObjectURL(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadImage() {
|
function uploadImage() {
|
||||||
const imageBuffer = captureSnapshot();
|
const imageBuffer = captureSnapshot();
|
||||||
|
// $FlowFixMe
|
||||||
const file = new File([imageBuffer], 'thumbnail.png', { type: 'image/png' });
|
const file = new File([imageBuffer], 'thumbnail.png', { type: 'image/png' });
|
||||||
|
|
||||||
if (imageBuffer) {
|
if (imageBuffer) {
|
||||||
|
// $FlowFixMe
|
||||||
upload(file);
|
upload(file);
|
||||||
closeModal();
|
closeModal();
|
||||||
} else {
|
} else {
|
||||||
|
@ -75,7 +78,7 @@ function ModalAutoGenerateThumbnail(props: Props) {
|
||||||
onConfirmed={uploadImage}
|
onConfirmed={uploadImage}
|
||||||
onAborted={closeModal}
|
onAborted={closeModal}
|
||||||
>
|
>
|
||||||
<p className="card__subtitle">{__('Pause at any time to select a thumbnail from your video')}.</p>
|
<p className="section__subtitle">{__('Pause at any time to select a thumbnail from your video')}.</p>
|
||||||
<video ref={playerRef} src={videoSrc} onLoadedMetadata={resize} onError={onError} controls />
|
<video ref={playerRef} src={videoSrc} onLoadedMetadata={resize} onError={onError} controls />
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Modal } from 'modal/modal';
|
import { Modal } from 'modal/modal';
|
||||||
import { formatPathForWeb } from 'util/uri';
|
import { formatFileSystemPath } from 'util/url';
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
import { shell } from 'electron';
|
import { shell } from 'electron';
|
||||||
// @endif
|
// @endif
|
||||||
|
@ -37,7 +37,7 @@ function ModalOpenExternalResource(props: Props) {
|
||||||
if (uri) {
|
if (uri) {
|
||||||
window.open(uri);
|
window.open(uri);
|
||||||
} else if (path) {
|
} else if (path) {
|
||||||
window.open(formatPathForWeb(path));
|
window.open(formatFileSystemPath(path));
|
||||||
}
|
}
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ class ModalPublishSuccess extends React.PureComponent<Props> {
|
||||||
closeModal();
|
closeModal();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<p className="card__subtitle">
|
<p className="section__subtitle">
|
||||||
{__(`Your %publishMessage% pending on LBRY. It will take a few minutes to appear for other users.`, {
|
{__(`Your %publishMessage% pending on LBRY. It will take a few minutes to appear for other users.`, {
|
||||||
publishMessage,
|
publishMessage,
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -27,6 +27,8 @@ import ModalWalletUnlock from 'modal/modalWalletUnlock';
|
||||||
import ModalRewardCode from 'modal/modalRewardCode';
|
import ModalRewardCode from 'modal/modalRewardCode';
|
||||||
import ModalPasswordUnsave from 'modal/modalPasswordUnsave';
|
import ModalPasswordUnsave from 'modal/modalPasswordUnsave';
|
||||||
import ModalCommentAcknowledgement from 'modal/modalCommentAcknowledgement';
|
import ModalCommentAcknowledgement from 'modal/modalCommentAcknowledgement';
|
||||||
|
import ModalWalletSend from 'modal/modalWalletSend';
|
||||||
|
import ModalWalletReceive from 'modal/modalWalletReceive';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
modal: { id: string, modalProps: {} },
|
modal: { id: string, modalProps: {} },
|
||||||
|
@ -99,6 +101,10 @@ function ModalRouter(props: Props) {
|
||||||
return <ModalRewardCode {...modalProps} />;
|
return <ModalRewardCode {...modalProps} />;
|
||||||
case MODALS.COMMENT_ACKNOWEDGEMENT:
|
case MODALS.COMMENT_ACKNOWEDGEMENT:
|
||||||
return <ModalCommentAcknowledgement {...modalProps} />;
|
return <ModalCommentAcknowledgement {...modalProps} />;
|
||||||
|
case MODALS.WALLET_SEND:
|
||||||
|
return <ModalWalletSend {...modalProps} />;
|
||||||
|
case MODALS.WALLET_RECEIVE:
|
||||||
|
return <ModalWalletReceive {...modalProps} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@ class ModalSocialShare extends React.PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { closeModal, uri, webShareable, isChannel } = this.props;
|
const { closeModal, uri, webShareable, isChannel } = this.props;
|
||||||
return (
|
return (
|
||||||
<Modal isOpen onAborted={closeModal} type="custom" title={__('Share')}>
|
<Modal isOpen onAborted={closeModal} onConfirmed={closeModal} title={__('Share')}>
|
||||||
<SocialShare uri={uri} onDone={closeModal} webShareable={webShareable} isChannel={isChannel} />
|
<SocialShare uri={uri} webShareable={webShareable} isChannel={isChannel} />
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,7 @@ class ModalWalletEncrypt extends React.PureComponent<Props, State> {
|
||||||
/>
|
/>
|
||||||
</fieldset-section>
|
</fieldset-section>
|
||||||
|
|
||||||
<div className="card__subtitle--status">
|
<div className="section__subtitle--status">
|
||||||
{__(
|
{__(
|
||||||
'If your password is lost, it cannot be recovered. You will not be able to access your wallet without a password.'
|
'If your password is lost, it cannot be recovered. You will not be able to access your wallet without a password.'
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { doHideModal } from 'redux/actions/app';
|
||||||
import WalletReceive from './view';
|
import WalletReceive from './view';
|
||||||
|
|
||||||
const select = state => ({});
|
const select = state => ({});
|
||||||
|
|
||||||
const perform = dispatch => ({});
|
const perform = {
|
||||||
|
doHideModal,
|
||||||
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
select,
|
select,
|
16
ui/modal/modalWalletReceive/view.jsx
Normal file
16
ui/modal/modalWalletReceive/view.jsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import WalletAddress from 'component/walletAddress';
|
||||||
|
import { Modal } from 'modal/modal';
|
||||||
|
|
||||||
|
type Props = { doHideModal: () => void };
|
||||||
|
|
||||||
|
const WalletAddressPage = (props: Props) => {
|
||||||
|
const { doHideModal } = props;
|
||||||
|
return (
|
||||||
|
<Modal isOpen type="card" onAborted={doHideModal}>
|
||||||
|
<WalletAddress />
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default WalletAddressPage;
|
|
@ -1,9 +1,12 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { doHideModal } from 'redux/actions/app';
|
||||||
import WalletSend from './view';
|
import WalletSend from './view';
|
||||||
|
|
||||||
const select = state => ({});
|
const select = state => ({});
|
||||||
|
|
||||||
const perform = dispatch => ({});
|
const perform = {
|
||||||
|
doHideModal,
|
||||||
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
select,
|
select,
|
16
ui/modal/modalWalletSend/view.jsx
Normal file
16
ui/modal/modalWalletSend/view.jsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import WalletSend from 'component/walletSend';
|
||||||
|
import { Modal } from 'modal/modal';
|
||||||
|
|
||||||
|
type Props = { doHideModal: () => void };
|
||||||
|
|
||||||
|
const WalletSendModal = (props: Props) => {
|
||||||
|
const { doHideModal } = props;
|
||||||
|
return (
|
||||||
|
<Modal isOpen type="card" onAborted={doHideModal}>
|
||||||
|
<WalletSend />
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default WalletSendModal;
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import WalletAddress from 'component/walletAddress';
|
import WalletAddress from './node_modules/component/walletAddress';
|
||||||
import Page from 'component/page';
|
import Page from './node_modules/component/page';
|
||||||
|
|
||||||
const WalletAddressPage = () => (
|
const WalletAddressPage = () => (
|
||||||
<Page className="main--contained">
|
<Page className="main--contained">
|
10
ui/modal/walletSend/view.jsx
Normal file
10
ui/modal/walletSend/view.jsx
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import React from 'react';
|
||||||
|
import WalletSend from './node_modules/component/walletSend';
|
||||||
|
|
||||||
|
const WalletSendModal = () => (
|
||||||
|
<div>
|
||||||
|
<WalletSend />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default WalletSendModal;
|
|
@ -8,7 +8,7 @@ import ShareButton from 'component/shareButton';
|
||||||
import { Tabs, TabList, Tab, TabPanels, TabPanel } from 'component/common/tabs';
|
import { Tabs, TabList, Tab, TabPanels, TabPanel } from 'component/common/tabs';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import { formatLbryUriForWeb } from 'util/uri';
|
import { formatLbryUrlForWeb } from 'util/url';
|
||||||
import ChannelContent from 'component/channelContent';
|
import ChannelContent from 'component/channelContent';
|
||||||
import ChannelAbout from 'component/channelAbout';
|
import ChannelAbout from 'component/channelAbout';
|
||||||
import ChannelDiscussion from 'component/channelDiscussion';
|
import ChannelDiscussion from 'component/channelDiscussion';
|
||||||
|
@ -93,7 +93,7 @@ function ChannelPage(props: Props) {
|
||||||
const tabIndex = currentView === ABOUT_PAGE || editing ? 1 : currentView === DISCUSSION_PAGE ? 2 : 0;
|
const tabIndex = currentView === ABOUT_PAGE || editing ? 1 : currentView === DISCUSSION_PAGE ? 2 : 0;
|
||||||
|
|
||||||
function onTabChange(newTabIndex) {
|
function onTabChange(newTabIndex) {
|
||||||
let url = formatLbryUriForWeb(uri);
|
let url = formatLbryUrlForWeb(uri);
|
||||||
let search = '?';
|
let search = '?';
|
||||||
|
|
||||||
if (newTabIndex === 0) {
|
if (newTabIndex === 0) {
|
||||||
|
@ -207,14 +207,12 @@ function ChannelPage(props: Props) {
|
||||||
thumbnailPreview={thumbPreview}
|
thumbnailPreview={thumbPreview}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<h1 className="channel__title">
|
<h1 className="channel__title">{title || '@' + channelName}</h1>
|
||||||
{title || '@' + channelName}
|
|
||||||
{channelIsMine && !editing && (
|
{channelIsMine && !editing && (
|
||||||
<Button title={__('Edit')} onClick={() => setEditing(!editing)} icon={ICONS.EDIT} iconSize={49} />
|
<Button button="alt" title={__('Edit')} onClick={() => setEditing(!editing)} icon={ICONS.EDIT} />
|
||||||
)}
|
)}
|
||||||
</h1>
|
|
||||||
<div className="channel__meta">
|
<div className="channel__meta">
|
||||||
<ClaimUri uri={uri} />
|
<ClaimUri uri={uri} inline />
|
||||||
<span>
|
<span>
|
||||||
{subCount} {subCount !== 1 ? __('Subscribers') : __('Subscriber')}
|
{subCount} {subCount !== 1 ? __('Subscribers') : __('Subscriber')}
|
||||||
<HelpLink href="https://lbry.com/faq/views" />
|
<HelpLink href="https://lbry.com/faq/views" />
|
||||||
|
@ -226,7 +224,7 @@ function ChannelPage(props: Props) {
|
||||||
<TabList className="tabs__list--channel-page">
|
<TabList className="tabs__list--channel-page">
|
||||||
<Tab disabled={editing}>{__('Content')}</Tab>
|
<Tab disabled={editing}>{__('Content')}</Tab>
|
||||||
<Tab>{editing ? __('Editing Your Channel') : __('About')}</Tab>
|
<Tab>{editing ? __('Editing Your Channel') : __('About')}</Tab>
|
||||||
<Tab disabled={editing}>{__('Discussion')}</Tab>
|
<Tab disabled={editing}>{__('Comments')}</Tab>
|
||||||
{/* only render searchbar on content page (tab index 0 === content page) */}
|
{/* only render searchbar on content page (tab index 0 === content page) */}
|
||||||
{tabIndex === 0 ? (
|
{tabIndex === 0 ? (
|
||||||
<Form onSubmit={handleSearch} className="wunderbar--inline">
|
<Form onSubmit={handleSearch} className="wunderbar--inline">
|
||||||
|
|
|
@ -4,6 +4,7 @@ import ClaimList from 'component/claimList';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import YoutubeTransferStatus from 'component/youtubeTransferStatus';
|
import YoutubeTransferStatus from 'component/youtubeTransferStatus';
|
||||||
|
import Spinner from 'component/spinner';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
channels: Array<ChannelClaim>,
|
channels: Array<ChannelClaim>,
|
||||||
|
@ -36,7 +37,7 @@ export default function ChannelsPage(props: Props) {
|
||||||
<Page>
|
<Page>
|
||||||
{hasYoutubeChannels && <YoutubeTransferStatus hideChannelLink />}
|
{hasYoutubeChannels && <YoutubeTransferStatus hideChannelLink />}
|
||||||
|
|
||||||
{channels && channels.length ? (
|
{channels && Boolean(channels.length) && (
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<ClaimList
|
<ClaimList
|
||||||
header={__('Your Channels')}
|
header={__('Your Channels')}
|
||||||
|
@ -44,7 +45,10 @@ export default function ChannelsPage(props: Props) {
|
||||||
uris={channels.map(channel => channel.permanent_url)}
|
uris={channels.map(channel => channel.permanent_url)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
)}
|
||||||
|
{!(channels && channels.length) && (
|
||||||
|
<React.Fragment>
|
||||||
|
{!fetchingChannels ? (
|
||||||
<section className="main--empty">
|
<section className="main--empty">
|
||||||
<div className=" section--small">
|
<div className=" section--small">
|
||||||
<h2 className="section__title--large">{__('No Channels Created Yet')}</h2>
|
<h2 className="section__title--large">{__('No Channels Created Yet')}</h2>
|
||||||
|
@ -54,6 +58,17 @@ export default function ChannelsPage(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
) : (
|
||||||
|
<section className="main--empty">
|
||||||
|
<div className=" section--small">
|
||||||
|
<h2 className="section__title--small">
|
||||||
|
{__('Checking for channels')}
|
||||||
|
<Spinner type="small" />
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue