purchases page, cleanup on pages with <ClaimList />

This commit is contained in:
Sean Yesmunt 2020-05-11 11:54:39 -04:00
parent ca5f54cbfd
commit 919f82ba94
32 changed files with 366 additions and 142 deletions

View file

@ -132,7 +132,7 @@
"imagesloaded": "^4.1.4",
"json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#259317250af62391718ac0cb8b8e25f172dc4223",
"lbry-redux": "lbryio/lbry-redux#cd9c15567f2934ddc82de364d88b378ff04d5571",
"lbryinc": "lbryio/lbryinc#cc62a4eec10845cc0b31da7d0f27287cfa7c4866",
"lint-staged": "^7.0.2",
"localforage": "^1.7.1",

View file

@ -26,7 +26,6 @@ type Props = {
// If using the default header, this is a unique ID needed to persist the state of the filter setting
persistedStorageKey?: string,
showHiddenByUser: boolean,
headerLabel?: string | Node,
showUnresolvedClaims?: boolean,
renderProperties: ?(Claim) => Node,
includeSupportAction?: boolean,
@ -51,7 +50,6 @@ export default function ClaimList(props: Props) {
page,
id,
showHiddenByUser,
headerLabel,
showUnresolvedClaims,
renderProperties,
includeSupportAction,
@ -105,12 +103,10 @@ export default function ClaimList(props: Props) {
<section
className={classnames('claim-list', {
'claim-list--small': type === 'small',
'claim-list--card-body': isCardBody,
})}
>
{header !== false && (
<React.Fragment>
{headerLabel && <label className="claim-list__header-label">{headerLabel}</label>}
{header && (
<div className={classnames('claim-list__header', { 'section__title--small': type === 'small' })}>
{header}
@ -139,6 +135,7 @@ export default function ClaimList(props: Props) {
<ul
className={classnames('ul--no-style', {
card: !isCardBody,
'claim-list--card-body': isCardBody,
})}
>
{sortedUris.map((uri, index) => (

View file

@ -13,6 +13,7 @@ import ClaimPreview from 'component/claimPreview';
import { toCapitalCase } from 'util/string';
import I18nMessage from 'component/i18nMessage';
import * as ICONS from 'constants/icons';
import Card from 'component/common/card';
type Props = {
uris: Array<string>,
@ -595,30 +596,32 @@ function ClaimListDiscover(props: Props) {
return (
<React.Fragment>
<ClaimList
id={claimSearchCacheQuery}
loading={loading}
uris={claimSearchResult}
header={header || defaultHeader}
headerLabel={headerLabel}
headerAltControls={meta}
onScrollBottom={handleScrollBottom}
page={page}
pageSize={CS.PAGE_SIZE}
timedOutMessage={timedOutMessage}
renderProperties={renderProperties}
includeSupportAction={includeSupportAction}
hideBlock={hideBlock}
injectedItem={injectedItem}
{headerLabel && <label className="claim-list__header-label">{headerLabel}</label>}
<Card
title={header || defaultHeader}
titleActions={meta && <div className="card__actions--inline">{meta}</div>}
isBodyList
body={
<>
<ClaimList
isCardBody
id={claimSearchCacheQuery}
loading={loading}
uris={claimSearchResult}
onScrollBottom={handleScrollBottom}
page={page}
pageSize={CS.PAGE_SIZE}
timedOutMessage={timedOutMessage}
renderProperties={renderProperties}
includeSupportAction={includeSupportAction}
hideBlock={hideBlock}
injectedItem={injectedItem}
/>
{loading &&
new Array(pageSize || CS.PAGE_SIZE).fill(1).map((x, i) => <ClaimPreview key={i} placeholder="loading" />)}
</>
}
/>
{loading && (
<div className="card">
{new Array(pageSize || CS.PAGE_SIZE).fill(1).map((x, i) => (
<ClaimPreview key={i} placeholder="loading" />
))}
</div>
)}
</React.Fragment>
);
}

View file

@ -12,6 +12,7 @@ import {
selectChannelIsBlocked,
doFileGet,
makeSelectReflectingClaimForUri,
makeSelectClaimWasPurchased,
} from 'lbry-redux';
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
import { selectShowMatureContent } from 'redux/selectors/settings';
@ -36,6 +37,7 @@ const select = (state, props) => ({
channelIsBlocked: props.uri && selectChannelIsBlocked(props.uri)(state),
isSubscribed: props.uri && makeSelectIsSubscribed(props.uri, true)(state),
streamingUrl: props.uri && makeSelectStreamingUrlForUriWebProxy(props.uri)(state),
wasPurchased: props.uri && makeSelectClaimWasPurchased(props.uri)(state),
});
const perform = dispatch => ({

View file

@ -52,7 +52,7 @@ export default function ClaimTags(props: Props) {
}
return (
<div className={classnames('file-properties', { 'file-properties--large': type === 'large' })}>
<div className={classnames('file-properties--small', { 'file-properties--large': type === 'large' })}>
{tagsToDisplay.map(tag => (
<Tag key={tag} title={tag} name={tag} />
))}

View file

@ -13,6 +13,7 @@ type Props = {
showLBC?: boolean,
fee?: boolean,
badge?: boolean,
className?: string,
};
class CreditAmount extends React.PureComponent<Props> {
@ -26,7 +27,18 @@ class CreditAmount extends React.PureComponent<Props> {
};
render() {
const { amount, precision, showFullPrice, showFree, showPlus, isEstimate, fee, showLBC, badge } = this.props;
const {
amount,
precision,
showFullPrice,
showFree,
showPlus,
isEstimate,
fee,
showLBC,
badge,
className,
} = this.props;
const minimumRenderableAmount = 10 ** (-1 * precision);
const fullPrice = formatFullPrice(amount, 2);
@ -64,13 +76,13 @@ class CreditAmount extends React.PureComponent<Props> {
return (
<span
title={fullPrice}
className={classnames({
className={classnames(className, {
badge,
'badge--cost': badge && amount > 0,
'badge--free': badge && isFree,
})}
>
{amountText}
<span>{amountText}</span>
{isEstimate ? (
<span className="credit-amount__estimate" title={__('This is an estimate and does not include data fees')}>

View file

@ -618,4 +618,9 @@ export const icons = {
viewBox: '0 0 60 60',
}
),
[ICONS.PURCHASED]: buildIcon(
<g>
<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" />
</g>
),
};

View file

@ -14,6 +14,7 @@ type Props = {
inheritStyle?: boolean,
showLBC?: boolean,
hideFree?: boolean, // hide the file price if it's free
className?: string,
};
class FilePrice extends React.PureComponent<Props> {
@ -38,7 +39,7 @@ class FilePrice extends React.PureComponent<Props> {
};
render() {
const { costInfo, showFullPrice, badge, inheritStyle, showLBC, hideFree } = this.props;
const { costInfo, showFullPrice, badge, inheritStyle, showLBC, hideFree, className } = this.props;
if (costInfo && (!costInfo.cost || (!costInfo.cost && hideFree))) {
return null;
}
@ -52,6 +53,7 @@ class FilePrice extends React.PureComponent<Props> {
amount={costInfo.cost}
isEstimate={!costInfo.includesData}
showFullPrice={showFullPrice}
className={className}
/>
) : null;
}

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux';
import { makeSelectFilePartlyDownloaded, makeSelectClaimIsMine } from 'lbry-redux';
import { makeSelectFilePartlyDownloaded, makeSelectClaimIsMine, makeSelectClaimWasPurchased } from 'lbry-redux';
import { makeSelectIsSubscribed, makeSelectIsNew } from 'redux/selectors/subscriptions';
import FileProperties from './view';
@ -8,9 +8,7 @@ const select = (state, props) => ({
isSubscribed: makeSelectIsSubscribed(props.uri)(state),
isNew: makeSelectIsNew(props.uri)(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
claimWasPurchased: makeSelectClaimWasPurchased(props.uri)(state),
});
export default connect(
select,
null
)(FileProperties);
export default connect(select, null)(FileProperties);

View file

@ -1,5 +1,5 @@
// @flow
import * as icons from 'constants/icons';
import * as ICONS from 'constants/icons';
import * as React from 'react';
import classnames from 'classnames';
import Icon from 'component/common/icon';
@ -14,18 +14,28 @@ type Props = {
isSubscribed: boolean,
isNew: boolean,
small: boolean,
claimWasPurchased: boolean,
};
export default function FileProperties(props: Props) {
const { uri, downloaded, claimIsMine, isSubscribed, small = false } = props;
const { uri, downloaded, claimIsMine, claimWasPurchased, isSubscribed, small = false } = props;
return (
<div className={classnames('file-properties', { 'file-properties--small': small })}>
<FilePrice hideFree uri={uri} />
<div
className={classnames('file-properties', {
'file-properties--small': small,
})}
>
<VideoDuration uri={uri} />
<FileType uri={uri} />
{isSubscribed && <Icon tooltip icon={icons.SUBSCRIBE} />}
{!claimIsMine && downloaded && <Icon tooltip icon={icons.LIBRARY} />}
{isSubscribed && <Icon tooltip icon={ICONS.SUBSCRIBE} />}
{!claimIsMine && downloaded && <Icon tooltip icon={ICONS.LIBRARY} />}
{claimWasPurchased ? (
<span className="file-properties__purchased">
<Icon icon={ICONS.PURCHASED} />
</span>
) : (
<FilePrice hideFree uri={uri} badge={false} className="file-properties__not-purchased" />
)}
</div>
);
}

View file

@ -14,7 +14,6 @@ import DiscoverPage from 'page/discover';
import HomePage from 'page/home';
import InvitedPage from 'page/invited';
import RewardsPage from 'page/rewards';
import FileListDownloaded from 'page/fileListDownloaded';
import FileListPublished from 'page/fileListPublished';
import InvitePage from 'page/invite';
import SearchPage from 'page/search';
@ -177,7 +176,6 @@ function AppRouter(props: Props) {
<Route path={`/$/${PAGES.INVITE}/:referrer`} exact component={InvitedPage} />
<PrivateRoute {...props} path={`/$/${PAGES.INVITE}`} component={InvitePage} />
<PrivateRoute {...props} path={`/$/${PAGES.DOWNLOADED}`} component={FileListDownloaded} />
<PrivateRoute {...props} path={`/$/${PAGES.PUBLISHED}`} component={FileListPublished} />
<PrivateRoute {...props} path={`/$/${PAGES.CREATOR_DASHBOARD}`} component={CreatorDashboard} />
<PrivateRoute {...props} path={`/$/${PAGES.PUBLISH}`} component={PublishPage} />

View file

@ -3,7 +3,7 @@ export const MINIMUM_PUBLISH_BID = 0.00001;
export const CHANNEL_ANONYMOUS = 'anonymous';
export const CHANNEL_NEW = 'new';
export const PAGE_SIZE = 20;
export const PUBLISHES_PAGE_SIZE = 10;
export const MY_CLAIMS_PAGE_SIZE = 10;
export const PAGE_PARAM = 'page';
export const PAGE_SIZE_PARAM = 'page_size';

View file

@ -99,3 +99,4 @@ export const VALIDATED = 'Check';
export const SLIDERS = 'Sliders';
export const SCIENCE = 'Science';
export const ANALYTICS = 'BarChart2';
export const PURCHASED = 'Key';

View file

@ -6,7 +6,6 @@ exports.BACKUP = 'backup';
exports.CHANNEL = 'channel';
exports.DISCOVER = 'discover';
exports.HOME = 'home';
exports.DOWNLOADED = 'downloaded';
exports.HELP = 'help';
exports.LIBRARY = 'library';
exports.INVITE = 'invite';

View file

@ -2,6 +2,9 @@
import React from 'react';
import FilePrice from 'component/filePrice';
import { Modal } from 'modal/modal';
import Card from 'component/common/card';
import I18nMessage from 'component/i18nMessage';
import Button from 'component/button';
type Props = {
closeModal: () => void,
@ -30,22 +33,29 @@ class ModalAffirmPurchase extends React.PureComponent<Props> {
uri,
} = this.props;
const modalTitle = __('Confirm Purchase');
return (
<Modal
type="confirm"
isOpen
title={__('Confirm Purchase')}
contentLabel={__('Confirm Purchase')}
onConfirmed={this.onAffirmPurchase}
onAborted={cancelPurchase}
>
<p className="section__subtitle">
{__('This will purchase')} <strong>{title ? `"${title}"` : uri}</strong> {__('for')}{' '}
<strong>
<FilePrice uri={uri} showFullPrice inheritStyle showLBC={false} />
</strong>{' '}
{__('credits')}.
</p>
<Modal type="card" isOpen contentLabel={modalTitle} onAborted={cancelPurchase}>
<Card
title={modalTitle}
subtitle={
<I18nMessage
tokens={{
claim_title: <strong>{title ? `"${title}"` : uri}</strong>,
amount: <FilePrice uri={uri} showFullPrice inheritStyle />,
}}
>
This will purchase %claim_title% for %amount%.
</I18nMessage>
}
actions={
<div className="section__actions">
<Button button="primary" label={__('Confirm')} onClick={this.onAffirmPurchase} />
<Button button="link" label={__('Cancel')} onClick={cancelPurchase} />
</div>
}
/>
</Modal>
);
}

View file

@ -33,8 +33,8 @@ function ChannelsFollowingPage(props: Props) {
meta={
<Button
icon={ICONS.SEARCH}
button="link"
label={__('Discover New Channels')}
button="alt"
label={__('Discover Channels')}
navigate={`/$/${PAGES.CHANNELS_FOLLOWING_DISCOVER}`}
/>
}

View file

@ -1,10 +1,18 @@
import { connect } from 'react-redux';
import { makeSelectSearchDownloadUrlsForPage, makeSelectSearchDownloadUrlsCount, selectDownloadUrlsCount, selectIsFetchingFileList } from 'lbry-redux';
import {
makeSelectSearchDownloadUrlsForPage,
selectDownloadUrlsCount,
selectIsFetchingFileList,
doPurchaseList,
makeSelectMyPurchasesForPage,
selectIsFetchingMyPurchases,
selectMyPurchasesCount,
} from 'lbry-redux';
import FileListDownloaded from './view';
import { withRouter } from 'react-router';
const select = (state, props) => {
const { history, location } = props;
const { history, location } = props;
const { search } = location;
const urlParams = new URLSearchParams(search);
const query = urlParams.get('query') || '';
@ -13,16 +21,17 @@ const select = (state, props) => {
page,
history,
query,
allDownloadedUrlsCount: selectDownloadUrlsCount(state),
downloadedUrls: makeSelectSearchDownloadUrlsForPage(query, page)(state),
downloadedUrlsCount: makeSelectSearchDownloadUrlsCount(query)(state),
fetching: selectIsFetchingFileList(state),
downloadedUrlsCount: selectDownloadUrlsCount(state),
myPurchasesCount: selectMyPurchasesCount(state),
myPurchases: makeSelectMyPurchasesForPage(query, page)(state),
myDownloads: makeSelectSearchDownloadUrlsForPage(query, page)(state),
fetchingFileList: selectIsFetchingFileList(state),
fetchingMyPurchases: selectIsFetchingMyPurchases(state),
};
};
export default withRouter(
connect(
select,
null
)(FileListDownloaded)
connect(select, {
doPurchaseList,
})(FileListDownloaded)
);

View file

@ -1,4 +1,5 @@
// @flow
import * as ICONS from 'constants/icons';
import React, { useState } from 'react';
import Button from 'component/button';
import ClaimList from 'component/claimList';
@ -6,24 +7,41 @@ import Paginate from 'component/common/paginate';
import { PAGE_SIZE } from 'constants/claim';
import { Form } from 'component/common/form-components/form';
import Icon from 'component/common/icon';
import * as ICONS from '../../constants/icons';
import { FormField } from '../../component/common/form-components/form-field';
import { FormField } from 'component/common/form-components/form-field';
import { withRouter } from 'react-router';
import Card from 'component/common/card';
import classnames from 'classnames';
type Props = {
fetching: boolean,
allDownloadedUrlsCount: number,
fetchingFileList: boolean,
downloadedUrls: Array<string>,
downloadedUrlsCount: ?number,
history: { replace: string => void },
page: number,
query: string,
doPurchaseList: () => void,
myDownloads: Array<string>,
myPurchases: Array<string>,
myPurchasesCount: ?number,
fetchingMyPurchases: boolean,
};
function FileListDownloaded(props: Props) {
const { fetching, history, query, allDownloadedUrlsCount, downloadedUrls, downloadedUrlsCount } = props;
const hasDownloads = allDownloadedUrlsCount > 0;
const VIEW_DOWNLOADS = 'view_download';
const VIEW_PURCHASES = 'view_purchases';
function FileListDownloaded(props: Props) {
const {
history,
query,
downloadedUrlsCount,
myPurchasesCount,
myPurchases,
myDownloads,
fetchingFileList,
fetchingMyPurchases,
doPurchaseList,
} = props;
const loading = fetchingFileList || fetchingMyPurchases;
const [viewMode, setViewMode] = React.useState(VIEW_PURCHASES);
const [searchQuery, setSearchQuery] = useState('');
function handleInputChange(e) {
@ -34,45 +52,76 @@ function FileListDownloaded(props: Props) {
}
}
React.useEffect(() => {
doPurchaseList();
}, [doPurchaseList]);
return (
<React.Fragment>
{hasDownloads ? (
<React.Fragment>
<ClaimList
header={__('Your Library')}
headerAltControls={
<Form onSubmit={() => {}} className="wunderbar--inline">
<Icon icon={ICONS.SEARCH} />
<FormField
className="wunderbar__input"
onChange={handleInputChange}
value={query}
type="text"
name="query"
placeholder={__('Search')}
/>
</Form>
}
persistedStorageKey="claim-list-downloaded"
empty={__('No results for %query%', { query })}
uris={downloadedUrls}
loading={fetching}
<Card
title={
<div>
<Button
icon={ICONS.LIBRARY}
button="alt"
label={__('All Downloads')}
className={classnames(`button-toggle`, {
'button-toggle--active': viewMode === VIEW_DOWNLOADS,
})}
onClick={() => setViewMode(VIEW_DOWNLOADS)}
/>
<Button
icon={ICONS.PURCHASED}
button="alt"
label={__('Your Purchases')}
className={classnames(`button-toggle`, {
'button-toggle--active': viewMode === VIEW_PURCHASES,
})}
onClick={() => setViewMode(VIEW_PURCHASES)}
/>
<Paginate totalPages={Math.ceil(Number(downloadedUrlsCount) / Number(PAGE_SIZE))} loading={fetching} />
</React.Fragment>
) : (
<div className="main--empty">
<section className="card card--section">
<h2 className="card__title card__title--deprecated">
{__("You haven't downloaded anything from LBRY yet.")}
</h2>
<div className="card__actions card__actions--center">
<Button button="primary" navigate="/" label={__('Explore new content')} />
</div>
</section>
</div>
)}
</React.Fragment>
}
titleActions={
<div className="card__actions--inline">
<Form onSubmit={() => {}} className="wunderbar--inline">
<Icon icon={ICONS.SEARCH} />
<FormField
className="wunderbar__input"
onChange={handleInputChange}
value={query}
type="text"
name="query"
placeholder={__('Search')}
/>
</Form>
</div>
}
isBodyList
body={
<div>
<ClaimList
isCardBody
renderProperties={() => null}
empty={
viewMode === VIEW_PURCHASES && !query ? (
<div>{__("You haven't purchased anything yet silly goose.")}</div>
) : (
__('No results for %query%', { query })
)
}
uris={viewMode === VIEW_PURCHASES ? myPurchases : myDownloads}
loading={loading}
/>
{!query && (
<Paginate
loading={loading}
totalPages={Math.ceil(
Number(viewMode === VIEW_PURCHASES ? myPurchasesCount : downloadedUrlsCount) / Number(PAGE_SIZE)
)}
/>
)}
</div>
}
/>
);
}

View file

@ -11,13 +11,13 @@ import { selectUploadCount } from 'lbryinc';
import { doCheckPendingPublishesApp } from 'redux/actions/publish';
import FileListPublished from './view';
import { withRouter } from 'react-router';
import { PUBLISHES_PAGE_SIZE, PAGE_PARAM, PAGE_SIZE_PARAM } from 'constants/claim';
import { MY_CLAIMS_PAGE_SIZE, PAGE_PARAM, PAGE_SIZE_PARAM } from 'constants/claim';
const select = (state, props) => {
const { search } = props.location;
const urlParams = new URLSearchParams(search);
const page = Number(urlParams.get(PAGE_PARAM)) || '1';
const pageSize = urlParams.get(PAGE_SIZE_PARAM) || String(PUBLISHES_PAGE_SIZE);
const pageSize = urlParams.get(PAGE_SIZE_PARAM) || String(MY_CLAIMS_PAGE_SIZE);
return {
page,

View file

@ -1,3 +1,17 @@
import { connect } from 'react-redux';
import {
selectDownloadUrlsCount,
selectIsFetchingFileList,
selectMyPurchases,
selectIsFetchingMyPurchases,
} from 'lbry-redux';
import LibraryPage from './view';
export default LibraryPage;
const select = state => ({
allDownloadedUrlsCount: selectDownloadUrlsCount(state),
fetchingFileList: selectIsFetchingFileList(state),
myPurchases: selectMyPurchases(state),
fetchingMyPurchases: selectIsFetchingMyPurchases(state),
});
export default connect(select)(LibraryPage);

View file

@ -1,12 +1,45 @@
// @flow
import React from 'react';
import Button from 'component/button';
import Page from 'component/page';
import Spinner from 'component/spinner';
import DownloadList from 'page/fileListDownloaded';
import Yrbl from 'component/yrbl';
type Props = {
allDownloadedUrlsCount: number,
myPurchases: Array<string>,
fetchingMyPurchases: boolean,
fetchingFileList: boolean,
};
function LibraryPage(props: Props) {
const { allDownloadedUrlsCount, myPurchases, fetchingMyPurchases, fetchingFileList } = props;
const hasDownloads = allDownloadedUrlsCount > 0 || (myPurchases && myPurchases.length);
const loading = fetchingFileList || fetchingMyPurchases;
function LibraryPage() {
return (
<Page>
<DownloadList />
{loading && !hasDownloads && (
<div className="main--empty">
<Spinner delayed />
</div>
)}
{!loading && !hasDownloads && (
<div className="main--empty">
<Yrbl
title={__("You haven't downloaded anything from LBRY yet")}
subtitle={
<div className="section__actions">
<Button button="primary" navigate="/" label={__('Explore new content')} />
</div>
}
/>
</div>
)}
{hasDownloads && <DownloadList />}
</Page>
);
}

View file

@ -22,7 +22,7 @@ function DiscoverPage() {
defaultTags={CS.TAGS_FOLLOWED}
meta={
<Button
button="link"
button="alt"
icon={ICONS.EDIT}
label={__('Manage')}
requiresAuth={IS_WEB}

View file

@ -45,7 +45,8 @@ export const makeSelectIsPlayerFloating = (location: UrlLocation) =>
// If there is no floatingPlayer explicitly set, see if the playingUri can float
try {
const { pathname } = location;
const pageUrl = buildURI(parseURI(pathname.slice(1).replace(/:/g, '#')));
const { streamName, streamClaimId, channelName, channelClaimId } = parseURI(pathname.slice(1).replace(/:/g, '#'));
const pageUrl = buildURI({ streamName, streamClaimId, channelName, channelClaimId });
const claimFromUrl = claimsByUri[pageUrl];
const playingClaim = claimsByUri[playingUri];
return (claimFromUrl && claimFromUrl.claim_id) !== (playingClaim && playingClaim.claim_id);

View file

@ -4,12 +4,15 @@
@media (max-width: $breakpoint-small) {
font-size: var(--font-small);
}
}
@media (min-width: $breakpoint-small) {
&:focus {
@include focus;
z-index: 2;
}
.button--primary,
.button--secondary,
.button--alt,
.button--link {
&:focus {
@include focus;
z-index: 2; // Ensure focus box-shadow is always visible on every button side
}
}
@ -48,6 +51,10 @@
background-color: var(--color-button-primary-bg);
}
&:focus {
@include focus;
}
@media (max-width: $breakpoint-small) {
padding: var(--spacing-medium) var(--spacing-small);
}
@ -124,6 +131,7 @@ svg + .button__label,
&:hover {
cursor: default;
text-decoration: none;
background-color: var(--color-primary-alt);
}
}

View file

@ -2,28 +2,30 @@
background-color: var(--color-card-background);
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
padding: var(--spacing-medium);
padding-bottom: var(--spacing-small);
margin-top: var(--spacing-medium);
color: var(--color-text-subtitle);
@media (max-width: $breakpoint-small) {
flex-direction: column;
align-items: flex-start;
}
color: var(--color-text-subtitle);
}
.claim-search__dropdown {
padding: 0 var(--spacing-medium);
max-width: 400px;
font-size: var(--font-body);
background-color: var(--color-card-background);
width: var(--option-select-width);
@media (max-width: $breakpoint-small) {
margin-left: 0;
}
background-color: var(--color-card-background);
width: var(--option-select-width);
}
.claim-search__dropdown--selected {
@ -51,13 +53,11 @@
.claim-search__extra {
display: flex;
flex-direction: row;
align-items: center;
}
.claim-search__top {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}

View file

@ -4,7 +4,7 @@
align-items: center;
font-size: var(--font-small);
color: var(--color-text-help);
margin-left: var(--spacing-small);
margin-left: var(--spacing-medium);
white-space: nowrap;
& > *:not(:last-child) {
@ -20,12 +20,68 @@
font-size: var(--font-xsmall);
line-height: 1.2;
margin-left: 0;
position: relative;
.tag {
font-size: var(--font-xsmall);
}
& > *:not(:last-child) {
margin-right: var(--spacing-miniscule);
}
}
.file-properties__purchased {
position: relative;
display: flex;
align-items: center;
margin-left: var(--spacing-xsmall);
color: var(--color-gray-5);
span,
svg {
position: relative;
fill: white;
}
.icon {
margin-left: 0.5rem;
}
&::before {
position: absolute;
content: '';
left: -0.4rem;
right: -5rem;
height: 1.75rem;
transform: skew(20deg);
background-color: var(--color-purchased);
}
}
.file-properties__not-purchased {
position: relative;
display: flex;
align-items: center;
color: var(--color-purchased-text);
span {
position: relative;
margin-left: 0.75rem;
}
&::before {
position: absolute;
content: '';
left: 0;
right: -5rem;
height: 1.75rem;
background-color: var(--color-purchased-alt);
border: 2px solid var(--color-purchased);
transform: skew(20deg);
}
}
.file-properties--large {
flex-wrap: wrap;
margin-bottom: var(--spacing-large);

View file

@ -5,6 +5,10 @@
input-submit {
align-items: center;
input {
z-index: 2;
}
}
input[type='number'] {

View file

@ -71,3 +71,9 @@
.pagination__item--selected {
background-color: var(--color-button-secondary-bg);
}
.paginate-channel {
width: 5rem;
border-top-left-radius: var(--border-radius);
border-bottom-left-radius: var(--border-radius);
}

View file

@ -42,7 +42,6 @@ p {
ul,
ol {
li {
list-style-position: outside;
margin: var(--spacing-xsmall) var(--spacing-medium);
@ -218,8 +217,10 @@ img {
color: var(--color-text-empty);
font-style: italic;
}
.empty--centered {
text-align: center;
padding: calc(var(--spacing-large) * 3) 0;
}
.qr-code {

View file

@ -43,6 +43,9 @@
--color-comment-menu-hovering: #e0e0e0;
--color-notice: #58563b;
--color-error: #61373f;
--color-purchased: #ffd580;
--color-purchased-alt: #ffd5804a;
--color-purchased-text: #eeeeee;
// Text
--color-text: #eeeeee;

View file

@ -15,6 +15,9 @@
--color-comment-menu: #e0e0e0;
--color-comment-menu-hovering: #6a6a6a;
--color-notice: #fef3ca;
--color-purchased: #ffd580;
--color-purchased-alt: #ffebc2;
--color-purchased-text: var(--color-gray-5);
// Icons
--color-follow-bg: #ffd4da;

View file

@ -6178,9 +6178,9 @@ lazy-val@^1.0.4:
yargs "^13.2.2"
zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#259317250af62391718ac0cb8b8e25f172dc4223:
lbry-redux@lbryio/lbry-redux#cd9c15567f2934ddc82de364d88b378ff04d5571:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/259317250af62391718ac0cb8b8e25f172dc4223"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/cd9c15567f2934ddc82de364d88b378ff04d5571"
dependencies:
proxy-polyfill "0.1.6"
reselect "^3.0.0"