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