From 3dcdf913cf4475c408cc978ad172e93954290b16 Mon Sep 17 00:00:00 2001 From: btzr-io Date: Wed, 5 Jun 2019 00:07:53 -0600 Subject: [PATCH] create channel tooltip --- package.json | 1 + src/ui/component/button/view.jsx | 12 +- src/ui/component/channelLink/index.js | 39 ++++++ src/ui/component/channelLink/view.jsx | 163 +++++++++++++++++++++++++ src/ui/component/externalLink/view.jsx | 3 +- src/ui/scss/component/_channel.scss | 63 ++++++++++ yarn.lock | 5 + 7 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 src/ui/component/channelLink/index.js create mode 100644 src/ui/component/channelLink/view.jsx diff --git a/package.json b/package.json index ff0048352..fcc31a9a5 100644 --- a/package.json +++ b/package.json @@ -147,6 +147,7 @@ "react-hot-loader": "^4.7.2", "react-modal": "^3.1.7", "react-paginate": "^5.2.1", + "react-portal-tooltip": "^2.4.7", "react-pose": "^4.0.5", "react-redux": "^6.0.1", "react-router": "^5.0.0", diff --git a/src/ui/component/button/view.jsx b/src/ui/component/button/view.jsx index 2f616d603..d1c8c89a0 100644 --- a/src/ui/component/button/view.jsx +++ b/src/ui/component/button/view.jsx @@ -7,7 +7,6 @@ import { formatLbryUriForWeb } from 'util/uri'; import { OutboundLink } from 'react-ga'; type Props = { - onClick: ?(any) => any, href: ?string, title: ?string, label: ?string, @@ -24,6 +23,11 @@ type Props = { iconSize?: number, constrict: ?boolean, // to shorten the button and ellipsis, only use for links activeClass?: string, + innerRef: ?any, + // Events + onClick: ?(any) => any, + onMouseEnter: ?(any) => any, + onMouseLeave: ?(any) => any, }; class Button extends React.PureComponent { @@ -34,6 +38,9 @@ class Button extends React.PureComponent { render() { const { onClick, + onMouseEnter, + onMouseLeave, + innerRef, href, title, label, @@ -111,8 +118,11 @@ class Button extends React.PureComponent { onClick(); } }} + onMouseEnter={onMouseEnter} + onMouseLeave={onMouseLeave} className={combinedClassName} activeClassName={activeClass} + innerRef={innerRef} > {content} diff --git a/src/ui/component/channelLink/index.js b/src/ui/component/channelLink/index.js new file mode 100644 index 000000000..c99d49f96 --- /dev/null +++ b/src/ui/component/channelLink/index.js @@ -0,0 +1,39 @@ +import { connect } from 'react-redux'; + +import { + doResolveUri, + makeSelectClaimIsMine, + makeSelectTitleForUri, + makeSelectThumbnailForUri, + makeSelectCoverForUri, + makeSelectClaimForUri, + makeSelectIsUriResolving, + makeSelectMetadataItemForUri, +} from 'lbry-redux'; + +import { selectBlackListedOutpoints } from 'lbryinc'; + +import ChannelLink from './view'; + +const select = (state, props) => { + return { + uri: props.uri, + claim: makeSelectClaimForUri(props.uri)(state), + title: makeSelectTitleForUri(props.uri)(state), + cover: makeSelectCoverForUri(props.uri)(state), + thumbnail: makeSelectThumbnailForUri(props.uri)(state), + description: makeSelectMetadataItemForUri(props.uri, 'description')(state), + channelIsMine: makeSelectClaimIsMine(props.uri)(state), + isResolvingUri: makeSelectIsUriResolving(props.uri)(state), + blackListedOutpoints: selectBlackListedOutpoints(state), + }; +}; + +const perform = dispatch => ({ + resolveUri: uri => dispatch(doResolveUri(uri)), +}); + +export default connect( + select, + perform +)(ChannelLink); diff --git a/src/ui/component/channelLink/view.jsx b/src/ui/component/channelLink/view.jsx new file mode 100644 index 000000000..abef07ab3 --- /dev/null +++ b/src/ui/component/channelLink/view.jsx @@ -0,0 +1,163 @@ +// @flow +import * as React from 'react'; +import { parseURI } from 'lbry-redux'; +import ToolTip from 'react-portal-tooltip'; +import Button from 'component/button'; + +type Props = { + uri: string, + title: ?string, + cover: ?string, + claim: StreamClaim, + children: React.Node, + thumbnail: ?string, + description: ?string, + isResolvingUri: boolean, + resolveUri: string => void, + blackListedOutpoints: Array<{ + txid: string, + nout: number, + }>, +}; + +type TooltipProps = { + uri: string, + style: Object, + title: ?string, + active: ?boolean, + parent: ?HTMLElement, + claimId: ?string, + thumbnail: ?string, + claimName: ?string, + channelName: ?string, + description: ?string, +}; + +type State = { + isTooltipActive: boolean, +}; + +const ChannelTooltip = (props: TooltipProps) => { + const { style, title, active, parent, claimId, thumbnail, claimName, channelName, description } = props; + + return ( + +
+
+ +
+

{title || channelName}

+

+ {claimName} + {claimId && `#${claimId}`} +

+
+
+
+

{description}

+
+
+
+ + ); +}; + +class ChannelLink extends React.Component { + buttonRef: { current: ?any }; + + static defaultProps = { + href: null, + title: null, + }; + + constructor(props: Props) { + super(props); + this.state = { isTooltipActive: false }; + (this: any).buttonRef = React.createRef(); + } + + showTooltip = () => { + this.setState({ isTooltipActive: true }); + }; + + hideTooltip = () => { + this.setState({ isTooltipActive: false }); + }; + + isClaimBlackListed() { + const { claim, blackListedOutpoints } = this.props; + + if (claim && blackListedOutpoints) { + let blackListed = false; + + for (let i = 0; i < blackListedOutpoints.length; i += 1) { + const outpoint = blackListedOutpoints[i]; + if (outpoint.txid === claim.txid && outpoint.nout === claim.nout) { + blackListed = true; + break; + } + } + return blackListed; + } + } + + componentDidMount() { + const { isResolvingUri, resolveUri, uri } = this.props; + if (!isResolvingUri) { + resolveUri(uri); + } + } + + componentDidUpdate() { + const { isResolvingUri, resolveUri, claim, uri } = this.props; + if (!isResolvingUri && uri && claim === undefined) { + resolveUri(uri); + } + } + + render() { + const { uri, claim, title, description, thumbnail, children, isResolvingUri } = this.props; + const { channelName, claimName, claimId } = parseURI(uri); + const blackListed = this.isClaimBlackListed(); + const isReady = !blackListed && !isResolvingUri && claim !== null; + const tooltipReady = isReady && this.buttonRef.current !== null; + + const bgColor = '#32373b'; + + const tooltipStyle = { + style: { background: bgColor }, + arrowStyle: { color: bgColor }, + }; + + return isReady ? ( + +