diff --git a/src/component/fileResultItem/index.js b/src/component/claimResultItem/index.js similarity index 95% rename from src/component/fileResultItem/index.js rename to src/component/claimResultItem/index.js index f1bff41..4a44701 100644 --- a/src/component/fileResultItem/index.js +++ b/src/component/claimResultItem/index.js @@ -12,7 +12,7 @@ import { } from 'lbry-redux'; import { selectBlackListedOutpoints, selectFilteredOutpoints, selectRewardContentClaimIds } from 'lbryinc'; import { selectShowNsfw } from 'redux/selectors/settings'; -import FileResultItem from './view'; +import ClaimResultItem from './view'; const select = (state, props) => ({ blackListedOutpoints: selectBlackListedOutpoints(state), @@ -37,4 +37,4 @@ const perform = dispatch => ({ export default connect( select, perform, -)(FileResultItem); +)(ClaimResultItem); diff --git a/src/component/fileResultItem/view.js b/src/component/claimResultItem/view.js similarity index 54% rename from src/component/fileResultItem/view.js rename to src/component/claimResultItem/view.js index 0c0b10e..1f55801 100644 --- a/src/component/fileResultItem/view.js +++ b/src/component/claimResultItem/view.js @@ -1,10 +1,13 @@ import React from 'react'; import { normalizeURI, parseURI } from 'lbry-redux'; import { ActivityIndicator, Platform, Text, TouchableOpacity, View } from 'react-native'; -import { navigateToUri, formatBytes } from 'utils/helper'; +import { navigateToUri, formatTitle, getDownloadProgress, getStorageForFileInfo } from 'utils/helper'; import Colors from 'styles/colors'; +import ChannelIconItem from 'component/channelIconItem'; +import channelIconStyle from 'styles/channelIcon'; import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import DateTime from 'component/dateTime'; +import FastImage from 'react-native-fast-image'; import FileItemMedia from 'component/fileItemMedia'; import FilePrice from 'component/filePrice'; import Icon from 'react-native-vector-icons/FontAwesome5'; @@ -12,29 +15,28 @@ import Link from 'component/link'; import NsfwOverlay from 'component/nsfwOverlay'; import ProgressBar from 'component/progressBar'; import fileListStyle from 'styles/fileList'; +import seedrandom from 'seedrandom'; -class FileResultItem extends React.PureComponent { - getStorageForFileInfo = fileInfo => { - if (!fileInfo.completed) { - const written = formatBytes(fileInfo.written_bytes); - const total = formatBytes(fileInfo.total_bytes); - return `(${written} / ${total})`; +class ClaimResultItem extends React.PureComponent { + state = { + autoStyle: null, + }; + + componentDidMount() { + const { result } = this.props; + + if (!result || !result.name || !result.claimId) { + this.setState({ + autoStyle: + ChannelIconItem.AUTO_THUMB_STYLES[Math.floor(Math.random() * ChannelIconItem.AUTO_THUMB_STYLES.length)], + }); + } else { + // result property set, use deterministic random style + const rng = seedrandom(normalizeURI(`${result.name}#${result.claimId}`)); + const index = Math.floor(rng.quick() * ChannelIconItem.AUTO_THUMB_STYLES.length); + this.setState({ autoStyle: ChannelIconItem.AUTO_THUMB_STYLES[index] }); } - - return formatBytes(fileInfo.written_bytes); - }; - - formatTitle = title => { - if (!title) { - return title; - } - - return title.length > 80 ? title.substring(0, 77).trim() + '...' : title; - }; - - getDownloadProgress = fileInfo => { - return Math.ceil((fileInfo.written_bytes / fileInfo.total_bytes) * 100); - }; + } onPressHandler = () => { const { autoplay, navigation, result } = this.props; @@ -58,6 +60,8 @@ class FileResultItem extends React.PureComponent { title, } = result; + const isChannel = name && name.startsWith('@'); + const hasThumbnail = !!thumbnailUrl; const obscure = obscureNsfw && nsfw; const url = normalizeURI(`${name}#${claimId}`); const hasChannel = !!channel; @@ -66,14 +70,39 @@ class FileResultItem extends React.PureComponent { return ( - - + + {!isChannel && ( + + )} + + {isChannel && ( + + + {hasThumbnail && ( + + )} + {!hasThumbnail && ( + + {title ? title.substring(0, 1).toUpperCase() : name.substring(1, 2).toUpperCase()} + + )} + + + )} + {fileInfo && fileInfo.completed && fileInfo.download_path && ( - {this.formatTitle(title) || this.formatTitle(name)} + {formatTitle(title) || formatTitle(name)} {isRewardContent && } )} - {hasChannel && ( - { - navigateToUri(navigation, normalizeURI(channelUrl), null, false, channelUrl); - }} - /> - )} + {hasChannel || + (isChannel && ( + { + navigateToUri( + navigation, + normalizeURI(isChannel ? url : channelUrl), + null, + false, + isChannel ? url : channelUrl, + ); + }} + /> + ))} {fileInfo && !isNaN(fileInfo.written_bytes) && fileInfo.written_bytes > 0 && ( - {this.getStorageForFileInfo(fileInfo)} + {getStorageForFileInfo(fileInfo)} )} )} @@ -143,4 +179,4 @@ class FileResultItem extends React.PureComponent { } } -export default FileResultItem; +export default ClaimResultItem; diff --git a/src/component/fileListItem/view.js b/src/component/fileListItem/view.js index 8d8ffdc..97398d3 100644 --- a/src/component/fileListItem/view.js +++ b/src/component/fileListItem/view.js @@ -1,10 +1,13 @@ import React from 'react'; import { normalizeURI, parseURI } from 'lbry-redux'; import { ActivityIndicator, Platform, Text, TouchableOpacity, View } from 'react-native'; -import { navigateToUri, formatBytes } from 'utils/helper'; +import { navigateToUri, formatTitle, getDownloadProgress, getStorageForFileInfo } from 'utils/helper'; import Colors from 'styles/colors'; +import ChannelIconItem from 'component/channelIconItem'; +import channelIconStyle from 'styles/channelIcon'; import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import DateTime from 'component/dateTime'; +import FastImage from 'react-native-fast-image'; import FileItemMedia from 'component/fileItemMedia'; import FilePrice from 'component/filePrice'; import Icon from 'react-native-vector-icons/FontAwesome5'; @@ -15,27 +18,10 @@ import fileListStyle from 'styles/fileList'; class FileListItem extends React.PureComponent { state = { + autoStyle: null, url: null, }; - getStorageForFileInfo = fileInfo => { - if (!fileInfo.completed) { - const written = formatBytes(fileInfo.written_bytes); - const total = formatBytes(fileInfo.total_bytes); - return `(${written} / ${total})`; - } - - return formatBytes(fileInfo.written_bytes); - }; - - formatTitle = title => { - if (!title) { - return title; - } - - return title.length > 80 ? title.substring(0, 77).trim() + '...' : title; - }; - getDownloadProgress = fileInfo => { return Math.ceil((fileInfo.written_bytes / fileInfo.total_bytes) * 100); }; @@ -45,6 +31,11 @@ class FileListItem extends React.PureComponent { if (!claim && !batchResolve) { resolveUri(uri); } + + this.setState({ + autoStyle: + ChannelIconItem.AUTO_THUMB_STYLES[Math.floor(Math.random() * ChannelIconItem.AUTO_THUMB_STYLES.length)], + }); } componentDidUpdate() { @@ -135,10 +126,13 @@ class FileListItem extends React.PureComponent { return null; } + const isChannel = name && name.startsWith('@'); + const hasThumbnail = !!thumbnail; + return ( { if (onLongPress) { @@ -146,13 +140,35 @@ class FileListItem extends React.PureComponent { } }} > - + {!isChannel && ( + + )} + + {isChannel && ( + + + {hasThumbnail && ( + + )} + {!hasThumbnail && ( + + {title ? title.substring(0, 1).toUpperCase() : claim.name.substring(1, 2).toUpperCase()} + + )} + + + )} + {selected && ( @@ -194,7 +210,7 @@ class FileListItem extends React.PureComponent { {(title || name) && ( - {this.formatTitle(title) || this.formatTitle(name)} + {formatTitle(title) || formatTitle(name)} {isRewardContent && } @@ -206,17 +222,17 @@ class FileListItem extends React.PureComponent { )} - {channel && !hideChannel && ( + {(channel || isChannel) && !hideChannel && ( { navigateToUri( navigation, - normalizeURI(shortChannelUri || fullChannelUri), + normalizeURI(isChannel ? uri : shortChannelUri || fullChannelUri), null, false, - fullChannelUri, + isChannel ? claim && claim.permanent_url : fullChannelUri, ); }} /> @@ -224,7 +240,7 @@ class FileListItem extends React.PureComponent { {fileInfo && !isNaN(fileInfo.written_bytes) && fileInfo.written_bytes > 0 && ( - {this.getStorageForFileInfo(fileInfo)} + {getStorageForFileInfo(fileInfo)} )} @@ -237,7 +253,7 @@ class FileListItem extends React.PureComponent { color={Colors.NextLbryGreen} height={3} style={fileListStyle.progress} - progress={this.getDownloadProgress(fileInfo)} + progress={getDownloadProgress(fileInfo)} /> )} diff --git a/src/component/relatedContent/view.js b/src/component/relatedContent/view.js index e0830f4..f5ab361 100644 --- a/src/component/relatedContent/view.js +++ b/src/component/relatedContent/view.js @@ -4,7 +4,7 @@ import { normalizeURI } from 'lbry-redux'; import { navigateToUri } from 'utils/helper'; import Colors from 'styles/colors'; import FileListItem from 'component/fileListItem'; -import FileResultItem from 'component/fileResultItem'; +import ClaimResultItem from 'component/claimResultItem'; import fileListStyle from 'styles/fileList'; import relatedContentStyle from 'styles/relatedContent'; @@ -30,7 +30,7 @@ export default class RelatedContent extends React.PureComponent { {isSearching && } {recommendedContent && recommendedContent.map(result => ( - @@ -38,12 +50,15 @@ class SuggestedSubscriptionItem extends React.PureComponent { return ( - - + + {hasThumbnail && ( + + )} + {!hasThumbnail && ( + + {title ? title.substring(0, 1).toUpperCase() : claim ? claim.name.substring(1, 2).toUpperCase() : ''} + + )} diff --git a/src/page/channelCreator/view.js b/src/page/channelCreator/view.js index 8a86ad8..55baef9 100644 --- a/src/page/channelCreator/view.js +++ b/src/page/channelCreator/view.js @@ -21,6 +21,7 @@ import ChannelRewardsDriver from 'component/channelRewardsDriver'; import Colors from 'styles/colors'; import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import EmptyStateView from 'component/emptyStateView'; +import FastImage from 'react-native-fast-image'; import FloatingWalletBalance from 'component/floatingWalletBalance'; import Icon from 'react-native-vector-icons/FontAwesome5'; import Link from 'component/link'; @@ -867,9 +868,9 @@ export default class ChannelCreator extends React.PureComponent { > {itemThumbnailUrl && ( - )} diff --git a/src/page/search/view.js b/src/page/search/view.js index 5747637..22dc55b 100644 --- a/src/page/search/view.js +++ b/src/page/search/view.js @@ -15,7 +15,7 @@ import Colors from 'styles/colors'; import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import PageHeader from 'component/pageHeader'; import FileListItem from 'component/fileListItem'; -import FileResultItem from 'component/fileResultItem'; +import ClaimResultItem from 'component/claimResultItem'; import FloatingWalletBalance from 'component/floatingWalletBalance'; import UriBar from 'component/uriBar'; import searchStyle from 'styles/search'; @@ -213,7 +213,12 @@ class SearchPage extends React.PureComponent { ListEmptyComponent={!isSearching ? this.listEmptyComponent() : null} ListHeaderComponent={this.listHeaderComponent(this.state.showTagResult, this.state.currentQuery)} renderItem={({ item }) => ( - + )} /> )} diff --git a/src/page/subscriptions/view.js b/src/page/subscriptions/view.js index e48487e..83b9027 100644 --- a/src/page/subscriptions/view.js +++ b/src/page/subscriptions/view.js @@ -73,7 +73,7 @@ class SubscriptionsPage extends React.PureComponent { }; componentDidMount() { - // this.onComponentFocused(); + this.onComponentFocused(); } componentWillReceiveProps(nextProps) { diff --git a/src/styles/fileList.js b/src/styles/fileList.js index fe5811b..d3d5f4f 100644 --- a/src/styles/fileList.js +++ b/src/styles/fileList.js @@ -38,6 +38,18 @@ const fileListStyle = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, + channelThumbnailContainer: { + width: thumbnailHeight, // maintain same width and height + height: thumbnailHeight, + borderRadius: 140, + overflow: 'hidden', + alignItems: 'center', + justifyContent: 'center', + }, + channelThumbnail: { + width: '100%', + height: '100%', + }, selectedOverlay: { position: 'absolute', left: 0, @@ -63,6 +75,9 @@ const fileListStyle = StyleSheet.create({ marginTop: screenWidthPixels <= 720 ? 1 : 3, color: Colors.LbryGreen, }, + channelContainer: { + alignItems: 'center', + }, loading: { position: 'absolute', }, diff --git a/src/styles/subscriptions.js b/src/styles/subscriptions.js index bb1ff78..20e90d8 100644 --- a/src/styles/subscriptions.js +++ b/src/styles/subscriptions.js @@ -199,6 +199,8 @@ const subscriptionsStyle = StyleSheet.create({ height: 70, borderRadius: 140, overflow: 'hidden', + alignItems: 'center', + justifyContent: 'center', }, suggestedItemThumbnail: { width: '100%', diff --git a/src/utils/helper.js b/src/utils/helper.js index bf3e938..4f57b62 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -370,3 +370,25 @@ export function uploadImageAsset(filePath, success, failure) { export function formatLbryUrlForWeb(url) { return url.replace('lbry://', '/').replace(/#/g, ':'); } + +export function getDownloadProgress(fileInfo) { + return Math.ceil((fileInfo.written_bytes / fileInfo.total_bytes) * 100); +} + +export function getStorageForFileInfo(fileInfo) { + if (!fileInfo.completed) { + const written = formatBytes(fileInfo.written_bytes); + const total = formatBytes(fileInfo.total_bytes); + return `(${written} / ${total})`; + } + + return formatBytes(fileInfo.written_bytes); +} + +export function formatTitle(title) { + if (!title) { + return title; + } + + return title.length > 80 ? title.substring(0, 77).trim() + '...' : title; +}