respond to ux feedback
This commit is contained in:
parent
c6412eebef
commit
fa24060b3f
38 changed files with 228 additions and 200 deletions
|
@ -23,6 +23,7 @@
|
|||
"WEBPACK_PORT": true
|
||||
},
|
||||
"rules": {
|
||||
"brace-style": 0,
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"handle-callback-err": 0,
|
||||
"indent": 0,
|
||||
|
|
|
@ -35,7 +35,7 @@ function ChannelContent(props: Props) {
|
|||
|
||||
{!channelIsMine && <HiddenNsfwClaims className="card__content help" uri={uri} />}
|
||||
|
||||
{hasContent && <ClaimList header={false} uris={claimsInChannel.map(claim => claim.permanent_url).reverse()} />}
|
||||
{hasContent && <ClaimList header={false} uris={claimsInChannel.map(claim => claim.permanent_url)} />}
|
||||
|
||||
<Paginate
|
||||
onPageChange={page => fetchClaims(uri, page)}
|
||||
|
|
|
@ -19,7 +19,7 @@ type Props = {
|
|||
loading: boolean,
|
||||
type: string,
|
||||
empty?: string,
|
||||
meta?: Node,
|
||||
defaultSort?: boolean,
|
||||
onScrollBottom?: any => void,
|
||||
// If using the default header, this is a unique ID needed to persist the state of the filter setting
|
||||
persistedStorageKey?: string,
|
||||
|
@ -33,7 +33,7 @@ export default function ClaimList(props: Props) {
|
|||
loading,
|
||||
persistedStorageKey,
|
||||
empty,
|
||||
meta,
|
||||
defaultSort,
|
||||
type,
|
||||
header,
|
||||
onScrollBottom,
|
||||
|
@ -46,7 +46,21 @@ export default function ClaimList(props: Props) {
|
|||
setCurrentSort(currentSort === SORT_NEW ? SORT_OLD : SORT_NEW);
|
||||
}
|
||||
|
||||
const urisLength = uris && uris.length;
|
||||
useEffect(() => {
|
||||
function handleScroll(e) {
|
||||
if (onScrollBottom) {
|
||||
const x = document.querySelector(`.${MAIN_WRAPPER_CLASS}`);
|
||||
|
||||
if (x && window.scrollY + window.innerHeight >= x.offsetHeight) {
|
||||
// fix this
|
||||
if (!loading && urisLength > 19) {
|
||||
onScrollBottom();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onScrollBottom) {
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
|
@ -54,42 +68,35 @@ export default function ClaimList(props: Props) {
|
|||
window.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}
|
||||
}, [loading, handleScroll]);
|
||||
|
||||
function handleScroll(e) {
|
||||
if (onScrollBottom) {
|
||||
const x = document.querySelector(`.${MAIN_WRAPPER_CLASS}`);
|
||||
|
||||
if (x && window.scrollY + window.innerHeight >= x.offsetHeight) {
|
||||
// fix this
|
||||
if (!loading && uris.length > 19) {
|
||||
onScrollBottom();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [loading, onScrollBottom, urisLength]);
|
||||
|
||||
return (
|
||||
<section className={classnames('claim-list')}>
|
||||
<section
|
||||
className={classnames('claim-list', {
|
||||
'claim-list--small': type === 'small',
|
||||
})}
|
||||
>
|
||||
{header !== false && (
|
||||
<div className={classnames('claim-list__header', { 'claim-list__header--small': type === 'small' })}>
|
||||
{header || (
|
||||
<FormField
|
||||
className="claim-list__dropdown"
|
||||
type="select"
|
||||
name="file_sort"
|
||||
value={currentSort}
|
||||
onChange={handleSortChange}
|
||||
>
|
||||
<option value={SORT_NEW}>{__('Newest First')}</option>
|
||||
<option value={SORT_OLD}>{__('Oldest First')}</option>
|
||||
</FormField>
|
||||
)}
|
||||
{header}
|
||||
{loading && <Spinner light type="small" />}
|
||||
<div className="claim-list__alt-controls">{headerAltControls}</div>
|
||||
<div className="claim-list__alt-controls">
|
||||
{headerAltControls}
|
||||
{defaultSort && (
|
||||
<FormField
|
||||
className="claim-list__dropdown"
|
||||
type="select"
|
||||
name="file_sort"
|
||||
value={currentSort}
|
||||
onChange={handleSortChange}
|
||||
>
|
||||
<option value={SORT_NEW}>{__('Newest First')}</option>
|
||||
<option value={SORT_OLD}>{__('Oldest First')}</option>
|
||||
</FormField>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{meta && <div className="claim-list__meta">{meta}</div>}
|
||||
{hasUris && (
|
||||
<ul>
|
||||
{sortedUris.map((uri, index) => (
|
||||
|
|
|
@ -116,6 +116,7 @@ function ClaimListDiscover(props: Props) {
|
|||
|
||||
const headerAltControls = (
|
||||
<React.Fragment>
|
||||
{meta}
|
||||
{typeSort === 'top' && (
|
||||
<FormField
|
||||
className="claim-list__dropdown"
|
||||
|
@ -137,7 +138,6 @@ function ClaimListDiscover(props: Props) {
|
|||
return (
|
||||
<div className="card">
|
||||
<ClaimList
|
||||
meta={meta}
|
||||
loading={loading}
|
||||
uris={uris}
|
||||
injectedItem={personalSort === SEARCH_SORT_YOU && injectedItem}
|
||||
|
|
|
@ -68,7 +68,7 @@ export const icons = {
|
|||
<path d="M3, 10 C3, 10 3, 10.4453982 3, 10.9968336 L3, 20.0170446 C3, 20.5675806 3.43788135, 21.0138782 4.00292933, 21.0138781 L8.99707067, 21.0138779 C9.55097324, 21.0138779 10, 20.5751284 10, 20.0089602 L10, 15.0049177 C10, 14.449917 10.4433532, 14 11.0093689, 14 L12.9906311, 14 C13.5480902, 14 14, 14.4387495 14, 15.0049177 L14, 20.0089602 C14, 20.5639609 14.4378817, 21.0138779 15.0029302, 21.0138779 L19.9970758, 21.0138781 C20.5509789, 21.0138782 21.000006, 20.56848 21.000006, 20.0170446 L21.0000057, 10" />
|
||||
</g>
|
||||
),
|
||||
[ICONS.UPLOAD]: buildIcon(
|
||||
[ICONS.PUBLISH]: buildIcon(
|
||||
<g fill="none" fillRule="evenodd" strokeLinecap="round">
|
||||
<path
|
||||
d="M8, 18 L5, 18 L5, 18 C2.790861, 18 1, 16.209139 1, 14 C1, 11.790861 2.790861, 10 5, 10 C5.35840468, 10 5.70579988, 10.0471371 6.03632437, 10.1355501 C6.01233106, 9.92702603 6, 9.71495305 6, 9.5 C6, 6.46243388 8.46243388, 4 11.5, 4 C14.0673313, 4 16.2238156, 5.7590449 16.8299648, 8.1376465 C17.2052921, 8.04765874 17.5970804, 8 18, 8 C20.7614237, 8 23, 10.2385763 23, 13 C23, 15.7614237 20.7614237, 18 18, 18 L16, 18"
|
||||
|
@ -82,18 +82,12 @@ export const icons = {
|
|||
/>
|
||||
</g>
|
||||
),
|
||||
[ICONS.PUBLISHED]: buildIcon(
|
||||
<g fill="none" fillRule="evenodd" strokeLinecap="round">
|
||||
<path
|
||||
d="M8, 18 L5, 18 L5, 18 C2.790861, 18 1, 16.209139 1, 14 C1, 11.790861 2.790861, 10 5, 10 C5.35840468, 10 5.70579988, 10.0471371 6.03632437, 10.1355501 C6.01233106, 9.92702603 6, 9.71495305 6, 9.5 C6, 6.46243388 8.46243388, 4 11.5, 4 C14.0673313, 4 16.2238156, 5.7590449 16.8299648, 8.1376465 C17.2052921, 8.04765874 17.5970804, 8 18, 8 C20.7614237, 8 23, 10.2385763 23, 13 C23, 15.7614237 20.7614237, 18 18, 18 L16, 18, L8, 18"
|
||||
strokeLinejoin="round"
|
||||
transform="scale(1, 1.2) translate(0, -2)"
|
||||
/>
|
||||
</g>
|
||||
),
|
||||
[ICONS.SUBSCRIPTION]: buildIcon(
|
||||
[ICONS.SUBSCRIBE]: buildIcon(
|
||||
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
|
||||
),
|
||||
[ICONS.UNSUBSCRIBE]: buildIcon(
|
||||
<path d="M 12,5.67 10.94,4.61 C 5.7533356,-0.57666427 -2.0266644,7.2033357 3.16,12.39 l 1.06,1.06 7.78,7.78 7.78,-7.78 1.06,-1.06 c 2.149101,-2.148092 2.149101,-5.6319078 0,-7.78 -2.148092,-2.1491008 -5.631908,-2.1491008 -7.78,0 L 9.4481298,8.2303201 15.320603,9.2419066 11.772427,13.723825" />
|
||||
),
|
||||
[ICONS.SETTINGS]: buildIcon(
|
||||
<g>
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
|
@ -109,12 +103,8 @@ export const icons = {
|
|||
[ICONS.OVERVIEW]: buildIcon(<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />),
|
||||
[ICONS.WALLET]: buildIcon(
|
||||
<g>
|
||||
<line x1="8" y1="6" x2="21" y2="6" />
|
||||
<line x1="8" y1="12" x2="21" y2="12" />
|
||||
<line x1="8" y1="18" x2="21" y2="18" />
|
||||
<line x1="3" y1="6" x2="3" y2="6" />
|
||||
<line x1="3" y1="12" x2="3" y2="12" />
|
||||
<line x1="3" y1="18" x2="3" y2="18" />
|
||||
<rect x="1" y="4" width="22" height="16" rx="2" ry="2" />
|
||||
<line x1="1" y1="10" x2="23" y2="10" />
|
||||
</g>
|
||||
),
|
||||
[ICONS.LIBRARY]: buildIcon(<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />),
|
||||
|
@ -224,4 +214,30 @@ export const icons = {
|
|||
),
|
||||
[ICONS.UP]: buildIcon(<polyline points="18 15 12 9 6 15" />),
|
||||
[ICONS.DOWN]: buildIcon(<polyline points="6 9 12 15 18 9" />),
|
||||
[ICONS.FULLSCREEN]: buildIcon(
|
||||
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
|
||||
),
|
||||
[ICONS.FILE]: buildIcon(
|
||||
<g>
|
||||
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" />
|
||||
<polyline points="13 2 13 9 20 9" />
|
||||
</g>
|
||||
),
|
||||
[ICONS.CHANNEL]: buildIcon(
|
||||
<g>
|
||||
<circle cx="12" cy="12" r="4" />
|
||||
<path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94" />
|
||||
</g>
|
||||
),
|
||||
[ICONS.TWITTER]: buildIcon(
|
||||
<path d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z" />
|
||||
),
|
||||
[ICONS.FACEBOOK]: buildIcon(<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z" />),
|
||||
[ICONS.WEB]: buildIcon(
|
||||
<g>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<line x1="2" y1="12" x2="22" y2="12" />
|
||||
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
||||
</g>
|
||||
),
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ class IconComponent extends React.PureComponent<Props> {
|
|||
return __('Featured content. Earn rewards for watching.');
|
||||
case ICONS.DOWNLOAD:
|
||||
return __('This file is downloaded.');
|
||||
case ICONS.SUBSCRIPTION:
|
||||
case ICONS.SUBSCRIBE:
|
||||
return __('You are subscribed to this channel.');
|
||||
case ICONS.SETTINGS:
|
||||
return __('Your settings.');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import type { Node } from 'react';
|
||||
import type { ElementRef } from 'react';
|
||||
import * as MODALS from 'constants/modal_types';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import React from 'react';
|
||||
|
@ -17,7 +17,7 @@ type Props = {
|
|||
openModal: (id: string, { uri: string }) => void,
|
||||
claimIsMine: boolean,
|
||||
fileInfo: FileInfo,
|
||||
viewerContainer: ?{ current: Node },
|
||||
viewerContainer: { current: ElementRef<any> },
|
||||
showFullscreen: boolean,
|
||||
};
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ export default function FileProperties(props: Props) {
|
|||
|
||||
return (
|
||||
<div className="file-properties">
|
||||
{isSubscribed && <Icon tooltip icon={icons.SUBSCRIPTION} />}
|
||||
{isSubscribed && <Icon tooltip icon={icons.SUBSCRIBE} />}
|
||||
{!claimIsMine && downloaded && <Icon tooltip icon={icons.DOWNLOAD} />}
|
||||
{isRewardContent && <Icon tooltip icon={icons.FEATURED} />}
|
||||
<FilePrice hideFree uri={uri} />
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// @flow
|
||||
import type { ElementRef } from 'react';
|
||||
import '@babel/polyfill';
|
||||
import * as React from 'react';
|
||||
|
||||
|
@ -29,7 +30,7 @@ type Props = {
|
|||
onFinishCb: ?() => void,
|
||||
savePosition: number => void,
|
||||
changeVolume: number => void,
|
||||
viewerContainer: React.Ref,
|
||||
viewerContainer: { current: ElementRef<any> },
|
||||
searchBarFocused: boolean,
|
||||
};
|
||||
|
||||
|
@ -114,7 +115,9 @@ class MediaPlayer extends React.PureComponent<Props, State> {
|
|||
componentWillUnmount() {
|
||||
const mediaElement = this.mediaContainer.current.children[0];
|
||||
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
// Temorarily removing for comments the keydown handler needs to know
|
||||
// if a user is typing
|
||||
// document.removeEventListener('keydown', this.handleKeyDown);
|
||||
|
||||
if (mediaElement) {
|
||||
mediaElement.removeEventListener('click', this.togglePlay);
|
||||
|
@ -128,11 +131,11 @@ class MediaPlayer extends React.PureComponent<Props, State> {
|
|||
if (!searchBarFocused) {
|
||||
// Handle fullscreen shortcut key (f)
|
||||
if (event.keyCode === F_KEYCODE) {
|
||||
this.toggleFullscreen();
|
||||
// this.toggleFullscreen();
|
||||
}
|
||||
// Handle toggle play
|
||||
// @if TARGET='app'
|
||||
this.togglePlay(event);
|
||||
// this.togglePlay(event);
|
||||
// @endif
|
||||
}
|
||||
};
|
||||
|
@ -263,9 +266,6 @@ class MediaPlayer extends React.PureComponent<Props, State> {
|
|||
this.renderFile();
|
||||
}
|
||||
// @endif
|
||||
|
||||
// Fullscreen event for web and app
|
||||
document.addEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
|
||||
// @if TARGET='app'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// @flow
|
||||
import type { ElementRef } from 'react';
|
||||
import * as PAGES from 'constants/pages';
|
||||
import React, { Suspense } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
@ -52,7 +53,7 @@ type Props = {
|
|||
nsfw: boolean,
|
||||
thumbnail: ?string,
|
||||
isPlayableType: boolean,
|
||||
viewerContainer: React.Ref,
|
||||
viewerContainer: { current: ElementRef<any> },
|
||||
};
|
||||
|
||||
class FileViewer extends React.PureComponent<Props> {
|
||||
|
@ -126,7 +127,7 @@ class FileViewer extends React.PureComponent<Props> {
|
|||
}
|
||||
|
||||
this.props.cancelPlay();
|
||||
window.removeEventListener('keydown', this.handleKeyDown);
|
||||
// window.removeEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
|
||||
handleKeyDown(event: SyntheticKeyboardEvent<*>) {
|
||||
|
|
|
@ -91,11 +91,12 @@ const Header = (props: Props) => {
|
|||
{__('Wallet')}
|
||||
</MenuItem>
|
||||
<MenuItem className="menu__link" onSelect={() => history.push(`/$/publish`)}>
|
||||
<Icon aria-hidden icon={ICONS.UPLOAD} />
|
||||
<Icon aria-hidden icon={ICONS.PUBLISH} />
|
||||
{__('Publish')}
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
|
||||
<Menu>
|
||||
<MenuButton className="header__navigation-item menu__title">
|
||||
<Icon size={18} icon={ICONS.SETTINGS} />
|
||||
|
|
|
@ -16,7 +16,7 @@ import AuthPage from 'page/auth';
|
|||
import InvitePage from 'page/invite';
|
||||
import SubscriptionsPage from 'page/subscriptions';
|
||||
import SearchPage from 'page/search';
|
||||
import UserHistoryPage from 'page/userHistory';
|
||||
import LibraryPage from 'page/library';
|
||||
import WalletPage from 'page/wallet';
|
||||
import NavigationHistory from 'page/navigationHistory';
|
||||
import TagsPage from 'page/tags';
|
||||
|
@ -24,6 +24,7 @@ import FollowingPage from 'page/following';
|
|||
|
||||
const Scroll = withRouter(function ScrollWrapper(props) {
|
||||
const { pathname } = props.location;
|
||||
|
||||
useEffect(() => {
|
||||
// Auto scroll to the top of a window for new pages
|
||||
// The browser will handle scrolling if it needs to, but
|
||||
|
@ -51,12 +52,12 @@ export default function AppRouter() {
|
|||
<Route path={`/$/${PAGES.SEARCH}`} exact component={SearchPage} />
|
||||
<Route path={`/$/${PAGES.SETTINGS}`} exact component={SettingsPage} />
|
||||
<Route path={`/$/${PAGES.TRANSACTIONS}`} exact component={TransactionHistoryPage} />
|
||||
<Route path={`/$/${PAGES.LIBRARY}`} exact component={UserHistoryPage} />
|
||||
<Route path={`/$/${PAGES.LIBRARY}`} exact component={LibraryPage} />
|
||||
<Route path={`/$/${PAGES.ACCOUNT}`} exact component={AccountPage} />
|
||||
<Route path={`/$/${PAGES.LIBRARY}/all`} exact component={NavigationHistory} />
|
||||
<Route path={`/$/${PAGES.TAGS}`} exact component={TagsPage} />
|
||||
<Route path={`/$/${PAGES.FOLLOWING}`} exact component={SubscriptionsPage} />
|
||||
<Route path={`/$/${PAGES.FOLLOWING}/edit`} exact component={FollowingPage} />
|
||||
<Route path={`/$/${PAGES.FOLLOWING}/customize`} exact component={FollowingPage} />
|
||||
<Route path={`/$/${PAGES.WALLET}`} exact component={WalletPage} />
|
||||
{/* Below need to go at the end to make sure we don't match any of our pages first */}
|
||||
<Route path="/:claimName" exact component={ShowPage} />
|
||||
|
|
|
@ -26,7 +26,7 @@ const SearchOptions = (props: Props) => {
|
|||
<div>
|
||||
<Button
|
||||
button="alt"
|
||||
label={__('FILTER')}
|
||||
label={__('Filter')}
|
||||
iconRight={expanded ? ICONS.UP : ICONS.DOWN}
|
||||
onClick={toggleSearchExpanded}
|
||||
/>
|
||||
|
|
|
@ -34,23 +34,23 @@ function SideBar(props: Props) {
|
|||
...buildLink(null, __('Home'), ICONS.HOME),
|
||||
},
|
||||
{
|
||||
...buildLink(PAGES.FOLLOWING, __('Following'), ICONS.SUBSCRIPTION),
|
||||
...buildLink(PAGES.FOLLOWING, __('Following'), ICONS.SUBSCRIBE),
|
||||
},
|
||||
{
|
||||
...buildLink(PAGES.LIBRARY, __('Library'), ICONS.LIBRARY),
|
||||
},
|
||||
{
|
||||
...buildLink(PAGES.PUBLISHED, __('Publishes'), ICONS.PUBLISHED),
|
||||
...buildLink(PAGES.PUBLISHED, __('Publishes'), ICONS.PUBLISH),
|
||||
},
|
||||
].map(renderLink)}
|
||||
|
||||
<li>
|
||||
<Button
|
||||
navigate="/$/following/edit"
|
||||
navigate="/$/following/customize"
|
||||
icon={ICONS.EDIT}
|
||||
className="navigation__link"
|
||||
activeClass="navigation__link--active"
|
||||
label={__('Edit')}
|
||||
label={__('Customize')}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -3,10 +3,9 @@ import * as ICONS from 'constants/icons';
|
|||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
import CopyableText from 'component/copyableText';
|
||||
import Tooltip from 'component/common/tooltip';
|
||||
|
||||
type Props = {
|
||||
claim: StreamClaim,
|
||||
claim: Claim,
|
||||
onDone: () => void,
|
||||
speechShareable: boolean,
|
||||
isChannel: boolean,
|
||||
|
@ -27,21 +26,15 @@ class SocialShare extends React.PureComponent<Props> {
|
|||
|
||||
render() {
|
||||
const { claim, isChannel } = this.props;
|
||||
const { claim_id: claimId, name: claimName, channel_name: channelName } = claim;
|
||||
const { claim_id: claimId, name: claimName } = claim;
|
||||
|
||||
const { speechShareable, onDone } = this.props;
|
||||
const channelClaimId = claim.signing_channel && claim.signing_channel.claim_id;
|
||||
const signingChannel = claim.signing_channel;
|
||||
const channelClaimId = signingChannel && signingChannel.claim_id;
|
||||
const channelName = signingChannel && signingChannel.name;
|
||||
|
||||
const getSpeechUri = (): string => {
|
||||
if (isChannel) {
|
||||
// For channel claims, the channel name (@something) is in `claim.name`
|
||||
return `${claimName}:${claimId}`;
|
||||
} else {
|
||||
// If it's for a regular claim, check if it has an associated channel
|
||||
return channelName && channelClaimId
|
||||
? `${channelName}:${channelClaimId}/${claimName}`
|
||||
: `${claimId}/${claimName}`;
|
||||
}
|
||||
const getLbryTvUri = (): string => {
|
||||
return `${claimName}/${claimId}`;
|
||||
};
|
||||
|
||||
const getLbryUri = (): string => {
|
||||
|
@ -56,69 +49,57 @@ class SocialShare extends React.PureComponent<Props> {
|
|||
}
|
||||
};
|
||||
|
||||
const speechPrefix = 'https://spee.ch/';
|
||||
const lbryTvPrefix = 'https://beta.lbry.tv/';
|
||||
const lbryPrefix = 'https://open.lbry.com/';
|
||||
const lbryUri = getLbryUri();
|
||||
const speechUri = getSpeechUri();
|
||||
const lbryTvUri = getLbryTvUri();
|
||||
const encodedLbryURL: string = `${lbryPrefix}${encodeURIComponent(lbryUri)}`;
|
||||
const lbryURL: string = `${lbryPrefix}${getLbryUri()}`;
|
||||
const encodedLbryTvUrl = `${lbryTvPrefix}${encodeURIComponent(lbryTvUri)}`;
|
||||
const lbryTvUrl = `${lbryTvPrefix}${lbryTvUri}`;
|
||||
|
||||
const encodedSpeechURL = `${speechPrefix}${encodeURIComponent(speechUri)}`;
|
||||
const speechURL = `${speechPrefix}${speechUri}`;
|
||||
const shareOnFb = __('Share on Facebook');
|
||||
const shareOnTwitter = __('Share On Twitter');
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{speechShareable && (
|
||||
<div className="card__content">
|
||||
<label className="help">{__('Web link')}</label>
|
||||
<CopyableText copyable={speechURL} />
|
||||
<label className="card__subtitle">{__('Web link')}</label>
|
||||
<CopyableText copyable={lbryTvUrl} />
|
||||
<div className="card__actions card__actions--center">
|
||||
<Tooltip label={__('Facebook')}>
|
||||
<Button
|
||||
iconColor="blue"
|
||||
icon={ICONS.FACEBOOK}
|
||||
button="alt"
|
||||
label={__('')}
|
||||
href={`https://facebook.com/sharer/sharer.php?u=${encodedSpeechURL}`}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label={__('Twitter')}>
|
||||
<Button
|
||||
iconColor="blue"
|
||||
icon={ICONS.TWITTER}
|
||||
button="alt"
|
||||
label={__('')}
|
||||
href={`https://twitter.com/home?status=${encodedSpeechURL}`}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label={__('View on Spee.ch')}>
|
||||
<Button icon={ICONS.WEB} iconColor="blue" button="alt" label={__('')} href={`${speechURL}`} />
|
||||
</Tooltip>
|
||||
<Button
|
||||
icon={ICONS.FACEBOOK}
|
||||
button="link"
|
||||
description={shareOnFb}
|
||||
href={`https://facebook.com/sharer/sharer.php?u=${encodedLbryTvUrl}`}
|
||||
/>
|
||||
<Button
|
||||
icon={ICONS.TWITTER}
|
||||
button="link"
|
||||
description={shareOnTwitter}
|
||||
href={`https://twitter.com/home?status=${encodedLbryTvUrl}`}
|
||||
/>
|
||||
<Button icon={ICONS.WEB} button="link" description={__('View on lbry.tv')} href={`${lbryTvUrl}`} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="card__content">
|
||||
<label className="help">{__('LBRY App link')}</label>
|
||||
<label className="card__subtitle">{__('LBRY App link')}</label>
|
||||
<CopyableText copyable={lbryURL} noSnackbar />
|
||||
<div className="card__actions card__actions--center">
|
||||
<Tooltip label={__('Facebook')}>
|
||||
<Button
|
||||
iconColor="blue"
|
||||
icon={ICONS.FACEBOOK}
|
||||
button="alt"
|
||||
label={__('')}
|
||||
href={`https://facebook.com/sharer/sharer.php?u=${encodedLbryURL}`}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label={__('Twitter')}>
|
||||
<Button
|
||||
iconColor="blue"
|
||||
icon={ICONS.TWITTER}
|
||||
button="alt"
|
||||
label={__('')}
|
||||
href={`https://twitter.com/home?status=${encodedLbryURL}`}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Button
|
||||
icon={ICONS.FACEBOOK}
|
||||
button="link"
|
||||
description={shareOnFb}
|
||||
href={`https://facebook.com/sharer/sharer.php?u=${encodedLbryURL}`}
|
||||
/>
|
||||
<Button
|
||||
icon={ICONS.TWITTER}
|
||||
button="link"
|
||||
description={shareOnTwitter}
|
||||
href={`https://twitter.com/home?status=${encodedLbryURL}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card__actions">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import * as MODALS from 'constants/modal_types';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import React from 'react';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { parseURI } from 'lbry-redux';
|
||||
import Button from 'component/button';
|
||||
|
||||
|
@ -32,18 +32,36 @@ export default function SubscribeButton(props: Props) {
|
|||
showSnackBarOnSubscribe,
|
||||
doToast,
|
||||
} = props;
|
||||
|
||||
const buttonRef = useRef();
|
||||
const [isHovering, setIsHovering] = useState(false);
|
||||
const { claimName } = parseURI(uri);
|
||||
const subscriptionHandler = isSubscribed ? doChannelUnsubscribe : doChannelSubscribe;
|
||||
const subscriptionLabel = isSubscribed ? __('Following') : __('Follow');
|
||||
const unfollowOverride = isSubscribed && isHovering && __('Unfollow');
|
||||
|
||||
const { claimName } = parseURI(uri);
|
||||
useEffect(() => {
|
||||
function handleHover() {
|
||||
setIsHovering(!isHovering);
|
||||
}
|
||||
|
||||
const button = buttonRef.current;
|
||||
if (button) {
|
||||
button.addEventListener('mouseover', handleHover);
|
||||
button.addEventListener('mouseleave', handleHover);
|
||||
return () => {
|
||||
button.removeEventListener('mouseover', handleHover);
|
||||
button.removeEventListener('mouseleave', handleHover);
|
||||
};
|
||||
}
|
||||
}, [buttonRef, isHovering]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
ref={buttonRef}
|
||||
iconColor="red"
|
||||
icon={ICONS.SUBSCRIPTION}
|
||||
icon={unfollowOverride ? ICONS.UNSUBSCRIBE : ICONS.SUBSCRIBE}
|
||||
button={'alt'}
|
||||
label={subscriptionLabel}
|
||||
label={unfollowOverride || subscriptionLabel}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
|
||||
|
|
|
@ -10,8 +10,7 @@ export const COPY = 'Clipboard';
|
|||
export const ARROW_LEFT = 'ChevronLeft';
|
||||
export const ARROW_RIGHT = 'ChevronRight';
|
||||
export const DOWNLOAD = 'Download';
|
||||
export const UPLOAD = 'UploadCloud';
|
||||
export const PUBLISHED = 'Cloud';
|
||||
export const PUBLISH = 'UploadCloud';
|
||||
export const REMOVE = 'X';
|
||||
export const ADD = 'Plus';
|
||||
export const EDIT = 'Edit';
|
||||
|
@ -29,7 +28,7 @@ export const WALLET = 'List';
|
|||
export const PHONE = 'Phone';
|
||||
export const COMPLETE = 'Check';
|
||||
export const COMPLETED = 'CheckCircle';
|
||||
export const SUBSCRIPTION = 'Heart';
|
||||
export const SUBSCRIBE = 'Heart';
|
||||
export const UNSUBSCRIBE = 'BrokenHeart';
|
||||
export const UNLOCK = 'Unlock';
|
||||
export const WEB = 'Globe';
|
||||
|
|
|
@ -77,7 +77,7 @@ function ChannelPage(props: Props) {
|
|||
</div>
|
||||
</TabList>
|
||||
|
||||
<TabPanels className="channel__data">
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<ChannelContent uri={uri} />
|
||||
</TabPanel>
|
||||
|
|
|
@ -10,6 +10,7 @@ type Props = {
|
|||
|
||||
function DiscoverPage(props: Props) {
|
||||
const { followedTags } = props;
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<ClaimListDiscover
|
||||
|
|
|
@ -18,7 +18,13 @@ function FileListDownloaded(props: Props) {
|
|||
<React.Fragment>
|
||||
{hasDownloads ? (
|
||||
<div className="card">
|
||||
<ClaimList persistedStorageKey="claim-list-downloaded" uris={downloadedUris} loading={fetching} />
|
||||
<ClaimList
|
||||
header={<h1>{__('Your Library')}</h1>}
|
||||
defaultSort
|
||||
persistedStorageKey="claim-list-downloaded"
|
||||
uris={downloadedUris}
|
||||
loading={fetching}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="main--empty">
|
||||
|
|
|
@ -21,7 +21,14 @@ function FileListPublished(props: Props) {
|
|||
<Page notContained>
|
||||
{uris && uris.length ? (
|
||||
<div className="card">
|
||||
<ClaimList loading={fetching} persistedStorageKey="claim-list-published" uris={uris} />
|
||||
<ClaimList
|
||||
header={<h1>{__('Your Publishes')}</h1>}
|
||||
loading={fetching}
|
||||
persistedStorageKey="claim-list-published"
|
||||
uris={uris}
|
||||
defaultSort
|
||||
headerAltControls={<Button button="link" label={__('New Publish')} navigate="/$/publish" />}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="main--empty">
|
||||
|
|
|
@ -14,10 +14,10 @@ function FollowingEditPage(props: Props) {
|
|||
return (
|
||||
<Page>
|
||||
<div className="card">
|
||||
<TagsSelect showClose={false} title={__('Find New Tags To Follow')} />
|
||||
<TagsSelect showClose={false} title={__('Customize Your Tags')} />
|
||||
</div>
|
||||
<div className="card">
|
||||
<ClaimList uris={channelUris} />
|
||||
<ClaimList header={<h1>{__('Channels You Follow')}</h1>} uris={channelUris} />
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
|
|
3
src/ui/page/library/index.js
Normal file
3
src/ui/page/library/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import LibraryPage from './view';
|
||||
|
||||
export default LibraryPage;
|
14
src/ui/page/library/view.jsx
Normal file
14
src/ui/page/library/view.jsx
Normal file
|
@ -0,0 +1,14 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Page from 'component/page';
|
||||
import DownloadList from 'page/fileListDownloaded';
|
||||
|
||||
function LibraryPage() {
|
||||
return (
|
||||
<Page>
|
||||
<DownloadList />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export default LibraryPage;
|
|
@ -3,6 +3,7 @@ import * as PAGES from 'constants/pages';
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import Page from 'component/page';
|
||||
import ClaimList from 'component/claimList';
|
||||
import ClaimPreview from 'component/claimPreview';
|
||||
import Button from 'component/button';
|
||||
|
||||
type Props = {
|
||||
|
@ -79,6 +80,7 @@ export default function SubscriptionsPage(props: Props) {
|
|||
uris={viewingSuggestedSubs ? suggestedSubscriptions.map(sub => sub.uri) : uris}
|
||||
onScrollBottom={() => console.log('scroll bottom') || setPage(page + 1)}
|
||||
/>
|
||||
{loading && page > 1 && new Array(20).fill(1).map((x, i) => <ClaimPreview key={i} placeholder />)}
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
|
|
|
@ -32,9 +32,9 @@ function TagsPage(props: Props) {
|
|||
tags={tags}
|
||||
meta={
|
||||
<Button
|
||||
button="alt"
|
||||
button="link"
|
||||
onClick={() => doToggleTagFollow(tag)}
|
||||
label={isFollowing ? __('Unfollow this tag') : __('Follow this tag')}
|
||||
label={isFollowing ? __('Following') : __('Follow')}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
import UserHistoryPage from './view';
|
||||
|
||||
export default UserHistoryPage;
|
|
@ -1,17 +0,0 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Page from 'component/page';
|
||||
import DownloadList from 'page/fileListDownloaded';
|
||||
|
||||
type Props = {};
|
||||
|
||||
class UserHistoryPage extends React.PureComponent<Props> {
|
||||
render() {
|
||||
return (
|
||||
<Page>
|
||||
<DownloadList {...this.props} />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default UserHistoryPage;
|
|
@ -257,6 +257,7 @@ export const doPublish = () => (dispatch: Dispatch, getState: () => {}) => {
|
|||
tags: Array<string>,
|
||||
locations?: Array<Location>,
|
||||
license_url?: string,
|
||||
license?: string,
|
||||
thumbnail_url?: string,
|
||||
release_time?: number,
|
||||
fee_currency?: string,
|
||||
|
@ -267,13 +268,19 @@ export const doPublish = () => (dispatch: Dispatch, getState: () => {}) => {
|
|||
description,
|
||||
locations,
|
||||
bid: creditsToString(bid),
|
||||
license: publishingLicense,
|
||||
languages: [language],
|
||||
tags: tags && tags.map(tag => tag.name),
|
||||
license_url: licenseType === COPYRIGHT ? '' : licenseUrl,
|
||||
thumbnail_url: thumbnail,
|
||||
};
|
||||
|
||||
if (publishingLicense) {
|
||||
publishPayload.license = publishingLicense;
|
||||
}
|
||||
|
||||
if (licenseUrl) {
|
||||
publishPayload.license_url = licenseUrl;
|
||||
}
|
||||
|
||||
if (myClaimForUri && myClaimForUri.value.release_time) {
|
||||
publishPayload.release_time = Number(myClaimForUri.value.release_time);
|
||||
}
|
||||
|
|
|
@ -93,8 +93,3 @@ $metadata-z-index: 1;
|
|||
margin-top: -0.25rem;
|
||||
color: rgba($lbry-white, 0.75);
|
||||
}
|
||||
|
||||
// TODO: rename
|
||||
.channel__data {
|
||||
min-height: 10rem;
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
font-size: 1.4em;
|
||||
font-size: 1.1em;
|
||||
|
||||
& > * {
|
||||
margin-left: var(--spacing-small);
|
||||
|
@ -96,7 +96,7 @@
|
|||
}
|
||||
|
||||
.claim-preview--injected,
|
||||
.claim-preview + .claim-preview {
|
||||
.claim-preview {
|
||||
border-bottom: 1px solid rgba($lbry-teal-5, 0.1);
|
||||
}
|
||||
|
||||
|
@ -158,8 +158,3 @@
|
|||
.claim-preview-tags {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.claim-list__meta {
|
||||
padding: var(--spacing-medium);
|
||||
background-color: lighten($lbry-teal-5, 55%);
|
||||
}
|
||||
|
|
|
@ -8,12 +8,17 @@
|
|||
box-shadow: var(--card-box-shadow) $lbry-gray-1;
|
||||
padding-left: var(--spacing-large);
|
||||
padding-right: var(--spacing-large);
|
||||
|
||||
html[data-mode='dark'] & {
|
||||
background-color: mix($lbry-black, $lbry-gray-3, 90%);
|
||||
color: $lbry-white;
|
||||
border-bottom: none;
|
||||
box-shadow: var(--card-box-shadow) $lbry-black;
|
||||
}
|
||||
|
||||
& > * {
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.header__contents {
|
||||
|
@ -49,10 +54,6 @@
|
|||
align-items: center;
|
||||
border-radius: 0;
|
||||
|
||||
svg {
|
||||
stroke: $lbry-gray-5;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $lbry-teal-5;
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
padding-left: var(--spacing-large);
|
||||
padding-right: var(--spacing-large);
|
||||
padding-bottom: var(--spacing-large);
|
||||
background-color: mix($lbry-white, $lbry-gray-1, 70%);
|
||||
display: flex;
|
||||
|
||||
[data-mode='dark'] & {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
.navigation {
|
||||
width: var(--side-nav-width);
|
||||
padding-bottom: var(--spacing-main-padding);
|
||||
font-size: 1.4rem;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
.search__options {
|
||||
margin-top: var(--spacing-large);
|
||||
|
||||
.button {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
legend {
|
||||
&.search__legend--1 {
|
||||
background-color: $lbry-teal-4;
|
||||
|
|
|
@ -57,10 +57,6 @@ $main: $lbry-teal-5;
|
|||
&:active {
|
||||
background-color: $main;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include focus;
|
||||
}
|
||||
}
|
||||
|
||||
.tag--remove {
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
// Generic html styles used accross the App
|
||||
// component specific styling should go in the component scss file
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
-webkit-user-select: text;
|
||||
}
|
||||
|
||||
html {
|
||||
@include font-sans;
|
||||
background-color: $lbry-white;
|
||||
|
||||
font-size: 12px;
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
|
@ -23,6 +17,7 @@ body {
|
|||
height: 100%;
|
||||
line-height: 1.5;
|
||||
overflow: hidden;
|
||||
background-color: mix($lbry-white, $lbry-gray-1, 70%);
|
||||
|
||||
html[data-mode='dark'] & {
|
||||
background-color: $lbry-black;
|
||||
|
|
|
@ -20,10 +20,7 @@ html {
|
|||
user-select: none;
|
||||
}
|
||||
|
||||
a,
|
||||
area,
|
||||
button,
|
||||
[role='button'],
|
||||
input,
|
||||
label,
|
||||
select,
|
||||
|
|
Loading…
Reference in a new issue