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",
|
"private": "true",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||||
|
"devtools": "react-devtools",
|
||||||
"format": "prettier 'src/**/*.{js,json}' --write",
|
"format": "prettier 'src/**/*.{js,json}' --write",
|
||||||
"precommit": "lint-staged"
|
"precommit": "lint-staged"
|
||||||
},
|
},
|
||||||
|
@ -11,7 +12,7 @@
|
||||||
"base-64": "^0.1.0",
|
"base-64": "^0.1.0",
|
||||||
"@expo/vector-icons": "^8.1.0",
|
"@expo/vector-icons": "^8.1.0",
|
||||||
"gfycat-style-urls": "^1.0.3",
|
"gfycat-style-urls": "^1.0.3",
|
||||||
"lbry-redux": "lbryio/lbry-redux#multi-claim-search",
|
"lbry-redux": "lbryio/lbry-redux#67a654f60630710cae72419448a73a18d076d18a",
|
||||||
"lbryinc": "lbryio/lbryinc",
|
"lbryinc": "lbryio/lbryinc",
|
||||||
"lodash": ">=4.17.11",
|
"lodash": ">=4.17.11",
|
||||||
"merge": ">=1.2.1",
|
"merge": ">=1.2.1",
|
||||||
|
@ -63,6 +64,7 @@
|
||||||
"husky": "^0.14.3",
|
"husky": "^0.14.3",
|
||||||
"lint-staged": "^7.0.4",
|
"lint-staged": "^7.0.4",
|
||||||
"metro-react-native-babel-preset": "^0.55.0",
|
"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 discoverStyle from 'styles/discover';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
const horizontalLimit = 10;
|
const horizontalLimit = 7;
|
||||||
const softLimit = 500;
|
const softLimit = 500;
|
||||||
|
|
||||||
class ClaimList extends React.PureComponent {
|
class ClaimList extends React.PureComponent {
|
||||||
|
@ -199,8 +199,8 @@ class ClaimList extends React.PureComponent {
|
||||||
ListHeaderComponent={ListHeaderComponent}
|
ListHeaderComponent={ListHeaderComponent}
|
||||||
style={claimListStyle.verticalScrollContainer}
|
style={claimListStyle.verticalScrollContainer}
|
||||||
contentContainerStyle={claimListStyle.verticalScrollPadding}
|
contentContainerStyle={claimListStyle.verticalScrollPadding}
|
||||||
initialNumToRender={8}
|
initialNumToRender={10}
|
||||||
maxToRenderPerBatch={24}
|
maxToRenderPerBatch={20}
|
||||||
removeClippedSubviews
|
removeClippedSubviews
|
||||||
renderItem={({ item }) => (
|
renderItem={({ item }) => (
|
||||||
<FileListItem key={item} uri={item} style={claimListStyle.verticalListItem} navigation={navigation} />
|
<FileListItem key={item} uri={item} style={claimListStyle.verticalListItem} navigation={navigation} />
|
||||||
|
@ -208,7 +208,7 @@ class ClaimList extends React.PureComponent {
|
||||||
data={data}
|
data={data}
|
||||||
keyExtractor={(item, index) => item}
|
keyExtractor={(item, index) => item}
|
||||||
onEndReached={this.handleVerticalEndReached}
|
onEndReached={this.handleVerticalEndReached}
|
||||||
onEndReachedThreshold={0.9}
|
onEndReachedThreshold={0.2}
|
||||||
/>
|
/>
|
||||||
{(((subscriptionsView || trendingForAllView) && claimSearchLoading) || loading) && (
|
{(((subscriptionsView || trendingForAllView) && claimSearchLoading) || loading) && (
|
||||||
<View style={claimListStyle.verticalLoading}>
|
<View style={claimListStyle.verticalLoading}>
|
||||||
|
|
|
@ -57,7 +57,7 @@ class CustomRewardCard extends React.PureComponent<Props> {
|
||||||
return (
|
return (
|
||||||
<View style={[rewardStyle.rewardCard, rewardStyle.row]}>
|
<View style={[rewardStyle.rewardCard, rewardStyle.row]}>
|
||||||
<View style={rewardStyle.leftCol}>
|
<View style={rewardStyle.leftCol}>
|
||||||
{rewardIsPending && <ActivityIndicator size="small" color={Colors.LbryGreen} />}
|
{rewardIsPending && <ActivityIndicator size="small" color={Colors.NextLbryGreen} />}
|
||||||
</View>
|
</View>
|
||||||
<View style={rewardStyle.midCol}>
|
<View style={rewardStyle.midCol}>
|
||||||
<Text style={rewardStyle.rewardTitle}>Custom Code</Text>
|
<Text style={rewardStyle.rewardTitle}>Custom Code</Text>
|
||||||
|
|
|
@ -8,12 +8,15 @@ import {
|
||||||
makeSelectTitleForUri,
|
makeSelectTitleForUri,
|
||||||
makeSelectThumbnailForUri,
|
makeSelectThumbnailForUri,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
|
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
|
||||||
import { selectShowNsfw } from 'redux/selectors/settings';
|
import { selectShowNsfw } from 'redux/selectors/settings';
|
||||||
import FileListItem from './view';
|
import FileListItem from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
blackListedOutpoints: selectBlackListedOutpoints(state),
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
|
filteredOutpoints: selectFilteredOutpoints(state),
|
||||||
isDownloaded: !!makeSelectFileInfoForUri(props.uri)(state),
|
isDownloaded: !!makeSelectFileInfoForUri(props.uri)(state),
|
||||||
metadata: makeSelectMetadataForUri(props.uri)(state),
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
|
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
|
||||||
|
|
|
@ -48,8 +48,10 @@ class FileListItem extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
blackListedOutpoints,
|
||||||
claim,
|
claim,
|
||||||
fileInfo,
|
fileInfo,
|
||||||
|
filteredOutpoints,
|
||||||
metadata,
|
metadata,
|
||||||
featuredResult,
|
featuredResult,
|
||||||
isResolvingUri,
|
isResolvingUri,
|
||||||
|
@ -66,7 +68,7 @@ class FileListItem extends React.PureComponent {
|
||||||
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||||
const isResolving = !fileInfo && isResolvingUri;
|
const isResolving = !fileInfo && isResolvingUri;
|
||||||
|
|
||||||
let name, channel, height, channelClaimId, fullChannelUri, signingChannel;
|
let name, channel, height, channelClaimId, fullChannelUri, shouldHide, signingChannel;
|
||||||
if (claim) {
|
if (claim) {
|
||||||
name = claim.name;
|
name = claim.name;
|
||||||
signingChannel = claim.signing_channel;
|
signingChannel = claim.signing_channel;
|
||||||
|
@ -74,9 +76,14 @@ class FileListItem extends React.PureComponent {
|
||||||
height = claim.height;
|
height = claim.height;
|
||||||
channelClaimId = signingChannel ? signingChannel.claim_id : null;
|
channelClaimId = signingChannel ? signingChannel.claim_id : null;
|
||||||
fullChannelUri = channelClaimId ? `${channel}#${channelClaimId}` : channel;
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -468,7 +468,7 @@ class MediaPlayer extends React.PureComponent {
|
||||||
|
|
||||||
{this.state.buffering && (
|
{this.state.buffering && (
|
||||||
<View style={mediaPlayerStyle.loadingContainer}>
|
<View style={mediaPlayerStyle.loadingContainer}>
|
||||||
<ActivityIndicator color={Colors.LbryGreen} size="large" />
|
<ActivityIndicator color={Colors.NextLbryGreen} size="large" />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
doSearch,
|
|
||||||
makeSelectRecommendedContentForUri,
|
makeSelectRecommendedContentForUri,
|
||||||
makeSelectTitleForUri,
|
makeSelectTitleForUri,
|
||||||
selectIsSearching,
|
selectIsSearching,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
|
import { doNativeSearch } from 'redux/actions/performance';
|
||||||
import RelatedContent from './view';
|
import RelatedContent from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
@ -16,7 +16,7 @@ const select = (state, props) => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
search: query => dispatch(doSearch(query, 20, undefined, true)),
|
search: query => dispatch(doNativeSearch(query, 20, undefined, true)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -6,7 +6,7 @@ import FileListItem from 'component/fileListItem';
|
||||||
import fileListStyle from 'styles/fileList';
|
import fileListStyle from 'styles/fileList';
|
||||||
import relatedContentStyle from 'styles/relatedContent';
|
import relatedContentStyle from 'styles/relatedContent';
|
||||||
|
|
||||||
export default class RelatedContent extends React.PureComponent<Props> {
|
export default class RelatedContent extends React.PureComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ export default class RelatedContent extends React.PureComponent<Props> {
|
||||||
this.getRecommendedContent();
|
this.getRecommendedContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props) {
|
componentDidUpdate(prevProps) {
|
||||||
const { claim, uri } = this.props;
|
const { claim, uri } = this.props;
|
||||||
|
|
||||||
if (uri !== prevProps.uri) {
|
if (uri !== prevProps.uri) {
|
||||||
|
@ -38,7 +38,7 @@ export default class RelatedContent extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
didSearch: ?boolean;
|
didSearch;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { recommendedContent, isSearching, navigation } = this.props;
|
const { recommendedContent, isSearching, navigation } = this.props;
|
||||||
|
@ -60,7 +60,9 @@ export default class RelatedContent extends React.PureComponent<Props> {
|
||||||
onPress={() => navigateToUri(navigation, recommendedUri, { autoplay: true })}
|
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>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ class RewardCard extends React.PureComponent<Props> {
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
{isPending && <ActivityIndicator size="small" color={Colors.LbryGreen} />}
|
{isPending && <ActivityIndicator size="small" color={Colors.NextLbryGreen} />}
|
||||||
</View>
|
</View>
|
||||||
<View style={rewardStyle.midCol}>
|
<View style={rewardStyle.midCol}>
|
||||||
<Text style={rewardStyle.rewardTitle}>{reward.reward_title}</Text>
|
<Text style={rewardStyle.rewardTitle}>{reward.reward_title}</Text>
|
||||||
|
|
|
@ -19,7 +19,7 @@ class SubscribeButton extends React.PureComponent {
|
||||||
|
|
||||||
const iconColor = isSubscribed ? null : Colors.Red;
|
const iconColor = isSubscribed ? null : Colors.Red;
|
||||||
const subscriptionHandler = isSubscribed ? doChannelUnsubscribe : doChannelSubscribe;
|
const subscriptionHandler = isSubscribed ? doChannelUnsubscribe : doChannelSubscribe;
|
||||||
const subscriptionLabel = isSubscribed ? null : __('Subscribe');
|
const subscriptionLabel = isSubscribed ? null : __('Follow');
|
||||||
const { claimName } = parseURI(uri);
|
const { claimName } = parseURI(uri);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -28,7 +28,7 @@ class SubscribeButton extends React.PureComponent {
|
||||||
theme={'light'}
|
theme={'light'}
|
||||||
icon={isSubscribed ? 'heart-broken' : 'heart'}
|
icon={isSubscribed ? 'heart-broken' : 'heart'}
|
||||||
iconColor={iconColor}
|
iconColor={iconColor}
|
||||||
solid={isSubscribed ? false : true}
|
solid={!isSubscribed}
|
||||||
text={hideText ? null : subscriptionLabel}
|
text={hideText ? null : subscriptionLabel}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
subscriptionHandler({
|
subscriptionHandler({
|
||||||
|
|
|
@ -42,7 +42,6 @@ class SuggestedSubscriptionItem extends React.PureComponent {
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={subscriptionsStyle.suggestedItemDetails}>
|
<View style={subscriptionsStyle.suggestedItemDetails}>
|
||||||
<View style={subscriptionsStyle.suggestedItemInfo}>
|
|
||||||
{title && (
|
{title && (
|
||||||
<Text style={subscriptionsStyle.suggestedItemTitle} numberOfLines={1}>
|
<Text style={subscriptionsStyle.suggestedItemTitle} numberOfLines={1}>
|
||||||
{title}
|
{title}
|
||||||
|
@ -60,7 +59,6 @@ class SuggestedSubscriptionItem extends React.PureComponent {
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
|
||||||
|
|
||||||
<SubscribeButton style={subscriptionsStyle.suggestedItemSubscribe} uri={normalizeURI(uri)} />
|
<SubscribeButton style={subscriptionsStyle.suggestedItemSubscribe} uri={normalizeURI(uri)} />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -43,7 +43,7 @@ class SuggestedSubscriptions extends React.PureComponent {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<View style={subscriptionsStyle.centered}>
|
<View style={subscriptionsStyle.centered}>
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, TouchableOpacity, View } from 'react-native';
|
import { Text, TouchableOpacity, View } from 'react-native';
|
||||||
|
import { formatTagName } from 'utils/helper';
|
||||||
import tagStyle from 'styles/tag';
|
import tagStyle from 'styles/tag';
|
||||||
import Colors from 'styles/colors';
|
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';
|
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||||
|
|
||||||
export default class Tag extends React.PureComponent {
|
export default class Tag extends React.PureComponent {
|
||||||
onPressDefault = () => {
|
onPressDefault = () => {
|
||||||
const { name, navigation, type, onAddPress, onRemovePress } = this.props;
|
const { name, navigation, type, onAddPress, onRemovePress } = this.props;
|
||||||
if ('add' === type) {
|
if (type === 'add') {
|
||||||
if (onAddPress) {
|
if (onAddPress) {
|
||||||
onAddPress(name);
|
onAddPress(name);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ('remove' === type) {
|
if (type === 'remove') {
|
||||||
if (onRemovePress) {
|
if (onRemovePress) {
|
||||||
onRemovePress(name);
|
onRemovePress(name);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +49,7 @@ export default class Tag extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity style={styles} onPress={onPress || this.onPressDefault}>
|
<TouchableOpacity style={styles} onPress={onPress || this.onPressDefault}>
|
||||||
<View style={tagStyle.content}>
|
<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} />}
|
{type && <Icon style={tagStyle.icon} name={type === 'add' ? 'plus' : 'times'} size={8} />}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
|
@ -103,7 +103,7 @@ const Constants = {
|
||||||
TIME_ALL,
|
TIME_ALL,
|
||||||
|
|
||||||
CLAIM_SEARCH_SORT_BY_ITEMS: [
|
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: 'certificate', name: SORT_BY_NEW, label: 'New content' },
|
||||||
{ icon: 'chart-line', name: SORT_BY_TOP, label: 'Top 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,
|
authReducer,
|
||||||
blacklistReducer,
|
blacklistReducer,
|
||||||
costInfoReducer,
|
costInfoReducer,
|
||||||
|
filteredReducer,
|
||||||
homepageReducer,
|
homepageReducer,
|
||||||
rewardsReducer,
|
rewardsReducer,
|
||||||
subscriptionsReducer,
|
subscriptionsReducer,
|
||||||
|
@ -32,7 +33,6 @@ import AppWithNavigationState, {
|
||||||
} from 'component/AppNavigator';
|
} from 'component/AppNavigator';
|
||||||
import { REHYDRATE, PURGE, persistCombineReducers, persistStore } from 'redux-persist';
|
import { REHYDRATE, PURGE, persistCombineReducers, persistStore } from 'redux-persist';
|
||||||
import getStoredStateMigrateV4 from 'redux-persist/lib/integration/getStoredStateMigrateV4';
|
import getStoredStateMigrateV4 from 'redux-persist/lib/integration/getStoredStateMigrateV4';
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
|
||||||
import FilesystemStorage from 'redux-persist-filesystem-storage';
|
import FilesystemStorage from 'redux-persist-filesystem-storage';
|
||||||
import createCompressor from 'redux-persist-transform-compress';
|
import createCompressor from 'redux-persist-transform-compress';
|
||||||
import createFilter from 'redux-persist-transform-filter';
|
import createFilter from 'redux-persist-transform-filter';
|
||||||
|
@ -43,7 +43,6 @@ import thunk from 'redux-thunk';
|
||||||
|
|
||||||
const globalExceptionHandler = (error, isFatal) => {
|
const globalExceptionHandler = (error, isFatal) => {
|
||||||
if (error && NativeModules.Firebase) {
|
if (error && NativeModules.Firebase) {
|
||||||
console.log(error);
|
|
||||||
NativeModules.Firebase.logException(isFatal, error.message ? error.message : 'No message', JSON.stringify(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 compressor = createCompressor();
|
||||||
const authFilter = createFilter('auth', ['authToken']);
|
const authFilter = createFilter('auth', ['authToken']);
|
||||||
const contentFilter = createFilter('content', ['positions']);
|
const contentFilter = createFilter('content', ['positions']);
|
||||||
const saveClaimsFilter = createFilter('claims', ['byId', 'claimsByUri']);
|
const saveClaimsFilter = createFilter('claims', ['claimsByUri']);
|
||||||
const subscriptionsFilter = createFilter('subscriptions', ['enabledChannelNotifications', 'subscriptions']);
|
const subscriptionsFilter = createFilter('subscriptions', ['enabledChannelNotifications', 'subscriptions']);
|
||||||
const settingsFilter = createFilter('settings', ['clientSettings']);
|
const settingsFilter = createFilter('settings', ['clientSettings']);
|
||||||
const tagsFilter = createFilter('tags', ['followedTags']);
|
const tagsFilter = createFilter('tags', ['followedTags']);
|
||||||
|
@ -109,6 +108,7 @@ const reducers = persistCombineReducers(persistOptions, {
|
||||||
drawer: drawerReducer,
|
drawer: drawerReducer,
|
||||||
file: fileReducer,
|
file: fileReducer,
|
||||||
fileInfo: fileInfoReducer,
|
fileInfo: fileInfoReducer,
|
||||||
|
filtered: filteredReducer,
|
||||||
homepage: homepageReducer,
|
homepage: homepageReducer,
|
||||||
nav: navigatorReducer,
|
nav: navigatorReducer,
|
||||||
notifications: notificationsReducer,
|
notifications: notificationsReducer,
|
||||||
|
@ -136,11 +136,29 @@ const store = createStore(
|
||||||
);
|
);
|
||||||
window.store = store;
|
window.store = store;
|
||||||
|
|
||||||
persistStore(store, persistOptions, err => {
|
const persistor = persistStore(store, persistOptions, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log('Unable to load saved SETTINGS');
|
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
|
// TODO: Find i18n module that is compatible with react-native
|
||||||
global.__ = str => str;
|
global.__ = str => str;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { ActivityIndicator, Dimensions, Image, ScrollView, Text, TouchableOpacit
|
||||||
import { TabView, SceneMap } from 'react-native-tab-view';
|
import { TabView, SceneMap } from 'react-native-tab-view';
|
||||||
import { navigateBack } from 'utils/helper';
|
import { navigateBack } from 'utils/helper';
|
||||||
import Colors from 'styles/colors';
|
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 Button from 'component/button';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import FileList from 'component/fileList';
|
import FileList from 'component/fileList';
|
||||||
|
@ -53,7 +53,7 @@ class ChannelPage extends React.PureComponent {
|
||||||
if (fetching) {
|
if (fetching) {
|
||||||
contentList = (
|
contentList = (
|
||||||
<View style={channelPageStyle.busyContainer}>
|
<View style={channelPageStyle.busyContainer}>
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
|
||||||
<Text style={channelPageStyle.infoText}>Fetching content...</Text>
|
<Text style={channelPageStyle.infoText}>Fetching content...</Text>
|
||||||
</View>
|
</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 (
|
return (
|
||||||
<View style={channelPageStyle.aboutTab}>
|
<View style={channelPageStyle.aboutTab}>
|
||||||
{!website_url && !email && !description && (
|
{!websiteUrl && !email && !description && (
|
||||||
<View style={channelPageStyle.busyContainer}>
|
<View style={channelPageStyle.busyContainer}>
|
||||||
<Text style={channelPageStyle.infoText}>Nothing here yet. Please check back later.</Text>
|
<Text style={channelPageStyle.infoText}>Nothing here yet. Please check back later.</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(website_url || email || description) && (
|
{(websiteUrl || email || description) && (
|
||||||
<ScrollView style={channelPageStyle.aboutScroll} contentContainerStyle={channelPageStyle.aboutScrollContent}>
|
<ScrollView style={channelPageStyle.aboutScroll} contentContainerStyle={channelPageStyle.aboutScrollContent}>
|
||||||
{website_url && website_url.trim().length > 0 && (
|
{websiteUrl && websiteUrl.trim().length > 0 && (
|
||||||
<View style={channelPageStyle.aboutItem}>
|
<View style={channelPageStyle.aboutItem}>
|
||||||
<Text style={channelPageStyle.aboutTitle}>Website</Text>
|
<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>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -207,7 +207,7 @@ class DiscoverPage extends React.PureComponent {
|
||||||
|
|
||||||
buildSections = () => {
|
buildSections = () => {
|
||||||
return this.state.tagCollection.map(tags => ({
|
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],
|
data: [tags],
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
@ -216,7 +216,9 @@ class DiscoverPage extends React.PureComponent {
|
||||||
const tags = followedTags.map(tag => tag.name);
|
const tags = followedTags.map(tag => tag.name);
|
||||||
|
|
||||||
// each of the followed tags
|
// each of the followed tags
|
||||||
const tagCollection = tags.map(tag => [tag]);
|
const tagCollection = _.shuffle(tags)
|
||||||
|
.slice(0, 6)
|
||||||
|
.map(tag => [tag]);
|
||||||
// everything
|
// everything
|
||||||
tagCollection.unshift(tags);
|
tagCollection.unshift(tags);
|
||||||
|
|
||||||
|
@ -268,7 +270,8 @@ class DiscoverPage extends React.PureComponent {
|
||||||
renderItem={({ item, index, section }) => (
|
renderItem={({ item, index, section }) => (
|
||||||
<ClaimList
|
<ClaimList
|
||||||
key={item.sort().join(',')}
|
key={item.sort().join(',')}
|
||||||
orderBy={item.length > 1 ? Constants.DEFAULT_ORDER_BY : orderBy}
|
orderBy={orderBy}
|
||||||
|
time={Constants.TIME_WEEK}
|
||||||
tags={item}
|
tags={item}
|
||||||
morePlaceholder
|
morePlaceholder
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Lbry, buildURI } from 'lbry-redux';
|
||||||
import { ActivityIndicator, Button, FlatList, Text, TextInput, View, ScrollView } from 'react-native';
|
import { ActivityIndicator, Button, FlatList, Text, TextInput, View, ScrollView } from 'react-native';
|
||||||
import { navigateToUri, uriFromFileInfo } from 'utils/helper';
|
import { navigateToUri, uriFromFileInfo } from 'utils/helper';
|
||||||
import Colors from 'styles/colors';
|
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 PageHeader from 'component/pageHeader';
|
||||||
import FileListItem from 'component/fileListItem';
|
import FileListItem from 'component/fileListItem';
|
||||||
import FloatingWalletBalance from 'component/floatingWalletBalance';
|
import FloatingWalletBalance from 'component/floatingWalletBalance';
|
||||||
|
@ -65,7 +65,7 @@ class DownloadsPage extends React.PureComponent {
|
||||||
)}
|
)}
|
||||||
{fetching && !hasDownloads && (
|
{fetching && !hasDownloads && (
|
||||||
<View style={downloadsStyle.busyContainer}>
|
<View style={downloadsStyle.busyContainer}>
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} style={downloadsStyle.loading} />
|
<ActivityIndicator size="large" color={Colors.NextLbryGreen} style={downloadsStyle.loading} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{hasDownloads && (
|
{hasDownloads && (
|
||||||
|
|
|
@ -563,11 +563,11 @@ class FilePage extends React.PureComponent {
|
||||||
|
|
||||||
let innerContent = null;
|
let innerContent = null;
|
||||||
if ((isResolvingUri && !claim) || !claim) {
|
if ((isResolvingUri && !claim) || !claim) {
|
||||||
innerContent = (
|
return (
|
||||||
<View style={filePageStyle.container}>
|
<View style={filePageStyle.container}>
|
||||||
{isResolvingUri && (
|
{isResolvingUri && (
|
||||||
<View style={filePageStyle.busyContainer}>
|
<View style={filePageStyle.busyContainer}>
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
|
||||||
<Text style={filePageStyle.infoText}>Loading decentralized data...</Text>
|
<Text style={filePageStyle.infoText}>Loading decentralized data...</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
@ -579,9 +579,13 @@ class FilePage extends React.PureComponent {
|
||||||
<UriBar value={uri} navigation={navigation} />
|
<UriBar value={uri} navigation={navigation} />
|
||||||
</View>
|
</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;
|
let isClaimBlackListed = false;
|
||||||
|
|
||||||
if (blackListedOutpoints) {
|
if (blackListedOutpoints) {
|
||||||
|
@ -595,7 +599,7 @@ class FilePage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isClaimBlackListed) {
|
if (isClaimBlackListed) {
|
||||||
innerContent = (
|
return (
|
||||||
<View style={filePageStyle.pageContainer}>
|
<View style={filePageStyle.pageContainer}>
|
||||||
<View style={filePageStyle.dmcaContainer}>
|
<View style={filePageStyle.dmcaContainer}>
|
||||||
<Text style={filePageStyle.dmcaText}>
|
<Text style={filePageStyle.dmcaText}>
|
||||||
|
@ -607,7 +611,8 @@ class FilePage extends React.PureComponent {
|
||||||
<UriBar value={uri} navigation={navigation} />
|
<UriBar value={uri} navigation={navigation} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
let tags = [];
|
let tags = [];
|
||||||
if (claim && claim.value && claim.value.tags) {
|
if (claim && claim.value && claim.value.tags) {
|
||||||
tags = claim.value.tags;
|
tags = claim.value.tags;
|
||||||
|
@ -678,12 +683,7 @@ class FilePage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (
|
if (fileInfo && !this.state.autoDownloadStarted && this.state.uriVars && this.state.uriVars.download === 'true') {
|
||||||
fileInfo &&
|
|
||||||
!this.state.autoDownloadStarted &&
|
|
||||||
this.state.uriVars &&
|
|
||||||
this.state.uriVars.download === 'true'
|
|
||||||
) {
|
|
||||||
this.setState({ autoDownloadStarted: true }, () => {
|
this.setState({ autoDownloadStarted: true }, () => {
|
||||||
purchaseUri(uri, costInfo, !isPlayable);
|
purchaseUri(uri, costInfo, !isPlayable);
|
||||||
if (NativeModules.UtilityModule) {
|
if (NativeModules.UtilityModule) {
|
||||||
|
@ -697,7 +697,7 @@ class FilePage extends React.PureComponent {
|
||||||
openFile();
|
openFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
innerContent = (
|
return (
|
||||||
<View style={filePageStyle.pageContainer}>
|
<View style={filePageStyle.pageContainer}>
|
||||||
{!this.state.fullscreenMode && <UriBar value={uri} navigation={navigation} />}
|
{!this.state.fullscreenMode && <UriBar value={uri} navigation={navigation} />}
|
||||||
{this.state.showWebView && isWebViewable && (
|
{this.state.showWebView && isWebViewable && (
|
||||||
|
@ -724,7 +724,7 @@ class FilePage extends React.PureComponent {
|
||||||
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={thumbnail} />
|
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={thumbnail} />
|
||||||
)}
|
)}
|
||||||
{(!this.state.downloadButtonShown || this.state.downloadPressed) && !this.state.mediaLoaded && (
|
{(!this.state.downloadButtonShown || this.state.downloadPressed) && !this.state.mediaLoaded && (
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} style={filePageStyle.loading} />
|
<ActivityIndicator size="large" color={Colors.NextLbryGreen} style={filePageStyle.loading} />
|
||||||
)}
|
)}
|
||||||
{((isPlayable && !completed && !canLoadMedia) ||
|
{((isPlayable && !completed && !canLoadMedia) ||
|
||||||
canOpen ||
|
canOpen ||
|
||||||
|
@ -766,8 +766,9 @@ class FilePage extends React.PureComponent {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) &&
|
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && this.state.fullscreenMode && (
|
||||||
this.state.fullscreenMode && <View style={fsPlayerBgStyle} />}
|
<View style={fsPlayerBgStyle} />
|
||||||
|
)}
|
||||||
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && (
|
{(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && (
|
||||||
<MediaPlayer
|
<MediaPlayer
|
||||||
claim={claim}
|
claim={claim}
|
||||||
|
@ -844,9 +845,9 @@ class FilePage extends React.PureComponent {
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</TouchableWithoutFeedback>
|
</TouchableWithoutFeedback>
|
||||||
{channelName && (
|
|
||||||
<View style={filePageStyle.channelRow}>
|
<View style={filePageStyle.channelRow}>
|
||||||
<View style={filePageStyle.publishInfo}>
|
<View style={filePageStyle.publishInfo}>
|
||||||
|
{channelName && (
|
||||||
<Link
|
<Link
|
||||||
style={filePageStyle.channelName}
|
style={filePageStyle.channelName}
|
||||||
selectable
|
selectable
|
||||||
|
@ -857,6 +858,12 @@ class FilePage extends React.PureComponent {
|
||||||
navigateToUri(navigation, normalizeURI(fullChannelUri));
|
navigateToUri(navigation, normalizeURI(fullChannelUri));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
{!channelName && (
|
||||||
|
<Text style={filePageStyle.anonChannelName} selectable ellipsizeMode={'tail'}>
|
||||||
|
Anonymous
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
<DateTime
|
<DateTime
|
||||||
style={filePageStyle.publishDate}
|
style={filePageStyle.publishDate}
|
||||||
textStyle={filePageStyle.publishDateText}
|
textStyle={filePageStyle.publishDateText}
|
||||||
|
@ -866,8 +873,7 @@ class FilePage extends React.PureComponent {
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={filePageStyle.subscriptionRow}>
|
<View style={filePageStyle.subscriptionRow}>
|
||||||
{false &&
|
{false && ((isPlayable && !fileInfo) || (isPlayable && fileInfo && !fileInfo.download_path)) && (
|
||||||
((isPlayable && !fileInfo) || (isPlayable && fileInfo && !fileInfo.download_path)) && (
|
|
||||||
<Button
|
<Button
|
||||||
style={[filePageStyle.actionButton, filePageStyle.saveFileButton]}
|
style={[filePageStyle.actionButton, filePageStyle.saveFileButton]}
|
||||||
theme={'light'}
|
theme={'light'}
|
||||||
|
@ -881,20 +887,23 @@ class FilePage extends React.PureComponent {
|
||||||
icon={'gift'}
|
icon={'gift'}
|
||||||
onPress={() => this.setState({ showTipView: true })}
|
onPress={() => this.setState({ showTipView: true })}
|
||||||
/>
|
/>
|
||||||
|
{channelName && (
|
||||||
<SubscribeButton
|
<SubscribeButton
|
||||||
style={filePageStyle.actionButton}
|
style={filePageStyle.actionButton}
|
||||||
uri={fullChannelUri}
|
uri={fullChannelUri}
|
||||||
name={channelName}
|
name={channelName}
|
||||||
hideText={false}
|
hideText={false}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
{channelName && (
|
||||||
<SubscribeNotificationButton
|
<SubscribeNotificationButton
|
||||||
style={[filePageStyle.actionButton, filePageStyle.bellButton]}
|
style={[filePageStyle.actionButton, filePageStyle.bellButton]}
|
||||||
uri={fullChannelUri}
|
uri={fullChannelUri}
|
||||||
name={channelName}
|
name={channelName}
|
||||||
/>
|
/>
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
{this.state.showTipView && <View style={filePageStyle.divider} />}
|
{this.state.showTipView && <View style={filePageStyle.divider} />}
|
||||||
{this.state.showTipView && (
|
{this.state.showTipView && (
|
||||||
|
@ -956,9 +965,8 @@ class FilePage extends React.PureComponent {
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return innerContent;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -550,7 +550,7 @@ class PublishPage extends React.PureComponent {
|
||||||
</View>
|
</View>
|
||||||
{(!videos || !thumbnailPath || !galleryThumbnailsChecked) && (
|
{(!videos || !thumbnailPath || !galleryThumbnailsChecked) && (
|
||||||
<View style={publishStyle.loadingView}>
|
<View style={publishStyle.loadingView}>
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{thumbnailPath && (!videos || videos.length === 0) && (
|
{thumbnailPath && (!videos || videos.length === 0) && (
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { Lbry } from 'lbry-redux';
|
import { Lbry } from 'lbry-redux';
|
||||||
import { ActivityIndicator, NativeModules, ScrollView, Text, View } from 'react-native';
|
import { ActivityIndicator, NativeModules, ScrollView, Text, View } from 'react-native';
|
||||||
import Colors from 'styles/colors';
|
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 Link from 'component/link';
|
||||||
import CustomRewardCard from 'component/customRewardCard';
|
import CustomRewardCard from 'component/customRewardCard';
|
||||||
import PageHeader from 'component/pageHeader';
|
import PageHeader from 'component/pageHeader';
|
||||||
|
@ -87,7 +87,7 @@ class RewardsPage extends React.PureComponent {
|
||||||
// claim new_user and new_mobile rewards
|
// claim new_user and new_mobile rewards
|
||||||
for (let i = 0; i < rewards.length; i++) {
|
for (let i = 0; i < rewards.length; i++) {
|
||||||
const { reward_type: type } = rewards[i];
|
const { reward_type: type } = rewards[i];
|
||||||
if ('new_user' === type || 'new_mobile' === type) {
|
if (type === 'new_user' || type === 'new_mobile') {
|
||||||
claimReward(rewards[i]);
|
claimReward(rewards[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ class RewardsPage extends React.PureComponent {
|
||||||
if (fetching) {
|
if (fetching) {
|
||||||
return (
|
return (
|
||||||
<View style={rewardStyle.busyContainer}>
|
<View style={rewardStyle.busyContainer}>
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
|
||||||
<Text style={rewardStyle.infoText}>Fetching rewards...</Text>
|
<Text style={rewardStyle.infoText}>Fetching rewards...</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Lbry, parseURI, normalizeURI, isURIValid } from 'lbry-redux';
|
||||||
import { ActivityIndicator, Button, Text, TextInput, View, ScrollView } from 'react-native';
|
import { ActivityIndicator, Button, Text, TextInput, View, ScrollView } from 'react-native';
|
||||||
import { navigateToUri } from 'utils/helper';
|
import { navigateToUri } from 'utils/helper';
|
||||||
import Colors from 'styles/colors';
|
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 PageHeader from 'component/pageHeader';
|
||||||
import FileListItem from 'component/fileListItem';
|
import FileListItem from 'component/fileListItem';
|
||||||
import FloatingWalletBalance from 'component/floatingWalletBalance';
|
import FloatingWalletBalance from 'component/floatingWalletBalance';
|
||||||
|
@ -91,7 +91,7 @@ class SearchPage extends React.PureComponent {
|
||||||
<UriBar value={query} navigation={navigation} onSearchSubmitted={this.handleSearchSubmitted} />
|
<UriBar value={query} navigation={navigation} onSearchSubmitted={this.handleSearchSubmitted} />
|
||||||
{isSearching && (
|
{isSearching && (
|
||||||
<View style={searchStyle.busyContainer}>
|
<View style={searchStyle.busyContainer}>
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} style={searchStyle.loading} />
|
<ActivityIndicator size="large" color={Colors.NextLbryGreen} style={searchStyle.loading} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ class SearchPage extends React.PureComponent {
|
||||||
<FileListItem
|
<FileListItem
|
||||||
key={this.state.currentUri}
|
key={this.state.currentUri}
|
||||||
uri={this.state.currentUri}
|
uri={this.state.currentUri}
|
||||||
featuredResult={true}
|
featuredResult
|
||||||
style={searchStyle.featuredResultItem}
|
style={searchStyle.featuredResultItem}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
onPress={() => navigateToUri(navigation, this.state.currentUri)}
|
onPress={() => navigateToUri(navigation, this.state.currentUri)}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { doBalanceSubscribe, doUpdateBlockHeight, doToast } from 'lbry-redux';
|
||||||
import {
|
import {
|
||||||
doAuthenticate,
|
doAuthenticate,
|
||||||
doBlackListedOutpointsSubscribe,
|
doBlackListedOutpointsSubscribe,
|
||||||
|
doFilteredOutpointsSubscribe,
|
||||||
doCheckSubscriptionsInit,
|
doCheckSubscriptionsInit,
|
||||||
doFetchMySubscriptions,
|
doFetchMySubscriptions,
|
||||||
doFetchRewardedContent,
|
doFetchRewardedContent,
|
||||||
|
@ -25,6 +26,7 @@ const perform = dispatch => ({
|
||||||
authenticate: (appVersion, os) => dispatch(doAuthenticate(appVersion, os)),
|
authenticate: (appVersion, os) => dispatch(doAuthenticate(appVersion, os)),
|
||||||
balanceSubscribe: () => dispatch(doBalanceSubscribe()),
|
balanceSubscribe: () => dispatch(doBalanceSubscribe()),
|
||||||
blacklistedOutpointsSubscribe: () => dispatch(doBlackListedOutpointsSubscribe()),
|
blacklistedOutpointsSubscribe: () => dispatch(doBlackListedOutpointsSubscribe()),
|
||||||
|
filteredOutpointsSubscribe: () => dispatch(doFilteredOutpointsSubscribe()),
|
||||||
checkSubscriptionsInit: () => dispatch(doCheckSubscriptionsInit()),
|
checkSubscriptionsInit: () => dispatch(doCheckSubscriptionsInit()),
|
||||||
fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
|
fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
|
||||||
fetchSubscriptions: callback => dispatch(doFetchMySubscriptions(callback)),
|
fetchSubscriptions: callback => dispatch(doFetchMySubscriptions(callback)),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import Button from 'component/button';
|
||||||
import ProgressBar from 'component/progressBar';
|
import ProgressBar from 'component/progressBar';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Colors from 'styles/colors';
|
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';
|
import splashStyle from 'styles/splash';
|
||||||
|
|
||||||
const BLOCK_HEIGHT_INTERVAL = 1000 * 60 * 2.5; // every 2.5 minutes
|
const BLOCK_HEIGHT_INTERVAL = 1000 * 60 * 2.5; // every 2.5 minutes
|
||||||
|
@ -50,7 +50,7 @@ class SplashScreen extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateToMain = () => {
|
navigateToMain = () => {
|
||||||
const { navigation } = this.props;
|
const { navigation, notify, verifyUserEmail, verifyUserEmailFailure } = this.props;
|
||||||
const resetAction = StackActions.reset({
|
const resetAction = StackActions.reset({
|
||||||
index: 0,
|
index: 0,
|
||||||
actions: [NavigationActions.navigate({ routeName: 'Main' })],
|
actions: [NavigationActions.navigate({ routeName: 'Main' })],
|
||||||
|
@ -114,9 +114,8 @@ class SplashScreen extends React.PureComponent {
|
||||||
balanceSubscribe,
|
balanceSubscribe,
|
||||||
blacklistedOutpointsSubscribe,
|
blacklistedOutpointsSubscribe,
|
||||||
checkSubscriptionsInit,
|
checkSubscriptionsInit,
|
||||||
|
filteredOutpointsSubscribe,
|
||||||
getSync,
|
getSync,
|
||||||
navigation,
|
|
||||||
notify,
|
|
||||||
updateBlockHeight,
|
updateBlockHeight,
|
||||||
user,
|
user,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -125,11 +124,8 @@ class SplashScreen extends React.PureComponent {
|
||||||
// Leave the splash screen
|
// Leave the splash screen
|
||||||
balanceSubscribe();
|
balanceSubscribe();
|
||||||
blacklistedOutpointsSubscribe();
|
blacklistedOutpointsSubscribe();
|
||||||
|
filteredOutpointsSubscribe();
|
||||||
checkSubscriptionsInit();
|
checkSubscriptionsInit();
|
||||||
updateBlockHeight();
|
|
||||||
setInterval(() => {
|
|
||||||
updateBlockHeight();
|
|
||||||
}, BLOCK_HEIGHT_INTERVAL);
|
|
||||||
|
|
||||||
if (user && user.id && user.has_verified_email) {
|
if (user && user.id && user.has_verified_email) {
|
||||||
// user already authenticated
|
// user already authenticated
|
||||||
|
@ -186,7 +182,6 @@ class SplashScreen extends React.PureComponent {
|
||||||
this.finishSplashScreen();
|
this.finishSplashScreen();
|
||||||
})
|
})
|
||||||
.catch(() => this.handleAccountUnlockFailed());
|
.catch(() => this.handleAccountUnlockFailed());
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
message: testingNetwork,
|
message: testingNetwork,
|
||||||
|
@ -222,7 +217,7 @@ class SplashScreen extends React.PureComponent {
|
||||||
});
|
});
|
||||||
} else if (walletStatus && walletStatus.blocks_behind > 0) {
|
} else if (walletStatus && walletStatus.blocks_behind > 0) {
|
||||||
const behind = walletStatus.blocks_behind;
|
const behind = walletStatus.blocks_behind;
|
||||||
const behindText = behind + ' block' + (behind == 1 ? '' : 's') + ' behind';
|
const behindText = behind + ' block' + (behind === 1 ? '' : 's') + ' behind';
|
||||||
this.setState({
|
this.setState({
|
||||||
message: 'Blockchain Sync',
|
message: 'Blockchain Sync',
|
||||||
details: behindText,
|
details: behindText,
|
||||||
|
@ -254,7 +249,7 @@ class SplashScreen extends React.PureComponent {
|
||||||
// Start measuring the first launch time from the splash screen
|
// Start measuring the first launch time from the splash screen
|
||||||
// (time to first user interaction - after first run completed)
|
// (time to first user interaction - after first run completed)
|
||||||
AsyncStorage.getItem('hasLaunched').then(value => {
|
AsyncStorage.getItem('hasLaunched').then(value => {
|
||||||
if ('true' !== value) {
|
if (value !== 'true') {
|
||||||
AsyncStorage.setItem('hasLaunched', 'true');
|
AsyncStorage.setItem('hasLaunched', 'true');
|
||||||
// only set firstLaunchTime since we've determined that this is the first app launch ever
|
// only set firstLaunchTime since we've determined that this is the first app launch ever
|
||||||
AsyncStorage.setItem('firstLaunchTime', String(moment().unix()));
|
AsyncStorage.setItem('firstLaunchTime', String(moment().unix()));
|
||||||
|
|
|
@ -5,8 +5,9 @@ import {
|
||||||
doFetchRecommendedSubscriptions,
|
doFetchRecommendedSubscriptions,
|
||||||
selectSubscriptionClaims,
|
selectSubscriptionClaims,
|
||||||
selectSubscriptions,
|
selectSubscriptions,
|
||||||
selectSubscriptionsBeingFetched,
|
|
||||||
selectIsFetchingSubscriptions,
|
selectIsFetchingSubscriptions,
|
||||||
|
selectIsFetchingSuggested,
|
||||||
|
selectSuggestedChannels,
|
||||||
selectUnreadSubscriptions,
|
selectUnreadSubscriptions,
|
||||||
selectViewMode,
|
selectViewMode,
|
||||||
selectFirstRunCompleted,
|
selectFirstRunCompleted,
|
||||||
|
@ -16,13 +17,15 @@ import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
||||||
import { doSetClientSetting } from 'redux/actions/settings';
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import { selectCurrentRoute } from 'redux/selectors/drawer';
|
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';
|
import SubscriptionsPage from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
currentRoute: selectCurrentRoute(state),
|
currentRoute: selectCurrentRoute(state),
|
||||||
loading: selectIsFetchingSubscriptions(state) || Boolean(Object.keys(selectSubscriptionsBeingFetched(state)).length),
|
loading: selectIsFetchingSubscriptions(state),
|
||||||
|
loadingSuggested: selectIsFetchingSuggested(state),
|
||||||
subscribedChannels: selectSubscriptions(state),
|
subscribedChannels: selectSubscriptions(state),
|
||||||
|
suggestedChannels: selectSuggestedChannels(state),
|
||||||
subscriptionsViewMode: makeSelectClientSetting(Constants.SETTING_SUBSCRIPTIONS_VIEW_MODE)(state),
|
subscriptionsViewMode: makeSelectClientSetting(Constants.SETTING_SUBSCRIPTIONS_VIEW_MODE)(state),
|
||||||
allSubscriptions: selectSubscriptionClaims(state),
|
allSubscriptions: selectSubscriptionClaims(state),
|
||||||
unreadSubscriptions: selectUnreadSubscriptions(state),
|
unreadSubscriptions: selectUnreadSubscriptions(state),
|
||||||
|
|
|
@ -115,11 +115,12 @@ class SubscriptionsPage extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
suggestedChannels,
|
||||||
subscribedChannels,
|
subscribedChannels,
|
||||||
allSubscriptions,
|
allSubscriptions,
|
||||||
loading,
|
|
||||||
viewMode,
|
viewMode,
|
||||||
doSetViewMode,
|
doSetViewMode,
|
||||||
|
loading,
|
||||||
loadingSuggested,
|
loadingSuggested,
|
||||||
firstRunCompleted,
|
firstRunCompleted,
|
||||||
doCompleteFirstRun,
|
doCompleteFirstRun,
|
||||||
|
@ -182,7 +183,7 @@ class SubscriptionsPage extends React.PureComponent {
|
||||||
|
|
||||||
{hasSubscriptions && loading && (
|
{hasSubscriptions && loading && (
|
||||||
<View style={subscriptionsStyle.busyContainer}>
|
<View style={subscriptionsStyle.busyContainer}>
|
||||||
<ActivityIndicator size="large" color={Colors.LbryGreen} style={subscriptionsStyle.loading} />
|
<ActivityIndicator size="large" color={Colors.NextLbryGreen} style={subscriptionsStyle.loading} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -209,8 +210,7 @@ class SubscriptionsPage extends React.PureComponent {
|
||||||
|
|
||||||
{loadingSuggested && (
|
{loadingSuggested && (
|
||||||
<View style={subscriptionsStyle.centered}>
|
<View style={subscriptionsStyle.centered}>
|
||||||
<ActivityIndicator size="large" colors={Colors.LbryGreen} style={subscriptionsStyle.loading} />
|
<ActivityIndicator size="large" colors={Colors.NextLbryGreen} style={subscriptionsStyle.loading} />
|
||||||
\\
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{!loadingSuggested && <SuggestedSubscriptions navigation={navigation} />}
|
{!loadingSuggested && <SuggestedSubscriptions navigation={navigation} />}
|
||||||
|
|
|
@ -78,7 +78,7 @@ class TagPage extends React.PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
handleTimeItemSelected = item => {
|
handleTimeItemSelected = item => {
|
||||||
this.setState({ time: item.name });
|
this.setState({ currentTimeItem: item, time: item.name, showTimePicker: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -95,6 +95,7 @@ class TrendingPage extends React.PureComponent {
|
||||||
orderBy={Constants.DEFAULT_ORDER_BY}
|
orderBy={Constants.DEFAULT_ORDER_BY}
|
||||||
trendingForAll={TRENDING_FOR_ITEMS[0].name === currentTrendingForItem.name}
|
trendingForAll={TRENDING_FOR_ITEMS[0].name === currentTrendingForItem.name}
|
||||||
tags={followedTags.map(tag => tag.name)}
|
tags={followedTags.map(tag => tag.name)}
|
||||||
|
time={null}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
orientation={Constants.ORIENTATION_VERTICAL}
|
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';
|
import { ACTIONS } from 'lbry-redux';
|
||||||
|
|
||||||
export function doSetClientSetting(key, value) {
|
export function doSetClientSetting(key, value) {
|
||||||
return {
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
type: ACTIONS.CLIENT_SETTING_CHANGED,
|
type: ACTIONS.CLIENT_SETTING_CHANGED,
|
||||||
data: {
|
data: {
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
},
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (window.persistor) {
|
||||||
|
window.persistor.flush();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,11 @@ const filePageStyle = StyleSheet.create({
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Colors.LbryGreen,
|
color: Colors.LbryGreen,
|
||||||
},
|
},
|
||||||
|
anonChannelName: {
|
||||||
|
fontFamily: 'Inter-UI-Regular',
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.DescriptionGrey,
|
||||||
|
},
|
||||||
publishDateText: {
|
publishDateText: {
|
||||||
fontFamily: 'Inter-UI-SemiBold',
|
fontFamily: 'Inter-UI-SemiBold',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
|
|
@ -197,7 +197,8 @@ const subscriptionsStyle = StyleSheet.create({
|
||||||
},
|
},
|
||||||
suggestedItemDetails: {
|
suggestedItemDetails: {
|
||||||
marginLeft: 16,
|
marginLeft: 16,
|
||||||
flexDirection: 'row',
|
marginRight: 16,
|
||||||
|
flex: 0.8,
|
||||||
},
|
},
|
||||||
suggestedItemSubscribe: {
|
suggestedItemSubscribe: {
|
||||||
backgroundColor: Colors.White,
|
backgroundColor: Colors.White,
|
||||||
|
@ -209,12 +210,14 @@ const subscriptionsStyle = StyleSheet.create({
|
||||||
fontFamily: 'Inter-UI-Regular',
|
fontFamily: 'Inter-UI-Regular',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
|
width: '85%',
|
||||||
},
|
},
|
||||||
suggestedItemName: {
|
suggestedItemName: {
|
||||||
fontFamily: 'Inter-UI-SemiBold',
|
fontFamily: 'Inter-UI-SemiBold',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
color: Colors.LbryGreen,
|
color: Colors.LbryGreen,
|
||||||
|
width: '95%',
|
||||||
},
|
},
|
||||||
suggestedItemTagList: {
|
suggestedItemTagList: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { buildURI, isURIValid } from 'lbry-redux';
|
||||||
import { doPopDrawerStack, doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
import { doPopDrawerStack, doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
||||||
import Constants, { DrawerRoutes } from 'constants'; // eslint-disable-line node/no-deprecated-api
|
import Constants, { DrawerRoutes } from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||||
|
|
||||||
|
const tagNameLength = 10;
|
||||||
|
|
||||||
function getRouteForSpecialUri(uri) {
|
function getRouteForSpecialUri(uri) {
|
||||||
let targetRoute;
|
let targetRoute;
|
||||||
const page = uri.substring(8).trim(); // 'lbry://?'.length == 8
|
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);
|
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
|
// i18n placeholder until we find a good react-native i18n module
|
||||||
export function __(str) {
|
export function __(str) {
|
||||||
return str;
|
return str;
|
||||||
|
|
Loading…
Reference in a new issue