Performance (#9)
* performance improvements * fix time picker for top content on tag page * redux-persist v5. style tweaks.
This commit is contained in:
parent
22d860a885
commit
18d654cc66
34 changed files with 1725 additions and 439 deletions
1132
package-lock.json
generated
1132
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,7 @@
|
|||
"private": "true",
|
||||
"scripts": {
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
"devtools": "react-devtools",
|
||||
"format": "prettier 'src/**/*.{js,json}' --write",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
|
@ -11,7 +12,7 @@
|
|||
"base-64": "^0.1.0",
|
||||
"@expo/vector-icons": "^8.1.0",
|
||||
"gfycat-style-urls": "^1.0.3",
|
||||
"lbry-redux": "lbryio/lbry-redux#multi-claim-search",
|
||||
"lbry-redux": "lbryio/lbry-redux#67a654f60630710cae72419448a73a18d076d18a",
|
||||
"lbryinc": "lbryio/lbryinc",
|
||||
"lodash": ">=4.17.11",
|
||||
"merge": ">=1.2.1",
|
||||
|
@ -63,6 +64,7 @@
|
|||
"husky": "^0.14.3",
|
||||
"lint-staged": "^7.0.4",
|
||||
"metro-react-native-babel-preset": "^0.55.0",
|
||||
"prettier": "^1.11.1"
|
||||
"prettier": "^1.11.1",
|
||||
"react-devtools": "^3.6.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import claimListStyle from 'styles/claimList';
|
|||
import discoverStyle from 'styles/discover';
|
||||
import moment from 'moment';
|
||||
|
||||
const horizontalLimit = 10;
|
||||
const horizontalLimit = 7;
|
||||
const softLimit = 500;
|
||||
|
||||
class ClaimList extends React.PureComponent {
|
||||
|
@ -199,8 +199,8 @@ class ClaimList extends React.PureComponent {
|
|||
ListHeaderComponent={ListHeaderComponent}
|
||||
style={claimListStyle.verticalScrollContainer}
|
||||
contentContainerStyle={claimListStyle.verticalScrollPadding}
|
||||
initialNumToRender={8}
|
||||
maxToRenderPerBatch={24}
|
||||
initialNumToRender={10}
|
||||
maxToRenderPerBatch={20}
|
||||
removeClippedSubviews
|
||||
renderItem={({ item }) => (
|
||||
<FileListItem key={item} uri={item} style={claimListStyle.verticalListItem} navigation={navigation} />
|
||||
|
@ -208,7 +208,7 @@ class ClaimList extends React.PureComponent {
|
|||
data={data}
|
||||
keyExtractor={(item, index) => item}
|
||||
onEndReached={this.handleVerticalEndReached}
|
||||
onEndReachedThreshold={0.9}
|
||||
onEndReachedThreshold={0.2}
|
||||
/>
|
||||
{(((subscriptionsView || trendingForAllView) && claimSearchLoading) || loading) && (
|
||||
<View style={claimListStyle.verticalLoading}>
|
||||
|
|
|
@ -57,7 +57,7 @@ class CustomRewardCard extends React.PureComponent<Props> {
|
|||
return (
|
||||
<View style={[rewardStyle.rewardCard, rewardStyle.row]}>
|
||||
<View style={rewardStyle.leftCol}>
|
||||
{rewardIsPending && <ActivityIndicator size="small" color={Colors.LbryGreen} />}
|
||||
{rewardIsPending && <ActivityIndicator size="small" color={Colors.NextLbryGreen} />}
|
||||
</View>
|
||||
<View style={rewardStyle.midCol}>
|
||||
<Text style={rewardStyle.rewardTitle}>Custom Code</Text>
|
||||
|
|
|
@ -8,12 +8,15 @@ import {
|
|||
makeSelectTitleForUri,
|
||||
makeSelectThumbnailForUri,
|
||||
} from 'lbry-redux';
|
||||
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||
import { selectShowNsfw } from 'redux/selectors/settings';
|
||||
import FileListItem from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
blackListedOutpoints: selectBlackListedOutpoints(state),
|
||||
claim: makeSelectClaimForUri(props.uri)(state),
|
||||
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||
filteredOutpoints: selectFilteredOutpoints(state),
|
||||
isDownloaded: !!makeSelectFileInfoForUri(props.uri)(state),
|
||||
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
|
||||
|
|
|
@ -48,8 +48,10 @@ class FileListItem extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const {
|
||||
blackListedOutpoints,
|
||||
claim,
|
||||
fileInfo,
|
||||
filteredOutpoints,
|
||||
metadata,
|
||||
featuredResult,
|
||||
isResolvingUri,
|
||||
|
@ -66,7 +68,7 @@ class FileListItem extends React.PureComponent {
|
|||
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||
const isResolving = !fileInfo && isResolvingUri;
|
||||
|
||||
let name, channel, height, channelClaimId, fullChannelUri, signingChannel;
|
||||
let name, channel, height, channelClaimId, fullChannelUri, shouldHide, signingChannel;
|
||||
if (claim) {
|
||||
name = claim.name;
|
||||
signingChannel = claim.signing_channel;
|
||||
|
@ -74,9 +76,14 @@ class FileListItem extends React.PureComponent {
|
|||
height = claim.height;
|
||||
channelClaimId = signingChannel ? signingChannel.claim_id : null;
|
||||
fullChannelUri = channelClaimId ? `${channel}#${channelClaimId}` : channel;
|
||||
|
||||
if (blackListedOutpoints || filteredOutpoints) {
|
||||
const outpointsToHide = blackListedOutpoints.concat(filteredOutpoints);
|
||||
shouldHide = outpointsToHide.some(outpoint => outpoint.txid === claim.txid && outpoint.nout === claim.nout);
|
||||
}
|
||||
}
|
||||
|
||||
if (featuredResult && !isResolvingUri && !claim && !title && !name) {
|
||||
if (shouldHide || (featuredResult && !isResolvingUri && !claim && !title && !name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -468,7 +468,7 @@ class MediaPlayer extends React.PureComponent {
|
|||
|
||||
{this.state.buffering && (
|
||||
<View style={mediaPlayerStyle.loadingContainer}>
|
||||
<ActivityIndicator color={Colors.LbryGreen} size="large" />
|
||||
<ActivityIndicator color={Colors.NextLbryGreen} size="large" />
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
makeSelectClaimForUri,
|
||||
doSearch,
|
||||
makeSelectRecommendedContentForUri,
|
||||
makeSelectTitleForUri,
|
||||
selectIsSearching,
|
||||
} from 'lbry-redux';
|
||||
import { doNativeSearch } from 'redux/actions/performance';
|
||||
import RelatedContent from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
|
@ -16,7 +16,7 @@ const select = (state, props) => ({
|
|||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
search: query => dispatch(doSearch(query, 20, undefined, true)),
|
||||
search: query => dispatch(doNativeSearch(query, 20, undefined, true)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
|
|
@ -6,7 +6,7 @@ import FileListItem from 'component/fileListItem';
|
|||
import fileListStyle from 'styles/fileList';
|
||||
import relatedContentStyle from 'styles/relatedContent';
|
||||
|
||||
export default class RelatedContent extends React.PureComponent<Props> {
|
||||
export default class RelatedContent extends React.PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
|
@ -17,7 +17,7 @@ export default class RelatedContent extends React.PureComponent<Props> {
|
|||
this.getRecommendedContent();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
componentDidUpdate(prevProps) {
|
||||
const { claim, uri } = this.props;
|
||||
|
||||
if (uri !== prevProps.uri) {
|
||||
|
@ -38,7 +38,7 @@ export default class RelatedContent extends React.PureComponent<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
didSearch: ?boolean;
|
||||
didSearch;
|
||||
|
||||
render() {
|
||||
const { recommendedContent, isSearching, navigation } = this.props;
|
||||
|
@ -60,7 +60,9 @@ export default class RelatedContent extends React.PureComponent<Props> {
|
|||
onPress={() => navigateToUri(navigation, recommendedUri, { autoplay: true })}
|
||||
/>
|
||||
))}
|
||||
{isSearching && <ActivityIndicator size="small" color={Colors.LbryGreen} style={relatedContentStyle.loading} />}
|
||||
{isSearching && (
|
||||
<ActivityIndicator size="small" color={Colors.NextLbryGreen} style={relatedContentStyle.loading} />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ class RewardCard extends React.PureComponent<Props> {
|
|||
)}
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{isPending && <ActivityIndicator size="small" color={Colors.LbryGreen} />}
|
||||
{isPending && <ActivityIndicator size="small" color={Colors.NextLbryGreen} />}
|
||||
</View>
|
||||
<View style={rewardStyle.midCol}>
|
||||
<Text style={rewardStyle.rewardTitle}>{reward.reward_title}</Text>
|
||||
|
|
|
@ -19,7 +19,7 @@ class SubscribeButton extends React.PureComponent {
|
|||
|
||||
const iconColor = isSubscribed ? null : Colors.Red;
|
||||
const subscriptionHandler = isSubscribed ? doChannelUnsubscribe : doChannelSubscribe;
|
||||
const subscriptionLabel = isSubscribed ? null : __('Subscribe');
|
||||
const subscriptionLabel = isSubscribed ? null : __('Follow');
|
||||
const { claimName } = parseURI(uri);
|
||||
|
||||
return (
|
||||
|
@ -28,7 +28,7 @@ class SubscribeButton extends React.PureComponent {
|
|||
theme={'light'}
|
||||
icon={isSubscribed ? 'heart-broken' : 'heart'}
|
||||
iconColor={iconColor}
|
||||
solid={isSubscribed ? false : true}
|
||||
solid={!isSubscribed}
|
||||
text={hideText ? null : subscriptionLabel}
|
||||
onPress={() => {
|
||||
subscriptionHandler({
|
||||
|
|
|
@ -42,24 +42,22 @@ class SuggestedSubscriptionItem extends React.PureComponent {
|
|||
</View>
|
||||
|
||||
<View style={subscriptionsStyle.suggestedItemDetails}>
|
||||
<View style={subscriptionsStyle.suggestedItemInfo}>
|
||||
{title && (
|
||||
<Text style={subscriptionsStyle.suggestedItemTitle} numberOfLines={1}>
|
||||
{title}
|
||||
</Text>
|
||||
)}
|
||||
<Text style={subscriptionsStyle.suggestedItemName} numberOfLines={1}>
|
||||
{claim && claim.name}
|
||||
{title && (
|
||||
<Text style={subscriptionsStyle.suggestedItemTitle} numberOfLines={1}>
|
||||
{title}
|
||||
</Text>
|
||||
{tags && (
|
||||
<View style={subscriptionsStyle.suggestedItemTagList}>
|
||||
{tags &&
|
||||
tags
|
||||
.slice(0, 3)
|
||||
.map(tag => <Tag style={subscriptionsStyle.tag} key={tag} name={tag} navigation={navigation} />)}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
<Text style={subscriptionsStyle.suggestedItemName} numberOfLines={1}>
|
||||
{claim && claim.name}
|
||||
</Text>
|
||||
{tags && (
|
||||
<View style={subscriptionsStyle.suggestedItemTagList}>
|
||||
{tags &&
|
||||
tags
|
||||
.slice(0, 3)
|
||||
.map(tag => <Tag style={subscriptionsStyle.tag} key={tag} name={tag} navigation={navigation} />)}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<SubscribeButton style={subscriptionsStyle.suggestedItemSubscribe} uri={normalizeURI(uri)} />
|
||||
|
|
|
@ -43,7 +43,7 @@ class SuggestedSubscriptions extends React.PureComponent {
|
|||
if (loading) {
|
||||
return (
|
||||
<View style={subscriptionsStyle.centered}>
|
||||
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
||||
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
import React from 'react';
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
import { formatTagName } from 'utils/helper';
|
||||
import tagStyle from 'styles/tag';
|
||||
import Colors from 'styles/colors';
|
||||
import Constants from 'constants';
|
||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
|
||||
export default class Tag extends React.PureComponent {
|
||||
onPressDefault = () => {
|
||||
const { name, navigation, type, onAddPress, onRemovePress } = this.props;
|
||||
if ('add' === type) {
|
||||
if (type === 'add') {
|
||||
if (onAddPress) {
|
||||
onAddPress(name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ('remove' === type) {
|
||||
if (type === 'remove') {
|
||||
if (onRemovePress) {
|
||||
onRemovePress(name);
|
||||
}
|
||||
|
@ -48,7 +49,7 @@ export default class Tag extends React.PureComponent {
|
|||
return (
|
||||
<TouchableOpacity style={styles} onPress={onPress || this.onPressDefault}>
|
||||
<View style={tagStyle.content}>
|
||||
<Text style={tagStyle.text}>{name}</Text>
|
||||
<Text style={tagStyle.text}>{formatTagName(name)}</Text>
|
||||
{type && <Icon style={tagStyle.icon} name={type === 'add' ? 'plus' : 'times'} size={8} />}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
|
|
@ -103,7 +103,7 @@ const Constants = {
|
|||
TIME_ALL,
|
||||
|
||||
CLAIM_SEARCH_SORT_BY_ITEMS: [
|
||||
{ icon: 'fire-alt', name: SORT_BY_HOT, label: 'Hot content' },
|
||||
{ icon: 'fire-alt', name: SORT_BY_HOT, label: 'Trending content' },
|
||||
{ icon: 'certificate', name: SORT_BY_NEW, label: 'New content' },
|
||||
{ icon: 'chart-line', name: SORT_BY_TOP, label: 'Top content' },
|
||||
],
|
||||
|
|
26
src/index.js
26
src/index.js
|
@ -18,6 +18,7 @@ import {
|
|||
authReducer,
|
||||
blacklistReducer,
|
||||
costInfoReducer,
|
||||
filteredReducer,
|
||||
homepageReducer,
|
||||
rewardsReducer,
|
||||
subscriptionsReducer,
|
||||
|
@ -32,7 +33,6 @@ import AppWithNavigationState, {
|
|||
} from 'component/AppNavigator';
|
||||
import { REHYDRATE, PURGE, persistCombineReducers, persistStore } from 'redux-persist';
|
||||
import getStoredStateMigrateV4 from 'redux-persist/lib/integration/getStoredStateMigrateV4';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import FilesystemStorage from 'redux-persist-filesystem-storage';
|
||||
import createCompressor from 'redux-persist-transform-compress';
|
||||
import createFilter from 'redux-persist-transform-filter';
|
||||
|
@ -43,7 +43,6 @@ import thunk from 'redux-thunk';
|
|||
|
||||
const globalExceptionHandler = (error, isFatal) => {
|
||||
if (error && NativeModules.Firebase) {
|
||||
console.log(error);
|
||||
NativeModules.Firebase.logException(isFatal, error.message ? error.message : 'No message', JSON.stringify(error));
|
||||
}
|
||||
};
|
||||
|
@ -80,7 +79,7 @@ function enableBatching(reducer) {
|
|||
const compressor = createCompressor();
|
||||
const authFilter = createFilter('auth', ['authToken']);
|
||||
const contentFilter = createFilter('content', ['positions']);
|
||||
const saveClaimsFilter = createFilter('claims', ['byId', 'claimsByUri']);
|
||||
const saveClaimsFilter = createFilter('claims', ['claimsByUri']);
|
||||
const subscriptionsFilter = createFilter('subscriptions', ['enabledChannelNotifications', 'subscriptions']);
|
||||
const settingsFilter = createFilter('settings', ['clientSettings']);
|
||||
const tagsFilter = createFilter('tags', ['followedTags']);
|
||||
|
@ -109,6 +108,7 @@ const reducers = persistCombineReducers(persistOptions, {
|
|||
drawer: drawerReducer,
|
||||
file: fileReducer,
|
||||
fileInfo: fileInfoReducer,
|
||||
filtered: filteredReducer,
|
||||
homepage: homepageReducer,
|
||||
nav: navigatorReducer,
|
||||
notifications: notificationsReducer,
|
||||
|
@ -136,11 +136,29 @@ const store = createStore(
|
|||
);
|
||||
window.store = store;
|
||||
|
||||
persistStore(store, persistOptions, err => {
|
||||
const persistor = persistStore(store, persistOptions, err => {
|
||||
if (err) {
|
||||
console.log('Unable to load saved SETTINGS');
|
||||
}
|
||||
});
|
||||
window.persistor = persistor;
|
||||
|
||||
/*
|
||||
const persistFilter = {
|
||||
'auth': ['authToken'],
|
||||
'claims': ['byId', 'claimsByUri'],
|
||||
'content': ['positions'],
|
||||
'subscriptions': ['enabledChannelNotifications', 'subscriptions'],
|
||||
'settings': ['clientSettings'],
|
||||
'tags': ['followedTags'],
|
||||
'wallet': ['receiveAddress']
|
||||
};
|
||||
|
||||
store.subscribe(() => {
|
||||
const state = (({ auth, claims, content, subscriptions, settings, tags, wallet }) =>
|
||||
({ auth, claims, content, subscriptions, settings, tags, wallet }))(store.getState());
|
||||
NativeModules.StatePersistor.update(state, persistFilter);
|
||||
}); */
|
||||
|
||||
// TODO: Find i18n module that is compatible with react-native
|
||||
global.__ = str => str;
|
||||
|
|
|
@ -4,7 +4,7 @@ import { ActivityIndicator, Dimensions, Image, ScrollView, Text, TouchableOpacit
|
|||
import { TabView, SceneMap } from 'react-native-tab-view';
|
||||
import { navigateBack } from 'utils/helper';
|
||||
import Colors from 'styles/colors';
|
||||
import Constants from 'constants';
|
||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
import Button from 'component/button';
|
||||
import Link from 'component/link';
|
||||
import FileList from 'component/fileList';
|
||||
|
@ -53,7 +53,7 @@ class ChannelPage extends React.PureComponent {
|
|||
if (fetching) {
|
||||
contentList = (
|
||||
<View style={channelPageStyle.busyContainer}>
|
||||
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
||||
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
|
||||
<Text style={channelPageStyle.infoText}>Fetching content...</Text>
|
||||
</View>
|
||||
);
|
||||
|
@ -123,21 +123,21 @@ class ChannelPage extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
const { cover, description, thumbnail, email, website_url, title } = claim.value;
|
||||
const { cover, description, thumbnail, email, website_url: websiteUrl, title } = claim.value;
|
||||
return (
|
||||
<View style={channelPageStyle.aboutTab}>
|
||||
{!website_url && !email && !description && (
|
||||
{!websiteUrl && !email && !description && (
|
||||
<View style={channelPageStyle.busyContainer}>
|
||||
<Text style={channelPageStyle.infoText}>Nothing here yet. Please check back later.</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{(website_url || email || description) && (
|
||||
{(websiteUrl || email || description) && (
|
||||
<ScrollView style={channelPageStyle.aboutScroll} contentContainerStyle={channelPageStyle.aboutScrollContent}>
|
||||
{website_url && website_url.trim().length > 0 && (
|
||||
{websiteUrl && websiteUrl.trim().length > 0 && (
|
||||
<View style={channelPageStyle.aboutItem}>
|
||||
<Text style={channelPageStyle.aboutTitle}>Website</Text>
|
||||
<Link style={channelPageStyle.aboutText} text={website_url} href={website_url} />
|
||||
<Link style={channelPageStyle.aboutText} text={websiteUrl} href={websiteUrl} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ class DiscoverPage extends React.PureComponent {
|
|||
|
||||
buildSections = () => {
|
||||
return this.state.tagCollection.map(tags => ({
|
||||
title: tags.length === 1 ? tags[0] : 'Trending',
|
||||
title: tags.length === 1 ? tags[0] : 'All tags you follow',
|
||||
data: [tags],
|
||||
}));
|
||||
};
|
||||
|
@ -216,7 +216,9 @@ class DiscoverPage extends React.PureComponent {
|
|||
const tags = followedTags.map(tag => tag.name);
|
||||
|
||||
// each of the followed tags
|
||||
const tagCollection = tags.map(tag => [tag]);
|
||||
const tagCollection = _.shuffle(tags)
|
||||
.slice(0, 6)
|
||||
.map(tag => [tag]);
|
||||
// everything
|
||||
tagCollection.unshift(tags);
|
||||
|
||||
|
@ -268,7 +270,8 @@ class DiscoverPage extends React.PureComponent {
|
|||
renderItem={({ item, index, section }) => (
|
||||
<ClaimList
|
||||
key={item.sort().join(',')}
|
||||
orderBy={item.length > 1 ? Constants.DEFAULT_ORDER_BY : orderBy}
|
||||
orderBy={orderBy}
|
||||
time={Constants.TIME_WEEK}
|
||||
tags={item}
|
||||
morePlaceholder
|
||||
navigation={navigation}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Lbry, buildURI } from 'lbry-redux';
|
|||
import { ActivityIndicator, Button, FlatList, Text, TextInput, View, ScrollView } from 'react-native';
|
||||
import { navigateToUri, uriFromFileInfo } from 'utils/helper';
|
||||
import Colors from 'styles/colors';
|
||||
import Constants from 'constants';
|
||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
import PageHeader from 'component/pageHeader';
|
||||
import FileListItem from 'component/fileListItem';
|
||||
import FloatingWalletBalance from 'component/floatingWalletBalance';
|
||||
|
@ -65,7 +65,7 @@ class DownloadsPage extends React.PureComponent {
|
|||
)}
|
||||
{fetching && !hasDownloads && (
|
||||
<View style={downloadsStyle.busyContainer}>
|
||||
<ActivityIndicator size="large" color={Colors.LbryGreen} style={downloadsStyle.loading} />
|
||||
<ActivityIndicator size="large" color={Colors.NextLbryGreen} style={downloadsStyle.loading} />
|
||||
</View>
|
||||
)}
|
||||
{hasDownloads && (
|
||||
|
|
|
@ -563,11 +563,11 @@ class FilePage extends React.PureComponent {
|
|||
|
||||
let innerContent = null;
|
||||
if ((isResolvingUri && !claim) || !claim) {
|
||||
innerContent = (
|
||||
return (
|
||||
<View style={filePageStyle.container}>
|
||||
{isResolvingUri && (
|
||||
<View style={filePageStyle.busyContainer}>
|
||||
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
||||
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
|
||||
<Text style={filePageStyle.infoText}>Loading decentralized data...</Text>
|
||||
</View>
|
||||
)}
|
||||
|
@ -579,9 +579,13 @@ class FilePage extends React.PureComponent {
|
|||
<UriBar value={uri} navigation={navigation} />
|
||||
</View>
|
||||
);
|
||||
} else if (claim && claim.name.length && claim.name[0] === '@') {
|
||||
innerContent = <ChannelPage uri={uri} navigation={navigation} />;
|
||||
} else if (claim) {
|
||||
}
|
||||
|
||||
if (claim) {
|
||||
if (claim && claim.name.length && claim.name[0] === '@') {
|
||||
return <ChannelPage uri={uri} navigation={navigation} />;
|
||||
}
|
||||
|
||||
let isClaimBlackListed = false;
|
||||
|
||||
if (blackListedOutpoints) {
|
||||
|
@ -595,7 +599,7 @@ class FilePage extends React.PureComponent {
|
|||
}
|
||||
|
||||
if (isClaimBlackListed) {
|
||||
innerContent = (
|
||||
return (
|
||||
<View style={filePageStyle.pageContainer}>
|
||||
<View style={filePageStyle.dmcaContainer}>
|
||||
<Text style={filePageStyle.dmcaText}>
|
||||
|
@ -607,358 +611,362 @@ class FilePage extends React.PureComponent {
|
|||
<UriBar value={uri} navigation={navigation} />
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
let tags = [];
|
||||
if (claim && claim.value && claim.value.tags) {
|
||||
tags = claim.value.tags;
|
||||
}
|
||||
}
|
||||
|
||||
const completed = fileInfo && fileInfo.completed;
|
||||
const isRewardContent = rewardedContentClaimIds.includes(claim.claim_id);
|
||||
const description = metadata.description ? metadata.description : null;
|
||||
const mediaType = Lbry.getMediaType(contentType);
|
||||
const isPlayable = mediaType === 'video' || mediaType === 'audio';
|
||||
const { height, signing_channel: signingChannel, value } = claim;
|
||||
const channelName = signingChannel && signingChannel.name;
|
||||
const showActions =
|
||||
fileInfo &&
|
||||
fileInfo.download_path &&
|
||||
!this.state.fullscreenMode &&
|
||||
!this.state.showImageViewer &&
|
||||
!this.state.showWebView;
|
||||
const showFileActions =
|
||||
fileInfo &&
|
||||
fileInfo.download_path &&
|
||||
(completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes));
|
||||
const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id;
|
||||
const canSendTip = this.state.tipAmount > 0;
|
||||
const fullChannelUri =
|
||||
channelClaimId && channelClaimId.trim().length > 0 ? `${channelName}#${channelClaimId}` : channelName;
|
||||
let tags = [];
|
||||
if (claim && claim.value && claim.value.tags) {
|
||||
tags = claim.value.tags;
|
||||
}
|
||||
|
||||
const playerStyle = [
|
||||
filePageStyle.player,
|
||||
this.state.isLandscape
|
||||
? filePageStyle.containedPlayerLandscape
|
||||
: this.state.fullscreenMode
|
||||
? filePageStyle.fullscreenPlayer
|
||||
: filePageStyle.containedPlayer,
|
||||
];
|
||||
const playerBgStyle = [filePageStyle.playerBackground, filePageStyle.containedPlayerBackground];
|
||||
const fsPlayerBgStyle = [filePageStyle.playerBackground, filePageStyle.fullscreenPlayerBackground];
|
||||
// at least 2MB (or the full download) before media can be loaded
|
||||
const canLoadMedia =
|
||||
this.state.streamingMode ||
|
||||
(fileInfo && (fileInfo.written_bytes >= 2097152 || fileInfo.written_bytes === fileInfo.total_bytes)); // 2MB = 1024*1024*2
|
||||
const isViewable = mediaType === 'image' || mediaType === 'text';
|
||||
const isWebViewable = mediaType === 'text';
|
||||
const canOpen = isViewable && completed;
|
||||
const localFileUri = this.localUriForFileInfo(fileInfo);
|
||||
const completed = fileInfo && fileInfo.completed;
|
||||
const isRewardContent = rewardedContentClaimIds.includes(claim.claim_id);
|
||||
const description = metadata.description ? metadata.description : null;
|
||||
const mediaType = Lbry.getMediaType(contentType);
|
||||
const isPlayable = mediaType === 'video' || mediaType === 'audio';
|
||||
const { height, signing_channel: signingChannel, value } = claim;
|
||||
const channelName = signingChannel && signingChannel.name;
|
||||
const showActions =
|
||||
fileInfo &&
|
||||
fileInfo.download_path &&
|
||||
!this.state.fullscreenMode &&
|
||||
!this.state.showImageViewer &&
|
||||
!this.state.showWebView;
|
||||
const showFileActions =
|
||||
fileInfo &&
|
||||
fileInfo.download_path &&
|
||||
(completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes));
|
||||
const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id;
|
||||
const canSendTip = this.state.tipAmount > 0;
|
||||
const fullChannelUri =
|
||||
channelClaimId && channelClaimId.trim().length > 0 ? `${channelName}#${channelClaimId}` : channelName;
|
||||
|
||||
const openFile = () => {
|
||||
if (mediaType === 'image') {
|
||||
// use image viewer
|
||||
if (!this.state.showImageViewer) {
|
||||
this.setState({
|
||||
imageUrls: [
|
||||
{
|
||||
url: localFileUri,
|
||||
},
|
||||
],
|
||||
showImageViewer: true,
|
||||
});
|
||||
}
|
||||
const playerStyle = [
|
||||
filePageStyle.player,
|
||||
this.state.isLandscape
|
||||
? filePageStyle.containedPlayerLandscape
|
||||
: this.state.fullscreenMode
|
||||
? filePageStyle.fullscreenPlayer
|
||||
: filePageStyle.containedPlayer,
|
||||
];
|
||||
const playerBgStyle = [filePageStyle.playerBackground, filePageStyle.containedPlayerBackground];
|
||||
const fsPlayerBgStyle = [filePageStyle.playerBackground, filePageStyle.fullscreenPlayerBackground];
|
||||
// at least 2MB (or the full download) before media can be loaded
|
||||
const canLoadMedia =
|
||||
this.state.streamingMode ||
|
||||
(fileInfo && (fileInfo.written_bytes >= 2097152 || fileInfo.written_bytes === fileInfo.total_bytes)); // 2MB = 1024*1024*2
|
||||
const isViewable = mediaType === 'image' || mediaType === 'text';
|
||||
const isWebViewable = mediaType === 'text';
|
||||
const canOpen = isViewable && completed;
|
||||
const localFileUri = this.localUriForFileInfo(fileInfo);
|
||||
|
||||
const openFile = () => {
|
||||
if (mediaType === 'image') {
|
||||
// use image viewer
|
||||
if (!this.state.showImageViewer) {
|
||||
this.setState({
|
||||
imageUrls: [
|
||||
{
|
||||
url: localFileUri,
|
||||
},
|
||||
],
|
||||
showImageViewer: true,
|
||||
});
|
||||
}
|
||||
if (isWebViewable) {
|
||||
// show webview
|
||||
if (!this.state.showWebView) {
|
||||
this.setState({
|
||||
showWebView: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (isWebViewable) {
|
||||
// show webview
|
||||
if (!this.state.showWebView) {
|
||||
this.setState({
|
||||
showWebView: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (
|
||||
fileInfo &&
|
||||
!this.state.autoDownloadStarted &&
|
||||
this.state.uriVars &&
|
||||
this.state.uriVars.download === 'true'
|
||||
) {
|
||||
this.setState({ autoDownloadStarted: true }, () => {
|
||||
purchaseUri(uri, costInfo, !isPlayable);
|
||||
if (NativeModules.UtilityModule) {
|
||||
NativeModules.UtilityModule.checkDownloads();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (this.state.downloadPressed && canOpen) {
|
||||
// automatically open a web viewable or image file after the download button is pressed
|
||||
openFile();
|
||||
}
|
||||
if (fileInfo && !this.state.autoDownloadStarted && this.state.uriVars && this.state.uriVars.download === 'true') {
|
||||
this.setState({ autoDownloadStarted: true }, () => {
|
||||
purchaseUri(uri, costInfo, !isPlayable);
|
||||
if (NativeModules.UtilityModule) {
|
||||
NativeModules.UtilityModule.checkDownloads();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
innerContent = (
|
||||
<View style={filePageStyle.pageContainer}>
|
||||
{!this.state.fullscreenMode && <UriBar value={uri} navigation={navigation} />}
|
||||
{this.state.showWebView && isWebViewable && (
|
||||
<WebView source={{ uri: localFileUri }} style={filePageStyle.viewer} />
|
||||
)}
|
||||
if (this.state.downloadPressed && canOpen) {
|
||||
// automatically open a web viewable or image file after the download button is pressed
|
||||
openFile();
|
||||
}
|
||||
|
||||
{this.state.showImageViewer && (
|
||||
<ImageViewer
|
||||
style={StyleSheet.flatten(filePageStyle.viewer)}
|
||||
imageUrls={this.state.imageUrls}
|
||||
renderIndicator={() => null}
|
||||
/>
|
||||
)}
|
||||
return (
|
||||
<View style={filePageStyle.pageContainer}>
|
||||
{!this.state.fullscreenMode && <UriBar value={uri} navigation={navigation} />}
|
||||
{this.state.showWebView && isWebViewable && (
|
||||
<WebView source={{ uri: localFileUri }} style={filePageStyle.viewer} />
|
||||
)}
|
||||
|
||||
{!this.state.showWebView && (
|
||||
<View
|
||||
style={
|
||||
this.state.fullscreenMode ? filePageStyle.innerPageContainerFsMode : filePageStyle.innerPageContainer
|
||||
}
|
||||
onLayout={this.checkOrientation}
|
||||
>
|
||||
<View style={filePageStyle.mediaContainer}>
|
||||
{(canOpen || (!fileInfo || (isPlayable && !canLoadMedia)) || (!canOpen && fileInfo)) && (
|
||||
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={thumbnail} />
|
||||
)}
|
||||
{(!this.state.downloadButtonShown || this.state.downloadPressed) && !this.state.mediaLoaded && (
|
||||
<ActivityIndicator size="large" color={Colors.LbryGreen} style={filePageStyle.loading} />
|
||||
)}
|
||||
{((isPlayable && !completed && !canLoadMedia) ||
|
||||
canOpen ||
|
||||
(!completed && !this.state.streamingMode)) &&
|
||||
!this.state.downloadPressed && (
|
||||
<FileDownloadButton
|
||||
uri={uri}
|
||||
style={filePageStyle.downloadButton}
|
||||
openFile={openFile}
|
||||
isPlayable={isPlayable}
|
||||
isViewable={isViewable}
|
||||
onPlay={this.onFileDownloadButtonPlayed}
|
||||
onView={() => this.setState({ downloadPressed: true })}
|
||||
onButtonLayout={() => this.setState({ downloadButtonShown: true })}
|
||||
/>
|
||||
)}
|
||||
{!fileInfo && (
|
||||
<FilePrice
|
||||
uri={uri}
|
||||
style={filePageStyle.filePriceContainer}
|
||||
textStyle={filePageStyle.filePriceText}
|
||||
/>
|
||||
)}
|
||||
{this.state.showImageViewer && (
|
||||
<ImageViewer
|
||||
style={StyleSheet.flatten(filePageStyle.viewer)}
|
||||
imageUrls={this.state.imageUrls}
|
||||
renderIndicator={() => null}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TouchableOpacity style={filePageStyle.backButton} onPress={this.onBackButtonPressed}>
|
||||
<Icon name={'arrow-left'} size={18} style={filePageStyle.backButtonIcon} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && (
|
||||
<View
|
||||
style={playerBgStyle}
|
||||
ref={ref => {
|
||||
this.playerBackground = ref;
|
||||
}}
|
||||
onLayout={evt => {
|
||||
if (!this.state.playerBgHeight) {
|
||||
this.setState({ playerBgHeight: evt.nativeEvent.layout.height });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{!this.state.showWebView && (
|
||||
<View
|
||||
style={
|
||||
this.state.fullscreenMode ? filePageStyle.innerPageContainerFsMode : filePageStyle.innerPageContainer
|
||||
}
|
||||
onLayout={this.checkOrientation}
|
||||
>
|
||||
<View style={filePageStyle.mediaContainer}>
|
||||
{(canOpen || (!fileInfo || (isPlayable && !canLoadMedia)) || (!canOpen && fileInfo)) && (
|
||||
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={thumbnail} />
|
||||
)}
|
||||
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) &&
|
||||
this.state.fullscreenMode && <View style={fsPlayerBgStyle} />}
|
||||
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && (
|
||||
<MediaPlayer
|
||||
claim={claim}
|
||||
assignPlayer={ref => {
|
||||
this.player = ref;
|
||||
}}
|
||||
{(!this.state.downloadButtonShown || this.state.downloadPressed) && !this.state.mediaLoaded && (
|
||||
<ActivityIndicator size="large" color={Colors.NextLbryGreen} style={filePageStyle.loading} />
|
||||
)}
|
||||
{((isPlayable && !completed && !canLoadMedia) ||
|
||||
canOpen ||
|
||||
(!completed && !this.state.streamingMode)) &&
|
||||
!this.state.downloadPressed && (
|
||||
<FileDownloadButton
|
||||
uri={uri}
|
||||
source={this.playerUriForFileInfo(fileInfo)}
|
||||
style={playerStyle}
|
||||
autoPlay={autoplay || this.state.autoPlayMedia}
|
||||
onFullscreenToggled={this.handleFullscreenToggle}
|
||||
onLayout={evt => {
|
||||
if (!this.state.playerHeight) {
|
||||
this.setState({ playerHeight: evt.nativeEvent.layout.height });
|
||||
}
|
||||
}}
|
||||
onMediaLoaded={() => this.onMediaLoaded(channelName, title, uri)}
|
||||
onBackButtonPressed={this.onBackButtonPressed}
|
||||
onPlaybackStarted={this.onPlaybackStarted}
|
||||
onPlaybackFinished={this.onPlaybackFinished}
|
||||
thumbnail={thumbnail}
|
||||
position={position}
|
||||
style={filePageStyle.downloadButton}
|
||||
openFile={openFile}
|
||||
isPlayable={isPlayable}
|
||||
isViewable={isViewable}
|
||||
onPlay={this.onFileDownloadButtonPlayed}
|
||||
onView={() => this.setState({ downloadPressed: true })}
|
||||
onButtonLayout={() => this.setState({ downloadButtonShown: true })}
|
||||
/>
|
||||
)}
|
||||
{!fileInfo && (
|
||||
<FilePrice
|
||||
uri={uri}
|
||||
style={filePageStyle.filePriceContainer}
|
||||
textStyle={filePageStyle.filePriceText}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showActions && showFileActions && (
|
||||
<View style={filePageStyle.actions}>
|
||||
{showFileActions && (
|
||||
<View style={filePageStyle.fileActions}>
|
||||
{completed && (
|
||||
<Button
|
||||
style={filePageStyle.actionButton}
|
||||
theme={'light'}
|
||||
icon={'trash'}
|
||||
text={'Delete'}
|
||||
onPress={this.onDeletePressed}
|
||||
/>
|
||||
)}
|
||||
{!completed &&
|
||||
fileInfo &&
|
||||
!fileInfo.stopped &&
|
||||
fileInfo.written_bytes < fileInfo.total_bytes &&
|
||||
!this.state.stopDownloadConfirmed && (
|
||||
<Button
|
||||
style={filePageStyle.actionButton}
|
||||
icon={'stop'}
|
||||
theme={'light'}
|
||||
text={'Stop Download'}
|
||||
onPress={this.onStopDownloadPressed}
|
||||
/>
|
||||
)}
|
||||
<TouchableOpacity style={filePageStyle.backButton} onPress={this.onBackButtonPressed}>
|
||||
<Icon name={'arrow-left'} size={18} style={filePageStyle.backButtonIcon} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && (
|
||||
<View
|
||||
style={playerBgStyle}
|
||||
ref={ref => {
|
||||
this.playerBackground = ref;
|
||||
}}
|
||||
onLayout={evt => {
|
||||
if (!this.state.playerBgHeight) {
|
||||
this.setState({ playerBgHeight: evt.nativeEvent.layout.height });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && this.state.fullscreenMode && (
|
||||
<View style={fsPlayerBgStyle} />
|
||||
)}
|
||||
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && (
|
||||
<MediaPlayer
|
||||
claim={claim}
|
||||
assignPlayer={ref => {
|
||||
this.player = ref;
|
||||
}}
|
||||
uri={uri}
|
||||
source={this.playerUriForFileInfo(fileInfo)}
|
||||
style={playerStyle}
|
||||
autoPlay={autoplay || this.state.autoPlayMedia}
|
||||
onFullscreenToggled={this.handleFullscreenToggle}
|
||||
onLayout={evt => {
|
||||
if (!this.state.playerHeight) {
|
||||
this.setState({ playerHeight: evt.nativeEvent.layout.height });
|
||||
}
|
||||
}}
|
||||
onMediaLoaded={() => this.onMediaLoaded(channelName, title, uri)}
|
||||
onBackButtonPressed={this.onBackButtonPressed}
|
||||
onPlaybackStarted={this.onPlaybackStarted}
|
||||
onPlaybackFinished={this.onPlaybackFinished}
|
||||
thumbnail={thumbnail}
|
||||
position={position}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showActions && showFileActions && (
|
||||
<View style={filePageStyle.actions}>
|
||||
{showFileActions && (
|
||||
<View style={filePageStyle.fileActions}>
|
||||
{completed && (
|
||||
<Button
|
||||
style={filePageStyle.actionButton}
|
||||
theme={'light'}
|
||||
icon={'trash'}
|
||||
text={'Delete'}
|
||||
onPress={this.onDeletePressed}
|
||||
/>
|
||||
)}
|
||||
{!completed &&
|
||||
fileInfo &&
|
||||
!fileInfo.stopped &&
|
||||
fileInfo.written_bytes < fileInfo.total_bytes &&
|
||||
!this.state.stopDownloadConfirmed && (
|
||||
<Button
|
||||
style={filePageStyle.actionButton}
|
||||
icon={'stop'}
|
||||
theme={'light'}
|
||||
text={'Stop Download'}
|
||||
onPress={this.onStopDownloadPressed}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
<ScrollView
|
||||
style={showActions ? filePageStyle.scrollContainerActions : filePageStyle.scrollContainer}
|
||||
contentContainerstyle={showActions ? null : filePageStyle.scrollContent}
|
||||
keyboardShouldPersistTaps={'handled'}
|
||||
ref={ref => {
|
||||
this.scrollView = ref;
|
||||
}}
|
||||
>
|
||||
<TouchableWithoutFeedback
|
||||
style={filePageStyle.titleTouch}
|
||||
onPress={() => this.setState({ showDescription: !this.state.showDescription })}
|
||||
>
|
||||
<View style={filePageStyle.titleRow}>
|
||||
<Text style={filePageStyle.title} selectable>
|
||||
{title}
|
||||
</Text>
|
||||
<View style={filePageStyle.descriptionToggle}>
|
||||
<Icon name={this.state.showDescription ? 'caret-up' : 'caret-down'} size={24} />
|
||||
</View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
<View style={filePageStyle.channelRow}>
|
||||
<View style={filePageStyle.publishInfo}>
|
||||
{channelName && (
|
||||
<Link
|
||||
style={filePageStyle.channelName}
|
||||
selectable
|
||||
text={channelName}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode={'tail'}
|
||||
onPress={() => {
|
||||
navigateToUri(navigation, normalizeURI(fullChannelUri));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!channelName && (
|
||||
<Text style={filePageStyle.anonChannelName} selectable ellipsizeMode={'tail'}>
|
||||
Anonymous
|
||||
</Text>
|
||||
)}
|
||||
<DateTime
|
||||
style={filePageStyle.publishDate}
|
||||
textStyle={filePageStyle.publishDateText}
|
||||
uri={uri}
|
||||
formatOptions={{ day: 'numeric', month: 'long', year: 'numeric' }}
|
||||
show={DateTime.SHOW_DATE}
|
||||
/>
|
||||
</View>
|
||||
<View style={filePageStyle.subscriptionRow}>
|
||||
{false && ((isPlayable && !fileInfo) || (isPlayable && fileInfo && !fileInfo.download_path)) && (
|
||||
<Button
|
||||
style={[filePageStyle.actionButton, filePageStyle.saveFileButton]}
|
||||
theme={'light'}
|
||||
icon={'download'}
|
||||
onPress={this.onSaveFilePressed}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
style={[filePageStyle.actionButton, filePageStyle.tipButton]}
|
||||
theme={'light'}
|
||||
icon={'gift'}
|
||||
onPress={() => this.setState({ showTipView: true })}
|
||||
/>
|
||||
{channelName && (
|
||||
<SubscribeButton
|
||||
style={filePageStyle.actionButton}
|
||||
uri={fullChannelUri}
|
||||
name={channelName}
|
||||
hideText={false}
|
||||
/>
|
||||
)}
|
||||
{channelName && (
|
||||
<SubscribeNotificationButton
|
||||
style={[filePageStyle.actionButton, filePageStyle.bellButton]}
|
||||
uri={fullChannelUri}
|
||||
name={channelName}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{this.state.showTipView && <View style={filePageStyle.divider} />}
|
||||
{this.state.showTipView && (
|
||||
<View style={filePageStyle.tipCard}>
|
||||
<View style={filePageStyle.row}>
|
||||
<View style={filePageStyle.amountRow}>
|
||||
<TextInput
|
||||
ref={ref => (this.tipAmountInput = ref)}
|
||||
onChangeText={value => this.setState({ tipAmount: value })}
|
||||
keyboardType={'numeric'}
|
||||
placeholder={'0'}
|
||||
value={this.state.tipAmount}
|
||||
style={[filePageStyle.input, filePageStyle.tipAmountInput]}
|
||||
/>
|
||||
<Text style={[filePageStyle.text, filePageStyle.currency]}>LBC</Text>
|
||||
</View>
|
||||
<Link
|
||||
style={[filePageStyle.link, filePageStyle.cancelTipLink]}
|
||||
text={'Cancel'}
|
||||
onPress={() => this.setState({ showTipView: false })}
|
||||
/>
|
||||
<Button
|
||||
text={'Send a tip'}
|
||||
style={[filePageStyle.button, filePageStyle.sendButton]}
|
||||
disabled={!canSendTip}
|
||||
onPress={this.handleSendTip}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{this.state.showDescription && description && description.length > 0 && (
|
||||
<View style={filePageStyle.divider} />
|
||||
)}
|
||||
{this.state.showDescription && description && (
|
||||
<View>
|
||||
<Text style={filePageStyle.description} selectable>
|
||||
{this.linkify(description)}
|
||||
</Text>
|
||||
{tags && tags.length > 0 && (
|
||||
<View style={filePageStyle.tagContainer}>
|
||||
<Text style={filePageStyle.tagTitle}>Tags</Text>
|
||||
<View style={filePageStyle.tagList}>{this.renderTags(tags)}</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
<ScrollView
|
||||
style={showActions ? filePageStyle.scrollContainerActions : filePageStyle.scrollContainer}
|
||||
contentContainerstyle={showActions ? null : filePageStyle.scrollContent}
|
||||
keyboardShouldPersistTaps={'handled'}
|
||||
ref={ref => {
|
||||
this.scrollView = ref;
|
||||
}}
|
||||
>
|
||||
<TouchableWithoutFeedback
|
||||
style={filePageStyle.titleTouch}
|
||||
onPress={() => this.setState({ showDescription: !this.state.showDescription })}
|
||||
>
|
||||
<View style={filePageStyle.titleRow}>
|
||||
<Text style={filePageStyle.title} selectable>
|
||||
{title}
|
||||
</Text>
|
||||
<View style={filePageStyle.descriptionToggle}>
|
||||
<Icon name={this.state.showDescription ? 'caret-up' : 'caret-down'} size={24} />
|
||||
</View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
{channelName && (
|
||||
<View style={filePageStyle.channelRow}>
|
||||
<View style={filePageStyle.publishInfo}>
|
||||
<Link
|
||||
style={filePageStyle.channelName}
|
||||
selectable
|
||||
text={channelName}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode={'tail'}
|
||||
onPress={() => {
|
||||
navigateToUri(navigation, normalizeURI(fullChannelUri));
|
||||
}}
|
||||
/>
|
||||
<DateTime
|
||||
style={filePageStyle.publishDate}
|
||||
textStyle={filePageStyle.publishDateText}
|
||||
uri={uri}
|
||||
formatOptions={{ day: 'numeric', month: 'long', year: 'numeric' }}
|
||||
show={DateTime.SHOW_DATE}
|
||||
/>
|
||||
</View>
|
||||
<View style={filePageStyle.subscriptionRow}>
|
||||
{false &&
|
||||
((isPlayable && !fileInfo) || (isPlayable && fileInfo && !fileInfo.download_path)) && (
|
||||
<Button
|
||||
style={[filePageStyle.actionButton, filePageStyle.saveFileButton]}
|
||||
theme={'light'}
|
||||
icon={'download'}
|
||||
onPress={this.onSaveFilePressed}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
style={[filePageStyle.actionButton, filePageStyle.tipButton]}
|
||||
theme={'light'}
|
||||
icon={'gift'}
|
||||
onPress={() => this.setState({ showTipView: true })}
|
||||
/>
|
||||
<SubscribeButton
|
||||
style={filePageStyle.actionButton}
|
||||
uri={fullChannelUri}
|
||||
name={channelName}
|
||||
hideText={false}
|
||||
/>
|
||||
<SubscribeNotificationButton
|
||||
style={[filePageStyle.actionButton, filePageStyle.bellButton]}
|
||||
uri={fullChannelUri}
|
||||
name={channelName}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{this.state.showTipView && <View style={filePageStyle.divider} />}
|
||||
{this.state.showTipView && (
|
||||
<View style={filePageStyle.tipCard}>
|
||||
<View style={filePageStyle.row}>
|
||||
<View style={filePageStyle.amountRow}>
|
||||
<TextInput
|
||||
ref={ref => (this.tipAmountInput = ref)}
|
||||
onChangeText={value => this.setState({ tipAmount: value })}
|
||||
keyboardType={'numeric'}
|
||||
placeholder={'0'}
|
||||
value={this.state.tipAmount}
|
||||
style={[filePageStyle.input, filePageStyle.tipAmountInput]}
|
||||
/>
|
||||
<Text style={[filePageStyle.text, filePageStyle.currency]}>LBC</Text>
|
||||
</View>
|
||||
<Link
|
||||
style={[filePageStyle.link, filePageStyle.cancelTipLink]}
|
||||
text={'Cancel'}
|
||||
onPress={() => this.setState({ showTipView: false })}
|
||||
/>
|
||||
<Button
|
||||
text={'Send a tip'}
|
||||
style={[filePageStyle.button, filePageStyle.sendButton]}
|
||||
disabled={!canSendTip}
|
||||
onPress={this.handleSendTip}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{costInfo && parseFloat(costInfo.cost) > balance && <FileRewardsDriver navigation={navigation} />}
|
||||
|
||||
{this.state.showDescription && description && description.length > 0 && (
|
||||
<View style={filePageStyle.divider} />
|
||||
)}
|
||||
{this.state.showDescription && description && (
|
||||
<View>
|
||||
<Text style={filePageStyle.description} selectable>
|
||||
{this.linkify(description)}
|
||||
</Text>
|
||||
{tags && tags.length > 0 && (
|
||||
<View style={filePageStyle.tagContainer}>
|
||||
<Text style={filePageStyle.tagTitle}>Tags</Text>
|
||||
<View style={filePageStyle.tagList}>{this.renderTags(tags)}</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{costInfo && parseFloat(costInfo.cost) > balance && <FileRewardsDriver navigation={navigation} />}
|
||||
|
||||
<View onLayout={this.setRelatedContentPosition} />
|
||||
<RelatedContent navigation={navigation} uri={uri} />
|
||||
</ScrollView>
|
||||
</View>
|
||||
)}
|
||||
{!this.state.fullscreenMode && !this.state.showImageViewer && !this.state.showWebView && (
|
||||
<FloatingWalletBalance navigation={navigation} />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
<View onLayout={this.setRelatedContentPosition} />
|
||||
<RelatedContent navigation={navigation} uri={uri} />
|
||||
</ScrollView>
|
||||
</View>
|
||||
)}
|
||||
{!this.state.fullscreenMode && !this.state.showImageViewer && !this.state.showWebView && (
|
||||
<FloatingWalletBalance navigation={navigation} />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return innerContent;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -550,7 +550,7 @@ class PublishPage extends React.PureComponent {
|
|||
</View>
|
||||
{(!videos || !thumbnailPath || !galleryThumbnailsChecked) && (
|
||||
<View style={publishStyle.loadingView}>
|
||||
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
||||
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
|
||||
</View>
|
||||
)}
|
||||
{thumbnailPath && (!videos || videos.length === 0) && (
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { Lbry } from 'lbry-redux';
|
||||
import { ActivityIndicator, NativeModules, ScrollView, Text, View } from 'react-native';
|
||||
import Colors from 'styles/colors';
|
||||
import Constants from 'constants';
|
||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
import Link from 'component/link';
|
||||
import CustomRewardCard from 'component/customRewardCard';
|
||||
import PageHeader from 'component/pageHeader';
|
||||
|
@ -87,7 +87,7 @@ class RewardsPage extends React.PureComponent {
|
|||
// claim new_user and new_mobile rewards
|
||||
for (let i = 0; i < rewards.length; i++) {
|
||||
const { reward_type: type } = rewards[i];
|
||||
if ('new_user' === type || 'new_mobile' === type) {
|
||||
if (type === 'new_user' || type === 'new_mobile') {
|
||||
claimReward(rewards[i]);
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ class RewardsPage extends React.PureComponent {
|
|||
if (fetching) {
|
||||
return (
|
||||
<View style={rewardStyle.busyContainer}>
|
||||
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
||||
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
|
||||
<Text style={rewardStyle.infoText}>Fetching rewards...</Text>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Lbry, parseURI, normalizeURI, isURIValid } from 'lbry-redux';
|
|||
import { ActivityIndicator, Button, Text, TextInput, View, ScrollView } from 'react-native';
|
||||
import { navigateToUri } from 'utils/helper';
|
||||
import Colors from 'styles/colors';
|
||||
import Constants from 'constants';
|
||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
import PageHeader from 'component/pageHeader';
|
||||
import FileListItem from 'component/fileListItem';
|
||||
import FloatingWalletBalance from 'component/floatingWalletBalance';
|
||||
|
@ -91,7 +91,7 @@ class SearchPage extends React.PureComponent {
|
|||
<UriBar value={query} navigation={navigation} onSearchSubmitted={this.handleSearchSubmitted} />
|
||||
{isSearching && (
|
||||
<View style={searchStyle.busyContainer}>
|
||||
<ActivityIndicator size="large" color={Colors.LbryGreen} style={searchStyle.loading} />
|
||||
<ActivityIndicator size="large" color={Colors.NextLbryGreen} style={searchStyle.loading} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
@ -105,7 +105,7 @@ class SearchPage extends React.PureComponent {
|
|||
<FileListItem
|
||||
key={this.state.currentUri}
|
||||
uri={this.state.currentUri}
|
||||
featuredResult={true}
|
||||
featuredResult
|
||||
style={searchStyle.featuredResultItem}
|
||||
navigation={navigation}
|
||||
onPress={() => navigateToUri(navigation, this.state.currentUri)}
|
||||
|
@ -113,14 +113,14 @@ class SearchPage extends React.PureComponent {
|
|||
)}
|
||||
{uris && uris.length
|
||||
? uris.map(uri => (
|
||||
<FileListItem
|
||||
key={uri}
|
||||
uri={uri}
|
||||
style={searchStyle.resultItem}
|
||||
navigation={navigation}
|
||||
onPress={() => navigateToUri(navigation, uri)}
|
||||
/>
|
||||
))
|
||||
<FileListItem
|
||||
key={uri}
|
||||
uri={uri}
|
||||
style={searchStyle.resultItem}
|
||||
navigation={navigation}
|
||||
onPress={() => navigateToUri(navigation, uri)}
|
||||
/>
|
||||
))
|
||||
: null}
|
||||
{(!uris || uris.length === 0) && (
|
||||
<View style={searchStyle.noResults}>
|
||||
|
|
|
@ -3,6 +3,7 @@ import { doBalanceSubscribe, doUpdateBlockHeight, doToast } from 'lbry-redux';
|
|||
import {
|
||||
doAuthenticate,
|
||||
doBlackListedOutpointsSubscribe,
|
||||
doFilteredOutpointsSubscribe,
|
||||
doCheckSubscriptionsInit,
|
||||
doFetchMySubscriptions,
|
||||
doFetchRewardedContent,
|
||||
|
@ -25,6 +26,7 @@ const perform = dispatch => ({
|
|||
authenticate: (appVersion, os) => dispatch(doAuthenticate(appVersion, os)),
|
||||
balanceSubscribe: () => dispatch(doBalanceSubscribe()),
|
||||
blacklistedOutpointsSubscribe: () => dispatch(doBlackListedOutpointsSubscribe()),
|
||||
filteredOutpointsSubscribe: () => dispatch(doFilteredOutpointsSubscribe()),
|
||||
checkSubscriptionsInit: () => dispatch(doCheckSubscriptionsInit()),
|
||||
fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
|
||||
fetchSubscriptions: callback => dispatch(doFetchMySubscriptions(callback)),
|
||||
|
|
|
@ -10,7 +10,7 @@ import Button from 'component/button';
|
|||
import ProgressBar from 'component/progressBar';
|
||||
import PropTypes from 'prop-types';
|
||||
import Colors from 'styles/colors';
|
||||
import Constants from 'constants';
|
||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
import splashStyle from 'styles/splash';
|
||||
|
||||
const BLOCK_HEIGHT_INTERVAL = 1000 * 60 * 2.5; // every 2.5 minutes
|
||||
|
@ -50,7 +50,7 @@ class SplashScreen extends React.PureComponent {
|
|||
}
|
||||
|
||||
navigateToMain = () => {
|
||||
const { navigation } = this.props;
|
||||
const { navigation, notify, verifyUserEmail, verifyUserEmailFailure } = this.props;
|
||||
const resetAction = StackActions.reset({
|
||||
index: 0,
|
||||
actions: [NavigationActions.navigate({ routeName: 'Main' })],
|
||||
|
@ -114,9 +114,8 @@ class SplashScreen extends React.PureComponent {
|
|||
balanceSubscribe,
|
||||
blacklistedOutpointsSubscribe,
|
||||
checkSubscriptionsInit,
|
||||
filteredOutpointsSubscribe,
|
||||
getSync,
|
||||
navigation,
|
||||
notify,
|
||||
updateBlockHeight,
|
||||
user,
|
||||
} = this.props;
|
||||
|
@ -125,11 +124,8 @@ class SplashScreen extends React.PureComponent {
|
|||
// Leave the splash screen
|
||||
balanceSubscribe();
|
||||
blacklistedOutpointsSubscribe();
|
||||
filteredOutpointsSubscribe();
|
||||
checkSubscriptionsInit();
|
||||
updateBlockHeight();
|
||||
setInterval(() => {
|
||||
updateBlockHeight();
|
||||
}, BLOCK_HEIGHT_INTERVAL);
|
||||
|
||||
if (user && user.id && user.has_verified_email) {
|
||||
// user already authenticated
|
||||
|
@ -186,7 +182,6 @@ class SplashScreen extends React.PureComponent {
|
|||
this.finishSplashScreen();
|
||||
})
|
||||
.catch(() => this.handleAccountUnlockFailed());
|
||||
return;
|
||||
} else {
|
||||
this.setState({
|
||||
message: testingNetwork,
|
||||
|
@ -222,7 +217,7 @@ class SplashScreen extends React.PureComponent {
|
|||
});
|
||||
} else if (walletStatus && walletStatus.blocks_behind > 0) {
|
||||
const behind = walletStatus.blocks_behind;
|
||||
const behindText = behind + ' block' + (behind == 1 ? '' : 's') + ' behind';
|
||||
const behindText = behind + ' block' + (behind === 1 ? '' : 's') + ' behind';
|
||||
this.setState({
|
||||
message: 'Blockchain Sync',
|
||||
details: behindText,
|
||||
|
@ -254,7 +249,7 @@ class SplashScreen extends React.PureComponent {
|
|||
// Start measuring the first launch time from the splash screen
|
||||
// (time to first user interaction - after first run completed)
|
||||
AsyncStorage.getItem('hasLaunched').then(value => {
|
||||
if ('true' !== value) {
|
||||
if (value !== 'true') {
|
||||
AsyncStorage.setItem('hasLaunched', 'true');
|
||||
// only set firstLaunchTime since we've determined that this is the first app launch ever
|
||||
AsyncStorage.setItem('firstLaunchTime', String(moment().unix()));
|
||||
|
|
|
@ -5,8 +5,9 @@ import {
|
|||
doFetchRecommendedSubscriptions,
|
||||
selectSubscriptionClaims,
|
||||
selectSubscriptions,
|
||||
selectSubscriptionsBeingFetched,
|
||||
selectIsFetchingSubscriptions,
|
||||
selectIsFetchingSuggested,
|
||||
selectSuggestedChannels,
|
||||
selectUnreadSubscriptions,
|
||||
selectViewMode,
|
||||
selectFirstRunCompleted,
|
||||
|
@ -16,13 +17,15 @@ import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
|||
import { doSetClientSetting } from 'redux/actions/settings';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { selectCurrentRoute } from 'redux/selectors/drawer';
|
||||
import Constants from 'constants';
|
||||
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
import SubscriptionsPage from './view';
|
||||
|
||||
const select = state => ({
|
||||
currentRoute: selectCurrentRoute(state),
|
||||
loading: selectIsFetchingSubscriptions(state) || Boolean(Object.keys(selectSubscriptionsBeingFetched(state)).length),
|
||||
loading: selectIsFetchingSubscriptions(state),
|
||||
loadingSuggested: selectIsFetchingSuggested(state),
|
||||
subscribedChannels: selectSubscriptions(state),
|
||||
suggestedChannels: selectSuggestedChannels(state),
|
||||
subscriptionsViewMode: makeSelectClientSetting(Constants.SETTING_SUBSCRIPTIONS_VIEW_MODE)(state),
|
||||
allSubscriptions: selectSubscriptionClaims(state),
|
||||
unreadSubscriptions: selectUnreadSubscriptions(state),
|
||||
|
|
|
@ -115,11 +115,12 @@ class SubscriptionsPage extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const {
|
||||
suggestedChannels,
|
||||
subscribedChannels,
|
||||
allSubscriptions,
|
||||
loading,
|
||||
viewMode,
|
||||
doSetViewMode,
|
||||
loading,
|
||||
loadingSuggested,
|
||||
firstRunCompleted,
|
||||
doCompleteFirstRun,
|
||||
|
@ -182,7 +183,7 @@ class SubscriptionsPage extends React.PureComponent {
|
|||
|
||||
{hasSubscriptions && loading && (
|
||||
<View style={subscriptionsStyle.busyContainer}>
|
||||
<ActivityIndicator size="large" color={Colors.LbryGreen} style={subscriptionsStyle.loading} />
|
||||
<ActivityIndicator size="large" color={Colors.NextLbryGreen} style={subscriptionsStyle.loading} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
@ -209,8 +210,7 @@ class SubscriptionsPage extends React.PureComponent {
|
|||
|
||||
{loadingSuggested && (
|
||||
<View style={subscriptionsStyle.centered}>
|
||||
<ActivityIndicator size="large" colors={Colors.LbryGreen} style={subscriptionsStyle.loading} />
|
||||
\\
|
||||
<ActivityIndicator size="large" colors={Colors.NextLbryGreen} style={subscriptionsStyle.loading} />
|
||||
</View>
|
||||
)}
|
||||
{!loadingSuggested && <SuggestedSubscriptions navigation={navigation} />}
|
||||
|
|
|
@ -78,7 +78,7 @@ class TagPage extends React.PureComponent {
|
|||
};
|
||||
|
||||
handleTimeItemSelected = item => {
|
||||
this.setState({ time: item.name });
|
||||
this.setState({ currentTimeItem: item, time: item.name, showTimePicker: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
@ -95,6 +95,7 @@ class TrendingPage extends React.PureComponent {
|
|||
orderBy={Constants.DEFAULT_ORDER_BY}
|
||||
trendingForAll={TRENDING_FOR_ITEMS[0].name === currentTrendingForItem.name}
|
||||
tags={followedTags.map(tag => tag.name)}
|
||||
time={null}
|
||||
navigation={navigation}
|
||||
orientation={Constants.ORIENTATION_VERTICAL}
|
||||
/>
|
||||
|
|
96
src/redux/actions/performance.js
Normal file
96
src/redux/actions/performance.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
// @flow
|
||||
import { NativeModules } from 'react-native';
|
||||
import {
|
||||
ACTIONS,
|
||||
batchActions,
|
||||
buildURI,
|
||||
doResolveUri,
|
||||
doUpdateSearchQuery,
|
||||
makeSelectSearchUris,
|
||||
selectSuggestions,
|
||||
makeSelectQueryWithOptions,
|
||||
selectSearchValue,
|
||||
} from 'lbry-redux';
|
||||
|
||||
let CONNECTION_STRING = 'https://lighthouse.lbry.com/';
|
||||
|
||||
const handleNativeFetchResponse = str => {
|
||||
const json = JSON.parse(str);
|
||||
if (json.error) {
|
||||
return Promise.reject(new Error(json.error));
|
||||
}
|
||||
|
||||
return Promise.resolve(json);
|
||||
};
|
||||
|
||||
// Use a native asyncTask to call the lighthouse api
|
||||
export const doNativeSearch = (
|
||||
rawQuery: string, // pass in a query if you don't want to search for what's in the search bar
|
||||
size: ?number, // only pass in if you don't want to use the users setting (ex: related content)
|
||||
from: ?number,
|
||||
isBackgroundSearch: boolean = false
|
||||
) => (dispatch: Dispatch, getState: GetState) => {
|
||||
const query = rawQuery.replace(/^lbry:\/\//i, '').replace(/\//, ' ');
|
||||
|
||||
if (!query) {
|
||||
dispatch({
|
||||
type: ACTIONS.SEARCH_FAIL,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
const queryWithOptions = makeSelectQueryWithOptions(query, size, from, isBackgroundSearch)(state);
|
||||
|
||||
// If we have already searched for something, we don't need to do anything
|
||||
const urisForQuery = makeSelectSearchUris(queryWithOptions)(state);
|
||||
if (urisForQuery && !!urisForQuery.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.SEARCH_START,
|
||||
});
|
||||
|
||||
// If the user is on the file page with a pre-populated uri and they select
|
||||
// the search option without typing anything, searchQuery will be empty
|
||||
// We need to populate it so the input is filled on the search page
|
||||
// isBackgroundSearch means the search is happening in the background, don't update the search query
|
||||
if (!state.search.searchQuery && !isBackgroundSearch) {
|
||||
dispatch(doUpdateSearchQuery(query));
|
||||
}
|
||||
|
||||
const url = `${CONNECTION_STRING}search?${queryWithOptions}`;
|
||||
NativeModules.Requests.get(url)
|
||||
.then(handleNativeFetchResponse)
|
||||
.then((data: Array<{ name: String, claimId: string }>) => {
|
||||
const uris = [];
|
||||
const actions = [];
|
||||
|
||||
data.forEach(result => {
|
||||
if (result.name) {
|
||||
const uri = buildURI({
|
||||
claimName: result.name,
|
||||
claimId: result.claimId,
|
||||
});
|
||||
actions.push(doResolveUri(uri));
|
||||
uris.push(uri);
|
||||
}
|
||||
});
|
||||
|
||||
actions.push({
|
||||
type: ACTIONS.SEARCH_SUCCESS,
|
||||
data: {
|
||||
query: queryWithOptions,
|
||||
uris,
|
||||
},
|
||||
});
|
||||
dispatch(batchActions(...actions));
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
dispatch({
|
||||
type: ACTIONS.SEARCH_FAIL,
|
||||
});
|
||||
});
|
||||
};
|
|
@ -1,11 +1,17 @@
|
|||
import { ACTIONS } from 'lbry-redux';
|
||||
|
||||
export function doSetClientSetting(key, value) {
|
||||
return {
|
||||
type: ACTIONS.CLIENT_SETTING_CHANGED,
|
||||
data: {
|
||||
key,
|
||||
value,
|
||||
},
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: ACTIONS.CLIENT_SETTING_CHANGED,
|
||||
data: {
|
||||
key,
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
if (window.persistor) {
|
||||
window.persistor.flush();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -93,6 +93,11 @@ const filePageStyle = StyleSheet.create({
|
|||
fontSize: 14,
|
||||
color: Colors.LbryGreen,
|
||||
},
|
||||
anonChannelName: {
|
||||
fontFamily: 'Inter-UI-Regular',
|
||||
fontSize: 14,
|
||||
color: Colors.DescriptionGrey,
|
||||
},
|
||||
publishDateText: {
|
||||
fontFamily: 'Inter-UI-SemiBold',
|
||||
fontSize: 12,
|
||||
|
|
|
@ -197,7 +197,8 @@ const subscriptionsStyle = StyleSheet.create({
|
|||
},
|
||||
suggestedItemDetails: {
|
||||
marginLeft: 16,
|
||||
flexDirection: 'row',
|
||||
marginRight: 16,
|
||||
flex: 0.8,
|
||||
},
|
||||
suggestedItemSubscribe: {
|
||||
backgroundColor: Colors.White,
|
||||
|
@ -209,12 +210,14 @@ const subscriptionsStyle = StyleSheet.create({
|
|||
fontFamily: 'Inter-UI-Regular',
|
||||
fontSize: 16,
|
||||
marginBottom: 4,
|
||||
width: '85%',
|
||||
},
|
||||
suggestedItemName: {
|
||||
fontFamily: 'Inter-UI-SemiBold',
|
||||
fontSize: 14,
|
||||
marginBottom: 4,
|
||||
color: Colors.LbryGreen,
|
||||
width: '95%',
|
||||
},
|
||||
suggestedItemTagList: {
|
||||
flexDirection: 'row',
|
||||
|
|
|
@ -3,6 +3,8 @@ import { buildURI, isURIValid } from 'lbry-redux';
|
|||
import { doPopDrawerStack, doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
||||
import Constants, { DrawerRoutes } from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||
|
||||
const tagNameLength = 10;
|
||||
|
||||
function getRouteForSpecialUri(uri) {
|
||||
let targetRoute;
|
||||
const page = uri.substring(8).trim(); // 'lbry://?'.length == 8
|
||||
|
@ -191,6 +193,17 @@ export function formatTagTitle(title) {
|
|||
return title.charAt(0).toUpperCase() + title.substring(1);
|
||||
}
|
||||
|
||||
export function formatTagName(name) {
|
||||
if (!name) {
|
||||
return null;
|
||||
}
|
||||
if (name.length <= tagNameLength) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return name.substring(0, 7) + '...';
|
||||
}
|
||||
|
||||
// i18n placeholder until we find a good react-native i18n module
|
||||
export function __(str) {
|
||||
return str;
|
||||
|
|
Loading…
Reference in a new issue