Redesign fixes (#2164)

* fix: share modal not opening

* fix: add more spacing above snackbar link

* fix: properly close thumbnail error modal

* fix: better align media property icons

* fix: tx filter alignment and prevent hiding filter if no current tx's

* fix: publish markdown on dark mode

* fix: add max-width on container for large screens

* fix: channel pagination aligmnent and spacing

* fix: modal spacing and flow errors

* fix: home page scrolling (now with mouse scrolling)

* fix: hover color in dark mode for outline buttons

* fix: improve file page spacing/layout

* cleanup

* fix: wrap file actions on smaller screens

* fix: comment button spacing
This commit is contained in:
Sean Yesmunt 2019-01-07 18:29:40 -05:00 committed by GitHub
parent 42e3d74805
commit ad90c1f96e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 384 additions and 347 deletions

View file

@ -115,7 +115,7 @@
"eslint-plugin-jsx-a11y": "^6.0.3", "eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-prettier": "^2.6.0", "eslint-plugin-prettier": "^2.6.0",
"eslint-plugin-react": "^7.7.0", "eslint-plugin-react": "^7.7.0",
"flow-bin": "^0.69.0", "flow-bin": "^0.89.0",
"flow-typed": "^2.3.0", "flow-typed": "^2.3.0",
"husky": "^0.14.3", "husky": "^0.14.3",
"i18n-extract": "^0.5.1", "i18n-extract": "^0.5.1",

View file

@ -1,12 +1,13 @@
// @flow // @flow
import type { Claim } from 'types/claim'; import type { Claim } from 'types/claim';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import React, { PureComponent } from 'react'; import React, { PureComponent, createRef } from 'react';
import { normalizeURI } from 'lbry-redux'; import { normalizeURI } from 'lbry-redux';
import ToolTip from 'component/common/tooltip'; import ToolTip from 'component/common/tooltip';
import FileCard from 'component/fileCard'; import FileCard from 'component/fileCard';
import Button from 'component/button'; import Button from 'component/button';
import SubscribeButton from 'component/subscribeButton'; import SubscribeButton from 'component/subscribeButton';
import throttle from 'util/throttle';
type Props = { type Props = {
category: string, category: string,
@ -33,12 +34,14 @@ class CategoryList extends PureComponent<Props, State> {
this.state = { this.state = {
canScrollPrevious: false, canScrollPrevious: false,
canScrollNext: false, canScrollNext: true,
}; };
(this: any).handleScrollNext = this.handleScrollNext.bind(this); (this: any).handleScrollNext = this.handleScrollNext.bind(this);
(this: any).handleScrollPrevious = this.handleScrollPrevious.bind(this); (this: any).handleScrollPrevious = this.handleScrollPrevious.bind(this);
this.rowItems = undefined; (this: any).handleArrowButtonsOnScroll = this.handleArrowButtonsOnScroll.bind(this);
this.scrollWrapper = createRef();
} }
componentDidMount() { componentDidMount() {
@ -47,54 +50,50 @@ class CategoryList extends PureComponent<Props, State> {
fetchChannel(categoryLink); fetchChannel(categoryLink);
} }
const cardRow = this.rowItems; const scrollWrapper = this.scrollWrapper.current;
if (cardRow) { if (scrollWrapper) {
const cards = cardRow.getElementsByTagName('section'); scrollWrapper.addEventListener('scroll', throttle(this.handleArrowButtonsOnScroll, 500));
const lastCard = cards[cards.length - 1];
const isCompletelyVisible = this.isCardVisible(lastCard);
if (!isCompletelyVisible) {
// not sure how we can avoid doing this
/* eslint-disable react/no-did-mount-set-state */
this.setState({
canScrollNext: true,
});
/* eslint-enable react/no-did-mount-set-state */
}
} }
} }
rowItems: ?HTMLDivElement; scrollWrapper: { current: null | HTMLUListElement };
handleArrowButtonsOnScroll() {
// Determine if the arrow buttons should be disabled
const scrollWrapper = this.scrollWrapper.current;
if (scrollWrapper) {
// firstElementChild and lastElementChild will always exist
// $FlowFixMe
const hasHiddenCardToLeft = !this.isCardVisible(scrollWrapper.firstElementChild);
// $FlowFixMe
const hasHiddenCardToRight = !this.isCardVisible(scrollWrapper.lastElementChild);
handleScroll(cardRow: HTMLDivElement, scrollTarget: number) {
const cards = cardRow.getElementsByTagName('section');
const animationCallback = () => {
const firstCard = cards[0];
const lastCard = cards[cards.length - 1];
const firstCardVisible = this.isCardVisible(firstCard);
const lastCardVisible = this.isCardVisible(lastCard);
this.setState({ this.setState({
canScrollNext: !lastCardVisible, canScrollPrevious: hasHiddenCardToLeft,
canScrollPrevious: !firstCardVisible, canScrollNext: hasHiddenCardToRight,
}); });
}; }
}
const currentScrollLeft = cardRow.scrollLeft; handleScroll(scrollTarget: number) {
const direction = currentScrollLeft > scrollTarget ? 'left' : 'right'; const scrollWrapper = this.scrollWrapper.current;
this.scrollCardsAnimated(cardRow, scrollTarget, direction, animationCallback); if (scrollWrapper) {
const currentScrollLeft = scrollWrapper.scrollLeft;
const direction = currentScrollLeft > scrollTarget ? 'left' : 'right';
this.scrollCardsAnimated(scrollWrapper, scrollTarget, direction);
}
} }
scrollCardsAnimated = ( scrollCardsAnimated = (
cardRow: HTMLDivElement, scrollWrapper: HTMLUListElement,
scrollTarget: number, scrollTarget: number,
direction: string, direction: string
callback: () => any
) => { ) => {
let start; let start;
const step = timestamp => { const step = timestamp => {
if (!start) start = timestamp; if (!start) start = timestamp;
const currentLeftVal = cardRow.scrollLeft; const currentLeftVal = scrollWrapper.scrollLeft;
let newTarget; let newTarget;
let shouldContinue; let shouldContinue;
@ -110,12 +109,10 @@ class CategoryList extends PureComponent<Props, State> {
shouldContinue = newTarget > scrollTarget; shouldContinue = newTarget > scrollTarget;
} }
cardRow.scrollLeft = newTarget; // eslint-disable-line no-param-reassign scrollWrapper.scrollLeft = newTarget; // eslint-disable-line no-param-reassign
if (shouldContinue) { if (shouldContinue) {
window.requestAnimationFrame(step); window.requestAnimationFrame(step);
} else {
callback();
} }
}; };
@ -123,85 +120,93 @@ class CategoryList extends PureComponent<Props, State> {
}; };
// check if a card is fully visible horizontally // check if a card is fully visible horizontally
isCardVisible = (section: HTMLElement) => { isCardVisible = (card: HTMLLIElement): boolean => {
if (!section) { if (!card) {
return false; return false;
} }
const rect = section.getBoundingClientRect(); const scrollWrapper = this.scrollWrapper.current;
const isVisible = rect.left >= 0 && rect.right <= window.innerWidth; if (scrollWrapper) {
return isVisible; const rect = card.getBoundingClientRect();
const isVisible =
scrollWrapper.scrollLeft < card.offsetLeft &&
rect.left >= 0 &&
rect.right <= window.innerWidth;
return isVisible;
}
return false;
}; };
handleScrollNext() { handleScrollNext() {
const cardRow = this.rowItems; const scrollWrapper = this.scrollWrapper.current;
if (cardRow) { if (!scrollWrapper) {
const cards = cardRow.getElementsByTagName('section'); return;
}
// loop over items until we find one that is on the screen const cards = scrollWrapper.getElementsByTagName('li');
// continue searching until a card isn't fully visible, this is the new target
let firstFullVisibleCard;
let firstSemiVisibleCard;
for (let i = 0; i < cards.length; i += 1) { // Loop over items until we find one that is visible
const currentCardVisible = this.isCardVisible(cards[i]); // The card before that (starting from the end) is the new "first" card on the screen
if (firstFullVisibleCard && !currentCardVisible) { let previousCard: ?HTMLLIElement;
firstSemiVisibleCard = cards[i]; for (let i = cards.length - 1; i > 0; i -= 1) {
break; const currentCard: HTMLLIElement = cards[i];
} else if (currentCardVisible) { const currentCardVisible = this.isCardVisible(currentCard);
[firstFullVisibleCard] = cards;
} if (currentCardVisible && previousCard) {
const scrollTarget = previousCard.offsetLeft;
this.handleScroll(scrollTarget);
break;
} }
if (firstFullVisibleCard && firstSemiVisibleCard) { previousCard = currentCard;
const scrollTarget = firstSemiVisibleCard.offsetLeft - firstFullVisibleCard.offsetLeft;
this.handleScroll(cardRow, scrollTarget);
}
} }
} }
handleScrollPrevious() { handleScrollPrevious() {
const cardRow = this.rowItems; const scrollWrapper = this.scrollWrapper.current;
if (cardRow) { if (!scrollWrapper) {
const cards = cardRow.getElementsByTagName('section'); return;
}
let hasFoundCard; const cards = scrollWrapper.getElementsByTagName('li');
let numberOfCardsThatCanFit = 0;
// loop starting at the end until we find a visible card let hasFoundCard;
// then count to find how many cards can fit on the screen let numberOfCardsThatCanFit = 0;
for (let i = cards.length - 1; i >= 0; i -= 1) {
const currentCard = cards[i];
const isCurrentCardVisible = this.isCardVisible(currentCard);
if (isCurrentCardVisible) { // loop starting at the end until we find a visible card
if (!hasFoundCard) { // then count to find how many cards can fit on the screen
hasFoundCard = true; for (let i = cards.length - 1; i >= 0; i -= 1) {
} const currentCard = cards[i];
const isCurrentCardVisible = this.isCardVisible(currentCard);
numberOfCardsThatCanFit += 1; if (isCurrentCardVisible) {
} else if (hasFoundCard) { if (!hasFoundCard) {
// this card is off the screen to the left hasFoundCard = true;
// we know how many cards can fit on a screen
// find the new target and scroll
const firstCardOffsetLeft = cards[0].offsetLeft;
const cardIndexToScrollTo = i + 1 - numberOfCardsThatCanFit;
const newFirstCard = cards[cardIndexToScrollTo];
let scrollTarget;
if (newFirstCard) {
scrollTarget = newFirstCard.offsetLeft;
} else {
// more cards can fit on the screen than are currently hidden
// just scroll to the first card
scrollTarget = cards[0].offsetLeft;
}
scrollTarget -= firstCardOffsetLeft; // to play nice with the margins
this.handleScroll(cardRow, scrollTarget);
break;
} }
numberOfCardsThatCanFit += 1;
} else if (hasFoundCard) {
// this card is off the screen to the left
// we know how many cards can fit on a screen
// find the new target and scroll
const firstCardOffsetLeft = cards[0].offsetLeft;
const cardIndexToScrollTo = i + 1 - numberOfCardsThatCanFit;
const newFirstCard = cards[cardIndexToScrollTo];
let scrollTarget;
if (newFirstCard) {
scrollTarget = newFirstCard.offsetLeft;
} else {
// more cards can fit on the screen than are currently hidden
// just scroll to the first card
scrollTarget = cards[0].offsetLeft;
}
scrollTarget -= firstCardOffsetLeft; // to play nice with the margins
this.handleScroll(scrollTarget);
break;
} }
} }
} }
@ -266,12 +271,7 @@ class CategoryList extends PureComponent<Props, State> {
<Button button="link" navigate="/settings" label={__('here')} />. <Button button="link" navigate="/settings" label={__('here')} />.
</p> </p>
) : ( ) : (
<ul <ul className="media-scrollhouse" ref={this.scrollWrapper}>
className="media-scrollhouse"
ref={ref => {
this.rowItems = ref;
}}
>
{names && {names &&
names.length && names.length &&
names.map(name => ( names.map(name => (

View file

@ -64,11 +64,7 @@ class CreditAmount extends React.PureComponent<Props> {
} }
if (showLBC) { if (showLBC) {
amountText = ( amountText = `${amountText} ${__('LBC')}`;
<span>
{amountText} {__('LBC')}
</span>
);
} }
if (fee) { if (fee) {

View file

@ -81,6 +81,7 @@ class FileCard extends React.PureComponent<Props> {
<div className="media__title media__title--placeholder" /> <div className="media__title media__title--placeholder" />
<div className="media__channel media__channel--placeholder" /> <div className="media__channel media__channel--placeholder" />
<div className="media__date media__date--placeholder" /> <div className="media__date media__date--placeholder" />
<div className="media__properties media__properties--placeholder" />
</li> </li>
); );
} }

View file

@ -108,7 +108,7 @@ class FileDetails extends PureComponent<Props> {
<div className="media__info-title">Comments</div> <div className="media__info-title">Comments</div>
<div className="card__actions"> <div className="card__actions--center">
<Button <Button
data-id="add-comment" data-id="add-comment"
disabled={hasClickedComment} disabled={hasClickedComment}
@ -119,7 +119,7 @@ class FileDetails extends PureComponent<Props> {
</div> </div>
<br /> <br />
{hasClickedComment && ( {hasClickedComment && (
<p className="main--for-content"> <p className="media__info-text media__info-text--center">
{user {user
? __('Your support has been added. You will be notified when comments are available.') ? __('Your support has been added. You will be notified when comments are available.')
: __('Your support has been added. Comments are coming soon.')} : __('Your support has been added. Comments are coming soon.')}

View file

@ -292,9 +292,9 @@ class PublishForm extends React.PureComponent<Props> {
{uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS && ( {uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS && (
<div>{__('Please wait for thumbnail to finish uploading')}</div> <div>{__('Please wait for thumbnail to finish uploading')}</div>
)} )}
{!!editingURI && !isStillEditing && !filePath && ( {!!editingURI &&
<div>{__('You need to reselect a file after changing the LBRY URL')}</div> !isStillEditing &&
)} !filePath && <div>{__('You need to reselect a file after changing the LBRY URL')}</div>}
</div> </div>
) )
); );
@ -351,7 +351,9 @@ class PublishForm extends React.PureComponent<Props> {
<header className="card__header"> <header className="card__header">
<h2 className="card__title">{__('Content')}</h2> <h2 className="card__title">{__('Content')}</h2>
<p className="card__subtitle"> <p className="card__subtitle">
{isStillEditing ? __('Editing a claim') : __('What are you publishing?')}{' '} {isStillEditing
? __('You are currently editing a claim.')
: __('What are you publishing?')}{' '}
{__('Read our')}{' '} {__('Read our')}{' '}
<Button button="link" label={__('FAQ')} href="https://lbry.io/faq/how-to-publish" />{' '} <Button button="link" label={__('FAQ')} href="https://lbry.io/faq/how-to-publish" />{' '}
{__('to learn more.')} {__('to learn more.')}
@ -370,13 +372,14 @@ class PublishForm extends React.PureComponent<Props> {
)} )}
<div className="card__content"> <div className="card__content">
<FileSelector currentPath={filePath} onFileChosen={this.handleFileChange} /> <FileSelector currentPath={filePath} onFileChosen={this.handleFileChange} />
{!!isStillEditing && name && ( {!!isStillEditing &&
<p className="help"> name && (
{__("If you don't choose a file, the file from your existing claim")} <p className="help">
{` "${name}" `} {__("If you don't choose a file, the file from your existing claim")}
{__('will be used.')} {` "${name}" `}
</p> {__('will be used.')}
)} </p>
)}
</div> </div>
</section> </section>
<div className={classnames({ 'card--disabled': formDisabled })}> <div className={classnames({ 'card--disabled': formDisabled })}>

View file

@ -12,7 +12,7 @@ type Props = {
formDisabled: boolean, formDisabled: boolean,
uploadThumbnailStatus: string, uploadThumbnailStatus: string,
thumbnailPath: ?string, thumbnailPath: ?string,
openModal: ({ id: string }, {}) => void, openModal: (id: string, {}) => void,
updatePublishForm: ({}) => void, updatePublishForm: ({}) => void,
resetThumbnailStatus: () => void, resetThumbnailStatus: () => void,
}; };

View file

@ -9,6 +9,7 @@ type Props = {
linkTarget: ?string, linkTarget: ?string,
linkText: ?string, linkText: ?string,
message: string, message: string,
isError: boolean,
}, },
}; };
@ -20,6 +21,9 @@ class SnackBar extends React.PureComponent<Props> {
this.hideTimeout = null; this.hideTimeout = null;
} }
hideTimeout: ?TimeoutID;
displayTime: number;
render() { render() {
const { snack, removeSnack } = this.props; const { snack, removeSnack } = this.props;
@ -47,9 +51,10 @@ class SnackBar extends React.PureComponent<Props> {
<div>&#9432;</div> <div>&#9432;</div>
<div>{message}</div> <div>{message}</div>
</div> </div>
{linkText && linkTarget && ( {linkText &&
<Button navigate={linkTarget} className="snack-bar__action" label={linkText} /> linkTarget && (
)} <Button navigate={linkTarget} className="snack-bar__action" label={linkText} />
)}
</div> </div>
); );
} }

View file

@ -16,7 +16,7 @@ type Props = {
subscriptions: Array<string>, subscriptions: Array<string>,
doChannelSubscribe: ({ channelName: string, uri: string }) => void, doChannelSubscribe: ({ channelName: string, uri: string }) => void,
doChannelUnsubscribe: SubscribtionArgs => void, doChannelUnsubscribe: SubscribtionArgs => void,
doOpenModal: ({ id: string }) => void, doOpenModal: (id: string) => void,
firstRunCompleted: boolean, firstRunCompleted: boolean,
showSnackBarOnSubscribe: boolean, showSnackBarOnSubscribe: boolean,
doToast: ({ message: string }) => void, doToast: ({ message: string }) => void,
@ -46,7 +46,7 @@ export default (props: Props) => {
<Button <Button
iconColor="red" iconColor="red"
icon={isSubscribed ? undefined : ICONS.HEART} icon={isSubscribed ? undefined : ICONS.HEART}
button={buttonStyle ? buttonStyle : 'alt'} button={buttonStyle || 'alt'}
label={subscriptionLabel} label={subscriptionLabel}
onClick={e => { onClick={e => {
e.stopPropagation(); e.stopPropagation();

View file

@ -24,7 +24,7 @@ type Props = {
slim?: boolean, slim?: boolean,
transactions: Array<Transaction>, transactions: Array<Transaction>,
rewards: {}, rewards: {},
openModal: ({ id: string }, { nout: number, txid: string }) => void, openModal: (id: string, { nout: number, txid: string }) => void,
myClaims: any, myClaims: any,
filterSetting: string, filterSetting: string,
setTransactionFilter: string => void, setTransactionFilter: string => void,
@ -79,43 +79,44 @@ class TransactionList extends React.PureComponent<Props> {
return ( return (
<React.Fragment> <React.Fragment>
<header className="card__header"> <header className="card__header">
{!transactionList.length && ( {!slim &&
<p className="card__content">{emptyMessage || __('No transactions to list.')}</p> !!transactions.length && (
)} <div className="card__actions card__actions--between card__actions--top-space">
{!slim && !!transactionList.length && ( <FileExporter
<div className="card__actions card__actions--between card__actions--top-space"> data={transactionList}
<FileExporter label={__('Export')}
data={transactionList} title={__('Export Transactions')}
label={__('Export')} filters={['nout']}
title={__('Export Transactions')} defaultPath={__('lbry-transactions-history')}
filters={['nout']} />
defaultPath={__('lbry-transactions-history')}
/>
<FormField <FormField
type="select" type="select"
value={filterSetting || TRANSACTIONS.ALL} value={filterSetting || TRANSACTIONS.ALL}
onChange={this.handleFilterChanged} onChange={this.handleFilterChanged}
affixClass="form-field--align-center" affixClass="form-field--align-center"
prefix={__('Show')} prefix={__('Show')}
postfix={ postfix={
<Button <Button
button="link" button="link"
icon={icons.HELP} icon={icons.HELP}
href="https://lbry.io/faq/transaction-types" href="https://lbry.io/faq/transaction-types"
title={__('Help')} title={__('Help')}
/> />
} }
> >
{transactionTypes.map(tt => ( {transactionTypes.map(tt => (
<option key={tt} value={tt}> <option key={tt} value={tt}>
{__(`${this.capitalize(tt)}`)} {__(`${this.capitalize(tt)}`)}
</option> </option>
))} ))}
</FormField> </FormField>
</div> </div>
)} )}
</header> </header>
{!transactionList.length && (
<p className="card__content">{emptyMessage || __('No transactions to list.')}</p>
)}
{!!transactionList.length && ( {!!transactionList.length && (
<div className="card__content"> <div className="card__content">

View file

@ -12,7 +12,7 @@ type DraftTransaction = {
}; };
type Props = { type Props = {
openModal: ({ id: string }, { address: string, amount: number }) => void, openModal: (id: string, { address: string, amount: number }) => void,
balance: number, balance: number,
}; };

View file

@ -69,27 +69,24 @@ export class Modal extends React.PureComponent<ModalProps> {
> >
<h1 className="card__title">{title}</h1> <h1 className="card__title">{title}</h1>
<div className="card__content">{children}</div> <div className="card__content">{children}</div>
{type === 'custom' ? null : ( // custom modals define their own buttons
<div className="card__content"> <div className="card__actions">
{type === 'custom' ? null : ( // custom modals define their own buttons <Button
<div className="card__actions"> button="primary"
label={confirmButtonLabel}
disabled={confirmButtonDisabled}
onClick={onConfirmed}
/>
{type === 'confirm' ? (
<Button <Button
button="primary" button="link"
label={confirmButtonLabel} label={abortButtonLabel}
disabled={confirmButtonDisabled} disabled={abortButtonDisabled}
onClick={onConfirmed} onClick={onAborted}
/> />
{type === 'confirm' ? ( ) : null}
<Button </div>
button="link" )}
label={abortButtonLabel}
disabled={abortButtonDisabled}
onClick={onAborted}
/>
) : null}
</div>
)}
</div>
</ReactModal> </ReactModal>
); );
} }

View file

@ -6,13 +6,23 @@ import Button from 'component/button';
type Props = { type Props = {
closeModal: () => void, closeModal: () => void,
unlockWallet: string => void,
walletEncryptSucceded: boolean, walletEncryptSucceded: boolean,
walletEncryptResult: boolean,
updateWalletStatus: boolean, updateWalletStatus: boolean,
encryptWallet: string => void,
updateWalletStatus: () => void,
}; };
class ModalWalletEncrypt extends React.PureComponent<Props> { type State = {
newPassword: ?string,
newPasswordConfirm: ?string,
passwordMismatch: boolean,
understandConfirmed: boolean,
understandError: boolean,
submitted: boolean,
failMessage: boolean,
};
class ModalWalletEncrypt extends React.PureComponent<Props, State> {
state = { state = {
newPassword: null, newPassword: null,
newPasswordConfirm: null, newPasswordConfirm: null,
@ -23,15 +33,30 @@ class ModalWalletEncrypt extends React.PureComponent<Props> {
failMessage: false, failMessage: false,
}; };
onChangeNewPassword(event) { componentDidUpdate() {
const { props, state } = this;
if (state.submitted) {
if (props.walletEncryptSucceded === true) {
props.closeModal();
props.updateWalletStatus();
} else if (props.walletEncryptSucceded === false) {
// See https://github.com/lbryio/lbry/issues/1307
// eslint-disable-next-line react/no-did-update-set-state
this.setState({ failMessage: 'Unable to encrypt wallet.' });
}
}
}
onChangeNewPassword(event: SyntheticInputEvent<>) {
this.setState({ newPassword: event.target.value }); this.setState({ newPassword: event.target.value });
} }
onChangeNewPasswordConfirm(event) { onChangeNewPasswordConfirm(event: SyntheticInputEvent<>) {
this.setState({ newPasswordConfirm: event.target.value }); this.setState({ newPasswordConfirm: event.target.value });
} }
onChangeUnderstandConfirm(event) { onChangeUnderstandConfirm(event: SyntheticInputEvent<>) {
this.setState({ this.setState({
understandConfirmed: /^.?i understand.?$/i.test(event.target.value), understandConfirmed: /^.?i understand.?$/i.test(event.target.value),
}); });
@ -60,20 +85,6 @@ class ModalWalletEncrypt extends React.PureComponent<Props> {
this.props.encryptWallet(state.newPassword); this.props.encryptWallet(state.newPassword);
} }
componentDidUpdate() {
const { props, state } = this;
if (state.submitted) {
if (props.walletEncryptSucceded === true) {
props.closeModal();
props.updateWalletStatus();
} else if (props.walletEncryptSucceded === false) {
// See https://github.com/lbryio/lbry/issues/1307
this.setState({ failMessage: 'Unable to encrypt wallet.' });
}
}
}
render() { render() {
const { closeModal } = this.props; const { closeModal } = this.props;

View file

@ -23,7 +23,7 @@ type Props = {
channelIsMine: boolean, channelIsMine: boolean,
fetchClaims: (string, number) => void, fetchClaims: (string, number) => void,
navigate: (string, {}) => void, navigate: (string, {}) => void,
openModal: ({ id: string }, { uri: string }) => void, openModal: (id: string, { uri: string }) => void,
}; };
class ChannelPage extends React.PureComponent<Props> { class ChannelPage extends React.PureComponent<Props> {
@ -99,10 +99,7 @@ class ChannelPage extends React.PureComponent<Props> {
icon={icons.GLOBE} icon={icons.GLOBE}
label={__('Share Channel')} label={__('Share Channel')}
onClick={() => onClick={() =>
openModal( openModal(MODALS.SOCIAL_SHARE, { uri, speechShareable: true, isChannel: true })
{ id: MODALS.SOCIAL_SHARE },
{ uri, speechShareable: true, isChannel: true }
)
} }
/> />
</div> </div>
@ -111,33 +108,34 @@ class ChannelPage extends React.PureComponent<Props> {
<section className="media-group--list">{contentList}</section> <section className="media-group--list">{contentList}</section>
{(!fetching || (claimsInChannel && claimsInChannel.length)) && totalPages > 1 && ( {(!fetching || (claimsInChannel && claimsInChannel.length)) &&
<FormRow verticallyCentered centered> totalPages > 1 && (
<ReactPaginate <FormRow verticallyCentered centered>
pageCount={totalPages} <ReactPaginate
pageRangeDisplayed={2} pageCount={totalPages}
previousLabel="" pageRangeDisplayed={2}
nextLabel="" previousLabel=""
activeClassName="pagination__item--selected" nextLabel=""
pageClassName="pagination__item" activeClassName="pagination__item--selected"
previousClassName="pagination__item pagination__item--previous" pageClassName="pagination__item"
nextClassName="pagination__item pagination__item--next" previousClassName="pagination__item pagination__item--previous"
breakClassName="pagination__item pagination__item--break" nextClassName="pagination__item pagination__item--next"
marginPagesDisplayed={2} breakClassName="pagination__item pagination__item--break"
onPageChange={e => this.changePage(e.selected + 1)} marginPagesDisplayed={2}
forcePage={currentPage} onPageChange={e => this.changePage(e.selected + 1)}
initialPage={currentPage} forcePage={currentPage}
containerClassName="pagination" initialPage={currentPage}
/> containerClassName="pagination"
/>
<FormField <FormField
className="paginate-channel" className="paginate-channel"
onKeyUp={e => this.paginate(e, totalPages)} onKeyUp={e => this.paginate(e, totalPages)}
prefix={__('Go to page:')} prefix={__('Go to page:')}
type="text" type="text"
/> />
</FormRow> </FormRow>
)} )}
{!channelIsMine && <HiddenNsfwClaims className="card__content help" uri={uri} />} {!channelIsMine && <HiddenNsfwClaims className="card__content help" uri={uri} />}
</Page> </Page>

View file

@ -216,14 +216,14 @@ class FilePage extends React.Component<Props> {
button="alt" button="alt"
icon={icons.GIFT} icon={icons.GIFT}
label={__('Send a tip')} label={__('Send a tip')}
onClick={() => openModal({ id: MODALS.SEND_TIP }, { uri })} onClick={() => openModal(MODALS.SEND_TIP, { uri })}
/> />
)} )}
<Button <Button
button="alt" button="alt"
icon={icons.GLOBE} icon={icons.GLOBE}
label={__('Share')} label={__('Share')}
onClick={() => openModal({ id: MODALS.SOCIAL_SHARE }, { uri, speechShareable })} onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, speechShareable })}
/> />
</div> </div>

View file

@ -191,8 +191,8 @@ class HelpPage extends React.PureComponent<Props, State> {
<h2 className="card__title">{__('Report a Bug or Suggest a New Feature')}</h2> <h2 className="card__title">{__('Report a Bug or Suggest a New Feature')}</h2>
<p className="card__subtitle"> <p className="card__subtitle">
{__('Did you find something wrong? Think LBRY could add something useful and cool?')} {__('Did you find something wrong? Think LBRY could add something useful and cool?')}{' '}
<Button button="link" label={__('Learn more')} href="https://lbry.io/faq/support" /> <Button button="link" label={__('Learn more')} href="https://lbry.io/faq/support" />.
</p> </p>
</header> </header>
@ -274,14 +274,15 @@ class HelpPage extends React.PureComponent<Props, State> {
{this.state.accessTokenHidden && ( {this.state.accessTokenHidden && (
<Button button="link" label={__('View')} onClick={this.showAccessToken} /> <Button button="link" label={__('View')} onClick={this.showAccessToken} />
)} )}
{!this.state.accessTokenHidden && accessToken && ( {!this.state.accessTokenHidden &&
<div> accessToken && (
<p>{accessToken}</p> <div>
<div className="alert-text"> <p>{accessToken}</p>
{__('This is equivalent to a password. Do not post or share this.')} <div className="alert-text">
{__('This is equivalent to a password. Do not post or share this.')}
</div>
</div> </div>
</div> )}
)}
</td> </td>
</tr> </tr>
</tbody> </tbody>

View file

@ -99,7 +99,7 @@ export const doUploadThumbnail = (filePath: string, nsfw: boolean) => (
type: ACTIONS.UPDATE_PUBLISH_FORM, type: ACTIONS.UPDATE_PUBLISH_FORM,
data: { uploadThumbnailStatus: THUMBNAIL_STATUSES.API_DOWN }, data: { uploadThumbnailStatus: THUMBNAIL_STATUSES.API_DOWN },
}, },
dispatch(doOpenModal(MODALS.ERROR, { error })) doError(MODALS.ERROR, { error })
) )
); );
@ -119,16 +119,17 @@ export const doUploadThumbnail = (filePath: string, nsfw: boolean) => (
body: data, body: data,
}) })
.then(response => response.json()) .then(response => response.json())
.then(json => .then(
json.success json =>
? dispatch({ json.success
type: ACTIONS.UPDATE_PUBLISH_FORM, ? dispatch({
data: { type: ACTIONS.UPDATE_PUBLISH_FORM,
uploadThumbnailStatus: THUMBNAIL_STATUSES.COMPLETE, data: {
thumbnail: `${json.data.url}${fileExt}`, uploadThumbnailStatus: THUMBNAIL_STATUSES.COMPLETE,
}, thumbnail: `${json.data.url}${fileExt}`,
}) },
: uploadError(json.message) })
: uploadError(json.message)
) )
.catch(err => uploadError(err.message)); .catch(err => uploadError(err.message));
}; };

View file

@ -21,7 +21,6 @@
@import 'component/form-field'; @import 'component/form-field';
@import 'component/form-row'; @import 'component/form-row';
@import 'component/header'; @import 'component/header';
@import 'component/icon';
@import 'component/item-list'; @import 'component/item-list';
@import 'component/load-screen'; @import 'component/load-screen';
@import 'component/main'; @import 'component/main';

View file

@ -91,7 +91,6 @@
&:hover { &:hover {
background-color: $lbry-gray-1; background-color: $lbry-gray-1;
color: $lbry-black;
html[data-theme='dark'] & { html[data-theme='dark'] & {
background-color: rgba($lbry-white, 0.1); background-color: rgba($lbry-white, 0.1);

View file

@ -60,6 +60,7 @@
} }
.card__actions--center { .card__actions--center {
display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }

View file

@ -19,13 +19,12 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
position: absolute; position: absolute;
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: contain;
&:not(.card__media--nsfw) { &:not(.card__media--nsfw) {
background-color: $lbry-black; background-color: #000; // solid black to blend nicely when the video starts (if it doesn't take the full width)
html[data-theme='dark'] & {
background-color: rgba($lbry-white, 0.1);
}
} }
&:hover { &:hover {
@ -76,7 +75,6 @@
height: 100%; height: 100%;
align-items: center; align-items: center;
background-color: rgba($lbry-black, 0.5);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
@ -93,6 +91,7 @@
top: 0; top: 0;
left: 0; left: 0;
background-color: #000;
align-items: center; align-items: center;
display: flex; display: flex;
justify-content: center; justify-content: center;

View file

@ -18,7 +18,6 @@
align-items: center; align-items: center;
display: flex; display: flex;
font-size: 1.25rem; font-size: 1.25rem;
margin-bottom: var(--spacing-vertical-small);
&.form-field--auto-height { &.form-field--auto-height {
height: auto; height: auto;
@ -95,7 +94,7 @@
.form-field__select-wrapper { .form-field__select-wrapper {
position: relative; position: relative;
width: 20rem; width: 20rem;
height: 3rem; height: 2rem;
&::after { &::after {
width: 100%; width: 100%;
@ -104,8 +103,7 @@
left: 0; left: 0;
// TRIANGLE_DOWN // TRIANGLE_DOWN
// background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E %3Cpath d='M3 4 L21 4 12 20 3 4 Z' stroke='black' stroke-width='2' fill='black' fill-rule='evenodd' stroke-linejoin='round'/%3E %3C/svg%3E"), background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E %3Cpath d='M3 4 L21 4 12 20 3 4 Z' stroke='black' stroke-width='2' fill='black' fill-rule='evenodd' stroke-linejoin='round'/%3E %3C/svg%3E");
// linear-gradient(to right, transparent 80%, $lbry-gray-1 85%);
background-position: 95% center, right top; background-position: 95% center, right top;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 0.8rem, 100%; background-size: 0.8rem, 100%;
@ -121,7 +119,7 @@
background-color: $lbry-gray-1; background-color: $lbry-gray-1;
border-radius: 0; border-radius: 0;
padding: var(--spacing-vertical-small); padding: 0 var(--spacing-vertical-small);
-webkit-appearance: none; -webkit-appearance: none;

View file

@ -1,6 +0,0 @@
// Icons with icons directly following should have a margin-right for proper spacing
// Same for prices on cards
.icon + .icon,
.credit-amount + .icon {
margin-left: var(--spacing-vertical-small);
}

View file

@ -4,6 +4,10 @@
position: relative; position: relative;
z-index: 1; z-index: 1;
&.main--contained {
max-width: 1000px;
}
&:not(.main--no-padding) { &:not(.main--no-padding) {
padding: var(--spacing-vertical-large); padding: var(--spacing-vertical-large);
} }

View file

@ -15,20 +15,28 @@
opacity: 0.5; opacity: 0.5;
} }
.CodeMirror-selected {
// background-color: transparent;
}
.CodeMirror-selectedtext {
background-color: $lbry-teal-5 !important;
color: $lbry-white;
}
.cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word) { .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word) {
text-decoration: underline; text-decoration: underline;
text-decoration-color: $lbry-red-3; text-decoration-color: $lbry-red-3;
text-decoration-style: dotted; text-decoration-style: dotted;
} }
html[data-theme='dark'] & {
border-left: 1px solid rgba($lbry-white, 0.2);
border-right: 1px solid rgba($lbry-white, 0.2);
border-bottom: 1px solid rgba($lbry-white, 0.2);
background-color: rgba($lbry-white, 0.1);
color: $lbry-white;
.CodeMirror-selectedtext {
color: $lbry-black;
}
.editor-preview.editor-preview-active {
background-color: $lbry-gray-5;
color: $lbry-black;
}
}
} }
// Fix selection // Fix selection
@ -82,6 +90,33 @@
i.separator { i.separator {
border: none; border: none;
} }
html[data-theme='dark'] & {
background-color: rgba($lbry-white, 0.2);
a {
color: $lbry-white !important; // We need to use !important to override the CodeMirror styles
&.active {
background-color: rgba($lbry-black, 0.4);
}
&:hover {
color: $lbry-black !important;
}
}
&.disabled-for-preview {
a:not(.no-disable) {
background-color: transparent;
}
// The markdown preview button is highlighted during preview when the other buttons are disabled
a.no-disable {
background-color: rgba($lbry-white, 0.3);
}
}
}
} }
.editor-statusbar { .editor-statusbar {

View file

@ -44,7 +44,8 @@
.media--placeholder { .media--placeholder {
.media__channel, .media__channel,
.media__date, .media__date,
.media__title { .media__title,
.media__properties {
min-height: 1rem; min-height: 1rem;
} }
@ -154,6 +155,7 @@
.media__actions { .media__actions {
display: flex; display: flex;
flex-wrap: wrap;
padding-top: var(--spacing-vertical-large); padding-top: var(--spacing-vertical-large);
padding-bottom: var(--spacing-vertical-large); padding-bottom: var(--spacing-vertical-large);
} }
@ -254,11 +256,16 @@
&:not(:last-of-type) { &:not(:last-of-type) {
margin-bottom: var(--spacing-vertical-large); margin-bottom: var(--spacing-vertical-large);
} }
&.media__info-text--center {
text-align: center;
}
} }
.media__info-title { .media__info-title {
font-size: 1.25rem; font-size: 1.25rem;
font-weight: 500; font-weight: 500;
margin-bottom: var(--spacing-vertical-small);
} }
// M E D I A // M E D I A
@ -298,10 +305,6 @@
position: relative; position: relative;
top: 0.2rem; top: 0.2rem;
} }
svg {
padding-top: 0.2rem;
}
} }
svg { svg {
@ -328,6 +331,10 @@
} }
} }
.media__properties--placeholder {
@include placeholder;
}
// M E D I A // M E D I A
// S U B T I T L E // S U B T I T L E
@ -518,18 +525,6 @@
rgba(mix($lbry-blue-3, $lbry-black, 70%), 0.8) 100% rgba(mix($lbry-blue-3, $lbry-black, 70%), 0.8) 100%
); );
} }
// The featured row needs different top spacing
// depending on screen width
@media (min-width: 601px) {
padding-bottom: var(--spacing-vertical-large);
}
@media (max-width: 600px) {
padding-bottom: var(--spacing-vertical-medium);
}
.media-group__header-title { .media-group__header-title {
background-image: linear-gradient(to right, $lbry-white 80%, transparent 100%); background-image: linear-gradient(to right, $lbry-white 80%, transparent 100%);
} }
@ -548,8 +543,6 @@
} }
&:not(:first-of-type) { &:not(:first-of-type) {
padding-bottom: var(--spacing-vertical-large);
.media-group__header-title { .media-group__header-title {
background-image: linear-gradient(to right, $lbry-black 80%, transparent 100%); background-image: linear-gradient(to right, $lbry-black 80%, transparent 100%);
} }
@ -643,7 +636,15 @@
.media-scrollhouse { .media-scrollhouse {
min-height: 200px; min-height: 200px;
overflow: hidden; padding-bottom: var(--spacing-vertical-large);
// Show the scroll bar on hover
// `overlay` doesn't take up any vertical space
overflow-x: hidden;
overflow-y: hidden;
&:hover {
overflow-x: overlay;
}
// The media queries in this block ensure that a row of // The media queries in this block ensure that a row of
// content and its' child elements look good at certain breakpoints // content and its' child elements look good at certain breakpoints
@ -666,35 +667,30 @@
@media (min-width: 601px) { @media (min-width: 601px) {
width: calc((100% / 6) - 2.25rem); width: calc((100% / 6) - 2.25rem);
margin-left: var(--spacing-vertical-large);
&:not(:first-of-type) {
margin-left: var(--spacing-vertical-large);
}
&:first-of-type {
margin-left: var(--spacing-vertical-large);
}
&:last-of-type { &:last-of-type {
margin-right: var(--spacing-vertical-large); // For some reason margin doesn't work here.
padding-right: var(--spacing-vertical-large);
} }
} }
@media (max-width: 600px) { // May be needed for mobile design
width: calc((100% / 3) - 3rem); // @media (max-width: 600px) {
// width: calc((100% / 3) - 3rem);
&:not(:first-of-type) { //
margin-left: var(--spacing-vertical-medium); // &:not(:first-of-type) {
} // margin-left: var(--spacing-vertical-medium);
// }
&:first-of-type { //
margin-left: var(--spacing-vertical-large); // &:first-of-type {
} // margin-left: var(--spacing-vertical-large);
// }
&:last-of-type { //
margin-right: var(--spacing-vertical-large); // &:last-of-type {
} // margin-right: var(--spacing-vertical-large);
} // }
// }
} }
.media__title { .media__title {

View file

@ -1,4 +1,7 @@
.pagination { .pagination {
display: flex;
align-items: center;
+ .form-field { + .form-field {
margin-left: var(--spacing-vertical-medium); margin-left: var(--spacing-vertical-medium);
} }
@ -9,19 +12,19 @@
height: 3rem; height: 3rem;
border-radius: 50%; border-radius: 50%;
display: inline-block;
text-align: center; text-align: center;
&:not(&--selected):not(&--break):not(.disabled):hover { &:not(.pagination__item--selected):not(.pagination__item--break):not(.disabled):hover {
background-color: $lbry-gray-1; background-color: $lbry-gray-1;
} }
&:not(&--previous):not(&--next) { &:not(.pagination__item--previous):not(.pagination__item--next) {
font-weight: 600; font-weight: 600;
line-height: 3rem; line-height: 3rem;
margin: 0 0.5em;
} }
&:not(&--break):not(.disabled) { &:not(.pagination__item--break):not(.disabled) {
cursor: pointer; cursor: pointer;
} }
@ -36,7 +39,6 @@
.pagination__item--previous, .pagination__item--previous,
.pagination__item--next { .pagination__item--next {
bottom: -0.4rem;
font-size: 2.5rem; font-size: 2.5rem;
line-height: 1; line-height: 1;
position: relative; position: relative;

View file

@ -5,7 +5,6 @@
background-color: $lbry-teal-4; background-color: $lbry-teal-4;
border-radius: 0.5rem; border-radius: 0.5rem;
color: $lbry-white; color: $lbry-white;
max-width: var(--snack-bar-width);
padding: var(--spacing-vertical-small) var(--spacing-vertical-large) var(--spacing-vertical-small) padding: var(--spacing-vertical-small) var(--spacing-vertical-large) var(--spacing-vertical-small)
var(--spacing-vertical-medium); var(--spacing-vertical-medium);
position: fixed; position: fixed;
@ -19,7 +18,7 @@
.snack-bar__action { .snack-bar__action {
display: inline-block; display: inline-block;
margin-bottom: var(--spacing-vertical-small); margin: var(--spacing-vertical-small) 0;
min-width: min-content; min-width: min-content;
text-transform: uppercase; text-transform: uppercase;

View file

@ -69,12 +69,6 @@ input {
&:not(.input-copyable):not(.wunderbar__input) { &:not(.input-copyable):not(.wunderbar__input) {
border-bottom: var(--input-border-size) solid $lbry-gray-5; border-bottom: var(--input-border-size) solid $lbry-gray-5;
} }
&:not(.input-copyable):not(.wunderbar__input):not(:placeholder-shown):not(:disabled) {
&:hover {
border-color: rgba($lbry-black, 0.8);
}
}
} }
.input--price-amount { .input--price-amount {

View file

@ -19,7 +19,6 @@ $large-breakpoint: 1921px;
--spacing-vertical-large: 2rem; --spacing-vertical-large: 2rem;
--video-aspect-ratio: 56.25%; // 9 x 16 --video-aspect-ratio: 56.25%; // 9 x 16
--snack-bar-width: 756px;
// Text // Text
--text-max-width: 660px; --text-max-width: 660px;

View file

@ -105,6 +105,10 @@
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/@lbry/color/-/color-1.0.3.tgz#ec22b2c48b0e358759528fb3bbe7ba468d4e41ca" resolved "https://registry.yarnpkg.com/@lbry/color/-/color-1.0.3.tgz#ec22b2c48b0e358759528fb3bbe7ba468d4e41ca"
"@lbry/components@^1.5.1":
version "1.6.3"
resolved "https://registry.yarnpkg.com/@lbry/components/-/components-1.6.3.tgz#5187736e8d51d24a4678f972d3c062d880d6d853"
"@mapbox/hast-util-table-cell-style@^0.1.3": "@mapbox/hast-util-table-cell-style@^0.1.3":
version "0.1.3" version "0.1.3"
resolved "https://registry.yarnpkg.com/@mapbox/hast-util-table-cell-style/-/hast-util-table-cell-style-0.1.3.tgz#5b7166ae01297d72216932b245e4b2f0b642dca6" resolved "https://registry.yarnpkg.com/@mapbox/hast-util-table-cell-style/-/hast-util-table-cell-style-0.1.3.tgz#5b7166ae01297d72216932b245e4b2f0b642dca6"
@ -4074,9 +4078,9 @@ flatten@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
flow-bin@^0.69.0: flow-bin@^0.89.0:
version "0.69.0" version "0.89.0"
resolved "http://registry.npmjs.org/flow-bin/-/flow-bin-0.69.0.tgz#053159a684a6051fcbf0b71a2eb19a9679082da6" resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.89.0.tgz#6bd29c2af7e0f429797f820662f33749105c32fa"
flow-typed@^2.3.0: flow-typed@^2.3.0:
version "2.5.1" version "2.5.1"