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:
parent
42e3d74805
commit
ad90c1f96e
31 changed files with 384 additions and 347 deletions
|
@ -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",
|
||||||
|
|
|
@ -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 => (
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.')}
|
||||||
|
|
|
@ -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 })}>
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>ⓘ</div>
|
<div>ⓘ</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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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));
|
||||||
};
|
};
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__actions--center {
|
.card__actions--center {
|
||||||
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue