2017-06-06 23:19:12 +02:00
import React from "react" ;
2017-07-27 02:55:00 +02:00
import ReactDOM from "react-dom" ;
2017-06-06 23:19:12 +02:00
import lbryuri from "lbryuri" ;
import FileCard from "component/fileCard" ;
2017-07-27 02:55:00 +02:00
import { Icon , BusyMessage } from "component/common.js" ;
2017-06-06 23:19:12 +02:00
import ToolTip from "component/tooltip.js" ;
2017-12-08 21:14:35 +01:00
import SubHeader from "component/subHeader" ;
import classnames from "classnames" ;
import Link from "component/link" ;
// This should be in a separate file
export class FeaturedCategory extends React . PureComponent {
constructor ( ) {
super ( ) ;
this . state = {
numItems : undefined ,
canScrollPrevious : false ,
canScrollNext : false ,
} ;
}
2016-04-10 02:00:56 +02:00
2017-07-27 02:55:00 +02:00
componentWillMount ( ) {
this . setState ( {
numItems : this . props . names . length ,
} ) ;
}
2017-12-08 21:14:35 +01:00
componentDidMount ( ) {
const cardRow = ReactDOM . findDOMNode ( this . refs . rowitems ) ;
const cards = cardRow . getElementsByTagName ( "section" ) ;
// check if the last card is visible
const lastCard = cards [ cards . length - 1 ] ;
const isCompletelyVisible = this . isCardVisible ( lastCard , cardRow , false ) ;
if ( ! isCompletelyVisible ) {
this . setState ( {
canScrollNext : true ,
} ) ;
}
}
2017-07-27 02:55:00 +02:00
handleScrollPrevious ( ) {
const cardRow = ReactDOM . findDOMNode ( this . refs . rowitems ) ;
if ( cardRow . scrollLeft > 0 ) {
// check the visible cards
const cards = cardRow . getElementsByTagName ( "section" ) ;
let firstVisibleCard = null ;
let firstVisibleIdx = - 1 ;
for ( var i = 0 ; i < cards . length ; i ++ ) {
if ( this . isCardVisible ( cards [ i ] , cardRow , false ) ) {
firstVisibleCard = cards [ i ] ;
firstVisibleIdx = i ;
break ;
}
}
const numDisplayed = this . numDisplayedCards ( cardRow ) ;
const scrollToIdx = firstVisibleIdx - numDisplayed ;
const animationCallback = ( ) => {
this . setState ( {
canScrollPrevious : cardRow . scrollLeft !== 0 ,
canScrollNext : true ,
} ) ;
} ;
this . scrollCardItemsLeftAnimated (
cardRow ,
scrollToIdx < 0 ? 0 : cards [ scrollToIdx ] . offsetLeft ,
100 ,
animationCallback
) ;
}
}
handleScrollNext ( ) {
const cardRow = ReactDOM . findDOMNode ( this . refs . rowitems ) ;
// check the visible cards
const cards = cardRow . getElementsByTagName ( "section" ) ;
let lastVisibleCard = null ;
let lastVisibleIdx = - 1 ;
for ( var i = 0 ; i < cards . length ; i ++ ) {
if ( this . isCardVisible ( cards [ i ] , cardRow , true ) ) {
lastVisibleCard = cards [ i ] ;
lastVisibleIdx = i ;
}
}
if ( lastVisibleCard ) {
const numDisplayed = this . numDisplayedCards ( cardRow ) ;
const animationCallback = ( ) => {
// update last visible index after scroll
for ( var i = 0 ; i < cards . length ; i ++ ) {
if ( this . isCardVisible ( cards [ i ] , cardRow , true ) ) {
lastVisibleIdx = i ;
}
}
this . setState ( { canScrollPrevious : true } ) ;
if ( lastVisibleIdx === cards . length - 1 ) {
this . setState ( { canScrollNext : false } ) ;
}
} ;
this . scrollCardItemsLeftAnimated (
cardRow ,
Math . min (
lastVisibleCard . offsetLeft ,
cardRow . scrollWidth - cardRow . clientWidth
) ,
100 ,
animationCallback
) ;
}
}
scrollCardItemsLeftAnimated ( cardRow , target , duration , callback ) {
if ( ! duration || duration <= diff ) {
cardRow . scrollLeft = target ;
if ( callback ) {
callback ( ) ;
}
return ;
}
const component = this ;
const diff = target - cardRow . scrollLeft ;
const tick = diff / duration * 10 ;
setTimeout ( ( ) => {
cardRow . scrollLeft = cardRow . scrollLeft + tick ;
if ( cardRow . scrollLeft === target ) {
if ( callback ) {
callback ( ) ;
}
return ;
}
component . scrollCardItemsLeftAnimated (
cardRow ,
target ,
duration - 10 ,
callback
) ;
} , 10 ) ;
}
isCardVisible ( section , cardRow , partialVisibility ) {
// check if a card is fully or partialy visible in its parent
const cardRowWidth = cardRow . offsetWidth ;
const cardRowLeft = cardRow . scrollLeft ;
const cardRowEnd = cardRowLeft + cardRow . offsetWidth ;
const sectionLeft = section . offsetLeft - cardRowLeft ;
const sectionEnd = sectionLeft + section . offsetWidth ;
return (
( sectionLeft >= 0 && sectionEnd <= cardRowWidth ) ||
( ( ( sectionLeft < 0 && sectionEnd > 0 ) ||
( sectionLeft > 0 && sectionLeft <= cardRowWidth ) ) &&
partialVisibility )
) ;
}
numDisplayedCards ( cardRow ) {
const cards = cardRow . getElementsByTagName ( "section" ) ;
const cardRowWidth = cardRow . offsetWidth ;
// get the width of the first card and then calculate
const cardWidth = cards . length > 0 ? cards [ 0 ] . offsetWidth : 0 ;
if ( cardWidth > 0 ) {
return Math . ceil ( cardRowWidth / cardWidth ) ;
}
// return a default value of 1 card displayed if the card width couldn't be determined
return 1 ;
}
render ( ) {
2017-12-08 21:14:35 +01:00
const { category , names , categoryLink } = this . props ;
2017-07-27 02:55:00 +02:00
return (
< div className = "card-row card-row--small" >
< h3 className = "card-row__header" >
2017-12-08 21:14:35 +01:00
{ categoryLink ? (
< Link
2017-12-10 19:32:05 +01:00
className = "button-text no-underline"
2017-12-08 21:14:35 +01:00
label = { category }
2017-12-09 20:07:17 +01:00
navigate = "/show"
navigateParams = { { uri : categoryLink } }
2017-12-08 21:14:35 +01:00
/ >
) : (
category
) }
2017-07-27 02:55:00 +02:00
{ category &&
2017-11-24 15:31:05 +01:00
category . match ( /^community/i ) && (
< ToolTip
label = { _ _ ( "What's this?" ) }
body = { _ _ (
'Community Content is a public space where anyone can share content with the rest of the LBRY community. Bid on the names "one," "two," "three," "four" and "five" to put your content here!'
) }
className = "tooltip--header"
/ >
) }
2017-07-27 02:55:00 +02:00
< / h3 >
2017-07-29 20:12:13 +02:00
< div className = "card-row__scrollhouse" >
2017-11-24 15:31:05 +01:00
{ this . state . canScrollPrevious && (
2017-07-29 20:12:13 +02:00
< div className = "card-row__nav card-row__nav--left" >
< a
className = "card-row__scroll-button"
onClick = { this . handleScrollPrevious . bind ( this ) }
>
< Icon icon = "icon-chevron-left" / >
< / a >
2017-11-24 15:31:05 +01:00
< / div >
) }
{ this . state . canScrollNext && (
2017-07-29 20:12:13 +02:00
< div className = "card-row__nav card-row__nav--right" >
< a
className = "card-row__scroll-button"
onClick = { this . handleScrollNext . bind ( this ) }
>
< Icon icon = "icon-chevron-right" / >
< / a >
2017-11-24 15:31:05 +01:00
< / div >
) }
2017-07-29 20:12:13 +02:00
< div ref = "rowitems" className = "card-row__items" >
{ names &&
2017-11-24 15:31:05 +01:00
names . map ( name => (
2017-07-29 20:12:13 +02:00
< FileCard
key = { name }
displayStyle = "card"
uri = { lbryuri . normalize ( name ) }
/ >
2017-11-24 15:31:05 +01:00
) ) }
2017-07-29 20:12:13 +02:00
< / div >
2017-07-27 02:55:00 +02:00
< / div >
< / div >
) ;
}
}
2016-05-16 15:20:32 +02:00
2017-06-08 06:42:19 +02:00
class DiscoverPage extends React . PureComponent {
2017-05-11 02:59:47 +02:00
componentWillMount ( ) {
2017-06-06 23:19:12 +02:00
this . props . fetchFeaturedUris ( ) ;
2017-05-07 09:39:13 +02:00
}
2017-05-11 02:59:47 +02:00
render ( ) {
2017-06-06 23:19:12 +02:00
const { featuredUris , fetchingFeaturedUris } = this . props ;
2017-07-15 21:24:57 +02:00
const hasContent =
2017-11-24 15:31:05 +01:00
typeof featuredUris === "object" && Object . keys ( featuredUris ) . length ,
2017-07-15 21:24:57 +02:00
failedToLoad = ! fetchingFeaturedUris && ! hasContent ;
2017-05-11 02:59:47 +02:00
return (
2017-12-08 21:14:35 +01:00
< main
className = { classnames ( "main main--no-margin" , {
reloading : hasContent && fetchingFeaturedUris ,
} ) }
>
< SubHeader fullWidth smallMargin / >
2017-07-15 21:15:17 +02:00
{ ! hasContent &&
2017-11-24 15:31:05 +01:00
fetchingFeaturedUris && (
< BusyMessage message = { _ _ ( "Fetching content" ) } / >
) }
2017-07-15 21:15:17 +02:00
{ hasContent &&
2017-12-08 21:14:35 +01:00
Object . keys ( featuredUris ) . map ( category => {
return featuredUris [ category ] . length ? (
< FeaturedCategory
key = { category }
category = { category }
names = { featuredUris [ category ] }
/ >
) : (
""
) ;
} ) }
2017-11-24 15:31:05 +01:00
{ failedToLoad && (
< div className = "empty" > { _ _ ( "Failed to load landing content." ) } < / div >
) }
2017-05-22 17:22:24 +02:00
< / main >
2017-06-06 23:19:12 +02:00
) ;
2017-05-11 02:59:47 +02:00
}
2017-05-04 05:44:08 +02:00
}
2017-05-25 18:29:56 +02:00
export default DiscoverPage ;